1 /* $OpenBSD: ftp-proxy.c,v 1.15 2007/08/15 15:18:02 camield Exp $ */
4 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
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.
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.
19 #include <sys/queue.h>
20 #include <sys/types.h>
21 #include <sys/event.h>
23 #include <sys/resource.h>
24 #include <sys/socket.h>
27 #include <net/pf/pfvar.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
47 #define CONNECT_TIMEOUT 30
50 #define MAX_LOGLINE 300
52 #define TCP_BACKLOG 10
54 #define CHROOT_DIR "/var/empty"
55 #define NOPRIV_USER "proxy"
57 /* pfctl standard NAT range. */
58 #define PF_NAT_PROXY_PORT_LOW 50001
59 #define PF_NAT_PROXY_PORT_HIGH 65535
61 #define sstosa(ss) ((struct sockaddr *)(ss))
63 enum { CMD_NONE
= 0, CMD_PORT
, CMD_EPRT
, CMD_PASV
, CMD_EPSV
};
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
;
88 LIST_ENTRY(session
) entry
;
91 LIST_HEAD(, session
) sessions
= LIST_HEAD_INITIALIZER(sessions
);
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
*);
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 *, ...) __printflike(2, 3);
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 int allow_data_connection(struct session
*s
);
114 const char *sock_ntop(struct sockaddr
*);
117 char linebuf
[MAX_LINE
+ 1];
120 char ntop_buf
[NTOP_BUFS
][INET6_ADDRSTRLEN
];
122 #define KQ_NEVENTS 64
123 struct kevent changes
[KQ_NEVENTS
];
126 struct sockaddr_storage fixed_server_ss
, fixed_proxy_ss
;
127 char *fixed_server
, *fixed_server_port
, *fixed_proxy
, *listen_ip
, *listen_port
,
129 int anonymous_only
, daemonize
, id_count
, ipv6_mode
, loglevel
, max_sessions
,
130 rfc_mode
, session_count
, timeout
, verbose
;
131 extern char *__progname
;
134 buffer_data(struct session
*s
, struct cbuf
*cb
, char *buf
, size_t len
)
139 if (cb
->buffer
== NULL
)
140 if ((cb
->buffer
= malloc(MAX_LINE
)) == NULL
)
143 memcpy(cb
->buffer
, buf
, len
);
144 cb
->buffer_size
= len
;
148 logmsg(LOG_ERR
, "#%d could not allocate memory for buffer", s
->id
);
153 client_parse(struct session
*s
)
155 /* Reset any previous command. */
159 /* Commands we are looking for are at least 4 chars long. */
163 if (linebuf
[0] == 'P' || linebuf
[0] == 'p' ||
164 linebuf
[0] == 'E' || linebuf
[0] == 'e') {
165 if (!client_parse_cmd(s
))
169 * Allow active mode connections immediately, instead of
170 * waiting for a positive reply from the server. Some
171 * rare servers/proxies try to probe or setup the data
172 * connection before an actual transfer request.
174 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
)
175 return (allow_data_connection(s
));
178 if (anonymous_only
&& (linebuf
[0] == 'U' || linebuf
[0] == 'u'))
179 return (client_parse_anon(s
));
185 client_parse_anon(struct session
*s
)
189 if (strcasecmp("USER ftp\r\n", linebuf
) != 0 &&
190 strcasecmp("USER anonymous\r\n", linebuf
) != 0) {
191 snprintf(linebuf
, sizeof linebuf
,
192 "500 Only anonymous FTP allowed\r\n");
193 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
195 /* Talk back to the client ourself. */
196 linelen
= strlen(linebuf
);
197 written
= write(s
->client_fd
, linebuf
, linelen
);
199 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
200 return (0); /* Session will be ended for us */
201 } else if (written
< linelen
) {
202 EV_SET(&changes
[nchanges
++], s
->server_fd
,
203 EVFILT_READ
, EV_DISABLE
, 0, 0, s
);
204 EV_SET(&changes
[nchanges
++], s
->client_fd
,
205 EVFILT_WRITE
, EV_ADD
, 0, 0, s
);
206 buffer_data(s
, &s
->client
, linebuf
+ written
,
211 /* Clear buffer so it's not sent to the server. */
220 client_parse_cmd(struct session
*s
)
222 if (strncasecmp("PASV", linebuf
, 4) == 0)
224 else if (strncasecmp("PORT ", linebuf
, 5) == 0)
226 else if (strncasecmp("EPSV", linebuf
, 4) == 0)
228 else if (strncasecmp("EPRT ", linebuf
, 5) == 0)
233 if (ipv6_mode
&& (s
->cmd
== CMD_PASV
|| s
->cmd
== CMD_PORT
)) {
234 logmsg(LOG_CRIT
, "PASV and PORT not allowed with IPv6");
238 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
) {
239 s
->port
= parse_port(s
->cmd
);
240 if (s
->port
< MIN_PORT
) {
241 logmsg(LOG_CRIT
, "#%d bad port in '%s'", s
->id
,
245 s
->proxy_port
= pick_proxy_port();
246 proxy_reply(s
->cmd
, sstosa(&s
->proxy_ss
), s
->proxy_port
);
247 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
254 client_read(struct session
*s
)
256 size_t buf_avail
, bread
, bwritten
;
260 buf_avail
= sizeof s
->cbuf
- s
->cbuf_valid
;
261 bread
= read(s
->client_fd
, s
->cbuf
+ s
->cbuf_valid
, buf_avail
);
262 s
->cbuf_valid
+= bread
;
264 while ((n
= getline(s
->cbuf
, &s
->cbuf_valid
)) > 0) {
265 logmsg(LOG_DEBUG
, "#%d client: %s", s
->id
, linebuf
);
266 if (!client_parse(s
)) {
270 bwritten
= write(s
->server_fd
, linebuf
, linelen
);
271 if (bwritten
== -1) {
272 } else if (bwritten
< linelen
) {
273 EV_SET(&changes
[nchanges
++], s
->client_fd
,
274 EVFILT_READ
, EV_DISABLE
, 0, 0, s
);
275 EV_SET(&changes
[nchanges
++], s
->server_fd
,
276 EVFILT_WRITE
, EV_ADD
, 0, 0, s
);
277 buffer_data(s
, &s
->server
, linebuf
+ bwritten
,
284 logmsg(LOG_ERR
, "#%d client command too long or not"
289 } while (bread
== buf_avail
);
293 client_write(struct session
*s
)
297 written
= write(s
->client_fd
, s
->client
.buffer
+ s
->client
.buffer_offset
,
298 s
->client
.buffer_size
- s
->client
.buffer_offset
);
300 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
302 } else if (written
== (s
->client
.buffer_size
- s
->client
.buffer_offset
)) {
303 free(s
->client
.buffer
);
304 s
->client
.buffer
= NULL
;
305 s
->client
.buffer_size
= 0;
306 s
->client
.buffer_offset
= 0;
307 EV_SET(&changes
[nchanges
++], s
->server_fd
,
308 EVFILT_READ
, EV_ENABLE
, 0, 0, s
);
310 s
->client
.buffer_offset
+= written
;
319 pw
= getpwnam(NOPRIV_USER
);
324 if (chroot(CHROOT_DIR
) != 0 || chdir("/") != 0 ||
325 setgroups(1, &pw
->pw_gid
) != 0 ||
326 setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) != 0 ||
327 setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) != 0)
334 end_session(struct session
*s
)
338 logmsg(LOG_INFO
, "#%d ending session", s
->id
);
340 if (s
->client_fd
!= -1)
342 if (s
->server_fd
!= -1)
345 if (s
->client
.buffer
)
346 free(s
->client
.buffer
);
347 if (s
->server
.buffer
)
348 free(s
->server
.buffer
);
350 /* Remove rulesets by commiting empty ones. */
352 if (prepare_commit(s
->id
) == -1)
354 else if (do_commit() == -1) {
359 logmsg(LOG_ERR
, "#%d pf rule removal failed: %s", s
->id
,
362 LIST_REMOVE(s
, entry
);
370 struct session
*s
, *tmp
;
372 LIST_FOREACH_MUTABLE(s
, &sessions
, entry
, tmp
) {
386 getline(char *buf
, size_t *valid
)
390 if (*valid
> MAX_LINE
)
393 /* Copy to linebuf while searching for a newline. */
394 for (i
= 0; i
< *valid
; i
++) {
403 /* No newline found. */
412 linebuf
[linelen
] = '\0';
415 /* Move leftovers to the start. */
417 bcopy(buf
+ linelen
, buf
, *valid
);
419 return ((int)linelen
);
423 handle_connection(const int listen_fd
)
425 struct sockaddr_storage tmp_ss
;
426 struct sockaddr
*client_sa
, *server_sa
, *fixed_server_sa
;
427 struct sockaddr
*client_to_proxy_sa
, *proxy_to_server_sa
;
430 int client_fd
, fc
, on
;
433 * We _must_ accept the connection, otherwise libevent will keep
434 * coming back, and we will chew up all CPU.
436 client_sa
= sstosa(&tmp_ss
);
437 len
= sizeof(struct sockaddr_storage
);
438 if ((client_fd
= accept(listen_fd
, client_sa
, &len
)) < 0) {
439 logmsg(LOG_CRIT
, "accept failed: %s", strerror(errno
));
443 /* Refuse connection if the maximum is reached. */
444 if (session_count
>= max_sessions
) {
445 logmsg(LOG_ERR
, "client limit (%d) reached, refusing "
446 "connection from %s", max_sessions
, sock_ntop(client_sa
));
451 /* Allocate session and copy back the info from the accept(). */
454 logmsg(LOG_CRIT
, "init_session failed");
458 s
->client_fd
= client_fd
;
459 memcpy(sstosa(&s
->client_ss
), client_sa
, client_sa
->sa_len
);
461 /* Cast it once, and be done with it. */
462 client_sa
= sstosa(&s
->client_ss
);
463 server_sa
= sstosa(&s
->server_ss
);
464 client_to_proxy_sa
= sstosa(&tmp_ss
);
465 proxy_to_server_sa
= sstosa(&s
->proxy_ss
);
466 fixed_server_sa
= sstosa(&fixed_server_ss
);
468 /* Log id/client early to ease debugging. */
469 logmsg(LOG_DEBUG
, "#%d accepted connection from %s", s
->id
,
470 sock_ntop(client_sa
));
473 * Find out the real server and port that the client wanted.
475 len
= sizeof(struct sockaddr_storage
);
476 if ((getsockname(s
->client_fd
, client_to_proxy_sa
, &len
)) < 0) {
477 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
481 if (server_lookup(client_sa
, client_to_proxy_sa
, server_sa
) != 0) {
482 logmsg(LOG_CRIT
, "#%d server lookup failed (no rdr?)", s
->id
);
486 memcpy(sstosa(&s
->orig_server_ss
), server_sa
,
488 memcpy(server_sa
, fixed_server_sa
, fixed_server_sa
->sa_len
);
491 /* XXX: check we are not connecting to ourself. */
494 * Setup socket and connect to server.
496 if ((s
->server_fd
= socket(server_sa
->sa_family
, SOCK_STREAM
,
498 logmsg(LOG_CRIT
, "#%d server socket failed: %s", s
->id
,
502 if (fixed_proxy
&& bind(s
->server_fd
, sstosa(&fixed_proxy_ss
),
503 fixed_proxy_ss
.ss_len
) != 0) {
504 logmsg(LOG_CRIT
, "#%d cannot bind fixed proxy address: %s",
505 s
->id
, strerror(errno
));
509 /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */
510 if ((fc
= fcntl(s
->server_fd
, F_GETFL
)) == -1 ||
511 fcntl(s
->server_fd
, F_SETFL
, fc
| O_NONBLOCK
) == -1) {
512 logmsg(LOG_CRIT
, "#%d cannot mark socket non-blocking: %s",
513 s
->id
, strerror(errno
));
516 if (connect(s
->server_fd
, server_sa
, server_sa
->sa_len
) < 0 &&
517 errno
!= EINPROGRESS
) {
518 logmsg(LOG_CRIT
, "#%d proxy cannot connect to server %s: %s",
519 s
->id
, sock_ntop(server_sa
), strerror(errno
));
523 len
= sizeof(struct sockaddr_storage
);
524 if ((getsockname(s
->server_fd
, proxy_to_server_sa
, &len
)) < 0) {
525 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
530 logmsg(LOG_INFO
, "#%d FTP session %d/%d started: client %s to server "
531 "%s via proxy %s ", s
->id
, session_count
, max_sessions
,
532 sock_ntop(client_sa
), sock_ntop(server_sa
),
533 sock_ntop(proxy_to_server_sa
));
535 /* Keepalive is nice, but don't care if it fails. */
537 setsockopt(s
->client_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
539 setsockopt(s
->server_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
542 EV_SET(&changes
[nchanges
++], s
->client_fd
, EVFILT_READ
, EV_ADD
, 0, 0, s
);
543 EV_SET(&changes
[nchanges
++], s
->server_fd
, EVFILT_READ
, EV_ADD
, 0, 0, s
);
552 handle_signal(int sig
)
555 * Signal handler rules don't apply.
558 logmsg(LOG_ERR
, "%s exiting on signal %d", __progname
, sig
);
569 s
= calloc(1, sizeof(struct session
));
580 s
->client
.buffer
= NULL
;
581 s
->client
.buffer_size
= 0;
582 s
->client
.buffer_offset
= 0;
583 s
->server
.buffer
= NULL
;
584 s
->server
.buffer_size
= 0;
585 s
->server
.buffer_offset
= 0;
589 LIST_INSERT_HEAD(&sessions
, s
, entry
);
596 logmsg(int pri
, const char *message
, ...)
603 va_start(ap
, message
);
606 /* syslog does its own vissing. */
607 vsyslog(pri
, message
, ap
);
609 char buf
[MAX_LOGLINE
];
610 char visbuf
[2 * MAX_LOGLINE
];
612 /* We don't care about truncation. */
613 vsnprintf(buf
, sizeof buf
, message
, ap
);
614 strnvis(visbuf
, buf
, sizeof visbuf
, VIS_CSTYLE
| VIS_NL
);
615 fprintf(stderr
, "%s\n", visbuf
);
622 main(int argc
, char *argv
[])
625 struct addrinfo hints
, *res
;
626 int kq
, ch
, error
, listenfd
, on
;
634 fixed_server_port
= "21";
637 listen_port
= "8021";
638 loglevel
= LOG_NOTICE
;
646 /* Other initialization. */
651 while ((ch
= getopt(argc
, argv
, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) {
660 fixed_proxy
= optarg
;
666 loglevel
= strtonum(optarg
, LOG_EMERG
, LOG_DEBUG
,
669 errx(1, "loglevel %s", errstr
);
675 max_sessions
= strtonum(optarg
, 1, 500, &errstr
);
677 errx(1, "max sessions %s", errstr
);
680 fixed_server_port
= optarg
;
683 listen_port
= optarg
;
686 if (strlen(optarg
) >= PF_QNAME_SIZE
)
687 errx(1, "queuename too long");
691 fixed_server
= optarg
;
697 if (strlen(optarg
) >= PF_TAG_NAME_SIZE
)
698 errx(1, "tagname too long");
702 timeout
= strtonum(optarg
, 0, 86400, &errstr
);
704 errx(1, "timeout %s", errstr
);
716 if (listen_ip
== NULL
)
717 listen_ip
= ipv6_mode
? "::1" : "127.0.0.1";
719 /* Check for root to save the user from cryptic failure messages. */
721 errx(1, "needs to start as root");
723 /* Raise max. open files limit to satisfy max. sessions. */
724 rlp
.rlim_cur
= rlp
.rlim_max
= (2 * max_sessions
) + 10;
725 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
729 memset(&hints
, 0, sizeof hints
);
730 hints
.ai_flags
= AI_NUMERICHOST
;
731 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
732 hints
.ai_socktype
= SOCK_STREAM
;
733 error
= getaddrinfo(fixed_proxy
, NULL
, &hints
, &res
);
735 errx(1, "getaddrinfo fixed proxy address failed: %s",
736 gai_strerror(error
));
737 memcpy(&fixed_proxy_ss
, res
->ai_addr
, res
->ai_addrlen
);
738 logmsg(LOG_INFO
, "using %s to connect to servers",
739 sock_ntop(sstosa(&fixed_proxy_ss
)));
744 memset(&hints
, 0, sizeof hints
);
745 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
746 hints
.ai_socktype
= SOCK_STREAM
;
747 error
= getaddrinfo(fixed_server
, fixed_server_port
, &hints
,
750 errx(1, "getaddrinfo fixed server address failed: %s",
751 gai_strerror(error
));
752 memcpy(&fixed_server_ss
, res
->ai_addr
, res
->ai_addrlen
);
753 logmsg(LOG_INFO
, "using fixed server %s",
754 sock_ntop(sstosa(&fixed_server_ss
)));
758 /* Setup listener. */
759 memset(&hints
, 0, sizeof hints
);
760 hints
.ai_flags
= AI_NUMERICHOST
| AI_PASSIVE
;
761 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
762 hints
.ai_socktype
= SOCK_STREAM
;
763 error
= getaddrinfo(listen_ip
, listen_port
, &hints
, &res
);
765 errx(1, "getaddrinfo listen address failed: %s",
766 gai_strerror(error
));
767 if ((listenfd
= socket(res
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
)) == -1)
768 errx(1, "socket failed");
770 if (setsockopt(listenfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
,
772 err(1, "setsockopt failed");
773 if (bind(listenfd
, (struct sockaddr
*)res
->ai_addr
,
774 (socklen_t
)res
->ai_addrlen
) != 0)
775 err(1, "bind failed");
776 if (listen(listenfd
, TCP_BACKLOG
) != 0)
777 err(1, "listen failed");
781 init_filter(qname
, tagname
, verbose
);
784 if (daemon(0, 0) == -1)
785 err(1, "cannot daemonize");
786 openlog(__progname
, LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
789 /* Use logmsg for output from here on. */
792 logmsg(LOG_ERR
, "cannot drop privileges: %s", strerror(errno
));
796 if ((kq
= kqueue()) == -1) {
797 logmsg(LOG_ERR
, "cannot create new kqueue(2): %s", strerror(errno
));
801 /* Setup signal handler. */
802 signal(SIGPIPE
, SIG_IGN
);
803 EV_SET(&changes
[nchanges
++], SIGHUP
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
804 EV_SET(&changes
[nchanges
++], SIGINT
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
805 EV_SET(&changes
[nchanges
++], SIGTERM
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
807 EV_SET(&changes
[nchanges
++], listenfd
, EVFILT_READ
, EV_ADD
, 0, 0, NULL
);
809 logmsg(LOG_NOTICE
, "listening on %s port %s", listen_ip
, listen_port
);
814 struct kevent events
[KQ_NEVENTS
], *event
;
817 nevents
= kevent(kq
, &changes
[0], nchanges
, &events
[0],
820 logmsg(LOG_ERR
, "cannot create new kqueue(2): %s", strerror(errno
));
825 for (i
= 0; i
< nevents
; ++i
) {
828 if (event
->filter
== EVFILT_SIGNAL
) {
829 handle_signal(event
->ident
);
833 if (event
->ident
== listenfd
) {
834 /* Handle new connection */
835 handle_connection(event
->ident
);
837 /* Process existing connection */
838 s
= (struct session
*)event
->udata
;
840 if (event
->ident
== s
->client_fd
) {
841 if (event
->filter
== EVFILT_READ
)
846 if (event
->filter
== EVFILT_READ
)
853 /* The next loop might overflow changes */
854 if (nchanges
> KQ_NEVENTS
- 4)
868 unsigned int port
, v
[6];
872 /* Find the last space or left-parenthesis. */
873 for (p
= linebuf
+ linelen
; p
> linebuf
; p
--)
874 if (*p
== ' ' || *p
== '(')
881 n
= sscanf(p
, " %u,%u,%u,%u,%u,%u", &v
[0], &v
[1], &v
[2],
882 &v
[3], &v
[4], &v
[5]);
883 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
884 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
885 return ((v
[4] << 8) | v
[5]);
888 n
= sscanf(p
, "(%u,%u,%u,%u,%u,%u)", &v
[0], &v
[1], &v
[2],
889 &v
[3], &v
[4], &v
[5]);
890 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
891 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
892 return ((v
[4] << 8) | v
[5]);
895 n
= sscanf(p
, "(|||%u|)", &port
);
896 if (n
== 1 && port
< 65536)
900 n
= sscanf(p
, " |1|%u.%u.%u.%u|%u|", &v
[0], &v
[1], &v
[2],
902 if (n
== 5 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
903 v
[3] < 256 && port
< 65536)
905 n
= sscanf(p
, " |2|%*[a-fA-F0-9:]|%u|", &port
);
906 if (n
== 1 && port
< 65536)
917 pick_proxy_port(void)
919 /* Random should be good enough for avoiding port collisions. */
920 return (IPPORT_HIFIRSTAUTO
+ (arc4random() %
921 (IPPORT_HILASTAUTO
- IPPORT_HIFIRSTAUTO
)));
925 proxy_reply(int cmd
, struct sockaddr
*sa
, u_int16_t port
)
931 r
= snprintf(linebuf
, sizeof linebuf
,
932 "PORT %s,%u,%u\r\n", sock_ntop(sa
), port
/ 256,
936 r
= snprintf(linebuf
, sizeof linebuf
,
937 "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa
),
938 port
/ 256, port
% 256);
941 if (sa
->sa_family
== AF_INET
)
942 r
= snprintf(linebuf
, sizeof linebuf
,
943 "EPRT |1|%s|%u|\r\n", sock_ntop(sa
), port
);
944 else if (sa
->sa_family
== AF_INET6
)
945 r
= snprintf(linebuf
, sizeof linebuf
,
946 "EPRT |2|%s|%u|\r\n", sock_ntop(sa
), port
);
949 r
= snprintf(linebuf
, sizeof linebuf
,
950 "229 Entering Extended Passive Mode (|||%u|)\r\n", port
);
954 if (r
< 0 || r
>= sizeof linebuf
) {
955 logmsg(LOG_ERR
, "proxy_reply failed: %d", r
);
962 if (cmd
== CMD_PORT
|| cmd
== CMD_PASV
) {
963 /* Replace dots in IP address with commas. */
964 for (i
= 0; i
< linelen
; i
++)
965 if (linebuf
[i
] == '.')
971 server_parse(struct session
*s
)
973 if (s
->cmd
== CMD_NONE
|| linelen
< 4 || linebuf
[0] != '2')
976 if ((s
->cmd
== CMD_PASV
&& strncmp("227 ", linebuf
, 4) == 0) ||
977 (s
->cmd
== CMD_EPSV
&& strncmp("229 ", linebuf
, 4) == 0))
978 return (allow_data_connection(s
));
988 allow_data_connection(struct session
*s
)
990 struct sockaddr
*client_sa
, *orig_sa
, *proxy_sa
, *server_sa
;
993 if (s
->cmd
== CMD_NONE
|| linelen
< 4 || linebuf
[0] != '2')
997 * The pf rules below do quite some NAT rewriting, to keep up
998 * appearances. Points to keep in mind:
999 * 1) The client must think it's talking to the real server,
1000 * for both control and data connections. Transparently.
1001 * 2) The server must think that the proxy is the client.
1002 * 3) Source and destination ports are rewritten to minimize
1003 * port collisions, to aid security (some systems pick weak
1004 * ports) or to satisfy RFC requirements (source port 20).
1007 /* Cast this once, to make code below it more readable. */
1008 client_sa
= sstosa(&s
->client_ss
);
1009 server_sa
= sstosa(&s
->server_ss
);
1010 proxy_sa
= sstosa(&s
->proxy_ss
);
1012 /* Fixed server: data connections must appear to come
1013 from / go to the original server, not the fixed one. */
1014 orig_sa
= sstosa(&s
->orig_server_ss
);
1016 /* Server not fixed: orig_server == server. */
1017 orig_sa
= sstosa(&s
->server_ss
);
1019 /* Passive modes. */
1020 if (s
->cmd
== CMD_PASV
|| s
->cmd
== CMD_EPSV
) {
1021 s
->port
= parse_port(s
->cmd
);
1022 if (s
->port
< MIN_PORT
) {
1023 logmsg(LOG_CRIT
, "#%d bad port in '%s'", s
->id
,
1027 s
->proxy_port
= pick_proxy_port();
1028 logmsg(LOG_INFO
, "#%d passive: client to server port %d"
1029 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1031 if (prepare_commit(s
->id
) == -1)
1035 proxy_reply(s
->cmd
, orig_sa
, s
->proxy_port
);
1036 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
1038 /* rdr from $client to $orig_server port $proxy_port -> $server
1040 if (add_rdr(s
->id
, client_sa
, orig_sa
, s
->proxy_port
,
1041 server_sa
, s
->port
) == -1)
1044 /* nat from $client to $server port $port -> $proxy */
1045 if (add_nat(s
->id
, client_sa
, server_sa
, s
->port
, proxy_sa
,
1046 PF_NAT_PROXY_PORT_LOW
, PF_NAT_PROXY_PORT_HIGH
) == -1)
1049 /* pass in from $client to $server port $port */
1050 if (add_filter(s
->id
, PF_IN
, client_sa
, server_sa
,
1054 /* pass out from $proxy to $server port $port */
1055 if (add_filter(s
->id
, PF_OUT
, proxy_sa
, server_sa
,
1061 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
) {
1062 logmsg(LOG_INFO
, "#%d active: server to client port %d"
1063 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1065 if (prepare_commit(s
->id
) == -1)
1069 /* rdr from $server to $proxy port $proxy_port -> $client port
1071 if (add_rdr(s
->id
, server_sa
, proxy_sa
, s
->proxy_port
,
1072 client_sa
, s
->port
) == -1)
1075 /* nat from $server to $client port $port -> $orig_server port
1077 if (rfc_mode
&& s
->cmd
== CMD_PORT
) {
1078 /* Rewrite sourceport to RFC mandated 20. */
1079 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1080 orig_sa
, 20, 20) == -1)
1083 /* Let pf pick a source port from the standard range. */
1084 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1085 orig_sa
, PF_NAT_PROXY_PORT_LOW
,
1086 PF_NAT_PROXY_PORT_HIGH
) == -1)
1090 /* pass in from $server to $client port $port */
1091 if (add_filter(s
->id
, PF_IN
, server_sa
, client_sa
, s
->port
) ==
1095 /* pass out from $orig_server to $client port $port */
1096 if (add_filter(s
->id
, PF_OUT
, orig_sa
, client_sa
, s
->port
) ==
1101 /* Commit rules if they were prepared. */
1102 if (prepared
&& (do_commit() == -1)) {
1105 /* One more try if busy. */
1107 if (do_commit() == -1)
1118 logmsg(LOG_CRIT
, "#%d pf operation failed: %s", s
->id
, strerror(errno
));
1125 server_read(struct session
*s
)
1127 size_t buf_avail
, bread
, bwritten
;
1131 buf_avail
= sizeof s
->sbuf
- s
->sbuf_valid
;
1132 bread
= read(s
->server_fd
, s
->sbuf
+ s
->sbuf_valid
, buf_avail
);
1133 s
->sbuf_valid
+= bread
;
1135 while ((n
= getline(s
->sbuf
, &s
->sbuf_valid
)) > 0) {
1136 logmsg(LOG_DEBUG
, "#%d server: %s", s
->id
, linebuf
);
1137 if (!server_parse(s
)) {
1141 bwritten
= write(s
->client_fd
, linebuf
, linelen
);
1142 if (bwritten
== -1) {
1143 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
1146 } else if (bwritten
< linelen
) {
1147 EV_SET(&changes
[nchanges
++], s
->server_fd
,
1148 EVFILT_READ
, EV_DISABLE
, 0, 0, s
);
1149 EV_SET(&changes
[nchanges
++], s
->client_fd
,
1150 EVFILT_WRITE
, EV_ADD
, 0, 0, s
);
1151 buffer_data(s
, &s
->client
, linebuf
+ bwritten
,
1152 linelen
- bwritten
);
1158 logmsg(LOG_ERR
, "#%d server reply too long or not"
1163 } while (bread
== buf_avail
);
1167 server_write(struct session
*s
)
1171 written
= write(s
->server_fd
, s
->server
.buffer
+ s
->server
.buffer_offset
,
1172 s
->server
.buffer_size
- s
->server
.buffer_offset
);
1173 if (written
== -1) {
1174 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
1176 } else if (written
== (s
->server
.buffer_size
- s
->server
.buffer_offset
)) {
1177 free(s
->server
.buffer
);
1178 s
->server
.buffer
= NULL
;
1179 s
->server
.buffer_size
= 0;
1180 s
->server
.buffer_offset
= 0;
1181 EV_SET(&changes
[nchanges
++], s
->client_fd
,
1182 EVFILT_READ
, EV_ENABLE
, 0, 0, s
);
1184 s
->server
.buffer_offset
+= written
;
1189 sock_ntop(struct sockaddr
*sa
)
1193 /* Cycle to next buffer. */
1194 n
= (n
+ 1) % NTOP_BUFS
;
1195 ntop_buf
[n
][0] = '\0';
1197 if (sa
->sa_family
== AF_INET
) {
1198 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1200 return (inet_ntop(AF_INET
, &sin
->sin_addr
, ntop_buf
[n
],
1201 sizeof ntop_buf
[0]));
1204 if (sa
->sa_family
== AF_INET6
) {
1205 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
1207 return (inet_ntop(AF_INET6
, &sin6
->sin6_addr
, ntop_buf
[n
],
1208 sizeof ntop_buf
[0]));
1217 fprintf(stderr
, "usage: %s [-6Adrv] [-a address] [-b address]"
1218 " [-D level] [-m maxsessions]\n [-P port]"
1219 " [-p port] [-q queue] [-R address] [-T tag] [-t timeout]\n", __progname
);