~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/ftp.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2009-09-24 14:51:06 UTC
  • mfrom: (1.1.12 upstream)
  • mto: (20.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20090924145106-38jgrzmj0d73pha5
Tags: 3.1.0.13-1
* Upload to experimental

* New upstream release
  - Fixes Follow-X-Forwarded-For support (Closes: #523943)
  - Adds IPv6 support (Closes: #432351)

* debian/rules
  - Removed obsolete configuration options
  - Enable db and radius basic authentication modules

* debian/patches/01-cf.data.debian
  - Adapted to new upstream version

* debian/patches/02-makefile-defaults
  - Adapted to new upstream version

* debian/{squid.postinst,squid.rc,README.Debian,watch}
  - Updated references to squid 3.1

* debian/squid3.install
  - Install CSS file for error pages
  - Install manual pages for new authentication modules

* debian/squid3-common.install
  - Install documented version of configuration file in /usr/share/doc/squid3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * $Id: ftp.cc,v 1.441 2007/09/27 14:34:06 rousskov Exp $
 
2
 * $Id$
3
3
 *
4
4
 * DEBUG: section 9     File Transfer Protocol (FTP)
5
5
 * AUTHOR: Harvest Derived
20
20
 *  it under the terms of the GNU General Public License as published by
21
21
 *  the Free Software Foundation; either version 2 of the License, or
22
22
 *  (at your option) any later version.
23
 
 *  
 
23
 *
24
24
 *  This program is distributed in the hope that it will be useful,
25
25
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
26
26
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
27
 *  GNU General Public License for more details.
28
 
 *  
 
28
 *
29
29
 *  You should have received a copy of the GNU General Public License
30
30
 *  along with this program; if not, write to the Free Software
31
31
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
54
54
#include "SquidTime.h"
55
55
#include "URLScheme.h"
56
56
 
 
57
/**
 
58
 \defgroup ServerProtocolFTPInternal Server-Side FTP Internals
 
59
 \ingroup ServerProtocolFTPAPI
 
60
 */
 
61
 
 
62
/// \ingroup ServerProtocolFTPInternal
57
63
static const char *const crlf = "\r\n";
 
64
 
 
65
/// \ingroup ServerProtocolFTPInternal
58
66
static char cbuf[1024];
59
67
 
 
68
/// \ingroup ServerProtocolFTPInternal
60
69
typedef enum {
61
70
    BEGIN,
62
71
    SENT_USER,
64
73
    SENT_TYPE,
65
74
    SENT_MDTM,
66
75
    SENT_SIZE,
 
76
    SENT_EPRT,
67
77
    SENT_PORT,
 
78
    SENT_EPSV_ALL,
 
79
    SENT_EPSV_1,
 
80
    SENT_EPSV_2,
68
81
    SENT_PASV,
69
82
    SENT_CWD,
70
83
    SENT_LIST,
78
91
    SENT_MKDIR
79
92
} ftp_state_t;
80
93
 
81
 
struct _ftp_flags
82
 
{
 
94
/// \ingroup ServerProtocolFTPInternal
 
95
struct _ftp_flags {
 
96
 
 
97
    /* passive mode */
 
98
    bool pasv_supported;  ///< PASV command is allowed
 
99
    bool epsv_all_sent;   ///< EPSV ALL has been used. Must abort on failures.
 
100
    bool pasv_only;
 
101
 
 
102
    /* authentication */
 
103
    bool authenticated;         ///< authentication success
 
104
    bool tried_auth_anonymous;  ///< auth has tried to use anonymous credentials already.
 
105
    bool tried_auth_nopass;     ///< auth tried username with no password already.
 
106
 
 
107
    /* other */
83
108
    bool isdir;
84
 
    bool pasv_supported;
85
109
    bool skip_whitespace;
86
110
    bool rest_supported;
87
 
    bool pasv_only;
88
 
    bool authenticated;
89
111
    bool http_header_sent;
90
112
    bool tried_nlst;
91
113
    bool need_base_href;
103
125
};
104
126
 
105
127
class FtpStateData;
 
128
 
 
129
/// \ingroup ServerProtocolFTPInternal
106
130
typedef void (FTPSM) (FtpStateData *);
107
131
 
 
132
/// common code for FTP control and data channels
 
133
// does not own the channel descriptor, which is managed by FtpStateData
 
134
class FtpChannel
 
135
{
 
136
public:
 
137
    FtpChannel(): fd(-1) {}
 
138
 
 
139
    /// called after the socket is opened, sets up close handler
 
140
    void opened(int aFd, const AsyncCall::Pointer &aCloser);
 
141
 
 
142
    void close(); /// clears the close handler and calls comm_close
 
143
    void clear(); /// just resets fd and close handler
 
144
 
 
145
    int fd; /// channel descriptor; \todo: remove because the closer has it
 
146
 
 
147
private:
 
148
    AsyncCall::Pointer closer; /// Comm close handler callback
 
149
};
 
150
 
 
151
/// \ingroup ServerProtocolFTPInternal
108
152
class FtpStateData : public ServerStateData
109
153
{
110
154
 
111
155
public:
112
156
    void *operator new (size_t);
113
157
    void operator delete (void *);
 
158
    void *toCbdata() { return this; }
 
159
 
114
160
    FtpStateData(FwdState *);
115
161
    ~FtpStateData();
116
162
    char user[MAX_URL];
138
184
    char *old_filepath;
139
185
    char typecode;
140
186
 
141
 
    struct
142
 
    {
143
 
        int fd;
 
187
    // \todo: optimize ctrl and data structs member order, to minimize size
 
188
    /// FTP control channel info; the channel is opened once per transaction
 
189
    struct CtrlChannel: public FtpChannel {
144
190
        char *buf;
145
191
        size_t size;
146
192
        size_t offset;
148
194
        char *last_command;
149
195
        char *last_reply;
150
196
        int replycode;
151
 
    }
152
 
 
153
 
    ctrl;
154
 
 
155
 
    struct
156
 
    {
157
 
        int fd;
 
197
    } ctrl;
 
198
 
 
199
    /// FTP data channel info; the channel may be opened/closed a few times
 
200
    struct DataChannel: public FtpChannel {
158
201
        MemBuf *readBuf;
159
202
        char *host;
160
203
        u_short port;
161
204
        bool read_pending;
162
 
    }
163
 
 
164
 
    data;
 
205
    } data;
165
206
 
166
207
    struct _ftp_flags flags;
167
208
 
186
227
    char *htmlifyListEntry(const char *line);
187
228
    void parseListing();
188
229
    void dataComplete();
189
 
    void dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno);
 
230
    void dataRead(const CommIoCbParams &io);
190
231
    int checkAuth(const HttpHeader * req_hdr);
191
232
    void checkUrlpath();
192
233
    void buildTitleUrl();
203
244
    void setCurrentOffset(int64_t offset) { currentOffset = offset; }
204
245
    int64_t getCurrentOffset() const { return currentOffset; }
205
246
 
206
 
    static PF ftpSocketClosed;
207
247
    static CNCB ftpPasvCallback;
208
 
    static IOCB dataReadWrapper;
209
248
    static PF ftpDataWrite;
210
 
    static PF ftpTimeout;
211
 
    static IOCB ftpReadControlReply;
212
 
    static IOCB ftpWriteCommandCallback;
 
249
    void ftpTimeout(const CommTimeoutCbParams &io);
 
250
    void ctrlClosed(const CommCloseCbParams &io);
 
251
    void dataClosed(const CommCloseCbParams &io);
 
252
    void ftpReadControlReply(const CommIoCbParams &io);
 
253
    void ftpWriteCommandCallback(const CommIoCbParams &io);
 
254
    void ftpAcceptDataConnection(const CommAcceptCbParams &io);
 
255
 
213
256
    static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm);
 
257
    const char *ftpRealm(void);
 
258
    void loginFailed(void);
214
259
    static wordlist *ftpParseControlReply(char *, size_t, int *, size_t *);
215
260
 
216
261
    // sending of the request body to the server
217
 
    virtual void sentRequestBody(int fd, size_t size, comm_err_t errflag);
 
262
    virtual void sentRequestBody(const CommIoCbParams&);
218
263
    virtual void doneSendingRequestBody();
219
264
 
220
265
    virtual void haveParsedReplyHeaders();
221
266
 
222
267
    virtual bool doneWithServer() const;
223
268
    virtual bool haveControlChannel(const char *caller_name) const;
 
269
    AsyncCall::Pointer dataCloser(); /// creates a Comm close callback
224
270
 
225
271
private:
226
272
    // BodyConsumer for HTTP: consume request body.
244
290
    cbdataFree(t);
245
291
}
246
292
 
247
 
typedef struct
248
 
{
 
293
/// \ingroup ServerProtocolFTPInternal
 
294
typedef struct {
249
295
    char type;
250
296
    int64_t size;
251
297
    char *date;
252
298
    char *name;
253
299
    char *showname;
254
300
    char *link;
255
 
}
256
 
 
257
 
ftpListParts;
258
 
 
259
 
#define FTP_LOGIN_ESCAPED 1
260
 
#define FTP_LOGIN_NOT_ESCAPED 0
 
301
} ftpListParts;
 
302
 
 
303
/// \ingroup ServerProtocolFTPInternal
 
304
#define FTP_LOGIN_ESCAPED       1
 
305
 
 
306
/// \ingroup ServerProtocolFTPInternal
 
307
#define FTP_LOGIN_NOT_ESCAPED   0
261
308
 
262
309
/*
263
310
 * State machine functions
276
323
static FTPSM ftpReadMdtm;
277
324
static FTPSM ftpSendSize;
278
325
static FTPSM ftpReadSize;
279
 
static FTPSM ftpSendPort;
280
 
static FTPSM ftpReadPort;
281
 
static FTPSM ftpSendPasv;
 
326
static FTPSM ftpSendEPRT;
 
327
static FTPSM ftpReadEPRT;
 
328
static FTPSM ftpSendPORT;
 
329
static FTPSM ftpReadPORT;
 
330
static FTPSM ftpSendPassive;
 
331
static FTPSM ftpReadEPSV;
282
332
static FTPSM ftpReadPasv;
283
333
static FTPSM ftpTraverseDirectory;
284
334
static FTPSM ftpListDir;
303
353
static FTPSM ftpFail;
304
354
static FTPSM ftpSendQuit;
305
355
static FTPSM ftpReadQuit;
 
356
 
 
357
/************************************************
 
358
** Debugs Levels used here                     **
 
359
*************************************************
 
360
0       CRITICAL Events
 
361
1       IMPORTANT Events
 
362
        Protocol and Transmission failures.
 
363
2       FTP Protocol Chatter
 
364
3       Logic Flows
 
365
4       Data Parsing Flows
 
366
5       Data Dumps
 
367
7       ??
 
368
************************************************/
 
369
 
306
370
/************************************************
307
371
** State Machine Description (excluding hacks) **
308
372
*************************************************
316
380
Cwd                     TraverseDirectory / Mkdir
317
381
GetFile                 Mdtm
318
382
Mdtm                    Size
319
 
Size                    Pasv
320
 
ListDir                 Pasv
321
 
Pasv                    FileOrList
 
383
Size                    Epsv
 
384
ListDir                 Epsv
 
385
Epsv                    FileOrList
322
386
FileOrList              Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
323
387
Rest                    Retr
324
388
Retr / Nlst / List      DataRead* (on datachannel)
332
396
Quit                    -
333
397
************************************************/
334
398
 
335
 
FTPSM *FTP_SM_FUNCS[] =
336
 
    {
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 */
355
 
    };
356
 
 
357
 
void
358
 
FtpStateData::ftpSocketClosed(int fdnotused, void *data)
359
 
{
360
 
    FtpStateData *ftpState = (FtpStateData *)data;
361
 
    ftpState->ctrl.fd = -1;
362
 
    delete ftpState;
363
 
}
364
 
 
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 */
 
423
};
 
424
 
 
425
/// handler called by Comm when FTP control channel is closed unexpectedly
 
426
void
 
427
FtpStateData::ctrlClosed(const CommCloseCbParams &io)
 
428
{
 
429
    ctrl.clear();
 
430
    deleteThis("FtpStateData::ctrlClosed");
 
431
}
 
432
 
 
433
/// handler called by Comm when FTP data channel is closed unexpectedly
 
434
void
 
435
FtpStateData::dataClosed(const CommCloseCbParams &io)
 
436
{
 
437
    data.clear();
 
438
    failed(ERR_FTP_FAILURE, 0);
 
439
    /* failed closes ctrl.fd and frees ftpState */
 
440
 
 
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.
 
444
     */
 
445
}
 
446
 
 
447
FtpStateData::FtpStateData(FwdState *theFwdState) : AsyncJob("FtpStateData"), ServerStateData(theFwdState)
366
448
{
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;
372
 
    data.fd = -1;
373
453
    theSize = -1;
374
454
    mdtm = -1;
375
455
 
378
458
 
379
459
    flags.rest_supported = 1;
380
460
 
381
 
    comm_add_close_handler(ctrl.fd, ftpSocketClosed, this);
 
461
    typedef CommCbMemFunT<FtpStateData, CommCloseCbParams> Dialer;
 
462
    AsyncCall::Pointer closer = asyncCall(9, 5, "FtpStateData::ctrlClosed",
 
463
                                          Dialer(this, &FtpStateData::ctrlClosed));
 
464
    ctrl.opened(theFwdState->server_fd, closer);
382
465
 
383
466
    if (request->method == METHOD_PUT)
384
467
        flags.put = 1;
386
469
 
387
470
FtpStateData::~FtpStateData()
388
471
{
389
 
    debugs(9, 3, "~ftpStateData: " << entry->url()  );
 
472
    debugs(9, 3, HERE << entry->url()  );
390
473
 
391
474
    if (reply_hdr) {
392
475
        memFree(reply_hdr, MEM_8K_BUF);
393
476
        reply_hdr = NULL;
394
477
    }
395
478
 
396
 
    if (data.fd > -1) {
397
 
        int fd = data.fd;
398
 
        data.fd = -1;
399
 
        comm_close(fd);
 
479
    data.close();
 
480
 
 
481
    if (ctrl.fd >= 0) {
 
482
        debugs(9, DBG_IMPORTANT, HERE << "Internal bug: FtpStateData left " <<
 
483
               "control FD " << ctrl.fd << " open");
400
484
    }
401
485
 
402
486
    if (ctrl.buf) {
406
490
 
407
491
    if (data.readBuf) {
408
492
        if (!data.readBuf->isNull())
409
 
           data.readBuf->clean();
 
493
            data.readBuf->clean();
410
494
 
411
495
        delete data.readBuf;
412
496
    }
443
527
    fwd = NULL; // refcounted
444
528
}
445
529
 
 
530
/**
 
531
 * Parse a possible login username:password pair.
 
532
 * Produces filled member varisbles user, password, password_url if anything found.
 
533
 */
446
534
void
447
535
FtpStateData::loginParser(const char *login, int escaped)
448
536
{
449
537
    char *s = NULL;
450
 
    xstrncpy(user, login, MAX_URL);
451
 
 
452
 
    if ((s = strchr(user, ':'))) {
453
 
        *s = 0;
454
 
        xstrncpy(password, s + 1, MAX_URL);
455
 
 
456
 
        if (escaped) {
457
 
            rfc1738_unescape(password);
458
 
            password_url = 1;
459
 
        }
460
 
    } else {
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);
 
540
 
 
541
    if ((s = strchr(login, ':'))) {
 
542
        *s = '\0';
 
543
 
 
544
        /* if there was a username part */
 
545
        if (s > login) {
 
546
            xstrncpy(user, login, MAX_URL);
 
547
            if (escaped)
 
548
                rfc1738_unescape(user);
 
549
        }
 
550
 
 
551
        /* if there was a password part */
 
552
        if ( s[1] != '\0' ) {
 
553
            xstrncpy(password, s + 1, MAX_URL);
 
554
            if (escaped) {
 
555
                rfc1738_unescape(password);
 
556
                password_url = 1;
 
557
            }
 
558
        }
 
559
    } else if (login[0]) {
 
560
        /* no password, just username */
 
561
        xstrncpy(user, login, MAX_URL);
 
562
        if (escaped)
 
563
            rfc1738_unescape(user);
462
564
    }
463
565
 
464
 
    if (escaped)
465
 
        rfc1738_unescape(user);
466
 
 
467
 
    if (!user[0])
468
 
        xstrncpy(user, "anonymous", MAX_URL);
469
 
 
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);
472
567
}
473
568
 
474
569
void
475
 
FtpStateData::ftpTimeout(int fd, void *data)
 
570
FtpStateData::ftpTimeout(const CommTimeoutCbParams &io)
476
571
{
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() << "'" );
480
573
 
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" );
486
579
    }
487
580
 
488
 
    ftpState->failed(ERR_READ_TIMEOUT, 0);
 
581
    failed(ERR_READ_TIMEOUT, 0);
489
582
    /* failed() closes ctrl.fd and frees ftpState */
490
583
}
491
584
 
492
585
void
493
586
FtpStateData::listingStart()
494
587
{
495
 
    debugs(9,3,HERE << "listingStart()");
 
588
    debugs(9,3, HERE);
496
589
    wordlist *w;
497
590
    char *dirup;
498
591
    int i, j, k;
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",
515
608
 
516
609
    if (flags.need_base_href)
517
610
        printfReplyBody("<BASE HREF=\"%s\">\n",
518
 
                        html_quote(base_href.buf()));
 
611
                        html_quote(base_href.termedBuf()));
519
612
 
520
613
    printfReplyBody("</HEAD><BODY>\n");
521
614
 
563
656
            /* Error guard, or "assert" */
564
657
            printfReplyBody("ERROR: Failed to parse URL: %s\n",
565
658
                            html_quote(title));
566
 
            debugs(9, 0, "Failed to parse URL: " << title);
 
659
            debugs(9, DBG_CRITICAL, "Failed to parse URL: " << title);
567
660
            break;
568
661
        }
