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