tcpip.c revision 8837
172878Skris/*
272878Skris * $Id: tcpip.c,v 1.26 1995/05/28 23:12:09 jkh Exp $
372878Skris *
472878Skris * Copyright (c) 1995
572878Skris *      Gary J Palmer. All rights reserved.
672878Skris *
772878Skris * Redistribution and use in source and binary forms, with or without
872878Skris * modification, are permitted provided that the following conditions
972878Skris * are met:
1072878Skris * 1. Redistributions of source code must retain the above copyright
1172878Skris *    notice, this list of conditions and the following disclaimer,
1296312Sobrien *    verbatim and that no modifications are made prior to this
1396312Sobrien *    point in the file.
1472878Skris * 2. Redistributions in binary form must reproduce the above copyright
1572878Skris *    notice, this list of conditions and the following disclaimer in the
1672878Skris *    documentation and/or other materials provided with the distribution.
1772878Skris * 3. All advertising materials mentioning features or use of this software
1872878Skris *    must display the following acknowledgement:
1972878Skris *      This product includes software developed by Gary J Palmer
2072878Skris *	for the FreeBSD Project.
2172878Skris * 4. The name of Gary J Palmer or the FreeBSD Project may
2272878Skris *    not be used to endorse or promote products derived from this software
2372878Skris *    without specific prior written permission.
2472878Skris *
2596419Sobrien * THIS SOFTWARE IS PROVIDED BY GARY J PALMER ``AS IS'' AND ANY EXPRESS OR
2672878Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2772878Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2872878Skris * IN NO EVENT SHALL GARY J PALMER BE LIABLE FOR ANY DIRECT, INDIRECT,
2972878Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
3072878Skris * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
3172878Skris * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3272878Skris * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3374146Skris * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3472878Skris * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3596419Sobrien *
3696419Sobrien */
3773145Skris
3874146Skris/*
3972878Skris * All kinds of hacking also performed by jkh on this code.  Don't
4074146Skris * blame Gary for every bogosity you see here.. :-)
4172878Skris *
4274146Skris * -jkh
4373145Skris */
4474146Skris
4573145Skris#include <stdio.h>
4674146Skris#include <stdlib.h>
4773145Skris#include <unistd.h>
4874146Skris#include <sys/param.h>
4972878Skris#include <string.h>
5074146Skris#include <dialog.h>
5173145Skris#include "ui_objects.h"
5274146Skris#include "dir.h"
5372878Skris#include "dialog.priv.h"
5474146Skris#include "colors.h"
5572878Skris#include "rc.h"
5694987Sru#include "sysinstall.h"
5772878Skris
5872878Skris/* These are nasty, but they make the layout structure a lot easier ... */
5972878Skris
6074146Skrisstatic char		hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
6172878Skris			gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN];
6274146Skrisstatic int		okbutton, cancelbutton;
6372878Skrisstatic char		ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
6474146Skris
6572878Skris/* What the screen size is meant to be */
6674146Skris#define TCP_DIALOG_Y		0
6772878Skris#define TCP_DIALOG_X		8
6874146Skris#define TCP_DIALOG_WIDTH	COLS - 16
6972878Skris#define TCP_DIALOG_HEIGHT	LINES - 2
7074146Skris
7172878Skris/* The screen layout structure */
7272878Skristypedef struct _layout {
7372878Skris    int		y;		/* x & Y co-ordinates */
7472878Skris    int		x;
7574146Skris    int		len;		/* The size of the dialog on the screen */
7674146Skris    int		maxlen;		/* How much the user can type in ... */
7774146Skris    char	*prompt;	/* The string for the prompt */
7874146Skris    char	*help;		/* The display for the help line */
7974146Skris    void	*var;		/* The var to set when this changes */
8074146Skris    int		type;		/* The type of the dialog to create */
8172878Skris    void	*obj;		/* The obj pointer returned by libdialog */
8272878Skris} Layout;
8372878Skris
8472878Skrisstatic Layout layout[] = {
8572878Skris{ 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
8696419Sobrien      "Host name:", "The name of your machine on a network, e.g. foo.bar.com",
8796419Sobrien      hostname, STRINGOBJ, NULL },
8873145Skris#define LAYOUT_HOSTNAME		0
8974069Ssobomax{ 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
9072878Skris      "Domain name:",
9174069Ssobomax      "The name of the domain that your machine is in, e.g. bar.com",
9272878Skris      domainname, STRINGOBJ, NULL },
9372878Skris#define LAYOUT_DOMAINNAME	1
9473145Skris{ 5, 2, 18, IPADDR_FIELD_LEN - 1,
9573145Skris      "Gateway:",
9673145Skris      "IP address of host forwarding packets to non-local destinations",
9773145Skris      gateway, STRINGOBJ, NULL },
9873145Skris#define LAYOUT_GATEWAY		2
9974553Skris{ 5, 35, 18, IPADDR_FIELD_LEN - 1,
10072878Skris      "Name server:", "IP address of your local DNS server",
10172878Skris      nameserver, STRINGOBJ, NULL },
10273145Skris#define LAYOUT_NAMESERVER	3
10373145Skris{ 10, 10, 18, IPADDR_FIELD_LEN - 1,
10472878Skris      "IP Address:",
10572878Skris      "The IP address to be used for this interface",
10672878Skris      ipaddr, STRINGOBJ, NULL },
10772878Skris#define LAYOUT_IPADDR		5
10872878Skris{ 10, 35, 18, IPADDR_FIELD_LEN - 1,
10972878Skris      "Netmask:",
11072878Skris      "The netmask for this interfaace, e.g. 0xffffff00 for a class C network",
11172878Skris      netmask, STRINGOBJ, NULL },
11272878Skris#define LAYOUT_NETMASK		6
11372878Skris{ 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
11472878Skris      "Extra options to ifconfig:",
11572878Skris      "Any interface-specific options to ifconfig you would like to use",
11672878Skris      extras, STRINGOBJ, NULL },
11772878Skris#define LAYOUT_EXTRAS		7
11872878Skris{ 19, 15, 0, 0,
11972878Skris      "OK", "Select this if you are happy with these settings",
12072878Skris      &okbutton, BUTTONOBJ, NULL },
12172878Skris#define LAYOUT_OKBUTTON		8
12272878Skris{ 19, 35, 0, 0,
12372878Skris      "CANCEL", "Select this if you wish to cancel this screen",
12472878Skris      &cancelbutton, BUTTONOBJ, NULL },
12572878Skris#define LAYOUT_CANCELBUTTON	9
12672878Skris{ NULL },
12772878Skris};
12872878Skris
12972878Skris#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 + 15, " 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
396	variable_set2(VAR_HOSTNAME, hostname);
397	variable_set2(VAR_DOMAINNAME, domainname);
398	if (gateway[0])
399	    variable_set2(VAR_GATEWAY, gateway);
400	if (nameserver[0])
401	    variable_set2(VAR_NAMESERVER, nameserver);
402
403	if (!devp->private)
404	    devp->private = (DevInfo *)malloc(sizeof(DevInfo));
405	di = devp->private;
406	strcpy(di->ipaddr, ipaddr);
407	strcpy(di->netmask, netmask);
408	strcpy(di->extras, extras);
409
410	sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask);
411	sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
412	variable_set2(ifn, temp);
413	sprintf(ifn, "%s %s", devp->name, getenv(VAR_INTERFACES) ? getenv(VAR_INTERFACES) : "");
414	variable_set2(VAR_INTERFACES, ifn);
415	if (ipaddr[0])
416	    variable_set2(VAR_IPADDR, ipaddr);
417	return 0;
418    }
419    return 1;
420}
421
422static int
423netHook(char *str)
424{
425    Device **devs;
426
427    /* Clip garbage off the ends */
428    string_prune(str);
429    str = string_skipwhite(str);
430    if (!*str)
431	return 0;
432    devs = deviceFind(str, DEVICE_TYPE_NETWORK);
433    if (devs) {
434	tcpOpenDialog(devs[0]);
435	mediaDevice = devs[0];
436    }
437    return devs ? 1 : 0;
438}
439
440/* Get a network device */
441int
442tcpDeviceSelect(char *str)
443{
444    DMenu *menu;
445
446    menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook);
447    if (!menu)
448	msgFatal("Unable to create network device menu!  Argh!");
449    dmenuOpenSimple(menu);
450    free(menu);
451    return 0;
452}
453