tcpip.c revision 14321
1/* 2 * $Id: tcpip.c,v 1.32 1995/12/07 10:34:19 peter 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/* 39 * All kinds of hacking also performed by jkh on this code. Don't 40 * blame Gary for every bogosity you see here.. :-) 41 * 42 * -jkh 43 */ 44 45#include <stdio.h> 46#include <stdlib.h> 47#include <unistd.h> 48#include <sys/param.h> 49#include <string.h> 50#include <dialog.h> 51#include "ui_objects.h" 52#include "dir.h" 53#include "dialog.priv.h" 54#include "colors.h" 55#include "sysinstall.h" 56 57/* The help file for the TCP/IP setup screen */ 58#define TCP_HELPFILE "tcp" 59 60/* These are nasty, but they make the layout structure a lot easier ... */ 61 62static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN], 63 gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN]; 64static int okbutton, cancelbutton; 65static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN]; 66 67/* What the screen size is meant to be */ 68#define TCP_DIALOG_Y 0 69#define TCP_DIALOG_X 8 70#define TCP_DIALOG_WIDTH COLS - 16 71#define TCP_DIALOG_HEIGHT LINES - 2 72 73/* The screen layout structure */ 74typedef struct _layout { 75 int y; /* x & Y co-ordinates */ 76 int x; 77 int len; /* The size of the dialog on the screen */ 78 int maxlen; /* How much the user can type in ... */ 79 char *prompt; /* The string for the prompt */ 80 char *help; /* The display for the help line */ 81 void *var; /* The var to set when this changes */ 82 int type; /* The type of the dialog to create */ 83 void *obj; /* The obj pointer returned by libdialog */ 84} Layout; 85 86static Layout layout[] = { 87{ 1, 2, 25, HOSTNAME_FIELD_LEN - 1, 88 "Host name:", "Your fully-qualified hostname, e.g. foo.bar.com", 89 hostname, STRINGOBJ, NULL }, 90#define LAYOUT_HOSTNAME 0 91{ 1, 35, 20, HOSTNAME_FIELD_LEN - 1, 92 "Domain name:", 93 "The name of the domain that your machine is in, e.g. bar.com", 94 domainname, STRINGOBJ, NULL }, 95#define LAYOUT_DOMAINNAME 1 96{ 5, 2, 18, IPADDR_FIELD_LEN - 1, 97 "Gateway:", 98 "IP address of host forwarding packets to non-local destinations", 99 gateway, STRINGOBJ, NULL }, 100#define LAYOUT_GATEWAY 2 101{ 5, 35, 18, IPADDR_FIELD_LEN - 1, 102 "Name server:", "IP address of your local DNS server", 103 nameserver, STRINGOBJ, NULL }, 104#define LAYOUT_NAMESERVER 3 105{ 10, 10, 18, IPADDR_FIELD_LEN - 1, 106 "IP Address:", 107 "The IP address to be used for this interface", 108 ipaddr, STRINGOBJ, NULL }, 109#define LAYOUT_IPADDR 4 110{ 10, 35, 18, IPADDR_FIELD_LEN - 1, 111 "Netmask:", 112 "The netmask for this interface, e.g. 0xffffff00 for a class C network", 113 netmask, STRINGOBJ, NULL }, 114#define LAYOUT_NETMASK 5 115{ 14, 10, 37, HOSTNAME_FIELD_LEN - 1, 116 "Extra options to ifconfig:", 117 "Any interface-specific options to ifconfig you would like to use", 118 extras, STRINGOBJ, NULL }, 119#define LAYOUT_EXTRAS 6 120{ 19, 15, 0, 0, 121 "OK", "Select this if you are happy with these settings", 122 &okbutton, BUTTONOBJ, NULL }, 123#define LAYOUT_OKBUTTON 7 124{ 19, 35, 0, 0, 125 "CANCEL", "Select this if you wish to cancel this screen", 126 &cancelbutton, BUTTONOBJ, NULL }, 127#define LAYOUT_CANCELBUTTON 8 128{ NULL }, 129}; 130 131#define _validByte(b) ((b) >= 0 && (b) <= 255) 132 133/* whine */ 134static void 135feepout(char *msg) 136{ 137 beep(); 138 dialog_notify(msg); 139} 140 141/* Very basic IP address integrity check - could be drastically improved */ 142static int 143verifyIP(char *ip) 144{ 145 int a, b, c, d; 146 147 if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 && 148 _validByte(a) && _validByte(b) && _validByte(c) && 149 _validByte(d) && (d != 255)) 150 return 1; 151 else 152 return 0; 153} 154 155/* Check for the settings on the screen - the per interface stuff is 156 moved to the main handling code now to do it on the fly - sigh */ 157 158static int 159verifySettings(void) 160{ 161 if (!hostname[0]) 162 feepout("Must specify a host name of some sort!"); 163 else if (gateway[0] && !verifyIP(gateway)) 164 feepout("Invalid gateway IP address specified"); 165 else if (nameserver[0] && !verifyIP(nameserver)) 166 feepout("Invalid name server IP address specified"); 167 else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3')) 168 feepout("Invalid netmask value"); 169 else if (ipaddr[0] && !verifyIP(ipaddr)) 170 feepout("Invalid IP address"); 171 else 172 return 1; 173 return 0; 174} 175 176int 177tcpInstallDevice(char *str) 178{ 179 Device **devs; 180 Device *dp = NULL; 181 182 /* Clip garbage off the ends */ 183 string_prune(str); 184 str = string_skipwhite(str); 185 if (!*str) 186 return RET_FAIL; 187 devs = deviceFind(str, DEVICE_TYPE_NETWORK); 188 if (devs && (dp = devs[0])) { 189 char temp[512], ifn[255]; 190 191 if (!dp->private) { 192 DevInfo *di; 193 char *ipaddr, *netmask, *extras; 194 195 di = dp->private = (DevInfo *)malloc(sizeof(DevInfo)); 196 197 if ((ipaddr = variable_get(string_concat3(VAR_IPADDR, "_", dp->name))) == NULL) 198 ipaddr = variable_get(VAR_IPADDR); 199 200 if ((netmask = variable_get(string_concat3(VAR_NETMASK, "_", dp->name))) == NULL) 201 netmask = variable_get(VAR_NETMASK); 202 203 if ((extras = variable_get(string_concat3(VAR_EXTRAS, "_", dp->name))) == NULL) 204 extras = variable_get(VAR_EXTRAS); 205 206 string_copy(di->ipaddr, ipaddr); 207 string_copy(di->netmask, netmask); 208 string_copy(di->extras, extras); 209 210 if (ipaddr) { 211 char *ifaces; 212 213 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras ? extras : "", netmask); 214 sprintf(ifn, "%s%s", VAR_IFCONFIG, dp->name); 215 variable_set2(ifn, temp); 216 ifaces = variable_get(VAR_INTERFACES); 217 if (!ifaces) 218 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 219 /* Only add it if it's not there already */ 220 if (!strstr(ifaces, dp->name)) { 221 sprintf(ifn, "%s %s", dp->name, ifaces); 222 variable_set2(VAR_INTERFACES, ifn); 223 } 224 } 225 } 226 mediaDevice = dp; 227 } 228 return dp ? RET_SUCCESS : RET_FAIL; 229} 230 231/* This is it - how to get TCP setup values */ 232int 233tcpOpenDialog(Device *devp) 234{ 235 WINDOW *ds_win; 236 ComposeObj *obj = NULL; 237 ComposeObj *first, *last; 238 int n=0, quit=FALSE, cancel=FALSE, ret; 239 int max; 240 char *tmp; 241 char help[FILENAME_MAX]; 242 char title[80]; 243 244 /* We need a curses window */ 245 ds_win = newwin(LINES, COLS, 0, 0); 246 if (ds_win == 0) 247 msgFatal("Cannot open TCP/IP dialog window!!"); 248 249 /* Say where our help comes from */ 250 systemHelpFile(TCP_HELPFILE, help); 251 use_helpfile(help); 252 253 /* Setup a nice screen for us to splat stuff onto */ 254 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr); 255 wattrset(ds_win, dialog_attr); 256 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration "); 257 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17, 258 dialog_attr, border_attr); 259 wattrset(ds_win, dialog_attr); 260 sprintf(title, " Configuration for Interface %s ", devp->name); 261 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title); 262 263 /* Initialise vars from previous device values */ 264 if (devp->private) { 265 DevInfo *di = (DevInfo *)devp->private; 266 267 strcpy(ipaddr, di->ipaddr); 268 strcpy(netmask, di->netmask); 269 strcpy(extras, di->extras); 270 } 271 else { /* See if there are any defaults */ 272 char *cp; 273 274 if (!ipaddr[0]) { 275 if ((cp = variable_get(VAR_IPADDR)) != NULL) 276 strcpy(ipaddr, cp); 277 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL) 278 strcpy(ipaddr, cp); 279 } 280 if (!netmask[0]) { 281 if ((cp = variable_get(VAR_NETMASK)) != NULL) 282 strcpy(netmask, cp); 283 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL) 284 strcpy(netmask, cp); 285 } 286 if (!extras[0]) { 287 if ((cp = variable_get(VAR_EXTRAS)) != NULL) 288 strcpy(extras, cp); 289 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL) 290 strcpy(extras, cp); 291 } 292 } 293 /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */ 294 tmp = variable_get(VAR_HOSTNAME); 295 if (tmp) 296 strcpy(hostname, tmp); 297 else 298 bzero(hostname, sizeof(hostname)); 299 tmp = variable_get(VAR_DOMAINNAME); 300 if (tmp) 301 strcpy(domainname, tmp); 302 else 303 bzero(domainname, sizeof(domainname)); 304 tmp = variable_get(VAR_GATEWAY); 305 if (tmp) 306 strcpy(gateway, tmp); 307 else 308 bzero(gateway, sizeof(gateway)); 309 tmp = variable_get(VAR_NAMESERVER); 310 if (tmp) 311 strcpy(nameserver, tmp); 312 else 313 bzero(nameserver, sizeof(nameserver)); 314 315 /* Loop over the layout list, create the objects, and add them 316 onto the chain of objects that dialog uses for traversal*/ 317 n = 0; 318#define lt layout[n] 319 while (lt.help != NULL) { 320 switch (lt.type) { 321 case STRINGOBJ: 322 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 323 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 324 lt.len, lt.maxlen); 325 break; 326 327 case BUTTONOBJ: 328 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 329 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 330 break; 331 332 default: 333 msgFatal("Don't support this object yet!"); 334 } 335 AddObj(&obj, lt.type, (void *) lt.obj); 336 n++; 337 } 338 max = n - 1; 339 340 /* Find the last object we can traverse to */ 341 last = obj; 342 while (last->next) 343 last = last->next; 344 345 /* Find the first object in the list */ 346 first = obj; 347 for (first = obj; first->prev; first = first->prev); 348 349 /* Some more initialisation before we go into the main input loop */ 350 n = 0; 351 cancelbutton = okbutton = 0; 352 353 /* Incoming user data - DUCK! */ 354 while (!quit) { 355 char help_line[80]; 356 int i, len = strlen(lt.help); 357 358 /* Display the help line at the bottom of the screen */ 359 for (i = 0; i < 79; i++) 360 help_line[i] = (i < len) ? lt.help[i] : ' '; 361 help_line[i] = '\0'; 362 use_helpline(help_line); 363 display_helpline(ds_win, LINES - 1, COLS - 1); 364 365 /* Ask for libdialog to do its stuff */ 366 ret = PollObj(&obj); 367 368 if (n == LAYOUT_HOSTNAME) { 369 /* We are in the Hostname field - calculate the domainname */ 370 if ((tmp = index(hostname, '.')) != NULL) { 371 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 372 domainname[strlen(tmp+1)] = '\0'; 373 RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj); 374 } 375 } 376 else if (n == LAYOUT_IPADDR) { 377 /* Insert a default value for the netmask, 0xffffff00 is 378 the most appropriate one (entire class C, or subnetted 379 class A/B network). */ 380 if(netmask[0] == '\0') { 381 strcpy(netmask, "255.255.255.0"); 382 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 383 } 384 } 385 else if (n == LAYOUT_DOMAINNAME) { 386 if (!index(hostname, '.') && domainname[0]) { 387 strcat(hostname, "."); 388 strcat(hostname, domainname); 389 RefreshStringObj(layout[LAYOUT_HOSTNAME].obj); 390 } 391 } 392 /* Handle special case stuff that libdialog misses. Sigh */ 393 switch (ret) { 394 /* Bail out */ 395 case SEL_ESC: 396 quit = TRUE, cancel=TRUE; 397 break; 398 399 /* This doesn't work for list dialogs. Oh well. Perhaps 400 should special case the move from the OK button ``up'' 401 to make it go to the interface list, but then it gets 402 awkward for the user to go back and correct screw up's 403 in the per-interface section */ 404 405 case KEY_UP: 406 if (obj->prev !=NULL ) { 407 obj = obj->prev; 408 --n; 409 } else { 410 obj = last; 411 n = max; 412 } 413 break; 414 415 case KEY_DOWN: 416 if (obj->next != NULL) { 417 obj = obj->next; 418 ++n; 419 } else { 420 obj = first; 421 n = 0; 422 } 423 break; 424 425 case SEL_TAB: 426 if (n < max) 427 ++n; 428 else 429 n = 0; 430 break; 431 432 /* The user has pressed enter over a button object */ 433 case SEL_BUTTON: 434 if (cancelbutton) 435 cancel = TRUE, quit = TRUE; 436 else { 437 if (verifySettings()) 438 quit = TRUE; 439 } 440 break; 441 442 /* Generic CR handler */ 443 case SEL_CR: 444 if (n < max) 445 ++n; 446 else 447 n = 0; 448 break; 449 450 case SEL_BACKTAB: 451 if (n) 452 --n; 453 else 454 n = max; 455 break; 456 457 case KEY_F(1): 458 display_helpfile(); 459 460 /* They tried some key combination we don't support - tell them! */ 461 default: 462 beep(); 463 } 464 465 /* BODGE ALERT! */ 466 if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) { 467 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 468 domainname[strlen(tmp+1)] = '\0'; 469 RefreshStringObj(layout[1].obj); 470 } 471 } 472 473 /* Clear this crap off the screen */ 474 dialog_clear(); 475 refresh(); 476 use_helpfile(NULL); 477 478 /* We actually need to inform the rest of sysinstall about this 479 data now - if the user hasn't selected cancel, save the stuff 480 out to the environment via the variable_set layers */ 481 482 if (!cancel) { 483 DevInfo *di; 484 char temp[512], ifn[255]; 485 char *ifaces; 486 487 variable_set2(VAR_HOSTNAME, hostname); 488 if (domainname[0]) 489 variable_set2(VAR_DOMAINNAME, domainname); 490 if (gateway[0]) 491 variable_set2(VAR_GATEWAY, gateway); 492 if (nameserver[0]) 493 variable_set2(VAR_NAMESERVER, nameserver); 494 495 if (!devp->private) 496 devp->private = (DevInfo *)malloc(sizeof(DevInfo)); 497 di = devp->private; 498 strcpy(di->ipaddr, ipaddr); 499 strcpy(di->netmask, netmask); 500 strcpy(di->extras, extras); 501 502 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask); 503 sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name); 504 variable_set2(ifn, temp); 505 ifaces = variable_get(VAR_INTERFACES); 506 if (!ifaces) 507 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 508 /* Only add it if it's not there already */ 509 if (!strstr(ifaces, devp->name)) { 510 sprintf(ifn, "%s %s", devp->name, ifaces); 511 variable_set2(VAR_INTERFACES, ifn); 512 } 513 if (ipaddr[0]) 514 variable_set2(VAR_IPADDR, ipaddr); 515 return RET_SUCCESS; 516 } 517 return RET_FAIL; 518} 519 520static int 521netHook(char *str) 522{ 523 Device **devs; 524 525 /* Clip garbage off the ends */ 526 string_prune(str); 527 str = string_skipwhite(str); 528 if (!*str) 529 return RET_FAIL; 530 devs = deviceFind(str, DEVICE_TYPE_NETWORK); 531 if (devs) { 532 tcpOpenDialog(devs[0]); 533 mediaDevice = devs[0]; 534 } 535 return devs ? RET_DONE : RET_FAIL; 536} 537 538/* Get a network device */ 539Boolean 540tcpDeviceSelect(void) 541{ 542 DMenu *menu; 543 Device **devs; 544 int cnt; 545 int status; 546 547 devs = deviceFind(NULL, DEVICE_TYPE_NETWORK); 548 cnt = deviceCount(devs); 549 if (!cnt) { 550 msgConfirm("No network devices available!"); 551 status = FALSE; 552 } 553 else if (cnt == 1 || !RunningAsInit) { 554 /* If we're running in user mode, assume network already up */ 555 if (RunningAsInit) 556 tcpOpenDialog(devs[0]); 557 mediaDevice = devs[0]; 558 status = TRUE; 559 } 560 else { 561 menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook); 562 if (!menu) 563 msgFatal("Unable to create network device menu! Argh!"); 564 status = dmenuOpenSimple(menu); 565 free(menu); 566 } 567 return status; 568} 569 570/* Do it from a menu that doesn't care about status */ 571int 572tcpMenuSelect(char *str) 573{ 574 (void)tcpDeviceSelect(); 575 configResolv(); 576 return RET_SUCCESS; 577} 578