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