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