333
397
************************************************/
335
FTPSM *FTP_SM_FUNCS[] =
337
ftpReadWelcome, /* BEGIN */
338
ftpReadUser, /* SENT_USER */
339
ftpReadPass, /* SENT_PASS */
340
ftpReadType, /* SENT_TYPE */
341
ftpReadMdtm, /* SENT_MDTM */
342
ftpReadSize, /* SENT_SIZE */
343
ftpReadPort, /* SENT_PORT */
344
ftpReadPasv, /* SENT_PASV */
345
ftpReadCwd, /* SENT_CWD */
346
ftpReadList, /* SENT_LIST */
347
ftpReadList, /* SENT_NLST */
348
ftpReadRest, /* SENT_REST */
349
ftpReadRetr, /* SENT_RETR */
350
ftpReadStor, /* SENT_STOR */
351
ftpReadQuit, /* SENT_QUIT */
352
ftpReadTransferDone, /* READING_DATA (RETR,LIST,NLST) */
353
ftpWriteTransferDone, /* WRITING_DATA (STOR) */
354
ftpReadMkdir /* SENT_MKDIR */
358
FtpStateData::ftpSocketClosed(int fdnotused, void *data)
360
FtpStateData *ftpState = (FtpStateData *)data;
361
ftpState->ctrl.fd = -1;
365
FtpStateData::FtpStateData(FwdState *theFwdState) : ServerStateData(theFwdState)
399
/// \ingroup ServerProtocolFTPInternal
400
FTPSM *FTP_SM_FUNCS[] = {
401
ftpReadWelcome, /* BEGIN */
402
ftpReadUser, /* SENT_USER */
403
ftpReadPass, /* SENT_PASS */
404
ftpReadType, /* SENT_TYPE */
405
ftpReadMdtm, /* SENT_MDTM */
406
ftpReadSize, /* SENT_SIZE */
407
ftpReadEPRT, /* SENT_EPRT */
408
ftpReadPORT, /* SENT_PORT */
409
ftpReadEPSV, /* SENT_EPSV_ALL */
410
ftpReadEPSV, /* SENT_EPSV_1 */
411
ftpReadEPSV, /* SENT_EPSV_2 */
412
ftpReadPasv, /* SENT_PASV */
413
ftpReadCwd, /* SENT_CWD */
414
ftpReadList, /* SENT_LIST */
415
ftpReadList, /* SENT_NLST */
416
ftpReadRest, /* SENT_REST */
417
ftpReadRetr, /* SENT_RETR */
418
ftpReadStor, /* SENT_STOR */
419
ftpReadQuit, /* SENT_QUIT */
420
ftpReadTransferDone, /* READING_DATA (RETR,LIST,NLST) */
421
ftpWriteTransferDone, /* WRITING_DATA (STOR) */
422
ftpReadMkdir /* SENT_MKDIR */
425
/// handler called by Comm when FTP control channel is closed unexpectedly
427
FtpStateData::ctrlClosed(const CommCloseCbParams &io)
430
deleteThis("FtpStateData::ctrlClosed");
433
/// handler called by Comm when FTP data channel is closed unexpectedly
435
FtpStateData::dataClosed(const CommCloseCbParams &io)
438
failed(ERR_FTP_FAILURE, 0);
439
/* failed closes ctrl.fd and frees ftpState */
441
/* NP: failure recovery may be possible when its only a data.fd failure.
442
* is the ctrl.fd is still fine, we can send ABOR down it and retry.
443
* Just need to watch out for wider Squid states like shutting down or reconfigure.
447
FtpStateData::FtpStateData(FwdState *theFwdState) : AsyncJob("FtpStateData"), ServerStateData(theFwdState)
367
449
const char *url = entry->url();
368
debugs(9, 3, "ftpStart: '" << url << "'" );
450
debugs(9, 3, HERE << "'" << url << "'" );
369
451
statCounter.server.all.requests++;
370
452
statCounter.server.ftp.requests++;
371
ctrl.fd = theFwdState->server_fd;
443
527
fwd = NULL; // refcounted
531
* Parse a possible login username:password pair.
532
* Produces filled member varisbles user, password, password_url if anything found.
447
535
FtpStateData::loginParser(const char *login, int escaped)
450
xstrncpy(user, login, MAX_URL);
452
if ((s = strchr(user, ':'))) {
454
xstrncpy(password, s + 1, MAX_URL);
457
rfc1738_unescape(password);
461
xstrncpy(password, null_string, MAX_URL);
538
debugs(9, 4, HERE << ": login='" << login << "', escaped=" << escaped);
539
debugs(9, 9, HERE << ": IN : login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password);
541
if ((s = strchr(login, ':'))) {
544
/* if there was a username part */
546
xstrncpy(user, login, MAX_URL);
548
rfc1738_unescape(user);
551
/* if there was a password part */
552
if ( s[1] != '\0' ) {
553
xstrncpy(password, s + 1, MAX_URL);
555
rfc1738_unescape(password);
559
} else if (login[0]) {
560
/* no password, just username */
561
xstrncpy(user, login, MAX_URL);
563
rfc1738_unescape(user);
465
rfc1738_unescape(user);
468
xstrncpy(user, "anonymous", MAX_URL);
470
if (strcmp(user, "anonymous") == 0 && !password[0])
471
xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
566
debugs(9, 9, HERE << ": OUT: login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password);
475
FtpStateData::ftpTimeout(int fd, void *data)
570
FtpStateData::ftpTimeout(const CommTimeoutCbParams &io)
477
FtpStateData *ftpState = (FtpStateData *)data;
478
StoreEntry *entry = ftpState->entry;
479
debugs(9, 4, "ftpTimeout: FD " << fd << ": '" << entry->url() << "'" );
572
debugs(9, 4, "ftpTimeout: FD " << io.fd << ": '" << entry->url() << "'" );
481
if (SENT_PASV == ftpState->state && fd == ftpState->data.fd) {
574
if (SENT_PASV == state && io.fd == data.fd) {
482
575
/* stupid ftp.netscape.com */
483
ftpState->fwd->dontRetry(false);
484
ftpState->fwd->ftpPasvFailed(true);
485
debugs(9, 1, "ftpTimeout: timeout in SENT_PASV state" );
576
fwd->dontRetry(false);
577
fwd->ftpPasvFailed(true);
578
debugs(9, DBG_IMPORTANT, "ftpTimeout: timeout in SENT_PASV state" );
488
ftpState->failed(ERR_READ_TIMEOUT, 0);
581
failed(ERR_READ_TIMEOUT, 0);
489
582
/* failed() closes ctrl.fd and frees ftpState */
493
586
FtpStateData::listingStart()
495
debugs(9,3,HERE << "listingStart()");
499
const char *title = title_url.buf();
592
const char *title = title_url.termedBuf();
500
593
flags.listing_started = true;
501
594
printfReplyBody("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
502
595
printfReplyBody("<!-- HTML listing generated by Squid %s -->\n",
1214
1291
data.read_pending = true;
1216
commSetTimeout(data.fd, Config.Timeout.read, ftpTimeout, this);
1293
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
1294
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
1295
TimeoutDialer(this,&FtpStateData::ftpTimeout));
1296
commSetTimeout(data.fd, Config.Timeout.read, timeoutCall);
1218
1298
debugs(9,5,HERE << "queueing read on FD " << data.fd);
1220
entry->delayAwareRead(data.fd, data.readBuf->space(), read_sz, dataReadWrapper, this);
1300
typedef CommCbMemFunT<FtpStateData, CommIoCbParams> Dialer;
1301
entry->delayAwareRead(data.fd, data.readBuf->space(), read_sz,
1302
asyncCall(9, 5, "FtpStateData::dataRead",
1303
Dialer(this, &FtpStateData::dataRead)));
1224
FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno)
1307
FtpStateData::dataRead(const CommIoCbParams &io)
1229
debugs(9, 3, HERE << "ftpDataRead: FD " << fd << " Read " << len << " bytes");
1232
kb_incr(&statCounter.server.all.kbytes_in, len);
1233
kb_incr(&statCounter.server.ftp.kbytes_in, len);
1312
data.read_pending = false;
1314
debugs(9, 3, HERE << "ftpDataRead: FD " << io.fd << " Read " << io.size << " bytes");
1317
kb_incr(&statCounter.server.all.kbytes_in, io.size);
1318
kb_incr(&statCounter.server.ftp.kbytes_in, io.size);
1236
if (errflag == COMM_ERR_CLOSING)
1321
if (io.flag == COMM_ERR_CLOSING)
1239
assert(fd == data.fd);
1324
assert(io.fd == data.fd);
1241
1326
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1242
1327
abortTransaction("entry aborted during dataRead");
1246
if (errflag == COMM_OK && len > 0) {
1247
debugs(9,5,HERE << "appended " << len << " bytes to readBuf");
1248
data.readBuf->appended(len);
1331
if (io.flag == COMM_OK && io.size > 0) {
1332
debugs(9,5,HERE << "appended " << io.size << " bytes to readBuf");
1333
data.readBuf->appended(io.size);
1249
1334
#if DELAY_POOLS
1250
1335
DelayId delayId = entry->mem_obj->mostBytesAllowed();
1251
delayId.bytesIn(len);
1336
delayId.bytesIn(io.size);
1253
1338
IOStats.Ftp.reads++;
1255
for (j = len - 1, bin = 0; j; bin++)
1340
for (j = io.size - 1, bin = 0; j; bin++)
1258
1343
IOStats.Ftp.read_hist[bin]++;
1261
if (errflag != COMM_OK || len < 0) {
1262
debugs(50, ignoreErrno(xerrno) ? 3 : 1, "ftpDataRead: read error: " << xstrerr(xerrno));
1264
if (ignoreErrno(xerrno)) {
1265
commSetTimeout(fd, Config.Timeout.read, ftpTimeout, this);
1346
if (io.flag != COMM_OK || io.size < 0) {
1347
debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
1348
"ftpDataRead: read error: " << xstrerr(io.xerrno));
1350
if (ignoreErrno(io.xerrno)) {
1351
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
1352
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
1353
TimeoutDialer(this,&FtpStateData::ftpTimeout));
1354
commSetTimeout(io.fd, Config.Timeout.read, timeoutCall);
1266
1356
maybeReadVirginBody();
1268
1358
if (!flags.http_header_sent && !fwd->ftpPasvFailed() && flags.pasv_supported) {
1329
1419
if (flags.isdir) {
1330
1420
parseListing();
1332
if (const int csize = data.readBuf->contentSize()) {
1333
writeReplyBody(data.readBuf->content(), csize);
1334
debugs(9,5,HERE << "consuming " << csize << " bytes of readBuf");
1335
data.readBuf->consume(csize);
1422
if (const int csize = data.readBuf->contentSize()) {
1423
writeReplyBody(data.readBuf->content(), csize);
1424
debugs(9, 5, HERE << "consuming " << csize << " bytes of readBuf");
1425
data.readBuf->consume(csize);
1338
1428
entry->flush();
1340
1430
maybeReadVirginBody();
1346
* Return 1 if we have everything needed to complete this request.
1347
* Return 0 if something is missing.
1434
* Locates the FTP user:password login.
1436
* Highest to lowest priority:
1437
* - Checks URL (ftp://user:pass@domain)
1438
* - Authorization: Basic header
1439
* - squid.conf anonymous-FTP settings (default: anonymous:Squid@).
1441
* Special Case: A username-only may be provided in the URL and password in the HTTP headers.
1443
* TODO: we might be able to do something about locating username from other sources:
1444
* ie, external ACL user=* tag or ident lookup
1446
\retval 1 if we have everything needed to complete this request.
1447
\retval 0 if something is missing.
1350
1450
FtpStateData::checkAuth(const HttpHeader * req_hdr)
1353
1452
const char *auth;
1454
/* default username */
1455
xstrncpy(user, "anonymous", MAX_URL);
1457
/* Check HTTP Authorization: headers (better than defaults, but less than URL) */
1458
if ( (auth = req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")) ) {
1459
flags.authenticated = 1;
1460
loginParser(auth, FTP_LOGIN_NOT_ESCAPED);
1462
/* we fail with authorization-required error later IFF the FTP server requests it */
1464
/* Test URL login syntax. Overrides any headers received. */
1354
1465
loginParser(request->login, FTP_LOGIN_ESCAPED);
1467
/* name is missing. thats fatal. */
1357
return 1; /* no name */
1359
if (password_url || password[0])
1360
return 1; /* passwd provided in URL */
1362
/* URL has name, but no passwd */
1363
if (!(auth = req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")))
1364
return 0; /* need auth header */
1366
flags.authenticated = 1;
1368
orig_user = xstrdup(user);
1370
loginParser(auth, FTP_LOGIN_NOT_ESCAPED);
1372
if (strcmp(orig_user, user) == 0) {
1374
return 1; /* same username */
1469
fatal("FTP login parsing destroyed username info");
1471
/* name + password == success */
1475
/* Setup default FTP password settings */
1476
/* this has to be done last so that we can have a no-password case above. */
1478
if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
1479
xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
1480
flags.tried_auth_anonymous=1;
1483
else if (!flags.tried_auth_nopass) {
1484
xstrncpy(password, null_string, MAX_URL);
1485
flags.tried_auth_nopass=1;
1377
xstrncpy(user, orig_user, sizeof(user));
1379
1490
return 0; /* different username */
1493
static String str_type_eq;
1383
1495
FtpStateData::checkUrlpath()
1388
if ((t = request->urlpath.rpos(';')) != NULL) {
1389
if (strncasecmp(t + 1, "type=", 5) == 0) {
1390
typecode = (char) xtoupper(*(t + 6));
1391
request->urlpath.cutPointer(t);
1500
if (str_type_eq.undefined()) //hack. String doesn't support global-static
1501
str_type_eq="type=";
1503
if ((t = request->urlpath.rfind(';')) != String::npos) {
1504
if (request->urlpath.substr(t+1,t+1+str_type_eq.size())==str_type_eq) {
1505
typecode = (char)xtoupper(request->urlpath[t+str_type_eq.size()+1]);
1506
request->urlpath.cut(t);
1678
1785
FtpStateData::scheduleReadControlReply(int buffered_ok)
1680
debugs(9, 3, "scheduleReadControlReply: FD " << ctrl.fd);
1787
debugs(9, 3, HERE << "FD " << ctrl.fd);
1682
1789
if (buffered_ok && ctrl.offset > 0) {
1683
1790
/* We've already read some reply data */
1684
1791
handleControlReply();
1686
1793
/* XXX What about Config.Timeout.read? */
1687
comm_read(ctrl.fd, ctrl.buf + ctrl.offset, ctrl.size - ctrl.offset, ftpReadControlReply, this);
1794
typedef CommCbMemFunT<FtpStateData, CommIoCbParams> Dialer;
1795
AsyncCall::Pointer reader=asyncCall(9, 5, "FtpStateData::ftpReadControlReply",
1796
Dialer(this, &FtpStateData::ftpReadControlReply));
1797
comm_read(ctrl.fd, ctrl.buf + ctrl.offset, ctrl.size - ctrl.offset, reader);
1689
1799
* Cancel the timeout on the Data socket (if any) and
1690
1800
* establish one on the control socket.
1694
commSetTimeout(data.fd, -1, NULL, NULL);
1696
commSetTimeout(ctrl.fd, Config.Timeout.read, ftpTimeout,
1804
AsyncCall::Pointer nullCall = NULL;
1805
commSetTimeout(data.fd, -1, nullCall);
1808
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
1809
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
1810
TimeoutDialer(this,&FtpStateData::ftpTimeout));
1812
commSetTimeout(ctrl.fd, Config.Timeout.read, timeoutCall);
1702
FtpStateData::ftpReadControlReply(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno, void *data)
1816
void FtpStateData::ftpReadControlReply(const CommIoCbParams &io)
1704
FtpStateData *ftpState = (FtpStateData *)data;
1705
StoreEntry *entry = ftpState->entry;
1706
debugs(9, 5, "ftpReadControlReply: FD " << fd << ", Read " << len << " bytes");
1818
debugs(9, 3, "ftpReadControlReply: FD " << io.fd << ", Read " << io.size << " bytes");
1709
kb_incr(&statCounter.server.all.kbytes_in, len);
1710
kb_incr(&statCounter.server.ftp.kbytes_in, len);
1821
kb_incr(&statCounter.server.all.kbytes_in, io.size);
1822
kb_incr(&statCounter.server.ftp.kbytes_in, io.size);
1713
if (errflag == COMM_ERR_CLOSING)
1825
if (io.flag == COMM_ERR_CLOSING)
1716
1828
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1717
ftpState->abortTransaction("entry aborted during control reply read");
1829
abortTransaction("entry aborted during control reply read");
1721
assert(ftpState->ctrl.offset < ftpState->ctrl.size);
1833
assert(ctrl.offset < ctrl.size);
1723
if (errflag == COMM_OK && len > 0) {
1724
fd_bytes(fd, len, FD_READ);
1835
if (io.flag == COMM_OK && io.size > 0) {
1836
fd_bytes(io.fd, io.size, FD_READ);
1728
if (errflag != COMM_OK || len < 0) {
1729
debugs(50, ignoreErrno(xerrno) ? 3 : 1, "ftpReadControlReply: read error: " << xstrerr(xerrno));
1731
if (ignoreErrno(xerrno)) {
1732
ftpState->scheduleReadControlReply(0);
1839
if (io.flag != COMM_OK || io.size < 0) {
1840
debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
1841
"ftpReadControlReply: read error: " << xstrerr(io.xerrno));
1843
if (ignoreErrno(io.xerrno)) {
1844
scheduleReadControlReply(0);
1734
ftpState->failed(ERR_READ_ERROR, xerrno);
1846
failed(ERR_READ_ERROR, io.xerrno);
1735
1847
/* failed closes ctrl.fd and frees ftpState */
1952
* Translate FTP login failure into HTTP error
1953
* this is an attmpt to get the 407 message to show up outside Squid.
1954
* its NOT a general failure. But a correct FTP response type.
1957
FtpStateData::loginFailed()
1959
ErrorState *err = NULL;
1960
const char *command, *reply;
1962
if (state == SENT_USER || state == SENT_PASS) {
1963
if (ctrl.replycode > 500) {
1965
err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN, fwd->request);
1967
err = errorCon(ERR_FTP_FORBIDDEN, HTTP_UNAUTHORIZED, fwd->request);
1968
} else if (ctrl.replycode == 421) {
1969
err = errorCon(ERR_FTP_UNAVAILABLE, HTTP_SERVICE_UNAVAILABLE, fwd->request);
1976
err->ftp.server_msg = ctrl.message;
1978
ctrl.message = NULL;
1981
command = old_request;
1983
command = ctrl.last_command;
1985
if (command && strncmp(command, "PASS", 4) == 0)
1986
command = "PASS <yourpassword>";
1991
reply = ctrl.last_reply;
1994
err->ftp.request = xstrdup(command);
1997
err->ftp.reply = xstrdup(reply);
2000
HttpReply *newrep = err->BuildHttpReply();
2001
errorStateFree(err);
2002
/* add Authenticate header */
2003
newrep->header.putAuth("Basic", ftpRealm());
2005
// add it to the store entry for response....
2006
entry->replaceHttpReply(newrep);
2011
FtpStateData::ftpRealm()
2013
static char realm[8192];
2015
/* This request is not fully authenticated */
2017
snprintf(realm, 8192, "FTP %s unknown", user);
2018
} else if (request->port == 21) {
2019
snprintf(realm, 8192, "FTP %s %s", user, request->GetHost());
2021
snprintf(realm, 8192, "FTP %s %s port %d", user, request->GetHost(), request->port);
2026
/// \ingroup ServerProtocolFTPInternal
1841
2028
ftpSendUser(FtpStateData * ftpState)
1843
2030
/* check the server control channel is still available */
1844
if(!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
2031
if (!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
1847
2034
if (ftpState->proxy_host != NULL)
1848
2035
snprintf(cbuf, 1024, "USER %s@%s\r\n",
1849
2036
ftpState->user,
1850
ftpState->request->host);
2037
ftpState->request->GetHost());
1852
2039
snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user);
2184
2386
ftpState->state = SENT_SIZE;
2186
2388
/* Skip to next state no non-binary transfers */
2187
ftpSendPasv(ftpState);
2389
ftpSendPassive(ftpState);
2392
/// \ingroup ServerProtocolFTPInternal
2191
2394
ftpReadSize(FtpStateData * ftpState)
2193
2396
int code = ftpState->ctrl.replycode;
2194
debugs(9, 3, "This is ftpReadSize");
2196
2399
if (code == 213) {
2197
2400
ftpState->unhack();
2198
2401
ftpState->theSize = strtoll(ftpState->ctrl.last_reply, NULL, 10);
2200
2403
if (ftpState->theSize == 0) {
2201
debugs(9, 2, "ftpReadSize: SIZE reported " <<
2202
ftpState->ctrl.last_reply << " on " <<
2203
ftpState->title_url.buf());
2404
debugs(9, 2, "SIZE reported " <<
2405
ftpState->ctrl.last_reply << " on " <<
2406
ftpState->title_url);
2204
2407
ftpState->theSize = -1;
2206
2409
} else if (code < 0) {
2207
2410
ftpFail(ftpState);
2211
ftpSendPasv(ftpState);
2215
ftpSendPasv(FtpStateData * ftpState)
2217
struct sockaddr_in addr;
2220
/* check the server control channel is still available */
2221
if(!ftpState || !ftpState->haveControlChannel("ftpSendPasv"))
2224
debugs(9, 3, HERE << "ftpSendPasv started");
2414
ftpSendPassive(ftpState);
2418
\ingroup ServerProtocolFTPInternal
2421
ftpReadEPSV(FtpStateData* ftpState)
2423
int code = ftpState->ctrl.replycode;
2424
char h1, h2, h3, h4;
2427
IpAddress ipa_remote;
2428
int fd = ftpState->data.fd;
2432
if (code != 229 && code != 522) {
2434
/* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
2435
/* vsftpd for one send '200 EPSV ALL ok.' without even port info.
2436
* Its okay to re-send EPSV 1/2 but nothing else. */
2437
debugs(9, DBG_IMPORTANT, "Broken FTP Server at " << fd_table[ftpState->ctrl.fd].ipaddr << ". Wrong accept code for EPSV");
2439
debugs(9, 2, "EPSV not supported by remote end");
2440
ftpState->state = SENT_EPSV_1; /* simulate having failed EPSV 1 (last EPSV to try before shifting to PASV) */
2442
ftpSendPassive(ftpState);
2447
/* server response with list of supported methods */
2448
/* 522 Network protocol not supported, use (1) */
2449
/* 522 Network protocol not supported, use (1,2) */
2450
/* TODO: handle the (1,2) case. We might get it back after EPSV ALL
2451
* which means close data + control without self-destructing and re-open from scratch. */
2452
debugs(9, 5, HERE << "scanning: " << ftpState->ctrl.last_reply);
2453
buf = ftpState->ctrl.last_reply;
2454
while (buf != NULL && *buf != '\0' && *buf != '\n' && *buf != '(') ++buf;
2455
if (buf != NULL && *buf == '\n') ++buf;
2457
if (buf == NULL || *buf == '\0') {
2458
/* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
2459
debugs(9, DBG_IMPORTANT, "Broken FTP Server at " << fd_table[ftpState->ctrl.fd].ipaddr << ". 522 error missing protocol negotiation hints");
2460
ftpSendPassive(ftpState);
2461
} else if (strcmp(buf, "(1)") == 0) {
2462
ftpState->state = SENT_EPSV_2; /* simulate having sent and failed EPSV 2 */
2463
ftpSendPassive(ftpState);
2464
} else if (strcmp(buf, "(2)") == 0) {
2466
/* If server only supports EPSV 2 and we have already tried that. Go straight to EPRT */
2467
if (ftpState->state == SENT_EPSV_2) {
2468
ftpSendEPRT(ftpState);
2470
/* or try the next Passive mode down the chain. */
2471
ftpSendPassive(ftpState);
2474
/* We do not support IPv6. Remote server requires it.
2475
So we must simulate having failed all EPSV methods. */
2476
ftpState->state = SENT_EPSV_1;
2477
ftpSendPassive(ftpState);
2481
/* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
2482
debugs(9, DBG_IMPORTANT, "WARNING: Server at " << fd_table[ftpState->ctrl.fd].ipaddr << " sent unknown protocol negotiation hint: " << buf);
2483
ftpSendPassive(ftpState);
2488
/* 229 Entering Extended Passive Mode (|||port|) */
2489
/* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
2490
debugs(9, 5, "scanning: " << ftpState->ctrl.last_reply);
2492
buf = ftpState->ctrl.last_reply + strcspn(ftpState->ctrl.last_reply, "(");
2494
n = sscanf(buf, "(%c%c%c%hu%c)", &h1, &h2, &h3, &port, &h4);
2496
if (h1 != h2 || h1 != h3 || h1 != h4) {
2497
debugs(9, DBG_IMPORTANT, "Invalid EPSV reply from " <<
2498
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2499
ftpState->ctrl.last_reply);
2501
ftpSendPassive(ftpState);
2506
debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
2507
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2508
ftpState->ctrl.last_reply);
2510
ftpSendPassive(ftpState);
2514
if (Config.Ftp.sanitycheck) {
2516
debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
2517
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2518
ftpState->ctrl.last_reply);
2520
ftpSendPassive(ftpState);
2525
ftpState->data.port = port;
2527
ftpState->data.host = xstrdup(fd_table[ftpState->ctrl.fd].ipaddr);
2529
safe_free(ftpState->ctrl.last_command);
2531
safe_free(ftpState->ctrl.last_reply);
2533
ftpState->ctrl.last_command = xstrdup("Connect to server data port");
2535
debugs(9, 3, HERE << "connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
2537
commConnectStart(fd, ftpState->data.host, port, FtpStateData::ftpPasvCallback, ftpState);
2540
/** \ingroup ServerProtocolFTPInternal
2542
* Send Passive connection request.
2543
* Default method is to use modern EPSV request.
2544
* The failover mechanism should check for previous state and re-call with alternates on failure.
2547
ftpSendPassive(FtpStateData * ftpState)
2550
struct addrinfo *AI = NULL;
2552
/** Checks the server control channel is still available before running. */
2553
if (!ftpState || !ftpState->haveControlChannel("ftpSendPassive"))
2559
* Checks for EPSV ALL special conditions:
2560
* If enabled to be sent, squid MUST NOT request any other connect methods.
2561
* If 'ALL' is sent and fails the entire FTP Session fails.
2562
* NP: By my reading exact EPSV protocols maybe attempted, but only EPSV method. */
2563
if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent && ftpState->state == SENT_EPSV_1 ) {
2564
debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
2570
* Checks for 'HEAD' method request and passes off for special handling by FtpStateData::processHeadResponse(). */
2226
2571
if (ftpState->request->method == METHOD_HEAD && (ftpState->flags.isdir || ftpState->theSize != -1)) {
2227
2572
ftpState->processHeadResponse(); // may call serverComplete
2231
if (ftpState->data.fd >= 0) {
2232
/* Close old connection */
2233
comm_close(ftpState->data.fd);
2234
ftpState->data.fd = -1;
2576
/// Closes any old FTP-Data connection which may exist. */
2577
ftpState->data.close();
2580
* Checks for previous EPSV/PASV failures on this server/session.
2581
* Diverts to EPRT immediately if they are not working. */
2237
2582
if (!ftpState->flags.pasv_supported) {
2238
ftpSendPort(ftpState);
2583
ftpSendEPRT(ftpState);
2242
addr_len = sizeof(addr);
2588
* Locates the Address of the remote server. */
2589
addr.InitAddrInfo(AI);
2244
if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
2245
debugs(9, 0, "ftpSendPasv: getsockname(" << ftpState->ctrl.fd << ",..): " << xstrerror());
2591
if (getsockname(ftpState->ctrl.fd, AI->ai_addr, &AI->ai_addrlen)) {
2592
/** If it cannot be located the FTP Session is killed. */
2593
addr.FreeAddrInfo(AI);
2594
debugs(9, DBG_CRITICAL, HERE << "getsockname(" << ftpState->ctrl.fd << ",'" << addr << "',...): " << xstrerror());
2246
2595
ftpFail(ftpState);
2250
/* Open data channel with the same local address as control channel */
2601
addr.FreeAddrInfo(AI);
2603
/** Otherwise, Open data channel with the same local address as control channel (on a new random port!) */
2251
2605
int fd = comm_open(SOCK_STREAM,
2255
2608
COMM_NONBLOCKING,
2256
2609
ftpState->entry->url());
2258
debugs(9, 3, "ftpSendPasv: Unconnected data socket created on FD " << fd);
2611
debugs(9, 3, HERE << "Unconnected data socket created on FD " << fd << " to " << addr);
2261
2614
ftpFail(ftpState);
2266
* No comm_add_close_handler() here. If we have both ctrl and
2267
* data FD's call ftpSocketClosed() upon close, then we have
2268
* to delete the close handler which did NOT get called
2269
* to prevent ftpSocketClosed() getting called twice.
2270
* Instead we'll always call comm_close() on the ctrl FD.
2272
* XXX this should not actually matter if the ftpState is cbdata
2273
* managed correctly and comm close handlers are cbdata fenced
2275
ftpState->data.fd = fd;
2277
snprintf(cbuf, 1024, "PASV\r\n");
2618
ftpState->data.opened(fd, ftpState->dataCloser());
2621
* Send EPSV (ALL,2,1) or PASV on the control channel.
2623
* - EPSV ALL is used if enabled.
2624
* - EPSV 2 is used if ALL is disabled and IPv6 is available.
2625
* - EPSV 1 is used if EPSV 2 (IPv6) fails or is not available.
2626
* - PASV is used if EPSV 1 fails.
2628
switch (ftpState->state) {
2629
case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
2630
snprintf(cbuf, 1024, "PASV\r\n");
2631
ftpState->state = SENT_PASV;
2634
case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
2635
snprintf(cbuf, 1024, "EPSV 1\r\n");
2636
ftpState->state = SENT_EPSV_1;
2639
case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
2640
ftpState->flags.epsv_all_sent = true;
2641
snprintf(cbuf, 1024, "EPSV 2\r\n");
2642
ftpState->state = SENT_EPSV_2;
2646
if (!Config.Ftp.epsv) {
2647
snprintf(cbuf, 1024, "PASV\r\n");
2648
ftpState->state = SENT_PASV;
2649
} else if (Config.Ftp.epsv_all) {
2650
snprintf(cbuf, 1024, "EPSV ALL\r\n");
2651
ftpState->state = SENT_EPSV_ALL;
2652
/* block other non-EPSV connections being attempted */
2653
ftpState->flags.epsv_all_sent = true;
2656
snprintf(cbuf, 1024, "EPSV 2\r\n");
2657
ftpState->state = SENT_EPSV_2;
2659
snprintf(cbuf, 1024, "EPSV 1\r\n");
2660
ftpState->state = SENT_EPSV_1;
2279
2666
ftpState->writeCommand(cbuf);
2281
ftpState->state = SENT_PASV;
2284
2669
* ugly hack for ftp servers like ftp.netscape.com that sometimes
2285
2670
* dont acknowledge PASV commands.
2287
commSetTimeout(ftpState->data.fd, 15, FtpStateData::ftpTimeout, ftpState);
2672
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
2673
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
2674
TimeoutDialer(ftpState,&FtpStateData::ftpTimeout));
2676
commSetTimeout(ftpState->data.fd, 15, timeoutCall);
2716
IpAddress ipa_remote;
2326
2717
int fd = ftpState->data.fd;
2328
2719
LOCAL_ARRAY(char, ipaddr, 1024);
2329
debugs(9, 3, "This is ftpReadPasv");
2331
2722
if (code != 227) {
2332
debugs(9, 3, "PASV not supported by remote end");
2333
ftpSendPort(ftpState);
2723
debugs(9, 2, "PASV not supported by remote end");
2724
ftpSendEPRT(ftpState);
2337
2728
/* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
2338
2729
/* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
2339
debugs(9, 5, "scanning: " << ftpState->ctrl.last_reply);
2730
debugs(9, 5, HERE << "scanning: " << ftpState->ctrl.last_reply);
2341
2732
buf = ftpState->ctrl.last_reply + strcspn(ftpState->ctrl.last_reply, "0123456789");
2343
2734
n = sscanf(buf, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2);
2345
2736
if (n != 6 || p1 < 0 || p2 < 0 || p1 > 255 || p2 > 255) {
2346
debugs(9, 1, "Unsafe PASV reply from " <<
2737
debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
2347
2738
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2348
2739
ftpState->ctrl.last_reply);
2350
ftpSendPort(ftpState);
2741
ftpSendEPRT(ftpState);
2354
2745
snprintf(ipaddr, 1024, "%d.%d.%d.%d", h1, h2, h3, h4);
2356
if (!safe_inet_addr(ipaddr, NULL)) {
2357
debugs(9, 1, "Unsafe PASV reply from " <<
2747
ipa_remote = ipaddr;
2749
if ( ipa_remote.IsAnyAddr() ) {
2750
debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
2358
2751
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2359
2752
ftpState->ctrl.last_reply);
2361
ftpSendPort(ftpState);
2754
ftpSendEPRT(ftpState);
2365
2758
port = ((p1 << 8) + p2);
2367
2760
if (0 == port) {
2368
debugs(9, 1, "Unsafe PASV reply from " <<
2761
debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
2369
2762
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2370
2763
ftpState->ctrl.last_reply);
2372
ftpSendPort(ftpState);
2765
ftpSendEPRT(ftpState);
2376
2769
if (Config.Ftp.sanitycheck) {
2377
2770
if (port < 1024) {
2378
debugs(9, 1, "Unsafe PASV reply from " <<
2771
debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
2379
2772
fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
2380
2773
ftpState->ctrl.last_reply);
2382
ftpSendPort(ftpState);
2775
ftpSendEPRT(ftpState);
2480
ftpState->data.fd = fd;
2877
ftpState->data.opened(fd, ftpState->dataCloser());
2481
2878
ftpState->data.port = comm_local_port(fd);
2482
2879
ftpState->data.host = NULL;
2883
/// \ingroup ServerProtocolFTPInternal
2487
ftpSendPort(FtpStateData * ftpState)
2885
ftpSendPORT(FtpStateData * ftpState)
2491
struct sockaddr_in addr;
2890
struct addrinfo *AI = NULL;
2493
2891
unsigned char *addrptr;
2494
2892
unsigned char *portptr;
2496
2894
/* check the server control channel is still available */
2497
if(!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
2500
debugs(9, 3, "This is ftpSendPort");
2895
if (!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
2898
if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
2899
debugs(9, DBG_IMPORTANT, "FTP does not allow PORT method after 'EPSV ALL' has been sent.");
2501
2904
ftpState->flags.pasv_supported = 0;
2502
2905
fd = ftpOpenListenSocket(ftpState, 0);
2503
addr_len = sizeof(addr);
2906
ipa.InitAddrInfo(AI);
2505
if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
2506
debugs(9, 0, "ftpSendPort: getsockname(" << fd << ",..): " << xstrerror());
2908
if (getsockname(fd, AI->ai_addr, &AI->ai_addrlen)) {
2909
ipa.FreeAddrInfo(AI);
2910
debugs(9, DBG_CRITICAL, HERE << "getsockname(" << fd << ",..): " << xstrerror());
2508
2912
/* XXX Need to set error message */
2509
2913
ftpFail(ftpState);
2513
addrptr = (unsigned char *) &addr.sin_addr.s_addr;
2514
portptr = (unsigned char *) &addr.sin_port;
2918
if ( AI->ai_addrlen != sizeof(struct sockaddr_in) ) {
2919
ipa.FreeAddrInfo(AI);
2920
/* IPv6 CANNOT send PORT command. */
2921
/* we got here by attempting and failing an EPRT */
2922
/* using the same reply code should simulate a PORT failure */
2923
ftpReadPORT(ftpState);
2928
addrptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_addr;
2929
portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port;
2515
2930
snprintf(cbuf, 1024, "PORT %d,%d,%d,%d,%d,%d\r\n",
2516
2931
addrptr[0], addrptr[1], addrptr[2], addrptr[3],
2517
2932
portptr[0], portptr[1]);
2518
2933
ftpState->writeCommand(cbuf);
2519
2934
ftpState->state = SENT_PORT;
2936
ipa.FreeAddrInfo(AI);
2939
/// \ingroup ServerProtocolFTPInternal
2523
ftpReadPort(FtpStateData * ftpState)
2941
ftpReadPORT(FtpStateData * ftpState)
2525
2943
int code = ftpState->ctrl.replycode;
2526
debugs(9, 3, "This is ftpReadPort");
2528
2946
if (code != 200) {
2529
2947
/* Fall back on using the same port as the control connection */
2534
2952
ftpRestOrList(ftpState);
2537
/* "read" handler to accept data connection */
2539
ftpAcceptDataConnection(int fd, int newfd, ConnectionDetail *details,
2540
comm_err_t flag, int xerrno, void *data)
2542
FtpStateData *ftpState = (FtpStateData *)data;
2955
/// \ingroup ServerProtocolFTPInternal
2957
ftpSendEPRT(FtpStateData * ftpState)
2961
struct addrinfo *AI = NULL;
2962
char buf[MAX_IPSTRLEN];
2964
if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
2965
debugs(9, DBG_IMPORTANT, "FTP does not allow EPRT method after 'EPSV ALL' has been sent.");
2970
ftpState->flags.pasv_supported = 0;
2971
fd = ftpOpenListenSocket(ftpState, 0);
2973
addr.InitAddrInfo(AI);
2975
if (getsockname(fd, AI->ai_addr, &AI->ai_addrlen)) {
2976
addr.FreeAddrInfo(AI);
2977
debugs(9, DBG_CRITICAL, HERE << "getsockname(" << fd << ",..): " << xstrerror());
2979
/* XXX Need to set error message */
2986
/* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
2987
/* Which can be used by EITHER protocol. */
2988
snprintf(cbuf, 1024, "EPRT |%d|%s|%d|\r\n",
2989
( addr.IsIPv6() ? 2 : 1 ),
2990
addr.NtoA(buf,MAX_IPSTRLEN),
2993
ftpState->writeCommand(cbuf);
2994
ftpState->state = SENT_EPRT;
2996
addr.FreeAddrInfo(AI);
3000
ftpReadEPRT(FtpStateData * ftpState)
3002
int code = ftpState->ctrl.replycode;
3006
/* Failover to attempting old PORT command. */
3007
debugs(9, 3, "EPRT not supported by remote end");
3008
ftpSendPORT(ftpState);
3012
ftpRestOrList(ftpState);
3016
\ingroup ServerProtocolFTPInternal
3018
* "read" handler to accept FTP data connections.
3020
\param io comm accept(2) callback parameters
3022
void FtpStateData::ftpAcceptDataConnection(const CommAcceptCbParams &io)
3024
char ntoapeer[MAX_IPSTRLEN];
2543
3025
debugs(9, 3, "ftpAcceptDataConnection");
2545
if (flag == COMM_ERR_CLOSING)
3027
if (io.flag == COMM_ERR_CLOSING)
2548
if (EBIT_TEST(ftpState->entry->flags, ENTRY_ABORTED)) {
2549
ftpState->abortTransaction("entry aborted when accepting data conn");
3030
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
3031
abortTransaction("entry aborted when accepting data conn");
3036
* When squid.conf ftp_sanitycheck is enabled, check the new connection is actually being
3037
* made by the remote client which is connected to the FTP control socket.
3038
* This prevents third-party hacks, but also third-party load balancing handshakes.
2553
3040
if (Config.Ftp.sanitycheck) {
2554
char *ipaddr = inet_ntoa(details->peer.sin_addr);
2556
if (strcmp(fd_table[ftpState->ctrl.fd].ipaddr, ipaddr) != 0) {
2557
debugs(9, 1, "FTP data connection from unexpected server (" <<
2558
ipaddr << ":" << (int) ntohs(details->peer.sin_port) <<
2559
"), expecting " << fd_table[ftpState->ctrl.fd].ipaddr);
2562
comm_accept(ftpState->data.fd, ftpAcceptDataConnection, ftpState);
3041
io.details.peer.NtoA(ntoapeer,MAX_IPSTRLEN);
3043
if (strcmp(fd_table[ctrl.fd].ipaddr, ntoapeer) != 0) {
3044
debugs(9, DBG_IMPORTANT,
3045
"FTP data connection from unexpected server (" <<
3046
io.details.peer << "), expecting " <<
3047
fd_table[ctrl.fd].ipaddr);
3050
typedef CommCbMemFunT<FtpStateData, CommAcceptCbParams> acceptDialer;
3051
AsyncCall::Pointer acceptCall = asyncCall(11, 5, "FtpStateData::ftpAcceptDataConnection",
3052
acceptDialer(this, &FtpStateData::ftpAcceptDataConnection));
3053
comm_accept(data.fd, acceptCall);
2567
if (flag != COMM_OK) {
2568
debugs(9, 1, "ftpHandleDataAccept: comm_accept(" << newfd << "): " << xstrerr(xerrno));
2569
/* XXX Need to set error message */
3058
if (io.flag != COMM_OK) {
3059
debugs(9, DBG_IMPORTANT, "ftpHandleDataAccept: comm_accept(" << io.nfd << "): " << xstrerr(io.xerrno));
3060
/** \todo XXX Need to set error message */
2574
/* Replace the Listen socket with the accepted data socket */
2575
comm_close(ftpState->data.fd);
2577
debugs(9, 3, "ftpAcceptDataConnection: Connected data socket on FD " << newfd);
2579
ftpState->data.fd = newfd;
2581
ftpState->data.port = ntohs(details->peer.sin_port);
2583
ftpState->data.host = xstrdup(inet_ntoa(details->peer.sin_addr));
2585
commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
2587
commSetTimeout(ftpState->data.fd, Config.Timeout.read, FtpStateData::ftpTimeout,
2590
/* XXX We should have a flag to track connect state...
3066
* Replace the Listen socket with the accepted data socket */
3068
data.opened(io.nfd, dataCloser());
3069
data.port = io.details.peer.GetPort();
3070
io.details.peer.NtoA(data.host,SQUIDHOSTNAMELEN);
3072
debugs(9, 3, "ftpAcceptDataConnection: Connected data socket on " <<
3073
"FD " << io.nfd << " to " << io.details.peer << " FD table says: " <<
3074
"ctrl-peer= " << fd_table[ctrl.fd].ipaddr << ", " <<
3075
"data-peer= " << fd_table[data.fd].ipaddr);
3078
AsyncCall::Pointer nullCall = NULL;
3079
commSetTimeout(ctrl.fd, -1, nullCall);
3081
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
3082
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
3083
TimeoutDialer(this,&FtpStateData::ftpTimeout));
3084
commSetTimeout(data.fd, Config.Timeout.read, timeoutCall);
3086
/*\todo XXX We should have a flag to track connect state...
2591
3087
* host NULL -> not connected, port == local port
2592
3088
* host set -> connected, port == remote port
2594
3090
/* Restart state (SENT_NLST/LIST/RETR) */
2595
FTP_SM_FUNCS[ftpState->state] (ftpState);
3091
FTP_SM_FUNCS[state] (this);
3094
/// \ingroup ServerProtocolFTPInternal
2599
3096
ftpRestOrList(FtpStateData * ftpState)
2601
debugs(9, 3, "This is ftpRestOrList");
2603
3100
if (ftpState->typecode == 'D') {
2604
3101
ftpState->flags.isdir = 1;
2661
/* Begin data transfer */
2662
debugs(9, 3, "ftpReadStor: starting data transfer");
3164
* When client status is 125, or 150 without a hostname, Begin data transfer. */
3165
debugs(9, 3, HERE << "starting data transfer");
2663
3166
sendMoreRequestBody();
2665
3168
* Cancel the timeout on the Control socket and
2666
3169
* establish one on the data socket.
2668
commSetTimeout(ctrl.fd, -1, NULL, NULL);
2669
commSetTimeout(data.fd, Config.Timeout.read, FtpStateData::ftpTimeout,
3171
AsyncCall::Pointer nullCall = NULL;
3172
commSetTimeout(ctrl.fd, -1, nullCall);
3174
typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
3175
AsyncCall::Pointer timeoutCall = asyncCall(9, 5, "FtpStateData::ftpTimeout",
3176
TimeoutDialer(this,&FtpStateData::ftpTimeout));
3178
commSetTimeout(data.fd, Config.Timeout.read, timeoutCall);
2672
3180
state = WRITING_DATA;
2673
debugs(9, 3, "ftpReadStor: writing data channel");
3181
debugs(9, 3, HERE << "writing data channel");
2674
3182
} else if (code == 150) {
2675
/* Accept data channel */
3184
* When client code is 150 with a hostname, Accept data channel. */
2676
3185
debugs(9, 3, "ftpReadStor: accepting data channel");
2677
comm_accept(data.fd, ftpAcceptDataConnection, this);
3186
typedef CommCbMemFunT<FtpStateData, CommAcceptCbParams> acceptDialer;
3187
AsyncCall::Pointer acceptCall = asyncCall(11, 5, "FtpStateData::ftpAcceptDataConnection",
3188
acceptDialer(this, &FtpStateData::ftpAcceptDataConnection));
3190
comm_accept(data.fd, acceptCall);
2679
debugs(9, 3, "ftpReadStor: Unexpected reply code "<< std::setfill('0') << std::setw(3) << code);
3192
debugs(9, DBG_IMPORTANT, HERE << "Unexpected reply code "<< std::setfill('0') << std::setw(3) << code);
3197
/// \ingroup ServerProtocolFTPInternal
2685
3199
ftpSendRest(FtpStateData * ftpState)
2687
3201
/* check the server control channel is still available */
2688
if(!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
3202
if (!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
2691
3207
snprintf(cbuf, 1024, "REST %"PRId64"\r\n", ftpState->restart_offset);
2692
3208
ftpState->writeCommand(cbuf);
2693
3209
ftpState->state = SENT_REST;
3264
3834
FtpStateData::ftpAuthRequired(HttpRequest * request, const char *realm)
3266
3836
ErrorState *err = errorCon(ERR_CACHE_ACCESS_DENIED, HTTP_UNAUTHORIZED, request);
3267
HttpReply *newrep = errorBuildReply(err);
3837
HttpReply *newrep = err->BuildHttpReply();
3268
3838
errorStateFree(err);
3269
3839
/* add Authenticate header */
3270
3840
newrep->header.putAuth("Basic", realm);
3275
ftpUrlWith2f(const HttpRequest * request)
3845
\ingroup ServerProtocolFTPAPI
3846
\todo Should be a URL class API call.
3848
* Construct an URI with leading / in PATH portion for use by CWD command
3849
* possibly others. FTP encodes absolute paths as beginning with '/'
3850
* after the initial URI path delimiter, which happens to be / itself.
3851
* This makes FTP absolute URI appear as: ftp:host:port//root/path
3852
* To encompass older software which compacts multiple // to / in transit
3853
* We use standard URI-encoding on the second / making it
3854
* ftp:host:port/%2froot/path AKA 'the FTP %2f hack'.
3857
ftpUrlWith2f(HttpRequest * request)
3277
LOCAL_ARRAY(char, buf, MAX_URL);
3278
LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
3279
LOCAL_ARRAY(char, portbuf, 32);
3859
String newbuf = "%2f";
3283
3861
if (request->protocol != PROTO_FTP)
3286
if (request->port != urlDefaultPort(request->protocol))
3287
snprintf(portbuf, 32, ":%d", request->port);
3291
if ((int) strlen(request->login) > 0) {
3292
xstrncpy(loginbuf, request->login, sizeof(loginbuf) - 2);
3294
if ((t = strchr(loginbuf, ':')))
3297
strcat(loginbuf, "@");
3864
if ( request->urlpath[0]=='/' ) {
3865
newbuf.append(request->urlpath);
3866
request->urlpath.absorb(newbuf);
3867
safe_free(request->canonical);
3868
} else if ( !strncmp(request->urlpath.termedBuf(), "%2f", 3) ) {
3869
newbuf.append(request->urlpath.substr(1,request->urlpath.size()));
3870
request->urlpath.absorb(newbuf);
3871
safe_free(request->canonical);
3300
snprintf(buf, MAX_URL, "%s://%s%s%s%s%s",
3301
ProtocolStr[request->protocol],
3306
request->urlpath.buf());
3308
if ((t = strchr(buf, '?')))
3874
return urlCanonical(request);