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