tcpip.c revision 8742
1/* 2 * $Id: tcpip.c,v 1.12 1995/05/24 01:27:15 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 22 * not be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY GARY J PALMER ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL GARY J PALMER BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 31 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 * 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 "rc.h" 49#include "sysinstall.h" 50 51/* These are nasty, but they make the layout structure a lot easier ... */ 52 53static char hostname[256], domainname[256], 54 gateway[32], nameserver[32], iface[8]; 55static int okbutton, cancelbutton; 56static char ipaddr[32], netmask[32], extras[256]; 57 58/* What the screen size is meant to be */ 59#define TCP_DIALOG_Y 0 60#define TCP_DIALOG_X 8 61#define TCP_DIALOG_WIDTH COLS - 16 62#define TCP_DIALOG_HEIGHT LINES - 2 63 64/* The per interface set of records */ 65typedef struct _interface { 66 char ipaddr[32]; 67 char netmask[32]; 68 char extras[256]; /* Extra stuff for ifconfig (link0, etc) */ 69 int valid; 70 Device *dptr; 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 this interface - use 127.0.0.1 for lo0", 117 ipaddr, STRINGOBJ, NULL }, 118#define LAYOUT_IPADDR 5 119{ 10, 37, 18, 15, 120 "Netmask:", 121 "The netmask for this interfaace, 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 interface-specific options to ifconfig you'd like to use", 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 else if (gateway[0] && !verifyIP(gateway)) 174 feepout("Invalid gateway IP address specified"); 175 else if (nameserver[0] && !verifyIP(nameserver)) 176 feepout("Invalid name server IP address specified"); 177 else 178 return 1; 179 return 0; 180} 181 182/* This is it - how to get TCP setup values */ 183int 184tcpOpenDialog(char *str) 185{ 186 WINDOW *ds_win; 187 ComposeObj *obj = NULL; 188 ComposeObj *first, *last; 189 int n=0, quit=FALSE, cancel=FALSE, ret, 190 max, n_iface; 191 char *tmp; 192 Device **devs; 193 char old_iface[8]; 194 char help[FILENAME_MAX]; 195 196 /* We need a curses window */ 197 ds_win = newwin(LINES, COLS, 0, 0); 198 if (ds_win == 0) 199 msgFatal("Cannot open TCP/IP dialog window!!"); 200 201 /* Look for net.devices for us to configure */ 202 devs = deviceFind(NULL, DEVICE_TYPE_NETWORK); 203 if (!devs) { 204 msgConfirm("Couldn't find any potential network devices!"); 205 return 0; 206 } 207 208 while (devs[n] != NULL) { 209 iface_names[n] = (devs[n])->name; 210 if_list[n].dptr = devs[n]; 211 ++n; 212 } 213 n_iface = n; 214 215 /* Setup a nice screen for us to splat stuff onto */ 216 systemHelpFile(TCP_HELPFILE, help); 217 use_helpfile(help); 218 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, 219 TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, 220 dialog_attr, border_attr); 221 wattrset(ds_win, dialog_attr); 222 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, 223 " Network Configuration "); 224 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 16, 225 TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 21, 226 dialog_attr, border_attr); 227 wattrset(ds_win, dialog_attr); 228 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 24, 229 " Per Interface Configuration "); 230 231 /* Initialise vars so that dialog has something to chew on */ 232 strcpy(ipaddr, if_list[0].ipaddr); 233 strcpy(netmask, if_list[0].netmask); 234 strcpy(extras, if_list[0].extras); 235 236 /* Look up values already recorded with the system, or blank the 237 string variables ready to accept some new data */ 238 tmp = getenv(VAR_HOSTNAME); 239 if (tmp) 240 strcpy(hostname, tmp); 241 else 242 bzero(hostname, sizeof(hostname)); 243 tmp = getenv(VAR_DOMAINNAME); 244 if (tmp) 245 strcpy(domainname, tmp); 246 else 247 bzero(domainname, sizeof(domainname)); 248 tmp = getenv(VAR_GATEWAY); 249 if (tmp) 250 strcpy(gateway, tmp); 251 else 252 bzero(gateway, sizeof(gateway)); 253 tmp = getenv(VAR_NAMESERVER); 254 if (tmp) 255 strcpy(nameserver, tmp); 256 else 257 bzero(nameserver, sizeof(nameserver)); 258 259 /* Loop over the layout list, create the objects, and add them 260 onto the chain of objects that dialog uses for traversal*/ 261 n = 0; 262#define lt layout[n] 263 while (lt.help != NULL) { 264 switch (lt.type) { 265 case STRINGOBJ: 266 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 267 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 268 lt.len, lt.maxlen); 269 break; 270 271 case BUTTONOBJ: 272 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 273 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 274 break; 275 276 case LISTOBJ: 277 lt.obj = NewListObj(ds_win, lt.prompt, (char **) iface_names, 278 lt.var, lt.y + TCP_DIALOG_Y, 279 lt.x + TCP_DIALOG_X, lt.len, 12, n_iface); 280 break; 281 default: 282 msgFatal("Don't support this object yet!"); 283 } 284 AddObj(&obj, lt.type, (void *) lt.obj); 285 n++; 286 } 287 max = n - 1; 288 289 /* Find the last object we can traverse to */ 290 last = obj; 291 while (last->next) 292 last = last->next; 293 294 /* Find the first object in the list */ 295 first = obj; 296 while (first->prev) 297 first = first->prev; 298 299 /* Some more initialisation before we go into the main input loop */ 300 n = 0; 301 cancelbutton = okbutton = 0; 302 strcpy(iface, iface_names[0]); 303 if_list[0].valid = FALSE; 304 strcpy(old_iface, iface); 305 306 /* Incoming user data - DUCK! */ 307 while (!quit) { 308 char help_line[80]; 309 int i, len = strlen(lt.help); 310 311 /* Display the help line at the bottom of the screen */ 312 for (i = 0; i < 79; i++) 313 help_line[i] = (i < len) ? lt.help[i] : ' '; 314 help_line[i] = '\0'; 315 use_helpline(help_line); 316 display_helpline(ds_win, LINES - 1, COLS - 1); 317 318 /* Ask for libdialog to do its stuff */ 319 ret = PollObj(&obj); 320 321 /* We are in the Hostname field - calculate the domainname */ 322 if (n == 0) { 323 if ((tmp = index(hostname, '.')) != NULL) { 324 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 325 domainname[strlen(tmp+1)] = '\0'; 326 RefreshStringObj(layout[1].obj); 327 } 328 } 329 330 /* Handle special case stuff that libdialog misses. Sigh */ 331 switch (ret) { 332 /* Bail out */ 333 case SEL_ESC: 334 quit = TRUE, cancel=TRUE; 335 break; 336 337 /* This doesn't work for list dialogs. Oh well. Perhaps 338 should special case the move from the OK button ``up'' 339 to make it go to the interface list, but then it gets 340 awkward for the user to go back and correct screw up's 341 in the per-interface section */ 342 343 case KEY_UP: 344 if (obj->prev !=NULL ) { 345 obj = obj->prev; 346 --n; 347 } else { 348 obj = last; 349 n = max; 350 } 351 break; 352 353 /* More special case handling - if we are at the interface 354 list, move to the OK button - the user hasn't selected 355 one of the entries in the list by pressing CR, so (s)he 356 must be wanting to skip to <OK> & <CANCEL> */ 357 case KEY_DOWN: 358 if (n == LAYOUT_EXTRAS) { 359 n = LAYOUT_IFACE; 360 obj = (((first->next)->next)->next)->next; 361 } else if (obj->next != NULL) { 362 obj = obj->next; 363 ++n; 364 } else { 365 obj = first; 366 n = 0; 367 } 368 break; 369 370 /* Same as KEY_DOWN, but dialog has already move us to the 371 next object on the list, which makes this slightly 372 different. */ 373 case SEL_TAB: 374 if (n == LAYOUT_EXTRAS) { 375 n = LAYOUT_IFACE; 376 obj = (((first->next)->next)->next)->next; 377 } else if (n < max) { 378 ++n; 379 } else { 380 n = 0; 381 } 382 383 /* This looks double dutch, but we have already MOVED onto 384 the next field, so we special case around getting to 385 that field, rather than moving off the previous 386 one. Hence we are really testing for 387 (n == LAYOUT_IFACE) */ 388 389 if (n == LAYOUT_IPADDR) { 390 n = LAYOUT_OKBUTTON; 391 obj = ((obj->next)->next)->next; 392 } 393 break; 394 395 /* The user has pressed enter over a button object */ 396 case SEL_BUTTON: 397 if (cancelbutton) { 398 cancel = TRUE, quit = TRUE; 399 } else { 400 if (verifySettings()) 401 quit = TRUE; 402 } 403 break; 404 405 /* Generic CR handler */ 406 case SEL_CR: 407 /* Has the user selected a new interface? */ 408 if (strcmp(old_iface, iface)) { 409 /* Now go find the new location */ 410 n_iface = 0; 411 while (strcmp(iface, iface_names[n_iface]) && 412 (iface_names[n_iface] != NULL)) 413 ++n_iface; 414 if (iface_names[n_iface] == NULL) 415 msgFatal("Erk - run off the end of the list of interfaces!"); 416 strcpy(ipaddr, if_list[n_iface].ipaddr); 417 strcpy(netmask, if_list[n_iface].netmask); 418 strcpy(extras, if_list[n_iface].extras); 419 if_list[n_iface].valid = FALSE; 420 421 RefreshStringObj(layout[LAYOUT_IPADDR].obj); 422 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 423 RefreshStringObj(layout[LAYOUT_EXTRAS].obj); 424 425 strcpy(old_iface, iface); 426 } 427 428 /* Loop back to the interface list from the extras box - 429 now we handle the case of saving out the data the user 430 typed in (and also do basic verification of its 431 sanity) */ 432 if (n == LAYOUT_EXTRAS) { 433 n = LAYOUT_IFACE; 434 obj = (((first->next)->next)->next)->next; 435 /* First, find the old value */ 436 n_iface = 0; 437 while (strcmp(old_iface, iface_names[n_iface]) && 438 (iface_names[n_iface] != NULL)) 439 ++n_iface; 440 441 if (iface_names[n_iface] == NULL) 442 msgFatal("Erk - run off the end of the list of interfaces!"); 443 444 /* Sanity check what the user supplied - this could probably 445 be better :-( */ 446 447 if (!verifyIP(ipaddr)) { 448 feepout("Invalid or missing IP address!"); 449 strcpy(iface, old_iface); 450 n = LAYOUT_IFACE; 451 obj = (((first->next)->next)->next)->next; 452 RefreshListObj(layout[LAYOUT_IFACE].obj); 453 } 454 455 if (netmask[0] < '0' || netmask[0] > '9') { 456 feepout("Invalid or missing netmask!"); 457 strcpy(iface, old_iface); 458 n = LAYOUT_IFACE; 459 obj = (((first->next)->next)->next)->next; 460 RefreshListObj(layout[LAYOUT_IFACE].obj); 461 } 462 463 strcpy(if_list[n_iface].ipaddr, ipaddr); 464 strcpy(if_list[n_iface].netmask, netmask); 465 strcpy(if_list[n_iface].extras, extras); 466 if_list[n_iface].valid = TRUE; 467 if_list[n_iface].dptr->enabled = TRUE; 468 } else if (n < max) 469 ++n; 470 else 471 n = 0; 472 break; 473 474 case SEL_BACKTAB: 475 if (n) 476 --n; 477 else 478 n = max; 479 break; 480 481 case KEY_F(1): 482 display_helpfile(); 483 484 /* They tried some key combination we don't support - tell them! */ 485 default: 486 beep(); 487 } 488 489 /* BODGE ALERT! */ 490 if ((tmp = index(hostname, '.')) != NULL) { 491 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 492 domainname[strlen(tmp+1)] = '\0'; 493 RefreshStringObj(layout[1].obj); 494 } 495 } 496 497 /* Clear this crap off the screen */ 498 dialog_clear(); 499 refresh(); 500 use_helpfile(NULL); 501 502 /* We actually need to inform the rest of sysinstall about this 503 data now - if the user hasn't selected cancel, save the stuff 504 out to the environment via the variable_set layers */ 505 506 if (!cancel) { 507 int foo; 508 variable_set2(VAR_HOSTNAME, hostname); 509 variable_set2(VAR_DOMAINNAME, domainname); 510 if (gateway[0]) 511 variable_set2(VAR_GATEWAY, gateway); 512 if (nameserver[0]) 513 variable_set2(VAR_NAMESERVER, nameserver); 514 515 /* Loop over the per-interface data saving data which has been 516 validated ... */ 517 for (foo = 0 ; foo < INTERFACE_MAX ; foo++) { 518 if (if_list[foo].valid == TRUE) { 519 char temp[512], ifn[64]; 520 sprintf(temp, "inet %s %s netmask %s", 521 if_list[foo].ipaddr, if_list[foo].extras, 522 if_list[foo].netmask); 523 sprintf(ifn, "%s%s", VAR_IFCONFIG, iface_names[foo]); 524 variable_set2(ifn, temp); 525 } 526 } 527 } 528 return 0; 529} 530 531static Device *netDevice; 532 533static int 534netHook(char *str) 535{ 536 Device **devs; 537 538 /* Clip garbage off the ends */ 539 string_prune(str); 540 str = string_skipwhite(str); 541 if (!*str) 542 return 0; 543 devs = deviceFind(str, DEVICE_TYPE_NETWORK); 544 if (devs) 545 netDevice = devs[0]; 546 return devs ? 1 : 0; 547} 548 549/* Get a network device */ 550Device * 551tcpDeviceSelect(void) 552{ 553 DMenu *menu; 554 555 /* If we can't find a hostname, ask user to set up TCP/IP */ 556 if (!getenv(VAR_HOSTNAME)) 557 tcpOpenDialog(NULL); 558 559 /* If we still can't, user is a bonehead */ 560 if (!getenv(VAR_HOSTNAME)) { 561 msgConfirm("Sorry, I can't do this if you don't set up your networking first!"); 562 return 0; 563 } 564 menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook); 565 if (!menu) 566 msgFatal("Unable to create network device menu! Argh!"); 567 dmenuOpenSimple(menu); 568 free(menu); 569 return netDevice; 570} 571 572/* Start PPP on the 3rd screen */ 573Boolean 574tcpStartPPP(void) 575{ 576 int fd; 577 578 fd = open("/dev/ttyv2", O_RDWR); 579 if (fd == -1) 580 return FALSE; 581 Mkdir("/var/log", NULL); 582 if (!fork()) { 583 dup2(fd, 0); 584 dup2(fd, 1); 585 dup2(fd, 2); 586 execl("/stand/ppp", "/stand/ppp", (char *)NULL); 587 exit(1); 588 } 589 return TRUE; 590} 591 592 593 594 595