1/*	$NetBSD: net.c,v 1.45 2023/12/17 18:46:42 martin Exp $	*/
2
3/*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
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. The name of Piermont Information Systems Inc. may not be used to endorse
18 *    or promote products derived from this software without specific prior
19 *    written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35/* net.c -- routines to fetch files off the network. */
36
37#include <sys/ioctl.h>
38#include <sys/param.h>
39#include <sys/resource.h>
40#include <sys/socket.h>
41#include <sys/stat.h>
42#include <sys/statvfs.h>
43#include <sys/statvfs.h>
44#include <sys/sysctl.h>
45#include <sys/wait.h>
46#include <arpa/inet.h>
47#include <net/if.h>
48#include <net/if_media.h>
49#include <netinet/in.h>
50#include <net80211/ieee80211_ioctl.h>
51#include <netinet/ip_var.h>
52#ifdef INET6
53#include <netinet6/ip6_var.h>
54#endif
55
56#include <err.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <curses.h>
61#include <time.h>
62#include <unistd.h>
63
64#include "defs.h"
65#include "md.h"
66#include "msg_defs.h"
67#include "menu_defs.h"
68#include "txtwalk.h"
69
70int network_up = 0;
71/* Access to network information */
72#define MAX_NETS 15
73struct net_desc {
74	char if_dev[STRSIZE];
75	char name[STRSIZE]; // TODO
76};
77
78static char net_dev[STRSIZE];
79static char net_domain[STRSIZE];
80static char net_host[STRSIZE];
81static char net_ip[SSTRSIZE];
82static char net_srv_ip[SSTRSIZE];
83static char net_mask[SSTRSIZE];
84char net_namesvr[STRSIZE];
85static char net_defroute[STRSIZE];
86static char net_media[STRSIZE];
87static char net_ssid[STRSIZE];
88static char net_passphrase[STRSIZE];
89static char sl_flags[STRSIZE];
90static int net_dhcpconf;
91#define DHCPCONF_IPADDR         0x01
92#define DHCPCONF_NAMESVR        0x02
93#define DHCPCONF_HOST           0x04
94#define DHCPCONF_DOMAIN         0x08
95#ifdef INET6
96static char net_ip6[STRSIZE];
97#define IP6CONF_AUTOHOST        0x01
98#endif
99
100
101/* URL encode unsafe characters.  */
102
103static char *url_encode (char *dst, const char *src, const char *ep,
104				const char *safe_chars,
105				int encode_leading_slash);
106
107static void write_etc_hosts(FILE *f);
108
109#define DHCPCD "/sbin/dhcpcd"
110#define WPA_SUPPLICANT "/usr/sbin/wpa_supplicant"
111#include <signal.h>
112static int config_eth_medium(char *);
113static int config_dhcp(char *);
114static int config_wlan(char *);
115
116#ifdef INET6
117static int is_v6kernel (void);
118#endif
119
120/*
121 * URL encode unsafe characters.  See RFC 1738.
122 *
123 * Copies src string to dst, encoding unsafe or reserved characters
124 * in %hex form as it goes, and returning a pointer to the result.
125 * The result is always a nul-terminated string even if it had to be
126 * truncated to avoid overflowing the available space.
127 *
128 * This url_encode() function does not operate on complete URLs, it
129 * operates on strings that make up parts of URLs.  For example, in a
130 * URL like "ftp://username:password@host/path", the username, password,
131 * host and path should each be encoded separately before they are
132 * joined together with the punctuation characters.
133 *
134 * In most ordinary use, the path portion of a URL does not start with
135 * a slash; the slash is a separator between the host portion and the
136 * path portion, and is dealt with by software outside the url_encode()
137 * function.  However, it is valid for url_encode() to be passed a
138 * string that does begin with a slash.  For example, the string might
139 * represent a password, or a path part of a URL that the user really
140 * does want to begin with a slash.
141 *
142 * len is the length of the destination buffer.  The result will be
143 * truncated if necessary to fit in the destination buffer.
144 *
145 * safe_chars is a string of characters that should not be encoded.  If
146 * safe_chars is non-NULL, any characters in safe_chars as well as any
147 * alphanumeric characters will be copied from src to dst without
148 * encoding.  Some potentially useful settings for this parameter are:
149 *
150 *	NULL		Everything is encoded (even alphanumerics)
151 *	""		Everything except alphanumerics are encoded
152 *	"/"		Alphanumerics and '/' remain unencoded
153 *	"$-_.+!*'(),"	Consistent with a strict reading of RFC 1738
154 *	"$-_.+!*'(),/"	As above, except '/' is not encoded
155 *	"-_.+!,/"	As above, except shell special characters are encoded
156 *
157 * encode_leading_slash is a flag that determines whether or not to
158 * encode a leading slash in a string.  If this flag is set, and if the
159 * first character in the src string is '/', then the leading slash will
160 * be encoded (as "%2F"), even if '/' is one of the characters in the
161 * safe_chars string.  Note that only the first character of the src
162 * string is affected by this flag, and that leading slashes are never
163 * deleted, but either retained unchanged or encoded.
164 *
165 * Unsafe and reserved characters are defined in RFC 1738 section 2.2.
166 * The most important parts are:
167 *
168 *      The characters ";", "/", "?", ":", "@", "=" and "&" are the
169 *      characters which may be reserved for special meaning within a
170 *      scheme. No other characters may be reserved within a scheme.
171 *      [...]
172 *
173 *      Thus, only alphanumerics, the special characters "$-_.+!*'(),",
174 *      and reserved characters used for their reserved purposes may be
175 *      used unencoded within a URL.
176 *
177 */
178
179#define RFC1738_SAFE				"$-_.+!*'(),"
180#define RFC1738_SAFE_LESS_SHELL			"-_.+!,"
181#define RFC1738_SAFE_LESS_SHELL_PLUS_SLASH	"-_.+!,/"
182
183static char *
184url_encode(char *dst, const char *src, const char *ep,
185	const char *safe_chars, int encode_leading_slash)
186{
187	int ch;
188
189	ep--;
190
191	for (; dst < ep; src++) {
192		ch = *src & 0xff;
193		if (ch == 0)
194			break;
195		if (safe_chars != NULL &&
196		    (ch != '/' || !encode_leading_slash) &&
197		    (isalnum(ch) || strchr(safe_chars, ch))) {
198			*dst++ = ch;
199		} else {
200			/* encode this char */
201			if (ep - dst < 3)
202				break;
203			snprintf(dst, ep - dst, "%%%02X", ch);
204			dst += 3;
205		}
206		encode_leading_slash = 0;
207	}
208	*dst = '\0';
209	return dst;
210}
211
212static const char *ignored_if_names[] = {
213	"gre",			/* net */
214	"ipip",			/* netinet */
215	"gif",			/* netinet6 */
216	"faith",		/* netinet6 */
217	"lo",			/* net */
218	"lo0",			/* net */
219#if 0
220	"mdecap",		/* netinet -- never in IF list (?) XXX */
221#endif
222	"ppp",			/* net */
223#if 0
224	"sl",			/* net */
225#endif
226	"strip",		/* net */
227	"tun",			/* net */
228	/* XXX others? */
229	NULL,
230};
231
232static bool
233have_working_ipv4(void)
234{
235	uint64_t ipstats[IP_NSTATS];
236	size_t size = sizeof(ipstats);
237
238	/* At least some packets delivered to upper layers? */
239	if (sysctlbyname("net.inet.ip.stats", ipstats, &size, NULL, 0) == -1)
240		return false;
241	if (ipstats[IP_STAT_DELIVERED] < 10)	/* arbitrary threshold */
242		return false;
243
244	/* do we have a default route? */
245	if (run_program(RUN_SILENT|RUN_ERROR_OK,
246	    "/sbin/route  get -inet default") != 0)
247		return false;
248
249	return true;
250}
251
252#ifdef INET6
253static bool
254have_working_ipv6(void)
255{
256	uint64_t ipstats[IP6_NSTATS];
257	size_t size = sizeof(ipstats);
258
259	/* At least some packets delivered to upper layers? */
260	if (sysctlbyname("net.inet6.ip6.stats", ipstats, &size, NULL, 0) == -1)
261		return false;
262	if (ipstats[IP6_STAT_DELIVERED] < 10)	/* arbitrary threshold */
263		return false;
264
265	/* do we have a default route? */
266	if (run_program(RUN_SILENT|RUN_ERROR_OK,
267	    "/sbin/route  get -inet6 default") != 0)
268		return false;
269
270	return true;
271}
272#else
273#define	have_working_ipv6()	false
274#endif
275
276static int
277get_ifconfig_info(struct net_desc *devs)
278{
279	char *buf_in;
280	char *buf_tmp;
281	const char **ignore;
282	char *buf;
283	char *tmp;
284	int textsize;
285	int i;
286
287	/* Get ifconfig information */
288	textsize = collect(T_OUTPUT, &buf_in, "/sbin/ifconfig -l 2>/dev/null");
289	if (textsize < 0) {
290		if (logfp)
291			(void)fprintf(logfp,
292			    "Aborting: Could not run ifconfig.\n");
293		(void)fprintf(stderr, "Could not run ifconfig.");
294		exit(1);
295	}
296
297	buf = malloc (STRSIZE * sizeof(char));
298	for (i = 0, buf_tmp = buf_in; i < MAX_NETS && strlen(buf_tmp) > 0
299	    && buf_tmp < buf_in + strlen(buf_in);) {
300		tmp = stpncpy(buf, buf_tmp, strcspn(buf_tmp," \n"));
301		*tmp='\0';
302		buf_tmp += (strcspn(buf_tmp, " \n") + 1) * sizeof(char);
303
304		/* Skip ignored interfaces */
305		for (ignore = ignored_if_names; *ignore != NULL; ignore++) {
306			size_t len = strlen(*ignore);
307			if (strncmp(buf, *ignore, len) == 0 &&
308			    isdigit((unsigned char)buf[len]))
309				break;
310		}
311		if (*ignore != NULL)
312			continue;
313
314		strlcpy (devs[i].if_dev, buf, STRSIZE);
315		i++;
316	}
317	if (i < MAX_NETS)
318		devs[i].if_dev[0] = 0;	/* XXX ? */
319
320	free(buf);
321	free(buf_in);
322	return i;
323}
324
325static int
326do_ifreq(struct ifreq *ifr, unsigned long cmd, void *data)
327{
328	int sock;
329	int rval;
330
331	sock = socket(PF_INET, SOCK_DGRAM, 0);
332	if (sock == -1)
333		return -1;
334
335	memset(ifr, 0, sizeof *ifr);
336	ifr->ifr_data = data;
337	strlcpy(ifr->ifr_name, net_dev, sizeof ifr->ifr_name);
338	rval = ioctl(sock, cmd, ifr);
339	close(sock);
340
341	return rval;
342}
343
344static int
345do_ifmreq(struct ifmediareq *ifmr, unsigned long cmd)
346{
347	int sock;
348	int rval;
349
350	sock = socket(PF_INET, SOCK_DGRAM, 0);
351	if (sock == -1)
352		return -1;
353
354	memset(ifmr, 0, sizeof *ifmr);
355	strlcpy(ifmr->ifm_name, net_dev, sizeof ifmr->ifm_name);
356	rval = ioctl(sock, cmd, ifmr);
357	close(sock);
358
359	return rval;
360}
361
362/* Fill in defaults network values for the selected interface */
363static void
364get_ifinterface_info(void)
365{
366	struct ifreq ifr;
367	struct ifmediareq ifmr;
368	struct sockaddr_in *sa_in = (void*)&ifr.ifr_addr;
369	int modew;
370	const char *media_opt;
371	const char *sep;
372
373	if (do_ifreq(&ifr, SIOCGIFADDR, NULL) == 0 &&
374	    sa_in->sin_addr.s_addr != 0)
375		strlcpy(net_ip, inet_ntoa(sa_in->sin_addr), sizeof net_ip);
376
377	if (do_ifreq(&ifr, SIOCGIFNETMASK, NULL) == 0 &&
378	    sa_in->sin_addr.s_addr != 0)
379		strlcpy(net_mask, inet_ntoa(sa_in->sin_addr), sizeof net_mask);
380
381	if (do_ifmreq(&ifmr, SIOCGIFMEDIA) == 0) {
382		/* Get the name of the media word */
383		modew = ifmr.ifm_current;
384		strlcpy(net_media, get_media_subtype_string(modew),
385		    sizeof net_media);
386		/* and add any media options */
387		sep = " mediaopt ";
388		while ((media_opt = get_media_option_string(&modew)) != NULL) {
389			strlcat(net_media, sep, sizeof net_media);
390			strlcat(net_media, media_opt, sizeof net_media);
391			sep = ",";
392		}
393	}
394}
395
396#ifndef INET6
397#define get_if6interface_info()
398#else
399static void
400get_if6interface_info(void)
401{
402	char *textbuf, *t;
403	int textsize;
404
405	textsize = collect(T_OUTPUT, &textbuf,
406	    "/sbin/ifconfig %s inet6 2>/dev/null", net_dev);
407	if (textsize >= 0) {
408		char *p;
409
410		(void)strtok(textbuf, "\n"); /* ignore first line */
411		while ((t = strtok(NULL, "\n")) != NULL) {
412			if (strncmp(t, "\tinet6 ", 7) != 0)
413				continue;
414			t += 7;
415			if (strstr(t, "tentative") || strstr(t, "duplicated"))
416				continue;
417			if (strncmp(t, "fe80:", 5) == 0)
418				continue;
419
420			p = t;
421			while (*p && *p != ' ' && *p != '\n')
422				p++;
423			*p = '\0';
424			strlcpy(net_ip6, t, sizeof(net_ip6));
425			break;
426		}
427	}
428	free(textbuf);
429}
430#endif
431
432static void
433get_host_info(void)
434{
435	char hostname[MAXHOSTNAMELEN + 1];
436	char *dot;
437
438	/* Check host (and domain?) name */
439	if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != 0) {
440		hostname[sizeof(hostname) - 1] = 0;
441		/* check for a . */
442		dot = strchr(hostname, '.');
443		if (dot == NULL) {
444			/* if not found its just a host, punt on domain */
445			strlcpy(net_host, hostname, sizeof net_host);
446		} else {
447			/* split hostname into host/domain parts */
448			*dot++ = 0;
449			strlcpy(net_host, hostname, sizeof net_host);
450			strlcpy(net_domain, dot, sizeof net_domain);
451		}
452	}
453}
454
455/*
456 * recombine name parts split in get_host_info and config_network
457 * (common code moved here from write_etc_hosts)
458 */
459static char *
460recombine_host_domain(void)
461{
462	static char recombined[MAXHOSTNAMELEN + 1];
463	int l = strlen(net_host) - strlen(net_domain);
464
465	strlcpy(recombined, net_host, sizeof(recombined));
466
467	if (strlen(net_domain) != 0 && (l <= 0 ||
468	    net_host[l - 1] != '.' ||
469	    strcasecmp(net_domain, net_host + l) != 0)) {
470		/* net_host isn't an FQDN. */
471		strlcat(recombined, ".", sizeof(recombined));
472		strlcat(recombined, net_domain, sizeof(recombined));
473	}
474	return recombined;
475}
476
477#ifdef INET6
478static int
479is_v6kernel(void)
480{
481	int s;
482
483	s = socket(PF_INET6, SOCK_DGRAM, 0);
484	if (s < 0)
485		return 0;
486	close(s);
487	return 1;
488}
489#endif
490
491static int
492handle_license(const char *dev)
493{
494	static struct {
495		const char *dev;
496		const char *lic;
497	} licdev[] = {
498		{ "iwi", "/libdata/firmware/if_iwi/LICENSE.ipw2200-fw" },
499		{ "ipw", "/libdata/firmware/if_ipw/LICENSE" },
500	};
501
502	size_t i;
503
504	for (i = 0; i < __arraycount(licdev); i++)
505		if (strncmp(dev, licdev[i].dev, 3) == 0) {
506			char buf[64];
507			int val;
508			size_t len = sizeof(int);
509			(void)snprintf(buf, sizeof(buf), "hw.%s.accept_eula",
510			    licdev[i].dev);
511			if (sysctlbyname(buf, &val, &len, NULL, 0) != -1
512			    && val != 0)
513				return 1;
514			msg_fmt_display(MSG_license, "%s%s",
515			    dev, licdev[i].lic);
516			if (ask_yesno(NULL)) {
517				val = 1;
518				if (sysctlbyname(buf, NULL, NULL, &val,
519				    0) == -1)
520					return 0;
521				add_sysctl_conf("%s=1", buf);
522				return 1;
523			} else
524				return 0;
525		}
526	return 1;
527}
528
529/*
530 * Get the information to configure the network, configure it and
531 * make sure both the gateway and the name server are up.
532 */
533int
534config_network(int force)
535{
536	char *textbuf;
537	int  octet0;
538	int  dhcp_config;
539	int  nfs_root = 0;
540 	int  slip = 0;
541 	int  pid, status;
542 	char **ap, *slcmd[10], *in_buf;
543 	char buffer[STRSIZE];
544	char hostname[MAXHOSTNAMELEN + 1];
545 	struct statvfs sb;
546	struct net_desc net_devs[MAX_NETS];
547	menu_ent *net_menu;
548	int menu_no;
549	int num_devs;
550	int selected_net;
551	int i;
552#ifdef INET6
553	int v6config = 1, rv;
554#endif
555
556	FILE *f;
557	time_t now;
558
559	if (network_up)
560		return (1);
561
562	num_devs = get_ifconfig_info(net_devs);
563
564	if (num_devs < 1) {
565		/* No network interfaces found! */
566		hit_enter_to_continue(NULL, MSG_nonet);
567		return -1;
568	}
569
570	if (!force && (have_working_ipv4() || have_working_ipv6())) {
571		if (ask_yesno(MSG_network_ok)) {
572			network_up = 1;
573			return 1;
574		}
575	}
576
577	net_menu = calloc(num_devs, sizeof(*net_menu));
578	if (net_menu == NULL) {
579		err_msg_win(err_outofmem);
580		return -1;
581	}
582
583	for (i = 0; i < num_devs; i++) {
584		net_menu[i].opt_name = net_devs[i].if_dev;
585		net_menu[i].opt_flags = OPT_EXIT;
586		net_menu[i].opt_action = set_menu_select;
587	}
588
589	menu_no = new_menu(MSG_netdevs,
590		net_menu, num_devs, -1, 4, 0, 0,
591		MC_SCROLL,
592		NULL, NULL, NULL, NULL, MSG_cancel);
593again:
594	selected_net = -1;
595	msg_display(MSG_asknetdev);
596	process_menu(menu_no, &selected_net);
597	msg_clear();
598
599	if (selected_net == -1) {
600		free_menu(menu_no);
601		free(net_menu);
602		return 0;
603	}
604
605	network_up = 1;
606	dhcp_config = 0;
607
608	strlcpy(net_dev, net_devs[selected_net].if_dev, sizeof net_dev);
609
610	if (!handle_license(net_dev))
611		goto done;
612
613	slip = net_dev[0] == 's' && net_dev[1] == 'l' &&
614	    isdigit((unsigned char)net_dev[2]);
615
616	/* If root is on NFS do not reconfigure the interface. */
617	if (statvfs("/", &sb) == 0 && strcmp(sb.f_fstypename, "nfs") == 0) {
618		nfs_root = 1;
619		get_ifinterface_info();
620		get_if6interface_info();
621		get_host_info();
622	} else if (!slip) {
623		/* Preload any defaults we can find */
624		get_ifinterface_info();
625		get_if6interface_info();
626		get_host_info();
627
628		/* domain and host */
629		msg_display(MSG_netinfo);
630
631		if (!config_wlan(net_dev)) {
632			config_eth_medium(net_dev);
633		}
634
635		net_dhcpconf = 0;
636		/* try a dhcp configuration */
637		dhcp_config = config_dhcp(net_dev);
638		if (dhcp_config) {
639			char *nline;
640
641			/* Get newly configured data off interface. */
642			get_ifinterface_info();
643			get_if6interface_info();
644			get_host_info();
645
646			net_dhcpconf |= DHCPCONF_IPADDR;
647
648			/*
649			 * Extract default route from output of
650			 * 'route -n show'
651			 */
652			if (collect(T_OUTPUT, &textbuf,
653			    "/sbin/route -n show | "
654			    "while read dest gateway flags;"
655			    " do [ \"$dest\" = default ] && {"
656			    " echo \"$gateway\"; break; };"
657			    " done" ) > 0)
658				strlcpy(net_defroute, textbuf,
659				    sizeof net_defroute);
660			free(textbuf);
661			if ((nline = strchr(net_defroute, '\n')))
662				*nline = '\0';
663
664			/* pull nameserver info out of /etc/resolv.conf */
665			if (collect(T_OUTPUT, &textbuf,
666			    "cat /etc/resolv.conf 2>/dev/null |"
667			    " while read keyword address rest;"
668			    " do [ \"$keyword\" = nameserver ] &&"
669			    " { echo \"$address\"; break; };"
670			    " done" ) > 0)
671				strlcpy(net_namesvr, textbuf,
672				    sizeof net_namesvr);
673			free(textbuf);
674			if ((nline = strchr(net_namesvr, '\n')))
675				*nline = '\0';
676			if (net_namesvr[0] != '\0')
677				net_dhcpconf |= DHCPCONF_NAMESVR;
678
679			/* pull domain info out of /etc/resolv.conf */
680			if (collect(T_OUTPUT, &textbuf,
681			    "cat /etc/resolv.conf 2>/dev/null |"
682			    " while read keyword domain rest;"
683			    " do [ \"$keyword\" = domain ] &&"
684			    " { echo \"$domain\"; break; };"
685			    " done" ) > 0)
686				strlcpy(net_domain, textbuf,
687				    sizeof net_domain);
688			free(textbuf);
689			if (net_domain[0] == '\0') {
690				/* pull domain info out of /etc/resolv.conf */
691				if (collect(T_OUTPUT, &textbuf,
692				    "cat /etc/resolv.conf 2>/dev/null |"
693				    " while read keyword search rest;"
694				    " do [ \"$keyword\" = search ] &&"
695				    " { echo \"$search\"; break; };"
696				    " done" ) > 0)
697					strlcpy(net_domain, textbuf,
698					    sizeof net_domain);
699				free(textbuf);
700			}
701			if ((nline = strchr(net_domain, '\n')))
702				*nline = '\0';
703			if (net_domain[0] != '\0')
704				net_dhcpconf |= DHCPCONF_DOMAIN;
705
706			if (gethostname(net_host, sizeof(net_host)) == 0 &&
707			    net_host[0] != 0)
708				net_dhcpconf |= DHCPCONF_HOST;
709		}
710	}
711
712	/*
713	 * Prompt for hostname and domain, even when using DHCP. The names
714	 * discovered on the network may not match the desired values
715	 * for the target system.
716	 */
717	strlcpy(hostname, recombine_host_domain(), MAXHOSTNAMELEN);
718	msg_prompt_add(MSG_net_host, net_host, net_host,
719	    sizeof net_host);
720	msg_prompt_add(MSG_net_domain, net_domain, net_domain,
721	    sizeof net_domain);
722	if (strcmp(hostname, recombine_host_domain()) != 0) {
723		net_dhcpconf &= ~(DHCPCONF_DOMAIN|DHCPCONF_HOST);
724	}
725
726	if (!dhcp_config) {
727		/* Manually configure IPv4 */
728		if (!nfs_root)
729			msg_prompt_add(MSG_net_ip, net_ip, net_ip,
730			    sizeof net_ip);
731		if (slip)
732			msg_prompt_add(MSG_net_srv_ip, net_srv_ip, net_srv_ip,
733			    sizeof net_srv_ip);
734		else if (!nfs_root) {
735			/* We don't want netmasks for SLIP */
736			octet0 = atoi(net_ip);
737			if (!net_mask[0]) {
738				if (0 <= octet0 && octet0 <= 127)
739					strlcpy(net_mask, "0xff000000",
740				    	sizeof(net_mask));
741				else if (128 <= octet0 && octet0 <= 191)
742					strlcpy(net_mask, "0xffff0000",
743				    	sizeof(net_mask));
744				else if (192 <= octet0 && octet0 <= 223)
745					strlcpy(net_mask, "0xffffff00",
746				    	sizeof(net_mask));
747			}
748			msg_prompt_add(MSG_net_mask, net_mask, net_mask,
749			    sizeof net_mask);
750		}
751		msg_prompt_add(MSG_net_defroute, net_defroute, net_defroute,
752		    sizeof net_defroute);
753	}
754
755	if (!(net_dhcpconf & DHCPCONF_NAMESVR)) {
756#ifdef INET6
757		if (v6config) {
758			rv = 0;
759			process_menu(MENU_namesrv6, &rv);
760			if (!rv)
761				msg_prompt_add(MSG_net_namesrv, net_namesvr,
762				    net_namesvr, sizeof net_namesvr);
763		} else
764#endif
765		msg_prompt_add(MSG_net_namesrv, net_namesvr, net_namesvr,
766		    sizeof net_namesvr);
767	}
768
769	/* confirm the setting */
770	msg_clear();
771	if (slip)
772		msg_fmt_table_add(MSG_netok_slip, "%s%s%s%s%s%s%s%s%s",
773		    net_domain,
774		    net_host,
775		    *net_namesvr == '\0' ? "<none>" : net_namesvr,
776		    net_dev,
777		    *net_media == '\0' ? "<default>" : net_media,
778		    *net_ip == '\0' ? "<none>" : net_ip,
779		    *net_srv_ip == '\0' ? "<none>" : net_srv_ip,
780		    *net_mask == '\0' ? "<none>" : net_mask,
781		    *net_defroute == '\0' ? "<none>" : net_defroute);
782	else
783		msg_fmt_table_add(MSG_netok, "%s%s%s%s%s%s%s%s",
784		    net_domain,
785		    net_host,
786		    *net_namesvr == '\0' ? "<none>" : net_namesvr,
787		    net_dev,
788		    *net_media == '\0' ? "<default>" : net_media,
789		    *net_ip == '\0' ? "<none>" : net_ip,
790		    *net_mask == '\0' ? "<none>" : net_mask,
791		    *net_defroute == '\0' ? "<none>" : net_defroute);
792#ifdef INET6
793	msg_fmt_table_add(MSG_netokv6, "%s",
794		     !is_v6kernel() ? "<not supported>" : net_ip6);
795#endif
796done:
797	if (!ask_yesno(MSG_netok_ok))
798		goto again;
799
800	free_menu(menu_no);
801	free(net_menu);
802
803	run_program(0, "/sbin/ifconfig lo0 127.0.0.1");
804
805	/* dhcpcd will have configured it all for us */
806	if (dhcp_config) {
807		fflush(NULL);
808		network_up = 1;
809		return network_up;
810	}
811
812	/*
813	 * we may want to perform checks against inconsistent configuration,
814	 * like IPv4 DNS server without IPv4 configuration.
815	 */
816
817	/* Create /etc/resolv.conf if a nameserver was given */
818	if (net_namesvr[0] != '\0') {
819		f = fopen("/etc/resolv.conf", "w");
820		if (f == NULL) {
821			if (logfp)
822				(void)fprintf(logfp,
823				    "%s", msg_string(MSG_resolv));
824			(void)fprintf(stderr, "%s", msg_string(MSG_resolv));
825			exit(1);
826		}
827		scripting_fprintf(NULL, "cat <<EOF >/etc/resolv.conf\n");
828		time(&now);
829		scripting_fprintf(f, ";\n; BIND data file\n; %s %s;\n",
830		    "Created by NetBSD sysinst on", safectime(&now));
831		if (net_domain[0] != '\0')
832			scripting_fprintf(f, "search %s\n", net_domain);
833		if (net_namesvr[0] != '\0')
834			scripting_fprintf(f, "nameserver %s\n", net_namesvr);
835		scripting_fprintf(NULL, "EOF\n");
836		fflush(NULL);
837		fclose(f);
838	}
839
840	if (net_ip[0] != '\0') {
841		if (slip) {
842			/* XXX: needs 'ifconfig sl0 create' much earlier */
843			/* Set SLIP interface UP */
844			run_program(0, "/sbin/ifconfig %s inet %s %s up",
845			    net_dev, net_ip, net_srv_ip);
846			strcpy(sl_flags, "-s 115200 -l /dev/tty00");
847			msg_prompt_win(MSG_slattach, -1, 12, 70, 0,
848				sl_flags, sl_flags, sizeof sl_flags);
849
850			/* XXX: wtf isn't run_program() used here? */
851			pid = fork();
852			if (pid == 0) {
853				strcpy(buffer, "/sbin/slattach ");
854				strcat(buffer, sl_flags);
855				in_buf = buffer;
856
857				for (ap = slcmd; (*ap = strsep(&in_buf, " ")) != NULL;)
858				if (**ap != '\0')
859					++ap;
860
861				execvp(slcmd[0], slcmd);
862			} else
863				wait4(pid, &status, WNOHANG, 0);
864		} else if (!nfs_root) {
865			if (net_mask[0] != '\0') {
866				run_program(0, "/sbin/ifconfig %s inet %s netmask %s",
867				    net_dev, net_ip, net_mask);
868			} else {
869				run_program(0, "/sbin/ifconfig %s inet %s",
870			    	net_dev, net_ip);
871			}
872		}
873	}
874
875	/* Set host name */
876	if (net_host[0] != '\0')
877	  	sethostname(net_host, strlen(net_host));
878
879	/* Set a default route if one was given */
880	if (!nfs_root && net_defroute[0] != '\0') {
881		run_program(RUN_DISPLAY | RUN_PROGRESS,
882				"/sbin/route -n flush -inet");
883		run_program(RUN_DISPLAY | RUN_PROGRESS,
884				"/sbin/route -n add default %s", net_defroute);
885	}
886
887	/*
888	 * wait for addresses to become valid
889	 */
890	if (!nfs_root) {
891		msg_display_add(MSG_wait_network);
892		network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS,
893		    "/sbin/ifconfig -w 15 -W 5");
894	} else {
895		/* Assume network is up. */
896		network_up = 1;
897	}
898
899	fflush(NULL);
900
901	return network_up;
902}
903
904const char *
905url_proto(unsigned int xfer)
906{
907	switch (xfer) {
908	case XFER_FTP:		return "ftp";
909	case XFER_HTTP:		return "http";
910	case XFER_HTTPS:	return "https";
911	}
912
913	return "";
914}
915
916void
917make_url(char *urlbuffer, struct ftpinfo *f, const char *dir)
918{
919	char ftp_user_encoded[STRSIZE];
920	char ftp_dir_encoded[STRSIZE];
921	char *cp;
922	const char *dir2;
923
924	/*
925	 * f->pass is quite likely to contain unsafe characters
926	 * that need to be encoded in the URL (for example,
927	 * "@", ":" and "/" need quoting).  Let's be
928	 * paranoid and also encode f->user and f->dir.  (For
929	 * example, f->dir could easily contain '~', which is
930	 * unsafe by a strict reading of RFC 1738).
931	 */
932	if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) {
933		ftp_user_encoded[0] = 0;
934	} else {
935		cp = url_encode(ftp_user_encoded, f->user,
936			ftp_user_encoded + sizeof ftp_user_encoded - 1,
937			RFC1738_SAFE_LESS_SHELL, 0);
938		*cp++ = ':';
939		cp = url_encode(cp, f->pass,
940			ftp_user_encoded + sizeof ftp_user_encoded - 1,
941			NULL, 0);
942		*cp++ = '@';
943		*cp = 0;
944	}
945	cp = url_encode(ftp_dir_encoded, f->dir,
946			ftp_dir_encoded + sizeof ftp_dir_encoded - 1,
947			RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 1);
948	if (cp != ftp_dir_encoded && cp[-1] != '/')
949		*cp++ = '/';
950
951	dir2 = dir;
952	while (*dir2 == '/')
953		++dir2;
954
955	url_encode(cp, dir2,
956			ftp_dir_encoded + sizeof ftp_dir_encoded,
957			RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 0);
958
959	snprintf(urlbuffer, STRSIZE, "%s://%s%s/%s", url_proto(f->xfer),
960	    ftp_user_encoded, f->xfer_host[XFER_HOST(f->xfer)],
961	    ftp_dir_encoded);
962}
963
964
965/* ftp_fetch() and pkgsrc_fetch() are essentially the same, with a different
966 * ftpinfo var and pkgsrc always using .tgz suffix, while for
967 * regular sets we only use .tgz for source sets on some architectures. */
968static int do_ftp_fetch(const char *, bool, struct ftpinfo *);
969
970static int
971ftp_fetch(const char *set_name)
972{
973	return do_ftp_fetch(set_name, use_tgz_for_set(set_name), &ftp);
974}
975
976static int
977pkgsrc_fetch(const char *set_name)
978{
979	return do_ftp_fetch(set_name, true, &pkgsrc);
980}
981
982static int
983do_ftp_fetch(const char *set_name, bool force_tgz, struct ftpinfo *f)
984{
985	const char *ftp_opt;
986	char url[STRSIZE];
987	int rval;
988
989	/*
990	 * Invoke ftp to fetch the file.
991	 */
992	if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) {
993		/* do anon ftp */
994		ftp_opt = "-a ";
995	} else {
996		ftp_opt = "";
997	}
998
999	make_url(url, f, set_dir_for_set(set_name));
1000	rval = run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_XFER_DIR,
1001		    "/usr/bin/ftp %s%s/%s%s",
1002		    ftp_opt, url, set_name,
1003		    force_tgz ? dist_tgz_postfix : dist_postfix);
1004
1005	return rval ? SET_RETRY : SET_OK;
1006}
1007
1008
1009// XXX: check MSG_netnotup_continueanyway and MSG_netnotup
1010
1011int
1012get_pkgsrc(void)
1013{
1014	int rv = -1;
1015
1016	process_menu(MENU_pkgsrc, &rv);
1017
1018	if (rv == SET_SKIP)
1019		return SET_SKIP;
1020
1021	fetch_fn = pkgsrc_fetch;
1022	snprintf(ext_dir_pkgsrc, sizeof ext_dir_pkgsrc, "%s/%s",
1023	    target_prefix(), xfer_dir + (*xfer_dir == '/'));
1024
1025	return SET_OK;
1026}
1027
1028int
1029get_via_ftp(unsigned int xfer)
1030{
1031	arg_rv arg;
1032
1033	if (!network_up)
1034		config_network(0);
1035
1036	arg.rv = -1;
1037	arg.arg = (void*)(uintptr_t)(xfer);
1038	process_menu(MENU_ftpsource, &arg);
1039
1040	if (arg.rv == SET_RETRY)
1041		return SET_RETRY;
1042
1043	/* We'll fetch each file just before installing it */
1044	fetch_fn = ftp_fetch;
1045	ftp.xfer = xfer;
1046	snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", target_prefix(),
1047	    xfer_dir + (*xfer_dir == '/'));
1048	snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", target_prefix(),
1049	    xfer_dir + (*xfer_dir == '/'));
1050
1051	return SET_OK;
1052}
1053
1054int
1055get_via_nfs(void)
1056{
1057	struct statvfs sb;
1058	int rv;
1059
1060	/* If root is on NFS and we have sets, skip this step. */
1061	if (statvfs(set_dir_bin, &sb) == 0 &&
1062	    strcmp(sb.f_fstypename, "nfs") == 0) {
1063	    	strlcpy(ext_dir_bin, set_dir_bin, sizeof ext_dir_bin);
1064	    	strlcpy(ext_dir_src, set_dir_src, sizeof ext_dir_src);
1065		return SET_OK;
1066	}
1067
1068	/* Get server and filepath */
1069	rv = -1;
1070	process_menu(MENU_nfssource, &rv);
1071
1072	if (rv == SET_RETRY)
1073		return SET_RETRY;
1074
1075	/* Mount it */
1076	if (run_program(0, "/sbin/mount -r -o -2,-i,-r=1024 -t nfs %s:%s /mnt2",
1077	    nfs_host, nfs_dir))
1078		return SET_RETRY;
1079
1080	mnt2_mounted = 1;
1081
1082	snprintf(ext_dir_bin, sizeof ext_dir_bin, "/mnt2/%s", set_dir_bin);
1083	snprintf(ext_dir_src, sizeof ext_dir_src, "/mnt2/%s", set_dir_src);
1084
1085	/* return location, don't clean... */
1086	return SET_OK;
1087}
1088
1089/*
1090 * write the new contents of /etc/hosts to the specified file
1091 */
1092static void
1093write_etc_hosts(FILE *f)
1094{
1095	scripting_fprintf(f, "#\n");
1096	scripting_fprintf(f, "# Added by NetBSD sysinst\n");
1097	scripting_fprintf(f, "#\n");
1098
1099	if (net_domain[0] != '\0')
1100		scripting_fprintf(f, "127.0.0.1	localhost.%s\n", net_domain);
1101
1102	scripting_fprintf(f, "%s\t", net_ip);
1103	if (net_domain[0] != '\0')
1104		scripting_fprintf(f, "%s ", recombine_host_domain());
1105	scripting_fprintf(f, "%s\n", net_host);
1106}
1107
1108/*
1109 * Write the network config info the user entered via menus into the
1110 * config files in the target disk.  Be careful not to lose any
1111 * information we don't immediately add back, in case the install
1112 * target is the currently-active root.
1113 */
1114void
1115mnt_net_config(void)
1116{
1117	char ifconfig_fn[STRSIZE];
1118	FILE *ifconf = NULL;
1119
1120	if (!network_up)
1121		return;
1122	if (!ask_yesno(MSG_mntnetconfig))
1123		return;
1124
1125	/* Write hostname to /etc/rc.conf */
1126	if ((net_dhcpconf & DHCPCONF_HOST) == 0)
1127		if (del_rc_conf("hostname") == 0)
1128			add_rc_conf("hostname=%s\n", recombine_host_domain());
1129
1130	/* Copy resolv.conf to target.  If DHCP was used to create it,
1131	 * it will be replaced on next boot anyway. */
1132	if (net_namesvr[0] != '\0')
1133		dup_file_into_target("/etc/resolv.conf");
1134
1135	/* Copy wpa_supplicant.conf to target. */
1136	if (net_ssid[0] != '\0')
1137		dup_file_into_target("/etc/wpa_supplicant.conf");
1138
1139	/*
1140	 * bring the interface up, it will be necessary for IPv6, and
1141	 * it won't make trouble with IPv4 case either
1142	 */
1143	snprintf(ifconfig_fn, sizeof ifconfig_fn, "/etc/ifconfig.%s", net_dev);
1144	ifconf = target_fopen(ifconfig_fn, "w");
1145	if (ifconf != NULL) {
1146		scripting_fprintf(NULL, "cat <<EOF >>%s%s\n",
1147		    target_prefix(), ifconfig_fn);
1148		scripting_fprintf(ifconf, "up\n");
1149		if (*net_media != '\0')
1150			scripting_fprintf(ifconf, "media %s\n", net_media);
1151		scripting_fprintf(NULL, "EOF\n");
1152	}
1153
1154	if ((net_dhcpconf & DHCPCONF_IPADDR) == 0) {
1155		FILE *hosts;
1156
1157		/* Write IPaddr and netmask to /etc/ifconfig.if[0-9] */
1158		if (ifconf != NULL) {
1159			scripting_fprintf(NULL, "cat <<EOF >>%s%s\n",
1160			    target_prefix(), ifconfig_fn);
1161			if (*net_media != '\0')
1162				scripting_fprintf(ifconf,
1163				    "%s netmask %s media %s\n",
1164				    net_ip, net_mask, net_media);
1165			else
1166				scripting_fprintf(ifconf, "%s netmask %s\n",
1167				    net_ip, net_mask);
1168			scripting_fprintf(NULL, "EOF\n");
1169		}
1170
1171		/*
1172		 * Add IPaddr/hostname to  /etc/hosts.
1173		 * Be careful not to clobber any existing contents.
1174		 * Relies on ordered search of /etc/hosts. XXX YP?
1175		 */
1176		hosts = target_fopen("/etc/hosts", "a");
1177		if (hosts != 0) {
1178			scripting_fprintf(NULL, "cat <<EOF >>%s/etc/hosts\n",
1179			    target_prefix());
1180			write_etc_hosts(hosts);
1181			(void)fclose(hosts);
1182			scripting_fprintf(NULL, "EOF\n");
1183		}
1184
1185		if (del_rc_conf("defaultroute") == 0)
1186			add_rc_conf("defaultroute=\"%s\"\n", net_defroute);
1187	} else {
1188		/*
1189		 * Start dhcpcd quietly and in master mode, but restrict
1190		 * it to our interface
1191		 */
1192		add_rc_conf("dhcpcd=YES\n");
1193		add_rc_conf("dhcpcd_flags=\"-qM %s\"\n", net_dev);
1194        }
1195
1196	if (net_ssid[0] != '\0') {
1197		add_rc_conf("wpa_supplicant=YES\n");
1198		add_rc_conf("wpa_supplicant_flags=\"-B -s -i %s -D bsd -c /etc/wpa_supplicant.conf\"\n", net_dev);
1199	}
1200
1201	if (ifconf)
1202		fclose(ifconf);
1203
1204	fflush(NULL);
1205}
1206
1207int
1208config_wlan(char *inter)
1209{
1210	FILE *wpa_conf = NULL;
1211	char wpa_cmd[256];
1212	struct ifreq ifr = {0};
1213	struct ieee80211_nwid nwid = {0};
1214
1215	/* skip non-WLAN devices */
1216	if (do_ifreq(&ifr, SIOCG80211NWID, &nwid) == -1)
1217		return 0;
1218
1219	if (!file_mode_match(WPA_SUPPLICANT, S_IFREG))
1220		return 0;
1221
1222	msg_prompt_add(MSG_net_ssid, net_ssid, net_ssid,
1223			sizeof net_ssid);
1224	if (net_ssid[0] == '\0')
1225		return 0;
1226
1227	msg_prompt_noecho(MSG_net_passphrase, net_passphrase, net_passphrase,
1228			sizeof net_passphrase);
1229
1230	wpa_conf = fopen("/etc/wpa_supplicant.conf", "a");
1231	if (wpa_conf == NULL)
1232		return 0;
1233
1234	scripting_fprintf(NULL,
1235	    "cat <<EOF >>%s/etc/wpa_supplicant.conf\n",
1236	    target_prefix());
1237	scripting_fprintf(wpa_conf, "\n#\n");
1238	scripting_fprintf(wpa_conf, "# Added by NetBSD sysinst\n");
1239	scripting_fprintf(wpa_conf, "#\n");
1240	scripting_fprintf(wpa_conf, "network={\n");
1241	scripting_fprintf(wpa_conf,
1242	    "\tssid=\"%s\"\n", net_ssid);
1243	if (net_passphrase[0] != '\0') {
1244		scripting_fprintf(wpa_conf, "\tpsk=\"%s\"\n",
1245		    net_passphrase);
1246	} else {
1247		scripting_fprintf(wpa_conf, "\tkey_mgmt=NONE\n");
1248	}
1249	scripting_fprintf(wpa_conf, "\tscan_ssid=1\n");
1250	scripting_fprintf(wpa_conf, "}\n");
1251	(void)fclose(wpa_conf);
1252	scripting_fprintf(NULL, "EOF\n");
1253
1254	if (run_program(RUN_DISPLAY | RUN_PROGRESS,
1255	    "/sbin/ifconfig %s up", inter) != 0)
1256		return 0;
1257
1258	/*
1259	 * have to use system() here to avoid the server process dying
1260	 */
1261	if (snprintf(wpa_cmd, sizeof(wpa_cmd),
1262	    WPA_SUPPLICANT
1263	    " -B -s -i %s -D bsd -c /etc/wpa_supplicant.conf", inter) < 0)
1264		return 0;
1265	(void)do_system(wpa_cmd);
1266
1267	return 1;
1268}
1269
1270int
1271config_dhcp(char *inter)
1272{
1273	int dhcpautoconf;
1274
1275	/*
1276	 * Don't bother checking for an existing instance of dhcpcd, just
1277	 * ask it to renew the lease.  It will fork and daemonize if there
1278	 * wasn't already an instance.
1279	 */
1280
1281	if (!file_mode_match(DHCPCD, S_IFREG))
1282		return 0;
1283	if (ask_yesno(MSG_Perform_autoconfiguration)) {
1284		/* spawn off dhcpcd and wait for parent to exit */
1285		dhcpautoconf = run_program(RUN_DISPLAY | RUN_PROGRESS,
1286		    "%s -d -n %s", DHCPCD, inter);
1287		return dhcpautoconf ? 0 : 1;
1288	}
1289	return 0;
1290}
1291
1292
1293int
1294config_eth_medium(char *inter)
1295{
1296	char *textbuf = NULL;
1297
1298	for (;;) {
1299		msg_prompt_add(MSG_net_media, net_media, net_media,
1300				sizeof net_media);
1301
1302		/*
1303		 * ifconfig does not allow media specifiers on
1304		 * IFM_MANUAL interfaces.  Our UI gives no way
1305		 * to set an option back
1306		 * to null-string if it gets accidentally set.
1307		 * Check for plausible alternatives.
1308		 */
1309		if (strcmp(net_media, "<default>") == 0 ||
1310		    strcmp(net_media, "default") == 0 ||
1311		    strcmp(net_media, "<manual>") == 0 ||
1312		    strcmp(net_media, "manual") == 0 ||
1313		    strcmp(net_media, "<none>") == 0 ||
1314		    strcmp(net_media, "none") == 0 ||
1315		    strcmp(net_media, " ") == 0) {
1316			*net_media = '\0';
1317		}
1318
1319		if (*net_media == '\0')
1320			break;
1321		/*
1322		 * We must set the media type here - to give dhcp
1323		 * a chance
1324		 */
1325		if (run_program(0, "/sbin/ifconfig %s media %s",
1326			    net_dev, net_media) == 0)
1327			break;
1328		/* Failed to set - output the supported values */
1329		if (collect(T_OUTPUT, &textbuf, "/sbin/ifconfig -m %s |"
1330			    "while IFS=; read line;"
1331			    " do [ \"$line\" = \"${line#*media}\" ] || "
1332			    "echo $line;"
1333			    " done", net_dev ) > 0)
1334			msg_display(textbuf);
1335		free(textbuf);
1336	}
1337	return 0;
1338}
1339