1315a7da3SJan Lentfer /* $OpenBSD: ftp-proxy.c,v 1.15 2007/08/15 15:18:02 camield Exp $ */ 2a50c4a2fSJan Lentfer 3a50c4a2fSJan Lentfer /* 4a50c4a2fSJan Lentfer * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> 5a50c4a2fSJan Lentfer * 6a50c4a2fSJan Lentfer * Permission to use, copy, modify, and distribute this software for any 7a50c4a2fSJan Lentfer * purpose with or without fee is hereby granted, provided that the above 8a50c4a2fSJan Lentfer * copyright notice and this permission notice appear in all copies. 9a50c4a2fSJan Lentfer * 10a50c4a2fSJan Lentfer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11a50c4a2fSJan Lentfer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12a50c4a2fSJan Lentfer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13a50c4a2fSJan Lentfer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14a50c4a2fSJan Lentfer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15a50c4a2fSJan Lentfer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16a50c4a2fSJan Lentfer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17a50c4a2fSJan Lentfer */ 18a50c4a2fSJan Lentfer 19a50c4a2fSJan Lentfer #include <sys/queue.h> 20a50c4a2fSJan Lentfer #include <sys/types.h> 212af0c10eSSamuel J. Greear #include <sys/event.h> 22a50c4a2fSJan Lentfer #include <sys/time.h> 23a50c4a2fSJan Lentfer #include <sys/resource.h> 24a50c4a2fSJan Lentfer #include <sys/socket.h> 25a50c4a2fSJan Lentfer 26a50c4a2fSJan Lentfer #include <net/if.h> 27a50c4a2fSJan Lentfer #include <net/pf/pfvar.h> 28a50c4a2fSJan Lentfer #include <netinet/in.h> 29a50c4a2fSJan Lentfer #include <arpa/inet.h> 30a50c4a2fSJan Lentfer 31a50c4a2fSJan Lentfer #include <err.h> 32a50c4a2fSJan Lentfer #include <errno.h> 33a50c4a2fSJan Lentfer #include <fcntl.h> 34a50c4a2fSJan Lentfer #include <netdb.h> 35a50c4a2fSJan Lentfer #include <pwd.h> 36a50c4a2fSJan Lentfer #include <signal.h> 37a50c4a2fSJan Lentfer #include <stdarg.h> 38a50c4a2fSJan Lentfer #include <stdio.h> 39a50c4a2fSJan Lentfer #include <stdlib.h> 40a50c4a2fSJan Lentfer #include <string.h> 41a50c4a2fSJan Lentfer #include <syslog.h> 42a50c4a2fSJan Lentfer #include <unistd.h> 43a50c4a2fSJan Lentfer #include <vis.h> 44a50c4a2fSJan Lentfer 45a50c4a2fSJan Lentfer #include "filter.h" 46a50c4a2fSJan Lentfer 47a50c4a2fSJan Lentfer #define CONNECT_TIMEOUT 30 48a50c4a2fSJan Lentfer #define MIN_PORT 1024 49a50c4a2fSJan Lentfer #define MAX_LINE 500 50a50c4a2fSJan Lentfer #define MAX_LOGLINE 300 51a50c4a2fSJan Lentfer #define NTOP_BUFS 3 52a50c4a2fSJan Lentfer #define TCP_BACKLOG 10 53a50c4a2fSJan Lentfer 54a50c4a2fSJan Lentfer #define CHROOT_DIR "/var/empty" 55a50c4a2fSJan Lentfer #define NOPRIV_USER "proxy" 56a50c4a2fSJan Lentfer 57a50c4a2fSJan Lentfer /* pfctl standard NAT range. */ 58a50c4a2fSJan Lentfer #define PF_NAT_PROXY_PORT_LOW 50001 59a50c4a2fSJan Lentfer #define PF_NAT_PROXY_PORT_HIGH 65535 60a50c4a2fSJan Lentfer 61a50c4a2fSJan Lentfer #define sstosa(ss) ((struct sockaddr *)(ss)) 62a50c4a2fSJan Lentfer 63a50c4a2fSJan Lentfer enum { CMD_NONE = 0, CMD_PORT, CMD_EPRT, CMD_PASV, CMD_EPSV }; 64a50c4a2fSJan Lentfer 652af0c10eSSamuel J. Greear struct cbuf { 662af0c10eSSamuel J. Greear char *buffer; 672af0c10eSSamuel J. Greear size_t buffer_size; 682af0c10eSSamuel J. Greear size_t buffer_offset; 692af0c10eSSamuel J. Greear }; 702af0c10eSSamuel J. Greear 71a50c4a2fSJan Lentfer struct session { 72a50c4a2fSJan Lentfer u_int32_t id; 73a50c4a2fSJan Lentfer struct sockaddr_storage client_ss; 74a50c4a2fSJan Lentfer struct sockaddr_storage proxy_ss; 75a50c4a2fSJan Lentfer struct sockaddr_storage server_ss; 76a50c4a2fSJan Lentfer struct sockaddr_storage orig_server_ss; 772af0c10eSSamuel J. Greear struct cbuf client; 782af0c10eSSamuel J. Greear struct cbuf server; 79a50c4a2fSJan Lentfer int client_fd; 80a50c4a2fSJan Lentfer int server_fd; 81a50c4a2fSJan Lentfer char cbuf[MAX_LINE]; 82a50c4a2fSJan Lentfer size_t cbuf_valid; 83a50c4a2fSJan Lentfer char sbuf[MAX_LINE]; 84a50c4a2fSJan Lentfer size_t sbuf_valid; 85a50c4a2fSJan Lentfer int cmd; 86a50c4a2fSJan Lentfer u_int16_t port; 87a50c4a2fSJan Lentfer u_int16_t proxy_port; 88a50c4a2fSJan Lentfer LIST_ENTRY(session) entry; 89a50c4a2fSJan Lentfer }; 90a50c4a2fSJan Lentfer 91a50c4a2fSJan Lentfer LIST_HEAD(, session) sessions = LIST_HEAD_INITIALIZER(sessions); 92a50c4a2fSJan Lentfer 932af0c10eSSamuel J. Greear void buffer_data(struct session *, struct cbuf *, char *, size_t); 942af0c10eSSamuel J. Greear int client_parse(struct session *); 952af0c10eSSamuel J. Greear int client_parse_anon(struct session *); 962af0c10eSSamuel J. Greear int client_parse_cmd(struct session *); 972af0c10eSSamuel J. Greear void client_read(struct session *); 982af0c10eSSamuel J. Greear void client_write(struct session *); 99a50c4a2fSJan Lentfer int drop_privs(void); 100a50c4a2fSJan Lentfer void end_session(struct session *); 101*c35a932cSSascha Wildner void exit_daemon(void); 102cae2835bSzrj int get_line(char *, size_t *); 1032af0c10eSSamuel J. Greear void handle_connection(const int); 1042af0c10eSSamuel J. Greear void handle_signal(int); 105a50c4a2fSJan Lentfer struct session * init_session(void); 106b58f1e66SSascha Wildner void logmsg(int, const char *, ...) __printflike(2, 3); 107a50c4a2fSJan Lentfer u_int16_t parse_port(int); 108a50c4a2fSJan Lentfer u_int16_t pick_proxy_port(void); 109a50c4a2fSJan Lentfer void proxy_reply(int, struct sockaddr *, u_int16_t); 1102af0c10eSSamuel J. Greear int server_parse(struct session *); 1112af0c10eSSamuel J. Greear void server_read(struct session *); 1122af0c10eSSamuel J. Greear void server_write(struct session *); 113315a7da3SJan Lentfer int allow_data_connection(struct session *s); 114a50c4a2fSJan Lentfer const char *sock_ntop(struct sockaddr *); 115a50c4a2fSJan Lentfer void usage(void); 116a50c4a2fSJan Lentfer 117a50c4a2fSJan Lentfer char linebuf[MAX_LINE + 1]; 118a50c4a2fSJan Lentfer size_t linelen; 119a50c4a2fSJan Lentfer 120a50c4a2fSJan Lentfer char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN]; 121a50c4a2fSJan Lentfer 1222af0c10eSSamuel J. Greear #define KQ_NEVENTS 64 1232af0c10eSSamuel J. Greear struct kevent changes[KQ_NEVENTS]; 1242af0c10eSSamuel J. Greear int nchanges; 1252af0c10eSSamuel J. Greear 126a50c4a2fSJan Lentfer struct sockaddr_storage fixed_server_ss, fixed_proxy_ss; 127a50c4a2fSJan Lentfer char *fixed_server, *fixed_server_port, *fixed_proxy, *listen_ip, *listen_port, 128315a7da3SJan Lentfer *qname, *tagname; 129a50c4a2fSJan Lentfer int anonymous_only, daemonize, id_count, ipv6_mode, loglevel, max_sessions, 130a50c4a2fSJan Lentfer rfc_mode, session_count, timeout, verbose; 131a50c4a2fSJan Lentfer extern char *__progname; 132a50c4a2fSJan Lentfer 133a50c4a2fSJan Lentfer void 1342af0c10eSSamuel J. Greear buffer_data(struct session *s, struct cbuf *cb, char *buf, size_t len) 135a50c4a2fSJan Lentfer { 1362af0c10eSSamuel J. Greear if (len < 1) 1372af0c10eSSamuel J. Greear return; 138a50c4a2fSJan Lentfer 1392af0c10eSSamuel J. Greear if (cb->buffer == NULL) 1402af0c10eSSamuel J. Greear if ((cb->buffer = malloc(MAX_LINE)) == NULL) 1412af0c10eSSamuel J. Greear goto error; 142a50c4a2fSJan Lentfer 1432af0c10eSSamuel J. Greear memcpy(cb->buffer, buf, len); 1442af0c10eSSamuel J. Greear cb->buffer_size = len; 1452af0c10eSSamuel J. Greear return; 1462af0c10eSSamuel J. Greear 1472af0c10eSSamuel J. Greear error: 1482af0c10eSSamuel J. Greear logmsg(LOG_ERR, "#%d could not allocate memory for buffer", s->id); 149a50c4a2fSJan Lentfer end_session(s); 150a50c4a2fSJan Lentfer } 151a50c4a2fSJan Lentfer 152a50c4a2fSJan Lentfer int 153a50c4a2fSJan Lentfer client_parse(struct session *s) 154a50c4a2fSJan Lentfer { 155a50c4a2fSJan Lentfer /* Reset any previous command. */ 156a50c4a2fSJan Lentfer s->cmd = CMD_NONE; 157a50c4a2fSJan Lentfer s->port = 0; 158a50c4a2fSJan Lentfer 159a50c4a2fSJan Lentfer /* Commands we are looking for are at least 4 chars long. */ 160a50c4a2fSJan Lentfer if (linelen < 4) 161a50c4a2fSJan Lentfer return (1); 162a50c4a2fSJan Lentfer 163a50c4a2fSJan Lentfer if (linebuf[0] == 'P' || linebuf[0] == 'p' || 164315a7da3SJan Lentfer linebuf[0] == 'E' || linebuf[0] == 'e') { 165315a7da3SJan Lentfer if (!client_parse_cmd(s)) 166315a7da3SJan Lentfer return (0); 167315a7da3SJan Lentfer 168315a7da3SJan Lentfer /* 169315a7da3SJan Lentfer * Allow active mode connections immediately, instead of 170315a7da3SJan Lentfer * waiting for a positive reply from the server. Some 171315a7da3SJan Lentfer * rare servers/proxies try to probe or setup the data 172315a7da3SJan Lentfer * connection before an actual transfer request. 173315a7da3SJan Lentfer */ 174315a7da3SJan Lentfer if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) 175315a7da3SJan Lentfer return (allow_data_connection(s)); 176315a7da3SJan Lentfer } 177a50c4a2fSJan Lentfer 178a50c4a2fSJan Lentfer if (anonymous_only && (linebuf[0] == 'U' || linebuf[0] == 'u')) 179a50c4a2fSJan Lentfer return (client_parse_anon(s)); 180a50c4a2fSJan Lentfer 181a50c4a2fSJan Lentfer return (1); 182a50c4a2fSJan Lentfer } 183a50c4a2fSJan Lentfer 184a50c4a2fSJan Lentfer int 185a50c4a2fSJan Lentfer client_parse_anon(struct session *s) 186a50c4a2fSJan Lentfer { 1872af0c10eSSamuel J. Greear size_t written; 1882af0c10eSSamuel J. Greear 189a50c4a2fSJan Lentfer if (strcasecmp("USER ftp\r\n", linebuf) != 0 && 190a50c4a2fSJan Lentfer strcasecmp("USER anonymous\r\n", linebuf) != 0) { 191a50c4a2fSJan Lentfer snprintf(linebuf, sizeof linebuf, 192a50c4a2fSJan Lentfer "500 Only anonymous FTP allowed\r\n"); 193a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); 194a50c4a2fSJan Lentfer 195a50c4a2fSJan Lentfer /* Talk back to the client ourself. */ 196a50c4a2fSJan Lentfer linelen = strlen(linebuf); 1972af0c10eSSamuel J. Greear written = write(s->client_fd, linebuf, linelen); 1982af0c10eSSamuel J. Greear if (written == -1) { 1992af0c10eSSamuel J. Greear logmsg(LOG_ERR, "#%d write failed", s->id); 2002af0c10eSSamuel J. Greear return (0); /* Session will be ended for us */ 2012af0c10eSSamuel J. Greear } else if (written < linelen) { 2022af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->server_fd, 2032af0c10eSSamuel J. Greear EVFILT_READ, EV_DISABLE, 0, 0, s); 2042af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->client_fd, 2052af0c10eSSamuel J. Greear EVFILT_WRITE, EV_ADD, 0, 0, s); 2062af0c10eSSamuel J. Greear buffer_data(s, &s->client, linebuf + written, 2072af0c10eSSamuel J. Greear linelen - written); 2082af0c10eSSamuel J. Greear return (1); 2092af0c10eSSamuel J. Greear } 210a50c4a2fSJan Lentfer 211a50c4a2fSJan Lentfer /* Clear buffer so it's not sent to the server. */ 212a50c4a2fSJan Lentfer linebuf[0] = '\0'; 213a50c4a2fSJan Lentfer linelen = 0; 214a50c4a2fSJan Lentfer } 215a50c4a2fSJan Lentfer 216a50c4a2fSJan Lentfer return (1); 217a50c4a2fSJan Lentfer } 218a50c4a2fSJan Lentfer 219a50c4a2fSJan Lentfer int 220a50c4a2fSJan Lentfer client_parse_cmd(struct session *s) 221a50c4a2fSJan Lentfer { 222a50c4a2fSJan Lentfer if (strncasecmp("PASV", linebuf, 4) == 0) 223a50c4a2fSJan Lentfer s->cmd = CMD_PASV; 224a50c4a2fSJan Lentfer else if (strncasecmp("PORT ", linebuf, 5) == 0) 225a50c4a2fSJan Lentfer s->cmd = CMD_PORT; 226a50c4a2fSJan Lentfer else if (strncasecmp("EPSV", linebuf, 4) == 0) 227a50c4a2fSJan Lentfer s->cmd = CMD_EPSV; 228a50c4a2fSJan Lentfer else if (strncasecmp("EPRT ", linebuf, 5) == 0) 229a50c4a2fSJan Lentfer s->cmd = CMD_EPRT; 230a50c4a2fSJan Lentfer else 231a50c4a2fSJan Lentfer return (1); 232a50c4a2fSJan Lentfer 233a50c4a2fSJan Lentfer if (ipv6_mode && (s->cmd == CMD_PASV || s->cmd == CMD_PORT)) { 234a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "PASV and PORT not allowed with IPv6"); 235a50c4a2fSJan Lentfer return (0); 236a50c4a2fSJan Lentfer } 237a50c4a2fSJan Lentfer 238a50c4a2fSJan Lentfer if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) { 239a50c4a2fSJan Lentfer s->port = parse_port(s->cmd); 240a50c4a2fSJan Lentfer if (s->port < MIN_PORT) { 241a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, 242a50c4a2fSJan Lentfer linebuf); 243a50c4a2fSJan Lentfer return (0); 244a50c4a2fSJan Lentfer } 245a50c4a2fSJan Lentfer s->proxy_port = pick_proxy_port(); 246a50c4a2fSJan Lentfer proxy_reply(s->cmd, sstosa(&s->proxy_ss), s->proxy_port); 247a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); 248a50c4a2fSJan Lentfer } 249a50c4a2fSJan Lentfer 250a50c4a2fSJan Lentfer return (1); 251a50c4a2fSJan Lentfer } 252a50c4a2fSJan Lentfer 253a50c4a2fSJan Lentfer void 2542af0c10eSSamuel J. Greear client_read(struct session *s) 255a50c4a2fSJan Lentfer { 2562af0c10eSSamuel J. Greear size_t buf_avail, bread, bwritten; 257a50c4a2fSJan Lentfer int n; 258a50c4a2fSJan Lentfer 259a50c4a2fSJan Lentfer do { 260a50c4a2fSJan Lentfer buf_avail = sizeof s->cbuf - s->cbuf_valid; 2612af0c10eSSamuel J. Greear bread = read(s->client_fd, s->cbuf + s->cbuf_valid, buf_avail); 2622af0c10eSSamuel J. Greear s->cbuf_valid += bread; 263a50c4a2fSJan Lentfer 264cae2835bSzrj while ((n = get_line(s->cbuf, &s->cbuf_valid)) > 0) { 265a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d client: %s", s->id, linebuf); 266a50c4a2fSJan Lentfer if (!client_parse(s)) { 267a50c4a2fSJan Lentfer end_session(s); 268a50c4a2fSJan Lentfer return; 269a50c4a2fSJan Lentfer } 2702af0c10eSSamuel J. Greear bwritten = write(s->server_fd, linebuf, linelen); 2712af0c10eSSamuel J. Greear if (bwritten == -1) { 2722af0c10eSSamuel J. Greear } else if (bwritten < linelen) { 2732af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->client_fd, 2742af0c10eSSamuel J. Greear EVFILT_READ, EV_DISABLE, 0, 0, s); 2752af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->server_fd, 2762af0c10eSSamuel J. Greear EVFILT_WRITE, EV_ADD, 0, 0, s); 2772af0c10eSSamuel J. Greear buffer_data(s, &s->server, linebuf + bwritten, 2782af0c10eSSamuel J. Greear linelen - bwritten); 2792af0c10eSSamuel J. Greear return; 2802af0c10eSSamuel J. Greear } 281a50c4a2fSJan Lentfer } 282a50c4a2fSJan Lentfer 283a50c4a2fSJan Lentfer if (n == -1) { 284a50c4a2fSJan Lentfer logmsg(LOG_ERR, "#%d client command too long or not" 285a50c4a2fSJan Lentfer " clean", s->id); 286a50c4a2fSJan Lentfer end_session(s); 287a50c4a2fSJan Lentfer return; 288a50c4a2fSJan Lentfer } 2892af0c10eSSamuel J. Greear } while (bread == buf_avail); 2902af0c10eSSamuel J. Greear } 2912af0c10eSSamuel J. Greear 2922af0c10eSSamuel J. Greear void 2932af0c10eSSamuel J. Greear client_write(struct session *s) 2942af0c10eSSamuel J. Greear { 2952af0c10eSSamuel J. Greear size_t written; 2962af0c10eSSamuel J. Greear 2972af0c10eSSamuel J. Greear written = write(s->client_fd, s->client.buffer + s->client.buffer_offset, 2982af0c10eSSamuel J. Greear s->client.buffer_size - s->client.buffer_offset); 2992af0c10eSSamuel J. Greear if (written == -1) { 3002af0c10eSSamuel J. Greear logmsg(LOG_ERR, "#%d write failed", s->id); 3012af0c10eSSamuel J. Greear end_session(s); 3022af0c10eSSamuel J. Greear } else if (written == (s->client.buffer_size - s->client.buffer_offset)) { 3032af0c10eSSamuel J. Greear free(s->client.buffer); 3042af0c10eSSamuel J. Greear s->client.buffer = NULL; 3052af0c10eSSamuel J. Greear s->client.buffer_size = 0; 3062af0c10eSSamuel J. Greear s->client.buffer_offset = 0; 3072af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->server_fd, 3082af0c10eSSamuel J. Greear EVFILT_READ, EV_ENABLE, 0, 0, s); 3092af0c10eSSamuel J. Greear } else { 3102af0c10eSSamuel J. Greear s->client.buffer_offset += written; 3112af0c10eSSamuel J. Greear } 312a50c4a2fSJan Lentfer } 313a50c4a2fSJan Lentfer 314a50c4a2fSJan Lentfer int 315a50c4a2fSJan Lentfer drop_privs(void) 316a50c4a2fSJan Lentfer { 317a50c4a2fSJan Lentfer struct passwd *pw; 318a50c4a2fSJan Lentfer 319a50c4a2fSJan Lentfer pw = getpwnam(NOPRIV_USER); 320a50c4a2fSJan Lentfer if (pw == NULL) 321a50c4a2fSJan Lentfer return (0); 322a50c4a2fSJan Lentfer 323a50c4a2fSJan Lentfer tzset(); 324a50c4a2fSJan Lentfer if (chroot(CHROOT_DIR) != 0 || chdir("/") != 0 || 325a50c4a2fSJan Lentfer setgroups(1, &pw->pw_gid) != 0 || 326a50c4a2fSJan Lentfer setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0 || 327a50c4a2fSJan Lentfer setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) 328a50c4a2fSJan Lentfer return (0); 329a50c4a2fSJan Lentfer 330a50c4a2fSJan Lentfer return (1); 331a50c4a2fSJan Lentfer } 332a50c4a2fSJan Lentfer 333a50c4a2fSJan Lentfer void 334a50c4a2fSJan Lentfer end_session(struct session *s) 335a50c4a2fSJan Lentfer { 336a50c4a2fSJan Lentfer int err; 337a50c4a2fSJan Lentfer 338a50c4a2fSJan Lentfer logmsg(LOG_INFO, "#%d ending session", s->id); 339a50c4a2fSJan Lentfer 340a50c4a2fSJan Lentfer if (s->client_fd != -1) 341a50c4a2fSJan Lentfer close(s->client_fd); 342a50c4a2fSJan Lentfer if (s->server_fd != -1) 343a50c4a2fSJan Lentfer close(s->server_fd); 344a50c4a2fSJan Lentfer 3452af0c10eSSamuel J. Greear if (s->client.buffer) 3462af0c10eSSamuel J. Greear free(s->client.buffer); 3472af0c10eSSamuel J. Greear if (s->server.buffer) 3482af0c10eSSamuel J. Greear free(s->server.buffer); 349a50c4a2fSJan Lentfer 350a50c4a2fSJan Lentfer /* Remove rulesets by commiting empty ones. */ 351a50c4a2fSJan Lentfer err = 0; 352a50c4a2fSJan Lentfer if (prepare_commit(s->id) == -1) 353a50c4a2fSJan Lentfer err = errno; 354a50c4a2fSJan Lentfer else if (do_commit() == -1) { 355a50c4a2fSJan Lentfer err = errno; 356a50c4a2fSJan Lentfer do_rollback(); 357a50c4a2fSJan Lentfer } 358a50c4a2fSJan Lentfer if (err) 359a50c4a2fSJan Lentfer logmsg(LOG_ERR, "#%d pf rule removal failed: %s", s->id, 360a50c4a2fSJan Lentfer strerror(err)); 361a50c4a2fSJan Lentfer 362a50c4a2fSJan Lentfer LIST_REMOVE(s, entry); 363a50c4a2fSJan Lentfer free(s); 364a50c4a2fSJan Lentfer session_count--; 365a50c4a2fSJan Lentfer } 366a50c4a2fSJan Lentfer 367*c35a932cSSascha Wildner void 368a50c4a2fSJan Lentfer exit_daemon(void) 369a50c4a2fSJan Lentfer { 370a50c4a2fSJan Lentfer struct session *s, *tmp; 371a50c4a2fSJan Lentfer 372a50c4a2fSJan Lentfer LIST_FOREACH_MUTABLE(s, &sessions, entry, tmp) { 373a50c4a2fSJan Lentfer end_session(s); 374a50c4a2fSJan Lentfer } 375a50c4a2fSJan Lentfer 376a50c4a2fSJan Lentfer if (daemonize) 377a50c4a2fSJan Lentfer closelog(); 378a50c4a2fSJan Lentfer 379a50c4a2fSJan Lentfer exit(0); 380a50c4a2fSJan Lentfer } 381a50c4a2fSJan Lentfer 382a50c4a2fSJan Lentfer int 383cae2835bSzrj get_line(char *buf, size_t *valid) 384a50c4a2fSJan Lentfer { 385a50c4a2fSJan Lentfer size_t i; 386a50c4a2fSJan Lentfer 387a50c4a2fSJan Lentfer if (*valid > MAX_LINE) 388a50c4a2fSJan Lentfer return (-1); 389a50c4a2fSJan Lentfer 390a50c4a2fSJan Lentfer /* Copy to linebuf while searching for a newline. */ 391a50c4a2fSJan Lentfer for (i = 0; i < *valid; i++) { 392a50c4a2fSJan Lentfer linebuf[i] = buf[i]; 393a50c4a2fSJan Lentfer if (buf[i] == '\0') 394a50c4a2fSJan Lentfer return (-1); 395a50c4a2fSJan Lentfer if (buf[i] == '\n') 396a50c4a2fSJan Lentfer break; 397a50c4a2fSJan Lentfer } 398a50c4a2fSJan Lentfer 399a50c4a2fSJan Lentfer if (i == *valid) { 400a50c4a2fSJan Lentfer /* No newline found. */ 401a50c4a2fSJan Lentfer linebuf[0] = '\0'; 402a50c4a2fSJan Lentfer linelen = 0; 403a50c4a2fSJan Lentfer if (i < MAX_LINE) 404a50c4a2fSJan Lentfer return (0); 405a50c4a2fSJan Lentfer return (-1); 406a50c4a2fSJan Lentfer } 407a50c4a2fSJan Lentfer 408a50c4a2fSJan Lentfer linelen = i + 1; 409a50c4a2fSJan Lentfer linebuf[linelen] = '\0'; 410a50c4a2fSJan Lentfer *valid -= linelen; 411a50c4a2fSJan Lentfer 412a50c4a2fSJan Lentfer /* Move leftovers to the start. */ 413a50c4a2fSJan Lentfer if (*valid != 0) 414a50c4a2fSJan Lentfer bcopy(buf + linelen, buf, *valid); 415a50c4a2fSJan Lentfer 416a50c4a2fSJan Lentfer return ((int)linelen); 417a50c4a2fSJan Lentfer } 418a50c4a2fSJan Lentfer 419a50c4a2fSJan Lentfer void 4202af0c10eSSamuel J. Greear handle_connection(const int listen_fd) 421a50c4a2fSJan Lentfer { 422a50c4a2fSJan Lentfer struct sockaddr_storage tmp_ss; 423a50c4a2fSJan Lentfer struct sockaddr *client_sa, *server_sa, *fixed_server_sa; 424a50c4a2fSJan Lentfer struct sockaddr *client_to_proxy_sa, *proxy_to_server_sa; 425a50c4a2fSJan Lentfer struct session *s; 426a50c4a2fSJan Lentfer socklen_t len; 427a50c4a2fSJan Lentfer int client_fd, fc, on; 428a50c4a2fSJan Lentfer 429a50c4a2fSJan Lentfer /* 430a50c4a2fSJan Lentfer * We _must_ accept the connection, otherwise libevent will keep 431a50c4a2fSJan Lentfer * coming back, and we will chew up all CPU. 432a50c4a2fSJan Lentfer */ 433a50c4a2fSJan Lentfer client_sa = sstosa(&tmp_ss); 434a50c4a2fSJan Lentfer len = sizeof(struct sockaddr_storage); 435a50c4a2fSJan Lentfer if ((client_fd = accept(listen_fd, client_sa, &len)) < 0) { 436a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "accept failed: %s", strerror(errno)); 437a50c4a2fSJan Lentfer return; 438a50c4a2fSJan Lentfer } 439a50c4a2fSJan Lentfer 440a50c4a2fSJan Lentfer /* Refuse connection if the maximum is reached. */ 441a50c4a2fSJan Lentfer if (session_count >= max_sessions) { 442a50c4a2fSJan Lentfer logmsg(LOG_ERR, "client limit (%d) reached, refusing " 443a50c4a2fSJan Lentfer "connection from %s", max_sessions, sock_ntop(client_sa)); 444a50c4a2fSJan Lentfer close(client_fd); 445a50c4a2fSJan Lentfer return; 446a50c4a2fSJan Lentfer } 447a50c4a2fSJan Lentfer 448a50c4a2fSJan Lentfer /* Allocate session and copy back the info from the accept(). */ 449a50c4a2fSJan Lentfer s = init_session(); 450a50c4a2fSJan Lentfer if (s == NULL) { 451a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "init_session failed"); 452a50c4a2fSJan Lentfer close(client_fd); 453a50c4a2fSJan Lentfer return; 454a50c4a2fSJan Lentfer } 455a50c4a2fSJan Lentfer s->client_fd = client_fd; 456a50c4a2fSJan Lentfer memcpy(sstosa(&s->client_ss), client_sa, client_sa->sa_len); 457a50c4a2fSJan Lentfer 458a50c4a2fSJan Lentfer /* Cast it once, and be done with it. */ 459a50c4a2fSJan Lentfer client_sa = sstosa(&s->client_ss); 460a50c4a2fSJan Lentfer server_sa = sstosa(&s->server_ss); 461a50c4a2fSJan Lentfer client_to_proxy_sa = sstosa(&tmp_ss); 462a50c4a2fSJan Lentfer proxy_to_server_sa = sstosa(&s->proxy_ss); 463a50c4a2fSJan Lentfer fixed_server_sa = sstosa(&fixed_server_ss); 464a50c4a2fSJan Lentfer 465a50c4a2fSJan Lentfer /* Log id/client early to ease debugging. */ 466a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d accepted connection from %s", s->id, 467a50c4a2fSJan Lentfer sock_ntop(client_sa)); 468a50c4a2fSJan Lentfer 469a50c4a2fSJan Lentfer /* 470a50c4a2fSJan Lentfer * Find out the real server and port that the client wanted. 471a50c4a2fSJan Lentfer */ 472a50c4a2fSJan Lentfer len = sizeof(struct sockaddr_storage); 473a50c4a2fSJan Lentfer if ((getsockname(s->client_fd, client_to_proxy_sa, &len)) < 0) { 474a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id, 475a50c4a2fSJan Lentfer strerror(errno)); 476a50c4a2fSJan Lentfer goto fail; 477a50c4a2fSJan Lentfer } 478a50c4a2fSJan Lentfer if (server_lookup(client_sa, client_to_proxy_sa, server_sa) != 0) { 479a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d server lookup failed (no rdr?)", s->id); 480a50c4a2fSJan Lentfer goto fail; 481a50c4a2fSJan Lentfer } 482a50c4a2fSJan Lentfer if (fixed_server) { 483a50c4a2fSJan Lentfer memcpy(sstosa(&s->orig_server_ss), server_sa, 484a50c4a2fSJan Lentfer server_sa->sa_len); 485a50c4a2fSJan Lentfer memcpy(server_sa, fixed_server_sa, fixed_server_sa->sa_len); 486a50c4a2fSJan Lentfer } 487a50c4a2fSJan Lentfer 488a50c4a2fSJan Lentfer /* XXX: check we are not connecting to ourself. */ 489a50c4a2fSJan Lentfer 490a50c4a2fSJan Lentfer /* 491a50c4a2fSJan Lentfer * Setup socket and connect to server. 492a50c4a2fSJan Lentfer */ 493a50c4a2fSJan Lentfer if ((s->server_fd = socket(server_sa->sa_family, SOCK_STREAM, 494a50c4a2fSJan Lentfer IPPROTO_TCP)) < 0) { 495a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d server socket failed: %s", s->id, 496a50c4a2fSJan Lentfer strerror(errno)); 497a50c4a2fSJan Lentfer goto fail; 498a50c4a2fSJan Lentfer } 499a50c4a2fSJan Lentfer if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss), 500a50c4a2fSJan Lentfer fixed_proxy_ss.ss_len) != 0) { 501a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d cannot bind fixed proxy address: %s", 502a50c4a2fSJan Lentfer s->id, strerror(errno)); 503a50c4a2fSJan Lentfer goto fail; 504a50c4a2fSJan Lentfer } 505a50c4a2fSJan Lentfer 506a50c4a2fSJan Lentfer /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */ 507a50c4a2fSJan Lentfer if ((fc = fcntl(s->server_fd, F_GETFL)) == -1 || 508a50c4a2fSJan Lentfer fcntl(s->server_fd, F_SETFL, fc | O_NONBLOCK) == -1) { 509a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d cannot mark socket non-blocking: %s", 510a50c4a2fSJan Lentfer s->id, strerror(errno)); 511a50c4a2fSJan Lentfer goto fail; 512a50c4a2fSJan Lentfer } 513a50c4a2fSJan Lentfer if (connect(s->server_fd, server_sa, server_sa->sa_len) < 0 && 514a50c4a2fSJan Lentfer errno != EINPROGRESS) { 515a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d proxy cannot connect to server %s: %s", 516a50c4a2fSJan Lentfer s->id, sock_ntop(server_sa), strerror(errno)); 517a50c4a2fSJan Lentfer goto fail; 518a50c4a2fSJan Lentfer } 519a50c4a2fSJan Lentfer 520a50c4a2fSJan Lentfer len = sizeof(struct sockaddr_storage); 521a50c4a2fSJan Lentfer if ((getsockname(s->server_fd, proxy_to_server_sa, &len)) < 0) { 522a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id, 523a50c4a2fSJan Lentfer strerror(errno)); 524a50c4a2fSJan Lentfer goto fail; 525a50c4a2fSJan Lentfer } 526a50c4a2fSJan Lentfer 527a50c4a2fSJan Lentfer logmsg(LOG_INFO, "#%d FTP session %d/%d started: client %s to server " 528a50c4a2fSJan Lentfer "%s via proxy %s ", s->id, session_count, max_sessions, 529a50c4a2fSJan Lentfer sock_ntop(client_sa), sock_ntop(server_sa), 530a50c4a2fSJan Lentfer sock_ntop(proxy_to_server_sa)); 531a50c4a2fSJan Lentfer 532a50c4a2fSJan Lentfer /* Keepalive is nice, but don't care if it fails. */ 533a50c4a2fSJan Lentfer on = 1; 534a50c4a2fSJan Lentfer setsockopt(s->client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 535a50c4a2fSJan Lentfer sizeof on); 536a50c4a2fSJan Lentfer setsockopt(s->server_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 537a50c4a2fSJan Lentfer sizeof on); 538a50c4a2fSJan Lentfer 5392af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->client_fd, EVFILT_READ, EV_ADD, 0, 0, s); 5402af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->server_fd, EVFILT_READ, EV_ADD, 0, 0, s); 541a50c4a2fSJan Lentfer 542a50c4a2fSJan Lentfer return; 543a50c4a2fSJan Lentfer 544a50c4a2fSJan Lentfer fail: 545a50c4a2fSJan Lentfer end_session(s); 546a50c4a2fSJan Lentfer } 547a50c4a2fSJan Lentfer 548a50c4a2fSJan Lentfer void 5492af0c10eSSamuel J. Greear handle_signal(int sig) 550a50c4a2fSJan Lentfer { 551a50c4a2fSJan Lentfer /* 5522af0c10eSSamuel J. Greear * Signal handler rules don't apply. 553a50c4a2fSJan Lentfer */ 554a50c4a2fSJan Lentfer 555a50c4a2fSJan Lentfer logmsg(LOG_ERR, "%s exiting on signal %d", __progname, sig); 556a50c4a2fSJan Lentfer 557a50c4a2fSJan Lentfer exit_daemon(); 558a50c4a2fSJan Lentfer } 559a50c4a2fSJan Lentfer 560a50c4a2fSJan Lentfer 561a50c4a2fSJan Lentfer struct session * 562a50c4a2fSJan Lentfer init_session(void) 563a50c4a2fSJan Lentfer { 564a50c4a2fSJan Lentfer struct session *s; 565a50c4a2fSJan Lentfer 566a50c4a2fSJan Lentfer s = calloc(1, sizeof(struct session)); 567a50c4a2fSJan Lentfer if (s == NULL) 568a50c4a2fSJan Lentfer return (NULL); 569a50c4a2fSJan Lentfer 570a50c4a2fSJan Lentfer s->id = id_count++; 571a50c4a2fSJan Lentfer s->client_fd = -1; 572a50c4a2fSJan Lentfer s->server_fd = -1; 573a50c4a2fSJan Lentfer s->cbuf[0] = '\0'; 574a50c4a2fSJan Lentfer s->cbuf_valid = 0; 575a50c4a2fSJan Lentfer s->sbuf[0] = '\0'; 576a50c4a2fSJan Lentfer s->sbuf_valid = 0; 5772af0c10eSSamuel J. Greear s->client.buffer = NULL; 5782af0c10eSSamuel J. Greear s->client.buffer_size = 0; 5792af0c10eSSamuel J. Greear s->client.buffer_offset = 0; 5802af0c10eSSamuel J. Greear s->server.buffer = NULL; 5812af0c10eSSamuel J. Greear s->server.buffer_size = 0; 5822af0c10eSSamuel J. Greear s->server.buffer_offset = 0; 583a50c4a2fSJan Lentfer s->cmd = CMD_NONE; 584a50c4a2fSJan Lentfer s->port = 0; 585a50c4a2fSJan Lentfer 586a50c4a2fSJan Lentfer LIST_INSERT_HEAD(&sessions, s, entry); 587a50c4a2fSJan Lentfer session_count++; 588a50c4a2fSJan Lentfer 589a50c4a2fSJan Lentfer return (s); 590a50c4a2fSJan Lentfer } 591a50c4a2fSJan Lentfer 592a50c4a2fSJan Lentfer void 593a50c4a2fSJan Lentfer logmsg(int pri, const char *message, ...) 594a50c4a2fSJan Lentfer { 595a50c4a2fSJan Lentfer va_list ap; 596a50c4a2fSJan Lentfer 597a50c4a2fSJan Lentfer if (pri > loglevel) 598a50c4a2fSJan Lentfer return; 599a50c4a2fSJan Lentfer 600a50c4a2fSJan Lentfer va_start(ap, message); 601a50c4a2fSJan Lentfer 602a50c4a2fSJan Lentfer if (daemonize) 603a50c4a2fSJan Lentfer /* syslog does its own vissing. */ 604a50c4a2fSJan Lentfer vsyslog(pri, message, ap); 605a50c4a2fSJan Lentfer else { 606a50c4a2fSJan Lentfer char buf[MAX_LOGLINE]; 607a50c4a2fSJan Lentfer char visbuf[2 * MAX_LOGLINE]; 608a50c4a2fSJan Lentfer 609a50c4a2fSJan Lentfer /* We don't care about truncation. */ 610a50c4a2fSJan Lentfer vsnprintf(buf, sizeof buf, message, ap); 611a50c4a2fSJan Lentfer strnvis(visbuf, buf, sizeof visbuf, VIS_CSTYLE | VIS_NL); 612a50c4a2fSJan Lentfer fprintf(stderr, "%s\n", visbuf); 613a50c4a2fSJan Lentfer } 614a50c4a2fSJan Lentfer 615a50c4a2fSJan Lentfer va_end(ap); 616a50c4a2fSJan Lentfer } 617a50c4a2fSJan Lentfer 618a50c4a2fSJan Lentfer int 619a50c4a2fSJan Lentfer main(int argc, char *argv[]) 620a50c4a2fSJan Lentfer { 621a50c4a2fSJan Lentfer struct rlimit rlp; 622a50c4a2fSJan Lentfer struct addrinfo hints, *res; 6232af0c10eSSamuel J. Greear int kq, ch, error, listenfd, on; 624a50c4a2fSJan Lentfer const char *errstr; 625a50c4a2fSJan Lentfer 626a50c4a2fSJan Lentfer /* Defaults. */ 627a50c4a2fSJan Lentfer anonymous_only = 0; 628a50c4a2fSJan Lentfer daemonize = 1; 629a50c4a2fSJan Lentfer fixed_proxy = NULL; 630a50c4a2fSJan Lentfer fixed_server = NULL; 631a50c4a2fSJan Lentfer fixed_server_port = "21"; 632a50c4a2fSJan Lentfer ipv6_mode = 0; 633a50c4a2fSJan Lentfer listen_ip = NULL; 634a50c4a2fSJan Lentfer listen_port = "8021"; 635a50c4a2fSJan Lentfer loglevel = LOG_NOTICE; 636a50c4a2fSJan Lentfer max_sessions = 100; 637a50c4a2fSJan Lentfer qname = NULL; 638a50c4a2fSJan Lentfer rfc_mode = 0; 639315a7da3SJan Lentfer tagname = NULL; 640a50c4a2fSJan Lentfer timeout = 24 * 3600; 641a50c4a2fSJan Lentfer verbose = 0; 642a50c4a2fSJan Lentfer 643a50c4a2fSJan Lentfer /* Other initialization. */ 644a50c4a2fSJan Lentfer id_count = 1; 645a50c4a2fSJan Lentfer session_count = 0; 6462af0c10eSSamuel J. Greear nchanges = 0; 647a50c4a2fSJan Lentfer 648315a7da3SJan Lentfer while ((ch = getopt(argc, argv, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) { 649a50c4a2fSJan Lentfer switch (ch) { 650a50c4a2fSJan Lentfer case '6': 651a50c4a2fSJan Lentfer ipv6_mode = 1; 652a50c4a2fSJan Lentfer break; 653a50c4a2fSJan Lentfer case 'A': 654a50c4a2fSJan Lentfer anonymous_only = 1; 655a50c4a2fSJan Lentfer break; 656a50c4a2fSJan Lentfer case 'a': 657a50c4a2fSJan Lentfer fixed_proxy = optarg; 658a50c4a2fSJan Lentfer break; 659a50c4a2fSJan Lentfer case 'b': 660a50c4a2fSJan Lentfer listen_ip = optarg; 661a50c4a2fSJan Lentfer break; 662a50c4a2fSJan Lentfer case 'D': 663a50c4a2fSJan Lentfer loglevel = strtonum(optarg, LOG_EMERG, LOG_DEBUG, 664a50c4a2fSJan Lentfer &errstr); 665a50c4a2fSJan Lentfer if (errstr) 666a50c4a2fSJan Lentfer errx(1, "loglevel %s", errstr); 667a50c4a2fSJan Lentfer break; 668a50c4a2fSJan Lentfer case 'd': 669a50c4a2fSJan Lentfer daemonize = 0; 670a50c4a2fSJan Lentfer break; 671a50c4a2fSJan Lentfer case 'm': 672a50c4a2fSJan Lentfer max_sessions = strtonum(optarg, 1, 500, &errstr); 673a50c4a2fSJan Lentfer if (errstr) 674a50c4a2fSJan Lentfer errx(1, "max sessions %s", errstr); 675a50c4a2fSJan Lentfer break; 676a50c4a2fSJan Lentfer case 'P': 677a50c4a2fSJan Lentfer fixed_server_port = optarg; 678a50c4a2fSJan Lentfer break; 679a50c4a2fSJan Lentfer case 'p': 680a50c4a2fSJan Lentfer listen_port = optarg; 681a50c4a2fSJan Lentfer break; 682a50c4a2fSJan Lentfer case 'q': 683a50c4a2fSJan Lentfer if (strlen(optarg) >= PF_QNAME_SIZE) 684a50c4a2fSJan Lentfer errx(1, "queuename too long"); 685a50c4a2fSJan Lentfer qname = optarg; 686a50c4a2fSJan Lentfer break; 687a50c4a2fSJan Lentfer case 'R': 688a50c4a2fSJan Lentfer fixed_server = optarg; 689a50c4a2fSJan Lentfer break; 690a50c4a2fSJan Lentfer case 'r': 691a50c4a2fSJan Lentfer rfc_mode = 1; 692a50c4a2fSJan Lentfer break; 693315a7da3SJan Lentfer case 'T': 694315a7da3SJan Lentfer if (strlen(optarg) >= PF_TAG_NAME_SIZE) 695315a7da3SJan Lentfer errx(1, "tagname too long"); 696315a7da3SJan Lentfer tagname = optarg; 697315a7da3SJan Lentfer break; 698a50c4a2fSJan Lentfer case 't': 699a50c4a2fSJan Lentfer timeout = strtonum(optarg, 0, 86400, &errstr); 700a50c4a2fSJan Lentfer if (errstr) 701a50c4a2fSJan Lentfer errx(1, "timeout %s", errstr); 702a50c4a2fSJan Lentfer break; 703a50c4a2fSJan Lentfer case 'v': 704a50c4a2fSJan Lentfer verbose++; 705a50c4a2fSJan Lentfer if (verbose > 2) 706a50c4a2fSJan Lentfer usage(); 707a50c4a2fSJan Lentfer break; 708a50c4a2fSJan Lentfer default: 709a50c4a2fSJan Lentfer usage(); 710a50c4a2fSJan Lentfer } 711a50c4a2fSJan Lentfer } 712a50c4a2fSJan Lentfer 713a50c4a2fSJan Lentfer if (listen_ip == NULL) 714a50c4a2fSJan Lentfer listen_ip = ipv6_mode ? "::1" : "127.0.0.1"; 715a50c4a2fSJan Lentfer 716a50c4a2fSJan Lentfer /* Check for root to save the user from cryptic failure messages. */ 717a50c4a2fSJan Lentfer if (getuid() != 0) 718a50c4a2fSJan Lentfer errx(1, "needs to start as root"); 719a50c4a2fSJan Lentfer 720a50c4a2fSJan Lentfer /* Raise max. open files limit to satisfy max. sessions. */ 721a50c4a2fSJan Lentfer rlp.rlim_cur = rlp.rlim_max = (2 * max_sessions) + 10; 722a50c4a2fSJan Lentfer if (setrlimit(RLIMIT_NOFILE, &rlp) == -1) 723a50c4a2fSJan Lentfer err(1, "setrlimit"); 724a50c4a2fSJan Lentfer 725a50c4a2fSJan Lentfer if (fixed_proxy) { 726a50c4a2fSJan Lentfer memset(&hints, 0, sizeof hints); 727a50c4a2fSJan Lentfer hints.ai_flags = AI_NUMERICHOST; 728a50c4a2fSJan Lentfer hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET; 729a50c4a2fSJan Lentfer hints.ai_socktype = SOCK_STREAM; 730a50c4a2fSJan Lentfer error = getaddrinfo(fixed_proxy, NULL, &hints, &res); 731a50c4a2fSJan Lentfer if (error) 732a50c4a2fSJan Lentfer errx(1, "getaddrinfo fixed proxy address failed: %s", 733a50c4a2fSJan Lentfer gai_strerror(error)); 734a50c4a2fSJan Lentfer memcpy(&fixed_proxy_ss, res->ai_addr, res->ai_addrlen); 735a50c4a2fSJan Lentfer logmsg(LOG_INFO, "using %s to connect to servers", 736a50c4a2fSJan Lentfer sock_ntop(sstosa(&fixed_proxy_ss))); 737a50c4a2fSJan Lentfer freeaddrinfo(res); 738a50c4a2fSJan Lentfer } 739a50c4a2fSJan Lentfer 740a50c4a2fSJan Lentfer if (fixed_server) { 741a50c4a2fSJan Lentfer memset(&hints, 0, sizeof hints); 742a50c4a2fSJan Lentfer hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET; 743a50c4a2fSJan Lentfer hints.ai_socktype = SOCK_STREAM; 744a50c4a2fSJan Lentfer error = getaddrinfo(fixed_server, fixed_server_port, &hints, 745a50c4a2fSJan Lentfer &res); 746a50c4a2fSJan Lentfer if (error) 747a50c4a2fSJan Lentfer errx(1, "getaddrinfo fixed server address failed: %s", 748a50c4a2fSJan Lentfer gai_strerror(error)); 749a50c4a2fSJan Lentfer memcpy(&fixed_server_ss, res->ai_addr, res->ai_addrlen); 750a50c4a2fSJan Lentfer logmsg(LOG_INFO, "using fixed server %s", 751a50c4a2fSJan Lentfer sock_ntop(sstosa(&fixed_server_ss))); 752a50c4a2fSJan Lentfer freeaddrinfo(res); 753a50c4a2fSJan Lentfer } 754a50c4a2fSJan Lentfer 755a50c4a2fSJan Lentfer /* Setup listener. */ 756a50c4a2fSJan Lentfer memset(&hints, 0, sizeof hints); 757a50c4a2fSJan Lentfer hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; 758a50c4a2fSJan Lentfer hints.ai_family = ipv6_mode ? AF_INET6 : AF_INET; 759a50c4a2fSJan Lentfer hints.ai_socktype = SOCK_STREAM; 760a50c4a2fSJan Lentfer error = getaddrinfo(listen_ip, listen_port, &hints, &res); 761a50c4a2fSJan Lentfer if (error) 762a50c4a2fSJan Lentfer errx(1, "getaddrinfo listen address failed: %s", 763a50c4a2fSJan Lentfer gai_strerror(error)); 764a50c4a2fSJan Lentfer if ((listenfd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP)) == -1) 765a50c4a2fSJan Lentfer errx(1, "socket failed"); 766a50c4a2fSJan Lentfer on = 1; 767a50c4a2fSJan Lentfer if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, 768a50c4a2fSJan Lentfer sizeof on) != 0) 769a50c4a2fSJan Lentfer err(1, "setsockopt failed"); 770a50c4a2fSJan Lentfer if (bind(listenfd, (struct sockaddr *)res->ai_addr, 771a50c4a2fSJan Lentfer (socklen_t)res->ai_addrlen) != 0) 772a50c4a2fSJan Lentfer err(1, "bind failed"); 773a50c4a2fSJan Lentfer if (listen(listenfd, TCP_BACKLOG) != 0) 774a50c4a2fSJan Lentfer err(1, "listen failed"); 775a50c4a2fSJan Lentfer freeaddrinfo(res); 776a50c4a2fSJan Lentfer 777a50c4a2fSJan Lentfer /* Initialize pf. */ 778315a7da3SJan Lentfer init_filter(qname, tagname, verbose); 779a50c4a2fSJan Lentfer 780a50c4a2fSJan Lentfer if (daemonize) { 781a50c4a2fSJan Lentfer if (daemon(0, 0) == -1) 782a50c4a2fSJan Lentfer err(1, "cannot daemonize"); 783a50c4a2fSJan Lentfer openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); 784a50c4a2fSJan Lentfer } 785a50c4a2fSJan Lentfer 786a50c4a2fSJan Lentfer /* Use logmsg for output from here on. */ 787a50c4a2fSJan Lentfer 788a50c4a2fSJan Lentfer if (!drop_privs()) { 789a50c4a2fSJan Lentfer logmsg(LOG_ERR, "cannot drop privileges: %s", strerror(errno)); 790a50c4a2fSJan Lentfer exit(1); 791a50c4a2fSJan Lentfer } 792a50c4a2fSJan Lentfer 7932af0c10eSSamuel J. Greear if ((kq = kqueue()) == -1) { 7942af0c10eSSamuel J. Greear logmsg(LOG_ERR, "cannot create new kqueue(2): %s", strerror(errno)); 7952af0c10eSSamuel J. Greear exit(1); 7962af0c10eSSamuel J. Greear } 797a50c4a2fSJan Lentfer 798a50c4a2fSJan Lentfer /* Setup signal handler. */ 799a50c4a2fSJan Lentfer signal(SIGPIPE, SIG_IGN); 8002af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 8012af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 8022af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 803a50c4a2fSJan Lentfer 8042af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], listenfd, EVFILT_READ, EV_ADD, 0, 0, NULL); 805a50c4a2fSJan Lentfer 806a50c4a2fSJan Lentfer logmsg(LOG_NOTICE, "listening on %s port %s", listen_ip, listen_port); 807a50c4a2fSJan Lentfer 808a50c4a2fSJan Lentfer /* Vroom, vroom. */ 8092af0c10eSSamuel J. Greear for ( ; ; ) { 8102af0c10eSSamuel J. Greear int i, nevents; 8112af0c10eSSamuel J. Greear struct kevent events[KQ_NEVENTS], *event; 8122af0c10eSSamuel J. Greear struct session *s; 813a50c4a2fSJan Lentfer 8142af0c10eSSamuel J. Greear nevents = kevent(kq, &changes[0], nchanges, &events[0], 8152af0c10eSSamuel J. Greear KQ_NEVENTS, NULL); 8162af0c10eSSamuel J. Greear if (nevents == -1) { 8172af0c10eSSamuel J. Greear logmsg(LOG_ERR, "cannot create new kqueue(2): %s", strerror(errno)); 8182af0c10eSSamuel J. Greear exit(1); 8192af0c10eSSamuel J. Greear } 8202af0c10eSSamuel J. Greear nchanges = 0; 8212af0c10eSSamuel J. Greear 8222af0c10eSSamuel J. Greear for (i = 0; i < nevents; ++i) { 8232af0c10eSSamuel J. Greear event = &events[i]; 8242af0c10eSSamuel J. Greear 8252af0c10eSSamuel J. Greear if (event->filter == EVFILT_SIGNAL) { 8262af0c10eSSamuel J. Greear handle_signal(event->ident); 8272af0c10eSSamuel J. Greear continue; 8282af0c10eSSamuel J. Greear } 8292af0c10eSSamuel J. Greear 8302af0c10eSSamuel J. Greear if (event->ident == listenfd) { 8312af0c10eSSamuel J. Greear /* Handle new connection */ 8322af0c10eSSamuel J. Greear handle_connection(event->ident); 8332af0c10eSSamuel J. Greear } else { 8342af0c10eSSamuel J. Greear /* Process existing connection */ 8352af0c10eSSamuel J. Greear s = (struct session *)event->udata; 8362af0c10eSSamuel J. Greear 8372af0c10eSSamuel J. Greear if (event->ident == s->client_fd) { 8382af0c10eSSamuel J. Greear if (event->filter == EVFILT_READ) 8392af0c10eSSamuel J. Greear client_read(s); 8402af0c10eSSamuel J. Greear else 8412af0c10eSSamuel J. Greear client_write(s); 8422af0c10eSSamuel J. Greear } else { 8432af0c10eSSamuel J. Greear if (event->filter == EVFILT_READ) 8442af0c10eSSamuel J. Greear server_read(s); 8452af0c10eSSamuel J. Greear else 8462af0c10eSSamuel J. Greear server_write(s); 8472af0c10eSSamuel J. Greear } 8482af0c10eSSamuel J. Greear } 8492af0c10eSSamuel J. Greear 8502af0c10eSSamuel J. Greear /* The next loop might overflow changes */ 8512af0c10eSSamuel J. Greear if (nchanges > KQ_NEVENTS - 4) 8522af0c10eSSamuel J. Greear break; 8532af0c10eSSamuel J. Greear } 8542af0c10eSSamuel J. Greear } 8552af0c10eSSamuel J. Greear 856a50c4a2fSJan Lentfer exit_daemon(); 857a50c4a2fSJan Lentfer 858a50c4a2fSJan Lentfer /* NOTREACHED */ 859a50c4a2fSJan Lentfer return (1); 860a50c4a2fSJan Lentfer } 861a50c4a2fSJan Lentfer 862a50c4a2fSJan Lentfer u_int16_t 863a50c4a2fSJan Lentfer parse_port(int mode) 864a50c4a2fSJan Lentfer { 865a50c4a2fSJan Lentfer unsigned int port, v[6]; 866a50c4a2fSJan Lentfer int n; 867a50c4a2fSJan Lentfer char *p; 868a50c4a2fSJan Lentfer 869a50c4a2fSJan Lentfer /* Find the last space or left-parenthesis. */ 870a50c4a2fSJan Lentfer for (p = linebuf + linelen; p > linebuf; p--) 871a50c4a2fSJan Lentfer if (*p == ' ' || *p == '(') 872a50c4a2fSJan Lentfer break; 873a50c4a2fSJan Lentfer if (p == linebuf) 874a50c4a2fSJan Lentfer return (0); 875a50c4a2fSJan Lentfer 876a50c4a2fSJan Lentfer switch (mode) { 877a50c4a2fSJan Lentfer case CMD_PORT: 878a50c4a2fSJan Lentfer n = sscanf(p, " %u,%u,%u,%u,%u,%u", &v[0], &v[1], &v[2], 879a50c4a2fSJan Lentfer &v[3], &v[4], &v[5]); 880a50c4a2fSJan Lentfer if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 && 881a50c4a2fSJan Lentfer v[3] < 256 && v[4] < 256 && v[5] < 256) 882a50c4a2fSJan Lentfer return ((v[4] << 8) | v[5]); 883a50c4a2fSJan Lentfer break; 884a50c4a2fSJan Lentfer case CMD_PASV: 885a50c4a2fSJan Lentfer n = sscanf(p, "(%u,%u,%u,%u,%u,%u)", &v[0], &v[1], &v[2], 886a50c4a2fSJan Lentfer &v[3], &v[4], &v[5]); 887a50c4a2fSJan Lentfer if (n == 6 && v[0] < 256 && v[1] < 256 && v[2] < 256 && 888a50c4a2fSJan Lentfer v[3] < 256 && v[4] < 256 && v[5] < 256) 889a50c4a2fSJan Lentfer return ((v[4] << 8) | v[5]); 890a50c4a2fSJan Lentfer break; 891a50c4a2fSJan Lentfer case CMD_EPSV: 892a50c4a2fSJan Lentfer n = sscanf(p, "(|||%u|)", &port); 893a50c4a2fSJan Lentfer if (n == 1 && port < 65536) 894a50c4a2fSJan Lentfer return (port); 895a50c4a2fSJan Lentfer break; 896a50c4a2fSJan Lentfer case CMD_EPRT: 897a50c4a2fSJan Lentfer n = sscanf(p, " |1|%u.%u.%u.%u|%u|", &v[0], &v[1], &v[2], 898a50c4a2fSJan Lentfer &v[3], &port); 899a50c4a2fSJan Lentfer if (n == 5 && v[0] < 256 && v[1] < 256 && v[2] < 256 && 900a50c4a2fSJan Lentfer v[3] < 256 && port < 65536) 901a50c4a2fSJan Lentfer return (port); 902a50c4a2fSJan Lentfer n = sscanf(p, " |2|%*[a-fA-F0-9:]|%u|", &port); 903a50c4a2fSJan Lentfer if (n == 1 && port < 65536) 904a50c4a2fSJan Lentfer return (port); 905a50c4a2fSJan Lentfer break; 906a50c4a2fSJan Lentfer default: 907a50c4a2fSJan Lentfer return (0); 908a50c4a2fSJan Lentfer } 909a50c4a2fSJan Lentfer 910a50c4a2fSJan Lentfer return (0); 911a50c4a2fSJan Lentfer } 912a50c4a2fSJan Lentfer 913a50c4a2fSJan Lentfer u_int16_t 914a50c4a2fSJan Lentfer pick_proxy_port(void) 915a50c4a2fSJan Lentfer { 916a50c4a2fSJan Lentfer /* Random should be good enough for avoiding port collisions. */ 917a50c4a2fSJan Lentfer return (IPPORT_HIFIRSTAUTO + (arc4random() % 918a50c4a2fSJan Lentfer (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO))); 919a50c4a2fSJan Lentfer } 920a50c4a2fSJan Lentfer 921a50c4a2fSJan Lentfer void 922a50c4a2fSJan Lentfer proxy_reply(int cmd, struct sockaddr *sa, u_int16_t port) 923a50c4a2fSJan Lentfer { 924a50c4a2fSJan Lentfer int i, r; 925a50c4a2fSJan Lentfer 926a50c4a2fSJan Lentfer switch (cmd) { 927a50c4a2fSJan Lentfer case CMD_PORT: 928a50c4a2fSJan Lentfer r = snprintf(linebuf, sizeof linebuf, 929a50c4a2fSJan Lentfer "PORT %s,%u,%u\r\n", sock_ntop(sa), port / 256, 930a50c4a2fSJan Lentfer port % 256); 931a50c4a2fSJan Lentfer break; 932a50c4a2fSJan Lentfer case CMD_PASV: 933a50c4a2fSJan Lentfer r = snprintf(linebuf, sizeof linebuf, 934a50c4a2fSJan Lentfer "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa), 935a50c4a2fSJan Lentfer port / 256, port % 256); 936a50c4a2fSJan Lentfer break; 937a50c4a2fSJan Lentfer case CMD_EPRT: 938a50c4a2fSJan Lentfer if (sa->sa_family == AF_INET) 939a50c4a2fSJan Lentfer r = snprintf(linebuf, sizeof linebuf, 940a50c4a2fSJan Lentfer "EPRT |1|%s|%u|\r\n", sock_ntop(sa), port); 941a50c4a2fSJan Lentfer else if (sa->sa_family == AF_INET6) 942a50c4a2fSJan Lentfer r = snprintf(linebuf, sizeof linebuf, 943a50c4a2fSJan Lentfer "EPRT |2|%s|%u|\r\n", sock_ntop(sa), port); 944a50c4a2fSJan Lentfer break; 945a50c4a2fSJan Lentfer case CMD_EPSV: 946a50c4a2fSJan Lentfer r = snprintf(linebuf, sizeof linebuf, 947a50c4a2fSJan Lentfer "229 Entering Extended Passive Mode (|||%u|)\r\n", port); 948a50c4a2fSJan Lentfer break; 949a50c4a2fSJan Lentfer } 950a50c4a2fSJan Lentfer 951a50c4a2fSJan Lentfer if (r < 0 || r >= sizeof linebuf) { 952a50c4a2fSJan Lentfer logmsg(LOG_ERR, "proxy_reply failed: %d", r); 953a50c4a2fSJan Lentfer linebuf[0] = '\0'; 954a50c4a2fSJan Lentfer linelen = 0; 955a50c4a2fSJan Lentfer return; 956a50c4a2fSJan Lentfer } 957a50c4a2fSJan Lentfer linelen = (size_t)r; 958a50c4a2fSJan Lentfer 959a50c4a2fSJan Lentfer if (cmd == CMD_PORT || cmd == CMD_PASV) { 960a50c4a2fSJan Lentfer /* Replace dots in IP address with commas. */ 961a50c4a2fSJan Lentfer for (i = 0; i < linelen; i++) 962a50c4a2fSJan Lentfer if (linebuf[i] == '.') 963a50c4a2fSJan Lentfer linebuf[i] = ','; 964a50c4a2fSJan Lentfer } 965a50c4a2fSJan Lentfer } 966a50c4a2fSJan Lentfer 967a50c4a2fSJan Lentfer int 968a50c4a2fSJan Lentfer server_parse(struct session *s) 969a50c4a2fSJan Lentfer { 970315a7da3SJan Lentfer if (s->cmd == CMD_NONE || linelen < 4 || linebuf[0] != '2') 971315a7da3SJan Lentfer goto out; 972315a7da3SJan Lentfer 973315a7da3SJan Lentfer if ((s->cmd == CMD_PASV && strncmp("227 ", linebuf, 4) == 0) || 974315a7da3SJan Lentfer (s->cmd == CMD_EPSV && strncmp("229 ", linebuf, 4) == 0)) 975315a7da3SJan Lentfer return (allow_data_connection(s)); 976315a7da3SJan Lentfer 977315a7da3SJan Lentfer out: 978315a7da3SJan Lentfer s->cmd = CMD_NONE; 979315a7da3SJan Lentfer s->port = 0; 980315a7da3SJan Lentfer 981315a7da3SJan Lentfer return (1); 982315a7da3SJan Lentfer } 983315a7da3SJan Lentfer 984315a7da3SJan Lentfer int 985315a7da3SJan Lentfer allow_data_connection(struct session *s) 986315a7da3SJan Lentfer { 987a50c4a2fSJan Lentfer struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa; 988a50c4a2fSJan Lentfer int prepared = 0; 989a50c4a2fSJan Lentfer 990a50c4a2fSJan Lentfer if (s->cmd == CMD_NONE || linelen < 4 || linebuf[0] != '2') 991a50c4a2fSJan Lentfer goto out; 992a50c4a2fSJan Lentfer 993a50c4a2fSJan Lentfer /* 994a50c4a2fSJan Lentfer * The pf rules below do quite some NAT rewriting, to keep up 995a50c4a2fSJan Lentfer * appearances. Points to keep in mind: 996a50c4a2fSJan Lentfer * 1) The client must think it's talking to the real server, 997a50c4a2fSJan Lentfer * for both control and data connections. Transparently. 998a50c4a2fSJan Lentfer * 2) The server must think that the proxy is the client. 999a50c4a2fSJan Lentfer * 3) Source and destination ports are rewritten to minimize 1000a50c4a2fSJan Lentfer * port collisions, to aid security (some systems pick weak 1001a50c4a2fSJan Lentfer * ports) or to satisfy RFC requirements (source port 20). 1002a50c4a2fSJan Lentfer */ 1003a50c4a2fSJan Lentfer 1004a50c4a2fSJan Lentfer /* Cast this once, to make code below it more readable. */ 1005a50c4a2fSJan Lentfer client_sa = sstosa(&s->client_ss); 1006a50c4a2fSJan Lentfer server_sa = sstosa(&s->server_ss); 1007a50c4a2fSJan Lentfer proxy_sa = sstosa(&s->proxy_ss); 1008a50c4a2fSJan Lentfer if (fixed_server) 1009a50c4a2fSJan Lentfer /* Fixed server: data connections must appear to come 1010a50c4a2fSJan Lentfer from / go to the original server, not the fixed one. */ 1011a50c4a2fSJan Lentfer orig_sa = sstosa(&s->orig_server_ss); 1012a50c4a2fSJan Lentfer else 1013a50c4a2fSJan Lentfer /* Server not fixed: orig_server == server. */ 1014a50c4a2fSJan Lentfer orig_sa = sstosa(&s->server_ss); 1015a50c4a2fSJan Lentfer 1016a50c4a2fSJan Lentfer /* Passive modes. */ 1017315a7da3SJan Lentfer if (s->cmd == CMD_PASV || s->cmd == CMD_EPSV) { 1018a50c4a2fSJan Lentfer s->port = parse_port(s->cmd); 1019a50c4a2fSJan Lentfer if (s->port < MIN_PORT) { 1020a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, 1021a50c4a2fSJan Lentfer linebuf); 1022a50c4a2fSJan Lentfer return (0); 1023a50c4a2fSJan Lentfer } 1024a50c4a2fSJan Lentfer s->proxy_port = pick_proxy_port(); 1025a50c4a2fSJan Lentfer logmsg(LOG_INFO, "#%d passive: client to server port %d" 1026a50c4a2fSJan Lentfer " via port %d", s->id, s->port, s->proxy_port); 1027a50c4a2fSJan Lentfer 1028a50c4a2fSJan Lentfer if (prepare_commit(s->id) == -1) 1029a50c4a2fSJan Lentfer goto fail; 1030a50c4a2fSJan Lentfer prepared = 1; 1031a50c4a2fSJan Lentfer 1032a50c4a2fSJan Lentfer proxy_reply(s->cmd, orig_sa, s->proxy_port); 1033a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); 1034a50c4a2fSJan Lentfer 1035a50c4a2fSJan Lentfer /* rdr from $client to $orig_server port $proxy_port -> $server 1036a50c4a2fSJan Lentfer port $port */ 1037a50c4a2fSJan Lentfer if (add_rdr(s->id, client_sa, orig_sa, s->proxy_port, 1038a50c4a2fSJan Lentfer server_sa, s->port) == -1) 1039a50c4a2fSJan Lentfer goto fail; 1040a50c4a2fSJan Lentfer 1041a50c4a2fSJan Lentfer /* nat from $client to $server port $port -> $proxy */ 1042a50c4a2fSJan Lentfer if (add_nat(s->id, client_sa, server_sa, s->port, proxy_sa, 1043a50c4a2fSJan Lentfer PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1) 1044a50c4a2fSJan Lentfer goto fail; 1045a50c4a2fSJan Lentfer 1046a50c4a2fSJan Lentfer /* pass in from $client to $server port $port */ 1047a50c4a2fSJan Lentfer if (add_filter(s->id, PF_IN, client_sa, server_sa, 1048a50c4a2fSJan Lentfer s->port) == -1) 1049a50c4a2fSJan Lentfer goto fail; 1050a50c4a2fSJan Lentfer 1051a50c4a2fSJan Lentfer /* pass out from $proxy to $server port $port */ 1052a50c4a2fSJan Lentfer if (add_filter(s->id, PF_OUT, proxy_sa, server_sa, 1053a50c4a2fSJan Lentfer s->port) == -1) 1054a50c4a2fSJan Lentfer goto fail; 1055a50c4a2fSJan Lentfer } 1056a50c4a2fSJan Lentfer 1057a50c4a2fSJan Lentfer /* Active modes. */ 1058315a7da3SJan Lentfer if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) { 1059a50c4a2fSJan Lentfer logmsg(LOG_INFO, "#%d active: server to client port %d" 1060a50c4a2fSJan Lentfer " via port %d", s->id, s->port, s->proxy_port); 1061a50c4a2fSJan Lentfer 1062a50c4a2fSJan Lentfer if (prepare_commit(s->id) == -1) 1063a50c4a2fSJan Lentfer goto fail; 1064a50c4a2fSJan Lentfer prepared = 1; 1065a50c4a2fSJan Lentfer 1066a50c4a2fSJan Lentfer /* rdr from $server to $proxy port $proxy_port -> $client port 1067a50c4a2fSJan Lentfer $port */ 1068a50c4a2fSJan Lentfer if (add_rdr(s->id, server_sa, proxy_sa, s->proxy_port, 1069a50c4a2fSJan Lentfer client_sa, s->port) == -1) 1070a50c4a2fSJan Lentfer goto fail; 1071a50c4a2fSJan Lentfer 1072a50c4a2fSJan Lentfer /* nat from $server to $client port $port -> $orig_server port 1073a50c4a2fSJan Lentfer $natport */ 1074a50c4a2fSJan Lentfer if (rfc_mode && s->cmd == CMD_PORT) { 1075a50c4a2fSJan Lentfer /* Rewrite sourceport to RFC mandated 20. */ 1076a50c4a2fSJan Lentfer if (add_nat(s->id, server_sa, client_sa, s->port, 1077a50c4a2fSJan Lentfer orig_sa, 20, 20) == -1) 1078a50c4a2fSJan Lentfer goto fail; 1079a50c4a2fSJan Lentfer } else { 1080a50c4a2fSJan Lentfer /* Let pf pick a source port from the standard range. */ 1081a50c4a2fSJan Lentfer if (add_nat(s->id, server_sa, client_sa, s->port, 1082a50c4a2fSJan Lentfer orig_sa, PF_NAT_PROXY_PORT_LOW, 1083a50c4a2fSJan Lentfer PF_NAT_PROXY_PORT_HIGH) == -1) 1084a50c4a2fSJan Lentfer goto fail; 1085a50c4a2fSJan Lentfer } 1086a50c4a2fSJan Lentfer 1087a50c4a2fSJan Lentfer /* pass in from $server to $client port $port */ 1088a50c4a2fSJan Lentfer if (add_filter(s->id, PF_IN, server_sa, client_sa, s->port) == 1089a50c4a2fSJan Lentfer -1) 1090a50c4a2fSJan Lentfer goto fail; 1091a50c4a2fSJan Lentfer 1092a50c4a2fSJan Lentfer /* pass out from $orig_server to $client port $port */ 1093a50c4a2fSJan Lentfer if (add_filter(s->id, PF_OUT, orig_sa, client_sa, s->port) == 1094a50c4a2fSJan Lentfer -1) 1095a50c4a2fSJan Lentfer goto fail; 1096a50c4a2fSJan Lentfer } 1097a50c4a2fSJan Lentfer 1098a50c4a2fSJan Lentfer /* Commit rules if they were prepared. */ 1099a50c4a2fSJan Lentfer if (prepared && (do_commit() == -1)) { 1100a50c4a2fSJan Lentfer if (errno != EBUSY) 1101a50c4a2fSJan Lentfer goto fail; 1102a50c4a2fSJan Lentfer /* One more try if busy. */ 1103a50c4a2fSJan Lentfer usleep(5000); 1104a50c4a2fSJan Lentfer if (do_commit() == -1) 1105a50c4a2fSJan Lentfer goto fail; 1106a50c4a2fSJan Lentfer } 1107a50c4a2fSJan Lentfer 1108a50c4a2fSJan Lentfer out: 1109a50c4a2fSJan Lentfer s->cmd = CMD_NONE; 1110a50c4a2fSJan Lentfer s->port = 0; 1111a50c4a2fSJan Lentfer 1112a50c4a2fSJan Lentfer return (1); 1113a50c4a2fSJan Lentfer 1114a50c4a2fSJan Lentfer fail: 1115a50c4a2fSJan Lentfer logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno)); 1116a50c4a2fSJan Lentfer if (prepared) 1117a50c4a2fSJan Lentfer do_rollback(); 1118a50c4a2fSJan Lentfer return (0); 1119a50c4a2fSJan Lentfer } 1120a50c4a2fSJan Lentfer 1121a50c4a2fSJan Lentfer void 11222af0c10eSSamuel J. Greear server_read(struct session *s) 1123a50c4a2fSJan Lentfer { 11242af0c10eSSamuel J. Greear size_t buf_avail, bread, bwritten; 1125a50c4a2fSJan Lentfer int n; 1126a50c4a2fSJan Lentfer 1127a50c4a2fSJan Lentfer do { 1128a50c4a2fSJan Lentfer buf_avail = sizeof s->sbuf - s->sbuf_valid; 11292af0c10eSSamuel J. Greear bread = read(s->server_fd, s->sbuf + s->sbuf_valid, buf_avail); 11302af0c10eSSamuel J. Greear s->sbuf_valid += bread; 1131a50c4a2fSJan Lentfer 1132cae2835bSzrj while ((n = get_line(s->sbuf, &s->sbuf_valid)) > 0) { 1133a50c4a2fSJan Lentfer logmsg(LOG_DEBUG, "#%d server: %s", s->id, linebuf); 1134a50c4a2fSJan Lentfer if (!server_parse(s)) { 1135a50c4a2fSJan Lentfer end_session(s); 1136a50c4a2fSJan Lentfer return; 1137a50c4a2fSJan Lentfer } 11382af0c10eSSamuel J. Greear bwritten = write(s->client_fd, linebuf, linelen); 11392af0c10eSSamuel J. Greear if (bwritten == -1) { 11402af0c10eSSamuel J. Greear logmsg(LOG_ERR, "#%d write failed", s->id); 11412af0c10eSSamuel J. Greear end_session(s); 11422af0c10eSSamuel J. Greear return; 11432af0c10eSSamuel J. Greear } else if (bwritten < linelen) { 11442af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->server_fd, 11452af0c10eSSamuel J. Greear EVFILT_READ, EV_DISABLE, 0, 0, s); 11462af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->client_fd, 11472af0c10eSSamuel J. Greear EVFILT_WRITE, EV_ADD, 0, 0, s); 11482af0c10eSSamuel J. Greear buffer_data(s, &s->client, linebuf + bwritten, 11492af0c10eSSamuel J. Greear linelen - bwritten); 11502af0c10eSSamuel J. Greear return; 11512af0c10eSSamuel J. Greear } 1152a50c4a2fSJan Lentfer } 1153a50c4a2fSJan Lentfer 1154a50c4a2fSJan Lentfer if (n == -1) { 1155a50c4a2fSJan Lentfer logmsg(LOG_ERR, "#%d server reply too long or not" 1156a50c4a2fSJan Lentfer " clean", s->id); 1157a50c4a2fSJan Lentfer end_session(s); 1158a50c4a2fSJan Lentfer return; 1159a50c4a2fSJan Lentfer } 11602af0c10eSSamuel J. Greear } while (bread == buf_avail); 11612af0c10eSSamuel J. Greear } 11622af0c10eSSamuel J. Greear 11632af0c10eSSamuel J. Greear void 11642af0c10eSSamuel J. Greear server_write(struct session *s) 11652af0c10eSSamuel J. Greear { 11662af0c10eSSamuel J. Greear size_t written; 11672af0c10eSSamuel J. Greear 11682af0c10eSSamuel J. Greear written = write(s->server_fd, s->server.buffer + s->server.buffer_offset, 11692af0c10eSSamuel J. Greear s->server.buffer_size - s->server.buffer_offset); 11702af0c10eSSamuel J. Greear if (written == -1) { 11712af0c10eSSamuel J. Greear logmsg(LOG_ERR, "#%d write failed", s->id); 11722af0c10eSSamuel J. Greear end_session(s); 11732af0c10eSSamuel J. Greear } else if (written == (s->server.buffer_size - s->server.buffer_offset)) { 11742af0c10eSSamuel J. Greear free(s->server.buffer); 11752af0c10eSSamuel J. Greear s->server.buffer = NULL; 11762af0c10eSSamuel J. Greear s->server.buffer_size = 0; 11772af0c10eSSamuel J. Greear s->server.buffer_offset = 0; 11782af0c10eSSamuel J. Greear EV_SET(&changes[nchanges++], s->client_fd, 11792af0c10eSSamuel J. Greear EVFILT_READ, EV_ENABLE, 0, 0, s); 11802af0c10eSSamuel J. Greear } else { 11812af0c10eSSamuel J. Greear s->server.buffer_offset += written; 11822af0c10eSSamuel J. Greear } 1183a50c4a2fSJan Lentfer } 1184a50c4a2fSJan Lentfer 1185a50c4a2fSJan Lentfer const char * 1186a50c4a2fSJan Lentfer sock_ntop(struct sockaddr *sa) 1187a50c4a2fSJan Lentfer { 1188a50c4a2fSJan Lentfer static int n = 0; 1189a50c4a2fSJan Lentfer 1190a50c4a2fSJan Lentfer /* Cycle to next buffer. */ 1191a50c4a2fSJan Lentfer n = (n + 1) % NTOP_BUFS; 1192a50c4a2fSJan Lentfer ntop_buf[n][0] = '\0'; 1193a50c4a2fSJan Lentfer 1194a50c4a2fSJan Lentfer if (sa->sa_family == AF_INET) { 1195a50c4a2fSJan Lentfer struct sockaddr_in *sin = (struct sockaddr_in *)sa; 1196a50c4a2fSJan Lentfer 1197a50c4a2fSJan Lentfer return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n], 1198a50c4a2fSJan Lentfer sizeof ntop_buf[0])); 1199a50c4a2fSJan Lentfer } 1200a50c4a2fSJan Lentfer 1201a50c4a2fSJan Lentfer if (sa->sa_family == AF_INET6) { 1202a50c4a2fSJan Lentfer struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 1203a50c4a2fSJan Lentfer 1204a50c4a2fSJan Lentfer return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n], 1205a50c4a2fSJan Lentfer sizeof ntop_buf[0])); 1206a50c4a2fSJan Lentfer } 1207a50c4a2fSJan Lentfer 1208a50c4a2fSJan Lentfer return (NULL); 1209a50c4a2fSJan Lentfer } 1210a50c4a2fSJan Lentfer 1211a50c4a2fSJan Lentfer void 1212a50c4a2fSJan Lentfer usage(void) 1213a50c4a2fSJan Lentfer { 1214a50c4a2fSJan Lentfer fprintf(stderr, "usage: %s [-6Adrv] [-a address] [-b address]" 1215a50c4a2fSJan Lentfer " [-D level] [-m maxsessions]\n [-P port]" 1216315a7da3SJan Lentfer " [-p port] [-q queue] [-R address] [-T tag] [-t timeout]\n", __progname); 1217a50c4a2fSJan Lentfer exit(1); 1218a50c4a2fSJan Lentfer } 1219