tcpip.c revision 15242
1/* 2 * $Id: tcpip.c,v 1.34 1996/04/07 03:52:36 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; 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 /* We need a curses window */ 238 ds_win = newwin(LINES, COLS, 0, 0); 239 if (ds_win == 0) 240 msgFatal("Cannot open TCP/IP dialog window!!"); 241 242 /* Say where our help comes from */ 243 systemHelpFile(TCP_HELPFILE, help); 244 use_helpfile(help); 245 246 /* Setup a nice screen for us to splat stuff onto */ 247 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr); 248 wattrset(ds_win, dialog_attr); 249 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration "); 250 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17, 251 dialog_attr, border_attr); 252 wattrset(ds_win, dialog_attr); 253 sprintf(title, " Configuration for Interface %s ", devp->name); 254 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title); 255 256 /* Initialise vars from previous device values */ 257 if (devp->private) { 258 DevInfo *di = (DevInfo *)devp->private; 259 260 strcpy(ipaddr, di->ipaddr); 261 strcpy(netmask, di->netmask); 262 strcpy(extras, di->extras); 263 } 264 else { /* See if there are any defaults */ 265 char *cp; 266 267 if (!ipaddr[0]) { 268 if ((cp = variable_get(VAR_IPADDR)) != NULL) 269 strcpy(ipaddr, cp); 270 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL) 271 strcpy(ipaddr, cp); 272 } 273 if (!netmask[0]) { 274 if ((cp = variable_get(VAR_NETMASK)) != NULL) 275 strcpy(netmask, cp); 276 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL) 277 strcpy(netmask, cp); 278 } 279 if (!extras[0]) { 280 if ((cp = variable_get(VAR_EXTRAS)) != NULL) 281 strcpy(extras, cp); 282 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL) 283 strcpy(extras, cp); 284 } 285 } 286 /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */ 287 tmp = variable_get(VAR_HOSTNAME); 288 if (tmp) 289 strcpy(hostname, tmp); 290 else 291 bzero(hostname, sizeof(hostname)); 292 tmp = variable_get(VAR_DOMAINNAME); 293 if (tmp) 294 strcpy(domainname, tmp); 295 else 296 bzero(domainname, sizeof(domainname)); 297 tmp = variable_get(VAR_GATEWAY); 298 if (tmp) 299 strcpy(gateway, tmp); 300 else 301 bzero(gateway, sizeof(gateway)); 302 tmp = variable_get(VAR_NAMESERVER); 303 if (tmp) 304 strcpy(nameserver, tmp); 305 else 306 bzero(nameserver, sizeof(nameserver)); 307 308 /* Loop over the layout list, create the objects, and add them 309 onto the chain of objects that dialog uses for traversal*/ 310 n = 0; 311#define lt layout[n] 312 while (lt.help != NULL) { 313 switch (lt.type) { 314 case STRINGOBJ: 315 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 316 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 317 lt.len, lt.maxlen); 318 break; 319 320 case BUTTONOBJ: 321 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 322 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 323 break; 324 325 default: 326 msgFatal("Don't support this object yet!"); 327 } 328 AddObj(&obj, lt.type, (void *) lt.obj); 329 n++; 330 } 331 max = n - 1; 332 333 /* Find the last object we can traverse to */ 334 last = obj; 335 while (last->next) 336 last = last->next; 337 338 /* Find the first object in the list */ 339 first = obj; 340 for (first = obj; first->prev; first = first->prev); 341 342 /* Some more initialisation before we go into the main input loop */ 343 n = 0; 344 cancelbutton = okbutton = 0; 345 346 /* Incoming user data - DUCK! */ 347 while (!quit) { 348 char help_line[80]; 349 int i, len = strlen(lt.help); 350 351 /* Display the help line at the bottom of the screen */ 352 for (i = 0; i < 79; i++) 353 help_line[i] = (i < len) ? lt.help[i] : ' '; 354 help_line[i] = '\0'; 355 use_helpline(help_line); 356 display_helpline(ds_win, LINES - 1, COLS - 1); 357 358 /* Ask for libdialog to do its stuff */ 359 ret = PollObj(&obj); 360 361 if (n == LAYOUT_HOSTNAME) { 362 /* We are in the Hostname field - calculate the domainname */ 363 if ((tmp = index(hostname, '.')) != NULL) { 364 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 365 domainname[strlen(tmp+1)] = '\0'; 366 RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj); 367 } 368 } 369 else if (n == LAYOUT_IPADDR) { 370 /* Insert a default value for the netmask, 0xffffff00 is 371 the most appropriate one (entire class C, or subnetted 372 class A/B network). */ 373 if(netmask[0] == '\0') { 374 strcpy(netmask, "255.255.255.0"); 375 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 376 } 377 } 378 else if (n == LAYOUT_DOMAINNAME) { 379 if (!index(hostname, '.') && domainname[0]) { 380 strcat(hostname, "."); 381 strcat(hostname, domainname); 382 RefreshStringObj(layout[LAYOUT_HOSTNAME].obj); 383 } 384 } 385 /* Handle special case stuff that libdialog misses. Sigh */ 386 switch (ret) { 387 /* Bail out */ 388 case SEL_ESC: 389 quit = TRUE, cancel=TRUE; 390 break; 391 392 /* This doesn't work for list dialogs. Oh well. Perhaps 393 should special case the move from the OK button ``up'' 394 to make it go to the interface list, but then it gets 395 awkward for the user to go back and correct screw up's 396 in the per-interface section */ 397 398 case KEY_UP: 399 if (obj->prev !=NULL ) { 400 obj = obj->prev; 401 --n; 402 } else { 403 obj = last; 404 n = max; 405 } 406 break; 407 408 case KEY_DOWN: 409 if (obj->next != NULL) { 410 obj = obj->next; 411 ++n; 412 } else { 413 obj = first; 414 n = 0; 415 } 416 break; 417 418 case SEL_TAB: 419 if (n < max) 420 ++n; 421 else 422 n = 0; 423 break; 424 425 /* The user has pressed enter over a button object */ 426 case SEL_BUTTON: 427 if (cancelbutton) 428 cancel = TRUE, quit = TRUE; 429 else { 430 if (verifySettings()) 431 quit = TRUE; 432 } 433 break; 434 435 /* Generic CR handler */ 436 case SEL_CR: 437 if (n < max) 438 ++n; 439 else 440 n = 0; 441 break; 442 443 case SEL_BACKTAB: 444 if (n) 445 --n; 446 else 447 n = max; 448 break; 449 450 case KEY_F(1): 451 display_helpfile(); 452 453 /* They tried some key combination we don't support - tell them! */ 454 default: 455 beep(); 456 } 457 458 /* BODGE ALERT! */ 459 if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) { 460 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 461 domainname[strlen(tmp+1)] = '\0'; 462 RefreshStringObj(layout[1].obj); 463 } 464 } 465 466 /* Clear this crap off the screen */ 467 dialog_clear(); 468 refresh(); 469 use_helpfile(NULL); 470 471 /* We actually need to inform the rest of sysinstall about this 472 data now - if the user hasn't selected cancel, save the stuff 473 out to the environment via the variable_set layers */ 474 475 if (!cancel) { 476 DevInfo *di; 477 char temp[512], ifn[255]; 478 char *ifaces; 479 480 variable_set2(VAR_HOSTNAME, hostname); 481 if (domainname[0]) 482 variable_set2(VAR_DOMAINNAME, domainname); 483 if (gateway[0]) 484 variable_set2(VAR_GATEWAY, gateway); 485 if (nameserver[0]) 486 variable_set2(VAR_NAMESERVER, nameserver); 487 488 if (!devp->private) 489 devp->private = (DevInfo *)malloc(sizeof(DevInfo)); 490 di = devp->private; 491 strcpy(di->ipaddr, ipaddr); 492 strcpy(di->netmask, netmask); 493 strcpy(di->extras, extras); 494 495 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask); 496 sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name); 497 variable_set2(ifn, temp); 498 ifaces = variable_get(VAR_INTERFACES); 499 if (!ifaces) 500 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 501 /* Only add it if it's not there already */ 502 if (!strstr(ifaces, devp->name)) { 503 sprintf(ifn, "%s %s", devp->name, ifaces); 504 variable_set2(VAR_INTERFACES, ifn); 505 } 506 if (ipaddr[0]) 507 variable_set2(VAR_IPADDR, ipaddr); 508 return DITEM_SUCCESS; 509 } 510 return DITEM_FAILURE; 511} 512 513static int 514netHook(dialogMenuItem *self) 515{ 516 Device **devs; 517 518 devs = deviceFind(self->prompt, DEVICE_TYPE_NETWORK); 519 if (devs) { 520 tcpOpenDialog(devs[0]); 521 mediaDevice = devs[0]; 522 } 523 return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE; 524} 525 526/* Get a network device */ 527Boolean 528tcpDeviceSelect(void) 529{ 530 DMenu *menu; 531 Device **devs; 532 int cnt; 533 int status; 534 535 devs = deviceFind(NULL, DEVICE_TYPE_NETWORK); 536 cnt = deviceCount(devs); 537 if (!cnt) { 538 msgConfirm("No network devices available!"); 539 status = FALSE; 540 } 541 else if (cnt == 1 || !RunningAsInit) { 542 /* If we're running in user mode, assume network already up */ 543 if (RunningAsInit) 544 tcpOpenDialog(devs[0]); 545 mediaDevice = devs[0]; 546 status = TRUE; 547 } 548 else { 549 menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL); 550 if (!menu) 551 msgFatal("Unable to create network device menu! Argh!"); 552 status = dmenuOpenSimple(menu); 553 free(menu); 554 } 555 return status; 556} 557 558/* Do it from a menu that doesn't care about status */ 559int 560tcpMenuSelect(dialogMenuItem *self) 561{ 562 (void)tcpDeviceSelect(); 563 configResolv(); 564 return DITEM_SUCCESS; 565} 566