/* 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;
}
}
syntax highlighted by Code2HTML, v. 0.9.1