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