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