ftp-proxy - Port from libevent to kqueue(2)/kevent(2)
[dragonfly.git] / usr.sbin / ftp-proxy / ftp-proxy.c
1 /*      $OpenBSD: ftp-proxy.c,v 1.13 2006/12/30 13:24:00 camield Exp $ */
2
3 /*
4  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/queue.h>
20 #include <sys/types.h>
21 #include <sys/event.h>
22 #include <sys/time.h>
23 #include <sys/resource.h>
24 #include <sys/socket.h>
25
26 #include <net/if.h>
27 #include <net/pf/pfvar.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30
31 #include <err.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <netdb.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43 #include <vis.h>
44
45 #include "filter.h"
46
47 #define CONNECT_TIMEOUT 30
48 #define MIN_PORT        1024
49 #define MAX_LINE        500
50 #define MAX_LOGLINE     300
51 #define NTOP_BUFS       3
52 #define TCP_BACKLOG     10
53
54 #define CHROOT_DIR      "/var/empty"
55 #define NOPRIV_USER     "proxy"
56
57 /* pfctl standard NAT range. */
58 #define PF_NAT_PROXY_PORT_LOW   50001
59 #define PF_NAT_PROXY_PORT_HIGH  65535
60
61 #define sstosa(ss)      ((struct sockaddr *)(ss))
62
63 enum { CMD_NONE = 0, CMD_PORT, CMD_EPRT, CMD_PASV, CMD_EPSV };
64
65 struct cbuf {
66         char                    *buffer;
67         size_t                   buffer_size;
68         size_t                   buffer_offset;
69 };
70
71 struct session {
72         u_int32_t                id;
73         struct sockaddr_storage  client_ss;
74         struct sockaddr_storage  proxy_ss;
75         struct sockaddr_storage  server_ss;
76         struct sockaddr_storage  orig_server_ss;
77         struct cbuf              client;
78         struct cbuf              server;
79         int                      client_fd;
80         int                      server_fd;
81         char                     cbuf[MAX_LINE];
82         size_t                   cbuf_valid;
83         char                     sbuf[MAX_LINE];
84         size_t                   sbuf_valid;
85         int                      cmd;
86         u_int16_t                port;
87         u_int16_t                proxy_port;
88         LIST_ENTRY(session)      entry;
89 };
90
91 LIST_HEAD(, session) sessions = LIST_HEAD_INITIALIZER(sessions);
92
93 void    buffer_data(struct session *, struct cbuf *, char *, size_t);
94 int     client_parse(struct session *);
95 int     client_parse_anon(struct session *);
96 int     client_parse_cmd(struct session *);
97 void    client_read(struct session *);
98 void    client_write(struct session *);
99 int     drop_privs(void);
100 void    end_session(struct session *);
101 int     exit_daemon(void);
102 int     getline(char *, size_t *);
103 void    handle_connection(const int);
104 void    handle_signal(int);
105 struct session * init_session(void);
106 void    logmsg(int, const char *, ...);
107 u_int16_t parse_port(int);
108 u_int16_t pick_proxy_port(void);
109 void    proxy_reply(int, struct sockaddr *, u_int16_t);
110 int     server_parse(struct session *);
111 void    server_read(struct session *);
112 void    server_write(struct session *);
113 const char *sock_ntop(struct sockaddr *);
114 void    usage(void);
115
116 char linebuf[MAX_LINE + 1];
117 size_t linelen;
118
119 char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
120
121 #define KQ_NEVENTS      64
122 struct kevent changes[KQ_NEVENTS];
123 int nchanges;
124
125 struct sockaddr_storage fixed_server_ss, fixed_proxy_ss;
126 char *fixed_server, *fixed_server_port, *fixed_proxy, *listen_ip, *listen_port,
127     *qname;
128 int anonymous_only, daemonize, id_count, ipv6_mode, loglevel, max_sessions,
129     rfc_mode, session_count, timeout, verbose;
130 extern char *__progname;
131
132 void
133 buffer_data(struct session *s, struct cbuf *cb, char *buf, size_t len)
134 {
135         if (len < 1)
136                 return;
137
138         if (cb->buffer == NULL)
139                 if ((cb->buffer = malloc(MAX_LINE)) == NULL)
140                         goto error;
141
142         memcpy(cb->buffer, buf, len);
143         cb->buffer_size = len;
144         return;
145
146 error:
147         logmsg(LOG_ERR, "#%d could not allocate memory for buffer", s->id);
148         end_session(s);
149 }
150
151 int
152 client_parse(struct session *s)
153 {
154         /* Reset any previous command. */
155         s->cmd = CMD_NONE;
156         s->port = 0;
157
158         /* Commands we are looking for are at least 4 chars long. */
159         if (linelen < 4)
160                 return (1);
161
162         if (linebuf[0] == 'P' || linebuf[0] == 'p' ||
163             linebuf[0] == 'E' || linebuf[0] == 'e')
164                 return (client_parse_cmd(s));
165         
166         if (anonymous_only && (linebuf[0] == 'U' || linebuf[0] == 'u'))
167                 return (client_parse_anon(s));
168
169         return (1);
170 }
171
172 int
173 client_parse_anon(struct session *s)
174 {
175         size_t written;
176
177         if (strcasecmp("USER ftp\r\n", linebuf) != 0 &&
178             strcasecmp("USER anonymous\r\n", linebuf) != 0) {
179                 snprintf(linebuf, sizeof linebuf,
180                     "500 Only anonymous FTP allowed\r\n");
181                 logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
182
183                 /* Talk back to the client ourself. */
184                 linelen = strlen(linebuf);
185                 written = write(s->client_fd, linebuf, linelen);
186                 if (written == -1) {
187                         logmsg(LOG_ERR, "#%d write failed", s->id);
188                         return (0); /* Session will be ended for us */
189                 } else if (written < linelen) {
190                         EV_SET(&changes[nchanges++], s->server_fd,
191                                EVFILT_READ, EV_DISABLE, 0, 0, s);
192                         EV_SET(&changes[nchanges++], s->client_fd,
193                                EVFILT_WRITE, EV_ADD, 0, 0, s);
194                         buffer_data(s, &s->client, linebuf + written,
195                                     linelen - written);
196                         return (1);
197                 }
198
199                 /* Clear buffer so it's not sent to the server. */
200                 linebuf[0] = '\0';
201                 linelen = 0;
202         }
203
204         return (1);
205 }
206
207 int
208 client_parse_cmd(struct session *s)
209 {
210         if (strncasecmp("PASV", linebuf, 4) == 0)
211                 s->cmd = CMD_PASV;
212         else if (strncasecmp("PORT ", linebuf, 5) == 0)
213                 s->cmd = CMD_PORT;
214         else if (strncasecmp("EPSV", linebuf, 4) == 0)
215                 s->cmd = CMD_EPSV;
216         else if (strncasecmp("EPRT ", linebuf, 5) == 0)
217                 s->cmd = CMD_EPRT;
218         else
219                 return (1);
220
221         if (ipv6_mode && (s->cmd == CMD_PASV || s->cmd == CMD_PORT)) {
222                 logmsg(LOG_CRIT, "PASV and PORT not allowed with IPv6");
223                 return (0);
224         }
225
226         if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) {
227                 s->port = parse_port(s->cmd);
228                 if (s->port < MIN_PORT) {
229                         logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id,
230                             linebuf);
231                         return (0);
232                 }
233                 s->proxy_port = pick_proxy_port();
234                 proxy_reply(s->cmd, sstosa(&s->proxy_ss), s->proxy_port);
235                 logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
236         }
237
238         return (1);
239 }
240
241 void
242 client_read(struct session *s)
243 {
244         size_t           buf_avail, bread, bwritten;
245         int              n;
246
247         do {
248                 buf_avail = sizeof s->cbuf - s->cbuf_valid;
249                 bread = read(s->client_fd, s->cbuf + s->cbuf_valid, buf_avail);
250                 s->cbuf_valid += bread;
251
252                 while ((n = getline(s->cbuf, &s->cbuf_valid)) > 0) {
253                         logmsg(LOG_DEBUG, "#%d client: %s", s->id, linebuf);
254                         if (!client_parse(s)) {
255                                 end_session(s);
256                                 return;
257                         }
258                         bwritten = write(s->server_fd, linebuf, linelen);
259                         if (bwritten == -1) {
260                         } else if (bwritten < linelen) {
261                                 EV_SET(&changes[nchanges++], s->client_fd,
262                                        EVFILT_READ, EV_DISABLE, 0, 0, s);
263                                 EV_SET(&changes[nchanges++], s->server_fd,
264                                        EVFILT_WRITE, EV_ADD, 0, 0, s);
265                                 buffer_data(s, &s->server, linebuf + bwritten,
266                                             linelen - bwritten);
267                                 return;
268                         }
269                 }
270
271                 if (n == -1) {
272                         logmsg(LOG_ERR, "#%d client command too long or not"
273                             " clean", s->id);
274                         end_session(s);
275                         return;
276                 }
277         } while (bread == buf_avail);
278 }
279
280 void
281 client_write(struct session *s)
282 {
283         size_t written;
284
285         written = write(s->client_fd, s->client.buffer + s->client.buffer_offset,
286                         s->client.buffer_size - s->client.buffer_offset);
287         if (written == -1) {
288                 logmsg(LOG_ERR, "#%d write failed", s->id);
289                 end_session(s);
290         } else if (written == (s->client.buffer_size - s->client.buffer_offset)) {
291                 free(s->client.buffer);
292                 s->client.buffer = NULL;
293                 s->client.buffer_size = 0;
294                 s->client.buffer_offset = 0;
295                 EV_SET(&changes[nchanges++], s->server_fd,
296                        EVFILT_READ, EV_ENABLE, 0, 0, s);
297         } else {
298                 s->client.buffer_offset += written;
299         }
300 }
301
302 int
303 drop_privs(void)
304 {
305         struct passwd *pw;
306
307         pw = getpwnam(NOPRIV_USER);
308         if (pw == NULL)
309                 return (0);
310
311         tzset();
312         if (chroot(CHROOT_DIR) != 0 || chdir("/") != 0 ||
313             setgroups(1, &pw->pw_gid) != 0 ||
314             setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0 ||
315             setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
316                 return (0);
317
318         return (1);
319 }
320
321 void
322 end_session(struct session *s)
323 {
324         int err;
325
326         logmsg(LOG_INFO, "#%d ending session", s->id);
327
328         if (s->client_fd != -1)
329                 close(s->client_fd);
330         if (s->server_fd != -1)
331                 close(s->server_fd);
332
333         if (s->client.buffer)
334                 free(s->client.buffer);
335         if (s->server.buffer)
336                 free(s->server.buffer);
337
338         /* Remove rulesets by commiting empty ones. */
339         err = 0;
340         if (prepare_commit(s->id) == -1)
341                 err = errno;
342         else if (do_commit() == -1) {
343                 err = errno;
344                 do_rollback();
345         }
346         if (err)
347                 logmsg(LOG_ERR, "#%d pf rule removal failed: %s", s->id,
348                     strerror(err));
349
350         LIST_REMOVE(s, entry);
351         free(s);
352         session_count--;
353 }
354
355 int
356 exit_daemon(void)
357 {
358         struct session *s, *tmp;
359
360         LIST_FOREACH_MUTABLE(s, &sessions, entry, tmp) {
361                 end_session(s);
362         }
363
364         if (daemonize)
365                 closelog();
366
367         exit(0);
368
369         /* NOTREACHED */
370         return (-1);
371 }
372
373 int
374 getline(char *buf, size_t *valid)
375 {
376         size_t i;
377
378         if (*valid > MAX_LINE)
379                 return (-1);
380
381         /* Copy to linebuf while searching for a newline. */
382         for (i = 0; i < *valid; i++) {
383                 linebuf[i] = buf[i];
384                 if (buf[i] == '\0')
385                         return (-1);
386                 if (buf[i] == '\n')
387                         break;
388         }
389
390         if (i == *valid) {
391                 /* No newline found. */
392                 linebuf[0] = '\0';
393                 linelen = 0;
394                 if (i < MAX_LINE)
395                         return (0);
396                 return (-1);
397         }
398
399         linelen = i + 1;
400         linebuf[linelen] = '\0';
401         *valid -= linelen;
402         
403         /* Move leftovers to the start. */
404         if (*valid != 0)
405                 bcopy(buf + linelen, buf, *valid);
406
407         return ((int)linelen);
408 }
409
410 void
411 handle_connection(const int listen_fd)
412 {
413         struct sockaddr_storage tmp_ss;
414         struct sockaddr *client_sa, *server_sa, *fixed_server_sa;
415         struct sockaddr *client_to_proxy_sa, *proxy_to_server_sa;
416         struct session *s;
417         socklen_t len;
418         int client_fd, fc, on;
419
420         /*
421          * We _must_ accept the connection, otherwise libevent will keep
422          * coming back, and we will chew up all CPU.
423          */
424         client_sa = sstosa(&tmp_ss);
425         len = sizeof(struct sockaddr_storage);
426         if ((client_fd = accept(listen_fd, client_sa, &len)) < 0) {
427                 logmsg(LOG_CRIT, "accept failed: %s", strerror(errno));
428                 return;
429         }
430
431         /* Refuse connection if the maximum is reached. */
432         if (session_count >= max_sessions) {
433                 logmsg(LOG_ERR, "client limit (%d) reached, refusing "
434                     "connection from %s", max_sessions, sock_ntop(client_sa));
435                 close(client_fd);
436                 return;
437         }
438
439         /* Allocate session and copy back the info from the accept(). */
440         s = init_session();
441         if (s == NULL) {
442                 logmsg(LOG_CRIT, "init_session failed");
443                 close(client_fd);
444                 return;
445         }
446         s->client_fd = client_fd;
447         memcpy(sstosa(&s->client_ss), client_sa, client_sa->sa_len);
448
449         /* Cast it once, and be done with it. */
450         client_sa = sstosa(&s->client_ss);
451         server_sa = sstosa(&s->server_ss);
452         client_to_proxy_sa = sstosa(&tmp_ss);
453         proxy_to_server_sa = sstosa(&s->proxy_ss);
454         fixed_server_sa = sstosa(&fixed_server_ss);
455
456         /* Log id/client early to ease debugging. */
457         logmsg(LOG_DEBUG, "#%d accepted connection from %s", s->id,
458             sock_ntop(client_sa));
459
460         /*
461          * Find out the real server and port that the client wanted.
462          */
463         len = sizeof(struct sockaddr_storage);
464         if ((getsockname(s->client_fd, client_to_proxy_sa, &len)) < 0) {
465                 logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id,
466                     strerror(errno));
467                 goto fail;
468         }
469         if (server_lookup(client_sa, client_to_proxy_sa, server_sa) != 0) {
470                 logmsg(LOG_CRIT, "#%d server lookup failed (no rdr?)", s->id);
471                 goto fail;
472         }
473         if (fixed_server) {
474                 memcpy(sstosa(&s->orig_server_ss), server_sa,
475                     server_sa->sa_len);
476                 memcpy(server_sa, fixed_server_sa, fixed_server_sa->sa_len);
477         }
478
479         /* XXX: check we are not connecting to ourself. */
480
481         /*
482          * Setup socket and connect to server.
483          */
484         if ((s->server_fd = socket(server_sa->sa_family, SOCK_STREAM,
485             IPPROTO_TCP)) < 0) {
486                 logmsg(LOG_CRIT, "#%d server socket failed: %s", s->id,
487                     strerror(errno));
488                 goto fail;
489         }
490         if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss),
491             fixed_proxy_ss.ss_len) != 0) {
492                 logmsg(LOG_CRIT, "#%d cannot bind fixed proxy address: %s",
493                     s->id, strerror(errno));
494                 goto fail;
495         }
496
497         /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */
498         if ((fc = fcntl(s->server_fd, F_GETFL)) == -1 ||
499             fcntl(s->server_fd, F_SETFL, fc | O_NONBLOCK) == -1) {
500                 logmsg(LOG_CRIT, "#%d cannot mark socket non-blocking: %s",
501                     s->id, strerror(errno));
502                 goto fail;
503         }
504         if (connect(s->server_fd, server_sa, server_sa->sa_len) < 0 &&
505             errno != EINPROGRESS) {
506                 logmsg(LOG_CRIT, "#%d proxy cannot connect to server %s: %s",
507                     s->id, sock_ntop(server_sa), strerror(errno));
508                 goto fail;
509         }
510
511         len = sizeof(struct sockaddr_storage);
512         if ((getsockname(s->server_fd, proxy_to_server_sa, &len)) < 0) {
513                 logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id,
514                     strerror(errno));
515                 goto fail;
516         }
517
518         logmsg(LOG_INFO, "#%d FTP session %d/%d started: client %s to server "
519             "%s via proxy %s ", s->id, session_count, max_sessions,
520             sock_ntop(client_sa), sock_ntop(server_sa),
521             sock_ntop(proxy_to_server_sa));
522
523         /* Keepalive is nice, but don't care if it fails. */
524         on = 1;
525         setsockopt(s->client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
526             sizeof on);
527         setsockopt(s->server_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
528             sizeof on);
529
530         EV_SET(&changes[nchanges++], s->client_fd, EVFILT_READ, EV_ADD, 0, 0, s);
531         EV_SET(&changes[nchanges++], s->server_fd, EVFILT_READ, EV_ADD, 0, 0, s);
532
533         return;
534
535  fail:
536         end_session(s);
537 }
538
539 void
540 handle_signal(int sig)
541 {
542         /*
543          * Signal handler rules don't apply.
544          */
545
546         logmsg(LOG_ERR, "%s exiting on signal %d", __progname, sig);
547
548         exit_daemon();
549 }
550         
551
552 struct session *
553 init_session(void)
554 {
555         struct session *s;
556
557         s = calloc(1, sizeof(struct session));
558         if (s == NULL)
559                 return (NULL);
560
561         s->id = id_count++;
562         s->client_fd = -1;
563         s->server_fd = -1;
564         s->cbuf[0] = '\0';
565         s->cbuf_valid = 0;
566         s->sbuf[0] = '\0';
567         s->sbuf_valid = 0;
568         s->client.buffer = NULL;
569         s->client.buffer_size = 0;
570         s->client.buffer_offset = 0;
571         s->server.buffer = NULL;
572         s->server.buffer_size = 0;
573         s->server.buffer_offset = 0;
574         s->cmd = CMD_NONE;
575         s->port = 0;
576
577         LIST_INSERT_HEAD(&sessions, s, entry);
578         session_count++;
579
580         return (s);
581 }
582
583 void
584 logmsg(int pri, const char *message, ...)
585 {
586         va_list ap;
587
588         if (pri > loglevel)
589                 return;
590
591         va_start(ap, message);
592
593         if (daemonize)
594                 /* syslog does its own vissing. */
595                 vsyslog(pri, message, ap);
596         else {
597                 char buf[MAX_LOGLINE];
598                 char visbuf[2 * MAX_LOGLINE];
599
600                 /* We don't care about truncation. */
601                 vsnprintf(buf, sizeof buf, message, ap);
602                 strnvis(visbuf, buf, sizeof visbuf, VIS_CSTYLE | VIS_NL);
603                 fprintf(stderr, "%s\n", visbuf);
604         }
605
606         va_end(ap);
607 }
608
609 int
610 main(int argc, char *argv[])
611 {
612         struct rlimit rlp;
613         struct addrinfo hints, *res;
614         int kq, ch, error, listenfd, on;
615         const char *errstr;
616
617         /* Defaults. */
618         anonymous_only  = 0;
619         daemonize       = 1;
620         fixed_proxy     = NULL;
621         fixed_server    = NULL;
622         fixed_server_port = "21";
623         ipv6_mode       = 0;
624         listen_ip       = NULL;
625         listen_port     = "8021";
626         loglevel        = LOG_NOTICE;
627         max_sessions    = 100;
628         qname           = NULL;
629         rfc_mode        = 0;
630         timeout         = 24 * 3600;
631         verbose         = 0;
632
633         /* Other initialization. */
634         id_count        = 1;
635         session_count   = 0;
636         nchanges        = 0;
637
638         while ((ch = getopt(argc, argv, "6Aa:b:D:dm:P:p:q:R:rt:v")) != -1) {
639                 switch (ch) {
640                 case '6':
641                         ipv6_mode = 1;
642                         break;
643                 case 'A':
644                         anonymous_only = 1;
645                         break;
646                 case 'a':
647                         fixed_proxy = optarg;
648                         break;
649                 case 'b':
650                         listen_ip = optarg;
651                         break;
652                 case 'D':
653                         loglevel = strtonum(optarg, LOG_EMERG, LOG_DEBUG,
654                             &errstr);
655                         if (errstr)
656                                 errx(1, "loglevel %s", errstr);
657                         break;
658                 case 'd':
659                         daemonize = 0;
660                         break;
661                 case 'm':
662                         max_sessions = strtonum(optarg, 1, 500, &errstr);
663                         if (errstr)
664                                 errx(1, "max sessions %s", errstr);
665                         break;
666                 case 'P':
667                         fixed_server_port = optarg;
668                         break;
669                 case 'p':
670                         listen_port = optarg;
671                         break;
672                 case 'q':
673                         if (strlen(optarg) >= PF_QNAME_SIZE)
674                                 errx(1, "queuename too long");
675                         qname = optarg;
676                         break;
677                 case 'R':
678                         fixed_server = optarg;
679                         break;
680                 case 'r':
681                         rfc_mode = 1;
682                         break;
683                 case 't':
684                         timeout = strtonum(optarg, 0, 86400, &errstr);
685                         if (errstr)
686                                 errx(1, "timeout %s", errstr);
687                         break;
688                 case 'v':
689                         verbose++;
690                         if (verbose > 2)
691                                 usage();
692                         break;
693                 default:
694                         usage();
695                 }
696         }
697
698         if (listen_ip == NULL)
699                 listen_ip = ipv6_mode ? "::1" : "127.0.0.1";
700
701         /* Check for root to save the user from cryptic failure messages. */
702         if (getuid() != 0)
703                 errx(1, "needs to start as root");
704
705         /* Raise max. open files limit to satisfy max. sessions. */
706         rlp.rlim_cur = rlp.rlim_max = (2 * max_sessions) + 10;
707         if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
708                 err(1, "setrlimit");
709
710         if (fixed_proxy) {
711                 memset(&hints, 0, sizeof hints);
712                 hints.ai_flags = AI_NUMERICHOST;
713                 hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
714                 hints.ai_socktype = SOCK_STREAM;
715                 error = getaddrinfo(fixed_proxy, NULL, &hints, &res);
716                 if (error)
717                         errx(1, "getaddrinfo fixed proxy address failed: %s",
718                             gai_strerror(error));
719                 memcpy(&fixed_proxy_ss, res->ai_addr, res->ai_addrlen);
720                 logmsg(LOG_INFO, "using %s to connect to servers",
721                     sock_ntop(sstosa(&fixed_proxy_ss)));
722                 freeaddrinfo(res);
723         }
724
725         if (fixed_server) {
726                 memset(&hints, 0, sizeof hints);
727                 hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
728                 hints.ai_socktype = SOCK_STREAM;
729                 error = getaddrinfo(fixed_server, fixed_server_port, &hints,
730                     &res);
731                 if (error)
732                         errx(1, "getaddrinfo fixed server address failed: %s",
733                             gai_strerror(error));
734                 memcpy(&fixed_server_ss, res->ai_addr, res->ai_addrlen);
735                 logmsg(LOG_INFO, "using fixed server %s",
736                     sock_ntop(sstosa(&fixed_server_ss)));
737                 freeaddrinfo(res);
738         }
739
740         /* Setup listener. */
741         memset(&hints, 0, sizeof hints);
742         hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
743         hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
744         hints.ai_socktype = SOCK_STREAM;
745         error = getaddrinfo(listen_ip, listen_port, &hints, &res);
746         if (error)
747                 errx(1, "getaddrinfo listen address failed: %s",
748                     gai_strerror(error));
749         if ((listenfd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
750                 errx(1, "socket failed");
751         on = 1;
752         if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
753             sizeof on) != 0)
754                 err(1, "setsockopt failed");
755         if (bind(listenfd, (struct sockaddr *)res->ai_addr,
756             (socklen_t)res->ai_addrlen) != 0)
757                 err(1, "bind failed");
758         if (listen(listenfd, TCP_BACKLOG) != 0)
759                 err(1, "listen failed");
760         freeaddrinfo(res);
761
762         /* Initialize pf. */
763         init_filter(qname, verbose);
764
765         if (daemonize) {
766                 if (daemon(0, 0) == -1)
767                         err(1, "cannot daemonize");
768                 openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
769         }
770
771         /* Use logmsg for output from here on. */
772
773         if (!drop_privs()) {
774                 logmsg(LOG_ERR, "cannot drop privileges: %s", strerror(errno));
775                 exit(1);
776         }
777         
778         if ((kq = kqueue()) == -1) {
779                 logmsg(LOG_ERR, "cannot create new kqueue(2): %s", strerror(errno));
780                 exit(1);
781         }
782
783         /* Setup signal handler. */
784         signal(SIGPIPE, SIG_IGN);
785         EV_SET(&changes[nchanges++], SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
786         EV_SET(&changes[nchanges++], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
787         EV_SET(&changes[nchanges++], SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
788
789         EV_SET(&changes[nchanges++], listenfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
790
791         logmsg(LOG_NOTICE, "listening on %s port %s", listen_ip, listen_port);
792
793         /*  Vroom, vroom.  */
794         for ( ; ; ) {
795                 int i, nevents;
796                 struct kevent events[KQ_NEVENTS], *event;
797                 struct session *s;
798
799                 nevents = kevent(kq, &changes[0], nchanges, &events[0],
800                                  KQ_NEVENTS, NULL);
801                 if (nevents == -1) {
802                         logmsg(LOG_ERR, "cannot create new kqueue(2): %s", strerror(errno));
803                         exit(1);
804                 }
805                 nchanges = 0;
806
807                 for (i = 0; i < nevents; ++i) {
808                         event = &events[i];
809
810                         if (event->filter == EVFILT_SIGNAL) {
811                                 handle_signal(event->ident);
812                                 continue;
813                         }
814
815                         if (event->ident == listenfd) {
816                                 /* Handle new connection */
817                                 handle_connection(event->ident);
818                         } else {
819                                 /* Process existing connection */
820                                 s = (struct session *)event->udata;
821
822                                 if (event->ident == s->client_fd) {
823                                         if (event->filter == EVFILT_READ)
824                                                 client_read(s);
825                                         else
826                                                 client_write(s);
827                                 } else {
828                                         if (event->filter == EVFILT_READ)
829                                                 server_read(s);
830                                         else
831                                                 server_write(s);
832                                 }
833                         }
834
835                         /* The next loop might overflow changes */
836                         if (nchanges > KQ_NEVENTS - 4)
837                                 break;
838                 }
839         }
840
841         exit_daemon();
842
843         /* NOTREACHED */
844         return (1);
845 }
846
847 u_int16_t
848 parse_port(int mode)
849 {
850         unsigned int     port, v[6];
851         int              n;
852         char            *p;
853
854         /* Find the last space or left-parenthesis. */
855         for (p = linebuf + linelen; p > linebuf; p--)
856                 if (*p == ' ' || *p == '(')
857                         break;
858         if (p == linebuf)
859                 return (0);
860
861         switch (mode) {
862         case CMD_PORT:
863                 n = sscanf(p, " %u,%u,%u,%u,%u,%u", &v[0], &v[1], &v[2],
864                     &v[3], &v[4], &v[5]);
865                 if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
866                     v[3] < 256 && v[4] < 256 && v[5] < 256)
867                         return ((v[4] << 8) | v[5]);
868                 break;
869         case CMD_PASV:
870                 n = sscanf(p, "(%u,%u,%u,%u,%u,%u)", &v[0], &v[1], &v[2],
871                     &v[3], &v[4], &v[5]);
872                 if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
873                     v[3] < 256 && v[4] < 256 && v[5] < 256)
874                         return ((v[4] << 8) | v[5]);
875                 break;
876         case CMD_EPSV:
877                 n = sscanf(p, "(|||%u|)", &port);
878                 if (n == 1 && port < 65536)
879                         return (port);
880                 break;
881         case CMD_EPRT:
882                 n = sscanf(p, " |1|%u.%u.%u.%u|%u|", &v[0], &v[1], &v[2],
883                     &v[3], &port);
884                 if (n == 5 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
885                     v[3] < 256 && port < 65536)
886                         return (port);
887                 n = sscanf(p, " |2|%*[a-fA-F0-9:]|%u|", &port);
888                 if (n == 1 && port < 65536)
889                         return (port);
890                 break;
891         default:
892                 return (0);
893         }
894
895         return (0);
896 }
897
898 u_int16_t
899 pick_proxy_port(void)
900 {
901         /* Random should be good enough for avoiding port collisions. */
902         return (IPPORT_HIFIRSTAUTO + (arc4random() %
903             (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)));
904 }
905
906 void
907 proxy_reply(int cmd, struct sockaddr *sa, u_int16_t port)
908 {
909         int i, r;
910
911         switch (cmd) {
912         case CMD_PORT:
913                 r = snprintf(linebuf, sizeof linebuf,
914                     "PORT %s,%u,%u\r\n", sock_ntop(sa), port / 256,
915                     port % 256);
916                 break;
917         case CMD_PASV:
918                 r = snprintf(linebuf, sizeof linebuf,
919                     "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa),
920                         port / 256, port % 256);
921                 break;
922         case CMD_EPRT:
923                 if (sa->sa_family == AF_INET)
924                         r = snprintf(linebuf, sizeof linebuf,
925                             "EPRT |1|%s|%u|\r\n", sock_ntop(sa), port);
926                 else if (sa->sa_family == AF_INET6)
927                         r = snprintf(linebuf, sizeof linebuf,
928                             "EPRT |2|%s|%u|\r\n", sock_ntop(sa), port);
929                 break;
930         case CMD_EPSV:
931                 r = snprintf(linebuf, sizeof linebuf,
932                     "229 Entering Extended Passive Mode (|||%u|)\r\n", port);
933                 break;
934         }
935
936         if (r < 0 || r >= sizeof linebuf) {
937                 logmsg(LOG_ERR, "proxy_reply failed: %d", r);
938                 linebuf[0] = '\0';
939                 linelen = 0;
940                 return;
941         }
942         linelen = (size_t)r;
943
944         if (cmd == CMD_PORT || cmd == CMD_PASV) {
945                 /* Replace dots in IP address with commas. */
946                 for (i = 0; i < linelen; i++)
947                         if (linebuf[i] == '.')
948                                 linebuf[i] = ',';
949         }
950 }
951
952 int
953 server_parse(struct session *s)
954 {
955         struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa;
956         int prepared = 0;
957
958         if (s->cmd == CMD_NONE || linelen < 4 || linebuf[0] != '2')
959                 goto out;
960
961         /*
962          * The pf rules below do quite some NAT rewriting, to keep up
963          * appearances.  Points to keep in mind:
964          * 1)  The client must think it's talking to the real server,
965          *     for both control and data connections.  Transparently.
966          * 2)  The server must think that the proxy is the client.
967          * 3)  Source and destination ports are rewritten to minimize
968          *     port collisions, to aid security (some systems pick weak
969          *     ports) or to satisfy RFC requirements (source port 20).
970          */
971         
972         /* Cast this once, to make code below it more readable. */
973         client_sa = sstosa(&s->client_ss);
974         server_sa = sstosa(&s->server_ss);
975         proxy_sa = sstosa(&s->proxy_ss);
976         if (fixed_server)
977                 /* Fixed server: data connections must appear to come
978                    from / go to the original server, not the fixed one. */
979                 orig_sa = sstosa(&s->orig_server_ss);
980         else
981                 /* Server not fixed: orig_server == server. */
982                 orig_sa = sstosa(&s->server_ss);
983
984         /* Passive modes. */
985         if ((s->cmd == CMD_PASV && strncmp("227 ", linebuf, 4) == 0) ||
986             (s->cmd == CMD_EPSV && strncmp("229 ", linebuf, 4) == 0)) {
987                 s->port = parse_port(s->cmd);
988                 if (s->port < MIN_PORT) {
989                         logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id,
990                             linebuf);
991                         return (0);
992                 }
993                 s->proxy_port = pick_proxy_port();
994                 logmsg(LOG_INFO, "#%d passive: client to server port %d"
995                     " via port %d", s->id, s->port, s->proxy_port);
996
997                 if (prepare_commit(s->id) == -1)
998                         goto fail;
999                 prepared = 1;
1000
1001                 proxy_reply(s->cmd, orig_sa, s->proxy_port);
1002                 logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
1003
1004                 /* rdr from $client to $orig_server port $proxy_port -> $server
1005                     port $port */
1006                 if (add_rdr(s->id, client_sa, orig_sa, s->proxy_port,
1007                     server_sa, s->port) == -1)
1008                         goto fail;
1009
1010                 /* nat from $client to $server port $port -> $proxy */
1011                 if (add_nat(s->id, client_sa, server_sa, s->port, proxy_sa,
1012                     PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1)
1013                         goto fail;
1014
1015                 /* pass in from $client to $server port $port */
1016                 if (add_filter(s->id, PF_IN, client_sa, server_sa,
1017                     s->port) == -1)
1018                         goto fail;
1019
1020                 /* pass out from $proxy to $server port $port */
1021                 if (add_filter(s->id, PF_OUT, proxy_sa, server_sa,
1022                     s->port) == -1)
1023                         goto fail;
1024         }
1025
1026         /* Active modes. */
1027         if ((s->cmd == CMD_PORT || s->cmd == CMD_EPRT) &&
1028             strncmp("200 ", linebuf, 4) == 0) {
1029                 logmsg(LOG_INFO, "#%d active: server to client port %d"
1030                     " via port %d", s->id, s->port, s->proxy_port);
1031
1032                 if (prepare_commit(s->id) == -1)
1033                         goto fail;
1034                 prepared = 1;
1035
1036                 /* rdr from $server to $proxy port $proxy_port -> $client port
1037                     $port */
1038                 if (add_rdr(s->id, server_sa, proxy_sa, s->proxy_port,
1039                     client_sa, s->port) == -1)
1040                         goto fail;
1041
1042                 /* nat from $server to $client port $port -> $orig_server port
1043                     $natport */
1044                 if (rfc_mode && s->cmd == CMD_PORT) {
1045                         /* Rewrite sourceport to RFC mandated 20. */
1046                         if (add_nat(s->id, server_sa, client_sa, s->port,
1047                             orig_sa, 20, 20) == -1)
1048                                 goto fail;
1049                 } else {
1050                         /* Let pf pick a source port from the standard range. */
1051                         if (add_nat(s->id, server_sa, client_sa, s->port,
1052                             orig_sa, PF_NAT_PROXY_PORT_LOW,
1053                             PF_NAT_PROXY_PORT_HIGH) == -1)
1054                                 goto fail;
1055                 }
1056
1057                 /* pass in from $server to $client port $port */
1058                 if (add_filter(s->id, PF_IN, server_sa, client_sa, s->port) ==
1059                     -1)
1060                         goto fail;
1061
1062                 /* pass out from $orig_server to $client port $port */
1063                 if (add_filter(s->id, PF_OUT, orig_sa, client_sa, s->port) ==
1064                     -1)
1065                         goto fail;
1066         }
1067
1068         /* Commit rules if they were prepared. */
1069         if (prepared && (do_commit() == -1)) {
1070                 if (errno != EBUSY)
1071                         goto fail;
1072                 /* One more try if busy. */
1073                 usleep(5000);
1074                 if (do_commit() == -1)
1075                         goto fail;
1076         }
1077
1078  out:
1079         s->cmd = CMD_NONE;
1080         s->port = 0;
1081
1082         return (1);
1083
1084  fail:
1085         logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno));
1086         if (prepared)
1087                 do_rollback();
1088         return (0);
1089 }
1090         
1091 void
1092 server_read(struct session *s)
1093 {
1094         size_t           buf_avail, bread, bwritten;
1095         int              n;
1096
1097         do {
1098                 buf_avail = sizeof s->sbuf - s->sbuf_valid;
1099                 bread = read(s->server_fd, s->sbuf + s->sbuf_valid, buf_avail);
1100                 s->sbuf_valid += bread;
1101
1102                 while ((n = getline(s->sbuf, &s->sbuf_valid)) > 0) {
1103                         logmsg(LOG_DEBUG, "#%d server: %s", s->id, linebuf);
1104                         if (!server_parse(s)) {
1105                                 end_session(s);
1106                                 return;
1107                         }
1108                         bwritten = write(s->client_fd, linebuf, linelen);
1109                         if (bwritten == -1) {
1110                                 logmsg(LOG_ERR, "#%d write failed", s->id);
1111                                 end_session(s);
1112                                 return;
1113                         } else if (bwritten < linelen) {
1114                                 EV_SET(&changes[nchanges++], s->server_fd,
1115                                        EVFILT_READ, EV_DISABLE, 0, 0, s);
1116                                 EV_SET(&changes[nchanges++], s->client_fd,
1117                                        EVFILT_WRITE, EV_ADD, 0, 0, s);
1118                                 buffer_data(s, &s->client, linebuf + bwritten,
1119                                             linelen - bwritten);
1120                                 return;
1121                         }
1122                 }
1123
1124                 if (n == -1) {
1125                         logmsg(LOG_ERR, "#%d server reply too long or not"
1126                             " clean", s->id);
1127                         end_session(s);
1128                         return;
1129                 }
1130         } while (bread == buf_avail);
1131 }
1132
1133 void
1134 server_write(struct session *s)
1135 {
1136         size_t written;
1137
1138         written = write(s->server_fd, s->server.buffer + s->server.buffer_offset,
1139                         s->server.buffer_size - s->server.buffer_offset);
1140         if (written == -1) {
1141                 logmsg(LOG_ERR, "#%d write failed", s->id);
1142                 end_session(s);
1143         } else if (written == (s->server.buffer_size - s->server.buffer_offset)) {
1144                 free(s->server.buffer);
1145                 s->server.buffer = NULL;
1146                 s->server.buffer_size = 0;
1147                 s->server.buffer_offset = 0;
1148                 EV_SET(&changes[nchanges++], s->client_fd,
1149                        EVFILT_READ, EV_ENABLE, 0, 0, s);
1150         } else {
1151                 s->server.buffer_offset += written;
1152         }
1153 }
1154
1155 const char *
1156 sock_ntop(struct sockaddr *sa)
1157 {
1158         static int n = 0;
1159
1160         /* Cycle to next buffer. */
1161         n = (n + 1) % NTOP_BUFS;
1162         ntop_buf[n][0] = '\0';
1163
1164         if (sa->sa_family == AF_INET) {
1165                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1166
1167                 return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
1168                     sizeof ntop_buf[0]));
1169         }
1170
1171         if (sa->sa_family == AF_INET6) {
1172                 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
1173
1174                 return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
1175                     sizeof ntop_buf[0]));
1176         }
1177
1178         return (NULL);
1179 }
1180
1181 void
1182 usage(void)
1183 {
1184         fprintf(stderr, "usage: %s [-6Adrv] [-a address] [-b address]"
1185             " [-D level] [-m maxsessions]\n                 [-P port]"
1186             " [-p port] [-q queue] [-R address] [-t timeout]\n", __progname);
1187         exit(1);
1188 }