tcpip.c revision 8576
1/*
2 * $Id: tcpip.c,v 1.4 1995/05/17 12:09:11 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} 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 your host - use 127.0.0.1 for loopback",
117      ipaddr, STRINGOBJ, NULL },
118#define LAYOUT_IPADDR		5
119{ 10, 37, 18, 15,
120      "Netmask:",
121      "The netmask for your network, 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 options to ifconfig you'd like to specify manually",
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#if 0
174    else if (!verifyIP(ipaddr))
175	feepout("Invalid or missing value for IP address");
176#endif
177    else if (gateway[0] && !verifyIP(gateway))
178	feepout("Invalid gateway IP address specified");
179    else if (nameserver[0] && !verifyIP(nameserver))
180	feepout("Invalid name server IP address specified");
181#if 0
182    else if (netmask[0] < '0' || netmask[0] > '9')
183	feepout("Invalid or missing netmask");
184#endif
185    else
186	return 1;
187    return 0;
188}
189
190/* This is it - how to get TCP setup values */
191int
192tcpOpenDialog(char *str)
193{
194    WINDOW              *ds_win;
195    ComposeObj          *obj = NULL;
196    ComposeObj		*first, *last;
197    int                 n=0, quit=FALSE, cancel=FALSE, ret,
198    			max, n_iface;
199    char                *tmp;
200    Device		**devs;
201    char		old_iface[8];
202    char		help[FILENAME_MAX];
203
204    /* We need a curses window */
205    ds_win = newwin(LINES, COLS, 0, 0);
206    if (ds_win == 0)
207	msgFatal("Cannot open TCP/IP dialog window!!");
208
209    /* Look for net.devices for us to configure */
210    devs = deviceFind(NULL, DEVICE_TYPE_NETWORK);
211    if (!devs) {
212	msgConfirm("Couldn't find any potential network devices!");
213	return 0;
214    }
215
216    while (devs[n] != NULL) {
217	iface_names[n] = (devs[n])->name;
218	++n;
219    }
220    n_iface = n;
221
222    /* Setup a nice screen for us to splat stuff onto */
223    systemHelpFile(TCP_HELPFILE, help);
224    use_helpfile(help);
225    draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X,
226	     TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH,
227	     dialog_attr, border_attr);
228    wattrset(ds_win, dialog_attr);
229    mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20,
230	      " Network Configuration ");
231    draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 16,
232	     TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 21,
233	     dialog_attr, border_attr);
234    wattrset(ds_win, dialog_attr);
235    mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 24,
236	      " Per Interface Configuration ");
237
238    /* Initialise vars so that dialog has something to chew on */
239    strcpy(ipaddr, if_list[0].ipaddr);
240    strcpy(netmask, if_list[0].netmask);
241    strcpy(extras, if_list[0].extras);
242
243    /* Look up values already recorded with the system, or blank the
244       string variables ready to accept some new data */
245    tmp = getenv(VAR_HOSTNAME);
246    if (tmp)
247	strcpy(hostname, tmp);
248    else
249	bzero(hostname, sizeof(hostname));
250    tmp = getenv(VAR_DOMAINNAME);
251    if (tmp)
252	strcpy(domainname, tmp);
253    else
254	bzero(domainname, sizeof(domainname));
255    tmp = getenv(VAR_GATEWAY);
256    if (tmp)
257	strcpy(gateway, tmp);
258    else
259	bzero(gateway, sizeof(gateway));
260    tmp = getenv(VAR_NAMESERVER);
261    if (tmp)
262	strcpy(nameserver, tmp);
263    else
264	bzero(nameserver, sizeof(nameserver));
265
266    /* Loop over the layout list, create the objects, and add them
267       onto the chain of objects that dialog uses for traversal*/
268    n = 0;
269#define lt layout[n]
270    while (lt.help != NULL) {
271	switch (lt.type) {
272	case STRINGOBJ:
273	    lt.obj = NewStringObj(ds_win, lt.prompt, lt.var,
274				  lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X,
275				  lt.len, lt.maxlen);
276	    break;
277
278	case BUTTONOBJ:
279	    lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var,
280				  lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X);
281	    break;
282
283	case LISTOBJ:
284	    lt.obj = NewListObj(ds_win, lt.prompt, (char **) iface_names,
285				lt.var, lt.y + TCP_DIALOG_Y,
286				lt.x + TCP_DIALOG_X, lt.len, 12, n_iface);
287	    break;
288	default:
289	    msgFatal("Don't support this object yet!");
290	}
291	AddObj(&obj, lt.type, (void *) lt.obj);
292	n++;
293    }
294    max = n - 1;
295
296    /* Find the last object we can traverse to */
297    last = obj;
298    while (last->next)
299	last = last->next;
300
301    /* Find the first object in the list */
302    first = obj;
303    while (first->prev)
304	first = first->prev;
305
306    /* Some more initialisation before we go into the main input loop */
307    n = 0;
308    cancelbutton = okbutton = 0;
309    strcpy(iface, iface_names[0]);
310    if_list[0].valid = FALSE;
311    strcpy(old_iface, iface);
312
313    /* Incoming user data - DUCK! */
314    while (!quit) {
315	char help_line[80];
316	int i, len = strlen(lt.help);
317
318	/* Display the help line at the bottom of the screen */
319	for (i = 0; i < 79; i++)
320	    help_line[i] = (i < len) ? lt.help[i] : ' ';
321	help_line[i] = '\0';
322	use_helpline(help_line);
323	display_helpline(ds_win, LINES - 1, COLS - 1);
324
325	/* Ask for libdialog to do its stuff */
326	ret = PollObj(&obj);
327
328	/* We are in the Hostname field - calculate the domainname */
329	if (n == 0) {
330	    if ((tmp = index(hostname, '.')) != NULL) {
331		strncpy(domainname, tmp + 1, strlen(tmp + 1));
332		domainname[strlen(tmp+1)] = '\0';
333		RefreshStringObj(layout[1].obj);
334	    }
335	}
336
337	/* Handle special case stuff that libdialog misses. Sigh */
338	switch (ret) {
339	    /* Bail out */
340	case SEL_ESC:
341	    quit = TRUE, cancel=TRUE;
342	    break;
343
344	    /* This doesn't work for list dialogs. Oh well. Perhaps
345	       should special case the move from the OK button ``up''
346	       to make it go to the interface list, but then it gets
347	       awkward for the user to go back and correct screw up's
348	       in the per-interface section */
349
350	case KEY_UP:
351	    if (obj->prev !=NULL ) {
352		obj = obj->prev;
353		--n;
354	    } else {
355		obj = last;
356		n = max;
357	    }
358	    break;
359
360	    /* More special case handling - if we are at the interface
361               list, move to the OK button - the user hasn't selected
362               one of the entries in the list by pressing CR, so (s)he
363               must be wanting to skip to <OK> & <CANCEL> */
364	case KEY_DOWN:
365	    if (n == LAYOUT_EXTRAS) {
366		n = LAYOUT_IFACE;
367		obj = (((first->next)->next)->next)->next;
368	    } else if (obj->next != NULL) {
369		obj = obj->next;
370		++n;
371	    } else {
372		obj = first;
373		n = 0;
374	    }
375	    break;
376
377	    /* Same as KEY_DOWN, but dialog has already move us to the
378               next object on the list, which makes this slightly
379               different. */
380	case SEL_TAB:
381	    if (n == LAYOUT_EXTRAS) {
382		n = LAYOUT_IFACE;
383		obj = (((first->next)->next)->next)->next;
384	    } else if (obj->next != NULL) {
385		++n;
386	    } else {
387		n = 0;
388	    }
389
390	    /* This looks double dutch, but we have already MOVED onto
391               the next field, so we special case around getting to
392               that field, rather than moving off the previous
393               one. Hence we are really testing for
394	       (n == LAYOUT_IFACE) */
395
396	    if (n == LAYOUT_IPADDR) {
397		n = LAYOUT_OKBUTTON;
398		obj = ((obj->next)->next)->next;
399	    }
400	    break;
401
402	    /* The user has pressed enter over a button object */
403	case SEL_BUTTON:
404 	    if (cancelbutton) {
405		cancel = TRUE, quit = TRUE;
406	    } else {
407		if (verifySettings())
408		    quit = TRUE;
409	    }
410	    break;
411
412	    /* Generic CR handler */
413	case SEL_CR:
414	    /* Has the user selected a new interface? */
415	    if (strcmp(old_iface, iface)) {
416		/* Moved to a different condition for better handling */
417#if 0
418		/* First, find the old value */
419		n_iface = 0;
420		while (strcmp(old_iface, iface_names[n_iface]) &&
421		       (iface_names[n_iface] != NULL))
422		    ++n_iface;
423
424		if (iface_names[n_iface] == NULL)
425		    msgFatal("Erk - run off the end of the list of interfaces!");
426
427		/* Sanity check what the user supplied - this could probably
428		   be better :-( */
429
430		if (!verifyIP(ipaddr)) {
431		    feepout("Invalid or missing IP address!");
432		    strcpy(iface, old_iface);
433		    n = LAYOUT_IFACE;
434		    obj = (((first->next)->next)->next)->next;
435		    RefreshListObj(layout[LAYOUT_IFACE].obj);
436		}
437
438		if (netmask[0] < '0' || netmask[0] > '9') {
439		    feepout("Invalid or missing netmask!");
440		    strcpy(iface, old_iface);
441		    n = LAYOUT_IFACE;
442		    obj = (((first->next)->next)->next)->next;
443		    RefreshListObj(layout[LAYOUT_IFACE].obj);
444		}
445
446		strcpy(if_list[n_iface].ipaddr, ipaddr);
447		strcpy(if_list[n_iface].netmask, netmask);
448		strcpy(if_list[n_iface].extras, extras);
449		if_list[n_iface].valid = TRUE;
450#endif /* if 0 */
451		/* Now go find the new location */
452		n_iface = 0;
453		while (strcmp(iface, iface_names[n_iface]) &&
454		       (iface_names[n_iface] != NULL))
455		    ++n_iface;
456		if (iface_names[n_iface] == NULL)
457		    msgFatal("Erk - run off the end of the list of interfaces!");
458		strcpy(ipaddr, if_list[n_iface].ipaddr);
459		strcpy(netmask, if_list[n_iface].netmask);
460		strcpy(extras, if_list[n_iface].extras);
461		if_list[n_iface].valid = FALSE;
462
463		RefreshStringObj(layout[LAYOUT_IPADDR].obj);
464		RefreshStringObj(layout[LAYOUT_NETMASK].obj);
465		RefreshStringObj(layout[LAYOUT_EXTRAS].obj);
466
467		strcpy(old_iface, iface);
468	    }
469
470	    /* Loop back to the interface list from the extras box -
471               now we handle the case of saving out the data the user
472               typed in (and also do basic verification of it's
473               sanity) */
474	    if (n == LAYOUT_EXTRAS) {
475		n = LAYOUT_IFACE;
476		obj = (((first->next)->next)->next)->next;
477		/* First, find the old value */
478		n_iface = 0;
479		while (strcmp(old_iface, iface_names[n_iface]) &&
480		       (iface_names[n_iface] != NULL))
481		    ++n_iface;
482
483		if (iface_names[n_iface] == NULL)
484		    msgFatal("Erk - run off the end of the list of interfaces!");
485
486		/* Sanity check what the user supplied - this could probably
487		   be better :-( */
488
489		if (!verifyIP(ipaddr)) {
490		    feepout("Invalid or missing IP address!");
491		    strcpy(iface, old_iface);
492		    n = LAYOUT_IFACE;
493		    obj = (((first->next)->next)->next)->next;
494		    RefreshListObj(layout[LAYOUT_IFACE].obj);
495		}
496
497		if (netmask[0] < '0' || netmask[0] > '9') {
498		    feepout("Invalid or missing netmask!");
499		    strcpy(iface, old_iface);
500		    n = LAYOUT_IFACE;
501		    obj = (((first->next)->next)->next)->next;
502		    RefreshListObj(layout[LAYOUT_IFACE].obj);
503		}
504
505		strcpy(if_list[n_iface].ipaddr, ipaddr);
506		strcpy(if_list[n_iface].netmask, netmask);
507		strcpy(if_list[n_iface].extras, extras);
508		if_list[n_iface].valid = TRUE;
509	    } else if (n < max)
510		++n;
511	    else
512		n = 0;
513	    break;
514
515	    /* This doesn't seem to work anymore - dunno why. Foo */
516
517	case SEL_BACKTAB:
518	    if (n)
519		--n;
520	    else
521		n = max;
522	    break;
523	case KEY_F(1):
524	    display_helpfile();
525
526	    /* They tried some key combination we don't support - tell them! */
527	default:
528	    beep();
529	}
530
531	/* BODGE ALERT! */
532	if ((tmp = index(hostname, '.')) != NULL) {
533	    strncpy(domainname, tmp + 1, strlen(tmp + 1));
534	    domainname[strlen(tmp+1)] = '\0';
535	    RefreshStringObj(layout[1].obj);
536	}
537    }
538
539    /* Clear this crap off the screen */
540    dialog_clear();
541    refresh();
542    use_helpfile(NULL);
543
544    /* We actually need to inform the rest of sysinstall about this
545       data now - if the user hasn't selected cancel, save the stuff
546       out to the environment via the variable_set layers */
547
548    if (!cancel) {
549	int foo;
550	variable_set2(VAR_HOSTNAME, hostname);
551	variable_set2(VAR_DOMAINNAME, domainname);
552	if (gateway[0])
553	    variable_set2(VAR_GATEWAY, gateway);
554	if (nameserver[0])
555	    variable_set2(VAR_NAMESERVER, nameserver);
556
557	/* Loop over the per-interface data saving data which has been
558           validated ... */
559	foo = 0;
560	for (foo = 0 ; foo < INTERFACE_MAX ; foo++) {
561	    if (if_list[foo].valid == TRUE) {
562		char temp[512], ifn[64];
563		sprintf(temp, "inet %s %s netmask %s",
564			if_list[foo].ipaddr, if_list[foo].extras,
565			if_list[foo].netmask);
566		sprintf(ifn, "%s%s", VAR_IFCONFIG, iface_names[foo]);
567		variable_set2(ifn, temp);
568	    }
569	}
570    }
571    return 0;
572}
573