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