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 void exit_daemon(void);
102 int get_line(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
= get_line(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
) {
383 get_line(char *buf
, size_t *valid
)
387 if (*valid
> MAX_LINE
)
390 /* Copy to linebuf while searching for a newline. */
391 for (i
= 0; i
< *valid
; i
++) {
400 /* No newline found. */
409 linebuf
[linelen
] = '\0';
412 /* Move leftovers to the start. */
414 bcopy(buf
+ linelen
, buf
, *valid
);
416 return ((int)linelen
);
420 handle_connection(const int listen_fd
)
422 struct sockaddr_storage tmp_ss
;
423 struct sockaddr
*client_sa
, *server_sa
, *fixed_server_sa
;
424 struct sockaddr
*client_to_proxy_sa
, *proxy_to_server_sa
;
427 int client_fd
, fc
, on
;
430 * We _must_ accept the connection, otherwise libevent will keep
431 * coming back, and we will chew up all CPU.
433 client_sa
= sstosa(&tmp_ss
);
434 len
= sizeof(struct sockaddr_storage
);
435 if ((client_fd
= accept(listen_fd
, client_sa
, &len
)) < 0) {
436 logmsg(LOG_CRIT
, "accept failed: %s", strerror(errno
));
440 /* Refuse connection if the maximum is reached. */
441 if (session_count
>= max_sessions
) {
442 logmsg(LOG_ERR
, "client limit (%d) reached, refusing "
443 "connection from %s", max_sessions
, sock_ntop(client_sa
));
448 /* Allocate session and copy back the info from the accept(). */
451 logmsg(LOG_CRIT
, "init_session failed");
455 s
->client_fd
= client_fd
;
456 memcpy(sstosa(&s
->client_ss
), client_sa
, client_sa
->sa_len
);
458 /* Cast it once, and be done with it. */
459 client_sa
= sstosa(&s
->client_ss
);
460 server_sa
= sstosa(&s
->server_ss
);
461 client_to_proxy_sa
= sstosa(&tmp_ss
);
462 proxy_to_server_sa
= sstosa(&s
->proxy_ss
);
463 fixed_server_sa
= sstosa(&fixed_server_ss
);
465 /* Log id/client early to ease debugging. */
466 logmsg(LOG_DEBUG
, "#%d accepted connection from %s", s
->id
,
467 sock_ntop(client_sa
));
470 * Find out the real server and port that the client wanted.
472 len
= sizeof(struct sockaddr_storage
);
473 if ((getsockname(s
->client_fd
, client_to_proxy_sa
, &len
)) < 0) {
474 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
478 if (server_lookup(client_sa
, client_to_proxy_sa
, server_sa
) != 0) {
479 logmsg(LOG_CRIT
, "#%d server lookup failed (no rdr?)", s
->id
);
483 memcpy(sstosa(&s
->orig_server_ss
), server_sa
,
485 memcpy(server_sa
, fixed_server_sa
, fixed_server_sa
->sa_len
);
488 /* XXX: check we are not connecting to ourself. */
491 * Setup socket and connect to server.
493 if ((s
->server_fd
= socket(server_sa
->sa_family
, SOCK_STREAM
,
495 logmsg(LOG_CRIT
, "#%d server socket failed: %s", s
->id
,
499 if (fixed_proxy
&& bind(s
->server_fd
, sstosa(&fixed_proxy_ss
),
500 fixed_proxy_ss
.ss_len
) != 0) {
501 logmsg(LOG_CRIT
, "#%d cannot bind fixed proxy address: %s",
502 s
->id
, strerror(errno
));
506 /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */
507 if ((fc
= fcntl(s
->server_fd
, F_GETFL
)) == -1 ||
508 fcntl(s
->server_fd
, F_SETFL
, fc
| O_NONBLOCK
) == -1) {
509 logmsg(LOG_CRIT
, "#%d cannot mark socket non-blocking: %s",
510 s
->id
, strerror(errno
));
513 if (connect(s
->server_fd
, server_sa
, server_sa
->sa_len
) < 0 &&
514 errno
!= EINPROGRESS
) {
515 logmsg(LOG_CRIT
, "#%d proxy cannot connect to server %s: %s",
516 s
->id
, sock_ntop(server_sa
), strerror(errno
));
520 len
= sizeof(struct sockaddr_storage
);
521 if ((getsockname(s
->server_fd
, proxy_to_server_sa
, &len
)) < 0) {
522 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
527 logmsg(LOG_INFO
, "#%d FTP session %d/%d started: client %s to server "
528 "%s via proxy %s ", s
->id
, session_count
, max_sessions
,
529 sock_ntop(client_sa
), sock_ntop(server_sa
),
530 sock_ntop(proxy_to_server_sa
));
532 /* Keepalive is nice, but don't care if it fails. */
534 setsockopt(s
->client_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
536 setsockopt(s
->server_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
539 EV_SET(&changes
[nchanges
++], s
->client_fd
, EVFILT_READ
, EV_ADD
, 0, 0, s
);
540 EV_SET(&changes
[nchanges
++], s
->server_fd
, EVFILT_READ
, EV_ADD
, 0, 0, s
);
549 handle_signal(int sig
)
552 * Signal handler rules don't apply.
555 logmsg(LOG_ERR
, "%s exiting on signal %d", __progname
, sig
);
566 s
= calloc(1, sizeof(struct session
));
577 s
->client
.buffer
= NULL
;
578 s
->client
.buffer_size
= 0;
579 s
->client
.buffer_offset
= 0;
580 s
->server
.buffer
= NULL
;
581 s
->server
.buffer_size
= 0;
582 s
->server
.buffer_offset
= 0;
586 LIST_INSERT_HEAD(&sessions
, s
, entry
);
593 logmsg(int pri
, const char *message
, ...)
600 va_start(ap
, message
);
603 /* syslog does its own vissing. */
604 vsyslog(pri
, message
, ap
);
606 char buf
[MAX_LOGLINE
];
607 char visbuf
[2 * MAX_LOGLINE
];
609 /* We don't care about truncation. */
610 vsnprintf(buf
, sizeof buf
, message
, ap
);
611 strnvis(visbuf
, buf
, sizeof visbuf
, VIS_CSTYLE
| VIS_NL
);
612 fprintf(stderr
, "%s\n", visbuf
);
619 main(int argc
, char *argv
[])
622 struct addrinfo hints
, *res
;
623 int kq
, ch
, error
, listenfd
, on
;
631 fixed_server_port
= "21";
634 listen_port
= "8021";
635 loglevel
= LOG_NOTICE
;
643 /* Other initialization. */
648 while ((ch
= getopt(argc
, argv
, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) {
657 fixed_proxy
= optarg
;
663 loglevel
= strtonum(optarg
, LOG_EMERG
, LOG_DEBUG
,
666 errx(1, "loglevel %s", errstr
);
672 max_sessions
= strtonum(optarg
, 1, 500, &errstr
);
674 errx(1, "max sessions %s", errstr
);
677 fixed_server_port
= optarg
;
680 listen_port
= optarg
;
683 if (strlen(optarg
) >= PF_QNAME_SIZE
)
684 errx(1, "queuename too long");
688 fixed_server
= optarg
;
694 if (strlen(optarg
) >= PF_TAG_NAME_SIZE
)
695 errx(1, "tagname too long");
699 timeout
= strtonum(optarg
, 0, 86400, &errstr
);
701 errx(1, "timeout %s", errstr
);
713 if (listen_ip
== NULL
)
714 listen_ip
= ipv6_mode
? "::1" : "127.0.0.1";
716 /* Check for root to save the user from cryptic failure messages. */
718 errx(1, "needs to start as root");
720 /* Raise max. open files limit to satisfy max. sessions. */
721 rlp
.rlim_cur
= rlp
.rlim_max
= (2 * max_sessions
) + 10;
722 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
726 memset(&hints
, 0, sizeof hints
);
727 hints
.ai_flags
= AI_NUMERICHOST
;
728 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
729 hints
.ai_socktype
= SOCK_STREAM
;
730 error
= getaddrinfo(fixed_proxy
, NULL
, &hints
, &res
);
732 errx(1, "getaddrinfo fixed proxy address failed: %s",
733 gai_strerror(error
));
734 memcpy(&fixed_proxy_ss
, res
->ai_addr
, res
->ai_addrlen
);
735 logmsg(LOG_INFO
, "using %s to connect to servers",
736 sock_ntop(sstosa(&fixed_proxy_ss
)));
741 memset(&hints
, 0, sizeof hints
);
742 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
743 hints
.ai_socktype
= SOCK_STREAM
;
744 error
= getaddrinfo(fixed_server
, fixed_server_port
, &hints
,
747 errx(1, "getaddrinfo fixed server address failed: %s",
748 gai_strerror(error
));
749 memcpy(&fixed_server_ss
, res
->ai_addr
, res
->ai_addrlen
);
750 logmsg(LOG_INFO
, "using fixed server %s",
751 sock_ntop(sstosa(&fixed_server_ss
)));
755 /* Setup listener. */
756 memset(&hints
, 0, sizeof hints
);
757 hints
.ai_flags
= AI_NUMERICHOST
| AI_PASSIVE
;
758 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
759 hints
.ai_socktype
= SOCK_STREAM
;
760 error
= getaddrinfo(listen_ip
, listen_port
, &hints
, &res
);
762 errx(1, "getaddrinfo listen address failed: %s",
763 gai_strerror(error
));
764 if ((listenfd
= socket(res
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
)) == -1)
765 errx(1, "socket failed");
767 if (setsockopt(listenfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
,
769 err(1, "setsockopt failed");
770 if (bind(listenfd
, (struct sockaddr
*)res
->ai_addr
,
771 (socklen_t
)res
->ai_addrlen
) != 0)
772 err(1, "bind failed");
773 if (listen(listenfd
, TCP_BACKLOG
) != 0)
774 err(1, "listen failed");
778 init_filter(qname
, tagname
, verbose
);
781 if (daemon(0, 0) == -1)
782 err(1, "cannot daemonize");
783 openlog(__progname
, LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
786 /* Use logmsg for output from here on. */
789 logmsg(LOG_ERR
, "cannot drop privileges: %s", strerror(errno
));
793 if ((kq
= kqueue()) == -1) {
794 logmsg(LOG_ERR
, "cannot create new kqueue(2): %s", strerror(errno
));
798 /* Setup signal handler. */
799 signal(SIGPIPE
, SIG_IGN
);
800 EV_SET(&changes
[nchanges
++], SIGHUP
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
801 EV_SET(&changes
[nchanges
++], SIGINT
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
802 EV_SET(&changes
[nchanges
++], SIGTERM
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, NULL
);
804 EV_SET(&changes
[nchanges
++], listenfd
, EVFILT_READ
, EV_ADD
, 0, 0, NULL
);
806 logmsg(LOG_NOTICE
, "listening on %s port %s", listen_ip
, listen_port
);
811 struct kevent events
[KQ_NEVENTS
], *event
;
814 nevents
= kevent(kq
, &changes
[0], nchanges
, &events
[0],
817 logmsg(LOG_ERR
, "cannot create new kqueue(2): %s", strerror(errno
));
822 for (i
= 0; i
< nevents
; ++i
) {
825 if (event
->filter
== EVFILT_SIGNAL
) {
826 handle_signal(event
->ident
);
830 if (event
->ident
== listenfd
) {
831 /* Handle new connection */
832 handle_connection(event
->ident
);
834 /* Process existing connection */
835 s
= (struct session
*)event
->udata
;
837 if (event
->ident
== s
->client_fd
) {
838 if (event
->filter
== EVFILT_READ
)
843 if (event
->filter
== EVFILT_READ
)
850 /* The next loop might overflow changes */
851 if (nchanges
> KQ_NEVENTS
- 4)
865 unsigned int port
, v
[6];
869 /* Find the last space or left-parenthesis. */
870 for (p
= linebuf
+ linelen
; p
> linebuf
; p
--)
871 if (*p
== ' ' || *p
== '(')
878 n
= sscanf(p
, " %u,%u,%u,%u,%u,%u", &v
[0], &v
[1], &v
[2],
879 &v
[3], &v
[4], &v
[5]);
880 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
881 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
882 return ((v
[4] << 8) | v
[5]);
885 n
= sscanf(p
, "(%u,%u,%u,%u,%u,%u)", &v
[0], &v
[1], &v
[2],
886 &v
[3], &v
[4], &v
[5]);
887 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
888 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
889 return ((v
[4] << 8) | v
[5]);
892 n
= sscanf(p
, "(|||%u|)", &port
);
893 if (n
== 1 && port
< 65536)
897 n
= sscanf(p
, " |1|%u.%u.%u.%u|%u|", &v
[0], &v
[1], &v
[2],
899 if (n
== 5 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
900 v
[3] < 256 && port
< 65536)
902 n
= sscanf(p
, " |2|%*[a-fA-F0-9:]|%u|", &port
);
903 if (n
== 1 && port
< 65536)
914 pick_proxy_port(void)
916 /* Random should be good enough for avoiding port collisions. */
917 return (IPPORT_HIFIRSTAUTO
+ (arc4random() %
918 (IPPORT_HILASTAUTO
- IPPORT_HIFIRSTAUTO
)));
922 proxy_reply(int cmd
, struct sockaddr
*sa
, u_int16_t port
)
928 r
= snprintf(linebuf
, sizeof linebuf
,
929 "PORT %s,%u,%u\r\n", sock_ntop(sa
), port
/ 256,
933 r
= snprintf(linebuf
, sizeof linebuf
,
934 "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa
),
935 port
/ 256, port
% 256);
938 if (sa
->sa_family
== AF_INET
)
939 r
= snprintf(linebuf
, sizeof linebuf
,
940 "EPRT |1|%s|%u|\r\n", sock_ntop(sa
), port
);
941 else if (sa
->sa_family
== AF_INET6
)
942 r
= snprintf(linebuf
, sizeof linebuf
,
943 "EPRT |2|%s|%u|\r\n", sock_ntop(sa
), port
);
946 r
= snprintf(linebuf
, sizeof linebuf
,
947 "229 Entering Extended Passive Mode (|||%u|)\r\n", port
);
951 if (r
< 0 || r
>= sizeof linebuf
) {
952 logmsg(LOG_ERR
, "proxy_reply failed: %d", r
);
959 if (cmd
== CMD_PORT
|| cmd
== CMD_PASV
) {
960 /* Replace dots in IP address with commas. */
961 for (i
= 0; i
< linelen
; i
++)
962 if (linebuf
[i
] == '.')
968 server_parse(struct session
*s
)
970 if (s
->cmd
== CMD_NONE
|| linelen
< 4 || linebuf
[0] != '2')
973 if ((s
->cmd
== CMD_PASV
&& strncmp("227 ", linebuf
, 4) == 0) ||
974 (s
->cmd
== CMD_EPSV
&& strncmp("229 ", linebuf
, 4) == 0))
975 return (allow_data_connection(s
));
985 allow_data_connection(struct session
*s
)
987 struct sockaddr
*client_sa
, *orig_sa
, *proxy_sa
, *server_sa
;
990 if (s
->cmd
== CMD_NONE
|| linelen
< 4 || linebuf
[0] != '2')
994 * The pf rules below do quite some NAT rewriting, to keep up
995 * appearances. Points to keep in mind:
996 * 1) The client must think it's talking to the real server,
997 * for both control and data connections. Transparently.
998 * 2) The server must think that the proxy is the client.
999 * 3) Source and destination ports are rewritten to minimize
1000 * port collisions, to aid security (some systems pick weak
1001 * ports) or to satisfy RFC requirements (source port 20).
1004 /* Cast this once, to make code below it more readable. */
1005 client_sa
= sstosa(&s
->client_ss
);
1006 server_sa
= sstosa(&s
->server_ss
);
1007 proxy_sa
= sstosa(&s
->proxy_ss
);
1009 /* Fixed server: data connections must appear to come
1010 from / go to the original server, not the fixed one. */
1011 orig_sa
= sstosa(&s
->orig_server_ss
);
1013 /* Server not fixed: orig_server == server. */
1014 orig_sa
= sstosa(&s
->server_ss
);
1016 /* Passive modes. */
1017 if (s
->cmd
== CMD_PASV
|| s
->cmd
== CMD_EPSV
) {
1018 s
->port
= parse_port(s
->cmd
);
1019 if (s
->port
< MIN_PORT
) {
1020 logmsg(LOG_CRIT
, "#%d bad port in '%s'", s
->id
,
1024 s
->proxy_port
= pick_proxy_port();
1025 logmsg(LOG_INFO
, "#%d passive: client to server port %d"
1026 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1028 if (prepare_commit(s
->id
) == -1)
1032 proxy_reply(s
->cmd
, orig_sa
, s
->proxy_port
);
1033 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
1035 /* rdr from $client to $orig_server port $proxy_port -> $server
1037 if (add_rdr(s
->id
, client_sa
, orig_sa
, s
->proxy_port
,
1038 server_sa
, s
->port
) == -1)
1041 /* nat from $client to $server port $port -> $proxy */
1042 if (add_nat(s
->id
, client_sa
, server_sa
, s
->port
, proxy_sa
,
1043 PF_NAT_PROXY_PORT_LOW
, PF_NAT_PROXY_PORT_HIGH
) == -1)
1046 /* pass in from $client to $server port $port */
1047 if (add_filter(s
->id
, PF_IN
, client_sa
, server_sa
,
1051 /* pass out from $proxy to $server port $port */
1052 if (add_filter(s
->id
, PF_OUT
, proxy_sa
, server_sa
,
1058 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
) {
1059 logmsg(LOG_INFO
, "#%d active: server to client port %d"
1060 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1062 if (prepare_commit(s
->id
) == -1)
1066 /* rdr from $server to $proxy port $proxy_port -> $client port
1068 if (add_rdr(s
->id
, server_sa
, proxy_sa
, s
->proxy_port
,
1069 client_sa
, s
->port
) == -1)
1072 /* nat from $server to $client port $port -> $orig_server port
1074 if (rfc_mode
&& s
->cmd
== CMD_PORT
) {
1075 /* Rewrite sourceport to RFC mandated 20. */
1076 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1077 orig_sa
, 20, 20) == -1)
1080 /* Let pf pick a source port from the standard range. */
1081 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1082 orig_sa
, PF_NAT_PROXY_PORT_LOW
,
1083 PF_NAT_PROXY_PORT_HIGH
) == -1)
1087 /* pass in from $server to $client port $port */
1088 if (add_filter(s
->id
, PF_IN
, server_sa
, client_sa
, s
->port
) ==
1092 /* pass out from $orig_server to $client port $port */
1093 if (add_filter(s
->id
, PF_OUT
, orig_sa
, client_sa
, s
->port
) ==
1098 /* Commit rules if they were prepared. */
1099 if (prepared
&& (do_commit() == -1)) {
1102 /* One more try if busy. */
1104 if (do_commit() == -1)
1115 logmsg(LOG_CRIT
, "#%d pf operation failed: %s", s
->id
, strerror(errno
));
1122 server_read(struct session
*s
)
1124 size_t buf_avail
, bread
, bwritten
;
1128 buf_avail
= sizeof s
->sbuf
- s
->sbuf_valid
;
1129 bread
= read(s
->server_fd
, s
->sbuf
+ s
->sbuf_valid
, buf_avail
);
1130 s
->sbuf_valid
+= bread
;
1132 while ((n
= get_line(s
->sbuf
, &s
->sbuf_valid
)) > 0) {
1133 logmsg(LOG_DEBUG
, "#%d server: %s", s
->id
, linebuf
);
1134 if (!server_parse(s
)) {
1138 bwritten
= write(s
->client_fd
, linebuf
, linelen
);
1139 if (bwritten
== -1) {
1140 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
1143 } else if (bwritten
< linelen
) {
1144 EV_SET(&changes
[nchanges
++], s
->server_fd
,
1145 EVFILT_READ
, EV_DISABLE
, 0, 0, s
);
1146 EV_SET(&changes
[nchanges
++], s
->client_fd
,
1147 EVFILT_WRITE
, EV_ADD
, 0, 0, s
);
1148 buffer_data(s
, &s
->client
, linebuf
+ bwritten
,
1149 linelen
- bwritten
);
1155 logmsg(LOG_ERR
, "#%d server reply too long or not"
1160 } while (bread
== buf_avail
);
1164 server_write(struct session
*s
)
1168 written
= write(s
->server_fd
, s
->server
.buffer
+ s
->server
.buffer_offset
,
1169 s
->server
.buffer_size
- s
->server
.buffer_offset
);
1170 if (written
== -1) {
1171 logmsg(LOG_ERR
, "#%d write failed", s
->id
);
1173 } else if (written
== (s
->server
.buffer_size
- s
->server
.buffer_offset
)) {
1174 free(s
->server
.buffer
);
1175 s
->server
.buffer
= NULL
;
1176 s
->server
.buffer_size
= 0;
1177 s
->server
.buffer_offset
= 0;
1178 EV_SET(&changes
[nchanges
++], s
->client_fd
,
1179 EVFILT_READ
, EV_ENABLE
, 0, 0, s
);
1181 s
->server
.buffer_offset
+= written
;
1186 sock_ntop(struct sockaddr
*sa
)
1190 /* Cycle to next buffer. */
1191 n
= (n
+ 1) % NTOP_BUFS
;
1192 ntop_buf
[n
][0] = '\0';
1194 if (sa
->sa_family
== AF_INET
) {
1195 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1197 return (inet_ntop(AF_INET
, &sin
->sin_addr
, ntop_buf
[n
],
1198 sizeof ntop_buf
[0]));
1201 if (sa
->sa_family
== AF_INET6
) {
1202 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
1204 return (inet_ntop(AF_INET6
, &sin6
->sin6_addr
, ntop_buf
[n
],
1205 sizeof ntop_buf
[0]));
1214 fprintf(stderr
, "usage: %s [-6Adrv] [-a address] [-b address]"
1215 " [-D level] [-m maxsessions]\n [-P port]"
1216 " [-p port] [-q queue] [-R address] [-T tag] [-t timeout]\n", __progname
);