xref: /freebsd/contrib/pf/ftp-proxy/ftp-proxy.c (revision f0a75d274af375d15b97b830966b99a02b7db911)
1 /*	$OpenBSD: ftp-proxy.c,v 1.41 2005/03/05 23:11:19 cloder Exp $ */
2 
3 /*
4  * Copyright (c) 1996-2001
5  *	Obtuse Systems Corporation.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the Obtuse Systems nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY OBTUSE SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL OBTUSE SYSTEMS CORPORATION OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 /*
37  * ftp proxy, Originally based on juniper_ftp_proxy from the Obtuse
38  * Systems juniper firewall, written by Dan Boulet <danny@obtuse.com>
39  * and Bob Beck <beck@obtuse.com>
40  *
41  * This version basically passes everything through unchanged except
42  * for the PORT and the * "227 Entering Passive Mode" reply.
43  *
44  * A PORT command is handled by noting the IP address and port number
45  * specified and then configuring a listen port on some very high port
46  * number and telling the server about it using a PORT message.
47  * We then watch for an in-bound connection on the port from the server
48  * and connect to the client's port when it happens.
49  *
50  * A "227 Entering Passive Mode" reply is handled by noting the IP address
51  * and port number specified and then configuring a listen port on some
52  * very high port number and telling the client about it using a
53  * "227 Entering Passive Mode" reply.
54  * We then watch for an in-bound connection on the port from the client
55  * and connect to the server's port when it happens.
56  *
57  * supports tcp wrapper lookups/access control with the -w flag using
58  * the real destination address - the tcp wrapper stuff is done after
59  * the real destination address is retrieved from pf
60  *
61  */
62 
63 /*
64  * TODO:
65  * Plenty, this is very basic, with the idea to get it in clean first.
66  *
67  * - IPv6 and EPASV support
68  * - Content filter support
69  * - filename filter support
70  * - per-user rules perhaps.
71  */
72 
73 #include <sys/param.h>
74 #include <sys/time.h>
75 #include <sys/socket.h>
76 
77 #include <net/if.h>
78 #include <netinet/in.h>
79 
80 #include <arpa/inet.h>
81 
82 #include <ctype.h>
83 #include <errno.h>
84 #include <grp.h>
85 #include <netdb.h>
86 #include <pwd.h>
87 #include <signal.h>
88 #include <stdarg.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <string.h>
92 #include <sysexits.h>
93 #include <syslog.h>
94 #include <unistd.h>
95 
96 #include "util.h"
97 
98 #ifdef LIBWRAP
99 #include <tcpd.h>
100 int allow_severity = LOG_INFO;
101 int deny_severity = LOG_NOTICE;
102 #endif /* LIBWRAP */
103 
104 int min_port = IPPORT_HIFIRSTAUTO;
105 int max_port = IPPORT_HILASTAUTO;
106 
107 #define STARTBUFSIZE  1024	/* Must be at least 3 */
108 
109 /*
110  * Variables used to support PORT mode connections.
111  *
112  * This gets a bit complicated.
113  *
114  * If PORT mode is on then client_listen_sa describes the socket that
115  * the real client is listening on and server_listen_sa describes the
116  * socket that we are listening on (waiting for the real server to connect
117  * with us).
118  *
119  * If PASV mode is on then client_listen_sa describes the socket that
120  * we are listening on (waiting for the real client to connect to us on)
121  * and server_listen_sa describes the socket that the real server is
122  * listening on.
123  *
124  * If the socket we are listening on gets a connection then we connect
125  * to the other side's socket.  Similarly, if a connected socket is
126  * shutdown then we shutdown the other side's socket.
127  */
128 
129 double xfer_start_time;
130 
131 struct sockaddr_in real_server_sa;
132 struct sockaddr_in client_listen_sa;
133 struct sockaddr_in server_listen_sa;
134 struct sockaddr_in proxy_sa;
135 struct in_addr src_addr;
136 
137 int client_listen_socket = -1;	/* Only used in PASV mode */
138 int client_data_socket = -1;	/* Connected socket to real client */
139 int server_listen_socket = -1;	/* Only used in PORT mode */
140 int server_data_socket = -1;	/* Connected socket to real server */
141 int client_data_bytes, server_data_bytes;
142 
143 int AnonFtpOnly;
144 int Verbose;
145 int NatMode;
146 int ReverseMode;
147 
148 char ClientName[NI_MAXHOST];
149 char RealServerName[NI_MAXHOST];
150 char OurName[NI_MAXHOST];
151 
152 const char *User = "proxy";
153 const char *Group;
154 
155 extern int Debug_Level;
156 extern int Use_Rdns;
157 extern in_addr_t Bind_Addr;
158 extern char *__progname;
159 
160 typedef enum {
161 	UNKNOWN_MODE,
162 	PORT_MODE,
163 	PASV_MODE,
164 	EPRT_MODE,
165 	EPSV_MODE
166 } connection_mode_t;
167 
168 connection_mode_t connection_mode;
169 
170 extern void	debuglog(int debug_level, const char *fmt, ...);
171 double		wallclock_time(void);
172 void		show_xfer_stats(void);
173 void		log_control_command (char *cmd, int client);
174 int		new_dataconn(int server);
175 void		do_client_cmd(struct csiob *client, struct csiob *server);
176 void		do_server_reply(struct csiob *server, struct csiob *client);
177 static void
178 usage(void)
179 {
180 	syslog(LOG_NOTICE,
181 	    "usage: %s [-AnrVw] [-a address] [-D debuglevel] [-g group]"
182 	    " [-M maxport] [-m minport] [-R address[:port]] [-S address]"
183 	    " [-t timeout] [-u user]", __progname);
184 	exit(EX_USAGE);
185 }
186 
187 static void
188 close_client_data(void)
189 {
190 	if (client_data_socket >= 0) {
191 		shutdown(client_data_socket, 2);
192 		close(client_data_socket);
193 		client_data_socket = -1;
194 	}
195 }
196 
197 static void
198 close_server_data(void)
199 {
200 	if (server_data_socket >= 0)  {
201 		shutdown(server_data_socket, 2);
202 		close(server_data_socket);
203 		server_data_socket = -1;
204 	}
205 }
206 
207 static void
208 drop_privs(void)
209 {
210 	struct passwd *pw;
211 	struct group *gr;
212 	uid_t uid = 0;
213 	gid_t gid = 0;
214 
215 	if (User != NULL) {
216 		pw = getpwnam(User);
217 		if (pw == NULL) {
218 			syslog(LOG_ERR, "cannot find user %s", User);
219 			exit(EX_USAGE);
220 		}
221 		uid = pw->pw_uid;
222 		gid = pw->pw_gid;
223 	}
224 
225 	if (Group != NULL) {
226 		gr = getgrnam(Group);
227 		if (gr == NULL) {
228 			syslog(LOG_ERR, "cannot find group %s", Group);
229 			exit(EX_USAGE);
230 		}
231 		gid = gr->gr_gid;
232 	}
233 
234 	if (gid != 0 && (setegid(gid) == -1 || setgid(gid) == -1)) {
235 		syslog(LOG_ERR, "cannot drop group privs (%m)");
236 		exit(EX_CONFIG);
237 	}
238 
239 	if (uid != 0 && (seteuid(uid) == -1 || setuid(uid) == -1)) {
240 		syslog(LOG_ERR, "cannot drop root privs (%m)");
241 		exit(EX_CONFIG);
242 	}
243 }
244 
245 #ifdef LIBWRAP
246 /*
247  * Check a connection against the tcpwrapper, log if we're going to
248  * reject it, returns: 0 -> reject, 1 -> accept. We add in hostnames
249  * if we are set to do reverse DNS, otherwise no.
250  */
251 static int
252 check_host(struct sockaddr_in *client_sin, struct sockaddr_in *server_sin)
253 {
254 	char cname[NI_MAXHOST];
255 	char sname[NI_MAXHOST];
256 	struct request_info request;
257 	int i;
258 
259 	request_init(&request, RQ_DAEMON, __progname, RQ_CLIENT_SIN,
260 	    client_sin, RQ_SERVER_SIN, server_sin, RQ_CLIENT_ADDR,
261 	    inet_ntoa(client_sin->sin_addr), 0);
262 
263 	if (Use_Rdns)  {
264 		/*
265 		 * We already looked these up, but we have to do it again
266 		 * for tcp wrapper, to ensure that we get the DNS name, since
267 		 * the tcp wrapper cares about these things, and we don't
268 		 * want to pass in a printed address as a name.
269 		 */
270 		i = getnameinfo((struct sockaddr *) &client_sin->sin_addr,
271 		    sizeof(&client_sin->sin_addr), cname, sizeof(cname),
272 		    NULL, 0, NI_NAMEREQD);
273 
274 		if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN)
275 			strlcpy(cname, STRING_UNKNOWN, sizeof(cname));
276 
277 		i = getnameinfo((struct sockaddr *)&server_sin->sin_addr,
278 		    sizeof(&server_sin->sin_addr), sname, sizeof(sname),
279 		    NULL, 0, NI_NAMEREQD);
280 
281 		if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN)
282 			strlcpy(sname, STRING_UNKNOWN, sizeof(sname));
283 	} else {
284 		/*
285 		 * ensure the TCP wrapper doesn't start doing
286 		 * reverse DNS lookups if we aren't supposed to.
287 		 */
288 		strlcpy(cname, STRING_UNKNOWN, sizeof(cname));
289 		strlcpy(sname, STRING_UNKNOWN, sizeof(sname));
290 	}
291 
292 	request_set(&request, RQ_SERVER_ADDR, inet_ntoa(server_sin->sin_addr),
293 	    0);
294 	request_set(&request, RQ_CLIENT_NAME, cname, RQ_SERVER_NAME, sname, 0);
295 
296 	if (!hosts_access(&request)) {
297 		syslog(LOG_NOTICE, "tcpwrappers rejected: %s -> %s",
298 		    ClientName, RealServerName);
299 		return(0);
300 	}
301 	return(1);
302 }
303 #endif /* LIBWRAP */
304 
305 double
306 wallclock_time(void)
307 {
308 	struct timeval tv;
309 
310 	gettimeofday(&tv, NULL);
311 	return(tv.tv_sec + tv.tv_usec / 1e6);
312 }
313 
314 /*
315  * Show the stats for this data transfer
316  */
317 void
318 show_xfer_stats(void)
319 {
320 	char tbuf[1000];
321 	double delta;
322 	size_t len;
323 	int i = -1;
324 
325 	if (!Verbose)
326 		return;
327 
328 	delta = wallclock_time() - xfer_start_time;
329 
330 	if (delta < 0.001)
331 		delta = 0.001;
332 
333 	if (client_data_bytes == 0 && server_data_bytes == 0) {
334 		syslog(LOG_INFO,
335 		  "data transfer complete (no bytes transferred)");
336 		return;
337 	}
338 
339 	len = sizeof(tbuf);
340 
341 	if (delta >= 60) {
342 		int idelta;
343 
344 		idelta = delta + 0.5;
345 		if (idelta >= 60*60) {
346 			i = snprintf(tbuf, len,
347 			    "data transfer complete (%dh %dm %ds",
348 			    idelta / (60*60), (idelta % (60*60)) / 60,
349 			    idelta % 60);
350 			if (i == -1 || i >= len)
351 				goto logit;
352 			len -= i;
353 		} else {
354 			i = snprintf(tbuf, len,
355 			    "data transfer complete (%dm %ds", idelta / 60,
356 			    idelta % 60);
357 			if (i == -1 || i >= len)
358 				goto logit;
359 			len -= i;
360 		}
361 	} else {
362 		i = snprintf(tbuf, len, "data transfer complete (%.1fs",
363 		    delta);
364 		if (i == -1 || i >= len)
365 			goto logit;
366 		len -= i;
367 	}
368 
369 	if (client_data_bytes > 0) {
370 		i = snprintf(&tbuf[strlen(tbuf)], len,
371 		    ", %d bytes to server) (%.1fKB/s", client_data_bytes,
372 		    (client_data_bytes / delta) / (double)1024);
373 		if (i == -1 || i >= len)
374 			goto logit;
375 		len -= i;
376 	}
377 	if (server_data_bytes > 0) {
378 		i = snprintf(&tbuf[strlen(tbuf)], len,
379 		    ", %d bytes to client) (%.1fKB/s", server_data_bytes,
380 		    (server_data_bytes / delta) / (double)1024);
381 		if (i == -1 || i >= len)
382 			goto logit;
383 		len -= i;
384 	}
385 	strlcat(tbuf, ")", sizeof(tbuf));
386  logit:
387 	if (i != -1)
388 		syslog(LOG_INFO, "%s", tbuf);
389 }
390 
391 void
392 log_control_command (char *cmd, int client)
393 {
394 	/* log an ftp control command or reply */
395 	const char *logstring;
396 	int level = LOG_DEBUG;
397 
398 	if (!Verbose)
399 		return;
400 
401 	/* don't log passwords */
402 	if (strncasecmp(cmd, "pass ", 5) == 0)
403 		logstring = "PASS XXXX";
404 	else
405 		logstring = cmd;
406 	if (client) {
407 		/* log interesting stuff at LOG_INFO, rest at LOG_DEBUG */
408 		if ((strncasecmp(cmd, "user ", 5) == 0) ||
409 		    (strncasecmp(cmd, "retr ", 5) == 0) ||
410 		    (strncasecmp(cmd, "cwd ", 4) == 0) ||
411 		    (strncasecmp(cmd, "stor " ,5) == 0))
412 			level = LOG_INFO;
413 	}
414 	syslog(level, "%s %s", client ? "client:" : " server:",
415 	    logstring);
416 }
417 
418 /*
419  * set ourselves up for a new data connection. Direction is toward client if
420  * "server" is 0, towards server otherwise.
421  */
422 int
423 new_dataconn(int server)
424 {
425 	/*
426 	 * Close existing data conn.
427 	 */
428 
429 	if (client_listen_socket != -1) {
430 		close(client_listen_socket);
431 		client_listen_socket = -1;
432 	}
433 	close_client_data();
434 
435 	if (server_listen_socket != -1) {
436 		close(server_listen_socket);
437 		server_listen_socket = -1;
438 	}
439 	close_server_data();
440 
441 	if (server) {
442 		bzero(&server_listen_sa, sizeof(server_listen_sa));
443 		server_listen_socket = get_backchannel_socket(SOCK_STREAM,
444 		    min_port, max_port, -1, 1, &server_listen_sa);
445 
446 		if (server_listen_socket == -1) {
447 			syslog(LOG_INFO, "server socket bind() failed (%m)");
448 			exit(EX_OSERR);
449 		}
450 		if (listen(server_listen_socket, 5) != 0) {
451 			syslog(LOG_INFO, "server socket listen() failed (%m)");
452 			exit(EX_OSERR);
453 		}
454 	} else {
455 		bzero(&client_listen_sa, sizeof(client_listen_sa));
456 		client_listen_socket = get_backchannel_socket(SOCK_STREAM,
457 		    min_port, max_port, -1, 1, &client_listen_sa);
458 
459 		if (client_listen_socket == -1) {
460 			syslog(LOG_NOTICE,
461 			    "cannot get client listen socket (%m)");
462 			exit(EX_OSERR);
463 		}
464 		if (listen(client_listen_socket, 5) != 0) {
465 			syslog(LOG_NOTICE,
466 			    "cannot listen on client socket (%m)");
467 			exit(EX_OSERR);
468 		}
469 	}
470 	return(0);
471 }
472 
473 static void
474 connect_pasv_backchannel(void)
475 {
476 	struct sockaddr_in listen_sa;
477 	socklen_t salen;
478 
479 	/*
480 	 * We are about to accept a connection from the client.
481 	 * This is a PASV data connection.
482 	 */
483 	debuglog(2, "client listen socket ready");
484 
485 	close_server_data();
486 	close_client_data();
487 
488 	salen = sizeof(listen_sa);
489 	client_data_socket = accept(client_listen_socket,
490 	    (struct sockaddr *)&listen_sa, &salen);
491 
492 	if (client_data_socket < 0) {
493 		syslog(LOG_NOTICE, "accept() failed (%m)");
494 		exit(EX_OSERR);
495 	}
496 	close(client_listen_socket);
497 	client_listen_socket = -1;
498 	memset(&listen_sa, 0, sizeof(listen_sa));
499 
500 	server_data_socket = get_backchannel_socket(SOCK_STREAM, min_port,
501 	    max_port, -1, 1, &listen_sa);
502 	if (server_data_socket < 0) {
503 		syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)");
504 		exit(EX_OSERR);
505 	}
506 	if (connect(server_data_socket, (struct sockaddr *) &server_listen_sa,
507 	    sizeof(server_listen_sa)) != 0) {
508 		syslog(LOG_NOTICE, "connect() failed (%m)");
509 		exit(EX_NOHOST);
510 	}
511 	client_data_bytes = 0;
512 	server_data_bytes = 0;
513 	xfer_start_time = wallclock_time();
514 }
515 
516 static void
517 connect_port_backchannel(void)
518 {
519 	struct sockaddr_in listen_sa;
520 	socklen_t salen;
521 
522 	/*
523 	 * We are about to accept a connection from the server.
524 	 * This is a PORT or EPRT data connection.
525 	 */
526 	debuglog(2, "server listen socket ready");
527 
528 	close_server_data();
529 	close_client_data();
530 
531 	salen = sizeof(listen_sa);
532 	server_data_socket = accept(server_listen_socket,
533 	    (struct sockaddr *)&listen_sa, &salen);
534 	if (server_data_socket < 0) {
535 		syslog(LOG_NOTICE, "accept() failed (%m)");
536 		exit(EX_OSERR);
537 	}
538 	close(server_listen_socket);
539 	server_listen_socket = -1;
540 
541 	if (getuid() != 0)  {
542 		/*
543 		 * We're not running as root, so we get a backchannel
544 		 * socket bound in our designated range, instead of
545 		 * getting one bound to port 20 - This is deliberately
546 		 * not RFC compliant.
547 		 */
548 		bcopy(&src_addr, &listen_sa.sin_addr, sizeof(struct in_addr));
549 		client_data_socket =  get_backchannel_socket(SOCK_STREAM,
550 		    min_port, max_port, -1, 1, &listen_sa);
551 		if (client_data_socket < 0) {
552 			syslog(LOG_NOTICE,  "get_backchannel_socket() failed (%m)");
553 			exit(EX_OSERR);
554 		}
555 
556 	} else {
557 
558 		/*
559 		 * We're root, get our backchannel socket bound to port
560 		 * 20 here, so we're fully RFC compliant.
561 		 */
562 		client_data_socket = socket(AF_INET, SOCK_STREAM, 0);
563 
564 		salen = 1;
565 		listen_sa.sin_family = AF_INET;
566 		bcopy(&src_addr, &listen_sa.sin_addr, sizeof(struct in_addr));
567 		listen_sa.sin_port = htons(20);
568 
569 		if (setsockopt(client_data_socket, SOL_SOCKET, SO_REUSEADDR,
570 		    &salen, sizeof(salen)) == -1) {
571 			syslog(LOG_NOTICE, "setsockopt() failed (%m)");
572 			exit(EX_OSERR);
573 		}
574 
575 		if (bind(client_data_socket, (struct sockaddr *)&listen_sa,
576 		    sizeof(listen_sa)) == - 1) {
577 			syslog(LOG_NOTICE, "data channel bind() failed (%m)");
578 			exit(EX_OSERR);
579 		}
580 	}
581 
582 	if (connect(client_data_socket, (struct sockaddr *) &client_listen_sa,
583 	    sizeof(client_listen_sa)) != 0) {
584 		syslog(LOG_INFO, "cannot connect data channel (%m)");
585 		exit(EX_NOHOST);
586 	}
587 
588 	client_data_bytes = 0;
589 	server_data_bytes = 0;
590 	xfer_start_time = wallclock_time();
591 }
592 
593 void
594 do_client_cmd(struct csiob *client, struct csiob *server)
595 {
596 	int i, j, rv;
597 	char tbuf[100];
598 	char *sendbuf = NULL;
599 
600 	log_control_command((char *)client->line_buffer, 1);
601 
602 	/* client->line_buffer is an ftp control command.
603 	 * There is no reason for these to be very long.
604 	 * In the interest of limiting buffer overrun attempts,
605 	 * we catch them here.
606 	 */
607 	if (strlen((char *)client->line_buffer) > 512) {
608 		syslog(LOG_NOTICE, "excessively long control command");
609 		exit(EX_DATAERR);
610 	}
611 
612 	/*
613 	 * Check the client user provided if needed
614 	 */
615 	if (AnonFtpOnly && strncasecmp((char *)client->line_buffer, "user ",
616 	    strlen("user ")) == 0) {
617 		char *cp;
618 
619 		cp = (char *) client->line_buffer + strlen("user ");
620 		if ((strcasecmp(cp, "ftp\r\n") != 0) &&
621 		    (strcasecmp(cp, "anonymous\r\n") != 0)) {
622 			/*
623 			 * this isn't anonymous - give the client an
624 			 * error before they send a password
625 			 */
626 			snprintf(tbuf, sizeof(tbuf),
627 			    "500 Only anonymous FTP is allowed\r\n");
628 			j = 0;
629 			i = strlen(tbuf);
630 			do {
631 				rv = send(client->fd, tbuf + j, i - j, 0);
632 				if (rv == -1 && errno != EAGAIN &&
633 				    errno != EINTR)
634 					break;
635 				else if (rv != -1)
636 					j += rv;
637 			} while (j >= 0 && j < i);
638 			sendbuf = NULL;
639 		} else
640 			sendbuf = (char *)client->line_buffer;
641 	} else if ((strncasecmp((char *)client->line_buffer, "eprt ",
642 	    strlen("eprt ")) == 0)) {
643 
644 		/* Watch out for EPRT commands */
645 		char *line = NULL,  *q, *p, *result[3], delim;
646 		struct addrinfo hints, *res = NULL;
647 		unsigned long proto;
648 
649 		j = 0;
650 		line = strdup((char *)client->line_buffer+strlen("eprt "));
651 		if (line == NULL) {
652 			syslog(LOG_ERR, "insufficient memory");
653 			exit(EX_UNAVAILABLE);
654 		}
655 		p = line;
656 		delim = p[0];
657 		p++;
658 
659 		memset(result,0, sizeof(result));
660 		for (i = 0; i < 3; i++) {
661 			q = strchr(p, delim);
662 			if (!q || *q != delim)
663 				goto parsefail;
664 			*q++ = '\0';
665 			result[i] = p;
666 			p = q;
667 		}
668 
669 		proto = strtoul(result[0], &p, 10);
670 		if (!*result[0] || *p)
671 			goto protounsupp;
672 
673 		memset(&hints, 0, sizeof(hints));
674 		if (proto != 1) /* 1 == AF_INET - all we support for now */
675 			goto protounsupp;
676 		hints.ai_family = AF_INET;
677 		hints.ai_socktype = SOCK_STREAM;
678 		hints.ai_flags = AI_NUMERICHOST;	/*no DNS*/
679 		if (getaddrinfo(result[1], result[2], &hints, &res))
680 			goto parsefail;
681 		if (res->ai_next)
682 			goto parsefail;
683 		if (sizeof(client_listen_sa) < res->ai_addrlen)
684 			goto parsefail;
685 		memcpy(&client_listen_sa, res->ai_addr, res->ai_addrlen);
686 
687 		debuglog(1, "client wants us to use %s:%u",
688 		    inet_ntoa(client_listen_sa.sin_addr),
689 		    htons(client_listen_sa.sin_port));
690 
691 		/*
692 		 * Configure our own listen socket and tell the server about it
693 		 */
694 		new_dataconn(1);
695 		connection_mode = EPRT_MODE;
696 
697 		debuglog(1, "we want server to use %s:%u",
698 		    inet_ntoa(server->sa.sin_addr),
699 		    ntohs(server_listen_sa.sin_port));
700 
701 		snprintf(tbuf, sizeof(tbuf), "EPRT |%d|%s|%u|\r\n", 1,
702 		    inet_ntoa(server->sa.sin_addr),
703 		    ntohs(server_listen_sa.sin_port));
704 		debuglog(1, "to server (modified): %s", tbuf);
705 		sendbuf = tbuf;
706 		goto out;
707 parsefail:
708 		snprintf(tbuf, sizeof(tbuf),
709 		    "500 Invalid argument; rejected\r\n");
710 		sendbuf = NULL;
711 		goto out;
712 protounsupp:
713 		/* we only support AF_INET for now */
714 		if (proto == 2)
715 			snprintf(tbuf, sizeof(tbuf),
716 			    "522 Protocol not supported, use (1)\r\n");
717 		else
718 			snprintf(tbuf, sizeof(tbuf),
719 			    "501 Protocol not supported\r\n");
720 		sendbuf = NULL;
721 out:
722 		if (line)
723 			free(line);
724 		if (res)
725 			freeaddrinfo(res);
726 		if (sendbuf == NULL) {
727 			debuglog(1, "to client (modified): %s", tbuf);
728 			i = strlen(tbuf);
729 			do {
730 				rv = send(client->fd, tbuf + j, i - j, 0);
731 				if (rv == -1 && errno != EAGAIN &&
732 				    errno != EINTR)
733 					break;
734 				else if (rv != -1)
735 					j += rv;
736 			} while (j >= 0 && j < i);
737 		}
738 	} else if (!NatMode && (strncasecmp((char *)client->line_buffer,
739 	    "epsv", strlen("epsv")) == 0)) {
740 
741 		/*
742 		 * If we aren't in NAT mode, deal with EPSV.
743 		 * EPSV is a problem - Unlike PASV, the reply from the
744 		 * server contains *only* a port, we can't modify the reply
745 		 * to the client and get the client to connect to us without
746 		 * resorting to using a dynamic rdr rule we have to add in
747 		 * for the reply to this connection, and take away afterwards.
748 		 * so this will wait until we have the right solution for rule
749 		 * additions/deletions in pf.
750 		 *
751 		 * in the meantime we just tell the client we don't do it,
752 		 * and most clients should fall back to using PASV.
753 		 */
754 
755 		snprintf(tbuf, sizeof(tbuf),
756 		    "500 EPSV command not understood\r\n");
757 		debuglog(1, "to client (modified): %s", tbuf);
758 		j = 0;
759 		i = strlen(tbuf);
760 		do {
761 			rv = send(client->fd, tbuf + j, i - j, 0);
762 			if (rv == -1 && errno != EAGAIN && errno != EINTR)
763 				break;
764 			else if (rv != -1)
765 				j += rv;
766 		} while (j >= 0 && j < i);
767 		sendbuf = NULL;
768 	} else if (strncasecmp((char *)client->line_buffer, "port ",
769 	    strlen("port ")) == 0) {
770 		unsigned int values[6];
771 		char *tailptr;
772 
773 		debuglog(1, "Got a PORT command");
774 
775 		tailptr = (char *)&client->line_buffer[strlen("port ")];
776 		values[0] = 0;
777 
778 		i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0],
779 		    &values[1], &values[2], &values[3], &values[4],
780 		    &values[5]);
781 		if (i != 6) {
782 			syslog(LOG_INFO, "malformed PORT command (%s)",
783 			    client->line_buffer);
784 			exit(EX_DATAERR);
785 		}
786 
787 		for (i = 0; i<6; i++) {
788 			if (values[i] > 255) {
789 				syslog(LOG_INFO,
790 				    "malformed PORT command (%s)",
791 				    client->line_buffer);
792 				exit(EX_DATAERR);
793 			}
794 		}
795 
796 		client_listen_sa.sin_family = AF_INET;
797 		client_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) |
798 		    (values[1] << 16) | (values[2] <<  8) |
799 		    (values[3] <<  0));
800 
801 		client_listen_sa.sin_port = htons((values[4] << 8) |
802 		    values[5]);
803 		debuglog(1, "client wants us to use %u.%u.%u.%u:%u",
804 		    values[0], values[1], values[2], values[3],
805 		    (values[4] << 8) | values[5]);
806 
807 		/*
808 		 * Configure our own listen socket and tell the server about it
809 		 */
810 		new_dataconn(1);
811 		connection_mode = PORT_MODE;
812 
813 		debuglog(1, "we want server to use %s:%u",
814 		    inet_ntoa(server->sa.sin_addr),
815 		    ntohs(server_listen_sa.sin_port));
816 
817 		snprintf(tbuf, sizeof(tbuf), "PORT %u,%u,%u,%u,%u,%u\r\n",
818 		    ((u_char *)&server->sa.sin_addr.s_addr)[0],
819 		    ((u_char *)&server->sa.sin_addr.s_addr)[1],
820 		    ((u_char *)&server->sa.sin_addr.s_addr)[2],
821 		    ((u_char *)&server->sa.sin_addr.s_addr)[3],
822 		    ((u_char *)&server_listen_sa.sin_port)[0],
823 		    ((u_char *)&server_listen_sa.sin_port)[1]);
824 
825 		debuglog(1, "to server (modified): %s", tbuf);
826 
827 		sendbuf = tbuf;
828 	} else
829 		sendbuf = (char *)client->line_buffer;
830 
831 	/*
832 	 *send our (possibly modified) control command in sendbuf
833 	 * on it's way to the server
834 	 */
835 	if (sendbuf != NULL) {
836 		j = 0;
837 		i = strlen(sendbuf);
838 		do {
839 			rv = send(server->fd, sendbuf + j, i - j, 0);
840 			if (rv == -1 && errno != EAGAIN && errno != EINTR)
841 				break;
842 			else if (rv != -1)
843 				j += rv;
844 		} while (j >= 0 && j < i);
845 	}
846 }
847 
848 void
849 do_server_reply(struct csiob *server, struct csiob *client)
850 {
851 	int code, i, j, rv;
852 	struct in_addr *iap;
853 	static int continuing = 0;
854 	char tbuf[100], *sendbuf, *p;
855 
856 	log_control_command((char *)server->line_buffer, 0);
857 
858 	if (strlen((char *)server->line_buffer) > 512) {
859 		/*
860 		 * someone's playing games. Have a cow in the syslogs and
861 		 * exit - we don't pass this on for fear of hurting
862 		 * our other end, which might be poorly implemented.
863 		 */
864 		syslog(LOG_NOTICE, "long FTP control reply");
865 		exit(EX_DATAERR);
866 	}
867 
868 	/*
869 	 * Watch out for "227 Entering Passive Mode ..." replies
870 	 */
871 	code = strtol((char *)server->line_buffer, &p, 10);
872 	if (isspace(server->line_buffer[0]))
873 		code = 0;
874 	if (!*(server->line_buffer) || (*p != ' ' && *p != '-')) {
875 		if (continuing)
876 			goto sendit;
877 		syslog(LOG_INFO, "malformed control reply");
878 		exit(EX_DATAERR);
879 	}
880 	if (code <= 0 || code > 999) {
881 		if (continuing)
882 			goto sendit;
883 		syslog(LOG_INFO, "invalid server reply code %d", code);
884 		exit(EX_DATAERR);
885 	}
886 	if (*p == '-')
887 		continuing = 1;
888 	else
889 		continuing = 0;
890 	if (code == 227 && !NatMode) {
891 		unsigned int values[6];
892 		char *tailptr;
893 
894 		debuglog(1, "Got a PASV reply");
895 		debuglog(1, "{%s}", (char *)server->line_buffer);
896 
897 		tailptr = (char *)strchr((char *)server->line_buffer, '(');
898 		if (tailptr == NULL) {
899 			tailptr = strrchr((char *)server->line_buffer, ' ');
900 			if (tailptr == NULL) {
901 				syslog(LOG_NOTICE, "malformed 227 reply");
902 				exit(EX_DATAERR);
903 			}
904 		}
905 		tailptr++; /* skip past space or ( */
906 
907 		values[0] = 0;
908 
909 		i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0],
910 		    &values[1], &values[2], &values[3], &values[4],
911 		    &values[5]);
912 		if (i != 6) {
913 			syslog(LOG_INFO, "malformed PASV reply (%s)",
914 			    client->line_buffer);
915 			exit(EX_DATAERR);
916 		}
917 		for (i = 0; i<6; i++)
918 			if (values[i] > 255) {
919 				syslog(LOG_INFO, "malformed PASV reply(%s)",
920 				    client->line_buffer);
921 				exit(EX_DATAERR);
922 			}
923 
924 		server_listen_sa.sin_family = AF_INET;
925 		server_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) |
926 		    (values[1] << 16) | (values[2] <<  8) | (values[3] <<  0));
927 		server_listen_sa.sin_port = htons((values[4] << 8) |
928 		    values[5]);
929 
930 		debuglog(1, "server wants us to use %s:%u",
931 		    inet_ntoa(server_listen_sa.sin_addr), (values[4] << 8) |
932 		    values[5]);
933 
934 		new_dataconn(0);
935 		connection_mode = PASV_MODE;
936 		if (ReverseMode)
937 			iap = &(proxy_sa.sin_addr);
938 		else
939 			iap = &(server->sa.sin_addr);
940 
941 		debuglog(1, "we want client to use %s:%u", inet_ntoa(*iap),
942 		    htons(client_listen_sa.sin_port));
943 
944 		snprintf(tbuf, sizeof(tbuf),
945 		    "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n",
946 		    ((u_char *)iap)[0], ((u_char *)iap)[1],
947 		    ((u_char *)iap)[2], ((u_char *)iap)[3],
948 		    ((u_char *)&client_listen_sa.sin_port)[0],
949 		    ((u_char *)&client_listen_sa.sin_port)[1]);
950 		debuglog(1, "to client (modified): %s", tbuf);
951 		sendbuf = tbuf;
952 	} else {
953  sendit:
954 		sendbuf = (char *)server->line_buffer;
955 	}
956 
957 	/*
958 	 * send our (possibly modified) control command in sendbuf
959 	 * on it's way to the client
960 	 */
961 	j = 0;
962 	i = strlen(sendbuf);
963 	do {
964 		rv = send(client->fd, sendbuf + j, i - j, 0);
965 		if (rv == -1 && errno != EAGAIN && errno != EINTR)
966 			break;
967 		else if (rv != -1)
968 			j += rv;
969 	} while (j >= 0 && j < i);
970 
971 }
972 
973 int
974 main(int argc, char *argv[])
975 {
976 	struct csiob client_iob, server_iob;
977 	struct sigaction new_sa, old_sa;
978 	int sval, ch, flags, i;
979 	socklen_t salen;
980 	int one = 1;
981 	long timeout_seconds = 0;
982 	struct timeval tv;
983 #ifdef LIBWRAP
984 	int use_tcpwrapper = 0;
985 #endif /* LIBWRAP */
986 
987 	while ((ch = getopt(argc, argv, "a:D:g:m:M:R:S:t:u:AnVwr")) != -1) {
988 		char *p;
989 		switch (ch) {
990 		case 'a':
991 			if (!*optarg)
992 				usage();
993 			if ((Bind_Addr = inet_addr(optarg)) == INADDR_NONE) {
994 				syslog(LOG_NOTICE,
995 					"%s: invalid address", optarg);
996 				usage();
997 			}
998 			break;
999 		case 'A':
1000 			AnonFtpOnly = 1; /* restrict to anon usernames only */
1001 			break;
1002 		case 'D':
1003 			Debug_Level = strtol(optarg, &p, 10);
1004 			if (!*optarg || *p)
1005 				usage();
1006 			break;
1007 		case 'g':
1008 			Group = optarg;
1009 			break;
1010 		case 'm':
1011 			min_port = strtol(optarg, &p, 10);
1012 			if (!*optarg || *p)
1013 				usage();
1014 			if (min_port < 0 || min_port > USHRT_MAX)
1015 				usage();
1016 			break;
1017 		case 'M':
1018 			max_port = strtol(optarg, &p, 10);
1019 			if (!*optarg || *p)
1020 				usage();
1021 			if (max_port < 0 || max_port > USHRT_MAX)
1022 				usage();
1023 			break;
1024 		case 'n':
1025 			NatMode = 1; /* pass all passives, we're using NAT */
1026 			break;
1027 		case 'r':
1028 			Use_Rdns = 1; /* look up hostnames */
1029 			break;
1030 		case 'R': {
1031 			char *s, *t;
1032 
1033 			if (!*optarg)
1034 				usage();
1035 			if ((s = strdup(optarg)) == NULL) {
1036 				syslog (LOG_NOTICE,
1037 				    "Insufficient memory (malloc failed)");
1038 				exit(EX_UNAVAILABLE);
1039 			}
1040 			memset(&real_server_sa, 0, sizeof(real_server_sa));
1041 			real_server_sa.sin_len = sizeof(struct sockaddr_in);
1042 			real_server_sa.sin_family = AF_INET;
1043 			t = strchr(s, ':');
1044 			if (t == NULL)
1045 				real_server_sa.sin_port = htons(21);
1046 			else {
1047 				long port = strtol(t + 1, &p, 10);
1048 
1049 				if (*p || port <= 0 || port > 65535)
1050 					usage();
1051 				real_server_sa.sin_port = htons(port);
1052 				*t = 0;
1053 			}
1054 			real_server_sa.sin_addr.s_addr = inet_addr(s);
1055 			if (real_server_sa.sin_addr.s_addr == INADDR_NONE)
1056 				usage();
1057 			free(s);
1058 			ReverseMode = 1;
1059 			break;
1060 		}
1061 		case 'S':
1062 			if (!inet_aton(optarg, &src_addr))
1063 				usage();
1064 			break;
1065 		case 't':
1066 			timeout_seconds = strtol(optarg, &p, 10);
1067 			if (!*optarg || *p)
1068 				usage();
1069 			break;
1070 		case 'u':
1071 			User = optarg;
1072 			break;
1073 		case 'V':
1074 			Verbose = 1;
1075 			break;
1076 #ifdef LIBWRAP
1077 		case 'w':
1078 			use_tcpwrapper = 1; /* do the libwrap thing */
1079 			break;
1080 #endif /* LIBWRAP */
1081 		default:
1082 			usage();
1083 			/* NOTREACHED */
1084 		}
1085 	}
1086 	argc -= optind;
1087 	argv += optind;
1088 
1089 	if (max_port < min_port)
1090 		usage();
1091 
1092 	openlog(__progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
1093 
1094 	setlinebuf(stdout);
1095 	setlinebuf(stderr);
1096 
1097 	memset(&client_iob, 0, sizeof(client_iob));
1098 	memset(&server_iob, 0, sizeof(server_iob));
1099 
1100 	if (get_proxy_env(0, &real_server_sa, &client_iob.sa,
1101 	    &proxy_sa) == -1)
1102 		exit(EX_PROTOCOL);
1103 
1104 	/*
1105 	 * We may now drop root privs, as we have done our ioctl for
1106 	 * pf. If we do drop root, we can't make backchannel connections
1107 	 * for PORT and EPRT come from port 20, which is not strictly
1108 	 * RFC compliant. This shouldn't cause problems for all but
1109 	 * the stupidest ftp clients and the stupidest packet filters.
1110 	 */
1111 	drop_privs();
1112 
1113 	/*
1114 	 * We check_host after get_proxy_env so that checks are done
1115 	 * against the original destination endpoint, not the endpoint
1116 	 * of our side of the rdr. This allows the use of tcpwrapper
1117 	 * rules to restrict destinations as well as sources of connections
1118 	 * for ftp.
1119 	 */
1120 	if (Use_Rdns)
1121 		flags = 0;
1122 	else
1123 		flags = NI_NUMERICHOST | NI_NUMERICSERV;
1124 
1125 	i = getnameinfo((struct sockaddr *)&client_iob.sa,
1126 	    sizeof(client_iob.sa), ClientName, sizeof(ClientName), NULL, 0,
1127 	    flags);
1128 
1129 	if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) {
1130 		debuglog(2, "name resolution failure (client)");
1131 		exit(EX_OSERR);
1132 	}
1133 
1134 	i = getnameinfo((struct sockaddr *)&real_server_sa,
1135 	    sizeof(real_server_sa), RealServerName, sizeof(RealServerName),
1136 	    NULL, 0, flags);
1137 
1138 	if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) {
1139 		debuglog(2, "name resolution failure (server)");
1140 		exit(EX_OSERR);
1141 	}
1142 
1143 #ifdef LIBWRAP
1144 	if (use_tcpwrapper && !check_host(&client_iob.sa, &real_server_sa))
1145 		exit(EX_NOPERM);
1146 #endif
1147 
1148 	client_iob.fd = 0;
1149 
1150 	syslog(LOG_INFO, "accepted connection from %s:%u to %s:%u", ClientName,
1151 		ntohs(client_iob.sa.sin_port), RealServerName,
1152 		ntohs(real_server_sa.sin_port));
1153 
1154 	server_iob.fd = get_backchannel_socket(SOCK_STREAM, min_port, max_port,
1155 	    -1,	1, &server_iob.sa);
1156 
1157 	if (connect(server_iob.fd, (struct sockaddr *)&real_server_sa,
1158 	    sizeof(real_server_sa)) != 0) {
1159 		syslog(LOG_INFO, "cannot connect to %s:%u (%m)", RealServerName,
1160 		    ntohs(real_server_sa.sin_port));
1161 		exit(EX_NOHOST);
1162 	}
1163 
1164 	/*
1165 	 * Now that we are connected to the real server, get the name
1166 	 * of our end of the server socket so we know our IP address
1167 	 * from the real server's perspective.
1168 	 */
1169 	salen = sizeof(server_iob.sa);
1170 	getsockname(server_iob.fd, (struct sockaddr *)&server_iob.sa, &salen);
1171 
1172 	i = getnameinfo((struct sockaddr *)&server_iob.sa,
1173 	    sizeof(server_iob.sa), OurName, sizeof(OurName), NULL, 0, flags);
1174 
1175 	if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) {
1176 		debuglog(2, "name resolution failure (local)");
1177 		exit(EX_OSERR);
1178 	}
1179 
1180 	debuglog(1, "local socket is %s:%u", OurName,
1181 	    ntohs(server_iob.sa.sin_port));
1182 
1183 	/* ignore SIGPIPE */
1184 	bzero(&new_sa, sizeof(new_sa));
1185 	new_sa.sa_handler = SIG_IGN;
1186 	(void)sigemptyset(&new_sa.sa_mask);
1187 	new_sa.sa_flags = SA_RESTART;
1188 	if (sigaction(SIGPIPE, &new_sa, &old_sa) != 0) {
1189 		syslog(LOG_ERR, "sigaction() failed (%m)");
1190 		exit(EX_OSERR);
1191 	}
1192 
1193 	if (setsockopt(client_iob.fd, SOL_SOCKET, SO_OOBINLINE, (char *)&one,
1194 	    sizeof(one)) == -1) {
1195 		syslog(LOG_NOTICE, "cannot set SO_OOBINLINE (%m)");
1196 		exit(EX_OSERR);
1197 	}
1198 
1199 	client_iob.line_buffer_size = STARTBUFSIZE;
1200 	client_iob.line_buffer = malloc(client_iob.line_buffer_size);
1201 	client_iob.io_buffer_size = STARTBUFSIZE;
1202 	client_iob.io_buffer = malloc(client_iob.io_buffer_size);
1203 	client_iob.next_byte = 0;
1204 	client_iob.io_buffer_len = 0;
1205 	client_iob.alive = 1;
1206 	client_iob.who = "client";
1207 	client_iob.send_oob_flags = 0;
1208 	client_iob.real_sa = client_iob.sa;
1209 
1210 	server_iob.line_buffer_size = STARTBUFSIZE;
1211 	server_iob.line_buffer = malloc(server_iob.line_buffer_size);
1212 	server_iob.io_buffer_size = STARTBUFSIZE;
1213 	server_iob.io_buffer = malloc(server_iob.io_buffer_size);
1214 	server_iob.next_byte = 0;
1215 	server_iob.io_buffer_len = 0;
1216 	server_iob.alive = 1;
1217 	server_iob.who = "server";
1218 	server_iob.send_oob_flags = MSG_OOB;
1219 	server_iob.real_sa = real_server_sa;
1220 
1221 	if (client_iob.line_buffer == NULL || client_iob.io_buffer == NULL ||
1222 	    server_iob.line_buffer == NULL || server_iob.io_buffer == NULL) {
1223 		syslog (LOG_NOTICE, "insufficient memory");
1224 		exit(EX_UNAVAILABLE);
1225 	}
1226 
1227 	while (client_iob.alive || server_iob.alive) {
1228 		int maxfd = 0;
1229 		fd_set *fdsp;
1230 
1231 		if (client_iob.fd > maxfd)
1232 			maxfd = client_iob.fd;
1233 		if (client_listen_socket > maxfd)
1234 			maxfd = client_listen_socket;
1235 		if (client_data_socket > maxfd)
1236 			maxfd = client_data_socket;
1237 		if (server_iob.fd > maxfd)
1238 			maxfd = server_iob.fd;
1239 		if (server_listen_socket > maxfd)
1240 			maxfd = server_listen_socket;
1241 		if (server_data_socket > maxfd)
1242 			maxfd = server_data_socket;
1243 
1244 		debuglog(3, "client is %s; server is %s",
1245 		    client_iob.alive ? "alive" : "dead",
1246 		    server_iob.alive ? "alive" : "dead");
1247 
1248 		fdsp = (fd_set *)calloc(howmany(maxfd + 1, NFDBITS),
1249 		    sizeof(fd_mask));
1250 		if (fdsp == NULL) {
1251 			syslog(LOG_NOTICE, "insufficient memory");
1252 			exit(EX_UNAVAILABLE);
1253 		}
1254 
1255 		if (client_iob.alive && telnet_getline(&client_iob,
1256 		    &server_iob)) {
1257 			debuglog(3, "client line buffer is \"%s\"",
1258 			    (char *)client_iob.line_buffer);
1259 			if (client_iob.line_buffer[0] != '\0')
1260 				do_client_cmd(&client_iob, &server_iob);
1261 		} else if (server_iob.alive && telnet_getline(&server_iob,
1262 		    &client_iob)) {
1263 			debuglog(3, "server line buffer is \"%s\"",
1264 			    (char *)server_iob.line_buffer);
1265 			if (server_iob.line_buffer[0] != '\0')
1266 				do_server_reply(&server_iob, &client_iob);
1267 		} else {
1268 			if (client_iob.alive) {
1269 				FD_SET(client_iob.fd, fdsp);
1270 				if (client_listen_socket >= 0)
1271 					FD_SET(client_listen_socket, fdsp);
1272 				if (client_data_socket >= 0)
1273 					FD_SET(client_data_socket, fdsp);
1274 			}
1275 			if (server_iob.alive) {
1276 				FD_SET(server_iob.fd, fdsp);
1277 				if (server_listen_socket >= 0)
1278 					FD_SET(server_listen_socket, fdsp);
1279 				if (server_data_socket >= 0)
1280 					FD_SET(server_data_socket, fdsp);
1281 			}
1282 			tv.tv_sec = timeout_seconds;
1283 			tv.tv_usec = 0;
1284 
1285 		doselect:
1286 			sval = select(maxfd + 1, fdsp, NULL, NULL,
1287 			    (tv.tv_sec == 0) ? NULL : &tv);
1288 			if (sval == 0) {
1289 				/*
1290 				 * This proxy has timed out. Expire it
1291 				 * quietly with an obituary in the syslogs
1292 				 * for any passing mourners.
1293 				 */
1294 				syslog(LOG_INFO,
1295 				    "timeout: no data for %ld seconds",
1296 				    timeout_seconds);
1297 				exit(EX_OK);
1298 			}
1299 			if (sval == -1) {
1300 				if (errno == EINTR || errno == EAGAIN)
1301 					goto doselect;
1302 				syslog(LOG_NOTICE,
1303 				    "select() failed (%m)");
1304 				exit(EX_OSERR);
1305 			}
1306 			if (client_data_socket >= 0 &&
1307 			    FD_ISSET(client_data_socket, fdsp)) {
1308 				int rval;
1309 
1310 				debuglog(3, "transfer: client to server");
1311 				rval = xfer_data("client to server",
1312 				    client_data_socket,
1313 				    server_data_socket,
1314 				    client_iob.sa.sin_addr,
1315 				    real_server_sa.sin_addr);
1316 				if (rval <= 0) {
1317 					close_client_data();
1318 					close_server_data();
1319 					show_xfer_stats();
1320 				} else
1321 					client_data_bytes += rval;
1322 			}
1323 			if (server_data_socket >= 0 &&
1324 			    FD_ISSET(server_data_socket, fdsp)) {
1325 				int rval;
1326 
1327 				debuglog(3, "transfer: server to client");
1328 				rval = xfer_data("server to client",
1329 				    server_data_socket,
1330 				    client_data_socket,
1331 				    real_server_sa.sin_addr,
1332 				    client_iob.sa.sin_addr);
1333 				if (rval <= 0) {
1334 					close_client_data();
1335 					close_server_data();
1336 					show_xfer_stats();
1337 				} else
1338 					server_data_bytes += rval;
1339 			}
1340 			if (server_listen_socket >= 0 &&
1341 			    FD_ISSET(server_listen_socket, fdsp)) {
1342 				connect_port_backchannel();
1343 			}
1344 			if (client_listen_socket >= 0 &&
1345 			    FD_ISSET(client_listen_socket, fdsp)) {
1346 				connect_pasv_backchannel();
1347 			}
1348 			if (client_iob.alive &&
1349 			    FD_ISSET(client_iob.fd, fdsp)) {
1350 				client_iob.data_available = 1;
1351 			}
1352 			if (server_iob.alive &&
1353 			    FD_ISSET(server_iob.fd, fdsp)) {
1354 				server_iob.data_available = 1;
1355 			}
1356 		}
1357 		free(fdsp);
1358 		if (client_iob.got_eof) {
1359 			shutdown(server_iob.fd, 1);
1360 			shutdown(client_iob.fd, 0);
1361 			client_iob.got_eof = 0;
1362 			client_iob.alive = 0;
1363 		}
1364 		if (server_iob.got_eof) {
1365 			shutdown(client_iob.fd, 1);
1366 			shutdown(server_iob.fd, 0);
1367 			server_iob.got_eof = 0;
1368 			server_iob.alive = 0;
1369 		}
1370 	}
1371 
1372 	if (Verbose)
1373 		syslog(LOG_INFO, "session ended");
1374 
1375 	exit(EX_OK);
1376 }
1377