569
662
    }
578
671
void
579
672
FtpStateData::listingFinish()
580
673
{
581
 
    debugs(9,3,HERE << "listingFinish()");
 
674
    debugs(9,3,HERE);
582
675
    entry->buffer();
583
676
    printfReplyBody("</PRE>\n");
584
677
 
599
692
    printfReplyBody("</ADDRESS></BODY></HTML>\n");
600
693
}
601
694
 
602
 
static const char *Month[] =
603
 
    {
604
 
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
605
 
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
606
 
    };
 
695
/// \ingroup ServerProtocolFTPInternal
 
696
static const char *Month[] = {
 
697
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
698
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
699
};
607
700
 
 
701
/// \ingroup ServerProtocolFTPInternal
608
702
static int
609
703
is_month(const char *buf)
610
704
{
617
711
    return 0;
618
712
}
619
713
 
620
 
 
 
714
/// \ingroup ServerProtocolFTPInternal
621
715
static void
622
716
ftpListPartsFree(ftpListParts ** parts)
623
717
{
628
722
    safe_free(*parts);
629
723
}
630
724
 
 
725
/// \ingroup ServerProtocolFTPInternal
631
726
#define MAX_TOKENS 64
632
727
 
 
728
/// \ingroup ServerProtocolFTPInternal
633
729
static ftpListParts *
634
 
 
635
730
ftpListParseParts(const char *buf, struct _ftp_flags flags)
636
731
{
637
732
    ftpListParts *p = NULL;
648
743
    static regex_t scan_ftp_dostime;
649
744
    static regex_t scan_ftp_dosdate;
650
745
 
651
 
    if (!scan_ftp_initialized)
652
 
    {
 
746
    if (!scan_ftp_initialized) {
653
747
        scan_ftp_initialized = 1;
654
748
        regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
655
749
        regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
671
765
 
672
766
    xbuf = xstrdup(buf);
673
767
 
674
 
    if (flags.tried_nlst)
675
 
    {
 
768
    if (flags.tried_nlst) {
676
769
        /* Machine readable format, one name per line */
677
770
        p->name = xbuf;
678
771
        p->type = '\0';
685
778
    xfree(xbuf);
686
779
 
687
780
    /* locate the Month field */
688
 
    for (i = 3; i < n_tokens - 2; i++)
689
 
    {
 
781
    for (i = 3; i < n_tokens - 2; i++) {
690
782
        char *size = tokens[i - 1];
691
783
        char *month = tokens[i];
692
784
        char *day = tokens[i + 1];
746
838
    /* try it as a DOS listing, 04-05-70 09:33PM ... */
747
839
    if (n_tokens > 3 &&
748
840
            regexec(&scan_ftp_dosdate, tokens[0], 0, NULL, 0) == 0 &&
749
 
            regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0)
750
 
    {
 
841
            regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0) {
751
842
        if (!strcasecmp(tokens[2], "<dir>")) {
752
843
            p->type = 'd';
753
844
        } else {
783
874
    }
784
875
 
785
876
    /* Try EPLF format; carson@lehman.com */
786
 
    if (buf[0] == '+')
787
 
    {
 
877
    if (buf[0] == '+') {
788
878
        ct = buf + 1;
789
879
        p->type = 0;
790
880
 
864
954
    return p;
865
955
}
866
956
 
 
957
/// \ingroup ServerProtocolFTPInternal
867
958
static const char *
868
959
dots_fill(size_t len)
869
960
{
944
1035
            if (flags.dir_slash) {
945
1036
                url = xstrdup("./");
946
1037
            } else {
947
 
                const char *title = title_url.buf();
 
1038
                const char *title = title_url.termedBuf();
948
1039
                int k = 6 + strcspn(&title[6], "/");
949
1040
                char *t;
950
1041
                url = xstrdup(title + k);
971
1062
        const char *p;
972
1063
        snprintf(html, 8192, "%s\n", line);
973
1064
 
974
 
        for (p = line; *p && xisspace(*p); p++)
975
 
 
976
 
            ;
 
1065
        for (p = line; *p && xisspace(*p); p++);
977
1066
        if (*p && !xisspace(*p))
978
1067
            flags.listformat_unknown = 1;
979
1068
 
1083
1172
FtpStateData::parseListing()
1084
1173
{
1085
1174
    char *buf = data.readBuf->content();
1086
 
    char *sbuf;                 /* NULL-terminated copy of buf */
 
1175
    char *sbuf;                 /* NULL-terminated copy of termedBuf */
1087
1176
    char *end;
1088
1177
    char *line;
1089
1178
    char *s;
1094
1183
    size_t len = data.readBuf->contentSize();
1095
1184
 
1096
1185
    if (!len) {
1097
 
        debugs(9, 3, "ftpParseListing: no content to parse for " << e->url()  );
 
1186
        debugs(9, 3, HERE << "no content to parse for " << e->url()  );
1098
1187
        return;
1099
1188
    }
1100
1189
 
1110
1199
 
1111
1200
    usable = end - sbuf;
1112
1201
 
1113
 
    debugs(9, 3, "ftpParseListing: usable = " << usable);
 
1202
    debugs(9, 3, HERE << "usable = " << usable);
1114
1203
 
1115
1204
    if (usable == 0) {
1116
 
        debugs(9, 3, "ftpParseListing: didn't find end for " << e->url()  );
 
1205
        debugs(9, 3, HERE << "didn't find end for " << e->url()  );
1117
1206
        xfree(sbuf);
1118
1207
        return;
1119
1208
    }
1120
1209
 
1121
 
    debugs(9, 3, "ftpParseListing: " << (unsigned long int)len << " bytes to play with");
 
1210
    debugs(9, 3, HERE << (unsigned long int)len << " bytes to play with");
1122
1211
 
1123
1212
    line = (char *)memAllocate(MEM_4K_BUF);
1124
1213
    end++;
1127
1216
    s += strspn(s, crlf);
1128
1217
 
1129
1218
    for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
1130
 
        debugs(9, 3, "ftpParseListing: s = {" << s << "}");
 
1219
        debugs(9, 7, HERE << "s = {" << s << "}");
1131
1220
        linelen = strcspn(s, crlf) + 1;
1132
1221
 
1133
1222
        if (linelen < 2)
1138
1227
 
1139
1228
        xstrncpy(line, s, linelen);
1140
1229
 
1141
 
        debugs(9, 7, "ftpParseListing: {" << line << "}");
 
1230
        debugs(9, 7, HERE << "{" << line << "}");
1142
1231
 
1143
1232
        if (!strncmp(line, "total", 5))
1144
1233
            continue;
1156
1245
}
1157
1246
 
1158
1247
int
1159
 
FtpStateData::dataDescriptor() const {
 
1248
FtpStateData::dataDescriptor() const
 
1249
{
1160
1250
    return data.fd;
1161
1251
}
1162
1252
 
1163
1253
void
1164
1254
FtpStateData::dataComplete()
1165
1255
{
1166
 
    debugs(9, 3, "ftpDataComplete");
 
1256
    debugs(9, 3,HERE);
 
1257
 
1167
1258
    /* Connection closed; transfer done. */
1168
1259
 
1169
 
    if (data.fd > -1) {
1170
 
        /*
1171
 
         * close data socket so it does not occupy resources while
1172
 
         * we wait
1173
 
         */
1174
 
        comm_close(data.fd);
1175
 
        data.fd = -1;
1176
 
    }
 
1260
    /// Close data channel, if any, to conserve resources while we wait.
 
1261
    data.close();
1177
1262
 
1178
1263
    /* expect the "transfer complete" message on the control socket */
1179
1264
    /*
1188
1273
}
1189
1274
 
1190
1275
void
1191
 
FtpStateData::dataReadWrapper(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno, void *data)
1192
 
{
1193
 
    FtpStateData *ftpState = (FtpStateData *)data;
1194
 
    ftpState->data.read_pending = false;
1195
 
    ftpState->dataRead(fd, buf, len, errflag, xerrno);
1196
 
}
1197
 
 
1198
 
void
1199
1276
FtpStateData::maybeReadVirginBody()
1200
1277
{
1201
1278
    if (data.fd < 0)
1213
1290
 
1214
1291
    data.read_pending = true;
1215
1292
 
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);
1217
1297
 
1218
1298
    debugs(9,5,HERE << "queueing read on FD " << data.fd);
1219
1299
 
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)));
1221
1304
}
1222
1305
 
1223
1306
void
1224
 
FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno)
 
1307
FtpStateData::dataRead(const CommIoCbParams &io)
1225
1308
{
1226
1309
    int j;
1227
1310
    int bin;
1228
1311
 
1229
 
    debugs(9, 3, HERE << "ftpDataRead: FD " << fd << " Read " << len << " bytes");
1230
 
 
1231
 
    if (len > 0) {
1232
 
        kb_incr(&statCounter.server.all.kbytes_in, len);
1233
 
        kb_incr(&statCounter.server.ftp.kbytes_in, len);
 
1312
    data.read_pending = false;
 
1313
 
 
1314
    debugs(9, 3, HERE << "ftpDataRead: FD " << io.fd << " Read " << io.size << " bytes");
 
1315
 
 
1316
    if (io.size > 0) {
 
1317
        kb_incr(&statCounter.server.all.kbytes_in, io.size);
 
1318
        kb_incr(&statCounter.server.ftp.kbytes_in, io.size);
1234
1319
    }
1235
1320
 
1236
 
    if (errflag == COMM_ERR_CLOSING)
 
1321
    if (io.flag == COMM_ERR_CLOSING)
1237
1322
        return;
1238
1323
 
1239
 
    assert(fd == data.fd);
 
1324
    assert(io.fd == data.fd);
1240
1325
 
1241
1326
    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1242
1327
        abortTransaction("entry aborted during dataRead");
1243
1328
        return;
1244
1329
    }
1245
1330
 
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);
1252
1337
#endif
1253
1338
        IOStats.Ftp.reads++;
1254
1339
 
1255
 
        for (j = len - 1, bin = 0; j; bin++)
 
1340
        for (j = io.size - 1, bin = 0; j; bin++)
1256
1341
            j >>= 1;
1257
1342
 
1258
1343
        IOStats.Ftp.read_hist[bin]++;
1259
1344
    }
1260
1345
 
1261
 
    if (errflag != COMM_OK || len < 0) {
1262
 
         debugs(50, ignoreErrno(xerrno) ? 3 : 1, "ftpDataRead: read error: " << xstrerr(xerrno));
1263
 
 
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));
 
1349
 
 
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);
 
1355
 
1266
1356
            maybeReadVirginBody();
1267
1357
        } else {
1268
1358
            if (!flags.http_header_sent && !fwd->ftpPasvFailed() && flags.pasv_supported) {
1274
1364
            /* failed closes ctrl.fd and frees ftpState */
1275
1365
            return;
1276
1366
        }
1277
 
    } else if (len == 0) {
1278
 
        debugs(9,5,HERE << "Calling dataComplete() because len == 0");
1279
 
        /*
1280
 
         * DPW 2007-04-23
1281
 
         * Dangerous curves ahead.  This call to dataComplete was
1282
 
         * calling scheduleReadControlReply, handleControlReply,
1283
 
         * and then ftpReadTransferDone.  If ftpReadTransferDone
1284
 
         * gets unexpected status code, it closes down the control
1285
 
         * socket and our FtpStateData object gets destroyed.   As
1286
 
         * a workaround we no longer set the 'buffered_ok' flag in
1287
 
         * the scheduleReadControlReply call.
1288
 
         */
 
1367
    } else if (io.size == 0) {
 
1368
        debugs(9,3, HERE << "Calling dataComplete() because io.size == 0");
 
1369
        /*
 
1370
         * DPW 2007-04-23
 
1371
         * Dangerous curves ahead.  This call to dataComplete was
 
1372
         * calling scheduleReadControlReply, handleControlReply,
 
1373
         * and then ftpReadTransferDone.  If ftpReadTransferDone
 
1374
         * gets unexpected status code, it closes down the control
 
1375
         * socket and our FtpStateData object gets destroyed.   As
 
1376
         * a workaround we no longer set the 'buffered_ok' flag in
 
1377
         * the scheduleReadControlReply call.
 
1378
         */
1289
1379
        dataComplete();
1290
1380
    }
1291
1381
 
1295
1385
void
1296
1386
FtpStateData::processReplyBody()
1297
1387
{
1298
 
    debugs(9, 5, HERE << "FtpStateData::processReplyBody starting.");
 
1388
    debugs(9, 3, HERE << "FtpStateData::processReplyBody starting.");
1299
1389
 
1300
1390
    if (request->method == METHOD_HEAD && (flags.isdir || theSize != -1)) {
1301
1391
        serverComplete();
1306
1396
        appendSuccessHeader();
1307
1397
 
1308
1398
    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1309
 
        /*
1310
 
         * probably was aborted because content length exceeds one
1311
 
         * of the maximum size limits.
1312
 
         */
 
1399
        /*
 
1400
         * probably was aborted because content length exceeds one
 
1401
         * of the maximum size limits.
 
1402
         */
1313
1403
        abortTransaction("entry aborted after calling appendSuccessHeader()");
1314
1404
        return;
1315
1405
    }
1316
1406
 
1317
 
#if ICAP_CLIENT
 
1407
#if USE_ADAPTATION
1318
1408
 
1319
 
    if (icapAccessCheckPending) {
1320
 
        debugs(9,3,HERE << "returning from FtpStateData::processReplyBody due to icapAccessCheckPending");
 
1409
    if (adaptationAccessCheckPending) {
 
1410
        debugs(9,3, HERE << "returning from FtpStateData::processReplyBody due to adaptationAccessCheckPending");
1321
1411
        return;
1322
1412
    }
1323
1413
 
1328
1418
 
1329
1419
    if (flags.isdir) {
1330
1420
        parseListing();
1331
 
    } else 
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);
1336
 
    }
 
1421
    } else
 
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);
 
1426
        }
1337
1427
 
1338
1428
    entry->flush();
1339
1429
 
1340
1430
    maybeReadVirginBody();
1341
1431
}
1342
1432
 
1343
 
/*
1344
 
 * ftpCheckAuth
1345
 
 *
1346
 
 * Return 1 if we have everything needed to complete this request.
1347
 
 * Return 0 if something is missing.
 
1433
/**
 
1434
 * Locates the FTP user:password login.
 
1435
 *
 
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@).
 
1440
 *
 
1441
 * Special Case: A username-only may be provided in the URL and password in the HTTP headers.
 
1442
 *
 
1443
 * TODO: we might be able to do something about locating username from other sources:
 
1444
 *       ie, external ACL user=* tag or ident lookup
 
1445
 *
 
1446
 \retval 1      if we have everything needed to complete this request.
 
1447
 \retval 0      if something is missing.
1348
1448
 */
1349
1449
int
1350
1450
FtpStateData::checkAuth(const HttpHeader * req_hdr)
1351
1451
{
1352
 
    char *orig_user;
1353
1452
    const char *auth;
 
1453
 
 
1454
    /* default username */
 
1455
    xstrncpy(user, "anonymous", MAX_URL);
 
1456
 
 
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);
 
1461
    }
 
1462
    /* we fail with authorization-required error later IFF the FTP server requests it */
 
1463
 
 
1464
    /* Test URL login syntax. Overrides any headers received. */
1354
1465
    loginParser(request->login, FTP_LOGIN_ESCAPED);
1355
1466
 
 
1467
    /* name is missing. thats fatal. */
1356
1468
    if (!user[0])
1357
 
        return 1;               /* no name */
1358
 
 
1359
 
    if (password_url || password[0])
1360
 
        return 1;               /* passwd provided in URL */
1361
 
 
1362
 
    /* URL has name, but no passwd */
1363
 
    if (!(auth = req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")))
1364
 
        return 0;               /* need auth header */
1365
 
 
1366
 
    flags.authenticated = 1;
1367
 
 
1368
 
    orig_user = xstrdup(user);
1369
 
 
1370
 
    loginParser(auth, FTP_LOGIN_NOT_ESCAPED);
1371
 
 
1372
 
    if (strcmp(orig_user, user) == 0) {
1373
 
        xfree(orig_user);
1374
 
        return 1;               /* same username */
 
1469
        fatal("FTP login parsing destroyed username info");
 
1470
 
 
1471
    /* name + password == success */
 
1472
    if (password[0])
 
1473
        return 1;
 
1474
 
 
1475
    /* Setup default FTP password settings */
 
1476
    /* this has to be done last so that we can have a no-password case above. */
 
1477
    if (!password[0]) {
 
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;
 
1481
            return 1;
 
1482
        }
 
1483
        else if (!flags.tried_auth_nopass) {
 
1484
            xstrncpy(password, null_string, MAX_URL);
 
1485
            flags.tried_auth_nopass=1;
 
1486
            return 1;
 
1487
        }
1375
1488
    }
1376
1489
 
1377
 
    xstrncpy(user, orig_user, sizeof(user));
1378
 
    xfree(orig_user);
1379
1490
    return 0;                   /* different username */
1380
1491
}
1381
1492
 
 
1493
static String str_type_eq;
1382
1494
void
1383
1495
FtpStateData::checkUrlpath()
1384
1496
{
1385
1497
    int l;
1386
 
    const char *t;
1387
 
 
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);
 
1498
    size_t t;
 
1499
 
 
1500
    if (str_type_eq.undefined()) //hack. String doesn't support global-static
 
1501
        str_type_eq="type=";
 
1502
 
 
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);
1392
1507
        }
1393
1508
    }
1394
1509
 
1403
1518
        /* UNIX root directory */
1404
1519
        flags.isdir = 1;
1405
1520
        flags.root_dir = 1;
1406
 
    } else if ((l >= 1) && (*(request->urlpath.buf() + l - 1) == '/')) {
 
1521
    } else if ((l >= 1) && (request->urlpath[l - 1] == '/')) {
1407
1522
        /* Directory URL, ending in / */
1408
1523
        flags.isdir = 1;
1409
1524
 
1424
1539
        title_url.append("@");
1425
1540
    }
1426
1541
 
1427
 
    title_url.append(request->host);
 
1542
    title_url.append(request->GetHost());
1428
1543
 
1429
1544
    if (request->port != urlDefaultPort(PROTO_FTP)) {
1430
1545
        title_url.append(":");
1446
1561
        base_href.append("@");
1447
1562
    }
1448
1563
 
1449
 
    base_href.append(request->host);
 
1564
    base_href.append(request->GetHost());
1450
1565
 
1451
1566
    if (request->port != urlDefaultPort(PROTO_FTP)) {
1452
1567
        base_href.append(":");
1457
1572
    base_href.append("/");
1458
1573
}
1459
1574
 
 
1575
/// \ingroup ServerProtocolFTPAPI
1460
1576
void
1461
1577
ftpStart(FwdState * fwd)
1462
1578
{
1468
1584
FtpStateData::start()
1469
1585
{
1470
1586
    if (!checkAuth(&request->header)) {
1471
 
        static char realm[8192];
1472
 
        /* This request is not fully authenticated */
1473
 
 
1474
 
        if (request->port == 21) {
1475
 
            snprintf(realm, 8192, "ftp %s", user);
1476
 
        } else {
1477
 
            snprintf(realm, 8192, "ftp %s port %d",
1478
 
                     user, request->port);
1479
 
        }
1480
 
 
1481
1587
        /* create appropriate reply */
1482
 
        HttpReply *reply = ftpAuthRequired(request, realm);
1483
 
 
 
1588
        HttpReply *reply = ftpAuthRequired(request, ftpRealm());
1484
1589
        entry->replaceHttpReply(reply);
1485
 
 
1486
1590
        serverComplete();
1487
 
 
1488
1591
        return;
1489
1592
    }
1490
1593
 
1491
1594
    checkUrlpath();
1492
1595
    buildTitleUrl();
1493
 
    debugs(9, 5, "ftpStart: host=" << request->host << ", path=" <<
1494
 
           request->urlpath.buf() << ", user=" << user << ", passwd=" <<
 
1596
    debugs(9, 5, HERE << "host=" << request->GetHost() << ", path=" <<
 
1597
           request->urlpath << ", user=" << user << ", passwd=" <<
1495
1598
           password);
1496
1599
 
1497
1600
    state = BEGIN;
1505
1608
 
1506
1609
/* ====================================================================== */
1507
1610
 
 
1611
/// \ingroup ServerProtocolFTPInternal
1508
1612
static char *
1509
1613
escapeIAC(const char *buf)
1510
1614
{
1535
1639
FtpStateData::writeCommand(const char *buf)
1536
1640
{
1537
1641
    char *ebuf;
1538
 
    debugs(9, 5, "ftpWriteCommand: " << buf);
 
1642
    /* trace FTP protocol communications at level 2 */
 
1643
    debugs(9, 2, "ftp<< " << buf);
1539
1644
 
1540
1645
    if (Config.Ftp.telnet)
1541
1646
        ebuf = escapeIAC(buf);
1548
1653
 
1549
1654
    ctrl.last_command = ebuf;
1550
1655
 
 
1656
    typedef CommCbMemFunT<FtpStateData, CommIoCbParams> Dialer;
 
1657
    AsyncCall::Pointer call = asyncCall(9, 5, "FtpStateData::ftpWriteCommandCallback",
 
1658
                                        Dialer(this, &FtpStateData::ftpWriteCommandCallback));
1551
1659
    comm_write(ctrl.fd,
1552
1660
               ctrl.last_command,
1553
1661
               strlen(ctrl.last_command),
1554
 
               FtpStateData::ftpWriteCommandCallback,
1555
 
               this, NULL);
 
1662
               call);
1556
1663
 
1557
1664
    scheduleReadControlReply(0);
1558
1665
}
1559
1666
 
1560
1667
void
1561
 
FtpStateData::ftpWriteCommandCallback(int fd, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
 
1668
FtpStateData::ftpWriteCommandCallback(const CommIoCbParams &io)
1562
1669
{
1563
 
    FtpStateData *ftpState = (FtpStateData *)data;
1564
 
 
1565
 
    debugs(9, 7, "ftpWriteCommandCallback: wrote " << size << " bytes");
1566
 
 
1567
 
    if (size > 0) {
1568
 
        fd_bytes(fd, size, FD_WRITE);
1569
 
        kb_incr(&statCounter.server.all.kbytes_out, size);
1570
 
        kb_incr(&statCounter.server.ftp.kbytes_out, size);
 
1670
 
 
1671
    debugs(9, 5, "ftpWriteCommandCallback: wrote " << io.size << " bytes");
 
1672
 
 
1673
    if (io.size > 0) {
 
1674
        fd_bytes(io.fd, io.size, FD_WRITE);
 
1675
        kb_incr(&statCounter.server.all.kbytes_out, io.size);
 
1676
        kb_incr(&statCounter.server.ftp.kbytes_out, io.size);
1571
1677
    }
1572
1678
 
1573
 
    if (errflag == COMM_ERR_CLOSING)
 
1679
    if (io.flag == COMM_ERR_CLOSING)
1574
1680
        return;
1575
1681
 
1576
 
    if (errflag) {
1577
 
        debugs(9, 1, "ftpWriteCommandCallback: FD " << fd << ": " << xstrerr(xerrno));
1578
 
        ftpState->failed(ERR_WRITE_ERROR, xerrno);
 
1682
    if (io.flag) {
 
1683
        debugs(9, DBG_IMPORTANT, "ftpWriteCommandCallback: FD " << io.fd << ": " << xstrerr(io.xerrno));
 
1684
        failed(ERR_WRITE_ERROR, io.xerrno);
1579
1685
        /* failed closes ctrl.fd and frees ftpState */
1580
1686
        return;
1581
1687
    }
1595
1701
    size_t offset;
1596
1702
    size_t linelen;
1597
1703
    int code = -1;
1598
 
    debugs(9, 5, "ftpParseControlReply");
 
1704
    debugs(9, 3, HERE);
1599
1705
    /*
1600
1706
     * We need a NULL-terminated buffer for scanning, ick
1601
1707
     */
1608
1714
 
1609
1715
    usable = end - sbuf;
1610
1716
 
1611
 
    debugs(9, 3, "ftpParseControlReply: usable = " << usable);
 
1717
    debugs(9, 3, HERE << "usable = " << usable);
1612
1718
 
1613
1719
    if (usable == 0) {
1614
 
        debugs(9, 3, "ftpParseControlReply: didn't find end of line");
 
1720
        debugs(9, 3, HERE << "didn't find end of line");
1615
1721
        safe_free(sbuf);
1616
1722
        return NULL;
1617
1723
    }
1618
1724
 
1619
 
    debugs(9, 3, "ftpParseControlReply: " << len << " bytes to play with");
 
1725
    debugs(9, 3, HERE << len << " bytes to play with");
1620
1726
    end++;
1621
1727
    s = sbuf;
1622
1728
    s += strspn(s, crlf);
1625
1731
        if (complete)
1626
1732
            break;
1627
1733
 
1628
 
        debugs(9, 3, "ftpParseControlReply: s = {" << s << "}");
 
1734
        debugs(9, 5, HERE << "s = {" << s << "}");
1629
1735
 
1630
1736
        linelen = strcspn(s, crlf) + 1;
1631
1737
 
1650
1756
 
1651
1757
        xstrncpy(list->key, s + offset, linelen - offset);
1652
1758
 
1653
 
        debugs(9, 7, "" << code << " " << list->key);
 
1759
        /* trace the FTP communication chat at level 2 */
 
1760
        debugs(9, 2, "ftp>> " << code << " " << list->key);
1654
1761
 
1655
1762
        *tail = list;
1656
1763
 
1669
1776
    return head;
1670
1777
}
1671
1778
 
1672
 
/*
 
1779
/**
1673
1780
 * DPW 2007-04-23
1674
1781
 * Looks like there are no longer anymore callers that set
1675
1782
 * buffered_ok=1.  Perhaps it can be removed at some point.
1677
1784
void
1678
1785
FtpStateData::scheduleReadControlReply(int buffered_ok)
1679
1786
{
1680
 
    debugs(9, 3, "scheduleReadControlReply: FD " << ctrl.fd);
 
1787
    debugs(9, 3, HERE << "FD " << ctrl.fd);
1681
1788
 
1682
1789
    if (buffered_ok && ctrl.offset > 0) {
1683
1790
        /* We've already read some reply data */
1684
1791
        handleControlReply();
1685
1792
    } else {
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);
1688
1798
        /*
1689
1799
         * Cancel the timeout on the Data socket (if any) and
1690
1800
         * establish one on the control socket.
1691
1801
         */
1692
1802
 
1693
 
        if (data.fd > -1)
1694
 
            commSetTimeout(data.fd, -1, NULL, NULL);
1695
 
 
1696
 
        commSetTimeout(ctrl.fd, Config.Timeout.read, ftpTimeout,
1697
 
                       this);
 
1803
        if (data.fd > -1) {
 
1804
            AsyncCall::Pointer nullCall =  NULL;
 
1805
            commSetTimeout(data.fd, -1, nullCall);
 
1806
        }
 
1807
 
 
1808
        typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
 
1809
        AsyncCall::Pointer timeoutCall =  asyncCall(9, 5, "FtpStateData::ftpTimeout",
 
1810
                                          TimeoutDialer(this,&FtpStateData::ftpTimeout));
 
1811
 
 
1812
        commSetTimeout(ctrl.fd, Config.Timeout.read, timeoutCall);
1698
1813
    }
1699
1814
}
1700
1815
 
1701
 
void
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)
1703
1817
{
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");
1707
1819
 
1708
 
    if (len > 0) {
1709
 
        kb_incr(&statCounter.server.all.kbytes_in, len);
1710
 
        kb_incr(&statCounter.server.ftp.kbytes_in, len);
 
1820
    if (io.size > 0) {
 
1821
        kb_incr(&statCounter.server.all.kbytes_in, io.size);
 
1822
        kb_incr(&statCounter.server.ftp.kbytes_in, io.size);
1711
1823
    }
1712
1824
 
1713
 
    if (errflag == COMM_ERR_CLOSING)
 
1825
    if (io.flag == COMM_ERR_CLOSING)
1714
1826
        return;
1715
1827
 
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");
1718
1830
        return;
1719
1831
    }
1720
1832
 
1721
 
    assert(ftpState->ctrl.offset < ftpState->ctrl.size);
 
1833
    assert(ctrl.offset < ctrl.size);
1722
1834
 
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);
1725
1837
    }
1726
1838
 
1727
 
 
1728
 
    if (errflag != COMM_OK || len < 0) {
1729
 
         debugs(50, ignoreErrno(xerrno) ? 3 : 1, "ftpReadControlReply: read error: " << xstrerr(xerrno));
1730
 
 
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));
 
1842
 
 
1843
        if (ignoreErrno(io.xerrno)) {
 
1844
            scheduleReadControlReply(0);
1733
1845
        } else {
1734
 
            ftpState->failed(ERR_READ_ERROR, xerrno);
 
1846
            failed(ERR_READ_ERROR, io.xerrno);
1735
1847
            /* failed closes ctrl.fd and frees ftpState */
1736
1848
            return;
1737
1849
        }
1739
1851
        return;
1740
1852
    }
1741
1853
 
1742
 
    if (len == 0) {
 
1854
    if (io.size == 0) {
1743
1855
        if (entry->store_status == STORE_PENDING) {
1744
 
            ftpState->failed(ERR_FTP_FAILURE, 0);
 
1856
            failed(ERR_FTP_FAILURE, 0);
1745
1857
            /* failed closes ctrl.fd and frees ftpState */
1746
1858
            return;
1747
1859
        }
1748
1860
 
1749
 
    /* XXX this may end up having to be serverComplete() .. */
1750
 
        ftpState->abortTransaction("zero control reply read");
 
1861
        /* XXX this may end up having to be serverComplete() .. */
 
1862
        abortTransaction("zero control reply read");
1751
1863
        return;
1752
1864
    }
1753
1865
 
1754
 
    len += ftpState->ctrl.offset;
1755
 
    ftpState->ctrl.offset = len;
1756
 
    assert(len <= ftpState->ctrl.size);
1757
 
    ftpState->handleControlReply();
 
1866
    unsigned int len =io.size + ctrl.offset;
 
1867
    ctrl.offset = len;
 
1868
    assert(len <= ctrl.size);
 
1869
    handleControlReply();
1758
1870
}
1759
1871
 
1760
1872
void
1787
1899
    }
1788
1900
 
1789
1901
    /* Move the last line of the reply message to ctrl.last_reply */
1790
 
    for (W = &ctrl.message; (*W)->next; W = &(*W)->next)
1791
 
 
1792
 
        ;
 
1902
    for (W = &ctrl.message; (*W)->next; W = &(*W)->next);
1793
1903
    safe_free(ctrl.last_reply);
1794
1904
 
1795
1905
    ctrl.last_reply = xstrdup((*W)->key);
1801
1911
     */
1802
1912
    wordlistAddWl(&cwd_message, ctrl.message);
1803
1913
 
1804
 
    debugs(9, 8, "handleControlReply: state=" << state << ", code=" << ctrl.replycode);
 
1914
    debugs(9, 3, HERE << "state=" << state << ", code=" << ctrl.replycode);
1805
1915
 
1806
1916
    FTP_SM_FUNCS[state] (this);
1807
1917
}
1808
1918
 
