/* Copyright (C) 1999 Beau Kuiper This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ftpd.h" #include "reply.h" extern int list_write(SELECTER *sel, int fd, void *peerv); extern int download_write(SELECTER *sel, int fd, void *peerv); extern int upload_read(SELECTER *sel, int fd, void *peerv); int datasock_connect(SELECTER *sel, int fd, void *peerv) { FTPSTATE *peer = (FTPSTATE *)peerv; DATAPORT *d = peer->dport; int error = 0; int on, ret = FALSE; char *modstr, transtr[100]; if (d->passive) { unsigned int ip; int temp = get_conn(fd, &ip); if (temp == -1) { reporterror(peer, "data sock", errno); closedatasocket(peer); return(2); } if ((ip != peer->remoteip) && (!peer->fxpallow)) { ftp_write(peer, FALSE, 550, REPLY_PASSIVEBADHOST); close(temp); closedatasocket(peer); return(2); } else { d->socketfd = temp; select_addfd(sel, temp); } ret = 2; /* delete the listening socket */ } else { int intsize = sizeof(int); getsockopt(d->socketfd, SOL_SOCKET, SO_ERROR, &error, &intsize); select_takewrite(sel, d->socketfd); if (error != 0) { reporterror(peer, "data socket", error); closedatasocket(peer); return(2); } } /* Now attempt to throttle data port to maximum throughput and NOPUSH */ /* I disabled logging of setsockopt failure becuase it isn't fatal and seemed to annoy people too much */ #ifdef SO_SNDLOWAT /* If we can set it */ on = LOWATERMARK; setsockopt(d->socketfd, SOL_SOCKET, SO_SNDLOWAT, &on, sizeof(int)); #endif #ifdef SO_RCVLOWAT on = LOWATERMARK; setsockopt(d->socketfd, SOL_SOCKET, SO_RCVLOWAT, &on, sizeof(int)); #endif /* set the socket non-blocking */ fcntl(d->socketfd, F_SETFL, O_NONBLOCK); /* this is here because we have to make sure the data port is connected before we give credits */ if ((d->trans_type == TRANS_SUPLOAD) && (peer->ratioinfo)) ratio_uploadfile(peer->ratioinfo); if (d->binary) modstr = "BINARY"; else modstr = "ASCII"; transtr[0] = 0; if (d->tsize != -1) snprintf(transtr, 99, " (%s bytes)", offt_tostr(d->tsize)); if (d->trans_type == TRANS_LIST) ftp_write(peer, FALSE, 150, REPLY_TRANSLISTSTART); else if ((d->pos == 0) && (d->startpos == 0)) ftp_write(peer, FALSE, 150, REPLY_TRANSSTART(modstr, transtr)); else ftp_write(peer, FALSE, 150, REPLY_TRANSRESUME(modstr, d->pos | d->startpos, transtr)); if (d->filefd != -1) select_addfd(sel, d->filefd); if (d->trans_type == TRANS_LIST) select_addwrite(sel, d->socketfd, list_write, peer); else if (d->trans_type == TRANS_DOWNLOAD) select_addwrite(sel, d->socketfd, download_write, peer); else select_addread(sel, d->socketfd, upload_read, peer); /* Set up a bandwith limiter if it is needed */ if (peer->maxtranspd_up) d->upload_limiter = limiter_new(peer->maxtranspd_up); if (peer->maxtranspd_down) d->download_limiter = limiter_new(peer->maxtranspd_down); if (peer->maxtranspd && (!peer->maxtranspd_up || !peer->maxtranspd_down )) { LIMITER *a = limiter_new(peer->maxtranspd); if (d->download_limiter == NULL) d->download_limiter = a; if (d->upload_limiter == NULL) d->upload_limiter = a; } d->trans_type = TRANS_RUNNING; return(ret); } int startdatasocket(FTPSTATE *peer, int filefd, int trans_type, off_t tsize) { DATAPORT *d = mallocwrapper(sizeof(DATAPORT)); /* if peer->remoteport is not set, use default port, RFC 959 */ if (peer->remoteport == 0) peer->remoteport = peer->connport - 1; d->download_limiter = NULL; d->upload_limiter = NULL; d->filefd = filefd; d->socketfd = -1; d->transbytes = 0; if ((peer->binary) && (trans_type != TRANS_LIST)) { d->pos = peer->restartpos; d->startpos = 0; } else { d->pos = 0; d->startpos = peer->restartpos; } d->binary = peer->binary; if ((trans_type == TRANS_UPLOAD) && (peer->restartpos == 0)) d->trans_type = TRANS_SUPLOAD; else d->trans_type = trans_type; d->tsize = tsize; d->lhandle = NULL; d->passive = (peer->passiveport != 0); d->buffer = NULL; if (!d->passive) { if (peer->epsv_forced) { ftp_write(peer, FALSE, 500, REPLY_EPSVSET); if (filefd != -1) close(filefd); freewrapper(d); return(-1); } file_becomeroot(peer); d->socketfd = conn_server_nonblocking(peer->dataip, peer->remoteport, peer->connport-1, peer->remotefd); file_becomeuser(peer); } else d->socketfd = peer->passiveport; d->buffer = string_new(); peer->remoteport = 0; peer->dataip = peer->remoteip; peer->passiveport = FALSE; peer->dport = d; peer->restartpos = 0; /* can't use default port in epsv all mode */ if (d->socketfd == -1) { reporterror(peer, "data socket", errno); if (filefd != -1) close(filefd); closedatasocket(peer); return(-1); } else { if (d->passive) select_addread(peer->sel, d->socketfd, datasock_connect, peer); else { select_addfd(peer->sel, d->socketfd); select_addwrite(peer->sel, d->socketfd, datasock_connect, peer); } } return(0); } void closedatasocket(FTPSTATE *peer) { if (peer->dport->lhandle) freelisthandle(peer->dport->lhandle); freeifnotnull(peer->dport->buffer); if (peer->dport->download_limiter == peer->dport->upload_limiter) { freeifnotnull(peer->dport->download_limiter); peer->dport->upload_limiter = NULL; } else { freeifnotnull(peer->dport->download_limiter); freeifnotnull(peer->dport->upload_limiter); } freewrapper(peer->dport); peer->dport = NULL; shinfo_changeop("idle"); } void abortdatasocket(FTPSTATE *peer) { if (peer->dport) { if (peer->dport->trans_type == TRANS_RUNNING) { select_delfd(peer->sel, peer->dport->socketfd); if (peer->dport->filefd != -1) select_delfd(peer->sel, peer->dport->filefd); ftp_write(peer, FALSE, 426, REPLY_TRANSABORT(peer->dport->transbytes)); } else { select_delfd(peer->sel, peer->dport->socketfd); ftp_write(peer, FALSE, 530, REPLY_TRANSSTILLBORN); } closedatasocket(peer); } /* also disband any data-ports open, or prepared, since the rfc says so */ peer->remoteport = 0; peer->dataip = peer->remoteip; if (peer->passiveport) { select_delfd(peer->sel, peer->passiveport); peer->passiveport = FALSE; } }