tcpip.c revision 9202
1/*
2 * $Id: tcpip.c,v 1.29.2.8 1995/06/06 06:08:29 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/*
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 "rc.h"
56#include "sysinstall.h"
57
58/* These are nasty, but they make the layout structure a lot easier ... */
59
60static char		hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
61			gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN];
62static int		okbutton, cancelbutton;
63static char		ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
64
65/* What the screen size is meant to be */
66#define TCP_DIALOG_Y		0
67#define TCP_DIALOG_X		8
68#define TCP_DIALOG_WIDTH	COLS - 16
69#define TCP_DIALOG_HEIGHT	LINES - 2
70
71/* The screen layout structure */
72typedef struct _layout {
73    int		y;		/* x & Y co-ordinates */
74    int		x;
75    int		len;		/* The size of the dialog on the screen */
76    int		maxlen;		/* How much the user can type in ... */
77    char	*prompt;	/* The string for the prompt */
78    char	*help;		/* The display for the help line */
79    void	*var;		/* The var to set when this changes */
80    int		type;		/* The type of the dialog to create */
81    void	*obj;		/* The obj pointer returned by libdialog */
82} Layout;
83
84static Layout layout[] = {
85{ 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
86      "Host name:", "Your fully-qualified hostname, e.g. foo.bar.com",
87      hostname, STRINGOBJ, NULL },
88#define LAYOUT_HOSTNAME		0
89{ 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
90      "Domain name:",
91      "The name of the domain that your machine is in, e.g. bar.com",
92      domainname, STRINGOBJ, NULL },
93#define LAYOUT_DOMAINNAME	1
94{ 5, 2, 18, IPADDR_FIELD_LEN - 1,
95      "Gateway:",
96      "IP address of host forwarding packets to non-local destinations",
97      gateway, STRINGOBJ, NULL },
98#define LAYOUT_GATEWAY		2
99{ 5, 35, 18, IPADDR_FIELD_LEN - 1,
100      "Name server:", "IP address of your local DNS server",
101      nameserver, STRINGOBJ, NULL },
102#define LAYOUT_NAMESERVER	3
103{ 10, 10, 18, IPADDR_FIELD_LEN - 1,
104      "IP Address:",
105      "The IP address to be used for this interface",
106      ipaddr, STRINGOBJ, NULL },
107#define LAYOUT_IPADDR		5
108{ 10, 35, 18, IPADDR_FIELD_LEN - 1,
109      "Netmask:",
110      "The netmask for this interfaace, e.g. 0xffffff00 for a class C network",
111      netmask, STRINGOBJ, NULL },
112#define LAYOUT_NETMASK		6
113{ 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
114      "Extra options to ifconfig:",
115      "Any interface-specific options to ifconfig you would like to use",
116      extras, STRINGOBJ, NULL },
117#define LAYOUT_EXTRAS		7
118{ 19, 15, 0, 0,
119      "OK", "Select this if you are happy with these settings",
120      &okbutton, BUTTONOBJ, NULL },
121#define LAYOUT_OKBUTTON		8
122{ 19, 35, 0, 0,
123      "CANCEL", "Select this if you wish to cancel this screen",
124      &cancelbutton, BUTTONOBJ, NULL },
125#define LAYOUT_CANCELBUTTON	9
126{ NULL },
127};
128
129#define _validByte(b) ((b) >= 0 && (b) < 255)
130
131/* whine */
132static void
133feepout(char *msg)
134{
135    beep();
136    dialog_notify(msg);
137}
138
139/* Very basic IP address integrity check - could be drastically improved */
140static int
141verifyIP(char *ip)
142{
143    int a, b, c, d;
144
145    if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
146	_validByte(a) && _validByte(b) && _validByte(c) &&
147	_validByte(d))
148	return 1;
149    else
150	return 0;
151}
152
153/* Check for the settings on the screen - the per interface stuff is
154   moved to the main handling code now to do it on the fly - sigh */
155
156static int
157verifySettings(void)
158{
159    if (!hostname[0])
160	feepout("Must specify a host name of some sort!");
161    else if (gateway[0] && !verifyIP(gateway))
162	feepout("Invalid gateway IP address specified");
163    else if (nameserver[0] && !verifyIP(nameserver))
164	feepout("Invalid name server IP address specified");
165    else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3'))
166	feepout("Invalid netmask value");
167    else if (ipaddr[0] && !verifyIP(ipaddr))
168	feepout("Invalid IP address");
169    else
170	return 1;
171    return 0;
172}
173
174/* This is it - how to get TCP setup values */
175int
176tcpOpenDialog(Device *devp)
177{
178    WINDOW              *ds_win;
179    ComposeObj          *obj = NULL;
180    ComposeObj		*first, *last;
181    int                 n=0, quit=FALSE, cancel=FALSE, ret;
182    int			max;
183    char                *tmp;
184    char		help[FILENAME_MAX];
185    char		title[80];
186
187    /* We need a curses window */
188    ds_win = newwin(LINES, COLS, 0, 0);
189    if (ds_win == 0)
190	msgFatal("Cannot open TCP/IP dialog window!!");
191
192    /* Say where our help comes from */
193    systemHelpFile(TCP_HELPFILE, help);
194    use_helpfile(help);
195
196    /* Setup a nice screen for us to splat stuff onto */
197    draw_box(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X, TCP_DIALOG_HEIGHT, TCP_DIALOG_WIDTH, dialog_attr, border_attr);
198    wattrset(ds_win, dialog_attr);
199    mvwaddstr(ds_win, TCP_DIALOG_Y, TCP_DIALOG_X + 20, " Network Configuration ");
200    draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
201	     dialog_attr, border_attr);
202    wattrset(ds_win, dialog_attr);
203    sprintf(title, " Configuration for Interface %s ", devp->name);
204    mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
205
206    /* Initialise vars from previous device values */
207    if (devp->private) {
208	DevInfo *di = (DevInfo *)devp->private;
209
210	strcpy(ipaddr, di->ipaddr);
211	strcpy(netmask, di->netmask);
212	strcpy(extras, di->extras);
213    }
214    else
215	ipaddr[0] = netmask[0] = extras[0] = '\0';
216
217    /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
218    tmp = getenv(VAR_HOSTNAME);
219    if (tmp)
220	strcpy(hostname, tmp);
221    else
222	bzero(hostname, sizeof(hostname));
223    tmp = getenv(VAR_DOMAINNAME);
224    if (tmp)
225	strcpy(domainname, tmp);
226    else
227	bzero(domainname, sizeof(domainname));
228    tmp = getenv(VAR_GATEWAY);
229    if (tmp)
230	strcpy(gateway, tmp);
231    else
232	bzero(gateway, sizeof(gateway));
233    tmp = getenv(VAR_NAMESERVER);
234    if (tmp)
235	strcpy(nameserver, tmp);
236    else
237	bzero(nameserver, sizeof(nameserver));
238
239    /* Loop over the layout list, create the objects, and add them
240       onto the chain of objects that dialog uses for traversal*/
241    n = 0;
242#define lt layout[n]
243    while (lt.help != NULL) {
244	switch (lt.type) {
245	case STRINGOBJ:
246	    lt.obj = NewStringObj(ds_win, lt.prompt, lt.var,
247				  lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X,
248				  lt.len, lt.maxlen);
249	    break;
250
251	case BUTTONOBJ:
252	    lt.obj = NewButtonObj(ds_win, lt.prompt, lt.var,
253				  lt.y + TCP_DIALOG_Y, lt.x + TCP_DIALOG_X);
254	    break;
255
256	default:
257	    msgFatal("Don't support this object yet!");
258	}
259	AddObj(&obj, lt.type, (void *) lt.obj);
260	n++;
261    }
262    max = n - 1;
263
264    /* Find the last object we can traverse to */
265    last = obj;
266    while (last->next)
267	last = last->next;
268
269    /* Find the first object in the list */
270    first = obj;
271    while (first->prev)
272	first = first->prev;
273
274    /* Some more initialisation before we go into the main input loop */
275    n = 0;
276    cancelbutton = okbutton = 0;
277
278    /* Incoming user data - DUCK! */
279    while (!quit) {
280	char help_line[80];
281	int i, len = strlen(lt.help);
282
283	/* Display the help line at the bottom of the screen */
284	for (i = 0; i < 79; i++)
285	    help_line[i] = (i < len) ? lt.help[i] : ' ';
286	help_line[i] = '\0';
287	use_helpline(help_line);
288	display_helpline(ds_win, LINES - 1, COLS - 1);
289
290	/* Ask for libdialog to do its stuff */
291	ret = PollObj(&obj);
292
293	/* We are in the Hostname field - calculate the domainname */
294	if (n == 0) {
295	    if ((tmp = index(hostname, '.')) != NULL) {
296		strncpy(domainname, tmp + 1, strlen(tmp + 1));
297		domainname[strlen(tmp+1)] = '\0';
298		RefreshStringObj(layout[1].obj);
299	    }
300	}
301
302	/* Handle special case stuff that libdialog misses. Sigh */
303	switch (ret) {
304	    /* Bail out */
305	case SEL_ESC:
306	    quit = TRUE, cancel=TRUE;
307	    break;
308
309	    /* This doesn't work for list dialogs. Oh well. Perhaps
310	       should special case the move from the OK button ``up''
311	       to make it go to the interface list, but then it gets
312	       awkward for the user to go back and correct screw up's
313	       in the per-interface section */
314
315	case KEY_UP:
316	    if (obj->prev !=NULL ) {
317		obj = obj->prev;
318		--n;
319	    } else {
320		obj = last;
321		n = max;
322	    }
323	    break;
324
325	case KEY_DOWN:
326	    if (obj->next != NULL) {
327		obj = obj->next;
328		++n;
329	    } else {
330		obj = first;
331		n = 0;
332	    }
333	    break;
334
335	case SEL_TAB:
336	    if (n < max)
337		++n;
338	    else
339		n = 0;
340	    break;
341
342	    /* The user has pressed enter over a button object */
343	case SEL_BUTTON:
344 	    if (cancelbutton)
345		cancel = TRUE, quit = TRUE;
346	    else {
347		if (verifySettings())
348		    quit = TRUE;
349	    }
350	    break;
351
352	    /* Generic CR handler */
353	case SEL_CR:
354	    if (n < max)
355		++n;
356	    else
357		n = 0;
358	    break;
359
360	case SEL_BACKTAB:
361	    if (n)
362		--n;
363	    else
364		n = max;
365	    break;
366
367	case KEY_F(1):
368	    display_helpfile();
369
370	    /* They tried some key combination we don't support - tell them! */
371	default:
372	    beep();
373	}
374
375	/* BODGE ALERT! */
376	if (((tmp = index(hostname, '.')) != NULL) && (strlen(domainname)==0)) {
377	    strncpy(domainname, tmp + 1, strlen(tmp + 1));
378	    domainname[strlen(tmp+1)] = '\0';
379	    RefreshStringObj(layout[1].obj);
380	}
381    }
382
383    /* Clear this crap off the screen */
384    dialog_clear();
385    refresh();
386    use_helpfile(NULL);
387
388    /* We actually need to inform the rest of sysinstall about this
389       data now - if the user hasn't selected cancel, save the stuff
390       out to the environment via the variable_set layers */
391
392    if (!cancel) {
393	DevInfo *di;
394	char temp[512], ifn[64];
395	char *ifaces;
396
397	variable_set2(VAR_HOSTNAME, hostname);
398	if (domainname[0])
399	    variable_set2(VAR_DOMAINNAME, domainname);
400	if (gateway[0])
401	    variable_set2(VAR_GATEWAY, gateway);
402	if (nameserver[0])
403	    variable_set2(VAR_NAMESERVER, nameserver);
404
405	if (!devp->private)
406	    devp->private = (DevInfo *)malloc(sizeof(DevInfo));
407	di = devp->private;
408	strcpy(di->ipaddr, ipaddr);
409	strcpy(di->netmask, netmask);
410	strcpy(di->extras, extras);
411
412	sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask);
413	sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
414	variable_set2(ifn, temp);
415	ifaces = getenv(VAR_INTERFACES);
416	if (!ifaces)
417	    variable_set2(VAR_INTERFACES, ifaces = "lo0");
418	/* Only add it if it's not there already */
419	if (!strstr(ifaces, devp->name)) {
420	    sprintf(ifn, "%s %s", devp->name, ifaces);
421	    variable_set2(VAR_INTERFACES, ifn);
422	}
423	if (ipaddr[0])
424	    variable_set2(VAR_IPADDR, ipaddr);
425	return 0;
426    }
427    return 1;
428}
429
430static int
431netHook(char *str)
432{
433    Device **devs;
434
435    /* Clip garbage off the ends */
436    string_prune(str);
437    str = string_skipwhite(str);
438    if (!*str)
439	return 0;
440    devs = deviceFind(str, DEVICE_TYPE_NETWORK);
441    if (devs) {
442	tcpOpenDialog(devs[0]);
443	mediaDevice = devs[0];
444    }
445    return devs ? 1 : 0;
446}
447
448/* Get a network device */
449Boolean
450tcpDeviceSelect(void)
451{
452    DMenu *menu;
453    Device **devs;
454    int cnt;
455    int status;
456
457    devs = deviceFind(NULL, DEVICE_TYPE_NETWORK);
458    cnt = deviceCount(devs);
459    if (!cnt) {
460	msgConfirm("No network devices available!");
461	status = FALSE;
462    }
463    else if (cnt == 1) {
464	tcpOpenDialog(devs[0]);
465	mediaDevice = devs[0];
466	status = TRUE;
467    }
468    else {
469
470	menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook);
471	if (!menu)
472	    msgFatal("Unable to create network device menu!  Argh!");
473	status = dmenuOpenSimple(menu);
474	free(menu);
475    }
476    return status;
477}
478
479/* Do it from a menu that doesn't care about status */
480int
481tcpMenuSelect(char *str)
482{
483    (void)tcpDeviceSelect();
484    configResolv();
485    return 0;
486}
487