1809
1919
/* ====================================================================== */
1810
1920
 
 
1921
/// \ingroup ServerProtocolFTPInternal
1811
1922
static void
1812
1923
ftpReadWelcome(FtpStateData * ftpState)
1813
1924
{
1814
1925
    int code = ftpState->ctrl.replycode;
1815
 
    debugs(9, 3, "ftpReadWelcome");
 
1926
    debugs(9, 3, HERE);
1816
1927
 
1817
1928
    if (ftpState->flags.pasv_only)
1818
1929
        ftpState->login_att++;
1829
1940
        ftpSendUser(ftpState);
1830
1941
    } else if (code == 120) {
1831
1942
        if (NULL != ftpState->ctrl.message)
1832
 
            debugs(9, 3, "FTP server is busy: " << ftpState->ctrl.message->key);
 
1943
            debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ftpState->ctrl.message->key);
1833
1944
 
1834
1945
        return;
1835
1946
    } else {
1837
1948
    }
1838
1949
}
1839
1950
 
 
1951
/**
 
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.
 
1955
 */
 
1956
void
 
1957
FtpStateData::loginFailed()
 
1958
{
 
1959
    ErrorState *err = NULL;
 
1960
    const char *command, *reply;
 
1961
 
 
1962
    if (state == SENT_USER || state == SENT_PASS) {
 
1963
        if (ctrl.replycode > 500) {
 
1964
            if (password_url)
 
1965
                err = errorCon(ERR_FTP_FORBIDDEN, HTTP_FORBIDDEN, fwd->request);
 
1966
            else
 
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);
 
1970
        }
 
1971
    } else {
 
1972
        ftpFail(this);
 
1973
        return;
 
1974
    }
 
