xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c (revision 11537:8eca52188202)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51926Sas198278  * Common Development and Distribution License (the "License").
61926Sas198278  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  *
21*11537SCasper.Dik@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
220Sstevel@tonic-gate  * Use is subject to license terms.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
270Sstevel@tonic-gate  * All Rights Reserved.
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
320Sstevel@tonic-gate  * The Regents of the University of California.
330Sstevel@tonic-gate  * All Rights Reserved.
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
360Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
370Sstevel@tonic-gate  * contributors.
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * Trivial file transfer protocol server.  A top level process runs in
420Sstevel@tonic-gate  * an infinite loop fielding new TFTP requests.  A child process,
430Sstevel@tonic-gate  * communicating via a pipe with the top level process, sends delayed
440Sstevel@tonic-gate  * NAKs for those that we can't handle.  A new child process is created
450Sstevel@tonic-gate  * to service each request that we can handle.  The top level process
460Sstevel@tonic-gate  * exits after a period of time during which no new requests are
470Sstevel@tonic-gate  * received.
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include <sys/types.h>
510Sstevel@tonic-gate #include <sys/socket.h>
520Sstevel@tonic-gate #include <sys/wait.h>
530Sstevel@tonic-gate #include <sys/stat.h>
540Sstevel@tonic-gate #include <sys/time.h>
550Sstevel@tonic-gate 
560Sstevel@tonic-gate #include <netinet/in.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate #include <arpa/inet.h>
590Sstevel@tonic-gate #include <dirent.h>
600Sstevel@tonic-gate #include <signal.h>
610Sstevel@tonic-gate #include <stdio.h>
620Sstevel@tonic-gate #include <stdlib.h>
630Sstevel@tonic-gate #include <unistd.h>
640Sstevel@tonic-gate #include <errno.h>
650Sstevel@tonic-gate #include <ctype.h>
660Sstevel@tonic-gate #include <netdb.h>
670Sstevel@tonic-gate #include <setjmp.h>
680Sstevel@tonic-gate #include <syslog.h>
690Sstevel@tonic-gate #include <sys/param.h>
700Sstevel@tonic-gate #include <fcntl.h>
710Sstevel@tonic-gate #include <pwd.h>
720Sstevel@tonic-gate #include <string.h>
730Sstevel@tonic-gate #include <priv_utils.h>
740Sstevel@tonic-gate #include "tftpcommon.h"
750Sstevel@tonic-gate 
760Sstevel@tonic-gate #define	TIMEOUT		5
770Sstevel@tonic-gate #define	DELAY_SECS	3
780Sstevel@tonic-gate #define	DALLYSECS 60
790Sstevel@tonic-gate 
800Sstevel@tonic-gate #define	SYSLOG_MSG(message) \
810Sstevel@tonic-gate 	(syslog((((errno == ENETUNREACH) || (errno == EHOSTUNREACH) || \
820Sstevel@tonic-gate 		(errno == ECONNREFUSED)) ? LOG_WARNING : LOG_ERR), message))
830Sstevel@tonic-gate 
840Sstevel@tonic-gate static int			rexmtval = TIMEOUT;
850Sstevel@tonic-gate static int			maxtimeout = 5*TIMEOUT;
860Sstevel@tonic-gate static int			securetftp;
870Sstevel@tonic-gate static int			debug;
880Sstevel@tonic-gate static int			disable_pnp;
890Sstevel@tonic-gate static int			standalone;
900Sstevel@tonic-gate static uid_t			uid_nobody = UID_NOBODY;
910Sstevel@tonic-gate static uid_t			gid_nobody = GID_NOBODY;
920Sstevel@tonic-gate static int			reqsock = -1;
930Sstevel@tonic-gate 				/* file descriptor of request socket */
940Sstevel@tonic-gate static socklen_t		fromlen;
950Sstevel@tonic-gate static socklen_t		fromplen;
960Sstevel@tonic-gate static struct sockaddr_storage	client;
970Sstevel@tonic-gate static struct sockaddr_in6 	*sin6_ptr;
980Sstevel@tonic-gate static struct sockaddr_in	*sin_ptr;
990Sstevel@tonic-gate static struct sockaddr_in6	*from6_ptr;
1000Sstevel@tonic-gate static struct sockaddr_in	*from_ptr;
1010Sstevel@tonic-gate static int			addrfmly;
1020Sstevel@tonic-gate static int			peer;
1030Sstevel@tonic-gate static off_t			tsize;
1040Sstevel@tonic-gate static tftpbuf			ackbuf;
1050Sstevel@tonic-gate static struct sockaddr_storage	from;
1060Sstevel@tonic-gate static boolean_t		tsize_set;
1070Sstevel@tonic-gate static pid_t			child;
1080Sstevel@tonic-gate 				/* pid of child handling delayed replys */
1090Sstevel@tonic-gate static int			delay_fd [2];
1100Sstevel@tonic-gate 				/* pipe for communicating with child */
1110Sstevel@tonic-gate static FILE			*file;
1120Sstevel@tonic-gate static char			*filename;
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static union {
1150Sstevel@tonic-gate 	struct tftphdr	hdr;
1160Sstevel@tonic-gate 	char		data[SEGSIZE + 4];
1170Sstevel@tonic-gate } buf;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate static union {
1200Sstevel@tonic-gate 	struct tftphdr	hdr;
1210Sstevel@tonic-gate 	char		data[SEGSIZE];
1220Sstevel@tonic-gate } oackbuf;
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate struct	delay_info {
1250Sstevel@tonic-gate 	long	timestamp;		/* time request received */
1260Sstevel@tonic-gate 	int	ecode;			/* error code to return */
1270Sstevel@tonic-gate 	struct	sockaddr_storage from;	/* address of client */
1280Sstevel@tonic-gate };
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate int	blocksize = SEGSIZE;	/* Number of data bytes in a DATA packet */
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * Default directory for unqualified names
1340Sstevel@tonic-gate  * Used by TFTP boot procedures
1350Sstevel@tonic-gate  */
1360Sstevel@tonic-gate static char	*homedir = "/tftpboot";
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate struct formats {
1390Sstevel@tonic-gate 	char	*f_mode;
1400Sstevel@tonic-gate 	int	(*f_validate)(int);
1410Sstevel@tonic-gate 	void	(*f_send)(struct formats *, int);
1420Sstevel@tonic-gate 	void	(*f_recv)(struct formats *, int);
1430Sstevel@tonic-gate 	int	f_convert;
1440Sstevel@tonic-gate };
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate static void	delayed_responder(void);
1470Sstevel@tonic-gate static void	tftp(struct tftphdr *, int);
1480Sstevel@tonic-gate static int	validate_filename(int);
1490Sstevel@tonic-gate static void	tftpd_sendfile(struct formats *, int);
1500Sstevel@tonic-gate static void	tftpd_recvfile(struct formats *, int);
1510Sstevel@tonic-gate static void	nak(int);
1520Sstevel@tonic-gate static char	*blksize_handler(int, char *, int *);
1530Sstevel@tonic-gate static char	*timeout_handler(int, char *, int *);
1540Sstevel@tonic-gate static char	*tsize_handler(int, char *, int *);
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate static struct formats formats[] = {
1570Sstevel@tonic-gate 	{ "netascii",	validate_filename, tftpd_sendfile, tftpd_recvfile, 1 },
1580Sstevel@tonic-gate 	{ "octet",	validate_filename, tftpd_sendfile, tftpd_recvfile, 0 },
1590Sstevel@tonic-gate 	{ NULL }
1600Sstevel@tonic-gate };
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate struct options {
1630Sstevel@tonic-gate 	char	*opt_name;
1640Sstevel@tonic-gate 	char	*(*opt_handler)(int, char *, int *);
1650Sstevel@tonic-gate };
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate static struct options options[] = {
1680Sstevel@tonic-gate 	{ "blksize",	blksize_handler },
1690Sstevel@tonic-gate 	{ "timeout",	timeout_handler },
1700Sstevel@tonic-gate 	{ "tsize",	tsize_handler },
1710Sstevel@tonic-gate 	{ NULL }
1720Sstevel@tonic-gate };
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate static char		optbuf[MAX_OPTVAL_LEN];
1750Sstevel@tonic-gate static int		timeout;
1760Sstevel@tonic-gate static sigjmp_buf	timeoutbuf;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate int
main(int argc,char ** argv)1790Sstevel@tonic-gate main(int argc, char **argv)
1800Sstevel@tonic-gate {
1810Sstevel@tonic-gate 	struct tftphdr *tp;
1820Sstevel@tonic-gate 	int n;
1830Sstevel@tonic-gate 	int c;
1840Sstevel@tonic-gate 	struct	passwd *pwd;		/* for "nobody" entry */
1850Sstevel@tonic-gate 	struct in_addr ipv4addr;
1860Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
1870Sstevel@tonic-gate 	socklen_t addrlen;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	openlog("tftpd", LOG_PID, LOG_DAEMON);
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	pwd = getpwnam("nobody");
1920Sstevel@tonic-gate 	if (pwd != NULL) {
1930Sstevel@tonic-gate 		uid_nobody = pwd->pw_uid;
1940Sstevel@tonic-gate 		gid_nobody = pwd->pw_gid;
1950Sstevel@tonic-gate 	}
1960Sstevel@tonic-gate 
197*11537SCasper.Dik@Sun.COM 	/* Tftp will not start new executables; clear the limit set.  */
198*11537SCasper.Dik@Sun.COM 	(void) __init_daemon_priv(PU_CLEARLIMITSET, uid_nobody, gid_nobody,
199*11537SCasper.Dik@Sun.COM 	    PRIV_PROC_CHROOT, PRIV_NET_PRIVADDR, NULL);
2000Sstevel@tonic-gate 
201*11537SCasper.Dik@Sun.COM 	/* Remove the unneeded basic privileges everywhere. */
202*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_PROC_EXEC,
203*11537SCasper.Dik@Sun.COM 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, PRIV_PROC_SESSION, NULL);
204*11537SCasper.Dik@Sun.COM 
205*11537SCasper.Dik@Sun.COM 	/* Remove the other privileges from E until we need them. */
206*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_CHROOT,
207*11537SCasper.Dik@Sun.COM 	    PRIV_NET_PRIVADDR, NULL);
2080Sstevel@tonic-gate 
2098034SSreedhar.Chalamalasetti@Sun.COM 	while ((c = getopt(argc, argv, "dspST:")) != EOF)
2100Sstevel@tonic-gate 		switch (c) {
2110Sstevel@tonic-gate 		case 'd':		/* enable debug */
2120Sstevel@tonic-gate 			debug++;
2130Sstevel@tonic-gate 			continue;
2140Sstevel@tonic-gate 		case 's':		/* secure daemon */
2150Sstevel@tonic-gate 			securetftp = 1;
2160Sstevel@tonic-gate 			continue;
2170Sstevel@tonic-gate 		case 'p':		/* disable name pnp mapping */
2180Sstevel@tonic-gate 			disable_pnp = 1;
2190Sstevel@tonic-gate 			continue;
2200Sstevel@tonic-gate 		case 'S':
2210Sstevel@tonic-gate 			standalone = 1;
2220Sstevel@tonic-gate 			continue;
2238034SSreedhar.Chalamalasetti@Sun.COM 		case 'T':
2248034SSreedhar.Chalamalasetti@Sun.COM 			rexmtval = atoi(optarg);
2258034SSreedhar.Chalamalasetti@Sun.COM 			if (rexmtval <= 0 || rexmtval > MAX_TIMEOUT) {
2268034SSreedhar.Chalamalasetti@Sun.COM 				(void) fprintf(stderr,
2278034SSreedhar.Chalamalasetti@Sun.COM 				    "%s: Invalid retransmission "
2288034SSreedhar.Chalamalasetti@Sun.COM 				    "timeout value: %s\n", argv[0], optarg);
2298034SSreedhar.Chalamalasetti@Sun.COM 				exit(1);
2308034SSreedhar.Chalamalasetti@Sun.COM 			}
2318034SSreedhar.Chalamalasetti@Sun.COM 			maxtimeout = 5 * rexmtval;
2328034SSreedhar.Chalamalasetti@Sun.COM 			continue;
2330Sstevel@tonic-gate 		case '?':
2340Sstevel@tonic-gate 		default:
2350Sstevel@tonic-gate usage:
2360Sstevel@tonic-gate 			(void) fprintf(stderr,
2378034SSreedhar.Chalamalasetti@Sun.COM 			    "usage: %s [-T rexmtval] [-spd] [home-directory]\n",
2388034SSreedhar.Chalamalasetti@Sun.COM 			    argv[0]);
2390Sstevel@tonic-gate 			for (; optind < argc; optind++)
2400Sstevel@tonic-gate 				syslog(LOG_ERR, "bad argument %s",
2410Sstevel@tonic-gate 				    argv[optind]);
2420Sstevel@tonic-gate 			exit(1);
2430Sstevel@tonic-gate 		}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	if (optind < argc)
2460Sstevel@tonic-gate 		if (optind == argc - 1 && *argv [optind] == '/')
2470Sstevel@tonic-gate 			homedir = argv [optind];
2480Sstevel@tonic-gate 		else
2490Sstevel@tonic-gate 			goto usage;
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	if (pipe(delay_fd) < 0) {
2520Sstevel@tonic-gate 		syslog(LOG_ERR, "pipe (main): %m");
2530Sstevel@tonic-gate 		exit(1);
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	(void) sigset(SIGCHLD, SIG_IGN); /* no zombies please */
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	if (standalone) {
2590Sstevel@tonic-gate 		socklen_t clientlen;
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 		sin6_ptr = (struct sockaddr_in6 *)&client;
2620Sstevel@tonic-gate 		clientlen = sizeof (struct sockaddr_in6);
2630Sstevel@tonic-gate 		reqsock = socket(AF_INET6, SOCK_DGRAM, 0);
2640Sstevel@tonic-gate 		if (reqsock == -1) {
2650Sstevel@tonic-gate 			perror("socket");
2660Sstevel@tonic-gate 			exit(1);
2670Sstevel@tonic-gate 		}
2680Sstevel@tonic-gate 		(void) memset(&client, 0, clientlen);
2690Sstevel@tonic-gate 		sin6_ptr->sin6_family = AF_INET6;
2700Sstevel@tonic-gate 		sin6_ptr->sin6_port = htons(IPPORT_TFTP);
2714921Sdm199272 
2724921Sdm199272 		/* Enable privilege as tftp port is < 1024 */
273*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_ON,
2744921Sdm199272 		    PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL);
2750Sstevel@tonic-gate 		if (bind(reqsock, (struct sockaddr *)&client,
2760Sstevel@tonic-gate 		    clientlen) == -1) {
2770Sstevel@tonic-gate 			perror("bind");
2780Sstevel@tonic-gate 			exit(1);
2790Sstevel@tonic-gate 		}
280*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR,
281*11537SCasper.Dik@Sun.COM 		    NULL);
2824921Sdm199272 
2830Sstevel@tonic-gate 		if (debug)
2840Sstevel@tonic-gate 			(void) puts("running in standalone mode...");
2850Sstevel@tonic-gate 	} else {
2860Sstevel@tonic-gate 		/* request socket passed on fd 0 by inetd */
2870Sstevel@tonic-gate 		reqsock = 0;
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 	if (debug) {
2900Sstevel@tonic-gate 		int on = 1;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 		(void) setsockopt(reqsock, SOL_SOCKET, SO_DEBUG,
2930Sstevel@tonic-gate 		    (char *)&on, sizeof (on));
2940Sstevel@tonic-gate 	}
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	(void) chdir(homedir);
2970Sstevel@tonic-gate 
298*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
2990Sstevel@tonic-gate 	if ((child = fork()) < 0) {
3000Sstevel@tonic-gate 		syslog(LOG_ERR, "fork (main): %m");
3010Sstevel@tonic-gate 		exit(1);
3020Sstevel@tonic-gate 	}
303*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	if (child == 0) {
3060Sstevel@tonic-gate 		delayed_responder();
3070Sstevel@tonic-gate 	} /* child */
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	/* close read side of pipe */
3100Sstevel@tonic-gate 	(void) close(delay_fd[0]);
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	/*
3140Sstevel@tonic-gate 	 * Top level handling of incomming tftp requests.  Read a request
3150Sstevel@tonic-gate 	 * and pass it off to be handled.  If request is valid, handling
3160Sstevel@tonic-gate 	 * forks off and parent returns to this loop.  If no new requests
3170Sstevel@tonic-gate 	 * are received for DALLYSECS, exit and return to inetd.
3180Sstevel@tonic-gate 	 */
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	for (;;) {
3210Sstevel@tonic-gate 		fd_set readfds;
3220Sstevel@tonic-gate 		struct timeval dally;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 		FD_ZERO(&readfds);
3250Sstevel@tonic-gate 		FD_SET(reqsock, &readfds);
3260Sstevel@tonic-gate 		dally.tv_sec = DALLYSECS;
3270Sstevel@tonic-gate 		dally.tv_usec = 0;
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 		n = select(reqsock + 1, &readfds, NULL, NULL, &dally);
3300Sstevel@tonic-gate 		if (n < 0) {
3310Sstevel@tonic-gate 			if (errno == EINTR)
3320Sstevel@tonic-gate 				continue;
3330Sstevel@tonic-gate 			syslog(LOG_ERR, "select: %m");
3340Sstevel@tonic-gate 			(void) kill(child, SIGKILL);
3350Sstevel@tonic-gate 			exit(1);
3360Sstevel@tonic-gate 		}
3370Sstevel@tonic-gate 		if (n == 0) {
3380Sstevel@tonic-gate 			/* Select timed out.  Its time to die. */
3390Sstevel@tonic-gate 			if (standalone)
3400Sstevel@tonic-gate 				continue;
3410Sstevel@tonic-gate 			else {
3420Sstevel@tonic-gate 				(void) kill(child, SIGKILL);
3430Sstevel@tonic-gate 				exit(0);
3440Sstevel@tonic-gate 			}
3450Sstevel@tonic-gate 		}
3460Sstevel@tonic-gate 		addrlen = sizeof (from);
3470Sstevel@tonic-gate 		if (getsockname(reqsock, (struct sockaddr  *)&from,
3480Sstevel@tonic-gate 		    &addrlen) < 0) {
3490Sstevel@tonic-gate 			syslog(LOG_ERR, "getsockname: %m");
3500Sstevel@tonic-gate 			exit(1);
3510Sstevel@tonic-gate 		}
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		switch (from.ss_family) {
3540Sstevel@tonic-gate 		case AF_INET:
3550Sstevel@tonic-gate 			fromlen = (socklen_t)sizeof (struct sockaddr_in);
3560Sstevel@tonic-gate 			break;
3570Sstevel@tonic-gate 		case AF_INET6:
3580Sstevel@tonic-gate 			fromlen = (socklen_t)sizeof (struct sockaddr_in6);
3590Sstevel@tonic-gate 			break;
3600Sstevel@tonic-gate 		default:
3610Sstevel@tonic-gate 			syslog(LOG_ERR,
3620Sstevel@tonic-gate 			    "Unknown address Family on peer connection %d",
3630Sstevel@tonic-gate 			    from.ss_family);
3640Sstevel@tonic-gate 			exit(1);
3650Sstevel@tonic-gate 		}
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 		n = recvfrom(reqsock, &buf, sizeof (buf), 0,
3684921Sdm199272 		    (struct sockaddr *)&from, &fromlen);
3690Sstevel@tonic-gate 		if (n < 0) {
3700Sstevel@tonic-gate 			if (errno == EINTR)
3710Sstevel@tonic-gate 				continue;
3720Sstevel@tonic-gate 			if (standalone)
3730Sstevel@tonic-gate 				perror("recvfrom");
3740Sstevel@tonic-gate 			else
3750Sstevel@tonic-gate 				syslog(LOG_ERR, "recvfrom: %m");
3760Sstevel@tonic-gate 			(void) kill(child, SIGKILL);
3770Sstevel@tonic-gate 			exit(1);
3780Sstevel@tonic-gate 		}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 		(void) alarm(0);
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 		switch (from.ss_family) {
3830Sstevel@tonic-gate 		case AF_INET:
3840Sstevel@tonic-gate 			addrfmly = AF_INET;
3850Sstevel@tonic-gate 			fromplen = sizeof (struct sockaddr_in);
3860Sstevel@tonic-gate 			sin_ptr = (struct sockaddr_in *)&client;
3870Sstevel@tonic-gate 			(void) memset(&client, 0, fromplen);
3880Sstevel@tonic-gate 			sin_ptr->sin_family = AF_INET;
3890Sstevel@tonic-gate 			break;
3900Sstevel@tonic-gate 		case AF_INET6:
3910Sstevel@tonic-gate 			addrfmly = AF_INET6;
3920Sstevel@tonic-gate 			fromplen = sizeof (struct sockaddr_in6);
3930Sstevel@tonic-gate 			sin6_ptr = (struct sockaddr_in6 *)&client;
3940Sstevel@tonic-gate 			(void) memset(&client, 0, fromplen);
3950Sstevel@tonic-gate 			sin6_ptr->sin6_family = AF_INET6;
3960Sstevel@tonic-gate 			break;
3970Sstevel@tonic-gate 		default:
3980Sstevel@tonic-gate 			syslog(LOG_ERR,
3990Sstevel@tonic-gate 			    "Unknown address Family on peer connection");
4000Sstevel@tonic-gate 			exit(1);
4010Sstevel@tonic-gate 		}
4020Sstevel@tonic-gate 		peer = socket(addrfmly, SOCK_DGRAM, 0);
4030Sstevel@tonic-gate 		if (peer < 0) {
4040Sstevel@tonic-gate 			if (standalone)
4050Sstevel@tonic-gate 				perror("socket (main)");
4060Sstevel@tonic-gate 			else
4070Sstevel@tonic-gate 				syslog(LOG_ERR, "socket (main): %m");
4080Sstevel@tonic-gate 			(void) kill(child, SIGKILL);
4090Sstevel@tonic-gate 			exit(1);
4100Sstevel@tonic-gate 		}
4110Sstevel@tonic-gate 		if (debug) {
4120Sstevel@tonic-gate 			int on = 1;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 			(void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
4150Sstevel@tonic-gate 			    (char *)&on, sizeof (on));
4160Sstevel@tonic-gate 		}
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 		if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
4190Sstevel@tonic-gate 			if (standalone)
4200Sstevel@tonic-gate 				perror("bind (main)");
4210Sstevel@tonic-gate 			else
4220Sstevel@tonic-gate 				syslog(LOG_ERR, "bind (main): %m");
4230Sstevel@tonic-gate 			(void) kill(child, SIGKILL);
4240Sstevel@tonic-gate 			exit(1);
4250Sstevel@tonic-gate 		}
4260Sstevel@tonic-gate 		if (standalone && debug) {
4270Sstevel@tonic-gate 			sin6_ptr = (struct sockaddr_in6 *)&client;
4280Sstevel@tonic-gate 			from6_ptr = (struct sockaddr_in6 *)&from;
4290Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&from6_ptr->sin6_addr)) {
4300Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&from6_ptr->sin6_addr,
4310Sstevel@tonic-gate 				    &ipv4addr);
4320Sstevel@tonic-gate 				(void) inet_ntop(AF_INET, &ipv4addr, abuf,
4330Sstevel@tonic-gate 				    sizeof (abuf));
4340Sstevel@tonic-gate 			} else {
4350Sstevel@tonic-gate 				(void) inet_ntop(AF_INET6,
4360Sstevel@tonic-gate 				    &from6_ptr->sin6_addr, abuf,
4370Sstevel@tonic-gate 				    sizeof (abuf));
4380Sstevel@tonic-gate 			}
4390Sstevel@tonic-gate 			/* get local port */
4400Sstevel@tonic-gate 			if (getsockname(peer, (struct sockaddr *)&client,
4410Sstevel@tonic-gate 			    &fromplen) < 0)
4420Sstevel@tonic-gate 				perror("getsockname (main)");
4430Sstevel@tonic-gate 			(void) fprintf(stderr,
4440Sstevel@tonic-gate 			    "request from %s port %d; local port %d\n",
4450Sstevel@tonic-gate 			    abuf, from6_ptr->sin6_port, sin6_ptr->sin6_port);
4460Sstevel@tonic-gate 		}
4470Sstevel@tonic-gate 		tp = &buf.hdr;
4480Sstevel@tonic-gate 		tp->th_opcode = ntohs((ushort_t)tp->th_opcode);
4490Sstevel@tonic-gate 		if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
4500Sstevel@tonic-gate 			tftp(tp, n);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		(void) close(peer);
4530Sstevel@tonic-gate 		(void) fclose(file);
4540Sstevel@tonic-gate 	}
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	/*NOTREACHED*/
4570Sstevel@tonic-gate 	return (0);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate static void
delayed_responder(void)4610Sstevel@tonic-gate delayed_responder(void)
4620Sstevel@tonic-gate {
4630Sstevel@tonic-gate 	struct delay_info dinfo;
4640Sstevel@tonic-gate 	long now;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	/* we don't use the descriptors passed in to the parent */
4670Sstevel@tonic-gate 	(void) close(0);
4680Sstevel@tonic-gate 	(void) close(1);
4690Sstevel@tonic-gate 	if (standalone)
4700Sstevel@tonic-gate 		(void) close(reqsock);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	/* close write side of pipe */
4730Sstevel@tonic-gate 	(void) close(delay_fd[1]);
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	for (;;) {
4760Sstevel@tonic-gate 		int n;
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		if ((n = read(delay_fd[0], &dinfo,
4790Sstevel@tonic-gate 		    sizeof (dinfo))) != sizeof (dinfo)) {
4800Sstevel@tonic-gate 			if (n < 0) {
4810Sstevel@tonic-gate 				if (errno == EINTR)
4820Sstevel@tonic-gate 					continue;
4830Sstevel@tonic-gate 				if (standalone)
4840Sstevel@tonic-gate 					perror("read from pipe "
4850Sstevel@tonic-gate 					    "(delayed responder)");
4860Sstevel@tonic-gate 				else
4870Sstevel@tonic-gate 					syslog(LOG_ERR, "read from pipe: %m");
4880Sstevel@tonic-gate 			}
4890Sstevel@tonic-gate 			exit(1);
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 		switch (dinfo.from.ss_family) {
4920Sstevel@tonic-gate 		case AF_INET:
4930Sstevel@tonic-gate 			addrfmly = AF_INET;
4940Sstevel@tonic-gate 			fromplen = sizeof (struct sockaddr_in);
4950Sstevel@tonic-gate 			sin_ptr = (struct sockaddr_in *)&client;
4960Sstevel@tonic-gate 			(void) memset(&client, 0, fromplen);
4970Sstevel@tonic-gate 			sin_ptr->sin_family = AF_INET;
4980Sstevel@tonic-gate 			break;
4990Sstevel@tonic-gate 		case AF_INET6:
5000Sstevel@tonic-gate 			addrfmly = AF_INET6;
5010Sstevel@tonic-gate 			fromplen = sizeof (struct sockaddr_in6);
5020Sstevel@tonic-gate 			sin6_ptr = (struct sockaddr_in6 *)&client;
5030Sstevel@tonic-gate 			(void) memset(&client, 0, fromplen);
5040Sstevel@tonic-gate 			sin6_ptr->sin6_family = AF_INET6;
5050Sstevel@tonic-gate 			break;
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		peer = socket(addrfmly, SOCK_DGRAM, 0);
5080Sstevel@tonic-gate 		if (peer == -1) {
5090Sstevel@tonic-gate 			if (standalone)
5100Sstevel@tonic-gate 				perror("socket (delayed responder)");
5110Sstevel@tonic-gate 			else
5120Sstevel@tonic-gate 				syslog(LOG_ERR, "socket (delay): %m");
5130Sstevel@tonic-gate 			exit(1);
5140Sstevel@tonic-gate 		}
5150Sstevel@tonic-gate 		if (debug) {
5160Sstevel@tonic-gate 			int on = 1;
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 			(void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
5190Sstevel@tonic-gate 			    (char *)&on, sizeof (on));
5200Sstevel@tonic-gate 		}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 		if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
5230Sstevel@tonic-gate 			if (standalone)
5240Sstevel@tonic-gate 				perror("bind (delayed responder)");
5250Sstevel@tonic-gate 			else
5260Sstevel@tonic-gate 				syslog(LOG_ERR, "bind (delay): %m");
5270Sstevel@tonic-gate 			exit(1);
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 		if (client.ss_family == AF_INET) {
5300Sstevel@tonic-gate 			from_ptr = (struct sockaddr_in *)&dinfo.from;
5310Sstevel@tonic-gate 			from_ptr->sin_family = AF_INET;
5320Sstevel@tonic-gate 		} else {
5330Sstevel@tonic-gate 			from6_ptr = (struct sockaddr_in6 *)&dinfo.from;
5340Sstevel@tonic-gate 			from6_ptr->sin6_family = AF_INET6;
5350Sstevel@tonic-gate 		}
5360Sstevel@tonic-gate 		/*
5370Sstevel@tonic-gate 		 * Since a request hasn't been received from the client
5380Sstevel@tonic-gate 		 * before the delayed responder process is forked, the
5390Sstevel@tonic-gate 		 * from variable is uninitialized.  So set it to contain
5400Sstevel@tonic-gate 		 * the client address.
5410Sstevel@tonic-gate 		 */
5420Sstevel@tonic-gate 		from = dinfo.from;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		/*
5450Sstevel@tonic-gate 		 * only sleep if DELAY_SECS has not elapsed since
5460Sstevel@tonic-gate 		 * original request was received.  Ensure that `now'
5470Sstevel@tonic-gate 		 * is not earlier than `dinfo.timestamp'
5480Sstevel@tonic-gate 		 */
5490Sstevel@tonic-gate 		now = time(0);
5500Sstevel@tonic-gate 		if ((uint_t)(now - dinfo.timestamp) < DELAY_SECS)
5510Sstevel@tonic-gate 			(void) sleep(DELAY_SECS - (now - dinfo.timestamp));
5520Sstevel@tonic-gate 		nak(dinfo.ecode);
5530Sstevel@tonic-gate 		(void) close(peer);
5540Sstevel@tonic-gate 	} /* for */
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	/* NOTREACHED */
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate /*
5600Sstevel@tonic-gate  * Handle the Blocksize option.
5610Sstevel@tonic-gate  * Return the blksize option value string to include in the OACK reply.
5620Sstevel@tonic-gate  */
5630Sstevel@tonic-gate /*ARGSUSED*/
5640Sstevel@tonic-gate static char *
blksize_handler(int opcode,char * optval,int * errcode)5650Sstevel@tonic-gate blksize_handler(int opcode, char *optval, int *errcode)
5660Sstevel@tonic-gate {
5670Sstevel@tonic-gate 	char *endp;
5680Sstevel@tonic-gate 	int value;
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	*errcode = -1;
5710Sstevel@tonic-gate 	errno = 0;
5720Sstevel@tonic-gate 	value = (int)strtol(optval, &endp, 10);
5730Sstevel@tonic-gate 	if (errno != 0 || value < MIN_BLKSIZE || *endp != '\0')
5740Sstevel@tonic-gate 		return (NULL);
5750Sstevel@tonic-gate 	/*
5760Sstevel@tonic-gate 	 * As the blksize value in the OACK reply can be less than the value
5770Sstevel@tonic-gate 	 * requested, to support broken clients if the value requested is larger
5780Sstevel@tonic-gate 	 * than allowed in the RFC, reply with the maximum value permitted.
5790Sstevel@tonic-gate 	 */
5800Sstevel@tonic-gate 	if (value > MAX_BLKSIZE)
5810Sstevel@tonic-gate 		value = MAX_BLKSIZE;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	blocksize = value;
5840Sstevel@tonic-gate 	(void) snprintf(optbuf, sizeof (optbuf), "%d", blocksize);
5850Sstevel@tonic-gate 	return (optbuf);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate /*
5890Sstevel@tonic-gate  * Handle the Timeout Interval option.
5900Sstevel@tonic-gate  * Return the timeout option value string to include in the OACK reply.
5910Sstevel@tonic-gate  */
5920Sstevel@tonic-gate /*ARGSUSED*/
5930Sstevel@tonic-gate static char *
timeout_handler(int opcode,char * optval,int * errcode)5940Sstevel@tonic-gate timeout_handler(int opcode, char *optval, int *errcode)
5950Sstevel@tonic-gate {
5960Sstevel@tonic-gate 	char *endp;
5970Sstevel@tonic-gate 	int value;
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	*errcode = -1;
6000Sstevel@tonic-gate 	errno = 0;
6010Sstevel@tonic-gate 	value = (int)strtol(optval, &endp, 10);
6020Sstevel@tonic-gate 	if (errno != 0 || *endp != '\0')
6030Sstevel@tonic-gate 		return (NULL);
6040Sstevel@tonic-gate 	/*
6050Sstevel@tonic-gate 	 * The timeout value in the OACK reply must match the value specified
6060Sstevel@tonic-gate 	 * by the client, so if an invalid timeout is requested don't include
6070Sstevel@tonic-gate 	 * the timeout option in the OACK reply.
6080Sstevel@tonic-gate 	 */
6090Sstevel@tonic-gate 	if (value < MIN_TIMEOUT || value > MAX_TIMEOUT)
6100Sstevel@tonic-gate 		return (NULL);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	rexmtval = value;
6130Sstevel@tonic-gate 	maxtimeout = 5 * rexmtval;
6140Sstevel@tonic-gate 	(void) snprintf(optbuf, sizeof (optbuf), "%d", rexmtval);
6150Sstevel@tonic-gate 	return (optbuf);
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate  * Handle the Transfer Size option.
6200Sstevel@tonic-gate  * Return the tsize option value string to include in the OACK reply.
6210Sstevel@tonic-gate  */
6220Sstevel@tonic-gate static char *
tsize_handler(int opcode,char * optval,int * errcode)6230Sstevel@tonic-gate tsize_handler(int opcode, char *optval, int *errcode)
6240Sstevel@tonic-gate {
6250Sstevel@tonic-gate 	char *endp;
6260Sstevel@tonic-gate 	longlong_t value;
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	*errcode = -1;
6290Sstevel@tonic-gate 	errno = 0;
6300Sstevel@tonic-gate 	value = strtoll(optval, &endp, 10);
6310Sstevel@tonic-gate 	if (errno != 0 || value < 0 || *endp != '\0')
6320Sstevel@tonic-gate 		return (NULL);
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	if (opcode == RRQ) {
6350Sstevel@tonic-gate 		if (tsize_set == B_FALSE)
6360Sstevel@tonic-gate 			return (NULL);
6370Sstevel@tonic-gate 		/*
6380Sstevel@tonic-gate 		 * The tsize value should be 0 for a read request, but to
6390Sstevel@tonic-gate 		 * support broken clients we don't check that it is.
6400Sstevel@tonic-gate 		 */
6410Sstevel@tonic-gate 	} else {
6420Sstevel@tonic-gate #if _FILE_OFFSET_BITS == 32
6430Sstevel@tonic-gate 		if (value > MAXOFF_T) {
6440Sstevel@tonic-gate 			*errcode = ENOSPACE;
6450Sstevel@tonic-gate 			return (NULL);
6460Sstevel@tonic-gate 		}
6470Sstevel@tonic-gate #endif
6480Sstevel@tonic-gate 		tsize = value;
6490Sstevel@tonic-gate 		tsize_set = B_TRUE;
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate 	(void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize);
6520Sstevel@tonic-gate 	return (optbuf);
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate /*
6560Sstevel@tonic-gate  * Process any options included by the client in the request packet.
6570Sstevel@tonic-gate  * Return the size of the OACK reply packet built or 0 for no OACK reply.
6580Sstevel@tonic-gate  */
6590Sstevel@tonic-gate static int
process_options(int opcode,char * opts,char * endopts)6600Sstevel@tonic-gate process_options(int opcode, char *opts, char *endopts)
6610Sstevel@tonic-gate {
6620Sstevel@tonic-gate 	char *cp, *optname, *optval, *ostr, *oackend;
6630Sstevel@tonic-gate 	struct tftphdr *oackp;
6640Sstevel@tonic-gate 	int i, errcode;
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	/*
6670Sstevel@tonic-gate 	 * To continue to interoperate with broken TFTP clients, ignore
6680Sstevel@tonic-gate 	 * null padding appended to requests which don't include options.
6690Sstevel@tonic-gate 	 */
6700Sstevel@tonic-gate 	cp = opts;
6710Sstevel@tonic-gate 	while ((cp < endopts) && (*cp == '\0'))
6720Sstevel@tonic-gate 		cp++;
6730Sstevel@tonic-gate 	if (cp == endopts)
6740Sstevel@tonic-gate 		return (0);
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	/*
6770Sstevel@tonic-gate 	 * Construct an Option ACKnowledgement packet if any requested option
6780Sstevel@tonic-gate 	 * is recognized.
6790Sstevel@tonic-gate 	 */
6800Sstevel@tonic-gate 	oackp = &oackbuf.hdr;
6810Sstevel@tonic-gate 	oackend = oackbuf.data + sizeof (oackbuf.data);
6820Sstevel@tonic-gate 	oackp->th_opcode = htons((ushort_t)OACK);
6830Sstevel@tonic-gate 	cp = (char *)&oackp->th_stuff;
6840Sstevel@tonic-gate 	while (opts < endopts) {
6850Sstevel@tonic-gate 		optname = opts;
6860Sstevel@tonic-gate 		if ((optval = next_field(optname, endopts)) == NULL) {
6870Sstevel@tonic-gate 			nak(EOPTNEG);
6880Sstevel@tonic-gate 			exit(1);
6890Sstevel@tonic-gate 		}
6900Sstevel@tonic-gate 		if ((opts = next_field(optval, endopts)) == NULL) {
6910Sstevel@tonic-gate 			nak(EOPTNEG);
6920Sstevel@tonic-gate 			exit(1);
6930Sstevel@tonic-gate 		}
6940Sstevel@tonic-gate 		for (i = 0; options[i].opt_name != NULL; i++) {
6950Sstevel@tonic-gate 			if (strcasecmp(optname, options[i].opt_name) == 0)
6960Sstevel@tonic-gate 				break;
6970Sstevel@tonic-gate 		}
6980Sstevel@tonic-gate 		if (options[i].opt_name != NULL) {
6990Sstevel@tonic-gate 			ostr = options[i].opt_handler(opcode, optval, &errcode);
7000Sstevel@tonic-gate 			if (ostr != NULL) {
7010Sstevel@tonic-gate 				cp += strlcpy(cp, options[i].opt_name,
7020Sstevel@tonic-gate 				    oackend - cp) + 1;
7030Sstevel@tonic-gate 				if (cp <= oackend)
7040Sstevel@tonic-gate 					cp += strlcpy(cp, ostr, oackend - cp)
7050Sstevel@tonic-gate 					    + 1;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 				if (cp > oackend) {
7080Sstevel@tonic-gate 					nak(EOPTNEG);
7090Sstevel@tonic-gate 					exit(1);
7100Sstevel@tonic-gate 				}
7110Sstevel@tonic-gate 			} else if (errcode >= 0) {
7120Sstevel@tonic-gate 				nak(errcode);
7130Sstevel@tonic-gate 				exit(1);
7140Sstevel@tonic-gate 			}
7150Sstevel@tonic-gate 		}
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate 	if (cp != (char *)&oackp->th_stuff)
7180Sstevel@tonic-gate 		return (cp - oackbuf.data);
7190Sstevel@tonic-gate 	return (0);
7200Sstevel@tonic-gate }
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate /*
7230Sstevel@tonic-gate  * Handle access errors caused by client requests.
7240Sstevel@tonic-gate  */
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate static void
delay_exit(int ecode)7270Sstevel@tonic-gate delay_exit(int ecode)
7280Sstevel@tonic-gate {
7290Sstevel@tonic-gate 	struct delay_info dinfo;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	/*
7320Sstevel@tonic-gate 	 * The most likely cause of an error here is that
7330Sstevel@tonic-gate 	 * someone has broadcast an RRQ packet because s/he's
7340Sstevel@tonic-gate 	 * trying to boot and doesn't know who the server is.
7350Sstevel@tonic-gate 	 * Rather then sending an ERROR packet immediately, we
7360Sstevel@tonic-gate 	 * wait a while so that the real server has a better chance
7370Sstevel@tonic-gate 	 * of getting through (in case client has lousy Ethernet
7380Sstevel@tonic-gate 	 * interface).  We write to a child that handles delayed
7390Sstevel@tonic-gate 	 * ERROR packets to avoid delaying service to new
7400Sstevel@tonic-gate 	 * requests.  Of course, we would rather just not answer
7410Sstevel@tonic-gate 	 * RRQ packets that are broadcasted, but there's no way
7420Sstevel@tonic-gate 	 * for a user process to determine this.
7430Sstevel@tonic-gate 	 */
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	dinfo.timestamp = time(0);
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	/*
7480Sstevel@tonic-gate 	 * If running in secure mode, we map all errors to EACCESS
7490Sstevel@tonic-gate 	 * so that the client gets no information about which files
7500Sstevel@tonic-gate 	 * or directories exist.
7510Sstevel@tonic-gate 	 */
7520Sstevel@tonic-gate 	if (securetftp)
7530Sstevel@tonic-gate 		dinfo.ecode = EACCESS;
7540Sstevel@tonic-gate 	else
7550Sstevel@tonic-gate 		dinfo.ecode = ecode;
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	dinfo.from = from;
7580Sstevel@tonic-gate 	if (write(delay_fd[1], &dinfo, sizeof (dinfo)) !=
7590Sstevel@tonic-gate 	    sizeof (dinfo)) {
7600Sstevel@tonic-gate 		syslog(LOG_ERR, "delayed write failed.");
7610Sstevel@tonic-gate 		(void) kill(child, SIGKILL);
7620Sstevel@tonic-gate 		exit(1);
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate 	exit(0);
7650Sstevel@tonic-gate }
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate /*
7680Sstevel@tonic-gate  * Handle initial connection protocol.
7690Sstevel@tonic-gate  */
7700Sstevel@tonic-gate static void
tftp(struct tftphdr * tp,int size)7710Sstevel@tonic-gate tftp(struct tftphdr *tp, int size)
7720Sstevel@tonic-gate {
7730Sstevel@tonic-gate 	char *cp;
7740Sstevel@tonic-gate 	int readmode, ecode;
7750Sstevel@tonic-gate 	struct formats *pf;
7760Sstevel@tonic-gate 	char *mode;
7770Sstevel@tonic-gate 	int fd;
7780Sstevel@tonic-gate 	static boolean_t firsttime = B_TRUE;
7790Sstevel@tonic-gate 	int oacklen;
7800Sstevel@tonic-gate 	struct stat statb;
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	readmode = (tp->th_opcode == RRQ);
7830Sstevel@tonic-gate 	filename = (char *)&tp->th_stuff;
7840Sstevel@tonic-gate 	mode = next_field(filename, &buf.data[size]);
7850Sstevel@tonic-gate 	cp = (mode != NULL) ? next_field(mode, &buf.data[size]) : NULL;
7860Sstevel@tonic-gate 	if (cp == NULL) {
7870Sstevel@tonic-gate 		nak(EBADOP);
7880Sstevel@tonic-gate 		exit(1);
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 	if (debug && standalone) {
7910Sstevel@tonic-gate 		(void) fprintf(stderr, "%s for %s %s ",
7920Sstevel@tonic-gate 		    readmode ? "RRQ" : "WRQ", filename, mode);
7930Sstevel@tonic-gate 		print_options(stderr, cp, size + buf.data - cp);
7940Sstevel@tonic-gate 		(void) putc('\n', stderr);
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 	for (pf = formats; pf->f_mode != NULL; pf++)
7970Sstevel@tonic-gate 		if (strcasecmp(pf->f_mode, mode) == 0)
7980Sstevel@tonic-gate 			break;
7990Sstevel@tonic-gate 	if (pf->f_mode == NULL) {
8000Sstevel@tonic-gate 		nak(EBADOP);
8010Sstevel@tonic-gate 		exit(1);
8020Sstevel@tonic-gate 	}
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	/*
8050Sstevel@tonic-gate 	 * XXX fork a new process to handle this request before
8060Sstevel@tonic-gate 	 * chroot(), otherwise the parent won't be able to create a
8070Sstevel@tonic-gate 	 * new socket as that requires library access to system files
8080Sstevel@tonic-gate 	 * and devices.
8090Sstevel@tonic-gate 	 */
810*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
8110Sstevel@tonic-gate 	switch (fork()) {
8120Sstevel@tonic-gate 	case -1:
8130Sstevel@tonic-gate 		syslog(LOG_ERR, "fork (tftp): %m");
814*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
8150Sstevel@tonic-gate 		return;
8160Sstevel@tonic-gate 	case 0:
817*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
8180Sstevel@tonic-gate 		break;
8190Sstevel@tonic-gate 	default:
820*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
8210Sstevel@tonic-gate 		return;
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	/*
8250Sstevel@tonic-gate 	 * Try to see if we can access the file.  The access can still
8260Sstevel@tonic-gate 	 * fail later if we are running in secure mode because of
8270Sstevel@tonic-gate 	 * the chroot() call.  We only want to execute the chroot()  once.
8280Sstevel@tonic-gate 	 */
8290Sstevel@tonic-gate 	if (securetftp && firsttime) {
830*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_CHROOT,
831*11537SCasper.Dik@Sun.COM 		    NULL);
8320Sstevel@tonic-gate 		if (chroot(homedir) == -1) {
8330Sstevel@tonic-gate 			syslog(LOG_ERR,
8340Sstevel@tonic-gate 			    "tftpd: cannot chroot to directory %s: %m\n",
8350Sstevel@tonic-gate 			    homedir);
8360Sstevel@tonic-gate 			delay_exit(EACCESS);
8370Sstevel@tonic-gate 		}
8380Sstevel@tonic-gate 		else
8390Sstevel@tonic-gate 		{
8400Sstevel@tonic-gate 			firsttime = B_FALSE;
8410Sstevel@tonic-gate 		}
842*11537SCasper.Dik@Sun.COM 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_CHROOT,
843*11537SCasper.Dik@Sun.COM 		    NULL);
8440Sstevel@tonic-gate 		(void) chdir("/");  /* cd to  new root */
8450Sstevel@tonic-gate 	}
846*11537SCasper.Dik@Sun.COM 	(void) priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_PROC_CHROOT,
847*11537SCasper.Dik@Sun.COM 	    PRIV_NET_PRIVADDR, NULL);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	ecode = (*pf->f_validate)(tp->th_opcode);
8500Sstevel@tonic-gate 	if (ecode != 0)
8510Sstevel@tonic-gate 		delay_exit(ecode);
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	/* we don't use the descriptors passed in to the parent */
8540Sstevel@tonic-gate 	(void) close(STDIN_FILENO);
8550Sstevel@tonic-gate 	(void) close(STDOUT_FILENO);
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	/*
8580Sstevel@tonic-gate 	 * Try to open file as low-priv setuid/setgid.  Note that
8590Sstevel@tonic-gate 	 * a chroot() has already been done.
8600Sstevel@tonic-gate 	 */
8610Sstevel@tonic-gate 	fd = open(filename,
8620Sstevel@tonic-gate 	    (readmode ? O_RDONLY : (O_WRONLY|O_TRUNC)) | O_NONBLOCK);
8630Sstevel@tonic-gate 	if ((fd < 0) || (fstat(fd, &statb) < 0))
8640Sstevel@tonic-gate 		delay_exit((errno == ENOENT) ? ENOTFOUND : EACCESS);
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	if (((statb.st_mode & ((readmode) ? S_IROTH : S_IWOTH)) == 0) ||
8670Sstevel@tonic-gate 	    ((statb.st_mode & S_IFMT) != S_IFREG))
8680Sstevel@tonic-gate 		delay_exit(EACCESS);
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	file = fdopen(fd, readmode ? "r" : "w");
8710Sstevel@tonic-gate 	if (file == NULL)
8720Sstevel@tonic-gate 		delay_exit(errno + 100);
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	/* Don't know the size of transfers which involve conversion */
8750Sstevel@tonic-gate 	tsize_set = (readmode && (pf->f_convert == 0));
8760Sstevel@tonic-gate 	if (tsize_set)
8770Sstevel@tonic-gate 		tsize = statb.st_size;
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	/* Deal with any options sent by the client */
8800Sstevel@tonic-gate 	oacklen = process_options(tp->th_opcode, cp, buf.data + size);
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	if (tp->th_opcode == WRQ)
8830Sstevel@tonic-gate 		(*pf->f_recv)(pf, oacklen);
8840Sstevel@tonic-gate 	else
8850Sstevel@tonic-gate 		(*pf->f_send)(pf, oacklen);
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	exit(0);
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate /*
8910Sstevel@tonic-gate  *	Maybe map filename into another one.
8920Sstevel@tonic-gate  *
8930Sstevel@tonic-gate  *	For PNP, we get TFTP boot requests for filenames like
8940Sstevel@tonic-gate  *	<Unknown Hex IP Addr>.<Architecture Name>.   We must
8950Sstevel@tonic-gate  *	map these to 'pnp.<Architecture Name>'.  Note that
8960Sstevel@tonic-gate  *	uppercase is mapped to lowercase in the architecture names.
8970Sstevel@tonic-gate  *
8980Sstevel@tonic-gate  *	For names <Hex IP Addr> there are two cases.  First,
8990Sstevel@tonic-gate  *	it may be a buggy prom that omits the architecture code.
9000Sstevel@tonic-gate  *	So first check if <Hex IP Addr>.<arch> is on the filesystem.
9010Sstevel@tonic-gate  *	Second, this is how most Sun3s work; assume <arch> is sun3.
9020Sstevel@tonic-gate  */
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate static char *
pnp_check(char * origname)9050Sstevel@tonic-gate pnp_check(char *origname)
9060Sstevel@tonic-gate {
9070Sstevel@tonic-gate 	static char buf [MAXNAMLEN + 1];
9080Sstevel@tonic-gate 	char *arch, *s, *bufend;
9090Sstevel@tonic-gate 	in_addr_t ipaddr;
9100Sstevel@tonic-gate 	int len = (origname ? strlen(origname) : 0);
9110Sstevel@tonic-gate 	DIR *dir;
9120Sstevel@tonic-gate 	struct dirent *dp;
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	if (securetftp || disable_pnp || len < 8 || len > 14)
9150Sstevel@tonic-gate 		return (NULL);
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	/*
9180Sstevel@tonic-gate 	 * XXX see if this cable allows pnp; if not, return NULL
9190Sstevel@tonic-gate 	 * Requires YP support for determining this!
9200Sstevel@tonic-gate 	 */
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	ipaddr = htonl(strtol(origname, &arch, 16));
9230Sstevel@tonic-gate 	if ((arch == NULL) || (len > 8 && *arch != '.'))
9240Sstevel@tonic-gate 		return (NULL);
9250Sstevel@tonic-gate 	if (len == 8)
9260Sstevel@tonic-gate 		arch = "SUN3";
9270Sstevel@tonic-gate 	else
9280Sstevel@tonic-gate 		arch++;
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	/*
9310Sstevel@tonic-gate 	 * Allow <Hex IP Addr>* filename request to to be
9320Sstevel@tonic-gate 	 * satisfied by <Hex IP Addr><Any Suffix> rather
9330Sstevel@tonic-gate 	 * than enforcing this to be Sun3 systems.  Also serves
9340Sstevel@tonic-gate 	 * to make case of suffix a don't-care.
9350Sstevel@tonic-gate 	 */
9360Sstevel@tonic-gate 	if ((dir = opendir(homedir)) == NULL)
9370Sstevel@tonic-gate 		return (NULL);
9380Sstevel@tonic-gate 	while ((dp = readdir(dir)) != NULL) {
9390Sstevel@tonic-gate 		if (strncmp(origname, dp->d_name, 8) == 0) {
9400Sstevel@tonic-gate 			(void) strlcpy(buf, dp->d_name, sizeof (buf));
9410Sstevel@tonic-gate 			(void) closedir(dir);
9420Sstevel@tonic-gate 			return (buf);
9430Sstevel@tonic-gate 		}
9440Sstevel@tonic-gate 	}
9450Sstevel@tonic-gate 	(void) closedir(dir);
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	/*
9480Sstevel@tonic-gate 	 * XXX maybe call YP master for most current data iff
9490Sstevel@tonic-gate 	 * pnp is enabled.
9500Sstevel@tonic-gate 	 */
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	/*
9530Sstevel@tonic-gate 	 * only do mapping PNP boot file name for machines that
9540Sstevel@tonic-gate 	 * are not in the hosts database.
9550Sstevel@tonic-gate 	 */
9560Sstevel@tonic-gate 	if (gethostbyaddr((char *)&ipaddr, sizeof (ipaddr), AF_INET) != NULL)
9570Sstevel@tonic-gate 		return (NULL);
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	s = buf + strlcpy(buf, "pnp.", sizeof (buf));
9600Sstevel@tonic-gate 	bufend = &buf[sizeof (buf) - 1];
9610Sstevel@tonic-gate 	while ((*arch != '\0') && (s < bufend))
9620Sstevel@tonic-gate 		*s++ = tolower (*arch++);
9630Sstevel@tonic-gate 	*s = '\0';
9640Sstevel@tonic-gate 	return (buf);
9650Sstevel@tonic-gate }
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate /*
9690Sstevel@tonic-gate  * Try to validate filename. If the filename doesn't exist try PNP mapping.
9700Sstevel@tonic-gate  */
9710Sstevel@tonic-gate static int
validate_filename(int mode)9720Sstevel@tonic-gate validate_filename(int mode)
9730Sstevel@tonic-gate {
9740Sstevel@tonic-gate 	struct stat stbuf;
9750Sstevel@tonic-gate 	char *origfile;
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	if (stat(filename, &stbuf) < 0) {
9780Sstevel@tonic-gate 		if (errno != ENOENT)
9790Sstevel@tonic-gate 			return (EACCESS);
9800Sstevel@tonic-gate 		if (mode == WRQ)
9810Sstevel@tonic-gate 			return (ENOTFOUND);
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 		/* try to map requested filename into a pnp filename */
9840Sstevel@tonic-gate 		origfile = filename;
9850Sstevel@tonic-gate 		filename = pnp_check(origfile);
9860Sstevel@tonic-gate 		if (filename == NULL)
9870Sstevel@tonic-gate 			return (ENOTFOUND);
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 		if (stat(filename, &stbuf) < 0)
9900Sstevel@tonic-gate 			return (errno == ENOENT ? ENOTFOUND : EACCESS);
9910Sstevel@tonic-gate 		syslog(LOG_NOTICE, "%s -> %s\n", origfile, filename);
9920Sstevel@tonic-gate 	}
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 	return (0);
9950Sstevel@tonic-gate }
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate /* ARGSUSED */
9980Sstevel@tonic-gate static void
timer(int signum)9990Sstevel@tonic-gate timer(int signum)
10000Sstevel@tonic-gate {
10010Sstevel@tonic-gate 	timeout += rexmtval;
10020Sstevel@tonic-gate 	if (timeout >= maxtimeout)
10030Sstevel@tonic-gate 		exit(1);
10040Sstevel@tonic-gate 	siglongjmp(timeoutbuf, 1);
10050Sstevel@tonic-gate }
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate /*
10080Sstevel@tonic-gate  * Send the requested file.
10090Sstevel@tonic-gate  */
10100Sstevel@tonic-gate static void
tftpd_sendfile(struct formats * pf,int oacklen)10110Sstevel@tonic-gate tftpd_sendfile(struct formats *pf, int oacklen)
10120Sstevel@tonic-gate {
10130Sstevel@tonic-gate 	struct tftphdr *dp;
10141926Sas198278 	volatile ushort_t block = 1;
10150Sstevel@tonic-gate 	int size, n, serrno;
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 	if (oacklen != 0) {
10180Sstevel@tonic-gate 		(void) sigset(SIGALRM, timer);
10190Sstevel@tonic-gate 		timeout = 0;
10200Sstevel@tonic-gate 		(void) sigsetjmp(timeoutbuf, 1);
10210Sstevel@tonic-gate 		if (debug && standalone) {
10220Sstevel@tonic-gate 			(void) fputs("Sending OACK ", stderr);
10230Sstevel@tonic-gate 			print_options(stderr, (char *)&oackbuf.hdr.th_stuff,
10240Sstevel@tonic-gate 			    oacklen - 2);
10250Sstevel@tonic-gate 			(void) putc('\n', stderr);
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		if (sendto(peer, &oackbuf, oacklen, 0,
10280Sstevel@tonic-gate 		    (struct sockaddr *)&from, fromplen) != oacklen) {
10290Sstevel@tonic-gate 			if (debug && standalone) {
10300Sstevel@tonic-gate 				serrno = errno;
10310Sstevel@tonic-gate 				perror("sendto (oack)");
10320Sstevel@tonic-gate 				errno = serrno;
10330Sstevel@tonic-gate 			}
10340Sstevel@tonic-gate 			SYSLOG_MSG("sendto (oack): %m");
10350Sstevel@tonic-gate 			goto abort;
10360Sstevel@tonic-gate 		}
10370Sstevel@tonic-gate 		(void) alarm(rexmtval); /* read the ack */
10380Sstevel@tonic-gate 		for (;;) {
10390Sstevel@tonic-gate 			(void) sigrelse(SIGALRM);
10400Sstevel@tonic-gate 			n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
10410Sstevel@tonic-gate 			(void) sighold(SIGALRM);
10420Sstevel@tonic-gate 			if (n < 0) {
10430Sstevel@tonic-gate 				if (errno == EINTR)
10440Sstevel@tonic-gate 					continue;
10450Sstevel@tonic-gate 				serrno = errno;
10460Sstevel@tonic-gate 				SYSLOG_MSG("recv (ack): %m");
10470Sstevel@tonic-gate 				if (debug && standalone) {
10480Sstevel@tonic-gate 					errno = serrno;
10490Sstevel@tonic-gate 					perror("recv (ack)");
10500Sstevel@tonic-gate 				}
10510Sstevel@tonic-gate 				goto abort;
10520Sstevel@tonic-gate 			}
10530Sstevel@tonic-gate 			ackbuf.tb_hdr.th_opcode =
10540Sstevel@tonic-gate 			    ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
10550Sstevel@tonic-gate 			ackbuf.tb_hdr.th_block =
10560Sstevel@tonic-gate 			    ntohs((ushort_t)ackbuf.tb_hdr.th_block);
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 			if (ackbuf.tb_hdr.th_opcode == ERROR) {
10590Sstevel@tonic-gate 				if (debug && standalone) {
10600Sstevel@tonic-gate 					(void) fprintf(stderr,
10610Sstevel@tonic-gate 					    "received ERROR %d",
10620Sstevel@tonic-gate 					    ackbuf.tb_hdr.th_code);
10630Sstevel@tonic-gate 					if (n > 4)
10640Sstevel@tonic-gate 						(void) fprintf(stderr,
10650Sstevel@tonic-gate 						    " %.*s", n - 4,
10660Sstevel@tonic-gate 						    ackbuf.tb_hdr.th_msg);
10670Sstevel@tonic-gate 					(void) putc('\n', stderr);
10680Sstevel@tonic-gate 				}
10690Sstevel@tonic-gate 				goto abort;
10700Sstevel@tonic-gate 			}
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 			if (ackbuf.tb_hdr.th_opcode == ACK) {
10730Sstevel@tonic-gate 				if (debug && standalone)
10740Sstevel@tonic-gate 					(void) fprintf(stderr,
10750Sstevel@tonic-gate 					    "received ACK for block %d\n",
10760Sstevel@tonic-gate 					    ackbuf.tb_hdr.th_block);
10770Sstevel@tonic-gate 				if (ackbuf.tb_hdr.th_block == 0)
10780Sstevel@tonic-gate 					break;
10790Sstevel@tonic-gate 				/*
10800Sstevel@tonic-gate 				 * Don't resend the OACK, avoids getting stuck
10810Sstevel@tonic-gate 				 * in an OACK/ACK loop if the client keeps
10820Sstevel@tonic-gate 				 * replying with a bad ACK. Client will either
10830Sstevel@tonic-gate 				 * send a good ACK or timeout sending bad ones.
10840Sstevel@tonic-gate 				 */
10850Sstevel@tonic-gate 			}
10860Sstevel@tonic-gate 		}
10870Sstevel@tonic-gate 		cancel_alarm();
10880Sstevel@tonic-gate 	}
10890Sstevel@tonic-gate 	dp = r_init();
10900Sstevel@tonic-gate 	do {
10910Sstevel@tonic-gate 		(void) sigset(SIGALRM, timer);
10920Sstevel@tonic-gate 		size = readit(file, &dp, pf->f_convert);
10930Sstevel@tonic-gate 		if (size < 0) {
10940Sstevel@tonic-gate 			nak(errno + 100);
10950Sstevel@tonic-gate 			goto abort;
10960Sstevel@tonic-gate 		}
10970Sstevel@tonic-gate 		dp->th_opcode = htons((ushort_t)DATA);
10980Sstevel@tonic-gate 		dp->th_block = htons((ushort_t)block);
10990Sstevel@tonic-gate 		timeout = 0;
11000Sstevel@tonic-gate 		(void) sigsetjmp(timeoutbuf, 1);
11010Sstevel@tonic-gate 		if (debug && standalone)
11020Sstevel@tonic-gate 			(void) fprintf(stderr, "Sending DATA block %d\n",
11030Sstevel@tonic-gate 			    block);
11040Sstevel@tonic-gate 		if (sendto(peer, dp, size + 4, 0,
11050Sstevel@tonic-gate 		    (struct sockaddr *)&from,  fromplen) != size + 4) {
11060Sstevel@tonic-gate 			if (debug && standalone) {
11070Sstevel@tonic-gate 				serrno = errno;
11080Sstevel@tonic-gate 				perror("sendto (data)");
11090Sstevel@tonic-gate 				errno = serrno;
11100Sstevel@tonic-gate 			}
11110Sstevel@tonic-gate 			SYSLOG_MSG("sendto (data): %m");
11120Sstevel@tonic-gate 			goto abort;
11130Sstevel@tonic-gate 		}
11140Sstevel@tonic-gate 		read_ahead(file, pf->f_convert);
11150Sstevel@tonic-gate 		(void) alarm(rexmtval); /* read the ack */
11160Sstevel@tonic-gate 		for (;;) {
11170Sstevel@tonic-gate 			(void) sigrelse(SIGALRM);
11180Sstevel@tonic-gate 			n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
11190Sstevel@tonic-gate 			(void) sighold(SIGALRM);
11200Sstevel@tonic-gate 			if (n < 0) {
11210Sstevel@tonic-gate 				if (errno == EINTR)
11220Sstevel@tonic-gate 					continue;
11230Sstevel@tonic-gate 				serrno = errno;
11240Sstevel@tonic-gate 				SYSLOG_MSG("recv (ack): %m");
11250Sstevel@tonic-gate 				if (debug && standalone) {
11260Sstevel@tonic-gate 					errno = serrno;
11270Sstevel@tonic-gate 					perror("recv (ack)");
11280Sstevel@tonic-gate 				}
11290Sstevel@tonic-gate 				goto abort;
11300Sstevel@tonic-gate 			}
11310Sstevel@tonic-gate 			ackbuf.tb_hdr.th_opcode =
11320Sstevel@tonic-gate 			    ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
11330Sstevel@tonic-gate 			ackbuf.tb_hdr.th_block =
11340Sstevel@tonic-gate 			    ntohs((ushort_t)ackbuf.tb_hdr.th_block);
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 			if (ackbuf.tb_hdr.th_opcode == ERROR) {
11370Sstevel@tonic-gate 				if (debug && standalone) {
11380Sstevel@tonic-gate 					(void) fprintf(stderr,
11390Sstevel@tonic-gate 					    "received ERROR %d",
11400Sstevel@tonic-gate 					    ackbuf.tb_hdr.th_code);
11410Sstevel@tonic-gate 					if (n > 4)
11420Sstevel@tonic-gate 						(void) fprintf(stderr,
11430Sstevel@tonic-gate 						    " %.*s", n - 4,
11440Sstevel@tonic-gate 						    ackbuf.tb_hdr.th_msg);
11450Sstevel@tonic-gate 					(void) putc('\n', stderr);
11460Sstevel@tonic-gate 				}
11470Sstevel@tonic-gate 				goto abort;
11480Sstevel@tonic-gate 			}
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 			if (ackbuf.tb_hdr.th_opcode == ACK) {
11510Sstevel@tonic-gate 				if (debug && standalone)
11520Sstevel@tonic-gate 					(void) fprintf(stderr,
11534921Sdm199272 					    "received ACK for block %d\n",
11544921Sdm199272 					    ackbuf.tb_hdr.th_block);
11550Sstevel@tonic-gate 				if (ackbuf.tb_hdr.th_block == block) {
11560Sstevel@tonic-gate 					break;
11570Sstevel@tonic-gate 				}
11580Sstevel@tonic-gate 				/*
11590Sstevel@tonic-gate 				 * Never resend the current DATA packet on
11600Sstevel@tonic-gate 				 * receipt of a duplicate ACK, doing so would
11610Sstevel@tonic-gate 				 * cause the "Sorcerer's Apprentice Syndrome".
11620Sstevel@tonic-gate 				 */
11630Sstevel@tonic-gate 			}
11640Sstevel@tonic-gate 		}
11650Sstevel@tonic-gate 		cancel_alarm();
11660Sstevel@tonic-gate 		block++;
11670Sstevel@tonic-gate 	} while (size == blocksize);
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate abort:
11700Sstevel@tonic-gate 	cancel_alarm();
11710Sstevel@tonic-gate 	(void) fclose(file);
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate /* ARGSUSED */
11750Sstevel@tonic-gate static void
justquit(int signum)11760Sstevel@tonic-gate justquit(int signum)
11770Sstevel@tonic-gate {
11780Sstevel@tonic-gate 	exit(0);
11790Sstevel@tonic-gate }
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate /*
11820Sstevel@tonic-gate  * Receive a file.
11830Sstevel@tonic-gate  */
11840Sstevel@tonic-gate static void
tftpd_recvfile(struct formats * pf,int oacklen)11850Sstevel@tonic-gate tftpd_recvfile(struct formats *pf, int oacklen)
11860Sstevel@tonic-gate {
11870Sstevel@tonic-gate 	struct tftphdr *dp;
11880Sstevel@tonic-gate 	struct tftphdr *ap;    /* ack buffer */
11891926Sas198278 	ushort_t block = 0;
11901926Sas198278 	int n, size, acklen, serrno;
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 	dp = w_init();
11930Sstevel@tonic-gate 	ap = &ackbuf.tb_hdr;
11940Sstevel@tonic-gate 	do {
11950Sstevel@tonic-gate 		(void) sigset(SIGALRM, timer);
11960Sstevel@tonic-gate 		timeout = 0;
11970Sstevel@tonic-gate 		if (oacklen == 0) {
11980Sstevel@tonic-gate 			ap->th_opcode = htons((ushort_t)ACK);
11990Sstevel@tonic-gate 			ap->th_block = htons((ushort_t)block);
12000Sstevel@tonic-gate 			acklen = 4;
12010Sstevel@tonic-gate 		} else {
12020Sstevel@tonic-gate 			/* copy OACK packet to the ack buffer ready to send */
12030Sstevel@tonic-gate 			(void) memcpy(&ackbuf, &oackbuf, oacklen);
12040Sstevel@tonic-gate 			acklen = oacklen;
12050Sstevel@tonic-gate 			oacklen = 0;
12060Sstevel@tonic-gate 		}
12070Sstevel@tonic-gate 		block++;
12080Sstevel@tonic-gate 		(void) sigsetjmp(timeoutbuf, 1);
12090Sstevel@tonic-gate send_ack:
12100Sstevel@tonic-gate 		if (debug && standalone) {
12110Sstevel@tonic-gate 			if (ap->th_opcode == htons((ushort_t)ACK)) {
12120Sstevel@tonic-gate 				(void) fprintf(stderr,
12130Sstevel@tonic-gate 				    "Sending ACK for block %d\n", block - 1);
12140Sstevel@tonic-gate 			} else {
12150Sstevel@tonic-gate 				(void) fprintf(stderr, "Sending OACK ");
12160Sstevel@tonic-gate 				print_options(stderr, (char *)&ap->th_stuff,
12170Sstevel@tonic-gate 				    acklen - 2);
12180Sstevel@tonic-gate 				(void) putc('\n', stderr);
12190Sstevel@tonic-gate 			}
12200Sstevel@tonic-gate 		}
12210Sstevel@tonic-gate 		if (sendto(peer, &ackbuf, acklen, 0, (struct sockaddr *)&from,
12220Sstevel@tonic-gate 		    fromplen) != acklen) {
12230Sstevel@tonic-gate 			if (ap->th_opcode == htons((ushort_t)ACK)) {
12240Sstevel@tonic-gate 				if (debug && standalone) {
12250Sstevel@tonic-gate 					serrno = errno;
12260Sstevel@tonic-gate 					perror("sendto (ack)");
12270Sstevel@tonic-gate 					errno = serrno;
12280Sstevel@tonic-gate 				}
12290Sstevel@tonic-gate 				syslog(LOG_ERR, "sendto (ack): %m\n");
12300Sstevel@tonic-gate 			} else {
12310Sstevel@tonic-gate 				if (debug && standalone) {
12320Sstevel@tonic-gate 					serrno = errno;
12330Sstevel@tonic-gate 					perror("sendto (oack)");
12340Sstevel@tonic-gate 					errno = serrno;
12350Sstevel@tonic-gate 				}
12360Sstevel@tonic-gate 				syslog(LOG_ERR, "sendto (oack): %m\n");
12370Sstevel@tonic-gate 			}
12380Sstevel@tonic-gate 			goto abort;
12390Sstevel@tonic-gate 		}
12400Sstevel@tonic-gate 		if (write_behind(file, pf->f_convert) < 0) {
12410Sstevel@tonic-gate 			nak(errno + 100);
12420Sstevel@tonic-gate 			goto abort;
12430Sstevel@tonic-gate 		}
12440Sstevel@tonic-gate 		(void) alarm(rexmtval);
12450Sstevel@tonic-gate 		for (;;) {
12460Sstevel@tonic-gate 			(void) sigrelse(SIGALRM);
12470Sstevel@tonic-gate 			n = recv(peer, dp, blocksize + 4, 0);
12480Sstevel@tonic-gate 			(void) sighold(SIGALRM);
12490Sstevel@tonic-gate 			if (n < 0) { /* really? */
12500Sstevel@tonic-gate 				if (errno == EINTR)
12510Sstevel@tonic-gate 					continue;
12520Sstevel@tonic-gate 				syslog(LOG_ERR, "recv (data): %m");
12530Sstevel@tonic-gate 				goto abort;
12540Sstevel@tonic-gate 			}
12550Sstevel@tonic-gate 			dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
12560Sstevel@tonic-gate 			dp->th_block = ntohs((ushort_t)dp->th_block);
12570Sstevel@tonic-gate 			if (dp->th_opcode == ERROR) {
12580Sstevel@tonic-gate 				cancel_alarm();
12590Sstevel@tonic-gate 				if (debug && standalone) {
12600Sstevel@tonic-gate 					(void) fprintf(stderr,
12610Sstevel@tonic-gate 					    "received ERROR %d", dp->th_code);
12620Sstevel@tonic-gate 					if (n > 4)
12630Sstevel@tonic-gate 						(void) fprintf(stderr,
12640Sstevel@tonic-gate 						    " %.*s", n - 4, dp->th_msg);
12650Sstevel@tonic-gate 					(void) putc('\n', stderr);
12660Sstevel@tonic-gate 				}
12670Sstevel@tonic-gate 				return;
12680Sstevel@tonic-gate 			}
12690Sstevel@tonic-gate 			if (dp->th_opcode == DATA) {
12700Sstevel@tonic-gate 				if (debug && standalone)
12710Sstevel@tonic-gate 					(void) fprintf(stderr,
12724921Sdm199272 					    "Received DATA block %d\n",
12734921Sdm199272 					    dp->th_block);
12740Sstevel@tonic-gate 				if (dp->th_block == block) {
12750Sstevel@tonic-gate 					break;   /* normal */
12760Sstevel@tonic-gate 				}
12770Sstevel@tonic-gate 				/* Re-synchronize with the other side */
12780Sstevel@tonic-gate 				if (synchnet(peer) < 0) {
12790Sstevel@tonic-gate 					nak(errno + 100);
12800Sstevel@tonic-gate 					goto abort;
12810Sstevel@tonic-gate 				}
12820Sstevel@tonic-gate 				if (dp->th_block == (block-1))
12830Sstevel@tonic-gate 					goto send_ack; /* rexmit */
12840Sstevel@tonic-gate 			}
12850Sstevel@tonic-gate 		}
12860Sstevel@tonic-gate 		cancel_alarm();
12870Sstevel@tonic-gate 		/*  size = write(file, dp->th_data, n - 4); */
12880Sstevel@tonic-gate 		size = writeit(file, &dp, n - 4, pf->f_convert);
12890Sstevel@tonic-gate 		if (size != (n - 4)) {
12900Sstevel@tonic-gate 			nak((size < 0) ? (errno + 100) : ENOSPACE);
12910Sstevel@tonic-gate 			goto abort;
12920Sstevel@tonic-gate 		}
12930Sstevel@tonic-gate 	} while (size == blocksize);
12940Sstevel@tonic-gate 	if (write_behind(file, pf->f_convert) < 0) {
12950Sstevel@tonic-gate 		nak(errno + 100);
12960Sstevel@tonic-gate 		goto abort;
12970Sstevel@tonic-gate 	}
12980Sstevel@tonic-gate 	n = fclose(file);	/* close data file */
12990Sstevel@tonic-gate 	file = NULL;
13000Sstevel@tonic-gate 	if (n == EOF) {
13010Sstevel@tonic-gate 		nak(errno + 100);
13020Sstevel@tonic-gate 		goto abort;
13030Sstevel@tonic-gate 	}
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 	ap->th_opcode = htons((ushort_t)ACK);    /* send the "final" ack */
13060Sstevel@tonic-gate 	ap->th_block = htons((ushort_t)(block));
13070Sstevel@tonic-gate 	if (debug && standalone)
13080Sstevel@tonic-gate 		(void) fprintf(stderr, "Sending ACK for block %d\n", block);
13090Sstevel@tonic-gate 	if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
13100Sstevel@tonic-gate 	    fromplen) == -1) {
13110Sstevel@tonic-gate 		if (debug && standalone)
13120Sstevel@tonic-gate 			perror("sendto (ack)");
13130Sstevel@tonic-gate 	}
13140Sstevel@tonic-gate 	(void) sigset(SIGALRM, justquit); /* just quit on timeout */
13150Sstevel@tonic-gate 	(void) alarm(rexmtval);
13160Sstevel@tonic-gate 	/* normally times out and quits */
13170Sstevel@tonic-gate 	n = recv(peer, dp, blocksize + 4, 0);
13180Sstevel@tonic-gate 	(void) alarm(0);
13190Sstevel@tonic-gate 	dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
13200Sstevel@tonic-gate 	dp->th_block = ntohs((ushort_t)dp->th_block);
13210Sstevel@tonic-gate 	if (n >= 4 &&		/* if read some data */
13220Sstevel@tonic-gate 	    dp->th_opcode == DATA && /* and got a data block */
13230Sstevel@tonic-gate 	    block == dp->th_block) {	/* then my last ack was lost */
13240Sstevel@tonic-gate 		if (debug && standalone) {
13250Sstevel@tonic-gate 			(void) fprintf(stderr, "Sending ACK for block %d\n",
13260Sstevel@tonic-gate 			    block);
13270Sstevel@tonic-gate 		}
13280Sstevel@tonic-gate 		/* resend final ack */
13290Sstevel@tonic-gate 		if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
13300Sstevel@tonic-gate 		    fromplen) == -1) {
13310Sstevel@tonic-gate 			if (debug && standalone)
13320Sstevel@tonic-gate 				perror("sendto (last ack)");
13330Sstevel@tonic-gate 		}
13340Sstevel@tonic-gate 	}
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate abort:
13370Sstevel@tonic-gate 	cancel_alarm();
13380Sstevel@tonic-gate 	if (file != NULL)
13390Sstevel@tonic-gate 		(void) fclose(file);
13400Sstevel@tonic-gate }
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate /*
13430Sstevel@tonic-gate  * Send a nak packet (error message).
13440Sstevel@tonic-gate  * Error code passed in is one of the
13450Sstevel@tonic-gate  * standard TFTP codes, or a UNIX errno
13460Sstevel@tonic-gate  * offset by 100.
13470Sstevel@tonic-gate  * Handles connected as well as unconnected peer.
13480Sstevel@tonic-gate  */
13490Sstevel@tonic-gate static void
nak(int error)13500Sstevel@tonic-gate nak(int error)
13510Sstevel@tonic-gate {
13520Sstevel@tonic-gate 	struct tftphdr *tp;
13530Sstevel@tonic-gate 	int length;
13540Sstevel@tonic-gate 	struct errmsg *pe;
13550Sstevel@tonic-gate 	int ret;
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 	tp = &buf.hdr;
13580Sstevel@tonic-gate 	tp->th_opcode = htons((ushort_t)ERROR);
13590Sstevel@tonic-gate 	tp->th_code = htons((ushort_t)error);
13600Sstevel@tonic-gate 	for (pe = errmsgs; pe->e_code >= 0; pe++)
13610Sstevel@tonic-gate 		if (pe->e_code == error)
13620Sstevel@tonic-gate 			break;
13630Sstevel@tonic-gate 	if (pe->e_code < 0) {
13640Sstevel@tonic-gate 		pe->e_msg = strerror(error - 100);
13650Sstevel@tonic-gate 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
13660Sstevel@tonic-gate 	}
13670Sstevel@tonic-gate 	(void) strlcpy(tp->th_msg, (pe->e_msg != NULL) ? pe->e_msg : "UNKNOWN",
13680Sstevel@tonic-gate 	    sizeof (buf) - sizeof (struct tftphdr));
13690Sstevel@tonic-gate 	length = strlen(tp->th_msg);
13700Sstevel@tonic-gate 	length += sizeof (struct tftphdr);
13710Sstevel@tonic-gate 	if (debug && standalone)
13720Sstevel@tonic-gate 		(void) fprintf(stderr, "Sending NAK: %s\n", tp->th_msg);
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate 	ret = sendto(peer, &buf, length, 0, (struct sockaddr *)&from,
13750Sstevel@tonic-gate 	    fromplen);
13760Sstevel@tonic-gate 	if (ret == -1 && errno == EISCONN) {
13770Sstevel@tonic-gate 		/* Try without an address */
13780Sstevel@tonic-gate 		ret = send(peer, &buf, length, 0);
13790Sstevel@tonic-gate 	}
13800Sstevel@tonic-gate 	if (ret == -1) {
13810Sstevel@tonic-gate 		if (standalone)
13820Sstevel@tonic-gate 			perror("sendto (nak)");
13830Sstevel@tonic-gate 		else
13840Sstevel@tonic-gate 			syslog(LOG_ERR, "tftpd: nak: %m\n");
13850Sstevel@tonic-gate 	} else if (ret != length) {
13860Sstevel@tonic-gate 		if (standalone)
13870Sstevel@tonic-gate 			perror("sendto (nak) lost data");
13880Sstevel@tonic-gate 		else
13890Sstevel@tonic-gate 			syslog(LOG_ERR, "tftpd: nak: %d lost\n", length - ret);
13900Sstevel@tonic-gate 	}
13910Sstevel@tonic-gate }
1392