tcpip.c revision 20233
1/* 2 * $Id: tcpip.c,v 1.49 1996/11/07 08:03:29 jkh Exp $ 3 * 4 * Copyright (c) 1995 5 * Gary J Palmer. All rights reserved. 6 * Copyright (c) 1996 7 * Jordan K. Hubbard. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer, 14 * verbatim and that no modifications are made prior to this 15 * point in the file. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 26 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33/* 34 * All kinds of hacking also performed by jkh on this code. Don't 35 * blame Gary for every bogosity you see here.. :-) 36 * 37 * -jkh 38 */ 39 40#include <stdio.h> 41#include <stdlib.h> 42#include <unistd.h> 43#include <sys/param.h> 44#include <string.h> 45#include <dialog.h> 46#include "ui_objects.h" 47#include "dir.h" 48#include "dialog.priv.h" 49#include "colors.h" 50#include "sysinstall.h" 51 52/* The help file for the TCP/IP setup screen */ 53#define TCP_HELPFILE "tcp" 54 55/* These are nasty, but they make the layout structure a lot easier ... */ 56 57static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN], 58 gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN]; 59static int okbutton, cancelbutton; 60static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN]; 61 62/* What the screen size is meant to be */ 63#define TCP_DIALOG_Y 0 64#define TCP_DIALOG_X 8 65#define TCP_DIALOG_WIDTH COLS - 16 66#define TCP_DIALOG_HEIGHT LINES - 2 67 68/* The screen layout structure */ 69typedef struct _layout { 70 int y; /* x & Y co-ordinates */ 71 int x; 72 int len; /* The size of the dialog on the screen */ 73 int maxlen; /* How much the user can type in ... */ 74 char *prompt; /* The string for the prompt */ 75 char *help; /* The display for the help line */ 76 void *var; /* The var to set when this changes */ 77 int type; /* The type of the dialog to create */ 78 void *obj; /* The obj pointer returned by libdialog */ 79} Layout; 80 81static Layout layout[] = { 82{ 1, 2, 25, HOSTNAME_FIELD_LEN - 1, 83 "Host:", "Your fully-qualified hostname, e.g. foo.bar.com", 84 hostname, STRINGOBJ, NULL }, 85#define LAYOUT_HOSTNAME 0 86{ 1, 35, 20, HOSTNAME_FIELD_LEN - 1, 87 "Domain:", 88 "The name of the domain that your machine is in, e.g. bar.com", 89 domainname, STRINGOBJ, NULL }, 90#define LAYOUT_DOMAINNAME 1 91{ 5, 2, 18, IPADDR_FIELD_LEN - 1, 92 "Gateway:", 93 "IP address of host forwarding packets to non-local destinations", 94 gateway, STRINGOBJ, NULL }, 95#define LAYOUT_GATEWAY 2 96{ 5, 35, 18, IPADDR_FIELD_LEN - 1, 97 "Name server:", "IP address of your local DNS server", 98 nameserver, STRINGOBJ, NULL }, 99#define LAYOUT_NAMESERVER 3 100{ 10, 10, 18, IPADDR_FIELD_LEN - 1, 101 "IP Address:", 102 "The IP address to be used for this interface", 103 ipaddr, STRINGOBJ, NULL }, 104#define LAYOUT_IPADDR 4 105{ 10, 35, 18, IPADDR_FIELD_LEN - 1, 106 "Netmask:", 107 "The netmask for this interface, e.g. 0xffffff00 for a class C network", 108 netmask, STRINGOBJ, NULL }, 109#define LAYOUT_NETMASK 5 110{ 14, 10, 37, HOSTNAME_FIELD_LEN - 1, 111 "Extra options to ifconfig:", 112 "Any interface-specific options to ifconfig you would like to add", 113 extras, STRINGOBJ, NULL }, 114#define LAYOUT_EXTRAS 6 115{ 19, 15, 0, 0, 116 "OK", "Select this if you are happy with these settings", 117 &okbutton, BUTTONOBJ, NULL }, 118#define LAYOUT_OKBUTTON 7 119{ 19, 35, 0, 0, 120 "CANCEL", "Select this if you wish to cancel this screen", 121 &cancelbutton, BUTTONOBJ, NULL }, 122#define LAYOUT_CANCELBUTTON 8 123{ NULL }, 124}; 125 126#define _validByte(b) ((b) >= 0 && (b) <= 255) 127 128/* whine */ 129static void 130feepout(char *msg) 131{ 132 beep(); 133 dialog_notify(msg); 134} 135 136/* Very basic IP address integrity check - could be drastically improved */ 137static int 138verifyIP(char *ip) 139{ 140 int a, b, c, d; 141 142 if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 && 143 _validByte(a) && _validByte(b) && _validByte(c) && 144 _validByte(d) && (d != 255)) 145 return 1; 146 else 147 return 0; 148} 149 150/* Check for the settings on the screen - the per interface stuff is 151 moved to the main handling code now to do it on the fly - sigh */ 152 153static int 154verifySettings(void) 155{ 156 if (!hostname[0]) 157 feepout("Must specify a host name of some sort!"); 158 else if (gateway[0] && !verifyIP(gateway)) 159 feepout("Invalid gateway IP address specified"); 160 else if (nameserver[0] && !verifyIP(nameserver)) 161 feepout("Invalid name server IP address specified"); 162 else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3')) 163 feepout("Invalid netmask value"); 164 else if (ipaddr[0] && !verifyIP(ipaddr)) 165 feepout("Invalid IP address"); 166 else 167 return 1; 168 return 0; 169} 170 171/* This is it - how to get TCP setup values */ 172int 173tcpOpenDialog(Device *devp) 174{ 175 WINDOW *ds_win, *save; 176 ComposeObj *obj = NULL; 177 ComposeObj *first, *last; 178 int n=0, quit=FALSE, cancel=FALSE, ret; 179 int max; 180 char *tmp; 181 char help[FILENAME_MAX]; 182 char title[80]; 183 184 if (!RunningAsInit) { 185 if (isDebug()) 186 msgDebug("Running multi-user, assuming that the network is already up\n"); 187 return DITEM_SUCCESS; 188 } 189 save = savescr(); 190 dialog_clear_norefresh(); 191 /* We need a curses window */ 192 ds_win = newwin(LINES, COLS, 0, 0); 193 if (ds_win == 0) 194 msgFatal("Cannot open TCP/IP dialog window!!"); 195 196 /* Say where our help comes from */ 197 use_helpfile(systemHelpFile(TCP_HELPFILE, help)); 198 199 /* Setup a nice screen for us to splat stuff onto */ 200 draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr); 201 wattrset(ds_win, dialog_attr); 202 mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration "); 203 draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17, 204 dialog_attr, border_attr); 205 wattrset(ds_win, dialog_attr); 206 sprintf(title, " Configuration for Interface %s ", devp->name); 207 mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title); 208 209 /* Initialise vars from previous device values */ 210 if (devp->private) { 211 DevInfo *di = (DevInfo *)devp->private; 212 213 strcpy(ipaddr, di->ipaddr); 214 strcpy(netmask, di->netmask); 215 strcpy(extras, di->extras); 216 } 217 else { /* See if there are any defaults */ 218 char *cp; 219 220 if (!ipaddr[0]) { 221 if ((cp = variable_get(VAR_IPADDR)) != NULL) 222 strcpy(ipaddr, cp); 223 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL) 224 strcpy(ipaddr, cp); 225 } 226 if (!netmask[0]) { 227 if ((cp = variable_get(VAR_NETMASK)) != NULL) 228 strcpy(netmask, cp); 229 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL) 230 strcpy(netmask, cp); 231 } 232 if (!extras[0]) { 233 if ((cp = variable_get(VAR_EXTRAS)) != NULL) 234 strcpy(extras, cp); 235 else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL) 236 strcpy(extras, cp); 237 } 238 } 239 /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */ 240 tmp = variable_get(VAR_HOSTNAME); 241 if (tmp) 242 strcpy(hostname, tmp); 243 else 244 bzero(hostname, sizeof(hostname)); 245 tmp = variable_get(VAR_DOMAINNAME); 246 if (tmp) 247 strcpy(domainname, tmp); 248 else 249 bzero(domainname, sizeof(domainname)); 250 tmp = variable_get(VAR_GATEWAY); 251 if (tmp) 252 strcpy(gateway, tmp); 253 else 254 bzero(gateway, sizeof(gateway)); 255 tmp = variable_get(VAR_NAMESERVER); 256 if (tmp) 257 strcpy(nameserver, tmp); 258 else 259 bzero(nameserver, sizeof(nameserver)); 260 261 /* Loop over the layout list, create the objects, and add them 262 onto the chain of objects that dialog uses for traversal*/ 263 n = 0; 264#define lt layout[n] 265 while (lt.help != NULL) { 266 switch (lt.type) { 267 case STRINGOBJ: 268 lt.obj = NewStringObj(ds_win, lt.prompt, lt.var, 269 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X, 270 lt.len, lt.maxlen); 271 break; 272 273 case BUTTONOBJ: 274 lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var, 275 lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X); 276 break; 277 278 default: 279 msgFatal("Don't support this object yet!"); 280 } 281 AddObj(&obj, lt.type, (void *) lt.obj); 282 n++; 283 } 284 max = n - 1; 285 286 /* Find the last object we can traverse to */ 287 last = obj; 288 while (last->next) 289 last = last->next; 290 291 /* Find the first object in the list */ 292 first = obj; 293 for (first = obj; first->prev; first = first->prev); 294 295 /* Some more initialisation before we go into the main input loop */ 296 n = 0; 297 cancelbutton = okbutton = 0; 298 299 /* Incoming user data - DUCK! */ 300 while (!quit) { 301 char help_line[80]; 302 int i, len = strlen(lt.help); 303 304 /* Display the help line at the bottom of the screen */ 305 for (i = 0; i < 79; i++) 306 help_line[i] = (i < len) ? lt.help[i] : ' '; 307 help_line[i] = '\0'; 308 use_helpline(help_line); 309 display_helpline(ds_win, LINES - 1, COLS - 1); 310 311 /* Ask for libdialog to do its stuff */ 312 ret = PollObj(&obj); 313 314 if (n == LAYOUT_HOSTNAME) { 315 /* We are in the Hostname field - calculate the domainname */ 316 if ((tmp = index(hostname, '.')) != NULL) { 317 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 318 domainname[strlen(tmp+1)] = '\0'; 319 RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj); 320 } 321 } 322 else if (n == LAYOUT_IPADDR) { 323 /* Insert a default value for the netmask, 0xffffff00 is 324 the most appropriate one (entire class C, or subnetted 325 class A/B network). */ 326 if(netmask[0] == '\0') { 327 strcpy(netmask, "255.255.255.0"); 328 RefreshStringObj(layout[LAYOUT_NETMASK].obj); 329 } 330 } 331 else if (n == LAYOUT_DOMAINNAME) { 332 if (!index(hostname, '.') && domainname[0]) { 333 strcat(hostname, "."); 334 strcat(hostname, domainname); 335 RefreshStringObj(layout[LAYOUT_HOSTNAME].obj); 336 } 337 } 338 /* Handle special case stuff that libdialog misses. Sigh */ 339 switch (ret) { 340 /* Bail out */ 341 case SEL_ESC: 342 quit = TRUE, cancel=TRUE; 343 break; 344 345 /* This doesn't work for list dialogs. Oh well. Perhaps 346 should special case the move from the OK button ``up'' 347 to make it go to the interface list, but then it gets 348 awkward for the user to go back and correct screw up's 349 in the per-interface section */ 350 351 case KEY_DOWN: 352 case SEL_TAB: 353 case SEL_CR: 354 if (n < max) 355 ++n; 356 else 357 n = 0; 358 break; 359 360 /* The user has pressed enter over a button object */ 361 case SEL_BUTTON: 362 if (cancelbutton) 363 cancel = TRUE, quit = TRUE; 364 else { 365 if (verifySettings()) 366 quit = TRUE; 367 } 368 break; 369 370 case KEY_UP: 371 case SEL_BACKTAB: 372 if (n) 373 --n; 374 else 375 n = max; 376 break; 377 378 case KEY_F(1): 379 display_helpfile(); 380 381 /* They tried some key combination we don't support - tell them! */ 382 default: 383 beep(); 384 } 385 386 /* BODGE ALERT! */ 387 if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) { 388 strncpy(domainname, tmp + 1, strlen(tmp + 1)); 389 domainname[strlen(tmp+1)] = '\0'; 390 RefreshStringObj(layout[1].obj); 391 } 392 } 393 394 /* Clear this crap off the screen */ 395 dialog_clear_norefresh(); 396 use_helpfile(NULL); 397 398 /* We actually need to inform the rest of sysinstall about this 399 data now - if the user hasn't selected cancel, save the stuff 400 out to the environment via the variable_set layers */ 401 402 if (!cancel) { 403 DevInfo *di; 404 char temp[512], ifn[255]; 405 char *ifaces; 406 407 variable_set2(VAR_HOSTNAME, hostname); 408 sethostname(hostname, strlen(hostname)); 409 if (domainname[0]) 410 variable_set2(VAR_DOMAINNAME, domainname); 411 if (gateway[0]) 412 variable_set2(VAR_GATEWAY, gateway); 413 if (nameserver[0]) 414 variable_set2(VAR_NAMESERVER, nameserver); 415 416 if (!devp->private) 417 devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo)); 418 di = devp->private; 419 strcpy(di->ipaddr, ipaddr); 420 strcpy(di->netmask, netmask); 421 strcpy(di->extras, extras); 422 423 sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask); 424 sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name); 425 variable_set2(ifn, temp); 426 ifaces = variable_get(VAR_INTERFACES); 427 if (!ifaces) 428 variable_set2(VAR_INTERFACES, ifaces = "lo0"); 429 /* Only add it if it's not there already */ 430 if (!strstr(ifaces, devp->name)) { 431 sprintf(ifn, "%s %s", devp->name, ifaces); 432 variable_set2(VAR_INTERFACES, ifn); 433 } 434 if (ipaddr[0]) 435 variable_set2(VAR_IPADDR, ipaddr); 436 restorescr(save); 437 configResolv(); /* XXX this will do it on the MFS copy XXX */ 438 return DITEM_SUCCESS; 439 } 440 restorescr(save); 441 return DITEM_FAILURE; 442} 443 444static int 445netHook(dialogMenuItem *self) 446{ 447 Device **devs; 448 449 devs = deviceFind(self->prompt, DEVICE_TYPE_NETWORK); 450 if (devs) { 451 if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE) 452 mediaDevice = devs[0]; 453 else 454 devs = NULL; 455 } 456 return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE; 457} 458 459/* Get a network device */ 460Boolean 461tcpDeviceSelect(void) 462{ 463 DMenu *menu; 464 Device **devs; 465 int cnt; 466 int status; 467 468 devs = deviceFind(NULL, DEVICE_TYPE_NETWORK); 469 cnt = deviceCount(devs); 470 if (!cnt) { 471 msgConfirm("No network devices available!"); 472 status = FALSE; 473 } 474 else if (cnt == 1) { 475 if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_FAILURE)) 476 return FALSE; 477 mediaDevice = devs[0]; 478 status = TRUE; 479 } 480 else { 481 menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL); 482 if (!menu) 483 msgFatal("Unable to create network device menu! Argh!"); 484 status = dmenuOpenSimple(menu, FALSE); 485 free(menu); 486 } 487 return status; 488} 489 490/* Do it from a menu that doesn't care about status */ 491int 492tcpMenuSelect(dialogMenuItem *self) 493{ 494 (void)tcpDeviceSelect(); 495 return DITEM_SUCCESS | DITEM_RECREATE | DITEM_RESTORE; 496} 497