Index: head/libexec/bootpd/bootpd.c =================================================================== --- head/libexec/bootpd/bootpd.c (revision 229779) +++ head/libexec/bootpd/bootpd.c (revision 229780) @@ -1,1408 +1,1408 @@ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Carnegie Mellon University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ************************************************************************/ /* * BOOTP (bootstrap protocol) server daemon. * * Answers BOOTP request packets from booting client machines. * See [SRI-NIC]RFC951.TXT for a description of the protocol. * See [SRI-NIC]RFC1048.TXT for vendor-information extensions. * See RFC 1395 for option tags 14-17. * See accompanying man page -- bootpd.8 * * HISTORY * See ./Changes * * BUGS * See ./ToDo */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* inet_ntoa */ #ifndef NO_UNISTD #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef NO_SETSID # include /* for O_RDONLY, etc */ #endif #ifndef USE_BFUNCS # include /* Yes, memcpy is OK here (no overlapped copies). */ # define bcopy(a,b,c) memcpy(b,a,c) # define bzero(p,l) memset(p,0,l) # define bcmp(a,b,c) memcmp(a,b,c) #endif #include "bootp.h" #include "hash.h" #include "hwaddr.h" #include "bootpd.h" #include "dovend.h" #include "getif.h" #include "readfile.h" #include "report.h" #include "tzone.h" #include "patchlevel.h" #ifndef CONFIG_FILE #define CONFIG_FILE "/etc/bootptab" #endif #ifndef DUMPTAB_FILE #define DUMPTAB_FILE "/tmp/bootpd.dump" #endif /* * Externals, forward declarations, and global variables */ extern void dumptab(char *); PRIVATE void catcher(int); PRIVATE int chk_access(char *, int32 *); #ifdef VEND_CMU PRIVATE void dovend_cmu(struct bootp *, struct host *); #endif PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); PRIVATE void handle_reply(void); PRIVATE void handle_request(void); PRIVATE void sendreply(int forward, int32 dest_override); PRIVATE void usage(void); /* * IP port numbers for client and server obtained from /etc/services */ u_short bootps_port, bootpc_port; /* * Internet socket and interface config structures */ struct sockaddr_in bind_addr; /* Listening */ struct sockaddr_in recv_addr; /* Packet source */ struct sockaddr_in send_addr; /* destination */ /* * option defaults */ int debug = 0; /* Debugging flag (level) */ struct timeval actualtimeout = { /* fifteen minutes */ 15 * 60L, /* tv_sec */ 0 /* tv_usec */ }; /* * General */ int s; /* Socket file descriptor */ char *pktbuf; /* Receive packet buffer */ int pktlen; char *progname; char *chdir_path; struct in_addr my_ip_addr; static const char *hostname; static char default_hostname[MAXHOSTNAMELEN]; /* Flags set by signal catcher. */ PRIVATE int do_readtab = 0; PRIVATE int do_dumptab = 0; /* * Globals below are associated with the bootp database file (bootptab). */ char *bootptab = CONFIG_FILE; char *bootpd_dump = DUMPTAB_FILE; /* * Initialization such as command-line processing is done and then the * main server loop is started. */ int main(argc, argv) int argc; char **argv; { struct timeval *timeout; struct bootp *bp; struct servent *servp; struct hostent *hep; char *stmp; socklen_t ba_len, ra_len; int n; int nfound; fd_set readfds; int standalone; #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ struct sigaction sa; #endif progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; /* * Initialize logging. */ report_init(0); /* uses progname */ /* * Log startup */ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); /* Debugging for compilers with struct padding. */ assert(sizeof(struct bootp) == BP_MINPKTSZ); /* Get space for receiving packets and composing replies. */ pktbuf = malloc(MAX_MSG_SIZE); if (!pktbuf) { report(LOG_ERR, "malloc failed"); exit(1); } bp = (struct bootp *) pktbuf; /* * Check to see if a socket was passed to us from inetd. * * Use getsockname() to determine if descriptor 0 is indeed a socket * (and thus we are probably a child of inetd) or if it is instead * something else and we are running standalone. */ s = 0; ba_len = sizeof(bind_addr); bzero((char *) &bind_addr, ba_len); errno = 0; standalone = TRUE; if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { /* * Descriptor 0 is a socket. Assume we are a child of inetd. */ if (bind_addr.sin_family == AF_INET) { standalone = FALSE; bootps_port = ntohs(bind_addr.sin_port); } else { /* Some other type of socket? */ report(LOG_ERR, "getsockname: not an INET socket"); } } /* * Set defaults that might be changed by option switches. */ stmp = NULL; timeout = &actualtimeout; if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { report(LOG_ERR, "bootpd: can't get hostname\n"); exit(1); } default_hostname[sizeof(default_hostname) - 1] = '\0'; hostname = default_hostname; /* * Read switches. */ for (argc--, argv++; argc > 0; argc--, argv++) { if (argv[0][0] != '-') break; switch (argv[0][1]) { case 'c': /* chdir_path */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (stmp[0] != '/')) { report(LOG_ERR, "bootpd: invalid chdir specification\n"); break; } chdir_path = stmp; break; case 'd': /* debug level */ if (argv[0][2]) { stmp = &(argv[0][2]); } else if (argv[1] && argv[1][0] == '-') { /* * Backwards-compatible behavior: * no parameter, so just increment the debug flag. */ debug++; break; } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { report(LOG_ERR, "%s: invalid debug level\n", progname); break; } debug = n; break; case 'h': /* override hostname */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp) { report(LOG_ERR, "bootpd: missing hostname\n"); break; } hostname = stmp; break; case 'i': /* inetd mode */ standalone = FALSE; break; case 's': /* standalone mode */ standalone = TRUE; break; case 't': /* timeout */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { report(LOG_ERR, "%s: invalid timeout specification\n", progname); break; } actualtimeout.tv_sec = (int32) (60 * n); /* * If the actual timeout is zero, pass a NULL pointer * to select so it blocks indefinitely, otherwise, * point to the actual timeout value. */ timeout = (n > 0) ? &actualtimeout : NULL; break; default: report(LOG_ERR, "%s: unknown switch: -%c\n", progname, argv[0][1]); usage(); break; } /* switch */ } /* for args */ /* * Override default file names if specified on the command line. */ if (argc > 0) bootptab = argv[0]; if (argc > 1) bootpd_dump = argv[1]; /* * Get my hostname and IP address. */ hep = gethostbyname(hostname); if (!hep) { report(LOG_ERR, "Can not get my IP address\n"); exit(1); } bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); if (standalone) { /* * Go into background and disassociate from controlling terminal. */ if (debug < 3) { if (fork()) exit(0); #ifdef NO_SETSID setpgrp(0,0); #ifdef TIOCNOTTY n = open(_PATH_TTY, O_RDWR); if (n >= 0) { ioctl(n, TIOCNOTTY, (char *) 0); (void) close(n); } #endif /* TIOCNOTTY */ #else /* SETSID */ if (setsid() < 0) perror("setsid"); #endif /* SETSID */ } /* if debug < 3 */ /* * Nuke any timeout value */ timeout = NULL; } /* if standalone (1st) */ /* Set the cwd (i.e. to /tftpboot) */ if (chdir_path) { if (chdir(chdir_path) < 0) report(LOG_ERR, "%s: chdir failed", chdir_path); } /* Get the timezone. */ tzone_init(); /* Allocate hash tables. */ rdtab_init(); /* * Read the bootptab file. */ readtab(1); /* force read */ if (standalone) { /* * Create a socket. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { report(LOG_ERR, "socket: %s", get_network_errmsg()); exit(1); } /* * Get server's listening port number */ servp = getservbyname("bootps", "udp"); if (servp) { bootps_port = ntohs((u_short) servp->s_port); } else { bootps_port = (u_short) IPPORT_BOOTPS; report(LOG_ERR, "bootps/udp: unknown service -- using port %d", bootps_port); } /* * Bind socket to BOOTPS port. */ bind_addr.sin_family = AF_INET; bind_addr.sin_addr.s_addr = INADDR_ANY; bind_addr.sin_port = htons(bootps_port); if (bind(s, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) < 0) { report(LOG_ERR, "bind: %s", get_network_errmsg()); exit(1); } } /* if standalone (2nd)*/ /* * Get destination port number so we can reply to client */ servp = getservbyname("bootpc", "udp"); if (servp) { bootpc_port = ntohs(servp->s_port); } else { report(LOG_ERR, "bootpc/udp: unknown service -- using port %d", IPPORT_BOOTPC); bootpc_port = (u_short) IPPORT_BOOTPC; } /* * Set up signals to read or dump the table. */ #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ sa.sa_handler = catcher; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) { report(LOG_ERR, "sigaction: %s", get_errmsg()); exit(1); } if (sigaction(SIGUSR1, &sa, NULL) < 0) { report(LOG_ERR, "sigaction: %s", get_errmsg()); exit(1); } #else /* SA_NOCLDSTOP */ /* Old-fashioned UNIX signals */ if ((int) signal(SIGHUP, catcher) < 0) { report(LOG_ERR, "signal: %s", get_errmsg()); exit(1); } if ((int) signal(SIGUSR1, catcher) < 0) { report(LOG_ERR, "signal: %s", get_errmsg()); exit(1); } #endif /* SA_NOCLDSTOP */ /* * Process incoming requests. */ FD_ZERO(&readfds); for (;;) { struct timeval tv; FD_SET(s, &readfds); if (timeout) tv = *timeout; nfound = select(s + 1, &readfds, NULL, NULL, (timeout) ? &tv : NULL); if (nfound < 0) { if (errno != EINTR) { report(LOG_ERR, "select: %s", get_errmsg()); } /* * Call readtab() or dumptab() here to avoid the * dangers of doing I/O from a signal handler. */ if (do_readtab) { do_readtab = 0; readtab(1); /* force read */ } if (do_dumptab) { do_dumptab = 0; dumptab(bootpd_dump); } continue; } if (!FD_ISSET(s, &readfds)) { if (debug > 1) report(LOG_INFO, "exiting after %jd minutes of inactivity", (intmax_t)actualtimeout.tv_sec / 60); exit(0); } ra_len = sizeof(recv_addr); n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, (struct sockaddr *) &recv_addr, &ra_len); if (n <= 0) { continue; } if (debug > 1) { report(LOG_INFO, "recvd pkt from IP addr %s", inet_ntoa(recv_addr.sin_addr)); } if (n < sizeof(struct bootp)) { if (debug) { report(LOG_NOTICE, "received short packet"); } continue; } pktlen = n; readtab(0); /* maybe re-read bootptab */ switch (bp->bp_op) { case BOOTREQUEST: handle_request(); break; case BOOTREPLY: handle_reply(); break; } } return 0; } /* * Print "usage" message and exit */ PRIVATE void usage() { fprintf(stderr, "usage: bootpd [-i | -s] [-c chdir-path] [-d level] [-h hostname] [-t timeout]\n"); fprintf(stderr, " [bootptab [dumpfile]]\n"); fprintf(stderr, "\t -c n\tset current directory\n"); fprintf(stderr, "\t -d n\tset debug level\n"); fprintf(stderr, "\t -h n\tset the hostname to listen on\n"); fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); exit(1); } /* Signal catchers */ PRIVATE void catcher(sig) int sig; { if (sig == SIGHUP) do_readtab = 1; if (sig == SIGUSR1) do_dumptab = 1; #if !defined(SA_NOCLDSTOP) && defined(SYSV) /* For older "System V" derivatives with no sigaction(). */ signal(sig, catcher); #endif } /* * Process BOOTREQUEST packet. * * Note: This version of the bootpd.c server never forwards * a request to another server. That is the job of a gateway * program such as the "bootpgw" program included here. * * (Also this version does not interpret the hostname field of * the request packet; it COULD do a name->address lookup and * forward the request there.) */ PRIVATE void handle_request() { struct bootp *bp = (struct bootp *) pktbuf; struct host *hp = NULL; struct host dummyhost; int32 bootsize = 0; unsigned hlen, hashcode; int32 dest; char realpath[1024]; char *clntpath; char *homedir, *bootfile; int n; bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ /* * If the servername field is set, compare it against us. * If we're not being addressed, ignore this request. * If the server name field is null, throw in our name. */ if (strlen(bp->bp_sname)) { if (strcmp(bp->bp_sname, hostname)) { if (debug) report(LOG_INFO, "\ ignoring request for server %s from client at %s address %s", bp->bp_sname, netname(bp->bp_htype), haddrtoa(bp->bp_chaddr, bp->bp_hlen)); /* XXX - Is it correct to ignore such a request? -gwr */ return; } } else { strcpy(bp->bp_sname, hostname); } /* Convert the request into a reply. */ bp->bp_op = BOOTREPLY; if (bp->bp_ciaddr.s_addr == 0) { /* - * client doesnt know his IP address, + * client doesn't know his IP address, * search by hardware address. */ if (debug > 1) { report(LOG_INFO, "request from %s address %s", netname(bp->bp_htype), haddrtoa(bp->bp_chaddr, bp->bp_hlen)); } hlen = haddrlength(bp->bp_htype); if (hlen != bp->bp_hlen) { report(LOG_NOTICE, "bad addr len from %s address %s", netname(bp->bp_htype), haddrtoa(bp->bp_chaddr, hlen)); } dummyhost.htype = bp->bp_htype; bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); hashcode = hash_HashFunction(bp->bp_chaddr, hlen); hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, &dummyhost); if (hp == NULL && bp->bp_htype == HTYPE_IEEE802) { /* Try again with address in "canonical" form. */ haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); if (debug > 1) { report(LOG_INFO, "\ HW addr type is IEEE 802. convert to %s and check again\n", haddrtoa(dummyhost.haddr, bp->bp_hlen)); } hashcode = hash_HashFunction(dummyhost.haddr, hlen); hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, &dummyhost); } if (hp == NULL) { /* * XXX - Add dynamic IP address assignment? */ if (debug) report(LOG_NOTICE, "unknown client %s address %s", netname(bp->bp_htype), haddrtoa(bp->bp_chaddr, bp->bp_hlen)); return; /* not found */ } (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; } else { /* * search by IP address. */ if (debug > 1) { report(LOG_INFO, "request from IP addr %s", inet_ntoa(bp->bp_ciaddr)); } dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, &dummyhost); if (hp == NULL) { if (debug) { report(LOG_NOTICE, "IP address not found: %s", inet_ntoa(bp->bp_ciaddr)); } return; } } if (debug) { report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), hp->hostname->string); } /* * If there is a response delay threshold, ignore requests * with a timestamp lower than the threshold. */ if (hp->flags.min_wait) { u_int32 t = (u_int32) ntohs(bp->bp_secs); if (t < hp->min_wait) { if (debug > 1) report(LOG_INFO, "ignoring request due to timestamp (%d < %d)", t, hp->min_wait); return; } } #ifdef YORK_EX_OPTION /* * The need for the "ex" tag arose out of the need to empty * shared networked drives on diskless PCs. This solution is * not very clean but it does work fairly well. * Written by Edmund J. Sutcliffe * * XXX - This could compromise security if a non-trusted user * managed to write an entry in the bootptab with :ex=trojan: * so I would leave this turned off unless you need it. -gwr */ /* Run a program, passing the client name as a parameter. */ if (hp->flags.exec_file) { char tst[100]; /* XXX - Check string lengths? -gwr */ strcpy (tst, hp->exec_file->string); strcat (tst, " "); strcat (tst, hp->hostname->string); strcat (tst, " &"); if (debug) report(LOG_INFO, "executing %s", tst); system(tst); /* Hope this finishes soon... */ } #endif /* YORK_EX_OPTION */ /* * If a specific TFTP server address was specified in the bootptab file, * fill it in, otherwise zero it. * XXX - Rather than zero it, should it be the bootpd address? -gwr */ (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? hp->bootserver.s_addr : 0L; #ifdef STANFORD_PROM_COMPAT /* * Stanford bootp PROMs (for a Sun?) have no way to leave * the boot file name field blank (because the boot file * name is automatically generated from some index). * As a work-around, this little hack allows those PROMs to * specify "sunboot14" with the same effect as a NULL name. * (The user specifies boot device 14 or some such magic.) */ if (strcmp(bp->bp_file, "sunboot14") == 0) bp->bp_file[0] = '\0'; /* treat it as unspecified */ #endif /* * Fill in the client's proper bootfile. * * If the client specifies an absolute path, try that file with a * ".host" suffix and then without. If the file cannot be found, no * reply is made at all. * * If the client specifies a null or relative file, use the following * table to determine the appropriate action: * * Homedir Bootfile Client's file * specified? specified? specification Action * ------------------------------------------------------------------- * No No Null Send null filename * No No Relative Discard request * No Yes Null Send if absolute else null * No Yes Relative Discard request *XXX * Yes No Null Send null filename * Yes No Relative Lookup with ".host" * Yes Yes Null Send home/boot or bootfile * Yes Yes Relative Lookup with ".host" *XXX * */ /* * XXX - I don't like the policy of ignoring a client when the * boot file is not accessible. The TFTP server might not be * running on the same machine as the BOOTP server, in which * case checking accessibility of the boot file is pointless. * * Therefore, file accessibility is now demanded ONLY if you * define CHECK_FILE_ACCESS in the Makefile options. -gwr */ /* * The "real" path is as seen by the BOOTP daemon on this * machine, while the client path is relative to the TFTP * daemon chroot directory (i.e. /tftpboot). */ if (hp->flags.tftpdir) { snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); clntpath = &realpath[strlen(realpath)]; } else { realpath[0] = '\0'; clntpath = realpath; } /* * Determine client's requested homedir and bootfile. */ homedir = NULL; bootfile = NULL; if (bp->bp_file[0]) { homedir = bp->bp_file; bootfile = strrchr(homedir, '/'); if (bootfile) { if (homedir == bootfile) homedir = NULL; *bootfile++ = '\0'; } else { /* no "/" in the string */ bootfile = homedir; homedir = NULL; } if (debug > 2) { report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", (homedir) ? homedir : "", (bootfile) ? bootfile : ""); } } /* * Specifications in bootptab override client requested values. */ if (hp->flags.homedir) homedir = hp->homedir->string; if (hp->flags.bootfile) bootfile = hp->bootfile->string; /* * Construct bootfile path. */ if (homedir) { if (homedir[0] != '/') strcat(clntpath, "/"); strcat(clntpath, homedir); homedir = NULL; } if (bootfile) { if (bootfile[0] != '/') strcat(clntpath, "/"); strcat(clntpath, bootfile); bootfile = NULL; } /* * First try to find the file with a ".host" suffix */ n = strlen(clntpath); strcat(clntpath, "."); strcat(clntpath, hp->hostname->string); if (chk_access(realpath, &bootsize) < 0) { clntpath[n] = 0; /* Try it without the suffix */ if (chk_access(realpath, &bootsize) < 0) { /* neither "file.host" nor "file" was found */ #ifdef CHECK_FILE_ACCESS if (bp->bp_file[0]) { /* * Client wanted specific file * and we didn't have it. */ report(LOG_NOTICE, "requested file not found: \"%s\"", clntpath); return; } /* * Client didn't ask for a specific file and we couldn't * access the default file, so just zero-out the bootfile * field in the packet and continue processing the reply. */ bzero(bp->bp_file, sizeof(bp->bp_file)); goto null_file_name; #else /* CHECK_FILE_ACCESS */ /* Complain only if boot file size was needed. */ if (hp->flags.bootsize_auto) { report(LOG_ERR, "can not determine size of file \"%s\"", clntpath); } #endif /* CHECK_FILE_ACCESS */ } } strncpy(bp->bp_file, clntpath, BP_FILE_LEN); if (debug > 2) report(LOG_INFO, "bootfile=\"%s\"", clntpath); #ifdef CHECK_FILE_ACCESS null_file_name: #endif /* CHECK_FILE_ACCESS */ /* * Handle vendor options based on magic number. */ if (debug > 1) { report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", (int) ((bp->bp_vend)[0]), (int) ((bp->bp_vend)[1]), (int) ((bp->bp_vend)[2]), (int) ((bp->bp_vend)[3])); } /* * If this host isn't set for automatic vendor info then copy the * specific cookie into the bootp packet, thus forcing a certain * reply format. Only force reply format if user specified it. */ if (hp->flags.vm_cookie) { /* Slam in the user specified magic number. */ bcopy(hp->vm_cookie, bp->bp_vend, 4); } /* * Figure out the format for the vendor-specific info. * Note that bp->bp_vend may have been set above. */ if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { /* RFC1048 conformant bootp client */ dovend_rfc1048(bp, hp, bootsize); if (debug > 1) { report(LOG_INFO, "sending reply (with RFC1048 options)"); } } #ifdef VEND_CMU else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { dovend_cmu(bp, hp); if (debug > 1) { report(LOG_INFO, "sending reply (with CMU options)"); } } #endif else { if (debug > 1) { report(LOG_INFO, "sending reply (with no options)"); } } dest = (hp->flags.reply_addr) ? hp->reply_addr.s_addr : 0L; /* not forwarded */ sendreply(0, dest); } /* * Process BOOTREPLY packet. */ PRIVATE void handle_reply() { if (debug) { report(LOG_INFO, "processing boot reply"); } /* forwarded, no destination override */ sendreply(1, 0); } /* * Send a reply packet to the client. 'forward' flag is set if we are * not the originator of this reply packet. */ PRIVATE void sendreply(forward, dst_override) int forward; int32 dst_override; { struct bootp *bp = (struct bootp *) pktbuf; struct in_addr dst; u_short port = bootpc_port; unsigned char *ha; int len, haf; /* * XXX - Should honor bp_flags "broadcast" bit here. * Temporary workaround: use the :ra=ADDR: option to * set the reply address to the broadcast address. */ /* * If the destination address was specified explicitly * (i.e. the broadcast address for HP compatibility) * then send the response to that address. Otherwise, * act in accordance with RFC951: * If the client IP address is specified, use that * else if gateway IP address is specified, use that * else make a temporary arp cache entry for the client's * NEW IP/hardware address and use that. */ if (dst_override) { dst.s_addr = dst_override; if (debug > 1) { report(LOG_INFO, "reply address override: %s", inet_ntoa(dst)); } } else if (bp->bp_ciaddr.s_addr) { dst = bp->bp_ciaddr; } else if (bp->bp_giaddr.s_addr && forward == 0) { dst = bp->bp_giaddr; port = bootps_port; if (debug > 1) { report(LOG_INFO, "sending reply to gateway %s", inet_ntoa(dst)); } } else { dst = bp->bp_yiaddr; ha = bp->bp_chaddr; len = bp->bp_hlen; if (len > MAXHADDRLEN) len = MAXHADDRLEN; haf = (int) bp->bp_htype; if (haf == 0) haf = HTYPE_ETHERNET; if (debug > 1) report(LOG_INFO, "setarp %s - %s", inet_ntoa(dst), haddrtoa(ha, len)); setarp(s, &dst, haf, ha, len); } if ((forward == 0) && (bp->bp_siaddr.s_addr == 0)) { struct ifreq *ifr; struct in_addr siaddr; /* * If we are originating this reply, we * need to find our own interface address to * put in the bp_siaddr field of the reply. * If this server is multi-homed, pick the * 'best' interface (the one on the same net * as the client). Of course, the client may * be on the other side of a BOOTP gateway... */ ifr = getif(s, &dst); if (ifr) { struct sockaddr_in *sip; sip = (struct sockaddr_in *) &(ifr->ifr_addr); siaddr = sip->sin_addr; } else { /* Just use my "official" IP address. */ siaddr = my_ip_addr; } /* XXX - No need to set bp_giaddr here. */ /* Finally, set the server address field. */ bp->bp_siaddr = siaddr; } /* Set up socket address for send. */ send_addr.sin_family = AF_INET; send_addr.sin_port = htons(port); send_addr.sin_addr = dst; /* Send reply with same size packet as request used. */ if (sendto(s, pktbuf, pktlen, 0, (struct sockaddr *) &send_addr, sizeof(send_addr)) < 0) { report(LOG_ERR, "sendto: %s", get_network_errmsg()); } } /* sendreply */ /* nmatch() - now in getif.c */ /* setarp() - now in hwaddr.c */ /* * This call checks read access to a file. It returns 0 if the file given - * by "path" exists and is publically readable. A value of -1 is returned if + * by "path" exists and is publicly readable. A value of -1 is returned if * access is not permitted or an error occurs. Successful calls also * return the file size in bytes using the long pointer "filesize". * * The read permission bit for "other" users is checked. This bit must be * set for tftpd(8) to allow clients to read the file. */ PRIVATE int chk_access(path, filesize) char *path; int32 *filesize; { struct stat st; if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { *filesize = (int32) st.st_size; return 0; } else { return -1; } } /* * Now in dumptab.c : * dumptab() * dump_host() * list_ipaddresses() */ #ifdef VEND_CMU /* * Insert the CMU "vendor" data for the host pointed to by "hp" into the * bootp packet pointed to by "bp". */ PRIVATE void dovend_cmu(bp, hp) struct bootp *bp; struct host *hp; { struct cmu_vend *vendp; struct in_addr_list *taddr; /* * Initialize the entire vendor field to zeroes. */ bzero(bp->bp_vend, sizeof(bp->bp_vend)); /* * Fill in vendor information. Subnet mask, default gateway, * domain name server, ien name server, time server */ vendp = (struct cmu_vend *) bp->bp_vend; strcpy(vendp->v_magic, (char *)vm_cmu); if (hp->flags.subnet_mask) { (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; (vendp->v_flags) |= VF_SMASK; if (hp->flags.gateway) { (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; } } if (hp->flags.domain_server) { taddr = hp->domain_server; if (taddr->addrcount > 0) { (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; if (taddr->addrcount > 1) { (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; } } } if (hp->flags.name_server) { taddr = hp->name_server; if (taddr->addrcount > 0) { (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; if (taddr->addrcount > 1) { (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; } } } if (hp->flags.time_server) { taddr = hp->time_server; if (taddr->addrcount > 0) { (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; if (taddr->addrcount > 1) { (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; } } } /* Log message now done by caller. */ } /* dovend_cmu */ #endif /* VEND_CMU */ /* * Insert the RFC1048 vendor data for the host pointed to by "hp" into the * bootp packet pointed to by "bp". */ #define NEED(LEN, MSG) do \ if (bytesleft < (LEN)) { \ report(LOG_NOTICE, noroom, \ hp->hostname->string, MSG); \ return; \ } while (0) PRIVATE void dovend_rfc1048(bp, hp, bootsize) struct bootp *bp; struct host *hp; int32 bootsize; { int bytesleft, len; byte *vp; static const char noroom[] = "%s: No room for \"%s\" option"; vp = bp->bp_vend; if (hp->flags.msg_size) { pktlen = hp->msg_size; } else { /* * If the request was longer than the official length, build * a response of that same length where the additional length * is assumed to be part of the bp_vend (options) area. */ if (pktlen > sizeof(*bp)) { if (debug > 1) report(LOG_INFO, "request message length=%d", pktlen); } /* * Check whether the request contains the option: * Maximum DHCP Message Size (RFC1533 sec. 9.8) * and if so, override the response length with its value. * This request must lie within the first BP_VEND_LEN * bytes of the option space. */ { byte *p, *ep; byte tag, len; short msgsz = 0; p = vp + 4; ep = p + BP_VEND_LEN - 4; while (p < ep) { tag = *p++; /* Check for tags with no data first. */ if (tag == TAG_PAD) continue; if (tag == TAG_END) break; /* Now scan the length byte. */ len = *p++; switch (tag) { case TAG_MAX_MSGSZ: if (len == 2) { bcopy(p, (char*)&msgsz, 2); msgsz = ntohs(msgsz); } break; case TAG_SUBNET_MASK: /* XXX - Should preserve this if given... */ break; } /* swtich */ p += len; } if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { if (debug > 1) report(LOG_INFO, "request has DHCP msglen=%d", msgsz); pktlen = msgsz - BP_MSG_OVERHEAD; } } } if (pktlen < sizeof(*bp)) { report(LOG_ERR, "invalid response length=%d", pktlen); pktlen = sizeof(*bp); } bytesleft = ((byte*)bp + pktlen) - vp; if (pktlen > sizeof(*bp)) { if (debug > 1) report(LOG_INFO, "extended reply, length=%d, options=%d", pktlen, bytesleft); } /* Copy in the magic cookie */ bcopy(vm_rfc1048, vp, 4); vp += 4; bytesleft -= 4; if (hp->flags.subnet_mask) { /* always enough room here. */ *vp++ = TAG_SUBNET_MASK;/* -1 byte */ *vp++ = 4; /* -1 byte */ insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ bytesleft -= 6; /* Fix real count */ if (hp->flags.gateway) { (void) insert_ip(TAG_GATEWAY, hp->gateway, &vp, &bytesleft); } } if (hp->flags.bootsize) { /* always enough room here */ bootsize = (hp->flags.bootsize_auto) ? ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ *vp++ = TAG_BOOT_SIZE; *vp++ = 2; *vp++ = (byte) ((bootsize >> 8) & 0xFF); *vp++ = (byte) (bootsize & 0xFF); bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ } /* * This one is special: Remaining options go in the ext file. * Only the subnet_mask, bootsize, and gateway should precede. */ if (hp->flags.exten_file) { /* * Check for room for exten_file. Add 3 to account for * TAG_EXTEN_FILE, length, and TAG_END. */ len = strlen(hp->exten_file->string); NEED((len + 3), "ef"); *vp++ = TAG_EXTEN_FILE; *vp++ = (byte) (len & 0xFF); bcopy(hp->exten_file->string, vp, len); vp += len; *vp++ = TAG_END; bytesleft -= len + 3; return; /* no more options here. */ } /* * The remaining options are inserted by the following * function (which is shared with bootpef.c). * Keep back one byte for the TAG_END. */ len = dovend_rfc1497(hp, vp, bytesleft - 1); vp += len; bytesleft -= len; /* There should be at least one byte left. */ NEED(1, "(end)"); *vp++ = TAG_END; bytesleft--; /* Log message done by caller. */ if (bytesleft > 0) { /* * Zero out any remaining part of the vendor area. */ bzero(vp, bytesleft); } } /* dovend_rfc1048 */ #undef NEED /* * Now in readfile.c: * hwlookcmp() * iplookcmp() */ /* haddrtoa() - now in hwaddr.c */ /* * Now in dovend.c: * insert_ip() * insert_generic() * insert_u_long() */ /* get_errmsg() - now in report.c */ /* * Local Variables: * tab-width: 4 * c-indent-level: 4 * c-argdecl-indent: 4 * c-continued-statement-offset: 4 * c-continued-brace-offset: -4 * c-label-offset: -4 * c-brace-offset: 0 * End: */ Index: head/libexec/bootpd/bootpgw/bootpgw.c =================================================================== --- head/libexec/bootpd/bootpgw/bootpgw.c (revision 229779) +++ head/libexec/bootpd/bootpgw/bootpgw.c (revision 229780) @@ -1,677 +1,677 @@ /* * bootpgw.c - BOOTP GateWay * This program forwards BOOTP Request packets to a BOOTP server. */ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Carnegie Mellon University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ************************************************************************/ /* * BOOTPGW is typically used to forward BOOTP client requests from * one subnet to a BOOTP server on a different subnet. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* inet_ntoa */ #ifndef NO_UNISTD #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef NO_SETSID # include /* for O_RDONLY, etc */ #endif #ifndef USE_BFUNCS # include /* Yes, memcpy is OK here (no overlapped copies). */ # define bcopy(a,b,c) memcpy(b,a,c) # define bzero(p,l) memset(p,0,l) # define bcmp(a,b,c) memcmp(a,b,c) #endif #include "bootp.h" #include "getif.h" #include "hwaddr.h" #include "report.h" #include "patchlevel.h" /* Local definitions: */ #define MAX_MSG_SIZE (3*512) /* Maximum packet size */ #define TRUE 1 #define FALSE 0 #define get_network_errmsg get_errmsg /* * Externals, forward declarations, and global variables */ static void usage(void); static void handle_reply(void); static void handle_request(void); /* * IP port numbers for client and server obtained from /etc/services */ u_short bootps_port, bootpc_port; /* * Internet socket and interface config structures */ struct sockaddr_in bind_addr; /* Listening */ struct sockaddr_in recv_addr; /* Packet source */ struct sockaddr_in send_addr; /* destination */ /* * option defaults */ int debug = 0; /* Debugging flag (level) */ struct timeval actualtimeout = { /* fifteen minutes */ 15 * 60L, /* tv_sec */ 0 /* tv_usec */ }; u_char maxhops = 4; /* Number of hops allowed for requests. */ u_int minwait = 3; /* Number of seconds client must wait before its bootrequest packets are forwarded. */ /* * General */ int s; /* Socket file descriptor */ char *pktbuf; /* Receive packet buffer */ int pktlen; char *progname; char *servername; int32 server_ipa; /* Real server IP address, network order. */ struct in_addr my_ip_addr; struct utsname my_uname; char *hostname; /* * Initialization such as command-line processing is done and then the * main server loop is started. */ int main(argc, argv) int argc; char **argv; { struct timeval *timeout; struct bootp *bp; struct servent *servp; struct hostent *hep; char *stmp; int n, ba_len, ra_len; int nfound, readfds; int standalone; progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; /* * Initialize logging. */ report_init(0); /* uses progname */ /* * Log startup */ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); /* Debugging for compilers with struct padding. */ assert(sizeof(struct bootp) == BP_MINPKTSZ); /* Get space for receiving packets and composing replies. */ pktbuf = malloc(MAX_MSG_SIZE); if (!pktbuf) { report(LOG_ERR, "malloc failed"); exit(1); } bp = (struct bootp *) pktbuf; /* * Check to see if a socket was passed to us from inetd. * * Use getsockname() to determine if descriptor 0 is indeed a socket * (and thus we are probably a child of inetd) or if it is instead * something else and we are running standalone. */ s = 0; ba_len = sizeof(bind_addr); bzero((char *) &bind_addr, ba_len); errno = 0; standalone = TRUE; if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { /* * Descriptor 0 is a socket. Assume we are a child of inetd. */ if (bind_addr.sin_family == AF_INET) { standalone = FALSE; bootps_port = ntohs(bind_addr.sin_port); } else { /* Some other type of socket? */ report(LOG_INFO, "getsockname: not an INET socket"); } } /* * Set defaults that might be changed by option switches. */ stmp = NULL; timeout = &actualtimeout; if (uname(&my_uname) < 0) errx(1, "can't get hostname"); hostname = my_uname.nodename; hep = gethostbyname(hostname); if (!hep) { printf("Can not get my IP address\n"); exit(1); } bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); /* * Read switches. */ for (argc--, argv++; argc > 0; argc--, argv++) { if (argv[0][0] != '-') break; switch (argv[0][1]) { case 'd': /* debug level */ if (argv[0][2]) { stmp = &(argv[0][2]); } else if (argv[1] && argv[1][0] == '-') { /* * Backwards-compatible behavior: * no parameter, so just increment the debug flag. */ debug++; break; } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { warnx("invalid debug level"); break; } debug = n; break; case 'h': /* hop count limit */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0) || (n > 16)) { warnx("invalid hop count limit"); break; } maxhops = (u_char)n; break; case 'i': /* inetd mode */ standalone = FALSE; break; case 's': /* standalone mode */ standalone = TRUE; break; case 't': /* timeout */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { warnx("invalid timeout specification"); break; } actualtimeout.tv_sec = (int32) (60 * n); /* * If the actual timeout is zero, pass a NULL pointer * to select so it blocks indefinitely, otherwise, * point to the actual timeout value. */ timeout = (n > 0) ? &actualtimeout : NULL; break; case 'w': /* wait time */ if (argv[0][2]) { stmp = &(argv[0][2]); } else { argc--; argv++; stmp = argv[0]; } if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0) || (n > 60)) { warnx("invalid wait time"); break; } minwait = (u_int)n; break; default: warnx("unknown switch: -%c", argv[0][1]); usage(); break; } /* switch */ } /* for args */ /* Make sure server name argument is suplied. */ servername = argv[0]; if (!servername) { warnx("missing server name"); usage(); } /* * Get address of real bootp server. */ if (isdigit(servername[0])) server_ipa = inet_addr(servername); else { hep = gethostbyname(servername); if (!hep) errx(1, "can't get addr for %s", servername); bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); } if (standalone) { /* * Go into background and disassociate from controlling terminal. * XXX - This is not the POSIX way (Should use setsid). -gwr */ if (debug < 3) { if (fork()) exit(0); #ifdef NO_SETSID setpgrp(0,0); #ifdef TIOCNOTTY n = open(_PATH_TTY, O_RDWR); if (n >= 0) { ioctl(n, TIOCNOTTY, (char *) 0); (void) close(n); } #endif /* TIOCNOTTY */ #else /* SETSID */ if (setsid() < 0) perror("setsid"); #endif /* SETSID */ } /* if debug < 3 */ /* * Nuke any timeout value */ timeout = NULL; /* * Here, bootpd would do: * chdir * tzone_init * rdtab_init * readtab */ /* * Create a socket. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { report(LOG_ERR, "socket: %s", get_network_errmsg()); exit(1); } /* * Get server's listening port number */ servp = getservbyname("bootps", "udp"); if (servp) { bootps_port = ntohs((u_short) servp->s_port); } else { bootps_port = (u_short) IPPORT_BOOTPS; report(LOG_ERR, "bootps/udp: unknown service -- using port %d", bootps_port); } /* * Bind socket to BOOTPS port. */ bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons(bootps_port); bind_addr.sin_addr.s_addr = INADDR_ANY; if (bind(s, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) < 0) { report(LOG_ERR, "bind: %s", get_network_errmsg()); exit(1); } } /* if standalone */ /* * Get destination port number so we can reply to client */ servp = getservbyname("bootpc", "udp"); if (servp) { bootpc_port = ntohs(servp->s_port); } else { report(LOG_ERR, "bootpc/udp: unknown service -- using port %d", IPPORT_BOOTPC); bootpc_port = (u_short) IPPORT_BOOTPC; } /* no signal catchers */ /* * Process incoming requests. */ for (;;) { struct timeval tv; readfds = 1 << s; if (timeout) tv = *timeout; nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, (timeout) ? &tv : NULL); if (nfound < 0) { if (errno != EINTR) { report(LOG_ERR, "select: %s", get_errmsg()); } continue; } if (!(readfds & (1 << s))) { report(LOG_INFO, "exiting after %ld minutes of inactivity", (long)(actualtimeout.tv_sec / 60)); exit(0); } ra_len = sizeof(recv_addr); n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, (struct sockaddr *) &recv_addr, &ra_len); if (n <= 0) { continue; } if (debug > 3) { report(LOG_INFO, "recvd pkt from IP addr %s", inet_ntoa(recv_addr.sin_addr)); } if (n < sizeof(struct bootp)) { if (debug) { report(LOG_INFO, "received short packet"); } continue; } pktlen = n; switch (bp->bp_op) { case BOOTREQUEST: handle_request(); break; case BOOTREPLY: handle_reply(); break; } } return 0; } /* * Print "usage" message and exit */ static void usage() { fprintf(stderr, "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); fprintf(stderr, "\t -d n\tset debug level\n"); fprintf(stderr, "\t -h n\tset max hop count\n"); fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); exit(1); } /* * Process BOOTREQUEST packet. * * Note, this just forwards the request to a real server. */ static void handle_request() { struct bootp *bp = (struct bootp *) pktbuf; u_short secs; u_char hops; /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ if (debug) { report(LOG_INFO, "request from %s", inet_ntoa(recv_addr.sin_addr)); } /* Has the client been waiting long enough? */ secs = ntohs(bp->bp_secs); if (secs < minwait) return; /* Has this packet hopped too many times? */ hops = bp->bp_hops; if (++hops > maxhops) { - report(LOG_NOTICE, "reqest from %s reached hop limit", + report(LOG_NOTICE, "request from %s reached hop limit", inet_ntoa(recv_addr.sin_addr)); return; } bp->bp_hops = hops; /* * Here one might discard a request from the same subnet as the * real server, but we can assume that the real server will send * a reply to the client before it waits for minwait seconds. */ /* If gateway address is not set, put in local interface addr. */ if (bp->bp_giaddr.s_addr == 0) { #if 0 /* BUG */ struct sockaddr_in *sip; struct ifreq *ifr; /* * XXX - This picks the wrong interface when the receive addr * is the broadcast address. There is no portable way to * find out which interface a broadcast was received on. -gwr * (Thanks to for finding this bug!) */ ifr = getif(s, &recv_addr.sin_addr); if (!ifr) { report(LOG_NOTICE, "no interface for request from %s", inet_ntoa(recv_addr.sin_addr)); return; } sip = (struct sockaddr_in *) &(ifr->ifr_addr); bp->bp_giaddr = sip->sin_addr; #else /* BUG */ /* * XXX - Just set "giaddr" to our "official" IP address. * RFC 1532 says giaddr MUST be set to the address of the * interface on which the request was received. Setting * it to our "default" IP address is not strictly correct, * but is good enough to allow the real BOOTP server to * get the reply back here. Then, before we forward the * reply to the client, the giaddr field is corrected. * (In case the client uses giaddr, which it should not.) * See handle_reply() */ bp->bp_giaddr = my_ip_addr; #endif /* BUG */ /* * XXX - DHCP says to insert a subnet mask option into the * options area of the request (if vendor magic == std). */ } /* Set up socket address for send. */ send_addr.sin_family = AF_INET; send_addr.sin_port = htons(bootps_port); send_addr.sin_addr.s_addr = server_ipa; /* Send reply with same size packet as request used. */ if (sendto(s, pktbuf, pktlen, 0, (struct sockaddr *) &send_addr, sizeof(send_addr)) < 0) { report(LOG_ERR, "sendto: %s", get_network_errmsg()); } } /* * Process BOOTREPLY packet. */ static void handle_reply() { struct bootp *bp = (struct bootp *) pktbuf; struct ifreq *ifr; struct sockaddr_in *sip; unsigned char *ha; int len, haf; if (debug) { report(LOG_INFO, " reply for %s", inet_ntoa(bp->bp_yiaddr)); } /* Make sure client is directly accessible. */ ifr = getif(s, &(bp->bp_yiaddr)); if (!ifr) { report(LOG_NOTICE, "no interface for reply to %s", inet_ntoa(bp->bp_yiaddr)); return; } #if 1 /* Experimental (see BUG above) */ /* #ifdef CATER_TO_OLD_CLIENTS ? */ /* * The giaddr field has been set to our "default" IP address * which might not be on the same interface as the client. * In case the client looks at giaddr, (which it should not) * giaddr is now set to the address of the correct interface. */ sip = (struct sockaddr_in *) &(ifr->ifr_addr); bp->bp_giaddr = sip->sin_addr; #endif /* Set up socket address for send to client. */ send_addr.sin_family = AF_INET; send_addr.sin_addr = bp->bp_yiaddr; send_addr.sin_port = htons(bootpc_port); /* Create an ARP cache entry for the client. */ ha = bp->bp_chaddr; len = bp->bp_hlen; if (len > MAXHADDRLEN) len = MAXHADDRLEN; haf = (int) bp->bp_htype; if (haf == 0) haf = HTYPE_ETHERNET; if (debug > 1) report(LOG_INFO, "setarp %s - %s", inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); setarp(s, &bp->bp_yiaddr, haf, ha, len); /* Send reply with same size packet as request used. */ if (sendto(s, pktbuf, pktlen, 0, (struct sockaddr *) &send_addr, sizeof(send_addr)) < 0) { report(LOG_ERR, "sendto: %s", get_network_errmsg()); } } /* * Local Variables: * tab-width: 4 * c-indent-level: 4 * c-argdecl-indent: 4 * c-continued-statement-offset: 4 * c-continued-brace-offset: -4 * c-label-offset: -4 * c-brace-offset: 0 * End: */ Index: head/libexec/bootpd/hash.h =================================================================== --- head/libexec/bootpd/hash.h (revision 229779) +++ head/libexec/bootpd/hash.h (revision 229780) @@ -1,148 +1,148 @@ #ifndef HASH_H #define HASH_H /* hash.h */ /************************************************************************ Copyright 1988, 1991 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Carnegie Mellon University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ************************************************************************/ /* * Generalized hash table ADT * $FreeBSD$ * * Provides multiple, dynamically-allocated, variable-sized hash tables on * various data and keys. * * This package attempts to follow some of the coding conventions suggested * by Bob Sidebotham and the AFS Clean Code Committee. */ /* * The user must supply the following: * * 1. A comparison function which is declared as: * * int compare(data1, data2) * hash_datum *data1, *data2; * * This function must compare the desired fields of data1 and * data2 and return TRUE (1) if the data should be considered * equivalent (i.e. have the same key value) or FALSE (0) * otherwise. This function is called through a pointer passed to * the various hashtable functions (thus pointers to different * functions may be passed to effect different tests on different * hash tables). * * Internally, all the functions of this package always call the * compare function with the "key" parameter as the first parameter, * and a full data element as the second parameter. Thus, the key * and element arguments to functions such as hash_Lookup() may * actually be of different types and the programmer may provide a * compare function which compares the two different object types * as desired. * * Example: * * int compare(key, element) * char *key; * struct some_complex_structure *element; * { * return !strcmp(key, element->name); * } * * key = "John C. Doe" * element = &some_complex_structure * hash_Lookup(table, hashcode, compare, key); * * 2. A hash function yielding an unsigned integer value to be used * as the hashcode (index into the hashtable). Thus, the user * may hash on whatever data is desired and may use several * different hash functions for various different hash tables. * The actual hash table index will be the passed hashcode modulo * the hash table size. * * A generalized hash function, hash_HashFunction(), is included * with this package to make things a little easier. It is not - * guarenteed to use the best hash algorithm in existence. . . . + * guaranteed to use the best hash algorithm in existence. . . . */ /* * Various hash table definitions */ /* * Define "hash_datum" as a universal data type */ typedef void hash_datum; typedef struct hash_memberstruct hash_member; typedef struct hash_tblstruct hash_tbl; typedef struct hash_tblstruct_hdr hash_tblhdr; struct hash_memberstruct { hash_member *next; hash_datum *data; }; struct hash_tblstruct_hdr { unsigned size, bucketnum; hash_member *member; }; struct hash_tblstruct { unsigned size, bucketnum; hash_member *member; /* Used for linear dump */ hash_member *table[1]; /* Dynamically extended */ }; /* ANSI function prototypes or empty arg list? */ typedef int (*hash_cmpfp)(hash_datum *, hash_datum *); typedef void (*hash_freefp)(hash_datum *); extern hash_tbl *hash_Init(u_int tablesize); extern void hash_Reset(hash_tbl *tbl, hash_freefp); extern unsigned hash_HashFunction(u_char *str, u_int len); extern int hash_Exists(hash_tbl *, u_int code, hash_cmpfp, hash_datum *key); extern int hash_Insert(hash_tbl *, u_int code, hash_cmpfp, hash_datum *key, hash_datum *element); extern int hash_Delete(hash_tbl *, u_int code, hash_cmpfp, hash_datum *key, hash_freefp); extern hash_datum *hash_Lookup(hash_tbl *, u_int code, hash_cmpfp, hash_datum *key); extern hash_datum *hash_FirstEntry(hash_tbl *); extern hash_datum *hash_NextEntry(hash_tbl *); #endif /* HASH_H */ Index: head/libexec/bootpd/tools/bootptest/bootptest.c =================================================================== --- head/libexec/bootpd/tools/bootptest/bootptest.c (revision 229779) +++ head/libexec/bootpd/tools/bootptest/bootptest.c (revision 229780) @@ -1,520 +1,520 @@ /* * bootptest.c - Test out a bootp server. * * This simple program was put together from pieces taken from * various places, including the CMU BOOTP client and server. * The packet printing routine is from the Berkeley "tcpdump" * program with some enhancements I added. The print-bootp.c * file was shared with my copy of "tcpdump" and therefore uses * some unusual utility routines that would normally be provided * by various parts of the tcpdump program. Gordon W. Ross * * Boilerplate: * * This program includes software developed by the University of * California, Lawrence Berkeley Laboratory and its contributors. * (See the copyright notice in print-bootp.c) * * The remainder of this program is public domain. You may do * whatever you like with it except claim that you wrote it. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * HISTORY: * * 12/02/93 Released version 1.4 (with bootp-2.3.2) * 11/05/93 Released version 1.3 * 10/14/93 Released version 1.2 * 10/11/93 Released version 1.1 * 09/28/93 Released version 1.0 * 09/93 Original developed by Gordon W. Ross * */ #include __FBSDID("$FreeBSD$"); char *usage = "bootptest [-h] server-name [vendor-data-template-file]"; #include #include #include #include #include #include #include #include #include #include /* inet_ntoa */ #ifndef NO_UNISTD #include #endif #include #include #include #include #include #include #include #include #include #include "bootp.h" #include "bootptest.h" #include "getif.h" #include "getether.h" #include "patchlevel.h" static void send_request(); #define LOG_ERR 1 #define BUFLEN 1024 #define WAITSECS 1 #define MAXWAIT 10 int vflag = 1; int tflag = 0; int thiszone; char *progname; unsigned char *packetp; unsigned char *snapend; int snaplen; /* * IP port numbers for client and server obtained from /etc/services */ u_short bootps_port, bootpc_port; /* * Internet socket and interface config structures */ struct sockaddr_in sin_server; /* where to send requests */ struct sockaddr_in sin_client; /* for bind and listen */ struct sockaddr_in sin_from; /* Packet source */ u_char eaddr[16]; /* Ethernet address */ /* * General */ int debug = 1; /* Debugging flag (level) */ char *sndbuf; /* Send packet buffer */ char *rcvbuf; /* Receive packet buffer */ struct utsname my_uname; char *hostname; /* * Vendor magic cookies for CMU and RFC1048 */ unsigned char vm_cmu[4] = VM_CMU; unsigned char vm_rfc1048[4] = VM_RFC1048; short secs; /* How long client has waited */ char *get_errmsg(); extern void bootp_print(); /* * Initialization such as command-line processing is done, then * the receiver loop is started. Die when interrupted. */ int main(argc, argv) int argc; char **argv; { struct bootp *bp; struct servent *sep; struct hostent *hep; char *servername = NULL; char *vendor_file = NULL; char *bp_file = NULL; int32 server_addr; /* inet addr, network order */ int s; /* Socket file descriptor */ int n, fromlen, recvcnt; int use_hwa = 0; int32 vend_magic; int32 xid; progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; argc--; argv++; if (debug) printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); /* * Verify that "struct bootp" has the correct official size. * (Catch evil compilers that do struct padding.) */ assert(sizeof(struct bootp) == BP_MINPKTSZ); if (uname(&my_uname) < 0) errx(1, "can't get hostname"); hostname = my_uname.nodename; sndbuf = malloc(BUFLEN); rcvbuf = malloc(BUFLEN); if (!sndbuf || !rcvbuf) { printf("malloc failed\n"); exit(1); } /* default magic number */ bcopy(vm_rfc1048, (char*)&vend_magic, 4); /* Handle option switches. */ while (argc > 0) { if (argv[0][0] != '-') break; switch (argv[0][1]) { - case 'f': /* File name to reqest. */ + case 'f': /* File name to request. */ if (argc < 2) goto error; argc--; argv++; bp_file = *argv; break; case 'h': /* Use hardware address. */ use_hwa = 1; break; case 'm': /* Magic number value. */ if (argc < 2) goto error; argc--; argv++; vend_magic = inet_addr(*argv); break; error: default: puts(usage); exit(1); } argc--; argv++; } /* Get server name (or address) for query. */ if (argc > 0) { servername = *argv; argc--; argv++; } /* Get optional vendor-data-template-file. */ if (argc > 0) { vendor_file = *argv; argc--; argv++; } if (!servername) { printf("missing server name.\n"); puts(usage); exit(1); } /* * Create a socket. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } /* * Get server's listening port number */ sep = getservbyname("bootps", "udp"); if (sep) { bootps_port = ntohs((u_short) sep->s_port); } else { warnx("bootps/udp: unknown service -- using port %d", IPPORT_BOOTPS); bootps_port = (u_short) IPPORT_BOOTPS; } /* * Set up server socket address (for send) */ if (servername) { if (isdigit(servername[0])) server_addr = inet_addr(servername); else { hep = gethostbyname(servername); if (!hep) errx(1, "%s: unknown host", servername); bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); } } else { /* Get broadcast address */ /* XXX - not yet */ server_addr = INADDR_ANY; } sin_server.sin_family = AF_INET; sin_server.sin_port = htons(bootps_port); sin_server.sin_addr.s_addr = server_addr; /* * Get client's listening port number */ sep = getservbyname("bootpc", "udp"); if (sep) { bootpc_port = ntohs(sep->s_port); } else { warnx("bootpc/udp: unknown service -- using port %d", IPPORT_BOOTPC); bootpc_port = (u_short) IPPORT_BOOTPC; } /* * Set up client socket address (for listen) */ sin_client.sin_family = AF_INET; sin_client.sin_port = htons(bootpc_port); sin_client.sin_addr.s_addr = INADDR_ANY; /* * Bind client socket to BOOTPC port. */ if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { if (errno == EACCES) { warn("bind BOOTPC port"); errx(1, "you need to run this as root"); } else err(1, "bind BOOTPC port"); } /* * Build a request. */ bp = (struct bootp *) sndbuf; bzero(bp, sizeof(*bp)); bp->bp_op = BOOTREQUEST; xid = (int32) getpid(); bp->bp_xid = (u_int32) htonl(xid); if (bp_file) strncpy(bp->bp_file, bp_file, BP_FILE_LEN); /* * Fill in the hardware address (or client IP address) */ if (use_hwa) { struct ifreq *ifr; ifr = getif(s, &sin_server.sin_addr); if (!ifr) { printf("No interface for %s\n", servername); exit(1); } if (getether(ifr->ifr_name, (char*)eaddr)) { printf("Can not get ether addr for %s\n", ifr->ifr_name); exit(1); } /* Copy Ethernet address into request packet. */ bp->bp_htype = 1; bp->bp_hlen = 6; bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); } else { /* Fill in the client IP address. */ hep = gethostbyname(hostname); if (!hep) { printf("Can not get my IP address\n"); exit(1); } bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); } /* * Copy in the default vendor data. */ bcopy((char*)&vend_magic, bp->bp_vend, 4); if (vend_magic) bp->bp_vend[4] = TAG_END; /* * Read in the "options" part of the request. * This also determines the size of the packet. */ snaplen = sizeof(*bp); if (vendor_file) { int fd = open(vendor_file, 0); if (fd < 0) { perror(vendor_file); exit(1); } /* Compute actual space for options. */ n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; n = read(fd, bp->bp_vend, n); close(fd); if (n < 0) { perror(vendor_file); exit(1); } printf("read %d bytes of vendor template\n", n); if (n > BP_VEND_LEN) { printf("warning: extended options in use (len > %d)\n", BP_VEND_LEN); snaplen += (n - BP_VEND_LEN); } } /* * Set globals needed by print_bootp * (called by send_request) */ packetp = (unsigned char *) eaddr; snapend = (unsigned char *) sndbuf + snaplen; /* Send a request once per second while waiting for replies. */ recvcnt = 0; bp->bp_secs = secs = 0; send_request(s); while (1) { struct timeval tv; int readfds; tv.tv_sec = WAITSECS; tv.tv_usec = 0L; readfds = (1 << s); n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); if (n < 0) { perror("select"); break; } if (n == 0) { /* * We have not received a response in the last second. * If we have ever received any responses, exit now. * Otherwise, bump the "wait time" field and re-send. */ if (recvcnt > 0) exit(0); secs += WAITSECS; if (secs > MAXWAIT) break; bp->bp_secs = htons(secs); send_request(s); continue; } fromlen = sizeof(sin_from); n = recvfrom(s, rcvbuf, BUFLEN, 0, (struct sockaddr *) &sin_from, &fromlen); if (n <= 0) { continue; } if (n < sizeof(struct bootp)) { printf("received short packet\n"); continue; } recvcnt++; /* Print the received packet. */ printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); /* set globals needed by bootp_print() */ snaplen = n; snapend = (unsigned char *) rcvbuf + snaplen; bootp_print(rcvbuf, n, sin_from.sin_port, 0); putchar('\n'); /* * This no longer exits immediately after receiving * one response because it is useful to know if the * client might get multiple responses. This code * will now listen for one second after a response. */ } errx(1, "no response from %s", servername); } static void send_request(s) int s; { /* Print the request packet. */ printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); bootp_print(sndbuf, snaplen, sin_from.sin_port, 0); putchar('\n'); /* Send the request packet. */ if (sendto(s, sndbuf, snaplen, 0, (struct sockaddr *) &sin_server, sizeof(sin_server)) < 0) { perror("sendto server"); exit(1); } } /* * Print out a filename (or other ascii string). * Return true if truncated. */ int printfn(s, ep) register u_char *s, *ep; { register u_char c; putchar('"'); while ((c = *s++) != '\0') { if (s > ep) { putchar('"'); return (1); } if (!isascii(c)) { c = toascii(c); putchar('M'); putchar('-'); } if (!isprint(c)) { c ^= 0x40; /* DEL to ?, others to alpha */ putchar('^'); } putchar(c); } putchar('"'); return (0); } /* * Convert an IP addr to a string. * (like inet_ntoa, but ina is a pointer) */ char * ipaddr_string(ina) struct in_addr *ina; { static char b[24]; u_char *p; p = (u_char *) ina; snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return (b); } /* * Local Variables: * tab-width: 4 * c-indent-level: 4 * c-argdecl-indent: 4 * c-continued-statement-offset: 4 * c-continued-brace-offset: -4 * c-label-offset: -4 * c-brace-offset: 0 * End: */ Index: head/libexec/bootpd/tools/bootptest/print-bootp.c =================================================================== --- head/libexec/bootpd/tools/bootptest/print-bootp.c (revision 229779) +++ head/libexec/bootpd/tools/bootptest/print-bootp.c (revision 229780) @@ -1,492 +1,492 @@ /* * Copyright (c) 1988-1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Format and print bootp packets. * * This file was copied from tcpdump-2.1.1 and modified. * There is an e-mail list for tcpdump: * * $FreeBSD$ */ #include #include #include #include #include /* for struct timeval in net/if.h */ #include #include #include #include #include "bootp.h" #include "bootptest.h" /* These decode the vendor data. */ extern int printfn(); static void rfc1048_print(); static void cmu_print(); static void other_print(); static void dump_hex(); /* * Print bootp requests */ void bootp_print(bp, length, sport, dport) struct bootp *bp; int length; u_short sport, dport; { static char tstr[] = " [|bootp]"; static unsigned char vm_cmu[4] = VM_CMU; static unsigned char vm_rfc1048[4] = VM_RFC1048; u_char *ep; int vdlen; #define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc /* Note funny sized packets */ if (length != sizeof(struct bootp)) (void) printf(" [len=%d]", length); /* 'ep' points to the end of avaible data. */ ep = (u_char *) snapend; switch (bp->bp_op) { case BOOTREQUEST: /* Usually, a request goes from a client to a server */ if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS) printf(" (request)"); break; case BOOTREPLY: /* Usually, a reply goes from a server to a client */ if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC) printf(" (reply)"); break; default: printf(" bootp-#%d", bp->bp_op); } /* The usual hardware address type is 1 (10Mb Ethernet) */ if (bp->bp_htype != 1) printf(" htype:%d", bp->bp_htype); /* The usual length for 10Mb Ethernet address is 6 bytes */ if (bp->bp_hlen != 6) printf(" hlen:%d", bp->bp_hlen); /* Client's Hardware address */ if (bp->bp_hlen) { register struct ether_header *eh; register char *e; TCHECK(bp->bp_chaddr[0], 6); eh = (struct ether_header *) packetp; if (bp->bp_op == BOOTREQUEST) e = (char *) ESRC(eh); else if (bp->bp_op == BOOTREPLY) e = (char *) EDST(eh); else e = 0; if (e == 0 || bcmp((char *) bp->bp_chaddr, e, 6)) dump_hex(bp->bp_chaddr, bp->bp_hlen); } /* Only print interesting fields */ if (bp->bp_hops) printf(" hops:%d", bp->bp_hops); if (bp->bp_xid) printf(" xid:%ld", (long)ntohl(bp->bp_xid)); if (bp->bp_secs) printf(" secs:%d", ntohs(bp->bp_secs)); /* Client's ip address */ TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); if (bp->bp_ciaddr.s_addr) printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); /* 'your' ip address (bootp client) */ TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); if (bp->bp_yiaddr.s_addr) printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); /* Server's ip address */ TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); if (bp->bp_siaddr.s_addr) printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); /* Gateway's ip address */ TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); if (bp->bp_giaddr.s_addr) printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); if (*bp->bp_sname) { printf(" sname:"); if (printfn(bp->bp_sname, ep)) { fputs(tstr + 1, stdout); return; } } TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); if (*bp->bp_file) { printf(" file:"); if (printfn(bp->bp_file, ep)) { fputs(tstr + 1, stdout); return; } } /* Don't try to decode the vendor buffer unless we're verbose */ if (vflag <= 0) return; vdlen = sizeof(bp->bp_vend); /* Vendor data can extend to the end of the packet. */ if (vdlen < (ep - bp->bp_vend)) vdlen = (ep - bp->bp_vend); TCHECK(bp->bp_vend[0], vdlen); printf(" vend"); if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) rfc1048_print(bp->bp_vend, vdlen); else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) cmu_print(bp->bp_vend, vdlen); else other_print(bp->bp_vend, vdlen); return; trunc: fputs(tstr, stdout); #undef TCHECK } /* * Option description data follows. - * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 + * These are described in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 * * The first char of each option string encodes the data format: * ?: unknown * a: ASCII * b: byte (8-bit) * i: inet address * l: int32 * s: short (16-bit) */ char * rfc1048_opts[] = { /* Originally from RFC-1048: */ "?PAD", /* 0: Padding - special, no data. */ "iSM", /* 1: subnet mask (RFC950)*/ "lTZ", /* 2: time offset, seconds from UTC */ "iGW", /* 3: gateways (or routers) */ "iTS", /* 4: time servers (RFC868) */ "iINS", /* 5: IEN name servers (IEN116) */ "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ "iLOG", /* 7: MIT log servers */ "iCS", /* 8: cookie servers (RFC865) */ "iLPR", /* 9: lpr server (RFC1179) */ "iIPS", /* 10: impress servers (Imagen) */ "iRLP", /* 11: resource location servers (RFC887) */ "aHN", /* 12: host name (ASCII) */ "sBFS", /* 13: boot file size (in 512 byte blocks) */ /* Added by RFC-1395: */ "aDUMP", /* 14: Merit Dump File */ "aDNAM", /* 15: Domain Name (for DNS) */ "iSWAP", /* 16: Swap Server */ "aROOT", /* 17: Root Path */ /* Added by RFC-1497: */ "aEXTF", /* 18: Extensions Path (more options) */ /* Added by RFC-1533: (many, many options...) */ #if 1 /* These might not be worth recognizing by name. */ /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ "bIP-forward", /* 19: IP Forwarding flag */ "bIP-srcroute", /* 20: IP Source Routing Enable flag */ "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ "bIP-ttlive", /* 23: IP Time to Live */ "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ "sIP-pmtutab", /* 25: IP Path MTU plateau table */ /* IP parameters, per-interface (RFC-1533, sect. 5) */ "sIP-mtu-sz", /* 26: IP MTU size */ "bIP-mtu-sl", /* 27: IP MTU all subnets local */ "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ "bIP-mask-d", /* 29: IP do mask discovery */ "bIP-mask-s", /* 30: IP do mask supplier */ "bIP-rt-dsc", /* 31: IP do router discovery */ "iIP-rt-sa", /* 32: IP router solicitation addr */ "iIP-routes", /* 33: IP static routes (dst,router) */ /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ "bLL-trailer", /* 34: do tralier encapsulation */ "lLL-arp-tmo", /* 35: ARP cache timeout */ "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ /* TCP parameters (RFC-1533, sect. 7) */ "bTCP-def-ttl", /* 37: default time to live */ "lTCP-KA-tmo", /* 38: keepalive time interval */ "bTCP-KA-junk", /* 39: keepalive sends extra junk */ /* Application and Service Parameters (RFC-1533, sect. 8) */ "aNISDOM", /* 40: NIS Domain (Sun YP) */ "iNISSRV", /* 41: NIS Servers */ "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ "bNBiosNT", /* 46: NetBIOS Note Type */ "?NBiosS", /* 47: NetBIOS Scope */ "iXW-FS", /* 48: X Window System Font Servers */ "iXW-DM", /* 49: X Window System Display Managers */ /* DHCP extensions (RFC-1533, sect. 9) */ #endif }; #define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) static void rfc1048_print(bp, length) register u_char *bp; int length; { u_char tag; u_char *ep; register int len; u_int32 ul; u_short us; struct in_addr ia; char *optstr; printf("-rfc1395"); /* Step over magic cookie */ bp += sizeof(int32); /* Setup end pointer */ ep = bp + length; while (bp < ep) { tag = *bp++; /* Check for tags with no data first. */ if (tag == TAG_PAD) continue; if (tag == TAG_END) return; if (tag < KNOWN_OPTIONS) { optstr = rfc1048_opts[tag]; printf(" %s:", optstr + 1); } else { printf(" T%d:", tag); optstr = "?"; } /* Now scan the length byte. */ len = *bp++; if (bp + len > ep) { /* truncated option */ printf(" |(%d>%td)", len, ep - bp); return; } /* Print the option value(s). */ switch (optstr[0]) { case 'a': /* ASCII string */ printfn(bp, bp + len); bp += len; len = 0; break; case 's': /* Word formats */ while (len >= 2) { bcopy((char *) bp, (char *) &us, 2); printf("%d", ntohs(us)); bp += 2; len -= 2; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'l': /* Long words */ while (len >= 4) { bcopy((char *) bp, (char *) &ul, 4); printf("%ld", (long)ntohl(ul)); bp += 4; len -= 4; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'i': /* INET addresses */ while (len >= 4) { bcopy((char *) bp, (char *) &ia, 4); printf("%s", ipaddr_string(&ia)); bp += 4; len -= 4; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'b': default: break; } /* switch */ /* Print as characters, if appropriate. */ if (len) { dump_hex(bp, len); if (isascii(*bp) && isprint(*bp)) { printf("("); printfn(bp, bp + len); printf(")"); } bp += len; len = 0; } } /* while bp < ep */ } static void cmu_print(bp, length) register u_char *bp; int length; { struct cmu_vend *v; u_char *ep; printf("-cmu"); v = (struct cmu_vend *) bp; if (length < sizeof(*v)) { printf(" |L=%d", length); return; } /* Setup end pointer */ ep = bp + length; /* Subnet mask */ if (v->v_flags & VF_SMASK) { printf(" SM:%s", ipaddr_string(&v->v_smask)); } /* Default gateway */ if (v->v_dgate.s_addr) printf(" GW:%s", ipaddr_string(&v->v_dgate)); /* Domain name servers */ if (v->v_dns1.s_addr) printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); if (v->v_dns2.s_addr) printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); /* IEN-116 name servers */ if (v->v_ins1.s_addr) printf(" INS1:%s", ipaddr_string(&v->v_ins1)); if (v->v_ins2.s_addr) printf(" INS2:%s", ipaddr_string(&v->v_ins2)); /* Time servers */ if (v->v_ts1.s_addr) printf(" TS1:%s", ipaddr_string(&v->v_ts1)); if (v->v_ts2.s_addr) printf(" TS2:%s", ipaddr_string(&v->v_ts2)); } /* * Print out arbitrary, unknown vendor data. */ static void other_print(bp, length) register u_char *bp; int length; { u_char *ep; /* end pointer */ u_char *zp; /* points one past last non-zero byte */ /* Setup end pointer */ ep = bp + length; /* Find the last non-zero byte. */ for (zp = ep; zp > bp; zp--) { if (zp[-1] != 0) break; } /* Print the all-zero case in a compact representation. */ if (zp == bp) { printf("-all-zero"); return; } printf("-unknown"); /* Are there enough trailing zeros to make "00..." worthwhile? */ if (zp + 2 > ep) zp = ep; /* print them all normally */ /* Now just print all the non-zero data. */ while (bp < zp) { printf(".%02X", *bp); bp++; } if (zp < ep) printf(".00..."); return; } static void dump_hex(bp, len) u_char *bp; int len; { while (len > 0) { printf("%02X", *bp); bp++; len--; if (len) printf("."); } } /* * Local Variables: * tab-width: 4 * c-indent-level: 4 * c-argdecl-indent: 4 * c-continued-statement-offset: 4 * c-continued-brace-offset: -4 * c-label-offset: -4 * c-brace-offset: 0 * End: */ Index: head/libexec/ftpd/ftpcmd.y =================================================================== --- head/libexec/ftpd/ftpcmd.y (revision 229779) +++ head/libexec/ftpd/ftpcmd.y (revision 229780) @@ -1,1817 +1,1817 @@ /* * Copyright (c) 1985, 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 */ /* * Grammar for FTP commands. * See RFC 959. */ %{ #ifndef lint #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "pathnames.h" off_t restart_point; static int cmd_type; static int cmd_form; static int cmd_bytesz; static int state; char cbuf[512]; char *fromname = NULL; %} %union { struct { off_t o; int i; } u; char *s; } %token A B C E F I L N P R S T ALL SP CRLF COMMA USER PASS ACCT REIN QUIT PORT PASV TYPE STRU MODE RETR STOR APPE MLFL MAIL MSND MSOM MSAM MRSQ MRCP ALLO REST RNFR RNTO ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM LPRT LPSV EPRT EPSV FEAT UMASK IDLE CHMOD MDFIVE LEXERR NOTIMPL %token STRING %token NUMBER %type check_login octal_number byte_size %type check_login_ro check_login_epsv %type struct_code mode_code type_code form_code %type pathstring pathname password username %type ALL NOTIMPL %start cmd_list %% cmd_list : /* empty */ | cmd_list cmd { if (fromname) free(fromname); fromname = NULL; restart_point = 0; } | cmd_list rcmd ; cmd : USER SP username CRLF { user($3); free($3); } | PASS SP password CRLF { pass($3); free($3); } | PASS CRLF { pass(""); } | PORT check_login SP host_port CRLF { if (epsvall) { reply(501, "No PORT allowed after EPSV ALL."); goto port_done; } if (!$2) goto port_done; if (port_check("PORT") == 1) goto port_done; #ifdef INET6 if ((his_addr.su_family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { /* shoud never happen */ usedefault = 1; reply(500, "Invalid address rejected."); goto port_done; } port_check_v6("pcmd"); #endif port_done: ; } | LPRT check_login SP host_long_port CRLF { if (epsvall) { reply(501, "No LPRT allowed after EPSV ALL."); goto lprt_done; } if (!$2) goto lprt_done; if (port_check("LPRT") == 1) goto lprt_done; #ifdef INET6 if (his_addr.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); goto lprt_done; } if (port_check_v6("LPRT") == 1) goto lprt_done; #endif lprt_done: ; } | EPRT check_login SP STRING CRLF { char delim; char *tmp = NULL; char *p, *q; char *result[3]; struct addrinfo hints; struct addrinfo *res; int i; if (epsvall) { reply(501, "No EPRT allowed after EPSV ALL."); goto eprt_done; } if (!$2) goto eprt_done; memset(&data_dest, 0, sizeof(data_dest)); tmp = strdup($4); if (ftpdebug) syslog(LOG_DEBUG, "%s", tmp); if (!tmp) { fatalerror("not enough core"); /*NOTREACHED*/ } p = tmp; delim = p[0]; p++; memset(result, 0, sizeof(result)); for (i = 0; i < 3; i++) { q = strchr(p, delim); if (!q || *q != delim) { parsefail: reply(500, "Invalid argument, rejected."); if (tmp) free(tmp); usedefault = 1; goto eprt_done; } *q++ = '\0'; result[i] = p; if (ftpdebug) syslog(LOG_DEBUG, "%d: %s", i, p); p = q; } /* some more sanity check */ p = result[0]; while (*p) { if (!isdigit(*p)) goto parsefail; p++; } p = result[2]; while (*p) { if (!isdigit(*p)) goto parsefail; p++; } /* grab address */ memset(&hints, 0, sizeof(hints)); if (atoi(result[0]) == 1) hints.ai_family = PF_INET; #ifdef INET6 else if (atoi(result[0]) == 2) hints.ai_family = PF_INET6; #endif else hints.ai_family = PF_UNSPEC; /*XXX*/ hints.ai_socktype = SOCK_STREAM; i = getaddrinfo(result[1], result[2], &hints, &res); if (i) goto parsefail; memcpy(&data_dest, res->ai_addr, res->ai_addrlen); #ifdef INET6 if (his_addr.su_family == AF_INET6 && data_dest.su_family == AF_INET6) { /* XXX more sanity checks! */ data_dest.su_sin6.sin6_scope_id = his_addr.su_sin6.sin6_scope_id; } #endif free(tmp); tmp = NULL; if (port_check("EPRT") == 1) goto eprt_done; #ifdef INET6 if (his_addr.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); goto eprt_done; } if (port_check_v6("EPRT") == 1) goto eprt_done; #endif eprt_done: free($4); } | PASV check_login CRLF { if (epsvall) reply(501, "No PASV allowed after EPSV ALL."); else if ($2) passive(); } | LPSV check_login CRLF { if (epsvall) reply(501, "No LPSV allowed after EPSV ALL."); else if ($2) long_passive("LPSV", PF_UNSPEC); } | EPSV check_login_epsv SP NUMBER CRLF { if ($2) { int pf; switch ($4.i) { case 1: pf = PF_INET; break; #ifdef INET6 case 2: pf = PF_INET6; break; #endif default: pf = -1; /*junk value*/ break; } long_passive("EPSV", pf); } } | EPSV check_login_epsv SP ALL CRLF { if ($2) { reply(200, "EPSV ALL command successful."); epsvall++; } } | EPSV check_login_epsv CRLF { if ($2) long_passive("EPSV", PF_UNSPEC); } | TYPE check_login SP type_code CRLF { if ($2) { switch (cmd_type) { case TYPE_A: if (cmd_form == FORM_N) { reply(200, "Type set to A."); type = cmd_type; form = cmd_form; } else reply(504, "Form must be N."); break; case TYPE_E: reply(504, "Type E not implemented."); break; case TYPE_I: reply(200, "Type set to I."); type = cmd_type; break; case TYPE_L: #if CHAR_BIT == 8 if (cmd_bytesz == 8) { reply(200, "Type set to L (byte size 8)."); type = cmd_type; } else reply(504, "Byte size must be 8."); #else /* CHAR_BIT == 8 */ UNIMPLEMENTED for CHAR_BIT != 8 #endif /* CHAR_BIT == 8 */ } } } | STRU check_login SP struct_code CRLF { if ($2) { switch ($4) { case STRU_F: reply(200, "STRU F accepted."); break; default: reply(504, "Unimplemented STRU type."); } } } | MODE check_login SP mode_code CRLF { if ($2) { switch ($4) { case MODE_S: reply(200, "MODE S accepted."); break; default: reply(502, "Unimplemented MODE type."); } } } | ALLO check_login SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | ALLO check_login SP NUMBER SP R SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | RETR check_login SP pathname CRLF { if (noretr || (guest && noguestretr)) reply(500, "RETR command disabled."); else if ($2 && $4 != NULL) retrieve(NULL, $4); if ($4 != NULL) free($4); } | STOR check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 0); if ($4 != NULL) free($4); } | APPE check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "a", 0); if ($4 != NULL) free($4); } | NLST check_login CRLF { if ($2) send_file_list("."); } | NLST check_login SP pathstring CRLF { if ($2) send_file_list($4); free($4); } | LIST check_login CRLF { if ($2) retrieve(_PATH_LS " -lgA", ""); } | LIST check_login SP pathstring CRLF { if ($2) retrieve(_PATH_LS " -lgA %s", $4); free($4); } | STAT check_login SP pathname CRLF { if ($2 && $4 != NULL) statfilecmd($4); if ($4 != NULL) free($4); } | STAT check_login CRLF { if ($2) { statcmd(); } } | DELE check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) delete($4); if ($4 != NULL) free($4); } | RNTO check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) { if (fromname) { renamecmd(fromname, $4); free(fromname); fromname = NULL; } else { reply(503, "Bad sequence of commands."); } } if ($4 != NULL) free($4); } | ABOR check_login CRLF { if ($2) reply(225, "ABOR command successful."); } | CWD check_login CRLF { if ($2) { cwd(homedir); } } | CWD check_login SP pathname CRLF { if ($2 && $4 != NULL) cwd($4); if ($4 != NULL) free($4); } | HELP CRLF { help(cmdtab, NULL); } | HELP SP STRING CRLF { char *cp = $3; if (strncasecmp(cp, "SITE", 4) == 0) { cp = $3 + 4; if (*cp == ' ') cp++; if (*cp) help(sitetab, cp); else help(sitetab, NULL); } else help(cmdtab, $3); free($3); } | NOOP CRLF { reply(200, "NOOP command successful."); } | MKD check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) makedir($4); if ($4 != NULL) free($4); } | RMD check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) removedir($4); if ($4 != NULL) free($4); } | PWD check_login CRLF { if ($2) pwd(); } | CDUP check_login CRLF { if ($2) cwd(".."); } | SITE SP HELP CRLF { help(sitetab, NULL); } | SITE SP HELP SP STRING CRLF { help(sitetab, $5); free($5); } | SITE SP MDFIVE check_login SP pathname CRLF { char p[64], *q; if ($4 && $6) { q = MD5File($6, p); if (q != NULL) reply(200, "MD5(%s) = %s", $6, p); else perror_reply(550, $6); } if ($6) free($6); } | SITE SP UMASK check_login CRLF { int oldmask; if ($4) { oldmask = umask(0); (void) umask(oldmask); reply(200, "Current UMASK is %03o.", oldmask); } } | SITE SP UMASK check_login SP octal_number CRLF { int oldmask; if ($4) { if (($6 == -1) || ($6 > 0777)) { reply(501, "Bad UMASK value."); } else { oldmask = umask($6); reply(200, "UMASK set to %03o (was %03o).", $6, oldmask); } } } | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF { if ($4 && ($8 != NULL)) { if (($6 == -1 ) || ($6 > 0777)) reply(501, "Bad mode value."); else if (chmod($8, $6) < 0) perror_reply(550, $8); else reply(200, "CHMOD command successful."); } if ($8 != NULL) free($8); } | SITE SP check_login IDLE CRLF { if ($3) reply(200, "Current IDLE time limit is %d seconds; max %d.", timeout, maxtimeout); } | SITE SP check_login IDLE SP NUMBER CRLF { if ($3) { if ($6.i < 30 || $6.i > maxtimeout) { reply(501, "Maximum IDLE time must be between 30 and %d seconds.", maxtimeout); } else { timeout = $6.i; (void) alarm(timeout); reply(200, "Maximum IDLE time set to %d seconds.", timeout); } } } | STOU check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 1); if ($4 != NULL) free($4); } | FEAT CRLF { lreply(211, "Extensions supported:"); #if 0 /* XXX these two keywords are non-standard */ printf(" EPRT\r\n"); if (!noepsv) printf(" EPSV\r\n"); #endif printf(" MDTM\r\n"); printf(" REST STREAM\r\n"); printf(" SIZE\r\n"); if (assumeutf8) { /* TVFS requires UTF8, see RFC 3659 */ printf(" TVFS\r\n"); printf(" UTF8\r\n"); } reply(211, "End."); } | SYST check_login CRLF { if ($2) { if (hostinfo) #ifdef BSD reply(215, "UNIX Type: L%d Version: BSD-%d", CHAR_BIT, BSD); #else /* BSD */ reply(215, "UNIX Type: L%d", CHAR_BIT); #endif /* BSD */ else reply(215, "UNKNOWN Type: L%d", CHAR_BIT); } } /* * SIZE is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return size of file in a format suitable for * using with RESTART (we just count bytes). */ | SIZE check_login SP pathname CRLF { if ($2 && $4 != NULL) sizecmd($4); if ($4 != NULL) free($4); } /* * MDTM is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return modification time of file as an ISO 3307 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx * where xxx is the fractional second (of any precision, * not necessarily 3 digits) */ | MDTM check_login SP pathname CRLF { if ($2 && $4 != NULL) { struct stat stbuf; if (stat($4, &stbuf) < 0) perror_reply(550, $4); else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", $4); } else { struct tm *t; t = gmtime(&stbuf.st_mtime); reply(213, "%04d%02d%02d%02d%02d%02d", 1900 + t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); } } if ($4 != NULL) free($4); } | QUIT CRLF { reply(221, "Goodbye."); dologout(0); } | NOTIMPL { nack($1); } | error { yyclearin; /* discard lookahead data */ yyerrok; /* clear error condition */ state = CMD; /* reset lexer state */ } ; rcmd : RNFR check_login_ro SP pathname CRLF { restart_point = 0; if ($2 && $4) { if (fromname) free(fromname); fromname = NULL; if (renamefrom($4)) fromname = $4; else free($4); } else if ($4) { free($4); } } | REST check_login SP NUMBER CRLF { if ($2) { if (fromname) free(fromname); fromname = NULL; restart_point = $4.o; reply(350, "Restarting at %jd. %s", (intmax_t)restart_point, "Send STORE or RETRIEVE to initiate transfer."); } } ; username : STRING ; password : /* empty */ { $$ = (char *)calloc(1, sizeof(char)); } | STRING ; byte_size : NUMBER { $$ = $1.i; } ; host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; data_dest.su_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_sin.sin_port; p[0] = $9.i; p[1] = $11.i; a = (char *)&data_dest.su_sin.sin_addr; a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; } ; host_long_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_len = sizeof(struct sockaddr_in6); data_dest.su_family = AF_INET6; p = (char *)&data_dest.su_port; p[0] = $39.i; p[1] = $41.i; a = (char *)&data_dest.su_sin6.sin6_addr; a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; if (his_addr.su_family == AF_INET6) { /* XXX more sanity checks! */ data_dest.su_sin6.sin6_scope_id = his_addr.su_sin6.sin6_scope_id; } if ($1.i != 6 || $3.i != 16 || $37.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_port; p[0] = $15.i; p[1] = $17.i; a = (char *)&data_dest.su_sin.sin_addr; a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; if ($1.i != 4 || $3.i != 4 || $13.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } ; form_code : N { $$ = FORM_N; } | T { $$ = FORM_T; } | C { $$ = FORM_C; } ; type_code : A { cmd_type = TYPE_A; cmd_form = FORM_N; } | A SP form_code { cmd_type = TYPE_A; cmd_form = $3; } | E { cmd_type = TYPE_E; cmd_form = FORM_N; } | E SP form_code { cmd_type = TYPE_E; cmd_form = $3; } | I { cmd_type = TYPE_I; } | L { cmd_type = TYPE_L; cmd_bytesz = CHAR_BIT; } | L SP byte_size { cmd_type = TYPE_L; cmd_bytesz = $3; } /* this is for a bug in the BBN ftp */ | L byte_size { cmd_type = TYPE_L; cmd_bytesz = $2; } ; struct_code : F { $$ = STRU_F; } | R { $$ = STRU_R; } | P { $$ = STRU_P; } ; mode_code : S { $$ = MODE_S; } | B { $$ = MODE_B; } | C { $$ = MODE_C; } ; pathname : pathstring { if (logged_in && $1) { char *p; /* * Expand ~user manually since glob(3) * will return the unexpanded pathname * if the corresponding file/directory * doesn't exist yet. Using sole glob(3) * would break natural commands like * MKD ~user/newdir * or * RNTO ~/newfile */ if ((p = exptilde($1)) != NULL) { $$ = expglob(p); free(p); } else $$ = NULL; free($1); } else $$ = $1; } ; pathstring : STRING ; octal_number : NUMBER { int ret, dec, multby, digit; /* * Convert a number that was read as decimal number * to what it would be if it had been read as octal. */ dec = $1.i; multby = 1; ret = 0; while (dec) { digit = dec%10; if (digit > 7) { ret = -1; break; } ret += digit * multby; multby *= 8; dec /= 10; } $$ = ret; } ; check_login : /* empty */ { $$ = check_login1(); } ; check_login_epsv : /* empty */ { if (noepsv) { reply(500, "EPSV command disabled."); $$ = 0; } else $$ = check_login1(); } ; check_login_ro : /* empty */ { if (readonly) { reply(550, "Permission denied."); $$ = 0; } else $$ = check_login1(); } ; %% #define CMD 0 /* beginning of command */ #define ARGS 1 /* expect miscellaneous arguments */ #define STR1 2 /* expect SP followed by STRING */ #define STR2 3 /* expect STRING */ #define OSTR 4 /* optional SP then STRING */ #define ZSTR1 5 /* optional SP then optional STRING */ #define ZSTR2 6 /* optional STRING after SP */ #define SITECMD 7 /* SITE command */ #define NSTR 8 /* Number followed by a string */ #define MAXGLOBARGS 1000 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ struct tab { char *name; short token; short state; short implemented; /* 1 if command is implemented */ char *help; }; struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, "[ [password]]" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4, b5" }, { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2..." }, { "EPRT", EPRT, STR1, 1, " |af|addr|port|" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, { "EPSV", EPSV, ARGS, 1, "[ af|ALL]" }, { "TYPE", TYPE, ARGS, 1, " { A | E | I | L }" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, { "RETR", RETR, STR1, 1, " file-name" }, { "STOR", STOR, STR1, 1, " file-name" }, { "APPE", APPE, STR1, 1, " file-name" }, { "MLFL", MLFL, OSTR, 0, "(mail file)" }, { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, { "REST", REST, ARGS, 1, " offset (restart command)" }, { "RNFR", RNFR, STR1, 1, " file-name" }, { "RNTO", RNTO, STR1, 1, " file-name" }, { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, { "DELE", DELE, STR1, 1, " file-name" }, { "CWD", CWD, OSTR, 1, "[ directory-name ]" }, { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, { "LIST", LIST, OSTR, 1, "[ path-name ]" }, { "NLST", NLST, OSTR, 1, "[ path-name ]" }, { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, { "STAT", STAT, OSTR, 1, "[ path-name ]" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { "NOOP", NOOP, ARGS, 1, "" }, { "MKD", MKD, STR1, 1, " path-name" }, { "XMKD", MKD, STR1, 1, " path-name" }, { "RMD", RMD, STR1, 1, " path-name" }, { "XRMD", RMD, STR1, 1, " path-name" }, { "PWD", PWD, ARGS, 1, "(return current directory)" }, { "XPWD", PWD, ARGS, 1, "(return current directory)" }, { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "STOU", STOU, STR1, 1, " file-name" }, { "SIZE", SIZE, OSTR, 1, " path-name" }, { "MDTM", MDTM, OSTR, 1, " path-name" }, { NULL, 0, 0, 0, 0 } }; struct tab sitetab[] = { { "MD5", MDFIVE, STR1, 1, "[ file-name ]" }, { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { NULL, 0, 0, 0, 0 } }; static char *copy(char *); static char *expglob(char *); static char *exptilde(char *); static void help(struct tab *, char *); static struct tab * lookup(struct tab *, char *); static int port_check(const char *); #ifdef INET6 static int port_check_v6(const char *); #endif static void sizecmd(char *); static void toolong(int); #ifdef INET6 static void v4map_data_dest(void); #endif static int yylex(void); static struct tab * lookup(struct tab *p, char *cmd) { for (; p->name != NULL; p++) if (strcmp(cmd, p->name) == 0) return (p); return (0); } #include /* * getline - a hacked up version of fgets to ignore TELNET escape codes. */ int getline(char *s, int n, FILE *iop) { int c; register char *cs; sigset_t sset, osset; cs = s; /* tmpline may contain saved command from urgent mode interruption */ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { *cs++ = tmpline[c]; if (tmpline[c] == '\n') { *cs++ = '\0'; if (ftpdebug) syslog(LOG_DEBUG, "command: %s", s); tmpline[0] = '\0'; return(0); } if (c == 0) tmpline[0] = '\0'; } /* SIGURG would interrupt stdio if not blocked during the read loop */ sigemptyset(&sset); sigaddset(&sset, SIGURG); sigprocmask(SIG_BLOCK, &sset, &osset); while ((c = getc(iop)) != EOF) { c &= 0377; if (c == IAC) { if ((c = getc(iop)) == EOF) goto got_eof; c &= 0377; switch (c) { case WILL: case WONT: if ((c = getc(iop)) == EOF) goto got_eof; printf("%c%c%c", IAC, DONT, 0377&c); (void) fflush(stdout); continue; case DO: case DONT: if ((c = getc(iop)) == EOF) goto got_eof; printf("%c%c%c", IAC, WONT, 0377&c); (void) fflush(stdout); continue; case IAC: break; default: continue; /* ignore command */ } } *cs++ = c; if (--n <= 0) { /* * If command doesn't fit into buffer, discard the * rest of the command and indicate truncation. * This prevents the command to be split up into * multiple commands. */ while (c != '\n' && (c = getc(iop)) != EOF) ; return (-2); } if (c == '\n') break; } got_eof: sigprocmask(SIG_SETMASK, &osset, NULL); if (c == EOF && cs == s) return (-1); *cs++ = '\0'; if (ftpdebug) { if (!guest && strncasecmp("pass ", s, 5) == 0) { /* Don't syslog passwords */ syslog(LOG_DEBUG, "command: %.5s ???", s); } else { register char *cp; register int len; /* Don't syslog trailing CR-LF */ len = strlen(s); cp = s + len - 1; while (cp >= s && (*cp == '\n' || *cp == '\r')) { --cp; --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); } } return (0); } static void toolong(int signo) { reply(421, "Timeout (%d seconds): closing control connection.", timeout); if (logging) syslog(LOG_INFO, "User %s timed out after %d seconds", (pw ? pw -> pw_name : "unknown"), timeout); dologout(1); } static int yylex(void) { static int cpos; char *cp, *cp2; struct tab *p; int n; char c; for (;;) { switch (state) { case CMD: (void) signal(SIGALRM, toolong); (void) alarm(timeout); n = getline(cbuf, sizeof(cbuf)-1, stdin); if (n == -1) { reply(221, "You could at least say goodbye."); dologout(0); } else if (n == -2) { reply(500, "Command too long."); (void) alarm(0); continue; } (void) alarm(0); #ifdef SETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) setproctitle("%s: %s", proctitle, cbuf); #endif /* SETPROCTITLE */ if ((cp = strchr(cbuf, '\r'))) { *cp++ = '\n'; *cp = '\0'; } if ((cp = strpbrk(cbuf, " \n"))) cpos = cp - cbuf; if (cpos == 0) cpos = 4; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cbuf); p = lookup(cmdtab, cbuf); cbuf[cpos] = c; if (p != 0) { yylval.s = p->name; if (!p->implemented) return (NOTIMPL); /* state remains CMD */ state = p->state; return (p->token); } break; case SITECMD: if (cbuf[cpos] == ' ') { cpos++; return (SP); } cp = &cbuf[cpos]; if ((cp2 = strpbrk(cp, " \n"))) cpos = cp2 - cbuf; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cp); p = lookup(sitetab, cp); cbuf[cpos] = c; if (guest == 0 && p != 0) { yylval.s = p->name; if (!p->implemented) { state = CMD; return (NOTIMPL); } state = p->state; return (p->token); } state = CMD; break; case ZSTR1: case OSTR: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR1: dostr1: if (cbuf[cpos] == ' ') { cpos++; state = state == OSTR ? STR2 : state+1; return (SP); } break; case ZSTR2: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR2: cp = &cbuf[cpos]; n = strlen(cp); cpos += n - 1; /* * Make sure the string is nonempty and \n terminated. */ if (n > 1 && cbuf[cpos] == '\n') { cbuf[cpos] = '\0'; yylval.s = copy(cp); cbuf[cpos] = '\n'; state = ARGS; return (STRING); } break; case NSTR: if (cbuf[cpos] == ' ') { cpos++; return (SP); } if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.u.i = atoi(cp); cbuf[cpos] = c; state = STR1; return (NUMBER); } state = STR1; goto dostr1; case ARGS: if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.u.i = atoi(cp); yylval.u.o = strtoull(cp, NULL, 10); cbuf[cpos] = c; return (NUMBER); } if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 && !isalnum(cbuf[cpos + 3])) { cpos += 3; return ALL; } switch (cbuf[cpos++]) { case '\n': state = CMD; return (CRLF); case ' ': return (SP); case ',': return (COMMA); case 'A': case 'a': return (A); case 'B': case 'b': return (B); case 'C': case 'c': return (C); case 'E': case 'e': return (E); case 'F': case 'f': return (F); case 'I': case 'i': return (I); case 'L': case 'l': return (L); case 'N': case 'n': return (N); case 'P': case 'p': return (P); case 'R': case 'r': return (R); case 'S': case 's': return (S); case 'T': case 't': return (T); } break; default: fatalerror("Unknown state in scanner."); } state = CMD; return (LEXERR); } } void upper(char *s) { while (*s != '\0') { if (islower(*s)) *s = toupper(*s); s++; } } static char * copy(char *s) { char *p; p = malloc(strlen(s) + 1); if (p == NULL) fatalerror("Ran out of memory."); (void) strcpy(p, s); return (p); } static void help(struct tab *ctab, char *s) { struct tab *c; int width, NCMDS; char *type; if (ctab == sitetab) type = "SITE "; else type = ""; width = 0, NCMDS = 0; for (c = ctab; c->name != NULL; c++) { int len = strlen(c->name); if (len > width) width = len; NCMDS++; } width = (width + 8) &~ 7; if (s == 0) { int i, j, w; int columns, lines; lreply(214, "The following %scommands are recognized %s.", type, "(* =>'s unimplemented)"); columns = 76 / width; if (columns == 0) columns = 1; lines = (NCMDS + columns - 1) / columns; for (i = 0; i < lines; i++) { printf(" "); for (j = 0; j < columns; j++) { c = ctab + j * lines + i; printf("%s%c", c->name, c->implemented ? ' ' : '*'); if (c + lines >= &ctab[NCMDS]) break; w = strlen(c->name) + 1; while (w < width) { putchar(' '); w++; } } printf("\r\n"); } (void) fflush(stdout); if (hostinfo) reply(214, "Direct comments to ftp-bugs@%s.", hostname); else reply(214, "End."); return; } upper(s); c = lookup(ctab, s); if (c == NULL) { reply(502, "Unknown command %s.", s); return; } if (c->implemented) reply(214, "Syntax: %s%s %s", type, c->name, c->help); else reply(214, "%s%-*s\t%s; unimplemented.", type, width, c->name, c->help); } static void sizecmd(char *filename) { switch (type) { case TYPE_L: case TYPE_I: { struct stat stbuf; if (stat(filename, &stbuf) < 0) perror_reply(550, filename); else if (!S_ISREG(stbuf.st_mode)) reply(550, "%s: not a plain file.", filename); else reply(213, "%jd", (intmax_t)stbuf.st_size); break; } case TYPE_A: { FILE *fin; int c; off_t count; struct stat stbuf; fin = fopen(filename, "r"); if (fin == NULL) { perror_reply(550, filename); return; } if (fstat(fileno(fin), &stbuf) < 0) { perror_reply(550, filename); (void) fclose(fin); return; } else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", filename); (void) fclose(fin); return; } else if (stbuf.st_size > MAXASIZE) { reply(550, "%s: too large for type A SIZE.", filename); (void) fclose(fin); return; } count = 0; while((c=getc(fin)) != EOF) { if (c == '\n') /* will get expanded to \r\n */ count++; count++; } (void) fclose(fin); reply(213, "%jd", (intmax_t)count); break; } default: reply(504, "SIZE not implemented for type %s.", typenames[type]); } } /* Return 1, if port check is done. Return 0, if not yet. */ static int port_check(const char *pcmd) { if (his_addr.su_family == AF_INET) { if (data_dest.su_family != AF_INET) { usedefault = 1; reply(500, "Invalid address rejected."); return 1; } if (paranoid && ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || memcmp(&data_dest.su_sin.sin_addr, &his_addr.su_sin.sin_addr, sizeof(data_dest.su_sin.sin_addr)))) { usedefault = 1; reply(500, "Illegal PORT range rejected."); } else { usedefault = 0; if (pdata >= 0) { (void) close(pdata); pdata = -1; } reply(200, "%s command successful.", pcmd); } return 1; } return 0; } static int check_login1(void) { if (logged_in) return 1; else { reply(530, "Please login with USER and PASS."); return 0; } } /* * Replace leading "~user" in a pathname by the user's login directory. * Returned string will be in a freshly malloced buffer unless it's NULL. */ static char * exptilde(char *s) { char *p, *q; char *path, *user; struct passwd *ppw; if ((p = strdup(s)) == NULL) return (NULL); if (*p != '~') return (p); user = p + 1; /* skip tilde */ if ((path = strchr(p, '/')) != NULL) *(path++) = '\0'; /* separate ~user from the rest of path */ if (*user == '\0') /* no user specified, use the current user */ user = pw->pw_name; /* read passwd even for the current user since we may be chrooted */ if ((ppw = getpwnam(user)) != NULL) { /* user found, substitute login directory for ~user */ if (path) asprintf(&q, "%s/%s", ppw->pw_dir, path); else q = strdup(ppw->pw_dir); free(p); p = q; } else { /* user not found, undo the damage */ if (path) path[-1] = '/'; } return (p); } /* * Expand glob(3) patterns possibly present in a pathname. * Avoid expanding to a pathname including '\r' or '\n' in order to * not disrupt the FTP protocol. * The expansion found must be unique. - * Return the result as a malloced string, or NULL if an error occured. + * Return the result as a malloced string, or NULL if an error occurred. * * Problem: this production is used for all pathname * processing, but only gives a 550 error reply. * This is a valid reply in some cases but not in others. */ static char * expglob(char *s) { char *p, **pp, *rval; int flags = GLOB_BRACE | GLOB_NOCHECK; int n; glob_t gl; memset(&gl, 0, sizeof(gl)); flags |= GLOB_LIMIT; gl.gl_matchc = MAXGLOBARGS; if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { p = *pp; n++; } if (n == 0) rval = strdup(s); else if (n == 1) rval = strdup(p); else { reply(550, "Wildcard is ambiguous."); rval = NULL; } } else { reply(550, "Wildcard expansion error."); rval = NULL; } globfree(&gl); return (rval); } #ifdef INET6 /* Return 1, if port check is done. Return 0, if not yet. */ static int port_check_v6(const char *pcmd) { if (his_addr.su_family == AF_INET6) { if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) /* Convert data_dest into v4 mapped sockaddr.*/ v4map_data_dest(); if (data_dest.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); return 1; } if (paranoid && ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || memcmp(&data_dest.su_sin6.sin6_addr, &his_addr.su_sin6.sin6_addr, sizeof(data_dest.su_sin6.sin6_addr)))) { usedefault = 1; reply(500, "Illegal PORT range rejected."); } else { usedefault = 0; if (pdata >= 0) { (void) close(pdata); pdata = -1; } reply(200, "%s command successful.", pcmd); } return 1; } return 0; } static void v4map_data_dest(void) { struct in_addr savedaddr; int savedport; if (data_dest.su_family != AF_INET) { usedefault = 1; reply(500, "Invalid address rejected."); return; } savedaddr = data_dest.su_sin.sin_addr; savedport = data_dest.su_port; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); data_dest.su_sin6.sin6_family = AF_INET6; data_dest.su_sin6.sin6_port = savedport; memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], (caddr_t)&savedaddr, sizeof(savedaddr)); } #endif Index: head/libexec/getty/init.c =================================================================== --- head/libexec/getty/init.c (revision 229779) +++ head/libexec/getty/init.c (revision 229780) @@ -1,154 +1,154 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: init.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * Getty table initializations. * * Melbourne getty. */ #include #include "gettytab.h" #include "extern.h" #include "pathnames.h" static char loginmsg[] = "login: "; static char nullstr[] = ""; static char loginprg[] = _PATH_LOGIN; static char datefmt[] = "%+"; struct gettystrs gettystrs[] = { { "nx" }, /* next table */ { "cl" }, /* screen clear characters */ { "im" }, /* initial message */ { "lm", loginmsg }, /* login message */ { "er", &omode.c_cc[VERASE] }, /* erase character */ { "kl", &omode.c_cc[VKILL] }, /* kill character */ { "et", &omode.c_cc[VEOF] }, /* eof chatacter (eot) */ { "pc", nullstr }, /* pad character */ { "tt" }, /* terminal type */ - { "ev" }, /* enviroment */ + { "ev" }, /* environment */ { "lo", loginprg }, /* login program */ { "hn", hostname }, /* host name */ { "he" }, /* host name edit */ { "in", &omode.c_cc[VINTR] }, /* interrupt char */ { "qu", &omode.c_cc[VQUIT] }, /* quit char */ { "xn", &omode.c_cc[VSTART] }, /* XON (start) char */ { "xf", &omode.c_cc[VSTOP] }, /* XOFF (stop) char */ { "bk", &omode.c_cc[VEOL] }, /* brk char (alt \n) */ { "su", &omode.c_cc[VSUSP] }, /* suspend char */ { "ds", &omode.c_cc[VDSUSP] }, /* delayed suspend */ { "rp", &omode.c_cc[VREPRINT] },/* reprint char */ { "fl", &omode.c_cc[VDISCARD] },/* flush output */ { "we", &omode.c_cc[VWERASE] }, /* word erase */ { "ln", &omode.c_cc[VLNEXT] }, /* literal next */ { "Lo" }, /* locale for strftime() */ { "pp" }, /* ppp login program */ { "if" }, /* sysv-like 'issue' filename */ { "ic" }, /* modem init-chat */ { "ac" }, /* modem answer-chat */ { "al" }, /* user to auto-login */ { "df", datefmt}, /* format for strftime() */ { 0 } }; struct gettynums gettynums[] = { { "is" }, /* input speed */ { "os" }, /* output speed */ { "sp" }, /* both speeds */ { "nd" }, /* newline delay */ { "cd" }, /* carriage-return delay */ { "td" }, /* tab delay */ { "fd" }, /* form-feed delay */ { "bd" }, /* backspace delay */ { "to" }, /* timeout */ { "f0" }, /* output flags */ { "f1" }, /* input flags */ { "f2" }, /* user mode flags */ { "pf" }, /* delay before flush at 1st prompt */ { "c0" }, /* output c_flags */ { "c1" }, /* input c_flags */ { "c2" }, /* user mode c_flags */ { "i0" }, /* output i_flags */ { "i1" }, /* input i_flags */ { "i2" }, /* user mode i_flags */ { "l0" }, /* output l_flags */ { "l1" }, /* input l_flags */ { "l2" }, /* user mode l_flags */ { "o0" }, /* output o_flags */ { "o1" }, /* input o_flags */ { "o2" }, /* user mode o_flags */ { "de" }, /* delay before sending 1st prompt */ { "rt" }, /* reset timeout */ { "ct" }, /* chat script timeout */ { "dc" }, /* debug chat script value */ { 0 } }; struct gettyflags gettyflags[] = { { "ht", 0 }, /* has tabs */ { "nl", 1 }, /* has newline char */ { "ep", 0 }, /* even parity */ { "op", 0 }, /* odd parity */ { "ap", 0 }, /* any parity */ { "ec", 1 }, /* no echo */ { "co", 0 }, /* console special */ { "cb", 0 }, /* crt backspace */ { "ck", 0 }, /* crt kill */ { "ce", 0 }, /* crt erase */ { "pe", 0 }, /* printer erase */ { "rw", 1 }, /* don't use raw */ { "xc", 1 }, /* don't ^X ctl chars */ { "lc", 0 }, /* terminal las lower case */ { "uc", 0 }, /* terminal has no lower case */ { "ig", 0 }, /* ignore garbage */ { "ps", 0 }, /* do port selector speed select */ { "hc", 1 }, /* don't set hangup on close */ { "ub", 0 }, /* unbuffered output */ { "ab", 0 }, /* auto-baud detect with '\r' */ { "dx", 0 }, /* set decctlq */ { "np", 0 }, /* no parity at all (8bit chars) */ { "mb", 0 }, /* do MDMBUF flow control */ { "hw", 0 }, /* do CTSRTS flow control */ { "nc", 0 }, /* set clocal (no carrier) */ { "pl", 0 }, /* use PPP instead of login(1) */ { 0 } }; Index: head/libexec/lukemftpd/nbsd_pidfile.h =================================================================== --- head/libexec/lukemftpd/nbsd_pidfile.h (revision 229779) +++ head/libexec/lukemftpd/nbsd_pidfile.h (revision 229780) @@ -1,30 +1,30 @@ /* $FreeBSD$ */ #include #include static int pidfile(const char *basename) { struct pidfh *pfh; pid_t otherpid, childpid; if (basename != NULL) { - errx(EX_USAGE, "Need to impliment NetBSD semantics."); + errx(EX_USAGE, "Need to implement NetBSD semantics."); } pfh = pidfile_open(basename, 0644, &otherpid); if (pfh == NULL) { if (errno == EEXIST) { errx(EXIT_FAILURE, "Daemon already running, pid: %jd.", (intmax_t)otherpid); } /* If we cannot create pidfile from other reasons, only warn. */ warn("Cannot open or create pidfile"); return -1; } pidfile_write(pfh); pidfile_close(pfh); return 0; } Index: head/libexec/rbootd/parseconf.c =================================================================== --- head/libexec/rbootd/parseconf.c (revision 229779) +++ head/libexec/rbootd/parseconf.c (revision 229780) @@ -1,362 +1,362 @@ /* * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)parseconf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: parseconf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ParseConfig -- parse the config file into linked list of clients. ** ** Parameters: ** None. ** ** Returns: ** 1 on success, 0 otherwise. ** ** Side Effects: ** - Linked list of clients will be (re)allocated. ** ** Warnings: ** - GetBootFiles() must be called before this routine ** to create a linked list of default boot files. */ int ParseConfig(void) { FILE *fp; CLIENT *client; u_int8_t *addr; char line[C_LINELEN]; char *cp, *bcp; int i, j; int omask, linecnt = 0; if (BootAny) /* ignore config file */ return(1); FreeClients(); /* delete old list of clients */ if ((fp = fopen(ConfigFile, "r")) == NULL) { syslog(LOG_ERR, "ParseConfig: can't open config file (%s)", ConfigFile); return(0); } /* * We've got to block SIGHUP to prevent reconfiguration while * dealing with the linked list of Clients. This can be done * when actually linking the new client into the list, but * this could have unexpected results if the server was HUP'd * whilst reconfiguring. Hence, it is done here. */ omask = sigblock(sigmask(SIGHUP)); /* * GETSTR positions `bcp' at the start of the current token, * and null terminates it. `cp' is positioned at the start * of the next token. spaces & commas are separators. */ #define GETSTR while (isspace(*cp) || *cp == ',') cp++; \ bcp = cp; \ while (*cp && *cp!=',' && !isspace(*cp)) cp++; \ if (*cp) *cp++ = '\0' /* * For each line, parse it into a new CLIENT struct. */ while (fgets(line, C_LINELEN, fp) != NULL) { linecnt++; /* line counter */ if (*line == '\0' || *line == '#') /* ignore comment */ continue; if ((cp = strchr(line,'#')) != NULL) /* trash comments */ *cp = '\0'; cp = line; /* init `cp' */ GETSTR; /* get RMP addr */ if (bcp == cp) /* all delimiters */ continue; /* * Get an RMP address from a string. Abort on failure. */ if ((addr = ParseAddr(bcp)) == NULL) { syslog(LOG_ERR, - "ParseConfig: line %d: cant parse <%s>", + "ParseConfig: line %d: can't parse <%s>", linecnt, bcp); continue; } if ((client = NewClient(addr)) == NULL) /* alloc new client */ continue; GETSTR; /* get first file */ /* * If no boot files are spec'd, use the default list. * Otherwise, validate each file (`bcp') against the * list of boot-able files. */ i = 0; if (bcp == cp) /* no files spec'd */ for (; i < C_MAXFILE && BootFiles[i] != NULL; i++) client->files[i] = BootFiles[i]; else { do { /* * For each boot file spec'd, make sure it's * in our list. If so, include a pointer to * it in the CLIENT's list of boot files. */ for (j = 0; ; j++) { if (j==C_MAXFILE||BootFiles[j]==NULL) { syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)", linecnt, bcp); break; } if (STREQN(BootFiles[j], bcp)) { if (i < C_MAXFILE) client->files[i++] = BootFiles[j]; else syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)", linecnt, bcp); break; } } GETSTR; /* get next file */ } while (bcp != cp); /* * Restricted list of boot files were spec'd, * however, none of them were found. Since we - * apparently cant let them boot "just anything", + * apparently can't let them boot "just anything", * the entire record is invalidated. */ if (i == 0) { FreeClient(client); continue; } } /* * Link this client into the linked list of clients. * SIGHUP has already been blocked. */ if (Clients) client->next = Clients; Clients = client; } (void) fclose(fp); /* close config file */ (void) sigsetmask(omask); /* reset signal mask */ return(1); /* return success */ } /* ** ParseAddr -- Parse a string containing an RMP address. ** ** This routine is fairly liberal at parsing an RMP address. The ** address must contain 6 octets consisting of between 0 and 2 hex ** chars (upper/lower case) separated by colons. If two colons are ** together (e.g. "::", the octet between them is recorded as being ** zero. Hence, the following addrs are all valid and parse to the ** same thing: ** ** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD ** ** For clarity, an RMP address is really an Ethernet address, but ** since the HP boot code uses IEEE 802.3, it's really an IEEE ** 802.3 address. Of course, all of these are identical. ** ** Parameters: ** str - string representation of an RMP address. ** ** Returns: ** pointer to a static array of RMP_ADDRLEN bytes. ** ** Side Effects: ** None. ** ** Warnings: ** - The return value points to a static buffer; it must ** be copied if it's to be saved. */ u_int8_t * ParseAddr(char *str) { static u_int8_t addr[RMP_ADDRLEN]; char *cp; unsigned i; int part, subpart; memset((char *)&addr[0], 0, RMP_ADDRLEN); /* zero static buffer */ part = subpart = 0; for (cp = str; *cp; cp++) { /* * A colon (`:') must be used to delimit each octet. */ if (*cp == ':') { if (++part == RMP_ADDRLEN) /* too many parts */ return(NULL); subpart = 0; continue; } /* * Convert hex character to an integer. */ if (isdigit(*cp)) i = *cp - '0'; else { i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10; if (i < 10 || i > 15) /* not a hex char */ return(NULL); } if (subpart++) { if (subpart > 2) /* too many hex chars */ return(NULL); addr[part] <<= 4; } addr[part] |= i; } if (part != (RMP_ADDRLEN-1)) /* too few parts */ return(NULL); return(&addr[0]); } /* ** GetBootFiles -- record list of files in current (boot) directory. ** ** Parameters: ** None. ** ** Returns: ** Number of boot files on success, 0 on failure. ** ** Side Effects: ** Strings in `BootFiles' are freed/allocated. ** ** Warnings: ** - After this routine is called, ParseConfig() must be ** called to re-order it's list of boot file pointers. */ int GetBootFiles(void) { DIR *dfd; struct stat statb; struct dirent *dp; int i; /* * Free the current list of boot files. */ for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) { FreeStr(BootFiles[i]); BootFiles[i] = NULL; } /* * Open current directory to read boot file names. */ if ((dfd = opendir(".")) == NULL) { /* open BootDir */ syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n", BootDir); return(0); } /* * Read each boot file name and allocate space for it in the * list of boot files (BootFiles). All boot files read after * C_MAXFILE will be ignored. */ i = 0; for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) { if (stat(dp->d_name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFREG) continue; if (i == C_MAXFILE) syslog(LOG_ERR, "GetBootFiles: too many boot files (%s ignored)", dp->d_name); else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL) i++; } (void) closedir(dfd); /* close BootDir */ - if (i == 0) /* cant find any boot files */ + if (i == 0) /* can't find any boot files */ syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir); return(i); } Index: head/libexec/rbootd/rbootd.c =================================================================== --- head/libexec/rbootd/rbootd.c (revision 229779) +++ head/libexec/rbootd/rbootd.c (revision 229780) @@ -1,454 +1,454 @@ /* * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)rbootd.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: rbootd.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rbootd.c 8.1 (Berkeley) 6/4/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" static void usage(void); int main(int argc, char *argv[]) { int c, fd, omask, maxfds; fd_set rset; /* * Close any open file descriptors. * Temporarily leave stdin & stdout open for `-d', * and stderr open for any pre-syslog error messages. */ { int i, nfds = getdtablesize(); for (i = 0; i < nfds; i++) if (i != fileno(stdin) && i != fileno(stdout) && i != fileno(stderr)) (void) close(i); } /* * Parse any arguments. */ while ((c = getopt(argc, argv, "adi:")) != -1) switch(c) { case 'a': BootAny++; break; case 'd': DebugFlg++; break; case 'i': IntfName = optarg; break; default: usage(); } for (; optind < argc; optind++) { if (ConfigFile == NULL) ConfigFile = argv[optind]; else { warnx("too many config files (`%s' ignored)", argv[optind]); } } if (ConfigFile == NULL) /* use default config file */ ConfigFile = DfltConfig; if (DebugFlg) { DbgFp = stdout; /* output to stdout */ (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */ (void) signal(SIGUSR2, SIG_IGN); (void) fclose(stderr); /* finished with it */ } else { if (daemon(0, 0)) err(1, "can't detach from terminal"); (void) signal(SIGUSR1, DebugOn); (void) signal(SIGUSR2, DebugOff); } openlog("rbootd", LOG_PID, LOG_DAEMON); /* * If no interface was specified, get one now. * * This is convoluted because we want to get the default interface * name for the syslog("restarted") message. If BpfGetIntfName() * runs into an error, it will return a syslog-able error message * (in `errmsg') which will be displayed here. */ if (IntfName == NULL) { char *errmsg; if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) { /* Backslash to avoid trigraph '??)'. */ syslog(LOG_NOTICE, "restarted (?\?)"); /* BpfGetIntfName() returns safe names, using %m */ syslog(LOG_ERR, "%s", errmsg); Exit(0); } } syslog(LOG_NOTICE, "restarted (%s)", IntfName); (void) signal(SIGHUP, ReConfig); (void) signal(SIGINT, Exit); (void) signal(SIGTERM, Exit); /* * Grab our host name and pid. */ if (gethostname(MyHost, MAXHOSTNAMELEN - 1) < 0) { syslog(LOG_ERR, "gethostname: %m"); Exit(0); } MyHost[MAXHOSTNAMELEN - 1] = '\0'; MyPid = getpid(); /* * Write proc's pid to a file. */ { FILE *fp; if ((fp = fopen(PidFile, "w")) != NULL) { (void) fprintf(fp, "%d\n", (int) MyPid); (void) fclose(fp); } else { syslog(LOG_WARNING, "fopen: failed (%s)", PidFile); } } /* * All boot files are relative to the boot directory, we might * as well chdir() there to make life easier. */ if (chdir(BootDir) < 0) { syslog(LOG_ERR, "chdir: %m (%s)", BootDir); Exit(0); } /* * Initial configuration. */ omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */ if (GetBootFiles() == 0) /* get list of boot files */ Exit(0); if (ParseConfig() == 0) /* parse config file */ Exit(0); /* * Open and initialize a BPF device for the appropriate interface. * If an error is encountered, a message is displayed and Exit() * is called. */ fd = BpfOpen(); (void) sigsetmask(omask); /* allow reconfig's */ /* * Main loop: receive a packet, determine where it came from, * and if we service this host, call routine to handle request. */ maxfds = fd + 1; FD_ZERO(&rset); FD_SET(fd, &rset); for (;;) { struct timeval timeout; fd_set r; int nsel; r = rset; - if (RmpConns == NULL) { /* timeout isnt necessary */ + if (RmpConns == NULL) { /* timeout isn't necessary */ nsel = select(maxfds, &r, NULL, NULL, NULL); } else { timeout.tv_sec = RMP_TIMEOUT; timeout.tv_usec = 0; nsel = select(maxfds, &r, NULL, NULL, &timeout); } if (nsel < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "select: %m"); Exit(0); } else if (nsel == 0) { /* timeout */ DoTimeout(); /* clear stale conns */ continue; } if (FD_ISSET(fd, &r)) { RMPCONN rconn; CLIENT *client, *FindClient(); int doread = 1; while (BpfRead(&rconn, doread)) { doread = 0; if (DbgFp != NULL) /* display packet */ DispPkt(&rconn,DIR_RCVD); omask = sigblock(sigmask(SIGHUP)); /* * If we do not restrict service, set the * client to NULL (ProcessPacket() handles * this). Otherwise, check that we can * service this host; if not, log a message * and ignore the packet. */ if (BootAny) { client = NULL; } else if ((client=FindClient(&rconn))==NULL) { syslog(LOG_INFO, "%s: boot packet ignored", EnetStr(&rconn)); (void) sigsetmask(omask); continue; } ProcessPacket(&rconn,client); (void) sigsetmask(omask); } } } } static void usage(void) { fprintf(stderr, "usage: rbootd [-ad] [-i interface] [config_file]\n"); exit (1); } /* ** DoTimeout -- Free any connections that have timed out. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Timed out connections in `RmpConns' will be freed. */ void DoTimeout(void) { RMPCONN *rtmp; struct timeval now; (void) gettimeofday(&now, (struct timezone *)0); /* * For each active connection, if RMP_TIMEOUT seconds have passed * since the last packet was sent, delete the connection. */ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) { syslog(LOG_WARNING, "%s: connection timed out (%u)", EnetStr(rtmp), rtmp->rmp.r_type); RemoveConn(rtmp); } } /* ** FindClient -- Find client associated with a packet. ** ** Parameters: ** rconn - the new packet. ** ** Returns: ** Pointer to client info if found, NULL otherwise. ** ** Side Effects: ** None. ** ** Warnings: ** - This routine must be called with SIGHUP blocked since ** a reconfigure can invalidate the information returned. */ CLIENT * FindClient(RMPCONN *rconn) { CLIENT *ctmp; for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next) if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0) break; return(ctmp); } /* ** Exit -- Log an error message and exit. ** ** Parameters: ** sig - caught signal (or zero if not dying on a signal). ** ** Returns: ** Does not return. ** ** Side Effects: ** - This process ceases to exist. */ void Exit(int sig) { if (sig > 0) syslog(LOG_ERR, "going down on signal %d", sig); else syslog(LOG_ERR, "going down with fatal error"); BpfClose(); exit(1); } /* ** ReConfig -- Get new list of boot files and reread config files. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - All active connections are dropped. ** - List of boot-able files is changed. ** - List of clients is changed. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void ReConfig(int signo __unused) { syslog(LOG_NOTICE, "reconfiguring boot server"); FreeConns(); if (GetBootFiles() == 0) Exit(0); if (ParseConfig() == 0) Exit(0); } /* ** DebugOff -- Turn off debugging. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Debug file is closed. */ void DebugOff(int signo __unused) { if (DbgFp != NULL) (void) fclose(DbgFp); DbgFp = NULL; } /* ** DebugOn -- Turn on debugging. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Debug file is opened/truncated if not already opened, ** otherwise do nothing. */ void DebugOn(int signo __unused) { if (DbgFp == NULL) { if ((DbgFp = fopen(DbgFile, "w")) == NULL) syslog(LOG_ERR, "can't open debug file (%s)", DbgFile); } } Index: head/libexec/rbootd/rmpproto.c =================================================================== --- head/libexec/rbootd/rmpproto.c (revision 229779) +++ head/libexec/rbootd/rmpproto.c (revision 229780) @@ -1,587 +1,587 @@ /* * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: rmpproto.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ProcessPacket -- determine packet type and do what's required. ** ** An RMP BOOT packet has been received. Look at the type field ** and process Boot Requests, Read Requests, and Boot Complete ** packets. Any other type will be dropped with a warning msg. ** ** Parameters: ** rconn - the new connection ** client - list of files available to this host ** ** Returns: ** Nothing. ** ** Side Effects: ** - If this is a valid boot request, it will be added to ** the linked list of outstanding requests (RmpConns). ** - If this is a valid boot complete, its associated ** entry in RmpConns will be deleted. ** - Also, unless we run out of memory, a reply will be ** sent to the host that sent the packet. */ void ProcessPacket(RMPCONN *rconn, CLIENT *client) { struct rmp_packet *rmp; RMPCONN *rconnout; rmp = &rconn->rmp; /* cache pointer to RMP packet */ switch(rmp->r_type) { /* do what we came here to do */ case RMP_BOOT_REQ: /* boot request */ if ((rconnout = NewConn(rconn)) == NULL) return; /* * If the Session ID is 0xffff, this is a "probe" * packet and we do not want to add the connection * to the linked list of active connections. There * are two types of probe packets, if the Sequence * Number is 0 they want to know our host name, o/w * they want the name of the file associated with * the number spec'd by the Sequence Number. * * If this is an actual boot request, open the file * and send a reply. If SendBootRepl() does not * return 0, add the connection to the linked list * of active connections, otherwise delete it since * an error was encountered. */ if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { if (WORDZE(rmp->r_brq.rmp_seqno)) (void) SendServerID(rconnout); else (void) SendFileNo(rmp, rconnout, client? client->files: BootFiles); FreeConn(rconnout); } else { if (SendBootRepl(rmp, rconnout, client? client->files: BootFiles)) AddConn(rconnout); else FreeConn(rconnout); } break; case RMP_BOOT_REPL: /* boot reply (not valid) */ syslog(LOG_WARNING, "%s: sent a boot reply", EnetStr(rconn)); break; case RMP_READ_REQ: /* read request */ /* * Send a portion of the boot file. */ (void) SendReadRepl(rconn); break; case RMP_READ_REPL: /* read reply (not valid) */ syslog(LOG_WARNING, "%s: sent a read reply", EnetStr(rconn)); break; case RMP_BOOT_DONE: /* boot complete */ /* * Remove the entry from the linked list of active * connections. */ (void) BootDone(rconn); break; default: /* unknown RMP packet type */ syslog(LOG_WARNING, "%s: unknown packet type (%u)", EnetStr(rconn), rmp->r_type); } } /* ** SendServerID -- send our host name to who ever requested it. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendServerID(RMPCONN *rconn) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; rpl->r_brpl.rmp_retcode = RMP_E_OKAY; ZEROWORD(rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ /* * Copy our host name into the reply packet incrementing the * length as we go. Stop at RMP_HOSTLEN or the first dot. */ src = MyHost; dst = (char *) &rpl->r_brpl.rmp_flnm; for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { if (*src == '.' || *src == '\0') break; *dst++ = *src++; } rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendFileNo -- send the name of a bootable file to the requester. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; int i; GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; PUTWORD(i, rpl->r_brpl.rmp_seqno); i--; rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ *size = 0; /* init length to zero */ /* * Copy the file name into the reply packet incrementing the * length as we go. Stop at end of string or when RMPBOOTDATA * characters have been copied. Also, set return code to * indicate success or "no more files". */ if (i < C_MAXFILE && filelist[i] != NULL) { src = filelist[i]; dst = (char *)&rpl->r_brpl.rmp_flnm; for (; *src && *size < RMPBOOTDATA; (*size)++) { if (*src == '\0') break; *dst++ = *src++; } rpl->r_brpl.rmp_retcode = RMP_E_OKAY; } else rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendBootRepl -- open boot file and respond to boot request. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { int retval; char *filename, filepath[RMPBOOTDATA+1]; RMPCONN *oldconn; struct rmp_packet *rpl; char *src, *dst1, *dst2; u_int8_t i; /* * If another connection already exists, delete it since we * are obviously starting again. */ if ((oldconn = FindConn(rconn)) != NULL) { syslog(LOG_WARNING, "%s: dropping existing connection", EnetStr(oldconn)); RemoveConn(oldconn); } rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = htons(GenSessID()); rpl->r_brpl.rmp_version = htons(RMP_VERSION); rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; /* * Copy file name to `filepath' string, and into reply packet. */ src = &req->r_brq.rmp_flnm; dst1 = filepath; dst2 = &rpl->r_brpl.rmp_flnm; for (i = 0; i < req->r_brq.rmp_flnmsize; i++) *dst1++ = *dst2++ = *src++; *dst1 = '\0'; /* * If we are booting HP-UX machines, their secondary loader will * ask for files like "/hp-ux". As a security measure, we do not * allow boot files to lay outside the boot directory (unless they * are purposely link'd out. So, make `filename' become the path- * stripped file name and spoof the client into thinking that it * really got what it wanted. */ filename = (filename = strrchr(filepath,'/'))? ++filename: filepath; /* * Check that this is a valid boot file name. */ for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) if (STREQN(filename, filelist[i])) goto match; /* * Invalid boot file name, set error and send reply packet. */ rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; retval = 0; goto sendpkt; match: /* * This is a valid boot file. Open the file and save the file * descriptor associated with this connection and set success * indication. If the file couldnt be opened, set error: * "no such file or dir" - RMP_E_NOFILE * "file table overflow" - RMP_E_BUSY * "too many open files" - RMP_E_BUSY * anything else - RMP_E_OPENFILE */ if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: RMP_E_OPENFILE; retval = 0; } else { rpl->r_brpl.rmp_retcode = RMP_E_OKAY; retval = 1; } sendpkt: syslog(LOG_INFO, "%s: request to boot %s (%s)", EnetStr(rconn), filename, retval? "granted": "denied"); rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); return (retval & SendPacket(rconn)); } /* ** SendReadRepl -- send a portion of the boot file to the requester. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendReadRepl(RMPCONN *rconn) { int retval = 0; RMPCONN *oldconn; struct rmp_packet *rpl, *req; int size = 0; int madeconn = 0; /* - * Find the old connection. If one doesnt exist, create one only + * Find the old connection. If one doesn't exist, create one only * to return the error code. */ if ((oldconn = FindConn(rconn)) == NULL) { if ((oldconn = NewConn(rconn)) == NULL) return(0); syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", EnetStr(rconn)); madeconn++; } req = &rconn->rmp; /* cache ptr to request packet */ rpl = &oldconn->rmp; /* cache ptr to reply packet */ if (madeconn) { /* no active connection above; abort */ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Make sure Session ID's match. */ if (ntohs(req->r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; retval = 1; goto sendpkt; } /* * If the requester asks for more data than we can fit, * silently clamp the request size down to RMPREADDATA. * * N.B. I do not know if this is "legal", however it seems * to work. This is necessary for bpfwrite() on machines * with MCLBYTES less than 1514. */ if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) req->r_rrq.rmp_size = htons(RMPREADDATA); /* * Position read head on file according to info in request packet. */ GETWORD(req->r_rrq.rmp_offset, size); if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Read data directly into reply packet. */ if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, (int) ntohs(req->r_rrq.rmp_size))) <= 0) { if (size < 0) { syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; } else { rpl->r_rrpl.rmp_retcode = RMP_E_EOF; } retval = 1; goto sendpkt; } /* * Set success indication. */ rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; sendpkt: /* * Set up assorted fields in reply packet. */ rpl->r_rrpl.rmp_type = RMP_READ_REPL; COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ retval &= SendPacket(oldconn); /* send packet */ if (madeconn) /* clean up after ourself */ FreeConn(oldconn); return (retval); } /* ** BootDone -- free up memory allocated for a connection. ** ** Parameters: ** rconn - incoming boot complete packet. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int BootDone(RMPCONN *rconn) { RMPCONN *oldconn; struct rmp_packet *rpl; /* - * If we cant find the connection, ignore the request. + * If we can't find the connection, ignore the request. */ if ((oldconn = FindConn(rconn)) == NULL) { syslog(LOG_ERR, "BootDone: no existing connection (%s)", EnetStr(rconn)); return(0); } rpl = &oldconn->rmp; /* cache ptr to RMP packet */ /* * Make sure Session ID's match. */ if (ntohs(rconn->rmp.r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "BootDone: bad session id (%s)", EnetStr(rconn)); return(0); } RemoveConn(oldconn); /* remove connection */ syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); return(1); } /* ** SendPacket -- send an RMP packet to a remote host. ** ** Parameters: ** rconn - packet to be sent. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendPacket(RMPCONN *rconn) { /* * Set Ethernet Destination address to Source (BPF and the enet * driver will take care of getting our source address set). */ memmove((char *)&rconn->rmp.hp_hdr.daddr[0], (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); /* * Reverse 802.2/HP Extended Source & Destination Access Pts. */ rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); /* * Last time this connection was active. */ (void) gettimeofday(&rconn->tstamp, (struct timezone *)0); if (DbgFp != NULL) /* display packet */ DispPkt(rconn,DIR_SENT); /* * Send RMP packet to remote host. */ return(BpfWrite(rconn)); } Index: head/libexec/revnetgroup/parse_netgroup.c =================================================================== --- head/libexec/revnetgroup/parse_netgroup.c (revision 229779) +++ head/libexec/revnetgroup/parse_netgroup.c (revision 229780) @@ -1,364 +1,364 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * This is a specially hacked-up version of getnetgrent.c used to parse * data from the stored hash table of netgroup info rather than from a * file. It's used mainly for the parse_netgroup() function. All the YP * stuff and file support has been stripped out since it isn't needed. */ #include #include #include #include #include #include "hash.h" /* * Static Variables and functions used by setnetgrent(), getnetgrent() and * __endnetgrent(). * There are two linked lists: * - linelist is just used by setnetgrent() to parse the net group file via. * parse_netgrp() * - netgrp is the list of entries for the current netgroup */ struct linelist { struct linelist *l_next; /* Chain ptr. */ int l_parsed; /* Flag for cycles */ char *l_groupname; /* Name of netgroup */ char *l_line; /* Netgroup entrie(s) to be parsed */ }; struct netgrp { struct netgrp *ng_next; /* Chain ptr */ char *ng_str[3]; /* Field pointers, see below */ }; #define NG_HOST 0 /* Host name */ #define NG_USER 1 /* User name */ #define NG_DOM 2 /* and Domain name */ static struct linelist *linehead = (struct linelist *)0; static struct netgrp *nextgrp = (struct netgrp *)0; static struct { struct netgrp *gr; char *grname; } grouphead = { (struct netgrp *)0, (char *)0, }; static int parse_netgrp(char *group); static struct linelist *read_for_group(char *group); extern struct group_entry *gtable[]; /* * setnetgrent() * Parse the netgroup file looking for the netgroup and build the list * of netgrp structures. Let parse_netgrp() and read_for_group() do * most of the work. */ void __setnetgrent(char *group) { /* Sanity check */ if (group == NULL || !strlen(group)) return; if (grouphead.gr == (struct netgrp *)0 || strcmp(group, grouphead.grname)) { __endnetgrent(); if (parse_netgrp(group)) __endnetgrent(); else { grouphead.grname = (char *) malloc(strlen(group) + 1); strcpy(grouphead.grname, group); } } nextgrp = grouphead.gr; } /* * Get the next netgroup off the list. */ int __getnetgrent(char **hostp, char **userp, char **domp) { if (nextgrp) { *hostp = nextgrp->ng_str[NG_HOST]; *userp = nextgrp->ng_str[NG_USER]; *domp = nextgrp->ng_str[NG_DOM]; nextgrp = nextgrp->ng_next; return (1); } return (0); } /* * __endnetgrent() - cleanup */ void __endnetgrent(void) { struct linelist *lp, *olp; struct netgrp *gp, *ogp; lp = linehead; while (lp) { olp = lp; lp = lp->l_next; free(olp->l_groupname); free(olp->l_line); free((char *)olp); } linehead = (struct linelist *)0; if (grouphead.grname) { free(grouphead.grname); grouphead.grname = (char *)0; } gp = grouphead.gr; while (gp) { ogp = gp; gp = gp->ng_next; if (ogp->ng_str[NG_HOST]) free(ogp->ng_str[NG_HOST]); if (ogp->ng_str[NG_USER]) free(ogp->ng_str[NG_USER]); if (ogp->ng_str[NG_DOM]) free(ogp->ng_str[NG_DOM]); free((char *)ogp); } grouphead.gr = (struct netgrp *)0; } /* * Parse the netgroup file setting up the linked lists. */ static int parse_netgrp(char *group) { char *spos, *epos; int len, strpos; #ifdef DEBUG int fields; #endif char *pos, *gpos; struct netgrp *grp; struct linelist *lp = linehead; /* * First, see if the line has already been read in. */ while (lp) { if (!strcmp(group, lp->l_groupname)) break; lp = lp->l_next; } if (lp == (struct linelist *)0 && (lp = read_for_group(group)) == (struct linelist *)0) return (1); if (lp->l_parsed) { #ifdef DEBUG /* - * This error message is largely superflous since the - * code handles the error condition sucessfully, and + * This error message is largely superfluous since the + * code handles the error condition successfully, and * spewing it out from inside libc can actually hose * certain programs. */ warnx("cycle in netgroup %s", lp->l_groupname); #endif return (1); } else lp->l_parsed = 1; pos = lp->l_line; /* Watch for null pointer dereferences, dammit! */ while (pos != NULL && *pos != '\0') { if (*pos == '(') { grp = (struct netgrp *)malloc(sizeof (struct netgrp)); bzero((char *)grp, sizeof (struct netgrp)); grp->ng_next = grouphead.gr; grouphead.gr = grp; pos++; gpos = strsep(&pos, ")"); #ifdef DEBUG fields = 0; #endif for (strpos = 0; strpos < 3; strpos++) { if ((spos = strsep(&gpos, ","))) { #ifdef DEBUG fields++; #endif while (*spos == ' ' || *spos == '\t') spos++; if ((epos = strpbrk(spos, " \t"))) { *epos = '\0'; len = epos - spos; } else len = strlen(spos); if (len > 0) { grp->ng_str[strpos] = (char *) malloc(len + 1); bcopy(spos, grp->ng_str[strpos], len + 1); } } else { /* * All other systems I've tested * return NULL for empty netgroup * fields. It's up to user programs * to handle the NULLs appropriately. */ grp->ng_str[strpos] = NULL; } } #ifdef DEBUG /* * Note: on other platforms, malformed netgroup * entries are not normally flagged. While we * can catch bad entries and report them, we should * stay silent by default for compatibility's sake. */ if (fields < 3) warnx("bad entry (%s%s%s%s%s) in netgroup \"%s\"", grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], grp->ng_str[NG_USER] == NULL ? "" : ",", grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], grp->ng_str[NG_DOM] == NULL ? "" : ",", grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], lp->l_groupname); #endif } else { spos = strsep(&pos, ", \t"); if (parse_netgrp(spos)) continue; } /* Watch for null pointer dereferences, dammit! */ if (pos != NULL) while (*pos == ' ' || *pos == ',' || *pos == '\t') pos++; } return (0); } /* * Read the netgroup file and save lines until the line for the netgroup * is found. Return 1 if eof is encountered. */ static struct linelist * read_for_group(char *group) { char *pos, *spos, *linep = NULL, *olinep = NULL; int len, olen; int cont; struct linelist *lp; char line[LINSIZ + 1]; char *data = NULL; data = lookup (gtable, group); sprintf(line, "%s %s", group, data); pos = (char *)&line; #ifdef CANT_HAPPEN if (*pos == '#') continue; #endif while (*pos == ' ' || *pos == '\t') pos++; spos = pos; while (*pos != ' ' && *pos != '\t' && *pos != '\n' && *pos != '\0') pos++; len = pos - spos; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\n' && *pos != '\0') { lp = (struct linelist *)malloc(sizeof (*lp)); lp->l_parsed = 0; lp->l_groupname = (char *)malloc(len + 1); bcopy(spos, lp->l_groupname, len); *(lp->l_groupname + len) = '\0'; len = strlen(pos); olen = 0; /* * Loop around handling line continuations. */ do { if (*(pos + len - 1) == '\n') len--; if (*(pos + len - 1) == '\\') { len--; cont = 1; } else cont = 0; if (len > 0) { linep = (char *)malloc(olen + len + 1); if (olen > 0) { bcopy(olinep, linep, olen); free(olinep); } bcopy(pos, linep + olen, len); olen += len; *(linep + olen) = '\0'; olinep = linep; } #ifdef CANT_HAPPEN if (cont) { if (fgets(line, LINSIZ, netf)) { pos = line; len = strlen(pos); } else cont = 0; } #endif } while (cont); lp->l_line = linep; lp->l_next = linehead; linehead = lp; #ifdef CANT_HAPPEN /* * If this is the one we wanted, we are done. */ if (!strcmp(lp->l_groupname, group)) #endif return (lp); } return ((struct linelist *)0); } Index: head/libexec/rshd/rshd.8 =================================================================== --- head/libexec/rshd/rshd.8 (revision 229779) +++ head/libexec/rshd/rshd.8 (revision 229780) @@ -1,273 +1,273 @@ .\" Copyright (c) 1983, 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)rshd.8 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" .Dd June 4, 1993 .Dt RSHD 8 .Os .Sh NAME .Nm rshd .Nd remote shell server .Sh SYNOPSIS .Nm .Op Fl aDLln .Sh DESCRIPTION The .Nm utility is the server for the .Xr rcmd 3 routine and, consequently, for the .Xr rsh 1 utility. The server provides remote execution facilities with authentication based on privileged port numbers from trusted hosts. .Pp The .Nm utility listens for service requests at the port indicated in the .Dq cmd service specification; see .Xr services 5 . When a service request is received the following protocol is initiated: .Bl -enum .It The server checks the client's source port. If the port is not in the range 512-1023, the server aborts the connection. .It The server reads characters from the socket up to a .Tn NUL (`\e0') byte. The resultant string is interpreted as an .Tn ASCII number, base 10. .It If the number received in step 2 is non-zero, it is interpreted as the port number of a secondary stream to be used for the .Em stderr . A second connection is then created to the specified port on the client's machine. The source port of this second connection is also in the range 512-1023. .It The server checks the client's source address and requests the corresponding host name (see .Xr gethostbyaddr 3 , .Xr hosts 5 and .Xr named 8 ) . If the hostname cannot be determined or the hostname and address do not match after verification, the dot-notation representation of the host address is used. .It A null terminated user name of at most 16 characters is retrieved on the initial socket. This user name is interpreted as the user identity on the .Em client Ns 's machine. .It A null terminated user name of at most 16 characters is retrieved on the initial socket. This user name is interpreted as a user identity to use on the .Em server Ns 's machine. .It A null terminated command to be passed to a shell is retrieved on the initial socket. The length of the command is limited by the upper bound on the size of the system's argument list. .It The .Nm utility then validates the user using .Xr ruserok 3 , which uses the file .Pa /etc/hosts.equiv and the .Pa .rhosts file found in the user's home directory. The .Fl l option prevents .Xr ruserok 3 from doing any validation based on the user's .Pa .rhosts file, unless the user is the superuser. .It A .Tn NUL byte is returned on the initial socket and the command line is passed to the normal login shell of the user. The shell inherits the network connections established by .Nm . .El .Pp The options are as follows: .Bl -tag -width indent .It Fl a -This flag is ignored, and is present for compatability purposes. +This flag is ignored, and is present for compatibility purposes. .It Fl D Sets the TCP_NODELAY socket option, which improves the performance of small back-to-back writes at the expense of additional network traffic. .It Fl L Causes all successful accesses to be logged to .Xr syslogd 8 as .Li auth.info messages. .It Fl l Do not use the user's .Pa .rhosts file for authentication, unless the user is the superuser. .It Fl n Turn off transport level keepalive messages. This will prevent sessions from timing out if the client crashes or becomes unreachable. .El .Sh FILES .Bl -tag -width /var/run/nologin -compact .It Pa /etc/hosts .It Pa /etc/hosts.equiv .It Pa /etc/login.conf .It Ev $HOME Ns Pa /.rhosts .Pp .It Pa /etc/pam.conf .Nm uses .Pa /etc/pam.conf entries with service name .Dq rsh . Authentication modules requiring passwords (such as .Nm pam_unix ) are not supported. .El .Sh DIAGNOSTICS Except for the last one listed below, all diagnostic messages are returned on the initial socket, after which any network connections are closed. An error is indicated by a leading byte with a value of 1 (0 is returned in step 10 above upon successful completion of all the steps prior to the execution of the login shell). .Bl -tag -width indent .It Sy Locuser too long. The name of the user on the client's machine is longer than 16 characters. .It Sy Ruser too long. The name of the user on the remote machine is longer than 16 characters. .It Sy Command too long. The command line passed exceeds the size of the argument list (as configured into the system). .It Sy Login incorrect. No password file entry for the user name existed or the authentication procedure described above failed. .It Sy Remote directory. The .Xr chdir 2 function to the home directory failed. .It Sy Logins not available right now. The .Xr rsh 1 utility was attempted outside the allowed hours defined in .Pa /etc/login.conf for the local user's login class. .It Sy Can't make pipe. The pipe needed for the .Em stderr , was not created. .It Sy Can't fork; try again. A .Xr fork 2 by the server failed. .It Sy : ... The user's login shell could not be started. This message is returned on the connection associated with the .Em stderr , and is not preceded by a flag byte. .El .Sh SEE ALSO .Xr rlogin 1 , .Xr rsh 1 , .Xr gethostbyaddr 3 , .Xr rcmd 3 , .Xr ruserok 3 , .Xr auth.conf 5 , .Xr hosts 5 , .Xr hosts.equiv 5 , .Xr login.conf 5 , .Xr services 5 , .Xr named 8 , .Xr rlogind 8 , .Xr syslogd 8 .Sh HISTORY IPv6 support was added by WIDE/KAME project. .Sh BUGS The authentication procedure used here assumes the integrity of each client machine and the connecting medium. This is insecure, but is useful in an .Dq open environment. .Pp A facility to allow all data exchanges to be encrypted should be present. .Pp Post-PAM, .Fx also needs the following patch applied besides properly configuring .Pa .rhosts : .Bd -literal -offset indent --- etc/pam.d/rsh.orig Wed Dec 17 14:36:20 2003 +++ etc/pam.d/rsh Wed Dec 17 14:30:43 2003 @@ -9 +9 @@ -auth required pam_rhosts.so no_warn +auth required pam_rhosts.so no_warn allow_root .Ed .Pp A more extensible protocol (such as Telnet) should be used. Index: head/libexec/rtld-aout/shlib.c =================================================================== --- head/libexec/rtld-aout/shlib.c (revision 229779) +++ head/libexec/rtld-aout/shlib.c (revision 229780) @@ -1,325 +1,325 @@ /* * Copyright (c) 1993 Paul Kranenburg * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Kranenburg. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "shlib.h" #include "support.h" /* * Standard directories to search for files specified by -l. */ #ifndef STANDARD_SEARCH_DIRS #define STANDARD_SEARCH_DIRS "/usr/lib/aout" #endif /* * Actual vector of library search directories, * including `-L'ed and LD_LIBRARY_PATH spec'd ones. */ char **search_dirs; int n_search_dirs; const char *standard_search_dirs[] = { STANDARD_SEARCH_DIRS }; void add_search_dir(const char *name) { int n; for (n = 0; n < n_search_dirs; n++) if (strcmp(search_dirs[n], name) == 0) return; n_search_dirs++; search_dirs = (char **) xrealloc(search_dirs, n_search_dirs * sizeof search_dirs[0]); search_dirs[n_search_dirs - 1] = strdup(name); } void add_search_path(char *path) { register char *cp, *dup; if (path == NULL) return; /* Add search directories from `path' */ path = dup = strdup(path); while ((cp = strsep(&path, ":")) != NULL) add_search_dir(cp); free(dup); } void std_search_path(void) { int i, n; /* Append standard search directories */ n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; for (i = 0; i < n; i++) add_search_dir(standard_search_dirs[i]); } /* * Return true if CP points to a valid dewey number. * Decode and leave the result in the array DEWEY. * Return the number of decoded entries in DEWEY. */ int getdewey(int dewey[], char *cp) { int i, n; for (n = 0, i = 0; i < MAXDEWEY; i++) { if (*cp == '\0') break; if (*cp == '.') cp++; if (!isdigit(*cp)) return 0; dewey[n++] = strtol(cp, &cp, 10); } return n; } /* * Compare two dewey arrays. * Return -1 if `d1' represents a smaller value than `d2'. * Return 1 if `d1' represents a greater value than `d2'. * Return 0 if equal. */ int cmpndewey(int d1[], int n1, int d2[], int n2) { register int i; for (i = 0; i < n1 && i < n2; i++) { if (d1[i] < d2[i]) return -1; if (d1[i] > d2[i]) return 1; } if (n1 == n2) return 0; if (i == n1) return -1; if (i == n2) return 1; - errx(1, "cmpndewey: cant happen"); + errx(1, "cmpndewey: can't happen"); return 0; } /* * Search directories for a shared library matching the given * major and minor version numbers. See search_lib_dir() below for * the detailed matching rules. * * As soon as a directory with an acceptable match is found, the search * terminates. Subsequent directories are not searched for a better * match. This is in conformance with the SunOS searching rules. Also, * it avoids a lot of directory searches that are virtually guaranteed to * be fruitless. * * The return value is a full pathname to the matching library. The * string is dynamically allocated. If no matching library is found, the * function returns NULL. */ char * findshlib(char *name, int *majorp, int *minorp, int do_dot_a) { int i; for (i = 0; i < n_search_dirs; i++) { char *path; path = search_lib_dir(search_dirs[i], name, majorp, minorp, do_dot_a); if(path != NULL) return path; } return NULL; } /* * Search library directories for a file with the given name. The * return value is a full pathname to the matching file. The string * is dynamically allocated. If no matching file is found, the function * returns NULL. */ char * find_lib_file(const char *name) { int i; for (i = 0; i < n_search_dirs; i++) { char *path = concat(search_dirs[i], "/", name); struct stat sb; if (lstat(path, &sb) != -1) /* We found it */ return path; free(path); } return NULL; } /* * Search a given directory for a library (preferably shared) satisfying * the given criteria. * * The matching rules are as follows: * * if(*majorp == -1) * find the library with the highest major version; * else * insist on a major version identical to *majorp; * * Always find the library with the highest minor version; * if(*minorp != -1) * insist on a minor version >= *minorp; * * It is invalid to specify a specific minor number while wildcarding * the major number. * * The actual major and minor numbers found are returned via the pointer * arguments. * * A suitable shared library is always preferred over a static (.a) library. * If do_dot_a is false, then a static library will not be accepted in * any case. * * The return value is a full pathname to the matching library. The * string is dynamically allocated. If no matching library is found, the * function returns NULL. */ char * search_lib_dir(char *dir, char *name, int *majorp, int *minorp, int do_dot_a) { size_t namelen; DIR *dd; struct dirent *dp; int best_dewey[MAXDEWEY]; int best_ndewey; char dot_a_name[MAXNAMLEN+1]; char dot_so_name[MAXNAMLEN+1]; if((dd = opendir(dir)) == NULL) return NULL; namelen = strlen(name); best_ndewey = 0; dot_a_name[0] = '\0'; dot_so_name[0] = '\0'; while((dp = readdir(dd)) != NULL) { char *extension; if(strlen(dp->d_name) < 3 + namelen + 2 || /* lib+xxx+.a */ strncmp(dp->d_name, "lib", 3) != 0 || strncmp(dp->d_name + 3, name, namelen) != 0 || dp->d_name[3+namelen] != '.') continue; extension = dp->d_name + 3 + namelen + 1; /* a or so.* */ if(strncmp(extension, "so.", 3) == 0) { int cur_dewey[MAXDEWEY]; int cur_ndewey; cur_ndewey = getdewey(cur_dewey, extension+3); if(cur_ndewey < 2) /* Too few version numbers */ continue; if(*majorp != -1) { /* Need exact match on major */ if(cur_dewey[0] != *majorp) continue; if(*minorp != -1) { /* Need minor >= minimum */ if(cur_dewey[1] < *minorp) continue; } } if(cmpndewey(cur_dewey, cur_ndewey, best_dewey, best_ndewey) <= 0) /* No better than prior match */ continue; /* We found a better match */ strcpy(dot_so_name, dp->d_name); bcopy(cur_dewey, best_dewey, cur_ndewey * sizeof best_dewey[0]); best_ndewey = cur_ndewey; } else if(do_dot_a && strcmp(extension, "a") == 0) strcpy(dot_a_name, dp->d_name); } closedir(dd); if(dot_so_name[0] != '\0') { *majorp = best_dewey[0]; *minorp = best_dewey[1]; return concat(dir, "/", dot_so_name); } if(dot_a_name[0] != '\0') return concat(dir, "/", dot_a_name); return NULL; } Index: head/libexec/rtld-elf/mips/reloc.c =================================================================== --- head/libexec/rtld-elf/mips/reloc.c (revision 229779) +++ head/libexec/rtld-elf/mips/reloc.c (revision 229780) @@ -1,537 +1,537 @@ /* $NetBSD: mips_reloc.c,v 1.58 2010/01/14 11:57:06 skrll Exp $ */ /* * Copyright 1997 Michael L. Hitch * Portions copyright 2002 Charles M. Hannum * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "debug.h" #include "rtld.h" #ifdef __mips_n64 #define GOT1_MASK 0x8000000000000000UL #else #define GOT1_MASK 0x80000000UL #endif void init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start; if (obj->pltgot[1] & 0x80000000) obj->pltgot[1] = (Elf_Addr) obj | GOT1_MASK; } } int do_copy_relocations(Obj_Entry *dstobj) { /* Do nothing */ return 0; } void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); /* * It is possible for the compiler to emit relocations for unaligned data. * We handle this situation with these inlines. */ #ifdef __mips_n64 /* * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain * the symbol index. The top 32-bits contain three relocation types encoded * in big-endian integer with first relocation in LSB. This means for little * endian we have to byte swap that integer (r_type). */ #define Elf_Sxword Elf64_Sxword #define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64)) #if BYTE_ORDER == LITTLE_ENDIAN #undef ELF_R_SYM #undef ELF_R_TYPE #define ELF_R_SYM(r_info) ((r_info) & 0xffffffff) #define ELF_R_TYPE(r_info) bswap32((r_info) >> 32) #endif #else #define ELF_R_NXTTYPE_64_P(r_type) (0) #define Elf_Sxword Elf32_Sword #endif static __inline Elf_Sxword load_ptr(void *where, size_t len) { Elf_Sxword val; if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { #ifdef __mips_n64 if (len == sizeof(Elf_Sxword)) return *(Elf_Sxword *)where; #endif return *(Elf_Sword *)where; } val = 0; #if BYTE_ORDER == LITTLE_ENDIAN (void)memcpy(&val, where, len); #endif #if BYTE_ORDER == BIG_ENDIAN (void)memcpy((uint8_t *)((&val)+1) - len, where, len); #endif return (len == sizeof(Elf_Sxword)) ? val : (Elf_Sword)val; } static __inline void store_ptr(void *where, Elf_Sxword val, size_t len) { if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { #ifdef __mips_n64 if (len == sizeof(Elf_Sxword)) { *(Elf_Sxword *)where = val; return; } #endif *(Elf_Sword *)where = val; return; } #if BYTE_ORDER == LITTLE_ENDIAN (void)memcpy(where, &val, len); #endif #if BYTE_ORDER == BIG_ENDIAN (void)memcpy(where, (const uint8_t *)((&val)+1) - len, len); #endif } void _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rel *rel = 0, *rellim; Elf_Addr relsz = 0; const Elf_Sym *symtab = NULL, *sym; Elf_Addr *where; Elf_Addr *got = NULL; Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; size_t i; for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_REL: rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); break; case DT_RELSZ: relsz = dynp->d_un.d_val; break; case DT_SYMTAB: symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); break; case DT_PLTGOT: got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); break; case DT_MIPS_LOCAL_GOTNO: local_gotno = dynp->d_un.d_val; break; case DT_MIPS_SYMTABNO: symtabno = dynp->d_un.d_val; break; case DT_MIPS_GOTSYM: gotsym = dynp->d_un.d_val; break; } } i = (got[1] & GOT1_MASK) ? 2 : 1; /* Relocate the local GOT entries */ got += i; for (; i < local_gotno; i++) { *got++ += relocbase; } sym = symtab + gotsym; /* Now do the global GOT entries */ for (i = gotsym; i < symtabno; i++) { *got = sym->st_value + relocbase; ++sym; ++got; } rellim = (const Elf_Rel *)((caddr_t)rel + relsz); for (; rel < rellim; rel++) { Elf_Word r_symndx, r_type; where = (void *)(relocbase + rel->r_offset); r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(REL32): { const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; #ifdef __mips_n64 assert(r_type == R_TYPE(REL32) || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8))); #endif assert(r_symndx < gotsym); sym = symtab + r_symndx; assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); val += relocbase; store_ptr(where, val, sizeof(Elf_Sword)); dbg("REL32/L(%p) %p -> %p in ", where, (void *)old, (void *)val); store_ptr(where, val, rlen); break; } case R_TYPE(GPREL32): case R_TYPE(NONE): break; default: abort(); break; } } } Elf_Addr _mips_rtld_bind(Obj_Entry *obj, Elf_Size reloff) { Elf_Addr *got = obj->pltgot; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr target; def = find_symdef(reloff, obj, &defobj, SYMLOOK_IN_PLT, NULL, NULL); if (def == NULL) _rtld_error("bind failed no symbol"); target = (Elf_Addr)(defobj->relocbase + def->st_value); dbg("bind now/fixup at %s sym # %d in %s --> was=%p new=%p", obj->path, reloff, defobj->strtab + def->st_name, (void *)got[obj->local_gotno + reloff - obj->gotsym], (void *)target); got[obj->local_gotno + reloff - obj->gotsym] = target; return (Elf_Addr)target; } int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate) { const Elf_Rel *rel; const Elf_Rel *rellim; Elf_Addr *got = obj->pltgot; const Elf_Sym *sym, *def; const Obj_Entry *defobj; Elf_Word i; #ifdef SUPPORT_OLD_BROKEN_LD int broken; #endif /* The relocation for the dynamic loader has already been done. */ if (obj == obj_rtld) return (0); #ifdef SUPPORT_OLD_BROKEN_LD broken = 0; sym = obj->symtab; for (i = 1; i < 12; i++) if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) broken = 1; dbg("%s: broken=%d", obj->path, broken); #endif i = (got[1] & GOT1_MASK) ? 2 : 1; /* Relocate the local GOT entries */ got += i; dbg("got:%p for %d entries adding %x", got, obj->local_gotno, (uint32_t)obj->relocbase); for (; i < obj->local_gotno; i++) { *got += (Elf_Addr)obj->relocbase; got++; } sym = obj->symtab + obj->gotsym; dbg("got:%p for %d entries", got, obj->symtabno); /* Now do the global GOT entries */ for (i = obj->gotsym; i < obj->symtabno; i++) { dbg(" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym, sym->st_name + obj->strtab, (u_long) *got); #ifdef SUPPORT_OLD_BROKEN_LD if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && broken && sym->st_shndx == SHN_UNDEF) { /* * XXX DANGER WILL ROBINSON! * You might think this is stupid, as it intentionally * defeats lazy binding -- and you'd be right. * Unfortunately, for lazy binding to work right, we * need to a way to force the GOT slots used for * function pointers to be resolved immediately. This * is supposed to be done automatically by the linker, * by not outputting a PLT slot and setting st_value * to 0 if there are non-PLT references, but older * versions of GNU ld do not do this. */ def = find_symdef(i, obj, &defobj, false, NULL, lockstate); if (def == NULL) return -1; *got = def->st_value + (Elf_Addr)defobj->relocbase; } else #endif if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { /* * If there are non-PLT references to the function, * st_value should be 0, forcing us to resolve the * address immediately. * * XXX DANGER WILL ROBINSON! * The linker is not outputting PLT slots for calls to * functions that are defined in the same shared * library. This is a bug, because it can screw up * link ordering rules if the symbol is defined in * more than one module. For now, if there is a * definition, we fail the test above and force a full * symbol lookup. This means that all intra-module * calls are bound immediately. - mycroft, 2003/09/24 */ *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning2, i:%d maps to relocbase address:%x", i, (uint32_t)obj->relocbase); } } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { /* Symbols with index SHN_ABS are not relocated. */ if (sym->st_shndx != SHN_ABS) { *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning3, i:%d maps to relocbase address:%x", i, (uint32_t)obj->relocbase); } } } else { /* TODO: add cache here */ def = find_symdef(i, obj, &defobj, false, NULL, lockstate); if (def == NULL) { - dbg("Warning4, cant find symbole %d", i); + dbg("Warning4, can't find symbole %d", i); return -1; } *got = def->st_value + (Elf_Addr)defobj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning4, i:%d maps to relocbase address:%x", i, (uint32_t)obj->relocbase); dbg("via first obj symbol %s", obj->strtab + obj->symtab[i].st_name); dbg("found in obj %p:%s", defobj, defobj->path); } } dbg(" --> now %lx", (u_long) *got); ++sym; ++got; } got = obj->pltgot; rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { Elf_Word r_symndx, r_type; void *where; where = obj->relocbase + rel->r_offset; r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(NONE): break; case R_TYPE(REL32): { /* 32-bit PC-relative reference */ const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; def = obj->symtab + r_symndx; if (r_symndx >= obj->gotsym) { val += got[obj->local_gotno + r_symndx - obj->gotsym]; dbg("REL32/G(%p) %p --> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } else { /* * XXX: ABI DIFFERENCE! * * Old NetBSD binutils would generate shared * libs with section-relative relocations being * already adjusted for the start address of * the section. * * New binutils, OTOH, generate shared libs * with the same relocations being based at * zero, so we need to add in the start address * of the section. * * --rkb, Oct 6, 2001 */ if (def->st_info == ELF_ST_INFO(STB_LOCAL, STT_SECTION) #ifdef SUPPORT_OLD_BROKEN_LD && !broken #endif ) val += (Elf_Addr)def->st_value; val += (Elf_Addr)obj->relocbase; dbg("REL32/L(%p) %p -> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } store_ptr(where, val, rlen); break; } default: dbg("sym = %lu, type = %lu, offset = %p, " "contents = %p, symbol = %s", (u_long)r_symndx, (u_long)ELF_R_TYPE(rel->r_info), (void *)rel->r_offset, (void *)load_ptr(where, sizeof(Elf_Sword)), obj->strtab + obj->symtab[r_symndx].st_name); _rtld_error("%s: Unsupported relocation type %ld " "in non-PLT relocations", obj->path, (u_long) ELF_R_TYPE(rel->r_info)); return -1; } } return 0; } /* * Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { #if 0 const Elf_Rel *rellim; const Elf_Rel *rel; dbg("reloc_plt obj:%p pltrel:%p sz:%d", obj, obj->pltrel, (int)obj->pltrelsize); dbg("gottable %p num syms:%d", obj->pltgot, obj->symtabno ); dbg("*****************************************************"); rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where; where = (Elf_Addr *)(obj->relocbase + rel->r_offset); *where += (Elf_Addr )obj->relocbase; } #endif /* PLT fixups were done above in the GOT relocation. */ return (0); } /* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) { /* Do nothing */ obj->jmpslots_done = true; return (0); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, const Obj_Entry *obj, const Elf_Rel *rel) { /* Do nothing */ return target; } void allocate_initial_tls(Obj_Entry *objs) { } void * __tls_get_addr(tls_index* ti) { return (NULL); } Index: head/libexec/rtld-elf/rtld.h =================================================================== --- head/libexec/rtld-elf/rtld.h (revision 229779) +++ head/libexec/rtld-elf/rtld.h (revision 229780) @@ -1,344 +1,344 @@ /*- * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_H /* { */ #define RTLD_H 1 #include #include #include #include #include #include #include #include #include "rtld_lock.h" #include "rtld_machdep.h" #ifdef COMPAT_32BIT #undef STANDARD_LIBRARY_PATH #undef _PATH_ELF_HINTS #define _PATH_ELF_HINTS "/var/run/ld-elf32.so.hints" /* For running 32 bit binaries */ #define STANDARD_LIBRARY_PATH "/lib32:/usr/lib32" #define LD_ "LD_32_" #endif #ifndef STANDARD_LIBRARY_PATH #define STANDARD_LIBRARY_PATH "/lib:/usr/lib" #endif #ifndef LD_ #define LD_ "LD_" #endif #define NEW(type) ((type *) xmalloc(sizeof(type))) #define CNEW(type) ((type *) xcalloc(sizeof(type))) /* We might as well do booleans like C++. */ typedef unsigned char bool; #define false 0 #define true 1 extern size_t tls_last_offset; extern size_t tls_last_size; extern size_t tls_static_space; extern int tls_dtv_generation; extern int tls_max_index; struct stat; struct Struct_Obj_Entry; /* Lists of shared objects */ typedef struct Struct_Objlist_Entry { STAILQ_ENTRY(Struct_Objlist_Entry) link; struct Struct_Obj_Entry *obj; } Objlist_Entry; typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist; /* Types of init and fini functions */ typedef void (*InitFunc)(void); /* Lists of shared object dependencies */ typedef struct Struct_Needed_Entry { struct Struct_Needed_Entry *next; struct Struct_Obj_Entry *obj; unsigned long name; /* Offset of name in string table */ } Needed_Entry; typedef struct Struct_Name_Entry { STAILQ_ENTRY(Struct_Name_Entry) link; char name[1]; } Name_Entry; /* Lock object */ typedef struct Struct_LockInfo { void *context; /* Client context for creating locks */ void *thelock; /* The one big lock */ /* Debugging aids. */ volatile int rcount; /* Number of readers holding lock */ volatile int wcount; /* Number of writers holding lock */ /* Methods */ void *(*lock_create)(void *context); void (*rlock_acquire)(void *lock); void (*wlock_acquire)(void *lock); void (*rlock_release)(void *lock); void (*wlock_release)(void *lock); void (*lock_destroy)(void *lock); void (*context_destroy)(void *context); } LockInfo; typedef struct Struct_Ver_Entry { Elf_Word hash; unsigned int flags; const char *name; const char *file; } Ver_Entry; #define VER_INFO_HIDDEN 0x01 /* * Shared object descriptor. * * Items marked with "(%)" are dynamically allocated, and must be freed * when the structure is destroyed. * * CAUTION: It appears that the JDK port peeks into these structures. * It looks at "next" and "mapbase" at least. Don't add new members * near the front, until this can be straightened out. */ typedef struct Struct_Obj_Entry { /* * These two items have to be set right for compatibility with the * original ElfKit crt1.o. */ Elf_Size magic; /* Magic number (sanity check) */ Elf_Size version; /* Version number of struct format */ struct Struct_Obj_Entry *next; char *path; /* Pathname of underlying file (%) */ char *origin_path; /* Directory path of origin file */ int refcount; int dl_refcount; /* Number of times loaded by dlopen */ /* These items are computed by map_object() or by digest_phdr(). */ caddr_t mapbase; /* Base address of mapped region */ size_t mapsize; /* Size of mapped region in bytes */ size_t textsize; /* Size of text segment in bytes */ Elf_Addr vaddrbase; /* Base address in shared object file */ caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */ const Elf_Dyn *dynamic; /* Dynamic section */ caddr_t entry; /* Entry point */ const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */ size_t phsize; /* Size of program header in bytes */ const char *interp; /* Pathname of the interpreter, if any */ Elf_Word stack_flags; /* TLS information */ int tlsindex; /* Index in DTV for this module */ void *tlsinit; /* Base address of TLS init block */ size_t tlsinitsize; /* Size of TLS init block for this module */ size_t tlssize; /* Size of TLS block for this module */ size_t tlsoffset; /* Offset of static TLS block for this module */ size_t tlsalign; /* Alignment of static TLS block */ /* Items from the dynamic section. */ Elf_Addr *pltgot; /* PLT or GOT, depending on architecture */ const Elf_Rel *rel; /* Relocation entries */ unsigned long relsize; /* Size in bytes of relocation info */ const Elf_Rela *rela; /* Relocation entries with addend */ unsigned long relasize; /* Size in bytes of addend relocation info */ const Elf_Rel *pltrel; /* PLT relocation entries */ unsigned long pltrelsize; /* Size in bytes of PLT relocation info */ const Elf_Rela *pltrela; /* PLT relocation entries with addend */ unsigned long pltrelasize; /* Size in bytes of PLT addend reloc info */ const Elf_Sym *symtab; /* Symbol table */ const char *strtab; /* String table */ unsigned long strsize; /* Size in bytes of string table */ #ifdef __mips__ Elf_Word local_gotno; /* Number of local GOT entries */ Elf_Word symtabno; /* Number of dynamic symbols */ Elf_Word gotsym; /* First dynamic symbol in GOT */ #endif const Elf_Verneed *verneed; /* Required versions. */ Elf_Word verneednum; /* Number of entries in verneed table */ const Elf_Verdef *verdef; /* Provided versions. */ Elf_Word verdefnum; /* Number of entries in verdef table */ const Elf_Versym *versyms; /* Symbol versions table */ const Elf_Hashelt *buckets; /* Hash table buckets array */ unsigned long nbuckets; /* Number of buckets */ const Elf_Hashelt *chains; /* Hash table chain array */ unsigned long nchains; /* Number of chains */ char *rpath; /* Search path specified in object */ Needed_Entry *needed; /* Shared objects needed by this one (%) */ Needed_Entry *needed_filtees; Needed_Entry *needed_aux_filtees; STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we know about. */ Ver_Entry *vertab; /* Versions required /defined by this object */ int vernum; /* Number of entries in vertab */ Elf_Addr init; /* Initialization function to call */ Elf_Addr fini; /* Termination function to call */ bool mainprog : 1; /* True if this is the main program */ bool rtld : 1; /* True if this is the dynamic linker */ bool textrel : 1; /* True if there are relocations to text seg */ bool symbolic : 1; /* True if generated with "-Bsymbolic" */ bool bind_now : 1; /* True if all relocations should be made first */ bool traced : 1; /* Already printed in ldd trace output */ bool jmpslots_done : 1; /* Already have relocated the jump slots */ bool init_done : 1; /* Already have added object to init list */ bool tls_done : 1; /* Already allocated offset for static TLS */ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */ bool z_origin : 1; /* Process rpath and soname tokens */ bool z_nodelete : 1; /* Do not unload the object and dependencies */ bool z_noopen : 1; /* Do not load on dlopen */ bool z_loadfltr : 1; /* Immediately load filtees */ bool ref_nodel : 1; /* Refcount increased to prevent dlclose */ bool init_scanned: 1; /* Object is already on init list. */ bool on_fini_list: 1; /* Object is already on fini list. */ bool dag_inited : 1; /* Object has its DAG initialized. */ bool filtees_loaded : 1; /* Filtees loaded */ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ Objlist dagmembers; /* DAG has these members (%) */ dev_t dev; /* Object's filesystem's device */ ino_t ino; /* Object's inode number */ - void *priv; /* Platform-dependant */ + void *priv; /* Platform-dependent */ } Obj_Entry; #define RTLD_MAGIC 0xd550b87a #define RTLD_VERSION 1 #define RTLD_STATIC_TLS_EXTRA 128 /* Flags to be passed into symlook_ family of functions. */ #define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */ #define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. Used by dlsym. */ /* Flags for load_object(). */ #define RTLD_LO_NOLOAD 0x01 /* dlopen() specified RTLD_NOLOAD. */ #define RTLD_LO_DLOPEN 0x02 /* Load_object() called from dlopen(). */ #define RTLD_LO_TRACE 0x04 /* Only tracing. */ #define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */ #define RTLD_LO_FILTEES 0x10 /* Loading filtee. */ /* * Symbol cache entry used during relocation to avoid multiple lookups * of the same symbol. */ typedef struct Struct_SymCache { const Elf_Sym *sym; /* Symbol table entry */ const Obj_Entry *obj; /* Shared object which defines it */ } SymCache; /* * This structure provides a reentrant way to keep a list of objects and * check which ones have already been processed in some way. */ typedef struct Struct_DoneList { const Obj_Entry **objs; /* Array of object pointers */ unsigned int num_alloc; /* Allocated size of the array */ unsigned int num_used; /* Number of array slots used */ } DoneList; struct Struct_RtldLockState { int lockstate; sigjmp_buf env; }; /* * The pack of arguments and results for the symbol lookup functions. */ typedef struct Struct_SymLook { const char *name; unsigned long hash; const Ver_Entry *ventry; int flags; const Obj_Entry *defobj_out; const Elf_Sym *sym_out; struct Struct_RtldLockState *lockstate; } SymLook; extern void _rtld_error(const char *, ...) __printflike(1, 2); extern Obj_Entry *map_object(int, const char *, const struct stat *); extern void *xcalloc(size_t); extern void *xmalloc(size_t); extern char *xstrdup(const char *); extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */ extern void dump_relocations (Obj_Entry *); extern void dump_obj_relocations (Obj_Entry *); extern void dump_Elf_Rel (Obj_Entry *, const Elf_Rel *, u_long); extern void dump_Elf_Rela (Obj_Entry *, const Elf_Rela *, u_long); /* * Function declarations. */ unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); void init_pltgot(Obj_Entry *); void lockdflt_init(void); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def); void symlook_init(SymLook *, const char *); int symlook_obj(SymLook *, const Obj_Entry *); void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset); void *allocate_tls(Obj_Entry *, void *, size_t, size_t); void free_tls(void *, size_t, size_t); void *allocate_module_tls(int index); bool allocate_tls_offset(Obj_Entry *obj); void free_tls_offset(Obj_Entry *obj); const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long); /* * MD function declarations. */ int do_copy_relocations(Obj_Entry *); int reloc_non_plt(Obj_Entry *, Obj_Entry *, struct Struct_RtldLockState *); int reloc_plt(Obj_Entry *); int reloc_jmpslots(Obj_Entry *, struct Struct_RtldLockState *); int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *); int reloc_gnu_ifunc(Obj_Entry *, struct Struct_RtldLockState *); void allocate_initial_tls(Obj_Entry *); #endif /* } */ Index: head/libexec/rtld-elf/sparc64/reloc.c =================================================================== --- head/libexec/rtld-elf/sparc64/reloc.c (revision 229779) +++ head/libexec/rtld-elf/sparc64/reloc.c (revision 229780) @@ -1,848 +1,848 @@ /* $NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp $ */ /*- * Copyright (c) 2000 Eduardo Horvath. * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" /* * The following table holds for each relocation type: * - the width in bits of the memory location the relocation * applies to (not currently used) * - the number of bits the relocation value must be shifted to the * right (i.e. discard least significant bits) to fit into * the appropriate field in the instruction word. * - flags indicating whether * * the relocation involves a symbol * * the relocation is relative to the current position * * the relocation is for a GOT entry * * the relocation is relative to the load address * */ #define _RF_S 0x80000000 /* Resolve symbol */ #define _RF_A 0x40000000 /* Use addend */ #define _RF_P 0x20000000 /* Location relative */ #define _RF_G 0x10000000 /* GOT offset */ #define _RF_B 0x08000000 /* Load address relative */ #define _RF_U 0x04000000 /* Unaligned */ #define _RF_X 0x02000000 /* Bare symbols, needs proc */ #define _RF_D 0x01000000 /* Use dynamic TLS offset */ #define _RF_O 0x00800000 /* Use static TLS offset */ #define _RF_I 0x00400000 /* Use TLS object ID */ #define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */ #define _RF_RS(s) ( (s) & 0xff) /* right shift */ static const int reloc_target_flags[] = { 0, /* NONE */ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 8 */ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 16 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HI22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 13 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LO10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */ _RF_SZ(32) | _RF_RS(0), /* COPY */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */ _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */ _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 11 */ _RF_S|_RF_A|_RF_X| _RF_SZ(64) | _RF_RS(0), /* 64 */ _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(42), /* HH22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(32), /* HM10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 7 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 5 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 6 */ _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */ _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HIX22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LOX10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(22), /* H44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(12), /* M44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* L44 */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */ _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */ _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */ /* TLS */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* GD_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GD_LO10 */ 0, /* GD_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* GD_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDM_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDM_LO10 */ 0, /* LDM_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* LDM_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDO_HIX22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDO_LOX10 */ 0, /* LDO_ADD */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* IE_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* IE_LO10 */ 0, /* IE_LD */ 0, /* IE_LDX */ 0, /* IE_ADD */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(10), /* LE_HIX22 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* LE_LOX10 */ _RF_S| _RF_I| _RF_SZ(32) | _RF_RS(0), /* DTPMOD32 */ _RF_S| _RF_I| _RF_SZ(64) | _RF_RS(0), /* DTPMOD64 */ _RF_S|_RF_A| _RF_D| _RF_SZ(32) | _RF_RS(0), /* DTPOFF32 */ _RF_S|_RF_A| _RF_D| _RF_SZ(64) | _RF_RS(0), /* DTPOFF64 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* TPOFF32 */ _RF_S|_RF_A| _RF_O| _RF_SZ(64) | _RF_RS(0) /* TPOFF64 */ }; #if 0 static const char *const reloc_names[] = { "NONE", "8", "16", "32", "DISP_8", "DISP_16", "DISP_32", "WDISP_30", "WDISP_22", "HI22", "22", "13", "LO10", "GOT10", "GOT13", "GOT22", "PC10", "PC22", "WPLT30", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE", "UA_32", "PLT32", "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22", "PCPLT32", "10", "11", "64", "OLO10", "HH22", "HM10", "LM22", "PC_HH22", "PC_HM10", "PC_LM22", "WDISP16", "WDISP19", "GLOB_JMP", "7", "5", "6", "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44", "L44", "REGISTER", "UA64", "UA16", "GD_HI22", "GD_LO10", "GD_ADD", "GD_CALL", "LDM_HI22", "LDMO10", "LDM_ADD", "LDM_CALL", "LDO_HIX22", "LDO_LOX10", "LDO_ADD", "IE_HI22", "IE_LO10", "IE_LD", "IE_LDX", "IE_ADD", "LE_HIX22", "LE_LOX10", "DTPMOD32", "DTPMOD64", "DTPOFF32", "DTPOFF64", "TPOFF32", "TPOFF64" }; #endif #define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0) #define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0) #define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0) #define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0) #define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0) #define RELOC_BARE_SYMBOL(t) ((reloc_target_flags[t] & _RF_X) != 0) #define RELOC_USE_TLS_DOFF(t) ((reloc_target_flags[t] & _RF_D) != 0) #define RELOC_USE_TLS_OFF(t) ((reloc_target_flags[t] & _RF_O) != 0) #define RELOC_USE_TLS_ID(t) ((reloc_target_flags[t] & _RF_I) != 0) #define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff) #define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff) static const long reloc_target_bitmask[] = { #define _BM(x) (~(-(1ULL << (x)))) 0, /* NONE */ _BM(8), _BM(16), _BM(32), /* 8, 16, 32 */ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */ _BM(30), _BM(22), /* WDISP30, WDISP22 */ _BM(22), _BM(22), /* HI22, 22 */ _BM(13), _BM(10), /* 13, LO10 */ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */ _BM(10), _BM(22), /* PC10, PC22 */ _BM(30), 0, /* WPLT30, COPY */ _BM(32), _BM(32), _BM(32), /* GLOB_DAT, JMP_SLOT, RELATIVE */ _BM(32), _BM(32), /* UA32, PLT32 */ _BM(22), _BM(10), /* HIPLT22, LOPLT10 */ _BM(32), _BM(22), _BM(10), /* PCPLT32, PCPLT22, PCPLT10 */ _BM(10), _BM(11), -1, /* 10, 11, 64 */ _BM(13), _BM(22), /* OLO10, HH22 */ _BM(10), _BM(22), /* HM10, LM22 */ _BM(22), _BM(10), _BM(22), /* PC_HH22, PC_HM10, PC_LM22 */ _BM(16), _BM(19), /* WDISP16, WDISP19 */ -1, /* GLOB_JMP */ _BM(7), _BM(5), _BM(6), /* 7, 5, 6 */ -1, -1, /* DISP64, PLT64 */ _BM(22), _BM(13), /* HIX22, LOX10 */ _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */ -1, -1, _BM(16), /* REGISTER, UA64, UA16 */ _BM(22), _BM(10), 0, _BM(30), /* GD_HI22, GD_LO10, GD_ADD, GD_CALL */ _BM(22), _BM(10), 0, /* LDM_HI22, LDMO10, LDM_ADD */ _BM(30), /* LDM_CALL */ _BM(22), _BM(10), 0, /* LDO_HIX22, LDO_LOX10, LDO_ADD */ _BM(22), _BM(10), 0, 0, /* IE_HI22, IE_LO10, IE_LD, IE_LDX */ 0, /* IE_ADD */ _BM(22), _BM(13), /* LE_HIX22, LE_LOX10 */ _BM(32), -1, /* DTPMOD32, DTPMOD64 */ _BM(32), -1, /* DTPOFF32, DTPOFF64 */ _BM(32), -1 /* TPOFF32, TPOFF64 */ #undef _BM }; #define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) #undef flush #define flush(va, offs) \ __asm __volatile("flush %0 + %1" : : "r" (va), "I" (offs)); static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, RtldLockState *lockstate); static void install_plt(Elf_Word *pltgot, Elf_Addr proc); extern char _rtld_bind_start_0[]; extern char _rtld_bind_start_1[]; int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *dstsym; const Elf_Sym *srcsym; void *dstaddr; const void *srcaddr; const Obj_Entry *srcobj, *defobj; SymLook req; const char *name; size_t size; int res; assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ relalim = (const Elf_Rela *)((caddr_t)dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_SPARC_COPY) { dstaddr = (void *)(dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\"" "referenced from COPY relocation" "in %s", name, dstobj->path); return (-1); } srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } return (0); } int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; int r = -1; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->nchains, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (reloc_nonplt_object(obj, rela, cache, lockstate) < 0) goto done; } r = 0; done: if (cache != NULL) free(cache); return (r); } static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Sym *def; Elf_Addr *where; Elf_Word *where32; Elf_Word type; Elf_Addr value; Elf_Addr mask; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); where32 = (Elf_Word *)where; defobj = NULL; def = NULL; type = ELF64_R_TYPE_ID(rela->r_info); if (type == R_SPARC_NONE) return (0); /* We do JMP_SLOTs below. */ if (type == R_SPARC_JMP_SLOT) return (0); /* COPY relocs are also handled elsewhere. */ if (type == R_SPARC_COPY) return (0); /* Ignore ADD and CALL relocations for dynamic TLS references. */ if (type == R_SPARC_TLS_GD_ADD || type == R_SPARC_TLS_GD_CALL || type == R_SPARC_TLS_LDM_ADD || type == R_SPARC_TLS_LDM_CALL || type == R_SPARC_TLS_LDO_ADD) return (0); /* * Note: R_SPARC_TLS_TPOFF64 must be the numerically largest * relocation type. */ if (type >= sizeof(reloc_target_bitmask) / sizeof(*reloc_target_bitmask)) { _rtld_error("%s: Unsupported relocation type %d in non-PLT " "object\n", obj->path, type); return (-1); } value = rela->r_addend; /* * Handle relative relocs here, because we might not be able to access * globals yet. */ if (type == R_SPARC_RELATIVE) { /* XXXX -- apparently we ignore the preexisting value. */ *where = (Elf_Addr)(obj->relocbase + value); return (0); } /* * If we get here while relocating rtld itself, we will crash because * a non-local variable is accessed. */ if (RELOC_RESOLVE_SYMBOL(type)) { /* Find the symbol. */ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, false, cache, lockstate); if (def == NULL) return (-1); if (RELOC_USE_TLS_ID(type)) value = (Elf_Addr)defobj->tlsindex; else if (RELOC_USE_TLS_DOFF(type)) value += (Elf_Addr)def->st_value; else if (RELOC_USE_TLS_OFF(type)) { /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the TLS * block. This allows us to support (small amounts * of) static TLS in dynamically loaded modules. If * we run out of space, we generate an error. */ if (!defobj->tls_done && !allocate_tls_offset((Obj_Entry*)defobj)) { _rtld_error("%s: No space available for " "static Thread Local Storage", obj->path); return (-1); } value += (Elf_Addr)(def->st_value - defobj->tlsoffset); } else { /* Add in the symbol's absolute address. */ value += (Elf_Addr)(def->st_value + defobj->relocbase); } } if (type == R_SPARC_OLO10) value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); if (type == R_SPARC_HIX22 || type == R_SPARC_TLS_LE_HIX22) value ^= 0xffffffffffffffff; if (RELOC_PC_RELATIVE(type)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(type)) { /* * Note that even though sparcs use `Elf_rela' exclusively * we still need the implicit memory addend in relocations * referring to GOT entries. Undoubtedly, someone f*cked * this up in the distant past, and now we're stuck with * it in the name of compatibility for all eternity ... * * In any case, the implicit and explicit should be mutually * exclusive. We provide a check for that here. */ /* XXXX -- apparently we ignore the preexisting value */ value += (Elf_Addr)(obj->relocbase); } mask = RELOC_VALUE_BITMASK(type); value >>= RELOC_VALUE_RIGHTSHIFT(type); value &= mask; if (type == R_SPARC_LOX10 || type == R_SPARC_TLS_LE_LOX10) value |= 0x1c00; if (RELOC_UNALIGNED(type)) { /* Handle unaligned relocations. */ Elf_Addr tmp; char *ptr; int size; int i; size = RELOC_TARGET_SIZE(type) / 8; ptr = (char *)where; tmp = 0; /* Read it in one byte at a time. */ for (i = 0; i < size; i++) tmp = (tmp << 8) | ptr[i]; tmp &= ~mask; tmp |= value; /* Write it back out. */ for (i = 0; i < size; i++) ptr[i] = ((tmp >> ((size - i - 1) * 8)) & 0xff); } else if (RELOC_TARGET_SIZE(type) > 32) { *where &= ~mask; *where |= value; } else { *where32 &= ~mask; *where32 |= value; } return (0); } int reloc_plt(Obj_Entry *obj) { #if 0 const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr value; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { if (rela->r_addend == 0) continue; assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL, lockstate); value = (Elf_Addr)(defobj->relocbase + def->st_value); *where = value; } #endif return (0); } /* * Instruction templates: */ #define BAA 0x10400000 /* ba,a %xcc, 0 */ #define SETHI 0x03000000 /* sethi %hi(0), %g1 */ #define JMP 0x81c06000 /* jmpl %g1+%lo(0), %g0 */ #define NOP 0x01000000 /* sethi %hi(0), %g0 */ #define OR 0x82806000 /* or %g1, 0, %g1 */ #define XOR 0x82c06000 /* xor %g1, 0, %g1 */ #define MOV71 0x8283a000 /* or %o7, 0, %g1 */ #define MOV17 0x9c806000 /* or %g1, 0, %o7 */ #define CALL 0x40000000 /* call 0 */ #define SLLX 0x8b407000 /* sllx %g1, 0, %g1 */ #define SETHIG5 0x0b000000 /* sethi %hi(0), %g5 */ #define ORG5 0x82804005 /* or %g1, %g5, %g1 */ /* %hi(v) with variable shift */ #define HIVAL(v, s) (((v) >> (s)) & 0x003fffff) #define LOVAL(v) ((v) & 0x000003ff) int reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr target; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL, lockstate); if (def == NULL) return -1; target = (Elf_Addr)(defobj->relocbase + def->st_value); reloc_jmpslot(where, target, defobj, obj, (Elf_Rel *)rela); } obj->jmpslots_done = true; return (0); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj, const Obj_Entry *refobj, const Elf_Rel *rel) { const Elf_Rela *rela = (const Elf_Rela *)rel; Elf_Addr offset; Elf_Word *where; if (rela - refobj->pltrela < 32764) { /* * At the PLT entry pointed at by `where', we now construct * a direct transfer to the now fully resolved function * address. * * A PLT entry is supposed to start by looking like this: * * sethi (. - .PLT0), %g1 * ba,a %xcc, .PLT1 * nop * nop * nop * nop * nop * nop * * When we replace these entries we start from the second * entry and do it in reverse order so the last thing we * do is replace the branch. That allows us to change this * atomically. * * We now need to find out how far we need to jump. We * have a choice of several different relocation techniques * which are increasingly expensive. */ where = (Elf_Word *)wherep; offset = ((Elf_Addr)where) - target; if (offset <= (1L<<20) && offset >= -(1L<<20)) { /* * We're within 1MB -- we can use a direct branch * instruction. * * We can generate this pattern: * * sethi %hi(. - .PLT0), %g1 * ba,a %xcc, addr * nop * nop * nop * nop * nop * nop * */ where[1] = BAA | ((offset >> 2) &0x3fffff); flush(where, 4); } else if (target >= 0 && target < (1L<<32)) { /* - * We're withing 32-bits of address zero. + * We're within 32-bits of address zero. * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hi(addr), %g1 * jmp %g1+%lo(addr) * nop * nop * nop * nop * nop * */ where[2] = JMP | LOVAL(target); flush(where, 8); where[1] = SETHI | HIVAL(target, 10); flush(where, 4); } else if (target <= 0 && target > -(1L<<32)) { /* - * We're withing 32-bits of address -1. + * We're within 32-bits of address -1. * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hix(addr), %g1 * xor %g1, %lox(addr), %g1 * jmp %g1 * nop * nop * nop * nop * */ where[3] = JMP; flush(where, 12); where[2] = XOR | ((~target) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(~target, 10); flush(where, 4); } else if (offset <= (1L<<32) && offset >= -((1L<<32) - 4)) { /* - * We're withing 32-bits -- we can use a direct call + * We're within 32-bits -- we can use a direct call * insn * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * mov %o7, %g1 * call (.+offset) * mov %g1, %o7 * nop * nop * nop * nop * */ where[3] = MOV17; flush(where, 12); where[2] = CALL | ((offset >> 4) & 0x3fffffff); flush(where, 8); where[1] = MOV71; flush(where, 4); } else if (offset >= 0 && offset < (1L<<44)) { /* - * We're withing 44 bits. We can generate this + * We're within 44 bits. We can generate this * pattern: * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %h44(addr), %g1 * or %g1, %m44(addr), %g1 * sllx %g1, 12, %g1 * jmp %g1+%l44(addr) * nop * nop * nop * */ where[4] = JMP | LOVAL(offset); flush(where, 16); where[3] = SLLX | 12; flush(where, 12); where[2] = OR | (((offset) >> 12) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(offset, 22); flush(where, 4); } else if (offset < 0 && offset > -(1L<<44)) { /* - * We're withing 44 bits. We can generate this + * We're within 44 bits. We can generate this * pattern: * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %h44(-addr), %g1 * xor %g1, %m44(-addr), %g1 * sllx %g1, 12, %g1 * jmp %g1+%l44(addr) * nop * nop * nop * */ where[4] = JMP | LOVAL(offset); flush(where, 16); where[3] = SLLX | 12; flush(where, 12); where[2] = XOR | (((~offset) >> 12) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(~offset, 22); flush(where, 4); } else { /* * We need to load all 64-bits * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hh(addr), %g1 * sethi %lm(addr), %g5 * or %g1, %hm(addr), %g1 * sllx %g1, 32, %g1 * or %g1, %g5, %g1 * jmp %g1+%lo(addr) * nop * */ where[6] = JMP | LOVAL(target); flush(where, 24); where[5] = ORG5; flush(where, 20); where[4] = SLLX | 32; flush(where, 16); where[3] = OR | LOVAL((target) >> 32); flush(where, 12); where[2] = SETHIG5 | HIVAL(target, 10); flush(where, 8); where[1] = SETHI | HIVAL(target, 42); flush(where, 4); } } else { /* * This is a high PLT slot; the relocation offset specifies a * pointer that needs to be frobbed; no actual code needs to * be modified. The pointer to be calculated needs the addend * added and the reference object relocation base subtraced. */ *wherep = target + rela->r_addend - (Elf_Addr)refobj->relocbase; } return (target); } /* * Install rtld function call into this PLT slot. */ #define SAVE 0x9de3bf50 #define SETHI_l0 0x21000000 #define SETHI_l1 0x23000000 #define OR_l0_l0 0xa0142000 #define SLLX_l0_32_l0 0xa12c3020 #define OR_l0_l1_l0 0xa0140011 #define JMPL_l0_o1 0x93c42000 #define MOV_g1_o0 0x90100001 void init_pltgot(Obj_Entry *obj) { Elf_Word *entry; if (obj->pltgot != NULL) { entry = (Elf_Word *)obj->pltgot; install_plt(&entry[0], (Elf_Addr)_rtld_bind_start_0); install_plt(&entry[8], (Elf_Addr)_rtld_bind_start_1); obj->pltgot[8] = (Elf_Addr)obj; } } static void install_plt(Elf_Word *pltgot, Elf_Addr proc) { pltgot[0] = SAVE; flush(pltgot, 0); pltgot[1] = SETHI_l0 | HIVAL(proc, 42); flush(pltgot, 4); pltgot[2] = SETHI_l1 | HIVAL(proc, 10); flush(pltgot, 8); pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32); flush(pltgot, 12); pltgot[4] = SLLX_l0_32_l0; flush(pltgot, 16); pltgot[5] = OR_l0_l1_l0; flush(pltgot, 20); pltgot[6] = JMPL_l0_o1 | LOVAL(proc); flush(pltgot, 24); pltgot[7] = MOV_g1_o0; flush(pltgot, 28); } void allocate_initial_tls(Obj_Entry *objs) { Elf_Addr* tpval; /* * Fix the size of the static TLS block by using the maximum offset * allocated so far and adding a bit for dynamic modules to use. */ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; tpval = allocate_tls(objs, NULL, 3 * sizeof(Elf_Addr), sizeof(Elf_Addr)); __asm __volatile("mov %0, %%g7" : : "r" (tpval)); } void *__tls_get_addr(tls_index *ti) { register Elf_Addr** tp __asm__("%g7"); return (tls_get_addr_common(tp, ti->ti_module, ti->ti_offset)); } Index: head/libexec/tftpd/tftp-io.c =================================================================== --- head/libexec/tftpd/tftp-io.c (revision 229779) +++ head/libexec/tftpd/tftp-io.c (revision 229780) @@ -1,477 +1,477 @@ /* * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-file.h" #include "tftp-io.h" #include "tftp-utils.h" #include "tftp-options.h" struct sockaddr_storage peer_sock; struct sockaddr_storage me_sock; static int send_packet(int peer, uint16_t block, char *pkt, int size); struct errmsg { int e_code; const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { EOPTNEG, "Option negotiation" }, { -1, NULL } }; #define DROPPACKET(s) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ - tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s); \ + tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return; \ } #define DROPPACKETn(s,n) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ - tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s); \ + tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return (n); \ } const char * errtomsg(int error) { static char ebuf[40]; struct errmsg *pe; char buf[MAXPKTSIZE]; if (error == 0) return ("success"); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return (pe->e_msg); snprintf(ebuf, sizeof(buf), "error %d", error); return (ebuf); } static int send_packet(int peer, uint16_t block, char *pkt, int size) { int i; int t = 1; for (i = 0; i < 12 ; i++) { DROPPACKETn("send_packet", 0); if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) == size) { if (i) tftp_log(LOG_ERR, "%s block %d, attempt %d successful", block, i); return (0); } tftp_log(LOG_ERR, "%s block %d, attempt %d failed (Error %d: %s)", packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), block, i, errno, strerror(errno)); sleep(t); if (t < 32) t <<= 1; } tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); return (1); } /* * Send an ERROR packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ void send_error(int peer, int error) { struct tftphdr *tp; int length; struct errmsg *pe; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error); DROPPACKET("send_error"); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; /* set 'undef' errorcode */ } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg); tp->th_msg[length] = '\0'; length += 5; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); if (sendto(peer, buf, length, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); } /* * Send an WRQ packet (write request). */ int send_wrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_wrq", 1); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)WRQ); size = 2; bp = tp->th_stuff; strcpy(bp, filename); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strcpy(bp, mode); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) size += make_options(peer, bp, sizeof(buf) - size); n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); return (1); } return (0); } /* * Send an RRQ packet (write request). */ int send_rrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_rrq", 1); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)RRQ); size = 2; bp = tp->th_stuff; strcpy(bp, filename); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strcpy(bp, mode); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) { options[OPT_TSIZE].o_request = strdup("0"); size += make_options(peer, bp, sizeof(buf) - size); } n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); return (1); } return (0); } /* * Send an OACK packet (option acknowledgement). */ int send_oack(int peer) { struct tftphdr *tp; int size, i, n; char *bp; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending OACK"); DROPPACKETn("send_oack", 0); /* * Send back an options acknowledgement (only the ones with * a reply for) */ tp = (struct tftphdr *)buf; bp = buf + 2; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)OACK); for (i = 0; options[i].o_type != NULL; i++) { if (options[i].o_reply != NULL) { n = snprintf(bp, size, "%s%c%s", options[i].o_type, 0, options[i].o_reply); bp += n+1; size -= n+1; if (size < 0) { tftp_log(LOG_ERR, "oack: buffer overflow"); exit(1); } } } size = bp - buf; if (sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); return (1); } return (0); } /* * Send an ACK packet (acknowledgement). */ int send_ack(int fp, uint16_t block) { struct tftphdr *tp; int size; char *bp; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); DROPPACKETn("send_ack", 0); tp = (struct tftphdr *)buf; bp = buf + 2; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)ACK); tp->th_block = htons((u_short)block); size = 4; if (sendto(fp, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); return (1); } return (0); } /* * Send a DATA packet */ int send_data(int peer, uint16_t block, char *data, int size) { char buf[MAXPKTSIZE]; struct tftphdr *pkt; int n; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", block, size); DROPPACKETn("send_data", 0); pkt = (struct tftphdr *)buf; pkt->th_opcode = htons((u_short)DATA); pkt->th_block = htons((u_short)block); memcpy(pkt->th_data, data, size); n = send_packet(peer, block, (char *)pkt, size + 4); return (n); } /* * Receive a packet */ jmp_buf timeoutbuf; static void timeout(int sig __unused) { /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ longjmp(timeoutbuf, 1); } int receive_packet(int peer, char *data, int size, struct sockaddr_storage *from, int thistimeout) { struct tftphdr *pkt; struct sockaddr_storage from_local; struct sockaddr_storage *pfrom; socklen_t fromlen; int n; static int waiting; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Waiting %d seconds for packet", timeoutpacket); pkt = (struct tftphdr *)data; waiting = 0; signal(SIGALRM, timeout); setjmp(timeoutbuf); alarm(thistimeout); if (waiting > 0) { alarm(0); return (RP_TIMEOUT); } if (waiting > 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); alarm(0); return (RP_TIMEOUT); } waiting++; pfrom = (from == NULL) ? &from_local : from; fromlen = sizeof(*pfrom); n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); alarm(0); DROPPACKETn("receive_packet", RP_TIMEOUT); if (n < 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); return (RP_TIMEOUT); } alarm(0); if (n < 0) { /* No idea what could have happened if it isn't a timeout */ tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); return (RP_RECVFROM); } if (n < 4) { tftp_log(LOG_ERR, "receive_packet: packet too small (%d bytes)", n); return (RP_TOOSMALL); } pkt->th_opcode = ntohs((u_short)pkt->th_opcode); if (pkt->th_opcode == DATA || pkt->th_opcode == ACK) pkt->th_block = ntohs((u_short)pkt->th_block); if (pkt->th_opcode == DATA && n > pktsize) { tftp_log(LOG_ERR, "receive_packet: packet too big"); return (RP_TOOBIG); } if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { tftp_log(LOG_ERR, "receive_packet: received packet from wrong source"); return (RP_WRONGSOURCE); } if (pkt->th_opcode == ERROR) { tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg); return (RP_ERROR); } if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", n, packettype(pkt->th_opcode)); return n - 4; } Index: head/libexec/tftpd/tftp-utils.h =================================================================== --- head/libexec/tftpd/tftp-utils.h (revision 229779) +++ head/libexec/tftpd/tftp-utils.h (revision 229780) @@ -1,124 +1,124 @@ /* * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* */ #define TIMEOUT 5 #define MAX_TIMEOUTS 5 /* Generic values */ #define MAXSEGSIZE 65464 /* Maximum size of the data segment */ #define MAXPKTSIZE (MAXSEGSIZE + 4) /* Maximum size of the packet */ /* For the blksize option */ -#define BLKSIZE_MIN 8 /* Minumum size of the data segment */ +#define BLKSIZE_MIN 8 /* Minimum size of the data segment */ #define BLKSIZE_MAX MAXSEGSIZE /* Maximum size of the data segment */ /* For the timeout option */ -#define TIMEOUT_MIN 0 /* Minumum timeout value */ +#define TIMEOUT_MIN 0 /* Minimum timeout value */ #define TIMEOUT_MAX 255 /* Maximum timeout value */ #define MIN_TIMEOUTS 3 extern int timeoutpacket; extern int timeoutnetwork; extern int maxtimeouts; int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts); extern uint16_t segsize; extern uint16_t pktsize; extern int acting_as_client; /* */ void unmappedaddr(struct sockaddr_in6 *sin6); ssize_t get_field(int peer, char *buffer, ssize_t size); /* * Packet types */ struct packettypes { int value; const char *const name; }; extern struct packettypes packettypes[]; const char *packettype(int); /* * RP_ */ struct rp_errors { int error; const char *const desc; }; extern struct rp_errors rp_errors[]; char *rp_strerror(int error); /* * Debug features */ #define DEBUG_NONE 0x0000 #define DEBUG_PACKETS 0x0001 #define DEBUG_SIMPLE 0x0002 #define DEBUG_OPTIONS 0x0004 #define DEBUG_ACCESS 0x0008 struct debugs { int value; const char *const name; const char *const desc; }; extern int debug; extern struct debugs debugs[]; extern int packetdroppercentage; int debug_find(char *s); int debug_finds(char *s); const char *debug_show(int d); /* * Log routines */ #define DEBUG(s) tftp_log(LOG_DEBUG, "%s", s) extern int tftp_logtostdout; void tftp_openlog(const char *ident, int logopt, int facility); void tftp_closelog(void); void tftp_log(int priority, const char *message, ...); /* * Performance figures */ struct tftp_stats { size_t amount; int rollovers; uint32_t blocks; int retries; struct timeval tstart; struct timeval tstop; }; void stats_init(struct tftp_stats *ts); void printstats(const char *direction, int verbose, struct tftp_stats *ts);