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: releng/10.3/contrib/pf/ftp-proxy/ftp-proxy.c 223637 2011-06-28 11:57:25Z bz $"); 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