📄 ftp.c
字号:
/* * $Id: ftp.c,v 1.280.2.9 1999/05/11 20:34:36 wessels Exp $ * * DEBUG: section 9 File Transfer Protocol (FTP) * AUTHOR: Harvest Derived * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from the * Internet community. Development is led by Duane Wessels of the * National Laboratory for Applied Network Research and funded by the * National Science Foundation. Squid is Copyrighted (C) 1998 by * Duane Wessels and the University of California San Diego. Please * see the COPYRIGHT file for full details. Squid incorporates * software developed and/or copyrighted by other sources. Please see * the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */#include "squid.h"static const char *const crlf = "\r\n";static char cbuf[1024];typedef enum { BEGIN, SENT_USER, SENT_PASS, SENT_TYPE, SENT_MDTM, SENT_SIZE, SENT_PORT, SENT_PASV, SENT_CWD, SENT_LIST, SENT_NLST, SENT_REST, SENT_RETR, SENT_STOR, SENT_QUIT, READING_DATA, WRITING_DATA, SENT_MKDIR} ftp_state_t;struct _ftp_flags { unsigned int isdir:1; unsigned int pasv_supported:1; unsigned int skip_whitespace:1; unsigned int rest_supported:1; unsigned int pasv_only:1; unsigned int authenticated:1; unsigned int http_header_sent:1; unsigned int tried_nlst:1; unsigned int use_base:1; unsigned int root_dir:1; unsigned int no_dotdot:1; unsigned int html_header_sent:1; unsigned int binary:1; unsigned int try_slash_hack:1; unsigned int put:1; unsigned int put_mkdir:1; unsigned int listformat_unknown:1; unsigned int datachannel_hack:1;};typedef struct _Ftpdata { StoreEntry *entry; request_t *request; char user[MAX_URL]; char password[MAX_URL]; char *reply_hdr; int reply_hdr_state; char *title_url; int conn_att; int login_att; ftp_state_t state; time_t mdtm; int size; wordlist *pathcomps; char *filepath; int restart_offset; int restarted_offset; int rest_att; char *proxy_host; size_t list_width; wordlist *cwd_message; char *old_request; char *old_reply; char *old_filepath; char typecode; struct { int fd; char *buf; size_t size; off_t offset; FREE *freefunc; wordlist *message; char *last_command; char *last_reply; int replycode; } ctrl; struct { int fd; char *buf; size_t size; off_t offset; FREE *freefunc; char *host; u_short port; } data; struct _ftp_flags flags; FwdState *fwd;} FtpStateData;typedef struct { char type; int size; char *date; char *name; char *showname; char *link;} ftpListParts;typedef void (FTPSM) (FtpStateData *);#define FTP_LOGIN_ESCAPED 1#define FTP_LOGIN_NOT_ESCAPED 0/* Local functions */static CNCB ftpPasvCallback;static PF ftpDataRead;static PF ftpStateFree;static PF ftpPumpClosedData;static PF ftpTimeout;static PF ftpReadControlReply;static CWCB ftpWriteCommandCallback;static void ftpLoginParser(const char *, FtpStateData *, int escaped);static wordlist *ftpParseControlReply(char *, size_t, int *, int *);static int ftpRestartable(FtpStateData * ftpState);static void ftpAppendSuccessHeader(FtpStateData * ftpState);static void ftpAuthRequired(HttpReply * reply, request_t * request, const char *realm);static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState);static void ftpPutStart(FtpStateData *);static CWCB ftpPutTransferDone;static void ftpUnhack(FtpStateData * ftpState);static void ftpScheduleReadControlReply(FtpStateData *, int);static void ftpHandleControlReply(FtpStateData *);static char *ftpHtmlifyListEntry(char *line, FtpStateData * ftpState);/* State machine functions * send == state transition * read == wait for response, and select next state transition * other == Transition logic */static FTPSM ftpReadWelcome;static FTPSM ftpSendUser;static FTPSM ftpReadUser;static FTPSM ftpSendPass;static FTPSM ftpReadPass;static FTPSM ftpSendType;static FTPSM ftpReadType;static FTPSM ftpSendMdtm;static FTPSM ftpReadMdtm;static FTPSM ftpSendSize;static FTPSM ftpReadSize;static FTPSM ftpSendPort;static FTPSM ftpReadPort;static FTPSM ftpSendPasv;static FTPSM ftpReadPasv;static FTPSM ftpTraverseDirectory;static FTPSM ftpListDir;static FTPSM ftpGetFile;static FTPSM ftpSendCwd;static FTPSM ftpReadCwd;static FTPSM ftpSendList;static FTPSM ftpSendNlst;static FTPSM ftpReadList;static FTPSM ftpSendRest;static FTPSM ftpReadRest;static FTPSM ftpSendRetr;static FTPSM ftpReadRetr;static FTPSM ftpReadTransferDone;static FTPSM ftpSendQuit;static FTPSM ftpReadQuit;static FTPSM ftpFail;static FTPSM ftpDataTransferDone;static FTPSM ftpRestOrList;static FTPSM ftpSendStor;static FTPSM ftpReadStor;static FTPSM ftpSendReply;static FTPSM ftpTryMkdir;static FTPSM ftpReadMkdir;/************************************************** State Machine Description (excluding hacks) ***************************************************From To---------------------------------------Welcome UserUser PassPass TypeType TraverseDirectory / GetFileTraverseDirectory Cwd / GetFile / ListDirCwd TraverseDirectoryGetFile MdtmMdtm SizeSize PasvListDir PasvPasv RestOrListRestOrList Rest / Retr / Nlst / ListRest RetrRetr / Nlst / List (ftpDataRead on datachannel)(ftpDataRead) ReadTransferDoneReadTransferDone DataTransferDoneDataTransferDone QuitQuit -************************************************/FTPSM *FTP_SM_FUNCS[] ={ ftpReadWelcome, ftpReadUser, ftpReadPass, ftpReadType, ftpReadMdtm, ftpReadSize, ftpReadPort, ftpReadPasv, ftpReadCwd, ftpReadList, /* SENT_LIST */ ftpReadList, /* SENT_NLST */ ftpReadRest, ftpReadRetr, ftpReadStor, ftpReadQuit, ftpReadTransferDone, ftpSendReply, ftpReadMkdir};static voidftpStateFree(int fdnotused, void *data){ FtpStateData *ftpState = data; if (ftpState == NULL) return; debug(9, 3) ("ftpStateFree: %s\n", storeUrl(ftpState->entry)); storeUnregisterAbort(ftpState->entry); storeUnlockObject(ftpState->entry); if (ftpState->reply_hdr) { memFree(ftpState->reply_hdr, MEM_8K_BUF); /* this seems unnecessary, but people report SEGV's * when freeing memory in this function */ ftpState->reply_hdr = NULL; } requestUnlink(ftpState->request); if (ftpState->ctrl.buf) { ftpState->ctrl.freefunc(ftpState->ctrl.buf); /* this seems unnecessary, but people report SEGV's * when freeing memory in this function */ ftpState->ctrl.buf = NULL; } if (ftpState->data.buf) { ftpState->data.freefunc(ftpState->data.buf); /* this seems unnecessary, but people report SEGV's * when freeing memory in this function */ ftpState->data.buf = NULL; } if (ftpState->pathcomps) wordlistDestroy(&ftpState->pathcomps); if (ftpState->ctrl.message) wordlistDestroy(&ftpState->ctrl.message); if (ftpState->cwd_message) wordlistDestroy(&ftpState->cwd_message); safe_free(ftpState->ctrl.last_reply); safe_free(ftpState->ctrl.last_command); safe_free(ftpState->old_request); safe_free(ftpState->old_reply); safe_free(ftpState->old_filepath); safe_free(ftpState->title_url); safe_free(ftpState->filepath); safe_free(ftpState->data.host); if (ftpState->data.fd > -1) { comm_close(ftpState->data.fd); ftpState->data.fd = -1; } cbdataFree(ftpState);}static voidftpLoginParser(const char *login, FtpStateData * ftpState, int escaped){ char *s = NULL; xstrncpy(ftpState->user, login, MAX_URL); if ((s = strchr(ftpState->user, ':'))) { *s = 0; xstrncpy(ftpState->password, s + 1, MAX_URL); if (escaped) rfc1738_unescape(ftpState->password); } else { xstrncpy(ftpState->password, null_string, MAX_URL); } if (escaped) rfc1738_unescape(ftpState->user); if (ftpState->user[0] || ftpState->password[0]) return; xstrncpy(ftpState->user, "anonymous", MAX_URL); xstrncpy(ftpState->password, Config.Ftp.anon_user, MAX_URL);}static voidftpTimeout(int fd, void *data){ FtpStateData *ftpState = data; StoreEntry *entry = ftpState->entry; debug(9, 4) ("ftpTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); if (entry->store_status == STORE_PENDING) { if (entry->mem_obj->inmem_hi == 0) { fwdFail(ftpState->fwd, errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT)); } } if (ftpState->data.fd > -1) { comm_close(ftpState->data.fd); ftpState->data.fd = -1; } comm_close(ftpState->ctrl.fd); /* don't modify ftpState here, it has been freed */}static voidftpListingStart(FtpStateData * ftpState){ StoreEntry *e = ftpState->entry; wordlist *w; char *dirup; int i, j, k; storeBuffer(e); storeAppendPrintf(e, "<!-- HTML listing generated by Squid %s -->\n", version_string); storeAppendPrintf(e, "<!-- %s -->\n", mkrfc1123(squid_curtime)); storeAppendPrintf(e, "<HTML><HEAD><TITLE>\n"); storeAppendPrintf(e, "FTP Directory: %s\n", ftpState->title_url); storeAppendPrintf(e, "</TITLE>\n"); if (ftpState->flags.use_base) storeAppendPrintf(e, "<BASE HREF=\"%s\">\n", ftpState->title_url); storeAppendPrintf(e, "</HEAD><BODY>\n"); if (ftpState->cwd_message) { storeAppendPrintf(e, "<PRE>\n"); for (w = ftpState->cwd_message; w; w = w->next) storeAppendPrintf(e, "%s\n", w->key); storeAppendPrintf(e, "</PRE>\n"); storeAppendPrintf(e, "<HR>\n"); wordlistDestroy(&ftpState->cwd_message); } storeAppendPrintf(e, "<H2>\n"); storeAppendPrintf(e, "FTP Directory: "); /* "ftp://" == 6 characters */ assert(strlen(ftpState->title_url) >= 6); for (i = 6, j = 0; ftpState->title_url[i]; j = i) { storeAppendPrintf(e, "<A HREF=\""); i += strcspn(&ftpState->title_url[i], "/"); if (ftpState->title_url[i] == '/') i++; for (k = 0; k < i; k++) storeAppendPrintf(e, "%c", ftpState->title_url[k]); storeAppendPrintf(e, "\">"); for (k = j; k < i - 1; k++) storeAppendPrintf(e, "%c", ftpState->title_url[k]); if (ftpState->title_url[k] != '/') storeAppendPrintf(e, "%c", ftpState->title_url[k++]); storeAppendPrintf(e, "</A>"); if (k < i) storeAppendPrintf(e, "%c", ftpState->title_url[k++]); if (i == j) { /* Error guard, or "assert" */ storeAppendPrintf(e, "ERROR: Failed to parse URL: %s\n", ftpState->title_url); debug(9, 0) ("Failed to parse URL: %s\n", ftpState->title_url); break; } } storeAppendPrintf(e, "</H2>\n"); storeAppendPrintf(e, "<PRE>\n"); dirup = ftpHtmlifyListEntry("<internal-dirup>", ftpState); storeAppend(e, dirup, strlen(dirup)); storeBufferFlush(e); ftpState->flags.html_header_sent = 1;}static voidftpListingFinish(FtpStateData * ftpState){ StoreEntry *e = ftpState->entry; storeBuffer(e); storeAppendPrintf(e, "</PRE>\n"); if (ftpState->flags.listformat_unknown && !ftpState->flags.tried_nlst) { storeAppendPrintf(e, "<A HREF=\"./;type=d\">[As plain directory]</A>\n"); } else if (ftpState->typecode == 'D') { storeAppendPrintf(e, "<A HREF=\"./\">[As extended directory]</A>\n"); } storeAppendPrintf(e, "<HR>\n"); storeAppendPrintf(e, "<ADDRESS>\n"); storeAppendPrintf(e, "Generated %s by %s (<a href=\"http://squid.nlanr.net/Squid/\">%s</a>)\n", mkrfc1123(squid_curtime), getMyHostname(), full_appname_string, version_string); storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n"); storeBufferFlush(e);}static const char *Month[] ={ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};static intis_month(const char *buf){ int i; for (i = 0; i < 12; i++) if (!strcasecmp(buf, Month[i])) return 1; return 0;}static voidftpListPartsFree(ftpListParts ** parts){ safe_free((*parts)->date); safe_free((*parts)->name); safe_free((*parts)->showname); safe_free((*parts)->link); safe_free(*parts);}#define MAX_TOKENS 64#define SCAN_FTP1 "%[0123456789]"#define SCAN_FTP2 "%[0123456789:]"#define SCAN_FTP3 "%[0123456789]-%[0123456789]-%[0123456789]"#define SCAN_FTP4 "%[0123456789]:%[0123456789]%[AaPp]%[Mm]"static ftpListParts *ftpListParseParts(const char *buf, struct _ftp_flags flags){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -