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