1145357Ssuz/* $KAME: ftp.c,v 1.24 2005/03/16 05:05:48 itojun Exp $ */ 262655Skris 356668Sshin/* 456668Sshin * Copyright (C) 1997 and 1998 WIDE Project. 556668Sshin * All rights reserved. 662655Skris * 756668Sshin * Redistribution and use in source and binary forms, with or without 856668Sshin * modification, are permitted provided that the following conditions 956668Sshin * are met: 1056668Sshin * 1. Redistributions of source code must retain the above copyright 1156668Sshin * notice, this list of conditions and the following disclaimer. 1256668Sshin * 2. Redistributions in binary form must reproduce the above copyright 1356668Sshin * notice, this list of conditions and the following disclaimer in the 1456668Sshin * documentation and/or other materials provided with the distribution. 1556668Sshin * 3. Neither the name of the project nor the names of its contributors 1656668Sshin * may be used to endorse or promote products derived from this software 1756668Sshin * without specific prior written permission. 1862655Skris * 1956668Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2056668Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2156668Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2256668Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2356668Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2456668Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2556668Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2656668Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2756668Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2856668Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2956668Sshin * SUCH DAMAGE. 3056668Sshin * 3156668Sshin * $FreeBSD$ 3256668Sshin */ 3356668Sshin 3456668Sshin#include <sys/param.h> 3556668Sshin#include <sys/types.h> 3656668Sshin#include <sys/socket.h> 3756668Sshin#include <sys/ioctl.h> 3856668Sshin#include <sys/time.h> 3956668Sshin 4056668Sshin#include <stdio.h> 4156668Sshin#include <stdlib.h> 4256668Sshin#include <string.h> 4356668Sshin#include <syslog.h> 4456668Sshin#include <unistd.h> 45122679Sume#ifdef HAVE_POLL_H 46122679Sume#include <poll.h> 47122679Sume#endif 4856668Sshin#include <errno.h> 4956668Sshin#include <ctype.h> 5056668Sshin 5156668Sshin#include <netinet/in.h> 5256668Sshin#include <arpa/inet.h> 5356668Sshin#include <netdb.h> 5456668Sshin 5556668Sshin#include "faithd.h" 5656668Sshin 5756668Sshinstatic char rbuf[MSS]; 5856668Sshinstatic char sbuf[MSS]; 5956668Sshinstatic int passivemode = 0; 6056668Sshinstatic int wport4 = -1; /* listen() to active */ 6156668Sshinstatic int wport6 = -1; /* listen() to passive */ 6256668Sshinstatic int port4 = -1; /* active: inbound passive: outbound */ 6356668Sshinstatic int port6 = -1; /* active: outbound passive: inbound */ 6456668Sshinstatic struct sockaddr_storage data4; /* server data address */ 6556668Sshinstatic struct sockaddr_storage data6; /* client data address */ 6656668Sshinstatic int epsvall = 0; 6756668Sshin 6856668Sshinenum state { NONE, LPRT, EPRT, LPSV, EPSV }; 6956668Sshin 70173412Skevlostatic int ftp_activeconn(void); 71173412Skevlostatic int ftp_passiveconn(void); 72173412Skevlostatic int ftp_copy(int, int); 73173412Skevlostatic int ftp_copyresult(int, int, enum state); 74173412Skevlostatic int ftp_copycommand(int, int, enum state *); 7556668Sshin 7656668Sshinvoid 7756668Sshinftp_relay(int ctl6, int ctl4) 7856668Sshin{ 79122679Sume#ifdef HAVE_POLL_H 80122679Sume struct pollfd pfd[6]; 81122679Sume#else 8256668Sshin fd_set readfds; 83122679Sume#endif 8456668Sshin int error; 8556668Sshin enum state state = NONE; 8656668Sshin struct timeval tv; 8756668Sshin 8856668Sshin syslog(LOG_INFO, "starting ftp control connection"); 8956668Sshin 9056668Sshin for (;;) { 91122679Sume#ifdef HAVE_POLL_H 92122679Sume pfd[0].fd = ctl4; 93122679Sume pfd[0].events = POLLIN; 94122679Sume pfd[1].fd = ctl6; 95122679Sume pfd[1].events = POLLIN; 96122679Sume if (0 <= port4) { 97122679Sume pfd[2].fd = port4; 98122679Sume pfd[2].events = POLLIN; 99122679Sume } else 100122679Sume pfd[2].fd = -1; 101122679Sume if (0 <= port6) { 102122679Sume pfd[3].fd = port6; 103122679Sume pfd[3].events = POLLIN; 104122679Sume } else 105122679Sume pfd[3].fd = -1; 106122679Sume#if 0 107122679Sume if (0 <= wport4) { 108122679Sume pfd[4].fd = wport4; 109122679Sume pfd[4].events = POLLIN; 110122679Sume } else 111122679Sume pfd[4].fd = -1; 112122679Sume if (0 <= wport6) { 113122679Sume pfd[5].fd = wport4; 114122679Sume pfd[5].events = POLLIN; 115122679Sume } else 116122679Sume pfd[5].fd = -1; 117122679Sume#else 118122679Sume pfd[4].fd = pfd[5].fd = -1; 119122679Sume pfd[4].events = pfd[5].events = 0; 120122679Sume#endif 121122679Sume#else 122122679Sume int maxfd = 0; 123122679Sume 12456668Sshin FD_ZERO(&readfds); 125122679Sume if (ctl4 >= FD_SETSIZE) 126122679Sume exit_failure("descriptor too big"); 12756668Sshin FD_SET(ctl4, &readfds); 128122679Sume maxfd = ctl4; 129122679Sume if (ctl6 >= FD_SETSIZE) 130122679Sume exit_failure("descriptor too big"); 13156668Sshin FD_SET(ctl6, &readfds); 132122679Sume maxfd = (ctl6 > maxfd) ? ctl6 : maxfd; 133122679Sume if (0 <= port4) { 134122679Sume if (port4 >= FD_SETSIZE) 135122679Sume exit_failure("descriptor too big"); 13656668Sshin FD_SET(port4, &readfds); 137122679Sume maxfd = (port4 > maxfd) ? port4 : maxfd; 138122679Sume } 139122679Sume if (0 <= port6) { 140122679Sume if (port6 >= FD_SETSIZE) 141122679Sume exit_failure("descriptor too big"); 14256668Sshin FD_SET(port6, &readfds); 143122679Sume maxfd = (port6 > maxfd) ? port6 : maxfd; 144122679Sume } 14556668Sshin#if 0 146122679Sume if (0 <= wport4) { 147122679Sume if (wport4 >= FD_SETSIZE) 148122679Sume exit_failure("descriptor too big"); 14956668Sshin FD_SET(wport4, &readfds); 150122679Sume maxfd = (wport4 > maxfd) ? wport4 : maxfd; 151122679Sume } 152122679Sume if (0 <= wport6) { 153122679Sume if (wport6 >= FD_SETSIZE) 154122679Sume exit_failure("descriptor too big"); 15556668Sshin FD_SET(wport6, &readfds); 156122679Sume maxfd = (wport6 > maxfd) ? wport6 : maxfd; 157122679Sume } 15856668Sshin#endif 159122679Sume#endif 16056668Sshin tv.tv_sec = FAITH_TIMEOUT; 16156668Sshin tv.tv_usec = 0; 16256668Sshin 163122679Sume#ifdef HAVE_POLL_H 164122679Sume error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000); 165122679Sume#else 166122679Sume error = select(maxfd + 1, &readfds, NULL, NULL, &tv); 167122679Sume#endif 168122679Sume if (error == -1) { 169122679Sume#ifdef HAVE_POLL_H 170122679Sume exit_failure("poll: %s", strerror(errno)); 171122679Sume#else 17295023Ssuz exit_failure("select: %s", strerror(errno)); 173122679Sume#endif 174122679Sume } 17556668Sshin else if (error == 0) 17656668Sshin exit_failure("connection timeout"); 17756668Sshin 17856668Sshin /* 17956668Sshin * The order of the following checks does (slightly) matter. 18056668Sshin * It is important to visit all checks (do not use "continue"), 18156668Sshin * otherwise some of the pipe may become full and we cannot 18256668Sshin * relay correctly. 18356668Sshin */ 184122679Sume#ifdef HAVE_POLL_H 185122679Sume if (pfd[1].revents & POLLIN) 186122679Sume#else 187122679Sume if (FD_ISSET(ctl6, &readfds)) 188122679Sume#endif 189122679Sume { 19056668Sshin /* 19156668Sshin * copy control connection from the client. 19256668Sshin * command translation is necessary. 19356668Sshin */ 19456668Sshin error = ftp_copycommand(ctl6, ctl4, &state); 19556668Sshin 19681981Sbrian if (error < 0) 19756668Sshin goto bad; 19881981Sbrian else if (error == 0) { 19956668Sshin close(ctl4); 20056668Sshin close(ctl6); 20156668Sshin exit_success("terminating ftp control connection"); 202122679Sume /*NOTREACHED*/ 20356668Sshin } 20456668Sshin } 205122679Sume#ifdef HAVE_POLL_H 206122679Sume if (pfd[0].revents & POLLIN) 207122679Sume#else 208122679Sume if (FD_ISSET(ctl4, &readfds)) 209122679Sume#endif 210122679Sume { 21156668Sshin /* 21256668Sshin * copy control connection from the server 21356668Sshin * translation of result code is necessary. 21456668Sshin */ 21556668Sshin error = ftp_copyresult(ctl4, ctl6, state); 21656668Sshin 21781981Sbrian if (error < 0) 21856668Sshin goto bad; 21981981Sbrian else if (error == 0) { 22056668Sshin close(ctl4); 22156668Sshin close(ctl6); 22256668Sshin exit_success("terminating ftp control connection"); 223122679Sume /*NOTREACHED*/ 22456668Sshin } 22556668Sshin } 226122679Sume#ifdef HAVE_POLL_H 227122679Sume if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN)) 228122679Sume#else 229122679Sume if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) 230122679Sume#endif 231122679Sume { 23256668Sshin /* 23356668Sshin * copy data connection. 23456668Sshin * no special treatment necessary. 23556668Sshin */ 236122679Sume#ifdef HAVE_POLL_H 237122679Sume if (pfd[2].revents & POLLIN) 238122679Sume#else 23956668Sshin if (FD_ISSET(port4, &readfds)) 240122679Sume#endif 24156668Sshin error = ftp_copy(port4, port6); 24256668Sshin switch (error) { 24356668Sshin case -1: 24456668Sshin goto bad; 24556668Sshin case 0: 24656668Sshin close(port4); 24756668Sshin close(port6); 24856668Sshin port4 = port6 = -1; 24956668Sshin syslog(LOG_INFO, "terminating data connection"); 25056668Sshin break; 25156668Sshin default: 25256668Sshin break; 25356668Sshin } 25456668Sshin } 255122679Sume#ifdef HAVE_POLL_H 256122679Sume if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN)) 257122679Sume#else 258122679Sume if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) 259122679Sume#endif 260122679Sume { 26156668Sshin /* 26256668Sshin * copy data connection. 26356668Sshin * no special treatment necessary. 26456668Sshin */ 265122679Sume#ifdef HAVE_POLL_H 266122679Sume if (pfd[3].revents & POLLIN) 267122679Sume#else 26856668Sshin if (FD_ISSET(port6, &readfds)) 269122679Sume#endif 27056668Sshin error = ftp_copy(port6, port4); 27156668Sshin switch (error) { 27256668Sshin case -1: 27356668Sshin goto bad; 27456668Sshin case 0: 27556668Sshin close(port4); 27656668Sshin close(port6); 27756668Sshin port4 = port6 = -1; 27856668Sshin syslog(LOG_INFO, "terminating data connection"); 27956668Sshin break; 28056668Sshin default: 28156668Sshin break; 28256668Sshin } 28356668Sshin } 28456668Sshin#if 0 285122679Sume#ifdef HAVE_POLL_H 286122679Sume if (wport4 && (pfd[4].revents & POLLIN)) 287122679Sume#else 288122679Sume if (wport4 && FD_ISSET(wport4, &readfds)) 289122679Sume#endif 290122679Sume { 29156668Sshin /* 29256668Sshin * establish active data connection from the server. 29356668Sshin */ 29456668Sshin ftp_activeconn(); 29556668Sshin } 296122679Sume#ifdef HAVE_POLL_H 297122679Sume if (wport4 && (pfd[5].revents & POLLIN)) 298122679Sume#else 299122679Sume if (wport6 && FD_ISSET(wport6, &readfds)) 300122679Sume#endif 301122679Sume { 30256668Sshin /* 30356668Sshin * establish passive data connection from the client. 30456668Sshin */ 30556668Sshin ftp_passiveconn(); 30656668Sshin } 30756668Sshin#endif 30856668Sshin } 30956668Sshin 31056668Sshin bad: 31195023Ssuz exit_failure("%s", strerror(errno)); 31256668Sshin} 31356668Sshin 31456668Sshinstatic int 31556668Sshinftp_activeconn() 31656668Sshin{ 317122679Sume socklen_t n; 31856668Sshin int error; 319122679Sume#ifdef HAVE_POLL_H 320122679Sume struct pollfd pfd[1]; 321122679Sume#else 32256668Sshin fd_set set; 323122679Sume#endif 32456668Sshin struct timeval timeout; 32556668Sshin struct sockaddr *sa; 32656668Sshin 32756668Sshin /* get active connection from server */ 328122679Sume#ifdef HAVE_POLL_H 329122679Sume pfd[0].fd = wport4; 330122679Sume pfd[0].events = POLLIN; 331122679Sume#else 33256668Sshin FD_ZERO(&set); 333122679Sume if (wport4 >= FD_SETSIZE) 334122679Sume exit_failure("descriptor too big"); 33556668Sshin FD_SET(wport4, &set); 336122679Sume#endif 33756668Sshin timeout.tv_sec = 120; 338122679Sume timeout.tv_usec = 0; 33956668Sshin n = sizeof(data4); 340122679Sume#ifdef HAVE_POLL_H 341122679Sume if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 || 342122679Sume (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) 343122679Sume#else 344122679Sume if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 || 345122679Sume (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) 346122679Sume#endif 347122679Sume { 34856668Sshin close(wport4); 34956668Sshin wport4 = -1; 35056668Sshin syslog(LOG_INFO, "active mode data connection failed"); 35156668Sshin return -1; 35256668Sshin } 35356668Sshin 35456668Sshin /* ask active connection to client */ 35556668Sshin sa = (struct sockaddr *)&data6; 35656668Sshin port6 = socket(sa->sa_family, SOCK_STREAM, 0); 35756668Sshin if (port6 == -1) { 35856668Sshin close(port4); 35956668Sshin close(wport4); 36056668Sshin port4 = wport4 = -1; 36156668Sshin syslog(LOG_INFO, "active mode data connection failed"); 36256668Sshin return -1; 36356668Sshin } 36456668Sshin error = connect(port6, sa, sa->sa_len); 36595355Sume if (error < 0) { 36656668Sshin close(port6); 36756668Sshin close(port4); 36856668Sshin close(wport4); 36956668Sshin port6 = port4 = wport4 = -1; 37056668Sshin syslog(LOG_INFO, "active mode data connection failed"); 37156668Sshin return -1; 37256668Sshin } 37356668Sshin 37456668Sshin syslog(LOG_INFO, "active mode data connection established"); 37556668Sshin return 0; 37656668Sshin} 37756668Sshin 37856668Sshinstatic int 37956668Sshinftp_passiveconn() 38056668Sshin{ 381122679Sume socklen_t len; 38256668Sshin int error; 383122679Sume#ifdef HAVE_POLL_H 384122679Sume struct pollfd pfd[1]; 385122679Sume#else 38656668Sshin fd_set set; 387122679Sume#endif 38856668Sshin struct timeval timeout; 38956668Sshin struct sockaddr *sa; 39056668Sshin 39156668Sshin /* get passive connection from client */ 392122679Sume#ifdef HAVE_POLL_H 393122679Sume pfd[0].fd = wport6; 394122679Sume pfd[0].events = POLLIN; 395122679Sume#else 39656668Sshin FD_ZERO(&set); 397122679Sume if (wport6 >= FD_SETSIZE) 398122679Sume exit_failure("descriptor too big"); 39956668Sshin FD_SET(wport6, &set); 400122679Sume#endif 40156668Sshin timeout.tv_sec = 120; 40256668Sshin timeout.tv_usec = 0; 403122679Sume len = sizeof(data6); 404122679Sume#ifdef HAVE_POLL_H 405122679Sume if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 || 406122679Sume (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0) 407122679Sume#else 408122679Sume if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 || 409122679Sume (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0) 410122679Sume#endif 411122679Sume { 41256668Sshin close(wport6); 41356668Sshin wport6 = -1; 41456668Sshin syslog(LOG_INFO, "passive mode data connection failed"); 41556668Sshin return -1; 41656668Sshin } 41756668Sshin 41856668Sshin /* ask passive connection to server */ 41956668Sshin sa = (struct sockaddr *)&data4; 42056668Sshin port4 = socket(sa->sa_family, SOCK_STREAM, 0); 42156668Sshin if (port4 == -1) { 42256668Sshin close(wport6); 42356668Sshin close(port6); 42456668Sshin wport6 = port6 = -1; 42556668Sshin syslog(LOG_INFO, "passive mode data connection failed"); 42656668Sshin return -1; 42756668Sshin } 42856668Sshin error = connect(port4, sa, sa->sa_len); 42995355Sume if (error < 0) { 43056668Sshin close(wport6); 43156668Sshin close(port4); 43256668Sshin close(port6); 43356668Sshin wport6 = port4 = port6 = -1; 43456668Sshin syslog(LOG_INFO, "passive mode data connection failed"); 43556668Sshin return -1; 43656668Sshin } 43756668Sshin 43856668Sshin syslog(LOG_INFO, "passive mode data connection established"); 43956668Sshin return 0; 44056668Sshin} 44156668Sshin 44256668Sshinstatic int 44356668Sshinftp_copy(int src, int dst) 44456668Sshin{ 445122679Sume int error, atmark, n; 44656668Sshin 44756668Sshin /* OOB data handling */ 44856668Sshin error = ioctl(src, SIOCATMARK, &atmark); 44956668Sshin if (error != -1 && atmark == 1) { 45056668Sshin n = read(src, rbuf, 1); 45156668Sshin if (n == -1) 45256668Sshin goto bad; 45356668Sshin send(dst, rbuf, n, MSG_OOB); 45456668Sshin#if 0 45556668Sshin n = read(src, rbuf, sizeof(rbuf)); 45656668Sshin if (n == -1) 45756668Sshin goto bad; 45856668Sshin write(dst, rbuf, n); 45956668Sshin return n; 46056668Sshin#endif 46156668Sshin } 46256668Sshin 46356668Sshin n = read(src, rbuf, sizeof(rbuf)); 46456668Sshin switch (n) { 46556668Sshin case -1: 46656668Sshin case 0: 46756668Sshin return n; 46856668Sshin default: 46956668Sshin write(dst, rbuf, n); 47056668Sshin return n; 47156668Sshin } 47256668Sshin 47356668Sshin bad: 47495023Ssuz exit_failure("%s", strerror(errno)); 47556668Sshin /*NOTREACHED*/ 47656668Sshin return 0; /* to make gcc happy */ 47756668Sshin} 47856668Sshin 47956668Sshinstatic int 48056668Sshinftp_copyresult(int src, int dst, enum state state) 48156668Sshin{ 482122679Sume int error, atmark, n; 483122679Sume socklen_t len; 48456668Sshin char *param; 48556668Sshin int code; 486122679Sume char *a, *p; 487122679Sume int i; 48856668Sshin 48956668Sshin /* OOB data handling */ 49056668Sshin error = ioctl(src, SIOCATMARK, &atmark); 49156668Sshin if (error != -1 && atmark == 1) { 49256668Sshin n = read(src, rbuf, 1); 49356668Sshin if (n == -1) 49456668Sshin goto bad; 49556668Sshin send(dst, rbuf, n, MSG_OOB); 49656668Sshin#if 0 49756668Sshin n = read(src, rbuf, sizeof(rbuf)); 49856668Sshin if (n == -1) 49956668Sshin goto bad; 50056668Sshin write(dst, rbuf, n); 50156668Sshin return n; 50256668Sshin#endif 50356668Sshin } 50456668Sshin 50556668Sshin n = read(src, rbuf, sizeof(rbuf)); 50656668Sshin if (n <= 0) 50756668Sshin return n; 50856668Sshin rbuf[n] = '\0'; 50956668Sshin 51056668Sshin /* 51156668Sshin * parse argument 51256668Sshin */ 51356668Sshin p = rbuf; 51456668Sshin for (i = 0; i < 3; i++) { 51556668Sshin if (!isdigit(*p)) { 51656668Sshin /* invalid reply */ 51756668Sshin write(dst, rbuf, n); 51856668Sshin return n; 51956668Sshin } 52056668Sshin p++; 52156668Sshin } 52256668Sshin if (!isspace(*p)) { 52356668Sshin /* invalid reply */ 52456668Sshin write(dst, rbuf, n); 52556668Sshin return n; 52656668Sshin } 52756668Sshin code = atoi(rbuf); 52856668Sshin param = p; 52956668Sshin /* param points to first non-command token, if any */ 53056668Sshin while (*param && isspace(*param)) 53156668Sshin param++; 53256668Sshin if (!*param) 53356668Sshin param = NULL; 53456668Sshin 53556668Sshin switch (state) { 53656668Sshin case NONE: 53756668Sshin if (!passivemode && rbuf[0] == '1') { 53856668Sshin if (ftp_activeconn() < 0) { 53956668Sshin n = snprintf(rbuf, sizeof(rbuf), 54062655Skris "425 Cannot open data connetion\r\n"); 541122679Sume if (n < 0 || n >= sizeof(rbuf)) 542122679Sume n = 0; 54356668Sshin } 54456668Sshin } 545122679Sume if (n) 546122679Sume write(dst, rbuf, n); 547122679Sume return n; 54856668Sshin case LPRT: 54956668Sshin case EPRT: 55056668Sshin /* expecting "200 PORT command successful." */ 55156668Sshin if (code == 200) { 55256668Sshin p = strstr(rbuf, "PORT"); 55356668Sshin if (p) { 55456668Sshin p[0] = (state == LPRT) ? 'L' : 'E'; 55556668Sshin p[1] = 'P'; 55656668Sshin } 55756668Sshin } else { 55856668Sshin close(wport4); 55956668Sshin wport4 = -1; 56056668Sshin } 56156668Sshin write(dst, rbuf, n); 56256668Sshin return n; 56356668Sshin case LPSV: 56456668Sshin case EPSV: 56565994Sume /* 56665994Sume * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" 56765994Sume * (in some cases result comes without paren) 56865994Sume */ 56956668Sshin if (code != 227) { 57056668Sshinpassivefail0: 57156668Sshin close(wport6); 57256668Sshin wport6 = -1; 57356668Sshin write(dst, rbuf, n); 57456668Sshin return n; 57556668Sshin } 57656668Sshin 57756668Sshin { 57856668Sshin unsigned int ho[4], po[2]; 57956668Sshin struct sockaddr_in *sin; 58056668Sshin struct sockaddr_in6 *sin6; 58156668Sshin u_short port; 58256668Sshin 58356668Sshin /* 58456668Sshin * PASV result -> LPSV/EPSV result 58556668Sshin */ 58656668Sshin p = param; 58765994Sume while (*p && *p != '(' && !isdigit(*p)) /*)*/ 58856668Sshin p++; 58956668Sshin if (!*p) 59056668Sshin goto passivefail0; /*XXX*/ 59165994Sume if (*p == '(') /*)*/ 59265994Sume p++; 59356668Sshin n = sscanf(p, "%u,%u,%u,%u,%u,%u", 59456668Sshin &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); 59556668Sshin if (n != 6) 59656668Sshin goto passivefail0; /*XXX*/ 59756668Sshin 59856668Sshin /* keep PORT parameter */ 59956668Sshin memset(&data4, 0, sizeof(data4)); 60056668Sshin sin = (struct sockaddr_in *)&data4; 60156668Sshin sin->sin_len = sizeof(*sin); 60256668Sshin sin->sin_family = AF_INET; 60356668Sshin sin->sin_addr.s_addr = 0; 60456668Sshin for (n = 0; n < 4; n++) { 60556668Sshin sin->sin_addr.s_addr |= 60656668Sshin htonl((ho[n] & 0xff) << ((3 - n) * 8)); 60756668Sshin } 60856668Sshin sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); 60956668Sshin 61056668Sshin /* get ready for passive data connection */ 61156668Sshin memset(&data6, 0, sizeof(data6)); 61256668Sshin sin6 = (struct sockaddr_in6 *)&data6; 61356668Sshin sin6->sin6_len = sizeof(*sin6); 61456668Sshin sin6->sin6_family = AF_INET6; 61556668Sshin wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0); 61656668Sshin if (wport6 == -1) { 61756668Sshinpassivefail: 61856668Sshin n = snprintf(sbuf, sizeof(sbuf), 61956668Sshin "500 could not translate from PASV\r\n"); 620122679Sume if (n < 0 || n >= sizeof(sbuf)) 621122679Sume n = 0; 622122679Sume if (n) 623122679Sume write(src, sbuf, n); 624122679Sume return n; 62556668Sshin } 62656668Sshin#ifdef IPV6_FAITH 62756668Sshin { 62856668Sshin int on = 1; 62956668Sshin error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH, 63056668Sshin &on, sizeof(on)); 63156668Sshin if (error == -1) 63295023Ssuz exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno)); 63356668Sshin } 63456668Sshin#endif 63556668Sshin error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len); 63656668Sshin if (error == -1) { 63756668Sshin close(wport6); 63856668Sshin wport6 = -1; 63956668Sshin goto passivefail; 64056668Sshin } 64156668Sshin error = listen(wport6, 1); 64256668Sshin if (error == -1) { 64356668Sshin close(wport6); 64456668Sshin wport6 = -1; 64556668Sshin goto passivefail; 64656668Sshin } 64756668Sshin 64856668Sshin /* transmit LPSV or EPSV */ 64956668Sshin /* 65056668Sshin * addr from dst, port from wport6 65156668Sshin */ 652122679Sume len = sizeof(data6); 653122679Sume error = getsockname(wport6, (struct sockaddr *)&data6, &len); 65456668Sshin if (error == -1) { 65556668Sshin close(wport6); 65656668Sshin wport6 = -1; 65756668Sshin goto passivefail; 65856668Sshin } 65956668Sshin sin6 = (struct sockaddr_in6 *)&data6; 66056668Sshin port = sin6->sin6_port; 66156668Sshin 662122679Sume len = sizeof(data6); 663122679Sume error = getsockname(dst, (struct sockaddr *)&data6, &len); 66456668Sshin if (error == -1) { 66556668Sshin close(wport6); 66656668Sshin wport6 = -1; 66756668Sshin goto passivefail; 66856668Sshin } 66956668Sshin sin6 = (struct sockaddr_in6 *)&data6; 67056668Sshin sin6->sin6_port = port; 67156668Sshin 67256668Sshin if (state == LPSV) { 67356668Sshin a = (char *)&sin6->sin6_addr; 67456668Sshin p = (char *)&sin6->sin6_port; 67556668Sshin n = snprintf(sbuf, sizeof(sbuf), 67656668Sshin"228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n", 67762655Skris 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 67862655Skris UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 67962655Skris UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 68062655Skris UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 68156668Sshin 2, UC(p[0]), UC(p[1])); 682122679Sume if (n < 0 || n >= sizeof(sbuf)) 683122679Sume n = 0; 684122679Sume if (n) 685122679Sume write(dst, sbuf, n); 68656668Sshin passivemode = 1; 68756668Sshin return n; 68856668Sshin } else { 68956668Sshin n = snprintf(sbuf, sizeof(sbuf), 69056668Sshin"229 Entering Extended Passive Mode (|||%d|)\r\n", 69156668Sshin ntohs(sin6->sin6_port)); 692122679Sume if (n < 0 || n >= sizeof(sbuf)) 693122679Sume n = 0; 694122679Sume if (n) 695122679Sume write(dst, sbuf, n); 69656668Sshin passivemode = 1; 69756668Sshin return n; 69856668Sshin } 69956668Sshin } 70056668Sshin } 70156668Sshin 70256668Sshin bad: 70395023Ssuz exit_failure("%s", strerror(errno)); 70456668Sshin /*NOTREACHED*/ 70556668Sshin return 0; /* to make gcc happy */ 70656668Sshin} 70756668Sshin 70856668Sshinstatic int 70956668Sshinftp_copycommand(int src, int dst, enum state *state) 71056668Sshin{ 711122679Sume int error, atmark, n; 712122679Sume socklen_t len; 71356668Sshin unsigned int af, hal, ho[16], pal, po[2]; 714122679Sume char *a, *p, *q; 71556668Sshin char cmd[5], *param; 71656668Sshin struct sockaddr_in *sin; 71756668Sshin struct sockaddr_in6 *sin6; 71856668Sshin enum state nstate; 71956668Sshin char ch; 720122679Sume int i; 72156668Sshin 72256668Sshin /* OOB data handling */ 72356668Sshin error = ioctl(src, SIOCATMARK, &atmark); 72456668Sshin if (error != -1 && atmark == 1) { 72556668Sshin n = read(src, rbuf, 1); 72656668Sshin if (n == -1) 72756668Sshin goto bad; 72856668Sshin send(dst, rbuf, n, MSG_OOB); 72956668Sshin#if 0 73056668Sshin n = read(src, rbuf, sizeof(rbuf)); 73156668Sshin if (n == -1) 73256668Sshin goto bad; 73356668Sshin write(dst, rbuf, n); 73456668Sshin return n; 73556668Sshin#endif 73656668Sshin } 73756668Sshin 73856668Sshin n = read(src, rbuf, sizeof(rbuf)); 73956668Sshin if (n <= 0) 74056668Sshin return n; 74156668Sshin rbuf[n] = '\0'; 74256668Sshin 74356668Sshin if (n < 4) { 74456668Sshin write(dst, rbuf, n); 74556668Sshin return n; 74656668Sshin } 74756668Sshin 74856668Sshin /* 74956668Sshin * parse argument 75056668Sshin */ 75156668Sshin p = rbuf; 75256668Sshin q = cmd; 75356668Sshin for (i = 0; i < 4; i++) { 75456668Sshin if (!isalpha(*p)) { 75556668Sshin /* invalid command */ 75656668Sshin write(dst, rbuf, n); 75756668Sshin return n; 75856668Sshin } 75956668Sshin *q++ = islower(*p) ? toupper(*p) : *p; 76056668Sshin p++; 76156668Sshin } 76256668Sshin if (!isspace(*p)) { 76356668Sshin /* invalid command */ 76456668Sshin write(dst, rbuf, n); 76556668Sshin return n; 76656668Sshin } 76756668Sshin *q = '\0'; 76856668Sshin param = p; 76956668Sshin /* param points to first non-command token, if any */ 77056668Sshin while (*param && isspace(*param)) 77156668Sshin param++; 77256668Sshin if (!*param) 77356668Sshin param = NULL; 77456668Sshin 77556668Sshin *state = NONE; 77656668Sshin 77756668Sshin if (strcmp(cmd, "LPRT") == 0 && param) { 77856668Sshin /* 77956668Sshin * LPRT -> PORT 78056668Sshin */ 78156668Sshin nstate = LPRT; 78256668Sshin 78356668Sshin close(wport4); 78456668Sshin close(wport6); 78556668Sshin close(port4); 78656668Sshin close(port6); 78756668Sshin wport4 = wport6 = port4 = port6 = -1; 78856668Sshin 78956668Sshin if (epsvall) { 79056668Sshin n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 79156668Sshin cmd); 792122679Sume if (n < 0 || n >= sizeof(sbuf)) 793122679Sume n = 0; 794122679Sume if (n) 795122679Sume write(src, sbuf, n); 796122679Sume return n; 79756668Sshin } 79856668Sshin 79956668Sshin n = sscanf(param, 80056668Sshin"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", 80156668Sshin &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3], 80256668Sshin &ho[4], &ho[5], &ho[6], &ho[7], 80356668Sshin &ho[8], &ho[9], &ho[10], &ho[11], 80456668Sshin &ho[12], &ho[13], &ho[14], &ho[15], 80556668Sshin &pal, &po[0], &po[1]); 80656668Sshin if (n != 21 || af != 6 || hal != 16|| pal != 2) { 80756668Sshin n = snprintf(sbuf, sizeof(sbuf), 80856668Sshin "501 illegal parameter to LPRT\r\n"); 809122679Sume if (n < 0 || n >= sizeof(sbuf)) 810122679Sume n = 0; 811122679Sume if (n) 812122679Sume write(src, sbuf, n); 813122679Sume return n; 81456668Sshin } 81556668Sshin 81656668Sshin /* keep LPRT parameter */ 81756668Sshin memset(&data6, 0, sizeof(data6)); 81856668Sshin sin6 = (struct sockaddr_in6 *)&data6; 81956668Sshin sin6->sin6_len = sizeof(*sin6); 82056668Sshin sin6->sin6_family = AF_INET6; 82156668Sshin for (n = 0; n < 16; n++) 82256668Sshin sin6->sin6_addr.s6_addr[n] = ho[n]; 82356668Sshin sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); 82456668Sshin 82556668Sshinsendport: 82656668Sshin /* get ready for active data connection */ 827122679Sume len = sizeof(data4); 828122679Sume error = getsockname(dst, (struct sockaddr *)&data4, &len); 82956668Sshin if (error == -1) { 83056668Sshinlprtfail: 83156668Sshin n = snprintf(sbuf, sizeof(sbuf), 83256668Sshin "500 could not translate to PORT\r\n"); 833122679Sume if (n < 0 || n >= sizeof(sbuf)) 834122679Sume n = 0; 835122679Sume if (n) 836122679Sume write(src, sbuf, n); 837122679Sume return n; 83856668Sshin } 83956668Sshin if (((struct sockaddr *)&data4)->sa_family != AF_INET) 84056668Sshin goto lprtfail; 84156668Sshin sin = (struct sockaddr_in *)&data4; 84256668Sshin sin->sin_port = 0; 84356668Sshin wport4 = socket(sin->sin_family, SOCK_STREAM, 0); 84456668Sshin if (wport4 == -1) 84556668Sshin goto lprtfail; 84656668Sshin error = bind(wport4, (struct sockaddr *)sin, sin->sin_len); 84756668Sshin if (error == -1) { 84856668Sshin close(wport4); 84956668Sshin wport4 = -1; 85056668Sshin goto lprtfail; 85156668Sshin } 85256668Sshin error = listen(wport4, 1); 85356668Sshin if (error == -1) { 85456668Sshin close(wport4); 85556668Sshin wport4 = -1; 85656668Sshin goto lprtfail; 85756668Sshin } 85856668Sshin 85956668Sshin /* transmit PORT */ 860122679Sume len = sizeof(data4); 861122679Sume error = getsockname(wport4, (struct sockaddr *)&data4, &len); 86256668Sshin if (error == -1) { 86356668Sshin close(wport4); 86456668Sshin wport4 = -1; 86556668Sshin goto lprtfail; 86656668Sshin } 86756668Sshin if (((struct sockaddr *)&data4)->sa_family != AF_INET) { 86856668Sshin close(wport4); 86956668Sshin wport4 = -1; 87056668Sshin goto lprtfail; 87156668Sshin } 87256668Sshin sin = (struct sockaddr_in *)&data4; 87356668Sshin a = (char *)&sin->sin_addr; 87456668Sshin p = (char *)&sin->sin_port; 87556668Sshin n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n", 87656668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 87756668Sshin UC(p[0]), UC(p[1])); 878122679Sume if (n < 0 || n >= sizeof(sbuf)) 879122679Sume n = 0; 880122679Sume if (n) 881122679Sume write(dst, sbuf, n); 88256668Sshin *state = nstate; 88356668Sshin passivemode = 0; 88456668Sshin return n; 88556668Sshin } else if (strcmp(cmd, "EPRT") == 0 && param) { 88656668Sshin /* 88756668Sshin * EPRT -> PORT 88856668Sshin */ 88956668Sshin char *afp, *hostp, *portp; 89056668Sshin struct addrinfo hints, *res; 89156668Sshin 89256668Sshin nstate = EPRT; 89356668Sshin 89456668Sshin close(wport4); 89556668Sshin close(wport6); 89656668Sshin close(port4); 89756668Sshin close(port6); 89856668Sshin wport4 = wport6 = port4 = port6 = -1; 89956668Sshin 90056668Sshin if (epsvall) { 90156668Sshin n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 90256668Sshin cmd); 903122679Sume if (n < 0 || n >= sizeof(sbuf)) 904122679Sume n = 0; 905122679Sume if (n) 906122679Sume write(src, sbuf, n); 907122679Sume return n; 90856668Sshin } 90956668Sshin 91056668Sshin p = param; 91156668Sshin ch = *p++; /* boundary character */ 91256668Sshin afp = p; 91356668Sshin while (*p && *p != ch) 91456668Sshin p++; 91556668Sshin if (!*p) { 91656668Sshineprtparamfail: 91756668Sshin n = snprintf(sbuf, sizeof(sbuf), 91856668Sshin "501 illegal parameter to EPRT\r\n"); 919122679Sume if (n < 0 || n >= sizeof(sbuf)) 920122679Sume n = 0; 921122679Sume if (n) 922122679Sume write(src, sbuf, n); 923122679Sume return n; 92456668Sshin } 92556668Sshin *p++ = '\0'; 92656668Sshin hostp = p; 92756668Sshin while (*p && *p != ch) 92856668Sshin p++; 92956668Sshin if (!*p) 93056668Sshin goto eprtparamfail; 93156668Sshin *p++ = '\0'; 93256668Sshin portp = p; 93356668Sshin while (*p && *p != ch) 93456668Sshin p++; 93556668Sshin if (!*p) 93656668Sshin goto eprtparamfail; 93756668Sshin *p++ = '\0'; 93856668Sshin 93956668Sshin n = sscanf(afp, "%d", &af); 94056668Sshin if (n != 1 || af != 2) { 94156668Sshin n = snprintf(sbuf, sizeof(sbuf), 94256668Sshin "501 unsupported address family to EPRT\r\n"); 943122679Sume if (n < 0 || n >= sizeof(sbuf)) 944122679Sume n = 0; 945122679Sume if (n) 946122679Sume write(src, sbuf, n); 947122679Sume return n; 94856668Sshin } 94956668Sshin memset(&hints, 0, sizeof(hints)); 95056668Sshin hints.ai_family = AF_UNSPEC; 95178064Sume hints.ai_socktype = SOCK_STREAM; 952122679Sume hints.ai_protocol = IPPROTO_TCP; 95356668Sshin error = getaddrinfo(hostp, portp, &hints, &res); 95456668Sshin if (error) { 95556668Sshin n = snprintf(sbuf, sizeof(sbuf), 95662655Skris "501 EPRT: %s\r\n", gai_strerror(error)); 957122679Sume if (n < 0 || n >= sizeof(sbuf)) 958122679Sume n = 0; 959122679Sume if (n) 960122679Sume write(src, sbuf, n); 961122679Sume return n; 96256668Sshin } 96356668Sshin if (res->ai_next) { 96456668Sshin n = snprintf(sbuf, sizeof(sbuf), 96556668Sshin "501 EPRT: %s resolved to multiple addresses\r\n", hostp); 966122679Sume if (n < 0 || n >= sizeof(sbuf)) 967122679Sume n = 0; 968122679Sume if (n) 969122679Sume write(src, sbuf, n); 970145357Ssuz freeaddrinfo(res); 971122679Sume return n; 97256668Sshin } 97356668Sshin 97456668Sshin memcpy(&data6, res->ai_addr, res->ai_addrlen); 97556668Sshin 976145791Ssuz freeaddrinfo(res); 97756668Sshin goto sendport; 97856668Sshin } else if (strcmp(cmd, "LPSV") == 0 && !param) { 97956668Sshin /* 98056668Sshin * LPSV -> PASV 98156668Sshin */ 98256668Sshin nstate = LPSV; 98356668Sshin 98456668Sshin close(wport4); 98556668Sshin close(wport6); 98656668Sshin close(port4); 98756668Sshin close(port6); 98856668Sshin wport4 = wport6 = port4 = port6 = -1; 98956668Sshin 99056668Sshin if (epsvall) { 99156668Sshin n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 99256668Sshin cmd); 993122679Sume if (n < 0 || n >= sizeof(sbuf)) 994122679Sume n = 0; 995122679Sume if (n) 996122679Sume write(src, sbuf, n); 997122679Sume return n; 99856668Sshin } 99956668Sshin 100056668Sshin /* transmit PASV */ 100156668Sshin n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); 1002122679Sume if (n < 0 || n >= sizeof(sbuf)) 1003122679Sume n = 0; 1004122679Sume if (n) 1005122679Sume write(dst, sbuf, n); 100656668Sshin *state = LPSV; 100756668Sshin passivemode = 0; /* to be set to 1 later */ 100856668Sshin return n; 100956668Sshin } else if (strcmp(cmd, "EPSV") == 0 && !param) { 101056668Sshin /* 101156668Sshin * EPSV -> PASV 101256668Sshin */ 101356668Sshin close(wport4); 101456668Sshin close(wport6); 101556668Sshin close(port4); 101656668Sshin close(port6); 101756668Sshin wport4 = wport6 = port4 = port6 = -1; 101856668Sshin 101956668Sshin n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); 1020122679Sume if (n < 0 || n >= sizeof(sbuf)) 1021122679Sume n = 0; 1022122679Sume if (n) 1023122679Sume write(dst, sbuf, n); 102456668Sshin *state = EPSV; 102556668Sshin passivemode = 0; /* to be set to 1 later */ 102656668Sshin return n; 102756668Sshin } else if (strcmp(cmd, "EPSV") == 0 && param 102856668Sshin && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) { 102956668Sshin /* 103056668Sshin * EPSV ALL 103156668Sshin */ 103256668Sshin epsvall = 1; 103356668Sshin n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n"); 1034122679Sume if (n < 0 || n >= sizeof(sbuf)) 1035122679Sume n = 0; 1036122679Sume if (n) 1037122679Sume write(src, sbuf, n); 103856668Sshin return n; 103956668Sshin } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) { 104056668Sshin /* 104156668Sshin * reject PORT/PASV 104256668Sshin */ 104356668Sshin n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd); 1044122679Sume if (n < 0 || n >= sizeof(sbuf)) 1045122679Sume n = 0; 1046122679Sume if (n) 1047122679Sume write(src, sbuf, n); 1048122679Sume return n; 104956668Sshin } else if (passivemode 105056668Sshin && (strcmp(cmd, "STOR") == 0 105156668Sshin || strcmp(cmd, "STOU") == 0 105256668Sshin || strcmp(cmd, "RETR") == 0 105356668Sshin || strcmp(cmd, "LIST") == 0 105456668Sshin || strcmp(cmd, "NLST") == 0 105556668Sshin || strcmp(cmd, "APPE") == 0)) { 105656668Sshin /* 105756668Sshin * commands with data transfer. need to care about passive 105856668Sshin * mode data connection. 105956668Sshin */ 106056668Sshin 106156668Sshin if (ftp_passiveconn() < 0) { 106256668Sshin n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n"); 1063122679Sume if (n < 0 || n >= sizeof(sbuf)) 1064122679Sume n = 0; 1065122679Sume if (n) 1066122679Sume write(src, sbuf, n); 106756668Sshin } else { 106856668Sshin /* simply relay the command */ 106956668Sshin write(dst, rbuf, n); 107056668Sshin } 107156668Sshin 107256668Sshin *state = NONE; 107356668Sshin return n; 107456668Sshin } else { 107556668Sshin /* simply relay it */ 107656668Sshin *state = NONE; 1077122679Sume write(dst, rbuf, n); 1078122679Sume return n; 107956668Sshin } 108056668Sshin 108156668Sshin bad: 108295023Ssuz exit_failure("%s", strerror(errno)); 108356668Sshin /*NOTREACHED*/ 108456668Sshin return 0; /* to make gcc happy */ 108556668Sshin} 1086