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