1975
 
 
1976
    err->ftp.server_msg = ctrl.message;
 
1977
 
 
1978
    ctrl.message = NULL;
 
1979
 
 
1980
    if (old_request)
 
1981
        command = old_request;
 
1982
    else
 
1983
        command = ctrl.last_command;
 
1984
 
 
1985
    if (command && strncmp(command, "PASS", 4) == 0)
 
1986
        command = "PASS <yourpassword>";
 
1987
 
 
1988
    if (old_reply)
 
1989
        reply = old_reply;
 
1990
    else
 
1991
        reply = ctrl.last_reply;
 
1992
 
 
1993
    if (command)
 
1994
        err->ftp.request = xstrdup(command);
 
1995
 
 
1996
    if (reply)
 
1997
        err->ftp.reply = xstrdup(reply);
 
1998
 
 
1999
 
 
2000
    HttpReply *newrep = err->BuildHttpReply();
 
2001
    errorStateFree(err);
 
2002
    /* add Authenticate header */
 
2003
    newrep->header.putAuth("Basic", ftpRealm());
 
2004
 
 
2005
    // add it to the store entry for response....
 
2006
    entry->replaceHttpReply(newrep);
 
2007
    serverComplete();
 
2008
}
 
2009
 
 
2010
const char *
 
2011
FtpStateData::ftpRealm()
 
2012
{
 
2013
    static char realm[8192];
 
2014
 
 
2015
    /* This request is not fully authenticated */
 
2016
    if (!request) {
 
2017
        snprintf(realm, 8192, "FTP %s unknown", user);
 
2018
    } else if (request->port == 21) {
 
2019
        snprintf(realm, 8192, "FTP %s %s", user, request->GetHost());
 
2020
    } else {
 
2021
        snprintf(realm, 8192, "FTP %s %s port %d", user, request->GetHost(), request->port);
 
2022
    }
 
2023
    return realm;
 
2024
}
 
2025
 
 
2026
/// \ingroup ServerProtocolFTPInternal
1840
2027
static void
1841
2028
ftpSendUser(FtpStateData * ftpState)
1842
2029
{
1843
2030
    /* check the server control channel is still available */
1844
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
 
2031
    if (!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
1845
2032
        return;
1846
2033
 
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());
1851
2038
    else
1852
2039
        snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user);
1853
2040
 
1856
2043
    ftpState->state = SENT_USER;
1857
2044
}
1858
2045
 
 
2046
/// \ingroup ServerProtocolFTPInternal
1859
2047
static void
1860
2048
ftpReadUser(FtpStateData * ftpState)
1861
2049
{
1862
2050
    int code = ftpState->ctrl.replycode;
1863
 
    debugs(9, 3, "ftpReadUser");
 
2051
    debugs(9, 3, HERE);
1864
2052
 
1865
2053
    if (code == 230) {
1866
2054
        ftpReadPass(ftpState);
1867
2055
    } else if (code == 331) {
1868
2056
        ftpSendPass(ftpState);
1869
2057
    } else {
1870
 
        ftpFail(ftpState);
 
2058
        ftpState->loginFailed();
1871
2059
    }
1872
2060
}
1873
2061
 
 
2062
/// \ingroup ServerProtocolFTPInternal
1874
2063
static void
1875
2064
ftpSendPass(FtpStateData * ftpState)
1876
2065
{
1877
2066
    /* check the server control channel is still available */
1878
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendPass"))
 
2067
    if (!ftpState || !ftpState->haveControlChannel("ftpSendPass"))
1879
2068
        return;
1880
2069
 
1881
2070
    snprintf(cbuf, 1024, "PASS %s\r\n", ftpState->password);
1883
2072
    ftpState->state = SENT_PASS;
1884
2073
}
1885
2074
 
 
2075
/// \ingroup ServerProtocolFTPInternal
1886
2076
static void
1887
2077
ftpReadPass(FtpStateData * ftpState)
1888
2078
{
1889
2079
    int code = ftpState->ctrl.replycode;
1890
 
    debugs(9, 3, "ftpReadPass");
 
2080
    debugs(9, 3, HERE << "code=" << code);
1891
2081
 
1892
2082
    if (code == 230) {
1893
2083
        ftpSendType(ftpState);
1894
2084
    } else {
1895
 
        ftpFail(ftpState);
 
2085
        ftpState->loginFailed();
1896
2086
    }
1897
2087
}
1898
2088
 
 
2089
/// \ingroup ServerProtocolFTPInternal
1899
2090
static void
1900
2091
ftpSendType(FtpStateData * ftpState)
1901
2092
{
1904
2095
    char mode;
1905
2096
 
1906
2097
    /* check the server control channel is still available */
1907
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendType"))
 
2098
    if (!ftpState || !ftpState->haveControlChannel("ftpSendType"))
1908
2099
        return;
1909
2100
 
1910
2101
    /*
1929
2120
            mode = 'A';
1930
2121
        } else {
1931
2122
            t = ftpState->request->urlpath.rpos('/');
1932
 
            filename = t ? t + 1 : ftpState->request->urlpath.buf();
 
2123
            filename = t ? t + 1 : ftpState->request->urlpath.termedBuf();
1933
2124
            mode = mimeGetTransferMode(filename);
1934
2125
        }
1935
2126
 
1948
2139
    ftpState->state = SENT_TYPE;
1949
2140
}
1950
2141
 
 
2142
/// \ingroup ServerProtocolFTPInternal
1951
2143
static void
1952
2144
ftpReadType(FtpStateData * ftpState)
1953
2145
{
1954
2146
    int code = ftpState->ctrl.replycode;
1955
2147
    char *path;
1956
2148
    char *d, *p;
1957
 
    debugs(9, 3, "This is ftpReadType");
 
2149
    debugs(9, 3, HERE);
1958
2150
 
1959
2151
    if (code == 200) {
1960
 
        p = path = xstrdup(ftpState->request->urlpath.buf());
 
2152
        p = path = xstrdup(ftpState->request->urlpath.termedBuf());
1961
2153
 
1962
2154
        if (*p == '/')
1963
2155
            p++;
1986
2178
    }
1987
2179
}
1988
2180
 
 
2181
/// \ingroup ServerProtocolFTPInternal
1989
2182
static void
1990
2183
ftpTraverseDirectory(FtpStateData * ftpState)
1991
2184
{
1992
2185
    wordlist *w;
1993
 
    debugs(9, 4, "ftpTraverseDirectory " << (ftpState->filepath ? ftpState->filepath : "<NULL>"));
 
2186
    debugs(9, 4, HERE << (ftpState->filepath ? ftpState->filepath : "<NULL>"));
1994
2187
 
1995
2188
    safe_free(ftpState->dirpath);
1996
2189
    ftpState->dirpath = ftpState->filepath;
1999
2192
    /* Done? */
2000
2193
 
2001
2194
    if (ftpState->pathcomps == NULL) {
2002
 
        debugs(9, 3, "the final component was a directory");
 
2195
        debugs(9, 3, HERE << "the final component was a directory");
2003
2196
        ftpListDir(ftpState);
2004
2197
        return;
2005
2198
    }
2017
2210
    if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
2018
2211
        ftpSendCwd(ftpState);
2019
2212
    } else {
2020
 
        debugs(9, 3, "final component is probably a file");
 
2213
        debugs(9, 3, HERE << "final component is probably a file");
2021
2214
        ftpGetFile(ftpState);
2022
2215
        return;
2023
2216
    }
2024
2217
}
2025
2218
 
 
2219
/// \ingroup ServerProtocolFTPInternal
2026
2220
static void
2027
2221
ftpSendCwd(FtpStateData * ftpState)
2028
2222
{
2029
2223
    char *path = NULL;
2030
2224
 
2031
2225
    /* check the server control channel is still available */
2032
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendCwd"))
 
2226
    if (!ftpState || !ftpState->haveControlChannel("ftpSendCwd"))
2033
2227
        return;
2034
2228
 
2035
 
    debugs(9, 3, "ftpSendCwd");
 
2229
    debugs(9, 3, HERE);
2036
2230
 
2037
2231
    path = ftpState->filepath;
2038
2232
 
2049
2243
    ftpState->state = SENT_CWD;
2050
2244
}
2051
2245
 
 
2246
/// \ingroup ServerProtocolFTPInternal
2052
2247
static void
2053
2248
ftpReadCwd(FtpStateData * ftpState)
2054
2249
{
2055
2250
    int code = ftpState->ctrl.replycode;
2056
 
    debugs(9, 3, "This is ftpReadCwd");
 
2251
    debugs(9, 3, HERE);
2057
2252
 
2058
2253
    if (code >= 200 && code < 300) {
2059
2254
        /* CWD OK */
2079
2274
    }
2080
2275
}
2081
2276
 
 
2277
/// \ingroup ServerProtocolFTPInternal
2082
2278
static void
2083
2279
ftpSendMkdir(FtpStateData * ftpState)
2084
2280
{
2085
2281
    char *path = NULL;
2086
2282
 
2087
2283
    /* check the server control channel is still available */
2088
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendMkdir"))
 
2284
    if (!ftpState || !ftpState->haveControlChannel("ftpSendMkdir"))
2089
2285
        return;
2090
2286
 
2091
2287
    path = ftpState->filepath;
2092
 
    debugs(9, 3, "ftpSendMkdir: with path=" << path);
 
2288
    debugs(9, 3, HERE << "with path=" << path);
2093
2289
    snprintf(cbuf, 1024, "MKD %s\r\n", path);
2094
2290
    ftpState->writeCommand(cbuf);
2095
2291
    ftpState->state = SENT_MKDIR;
2096
2292
}
2097
2293
 
 
2294
/// \ingroup ServerProtocolFTPInternal
2098
2295
static void
2099
2296
ftpReadMkdir(FtpStateData * ftpState)
2100
2297
{
2101
2298
    char *path = ftpState->filepath;
2102
2299
    int code = ftpState->ctrl.replycode;
2103
2300
 
2104
 
    debugs(9, 3, "ftpReadMkdir: path " << path << ", code " << code);
 
2301
    debugs(9, 3, HERE << "path " << path << ", code " << code);
2105
2302
 
2106
2303
    if (code == 257) {          /* success */
2107
2304
        ftpSendCwd(ftpState);
2116
2313
        ftpSendReply(ftpState);
2117
2314
}
2118
2315
 
 
2316
/// \ingroup ServerProtocolFTPInternal
2119
2317
static void
2120
2318
ftpGetFile(FtpStateData * ftpState)
2121
2319
{
2124
2322
    ftpSendMdtm(ftpState);
2125
2323
}
2126
2324
 
 
2325
/// \ingroup ServerProtocolFTPInternal
2127
2326
static void
2128
2327
ftpListDir(FtpStateData * ftpState)
2129
2328
{
2130
2329
    if (ftpState->flags.dir_slash) {
2131
 
        debugs(9, 3, "Directory path did not end in /");
 
2330
        debugs(9, 3, HERE << "Directory path did not end in /");
2132
2331
        ftpState->title_url.append("/");
2133
2332
        ftpState->flags.isdir = 1;
2134
2333
    }
2135
2334
 
2136
 
    ftpSendPasv(ftpState);
 
2335
    ftpSendPassive(ftpState);
2137
2336
}
2138
2337
 
 
2338
/// \ingroup ServerProtocolFTPInternal
2139
2339
static void
2140
2340
ftpSendMdtm(FtpStateData * ftpState)
2141
2341
{
2142
2342
    /* check the server control channel is still available */
2143
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendMdtm"))
 
2343
    if (!ftpState || !ftpState->haveControlChannel("ftpSendMdtm"))
2144
2344
        return;
2145
2345
 
2146
2346
    assert(*ftpState->filepath != '\0');
2149
2349
    ftpState->state = SENT_MDTM;
2150
2350
}
2151
2351
 
 
2352
/// \ingroup ServerProtocolFTPInternal
2152
2353
static void
2153
2354
ftpReadMdtm(FtpStateData * ftpState)
2154
2355
{
2155
2356
    int code = ftpState->ctrl.replycode;
2156
 
    debugs(9, 3, "This is ftpReadMdtm");
 
2357
    debugs(9, 3, HERE);
2157
2358
 
2158
2359
    if (code == 213) {
2159
2360
        ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply);
2160
2361
        ftpState->unhack();
2161
2362
    } else if (code < 0) {
2162
2363
        ftpFail(ftpState);
2163
 
        return;
 
2364
        return;
2164
2365
    }
2165
2366
 
2166
2367
    ftpSendSize(ftpState);
2167
2368
}
2168
2369
 
 
2370
/// \ingroup ServerProtocolFTPInternal
2169
2371
static void
2170
2372
ftpSendSize(FtpStateData * ftpState)
2171
2373
{
2172
2374
    /* check the server control channel is still available */
2173
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendPasv"))
 
2375
    if (!ftpState || !ftpState->haveControlChannel("ftpSendSize"))
2174
2376
        return;
2175
2377
 
2176
2378
    /* Only send SIZE for binary transfers. The returned size
2184
2386
        ftpState->state = SENT_SIZE;
2185
2387
    } else
2186
2388
        /* Skip to next state no non-binary transfers */
2187
 
        ftpSendPasv(ftpState);
 
2389
        ftpSendPassive(ftpState);
2188
2390
}
2189
2391
 
 
2392
/// \ingroup ServerProtocolFTPInternal
2190
2393
static void
2191
2394
ftpReadSize(FtpStateData * ftpState)
2192
2395
{
2193
2396
    int code = ftpState->ctrl.replycode;
2194
 
    debugs(9, 3, "This is ftpReadSize");
 
2397
    debugs(9, 3, HERE);
2195
2398
 
2196
2399
    if (code == 213) {
2197
2400
        ftpState->unhack();
2198
2401
        ftpState->theSize = strtoll(ftpState->ctrl.last_reply, NULL, 10);
2199
2402
 
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;
2205
2408
        }
2206
2409
    } else if (code < 0) {
2207
2410
        ftpFail(ftpState);
2208
 
        return;
2209
 
    }
