tcpip.c revision 18705
1251607Sdim/* 2251607Sdim * $Id: tcpip.c,v 1.46 1996/09/01 08:17:14 jkh Exp $ 3251607Sdim * 4251607Sdim * Copyright (c) 1995 5251607Sdim * Gary J Palmer. All rights reserved. 6251607Sdim * Copyright (c) 1996 7251607Sdim * Jordan K. Hubbard. All rights reserved. 8251607Sdim * 9251607Sdim * Redistribution and use in source and binary forms, with or without 10251607Sdim * modification, are permitted provided that the following conditions 11251607Sdim * are met: 12251607Sdim * 1. Redistributions of source code must retain the above copyright 13251607Sdim * notice, this list of conditions and the following disclaimer, 14251607Sdim * verbatim and that no modifications are made prior to this 15251607Sdim * point in the file. 16251607Sdim * 2. Redistributions in binary form must reproduce the above copyright 17251607Sdim * notice, this list of conditions and the following disclaimer in the 18251607Sdim * documentation and/or other materials provided with the distribution. 19251607Sdim * 20251607Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21251607Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22251607Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23251607Sdim * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24251607Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25251607Sdim * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 26251607Sdim * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27251607Sdim * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 28251607Sdim * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29251607Sdim * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30251607Sdim * 31251607Sdim */ 32263509Sdim 33263509Sdim/* 34263509Sdim * All kinds of hacking also performed by jkh on this code. Don't 35263509Sdim * blame Gary for every bogosity you see here.. :-) 36251607Sdim * 37251607Sdim * -jkh 38251607Sdim */ 39251607Sdim 40251607Sdim#include <stdio.h> 41251607Sdim#include <stdlib.h> 42251607Sdim#include <unistd.h> 43251607Sdim#include <sys/param.h> 44251607Sdim#include <string.h> 45251607Sdim#include <dialog.h> 46251607Sdim#include "ui_objects.h" 47251607Sdim#include "dir.h" 48251607Sdim#include "dialog.priv.h" 49251607Sdim#include "colors.h" 50251607Sdim#include "sysinstall.h" 51263509Sdim 52263509Sdim/* The help file for the TCP/IP setup screen */ 53251607Sdim#define TCP_HELPFILE "tcp" 54251607Sdim 55251607Sdim/* These are nasty, but they make the layout structure a lot easier ... */ 56251607Sdim 57251607Sdimstatic char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN], 58251607Sdim gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN]; 59251607Sdimstatic int okbutton, cancelbutton; 60251607Sdimstatic char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN]; 61251607Sdim 62251607Sdim/* What the screen size is meant to be */ 63251607Sdim#define TCP_DIALOG_Y 0 64251607Sdim#define TCP_DIALOG_X 8 65251607Sdim#define TCP_DIALOG_WIDTH COLS - 16 66251607Sdim#define TCP_DIALOG_HEIGHT LINES - 2 67251607Sdim 68251607Sdim/* The screen layout structure */ 69251607Sdimtypedef struct _layout { 70251607Sdim int y; /* x & Y co-ordinates */ 71251607Sdim int x; 72251607Sdim int len; /* The size of the dialog on the screen */ 73251607Sdim int maxlen; /* How much the user can type in ... */ 74251607Sdim char *prompt; /* The string for the prompt */ 75251607Sdim char *help; /* The display for the help line */ 76251607Sdim void *var; /* The var to set when this changes */ 77251607Sdim int type; /* The type of the dialog to create */ 78251607Sdim void *obj; /* The obj pointer returned by libdialog */ 79} Layout; 80 81static Layout layout[] = { 82{ 1, 2, 25, HOSTNAME_FIELD_LEN - 1, 83 "Host:", "Your fully-qualified hostname, e.g. foo.bar.com", 84 hostname, STRINGOBJ, NULL }, 85#define LAYOUT_HOSTNAME 0 86{ 1, 35, 20, HOSTNAME_FIELD_LEN - 1, 87 "Domain:", 88 "The name of the domain that your machine is in, e.g. bar.com", 89 domainname, STRINGOBJ, NULL }, 90#define LAYOUT_DOMAINNAME 1 91{ 5, 2, 18, IPADDR_FIELD_LEN - 1, 92 "Gateway:", 93 "IP address of host forwarding packets to non-local destinations", 94 gateway, STRINGOBJ, NULL }, 95#define LAYOUT_GATEWAY 2 96{ 5, 35, 18, IPADDR_FIELD_LEN - 1, 97 "Name server:", "IP address of your local DNS server", 98 nameserver, STRINGOBJ, NULL }, 99#define LAYOUT_NAMESERVER 3 100{ 10, 10, 18, IPADDR_FIELD_LEN - 1, 101 "IP Address:", 102 "The IP address to be used for this interface", 103 ipaddr, STRINGOBJ, NULL }, 104#define LAYOUT_IPADDR 4 105{ 10, 35, 18, IPADDR_FIELD_LEN - 1, 106 "Netmask:", 107 "The netmask for this interface, e.g. 0xffffff00 for a class C network", 108 netmask, STRINGOBJ, NULL }, 109#define LAYOUT_NETMASK 5 110{ 14, 10, 37, HOSTNAME_FIELD_LEN - 1, 111 "Extra options to ifconfig:", 112 "Any interface-specific options to ifconfig you would like to add", 113 extras, STRINGOBJ, NULL }, 114#define LAYOUT_EXTRAS 6 115{ 19, 15, 0, 0, 116 "OK", "Select this if you are happy with these settings", 117 &okbutton, BUTTONOBJ, NULL }, 118#define LAYOUT_OKBUTTON 7 119{ 19, 35, 0, 0, 120 "CANCEL", "Select this if you wish to cancel this screen", 121 &cancelbutton, BUTTONOBJ, NULL }, 122#define LAYOUT_CANCELBUTTON 8 123{ NULL }, 124}; 125 126#define _validByte(b) ((b) >= 0 && (b) <= 255) 127 128/* whine */ 129static void 130feepout(char *msg) 131{ 132 beep(); 133 dialog_notify(msg); 134} 135 136/* Very basic IP address integrity check - could be drastically improved */ 137static int 138verifyIP(char *ip) 139{ 140 int a, b, c, d; 141 142 if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 && 143 _validByte(a) && _validByte(b) && _validByte(c) && 144 _validByte(d) && (d != 255)) 145 return 1; 146 else 147 return 0; 148} 149 150/* Check for the settings on the screen - the per interface stuff is 151 moved to the main handling code now to do it on the fly - sigh */ 152 153static int 154verifySettings(void) 155{ 156 if (!hostname[0]) 157 feepout("Must specify a host name of some sort!"); 158 else if (gateway[0] && !verifyIP(gateway)) 159 feepout("Invalid gateway IP address specified"); 160 else if (nameserver[0] && !verifyIP(nameserver)) 161 feepout("Invalid name server IP address specified"); 162 else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3')) 163 feepout("Invalid netmask value"); 164 else if (ipaddr[0] && !verifyIP(ipaddr)) 165 feepout("Invalid IP address"); 166 else 167 return 1; 168 return 0; 169} 170 171int 172tcpInstallDevice(char *str) 173{ 174 Device **devs; 175 Device *dp = NULL; 176 177 /* Clip garbage off the ends */ 178 string_prune(str); 179 str = string_skipwhite(str); 180 if (!*str) 181 return DITEM_FAILURE; 182 devs = deviceFind(str, DEVICE_TYPE_NETWORK); 183 if (devs && (dp = devs[0])) { 184 char temp[512], ifn[255]; 185 186 if (!dp->private) { 187 DevInfo *di; 188 char *ipaddr, *netmask, *extras; 189 190 di = dp->private = (DevInfo *)malloc(sizeof(DevInfo)); 191 192 if ((ipaddr = variable_get(string_concat3(VAR_IPADDR, "_", dp->name))) == NULL) 193 ipaddr = variable_get(VAR_IPADDR); 194 195 if ((netmask = variable_get(string_concat3(VAR_NETMASK, "_", dp->name))) == NULL) 196 netmask = variable_get(VAR_NETMASK); 197 198 if ((extras = variable_get(string_concat3(VAR_EXTRAS, "_", dp->name))) == NULL) 199 extras = variable_get(VAR_EXTRAS); 200 201 string_copy(di->ipaddr, ipaddr); 202 string_copy(di->netmask, netmask); 203 string_copy(di->extras, extras); 204 205 if (ipaddr) { 206 char *ifaces; 207 208 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras ? extras : "", netmask); 209 sprintf(ifn, "%s%s", VAR_IFCONFIG, dp->name); 210 variable_set2(ifn, temp); 211 ifaces = variable_get(VAR_INTERFACES); 212 if (!ifaces) 213 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 214 /* Only add it if it's not there already */ 215 if (!strstr(ifaces, dp->name)) { 216 sprintf(ifn, "%s %s", dp->name, ifaces); 217 variable_set2(VAR_INTERFACES, ifn); 218 } 219 } 220 } 221 mediaDevice = dp; 222 } 223 return dp ? DITEM_SUCCESS : DITEM_FAILURE; 224} 225 226/* This is it - how to get TCP setup values */ 227int 228tcpOpenDialog(Device *devp) 229{ 230 WINDOW *ds_win, *save; 231 ComposeObj *obj = NULL; 232 ComposeObj *first, *last; 233 int n=0, quit=FALSE, cancel=FALSE, ret; 234 int max; 235 char *tmp; 236 char help[FILENAME_MAX]; 237 char title[80]; 238 239 if (!RunningAsInit) { 240 if (isDebug()) 241 msgDebug("Running multi-user, assuming that the network is already up\n"); 242 return DITEM_SUCCESS; 243 } 244 save = savescr(); 245 dialog_clear_norefresh(); 246 /* We need a curses window */ 247 ds_win = newwin(LINES, COLS, 0, 0); 248 if (ds_win == 0) 249 msgFatal("Cannot open TCP/IP dialog window!!"); 250 251 /* Say where our help comes from */ 252 systemHelpFile(TCP_HELPFILE, help); 253 use_helpfile(help); 254 255 /* Setup a nice screen for us to splat stuff onto */ 256 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr); 257 wattrset(ds_win, dialog_attr); 258 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration "); 259 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17, 260 dialog_attr, border_attr); 261 wattrset(ds_win, dialog_attr); 262 sprintf(title, " Configuration for Interface %s ", devp->name); 263 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title); 264 265 /* Initialise vars from previous device values */ 266 if (devp->private) { 267 DevInfo *di = (DevInfo *)devp->private; 268 269 strcpy(ipaddr, di->ipaddr); 270 strcpy(netmask, di->netmask); 271 strcpy(extras, di->extras); 272 } 273 else { /* See if there are any defaults */ 274 char *cp; 275 276 if (!ipaddr[0]) { 277 if ((cp = variable_get(VAR_IPADDR)) != NULL) 278 strcpy(ipaddr, cp); 279 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL) 280 strcpy(ipaddr, cp); 281 } 282 if (!netmask[0]) { 283 if ((cp = variable_get(VAR_NETMASK)) != NULL) 284 strcpy(netmask, cp); 285 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL) 286 strcpy(netmask, cp); 287 } 288 if (!extras[0]) { 289 if ((cp = variable_get(VAR_EXTRAS)) != NULL) 290 strcpy(extras, cp); 291 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL) 292 strcpy(extras, cp); 293 } 294 } 295 /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */ 296 tmp = variable_get(VAR_HOSTNAME); 297 if (tmp) 298 strcpy(hostname, tmp); 299 else 300 bzero(hostname, sizeof(hostname)); 301 tmp = variable_get(VAR_DOMAINNAME); 302 if (tmp) 303 strcpy(domainname, tmp); 304 else 305 bzero(domainname, sizeof(domainname)); 306 tmp = variable_get(VAR_GATEWAY); 307 if (tmp) 308 strcpy(gateway, tmp); 309 else 310 bzero(gateway, sizeof(gateway)); 311 tmp = variable_get(VAR_NAMESERVER); 312 if (tmp) 313 strcpy(nameserver, tmp); 314 else 315 bzero(nameserver, sizeof(nameserver)); 316 317 /* Loop over the layout list, create the objects, and add them 318 onto the chain of objects that dialog uses for traversal*/ 319 n = 0; 320#define lt layout[n] 321 while (lt.help != NULL) { 322 switch (lt.type) { 323 case STRINGOBJ: 324 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 325 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 326 lt.len, lt.maxlen); 327 break; 328 329 case BUTTONOBJ: 330 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 331 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 332 break; 333 334 default: 335 msgFatal("Don't support this object yet!"); 336 } 337 AddObj(&obj, lt.type, (void *) lt.obj); 338 n++; 339 } 340 max = n - 1; 341 342 /* Find the last object we can traverse to */ 343 last = obj; 344 while (last->next) 345 last = last->next; 346 347 /* Find the first object in the list */ 348 first = obj; 349 for (first = obj; first->prev; first = first->prev); 350 351 /* Some more initialisation before we go into the main input loop */ 352 n = 0; 353 cancelbutton = okbutton = 0; 354 355 /* Incoming user data - DUCK! */ 356 while (!quit) { 357 char help_line[80]; 358 int i, len = strlen(lt.help); 359 360 /* Display the help line at the bottom of the screen */ 361 for (i = 0; i < 79; i++) 362 help_line[i] = (i < len) ? lt.help[i] : ' '; 363 help_line[i] = '\0'; 364 use_helpline(help_line); 365 display_helpline(ds_win, LINES - 1, COLS - 1); 366 367 /* Ask for libdialog to do its stuff */ 368 ret = PollObj(&obj); 369 370 if (n == LAYOUT_HOSTNAME) { 371 /* We are in the Hostname field - calculate the domainname */ 372 if ((tmp = index(hostname, '.')) != NULL) { 373 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 374 domainname[strlen(tmp+1)] = '\0'; 375 RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj); 376 } 377 } 378 else if (n == LAYOUT_IPADDR) { 379 /* Insert a default value for the netmask, 0xffffff00 is 380 the most appropriate one (entire class C, or subnetted 381 class A/B network). */ 382 if(netmask[0] == '\0') { 383 strcpy(netmask, "255.255.255.0"); 384 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 385 } 386 } 387 else if (n == LAYOUT_DOMAINNAME) { 388 if (!index(hostname, '.') && domainname[0]) { 389 strcat(hostname, "."); 390 strcat(hostname, domainname); 391 RefreshStringObj(layout[LAYOUT_HOSTNAME].obj); 392 } 393 } 394 /* Handle special case stuff that libdialog misses. Sigh */ 395 switch (ret) { 396 /* Bail out */ 397 case SEL_ESC: 398 quit = TRUE, cancel=TRUE; 399 break; 400 401 /* This doesn't work for list dialogs. Oh well. Perhaps 402 should special case the move from the OK button ``up'' 403 to make it go to the interface list, but then it gets 404 awkward for the user to go back and correct screw up's 405 in the per-interface section */ 406 407 case KEY_DOWN: 408 case SEL_TAB: 409 case SEL_CR: 410 if (n < max) 411 ++n; 412 else 413 n = 0; 414 break; 415 416 /* The user has pressed enter over a button object */ 417 case SEL_BUTTON: 418 if (cancelbutton) 419 cancel = TRUE, quit = TRUE; 420 else { 421 if (verifySettings()) 422 quit = TRUE; 423 } 424 break; 425 426 case KEY_UP: 427 case SEL_BACKTAB: 428 if (n) 429 --n; 430 else 431 n = max; 432 break; 433 434 case KEY_F(1): 435 display_helpfile(); 436 437 /* They tried some key combination we don't support - tell them! */ 438 default: 439 beep(); 440 } 441 442 /* BODGE ALERT! */ 443 if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) { 444 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 445 domainname[strlen(tmp+1)] = '\0'; 446 RefreshStringObj(layout[1].obj); 447 } 448 } 449 450 /* Clear this crap off the screen */ 451 dialog_clear_norefresh(); 452 use_helpfile(NULL); 453 454 /* We actually need to inform the rest of sysinstall about this 455 data now - if the user hasn't selected cancel, save the stuff 456 out to the environment via the variable_set layers */ 457 458 if (!cancel) { 459 DevInfo *di; 460 char temp[512], ifn[255]; 461 char *ifaces; 462 463 variable_set2(VAR_HOSTNAME, hostname); 464 sethostname(hostname, strlen(hostname)); 465 if (domainname[0]) 466 variable_set2(VAR_DOMAINNAME, domainname); 467 if (gateway[0]) 468 variable_set2(VAR_GATEWAY, gateway); 469 if (nameserver[0]) 470 variable_set2(VAR_NAMESERVER, nameserver); 471 472 if (!devp->private) 473 devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo)); 474 di = devp->private; 475 strcpy(di->ipaddr, ipaddr); 476 strcpy(di->netmask, netmask); 477 strcpy(di->extras, extras); 478 479 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask); 480 sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name); 481 variable_set2(ifn, temp); 482 ifaces = variable_get(VAR_INTERFACES); 483 if (!ifaces) 484 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 485 /* Only add it if it's not there already */ 486 if (!strstr(ifaces, devp->name)) { 487 sprintf(ifn, "%s %s", devp->name, ifaces); 488 variable_set2(VAR_INTERFACES, ifn); 489 } 490 if (ipaddr[0]) 491 variable_set2(VAR_IPADDR, ipaddr); 492 restorescr(save); 493 return DITEM_SUCCESS; 494 } 495 restorescr(save); 496 return DITEM_FAILURE; 497} 498 499static int 500netHook(dialogMenuItem *self) 501{ 502 Device **devs; 503 504 devs = deviceFind(self->prompt, DEVICE_TYPE_NETWORK); 505 if (devs) { 506 if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE) 507 mediaDevice = devs[0]; 508 else 509 devs = NULL; 510 } 511 return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE; 512} 513 514/* Get a network device */ 515Boolean 516tcpDeviceSelect(void) 517{ 518 DMenu *menu; 519 Device **devs; 520 int cnt; 521 int status; 522 523 devs = deviceFind(NULL, DEVICE_TYPE_NETWORK); 524 cnt = deviceCount(devs); 525 if (!cnt) { 526 msgConfirm("No network devices available!"); 527 status = FALSE; 528 } 529 else if (cnt == 1) { 530 if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_FAILURE)) 531 return FALSE; 532 mediaDevice = devs[0]; 533 status = TRUE; 534 } 535 else { 536 menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL); 537 if (!menu) 538 msgFatal("Unable to create network device menu! Argh!"); 539 status = dmenuOpenSimple(menu, FALSE); 540 free(menu); 541 } 542 return status; 543} 544 545/* Do it from a menu that doesn't care about status */ 546int 547tcpMenuSelect(dialogMenuItem *self) 548{ 549 (void)tcpDeviceSelect(); 550 configResolv(); 551 return DITEM_SUCCESS | DITEM_RECREATE | DITEM_RESTORE; 552} 553