tcpip.c revision 8605
1/* 2 * $Id: tcpip.c,v 1.8 1995/05/18 16:36:14 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 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 /* Now go find the new location */ 419 n_iface = 0; 420 while (strcmp(iface, iface_names[n_iface]) && 421 (iface_names[n_iface] != NULL)) 422 ++n_iface; 423 if (iface_names[n_iface] == NULL) 424 msgFatal("Erk - run off the end of the list of interfaces!"); 425 strcpy(ipaddr, if_list[n_iface].ipaddr); 426 strcpy(netmask, if_list[n_iface].netmask); 427 strcpy(extras, if_list[n_iface].extras); 428 if_list[n_iface].valid = FALSE; 429 430 RefreshStringObj(layout[LAYOUT_IPADDR].obj); 431 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 432 RefreshStringObj(layout[LAYOUT_EXTRAS].obj); 433 434 strcpy(old_iface, iface); 435 } 436 437 /* Loop back to the interface list from the extras box - 438 now we handle the case of saving out the data the user 439 typed in (and also do basic verification of it's 440 sanity) */ 441 if (n == LAYOUT_EXTRAS) { 442 n = LAYOUT_IFACE; 443 obj = (((first->next)->next)->next)->next; 444 /* First, find the old value */ 445 n_iface = 0; 446 while (strcmp(old_iface, iface_names[n_iface]) && 447 (iface_names[n_iface] != NULL)) 448 ++n_iface; 449 450 if (iface_names[n_iface] == NULL) 451 msgFatal("Erk - run off the end of the list of interfaces!"); 452 453 /* Sanity check what the user supplied - this could probably 454 be better :-( */ 455 456 if (!verifyIP(ipaddr)) { 457 feepout("Invalid or missing IP address!"); 458 strcpy(iface, old_iface); 459 n = LAYOUT_IFACE; 460 obj = (((first->next)->next)->next)->next; 461 RefreshListObj(layout[LAYOUT_IFACE].obj); 462 } 463 464 if (netmask[0] < '0' || netmask[0] > '9') { 465 feepout("Invalid or missing netmask!"); 466 strcpy(iface, old_iface); 467 n = LAYOUT_IFACE; 468 obj = (((first->next)->next)->next)->next; 469 RefreshListObj(layout[LAYOUT_IFACE].obj); 470 } 471 472 strcpy(if_list[n_iface].ipaddr, ipaddr); 473 strcpy(if_list[n_iface].netmask, netmask); 474 strcpy(if_list[n_iface].extras, extras); 475 if_list[n_iface].valid = TRUE; 476 if_list[n_iface].dptr->enabled = TRUE; 477 } else if (n < max) 478 ++n; 479 else 480 n = 0; 481 break; 482 483 /* This doesn't seem to work anymore - dunno why. Foo */ 484 485 case SEL_BACKTAB: 486 if (n) 487 --n; 488 else 489 n = max; 490 break; 491 case KEY_F(1): 492 display_helpfile(); 493 494 /* They tried some key combination we don't support - tell them! */ 495 default: 496 beep(); 497 } 498 499 /* BODGE ALERT! */ 500 if ((tmp = index(hostname, '.')) != NULL) { 501 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 502 domainname[strlen(tmp+1)] = '\0'; 503 RefreshStringObj(layout[1].obj); 504 } 505 } 506 507 /* Clear this crap off the screen */ 508 dialog_clear(); 509 refresh(); 510 use_helpfile(NULL); 511 512 /* We actually need to inform the rest of sysinstall about this 513 data now - if the user hasn't selected cancel, save the stuff 514 out to the environment via the variable_set layers */ 515 516 if (!cancel) { 517 int foo; 518 variable_set2(VAR_HOSTNAME, hostname); 519 variable_set2(VAR_DOMAINNAME, domainname); 520 if (gateway[0]) 521 variable_set2(VAR_GATEWAY, gateway); 522 if (nameserver[0]) 523 variable_set2(VAR_NAMESERVER, nameserver); 524 525 /* Loop over the per-interface data saving data which has been 526 validated ... */ 527 for (foo = 0 ; foo < INTERFACE_MAX ; foo++) { 528 if (if_list[foo].valid == TRUE) { 529 char temp[512], ifn[64]; 530 sprintf(temp, "inet %s %s netmask %s", 531 if_list[foo].ipaddr, if_list[foo].extras, 532 if_list[foo].netmask); 533 sprintf(ifn, "%s%s", VAR_IFCONFIG, iface_names[foo]); 534 variable_set2(ifn, temp); 535 } 536 } 537 } 538 return 0; 539} 540