2210
 
 
2211
 
    ftpSendPasv(ftpState);
2212
 
}
2213
 
 
2214
 
static void
2215
 
ftpSendPasv(FtpStateData * ftpState)
2216
 
{
2217
 
    struct sockaddr_in addr;
2218
 
    socklen_t addr_len;
2219
 
 
2220
 
    /* check the server control channel is still available */
2221
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendPasv"))
2222
 
        return;
2223
 
 
2224
 
    debugs(9, 3, HERE << "ftpSendPasv started");
2225
 
 
 
2411
        return;
 
2412
    }
 
2413
 
 
2414
    ftpSendPassive(ftpState);
 
2415
}
 
2416
 
 
2417
/**
 
2418
 \ingroup ServerProtocolFTPInternal
 
2419
 */
 
2420
static void
 
2421
ftpReadEPSV(FtpStateData* ftpState)
 
2422
{
 
2423
    int code = ftpState->ctrl.replycode;
 
2424
    char h1, h2, h3, h4;
 
2425
    int n;
 
2426
    u_short port;
 
2427
    IpAddress ipa_remote;
 
2428
    int fd = ftpState->data.fd;
 
2429
    char *buf;
 
2430
    debugs(9, 3, HERE);
 
2431
 
 
2432
    if (code != 229 && code != 522) {
 
2433
        if (code == 200) {
 
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");
 
2438
        } else {
 
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) */
 
2441
        }
 
2442
        ftpSendPassive(ftpState);
 
2443
        return;
 
2444
    }
 
2445
 
 
2446
    if (code == 522) {
 
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;
 
2456
 
 
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) {
 
2465
#if USE_IPV6
 
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);
 
2469
            } else {
 
2470
                /* or try the next Passive mode down the chain. */
 
2471
                ftpSendPassive(ftpState);
 
2472
            }
 
2473
#else
 
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);
 
2478
#endif
 
2479
        }
 
2480
        else {
 
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);
 
2484
        }
 
2485
        return;
 
2486
    }
 
2487
 
 
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);
 
2491
 
 
2492
    buf = ftpState->ctrl.last_reply + strcspn(ftpState->ctrl.last_reply, "(");
 
2493
 
 
2494
    n = sscanf(buf, "(%c%c%c%hu%c)", &h1, &h2, &h3, &port, &h4);
 
2495
 
 
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);
 
2500
 
 
2501
        ftpSendPassive(ftpState);
 
2502
        return;
 
2503
    }
 
2504
 
 
2505
    if (0 == port) {
 
2506
        debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
 
2507
               fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
 
2508
               ftpState->ctrl.last_reply);
 
2509
 
 
2510
        ftpSendPassive(ftpState);
 
2511
        return;
 
2512
    }
 
2513
 
 
2514
    if (Config.Ftp.sanitycheck) {
 
2515
        if (port < 1024) {
 
2516
            debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
 
2517
                   fd_table[ftpState->ctrl.fd].ipaddr << ": " <<
 
2518
                   ftpState->ctrl.last_reply);
 
2519
 
 
2520
            ftpSendPassive(ftpState);
 
2521
            return;
 
2522
        }
 
2523
    }
 
2524
 
 
2525
    ftpState->data.port = port;
 
2526
 
 
2527
    ftpState->data.host = xstrdup(fd_table[ftpState->ctrl.fd].ipaddr);
 
2528
 
 
2529
    safe_free(ftpState->ctrl.last_command);
 
2530
 
 
2531
    safe_free(ftpState->ctrl.last_reply);
 
2532
 
 
2533
    ftpState->ctrl.last_command = xstrdup("Connect to server data port");
 
2534
 
 
2535
    debugs(9, 3, HERE << "connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
 
2536
 
 
2537
    commConnectStart(fd, ftpState->data.host, port, FtpStateData::ftpPasvCallback, ftpState);
 
2538
}
 
2539
 
 
2540
/** \ingroup ServerProtocolFTPInternal
 
2541
 *
 
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.
 
2545
 */
 
2546
static void
 
2547
ftpSendPassive(FtpStateData * ftpState)
 
2548
{
 
2549
    IpAddress addr;
 
2550
    struct addrinfo *AI = NULL;
 
2551
 
 
2552
    /** Checks the server control channel is still available before running. */
 
2553
    if (!ftpState || !ftpState->haveControlChannel("ftpSendPassive"))
 
2554
        return;
 
2555
 
 
2556
    debugs(9, 3, HERE);
 
2557
 
 
2558
    /** \par
 
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.");
 
2565
        ftpFail(ftpState);
 
2566
        return;
 
2567
    }
 
2568
 
 
2569
    /** \par
 
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
2228
2573
        return;
2229
2574
    }
2230
2575
 
2231
 
    if (ftpState->data.fd >= 0) {
2232
 
        /* Close old connection */
2233
 
        comm_close(ftpState->data.fd);
2234
 
        ftpState->data.fd = -1;
2235
 
    }
 
2576
    /// Closes any old FTP-Data connection which may exist. */
 
2577
    ftpState->data.close();
2236
2578
 
 
2579
    /** \par
 
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);
2239
2584
        return;
2240
2585
    }
2241
2586
 
2242
 
    addr_len = sizeof(addr);
 
2587
    /** \par
 
2588
      * Locates the Address of the remote server. */
 
2589
    addr.InitAddrInfo(AI);
2243
2590
 
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);
2247
2596
        return;
2248
2597
    }
2249
2598
 
2250
 
    /* Open data channel with the same local address as control channel */
 
2599
    addr = *AI;
 
2600
 
 
2601
    addr.FreeAddrInfo(AI);
 
2602
 
 
2603
    /** Otherwise, Open data channel with the same local address as control channel (on a new random port!) */
 
2604
    addr.SetPort(0);
2251
2605
    int fd = comm_open(SOCK_STREAM,
2252
2606
                       IPPROTO_TCP,
2253
 
                       addr.sin_addr,
2254
 
                       0,
 
2607
                       addr,
2255
2608
                       COMM_NONBLOCKING,
2256
2609
                       ftpState->entry->url());
2257
2610
 
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);
2259
2612
 
2260
2613
    if (fd < 0) {
2261
2614
        ftpFail(ftpState);
2262
2615
        return;
2263
2616
    }
2264
2617
 
2265
 
    /*
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.
2271
 
     *
2272
 
     * XXX this should not actually matter if the ftpState is cbdata
2273
 
     * managed correctly and comm close handlers are cbdata fenced
2274
 
     */
2275
 
    ftpState->data.fd = fd;
2276
 
 
2277
 
    snprintf(cbuf, 1024, "PASV\r\n");
 
2618
    ftpState->data.opened(fd, ftpState->dataCloser());
 
2619
 
 
2620
    /** \par
 
2621
      * Send EPSV (ALL,2,1) or PASV on the control channel.
 
2622
      *
 
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.
 
2627
      */
 
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;
 
2632
        break;
 
2633
 
 
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;
 
2637
        break;
 
2638
 
 
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;
 
2643
        break;
 
2644
 
 
2645
    default:
 
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;
 
2654
        } else {
 
2655
#if USE_IPV6
 
2656
            snprintf(cbuf, 1024, "EPSV 2\r\n");
 
2657
            ftpState->state = SENT_EPSV_2;
 
2658
#else
 
2659
            snprintf(cbuf, 1024, "EPSV 1\r\n");
 
2660
            ftpState->state = SENT_EPSV_1;
 
2661
#endif
 
2662
        }
 
2663
        break;
 
2664
    }
2278
2665
 
2279
2666
    ftpState->writeCommand(cbuf);
2280
2667
 
2281
 
    ftpState->state = SENT_PASV;
2282
 
 
2283
2668
    /*
2284
2669
     * ugly hack for ftp servers like ftp.netscape.com that sometimes
2285
2670
     * dont acknowledge PASV commands.
2286
2671
     */
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));
 
2675
 
 
2676
    commSetTimeout(ftpState->data.fd, 15, timeoutCall);
2288
2677
}
2289
2678
 
2290
2679
void
2304
2693
        return;
2305
2694
    }
2306
2695
 
2307
 
#if ICAP_CLIENT
2308
 
    if (icapAccessCheckPending) {
2309
 
        debugs(9,3,HERE << "returning from ftpSendPasv due to icapAccessCheckPending");
 
2696
#if USE_ADAPTATION
 
2697
    if (adaptationAccessCheckPending) {
 
2698
        debugs(9,3, HERE << "returning due to adaptationAccessCheckPending");
2310
2699
        return;
2311
2700
    }
2312
2701
#endif
2313
2702
 
2314
2703
    // processReplyBody calls serverComplete() since there is no body
2315
 
    processReplyBody(); 
 
2704
    processReplyBody();
2316
2705
}
2317
2706
 
 
2707
/// \ingroup ServerProtocolFTPInternal
2318
2708
static void
2319
2709
ftpReadPasv(FtpStateData * ftpState)
2320
2710
{
2323
2713
    int p1, p2;
2324
2714
    int n;
2325
2715
    u_short port;
 
2716
    IpAddress ipa_remote;
2326
2717
    int fd = ftpState->data.fd;
2327
2718
    char *buf;
2328
2719
    LOCAL_ARRAY(char, ipaddr, 1024);
2329
 
    debugs(9, 3, "This is ftpReadPasv");
 
2720
    debugs(9, 3, HERE);
2330
2721
 
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);
2334
2725
        return;
2335
2726
    }
2336
2727
 
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);
2340
2731
 
2341
2732
    buf = ftpState->ctrl.last_reply + strcspn(ftpState->ctrl.last_reply, "0123456789");
2342
2733
 
2343
2734
    n = sscanf(buf, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2);
2344
2735
 
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);
2349
2740
 
2350
 
        ftpSendPort(ftpState);
 
2741
        ftpSendEPRT(ftpState);
2351
2742
        return;
2352
2743
    }
2353
2744
 
2354
2745
    snprintf(ipaddr, 1024, "%d.%d.%d.%d", h1, h2, h3, h4);
2355
2746
 
2356
 
    if (!safe_inet_addr(ipaddr, NULL)) {
2357
 
        debugs(9, 1, "Unsafe PASV reply from " <<
 
2747
    ipa_remote = ipaddr;
 
2748
 
 
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);
2360
2753
 
2361
 
        ftpSendPort(ftpState);
 
2754
        ftpSendEPRT(ftpState);
2362
2755
        return;
2363
2756
    }
2364
2757
 
2365
2758
    port = ((p1 << 8) + p2);
2366
2759
 
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);
2371
2764
 
2372
 
        ftpSendPort(ftpState);
 
2765
        ftpSendEPRT(ftpState);
2373
2766
        return;
2374
2767
    }
2375
2768
 
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);
2381
2774
 
2382
 
            ftpSendPort(ftpState);
 
2775
            ftpSendEPRT(ftpState);
2383
2776
            return;
2384
2777
        }
2385
2778
    }
2397
2790
 
2398
2791
    ftpState->ctrl.last_command = xstrdup("Connect to server data port");
2399
2792
 
