tcpip.c revision 19488
1/* 2 * $Id: tcpip.c,v 1.48 1996/10/05 16:33:04 jkh Exp $ 3 * 4 * Copyright (c) 1995 5 * Gary J Palmer. All rights reserved. 6 * Copyright (c) 1996 7 * Jordan K. Hubbard. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer, 14 * verbatim and that no modifications are made prior to this 15 * point in the file. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 26 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33/* 34 * All kinds of hacking also performed by jkh on this code. Don't 35 * blame Gary for every bogosity you see here.. :-) 36 * 37 * -jkh 38 */ 39 40#include <stdio.h> 41#include <stdlib.h> 42#include <unistd.h> 43#include <sys/param.h> 44#include <string.h> 45#include <dialog.h> 46#include "ui_objects.h" 47#include "dir.h" 48#include "dialog.priv.h" 49#include "colors.h" 50#include "sysinstall.h" 51 52/* The help file for the TCP/IP setup screen */ 53#define TCP_HELPFILE "tcp" 54 55/* These are nasty, but they make the layout structure a lot easier ... */ 56 57static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN], 58 gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN]; 59static int okbutton, cancelbutton; 60static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN]; 61 62/* What the screen size is meant to be */ 63#define TCP_DIALOG_Y 0 64#define TCP_DIALOG_X 8 65#define TCP_DIALOG_WIDTH COLS - 16 66#define TCP_DIALOG_HEIGHT LINES - 2 67 68/* The screen layout structure */ 69typedef struct _layout { 70 int y; /* x & Y co-ordinates */ 71 int x; 72 int len; /* The size of the dialog on the screen */ 73 int maxlen; /* How much the user can type in ... */ 74 char *prompt; /* The string for the prompt */ 75 char *help; /* The display for the help line */ 76 void *var; /* The var to set when this changes */ 77 int type; /* The type of the dialog to create */ 78 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 use_helpfile(systemHelpFile(TCP_HELPFILE, help)); 253 254 /* Setup a nice screen for us to splat stuff onto */ 255 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr); 256 wattrset(ds_win, dialog_attr); 257 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration "); 258 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17, 259 dialog_attr, border_attr); 260 wattrset(ds_win, dialog_attr); 261 sprintf(title, " Configuration for Interface %s ", devp->name); 262 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title); 263 264 /* Initialise vars from previous device values */ 265 if (devp->private) { 266 DevInfo *di = (DevInfo *)devp->private; 267 268 strcpy(ipaddr, di->ipaddr); 269 strcpy(netmask, di->netmask); 270 strcpy(extras, di->extras); 271 } 272 else { /* See if there are any defaults */ 273 char *cp; 274 275 if (!ipaddr[0]) { 276 if ((cp = variable_get(VAR_IPADDR)) != NULL) 277 strcpy(ipaddr, cp); 278 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL) 279 strcpy(ipaddr, cp); 280 } 281 if (!netmask[0]) { 282 if ((cp = variable_get(VAR_NETMASK)) != NULL) 283 strcpy(netmask, cp); 284 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL) 285 strcpy(netmask, cp); 286 } 287 if (!extras[0]) { 288 if ((cp = variable_get(VAR_EXTRAS)) != NULL) 289 strcpy(extras, cp); 290 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL) 291 strcpy(extras, cp); 292 } 293 } 294 /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */ 295 tmp = variable_get(VAR_HOSTNAME); 296 if (tmp) 297 strcpy(hostname, tmp); 298 else 299 bzero(hostname, sizeof(hostname)); 300 tmp = variable_get(VAR_DOMAINNAME); 301 if (tmp) 302 strcpy(domainname, tmp); 303 else 304 bzero(domainname, sizeof(domainname)); 305 tmp = variable_get(VAR_GATEWAY); 306 if (tmp) 307 strcpy(gateway, tmp); 308 else 309 bzero(gateway, sizeof(gateway)); 310 tmp = variable_get(VAR_NAMESERVER); 311 if (tmp) 312 strcpy(nameserver, tmp); 313 else 314 bzero(nameserver, sizeof(nameserver)); 315 316 /* Loop over the layout list, create the objects, and add them 317 onto the chain of objects that dialog uses for traversal*/ 318 n = 0; 319#define lt layout[n] 320 while (lt.help != NULL) { 321 switch (lt.type) { 322 case STRINGOBJ: 323 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 324 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 325 lt.len, lt.maxlen); 326 break; 327 328 case BUTTONOBJ: 329 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 330 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 331 break; 332 333 default: 334 msgFatal("Don't support this object yet!"); 335 } 336 AddObj(&obj, lt.type, (void *) lt.obj); 337 n++; 338 } 339 max = n - 1; 340 341 /* Find the last object we can traverse to */ 342 last = obj; 343 while (last->next) 344 last = last->next; 345 346 /* Find the first object in the list */ 347 first = obj; 348 for (first = obj; first->prev; first = first->prev); 349 350 /* Some more initialisation before we go into the main input loop */ 351 n = 0; 352 cancelbutton = okbutton = 0; 353 354 /* Incoming user data - DUCK! */ 355 while (!quit) { 356 char help_line[80]; 357 int i, len = strlen(lt.help); 358 359 /* Display the help line at the bottom of the screen */ 360 for (i = 0; i < 79; i++) 361 help_line[i] = (i < len) ? lt.help[i] : ' '; 362 help_line[i] = '\0'; 363 use_helpline(help_line); 364 display_helpline(ds_win, LINES - 1, COLS - 1); 365 366 /* Ask for libdialog to do its stuff */ 367 ret = PollObj(&obj); 368 369 if (n == LAYOUT_HOSTNAME) { 370 /* We are in the Hostname field - calculate the domainname */ 371 if ((tmp = index(hostname, '.')) != NULL) { 372 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 373 domainname[strlen(tmp+1)] = '\0'; 374 RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj); 375 } 376 } 377 else if (n == LAYOUT_IPADDR) { 378 /* Insert a default value for the netmask, 0xffffff00 is 379 the most appropriate one (entire class C, or subnetted 380 class A/B network). */ 381 if(netmask[0] == '\0') { 382 strcpy(netmask, "255.255.255.0"); 383 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 384 } 385 } 386 else if (n == LAYOUT_DOMAINNAME) { 387 if (!index(hostname, '.') && domainname[0]) { 388 strcat(hostname, "."); 389 strcat(hostname, domainname); 390 RefreshStringObj(layout[LAYOUT_HOSTNAME].obj); 391 } 392 } 393 /* Handle special case stuff that libdialog misses. Sigh */ 394 switch (ret) { 395 /* Bail out */ 396 case SEL_ESC: 397 quit = TRUE, cancel=TRUE; 398 break; 399 400 /* This doesn't work for list dialogs. Oh well. Perhaps 401 should special case the move from the OK button ``up'' 402 to make it go to the interface list, but then it gets 403 awkward for the user to go back and correct screw up's 404 in the per-interface section */ 405 406 case KEY_DOWN: 407 case SEL_TAB: 408 case SEL_CR: 409 if (n < max) 410 ++n; 411 else 412 n = 0; 413 break; 414 415 /* The user has pressed enter over a button object */ 416 case SEL_BUTTON: 417 if (cancelbutton) 418 cancel = TRUE, quit = TRUE; 419 else { 420 if (verifySettings()) 421 quit = TRUE; 422 } 423 break; 424 425 case KEY_UP: 426 case SEL_BACKTAB: 427 if (n) 428 --n; 429 else 430 n = max; 431 break; 432 433 case KEY_F(1): 434 display_helpfile(); 435 436 /* They tried some key combination we don't support - tell them! */ 437 default: 438 beep(); 439 } 440 441 /* BODGE ALERT! */ 442 if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) { 443 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 444 domainname[strlen(tmp+1)] = '\0'; 445 RefreshStringObj(layout[1].obj); 446 } 447 } 448 449 /* Clear this crap off the screen */ 450 dialog_clear_norefresh(); 451 use_helpfile(NULL); 452 453 /* We actually need to inform the rest of sysinstall about this 454 data now - if the user hasn't selected cancel, save the stuff 455 out to the environment via the variable_set layers */ 456 457 if (!cancel) { 458 DevInfo *di; 459 char temp[512], ifn[255]; 460 char *ifaces; 461 462 variable_set2(VAR_HOSTNAME, hostname); 463 sethostname(hostname, strlen(hostname)); 464 if (domainname[0]) 465 variable_set2(VAR_DOMAINNAME, domainname); 466 if (gateway[0]) 467 variable_set2(VAR_GATEWAY, gateway); 468 if (nameserver[0]) 469 variable_set2(VAR_NAMESERVER, nameserver); 470 471 if (!devp->private) 472 devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo)); 473 di = devp->private; 474 strcpy(di->ipaddr, ipaddr); 475 strcpy(di->netmask, netmask); 476 strcpy(di->extras, extras); 477 478 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask); 479 sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name); 480 variable_set2(ifn, temp); 481 ifaces = variable_get(VAR_INTERFACES); 482 if (!ifaces) 483 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 484 /* Only add it if it's not there already */ 485 if (!strstr(ifaces, devp->name)) { 486 sprintf(ifn, "%s %s", devp->name, ifaces); 487 variable_set2(VAR_INTERFACES, ifn); 488 } 489 if (ipaddr[0]) 490 variable_set2(VAR_IPADDR, ipaddr); 491 restorescr(save); 492 configResolv(); /* XXX this will do it on the MFS copy XXX */ 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 return DITEM_SUCCESS | DITEM_RECREATE | DITEM_RESTORE; 551} 552