1223637Sbz/*	$OpenBSD: ftp-proxy.c,v 1.19 2008/06/13 07:25:26 claudio Exp $ */
2126353Smlaier
3126353Smlaier/*
4171172Smlaier * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5126353Smlaier *
6171172Smlaier * Permission to use, copy, modify, and distribute this software for any
7171172Smlaier * purpose with or without fee is hereby granted, provided that the above
8171172Smlaier * copyright notice and this permission notice appear in all copies.
9126353Smlaier *
10171172Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11171172Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12171172Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13171172Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14171172Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15171172Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16171172Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17126353Smlaier */
18126353Smlaier
19127082Sobrien#include <sys/cdefs.h>
20127082Sobrien__FBSDID("$FreeBSD$");
21127082Sobrien
22171172Smlaier#include <sys/queue.h>
23171172Smlaier#include <sys/types.h>
24126353Smlaier#include <sys/time.h>
25171172Smlaier#include <sys/resource.h>
26126353Smlaier#include <sys/socket.h>
27126353Smlaier
28126353Smlaier#include <net/if.h>
29171172Smlaier#include <net/pfvar.h>
30126353Smlaier#include <netinet/in.h>
31126353Smlaier#include <arpa/inet.h>
32126353Smlaier
33171172Smlaier#include <err.h>
34126353Smlaier#include <errno.h>
35171172Smlaier#include <event.h>
36171172Smlaier#include <fcntl.h>
37126353Smlaier#include <netdb.h>
38126353Smlaier#include <pwd.h>
39126353Smlaier#include <signal.h>
40126353Smlaier#include <stdarg.h>
41126353Smlaier#include <stdio.h>
42126353Smlaier#include <stdlib.h>
43126353Smlaier#include <string.h>
44126353Smlaier#include <syslog.h>
45126353Smlaier#include <unistd.h>
46171172Smlaier#include <vis.h>
47126353Smlaier
48171172Smlaier#include "filter.h"
49126353Smlaier
50171172Smlaier#define CONNECT_TIMEOUT	30
51171172Smlaier#define MIN_PORT	1024
52171172Smlaier#define MAX_LINE	500
53171172Smlaier#define MAX_LOGLINE	300
54171172Smlaier#define NTOP_BUFS	3
55171172Smlaier#define TCP_BACKLOG	10
56126353Smlaier
57171172Smlaier#define CHROOT_DIR	"/var/empty"
58171172Smlaier#define NOPRIV_USER	"proxy"
59126353Smlaier
60171172Smlaier/* pfctl standard NAT range. */
61171172Smlaier#define PF_NAT_PROXY_PORT_LOW	50001
62171172Smlaier#define PF_NAT_PROXY_PORT_HIGH	65535
63126353Smlaier
64223637Sbz#ifndef LIST_END
65223637Sbz#define LIST_END(a)     NULL
66223637Sbz#endif
67223637Sbz
68223637Sbz#ifndef getrtable
69223637Sbz#define getrtable(a)    0
70223637Sbz#endif
71223637Sbz
72171172Smlaier#define	sstosa(ss)	((struct sockaddr *)(ss))
73126353Smlaier
74171172Smlaierenum { CMD_NONE = 0, CMD_PORT, CMD_EPRT, CMD_PASV, CMD_EPSV };
75126353Smlaier
76171172Smlaierstruct session {
77171172Smlaier	u_int32_t		 id;
78171172Smlaier	struct sockaddr_storage  client_ss;
79171172Smlaier	struct sockaddr_storage  proxy_ss;
80171172Smlaier	struct sockaddr_storage  server_ss;
81171172Smlaier	struct sockaddr_storage  orig_server_ss;
82171172Smlaier	struct bufferevent	*client_bufev;
83171172Smlaier	struct bufferevent	*server_bufev;
84171172Smlaier	int			 client_fd;
85171172Smlaier	int			 server_fd;
86171172Smlaier	char			 cbuf[MAX_LINE];
87171172Smlaier	size_t			 cbuf_valid;
88171172Smlaier	char			 sbuf[MAX_LINE];
89171172Smlaier	size_t			 sbuf_valid;
90171172Smlaier	int			 cmd;
91171172Smlaier	u_int16_t		 port;
92171172Smlaier	u_int16_t		 proxy_port;
93171172Smlaier	LIST_ENTRY(session)	 entry;
94171172Smlaier};
95126353Smlaier
96171172SmlaierLIST_HEAD(, session) sessions = LIST_HEAD_INITIALIZER(sessions);
97126353Smlaier
98171172Smlaiervoid	client_error(struct bufferevent *, short, void *);
99171172Smlaierint	client_parse(struct session *s);
100171172Smlaierint	client_parse_anon(struct session *s);
101171172Smlaierint	client_parse_cmd(struct session *s);
102171172Smlaiervoid	client_read(struct bufferevent *, void *);
103171172Smlaierint	drop_privs(void);
104171172Smlaiervoid	end_session(struct session *);
105223637Sbzvoid	exit_daemon(void);
106171172Smlaierint	getline(char *, size_t *);
107171172Smlaiervoid	handle_connection(const int, short, void *);
108171172Smlaiervoid	handle_signal(int, short, void *);
109171172Smlaierstruct session * init_session(void);
110171172Smlaiervoid	logmsg(int, const char *, ...);
111171172Smlaieru_int16_t parse_port(int);
112171172Smlaieru_int16_t pick_proxy_port(void);
113171172Smlaiervoid	proxy_reply(int, struct sockaddr *, u_int16_t);
114171172Smlaiervoid	server_error(struct bufferevent *, short, void *);
115171172Smlaierint	server_parse(struct session *s);
116223637Sbzint	allow_data_connection(struct session *s);
117171172Smlaiervoid	server_read(struct bufferevent *, void *);
118171172Smlaierconst char *sock_ntop(struct sockaddr *);
119171172Smlaiervoid	usage(void);
120126353Smlaier
121171172Smlaierchar linebuf[MAX_LINE + 1];
122171172Smlaiersize_t linelen;
123126353Smlaier
124171172Smlaierchar ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
125126353Smlaier
126171172Smlaierstruct sockaddr_storage fixed_server_ss, fixed_proxy_ss;
127223637Sbzconst char *fixed_server, *fixed_server_port, *fixed_proxy, *listen_ip, *listen_port,
128223637Sbz    *qname, *tagname;
129171172Smlaierint anonymous_only, daemonize, id_count, ipv6_mode, loglevel, max_sessions,
130171172Smlaier    rfc_mode, session_count, timeout, verbose;
131126353Smlaierextern char *__progname;
132126353Smlaier
133171172Smlaiervoid
134223637Sbzclient_error(struct bufferevent *bufev __unused, short what, void *arg)
135171172Smlaier{
136171172Smlaier	struct session *s = arg;
137126353Smlaier
138171172Smlaier	if (what & EVBUFFER_EOF)
139171172Smlaier		logmsg(LOG_INFO, "#%d client close", s->id);
140171172Smlaier	else if (what == (EVBUFFER_ERROR | EVBUFFER_READ))
141171172Smlaier		logmsg(LOG_ERR, "#%d client reset connection", s->id);
142171172Smlaier	else if (what & EVBUFFER_TIMEOUT)
143171172Smlaier		logmsg(LOG_ERR, "#%d client timeout", s->id);
144171172Smlaier	else if (what & EVBUFFER_WRITE)
145171172Smlaier		logmsg(LOG_ERR, "#%d client write error: %d", s->id, what);
146171172Smlaier	else
147171172Smlaier		logmsg(LOG_ERR, "#%d abnormal client error: %d", s->id, what);
148126353Smlaier
149171172Smlaier	end_session(s);
150126353Smlaier}
151126353Smlaier
152171172Smlaierint
153171172Smlaierclient_parse(struct session *s)
154126353Smlaier{
155171172Smlaier	/* Reset any previous command. */
156171172Smlaier	s->cmd = CMD_NONE;
157171172Smlaier	s->port = 0;
158171172Smlaier
159171172Smlaier	/* Commands we are looking for are at least 4 chars long. */
160171172Smlaier	if (linelen < 4)
161171172Smlaier		return (1);
162171172Smlaier
163171172Smlaier	if (linebuf[0] == 'P' || linebuf[0] == 'p' ||
164223637Sbz	    linebuf[0] == 'E' || linebuf[0] == 'e') {
165223637Sbz		if (!client_parse_cmd(s))
166223637Sbz			return (0);
167223637Sbz
168223637Sbz		/*
169223637Sbz		 * Allow active mode connections immediately, instead of
170223637Sbz		 * waiting for a positive reply from the server.  Some
171223637Sbz		 * rare servers/proxies try to probe or setup the data
172223637Sbz		 * connection before an actual transfer request.
173223637Sbz		 */
174223637Sbz		if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT)
175223637Sbz			return (allow_data_connection(s));
176223637Sbz	}
177171172Smlaier
178171172Smlaier	if (anonymous_only && (linebuf[0] == 'U' || linebuf[0] == 'u'))
179171172Smlaier		return (client_parse_anon(s));
180171172Smlaier
181171172Smlaier	return (1);
182126353Smlaier}
183126353Smlaier
184171172Smlaierint
185171172Smlaierclient_parse_anon(struct session *s)
186126353Smlaier{
187171172Smlaier	if (strcasecmp("USER ftp\r\n", linebuf) != 0 &&
188171172Smlaier	    strcasecmp("USER anonymous\r\n", linebuf) != 0) {
189171172Smlaier		snprintf(linebuf, sizeof linebuf,
190171172Smlaier		    "500 Only anonymous FTP allowed\r\n");
191171172Smlaier		logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
192171172Smlaier
193171172Smlaier		/* Talk back to the client ourself. */
194171172Smlaier		linelen = strlen(linebuf);
195171172Smlaier		bufferevent_write(s->client_bufev, linebuf, linelen);
196171172Smlaier
197171172Smlaier		/* Clear buffer so it's not sent to the server. */
198171172Smlaier		linebuf[0] = '\0';
199171172Smlaier		linelen = 0;
200126353Smlaier	}
201171172Smlaier
202171172Smlaier	return (1);
203126353Smlaier}
204126353Smlaier
205171172Smlaierint
206171172Smlaierclient_parse_cmd(struct session *s)
207126353Smlaier{
208171172Smlaier	if (strncasecmp("PASV", linebuf, 4) == 0)
209171172Smlaier		s->cmd = CMD_PASV;
210171172Smlaier	else if (strncasecmp("PORT ", linebuf, 5) == 0)
211171172Smlaier		s->cmd = CMD_PORT;
212171172Smlaier	else if (strncasecmp("EPSV", linebuf, 4) == 0)
213171172Smlaier		s->cmd = CMD_EPSV;
214171172Smlaier	else if (strncasecmp("EPRT ", linebuf, 5) == 0)
215171172Smlaier		s->cmd = CMD_EPRT;
216171172Smlaier	else
217171172Smlaier		return (1);
218126353Smlaier
219171172Smlaier	if (ipv6_mode && (s->cmd == CMD_PASV || s->cmd == CMD_PORT)) {
220171172Smlaier		logmsg(LOG_CRIT, "PASV and PORT not allowed with IPv6");
221171172Smlaier		return (0);
222126353Smlaier	}
223126353Smlaier
224171172Smlaier	if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) {
225171172Smlaier		s->port = parse_port(s->cmd);
226171172Smlaier		if (s->port < MIN_PORT) {
227171172Smlaier			logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id,
228171172Smlaier			    linebuf);
229171172Smlaier			return (0);
230126353Smlaier		}
231171172Smlaier		s->proxy_port = pick_proxy_port();
232171172Smlaier		proxy_reply(s->cmd, sstosa(&s->proxy_ss), s->proxy_port);
233171172Smlaier		logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
234126353Smlaier	}
235126353Smlaier
236171172Smlaier	return (1);
237126353Smlaier}
238126353Smlaier
239171172Smlaiervoid
240171172Smlaierclient_read(struct bufferevent *bufev, void *arg)
241126353Smlaier{
242171172Smlaier	struct session	*s = arg;
243223637Sbz	size_t		 buf_avail, clientread;
244171172Smlaier	int		 n;
245126353Smlaier
246171172Smlaier	do {
247171172Smlaier		buf_avail = sizeof s->cbuf - s->cbuf_valid;
248223637Sbz		clientread = bufferevent_read(bufev, s->cbuf + s->cbuf_valid,
249171172Smlaier		    buf_avail);
250223637Sbz		s->cbuf_valid += clientread;
251126353Smlaier
252171172Smlaier		while ((n = getline(s->cbuf, &s->cbuf_valid)) > 0) {
253171172Smlaier			logmsg(LOG_DEBUG, "#%d client: %s", s->id, linebuf);
254171172Smlaier			if (!client_parse(s)) {
255171172Smlaier				end_session(s);
256171172Smlaier				return;
257171172Smlaier			}
258171172Smlaier			bufferevent_write(s->server_bufev, linebuf, linelen);
259171172Smlaier		}
260126353Smlaier
261171172Smlaier		if (n == -1) {
262171172Smlaier			logmsg(LOG_ERR, "#%d client command too long or not"
263171172Smlaier			    " clean", s->id);
264171172Smlaier			end_session(s);
265171172Smlaier			return;
266171172Smlaier		}
267223637Sbz	} while (clientread == buf_avail);
268171172Smlaier}
269126353Smlaier
270171172Smlaierint
271171172Smlaierdrop_privs(void)
272171172Smlaier{
273171172Smlaier	struct passwd *pw;
274126353Smlaier
275171172Smlaier	pw = getpwnam(NOPRIV_USER);
276171172Smlaier	if (pw == NULL)
277171172Smlaier		return (0);
278126353Smlaier
279171172Smlaier	tzset();
280171172Smlaier	if (chroot(CHROOT_DIR) != 0 || chdir("/") != 0 ||
281171172Smlaier	    setgroups(1, &pw->pw_gid) != 0 ||
282171172Smlaier	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0 ||
283171172Smlaier	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
284171172Smlaier		return (0);
285126353Smlaier
286171172Smlaier	return (1);
287126353Smlaier}
288126353Smlaier
289126353Smlaiervoid
290171172Smlaierend_session(struct session *s)
291126353Smlaier{
292223637Sbz	int serr;
293126353Smlaier
294171172Smlaier	logmsg(LOG_INFO, "#%d ending session", s->id);
295126353Smlaier
296223637Sbz	/* Flush output buffers. */
297223637Sbz	if (s->client_bufev && s->client_fd != -1)
298223637Sbz		evbuffer_write(s->client_bufev->output, s->client_fd);
299223637Sbz	if (s->server_bufev && s->server_fd != -1)
300223637Sbz		evbuffer_write(s->server_bufev->output, s->server_fd);
301223637Sbz
302171172Smlaier	if (s->client_fd != -1)
303171172Smlaier		close(s->client_fd);
304171172Smlaier	if (s->server_fd != -1)
305171172Smlaier		close(s->server_fd);
306126353Smlaier
307171172Smlaier	if (s->client_bufev)
308171172Smlaier		bufferevent_free(s->client_bufev);
309171172Smlaier	if (s->server_bufev)
310171172Smlaier		bufferevent_free(s->server_bufev);
311126353Smlaier
312171172Smlaier	/* Remove rulesets by commiting empty ones. */
313223637Sbz	serr = 0;
314171172Smlaier	if (prepare_commit(s->id) == -1)
315223637Sbz		serr = errno;
316171172Smlaier	else if (do_commit() == -1) {
317223637Sbz		serr = errno;
318171172Smlaier		do_rollback();
319126353Smlaier	}
320223637Sbz	if (serr)
321171172Smlaier		logmsg(LOG_ERR, "#%d pf rule removal failed: %s", s->id,
322223637Sbz		    strerror(serr));
323126353Smlaier
324171172Smlaier	LIST_REMOVE(s, entry);
325171172Smlaier	free(s);
326171172Smlaier	session_count--;
327171172Smlaier}
328126353Smlaier
329223637Sbzvoid
330171172Smlaierexit_daemon(void)
331171172Smlaier{
332171172Smlaier	struct session *s, *next;
333126353Smlaier
334171172Smlaier	for (s = LIST_FIRST(&sessions); s != LIST_END(&sessions); s = next) {
335171172Smlaier		next = LIST_NEXT(s, entry);
336171172Smlaier		end_session(s);
337126353Smlaier	}
338126353Smlaier
339171172Smlaier	if (daemonize)
340171172Smlaier		closelog();
341126353Smlaier
342171172Smlaier	exit(0);
343126353Smlaier}
344126353Smlaier
345126353Smlaierint
346171172Smlaiergetline(char *buf, size_t *valid)
347126353Smlaier{
348171172Smlaier	size_t i;
349126353Smlaier
350171172Smlaier	if (*valid > MAX_LINE)
351171172Smlaier		return (-1);
352171172Smlaier
353171172Smlaier	/* Copy to linebuf while searching for a newline. */
354171172Smlaier	for (i = 0; i < *valid; i++) {
355171172Smlaier		linebuf[i] = buf[i];
356171172Smlaier		if (buf[i] == '\0')
357171172Smlaier			return (-1);
358171172Smlaier		if (buf[i] == '\n')
359171172Smlaier			break;
360126353Smlaier	}
361126353Smlaier
362171172Smlaier	if (i == *valid) {
363171172Smlaier		/* No newline found. */
364171172Smlaier		linebuf[0] = '\0';
365171172Smlaier		linelen = 0;
366171172Smlaier		if (i < MAX_LINE)
367171172Smlaier			return (0);
368171172Smlaier		return (-1);
369126353Smlaier	}
370126353Smlaier
371171172Smlaier	linelen = i + 1;
372171172Smlaier	linebuf[linelen] = '\0';
373171172Smlaier	*valid -= linelen;
374171172Smlaier
375171172Smlaier	/* Move leftovers to the start. */
376171172Smlaier	if (*valid != 0)
377171172Smlaier		bcopy(buf + linelen, buf, *valid);
378126353Smlaier
379171172Smlaier	return ((int)linelen);
380126353Smlaier}
381126353Smlaier
382171172Smlaiervoid
383223637Sbzhandle_connection(const int listen_fd, short event __unused, void *ev __unused)
384126353Smlaier{
385171172Smlaier	struct sockaddr_storage tmp_ss;
386171172Smlaier	struct sockaddr *client_sa, *server_sa, *fixed_server_sa;
387171172Smlaier	struct sockaddr *client_to_proxy_sa, *proxy_to_server_sa;
388171172Smlaier	struct session *s;
389171172Smlaier	socklen_t len;
390171172Smlaier	int client_fd, fc, on;
391126353Smlaier
392126353Smlaier	/*
393171172Smlaier	 * We _must_ accept the connection, otherwise libevent will keep
394171172Smlaier	 * coming back, and we will chew up all CPU.
395126353Smlaier	 */
396171172Smlaier	client_sa = sstosa(&tmp_ss);
397171172Smlaier	len = sizeof(struct sockaddr_storage);
398171172Smlaier	if ((client_fd = accept(listen_fd, client_sa, &len)) < 0) {
399171172Smlaier		logmsg(LOG_CRIT, "accept failed: %s", strerror(errno));
400171172Smlaier		return;
401171172Smlaier	}
402126353Smlaier
403171172Smlaier	/* Refuse connection if the maximum is reached. */
404171172Smlaier	if (session_count >= max_sessions) {
405171172Smlaier		logmsg(LOG_ERR, "client limit (%d) reached, refusing "
406171172Smlaier		    "connection from %s", max_sessions, sock_ntop(client_sa));
407171172Smlaier		close(client_fd);
408171172Smlaier		return;
409171172Smlaier	}
410126353Smlaier
411171172Smlaier	/* Allocate session and copy back the info from the accept(). */
412171172Smlaier	s = init_session();
413171172Smlaier	if (s == NULL) {
414171172Smlaier		logmsg(LOG_CRIT, "init_session failed");
415171172Smlaier		close(client_fd);
416171172Smlaier		return;
417126353Smlaier	}
418171172Smlaier	s->client_fd = client_fd;
419171172Smlaier	memcpy(sstosa(&s->client_ss), client_sa, client_sa->sa_len);
420126353Smlaier
421171172Smlaier	/* Cast it once, and be done with it. */
422171172Smlaier	client_sa = sstosa(&s->client_ss);
423171172Smlaier	server_sa = sstosa(&s->server_ss);
424171172Smlaier	client_to_proxy_sa = sstosa(&tmp_ss);
425171172Smlaier	proxy_to_server_sa = sstosa(&s->proxy_ss);
426171172Smlaier	fixed_server_sa = sstosa(&fixed_server_ss);
427171172Smlaier
428171172Smlaier	/* Log id/client early to ease debugging. */
429171172Smlaier	logmsg(LOG_DEBUG, "#%d accepted connection from %s", s->id,
430171172Smlaier	    sock_ntop(client_sa));
431171172Smlaier
432171172Smlaier	/*
433171172Smlaier	 * Find out the real server and port that the client wanted.
434171172Smlaier	 */
435171172Smlaier	len = sizeof(struct sockaddr_storage);
436171172Smlaier	if ((getsockname(s->client_fd, client_to_proxy_sa, &len)) < 0) {
437171172Smlaier		logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id,
438171172Smlaier		    strerror(errno));
439171172Smlaier		goto fail;
440126353Smlaier	}
441171172Smlaier	if (server_lookup(client_sa, client_to_proxy_sa, server_sa) != 0) {
442171172Smlaier	    	logmsg(LOG_CRIT, "#%d server lookup failed (no rdr?)", s->id);
443171172Smlaier		goto fail;
444126353Smlaier	}
445171172Smlaier	if (fixed_server) {
446171172Smlaier		memcpy(sstosa(&s->orig_server_ss), server_sa,
447171172Smlaier		    server_sa->sa_len);
448171172Smlaier		memcpy(server_sa, fixed_server_sa, fixed_server_sa->sa_len);
449171172Smlaier	}
450126353Smlaier
451171172Smlaier	/* XXX: check we are not connecting to ourself. */
452126353Smlaier
453126353Smlaier	/*
454171172Smlaier	 * Setup socket and connect to server.
455126353Smlaier	 */
456171172Smlaier	if ((s->server_fd = socket(server_sa->sa_family, SOCK_STREAM,
457171172Smlaier	    IPPROTO_TCP)) < 0) {
458171172Smlaier		logmsg(LOG_CRIT, "#%d server socket failed: %s", s->id,
459171172Smlaier		    strerror(errno));
460171172Smlaier		goto fail;
461171172Smlaier	}
462171172Smlaier	if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss),
463171172Smlaier	    fixed_proxy_ss.ss_len) != 0) {
464171172Smlaier		logmsg(LOG_CRIT, "#%d cannot bind fixed proxy address: %s",
465171172Smlaier		    s->id, strerror(errno));
466171172Smlaier		goto fail;
467171172Smlaier	}
468126353Smlaier
469171172Smlaier	/* Use non-blocking connect(), see CONNECT_TIMEOUT below. */
470171172Smlaier	if ((fc = fcntl(s->server_fd, F_GETFL)) == -1 ||
471171172Smlaier	    fcntl(s->server_fd, F_SETFL, fc | O_NONBLOCK) == -1) {
472171172Smlaier		logmsg(LOG_CRIT, "#%d cannot mark socket non-blocking: %s",
473171172Smlaier		    s->id, strerror(errno));
474171172Smlaier		goto fail;
475171172Smlaier	}
476171172Smlaier	if (connect(s->server_fd, server_sa, server_sa->sa_len) < 0 &&
477171172Smlaier	    errno != EINPROGRESS) {
478171172Smlaier		logmsg(LOG_CRIT, "#%d proxy cannot connect to server %s: %s",
479171172Smlaier		    s->id, sock_ntop(server_sa), strerror(errno));
480171172Smlaier		goto fail;
481171172Smlaier	}
482126353Smlaier
483171172Smlaier	len = sizeof(struct sockaddr_storage);
484171172Smlaier	if ((getsockname(s->server_fd, proxy_to_server_sa, &len)) < 0) {
485171172Smlaier		logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id,
486171172Smlaier		    strerror(errno));
487171172Smlaier		goto fail;
488126353Smlaier	}
489126353Smlaier
490171172Smlaier	logmsg(LOG_INFO, "#%d FTP session %d/%d started: client %s to server "
491171172Smlaier	    "%s via proxy %s ", s->id, session_count, max_sessions,
492171172Smlaier	    sock_ntop(client_sa), sock_ntop(server_sa),
493171172Smlaier	    sock_ntop(proxy_to_server_sa));
494126353Smlaier
495171172Smlaier	/* Keepalive is nice, but don't care if it fails. */
496171172Smlaier	on = 1;
497171172Smlaier	setsockopt(s->client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
498171172Smlaier	    sizeof on);
499171172Smlaier	setsockopt(s->server_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
500171172Smlaier	    sizeof on);
501126353Smlaier
502171172Smlaier	/*
503171172Smlaier	 * Setup buffered events.
504171172Smlaier	 */
505171172Smlaier	s->client_bufev = bufferevent_new(s->client_fd, &client_read, NULL,
506171172Smlaier	    &client_error, s);
507171172Smlaier	if (s->client_bufev == NULL) {
508171172Smlaier		logmsg(LOG_CRIT, "#%d bufferevent_new client failed", s->id);
509171172Smlaier		goto fail;
510126353Smlaier	}
511171172Smlaier	bufferevent_settimeout(s->client_bufev, timeout, 0);
512171172Smlaier	bufferevent_enable(s->client_bufev, EV_READ | EV_TIMEOUT);
513126353Smlaier
514171172Smlaier	s->server_bufev = bufferevent_new(s->server_fd, &server_read, NULL,
515171172Smlaier	    &server_error, s);
516171172Smlaier	if (s->server_bufev == NULL) {
517171172Smlaier		logmsg(LOG_CRIT, "#%d bufferevent_new server failed", s->id);
518171172Smlaier		goto fail;
519126353Smlaier	}
520171172Smlaier	bufferevent_settimeout(s->server_bufev, CONNECT_TIMEOUT, 0);
521171172Smlaier	bufferevent_enable(s->server_bufev, EV_READ | EV_TIMEOUT);
522126353Smlaier
523171172Smlaier	return;
524171172Smlaier
525171172Smlaier fail:
526171172Smlaier	end_session(s);
527126353Smlaier}
528126353Smlaier
529126353Smlaiervoid
530223637Sbzhandle_signal(int sig, short event __unused, void *arg __unused)
531126353Smlaier{
532126353Smlaier	/*
533171172Smlaier	 * Signal handler rules don't apply, libevent decouples for us.
534126353Smlaier	 */
535126353Smlaier
536223637Sbz	logmsg(LOG_ERR, "exiting on signal %d", sig);
537126353Smlaier
538171172Smlaier	exit_daemon();
539171172Smlaier}
540171172Smlaier
541126353Smlaier
542171172Smlaierstruct session *
543171172Smlaierinit_session(void)
544171172Smlaier{
545171172Smlaier	struct session *s;
546126353Smlaier
547171172Smlaier	s = calloc(1, sizeof(struct session));
548171172Smlaier	if (s == NULL)
549171172Smlaier		return (NULL);
550126353Smlaier
551171172Smlaier	s->id = id_count++;
552171172Smlaier	s->client_fd = -1;
553171172Smlaier	s->server_fd = -1;
554171172Smlaier	s->cbuf[0] = '\0';
555171172Smlaier	s->cbuf_valid = 0;
556171172Smlaier	s->sbuf[0] = '\0';
557171172Smlaier	s->sbuf_valid = 0;
558171172Smlaier	s->client_bufev = NULL;
559171172Smlaier	s->server_bufev = NULL;
560171172Smlaier	s->cmd = CMD_NONE;
561171172Smlaier	s->port = 0;
562126353Smlaier
563171172Smlaier	LIST_INSERT_HEAD(&sessions, s, entry);
564171172Smlaier	session_count++;
565126353Smlaier
566171172Smlaier	return (s);
567126353Smlaier}
568126353Smlaier
569126353Smlaiervoid
570171172Smlaierlogmsg(int pri, const char *message, ...)
571126353Smlaier{
572171172Smlaier	va_list	ap;
573126353Smlaier
574171172Smlaier	if (pri > loglevel)
575171172Smlaier		return;
576126353Smlaier
577171172Smlaier	va_start(ap, message);
578126353Smlaier
579171172Smlaier	if (daemonize)
580171172Smlaier		/* syslog does its own vissing. */
581171172Smlaier		vsyslog(pri, message, ap);
582171172Smlaier	else {
583171172Smlaier		char buf[MAX_LOGLINE];
584171172Smlaier		char visbuf[2 * MAX_LOGLINE];
585126353Smlaier
586171172Smlaier		/* We don't care about truncation. */
587171172Smlaier		vsnprintf(buf, sizeof buf, message, ap);
588171172Smlaier#ifdef __FreeBSD__
589223637Sbz		strvis(visbuf, buf, VIS_CSTYLE | VIS_NL);
590171172Smlaier#else
591171172Smlaier		strnvis(visbuf, buf, sizeof visbuf, VIS_CSTYLE | VIS_NL);
592171172Smlaier#endif
593171172Smlaier		fprintf(stderr, "%s\n", visbuf);
594126353Smlaier	}
595126353Smlaier
596171172Smlaier	va_end(ap);
597126353Smlaier}
598126353Smlaier
599126353Smlaierint
600126353Smlaiermain(int argc, char *argv[])
601126353Smlaier{
602171172Smlaier	struct rlimit rlp;
603171172Smlaier	struct addrinfo hints, *res;
604171172Smlaier	struct event ev, ev_sighup, ev_sigint, ev_sigterm;
605171172Smlaier	int ch, error, listenfd, on;
606171172Smlaier	const char *errstr;
607126353Smlaier
608171172Smlaier	/* Defaults. */
609171172Smlaier	anonymous_only	= 0;
610171172Smlaier	daemonize	= 1;
611171172Smlaier	fixed_proxy	= NULL;
612171172Smlaier	fixed_server	= NULL;
613171172Smlaier	fixed_server_port = "21";
614171172Smlaier	ipv6_mode	= 0;
615171172Smlaier	listen_ip	= NULL;
616171172Smlaier	listen_port	= "8021";
617171172Smlaier	loglevel	= LOG_NOTICE;
618171172Smlaier	max_sessions	= 100;
619171172Smlaier	qname		= NULL;
620171172Smlaier	rfc_mode	= 0;
621223637Sbz	tagname		= NULL;
622171172Smlaier	timeout		= 24 * 3600;
623171172Smlaier	verbose		= 0;
624171172Smlaier
625171172Smlaier	/* Other initialization. */
626171172Smlaier	id_count	= 1;
627171172Smlaier	session_count	= 0;
628171172Smlaier
629223637Sbz	while ((ch = getopt(argc, argv, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) {
630126353Smlaier		switch (ch) {
631171172Smlaier		case '6':
632171172Smlaier			ipv6_mode = 1;
633130617Smlaier			break;
634126353Smlaier		case 'A':
635171172Smlaier			anonymous_only = 1;
636126353Smlaier			break;
637171172Smlaier		case 'a':
638171172Smlaier			fixed_proxy = optarg;
639171172Smlaier			break;
640171172Smlaier		case 'b':
641171172Smlaier			listen_ip = optarg;
642171172Smlaier			break;
643126353Smlaier		case 'D':
644171172Smlaier			loglevel = strtonum(optarg, LOG_EMERG, LOG_DEBUG,
645171172Smlaier			    &errstr);
646171172Smlaier			if (errstr)
647171172Smlaier				errx(1, "loglevel %s", errstr);
648126353Smlaier			break;
649171172Smlaier		case 'd':
650171172Smlaier			daemonize = 0;
651126353Smlaier			break;
652126353Smlaier		case 'm':
653171172Smlaier			max_sessions = strtonum(optarg, 1, 500, &errstr);
654171172Smlaier			if (errstr)
655171172Smlaier				errx(1, "max sessions %s", errstr);
656126353Smlaier			break;
657171172Smlaier		case 'P':
658171172Smlaier			fixed_server_port = optarg;
659126353Smlaier			break;
660171172Smlaier		case 'p':
661171172Smlaier			listen_port = optarg;
662126353Smlaier			break;
663171172Smlaier		case 'q':
664171172Smlaier			if (strlen(optarg) >= PF_QNAME_SIZE)
665171172Smlaier				errx(1, "queuename too long");
666171172Smlaier			qname = optarg;
667126353Smlaier			break;
668171172Smlaier		case 'R':
669171172Smlaier			fixed_server = optarg;
670145840Smlaier			break;
671171172Smlaier		case 'r':
672171172Smlaier			rfc_mode = 1;
673145840Smlaier			break;
674223637Sbz		case 'T':
675223637Sbz			if (strlen(optarg) >= PF_TAG_NAME_SIZE)
676223637Sbz				errx(1, "tagname too long");
677223637Sbz			tagname = optarg;
678223637Sbz			break;
679126353Smlaier		case 't':
680171172Smlaier			timeout = strtonum(optarg, 0, 86400, &errstr);
681171172Smlaier			if (errstr)
682171172Smlaier				errx(1, "timeout %s", errstr);
683171172Smlaier			break;
684171172Smlaier		case 'v':
685171172Smlaier			verbose++;
686171172Smlaier			if (verbose > 2)
687126353Smlaier				usage();
688126353Smlaier			break;
689126353Smlaier		default:
690126353Smlaier			usage();
691126353Smlaier		}
692126353Smlaier	}
693126353Smlaier
694171172Smlaier	if (listen_ip == NULL)
695171172Smlaier		listen_ip = ipv6_mode ? "::1" : "127.0.0.1";
696126353Smlaier
697171172Smlaier	/* Check for root to save the user from cryptic failure messages. */
698171172Smlaier	if (getuid() != 0)
699171172Smlaier		errx(1, "needs to start as root");
700126353Smlaier
701171172Smlaier	/* Raise max. open files limit to satisfy max. sessions. */
702171172Smlaier	rlp.rlim_cur = rlp.rlim_max = (2 * max_sessions) + 10;
703171172Smlaier	if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
704171172Smlaier		err(1, "setrlimit");
705126353Smlaier
706171172Smlaier	if (fixed_proxy) {
707171172Smlaier		memset(&hints, 0, sizeof hints);
708171172Smlaier		hints.ai_flags = AI_NUMERICHOST;
709171172Smlaier		hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
710171172Smlaier		hints.ai_socktype = SOCK_STREAM;
711171172Smlaier		error = getaddrinfo(fixed_proxy, NULL, &hints, &res);
712171172Smlaier		if (error)
713171172Smlaier			errx(1, "getaddrinfo fixed proxy address failed: %s",
714171172Smlaier			    gai_strerror(error));
715171172Smlaier		memcpy(&fixed_proxy_ss, res->ai_addr, res->ai_addrlen);
716171172Smlaier		logmsg(LOG_INFO, "using %s to connect to servers",
717171172Smlaier		    sock_ntop(sstosa(&fixed_proxy_ss)));
718171172Smlaier		freeaddrinfo(res);
719171172Smlaier	}
720126353Smlaier
721171172Smlaier	if (fixed_server) {
722171172Smlaier		memset(&hints, 0, sizeof hints);
723171172Smlaier		hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
724171172Smlaier		hints.ai_socktype = SOCK_STREAM;
725171172Smlaier		error = getaddrinfo(fixed_server, fixed_server_port, &hints,
726171172Smlaier		    &res);
727171172Smlaier		if (error)
728171172Smlaier			errx(1, "getaddrinfo fixed server address failed: %s",
729171172Smlaier			    gai_strerror(error));
730171172Smlaier		memcpy(&fixed_server_ss, res->ai_addr, res->ai_addrlen);
731171172Smlaier		logmsg(LOG_INFO, "using fixed server %s",
732171172Smlaier		    sock_ntop(sstosa(&fixed_server_ss)));
733171172Smlaier		freeaddrinfo(res);
734171172Smlaier	}
735126353Smlaier
736171172Smlaier	/* Setup listener. */
737171172Smlaier	memset(&hints, 0, sizeof hints);
738171172Smlaier	hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
739171172Smlaier	hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET;
740171172Smlaier	hints.ai_socktype = SOCK_STREAM;
741171172Smlaier	error = getaddrinfo(listen_ip, listen_port, &hints, &res);
742171172Smlaier	if (error)
743171172Smlaier		errx(1, "getaddrinfo listen address failed: %s",
744171172Smlaier		    gai_strerror(error));
745171172Smlaier	if ((listenfd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
746171172Smlaier		errx(1, "socket failed");
747171172Smlaier	on = 1;
748171172Smlaier	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
749171172Smlaier	    sizeof on) != 0)
750171172Smlaier		err(1, "setsockopt failed");
751171172Smlaier	if (bind(listenfd, (struct sockaddr *)res->ai_addr,
752171172Smlaier	    (socklen_t)res->ai_addrlen) != 0)
753171172Smlaier	    	err(1, "bind failed");
754171172Smlaier	if (listen(listenfd, TCP_BACKLOG) != 0)
755171172Smlaier		err(1, "listen failed");
756171172Smlaier	freeaddrinfo(res);
757126353Smlaier
758171172Smlaier	/* Initialize pf. */
759223637Sbz	init_filter(qname, tagname, verbose);
760126353Smlaier
761171172Smlaier	if (daemonize) {
762171172Smlaier		if (daemon(0, 0) == -1)
763171172Smlaier			err(1, "cannot daemonize");
764171172Smlaier		openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
765171172Smlaier	}
766126353Smlaier
767171172Smlaier	/* Use logmsg for output from here on. */
768171172Smlaier
769171172Smlaier	if (!drop_privs()) {
770171172Smlaier		logmsg(LOG_ERR, "cannot drop privileges: %s", strerror(errno));
771171172Smlaier		exit(1);
772126353Smlaier	}
773171172Smlaier
774171172Smlaier	event_init();
775126353Smlaier
776171172Smlaier	/* Setup signal handler. */
777171172Smlaier	signal(SIGPIPE, SIG_IGN);
778171172Smlaier	signal_set(&ev_sighup, SIGHUP, handle_signal, NULL);
779171172Smlaier	signal_set(&ev_sigint, SIGINT, handle_signal, NULL);
780171172Smlaier	signal_set(&ev_sigterm, SIGTERM, handle_signal, NULL);
781171172Smlaier	signal_add(&ev_sighup, NULL);
782171172Smlaier	signal_add(&ev_sigint, NULL);
783171172Smlaier	signal_add(&ev_sigterm, NULL);
784126353Smlaier
785171172Smlaier	event_set(&ev, listenfd, EV_READ | EV_PERSIST, handle_connection, &ev);
786171172Smlaier	event_add(&ev, NULL);
787171172Smlaier
788171172Smlaier	logmsg(LOG_NOTICE, "listening on %s port %s", listen_ip, listen_port);
789171172Smlaier
790171172Smlaier	/*  Vroom, vroom.  */
791171172Smlaier	event_dispatch();
792171172Smlaier
793171172Smlaier	logmsg(LOG_ERR, "event_dispatch error: %s", strerror(errno));
794171172Smlaier	exit_daemon();
795171172Smlaier
796171172Smlaier	/* NOTREACHED */
797171172Smlaier	return (1);
798171172Smlaier}
799171172Smlaier
800171172Smlaieru_int16_t
801171172Smlaierparse_port(int mode)
802171172Smlaier{
803171172Smlaier	unsigned int	 port, v[6];
804171172Smlaier	int		 n;
805171172Smlaier	char		*p;
806171172Smlaier
807171172Smlaier	/* Find the last space or left-parenthesis. */
808171172Smlaier	for (p = linebuf + linelen; p > linebuf; p--)
809171172Smlaier		if (*p == ' ' || *p == '(')
810171172Smlaier			break;
811171172Smlaier	if (p == linebuf)
812171172Smlaier		return (0);
813171172Smlaier
814171172Smlaier	switch (mode) {
815171172Smlaier	case CMD_PORT:
816171172Smlaier		n = sscanf(p, " %u,%u,%u,%u,%u,%u", &v[0], &v[1], &v[2],
817171172Smlaier		    &v[3], &v[4], &v[5]);
818171172Smlaier		if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
819171172Smlaier		    v[3] < 256 && v[4] < 256 && v[5] < 256)
820171172Smlaier			return ((v[4] << 8) | v[5]);
821171172Smlaier		break;
822171172Smlaier	case CMD_PASV:
823171172Smlaier		n = sscanf(p, "(%u,%u,%u,%u,%u,%u)", &v[0], &v[1], &v[2],
824171172Smlaier		    &v[3], &v[4], &v[5]);
825171172Smlaier		if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
826171172Smlaier		    v[3] < 256 && v[4] < 256 && v[5] < 256)
827171172Smlaier			return ((v[4] << 8) | v[5]);
828171172Smlaier		break;
829171172Smlaier	case CMD_EPSV:
830171172Smlaier		n = sscanf(p, "(|||%u|)", &port);
831171172Smlaier		if (n == 1 && port < 65536)
832171172Smlaier			return (port);
833171172Smlaier		break;
834171172Smlaier	case CMD_EPRT:
835171172Smlaier		n = sscanf(p, " |1|%u.%u.%u.%u|%u|", &v[0], &v[1], &v[2],
836171172Smlaier		    &v[3], &port);
837171172Smlaier		if (n == 5 && v[0] < 256 && v[1] < 256 && v[2] < 256 &&
838171172Smlaier		    v[3] < 256 && port < 65536)
839171172Smlaier			return (port);
840171172Smlaier		n = sscanf(p, " |2|%*[a-fA-F0-9:]|%u|", &port);
841171172Smlaier		if (n == 1 && port < 65536)
842171172Smlaier			return (port);
843171172Smlaier		break;
844171172Smlaier	default:
845171172Smlaier		return (0);
846126353Smlaier	}
847126353Smlaier
848171172Smlaier	return (0);
849171172Smlaier}
850126353Smlaier
851171172Smlaieru_int16_t
852171172Smlaierpick_proxy_port(void)
853171172Smlaier{
854171172Smlaier	/* Random should be good enough for avoiding port collisions. */
855223637Sbz	return (IPPORT_HIFIRSTAUTO +
856223637Sbz	    arc4random_uniform(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO));
857171172Smlaier}
858126353Smlaier
859171172Smlaiervoid
860171172Smlaierproxy_reply(int cmd, struct sockaddr *sa, u_int16_t port)
861171172Smlaier{
862223637Sbz	u_int i;
863223637Sbz	int r = 0;
864126353Smlaier
865171172Smlaier	switch (cmd) {
866171172Smlaier	case CMD_PORT:
867171172Smlaier		r = snprintf(linebuf, sizeof linebuf,
868171172Smlaier		    "PORT %s,%u,%u\r\n", sock_ntop(sa), port / 256,
869171172Smlaier		    port % 256);
870171172Smlaier		break;
871171172Smlaier	case CMD_PASV:
872171172Smlaier		r = snprintf(linebuf, sizeof linebuf,
873171172Smlaier		    "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa),
874171172Smlaier		        port / 256, port % 256);
875171172Smlaier		break;
876171172Smlaier	case CMD_EPRT:
877171172Smlaier		if (sa->sa_family == AF_INET)
878171172Smlaier			r = snprintf(linebuf, sizeof linebuf,
879171172Smlaier			    "EPRT |1|%s|%u|\r\n", sock_ntop(sa), port);
880171172Smlaier		else if (sa->sa_family == AF_INET6)
881171172Smlaier			r = snprintf(linebuf, sizeof linebuf,
882171172Smlaier			    "EPRT |2|%s|%u|\r\n", sock_ntop(sa), port);
883171172Smlaier		break;
884171172Smlaier	case CMD_EPSV:
885171172Smlaier		r = snprintf(linebuf, sizeof linebuf,
886171172Smlaier		    "229 Entering Extended Passive Mode (|||%u|)\r\n", port);
887171172Smlaier		break;
888171172Smlaier	}
889126353Smlaier
890223637Sbz	if (r < 0 || ((u_int)r) >= sizeof linebuf) {
891171172Smlaier		logmsg(LOG_ERR, "proxy_reply failed: %d", r);
892171172Smlaier		linebuf[0] = '\0';
893171172Smlaier		linelen = 0;
894171172Smlaier		return;
895126353Smlaier	}
896171172Smlaier	linelen = (size_t)r;
897126353Smlaier
898171172Smlaier	if (cmd == CMD_PORT || cmd == CMD_PASV) {
899171172Smlaier		/* Replace dots in IP address with commas. */
900171172Smlaier		for (i = 0; i < linelen; i++)
901171172Smlaier			if (linebuf[i] == '.')
902171172Smlaier				linebuf[i] = ',';
903171172Smlaier	}
904171172Smlaier}
905171172Smlaier
906171172Smlaiervoid
907223637Sbzserver_error(struct bufferevent *bufev __unused, short what, void *arg)
908171172Smlaier{
909171172Smlaier	struct session *s = arg;
910171172Smlaier
911171172Smlaier	if (what & EVBUFFER_EOF)
912171172Smlaier		logmsg(LOG_INFO, "#%d server close", s->id);
913171172Smlaier	else if (what == (EVBUFFER_ERROR | EVBUFFER_READ))
914171172Smlaier		logmsg(LOG_ERR, "#%d server refused connection", s->id);
915171172Smlaier	else if (what & EVBUFFER_WRITE)
916171172Smlaier		logmsg(LOG_ERR, "#%d server write error: %d", s->id, what);
917171172Smlaier	else if (what & EVBUFFER_TIMEOUT)
918171172Smlaier		logmsg(LOG_NOTICE, "#%d server timeout", s->id);
919171172Smlaier	else
920171172Smlaier		logmsg(LOG_ERR, "#%d abnormal server error: %d", s->id, what);
921171172Smlaier
922171172Smlaier	end_session(s);
923171172Smlaier}
924171172Smlaier
925171172Smlaierint
926171172Smlaierserver_parse(struct session *s)
927171172Smlaier{
928223637Sbz	if (s->cmd == CMD_NONE || linelen < 4 || linebuf[0] != '2')
929223637Sbz		goto out;
930223637Sbz
931223637Sbz	if ((s->cmd == CMD_PASV && strncmp("227 ", linebuf, 4) == 0) ||
932223637Sbz	    (s->cmd == CMD_EPSV && strncmp("229 ", linebuf, 4) == 0))
933223637Sbz		return (allow_data_connection(s));
934223637Sbz
935223637Sbz out:
936223637Sbz	s->cmd = CMD_NONE;
937223637Sbz	s->port = 0;
938223637Sbz
939223637Sbz	return (1);
940223637Sbz}
941223637Sbz
942223637Sbzint
943223637Sbzallow_data_connection(struct session *s)
944223637Sbz{
945171172Smlaier	struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa;
946171172Smlaier	int prepared = 0;
947171172Smlaier
948126353Smlaier	/*
949171172Smlaier	 * The pf rules below do quite some NAT rewriting, to keep up
950171172Smlaier	 * appearances.  Points to keep in mind:
951171172Smlaier	 * 1)  The client must think it's talking to the real server,
952171172Smlaier	 *     for both control and data connections.  Transparently.
953171172Smlaier	 * 2)  The server must think that the proxy is the client.
954171172Smlaier	 * 3)  Source and destination ports are rewritten to minimize
955171172Smlaier	 *     port collisions, to aid security (some systems pick weak
956171172Smlaier	 *     ports) or to satisfy RFC requirements (source port 20).
957126353Smlaier	 */
958171172Smlaier
959171172Smlaier	/* Cast this once, to make code below it more readable. */
960171172Smlaier	client_sa = sstosa(&s->client_ss);
961171172Smlaier	server_sa = sstosa(&s->server_ss);
962171172Smlaier	proxy_sa = sstosa(&s->proxy_ss);
963171172Smlaier	if (fixed_server)
964171172Smlaier		/* Fixed server: data connections must appear to come
965171172Smlaier		   from / go to the original server, not the fixed one. */
966171172Smlaier		orig_sa = sstosa(&s->orig_server_ss);
967171172Smlaier	else
968171172Smlaier		/* Server not fixed: orig_server == server. */
969171172Smlaier		orig_sa = sstosa(&s->server_ss);
970126353Smlaier
971171172Smlaier	/* Passive modes. */
972223637Sbz	if (s->cmd == CMD_PASV || s->cmd == CMD_EPSV) {
973171172Smlaier		s->port = parse_port(s->cmd);
974171172Smlaier		if (s->port < MIN_PORT) {
975171172Smlaier			logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id,
976171172Smlaier			    linebuf);
977171172Smlaier			return (0);
978171172Smlaier		}
979171172Smlaier		s->proxy_port = pick_proxy_port();
980171172Smlaier		logmsg(LOG_INFO, "#%d passive: client to server port %d"
981171172Smlaier		    " via port %d", s->id, s->port, s->proxy_port);
982126353Smlaier
983171172Smlaier		if (prepare_commit(s->id) == -1)
984171172Smlaier			goto fail;
985171172Smlaier		prepared = 1;
986171172Smlaier
987171172Smlaier		proxy_reply(s->cmd, orig_sa, s->proxy_port);
988171172Smlaier		logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
989171172Smlaier
990171172Smlaier		/* rdr from $client to $orig_server port $proxy_port -> $server
991171172Smlaier		    port $port */
992171172Smlaier		if (add_rdr(s->id, client_sa, orig_sa, s->proxy_port,
993171172Smlaier		    server_sa, s->port) == -1)
994171172Smlaier			goto fail;
995171172Smlaier
996171172Smlaier		/* nat from $client to $server port $port -> $proxy */
997171172Smlaier		if (add_nat(s->id, client_sa, server_sa, s->port, proxy_sa,
998171172Smlaier		    PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1)
999171172Smlaier			goto fail;
1000171172Smlaier
1001171172Smlaier		/* pass in from $client to $server port $port */
1002171172Smlaier		if (add_filter(s->id, PF_IN, client_sa, server_sa,
1003171172Smlaier		    s->port) == -1)
1004171172Smlaier			goto fail;
1005171172Smlaier
1006171172Smlaier		/* pass out from $proxy to $server port $port */
1007171172Smlaier		if (add_filter(s->id, PF_OUT, proxy_sa, server_sa,
1008171172Smlaier		    s->port) == -1)
1009171172Smlaier			goto fail;
1010126353Smlaier	}
1011126353Smlaier
1012171172Smlaier	/* Active modes. */
1013223637Sbz	if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) {
1014171172Smlaier		logmsg(LOG_INFO, "#%d active: server to client port %d"
1015171172Smlaier		    " via port %d", s->id, s->port, s->proxy_port);
1016126353Smlaier
1017171172Smlaier		if (prepare_commit(s->id) == -1)
1018171172Smlaier			goto fail;
1019171172Smlaier		prepared = 1;
1020171172Smlaier
1021171172Smlaier		/* rdr from $server to $proxy port $proxy_port -> $client port
1022171172Smlaier		    $port */
1023171172Smlaier		if (add_rdr(s->id, server_sa, proxy_sa, s->proxy_port,
1024171172Smlaier		    client_sa, s->port) == -1)
1025171172Smlaier			goto fail;
1026171172Smlaier
1027171172Smlaier		/* nat from $server to $client port $port -> $orig_server port
1028171172Smlaier		    $natport */
1029171172Smlaier		if (rfc_mode && s->cmd == CMD_PORT) {
1030171172Smlaier			/* Rewrite sourceport to RFC mandated 20. */
1031171172Smlaier			if (add_nat(s->id, server_sa, client_sa, s->port,
1032171172Smlaier			    orig_sa, 20, 20) == -1)
1033171172Smlaier				goto fail;
1034171172Smlaier		} else {
1035171172Smlaier			/* Let pf pick a source port from the standard range. */
1036171172Smlaier			if (add_nat(s->id, server_sa, client_sa, s->port,
1037171172Smlaier			    orig_sa, PF_NAT_PROXY_PORT_LOW,
1038171172Smlaier			    PF_NAT_PROXY_PORT_HIGH) == -1)
1039171172Smlaier			    	goto fail;
1040171172Smlaier		}
1041171172Smlaier
1042171172Smlaier		/* pass in from $server to $client port $port */
1043171172Smlaier		if (add_filter(s->id, PF_IN, server_sa, client_sa, s->port) ==
1044171172Smlaier		    -1)
1045171172Smlaier			goto fail;
1046171172Smlaier
1047171172Smlaier		/* pass out from $orig_server to $client port $port */
1048171172Smlaier		if (add_filter(s->id, PF_OUT, orig_sa, client_sa, s->port) ==
1049171172Smlaier		    -1)
1050171172Smlaier			goto fail;
1051126353Smlaier	}
1052126353Smlaier
1053171172Smlaier	/* Commit rules if they were prepared. */
1054171172Smlaier	if (prepared && (do_commit() == -1)) {
1055171172Smlaier		if (errno != EBUSY)
1056171172Smlaier			goto fail;
1057171172Smlaier		/* One more try if busy. */
1058171172Smlaier		usleep(5000);
1059171172Smlaier		if (do_commit() == -1)
1060171172Smlaier			goto fail;
1061126353Smlaier	}
1062126353Smlaier
1063171172Smlaier	s->cmd = CMD_NONE;
1064171172Smlaier	s->port = 0;
1065126353Smlaier
1066171172Smlaier	return (1);
1067126353Smlaier
1068171172Smlaier fail:
1069171172Smlaier	logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno));
1070171172Smlaier	if (prepared)
1071171172Smlaier		do_rollback();
1072171172Smlaier	return (0);
1073171172Smlaier}
1074171172Smlaier
1075171172Smlaiervoid
1076171172Smlaierserver_read(struct bufferevent *bufev, void *arg)
1077171172Smlaier{
1078171172Smlaier	struct session	*s = arg;
1079223637Sbz	size_t		 buf_avail, srvread;
1080171172Smlaier	int		 n;
1081126353Smlaier
1082171172Smlaier	bufferevent_settimeout(bufev, timeout, 0);
1083126353Smlaier
1084171172Smlaier	do {
1085171172Smlaier		buf_avail = sizeof s->sbuf - s->sbuf_valid;
1086223637Sbz		srvread = bufferevent_read(bufev, s->sbuf + s->sbuf_valid,
1087171172Smlaier		    buf_avail);
1088223637Sbz		s->sbuf_valid += srvread;
1089126353Smlaier
1090171172Smlaier		while ((n = getline(s->sbuf, &s->sbuf_valid)) > 0) {
1091171172Smlaier			logmsg(LOG_DEBUG, "#%d server: %s", s->id, linebuf);
1092171172Smlaier			if (!server_parse(s)) {
1093171172Smlaier				end_session(s);
1094171172Smlaier				return;
1095171172Smlaier			}
1096171172Smlaier			bufferevent_write(s->client_bufev, linebuf, linelen);
1097171172Smlaier		}
1098126353Smlaier
1099171172Smlaier		if (n == -1) {
1100171172Smlaier			logmsg(LOG_ERR, "#%d server reply too long or not"
1101171172Smlaier			    " clean", s->id);
1102171172Smlaier			end_session(s);
1103171172Smlaier			return;
1104126353Smlaier		}
1105223637Sbz	} while (srvread == buf_avail);
1106171172Smlaier}
1107126353Smlaier
1108171172Smlaierconst char *
1109171172Smlaiersock_ntop(struct sockaddr *sa)
1110171172Smlaier{
1111171172Smlaier	static int n = 0;
1112126353Smlaier
1113171172Smlaier	/* Cycle to next buffer. */
1114171172Smlaier	n = (n + 1) % NTOP_BUFS;
1115171172Smlaier	ntop_buf[n][0] = '\0';
1116126353Smlaier
1117171172Smlaier	if (sa->sa_family == AF_INET) {
1118171172Smlaier		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1119126353Smlaier
1120171172Smlaier		return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
1121171172Smlaier		    sizeof ntop_buf[0]));
1122126353Smlaier	}
1123126353Smlaier
1124171172Smlaier	if (sa->sa_family == AF_INET6) {
1125171172Smlaier		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
1126126353Smlaier
1127171172Smlaier		return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
1128171172Smlaier		    sizeof ntop_buf[0]));
1129171172Smlaier	}
1130171172Smlaier
1131171172Smlaier	return (NULL);
1132126353Smlaier}
1133171172Smlaier
1134171172Smlaiervoid
1135171172Smlaierusage(void)
1136171172Smlaier{
1137171172Smlaier	fprintf(stderr, "usage: %s [-6Adrv] [-a address] [-b address]"
1138171172Smlaier	    " [-D level] [-m maxsessions]\n                 [-P port]"
1139223637Sbz	    " [-p port] [-q queue] [-R address] [-T tag]\n"
1140223637Sbz            "                 [-t timeout]\n", __progname);
1141171172Smlaier	exit(1);
1142171172Smlaier}
1143