1 /* $NetBSD: ftp-proxy.c,v 1.2 2008/06/18 09:06:26 yamt Exp $ */
2 /* $OpenBSD: ftp-proxy.c,v 1.15 2007/08/15 15:18:02 camield Exp $ */
5 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/queue.h>
21 #include <sys/types.h>
23 #include <sys/resource.h>
24 #include <sys/socket.h>
27 #include <net/pfvar.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
48 #if defined(__NetBSD__) && defined(WITH_IPF)
50 #endif /* __NetBSD__ && WITH_IPF */
52 #define CONNECT_TIMEOUT 30
55 #define MAX_LOGLINE 300
57 #define TCP_BACKLOG 10
59 #define CHROOT_DIR "/var/chroot/ftp-proxy"
60 #define NOPRIV_USER "_proxy"
62 /* pfctl standard NAT range. */
63 #define PF_NAT_PROXY_PORT_LOW 50001
64 #define PF_NAT_PROXY_PORT_HIGH 65535
67 #define sstosa(ss) ((struct sockaddr *)(ss))
70 #ifndef IPPORT_HIFIRSTAUTO
71 #define IPPORT_HIFIRSTAUTO IPPORT_ANONMIN
72 #endif /* IPPORT_HIFIRSTAUTO */
74 #ifndef IPPORT_HILASTAUTO
75 #define IPPORT_HILASTAUTO IPPORT_ANONMAX
76 #endif /* IPPORT_HILASTAUTO */
78 enum { CMD_NONE
= 0, CMD_PORT
, CMD_EPRT
, CMD_PASV
, CMD_EPSV
};
82 struct sockaddr_storage client_ss
;
83 struct sockaddr_storage proxy_ss
;
84 struct sockaddr_storage server_ss
;
85 struct sockaddr_storage orig_server_ss
;
86 struct bufferevent
*client_bufev
;
87 struct bufferevent
*server_bufev
;
97 LIST_ENTRY(session
) entry
;
100 LIST_HEAD(, session
) sessions
= LIST_HEAD_INITIALIZER(sessions
);
102 void client_error(struct bufferevent
*, short, void *);
103 int client_parse(struct session
*s
);
104 int client_parse_anon(struct session
*s
);
105 int client_parse_cmd(struct session
*s
);
106 void client_read(struct bufferevent
*, void *);
107 int drop_privs(void);
108 void end_session(struct session
*);
109 int exit_daemon(void);
110 int get_line(char *, size_t *);
111 void handle_connection(const int, short, void *);
112 void handle_signal(int, short, void *);
113 struct session
* init_session(void);
114 void logmsg(int, const char *, ...);
115 u_int16_t
parse_port(int);
116 u_int16_t
pick_proxy_port(void);
117 void proxy_reply(int, struct sockaddr
*, u_int16_t
);
118 void server_error(struct bufferevent
*, short, void *);
119 int server_parse(struct session
*s
);
120 int allow_data_connection(struct session
*s
);
121 void server_read(struct bufferevent
*, void *);
122 const char *sock_ntop(struct sockaddr
*);
125 char linebuf
[MAX_LINE
+ 1];
128 char ntop_buf
[NTOP_BUFS
][INET6_ADDRSTRLEN
];
130 struct sockaddr_storage fixed_server_ss
, fixed_proxy_ss
;
131 char *fixed_server
, *fixed_server_port
, *fixed_proxy
, *listen_ip
, *listen_port
,
133 int anonymous_only
, daemonize
, id_count
, ipv6_mode
, loglevel
, max_sessions
,
134 rfc_mode
, session_count
, timeout
, verbose
;
135 extern char *__progname
;
137 #if defined(__NetBSD__) && defined(WITH_IPF)
139 #endif /* __NetBSD__ && WITH_IPF */
142 client_error(struct bufferevent
*bufev
, short what
, void *arg
)
144 struct session
*s
= arg
;
146 if (what
& EVBUFFER_EOF
)
147 logmsg(LOG_INFO
, "#%d client close", s
->id
);
148 else if (what
== (EVBUFFER_ERROR
| EVBUFFER_READ
))
149 logmsg(LOG_ERR
, "#%d client reset connection", s
->id
);
150 else if (what
& EVBUFFER_TIMEOUT
)
151 logmsg(LOG_ERR
, "#%d client timeout", s
->id
);
152 else if (what
& EVBUFFER_WRITE
)
153 logmsg(LOG_ERR
, "#%d client write error: %d", s
->id
, what
);
155 logmsg(LOG_ERR
, "#%d abnormal client error: %d", s
->id
, what
);
161 client_parse(struct session
*s
)
163 /* Reset any previous command. */
167 /* Commands we are looking for are at least 4 chars long. */
171 if (linebuf
[0] == 'P' || linebuf
[0] == 'p' ||
172 linebuf
[0] == 'E' || linebuf
[0] == 'e') {
173 if (!client_parse_cmd(s
))
177 * Allow active mode connections immediately, instead of
178 * waiting for a positive reply from the server. Some
179 * rare servers/proxies try to probe or setup the data
180 * connection before an actual transfer request.
182 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
)
183 return (allow_data_connection(s
));
186 if (anonymous_only
&& (linebuf
[0] == 'U' || linebuf
[0] == 'u'))
187 return (client_parse_anon(s
));
193 client_parse_anon(struct session
*s
)
195 if (strcasecmp("USER ftp\r\n", linebuf
) != 0 &&
196 strcasecmp("USER anonymous\r\n", linebuf
) != 0) {
197 snprintf(linebuf
, sizeof linebuf
,
198 "500 Only anonymous FTP allowed\r\n");
199 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
201 /* Talk back to the client ourself. */
202 linelen
= strlen(linebuf
);
203 bufferevent_write(s
->client_bufev
, linebuf
, linelen
);
205 /* Clear buffer so it's not sent to the server. */
214 client_parse_cmd(struct session
*s
)
216 if (strncasecmp("PASV", linebuf
, 4) == 0)
218 else if (strncasecmp("PORT ", linebuf
, 5) == 0)
220 else if (strncasecmp("EPSV", linebuf
, 4) == 0)
222 else if (strncasecmp("EPRT ", linebuf
, 5) == 0)
227 if (ipv6_mode
&& (s
->cmd
== CMD_PASV
|| s
->cmd
== CMD_PORT
)) {
228 logmsg(LOG_CRIT
, "PASV and PORT not allowed with IPv6");
232 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
) {
233 s
->port
= parse_port(s
->cmd
);
234 if (s
->port
< MIN_PORT
) {
235 logmsg(LOG_CRIT
, "#%d bad port in '%s'", s
->id
,
239 s
->proxy_port
= pick_proxy_port();
240 proxy_reply(s
->cmd
, sstosa(&s
->proxy_ss
), s
->proxy_port
);
241 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
248 client_read(struct bufferevent
*bufev
, void *arg
)
250 struct session
*s
= arg
;
251 size_t buf_avail
, nread
;
255 buf_avail
= sizeof s
->cbuf
- s
->cbuf_valid
;
256 nread
= bufferevent_read(bufev
, s
->cbuf
+ s
->cbuf_valid
,
258 s
->cbuf_valid
+= nread
;
260 while ((n
= get_line(s
->cbuf
, &s
->cbuf_valid
)) > 0) {
261 logmsg(LOG_DEBUG
, "#%d client: %s", s
->id
, linebuf
);
262 if (!client_parse(s
)) {
266 bufferevent_write(s
->server_bufev
, linebuf
, linelen
);
270 logmsg(LOG_ERR
, "#%d client command too long or not"
275 } while (nread
== buf_avail
);
283 pw
= getpwnam(NOPRIV_USER
);
289 if (chroot(CHROOT_DIR
) != 0 || chdir("/") != 0 ||
290 setgroups(1, &pw
->pw_gid
) != 0 ||
291 setgid(pw
->pw_gid
) != 0 ||
292 setuid(pw
->pw_uid
) != 0)
295 if (chroot(CHROOT_DIR
) != 0 || chdir("/") != 0 ||
296 setgroups(1, &pw
->pw_gid
) != 0 ||
297 setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) != 0 ||
298 setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) != 0)
300 #endif /* !__NetBSD__ */
306 end_session(struct session
*s
)
310 logmsg(LOG_INFO
, "#%d ending session", s
->id
);
312 if (s
->client_fd
!= -1)
314 if (s
->server_fd
!= -1)
318 bufferevent_free(s
->client_bufev
);
320 bufferevent_free(s
->server_bufev
);
322 /* Remove rulesets by commiting empty ones. */
324 if (prepare_commit(s
->id
) == -1)
326 else if (do_commit() == -1) {
331 logmsg(LOG_ERR
, "#%d pf rule removal failed: %s", s
->id
,
334 LIST_REMOVE(s
, entry
);
342 struct session
*s
, *next
;
344 for (s
= LIST_FIRST(&sessions
); s
!= LIST_END(&sessions
); s
= next
) {
345 next
= LIST_NEXT(s
, entry
);
359 get_line(char *buf
, size_t *valid
)
363 if (*valid
> MAX_LINE
)
366 /* Copy to linebuf while searching for a newline. */
367 for (i
= 0; i
< *valid
; i
++) {
376 /* No newline found. */
385 linebuf
[linelen
] = '\0';
388 /* Move leftovers to the start. */
390 bcopy(buf
+ linelen
, buf
, *valid
);
392 return ((int)linelen
);
396 handle_connection(const int listen_fd
, short event
, void *ev
)
398 struct sockaddr_storage tmp_ss
;
399 struct sockaddr
*client_sa
, *server_sa
, *fixed_server_sa
;
400 struct sockaddr
*client_to_proxy_sa
, *proxy_to_server_sa
;
403 int client_fd
, fc
, on
;
406 * We _must_ accept the connection, otherwise libevent will keep
407 * coming back, and we will chew up all CPU.
409 client_sa
= sstosa(&tmp_ss
);
410 len
= sizeof(struct sockaddr_storage
);
411 if ((client_fd
= accept(listen_fd
, client_sa
, &len
)) < 0) {
412 logmsg(LOG_CRIT
, "accept failed: %s", strerror(errno
));
416 /* Refuse connection if the maximum is reached. */
417 if (session_count
>= max_sessions
) {
418 logmsg(LOG_ERR
, "client limit (%d) reached, refusing "
419 "connection from %s", max_sessions
, sock_ntop(client_sa
));
424 /* Allocate session and copy back the info from the accept(). */
427 logmsg(LOG_CRIT
, "init_session failed");
431 s
->client_fd
= client_fd
;
432 memcpy(sstosa(&s
->client_ss
), client_sa
, client_sa
->sa_len
);
434 /* Cast it once, and be done with it. */
435 client_sa
= sstosa(&s
->client_ss
);
436 server_sa
= sstosa(&s
->server_ss
);
437 client_to_proxy_sa
= sstosa(&tmp_ss
);
438 proxy_to_server_sa
= sstosa(&s
->proxy_ss
);
439 fixed_server_sa
= sstosa(&fixed_server_ss
);
441 /* Log id/client early to ease debugging. */
442 logmsg(LOG_DEBUG
, "#%d accepted connection from %s", s
->id
,
443 sock_ntop(client_sa
));
446 * Find out the real server and port that the client wanted.
448 len
= sizeof(struct sockaddr_storage
);
449 if ((getsockname(s
->client_fd
, client_to_proxy_sa
, &len
)) < 0) {
450 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
454 if (server_lookup(client_sa
, client_to_proxy_sa
, server_sa
) != 0) {
455 logmsg(LOG_CRIT
, "#%d server lookup failed (no rdr?)", s
->id
);
459 memcpy(sstosa(&s
->orig_server_ss
), server_sa
,
461 memcpy(server_sa
, fixed_server_sa
, fixed_server_sa
->sa_len
);
464 /* XXX: check we are not connecting to ourself. */
467 * Setup socket and connect to server.
469 if ((s
->server_fd
= socket(server_sa
->sa_family
, SOCK_STREAM
,
471 logmsg(LOG_CRIT
, "#%d server socket failed: %s", s
->id
,
475 if (fixed_proxy
&& bind(s
->server_fd
, sstosa(&fixed_proxy_ss
),
476 fixed_proxy_ss
.ss_len
) != 0) {
477 logmsg(LOG_CRIT
, "#%d cannot bind fixed proxy address: %s",
478 s
->id
, strerror(errno
));
482 /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */
483 if ((fc
= fcntl(s
->server_fd
, F_GETFL
)) == -1 ||
484 fcntl(s
->server_fd
, F_SETFL
, fc
| O_NONBLOCK
) == -1) {
485 logmsg(LOG_CRIT
, "#%d cannot mark socket non-blocking: %s",
486 s
->id
, strerror(errno
));
489 if (connect(s
->server_fd
, server_sa
, server_sa
->sa_len
) < 0 &&
490 errno
!= EINPROGRESS
) {
491 logmsg(LOG_CRIT
, "#%d proxy cannot connect to server %s: %s",
492 s
->id
, sock_ntop(server_sa
), strerror(errno
));
496 len
= sizeof(struct sockaddr_storage
);
497 if ((getsockname(s
->server_fd
, proxy_to_server_sa
, &len
)) < 0) {
498 logmsg(LOG_CRIT
, "#%d getsockname failed: %s", s
->id
,
503 logmsg(LOG_INFO
, "#%d FTP session %d/%d started: client %s to server "
504 "%s via proxy %s ", s
->id
, session_count
, max_sessions
,
505 sock_ntop(client_sa
), sock_ntop(server_sa
),
506 sock_ntop(proxy_to_server_sa
));
508 /* Keepalive is nice, but don't care if it fails. */
510 setsockopt(s
->client_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
512 setsockopt(s
->server_fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&on
,
516 * Setup buffered events.
518 s
->client_bufev
= bufferevent_new(s
->client_fd
, &client_read
, NULL
,
520 if (s
->client_bufev
== NULL
) {
521 logmsg(LOG_CRIT
, "#%d bufferevent_new client failed", s
->id
);
524 bufferevent_settimeout(s
->client_bufev
, timeout
, 0);
525 bufferevent_enable(s
->client_bufev
, EV_READ
| EV_TIMEOUT
);
527 s
->server_bufev
= bufferevent_new(s
->server_fd
, &server_read
, NULL
,
529 if (s
->server_bufev
== NULL
) {
530 logmsg(LOG_CRIT
, "#%d bufferevent_new server failed", s
->id
);
533 bufferevent_settimeout(s
->server_bufev
, CONNECT_TIMEOUT
, 0);
534 bufferevent_enable(s
->server_bufev
, EV_READ
| EV_TIMEOUT
);
543 handle_signal(int sig
, short event
, void *arg
)
546 * Signal handler rules don't apply, libevent decouples for us.
549 logmsg(LOG_ERR
, "%s exiting on signal %d", __progname
, sig
);
560 s
= calloc(1, sizeof(struct session
));
571 s
->client_bufev
= NULL
;
572 s
->server_bufev
= NULL
;
576 LIST_INSERT_HEAD(&sessions
, s
, entry
);
583 logmsg(int pri
, const char *message
, ...)
590 va_start(ap
, message
);
593 /* syslog does its own vissing. */
594 vsyslog(pri
, message
, ap
);
596 char buf
[MAX_LOGLINE
];
597 char visbuf
[2 * MAX_LOGLINE
];
599 /* We don't care about truncation. */
600 vsnprintf(buf
, sizeof buf
, message
, ap
);
602 size_t len
= strlen(buf
);
603 if (len
> sizeof visbuf
) {
606 strvisx(visbuf
, buf
, len
, VIS_CSTYLE
| VIS_NL
);
608 strnvis(visbuf
, buf
, sizeof visbuf
, VIS_CSTYLE
| VIS_NL
);
609 #endif /* !__NetBSD__ */
610 fprintf(stderr
, "%s\n", visbuf
);
617 main(int argc
, char *argv
[])
620 struct addrinfo hints
, *res
;
621 struct event ev
, ev_sighup
, ev_sigint
, ev_sigterm
;
622 int ch
, error
, listenfd
, on
;
623 const char *errstr
= NULL
; /* XXX gcc */
630 fixed_server_port
= "21";
633 listen_port
= "8021";
634 loglevel
= LOG_NOTICE
;
642 /* Other initialization. */
646 #if defined(__NetBSD__) && defined(WITH_IPF)
647 while ((ch
= getopt(argc
, argv
, "6Aa:b:D:di:m:P:p:q:R:rT:t:v")) != -1) {
649 while ((ch
= getopt(argc
, argv
, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) {
650 #endif /* __NetBSD__ && WITH_IPF */
659 fixed_proxy
= optarg
;
665 loglevel
= strtonum(optarg
, LOG_EMERG
, LOG_DEBUG
,
668 errx(1, "loglevel %s", errstr
);
673 #if defined(__NetBSD__) && defined(WITH_IPF)
678 #endif /* __NetBSD__ && WITH_IPF */
680 max_sessions
= strtonum(optarg
, 1, 500, &errstr
);
682 errx(1, "max sessions %s", errstr
);
685 fixed_server_port
= optarg
;
688 listen_port
= optarg
;
691 if (strlen(optarg
) >= PF_QNAME_SIZE
)
692 errx(1, "queuename too long");
696 fixed_server
= optarg
;
702 if (strlen(optarg
) >= PF_TAG_NAME_SIZE
)
703 errx(1, "tagname too long");
707 timeout
= strtonum(optarg
, 0, 86400, &errstr
);
709 errx(1, "timeout %s", errstr
);
721 if (listen_ip
== NULL
)
722 listen_ip
= ipv6_mode
? "::1" : "127.0.0.1";
724 /* Check for root to save the user from cryptic failure messages. */
726 errx(1, "needs to start as root");
728 /* Raise max. open files limit to satisfy max. sessions. */
729 rlp
.rlim_cur
= rlp
.rlim_max
= (2 * max_sessions
) + 10;
730 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
734 memset(&hints
, 0, sizeof hints
);
735 hints
.ai_flags
= AI_NUMERICHOST
;
736 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
737 hints
.ai_socktype
= SOCK_STREAM
;
738 error
= getaddrinfo(fixed_proxy
, NULL
, &hints
, &res
);
740 errx(1, "getaddrinfo fixed proxy address failed: %s",
741 gai_strerror(error
));
742 memcpy(&fixed_proxy_ss
, res
->ai_addr
, res
->ai_addrlen
);
743 logmsg(LOG_INFO
, "using %s to connect to servers",
744 sock_ntop(sstosa(&fixed_proxy_ss
)));
749 memset(&hints
, 0, sizeof hints
);
750 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
751 hints
.ai_socktype
= SOCK_STREAM
;
752 error
= getaddrinfo(fixed_server
, fixed_server_port
, &hints
,
755 errx(1, "getaddrinfo fixed server address failed: %s",
756 gai_strerror(error
));
757 memcpy(&fixed_server_ss
, res
->ai_addr
, res
->ai_addrlen
);
758 logmsg(LOG_INFO
, "using fixed server %s",
759 sock_ntop(sstosa(&fixed_server_ss
)));
763 /* Setup listener. */
764 memset(&hints
, 0, sizeof hints
);
765 hints
.ai_flags
= AI_NUMERICHOST
| AI_PASSIVE
;
766 hints
.ai_family
= ipv6_mode
? AF_INET6
: AF_INET
;
767 hints
.ai_socktype
= SOCK_STREAM
;
768 error
= getaddrinfo(listen_ip
, listen_port
, &hints
, &res
);
770 errx(1, "getaddrinfo listen address failed: %s",
771 gai_strerror(error
));
772 if ((listenfd
= socket(res
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
)) == -1)
773 errx(1, "socket failed");
775 if (setsockopt(listenfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
,
777 err(1, "setsockopt failed");
778 if (bind(listenfd
, (struct sockaddr
*)res
->ai_addr
,
779 (socklen_t
)res
->ai_addrlen
) != 0)
780 err(1, "bind failed");
781 if (listen(listenfd
, TCP_BACKLOG
) != 0)
782 err(1, "listen failed");
786 init_filter(qname
, tagname
, verbose
);
789 if (daemon(0, 0) == -1)
790 err(1, "cannot daemonize");
791 openlog(__progname
, LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
794 /* Use logmsg for output from here on. */
797 logmsg(LOG_ERR
, "cannot drop privileges: %s", strerror(errno
));
803 /* Setup signal handler. */
804 signal(SIGPIPE
, SIG_IGN
);
805 signal_set(&ev_sighup
, SIGHUP
, handle_signal
, NULL
);
806 signal_set(&ev_sigint
, SIGINT
, handle_signal
, NULL
);
807 signal_set(&ev_sigterm
, SIGTERM
, handle_signal
, NULL
);
808 signal_add(&ev_sighup
, NULL
);
809 signal_add(&ev_sigint
, NULL
);
810 signal_add(&ev_sigterm
, NULL
);
812 event_set(&ev
, listenfd
, EV_READ
| EV_PERSIST
, handle_connection
, &ev
);
813 event_add(&ev
, NULL
);
815 logmsg(LOG_NOTICE
, "listening on %s port %s", listen_ip
, listen_port
);
820 logmsg(LOG_ERR
, "event_dispatch error: %s", strerror(errno
));
830 unsigned int port
, v
[6];
834 /* Find the last space or left-parenthesis. */
835 for (p
= linebuf
+ linelen
; p
> linebuf
; p
--)
836 if (*p
== ' ' || *p
== '(')
843 n
= sscanf(p
, " %u,%u,%u,%u,%u,%u", &v
[0], &v
[1], &v
[2],
844 &v
[3], &v
[4], &v
[5]);
845 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
846 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
847 return ((v
[4] << 8) | v
[5]);
850 n
= sscanf(p
, "(%u,%u,%u,%u,%u,%u)", &v
[0], &v
[1], &v
[2],
851 &v
[3], &v
[4], &v
[5]);
852 if (n
== 6 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
853 v
[3] < 256 && v
[4] < 256 && v
[5] < 256)
854 return ((v
[4] << 8) | v
[5]);
857 n
= sscanf(p
, "(|||%u|)", &port
);
858 if (n
== 1 && port
< 65536)
862 n
= sscanf(p
, " |1|%u.%u.%u.%u|%u|", &v
[0], &v
[1], &v
[2],
864 if (n
== 5 && v
[0] < 256 && v
[1] < 256 && v
[2] < 256 &&
865 v
[3] < 256 && port
< 65536)
867 n
= sscanf(p
, " |2|%*[a-fA-F0-9:]|%u|", &port
);
868 if (n
== 1 && port
< 65536)
879 pick_proxy_port(void)
881 /* Random should be good enough for avoiding port collisions. */
882 return (IPPORT_HIFIRSTAUTO
+ (arc4random() %
883 (IPPORT_HILASTAUTO
- IPPORT_HIFIRSTAUTO
)));
887 proxy_reply(int cmd
, struct sockaddr
*sa
, u_int16_t port
)
893 r
= snprintf(linebuf
, sizeof linebuf
,
894 "PORT %s,%u,%u\r\n", sock_ntop(sa
), port
/ 256,
898 r
= snprintf(linebuf
, sizeof linebuf
,
899 "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa
),
900 port
/ 256, port
% 256);
903 if (sa
->sa_family
== AF_INET
)
904 r
= snprintf(linebuf
, sizeof linebuf
,
905 "EPRT |1|%s|%u|\r\n", sock_ntop(sa
), port
);
906 else if (sa
->sa_family
== AF_INET6
)
907 r
= snprintf(linebuf
, sizeof linebuf
,
908 "EPRT |2|%s|%u|\r\n", sock_ntop(sa
), port
);
911 r
= snprintf(linebuf
, sizeof linebuf
,
912 "229 Entering Extended Passive Mode (|||%u|)\r\n", port
);
916 if (r
< 0 || r
>= sizeof linebuf
) {
917 logmsg(LOG_ERR
, "proxy_reply failed: %d", r
);
924 if (cmd
== CMD_PORT
|| cmd
== CMD_PASV
) {
925 /* Replace dots in IP address with commas. */
926 for (i
= 0; i
< linelen
; i
++)
927 if (linebuf
[i
] == '.')
933 server_error(struct bufferevent
*bufev
, short what
, void *arg
)
935 struct session
*s
= arg
;
937 if (what
& EVBUFFER_EOF
)
938 logmsg(LOG_INFO
, "#%d server close", s
->id
);
939 else if (what
== (EVBUFFER_ERROR
| EVBUFFER_READ
))
940 logmsg(LOG_ERR
, "#%d server refused connection", s
->id
);
941 else if (what
& EVBUFFER_WRITE
)
942 logmsg(LOG_ERR
, "#%d server write error: %d", s
->id
, what
);
943 else if (what
& EVBUFFER_TIMEOUT
)
944 logmsg(LOG_NOTICE
, "#%d server timeout", s
->id
);
946 logmsg(LOG_ERR
, "#%d abnormal server error: %d", s
->id
, what
);
952 server_parse(struct session
*s
)
954 if (s
->cmd
== CMD_NONE
|| linelen
< 4 || linebuf
[0] != '2')
957 if ((s
->cmd
== CMD_PASV
&& strncmp("227 ", linebuf
, 4) == 0) ||
958 (s
->cmd
== CMD_EPSV
&& strncmp("229 ", linebuf
, 4) == 0))
959 return (allow_data_connection(s
));
969 allow_data_connection(struct session
*s
)
971 struct sockaddr
*client_sa
, *orig_sa
, *proxy_sa
, *server_sa
;
975 * The pf rules below do quite some NAT rewriting, to keep up
976 * appearances. Points to keep in mind:
977 * 1) The client must think it's talking to the real server,
978 * for both control and data connections. Transparently.
979 * 2) The server must think that the proxy is the client.
980 * 3) Source and destination ports are rewritten to minimize
981 * port collisions, to aid security (some systems pick weak
982 * ports) or to satisfy RFC requirements (source port 20).
985 /* Cast this once, to make code below it more readable. */
986 client_sa
= sstosa(&s
->client_ss
);
987 server_sa
= sstosa(&s
->server_ss
);
988 proxy_sa
= sstosa(&s
->proxy_ss
);
990 /* Fixed server: data connections must appear to come
991 from / go to the original server, not the fixed one. */
992 orig_sa
= sstosa(&s
->orig_server_ss
);
994 /* Server not fixed: orig_server == server. */
995 orig_sa
= sstosa(&s
->server_ss
);
998 if (s
->cmd
== CMD_PASV
|| s
->cmd
== CMD_EPSV
) {
999 s
->port
= parse_port(s
->cmd
);
1000 if (s
->port
< MIN_PORT
) {
1001 logmsg(LOG_CRIT
, "#%d bad port in '%s'", s
->id
,
1005 s
->proxy_port
= pick_proxy_port();
1006 logmsg(LOG_INFO
, "#%d passive: client to server port %d"
1007 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1009 if (prepare_commit(s
->id
) == -1)
1013 proxy_reply(s
->cmd
, orig_sa
, s
->proxy_port
);
1014 logmsg(LOG_DEBUG
, "#%d proxy: %s", s
->id
, linebuf
);
1016 /* rdr from $client to $orig_server port $proxy_port -> $server
1018 if (add_rdr(s
->id
, client_sa
, orig_sa
, s
->proxy_port
,
1019 server_sa
, s
->port
) == -1)
1022 /* nat from $client to $server port $port -> $proxy */
1023 if (add_nat(s
->id
, client_sa
, server_sa
, s
->port
, proxy_sa
,
1024 PF_NAT_PROXY_PORT_LOW
, PF_NAT_PROXY_PORT_HIGH
) == -1)
1027 /* pass in from $client to $server port $port */
1028 if (add_filter(s
->id
, PF_IN
, client_sa
, server_sa
,
1032 /* pass out from $proxy to $server port $port */
1033 if (add_filter(s
->id
, PF_OUT
, proxy_sa
, server_sa
,
1039 if (s
->cmd
== CMD_PORT
|| s
->cmd
== CMD_EPRT
) {
1040 logmsg(LOG_INFO
, "#%d active: server to client port %d"
1041 " via port %d", s
->id
, s
->port
, s
->proxy_port
);
1043 if (prepare_commit(s
->id
) == -1)
1047 /* rdr from $server to $proxy port $proxy_port -> $client port
1049 if (add_rdr(s
->id
, server_sa
, proxy_sa
, s
->proxy_port
,
1050 client_sa
, s
->port
) == -1)
1053 /* nat from $server to $client port $port -> $orig_server port
1055 if (rfc_mode
&& s
->cmd
== CMD_PORT
) {
1056 /* Rewrite sourceport to RFC mandated 20. */
1057 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1058 orig_sa
, 20, 20) == -1)
1061 /* Let pf pick a source port from the standard range. */
1062 if (add_nat(s
->id
, server_sa
, client_sa
, s
->port
,
1063 orig_sa
, PF_NAT_PROXY_PORT_LOW
,
1064 PF_NAT_PROXY_PORT_HIGH
) == -1)
1068 /* pass in from $server to $client port $port */
1069 if (add_filter(s
->id
, PF_IN
, server_sa
, client_sa
, s
->port
) ==
1073 /* pass out from $orig_server to $client port $port */
1074 if (add_filter(s
->id
, PF_OUT
, orig_sa
, client_sa
, s
->port
) ==
1079 /* Commit rules if they were prepared. */
1080 if (prepared
&& (do_commit() == -1)) {
1083 /* One more try if busy. */
1085 if (do_commit() == -1)
1095 logmsg(LOG_CRIT
, "#%d pf operation failed: %s", s
->id
, strerror(errno
));
1102 server_read(struct bufferevent
*bufev
, void *arg
)
1104 struct session
*s
= arg
;
1105 size_t buf_avail
, nread
;
1108 bufferevent_settimeout(bufev
, timeout
, 0);
1111 buf_avail
= sizeof s
->sbuf
- s
->sbuf_valid
;
1112 nread
= bufferevent_read(bufev
, s
->sbuf
+ s
->sbuf_valid
,
1114 s
->sbuf_valid
+= nread
;
1116 while ((n
= get_line(s
->sbuf
, &s
->sbuf_valid
)) > 0) {
1117 logmsg(LOG_DEBUG
, "#%d server: %s", s
->id
, linebuf
);
1118 if (!server_parse(s
)) {
1122 bufferevent_write(s
->client_bufev
, linebuf
, linelen
);
1126 logmsg(LOG_ERR
, "#%d server reply too long or not"
1131 } while (nread
== buf_avail
);
1135 sock_ntop(struct sockaddr
*sa
)
1139 /* Cycle to next buffer. */
1140 n
= (n
+ 1) % NTOP_BUFS
;
1141 ntop_buf
[n
][0] = '\0';
1143 if (sa
->sa_family
== AF_INET
) {
1144 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1146 return (inet_ntop(AF_INET
, &sin
->sin_addr
, ntop_buf
[n
],
1147 sizeof ntop_buf
[0]));
1150 if (sa
->sa_family
== AF_INET6
) {
1151 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
1153 return (inet_ntop(AF_INET6
, &sin6
->sin6_addr
, ntop_buf
[n
],
1154 sizeof ntop_buf
[0]));
1163 fprintf(stderr
, "usage: %s [-6Adrv] [-a address] [-b address]"
1164 " [-D level] [-m maxsessions]\n [-P port]"
1165 #if defined(__NetBSD__) && defined(WITH_IPF)
1167 #endif /* __NetBSD__ && WITH_IPF */
1168 " [-p port] [-q queue] [-R address] [-T tag] [-t timeout]\n",