xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c (revision c2cb63342c63c60cee771d1af82f377e34aa1217)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 /*
26  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
27  * All Rights Reserved.
28  */
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California.
33  * All Rights Reserved.
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * Trivial file transfer protocol server.  A top level process runs in
44  * an infinite loop fielding new TFTP requests.  A child process,
45  * communicating via a pipe with the top level process, sends delayed
46  * NAKs for those that we can't handle.  A new child process is created
47  * to service each request that we can handle.  The top level process
48  * exits after a period of time during which no new requests are
49  * received.
50  */
51 
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/wait.h>
55 #include <sys/stat.h>
56 #include <sys/time.h>
57 
58 #include <netinet/in.h>
59 
60 #include <arpa/inet.h>
61 #include <dirent.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <unistd.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <netdb.h>
69 #include <setjmp.h>
70 #include <syslog.h>
71 #include <sys/param.h>
72 #include <fcntl.h>
73 #include <pwd.h>
74 #include <string.h>
75 #include <priv_utils.h>
76 #include "tftpcommon.h"
77 
78 #define	TIMEOUT		5
79 #define	DELAY_SECS	3
80 #define	DALLYSECS 60
81 
82 #define	SYSLOG_MSG(message) \
83 	(syslog((((errno == ENETUNREACH) || (errno == EHOSTUNREACH) || \
84 		(errno == ECONNREFUSED)) ? LOG_WARNING : LOG_ERR), message))
85 
86 static int			rexmtval = TIMEOUT;
87 static int			maxtimeout = 5*TIMEOUT;
88 static int			securetftp;
89 static int			debug;
90 static int			disable_pnp;
91 static int			standalone;
92 static uid_t			uid_nobody = UID_NOBODY;
93 static uid_t			gid_nobody = GID_NOBODY;
94 static int			reqsock = -1;
95 				/* file descriptor of request socket */
96 static socklen_t		fromlen;
97 static socklen_t		fromplen;
98 static struct sockaddr_storage	client;
99 static struct sockaddr_in6 	*sin6_ptr;
100 static struct sockaddr_in	*sin_ptr;
101 static struct sockaddr_in6	*from6_ptr;
102 static struct sockaddr_in	*from_ptr;
103 static int			addrfmly;
104 static int			peer;
105 static off_t			tsize;
106 static tftpbuf			ackbuf;
107 static struct sockaddr_storage	from;
108 static boolean_t		tsize_set;
109 static pid_t			child;
110 				/* pid of child handling delayed replys */
111 static int			delay_fd [2];
112 				/* pipe for communicating with child */
113 static FILE			*file;
114 static char			*filename;
115 
116 static union {
117 	struct tftphdr	hdr;
118 	char		data[SEGSIZE + 4];
119 } buf;
120 
121 static union {
122 	struct tftphdr	hdr;
123 	char		data[SEGSIZE];
124 } oackbuf;
125 
126 struct	delay_info {
127 	long	timestamp;		/* time request received */
128 	int	ecode;			/* error code to return */
129 	struct	sockaddr_storage from;	/* address of client */
130 };
131 
132 int	blocksize = SEGSIZE;	/* Number of data bytes in a DATA packet */
133 
134 /*
135  * Default directory for unqualified names
136  * Used by TFTP boot procedures
137  */
138 static char	*homedir = "/tftpboot";
139 
140 struct formats {
141 	char	*f_mode;
142 	int	(*f_validate)(int);
143 	void	(*f_send)(struct formats *, int);
144 	void	(*f_recv)(struct formats *, int);
145 	int	f_convert;
146 };
147 
148 static void	delayed_responder(void);
149 static void	tftp(struct tftphdr *, int);
150 static int	validate_filename(int);
151 static void	tftpd_sendfile(struct formats *, int);
152 static void	tftpd_recvfile(struct formats *, int);
153 static void	nak(int);
154 static char	*blksize_handler(int, char *, int *);
155 static char	*timeout_handler(int, char *, int *);
156 static char	*tsize_handler(int, char *, int *);
157 
158 static struct formats formats[] = {
159 	{ "netascii",	validate_filename, tftpd_sendfile, tftpd_recvfile, 1 },
160 	{ "octet",	validate_filename, tftpd_sendfile, tftpd_recvfile, 0 },
161 	{ NULL }
162 };
163 
164 struct options {
165 	char	*opt_name;
166 	char	*(*opt_handler)(int, char *, int *);
167 };
168 
169 static struct options options[] = {
170 	{ "blksize",	blksize_handler },
171 	{ "timeout",	timeout_handler },
172 	{ "tsize",	tsize_handler },
173 	{ NULL }
174 };
175 
176 static char		optbuf[MAX_OPTVAL_LEN];
177 static int		timeout;
178 static sigjmp_buf	timeoutbuf;
179 
180 int
181 main(int argc, char **argv)
182 {
183 	struct tftphdr *tp;
184 	int n;
185 	int c;
186 	struct	passwd *pwd;		/* for "nobody" entry */
187 	struct in_addr ipv4addr;
188 	char abuf[INET6_ADDRSTRLEN];
189 	socklen_t addrlen;
190 
191 	openlog("tftpd", LOG_PID, LOG_DAEMON);
192 
193 	pwd = getpwnam("nobody");
194 	if (pwd != NULL) {
195 		uid_nobody = pwd->pw_uid;
196 		gid_nobody = pwd->pw_gid;
197 	}
198 
199 	(void) __init_daemon_priv(
200 		PU_LIMITPRIVS,
201 		uid_nobody, gid_nobody,
202 		PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL);
203 
204 	/*
205 	 *  Limit set is still "all."  Trim it down to just what we need:
206 	 *  fork and chroot.
207 	 */
208 	(void) priv_set(PRIV_SET,
209 	    PRIV_ALLSETS, PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL);
210 	(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
211 	(void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL);
212 
213 	while ((c = getopt(argc, argv, "dspS")) != EOF)
214 		switch (c) {
215 		case 'd':		/* enable debug */
216 			debug++;
217 			continue;
218 		case 's':		/* secure daemon */
219 			securetftp = 1;
220 			continue;
221 		case 'p':		/* disable name pnp mapping */
222 			disable_pnp = 1;
223 			continue;
224 		case 'S':
225 			standalone = 1;
226 			continue;
227 		case '?':
228 		default:
229 usage:
230 			(void) fprintf(stderr,
231 			    "usage:  %s [-spd] [home-directory]\n", argv[0]);
232 			for (; optind < argc; optind++)
233 				syslog(LOG_ERR, "bad argument %s",
234 				    argv[optind]);
235 			exit(1);
236 		}
237 
238 	if (optind < argc)
239 		if (optind == argc - 1 && *argv [optind] == '/')
240 			homedir = argv [optind];
241 		else
242 			goto usage;
243 
244 	if (pipe(delay_fd) < 0) {
245 		syslog(LOG_ERR, "pipe (main): %m");
246 		exit(1);
247 	}
248 
249 	(void) sigset(SIGCHLD, SIG_IGN); /* no zombies please */
250 
251 	if (standalone) {
252 		socklen_t clientlen;
253 
254 		sin6_ptr = (struct sockaddr_in6 *)&client;
255 		clientlen = sizeof (struct sockaddr_in6);
256 		reqsock = socket(AF_INET6, SOCK_DGRAM, 0);
257 		if (reqsock == -1) {
258 			perror("socket");
259 			exit(1);
260 		}
261 		(void) memset(&client, 0, clientlen);
262 		sin6_ptr->sin6_family = AF_INET6;
263 		sin6_ptr->sin6_port = htons(IPPORT_TFTP);
264 		if (bind(reqsock, (struct sockaddr *)&client,
265 		    clientlen) == -1) {
266 			perror("bind");
267 			exit(1);
268 		}
269 		if (debug)
270 			(void) puts("running in standalone mode...");
271 	} else {
272 		/* request socket passed on fd 0 by inetd */
273 		reqsock = 0;
274 	}
275 	if (debug) {
276 		int on = 1;
277 
278 		(void) setsockopt(reqsock, SOL_SOCKET, SO_DEBUG,
279 		    (char *)&on, sizeof (on));
280 	}
281 
282 	(void) chdir(homedir);
283 
284 	(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
285 	if ((child = fork()) < 0) {
286 		syslog(LOG_ERR, "fork (main): %m");
287 		exit(1);
288 	}
289 	(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
290 
291 	if (child == 0) {
292 		(void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL);
293 		delayed_responder();
294 	} /* child */
295 
296 	/* close read side of pipe */
297 	(void) close(delay_fd[0]);
298 
299 
300 	/*
301 	 * Top level handling of incomming tftp requests.  Read a request
302 	 * and pass it off to be handled.  If request is valid, handling
303 	 * forks off and parent returns to this loop.  If no new requests
304 	 * are received for DALLYSECS, exit and return to inetd.
305 	 */
306 
307 	for (;;) {
308 		fd_set readfds;
309 		struct timeval dally;
310 
311 		FD_ZERO(&readfds);
312 		FD_SET(reqsock, &readfds);
313 		dally.tv_sec = DALLYSECS;
314 		dally.tv_usec = 0;
315 
316 		n = select(reqsock + 1, &readfds, NULL, NULL, &dally);
317 		if (n < 0) {
318 			if (errno == EINTR)
319 				continue;
320 			syslog(LOG_ERR, "select: %m");
321 			(void) kill(child, SIGKILL);
322 			exit(1);
323 		}
324 		if (n == 0) {
325 			/* Select timed out.  Its time to die. */
326 			if (standalone)
327 				continue;
328 			else {
329 				(void) kill(child, SIGKILL);
330 				exit(0);
331 			}
332 		}
333 		addrlen = sizeof (from);
334 		if (getsockname(reqsock, (struct sockaddr  *)&from,
335 		    &addrlen) < 0) {
336 			syslog(LOG_ERR, "getsockname: %m");
337 			exit(1);
338 		}
339 
340 		switch (from.ss_family) {
341 		case AF_INET:
342 			fromlen = (socklen_t)sizeof (struct sockaddr_in);
343 			break;
344 		case AF_INET6:
345 			fromlen = (socklen_t)sizeof (struct sockaddr_in6);
346 			break;
347 		default:
348 			syslog(LOG_ERR,
349 			    "Unknown address Family on peer connection %d",
350 			    from.ss_family);
351 			exit(1);
352 		}
353 
354 		n = recvfrom(reqsock, &buf, sizeof (buf), 0,
355 			(struct sockaddr *)&from, &fromlen);
356 		if (n < 0) {
357 			if (errno == EINTR)
358 				continue;
359 			if (standalone)
360 				perror("recvfrom");
361 			else
362 				syslog(LOG_ERR, "recvfrom: %m");
363 			(void) kill(child, SIGKILL);
364 			exit(1);
365 		}
366 
367 		(void) alarm(0);
368 
369 		switch (from.ss_family) {
370 		case AF_INET:
371 			addrfmly = AF_INET;
372 			fromplen = sizeof (struct sockaddr_in);
373 			sin_ptr = (struct sockaddr_in *)&client;
374 			(void) memset(&client, 0, fromplen);
375 			sin_ptr->sin_family = AF_INET;
376 			break;
377 		case AF_INET6:
378 			addrfmly = AF_INET6;
379 			fromplen = sizeof (struct sockaddr_in6);
380 			sin6_ptr = (struct sockaddr_in6 *)&client;
381 			(void) memset(&client, 0, fromplen);
382 			sin6_ptr->sin6_family = AF_INET6;
383 			break;
384 		default:
385 			syslog(LOG_ERR,
386 			    "Unknown address Family on peer connection");
387 			exit(1);
388 		}
389 		peer = socket(addrfmly, SOCK_DGRAM, 0);
390 		if (peer < 0) {
391 			if (standalone)
392 				perror("socket (main)");
393 			else
394 				syslog(LOG_ERR, "socket (main): %m");
395 			(void) kill(child, SIGKILL);
396 			exit(1);
397 		}
398 		if (debug) {
399 			int on = 1;
400 
401 			(void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
402 			    (char *)&on, sizeof (on));
403 		}
404 
405 		if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
406 			if (standalone)
407 				perror("bind (main)");
408 			else
409 				syslog(LOG_ERR, "bind (main): %m");
410 			(void) kill(child, SIGKILL);
411 			exit(1);
412 		}
413 		if (standalone && debug) {
414 			sin6_ptr = (struct sockaddr_in6 *)&client;
415 			from6_ptr = (struct sockaddr_in6 *)&from;
416 			if (IN6_IS_ADDR_V4MAPPED(&from6_ptr->sin6_addr)) {
417 				IN6_V4MAPPED_TO_INADDR(&from6_ptr->sin6_addr,
418 				    &ipv4addr);
419 				(void) inet_ntop(AF_INET, &ipv4addr, abuf,
420 				    sizeof (abuf));
421 			} else {
422 				(void) inet_ntop(AF_INET6,
423 				    &from6_ptr->sin6_addr, abuf,
424 				    sizeof (abuf));
425 			}
426 			/* get local port */
427 			if (getsockname(peer, (struct sockaddr *)&client,
428 			    &fromplen) < 0)
429 				perror("getsockname (main)");
430 			(void) fprintf(stderr,
431 			    "request from %s port %d; local port %d\n",
432 			    abuf, from6_ptr->sin6_port, sin6_ptr->sin6_port);
433 		}
434 		tp = &buf.hdr;
435 		tp->th_opcode = ntohs((ushort_t)tp->th_opcode);
436 		if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
437 			tftp(tp, n);
438 
439 		(void) close(peer);
440 		(void) fclose(file);
441 	}
442 
443 	/*NOTREACHED*/
444 	return (0);
445 }
446 
447 static void
448 delayed_responder(void)
449 {
450 	struct delay_info dinfo;
451 	long now;
452 
453 	/* we don't use the descriptors passed in to the parent */
454 	(void) close(0);
455 	(void) close(1);
456 	if (standalone)
457 		(void) close(reqsock);
458 
459 	/* close write side of pipe */
460 	(void) close(delay_fd[1]);
461 
462 	for (;;) {
463 		int n;
464 
465 		if ((n = read(delay_fd[0], &dinfo,
466 		    sizeof (dinfo))) != sizeof (dinfo)) {
467 			if (n < 0) {
468 				if (errno == EINTR)
469 					continue;
470 				if (standalone)
471 					perror("read from pipe "
472 					    "(delayed responder)");
473 				else
474 					syslog(LOG_ERR, "read from pipe: %m");
475 			}
476 			exit(1);
477 		}
478 		switch (dinfo.from.ss_family) {
479 		case AF_INET:
480 			addrfmly = AF_INET;
481 			fromplen = sizeof (struct sockaddr_in);
482 			sin_ptr = (struct sockaddr_in *)&client;
483 			(void) memset(&client, 0, fromplen);
484 			sin_ptr->sin_family = AF_INET;
485 			break;
486 		case AF_INET6:
487 			addrfmly = AF_INET6;
488 			fromplen = sizeof (struct sockaddr_in6);
489 			sin6_ptr = (struct sockaddr_in6 *)&client;
490 			(void) memset(&client, 0, fromplen);
491 			sin6_ptr->sin6_family = AF_INET6;
492 			break;
493 		}
494 		peer = socket(addrfmly, SOCK_DGRAM, 0);
495 		if (peer == -1) {
496 			if (standalone)
497 				perror("socket (delayed responder)");
498 			else
499 				syslog(LOG_ERR, "socket (delay): %m");
500 			exit(1);
501 		}
502 		if (debug) {
503 			int on = 1;
504 
505 			(void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
506 			    (char *)&on, sizeof (on));
507 		}
508 
509 		if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
510 			if (standalone)
511 				perror("bind (delayed responder)");
512 			else
513 				syslog(LOG_ERR, "bind (delay): %m");
514 			exit(1);
515 		}
516 		if (client.ss_family == AF_INET) {
517 			from_ptr = (struct sockaddr_in *)&dinfo.from;
518 			from_ptr->sin_family = AF_INET;
519 		} else {
520 			from6_ptr = (struct sockaddr_in6 *)&dinfo.from;
521 			from6_ptr->sin6_family = AF_INET6;
522 		}
523 		/*
524 		 * Since a request hasn't been received from the client
525 		 * before the delayed responder process is forked, the
526 		 * from variable is uninitialized.  So set it to contain
527 		 * the client address.
528 		 */
529 		from = dinfo.from;
530 
531 		/*
532 		 * only sleep if DELAY_SECS has not elapsed since
533 		 * original request was received.  Ensure that `now'
534 		 * is not earlier than `dinfo.timestamp'
535 		 */
536 		now = time(0);
537 		if ((uint_t)(now - dinfo.timestamp) < DELAY_SECS)
538 			(void) sleep(DELAY_SECS - (now - dinfo.timestamp));
539 		nak(dinfo.ecode);
540 		(void) close(peer);
541 	} /* for */
542 
543 	/* NOTREACHED */
544 }
545 
546 /*
547  * Handle the Blocksize option.
548  * Return the blksize option value string to include in the OACK reply.
549  */
550 /*ARGSUSED*/
551 static char *
552 blksize_handler(int opcode, char *optval, int *errcode)
553 {
554 	char *endp;
555 	int value;
556 
557 	*errcode = -1;
558 	errno = 0;
559 	value = (int)strtol(optval, &endp, 10);
560 	if (errno != 0 || value < MIN_BLKSIZE || *endp != '\0')
561 		return (NULL);
562 	/*
563 	 * As the blksize value in the OACK reply can be less than the value
564 	 * requested, to support broken clients if the value requested is larger
565 	 * than allowed in the RFC, reply with the maximum value permitted.
566 	 */
567 	if (value > MAX_BLKSIZE)
568 		value = MAX_BLKSIZE;
569 
570 	blocksize = value;
571 	(void) snprintf(optbuf, sizeof (optbuf), "%d", blocksize);
572 	return (optbuf);
573 }
574 
575 /*
576  * Handle the Timeout Interval option.
577  * Return the timeout option value string to include in the OACK reply.
578  */
579 /*ARGSUSED*/
580 static char *
581 timeout_handler(int opcode, char *optval, int *errcode)
582 {
583 	char *endp;
584 	int value;
585 
586 	*errcode = -1;
587 	errno = 0;
588 	value = (int)strtol(optval, &endp, 10);
589 	if (errno != 0 || *endp != '\0')
590 		return (NULL);
591 	/*
592 	 * The timeout value in the OACK reply must match the value specified
593 	 * by the client, so if an invalid timeout is requested don't include
594 	 * the timeout option in the OACK reply.
595 	 */
596 	if (value < MIN_TIMEOUT || value > MAX_TIMEOUT)
597 		return (NULL);
598 
599 	rexmtval = value;
600 	maxtimeout = 5 * rexmtval;
601 	(void) snprintf(optbuf, sizeof (optbuf), "%d", rexmtval);
602 	return (optbuf);
603 }
604 
605 /*
606  * Handle the Transfer Size option.
607  * Return the tsize option value string to include in the OACK reply.
608  */
609 static char *
610 tsize_handler(int opcode, char *optval, int *errcode)
611 {
612 	char *endp;
613 	longlong_t value;
614 
615 	*errcode = -1;
616 	errno = 0;
617 	value = strtoll(optval, &endp, 10);
618 	if (errno != 0 || value < 0 || *endp != '\0')
619 		return (NULL);
620 
621 	if (opcode == RRQ) {
622 		if (tsize_set == B_FALSE)
623 			return (NULL);
624 		/*
625 		 * The tsize value should be 0 for a read request, but to
626 		 * support broken clients we don't check that it is.
627 		 */
628 	} else {
629 #if _FILE_OFFSET_BITS == 32
630 		if (value > MAXOFF_T) {
631 			*errcode = ENOSPACE;
632 			return (NULL);
633 		}
634 #endif
635 		tsize = value;
636 		tsize_set = B_TRUE;
637 	}
638 	(void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize);
639 	return (optbuf);
640 }
641 
642 /*
643  * Process any options included by the client in the request packet.
644  * Return the size of the OACK reply packet built or 0 for no OACK reply.
645  */
646 static int
647 process_options(int opcode, char *opts, char *endopts)
648 {
649 	char *cp, *optname, *optval, *ostr, *oackend;
650 	struct tftphdr *oackp;
651 	int i, errcode;
652 
653 	/*
654 	 * To continue to interoperate with broken TFTP clients, ignore
655 	 * null padding appended to requests which don't include options.
656 	 */
657 	cp = opts;
658 	while ((cp < endopts) && (*cp == '\0'))
659 		cp++;
660 	if (cp == endopts)
661 		return (0);
662 
663 	/*
664 	 * Construct an Option ACKnowledgement packet if any requested option
665 	 * is recognized.
666 	 */
667 	oackp = &oackbuf.hdr;
668 	oackend = oackbuf.data + sizeof (oackbuf.data);
669 	oackp->th_opcode = htons((ushort_t)OACK);
670 	cp = (char *)&oackp->th_stuff;
671 	while (opts < endopts) {
672 		optname = opts;
673 		if ((optval = next_field(optname, endopts)) == NULL) {
674 			nak(EOPTNEG);
675 			exit(1);
676 		}
677 		if ((opts = next_field(optval, endopts)) == NULL) {
678 			nak(EOPTNEG);
679 			exit(1);
680 		}
681 		for (i = 0; options[i].opt_name != NULL; i++) {
682 			if (strcasecmp(optname, options[i].opt_name) == 0)
683 				break;
684 		}
685 		if (options[i].opt_name != NULL) {
686 			ostr = options[i].opt_handler(opcode, optval, &errcode);
687 			if (ostr != NULL) {
688 				cp += strlcpy(cp, options[i].opt_name,
689 				    oackend - cp) + 1;
690 				if (cp <= oackend)
691 					cp += strlcpy(cp, ostr, oackend - cp)
692 					    + 1;
693 
694 				if (cp > oackend) {
695 					nak(EOPTNEG);
696 					exit(1);
697 				}
698 			} else if (errcode >= 0) {
699 				nak(errcode);
700 				exit(1);
701 			}
702 		}
703 	}
704 	if (cp != (char *)&oackp->th_stuff)
705 		return (cp - oackbuf.data);
706 	return (0);
707 }
708 
709 /*
710  * Handle access errors caused by client requests.
711  */
712 
713 static void
714 delay_exit(int ecode)
715 {
716 	struct delay_info dinfo;
717 
718 	/*
719 	 * The most likely cause of an error here is that
720 	 * someone has broadcast an RRQ packet because s/he's
721 	 * trying to boot and doesn't know who the server is.
722 	 * Rather then sending an ERROR packet immediately, we
723 	 * wait a while so that the real server has a better chance
724 	 * of getting through (in case client has lousy Ethernet
725 	 * interface).  We write to a child that handles delayed
726 	 * ERROR packets to avoid delaying service to new
727 	 * requests.  Of course, we would rather just not answer
728 	 * RRQ packets that are broadcasted, but there's no way
729 	 * for a user process to determine this.
730 	 */
731 
732 	dinfo.timestamp = time(0);
733 
734 	/*
735 	 * If running in secure mode, we map all errors to EACCESS
736 	 * so that the client gets no information about which files
737 	 * or directories exist.
738 	 */
739 	if (securetftp)
740 		dinfo.ecode = EACCESS;
741 	else
742 		dinfo.ecode = ecode;
743 
744 	dinfo.from = from;
745 	if (write(delay_fd[1], &dinfo, sizeof (dinfo)) !=
746 	    sizeof (dinfo)) {
747 		syslog(LOG_ERR, "delayed write failed.");
748 		(void) kill(child, SIGKILL);
749 		exit(1);
750 	}
751 	exit(0);
752 }
753 
754 /*
755  * Handle initial connection protocol.
756  */
757 static void
758 tftp(struct tftphdr *tp, int size)
759 {
760 	char *cp;
761 	int readmode, ecode;
762 	struct formats *pf;
763 	char *mode;
764 	int fd;
765 	static boolean_t firsttime = B_TRUE;
766 	int oacklen;
767 	struct stat statb;
768 
769 	readmode = (tp->th_opcode == RRQ);
770 	filename = (char *)&tp->th_stuff;
771 	mode = next_field(filename, &buf.data[size]);
772 	cp = (mode != NULL) ? next_field(mode, &buf.data[size]) : NULL;
773 	if (cp == NULL) {
774 		nak(EBADOP);
775 		exit(1);
776 	}
777 	if (debug && standalone) {
778 		(void) fprintf(stderr, "%s for %s %s ",
779 		    readmode ? "RRQ" : "WRQ", filename, mode);
780 		print_options(stderr, cp, size + buf.data - cp);
781 		(void) putc('\n', stderr);
782 	}
783 	for (pf = formats; pf->f_mode != NULL; pf++)
784 		if (strcasecmp(pf->f_mode, mode) == 0)
785 			break;
786 	if (pf->f_mode == NULL) {
787 		nak(EBADOP);
788 		exit(1);
789 	}
790 
791 	/*
792 	 * XXX fork a new process to handle this request before
793 	 * chroot(), otherwise the parent won't be able to create a
794 	 * new socket as that requires library access to system files
795 	 * and devices.
796 	 */
797 	(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
798 	switch (fork()) {
799 	case -1:
800 		syslog(LOG_ERR, "fork (tftp): %m");
801 		(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
802 		return;
803 	case 0:
804 		(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
805 		break;
806 	default:
807 		(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
808 		return;
809 	}
810 
811 	/*
812 	 * Try to see if we can access the file.  The access can still
813 	 * fail later if we are running in secure mode because of
814 	 * the chroot() call.  We only want to execute the chroot()  once.
815 	 */
816 	if (securetftp && firsttime) {
817 		(void) priv_set(
818 		    PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_CHROOT, NULL);
819 		if (chroot(homedir) == -1) {
820 			syslog(LOG_ERR,
821 			    "tftpd: cannot chroot to directory %s: %m\n",
822 			    homedir);
823 			delay_exit(EACCESS);
824 		}
825 		else
826 		{
827 			firsttime = B_FALSE;
828 		}
829 		(void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
830 		(void) chdir("/");  /* cd to  new root */
831 	}
832 	(void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL);
833 
834 	ecode = (*pf->f_validate)(tp->th_opcode);
835 	if (ecode != 0)
836 		delay_exit(ecode);
837 
838 	/* we don't use the descriptors passed in to the parent */
839 	(void) close(STDIN_FILENO);
840 	(void) close(STDOUT_FILENO);
841 
842 	/*
843 	 * Try to open file as low-priv setuid/setgid.  Note that
844 	 * a chroot() has already been done.
845 	 */
846 	fd = open(filename,
847 	    (readmode ? O_RDONLY : (O_WRONLY|O_TRUNC)) | O_NONBLOCK);
848 	if ((fd < 0) || (fstat(fd, &statb) < 0))
849 		delay_exit((errno == ENOENT) ? ENOTFOUND : EACCESS);
850 
851 	if (((statb.st_mode & ((readmode) ? S_IROTH : S_IWOTH)) == 0) ||
852 	    ((statb.st_mode & S_IFMT) != S_IFREG))
853 		delay_exit(EACCESS);
854 
855 	file = fdopen(fd, readmode ? "r" : "w");
856 	if (file == NULL)
857 		delay_exit(errno + 100);
858 
859 	/* Don't know the size of transfers which involve conversion */
860 	tsize_set = (readmode && (pf->f_convert == 0));
861 	if (tsize_set)
862 		tsize = statb.st_size;
863 
864 	/* Deal with any options sent by the client */
865 	oacklen = process_options(tp->th_opcode, cp, buf.data + size);
866 
867 	if (tp->th_opcode == WRQ)
868 		(*pf->f_recv)(pf, oacklen);
869 	else
870 		(*pf->f_send)(pf, oacklen);
871 
872 	exit(0);
873 }
874 
875 /*
876  *	Maybe map filename into another one.
877  *
878  *	For PNP, we get TFTP boot requests for filenames like
879  *	<Unknown Hex IP Addr>.<Architecture Name>.   We must
880  *	map these to 'pnp.<Architecture Name>'.  Note that
881  *	uppercase is mapped to lowercase in the architecture names.
882  *
883  *	For names <Hex IP Addr> there are two cases.  First,
884  *	it may be a buggy prom that omits the architecture code.
885  *	So first check if <Hex IP Addr>.<arch> is on the filesystem.
886  *	Second, this is how most Sun3s work; assume <arch> is sun3.
887  */
888 
889 static char *
890 pnp_check(char *origname)
891 {
892 	static char buf [MAXNAMLEN + 1];
893 	char *arch, *s, *bufend;
894 	in_addr_t ipaddr;
895 	int len = (origname ? strlen(origname) : 0);
896 	DIR *dir;
897 	struct dirent *dp;
898 
899 	if (securetftp || disable_pnp || len < 8 || len > 14)
900 		return (NULL);
901 
902 	/*
903 	 * XXX see if this cable allows pnp; if not, return NULL
904 	 * Requires YP support for determining this!
905 	 */
906 
907 	ipaddr = htonl(strtol(origname, &arch, 16));
908 	if ((arch == NULL) || (len > 8 && *arch != '.'))
909 		return (NULL);
910 	if (len == 8)
911 		arch = "SUN3";
912 	else
913 		arch++;
914 
915 	/*
916 	 * Allow <Hex IP Addr>* filename request to to be
917 	 * satisfied by <Hex IP Addr><Any Suffix> rather
918 	 * than enforcing this to be Sun3 systems.  Also serves
919 	 * to make case of suffix a don't-care.
920 	 */
921 	if ((dir = opendir(homedir)) == NULL)
922 		return (NULL);
923 	while ((dp = readdir(dir)) != NULL) {
924 		if (strncmp(origname, dp->d_name, 8) == 0) {
925 			(void) strlcpy(buf, dp->d_name, sizeof (buf));
926 			(void) closedir(dir);
927 			return (buf);
928 		}
929 	}
930 	(void) closedir(dir);
931 
932 	/*
933 	 * XXX maybe call YP master for most current data iff
934 	 * pnp is enabled.
935 	 */
936 
937 	/*
938 	 * only do mapping PNP boot file name for machines that
939 	 * are not in the hosts database.
940 	 */
941 	if (gethostbyaddr((char *)&ipaddr, sizeof (ipaddr), AF_INET) != NULL)
942 		return (NULL);
943 
944 	s = buf + strlcpy(buf, "pnp.", sizeof (buf));
945 	bufend = &buf[sizeof (buf) - 1];
946 	while ((*arch != '\0') && (s < bufend))
947 		*s++ = tolower (*arch++);
948 	*s = '\0';
949 	return (buf);
950 }
951 
952 
953 /*
954  * Try to validate filename. If the filename doesn't exist try PNP mapping.
955  */
956 static int
957 validate_filename(int mode)
958 {
959 	struct stat stbuf;
960 	char *origfile;
961 
962 	if (stat(filename, &stbuf) < 0) {
963 		if (errno != ENOENT)
964 			return (EACCESS);
965 		if (mode == WRQ)
966 			return (ENOTFOUND);
967 
968 		/* try to map requested filename into a pnp filename */
969 		origfile = filename;
970 		filename = pnp_check(origfile);
971 		if (filename == NULL)
972 			return (ENOTFOUND);
973 
974 		if (stat(filename, &stbuf) < 0)
975 			return (errno == ENOENT ? ENOTFOUND : EACCESS);
976 		syslog(LOG_NOTICE, "%s -> %s\n", origfile, filename);
977 	}
978 
979 	return (0);
980 }
981 
982 /* ARGSUSED */
983 static void
984 timer(int signum)
985 {
986 	timeout += rexmtval;
987 	if (timeout >= maxtimeout)
988 		exit(1);
989 	siglongjmp(timeoutbuf, 1);
990 }
991 
992 /*
993  * Send the requested file.
994  */
995 static void
996 tftpd_sendfile(struct formats *pf, int oacklen)
997 {
998 	struct tftphdr *dp;
999 	volatile ushort_t block = 1;
1000 	int size, n, serrno;
1001 
1002 	if (oacklen != 0) {
1003 		(void) sigset(SIGALRM, timer);
1004 		timeout = 0;
1005 		(void) sigsetjmp(timeoutbuf, 1);
1006 		if (debug && standalone) {
1007 			(void) fputs("Sending OACK ", stderr);
1008 			print_options(stderr, (char *)&oackbuf.hdr.th_stuff,
1009 			    oacklen - 2);
1010 			(void) putc('\n', stderr);
1011 		}
1012 		if (sendto(peer, &oackbuf, oacklen, 0,
1013 		    (struct sockaddr *)&from, fromplen) != oacklen) {
1014 			if (debug && standalone) {
1015 				serrno = errno;
1016 				perror("sendto (oack)");
1017 				errno = serrno;
1018 			}
1019 			SYSLOG_MSG("sendto (oack): %m");
1020 			goto abort;
1021 		}
1022 		(void) alarm(rexmtval); /* read the ack */
1023 		for (;;) {
1024 			(void) sigrelse(SIGALRM);
1025 			n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
1026 			(void) sighold(SIGALRM);
1027 			if (n < 0) {
1028 				if (errno == EINTR)
1029 					continue;
1030 				serrno = errno;
1031 				SYSLOG_MSG("recv (ack): %m");
1032 				if (debug && standalone) {
1033 					errno = serrno;
1034 					perror("recv (ack)");
1035 				}
1036 				goto abort;
1037 			}
1038 			ackbuf.tb_hdr.th_opcode =
1039 			    ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
1040 			ackbuf.tb_hdr.th_block =
1041 			    ntohs((ushort_t)ackbuf.tb_hdr.th_block);
1042 
1043 			if (ackbuf.tb_hdr.th_opcode == ERROR) {
1044 				if (debug && standalone) {
1045 					(void) fprintf(stderr,
1046 					    "received ERROR %d",
1047 					    ackbuf.tb_hdr.th_code);
1048 					if (n > 4)
1049 						(void) fprintf(stderr,
1050 						    " %.*s", n - 4,
1051 						    ackbuf.tb_hdr.th_msg);
1052 					(void) putc('\n', stderr);
1053 				}
1054 				goto abort;
1055 			}
1056 
1057 			if (ackbuf.tb_hdr.th_opcode == ACK) {
1058 				if (debug && standalone)
1059 					(void) fprintf(stderr,
1060 					    "received ACK for block %d\n",
1061 					    ackbuf.tb_hdr.th_block);
1062 				if (ackbuf.tb_hdr.th_block == 0)
1063 					break;
1064 				/*
1065 				 * Don't resend the OACK, avoids getting stuck
1066 				 * in an OACK/ACK loop if the client keeps
1067 				 * replying with a bad ACK. Client will either
1068 				 * send a good ACK or timeout sending bad ones.
1069 				 */
1070 			}
1071 		}
1072 		cancel_alarm();
1073 	}
1074 	dp = r_init();
1075 	do {
1076 		(void) sigset(SIGALRM, timer);
1077 		size = readit(file, &dp, pf->f_convert);
1078 		if (size < 0) {
1079 			nak(errno + 100);
1080 			goto abort;
1081 		}
1082 		dp->th_opcode = htons((ushort_t)DATA);
1083 		dp->th_block = htons((ushort_t)block);
1084 		timeout = 0;
1085 		(void) sigsetjmp(timeoutbuf, 1);
1086 		if (debug && standalone)
1087 			(void) fprintf(stderr, "Sending DATA block %d\n",
1088 			    block);
1089 		if (sendto(peer, dp, size + 4, 0,
1090 		    (struct sockaddr *)&from,  fromplen) != size + 4) {
1091 			if (debug && standalone) {
1092 				serrno = errno;
1093 				perror("sendto (data)");
1094 				errno = serrno;
1095 			}
1096 			SYSLOG_MSG("sendto (data): %m");
1097 			goto abort;
1098 		}
1099 		read_ahead(file, pf->f_convert);
1100 		(void) alarm(rexmtval); /* read the ack */
1101 		for (;;) {
1102 			(void) sigrelse(SIGALRM);
1103 			n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
1104 			(void) sighold(SIGALRM);
1105 			if (n < 0) {
1106 				if (errno == EINTR)
1107 					continue;
1108 				serrno = errno;
1109 				SYSLOG_MSG("recv (ack): %m");
1110 				if (debug && standalone) {
1111 					errno = serrno;
1112 					perror("recv (ack)");
1113 				}
1114 				goto abort;
1115 			}
1116 			ackbuf.tb_hdr.th_opcode =
1117 			    ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
1118 			ackbuf.tb_hdr.th_block =
1119 			    ntohs((ushort_t)ackbuf.tb_hdr.th_block);
1120 
1121 			if (ackbuf.tb_hdr.th_opcode == ERROR) {
1122 				if (debug && standalone) {
1123 					(void) fprintf(stderr,
1124 					    "received ERROR %d",
1125 					    ackbuf.tb_hdr.th_code);
1126 					if (n > 4)
1127 						(void) fprintf(stderr,
1128 						    " %.*s", n - 4,
1129 						    ackbuf.tb_hdr.th_msg);
1130 					(void) putc('\n', stderr);
1131 				}
1132 				goto abort;
1133 			}
1134 
1135 			if (ackbuf.tb_hdr.th_opcode == ACK) {
1136 				if (debug && standalone)
1137 					(void) fprintf(stderr,
1138 						"received ACK for block %d\n",
1139 						ackbuf.tb_hdr.th_block);
1140 				if (ackbuf.tb_hdr.th_block == block) {
1141 					break;
1142 				}
1143 				/*
1144 				 * Never resend the current DATA packet on
1145 				 * receipt of a duplicate ACK, doing so would
1146 				 * cause the "Sorcerer's Apprentice Syndrome".
1147 				 */
1148 			}
1149 		}
1150 		cancel_alarm();
1151 		block++;
1152 	} while (size == blocksize);
1153 
1154 abort:
1155 	cancel_alarm();
1156 	(void) fclose(file);
1157 }
1158 
1159 /* ARGSUSED */
1160 static void
1161 justquit(int signum)
1162 {
1163 	exit(0);
1164 }
1165 
1166 /*
1167  * Receive a file.
1168  */
1169 static void
1170 tftpd_recvfile(struct formats *pf, int oacklen)
1171 {
1172 	struct tftphdr *dp;
1173 	struct tftphdr *ap;    /* ack buffer */
1174 	ushort_t block = 0;
1175 	int n, size, acklen, serrno;
1176 
1177 	dp = w_init();
1178 	ap = &ackbuf.tb_hdr;
1179 	do {
1180 		(void) sigset(SIGALRM, timer);
1181 		timeout = 0;
1182 		if (oacklen == 0) {
1183 			ap->th_opcode = htons((ushort_t)ACK);
1184 			ap->th_block = htons((ushort_t)block);
1185 			acklen = 4;
1186 		} else {
1187 			/* copy OACK packet to the ack buffer ready to send */
1188 			(void) memcpy(&ackbuf, &oackbuf, oacklen);
1189 			acklen = oacklen;
1190 			oacklen = 0;
1191 		}
1192 		block++;
1193 		(void) sigsetjmp(timeoutbuf, 1);
1194 send_ack:
1195 		if (debug && standalone) {
1196 			if (ap->th_opcode == htons((ushort_t)ACK)) {
1197 				(void) fprintf(stderr,
1198 				    "Sending ACK for block %d\n", block - 1);
1199 			} else {
1200 				(void) fprintf(stderr, "Sending OACK ");
1201 				print_options(stderr, (char *)&ap->th_stuff,
1202 				    acklen - 2);
1203 				(void) putc('\n', stderr);
1204 			}
1205 		}
1206 		if (sendto(peer, &ackbuf, acklen, 0, (struct sockaddr *)&from,
1207 		    fromplen) != acklen) {
1208 			if (ap->th_opcode == htons((ushort_t)ACK)) {
1209 				if (debug && standalone) {
1210 					serrno = errno;
1211 					perror("sendto (ack)");
1212 					errno = serrno;
1213 				}
1214 				syslog(LOG_ERR, "sendto (ack): %m\n");
1215 			} else {
1216 				if (debug && standalone) {
1217 					serrno = errno;
1218 					perror("sendto (oack)");
1219 					errno = serrno;
1220 				}
1221 				syslog(LOG_ERR, "sendto (oack): %m\n");
1222 			}
1223 			goto abort;
1224 		}
1225 		if (write_behind(file, pf->f_convert) < 0) {
1226 			nak(errno + 100);
1227 			goto abort;
1228 		}
1229 		(void) alarm(rexmtval);
1230 		for (;;) {
1231 			(void) sigrelse(SIGALRM);
1232 			n = recv(peer, dp, blocksize + 4, 0);
1233 			(void) sighold(SIGALRM);
1234 			if (n < 0) { /* really? */
1235 				if (errno == EINTR)
1236 					continue;
1237 				syslog(LOG_ERR, "recv (data): %m");
1238 				goto abort;
1239 			}
1240 			dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
1241 			dp->th_block = ntohs((ushort_t)dp->th_block);
1242 			if (dp->th_opcode == ERROR) {
1243 				cancel_alarm();
1244 				if (debug && standalone) {
1245 					(void) fprintf(stderr,
1246 					    "received ERROR %d", dp->th_code);
1247 					if (n > 4)
1248 						(void) fprintf(stderr,
1249 						    " %.*s", n - 4, dp->th_msg);
1250 					(void) putc('\n', stderr);
1251 				}
1252 				return;
1253 			}
1254 			if (dp->th_opcode == DATA) {
1255 				if (debug && standalone)
1256 					(void) fprintf(stderr,
1257 						"Received DATA block %d\n",
1258 						dp->th_block);
1259 				if (dp->th_block == block) {
1260 					break;   /* normal */
1261 				}
1262 				/* Re-synchronize with the other side */
1263 				if (synchnet(peer) < 0) {
1264 					nak(errno + 100);
1265 					goto abort;
1266 				}
1267 				if (dp->th_block == (block-1))
1268 					goto send_ack; /* rexmit */
1269 			}
1270 		}
1271 		cancel_alarm();
1272 		/*  size = write(file, dp->th_data, n - 4); */
1273 		size = writeit(file, &dp, n - 4, pf->f_convert);
1274 		if (size != (n - 4)) {
1275 			nak((size < 0) ? (errno + 100) : ENOSPACE);
1276 			goto abort;
1277 		}
1278 	} while (size == blocksize);
1279 	if (write_behind(file, pf->f_convert) < 0) {
1280 		nak(errno + 100);
1281 		goto abort;
1282 	}
1283 	n = fclose(file);	/* close data file */
1284 	file = NULL;
1285 	if (n == EOF) {
1286 		nak(errno + 100);
1287 		goto abort;
1288 	}
1289 
1290 	ap->th_opcode = htons((ushort_t)ACK);    /* send the "final" ack */
1291 	ap->th_block = htons((ushort_t)(block));
1292 	if (debug && standalone)
1293 		(void) fprintf(stderr, "Sending ACK for block %d\n", block);
1294 	if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
1295 	    fromplen) == -1) {
1296 		if (debug && standalone)
1297 			perror("sendto (ack)");
1298 	}
1299 	(void) sigset(SIGALRM, justquit); /* just quit on timeout */
1300 	(void) alarm(rexmtval);
1301 	/* normally times out and quits */
1302 	n = recv(peer, dp, blocksize + 4, 0);
1303 	(void) alarm(0);
1304 	dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
1305 	dp->th_block = ntohs((ushort_t)dp->th_block);
1306 	if (n >= 4 &&		/* if read some data */
1307 	    dp->th_opcode == DATA && /* and got a data block */
1308 	    block == dp->th_block) {	/* then my last ack was lost */
1309 		if (debug && standalone) {
1310 			(void) fprintf(stderr, "Sending ACK for block %d\n",
1311 			    block);
1312 		}
1313 		/* resend final ack */
1314 		if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
1315 		    fromplen) == -1) {
1316 			if (debug && standalone)
1317 				perror("sendto (last ack)");
1318 		}
1319 	}
1320 
1321 abort:
1322 	cancel_alarm();
1323 	if (file != NULL)
1324 		(void) fclose(file);
1325 }
1326 
1327 /*
1328  * Send a nak packet (error message).
1329  * Error code passed in is one of the
1330  * standard TFTP codes, or a UNIX errno
1331  * offset by 100.
1332  * Handles connected as well as unconnected peer.
1333  */
1334 static void
1335 nak(int error)
1336 {
1337 	struct tftphdr *tp;
1338 	int length;
1339 	struct errmsg *pe;
1340 	int ret;
1341 
1342 	tp = &buf.hdr;
1343 	tp->th_opcode = htons((ushort_t)ERROR);
1344 	tp->th_code = htons((ushort_t)error);
1345 	for (pe = errmsgs; pe->e_code >= 0; pe++)
1346 		if (pe->e_code == error)
1347 			break;
1348 	if (pe->e_code < 0) {
1349 		pe->e_msg = strerror(error - 100);
1350 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
1351 	}
1352 	(void) strlcpy(tp->th_msg, (pe->e_msg != NULL) ? pe->e_msg : "UNKNOWN",
1353 	    sizeof (buf) - sizeof (struct tftphdr));
1354 	length = strlen(tp->th_msg);
1355 	length += sizeof (struct tftphdr);
1356 	if (debug && standalone)
1357 		(void) fprintf(stderr, "Sending NAK: %s\n", tp->th_msg);
1358 
1359 	ret = sendto(peer, &buf, length, 0, (struct sockaddr *)&from,
1360 	    fromplen);
1361 	if (ret == -1 && errno == EISCONN) {
1362 		/* Try without an address */
1363 		ret = send(peer, &buf, length, 0);
1364 	}
1365 	if (ret == -1) {
1366 		if (standalone)
1367 			perror("sendto (nak)");
1368 		else
1369 			syslog(LOG_ERR, "tftpd: nak: %m\n");
1370 	} else if (ret != length) {
1371 		if (standalone)
1372 			perror("sendto (nak) lost data");
1373 		else
1374 			syslog(LOG_ERR, "tftpd: nak: %d lost\n", length - ret);
1375 	}
1376 }
1377