2400
 
    debugs(9, 5, "ftpReadPasv: connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
 
2793
    debugs(9, 3, HERE << "connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
2401
2794
 
2402
2795
    commConnectStart(fd, ipaddr, port, FtpStateData::ftpPasvCallback, ftpState);
2403
2796
}
2404
2797
 
2405
2798
void
2406
 
FtpStateData::ftpPasvCallback(int fd, comm_err_t status, int xerrno, void *data)
 
2799
FtpStateData::ftpPasvCallback(int fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
2407
2800
{
2408
2801
    FtpStateData *ftpState = (FtpStateData *)data;
2409
 
    debugs(9, 3, "ftpPasvCallback");
 
2802
    debugs(9, 3, HERE);
 
2803
    ftpState->request->recordLookup(dns);
2410
2804
 
2411
2805
    if (status != COMM_OK) {
2412
 
        debugs(9, 2, "ftpPasvCallback: failed to connect. Retrying without PASV.");
 
2806
        debugs(9, 2, HERE << "Failed to connect. Retrying without PASV.");
2413
2807
        ftpState->fwd->dontRetry(false);        /* this is a retryable error */
2414
2808
        ftpState->fwd->ftpPasvFailed(true);
2415
2809
        ftpState->failed(ERR_NONE, 0);
2420
2814
    ftpRestOrList(ftpState);
2421
2815
}
2422
2816
 
 
2817
/// \ingroup ServerProtocolFTPInternal
2423
2818
static int
2424
2819
ftpOpenListenSocket(FtpStateData * ftpState, int fallback)
2425
2820
{
2426
2821
    int fd;
2427
2822
 
2428
 
    struct sockaddr_in addr;
2429
 
    socklen_t addr_len;
 
2823
    IpAddress addr;
 
2824
    struct addrinfo *AI = NULL;
2430
2825
    int on = 1;
2431
 
    u_short port = 0;
2432
 
    /*
2433
 
     * Tear down any old data connection if any. We are about to
2434
 
     * establish a new one.
2435
 
     */
 
2826
    int x = 0;
2436
2827
 
2437
 
    if (ftpState->data.fd > 0) {
2438
 
        comm_close(ftpState->data.fd);
2439
 
        ftpState->data.fd = -1;
2440
 
    }
 
2828
    /// Close old data channel, if any. We may open a new one below.
 
2829
    ftpState->data.close();
2441
2830
 
2442
2831
    /*
2443
2832
     * Set up a listen socket on the same local address as the
2444
2833
     * control connection.
2445
2834
     */
2446
 
    addr_len = sizeof(addr);
2447
 
 
2448
 
    if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) {
2449
 
        debugs(9, 0, "ftpOpenListenSocket: getsockname(" << ftpState->ctrl.fd << ",..): " << xstrerror());
 
2835
 
 
2836
    addr.InitAddrInfo(AI);
 
2837
 
 
2838
    x = getsockname(ftpState->ctrl.fd, AI->ai_addr, &AI->ai_addrlen);
 
2839
 
 
2840
    addr = *AI;
 
2841
 
 
2842
    addr.FreeAddrInfo(AI);
 
2843
 
 
2844
    if (x) {
 
2845
        debugs(9, DBG_CRITICAL, HERE << "getsockname(" << ftpState->ctrl.fd << ",..): " << xstrerror());
2450
2846
        return -1;
2451
2847
    }
2452
2848
 
2456
2852
     */
2457
2853
    if (fallback) {
2458
2854
        setsockopt(ftpState->ctrl.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
2459
 
        port = ntohs(addr.sin_port);
 
2855
    } else {
 
2856
        /* if not running in fallback mode a new port needs to be retrieved */
 
2857
        addr.SetPort(0);
2460
2858
    }
2461
2859
 
2462
2860
    fd = comm_open(SOCK_STREAM,
2463
2861
                   IPPROTO_TCP,
2464
 
                   addr.sin_addr,
2465
 
                   port,
 
2862
                   addr,
2466
2863
                   COMM_NONBLOCKING | (fallback ? COMM_REUSEADDR : 0),
2467
2864
                   ftpState->entry->url());
2468
 
    debugs(9, 3, "ftpOpenListenSocket: Unconnected data socket created on FD " << fd  );
 
2865
    debugs(9, 3, HERE << "Unconnected data socket created on FD " << fd  );
2469
2866
 
2470
2867
    if (fd < 0) {
2471
 
        debugs(9, 0, "ftpOpenListenSocket: comm_open failed");
 
2868
        debugs(9, DBG_CRITICAL, HERE << "comm_open failed");
2472
2869
        return -1;
2473
2870
    }
2474
2871
 
2477
2874
        return -1;
2478
2875
    }
2479
2876
 
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;
2483
2880
    return fd;
2484
2881
}
2485
2882
 
 
2883
/// \ingroup ServerProtocolFTPInternal
2486
2884
static void
2487
 
ftpSendPort(FtpStateData * ftpState)
 
2885
ftpSendPORT(FtpStateData * ftpState)
2488
2886
{
2489
2887
    int fd;
2490
2888
 
2491
 
    struct sockaddr_in addr;
2492
 
    socklen_t addr_len;
 
2889
    IpAddress ipa;
 
2890
    struct addrinfo *AI = NULL;
2493
2891
    unsigned char *addrptr;
2494
2892
    unsigned char *portptr;
2495
2893
 
2496
2894
    /* check the server control channel is still available */
2497
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
2498
 
        return;
2499
 
 
2500
 
    debugs(9, 3, "This is ftpSendPort");
 
2895
    if (!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
 
2896
        return;
 
2897
 
 
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.");
 
2900
        return;
 
2901
    }
 
2902
 
 
2903
    debugs(9, 3, HERE);
2501
2904
    ftpState->flags.pasv_supported = 0;
2502
2905
    fd = ftpOpenListenSocket(ftpState, 0);
2503
 
    addr_len = sizeof(addr);
 
2906
    ipa.InitAddrInfo(AI);
2504
2907
 
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());
2507
2911
 
2508
2912
        /* XXX Need to set error message */
2509
2913
        ftpFail(ftpState);
2510
2914
        return;
2511
2915
    }
2512
2916
 
2513
 
    addrptr = (unsigned char *) &addr.sin_addr.s_addr;
2514
 
    portptr = (unsigned char *) &addr.sin_port;
 
2917
#if USE_IPV6
 
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);
 
2924
        return;
 
2925
    }
 
2926
#endif
 
2927
 
 
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;
 
2935
 
 
2936
    ipa.FreeAddrInfo(AI);
2520
2937
}
2521
2938
 
 
2939
/// \ingroup ServerProtocolFTPInternal
2522
2940
static void
2523
 
ftpReadPort(FtpStateData * ftpState)
 
2941
ftpReadPORT(FtpStateData * ftpState)
2524
2942
{
2525
2943
    int code = ftpState->ctrl.replycode;
2526
 
    debugs(9, 3, "This is ftpReadPort");
 
2944
    debugs(9, 3, HERE);
2527
2945
 
2528
2946
    if (code != 200) {
2529
2947
        /* Fall back on using the same port as the control connection */
2534
2952
    ftpRestOrList(ftpState);
2535
2953
}
2536
2954
 
2537
 
/* "read" handler to accept data connection */
2538
 
static void
2539
 
ftpAcceptDataConnection(int fd, int newfd, ConnectionDetail *details,
2540
 
                        comm_err_t flag, int xerrno, void *data)
2541
 
{
2542
 
    FtpStateData *ftpState = (FtpStateData *)data;
 
2955
/// \ingroup ServerProtocolFTPInternal
 
2956
static void
 
2957
ftpSendEPRT(FtpStateData * ftpState)
 
2958
{
 
2959
    int fd;
 
2960
    IpAddress addr;
 
2961
    struct addrinfo *AI = NULL;
 
2962
    char buf[MAX_IPSTRLEN];
 
2963
 
 
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.");
 
2966
        return;
 
2967
    }
 
2968
 
 
2969
    debugs(9, 3, HERE);
 
2970
    ftpState->flags.pasv_supported = 0;
 
2971
    fd = ftpOpenListenSocket(ftpState, 0);
 
2972
 
 
2973
    addr.InitAddrInfo(AI);
 
2974
 
 
2975
    if (getsockname(fd, AI->ai_addr, &AI->ai_addrlen)) {
 
2976
        addr.FreeAddrInfo(AI);
 
2977
        debugs(9, DBG_CRITICAL, HERE << "getsockname(" << fd << ",..): " << xstrerror());
 
2978
 
 
2979
        /* XXX Need to set error message */
 
2980
        ftpFail(ftpState);
 
2981
        return;
 
2982
    }
 
2983
 
 
2984
    addr = *AI;
 
2985
 
 
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),
 
2991
             addr.GetPort() );
 
2992
 
 
2993
    ftpState->writeCommand(cbuf);
 
2994
    ftpState->state = SENT_EPRT;
 
2995
 
 
2996
    addr.FreeAddrInfo(AI);
 
2997
}
 
2998
 
 
2999
static void
 
3000
ftpReadEPRT(FtpStateData * ftpState)
 
3001
{
 
3002
    int code = ftpState->ctrl.replycode;
 
3003
    debugs(9, 3, HERE);
 
3004
 
 
3005
    if (code != 200) {
 
3006
        /* Failover to attempting old PORT command. */
 
3007
        debugs(9, 3, "EPRT not supported by remote end");
 
3008
        ftpSendPORT(ftpState);
 
3009
        return;
 
3010
    }
 
3011
 
 
3012
    ftpRestOrList(ftpState);
 
3013
}
 
3014
 
 
3015
/**
 
3016
 \ingroup ServerProtocolFTPInternal
 
3017
 \par
 
3018
 * "read" handler to accept FTP data connections.
 
3019
 *
 
3020
 \param io    comm accept(2) callback parameters
 
3021
 */
 
3022
void FtpStateData::ftpAcceptDataConnection(const CommAcceptCbParams &io)
 
3023
{
 
3024
    char ntoapeer[MAX_IPSTRLEN];
2543
3025
    debugs(9, 3, "ftpAcceptDataConnection");
2544
3026
 
2545
 
    if (flag == COMM_ERR_CLOSING)
 
3027
    if (io.flag == COMM_ERR_CLOSING)
2546
3028
        return;
2547
3029
 
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");
2550
3032
        return;
2551
3033
    }
2552
3034
 
 
3035
    /** \par
 
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.
 
3039
     */
2553
3040
    if (Config.Ftp.sanitycheck) {
2554
 
        char *ipaddr = inet_ntoa(details->peer.sin_addr);
2555
 
 
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);
2560
 
 
2561
 
            comm_close(newfd);
2562
 
            comm_accept(ftpState->data.fd, ftpAcceptDataConnection, ftpState);
 
3041
        io.details.peer.NtoA(ntoapeer,MAX_IPSTRLEN);
 
3042
 
 
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);
 
3048
 
 
3049
            comm_close(io.nfd);
 
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);
2563
3054
            return;
2564
3055
        }
2565
3056
    }
2566
3057
 
2567
 
    if (flag != COMM_OK) {
2568
 
        debugs(9, 1, "ftpHandleDataAccept: comm_accept(" << newfd << "): " << xstrerr(xerrno));
2569
 
        /* XXX Need to set error message */
2570
 
        ftpFail(ftpState);
 
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 */
 
3061
        ftpFail(this);
2571
3062
        return;
2572
3063
    }
2573
3064
 
2574
 
    /* Replace the Listen socket with the accepted data socket */
2575
 
    comm_close(ftpState->data.fd);
2576
 
 
2577
 
    debugs(9, 3, "ftpAcceptDataConnection: Connected data socket on FD " << newfd);
2578
 
 
2579
 
    ftpState->data.fd = newfd;
2580
 
 
2581
 
    ftpState->data.port = ntohs(details->peer.sin_port);
2582
 
 
2583
 
    ftpState->data.host = xstrdup(inet_ntoa(details->peer.sin_addr));
2584
 
 
2585
 
    commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
2586
 
 
2587
 
    commSetTimeout(ftpState->data.fd, Config.Timeout.read, FtpStateData::ftpTimeout,
2588
 
                   ftpState);
2589
 
 
2590
 
    /* XXX We should have a flag to track connect state...
 
3065
    /**\par
 
3066
     * Replace the Listen socket with the accepted data socket */
 
3067
    data.close();
 
3068
    data.opened(io.nfd, dataCloser());
 
3069
    data.port = io.details.peer.GetPort();
 
3070
    io.details.peer.NtoA(data.host,SQUIDHOSTNAMELEN);
 
3071
 
 
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);
 
3076
 
 
3077
 
 
3078
    AsyncCall::Pointer nullCall = NULL;
 
3079
    commSetTimeout(ctrl.fd, -1, nullCall);
 
3080
 
 
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);
 
3085
 
 
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
2593
3089
     */
2594
3090
    /* Restart state (SENT_NLST/LIST/RETR) */
2595
 
    FTP_SM_FUNCS[ftpState->state] (ftpState);
 
3091
    FTP_SM_FUNCS[state] (this);
2596
3092
}
2597
3093
 
 
3094
/// \ingroup ServerProtocolFTPInternal
2598
3095
static void
2599
3096
ftpRestOrList(FtpStateData * ftpState)
2600
3097
{
2601
 
    debugs(9, 3, "This is ftpRestOrList");
 
3098
    debugs(9, 3, HERE);
2602
3099
 
2603
3100
    if (ftpState->typecode == 'D') {
2604
3101
        ftpState->flags.isdir = 1;
2609
3106
            ftpSendNlst(ftpState);      /* GET name;type=d  sec 3.2.2 of RFC 1738 */
2610
3107
        }
2611
3108
    } else if (ftpState->flags.put) {
2612
 
        debugs(9, 3, "ftpRestOrList: Sending STOR request...");
2613
3109
        ftpSendStor(ftpState);
2614
3110
    } else if (ftpState->flags.isdir)
2615
3111
        ftpSendList(ftpState);
2619
3115
        ftpSendRetr(ftpState);
2620
3116
}
2621
3117
 
 
3118
/// \ingroup ServerProtocolFTPInternal
2622
3119
static void
2623
3120
ftpSendStor(FtpStateData * ftpState)
2624
3121
{
2625
3122
    /* check the server control channel is still available */
2626
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendStor"))
 
3123
    if (!ftpState || !ftpState->haveControlChannel("ftpSendStor"))
2627
3124
        return;
2628
3125
 
 
3126
    debugs(9, 3, HERE);
 
3127
 
2629
3128
    if (ftpState->filepath != NULL) {
2630
3129
        /* Plain file upload */
2631
3130
        snprintf(cbuf, 1024, "STOR %s\r\n", ftpState->filepath);
2642
3141
    }
2643
3142
}
2644
3143
 
 
3144
/// \ingroup ServerProtocolFTPInternal
 
3145
/// \deprecated use ftpState->readStor() instead.
2645
3146
static void
2646
3147
ftpReadStor(FtpStateData * ftpState)
2647
3148
{
2648
3149
    ftpState->readStor();
2649
3150
}
2650
3151
 
2651
 
