1*13b9f610SMax Laier /* $OpenBSD: ftp-proxy.c,v 1.33 2003/08/22 21:50:34 david Exp $ */ 2*13b9f610SMax Laier 3*13b9f610SMax Laier /* 4*13b9f610SMax Laier * Copyright (c) 1996-2001 5*13b9f610SMax Laier * Obtuse Systems Corporation. All rights reserved. 6*13b9f610SMax Laier * 7*13b9f610SMax Laier * Redistribution and use in source and binary forms, with or without 8*13b9f610SMax Laier * modification, are permitted provided that the following conditions 9*13b9f610SMax Laier * are met: 10*13b9f610SMax Laier * 1. Redistributions of source code must retain the above copyright 11*13b9f610SMax Laier * notice, this list of conditions and the following disclaimer. 12*13b9f610SMax Laier * 2. Redistributions in binary form must reproduce the above copyright 13*13b9f610SMax Laier * notice, this list of conditions and the following disclaimer in the 14*13b9f610SMax Laier * documentation and/or other materials provided with the distribution. 15*13b9f610SMax Laier * 3. Neither the name of the Obtuse Systems nor the names of its contributors 16*13b9f610SMax Laier * may be used to endorse or promote products derived from this software 17*13b9f610SMax Laier * without specific prior written permission. 18*13b9f610SMax Laier * 19*13b9f610SMax Laier * THIS SOFTWARE IS PROVIDED BY OBTUSE SYSTEMS AND CONTRIBUTORS ``AS IS'' AND 20*13b9f610SMax Laier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21*13b9f610SMax Laier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22*13b9f610SMax Laier * ARE DISCLAIMED. IN NO EVENT SHALL OBTUSE SYSTEMS CORPORATION OR 23*13b9f610SMax Laier * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24*13b9f610SMax Laier * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25*13b9f610SMax Laier * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26*13b9f610SMax Laier * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27*13b9f610SMax Laier * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28*13b9f610SMax Laier * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29*13b9f610SMax Laier * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30*13b9f610SMax Laier * 31*13b9f610SMax Laier */ 32*13b9f610SMax Laier 33*13b9f610SMax Laier /* 34*13b9f610SMax Laier * ftp proxy, Originally based on juniper_ftp_proxy from the Obtuse 35*13b9f610SMax Laier * Systems juniper firewall, written by Dan Boulet <danny@obtuse.com> 36*13b9f610SMax Laier * and Bob Beck <beck@obtuse.com> 37*13b9f610SMax Laier * 38*13b9f610SMax Laier * This version basically passes everything through unchanged except 39*13b9f610SMax Laier * for the PORT and the * "227 Entering Passive Mode" reply. 40*13b9f610SMax Laier * 41*13b9f610SMax Laier * A PORT command is handled by noting the IP address and port number 42*13b9f610SMax Laier * specified and then configuring a listen port on some very high port 43*13b9f610SMax Laier * number and telling the server about it using a PORT message. 44*13b9f610SMax Laier * We then watch for an in-bound connection on the port from the server 45*13b9f610SMax Laier * and connect to the client's port when it happens. 46*13b9f610SMax Laier * 47*13b9f610SMax Laier * A "227 Entering Passive Mode" reply is handled by noting the IP address 48*13b9f610SMax Laier * and port number specified and then configuring a listen port on some 49*13b9f610SMax Laier * very high port number and telling the client about it using a 50*13b9f610SMax Laier * "227 Entering Passive Mode" reply. 51*13b9f610SMax Laier * We then watch for an in-bound connection on the port from the client 52*13b9f610SMax Laier * and connect to the server's port when it happens. 53*13b9f610SMax Laier * 54*13b9f610SMax Laier * supports tcp wrapper lookups/access control with the -w flag using 55*13b9f610SMax Laier * the real destination address - the tcp wrapper stuff is done after 56*13b9f610SMax Laier * the real destination address is retrieved from pf 57*13b9f610SMax Laier * 58*13b9f610SMax Laier */ 59*13b9f610SMax Laier 60*13b9f610SMax Laier /* 61*13b9f610SMax Laier * TODO: 62*13b9f610SMax Laier * Plenty, this is very basic, with the idea to get it in clean first. 63*13b9f610SMax Laier * 64*13b9f610SMax Laier * - IPv6 and EPASV support 65*13b9f610SMax Laier * - Content filter support 66*13b9f610SMax Laier * - filename filter support 67*13b9f610SMax Laier * - per-user rules perhaps. 68*13b9f610SMax Laier */ 69*13b9f610SMax Laier 70*13b9f610SMax Laier #include <sys/types.h> 71*13b9f610SMax Laier #include <sys/time.h> 72*13b9f610SMax Laier #include <sys/socket.h> 73*13b9f610SMax Laier 74*13b9f610SMax Laier #include <net/if.h> 75*13b9f610SMax Laier #include <netinet/in.h> 76*13b9f610SMax Laier 77*13b9f610SMax Laier #include <arpa/inet.h> 78*13b9f610SMax Laier 79*13b9f610SMax Laier #include <ctype.h> 80*13b9f610SMax Laier #include <errno.h> 81*13b9f610SMax Laier #include <grp.h> 82*13b9f610SMax Laier #include <netdb.h> 83*13b9f610SMax Laier #include <pwd.h> 84*13b9f610SMax Laier #include <signal.h> 85*13b9f610SMax Laier #include <stdarg.h> 86*13b9f610SMax Laier #include <stdio.h> 87*13b9f610SMax Laier #include <stdlib.h> 88*13b9f610SMax Laier #include <string.h> 89*13b9f610SMax Laier #include <sysexits.h> 90*13b9f610SMax Laier #include <syslog.h> 91*13b9f610SMax Laier #include <unistd.h> 92*13b9f610SMax Laier 93*13b9f610SMax Laier #include "util.h" 94*13b9f610SMax Laier 95*13b9f610SMax Laier #ifdef LIBWRAP 96*13b9f610SMax Laier #include <tcpd.h> 97*13b9f610SMax Laier int allow_severity = LOG_INFO; 98*13b9f610SMax Laier int deny_severity = LOG_NOTICE; 99*13b9f610SMax Laier #endif /* LIBWRAP */ 100*13b9f610SMax Laier 101*13b9f610SMax Laier int min_port = IPPORT_HIFIRSTAUTO; 102*13b9f610SMax Laier int max_port = IPPORT_HILASTAUTO; 103*13b9f610SMax Laier 104*13b9f610SMax Laier #define STARTBUFSIZE 1024 /* Must be at least 3 */ 105*13b9f610SMax Laier 106*13b9f610SMax Laier /* 107*13b9f610SMax Laier * Variables used to support PORT mode connections. 108*13b9f610SMax Laier * 109*13b9f610SMax Laier * This gets a bit complicated. 110*13b9f610SMax Laier * 111*13b9f610SMax Laier * If PORT mode is on then client_listen_sa describes the socket that 112*13b9f610SMax Laier * the real client is listening on and server_listen_sa describes the 113*13b9f610SMax Laier * socket that we are listening on (waiting for the real server to connect 114*13b9f610SMax Laier * with us). 115*13b9f610SMax Laier * 116*13b9f610SMax Laier * If PASV mode is on then client_listen_sa describes the socket that 117*13b9f610SMax Laier * we are listening on (waiting for the real client to connect to us on) 118*13b9f610SMax Laier * and server_listen_sa describes the socket that the real server is 119*13b9f610SMax Laier * listening on. 120*13b9f610SMax Laier * 121*13b9f610SMax Laier * If the socket we are listening on gets a connection then we connect 122*13b9f610SMax Laier * to the other side's socket. Similarly, if a connected socket is 123*13b9f610SMax Laier * shutdown then we shutdown the other side's socket. 124*13b9f610SMax Laier */ 125*13b9f610SMax Laier 126*13b9f610SMax Laier double xfer_start_time; 127*13b9f610SMax Laier 128*13b9f610SMax Laier struct sockaddr_in real_server_sa; 129*13b9f610SMax Laier struct sockaddr_in client_listen_sa; 130*13b9f610SMax Laier struct sockaddr_in server_listen_sa; 131*13b9f610SMax Laier 132*13b9f610SMax Laier int client_listen_socket = -1; /* Only used in PASV mode */ 133*13b9f610SMax Laier int client_data_socket = -1; /* Connected socket to real client */ 134*13b9f610SMax Laier int server_listen_socket = -1; /* Only used in PORT mode */ 135*13b9f610SMax Laier int server_data_socket = -1; /* Connected socket to real server */ 136*13b9f610SMax Laier int client_data_bytes, server_data_bytes; 137*13b9f610SMax Laier 138*13b9f610SMax Laier int AnonFtpOnly; 139*13b9f610SMax Laier int Verbose; 140*13b9f610SMax Laier int NatMode; 141*13b9f610SMax Laier 142*13b9f610SMax Laier char ClientName[NI_MAXHOST]; 143*13b9f610SMax Laier char RealServerName[NI_MAXHOST]; 144*13b9f610SMax Laier char OurName[NI_MAXHOST]; 145*13b9f610SMax Laier 146*13b9f610SMax Laier char *User = "proxy"; 147*13b9f610SMax Laier char *Group; 148*13b9f610SMax Laier 149*13b9f610SMax Laier extern int Debug_Level; 150*13b9f610SMax Laier extern int Use_Rdns; 151*13b9f610SMax Laier extern char *__progname; 152*13b9f610SMax Laier 153*13b9f610SMax Laier typedef enum { 154*13b9f610SMax Laier UNKNOWN_MODE, 155*13b9f610SMax Laier PORT_MODE, 156*13b9f610SMax Laier PASV_MODE, 157*13b9f610SMax Laier EPRT_MODE, 158*13b9f610SMax Laier EPSV_MODE 159*13b9f610SMax Laier } connection_mode_t; 160*13b9f610SMax Laier 161*13b9f610SMax Laier connection_mode_t connection_mode; 162*13b9f610SMax Laier 163*13b9f610SMax Laier extern void debuglog(int debug_level, const char *fmt, ...); 164*13b9f610SMax Laier double wallclock_time(void); 165*13b9f610SMax Laier void show_xfer_stats(void); 166*13b9f610SMax Laier void log_control_command (char *cmd, int client); 167*13b9f610SMax Laier int new_dataconn(int server); 168*13b9f610SMax Laier void do_client_cmd(struct csiob *client, struct csiob *server); 169*13b9f610SMax Laier void do_server_reply(struct csiob *server, struct csiob *client); 170*13b9f610SMax Laier static void 171*13b9f610SMax Laier usage(void) 172*13b9f610SMax Laier { 173*13b9f610SMax Laier syslog(LOG_NOTICE, 174*13b9f610SMax Laier "usage: %s [-AnrVw] [-D debuglevel] [-g group] %s %s", 175*13b9f610SMax Laier __progname, "[-m minport] [-M maxport] [-t timeout]", 176*13b9f610SMax Laier "[-u user]"); 177*13b9f610SMax Laier exit(EX_USAGE); 178*13b9f610SMax Laier } 179*13b9f610SMax Laier 180*13b9f610SMax Laier static void 181*13b9f610SMax Laier close_client_data(void) 182*13b9f610SMax Laier { 183*13b9f610SMax Laier if (client_data_socket >= 0) { 184*13b9f610SMax Laier shutdown(client_data_socket, 2); 185*13b9f610SMax Laier close(client_data_socket); 186*13b9f610SMax Laier client_data_socket = -1; 187*13b9f610SMax Laier } 188*13b9f610SMax Laier } 189*13b9f610SMax Laier 190*13b9f610SMax Laier static void 191*13b9f610SMax Laier close_server_data(void) 192*13b9f610SMax Laier { 193*13b9f610SMax Laier if (server_data_socket >= 0) { 194*13b9f610SMax Laier shutdown(server_data_socket, 2); 195*13b9f610SMax Laier close(server_data_socket); 196*13b9f610SMax Laier server_data_socket = -1; 197*13b9f610SMax Laier } 198*13b9f610SMax Laier } 199*13b9f610SMax Laier 200*13b9f610SMax Laier static void 201*13b9f610SMax Laier drop_privs(void) 202*13b9f610SMax Laier { 203*13b9f610SMax Laier struct passwd *pw; 204*13b9f610SMax Laier struct group *gr; 205*13b9f610SMax Laier uid_t uid = 0; 206*13b9f610SMax Laier gid_t gid = 0; 207*13b9f610SMax Laier 208*13b9f610SMax Laier if (User != NULL) { 209*13b9f610SMax Laier pw = getpwnam(User); 210*13b9f610SMax Laier if (pw == NULL) { 211*13b9f610SMax Laier syslog(LOG_ERR, "cannot find user %s", User); 212*13b9f610SMax Laier exit(EX_USAGE); 213*13b9f610SMax Laier } 214*13b9f610SMax Laier uid = pw->pw_uid; 215*13b9f610SMax Laier gid = pw->pw_gid; 216*13b9f610SMax Laier } 217*13b9f610SMax Laier 218*13b9f610SMax Laier if (Group != NULL) { 219*13b9f610SMax Laier gr = getgrnam(Group); 220*13b9f610SMax Laier if (gr == NULL) { 221*13b9f610SMax Laier syslog(LOG_ERR, "cannot find group %s", Group); 222*13b9f610SMax Laier exit(EX_USAGE); 223*13b9f610SMax Laier } 224*13b9f610SMax Laier gid = gr->gr_gid; 225*13b9f610SMax Laier } 226*13b9f610SMax Laier 227*13b9f610SMax Laier if (gid != 0 && (setegid(gid) == -1 || setgid(gid) == -1)) { 228*13b9f610SMax Laier syslog(LOG_ERR, "cannot drop group privs (%m)"); 229*13b9f610SMax Laier exit(EX_CONFIG); 230*13b9f610SMax Laier } 231*13b9f610SMax Laier 232*13b9f610SMax Laier if (uid != 0 && (seteuid(uid) == -1 || setuid(uid) == -1)) { 233*13b9f610SMax Laier syslog(LOG_ERR, "cannot drop root privs (%m)"); 234*13b9f610SMax Laier exit(EX_CONFIG); 235*13b9f610SMax Laier } 236*13b9f610SMax Laier } 237*13b9f610SMax Laier 238*13b9f610SMax Laier #ifdef LIBWRAP 239*13b9f610SMax Laier /* 240*13b9f610SMax Laier * Check a connection against the tcpwrapper, log if we're going to 241*13b9f610SMax Laier * reject it, returns: 0 -> reject, 1 -> accept. We add in hostnames 242*13b9f610SMax Laier * if we are set to do reverse DNS, otherwise no. 243*13b9f610SMax Laier */ 244*13b9f610SMax Laier static int 245*13b9f610SMax Laier check_host(struct sockaddr_in *client_sin, struct sockaddr_in *server_sin) 246*13b9f610SMax Laier { 247*13b9f610SMax Laier char cname[NI_MAXHOST]; 248*13b9f610SMax Laier char sname[NI_MAXHOST]; 249*13b9f610SMax Laier struct request_info request; 250*13b9f610SMax Laier int i; 251*13b9f610SMax Laier 252*13b9f610SMax Laier request_init(&request, RQ_DAEMON, __progname, RQ_CLIENT_SIN, 253*13b9f610SMax Laier client_sin, RQ_SERVER_SIN, server_sin, RQ_CLIENT_ADDR, 254*13b9f610SMax Laier inet_ntoa(client_sin->sin_addr), 0); 255*13b9f610SMax Laier 256*13b9f610SMax Laier if (Use_Rdns) { 257*13b9f610SMax Laier /* 258*13b9f610SMax Laier * We already looked these up, but we have to do it again 259*13b9f610SMax Laier * for tcp wrapper, to ensure that we get the DNS name, since 260*13b9f610SMax Laier * the tcp wrapper cares about these things, and we don't 261*13b9f610SMax Laier * want to pass in a printed address as a name. 262*13b9f610SMax Laier */ 263*13b9f610SMax Laier i = getnameinfo((struct sockaddr *) &client_sin->sin_addr, 264*13b9f610SMax Laier sizeof(&client_sin->sin_addr), cname, sizeof(cname), 265*13b9f610SMax Laier NULL, 0, NI_NAMEREQD); 266*13b9f610SMax Laier 267*13b9f610SMax Laier if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) 268*13b9f610SMax Laier strlcpy(cname, STRING_UNKNOWN, sizeof(cname)); 269*13b9f610SMax Laier 270*13b9f610SMax Laier i = getnameinfo((struct sockaddr *)&server_sin->sin_addr, 271*13b9f610SMax Laier sizeof(&server_sin->sin_addr), sname, sizeof(sname), 272*13b9f610SMax Laier NULL, 0, NI_NAMEREQD); 273*13b9f610SMax Laier 274*13b9f610SMax Laier if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) 275*13b9f610SMax Laier strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); 276*13b9f610SMax Laier } else { 277*13b9f610SMax Laier /* 278*13b9f610SMax Laier * ensure the TCP wrapper doesn't start doing 279*13b9f610SMax Laier * reverse DNS lookups if we aren't supposed to. 280*13b9f610SMax Laier */ 281*13b9f610SMax Laier strlcpy(cname, STRING_UNKNOWN, sizeof(cname)); 282*13b9f610SMax Laier strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); 283*13b9f610SMax Laier } 284*13b9f610SMax Laier 285*13b9f610SMax Laier request_set(&request, RQ_SERVER_ADDR, inet_ntoa(server_sin->sin_addr), 286*13b9f610SMax Laier 0); 287*13b9f610SMax Laier request_set(&request, RQ_CLIENT_NAME, cname, RQ_SERVER_NAME, sname, 0); 288*13b9f610SMax Laier 289*13b9f610SMax Laier if (!hosts_access(&request)) { 290*13b9f610SMax Laier syslog(LOG_NOTICE, "tcpwrappers rejected: %s -> %s", 291*13b9f610SMax Laier ClientName, RealServerName); 292*13b9f610SMax Laier return(0); 293*13b9f610SMax Laier } 294*13b9f610SMax Laier return(1); 295*13b9f610SMax Laier } 296*13b9f610SMax Laier #endif /* LIBWRAP */ 297*13b9f610SMax Laier 298*13b9f610SMax Laier double 299*13b9f610SMax Laier wallclock_time(void) 300*13b9f610SMax Laier { 301*13b9f610SMax Laier struct timeval tv; 302*13b9f610SMax Laier 303*13b9f610SMax Laier gettimeofday(&tv, NULL); 304*13b9f610SMax Laier return(tv.tv_sec + tv.tv_usec / 1e6); 305*13b9f610SMax Laier } 306*13b9f610SMax Laier 307*13b9f610SMax Laier /* 308*13b9f610SMax Laier * Show the stats for this data transfer 309*13b9f610SMax Laier */ 310*13b9f610SMax Laier void 311*13b9f610SMax Laier show_xfer_stats(void) 312*13b9f610SMax Laier { 313*13b9f610SMax Laier char tbuf[1000]; 314*13b9f610SMax Laier double delta; 315*13b9f610SMax Laier size_t len; 316*13b9f610SMax Laier int i; 317*13b9f610SMax Laier 318*13b9f610SMax Laier if (!Verbose) 319*13b9f610SMax Laier return; 320*13b9f610SMax Laier 321*13b9f610SMax Laier delta = wallclock_time() - xfer_start_time; 322*13b9f610SMax Laier 323*13b9f610SMax Laier if (delta < 0.001) 324*13b9f610SMax Laier delta = 0.001; 325*13b9f610SMax Laier 326*13b9f610SMax Laier if (client_data_bytes == 0 && server_data_bytes == 0) { 327*13b9f610SMax Laier syslog(LOG_INFO, 328*13b9f610SMax Laier "data transfer complete (no bytes transferred)"); 329*13b9f610SMax Laier return; 330*13b9f610SMax Laier } 331*13b9f610SMax Laier 332*13b9f610SMax Laier len = sizeof(tbuf); 333*13b9f610SMax Laier 334*13b9f610SMax Laier if (delta >= 60) { 335*13b9f610SMax Laier int idelta; 336*13b9f610SMax Laier 337*13b9f610SMax Laier idelta = delta + 0.5; 338*13b9f610SMax Laier if (idelta >= 60*60) { 339*13b9f610SMax Laier i = snprintf(tbuf, len, 340*13b9f610SMax Laier "data transfer complete (%dh %dm %ds", 341*13b9f610SMax Laier idelta / (60*60), (idelta % (60*60)) / 60, 342*13b9f610SMax Laier idelta % 60); 343*13b9f610SMax Laier if (i >= len) 344*13b9f610SMax Laier goto logit; 345*13b9f610SMax Laier len -= i; 346*13b9f610SMax Laier } else { 347*13b9f610SMax Laier i = snprintf(tbuf, len, 348*13b9f610SMax Laier "data transfer complete (%dm %ds", idelta / 60, 349*13b9f610SMax Laier idelta % 60); 350*13b9f610SMax Laier if (i >= len) 351*13b9f610SMax Laier goto logit; 352*13b9f610SMax Laier len -= i; 353*13b9f610SMax Laier } 354*13b9f610SMax Laier } else { 355*13b9f610SMax Laier i = snprintf(tbuf, len, "data transfer complete (%.1fs", 356*13b9f610SMax Laier delta); 357*13b9f610SMax Laier if (i >= len) 358*13b9f610SMax Laier goto logit; 359*13b9f610SMax Laier len -= i; 360*13b9f610SMax Laier } 361*13b9f610SMax Laier 362*13b9f610SMax Laier if (client_data_bytes > 0) { 363*13b9f610SMax Laier i = snprintf(&tbuf[strlen(tbuf)], len, 364*13b9f610SMax Laier ", %d bytes to server) (%.1fKB/s", client_data_bytes, 365*13b9f610SMax Laier (client_data_bytes / delta) / (double)1024); 366*13b9f610SMax Laier if (i >= len) 367*13b9f610SMax Laier goto logit; 368*13b9f610SMax Laier len -= i; 369*13b9f610SMax Laier } 370*13b9f610SMax Laier if (server_data_bytes > 0) { 371*13b9f610SMax Laier i = snprintf(&tbuf[strlen(tbuf)], len, 372*13b9f610SMax Laier ", %d bytes to client) (%.1fKB/s", server_data_bytes, 373*13b9f610SMax Laier (server_data_bytes / delta) / (double)1024); 374*13b9f610SMax Laier if (i >= len) 375*13b9f610SMax Laier goto logit; 376*13b9f610SMax Laier len -= i; 377*13b9f610SMax Laier } 378*13b9f610SMax Laier strlcat(tbuf, ")", sizeof(tbuf)); 379*13b9f610SMax Laier logit: 380*13b9f610SMax Laier syslog(LOG_INFO, "%s", tbuf); 381*13b9f610SMax Laier } 382*13b9f610SMax Laier 383*13b9f610SMax Laier void 384*13b9f610SMax Laier log_control_command (char *cmd, int client) 385*13b9f610SMax Laier { 386*13b9f610SMax Laier /* log an ftp control command or reply */ 387*13b9f610SMax Laier char *logstring; 388*13b9f610SMax Laier int level = LOG_DEBUG; 389*13b9f610SMax Laier 390*13b9f610SMax Laier if (!Verbose) 391*13b9f610SMax Laier return; 392*13b9f610SMax Laier 393*13b9f610SMax Laier /* don't log passwords */ 394*13b9f610SMax Laier if (strncasecmp(cmd, "pass ", 5) == 0) 395*13b9f610SMax Laier logstring = "PASS XXXX"; 396*13b9f610SMax Laier else 397*13b9f610SMax Laier logstring = cmd; 398*13b9f610SMax Laier if (client) { 399*13b9f610SMax Laier /* log interesting stuff at LOG_INFO, rest at LOG_DEBUG */ 400*13b9f610SMax Laier if ((strncasecmp(cmd, "user ", 5) == 0) || 401*13b9f610SMax Laier (strncasecmp(cmd, "retr ", 5) == 0) || 402*13b9f610SMax Laier (strncasecmp(cmd, "cwd ", 4) == 0) || 403*13b9f610SMax Laier (strncasecmp(cmd, "stor " ,5) == 0)) 404*13b9f610SMax Laier level = LOG_INFO; 405*13b9f610SMax Laier } 406*13b9f610SMax Laier syslog(level, "%s %s", client ? "client:" : " server:", 407*13b9f610SMax Laier logstring); 408*13b9f610SMax Laier } 409*13b9f610SMax Laier 410*13b9f610SMax Laier /* 411*13b9f610SMax Laier * set ourselves up for a new data connection. Direction is toward client if 412*13b9f610SMax Laier * "server" is 0, towards server otherwise. 413*13b9f610SMax Laier */ 414*13b9f610SMax Laier int 415*13b9f610SMax Laier new_dataconn(int server) 416*13b9f610SMax Laier { 417*13b9f610SMax Laier /* 418*13b9f610SMax Laier * Close existing data conn. 419*13b9f610SMax Laier */ 420*13b9f610SMax Laier 421*13b9f610SMax Laier if (client_listen_socket != -1) { 422*13b9f610SMax Laier close(client_listen_socket); 423*13b9f610SMax Laier client_listen_socket = -1; 424*13b9f610SMax Laier } 425*13b9f610SMax Laier close_client_data(); 426*13b9f610SMax Laier 427*13b9f610SMax Laier if (server_listen_socket != -1) { 428*13b9f610SMax Laier close(server_listen_socket); 429*13b9f610SMax Laier server_listen_socket = -1; 430*13b9f610SMax Laier } 431*13b9f610SMax Laier close_server_data(); 432*13b9f610SMax Laier 433*13b9f610SMax Laier if (server) { 434*13b9f610SMax Laier bzero(&server_listen_sa, sizeof(server_listen_sa)); 435*13b9f610SMax Laier server_listen_socket = get_backchannel_socket(SOCK_STREAM, 436*13b9f610SMax Laier min_port, max_port, -1, 1, &server_listen_sa); 437*13b9f610SMax Laier 438*13b9f610SMax Laier if (server_listen_socket == -1) { 439*13b9f610SMax Laier syslog(LOG_INFO, "server socket bind() failed (%m)"); 440*13b9f610SMax Laier exit(EX_OSERR); 441*13b9f610SMax Laier } 442*13b9f610SMax Laier if (listen(server_listen_socket, 5) != 0) { 443*13b9f610SMax Laier syslog(LOG_INFO, "server socket listen() failed (%m)"); 444*13b9f610SMax Laier exit(EX_OSERR); 445*13b9f610SMax Laier } 446*13b9f610SMax Laier } else { 447*13b9f610SMax Laier bzero(&client_listen_sa, sizeof(client_listen_sa)); 448*13b9f610SMax Laier client_listen_socket = get_backchannel_socket(SOCK_STREAM, 449*13b9f610SMax Laier min_port, max_port, -1, 1, &client_listen_sa); 450*13b9f610SMax Laier 451*13b9f610SMax Laier if (client_listen_socket == -1) { 452*13b9f610SMax Laier syslog(LOG_NOTICE, 453*13b9f610SMax Laier "cannot get client listen socket (%m)"); 454*13b9f610SMax Laier exit(EX_OSERR); 455*13b9f610SMax Laier } 456*13b9f610SMax Laier if (listen(client_listen_socket, 5) != 0) { 457*13b9f610SMax Laier syslog(LOG_NOTICE, 458*13b9f610SMax Laier "cannot listen on client socket (%m)"); 459*13b9f610SMax Laier exit(EX_OSERR); 460*13b9f610SMax Laier } 461*13b9f610SMax Laier } 462*13b9f610SMax Laier return(0); 463*13b9f610SMax Laier } 464*13b9f610SMax Laier 465*13b9f610SMax Laier static void 466*13b9f610SMax Laier connect_pasv_backchannel(void) 467*13b9f610SMax Laier { 468*13b9f610SMax Laier struct sockaddr_in listen_sa; 469*13b9f610SMax Laier socklen_t salen; 470*13b9f610SMax Laier 471*13b9f610SMax Laier /* 472*13b9f610SMax Laier * We are about to accept a connection from the client. 473*13b9f610SMax Laier * This is a PASV data connection. 474*13b9f610SMax Laier */ 475*13b9f610SMax Laier debuglog(2, "client listen socket ready"); 476*13b9f610SMax Laier 477*13b9f610SMax Laier close_server_data(); 478*13b9f610SMax Laier close_client_data(); 479*13b9f610SMax Laier 480*13b9f610SMax Laier salen = sizeof(listen_sa); 481*13b9f610SMax Laier client_data_socket = accept(client_listen_socket, 482*13b9f610SMax Laier (struct sockaddr *)&listen_sa, &salen); 483*13b9f610SMax Laier 484*13b9f610SMax Laier if (client_data_socket < 0) { 485*13b9f610SMax Laier syslog(LOG_NOTICE, "accept() failed (%m)"); 486*13b9f610SMax Laier exit(EX_OSERR); 487*13b9f610SMax Laier } 488*13b9f610SMax Laier close(client_listen_socket); 489*13b9f610SMax Laier client_listen_socket = -1; 490*13b9f610SMax Laier memset(&listen_sa, 0, sizeof(listen_sa)); 491*13b9f610SMax Laier 492*13b9f610SMax Laier server_data_socket = get_backchannel_socket(SOCK_STREAM, min_port, 493*13b9f610SMax Laier max_port, -1, 1, &listen_sa); 494*13b9f610SMax Laier if (server_data_socket < 0) { 495*13b9f610SMax Laier syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); 496*13b9f610SMax Laier exit(EX_OSERR); 497*13b9f610SMax Laier } 498*13b9f610SMax Laier if (connect(server_data_socket, (struct sockaddr *) &server_listen_sa, 499*13b9f610SMax Laier sizeof(server_listen_sa)) != 0) { 500*13b9f610SMax Laier syslog(LOG_NOTICE, "connect() failed (%m)"); 501*13b9f610SMax Laier exit(EX_NOHOST); 502*13b9f610SMax Laier } 503*13b9f610SMax Laier client_data_bytes = 0; 504*13b9f610SMax Laier server_data_bytes = 0; 505*13b9f610SMax Laier xfer_start_time = wallclock_time(); 506*13b9f610SMax Laier } 507*13b9f610SMax Laier 508*13b9f610SMax Laier static void 509*13b9f610SMax Laier connect_port_backchannel(void) 510*13b9f610SMax Laier { 511*13b9f610SMax Laier struct sockaddr_in listen_sa; 512*13b9f610SMax Laier socklen_t salen; 513*13b9f610SMax Laier 514*13b9f610SMax Laier /* 515*13b9f610SMax Laier * We are about to accept a connection from the server. 516*13b9f610SMax Laier * This is a PORT or EPRT data connection. 517*13b9f610SMax Laier */ 518*13b9f610SMax Laier debuglog(2, "server listen socket ready"); 519*13b9f610SMax Laier 520*13b9f610SMax Laier close_server_data(); 521*13b9f610SMax Laier close_client_data(); 522*13b9f610SMax Laier 523*13b9f610SMax Laier salen = sizeof(listen_sa); 524*13b9f610SMax Laier server_data_socket = accept(server_listen_socket, 525*13b9f610SMax Laier (struct sockaddr *)&listen_sa, &salen); 526*13b9f610SMax Laier if (server_data_socket < 0) { 527*13b9f610SMax Laier syslog(LOG_NOTICE, "accept() failed (%m)"); 528*13b9f610SMax Laier exit(EX_OSERR); 529*13b9f610SMax Laier } 530*13b9f610SMax Laier close(server_listen_socket); 531*13b9f610SMax Laier server_listen_socket = -1; 532*13b9f610SMax Laier 533*13b9f610SMax Laier if (getuid() != 0) { 534*13b9f610SMax Laier /* 535*13b9f610SMax Laier * We're not running as root, so we get a backchannel 536*13b9f610SMax Laier * socket bound in our designated range, instead of 537*13b9f610SMax Laier * getting one bound to port 20 - This is deliberately 538*13b9f610SMax Laier * not RFC compliant. 539*13b9f610SMax Laier */ 540*13b9f610SMax Laier bzero(&listen_sa.sin_addr, sizeof(struct in_addr)); 541*13b9f610SMax Laier client_data_socket = get_backchannel_socket(SOCK_STREAM, 542*13b9f610SMax Laier min_port, max_port, -1, 1, &listen_sa); 543*13b9f610SMax Laier if (client_data_socket < 0) { 544*13b9f610SMax Laier syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); 545*13b9f610SMax Laier exit(EX_OSERR); 546*13b9f610SMax Laier } 547*13b9f610SMax Laier 548*13b9f610SMax Laier } else { 549*13b9f610SMax Laier 550*13b9f610SMax Laier /* 551*13b9f610SMax Laier * We're root, get our backchannel socket bound to port 552*13b9f610SMax Laier * 20 here, so we're fully RFC compliant. 553*13b9f610SMax Laier */ 554*13b9f610SMax Laier client_data_socket = socket(AF_INET, SOCK_STREAM, 0); 555*13b9f610SMax Laier 556*13b9f610SMax Laier salen = 1; 557*13b9f610SMax Laier listen_sa.sin_family = AF_INET; 558*13b9f610SMax Laier bzero(&listen_sa.sin_addr, sizeof(struct in_addr)); 559*13b9f610SMax Laier listen_sa.sin_port = htons(20); 560*13b9f610SMax Laier 561*13b9f610SMax Laier if (setsockopt(client_data_socket, SOL_SOCKET, SO_REUSEADDR, 562*13b9f610SMax Laier &salen, sizeof(salen)) == -1) { 563*13b9f610SMax Laier syslog(LOG_NOTICE, "setsockopt() failed (%m)"); 564*13b9f610SMax Laier exit(EX_OSERR); 565*13b9f610SMax Laier } 566*13b9f610SMax Laier 567*13b9f610SMax Laier if (bind(client_data_socket, (struct sockaddr *)&listen_sa, 568*13b9f610SMax Laier sizeof(listen_sa)) == - 1) { 569*13b9f610SMax Laier syslog(LOG_NOTICE, "data channel bind() failed (%m)"); 570*13b9f610SMax Laier exit(EX_OSERR); 571*13b9f610SMax Laier } 572*13b9f610SMax Laier } 573*13b9f610SMax Laier 574*13b9f610SMax Laier if (connect(client_data_socket, (struct sockaddr *) &client_listen_sa, 575*13b9f610SMax Laier sizeof(client_listen_sa)) != 0) { 576*13b9f610SMax Laier syslog(LOG_INFO, "cannot connect data channel (%m)"); 577*13b9f610SMax Laier exit(EX_NOHOST); 578*13b9f610SMax Laier } 579*13b9f610SMax Laier 580*13b9f610SMax Laier client_data_bytes = 0; 581*13b9f610SMax Laier server_data_bytes = 0; 582*13b9f610SMax Laier xfer_start_time = wallclock_time(); 583*13b9f610SMax Laier } 584*13b9f610SMax Laier 585*13b9f610SMax Laier void 586*13b9f610SMax Laier do_client_cmd(struct csiob *client, struct csiob *server) 587*13b9f610SMax Laier { 588*13b9f610SMax Laier int i, j, rv; 589*13b9f610SMax Laier char tbuf[100]; 590*13b9f610SMax Laier char *sendbuf = NULL; 591*13b9f610SMax Laier 592*13b9f610SMax Laier log_control_command((char *)client->line_buffer, 1); 593*13b9f610SMax Laier 594*13b9f610SMax Laier /* client->line_buffer is an ftp control command. 595*13b9f610SMax Laier * There is no reason for these to be very long. 596*13b9f610SMax Laier * In the interest of limiting buffer overrun attempts, 597*13b9f610SMax Laier * we catch them here. 598*13b9f610SMax Laier */ 599*13b9f610SMax Laier if (strlen((char *)client->line_buffer) > 512) { 600*13b9f610SMax Laier syslog(LOG_NOTICE, "excessively long control command"); 601*13b9f610SMax Laier exit(EX_DATAERR); 602*13b9f610SMax Laier } 603*13b9f610SMax Laier 604*13b9f610SMax Laier /* 605*13b9f610SMax Laier * Check the client user provided if needed 606*13b9f610SMax Laier */ 607*13b9f610SMax Laier if (AnonFtpOnly && strncasecmp((char *)client->line_buffer, "user ", 608*13b9f610SMax Laier strlen("user ")) == 0) { 609*13b9f610SMax Laier char *cp; 610*13b9f610SMax Laier 611*13b9f610SMax Laier cp = (char *) client->line_buffer + strlen("user "); 612*13b9f610SMax Laier if ((strcasecmp(cp, "ftp\r\n") != 0) && 613*13b9f610SMax Laier (strcasecmp(cp, "anonymous\r\n") != 0)) { 614*13b9f610SMax Laier /* 615*13b9f610SMax Laier * this isn't anonymous - give the client an 616*13b9f610SMax Laier * error before they send a password 617*13b9f610SMax Laier */ 618*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 619*13b9f610SMax Laier "500 Only anonymous FTP is allowed\r\n"); 620*13b9f610SMax Laier j = 0; 621*13b9f610SMax Laier i = strlen(tbuf); 622*13b9f610SMax Laier do { 623*13b9f610SMax Laier rv = send(client->fd, tbuf + j, i - j, 0); 624*13b9f610SMax Laier if (rv == -1 && errno != EAGAIN && 625*13b9f610SMax Laier errno != EINTR) 626*13b9f610SMax Laier break; 627*13b9f610SMax Laier else if (rv != -1) 628*13b9f610SMax Laier j += rv; 629*13b9f610SMax Laier } while (j >= 0 && j < i); 630*13b9f610SMax Laier sendbuf = NULL; 631*13b9f610SMax Laier } else 632*13b9f610SMax Laier sendbuf = (char *)client->line_buffer; 633*13b9f610SMax Laier } else if ((strncasecmp((char *)client->line_buffer, "eprt ", 634*13b9f610SMax Laier strlen("eprt ")) == 0)) { 635*13b9f610SMax Laier 636*13b9f610SMax Laier /* Watch out for EPRT commands */ 637*13b9f610SMax Laier char *line = NULL, *q, *p, *result[3], delim; 638*13b9f610SMax Laier struct addrinfo hints, *res = NULL; 639*13b9f610SMax Laier unsigned long proto; 640*13b9f610SMax Laier 641*13b9f610SMax Laier j = 0; 642*13b9f610SMax Laier line = strdup((char *)client->line_buffer+strlen("eprt ")); 643*13b9f610SMax Laier if (line == NULL) { 644*13b9f610SMax Laier syslog(LOG_ERR, "insufficient memory"); 645*13b9f610SMax Laier exit(EX_UNAVAILABLE); 646*13b9f610SMax Laier } 647*13b9f610SMax Laier p = line; 648*13b9f610SMax Laier delim = p[0]; 649*13b9f610SMax Laier p++; 650*13b9f610SMax Laier 651*13b9f610SMax Laier memset(result,0, sizeof(result)); 652*13b9f610SMax Laier for (i = 0; i < 3; i++) { 653*13b9f610SMax Laier q = strchr(p, delim); 654*13b9f610SMax Laier if (!q || *q != delim) 655*13b9f610SMax Laier goto parsefail; 656*13b9f610SMax Laier *q++ = '\0'; 657*13b9f610SMax Laier result[i] = p; 658*13b9f610SMax Laier p = q; 659*13b9f610SMax Laier } 660*13b9f610SMax Laier 661*13b9f610SMax Laier proto = strtoul(result[0], &p, 10); 662*13b9f610SMax Laier if (!*result[0] || *p) 663*13b9f610SMax Laier goto protounsupp; 664*13b9f610SMax Laier 665*13b9f610SMax Laier memset(&hints, 0, sizeof(hints)); 666*13b9f610SMax Laier if (proto != 1) /* 1 == AF_INET - all we support for now */ 667*13b9f610SMax Laier goto protounsupp; 668*13b9f610SMax Laier hints.ai_family = AF_INET; 669*13b9f610SMax Laier hints.ai_socktype = SOCK_STREAM; 670*13b9f610SMax Laier hints.ai_flags = AI_NUMERICHOST; /*no DNS*/ 671*13b9f610SMax Laier if (getaddrinfo(result[1], result[2], &hints, &res)) 672*13b9f610SMax Laier goto parsefail; 673*13b9f610SMax Laier if (res->ai_next) 674*13b9f610SMax Laier goto parsefail; 675*13b9f610SMax Laier if (sizeof(client_listen_sa) < res->ai_addrlen) 676*13b9f610SMax Laier goto parsefail; 677*13b9f610SMax Laier memcpy(&client_listen_sa, res->ai_addr, res->ai_addrlen); 678*13b9f610SMax Laier 679*13b9f610SMax Laier debuglog(1, "client wants us to use %s:%u", 680*13b9f610SMax Laier inet_ntoa(client_listen_sa.sin_addr), 681*13b9f610SMax Laier htons(client_listen_sa.sin_port)); 682*13b9f610SMax Laier 683*13b9f610SMax Laier /* 684*13b9f610SMax Laier * Configure our own listen socket and tell the server about it 685*13b9f610SMax Laier */ 686*13b9f610SMax Laier new_dataconn(1); 687*13b9f610SMax Laier connection_mode = EPRT_MODE; 688*13b9f610SMax Laier 689*13b9f610SMax Laier debuglog(1, "we want server to use %s:%u", 690*13b9f610SMax Laier inet_ntoa(server->sa.sin_addr), 691*13b9f610SMax Laier ntohs(server_listen_sa.sin_port)); 692*13b9f610SMax Laier 693*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), "EPRT |%d|%s|%u|\r\n", 1, 694*13b9f610SMax Laier inet_ntoa(server->sa.sin_addr), 695*13b9f610SMax Laier ntohs(server_listen_sa.sin_port)); 696*13b9f610SMax Laier debuglog(1, "to server (modified): %s", tbuf); 697*13b9f610SMax Laier sendbuf = tbuf; 698*13b9f610SMax Laier goto out; 699*13b9f610SMax Laier parsefail: 700*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 701*13b9f610SMax Laier "500 Invalid argument; rejected\r\n"); 702*13b9f610SMax Laier sendbuf = NULL; 703*13b9f610SMax Laier goto out; 704*13b9f610SMax Laier protounsupp: 705*13b9f610SMax Laier /* we only support AF_INET for now */ 706*13b9f610SMax Laier if (proto == 2) 707*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 708*13b9f610SMax Laier "522 Protocol not supported, use (1)\r\n"); 709*13b9f610SMax Laier else 710*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 711*13b9f610SMax Laier "501 Protocol not supported\r\n"); 712*13b9f610SMax Laier sendbuf = NULL; 713*13b9f610SMax Laier out: 714*13b9f610SMax Laier if (line) 715*13b9f610SMax Laier free(line); 716*13b9f610SMax Laier if (res) 717*13b9f610SMax Laier freeaddrinfo(res); 718*13b9f610SMax Laier if (sendbuf == NULL) { 719*13b9f610SMax Laier debuglog(1, "to client (modified): %s", tbuf); 720*13b9f610SMax Laier i = strlen(tbuf); 721*13b9f610SMax Laier do { 722*13b9f610SMax Laier rv = send(client->fd, tbuf + j, i - j, 0); 723*13b9f610SMax Laier if (rv == -1 && errno != EAGAIN && 724*13b9f610SMax Laier errno != EINTR) 725*13b9f610SMax Laier break; 726*13b9f610SMax Laier else if (rv != -1) 727*13b9f610SMax Laier j += rv; 728*13b9f610SMax Laier } while (j >= 0 && j < i); 729*13b9f610SMax Laier } 730*13b9f610SMax Laier } else if (!NatMode && (strncasecmp((char *)client->line_buffer, 731*13b9f610SMax Laier "epsv", strlen("epsv")) == 0)) { 732*13b9f610SMax Laier 733*13b9f610SMax Laier /* 734*13b9f610SMax Laier * If we aren't in NAT mode, deal with EPSV. 735*13b9f610SMax Laier * EPSV is a problem - Unlike PASV, the reply from the 736*13b9f610SMax Laier * server contains *only* a port, we can't modify the reply 737*13b9f610SMax Laier * to the client and get the client to connect to us without 738*13b9f610SMax Laier * resorting to using a dynamic rdr rule we have to add in 739*13b9f610SMax Laier * for the reply to this connection, and take away afterwards. 740*13b9f610SMax Laier * so this will wait until we have the right solution for rule 741*13b9f610SMax Laier * additions/deletions in pf. 742*13b9f610SMax Laier * 743*13b9f610SMax Laier * in the meantime we just tell the client we don't do it, 744*13b9f610SMax Laier * and most clients should fall back to using PASV. 745*13b9f610SMax Laier */ 746*13b9f610SMax Laier 747*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 748*13b9f610SMax Laier "500 EPSV command not understood\r\n"); 749*13b9f610SMax Laier debuglog(1, "to client (modified): %s", tbuf); 750*13b9f610SMax Laier j = 0; 751*13b9f610SMax Laier i = strlen(tbuf); 752*13b9f610SMax Laier do { 753*13b9f610SMax Laier rv = send(client->fd, tbuf + j, i - j, 0); 754*13b9f610SMax Laier if (rv == -1 && errno != EAGAIN && errno != EINTR) 755*13b9f610SMax Laier break; 756*13b9f610SMax Laier else if (rv != -1) 757*13b9f610SMax Laier j += rv; 758*13b9f610SMax Laier } while (j >= 0 && j < i); 759*13b9f610SMax Laier sendbuf = NULL; 760*13b9f610SMax Laier } else if (strncasecmp((char *)client->line_buffer, "port ", 761*13b9f610SMax Laier strlen("port ")) == 0) { 762*13b9f610SMax Laier unsigned int values[6]; 763*13b9f610SMax Laier char *tailptr; 764*13b9f610SMax Laier 765*13b9f610SMax Laier debuglog(1, "Got a PORT command"); 766*13b9f610SMax Laier 767*13b9f610SMax Laier tailptr = (char *)&client->line_buffer[strlen("port ")]; 768*13b9f610SMax Laier values[0] = 0; 769*13b9f610SMax Laier 770*13b9f610SMax Laier i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0], 771*13b9f610SMax Laier &values[1], &values[2], &values[3], &values[4], 772*13b9f610SMax Laier &values[5]); 773*13b9f610SMax Laier if (i != 6) { 774*13b9f610SMax Laier syslog(LOG_INFO, "malformed PORT command (%s)", 775*13b9f610SMax Laier client->line_buffer); 776*13b9f610SMax Laier exit(EX_DATAERR); 777*13b9f610SMax Laier } 778*13b9f610SMax Laier 779*13b9f610SMax Laier for (i = 0; i<6; i++) { 780*13b9f610SMax Laier if (values[i] > 255) { 781*13b9f610SMax Laier syslog(LOG_INFO, 782*13b9f610SMax Laier "malformed PORT command (%s)", 783*13b9f610SMax Laier client->line_buffer); 784*13b9f610SMax Laier exit(EX_DATAERR); 785*13b9f610SMax Laier } 786*13b9f610SMax Laier } 787*13b9f610SMax Laier 788*13b9f610SMax Laier client_listen_sa.sin_family = AF_INET; 789*13b9f610SMax Laier client_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | 790*13b9f610SMax Laier (values[1] << 16) | (values[2] << 8) | 791*13b9f610SMax Laier (values[3] << 0)); 792*13b9f610SMax Laier 793*13b9f610SMax Laier client_listen_sa.sin_port = htons((values[4] << 8) | 794*13b9f610SMax Laier values[5]); 795*13b9f610SMax Laier debuglog(1, "client wants us to use %u.%u.%u.%u:%u", 796*13b9f610SMax Laier values[0], values[1], values[2], values[3], 797*13b9f610SMax Laier (values[4] << 8) | values[5]); 798*13b9f610SMax Laier 799*13b9f610SMax Laier /* 800*13b9f610SMax Laier * Configure our own listen socket and tell the server about it 801*13b9f610SMax Laier */ 802*13b9f610SMax Laier new_dataconn(1); 803*13b9f610SMax Laier connection_mode = PORT_MODE; 804*13b9f610SMax Laier 805*13b9f610SMax Laier debuglog(1, "we want server to use %s:%u", 806*13b9f610SMax Laier inet_ntoa(server->sa.sin_addr), 807*13b9f610SMax Laier ntohs(server_listen_sa.sin_port)); 808*13b9f610SMax Laier 809*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), "PORT %u,%u,%u,%u,%u,%u\r\n", 810*13b9f610SMax Laier ((u_char *)&server->sa.sin_addr.s_addr)[0], 811*13b9f610SMax Laier ((u_char *)&server->sa.sin_addr.s_addr)[1], 812*13b9f610SMax Laier ((u_char *)&server->sa.sin_addr.s_addr)[2], 813*13b9f610SMax Laier ((u_char *)&server->sa.sin_addr.s_addr)[3], 814*13b9f610SMax Laier ((u_char *)&server_listen_sa.sin_port)[0], 815*13b9f610SMax Laier ((u_char *)&server_listen_sa.sin_port)[1]); 816*13b9f610SMax Laier 817*13b9f610SMax Laier debuglog(1, "to server (modified): %s", tbuf); 818*13b9f610SMax Laier 819*13b9f610SMax Laier sendbuf = tbuf; 820*13b9f610SMax Laier } else 821*13b9f610SMax Laier sendbuf = (char *)client->line_buffer; 822*13b9f610SMax Laier 823*13b9f610SMax Laier /* 824*13b9f610SMax Laier *send our (possibly modified) control command in sendbuf 825*13b9f610SMax Laier * on it's way to the server 826*13b9f610SMax Laier */ 827*13b9f610SMax Laier if (sendbuf != NULL) { 828*13b9f610SMax Laier j = 0; 829*13b9f610SMax Laier i = strlen(sendbuf); 830*13b9f610SMax Laier do { 831*13b9f610SMax Laier rv = send(server->fd, sendbuf + j, i - j, 0); 832*13b9f610SMax Laier if (rv == -1 && errno != EAGAIN && errno != EINTR) 833*13b9f610SMax Laier break; 834*13b9f610SMax Laier else if (rv != -1) 835*13b9f610SMax Laier j += rv; 836*13b9f610SMax Laier } while (j >= 0 && j < i); 837*13b9f610SMax Laier } 838*13b9f610SMax Laier } 839*13b9f610SMax Laier 840*13b9f610SMax Laier void 841*13b9f610SMax Laier do_server_reply(struct csiob *server, struct csiob *client) 842*13b9f610SMax Laier { 843*13b9f610SMax Laier int code, i, j, rv; 844*13b9f610SMax Laier struct in_addr *iap; 845*13b9f610SMax Laier static int continuing = 0; 846*13b9f610SMax Laier char tbuf[100], *sendbuf, *p; 847*13b9f610SMax Laier 848*13b9f610SMax Laier log_control_command((char *)server->line_buffer, 0); 849*13b9f610SMax Laier 850*13b9f610SMax Laier if (strlen((char *)server->line_buffer) > 512) { 851*13b9f610SMax Laier /* 852*13b9f610SMax Laier * someone's playing games. Have a cow in the syslogs and 853*13b9f610SMax Laier * exit - we don't pass this on for fear of hurting 854*13b9f610SMax Laier * our other end, which might be poorly implemented. 855*13b9f610SMax Laier */ 856*13b9f610SMax Laier syslog(LOG_NOTICE, "long FTP control reply"); 857*13b9f610SMax Laier exit(EX_DATAERR); 858*13b9f610SMax Laier } 859*13b9f610SMax Laier 860*13b9f610SMax Laier /* 861*13b9f610SMax Laier * Watch out for "227 Entering Passive Mode ..." replies 862*13b9f610SMax Laier */ 863*13b9f610SMax Laier code = strtol((char *)server->line_buffer, &p, 10); 864*13b9f610SMax Laier if (isspace(server->line_buffer[0])) 865*13b9f610SMax Laier code = 0; 866*13b9f610SMax Laier if (!*(server->line_buffer) || (*p != ' ' && *p != '-')) { 867*13b9f610SMax Laier if (continuing) 868*13b9f610SMax Laier goto sendit; 869*13b9f610SMax Laier syslog(LOG_INFO, "malformed control reply"); 870*13b9f610SMax Laier exit(EX_DATAERR); 871*13b9f610SMax Laier } 872*13b9f610SMax Laier if (code <= 0 || code > 999) { 873*13b9f610SMax Laier if (continuing) 874*13b9f610SMax Laier goto sendit; 875*13b9f610SMax Laier syslog(LOG_INFO, "invalid server reply code %d", code); 876*13b9f610SMax Laier exit(EX_DATAERR); 877*13b9f610SMax Laier } 878*13b9f610SMax Laier if (*p == '-') 879*13b9f610SMax Laier continuing = 1; 880*13b9f610SMax Laier else 881*13b9f610SMax Laier continuing = 0; 882*13b9f610SMax Laier if (code == 227 && !NatMode) { 883*13b9f610SMax Laier unsigned int values[6]; 884*13b9f610SMax Laier char *tailptr; 885*13b9f610SMax Laier 886*13b9f610SMax Laier debuglog(1, "Got a PASV reply"); 887*13b9f610SMax Laier debuglog(1, "{%s}", (char *)server->line_buffer); 888*13b9f610SMax Laier 889*13b9f610SMax Laier tailptr = (char *)strchr((char *)server->line_buffer, '('); 890*13b9f610SMax Laier if (tailptr == NULL) { 891*13b9f610SMax Laier tailptr = strrchr((char *)server->line_buffer, ' '); 892*13b9f610SMax Laier if (tailptr == NULL) { 893*13b9f610SMax Laier syslog(LOG_NOTICE, "malformed 227 reply"); 894*13b9f610SMax Laier exit(EX_DATAERR); 895*13b9f610SMax Laier } 896*13b9f610SMax Laier } 897*13b9f610SMax Laier tailptr++; /* skip past space or ( */ 898*13b9f610SMax Laier 899*13b9f610SMax Laier values[0] = 0; 900*13b9f610SMax Laier 901*13b9f610SMax Laier i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0], 902*13b9f610SMax Laier &values[1], &values[2], &values[3], &values[4], 903*13b9f610SMax Laier &values[5]); 904*13b9f610SMax Laier if (i != 6) { 905*13b9f610SMax Laier syslog(LOG_INFO, "malformed PASV reply (%s)", 906*13b9f610SMax Laier client->line_buffer); 907*13b9f610SMax Laier exit(EX_DATAERR); 908*13b9f610SMax Laier } 909*13b9f610SMax Laier for (i = 0; i<6; i++) 910*13b9f610SMax Laier if (values[i] > 255) { 911*13b9f610SMax Laier syslog(LOG_INFO, "malformed PASV reply(%s)", 912*13b9f610SMax Laier client->line_buffer); 913*13b9f610SMax Laier exit(EX_DATAERR); 914*13b9f610SMax Laier } 915*13b9f610SMax Laier 916*13b9f610SMax Laier server_listen_sa.sin_family = AF_INET; 917*13b9f610SMax Laier server_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | 918*13b9f610SMax Laier (values[1] << 16) | (values[2] << 8) | (values[3] << 0)); 919*13b9f610SMax Laier server_listen_sa.sin_port = htons((values[4] << 8) | 920*13b9f610SMax Laier values[5]); 921*13b9f610SMax Laier 922*13b9f610SMax Laier debuglog(1, "server wants us to use %s:%u", 923*13b9f610SMax Laier inet_ntoa(server_listen_sa.sin_addr), (values[4] << 8) | 924*13b9f610SMax Laier values[5]); 925*13b9f610SMax Laier 926*13b9f610SMax Laier new_dataconn(0); 927*13b9f610SMax Laier connection_mode = PASV_MODE; 928*13b9f610SMax Laier iap = &(server->sa.sin_addr); 929*13b9f610SMax Laier 930*13b9f610SMax Laier debuglog(1, "we want client to use %s:%u", inet_ntoa(*iap), 931*13b9f610SMax Laier htons(client_listen_sa.sin_port)); 932*13b9f610SMax Laier 933*13b9f610SMax Laier snprintf(tbuf, sizeof(tbuf), 934*13b9f610SMax Laier "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n", 935*13b9f610SMax Laier ((u_char *)iap)[0], ((u_char *)iap)[1], 936*13b9f610SMax Laier ((u_char *)iap)[2], ((u_char *)iap)[3], 937*13b9f610SMax Laier ((u_char *)&client_listen_sa.sin_port)[0], 938*13b9f610SMax Laier ((u_char *)&client_listen_sa.sin_port)[1]); 939*13b9f610SMax Laier debuglog(1, "to client (modified): %s", tbuf); 940*13b9f610SMax Laier sendbuf = tbuf; 941*13b9f610SMax Laier } else { 942*13b9f610SMax Laier sendit: 943*13b9f610SMax Laier sendbuf = (char *)server->line_buffer; 944*13b9f610SMax Laier } 945*13b9f610SMax Laier 946*13b9f610SMax Laier /* 947*13b9f610SMax Laier * send our (possibly modified) control command in sendbuf 948*13b9f610SMax Laier * on it's way to the client 949*13b9f610SMax Laier */ 950*13b9f610SMax Laier j = 0; 951*13b9f610SMax Laier i = strlen(sendbuf); 952*13b9f610SMax Laier do { 953*13b9f610SMax Laier rv = send(client->fd, sendbuf + j, i - j, 0); 954*13b9f610SMax Laier if (rv == -1 && errno != EAGAIN && errno != EINTR) 955*13b9f610SMax Laier break; 956*13b9f610SMax Laier else if (rv != -1) 957*13b9f610SMax Laier j += rv; 958*13b9f610SMax Laier } while (j >= 0 && j < i); 959*13b9f610SMax Laier 960*13b9f610SMax Laier } 961*13b9f610SMax Laier 962*13b9f610SMax Laier int 963*13b9f610SMax Laier main(int argc, char *argv[]) 964*13b9f610SMax Laier { 965*13b9f610SMax Laier struct csiob client_iob, server_iob; 966*13b9f610SMax Laier struct sigaction new_sa, old_sa; 967*13b9f610SMax Laier int sval, ch, flags, i; 968*13b9f610SMax Laier socklen_t salen; 969*13b9f610SMax Laier int one = 1; 970*13b9f610SMax Laier long timeout_seconds = 0; 971*13b9f610SMax Laier struct timeval tv; 972*13b9f610SMax Laier #ifdef LIBWRAP 973*13b9f610SMax Laier int use_tcpwrapper = 0; 974*13b9f610SMax Laier #endif /* LIBWRAP */ 975*13b9f610SMax Laier 976*13b9f610SMax Laier while ((ch = getopt(argc, argv, "D:g:m:M:t:u:AnVwr")) != -1) { 977*13b9f610SMax Laier char *p; 978*13b9f610SMax Laier switch (ch) { 979*13b9f610SMax Laier case 'A': 980*13b9f610SMax Laier AnonFtpOnly = 1; /* restrict to anon usernames only */ 981*13b9f610SMax Laier break; 982*13b9f610SMax Laier case 'D': 983*13b9f610SMax Laier Debug_Level = strtol(optarg, &p, 10); 984*13b9f610SMax Laier if (!*optarg || *p) 985*13b9f610SMax Laier usage(); 986*13b9f610SMax Laier break; 987*13b9f610SMax Laier case 'g': 988*13b9f610SMax Laier Group = optarg; 989*13b9f610SMax Laier break; 990*13b9f610SMax Laier case 'm': 991*13b9f610SMax Laier min_port = strtol(optarg, &p, 10); 992*13b9f610SMax Laier if (!*optarg || *p) 993*13b9f610SMax Laier usage(); 994*13b9f610SMax Laier if (min_port < 0 || min_port > USHRT_MAX) 995*13b9f610SMax Laier usage(); 996*13b9f610SMax Laier break; 997*13b9f610SMax Laier case 'M': 998*13b9f610SMax Laier max_port = strtol(optarg, &p, 10); 999*13b9f610SMax Laier if (!*optarg || *p) 1000*13b9f610SMax Laier usage(); 1001*13b9f610SMax Laier if (max_port < 0 || max_port > USHRT_MAX) 1002*13b9f610SMax Laier usage(); 1003*13b9f610SMax Laier break; 1004*13b9f610SMax Laier case 'n': 1005*13b9f610SMax Laier NatMode = 1; /* pass all passives, we're using NAT */ 1006*13b9f610SMax Laier break; 1007*13b9f610SMax Laier case 'r': 1008*13b9f610SMax Laier Use_Rdns = 1; /* look up hostnames */ 1009*13b9f610SMax Laier break; 1010*13b9f610SMax Laier case 't': 1011*13b9f610SMax Laier timeout_seconds = strtol(optarg, &p, 10); 1012*13b9f610SMax Laier if (!*optarg || *p) 1013*13b9f610SMax Laier usage(); 1014*13b9f610SMax Laier break; 1015*13b9f610SMax Laier case 'u': 1016*13b9f610SMax Laier User = optarg; 1017*13b9f610SMax Laier break; 1018*13b9f610SMax Laier case 'V': 1019*13b9f610SMax Laier Verbose = 1; 1020*13b9f610SMax Laier break; 1021*13b9f610SMax Laier #ifdef LIBWRAP 1022*13b9f610SMax Laier case 'w': 1023*13b9f610SMax Laier use_tcpwrapper = 1; /* do the libwrap thing */ 1024*13b9f610SMax Laier break; 1025*13b9f610SMax Laier #endif /* LIBWRAP */ 1026*13b9f610SMax Laier default: 1027*13b9f610SMax Laier usage(); 1028*13b9f610SMax Laier /* NOTREACHED */ 1029*13b9f610SMax Laier } 1030*13b9f610SMax Laier } 1031*13b9f610SMax Laier argc -= optind; 1032*13b9f610SMax Laier argv += optind; 1033*13b9f610SMax Laier 1034*13b9f610SMax Laier if (max_port < min_port) 1035*13b9f610SMax Laier usage(); 1036*13b9f610SMax Laier 1037*13b9f610SMax Laier openlog(__progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); 1038*13b9f610SMax Laier 1039*13b9f610SMax Laier setlinebuf(stdout); 1040*13b9f610SMax Laier setlinebuf(stderr); 1041*13b9f610SMax Laier 1042*13b9f610SMax Laier memset(&client_iob, 0, sizeof(client_iob)); 1043*13b9f610SMax Laier memset(&server_iob, 0, sizeof(server_iob)); 1044*13b9f610SMax Laier 1045*13b9f610SMax Laier if (get_proxy_env(0, &real_server_sa, &client_iob.sa) == -1) 1046*13b9f610SMax Laier exit(EX_PROTOCOL); 1047*13b9f610SMax Laier 1048*13b9f610SMax Laier /* 1049*13b9f610SMax Laier * We may now drop root privs, as we have done our ioctl for 1050*13b9f610SMax Laier * pf. If we do drop root, we can't make backchannel connections 1051*13b9f610SMax Laier * for PORT and EPRT come from port 20, which is not strictly 1052*13b9f610SMax Laier * RFC compliant. This shouldn't cause problems for all but 1053*13b9f610SMax Laier * the stupidest ftp clients and the stupidest packet filters. 1054*13b9f610SMax Laier */ 1055*13b9f610SMax Laier drop_privs(); 1056*13b9f610SMax Laier 1057*13b9f610SMax Laier /* 1058*13b9f610SMax Laier * We check_host after get_proxy_env so that checks are done 1059*13b9f610SMax Laier * against the original destination endpoint, not the endpoint 1060*13b9f610SMax Laier * of our side of the rdr. This allows the use of tcpwrapper 1061*13b9f610SMax Laier * rules to restrict destinations as well as sources of connections 1062*13b9f610SMax Laier * for ftp. 1063*13b9f610SMax Laier */ 1064*13b9f610SMax Laier if (Use_Rdns) 1065*13b9f610SMax Laier flags = 0; 1066*13b9f610SMax Laier else 1067*13b9f610SMax Laier flags = NI_NUMERICHOST | NI_NUMERICSERV; 1068*13b9f610SMax Laier 1069*13b9f610SMax Laier i = getnameinfo((struct sockaddr *)&client_iob.sa, 1070*13b9f610SMax Laier sizeof(client_iob.sa), ClientName, sizeof(ClientName), NULL, 0, 1071*13b9f610SMax Laier flags); 1072*13b9f610SMax Laier 1073*13b9f610SMax Laier if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1074*13b9f610SMax Laier debuglog(2, "name resolution failure (client)"); 1075*13b9f610SMax Laier exit(EX_OSERR); 1076*13b9f610SMax Laier } 1077*13b9f610SMax Laier 1078*13b9f610SMax Laier i = getnameinfo((struct sockaddr *)&real_server_sa, 1079*13b9f610SMax Laier sizeof(real_server_sa), RealServerName, sizeof(RealServerName), 1080*13b9f610SMax Laier NULL, 0, flags); 1081*13b9f610SMax Laier 1082*13b9f610SMax Laier if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1083*13b9f610SMax Laier debuglog(2, "name resolution failure (server)"); 1084*13b9f610SMax Laier exit(EX_OSERR); 1085*13b9f610SMax Laier } 1086*13b9f610SMax Laier 1087*13b9f610SMax Laier #ifdef LIBWRAP 1088*13b9f610SMax Laier if (use_tcpwrapper && !check_host(&client_iob.sa, &real_server_sa)) 1089*13b9f610SMax Laier exit(EX_NOPERM); 1090*13b9f610SMax Laier #endif 1091*13b9f610SMax Laier 1092*13b9f610SMax Laier client_iob.fd = 0; 1093*13b9f610SMax Laier 1094*13b9f610SMax Laier syslog(LOG_INFO, "accepted connection from %s:%u to %s:%u", ClientName, 1095*13b9f610SMax Laier ntohs(client_iob.sa.sin_port), RealServerName, 1096*13b9f610SMax Laier ntohs(real_server_sa.sin_port)); 1097*13b9f610SMax Laier 1098*13b9f610SMax Laier server_iob.fd = get_backchannel_socket(SOCK_STREAM, min_port, max_port, 1099*13b9f610SMax Laier -1, 1, &server_iob.sa); 1100*13b9f610SMax Laier 1101*13b9f610SMax Laier if (connect(server_iob.fd, (struct sockaddr *)&real_server_sa, 1102*13b9f610SMax Laier sizeof(real_server_sa)) != 0) { 1103*13b9f610SMax Laier syslog(LOG_INFO, "cannot connect to %s:%u (%m)", RealServerName, 1104*13b9f610SMax Laier ntohs(real_server_sa.sin_port)); 1105*13b9f610SMax Laier exit(EX_NOHOST); 1106*13b9f610SMax Laier } 1107*13b9f610SMax Laier 1108*13b9f610SMax Laier /* 1109*13b9f610SMax Laier * Now that we are connected to the real server, get the name 1110*13b9f610SMax Laier * of our end of the server socket so we know our IP address 1111*13b9f610SMax Laier * from the real server's perspective. 1112*13b9f610SMax Laier */ 1113*13b9f610SMax Laier salen = sizeof(server_iob.sa); 1114*13b9f610SMax Laier getsockname(server_iob.fd, (struct sockaddr *)&server_iob.sa, &salen); 1115*13b9f610SMax Laier 1116*13b9f610SMax Laier i = getnameinfo((struct sockaddr *)&server_iob.sa, 1117*13b9f610SMax Laier sizeof(server_iob.sa), OurName, sizeof(OurName), NULL, 0, flags); 1118*13b9f610SMax Laier 1119*13b9f610SMax Laier if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1120*13b9f610SMax Laier debuglog(2, "name resolution failure (local)"); 1121*13b9f610SMax Laier exit(EX_OSERR); 1122*13b9f610SMax Laier } 1123*13b9f610SMax Laier 1124*13b9f610SMax Laier debuglog(1, "local socket is %s:%u", OurName, 1125*13b9f610SMax Laier ntohs(server_iob.sa.sin_port)); 1126*13b9f610SMax Laier 1127*13b9f610SMax Laier /* ignore SIGPIPE */ 1128*13b9f610SMax Laier bzero(&new_sa, sizeof(new_sa)); 1129*13b9f610SMax Laier new_sa.sa_handler = SIG_IGN; 1130*13b9f610SMax Laier (void)sigemptyset(&new_sa.sa_mask); 1131*13b9f610SMax Laier new_sa.sa_flags = SA_RESTART; 1132*13b9f610SMax Laier if (sigaction(SIGPIPE, &new_sa, &old_sa) != 0) { 1133*13b9f610SMax Laier syslog(LOG_ERR, "sigaction() failed (%m)"); 1134*13b9f610SMax Laier exit(EX_OSERR); 1135*13b9f610SMax Laier } 1136*13b9f610SMax Laier 1137*13b9f610SMax Laier if (setsockopt(client_iob.fd, SOL_SOCKET, SO_OOBINLINE, (char *)&one, 1138*13b9f610SMax Laier sizeof(one)) == -1) { 1139*13b9f610SMax Laier syslog(LOG_NOTICE, "cannot set SO_OOBINLINE (%m)"); 1140*13b9f610SMax Laier exit(EX_OSERR); 1141*13b9f610SMax Laier } 1142*13b9f610SMax Laier 1143*13b9f610SMax Laier client_iob.line_buffer_size = STARTBUFSIZE; 1144*13b9f610SMax Laier client_iob.line_buffer = malloc(client_iob.line_buffer_size); 1145*13b9f610SMax Laier client_iob.io_buffer_size = STARTBUFSIZE; 1146*13b9f610SMax Laier client_iob.io_buffer = malloc(client_iob.io_buffer_size); 1147*13b9f610SMax Laier client_iob.next_byte = 0; 1148*13b9f610SMax Laier client_iob.io_buffer_len = 0; 1149*13b9f610SMax Laier client_iob.alive = 1; 1150*13b9f610SMax Laier client_iob.who = "client"; 1151*13b9f610SMax Laier client_iob.send_oob_flags = 0; 1152*13b9f610SMax Laier client_iob.real_sa = client_iob.sa; 1153*13b9f610SMax Laier 1154*13b9f610SMax Laier server_iob.line_buffer_size = STARTBUFSIZE; 1155*13b9f610SMax Laier server_iob.line_buffer = malloc(server_iob.line_buffer_size); 1156*13b9f610SMax Laier server_iob.io_buffer_size = STARTBUFSIZE; 1157*13b9f610SMax Laier server_iob.io_buffer = malloc(server_iob.io_buffer_size); 1158*13b9f610SMax Laier server_iob.next_byte = 0; 1159*13b9f610SMax Laier server_iob.io_buffer_len = 0; 1160*13b9f610SMax Laier server_iob.alive = 1; 1161*13b9f610SMax Laier server_iob.who = "server"; 1162*13b9f610SMax Laier server_iob.send_oob_flags = MSG_OOB; 1163*13b9f610SMax Laier server_iob.real_sa = real_server_sa; 1164*13b9f610SMax Laier 1165*13b9f610SMax Laier if (client_iob.line_buffer == NULL || client_iob.io_buffer == NULL || 1166*13b9f610SMax Laier server_iob.line_buffer == NULL || server_iob.io_buffer == NULL) { 1167*13b9f610SMax Laier syslog (LOG_NOTICE, "insufficient memory"); 1168*13b9f610SMax Laier exit(EX_UNAVAILABLE); 1169*13b9f610SMax Laier } 1170*13b9f610SMax Laier 1171*13b9f610SMax Laier while (client_iob.alive || server_iob.alive) { 1172*13b9f610SMax Laier int maxfd = 0; 1173*13b9f610SMax Laier fd_set *fdsp; 1174*13b9f610SMax Laier 1175*13b9f610SMax Laier if (client_iob.fd > maxfd) 1176*13b9f610SMax Laier maxfd = client_iob.fd; 1177*13b9f610SMax Laier if (client_listen_socket > maxfd) 1178*13b9f610SMax Laier maxfd = client_listen_socket; 1179*13b9f610SMax Laier if (client_data_socket > maxfd) 1180*13b9f610SMax Laier maxfd = client_data_socket; 1181*13b9f610SMax Laier if (server_iob.fd > maxfd) 1182*13b9f610SMax Laier maxfd = server_iob.fd; 1183*13b9f610SMax Laier if (server_listen_socket > maxfd) 1184*13b9f610SMax Laier maxfd = server_listen_socket; 1185*13b9f610SMax Laier if (server_data_socket > maxfd) 1186*13b9f610SMax Laier maxfd = server_data_socket; 1187*13b9f610SMax Laier 1188*13b9f610SMax Laier debuglog(3, "client is %s; server is %s", 1189*13b9f610SMax Laier client_iob.alive ? "alive" : "dead", 1190*13b9f610SMax Laier server_iob.alive ? "alive" : "dead"); 1191*13b9f610SMax Laier 1192*13b9f610SMax Laier fdsp = (fd_set *)calloc(howmany(maxfd + 1, NFDBITS), 1193*13b9f610SMax Laier sizeof(fd_mask)); 1194*13b9f610SMax Laier if (fdsp == NULL) { 1195*13b9f610SMax Laier syslog(LOG_NOTICE, "insufficient memory"); 1196*13b9f610SMax Laier exit(EX_UNAVAILABLE); 1197*13b9f610SMax Laier } 1198*13b9f610SMax Laier 1199*13b9f610SMax Laier if (client_iob.alive && telnet_getline(&client_iob, 1200*13b9f610SMax Laier &server_iob)) { 1201*13b9f610SMax Laier debuglog(3, "client line buffer is \"%s\"", 1202*13b9f610SMax Laier (char *)client_iob.line_buffer); 1203*13b9f610SMax Laier if (client_iob.line_buffer[0] != '\0') 1204*13b9f610SMax Laier do_client_cmd(&client_iob, &server_iob); 1205*13b9f610SMax Laier } else if (server_iob.alive && telnet_getline(&server_iob, 1206*13b9f610SMax Laier &client_iob)) { 1207*13b9f610SMax Laier debuglog(3, "server line buffer is \"%s\"", 1208*13b9f610SMax Laier (char *)server_iob.line_buffer); 1209*13b9f610SMax Laier if (server_iob.line_buffer[0] != '\0') 1210*13b9f610SMax Laier do_server_reply(&server_iob, &client_iob); 1211*13b9f610SMax Laier } else { 1212*13b9f610SMax Laier if (client_iob.alive) { 1213*13b9f610SMax Laier FD_SET(client_iob.fd, fdsp); 1214*13b9f610SMax Laier if (client_listen_socket >= 0) 1215*13b9f610SMax Laier FD_SET(client_listen_socket, fdsp); 1216*13b9f610SMax Laier if (client_data_socket >= 0) 1217*13b9f610SMax Laier FD_SET(client_data_socket, fdsp); 1218*13b9f610SMax Laier } 1219*13b9f610SMax Laier if (server_iob.alive) { 1220*13b9f610SMax Laier FD_SET(server_iob.fd, fdsp); 1221*13b9f610SMax Laier if (server_listen_socket >= 0) 1222*13b9f610SMax Laier FD_SET(server_listen_socket, fdsp); 1223*13b9f610SMax Laier if (server_data_socket >= 0) 1224*13b9f610SMax Laier FD_SET(server_data_socket, fdsp); 1225*13b9f610SMax Laier } 1226*13b9f610SMax Laier tv.tv_sec = timeout_seconds; 1227*13b9f610SMax Laier tv.tv_usec = 0; 1228*13b9f610SMax Laier 1229*13b9f610SMax Laier doselect: 1230*13b9f610SMax Laier sval = select(maxfd + 1, fdsp, NULL, NULL, 1231*13b9f610SMax Laier (tv.tv_sec == 0) ? NULL : &tv); 1232*13b9f610SMax Laier if (sval == 0) { 1233*13b9f610SMax Laier /* 1234*13b9f610SMax Laier * This proxy has timed out. Expire it 1235*13b9f610SMax Laier * quietly with an obituary in the syslogs 1236*13b9f610SMax Laier * for any passing mourners. 1237*13b9f610SMax Laier */ 1238*13b9f610SMax Laier syslog(LOG_INFO, 1239*13b9f610SMax Laier "timeout: no data for %ld seconds", 1240*13b9f610SMax Laier timeout_seconds); 1241*13b9f610SMax Laier exit(EX_OK); 1242*13b9f610SMax Laier } 1243*13b9f610SMax Laier if (sval == -1) { 1244*13b9f610SMax Laier if (errno == EINTR || errno == EAGAIN) 1245*13b9f610SMax Laier goto doselect; 1246*13b9f610SMax Laier syslog(LOG_NOTICE, 1247*13b9f610SMax Laier "select() failed (%m)"); 1248*13b9f610SMax Laier exit(EX_OSERR); 1249*13b9f610SMax Laier } 1250*13b9f610SMax Laier if (client_data_socket >= 0 && 1251*13b9f610SMax Laier FD_ISSET(client_data_socket, fdsp)) { 1252*13b9f610SMax Laier int rval; 1253*13b9f610SMax Laier 1254*13b9f610SMax Laier debuglog(3, "transfer: client to server"); 1255*13b9f610SMax Laier rval = xfer_data("client to server", 1256*13b9f610SMax Laier client_data_socket, 1257*13b9f610SMax Laier server_data_socket, 1258*13b9f610SMax Laier client_iob.sa.sin_addr, 1259*13b9f610SMax Laier real_server_sa.sin_addr); 1260*13b9f610SMax Laier if (rval <= 0) { 1261*13b9f610SMax Laier close_client_data(); 1262*13b9f610SMax Laier close_server_data(); 1263*13b9f610SMax Laier show_xfer_stats(); 1264*13b9f610SMax Laier } else 1265*13b9f610SMax Laier client_data_bytes += rval; 1266*13b9f610SMax Laier } 1267*13b9f610SMax Laier if (server_data_socket >= 0 && 1268*13b9f610SMax Laier FD_ISSET(server_data_socket, fdsp)) { 1269*13b9f610SMax Laier int rval; 1270*13b9f610SMax Laier 1271*13b9f610SMax Laier debuglog(3, "transfer: server to client"); 1272*13b9f610SMax Laier rval = xfer_data("server to client", 1273*13b9f610SMax Laier server_data_socket, 1274*13b9f610SMax Laier client_data_socket, 1275*13b9f610SMax Laier real_server_sa.sin_addr, 1276*13b9f610SMax Laier client_iob.sa.sin_addr); 1277*13b9f610SMax Laier if (rval <= 0) { 1278*13b9f610SMax Laier close_client_data(); 1279*13b9f610SMax Laier close_server_data(); 1280*13b9f610SMax Laier show_xfer_stats(); 1281*13b9f610SMax Laier } else 1282*13b9f610SMax Laier server_data_bytes += rval; 1283*13b9f610SMax Laier } 1284*13b9f610SMax Laier if (server_listen_socket >= 0 && 1285*13b9f610SMax Laier FD_ISSET(server_listen_socket, fdsp)) { 1286*13b9f610SMax Laier connect_port_backchannel(); 1287*13b9f610SMax Laier } 1288*13b9f610SMax Laier if (client_listen_socket >= 0 && 1289*13b9f610SMax Laier FD_ISSET(client_listen_socket, fdsp)) { 1290*13b9f610SMax Laier connect_pasv_backchannel(); 1291*13b9f610SMax Laier } 1292*13b9f610SMax Laier if (client_iob.alive && 1293*13b9f610SMax Laier FD_ISSET(client_iob.fd, fdsp)) { 1294*13b9f610SMax Laier client_iob.data_available = 1; 1295*13b9f610SMax Laier } 1296*13b9f610SMax Laier if (server_iob.alive && 1297*13b9f610SMax Laier FD_ISSET(server_iob.fd, fdsp)) { 1298*13b9f610SMax Laier server_iob.data_available = 1; 1299*13b9f610SMax Laier } 1300*13b9f610SMax Laier } 1301*13b9f610SMax Laier free(fdsp); 1302*13b9f610SMax Laier if (client_iob.got_eof) { 1303*13b9f610SMax Laier shutdown(server_iob.fd, 1); 1304*13b9f610SMax Laier shutdown(client_iob.fd, 0); 1305*13b9f610SMax Laier client_iob.got_eof = 0; 1306*13b9f610SMax Laier client_iob.alive = 0; 1307*13b9f610SMax Laier } 1308*13b9f610SMax Laier if (server_iob.got_eof) { 1309*13b9f610SMax Laier shutdown(client_iob.fd, 1); 1310*13b9f610SMax Laier shutdown(server_iob.fd, 0); 1311*13b9f610SMax Laier server_iob.got_eof = 0; 1312*13b9f610SMax Laier server_iob.alive = 0; 1313*13b9f610SMax Laier } 1314*13b9f610SMax Laier } 1315*13b9f610SMax Laier 1316*13b9f610SMax Laier if (Verbose) 1317*13b9f610SMax Laier syslog(LOG_INFO, "session ended"); 1318*13b9f610SMax Laier 1319*13b9f610SMax Laier exit(EX_OK); 1320*13b9f610SMax Laier } 1321