ftp-proxy.c revision 145840
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: head/contrib/pf/ftp-proxy/ftp-proxy.c 145840 2005-05-03 16:55:20Z mlaier $");
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>
100int allow_severity = LOG_INFO;
101int deny_severity = LOG_NOTICE;
102#endif /* LIBWRAP */
103
104int min_port = IPPORT_HIFIRSTAUTO;
105int 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
129double xfer_start_time;
130
131struct sockaddr_in real_server_sa;
132struct sockaddr_in client_listen_sa;
133struct sockaddr_in server_listen_sa;
134struct sockaddr_in proxy_sa;
135struct in_addr src_addr;
136
137int client_listen_socket = -1;	/* Only used in PASV mode */
138int client_data_socket = -1;	/* Connected socket to real client */
139int server_listen_socket = -1;	/* Only used in PORT mode */
140int server_data_socket = -1;	/* Connected socket to real server */
141int client_data_bytes, server_data_bytes;
142
143int AnonFtpOnly;
144int Verbose;
145int NatMode;
146int ReverseMode;
147
148char ClientName[NI_MAXHOST];
149char RealServerName[NI_MAXHOST];
150char OurName[NI_MAXHOST];
151
152const char *User = "proxy";
153const char *Group;
154
155extern int Debug_Level;
156extern int Use_Rdns;
157extern in_addr_t Bind_Addr;
158extern char *__progname;
159
160typedef enum {
161	UNKNOWN_MODE,
162	PORT_MODE,
163	PASV_MODE,
164	EPRT_MODE,
165	EPSV_MODE
166} connection_mode_t;
167
168connection_mode_t connection_mode;
169
170extern void	debuglog(int debug_level, const char *fmt, ...);
171double		wallclock_time(void);
172void		show_xfer_stats(void);
173void		log_control_command (char *cmd, int client);
174int		new_dataconn(int server);
175void		do_client_cmd(struct csiob *client, struct csiob *server);
176void		do_server_reply(struct csiob *server, struct csiob *client);
177static void
178usage(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
187static void
188close_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
197static void
198close_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
207static void
208drop_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 */
251static int
252check_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
305double
306wallclock_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 */
317void
318show_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
391void
392log_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 */
422int
423new_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
473static void
474connect_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
516static void
517connect_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
593void
594do_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;
707parsefail:
708		snprintf(tbuf, sizeof(tbuf),
709		    "500 Invalid argument; rejected\r\n");
710		sendbuf = NULL;
711		goto out;
712protounsupp:
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;
721out:
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
848void
849do_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
973int
974main(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