void FtpStateData::readStor() {
 
3152
void FtpStateData::readStor()
 
3153
{
2652
3154
    int code = ctrl.replycode;
2653
 
    debugs(9, 3, "This is ftpReadStor");
 
3155
    debugs(9, 3, HERE);
2654
3156
 
2655
3157
    if (code == 125 || (code == 150 && data.host)) {
2656
3158
        if (!startRequestBodyFlow()) { // register to receive body data
2658
3160
            return;
2659
3161
        }
2660
3162
 
2661
 
        /* Begin data transfer */
2662
 
        debugs(9, 3, "ftpReadStor: starting data transfer");
 
3163
        /*\par
 
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();
2664
 
        /*
 
3167
        /** \par
2665
3168
         * Cancel the timeout on the Control socket and
2666
3169
         * establish one on the data socket.
2667
3170
         */
2668
 
        commSetTimeout(ctrl.fd, -1, NULL, NULL);
2669
 
        commSetTimeout(data.fd, Config.Timeout.read, FtpStateData::ftpTimeout,
2670
 
                       this);
 
3171
        AsyncCall::Pointer nullCall = NULL;
 
3172
        commSetTimeout(ctrl.fd, -1, nullCall);
 
3173
 
 
3174
        typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
 
3175
        AsyncCall::Pointer timeoutCall =  asyncCall(9, 5, "FtpStateData::ftpTimeout",
 
3176
                                          TimeoutDialer(this,&FtpStateData::ftpTimeout));
 
3177
 
 
3178
        commSetTimeout(data.fd, Config.Timeout.read, timeoutCall);
2671
3179
 
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 */
 
3183
        /*\par
 
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));
 
3189
 
 
3190
        comm_accept(data.fd, acceptCall);
2678
3191
    } else {
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);
2680
3193
        ftpFail(this);
2681
3194
    }
2682
3195
}
2683
3196
 
 
3197
/// \ingroup ServerProtocolFTPInternal
2684
3198
static void
2685
3199
ftpSendRest(FtpStateData * ftpState)
2686
3200
{
2687
3201
    /* check the server control channel is still available */
2688
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
 
3202
    if (!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
2689
3203
        return;
2690
3204
 
 
3205
    debugs(9, 3, HERE);
 
3206
 
2691
3207
    snprintf(cbuf, 1024, "REST %"PRId64"\r\n", ftpState->restart_offset);
2692
3208
    ftpState->writeCommand(cbuf);
2693
3209
    ftpState->state = SENT_REST;
2714
3230
        return 0;
2715
3231
 
2716
3232
    if (desired_offset >= theSize)
2717
 
        return 0;
 
3233
        return 0;
2718
3234
 
2719
3235
    restart_offset = desired_offset;
2720
3236
    return 1;
2721
3237
}
2722
3238
 
 
3239
/// \ingroup ServerProtocolFTPInternal
2723
3240
static void
2724
3241
ftpReadRest(FtpStateData * ftpState)
2725
3242
{
2726
3243
    int code = ftpState->ctrl.replycode;
2727
 
    debugs(9, 3, "This is ftpReadRest");
 
3244
    debugs(9, 3, HERE);
2728
3245
    assert(ftpState->restart_offset > 0);
2729
3246
 
2730
3247
    if (code == 350) {
2731
 
        ftpState->setCurrentOffset(ftpState->restart_offset);
 
3248
        ftpState->setCurrentOffset(ftpState->restart_offset);
2732
3249
        ftpSendRetr(ftpState);
2733
3250
    } else if (code > 0) {
2734
 
        debugs(9, 3, "ftpReadRest: REST not supported");
 
3251
        debugs(9, 3, HERE << "REST not supported");
2735
3252
        ftpState->flags.rest_supported = 0;
2736
3253
        ftpSendRetr(ftpState);
2737
3254
    } else {
2739
3256
    }
2740
3257
}
2741
3258
 
 
3259
/// \ingroup ServerProtocolFTPInternal
2742
3260
static void
2743
3261
ftpSendList(FtpStateData * ftpState)
2744
3262
{
2745
3263
    /* check the server control channel is still available */
2746
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendList"))
 
3264
    if (!ftpState || !ftpState->haveControlChannel("ftpSendList"))
2747
3265
        return;
2748
3266
 
 
3267
    debugs(9, 3, HERE);
 
3268
 
2749
3269
    if (ftpState->filepath) {
2750
3270
        snprintf(cbuf, 1024, "LIST %s\r\n", ftpState->filepath);
2751
3271
    } else {
2756
3276
    ftpState->state = SENT_LIST;
2757
3277
}
2758
3278
 
 
3279
/// \ingroup ServerProtocolFTPInternal
2759
3280
static void
2760
3281
ftpSendNlst(FtpStateData * ftpState)
2761
3282
{
2762
3283
    /* check the server control channel is still available */
2763
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendNlst"))
 
3284
    if (!ftpState || !ftpState->haveControlChannel("ftpSendNlst"))
2764
3285
        return;
2765
3286
 
 
3287
    debugs(9, 3, HERE);
 
3288
 
2766
3289
    ftpState->flags.tried_nlst = 1;
2767
3290
 
2768
3291
    if (ftpState->filepath) {
2775
3298
    ftpState->state = SENT_NLST;
2776
3299
}
2777
3300
 
 
3301
/// \ingroup ServerProtocolFTPInternal
2778
3302
static void
2779
3303
ftpReadList(FtpStateData * ftpState)
2780
3304
{
2781
3305
    int code = ftpState->ctrl.replycode;
2782
 
    debugs(9, 3, "This is ftpReadList");
 
3306
    debugs(9, 3, HERE);
2783
3307
 
2784
3308
    if (code == 125 || (code == 150 && ftpState->data.host)) {
2785
3309
        /* Begin data transfer */
2790
3314
         * Cancel the timeout on the Control socket and establish one
2791
3315
         * on the data socket
2792
3316
         */
2793
 
        commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
 
3317
        AsyncCall::Pointer nullCall = NULL;
 
3318
        commSetTimeout(ftpState->ctrl.fd, -1, nullCall);
2794
3319
        return;
2795
3320
    } else if (code == 150) {
2796
3321
        /* Accept data channel */
2797
 
        comm_accept(ftpState->data.fd, ftpAcceptDataConnection, ftpState);
 
3322
        typedef CommCbMemFunT<FtpStateData, CommAcceptCbParams> acceptDialer;
 
3323
        AsyncCall::Pointer acceptCall = asyncCall(11, 5, "FtpStateData::ftpAcceptDataConnection",
 
3324
                                        acceptDialer(ftpState, &FtpStateData::ftpAcceptDataConnection));
 
3325
 
 
3326
        comm_accept(ftpState->data.fd, acceptCall);
2798
3327
        /*
2799
3328
         * Cancel the timeout on the Control socket and establish one
2800
3329
         * on the data socket
2801
3330
         */
2802
 
        commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
2803
 
        commSetTimeout(ftpState->data.fd, Config.Timeout.read, FtpStateData::ftpTimeout, ftpState);
 
3331
        AsyncCall::Pointer nullCall = NULL;
 
3332
        commSetTimeout(ftpState->ctrl.fd, -1, nullCall);
 
3333
 
 
3334
        typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
 
3335
        AsyncCall::Pointer timeoutCall =  asyncCall(9, 5, "FtpStateData::ftpTimeout",
 
3336
                                          TimeoutDialer(ftpState,&FtpStateData::ftpTimeout));
 
3337
        commSetTimeout(ftpState->data.fd, Config.Timeout.read, timeoutCall);
2804
3338
        return;
2805
3339
    } else if (!ftpState->flags.tried_nlst && code > 300) {
2806
3340
        ftpSendNlst(ftpState);
2810
3344
    }
2811
3345
}
2812
3346
 
 
3347
/// \ingroup ServerProtocolFTPInternal
2813
3348
static void
2814
3349
ftpSendRetr(FtpStateData * ftpState)
2815
3350
{
2816
3351
    /* check the server control channel is still available */
2817
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendRetr"))
 
3352
    if (!ftpState || !ftpState->haveControlChannel("ftpSendRetr"))
2818
3353
        return;
2819
3354
 
 
3355
    debugs(9, 3, HERE);
 
3356
 
2820
3357
    assert(ftpState->filepath != NULL);
2821
3358
    snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath);
2822
3359
    ftpState->writeCommand(cbuf);
2823
3360
    ftpState->state = SENT_RETR;
2824
3361
}
2825
3362
 
 
3363
/// \ingroup ServerProtocolFTPInternal
2826
3364
static void
2827
3365
ftpReadRetr(FtpStateData * ftpState)
2828
3366
{
2829
3367
    int code = ftpState->ctrl.replycode;
2830
 
    debugs(9, 3, "This is ftpReadRetr");
 
3368
    debugs(9, 3, HERE);
2831
3369
 
2832
3370
    if (code == 125 || (code == 150 && ftpState->data.host)) {
2833
3371
        /* Begin data transfer */
2834
 
        debugs(9, 3, "ftpReadRetr: reading data channel");
 
3372
        debugs(9, 3, HERE << "reading data channel");
2835
3373
        /* XXX what about Config.Timeout.read? */
2836
3374
        ftpState->maybeReadVirginBody();
2837
3375
        ftpState->state = READING_DATA;
2839
3377
         * Cancel the timeout on the Control socket and establish one
2840
3378
         * on the data socket
2841
3379
         */
2842
 
        commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
 
3380
        AsyncCall::Pointer nullCall = NULL;
 
3381
        commSetTimeout(ftpState->ctrl.fd, -1, nullCall);
2843
3382
    } else if (code == 150) {
2844
3383
        /* Accept data channel */
2845
 
        comm_accept(ftpState->data.fd, ftpAcceptDataConnection, ftpState);
 
3384
        typedef CommCbMemFunT<FtpStateData, CommAcceptCbParams> acceptDialer;
 
3385
        AsyncCall::Pointer acceptCall = asyncCall(11, 5, "FtpStateData::ftpAcceptDataConnection",
 
3386
                                        acceptDialer(ftpState, &FtpStateData::ftpAcceptDataConnection));
 
3387
        comm_accept(ftpState->data.fd, acceptCall);
2846
3388
        /*
2847
3389
         * Cancel the timeout on the Control socket and establish one
2848
3390
         * on the data socket
2849
3391
         */
2850
 
        commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
2851
 
        commSetTimeout(ftpState->data.fd, Config.Timeout.read, FtpStateData::ftpTimeout,
2852
 
                       ftpState);
 
3392
        AsyncCall::Pointer nullCall = NULL;
 
3393
        commSetTimeout(ftpState->ctrl.fd, -1, nullCall);
 
3394
 
 
3395
        typedef CommCbMemFunT<FtpStateData, CommTimeoutCbParams> TimeoutDialer;
 
3396
        AsyncCall::Pointer timeoutCall =  asyncCall(9, 5, "FtpStateData::ftpTimeout",
 
3397
                                          TimeoutDialer(ftpState,&FtpStateData::ftpTimeout));
 
3398
        commSetTimeout(ftpState->data.fd, Config.Timeout.read, timeoutCall);
2853
3399
    } else if (code >= 300) {
2854
3400
        if (!ftpState->flags.try_slash_hack) {
2855
3401
            /* Try this as a directory missing trailing slash... */
2862
3408
    }
2863
3409
}
2864
3410
 
 
3411
/// \ingroup ServerProtocolFTPInternal
2865
3412
static void
2866
3413
ftpReadTransferDone(FtpStateData * ftpState)
2867
3414
{
2868
3415
    int code = ftpState->ctrl.replycode;
2869
 
    debugs(9, 3, "This is ftpReadTransferDone");
 
3416
    debugs(9, 3, HERE);
2870
3417
 
2871
3418
    if (code == 226 || code == 250) {
2872
3419
        /* Connection closed; retrieval done. */
2876
3423
 
2877
3424
        ftpSendQuit(ftpState);
2878
3425
    } else {                    /* != 226 */
2879
 
        debugs(9, 1, "ftpReadTransferDone: Got code " << code << " after reading data");
 
3426
        debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after reading data");
2880
3427
        ftpState->failed(ERR_FTP_FAILURE, 0);
2881
3428
        /* failed closes ctrl.fd and frees ftpState */
2882
3429
        return;
2888
3435
FtpStateData::handleRequestBodyProducerAborted()
2889
3436
{
2890
3437
    ServerStateData::handleRequestBodyProducerAborted();
2891
 
    debugs(9, 3, HERE << "noteBodyProducerAborted: ftpState=" << this);
 
3438
    debugs(9, 3, HERE << "ftpState=" << this);
2892
3439
    failed(ERR_READ_ERROR, 0);
2893
3440
}
2894
3441
 
2895
 
/* This will be called when the put write is completed */
 
3442
/**
 
3443
 * This will be called when the put write is completed
 
3444
 */
2896
3445
void
2897
 
FtpStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag)
 
3446
FtpStateData::sentRequestBody(const CommIoCbParams &io)
2898
3447
{
2899
 
    if (size > 0)
2900
 
        kb_incr(&statCounter.server.ftp.kbytes_out, size);
2901
 
    ServerStateData::sentRequestBody(fd, size, errflag);
 
3448
    if (io.size > 0)
 
3449
        kb_incr(&statCounter.server.ftp.kbytes_out, io.size);
 
3450
    ServerStateData::sentRequestBody(io);
2902
3451
}
2903
3452
 
 
3453
/// \ingroup ServerProtocolFTPInternal
2904
3454
static void
2905
3455
ftpWriteTransferDone(FtpStateData * ftpState)
2906
3456
{
2907
3457
    int code = ftpState->ctrl.replycode;
2908
 
    debugs(9, 3, "This is ftpWriteTransferDone");
 
3458
    debugs(9, 3, HERE);
2909
3459
 
2910
3460
    if (!(code == 226 || code == 250)) {
2911
 
        debugs(9, 1, "ftpReadTransferDone: Got code " << code << " after sending data");
 
3461
        debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after sending data");
2912
3462
        ftpState->failed(ERR_FTP_PUT_ERROR, 0);
2913
3463
        return;
2914
3464
    }
2917
3467
    ftpSendReply(ftpState);
2918
3468
}
2919
3469
 
 
3470
/// \ingroup ServerProtocolFTPInternal
2920
3471
static void
2921
3472
ftpSendQuit(FtpStateData * ftpState)
2922
3473
{
2923
3474
    /* check the server control channel is still available */
2924
 
    if(!ftpState || !ftpState->haveControlChannel("ftpSendQuit"))
 
3475
    if (!ftpState || !ftpState->haveControlChannel("ftpSendQuit"))
2925
3476
        return;
2926
3477
 
2927
3478
    snprintf(cbuf, 1024, "QUIT\r\n");
2929
3480
    ftpState->state = SENT_QUIT;
2930
3481
}
2931
3482
 
 
3483
/// \ingroup ServerProtocolFTPInternal
2932
3484
static void
2933
3485
ftpReadQuit(FtpStateData * ftpState)
2934
3486
{
2935
 
    /* XXX should this just be a case of abortTransaction? */
 
3487
    /** \todo XXX should this just be a case of abortTransaction? */
2936
3488
    ftpState->serverComplete();
2937
3489
}
2938
3490
 
 
3491
/// \ingroup ServerProtocolFTPInternal
2939
3492
static void
2940
3493
ftpTrySlashHack(FtpStateData * ftpState)
2941
3494
{
2943
3496
    ftpState->flags.try_slash_hack = 1;
2944
3497
    /* Free old paths */
2945
3498
 
 
3499
    debugs(9, 3, HERE);
 
3500
 
2946
3501
    if (ftpState->pathcomps)
2947
3502
        wordlistDestroy(&ftpState->pathcomps);
2948
3503
 
2949
3504
    safe_free(ftpState->filepath);
2950
3505
 
2951
3506
    /* Build the new path (urlpath begins with /) */
2952
 
    path = xstrdup(ftpState->request->urlpath.buf());
 
3507
    path = xstrdup(ftpState->request->urlpath.termedBuf());
2953
3508
 
2954
3509
    rfc1738_unescape(path);
2955
3510
 
2959
3514
    ftpGetFile(ftpState);
2960
3515
}
2961
3516
 
2962
 
/* Forget hack status. Next error is shown to the user */
 
3517
/**
 
3518
 * Forget hack status. Next error is shown to the user
 
3519
 */
2963
3520
void
2964
3521
FtpStateData::unhack()
2965
3522
{
 
3523
    debugs(9, 3, HERE);
 
3524
 
2966
3525
    if (old_request != NULL) {
2967
3526
        safe_free(old_request);
2968
3527
        safe_free(old_reply);
2977
3536
    restart_offset = 0;
2978
3537
    /* Save old error message & some state info */
2979
3538
 
 
3539
    debugs(9, 3, HERE);
 
3540
 
2980
3541
    if (old_request == NULL) {
2981
3542
        old_request = ctrl.last_command;
2982
3543
        ctrl.last_command = NULL;
2991
3552
    nextState(this);
2992
3553
}
2993
3554
 
 
3555
/// \ingroup ServerProtocolFTPInternal
2994
3556
static void
2995
3557
ftpFail(FtpStateData *ftpState)
2996
3558
{
2997
 
    debugs(9, 3, "ftpFail");
 
3559
    debugs(9, 6, HERE << "flags(" <<
 
3560
           (ftpState->flags.isdir?"IS_DIR,":"") <<
 
3561
           (ftpState->flags.try_slash_hack?"TRY_SLASH_HACK":"") << "), " <<
 
3562
           "mdtm=" << ftpState->mdtm << ", size=" << ftpState->theSize <<
 
3563
           "slashhack=" << (ftpState->request->urlpath.caseCmp("/%2f", 4)==0? "T":"F") );
 
3564
 
2998
3565
    /* Try the / hack to support "Netscape" FTP URL's for retreiving files */
2999
 
 
3000
3566
    if (!ftpState->flags.isdir &&       /* Not a directory */
3001
3567
            !ftpState->flags.try_slash_hack &&  /* Not in slash hack */
3002
3568
            ftpState->mdtm <= 0 && ftpState->theSize < 0 &&     /* Not known as a file */
3023
3589
void
3024
3590
FtpStateData::failed(err_type error, int xerrno)
3025
3591
{
 
3592
    debugs(9,3,HERE << "entry-null=" << (entry?entry->isEmpty():0) << ", entry=" << entry);
3026
3593
    if (entry->isEmpty())
3027
3594
        failedErrorMessage(error, xerrno);
3028
3595
 
3112
3679
    fwd->fail(err);
3113
3680
}
3114
3681
 
 
3682
/// \ingroup ServerProtocolFTPInternal
3115
3683
static void
3116
3684
ftpSendReply(FtpStateData * ftpState)
3117
3685
{
3120
3688
    http_status http_code;
3121
3689
    err_type err_code = ERR_NONE;
3122
3690
 
3123
 
    debugs(9, 5, "ftpSendReply: " << ftpState->entry->url() << ", code " << code  );
 
3691
    debugs(9, 3, HERE << ftpState->entry->url() << ", code " << code);
3124
3692
 
3125
3693
    if (cbdataReferenceValid(ftpState))
3126
 
        debugs(9, 5, "ftpSendReply: ftpState (" << ftpState << ") is valid!");
 
3694
        debugs(9, 5, HERE << "ftpState (" << ftpState << ") is valid!");
3127
3695
 
3128
3696
    if (code == 226 || code == 250) {
3129
3697
        err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
3165
3733
    const char *t = NULL;
3166
3734
    StoreEntry *e = entry;
3167
3735
 
3168
 
    debugs(9, 3, HERE << "FtpStateData::appendSuccessHeader starting");
 
3736
    debugs(9, 3, HERE);
3169
3737
 
3170
3738
    if (flags.http_header_sent)
3171
3739
        return;
3180
3748
 
3181
3749
    e->buffer();        /* released when done processing current data payload */
3182
3750
 
3183
 
    filename = (t = urlpath.rpos('/')) ? t + 1 : urlpath.buf();
 
3751
    filename = (t = urlpath.rpos('/')) ? t + 1 : urlpath.termedBuf();
3184
3752
 
3185
3753
    if (flags.isdir) {
3186
3754
        mime_type = "text/html";
3211
3779
        reply->setHeaders(version, HTTP_OK, "Gatewaying",
3212
3780
                          mime_type, theSize, mdtm, -2);
3213
3781
    } else if (theSize < getCurrentOffset()) {
3214
 
        /*
3215
 
         * DPW 2007-05-04
3216
 
         * offset should not be larger than theSize.  We should
3217
 
         * not be seeing this condition any more because we'll only
3218
 
         * send REST if we know the theSize and if it is less than theSize.
3219
 
         */
3220
 
        debugs(0,0,HERE << "Whoops! " <<
3221
 
                " restarted_offset=" << getCurrentOffset() <<
3222
 
                ", but theSize=" << theSize <<
3223
 
                ".  assuming full content response");
 
3782
        /*
 
3783
         * DPW 2007-05-04
 
3784
         * offset should not be larger than theSize.  We should
 
3785
         * not be seeing this condition any more because we'll only
 
3786
         * send REST if we know the theSize and if it is less than theSize.
 
3787
         */
 
3788
        debugs(0,DBG_CRITICAL,HERE << "Whoops! " <<
 
3789
               " current offset=" << getCurrentOffset() <<
 
3790
               ", but theSize=" << theSize <<
 
3791
               ".  assuming full content response");
3224
3792
        reply->setHeaders(version, HTTP_OK, "Gatewaying",
3225
3793
                          mime_type, theSize, mdtm, -2);
3226
3794
    } else {
3244
3812
void
3245
3813
FtpStateData::haveParsedReplyHeaders()
3246
3814
{
 
3815
    ServerStateData::haveParsedReplyHeaders();
 
3816
 
3247
3817
    StoreEntry *e = entry;
3248
3818
 
3249
3819
    e->timestampsSet();
3264
3834
FtpStateData::ftpAuthRequired(HttpRequest * request, const char *realm)
3265
3835
{
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);
3271
3841
    return newrep;
3272
3842
}
3273
3843
 
3274
 
char *
3275
 
ftpUrlWith2f(const HttpRequest * request)
 
3844
/**
 
3845
 \ingroup ServerProtocolFTPAPI
 
3846
 \todo Should be a URL class API call.
 
3847
 *
 
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'.
 
3855
 */
 
3856
const char *
 
3857
ftpUrlWith2f(HttpRequest * request)
3276
3858
{
3277
 
    LOCAL_ARRAY(char, buf, MAX_URL);
3278
 
    LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
3279
 
    LOCAL_ARRAY(char, portbuf, 32);
3280
 
    char *t;
3281
 
    portbuf[0] = '\0';
 
3859
    String newbuf = "%2f";
3282
3860
 
3283
3861
    if (request->protocol != PROTO_FTP)
3284
3862
        return NULL;
3285
3863
 
3286
 
    if (request->port != urlDefaultPort(request->protocol))
3287
 
        snprintf(portbuf, 32, ":%d", request->port);
3288
 
 
3289
 
    loginbuf[0] = '\0';
3290
 
 
3291
 
    if ((int) strlen(request->login) > 0) {
3292
 
        xstrncpy(loginbuf, request->login, sizeof(loginbuf) - 2);
3293
 
 
3294
 
        if ((t = strchr(loginbuf, ':')))
3295
 
            *t = '\0';
3296
 
 
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);
3298
3872
    }
3299
3873
 
3300
 
    snprintf(buf, MAX_URL, "%s://%s%s%s%s%s",
3301
 
             ProtocolStr[request->protocol],
3302
 
             loginbuf,
3303
 
             request->host,
3304
 
             portbuf,
3305
 
             "/%2f",
3306
 
             request->urlpath.buf());
3307
 
 
3308
 
    if ((t = strchr(buf, '?')))
3309
 
        *t = '\0';
3310
 
 
3311
 
    return buf;
 
3874
    return urlCanonical(request);
3312
3875
}
3313
3876
 
3314
3877
void
3322
3885
    writeReplyBody(buf, strlen(buf));
3323
3886
}
3324
3887
 
3325
 
/*
 
3888
/**
3326
3889
 * Call this when there is data from the origin server
3327
3890
 * which should be sent to either StoreEntry, or to ICAP...
3328
3891
 */
3329
3892
void
3330
3893
FtpStateData::writeReplyBody(const char *data, size_t len)
3331
3894
{
3332
 
    debugs(9,5,HERE << "writing " << len << " bytes to the reply");
 
3895
    debugs(9, 5, HERE << "writing " << len << " bytes to the reply");
3333
3896
    addVirginReplyBody(data, len);
3334
3897
}
3335
3898
 
3336
 
// called after we wrote the last byte of the request body
 
3899
/**
 
3900
 * called after we wrote the last byte of the request body
 
3901
 */
3337
3902
void
3338
3903
FtpStateData::doneSendingRequestBody()
3339
3904
{
3340
 
    debugs(9,3,HERE);
 
3905
    debugs(9,3, HERE);
3341
3906
    dataComplete();
3342
 
/* NP: RFC 959  3.3.  DATA CONNECTION MANAGEMENT
3343
 
 * if transfer type is 'stream' call dataComplete()
3344
 
 * otherwise leave open. (reschedule control channel read?)
3345
 
 */
 
3907
    /* NP: RFC 959  3.3.  DATA CONNECTION MANAGEMENT
 
3908
     * if transfer type is 'stream' call dataComplete()
 
3909
     * otherwise leave open. (reschedule control channel read?)
 
3910
     */
3346
3911
}
3347
3912
 
3348
 
// a hack to ensure we do not double-complete on the forward entry.
3349
 
// TODO: FtpStateData logic should probably be rewritten to avoid 
3350
 
// double-completion or FwdState should be rewritten to allow it.
 
3913
/**
 
3914
 * A hack to ensure we do not double-complete on the forward entry.
 
3915
 *
 
3916
 \todo FtpStateData logic should probably be rewritten to avoid
 
3917
 *      double-completion or FwdState should be rewritten to allow it.
 
3918
 */
3351
3919
void
3352
3920
FtpStateData::completeForwarding()
3353
3921
{
3354
3922
    if (fwd == NULL || flags.completed_forwarding) {
3355
 
        debugs(9,2,HERE << "completeForwarding avoids " <<
3356
 
            "double-complete on FD " << ctrl.fd << ", Data FD " << data.fd <<
3357
 
            ", this " << this << ", fwd " << fwd);
 
3923
        debugs(9, 3, HERE << "completeForwarding avoids " <<
 
3924
               "double-complete on FD " << ctrl.fd << ", Data FD " << data.fd <<
 
3925
               ", this " << this << ", fwd " << fwd);
3358
3926
        return;
3359
3927
    }
3360
3928
 
3362
3930
    ServerStateData::completeForwarding();
3363
3931
}
3364
3932
 
3365
 
// Close the FTP server connection(s). Used by serverComplete().
 
3933
/**
 
3934
 * Close the FTP server connection(s). Used by serverComplete().
 
3935
 */
3366
3936
void
3367
3937
FtpStateData::closeServer()
3368
3938
{
3369
 
    debugs(9,5, HERE << "closing FTP server FD " << ctrl.fd << ", Data FD " << data.fd << ", this " << this);
 
3939
    debugs(9,3, HERE << "closing FTP server FD " << ctrl.fd << ", Data FD " << data.fd << ", this " << this);
3370
3940
 
3371
3941
    if (ctrl.fd > -1) {
3372
3942
        fwd->unregister(ctrl.fd);
3373
 
        comm_remove_close_handler(ctrl.fd, ftpSocketClosed, this);
3374
 
        comm_close(ctrl.fd);
3375
 
        ctrl.fd = -1;
 
3943
        ctrl.close();
3376
3944
    }
3377
3945
 
3378
 
    if (data.fd > -1) {
3379
 
        comm_close(data.fd);
3380
 
        data.fd = -1;
3381
 
    }
 
3946
    data.close();
3382
3947
}
3383
3948
 
3384
 
// Did we close all FTP server connection(s)?
 
3949
/**
 
3950
 * Did we close all FTP server connection(s)?
 
3951
 *
 
3952
 \retval true   Both server control and data channels are closed.
 
3953
 \retval false  Either control channel or data is still active.
 
3954
 */
3385
3955
bool
3386
3956
FtpStateData::doneWithServer() const
3387
3957
{
3388
3958
    return ctrl.fd < 0 && data.fd < 0;
3389
3959
}
3390
3960
 
 
3961
/**
 
3962
 * Have we lost the FTP server control channel?
 
3963
 *
 
3964
 \retval true   The server control channel is available.
 
3965
 \retval false  The server control channel is not available.
 
3966
 */
3391
3967
bool
3392
3968
FtpStateData::haveControlChannel(const char *caller_name) const
3393
3969
{
3394
 
    if(doneWithServer())
 
3970
    if (doneWithServer())
3395
3971
        return false;
3396
3972
 
3397
3973
    /* doneWithServer() only checks BOTH channels are closed. */
3398
 
    if(ctrl.fd < 0) {
3399
 
        debugs(9, 1, "WARNING! FTP Server Control channel is closed, but Data channel still active.");
 
3974
    if (ctrl.fd < 0) {
 
3975
        debugs(9, DBG_IMPORTANT, "WARNING! FTP Server Control channel is closed, but Data channel still active.");
3400
3976
        debugs(9, 2, caller_name << ": attempted on a closed FTP channel.");
3401
3977
        return false;
3402
3978
    }
3404
3980
    return true;
3405
3981
}
3406
3982
 
3407
 
// Quickly abort the transaction
3408
 
// TODO: destruction should be sufficient as the destructor should cleanup,
3409
 
// including canceling close handlers
 
3983
/**
 
3984
 * Quickly abort the transaction
 
3985
 *
 
3986
 \todo destruction should be sufficient as the destructor should cleanup,
 
3987
 *      including canceling close handlers
 
3988
 */
3410
3989
void
3411
3990
FtpStateData::abortTransaction(const char *reason)
3412
3991
{
3413
 
    debugs(9,5,HERE << "aborting transaction for " << reason <<
3414
 
        "; FD " << ctrl.fd << ", Data FD " << data.fd << ", this " << this);
 
3992
    debugs(9, 3, HERE << "aborting transaction for " << reason <<
 
3993
           "; FD " << ctrl.fd << ", Data FD " << data.fd << ", this " << this);
3415
3994
    if (ctrl.fd >= 0) {
3416
3995
        comm_close(ctrl.fd);
3417
3996
        return;
3418
3997
    }
3419
 
    
 
3998
 
3420
3999
    fwd->handleUnregisteredServerEnd();
3421
 
    delete this;
 
4000
    deleteThis("FtpStateData::abortTransaction");
 
4001
}
 
4002
 
 
4003
/// creates a data channel Comm close callback
 
4004
AsyncCall::Pointer
 
4005
FtpStateData::dataCloser()
 
4006
{
 
4007
    typedef CommCbMemFunT<FtpStateData, CommCloseCbParams> Dialer;
 
4008
    return asyncCall(9, 5, "FtpStateData::dataClosed",
 
4009
                     Dialer(this, &FtpStateData::dataClosed));
 
4010
}
 
4011
 
 
4012
/// configures the channel with a descriptor and registers a close handler
 
4013
void
 
4014
FtpChannel::opened(int aFd, const AsyncCall::Pointer &aCloser)
 
4015
{
 
4016
    assert(fd < 0);
 
4017
    assert(closer == NULL);
 
4018
 
 
4019
    assert(aFd >= 0);
 
4020
    assert(aCloser != NULL);
 
4021
 
 
4022
    fd = aFd;
 
4023
    closer = aCloser;
 
4024
    comm_add_close_handler(fd, closer);
 
4025
}
 
4026
 
 
4027
/// planned close: removes the close handler and calls comm_close
 
4028
void
 
4029
FtpChannel::close()
 
4030
{
 
4031
    if (fd >= 0) {
 
4032
        comm_remove_close_handler(fd, closer);
 
4033
        closer = NULL;
 
4034
        comm_close(fd); // we do not expect to be called back
 
4035
        fd = -1;
 
4036
    }
 
4037
}
 
4038
 
 
4039
/// just resets fd and close handler
 
4040
void
 
4041
FtpChannel::clear()
 
4042
{
 
4043
    fd = -1;
 
4044
    closer = NULL;
3422
4045
}