ftp.c revision 145357
1238384Sjkim/* $KAME: ftp.c,v 1.24 2005/03/16 05:05:48 itojun Exp $ */ 2238384Sjkim 3238384Sjkim/* 4238384Sjkim * Copyright (C) 1997 and 1998 WIDE Project. 5238384Sjkim * All rights reserved. 6238384Sjkim * 7238384Sjkim * Redistribution and use in source and binary forms, with or without 8238384Sjkim * modification, are permitted provided that the following conditions 9238384Sjkim * are met: 10238384Sjkim * 1. Redistributions of source code must retain the above copyright 11238384Sjkim * notice, this list of conditions and the following disclaimer. 12238384Sjkim * 2. Redistributions in binary form must reproduce the above copyright 13238384Sjkim * notice, this list of conditions and the following disclaimer in the 14238384Sjkim * documentation and/or other materials provided with the distribution. 15238384Sjkim * 3. Neither the name of the project nor the names of its contributors 16238384Sjkim * may be used to endorse or promote products derived from this software 17238384Sjkim * without specific prior written permission. 18238384Sjkim * 19238384Sjkim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20238384Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21238384Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22238384Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23238384Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24238384Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25238384Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26238384Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27238384Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28238384Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29238384Sjkim * SUCH DAMAGE. 30238384Sjkim * 31246772Sjkim * $FreeBSD: head/usr.sbin/faithd/ftp.c 145357 2005-04-21 13:21:48Z suz $ 32246772Sjkim */ 33238384Sjkim 34238384Sjkim#include <sys/param.h> 35238384Sjkim#include <sys/types.h> 36238384Sjkim#include <sys/socket.h> 37238384Sjkim#include <sys/ioctl.h> 38238384Sjkim#include <sys/time.h> 39238384Sjkim 40238384Sjkim#include <stdio.h> 41238384Sjkim#include <stdlib.h> 42238384Sjkim#include <string.h> 43238384Sjkim#include <syslog.h> 44238384Sjkim#include <unistd.h> 45238384Sjkim#ifdef HAVE_POLL_H 46238384Sjkim#include <poll.h> 47238384Sjkim#endif 48238384Sjkim#include <errno.h> 49238384Sjkim#include <ctype.h> 50238384Sjkim 51238384Sjkim#include <netinet/in.h> 52238384Sjkim#include <arpa/inet.h> 53238384Sjkim#include <netdb.h> 54238384Sjkim 55238384Sjkim#include "faithd.h" 56238384Sjkim 57238384Sjkimstatic char rbuf[MSS]; 58238384Sjkimstatic char sbuf[MSS]; 59238384Sjkimstatic int passivemode = 0; 60238384Sjkimstatic int wport4 = -1; /* listen() to active */ 61238384Sjkimstatic int wport6 = -1; /* listen() to passive */ 62238384Sjkimstatic int port4 = -1; /* active: inbound passive: outbound */ 63238384Sjkimstatic int port6 = -1; /* active: outbound passive: inbound */ 64238384Sjkimstatic struct sockaddr_storage data4; /* server data address */ 65238384Sjkimstatic struct sockaddr_storage data6; /* client data address */ 66238384Sjkimstatic int epsvall = 0; 67238384Sjkim 68238384Sjkimenum state { NONE, LPRT, EPRT, LPSV, EPSV }; 69238384Sjkim 70238384Sjkimstatic int ftp_activeconn __P((void)); 71238384Sjkimstatic int ftp_passiveconn __P((void)); 72238384Sjkimstatic int ftp_copy __P((int, int)); 73238384Sjkimstatic int ftp_copyresult __P((int, int, enum state)); 74238384Sjkimstatic int ftp_copycommand __P((int, int, enum state *)); 75238384Sjkim 76238384Sjkimvoid 77238384Sjkimftp_relay(int ctl6, int ctl4) 78238384Sjkim{ 79238384Sjkim#ifdef HAVE_POLL_H 80238384Sjkim struct pollfd pfd[6]; 81238384Sjkim#else 82238384Sjkim fd_set readfds; 83238384Sjkim#endif 84238384Sjkim int error; 85238384Sjkim enum state state = NONE; 86238384Sjkim struct timeval tv; 87238384Sjkim 88238384Sjkim syslog(LOG_INFO, "starting ftp control connection"); 89238384Sjkim 90238384Sjkim for (;;) { 91238384Sjkim#ifdef HAVE_POLL_H 92238384Sjkim pfd[0].fd = ctl4; 93238384Sjkim pfd[0].events = POLLIN; 94238384Sjkim pfd[1].fd = ctl6; 95238384Sjkim pfd[1].events = POLLIN; 96238384Sjkim if (0 <= port4) { 97238384Sjkim pfd[2].fd = port4; 98238384Sjkim pfd[2].events = POLLIN; 99238384Sjkim } else 100238384Sjkim pfd[2].fd = -1; 101238384Sjkim if (0 <= port6) { 102238384Sjkim pfd[3].fd = port6; 103238384Sjkim pfd[3].events = POLLIN; 104238384Sjkim } else 105238384Sjkim pfd[3].fd = -1; 106238384Sjkim#if 0 107238384Sjkim if (0 <= wport4) { 108238384Sjkim pfd[4].fd = wport4; 109238384Sjkim pfd[4].events = POLLIN; 110238384Sjkim } else 111238384Sjkim pfd[4].fd = -1; 112238384Sjkim if (0 <= wport6) { 113238384Sjkim pfd[5].fd = wport4; 114238384Sjkim pfd[5].events = POLLIN; 115238384Sjkim } else 116238384Sjkim pfd[5].fd = -1; 117238384Sjkim#else 118238384Sjkim pfd[4].fd = pfd[5].fd = -1; 119238384Sjkim pfd[4].events = pfd[5].events = 0; 120238384Sjkim#endif 121238384Sjkim#else 122238384Sjkim int maxfd = 0; 123238384Sjkim 124238384Sjkim FD_ZERO(&readfds); 125238384Sjkim if (ctl4 >= FD_SETSIZE) 126238384Sjkim exit_failure("descriptor too big"); 127238384Sjkim FD_SET(ctl4, &readfds); 128238384Sjkim maxfd = ctl4; 129238384Sjkim if (ctl6 >= FD_SETSIZE) 130238384Sjkim exit_failure("descriptor too big"); 131238384Sjkim FD_SET(ctl6, &readfds); 132238384Sjkim maxfd = (ctl6 > maxfd) ? ctl6 : maxfd; 133238384Sjkim if (0 <= port4) { 134238384Sjkim if (port4 >= FD_SETSIZE) 135238384Sjkim exit_failure("descriptor too big"); 136238384Sjkim FD_SET(port4, &readfds); 137238384Sjkim maxfd = (port4 > maxfd) ? port4 : maxfd; 138238384Sjkim } 139238384Sjkim if (0 <= port6) { 140238384Sjkim if (port6 >= FD_SETSIZE) 141238384Sjkim exit_failure("descriptor too big"); 142238384Sjkim FD_SET(port6, &readfds); 143238384Sjkim maxfd = (port6 > maxfd) ? port6 : maxfd; 144238384Sjkim } 145238384Sjkim#if 0 146238384Sjkim if (0 <= wport4) { 147238384Sjkim if (wport4 >= FD_SETSIZE) 148238384Sjkim exit_failure("descriptor too big"); 149238384Sjkim FD_SET(wport4, &readfds); 150238384Sjkim maxfd = (wport4 > maxfd) ? wport4 : maxfd; 151238384Sjkim } 152238384Sjkim if (0 <= wport6) { 153238384Sjkim if (wport6 >= FD_SETSIZE) 154238384Sjkim exit_failure("descriptor too big"); 155238384Sjkim FD_SET(wport6, &readfds); 156238384Sjkim maxfd = (wport6 > maxfd) ? wport6 : maxfd; 157238384Sjkim } 158238384Sjkim#endif 159238384Sjkim#endif 160238384Sjkim tv.tv_sec = FAITH_TIMEOUT; 161238384Sjkim tv.tv_usec = 0; 162238384Sjkim 163238384Sjkim#ifdef HAVE_POLL_H 164238384Sjkim error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000); 165238384Sjkim#else 166238384Sjkim error = select(maxfd + 1, &readfds, NULL, NULL, &tv); 167238384Sjkim#endif 168238384Sjkim if (error == -1) { 169238384Sjkim#ifdef HAVE_POLL_H 170238384Sjkim exit_failure("poll: %s", strerror(errno)); 171238384Sjkim#else 172238384Sjkim exit_failure("select: %s", strerror(errno)); 173238384Sjkim#endif 174238384Sjkim } 175238384Sjkim else if (error == 0) 176238384Sjkim exit_failure("connection timeout"); 177238384Sjkim 178238384Sjkim /* 179238384Sjkim * The order of the following checks does (slightly) matter. 180238384Sjkim * It is important to visit all checks (do not use "continue"), 181238384Sjkim * otherwise some of the pipe may become full and we cannot 182238384Sjkim * relay correctly. 183238384Sjkim */ 184238384Sjkim#ifdef HAVE_POLL_H 185238384Sjkim if (pfd[1].revents & POLLIN) 186238384Sjkim#else 187238384Sjkim if (FD_ISSET(ctl6, &readfds)) 188238384Sjkim#endif 189238384Sjkim { 190238384Sjkim /* 191238384Sjkim * copy control connection from the client. 192238384Sjkim * command translation is necessary. 193238384Sjkim */ 194238384Sjkim error = ftp_copycommand(ctl6, ctl4, &state); 195238384Sjkim 196238384Sjkim if (error < 0) 197238384Sjkim goto bad; 198238384Sjkim else if (error == 0) { 199238384Sjkim close(ctl4); 200238384Sjkim close(ctl6); 201238384Sjkim exit_success("terminating ftp control connection"); 202238384Sjkim /*NOTREACHED*/ 203238384Sjkim } 204238384Sjkim } 205238384Sjkim#ifdef HAVE_POLL_H 206238384Sjkim if (pfd[0].revents & POLLIN) 207238384Sjkim#else 208238384Sjkim if (FD_ISSET(ctl4, &readfds)) 209238384Sjkim#endif 210238384Sjkim { 211238384Sjkim /* 212238384Sjkim * copy control connection from the server 213238384Sjkim * translation of result code is necessary. 214238384Sjkim */ 215238384Sjkim error = ftp_copyresult(ctl4, ctl6, state); 216238384Sjkim 217238384Sjkim if (error < 0) 218238384Sjkim goto bad; 219238384Sjkim else if (error == 0) { 220238384Sjkim close(ctl4); 221238384Sjkim close(ctl6); 222238384Sjkim exit_success("terminating ftp control connection"); 223238384Sjkim /*NOTREACHED*/ 224238384Sjkim } 225238384Sjkim } 226238384Sjkim#ifdef HAVE_POLL_H 227238384Sjkim if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN)) 228238384Sjkim#else 229238384Sjkim if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) 230238384Sjkim#endif 231238384Sjkim { 232238384Sjkim /* 233238384Sjkim * copy data connection. 234238384Sjkim * no special treatment necessary. 235238384Sjkim */ 236238384Sjkim#ifdef HAVE_POLL_H 237238384Sjkim if (pfd[2].revents & POLLIN) 238238384Sjkim#else 239238384Sjkim if (FD_ISSET(port4, &readfds)) 240238384Sjkim#endif 241238384Sjkim error = ftp_copy(port4, port6); 242238384Sjkim switch (error) { 243238384Sjkim case -1: 244238384Sjkim goto bad; 245238384Sjkim case 0: 246238384Sjkim close(port4); 247238384Sjkim close(port6); 248238384Sjkim port4 = port6 = -1; 249238384Sjkim syslog(LOG_INFO, "terminating data connection"); 250238384Sjkim break; 251238384Sjkim default: 252238384Sjkim break; 253238384Sjkim } 254238384Sjkim } 255238384Sjkim#ifdef HAVE_POLL_H 256238384Sjkim if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN)) 257238384Sjkim#else 258238384Sjkim if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) 259238384Sjkim#endif 260238384Sjkim { 261238384Sjkim /* 262238384Sjkim * copy data connection. 263238384Sjkim * no special treatment necessary. 264238384Sjkim */ 265238384Sjkim#ifdef HAVE_POLL_H 266238384Sjkim if (pfd[3].revents & POLLIN) 267238384Sjkim#else 268238384Sjkim if (FD_ISSET(port6, &readfds)) 269238384Sjkim#endif 270238384Sjkim error = ftp_copy(port6, port4); 271238384Sjkim switch (error) { 272238384Sjkim case -1: 273238384Sjkim goto bad; 274238384Sjkim case 0: 275238384Sjkim close(port4); 276238384Sjkim close(port6); 277238384Sjkim port4 = port6 = -1; 278238384Sjkim syslog(LOG_INFO, "terminating data connection"); 279238384Sjkim break; 280238384Sjkim default: 281238384Sjkim break; 282238384Sjkim } 283238384Sjkim } 284238384Sjkim#if 0 285238384Sjkim#ifdef HAVE_POLL_H 286238384Sjkim if (wport4 && (pfd[4].revents & POLLIN)) 287238384Sjkim#else 288238384Sjkim if (wport4 && FD_ISSET(wport4, &readfds)) 289238384Sjkim#endif 290238384Sjkim { 291238384Sjkim /* 292238384Sjkim * establish active data connection from the server. 293238384Sjkim */ 294238384Sjkim ftp_activeconn(); 295238384Sjkim } 296238384Sjkim#ifdef HAVE_POLL_H 297238384Sjkim if (wport4 && (pfd[5].revents & POLLIN)) 298238384Sjkim#else 299238384Sjkim if (wport6 && FD_ISSET(wport6, &readfds)) 300238384Sjkim#endif 301238384Sjkim { 302238384Sjkim /* 303238384Sjkim * establish passive data connection from the client. 304238384Sjkim */ 305238384Sjkim ftp_passiveconn(); 306238384Sjkim } 307238384Sjkim#endif 308238384Sjkim } 309238384Sjkim 310238384Sjkim bad: 311238384Sjkim exit_failure("%s", strerror(errno)); 312238384Sjkim} 313238384Sjkim 314238384Sjkimstatic int 315238384Sjkimftp_activeconn() 316238384Sjkim{ 317238384Sjkim socklen_t n; 318238384Sjkim int error; 319238384Sjkim#ifdef HAVE_POLL_H 320238384Sjkim struct pollfd pfd[1]; 321238384Sjkim#else 322238384Sjkim fd_set set; 323238384Sjkim#endif 324238384Sjkim struct timeval timeout; 325238384Sjkim struct sockaddr *sa; 326238384Sjkim 327238384Sjkim /* get active connection from server */ 328238384Sjkim#ifdef HAVE_POLL_H 329238384Sjkim pfd[0].fd = wport4; 330238384Sjkim pfd[0].events = POLLIN; 331238384Sjkim#else 332238384Sjkim FD_ZERO(&set); 333238384Sjkim if (wport4 >= FD_SETSIZE) 334238384Sjkim exit_failure("descriptor too big"); 335238384Sjkim FD_SET(wport4, &set); 336238384Sjkim#endif 337238384Sjkim timeout.tv_sec = 120; 338238384Sjkim timeout.tv_usec = 0; 339238384Sjkim n = sizeof(data4); 340238384Sjkim#ifdef HAVE_POLL_H 341238384Sjkim if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 || 342238384Sjkim (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) 343238384Sjkim#else 344238384Sjkim if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 || 345238384Sjkim (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) 346238384Sjkim#endif 347238384Sjkim { 348238384Sjkim close(wport4); 349238384Sjkim wport4 = -1; 350238384Sjkim syslog(LOG_INFO, "active mode data connection failed"); 351238384Sjkim return -1; 352238384Sjkim } 353238384Sjkim 354238384Sjkim /* ask active connection to client */ 355238384Sjkim sa = (struct sockaddr *)&data6; 356238384Sjkim port6 = socket(sa->sa_family, SOCK_STREAM, 0); 357238384Sjkim if (port6 == -1) { 358238384Sjkim close(port4); 359238384Sjkim close(wport4); 360238384Sjkim port4 = wport4 = -1; 361238384Sjkim syslog(LOG_INFO, "active mode data connection failed"); 362238384Sjkim return -1; 363238384Sjkim } 364238384Sjkim error = connect(port6, sa, sa->sa_len); 365238384Sjkim if (error < 0) { 366238384Sjkim close(port6); 367238384Sjkim close(port4); 368238384Sjkim close(wport4); 369238384Sjkim port6 = port4 = wport4 = -1; 370238384Sjkim syslog(LOG_INFO, "active mode data connection failed"); 371238384Sjkim return -1; 372238384Sjkim } 373238384Sjkim 374238384Sjkim syslog(LOG_INFO, "active mode data connection established"); 375238384Sjkim return 0; 376238384Sjkim} 377238384Sjkim 378238384Sjkimstatic int 379238384Sjkimftp_passiveconn() 380238384Sjkim{ 381238384Sjkim socklen_t len; 382238384Sjkim int error; 383238384Sjkim#ifdef HAVE_POLL_H 384238384Sjkim struct pollfd pfd[1]; 385238384Sjkim#else 386238384Sjkim fd_set set; 387238384Sjkim#endif 388238384Sjkim struct timeval timeout; 389238384Sjkim struct sockaddr *sa; 390238384Sjkim 391238384Sjkim /* get passive connection from client */ 392238384Sjkim#ifdef HAVE_POLL_H 393238384Sjkim pfd[0].fd = wport6; 394238384Sjkim pfd[0].events = POLLIN; 395238384Sjkim#else 396238384Sjkim FD_ZERO(&set); 397238384Sjkim if (wport6 >= FD_SETSIZE) 398238384Sjkim exit_failure("descriptor too big"); 399238384Sjkim FD_SET(wport6, &set); 400238384Sjkim#endif 401238384Sjkim timeout.tv_sec = 120; 402238384Sjkim timeout.tv_usec = 0; 403238384Sjkim len = sizeof(data6); 404238384Sjkim#ifdef HAVE_POLL_H 405238384Sjkim if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 || 406238384Sjkim (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0) 407238384Sjkim#else 408238384Sjkim if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 || 409238384Sjkim (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0) 410238384Sjkim#endif 411238384Sjkim { 412238384Sjkim close(wport6); 413238384Sjkim wport6 = -1; 414238384Sjkim syslog(LOG_INFO, "passive mode data connection failed"); 415238384Sjkim return -1; 416238384Sjkim } 417238384Sjkim 418238384Sjkim /* ask passive connection to server */ 419238384Sjkim sa = (struct sockaddr *)&data4; 420238384Sjkim port4 = socket(sa->sa_family, SOCK_STREAM, 0); 421238384Sjkim if (port4 == -1) { 422238384Sjkim close(wport6); 423238384Sjkim close(port6); 424238384Sjkim wport6 = port6 = -1; 425238384Sjkim syslog(LOG_INFO, "passive mode data connection failed"); 426238384Sjkim return -1; 427238384Sjkim } 428238384Sjkim error = connect(port4, sa, sa->sa_len); 429238384Sjkim if (error < 0) { 430238384Sjkim close(wport6); 431238384Sjkim close(port4); 432238384Sjkim close(port6); 433238384Sjkim wport6 = port4 = port6 = -1; 434238384Sjkim syslog(LOG_INFO, "passive mode data connection failed"); 435238384Sjkim return -1; 436238384Sjkim } 437238384Sjkim 438238384Sjkim syslog(LOG_INFO, "passive mode data connection established"); 439238384Sjkim return 0; 440238384Sjkim} 441238384Sjkim 442238384Sjkimstatic int 443238384Sjkimftp_copy(int src, int dst) 444238384Sjkim{ 445238384Sjkim int error, atmark, n; 446238384Sjkim 447238384Sjkim /* OOB data handling */ 448238384Sjkim error = ioctl(src, SIOCATMARK, &atmark); 449238384Sjkim if (error != -1 && atmark == 1) { 450238384Sjkim n = read(src, rbuf, 1); 451238384Sjkim if (n == -1) 452238384Sjkim goto bad; 453238384Sjkim send(dst, rbuf, n, MSG_OOB); 454238384Sjkim#if 0 455238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 456238384Sjkim if (n == -1) 457238384Sjkim goto bad; 458238384Sjkim write(dst, rbuf, n); 459238384Sjkim return n; 460238384Sjkim#endif 461238384Sjkim } 462238384Sjkim 463238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 464238384Sjkim switch (n) { 465238384Sjkim case -1: 466238384Sjkim case 0: 467238384Sjkim return n; 468238384Sjkim default: 469238384Sjkim write(dst, rbuf, n); 470238384Sjkim return n; 471238384Sjkim } 472238384Sjkim 473238384Sjkim bad: 474238384Sjkim exit_failure("%s", strerror(errno)); 475238384Sjkim /*NOTREACHED*/ 476238384Sjkim return 0; /* to make gcc happy */ 477238384Sjkim} 478238384Sjkim 479238384Sjkimstatic int 480238384Sjkimftp_copyresult(int src, int dst, enum state state) 481238384Sjkim{ 482238384Sjkim int error, atmark, n; 483238384Sjkim socklen_t len; 484238384Sjkim char *param; 485238384Sjkim int code; 486238384Sjkim char *a, *p; 487238384Sjkim int i; 488238384Sjkim 489238384Sjkim /* OOB data handling */ 490238384Sjkim error = ioctl(src, SIOCATMARK, &atmark); 491238384Sjkim if (error != -1 && atmark == 1) { 492238384Sjkim n = read(src, rbuf, 1); 493238384Sjkim if (n == -1) 494238384Sjkim goto bad; 495238384Sjkim send(dst, rbuf, n, MSG_OOB); 496238384Sjkim#if 0 497238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 498238384Sjkim if (n == -1) 499238384Sjkim goto bad; 500238384Sjkim write(dst, rbuf, n); 501238384Sjkim return n; 502238384Sjkim#endif 503238384Sjkim } 504238384Sjkim 505238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 506238384Sjkim if (n <= 0) 507238384Sjkim return n; 508238384Sjkim rbuf[n] = '\0'; 509238384Sjkim 510238384Sjkim /* 511238384Sjkim * parse argument 512238384Sjkim */ 513238384Sjkim p = rbuf; 514238384Sjkim for (i = 0; i < 3; i++) { 515238384Sjkim if (!isdigit(*p)) { 516238384Sjkim /* invalid reply */ 517238384Sjkim write(dst, rbuf, n); 518238384Sjkim return n; 519238384Sjkim } 520238384Sjkim p++; 521238384Sjkim } 522238384Sjkim if (!isspace(*p)) { 523238384Sjkim /* invalid reply */ 524238384Sjkim write(dst, rbuf, n); 525238384Sjkim return n; 526238384Sjkim } 527238384Sjkim code = atoi(rbuf); 528238384Sjkim param = p; 529238384Sjkim /* param points to first non-command token, if any */ 530238384Sjkim while (*param && isspace(*param)) 531238384Sjkim param++; 532238384Sjkim if (!*param) 533238384Sjkim param = NULL; 534238384Sjkim 535238384Sjkim switch (state) { 536238384Sjkim case NONE: 537238384Sjkim if (!passivemode && rbuf[0] == '1') { 538238384Sjkim if (ftp_activeconn() < 0) { 539238384Sjkim n = snprintf(rbuf, sizeof(rbuf), 540238384Sjkim "425 Cannot open data connetion\r\n"); 541238384Sjkim if (n < 0 || n >= sizeof(rbuf)) 542238384Sjkim n = 0; 543238384Sjkim } 544238384Sjkim } 545238384Sjkim if (n) 546238384Sjkim write(dst, rbuf, n); 547238384Sjkim return n; 548238384Sjkim case LPRT: 549238384Sjkim case EPRT: 550238384Sjkim /* expecting "200 PORT command successful." */ 551238384Sjkim if (code == 200) { 552238384Sjkim p = strstr(rbuf, "PORT"); 553238384Sjkim if (p) { 554238384Sjkim p[0] = (state == LPRT) ? 'L' : 'E'; 555238384Sjkim p[1] = 'P'; 556238384Sjkim } 557238384Sjkim } else { 558238384Sjkim close(wport4); 559238384Sjkim wport4 = -1; 560238384Sjkim } 561238384Sjkim write(dst, rbuf, n); 562238384Sjkim return n; 563238384Sjkim case LPSV: 564238384Sjkim case EPSV: 565238384Sjkim /* 566238384Sjkim * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" 567238384Sjkim * (in some cases result comes without paren) 568238384Sjkim */ 569238384Sjkim if (code != 227) { 570238384Sjkimpassivefail0: 571238384Sjkim close(wport6); 572238384Sjkim wport6 = -1; 573238384Sjkim write(dst, rbuf, n); 574238384Sjkim return n; 575238384Sjkim } 576238384Sjkim 577238384Sjkim { 578238384Sjkim unsigned int ho[4], po[2]; 579238384Sjkim struct sockaddr_in *sin; 580238384Sjkim struct sockaddr_in6 *sin6; 581238384Sjkim u_short port; 582238384Sjkim 583238384Sjkim /* 584238384Sjkim * PASV result -> LPSV/EPSV result 585238384Sjkim */ 586238384Sjkim p = param; 587238384Sjkim while (*p && *p != '(' && !isdigit(*p)) /*)*/ 588238384Sjkim p++; 589238384Sjkim if (!*p) 590238384Sjkim goto passivefail0; /*XXX*/ 591238384Sjkim if (*p == '(') /*)*/ 592238384Sjkim p++; 593238384Sjkim n = sscanf(p, "%u,%u,%u,%u,%u,%u", 594238384Sjkim &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); 595238384Sjkim if (n != 6) 596238384Sjkim goto passivefail0; /*XXX*/ 597238384Sjkim 598238384Sjkim /* keep PORT parameter */ 599238384Sjkim memset(&data4, 0, sizeof(data4)); 600238384Sjkim sin = (struct sockaddr_in *)&data4; 601238384Sjkim sin->sin_len = sizeof(*sin); 602238384Sjkim sin->sin_family = AF_INET; 603238384Sjkim sin->sin_addr.s_addr = 0; 604238384Sjkim for (n = 0; n < 4; n++) { 605238384Sjkim sin->sin_addr.s_addr |= 606238384Sjkim htonl((ho[n] & 0xff) << ((3 - n) * 8)); 607238384Sjkim } 608238384Sjkim sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); 609238384Sjkim 610238384Sjkim /* get ready for passive data connection */ 611238384Sjkim memset(&data6, 0, sizeof(data6)); 612238384Sjkim sin6 = (struct sockaddr_in6 *)&data6; 613238384Sjkim sin6->sin6_len = sizeof(*sin6); 614238384Sjkim sin6->sin6_family = AF_INET6; 615238384Sjkim wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0); 616238384Sjkim if (wport6 == -1) { 617238384Sjkimpassivefail: 618238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 619238384Sjkim "500 could not translate from PASV\r\n"); 620238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 621238384Sjkim n = 0; 622238384Sjkim if (n) 623238384Sjkim write(src, sbuf, n); 624238384Sjkim return n; 625238384Sjkim } 626238384Sjkim#ifdef IPV6_FAITH 627238384Sjkim { 628238384Sjkim int on = 1; 629238384Sjkim error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH, 630238384Sjkim &on, sizeof(on)); 631238384Sjkim if (error == -1) 632238384Sjkim exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno)); 633238384Sjkim } 634238384Sjkim#endif 635238384Sjkim error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len); 636238384Sjkim if (error == -1) { 637238384Sjkim close(wport6); 638238384Sjkim wport6 = -1; 639238384Sjkim goto passivefail; 640238384Sjkim } 641238384Sjkim error = listen(wport6, 1); 642238384Sjkim if (error == -1) { 643238384Sjkim close(wport6); 644238384Sjkim wport6 = -1; 645238384Sjkim goto passivefail; 646238384Sjkim } 647238384Sjkim 648238384Sjkim /* transmit LPSV or EPSV */ 649238384Sjkim /* 650238384Sjkim * addr from dst, port from wport6 651238384Sjkim */ 652238384Sjkim len = sizeof(data6); 653238384Sjkim error = getsockname(wport6, (struct sockaddr *)&data6, &len); 654238384Sjkim if (error == -1) { 655238384Sjkim close(wport6); 656238384Sjkim wport6 = -1; 657238384Sjkim goto passivefail; 658238384Sjkim } 659238384Sjkim sin6 = (struct sockaddr_in6 *)&data6; 660238384Sjkim port = sin6->sin6_port; 661238384Sjkim 662238384Sjkim len = sizeof(data6); 663238384Sjkim error = getsockname(dst, (struct sockaddr *)&data6, &len); 664238384Sjkim if (error == -1) { 665238384Sjkim close(wport6); 666238384Sjkim wport6 = -1; 667238384Sjkim goto passivefail; 668238384Sjkim } 669238384Sjkim sin6 = (struct sockaddr_in6 *)&data6; 670238384Sjkim sin6->sin6_port = port; 671238384Sjkim 672238384Sjkim if (state == LPSV) { 673238384Sjkim a = (char *)&sin6->sin6_addr; 674238384Sjkim p = (char *)&sin6->sin6_port; 675238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 676238384Sjkim"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", 677238384Sjkim 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 678238384Sjkim UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 679238384Sjkim UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 680238384Sjkim UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 681238384Sjkim 2, UC(p[0]), UC(p[1])); 682238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 683238384Sjkim n = 0; 684238384Sjkim if (n) 685238384Sjkim write(dst, sbuf, n); 686238384Sjkim passivemode = 1; 687238384Sjkim return n; 688238384Sjkim } else { 689238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 690238384Sjkim"229 Entering Extended Passive Mode (|||%d|)\r\n", 691238384Sjkim ntohs(sin6->sin6_port)); 692238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 693238384Sjkim n = 0; 694238384Sjkim if (n) 695238384Sjkim write(dst, sbuf, n); 696238384Sjkim passivemode = 1; 697238384Sjkim return n; 698238384Sjkim } 699238384Sjkim } 700238384Sjkim } 701238384Sjkim 702238384Sjkim bad: 703238384Sjkim exit_failure("%s", strerror(errno)); 704238384Sjkim /*NOTREACHED*/ 705238384Sjkim return 0; /* to make gcc happy */ 706238384Sjkim} 707238384Sjkim 708238384Sjkimstatic int 709238384Sjkimftp_copycommand(int src, int dst, enum state *state) 710238384Sjkim{ 711238384Sjkim int error, atmark, n; 712238384Sjkim socklen_t len; 713238384Sjkim unsigned int af, hal, ho[16], pal, po[2]; 714238384Sjkim char *a, *p, *q; 715238384Sjkim char cmd[5], *param; 716238384Sjkim struct sockaddr_in *sin; 717238384Sjkim struct sockaddr_in6 *sin6; 718238384Sjkim enum state nstate; 719238384Sjkim char ch; 720238384Sjkim int i; 721238384Sjkim 722238384Sjkim /* OOB data handling */ 723238384Sjkim error = ioctl(src, SIOCATMARK, &atmark); 724238384Sjkim if (error != -1 && atmark == 1) { 725238384Sjkim n = read(src, rbuf, 1); 726238384Sjkim if (n == -1) 727238384Sjkim goto bad; 728238384Sjkim send(dst, rbuf, n, MSG_OOB); 729238384Sjkim#if 0 730238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 731238384Sjkim if (n == -1) 732238384Sjkim goto bad; 733238384Sjkim write(dst, rbuf, n); 734238384Sjkim return n; 735238384Sjkim#endif 736238384Sjkim } 737238384Sjkim 738238384Sjkim n = read(src, rbuf, sizeof(rbuf)); 739238384Sjkim if (n <= 0) 740238384Sjkim return n; 741238384Sjkim rbuf[n] = '\0'; 742238384Sjkim 743238384Sjkim if (n < 4) { 744238384Sjkim write(dst, rbuf, n); 745238384Sjkim return n; 746238384Sjkim } 747238384Sjkim 748238384Sjkim /* 749238384Sjkim * parse argument 750238384Sjkim */ 751238384Sjkim p = rbuf; 752238384Sjkim q = cmd; 753238384Sjkim for (i = 0; i < 4; i++) { 754238384Sjkim if (!isalpha(*p)) { 755238384Sjkim /* invalid command */ 756238384Sjkim write(dst, rbuf, n); 757238384Sjkim return n; 758238384Sjkim } 759238384Sjkim *q++ = islower(*p) ? toupper(*p) : *p; 760238384Sjkim p++; 761238384Sjkim } 762238384Sjkim if (!isspace(*p)) { 763238384Sjkim /* invalid command */ 764238384Sjkim write(dst, rbuf, n); 765238384Sjkim return n; 766238384Sjkim } 767238384Sjkim *q = '\0'; 768238384Sjkim param = p; 769238384Sjkim /* param points to first non-command token, if any */ 770238384Sjkim while (*param && isspace(*param)) 771238384Sjkim param++; 772238384Sjkim if (!*param) 773238384Sjkim param = NULL; 774238384Sjkim 775238384Sjkim *state = NONE; 776238384Sjkim 777238384Sjkim if (strcmp(cmd, "LPRT") == 0 && param) { 778238384Sjkim /* 779238384Sjkim * LPRT -> PORT 780238384Sjkim */ 781238384Sjkim nstate = LPRT; 782238384Sjkim 783238384Sjkim close(wport4); 784238384Sjkim close(wport6); 785238384Sjkim close(port4); 786238384Sjkim close(port6); 787238384Sjkim wport4 = wport6 = port4 = port6 = -1; 788238384Sjkim 789238384Sjkim if (epsvall) { 790238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 791238384Sjkim cmd); 792238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 793238384Sjkim n = 0; 794238384Sjkim if (n) 795238384Sjkim write(src, sbuf, n); 796238384Sjkim return n; 797238384Sjkim } 798238384Sjkim 799238384Sjkim n = sscanf(param, 800238384Sjkim"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", 801238384Sjkim &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3], 802238384Sjkim &ho[4], &ho[5], &ho[6], &ho[7], 803238384Sjkim &ho[8], &ho[9], &ho[10], &ho[11], 804238384Sjkim &ho[12], &ho[13], &ho[14], &ho[15], 805238384Sjkim &pal, &po[0], &po[1]); 806238384Sjkim if (n != 21 || af != 6 || hal != 16|| pal != 2) { 807238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 808238384Sjkim "501 illegal parameter to LPRT\r\n"); 809238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 810238384Sjkim n = 0; 811238384Sjkim if (n) 812238384Sjkim write(src, sbuf, n); 813238384Sjkim return n; 814238384Sjkim } 815238384Sjkim 816238384Sjkim /* keep LPRT parameter */ 817238384Sjkim memset(&data6, 0, sizeof(data6)); 818238384Sjkim sin6 = (struct sockaddr_in6 *)&data6; 819238384Sjkim sin6->sin6_len = sizeof(*sin6); 820238384Sjkim sin6->sin6_family = AF_INET6; 821238384Sjkim for (n = 0; n < 16; n++) 822238384Sjkim sin6->sin6_addr.s6_addr[n] = ho[n]; 823238384Sjkim sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); 824238384Sjkim 825238384Sjkimsendport: 826238384Sjkim /* get ready for active data connection */ 827238384Sjkim len = sizeof(data4); 828238384Sjkim error = getsockname(dst, (struct sockaddr *)&data4, &len); 829238384Sjkim if (error == -1) { 830238384Sjkimlprtfail: 831238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 832238384Sjkim "500 could not translate to PORT\r\n"); 833238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 834238384Sjkim n = 0; 835238384Sjkim if (n) 836238384Sjkim write(src, sbuf, n); 837238384Sjkim return n; 838238384Sjkim } 839238384Sjkim if (((struct sockaddr *)&data4)->sa_family != AF_INET) 840238384Sjkim goto lprtfail; 841238384Sjkim sin = (struct sockaddr_in *)&data4; 842238384Sjkim sin->sin_port = 0; 843238384Sjkim wport4 = socket(sin->sin_family, SOCK_STREAM, 0); 844238384Sjkim if (wport4 == -1) 845238384Sjkim goto lprtfail; 846238384Sjkim error = bind(wport4, (struct sockaddr *)sin, sin->sin_len); 847238384Sjkim if (error == -1) { 848238384Sjkim close(wport4); 849238384Sjkim wport4 = -1; 850238384Sjkim goto lprtfail; 851238384Sjkim } 852238384Sjkim error = listen(wport4, 1); 853238384Sjkim if (error == -1) { 854238384Sjkim close(wport4); 855238384Sjkim wport4 = -1; 856238384Sjkim goto lprtfail; 857238384Sjkim } 858238384Sjkim 859238384Sjkim /* transmit PORT */ 860238384Sjkim len = sizeof(data4); 861238384Sjkim error = getsockname(wport4, (struct sockaddr *)&data4, &len); 862238384Sjkim if (error == -1) { 863238384Sjkim close(wport4); 864238384Sjkim wport4 = -1; 865238384Sjkim goto lprtfail; 866238384Sjkim } 867238384Sjkim if (((struct sockaddr *)&data4)->sa_family != AF_INET) { 868238384Sjkim close(wport4); 869238384Sjkim wport4 = -1; 870238384Sjkim goto lprtfail; 871238384Sjkim } 872238384Sjkim sin = (struct sockaddr_in *)&data4; 873238384Sjkim a = (char *)&sin->sin_addr; 874238384Sjkim p = (char *)&sin->sin_port; 875238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n", 876238384Sjkim UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 877238384Sjkim UC(p[0]), UC(p[1])); 878238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 879238384Sjkim n = 0; 880238384Sjkim if (n) 881238384Sjkim write(dst, sbuf, n); 882238384Sjkim *state = nstate; 883238384Sjkim passivemode = 0; 884238384Sjkim return n; 885238384Sjkim } else if (strcmp(cmd, "EPRT") == 0 && param) { 886238384Sjkim /* 887238384Sjkim * EPRT -> PORT 888238384Sjkim */ 889238384Sjkim char *afp, *hostp, *portp; 890238384Sjkim struct addrinfo hints, *res; 891238384Sjkim 892238384Sjkim nstate = EPRT; 893238384Sjkim 894238384Sjkim close(wport4); 895238384Sjkim close(wport6); 896238384Sjkim close(port4); 897238384Sjkim close(port6); 898238384Sjkim wport4 = wport6 = port4 = port6 = -1; 899238384Sjkim 900238384Sjkim if (epsvall) { 901238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 902238384Sjkim cmd); 903238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 904279264Sdelphij n = 0; 905279264Sdelphij if (n) 906238384Sjkim write(src, sbuf, n); 907238384Sjkim return n; 908238384Sjkim } 909238384Sjkim 910238384Sjkim p = param; 911238384Sjkim ch = *p++; /* boundary character */ 912238384Sjkim afp = p; 913238384Sjkim while (*p && *p != ch) 914238384Sjkim p++; 915238384Sjkim if (!*p) { 916238384Sjkimeprtparamfail: 917238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 918238384Sjkim "501 illegal parameter to EPRT\r\n"); 919238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 920238384Sjkim n = 0; 921238384Sjkim if (n) 922238384Sjkim write(src, sbuf, n); 923238384Sjkim return n; 924238384Sjkim } 925238384Sjkim *p++ = '\0'; 926238384Sjkim hostp = p; 927238384Sjkim while (*p && *p != ch) 928238384Sjkim p++; 929238384Sjkim if (!*p) 930238384Sjkim goto eprtparamfail; 931238384Sjkim *p++ = '\0'; 932238384Sjkim portp = p; 933238384Sjkim while (*p && *p != ch) 934238384Sjkim p++; 935238384Sjkim if (!*p) 936238384Sjkim goto eprtparamfail; 937238384Sjkim *p++ = '\0'; 938238384Sjkim 939238384Sjkim n = sscanf(afp, "%d", &af); 940238384Sjkim if (n != 1 || af != 2) { 941238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 942238384Sjkim "501 unsupported address family to EPRT\r\n"); 943238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 944238384Sjkim n = 0; 945238384Sjkim if (n) 946238384Sjkim write(src, sbuf, n); 947238384Sjkim return n; 948238384Sjkim } 949238384Sjkim memset(&hints, 0, sizeof(hints)); 950238384Sjkim hints.ai_family = AF_UNSPEC; 951238384Sjkim hints.ai_socktype = SOCK_STREAM; 952238384Sjkim hints.ai_protocol = IPPROTO_TCP; 953238384Sjkim error = getaddrinfo(hostp, portp, &hints, &res); 954238384Sjkim if (error) { 955238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 956238384Sjkim "501 EPRT: %s\r\n", gai_strerror(error)); 957238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 958238384Sjkim n = 0; 959238384Sjkim if (n) 960238384Sjkim write(src, sbuf, n); 961238384Sjkim return n; 962238384Sjkim } 963238384Sjkim if (res->ai_next) { 964238384Sjkim n = snprintf(sbuf, sizeof(sbuf), 965238384Sjkim "501 EPRT: %s resolved to multiple addresses\r\n", hostp); 966238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 967238384Sjkim n = 0; 968238384Sjkim if (n) 969238384Sjkim write(src, sbuf, n); 970238384Sjkim freeaddrinfo(res); 971238384Sjkim return n; 972238384Sjkim } 973238384Sjkim 974238384Sjkim freeaddrinfo(res); 975238384Sjkim memcpy(&data6, res->ai_addr, res->ai_addrlen); 976238384Sjkim 977238384Sjkim goto sendport; 978238384Sjkim } else if (strcmp(cmd, "LPSV") == 0 && !param) { 979238384Sjkim /* 980238384Sjkim * LPSV -> PASV 981238384Sjkim */ 982238384Sjkim nstate = LPSV; 983238384Sjkim 984238384Sjkim close(wport4); 985238384Sjkim close(wport6); 986238384Sjkim close(port4); 987238384Sjkim close(port6); 988238384Sjkim wport4 = wport6 = port4 = port6 = -1; 989238384Sjkim 990238384Sjkim if (epsvall) { 991238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", 992238384Sjkim cmd); 993238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 994238384Sjkim n = 0; 995238384Sjkim if (n) 996238384Sjkim write(src, sbuf, n); 997238384Sjkim return n; 998238384Sjkim } 999238384Sjkim 1000238384Sjkim /* transmit PASV */ 1001238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); 1002238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 1003238384Sjkim n = 0; 1004238384Sjkim if (n) 1005238384Sjkim write(dst, sbuf, n); 1006238384Sjkim *state = LPSV; 1007238384Sjkim passivemode = 0; /* to be set to 1 later */ 1008238384Sjkim return n; 1009238384Sjkim } else if (strcmp(cmd, "EPSV") == 0 && !param) { 1010238384Sjkim /* 1011238384Sjkim * EPSV -> PASV 1012238384Sjkim */ 1013238384Sjkim close(wport4); 1014238384Sjkim close(wport6); 1015238384Sjkim close(port4); 1016238384Sjkim close(port6); 1017238384Sjkim wport4 = wport6 = port4 = port6 = -1; 1018238384Sjkim 1019238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); 1020238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 1021238384Sjkim n = 0; 1022238384Sjkim if (n) 1023238384Sjkim write(dst, sbuf, n); 1024238384Sjkim *state = EPSV; 1025238384Sjkim passivemode = 0; /* to be set to 1 later */ 1026238384Sjkim return n; 1027238384Sjkim } else if (strcmp(cmd, "EPSV") == 0 && param 1028238384Sjkim && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) { 1029238384Sjkim /* 1030238384Sjkim * EPSV ALL 1031238384Sjkim */ 1032238384Sjkim epsvall = 1; 1033238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n"); 1034238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 1035238384Sjkim n = 0; 1036238384Sjkim if (n) 1037238384Sjkim write(src, sbuf, n); 1038238384Sjkim return n; 1039238384Sjkim } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) { 1040238384Sjkim /* 1041238384Sjkim * reject PORT/PASV 1042238384Sjkim */ 1043238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd); 1044238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 1045238384Sjkim n = 0; 1046238384Sjkim if (n) 1047238384Sjkim write(src, sbuf, n); 1048238384Sjkim return n; 1049238384Sjkim } else if (passivemode 1050238384Sjkim && (strcmp(cmd, "STOR") == 0 1051238384Sjkim || strcmp(cmd, "STOU") == 0 1052238384Sjkim || strcmp(cmd, "RETR") == 0 1053238384Sjkim || strcmp(cmd, "LIST") == 0 1054238384Sjkim || strcmp(cmd, "NLST") == 0 1055238384Sjkim || strcmp(cmd, "APPE") == 0)) { 1056238384Sjkim /* 1057238384Sjkim * commands with data transfer. need to care about passive 1058238384Sjkim * mode data connection. 1059238384Sjkim */ 1060238384Sjkim 1061238384Sjkim if (ftp_passiveconn() < 0) { 1062238384Sjkim n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n"); 1063238384Sjkim if (n < 0 || n >= sizeof(sbuf)) 1064238384Sjkim n = 0; 1065238384Sjkim if (n) 1066238384Sjkim write(src, sbuf, n); 1067238384Sjkim } else { 1068238384Sjkim /* simply relay the command */ 1069238384Sjkim write(dst, rbuf, n); 1070238384Sjkim } 1071238384Sjkim 1072 *state = NONE; 1073 return n; 1074 } else { 1075 /* simply relay it */ 1076 *state = NONE; 1077 write(dst, rbuf, n); 1078 return n; 1079 } 1080 1081 bad: 1082 exit_failure("%s", strerror(errno)); 1083 /*NOTREACHED*/ 1084 return 0; /* to make gcc happy */ 1085} 1086