📄 ftp.c
字号:
mode = mimeGetTransferMode(filename); } break; } if (mode == 'I') ftpState->flags.binary = 1; else ftpState->flags.binary = 0; snprintf(cbuf, 1024, "TYPE %c\r\n", mode); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_TYPE;}static voidftpReadType(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; char *path; char *d, *p; debug(9, 3) ("This is ftpReadType\n"); if (code == 200) { p = path = xstrdup(strBuf(ftpState->request->urlpath)); if (*p == '/') p++; while (*p) { d = p; p += strcspn(p, "/"); if (*p) *p++ = '\0'; rfc1738_unescape(d); wordlistAdd(&ftpState->pathcomps, d); } xfree(path); if (ftpState->pathcomps) ftpTraverseDirectory(ftpState); else ftpListDir(ftpState); } else { ftpFail(ftpState); }}static voidftpTraverseDirectory(FtpStateData * ftpState){ wordlist *w; debug(9, 4) ("ftpTraverseDirectory %s\n", ftpState->filepath ? ftpState->filepath : "<NULL>"); safe_free(ftpState->filepath); /* Done? */ if (ftpState->pathcomps == NULL) { debug(9, 3) ("the final component was a directory\n"); ftpListDir(ftpState); return; } /* Go to next path component */ w = ftpState->pathcomps; ftpState->filepath = w->key; ftpState->pathcomps = w->next; xfree(w); /* Check if we are to CWD or RETR */ if (ftpState->pathcomps != NULL || ftpState->flags.isdir) { ftpSendCwd(ftpState); } else { debug(9, 3) ("final component is probably a file\n"); ftpGetFile(ftpState); return; }}static voidftpSendCwd(FtpStateData * ftpState){ char *path = ftpState->filepath; debug(9, 3) ("ftpSendCwd\n"); if (!strcmp(path, "..") || !strcmp(path, "/")) { ftpState->flags.no_dotdot = 1; } else { ftpState->flags.no_dotdot = 0; } if (*path) snprintf(cbuf, 1024, "CWD %s\r\n", path); else snprintf(cbuf, 1024, "CWD\r\n"); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_CWD;}static voidftpReadCwd(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadCwd\n"); if (code >= 200 && code < 300) { /* CWD OK */ ftpUnhack(ftpState); if (ftpState->cwd_message) wordlistDestroy(&ftpState->cwd_message); ftpState->cwd_message = ftpState->ctrl.message; ftpState->ctrl.message = NULL; /* Continue to traverse the path */ ftpTraverseDirectory(ftpState); } else { /* CWD FAILED */ if (!ftpState->flags.put) ftpFail(ftpState); else ftpTryMkdir(ftpState); }}static voidftpTryMkdir(FtpStateData * ftpState){ char *path = ftpState->filepath; debug(9, 3) ("ftpTryMkdir: with path=%s\n", path); snprintf(cbuf, 1024, "MKD %s\r\n", path); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_MKDIR;}static voidftpReadMkdir(FtpStateData * ftpState){ char *path = ftpState->filepath; int code = ftpState->ctrl.replycode; debug(9, 3) ("ftpReadMkdir: path %s, code %d\n", path, code); if (code == 257) { /* success */ ftpSendCwd(ftpState); } else if (code == 550) { /* dir exists */ if (ftpState->flags.put_mkdir) { ftpState->flags.put_mkdir = 1; ftpSendCwd(ftpState); } else ftpSendReply(ftpState); } else ftpSendReply(ftpState);}static voidftpGetFile(FtpStateData * ftpState){ assert(*ftpState->filepath != '\0'); ftpState->flags.isdir = 0; ftpSendMdtm(ftpState);}static voidftpListDir(FtpStateData * ftpState){ if (!ftpState->flags.isdir) { debug(9, 3) ("Directory path did not end in /\n"); strcat(ftpState->title_url, "/"); ftpState->flags.isdir = 1; ftpState->flags.use_base = 1; } ftpSendPasv(ftpState);}static voidftpSendMdtm(FtpStateData * ftpState){ assert(*ftpState->filepath != '\0'); snprintf(cbuf, 1024, "MDTM %s\r\n", ftpState->filepath); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_MDTM;}static voidftpReadMdtm(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadMdtm\n"); if (code == 213) { ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply); ftpUnhack(ftpState); } else if (code < 0) { ftpFail(ftpState); } ftpSendSize(ftpState);}static voidftpSendSize(FtpStateData * ftpState){ /* Only send SIZE for binary transfers. The returned size * is useless on ASCII transfers */ if (ftpState->flags.binary) { assert(ftpState->filepath != NULL); assert(*ftpState->filepath != '\0'); snprintf(cbuf, 1024, "SIZE %s\r\n", ftpState->filepath); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_SIZE; } else /* Skip to next state no non-binary transfers */ ftpSendPasv(ftpState);}static voidftpReadSize(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadSize\n"); if (code == 213) { ftpUnhack(ftpState); ftpState->size = atoi(ftpState->ctrl.last_reply); if (ftpState->size == 0) { debug(9, 2) ("ftpReadSize: SIZE reported %s on %s\n", ftpState->ctrl.last_reply, ftpState->title_url); ftpState->size = -1; } } else if (code < 0) { ftpFail(ftpState); } ftpSendPasv(ftpState);}static voidftpSendPasv(FtpStateData * ftpState){ int fd; struct sockaddr_in addr; socklen_t addr_len; if (ftpState->data.fd >= 0) { if (!ftpState->flags.datachannel_hack) { /* We are already connected, reuse this connection. */ ftpRestOrList(ftpState); return; } else { /* Close old connection */ comm_close(ftpState->data.fd); ftpState->data.fd = -1; } } if (!ftpState->flags.pasv_supported) { ftpSendPort(ftpState); return; } addr_len = sizeof(addr); if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) { debug(9, 0) ("ftpSendPasv: getsockname(%d,..): %s\n", ftpState->ctrl.fd, xstrerror()); addr.sin_addr = Config.Addrs.tcp_outgoing; } /* Open data channel with the same local address as control channel */ fd = comm_open(SOCK_STREAM, 0, addr.sin_addr, 0, COMM_NONBLOCKING, storeUrl(ftpState->entry)); debug(9, 3) ("ftpSendPasv: Unconnected data socket created on FD %d\n", fd); if (fd < 0) { ftpFail(ftpState); return; } /* * No comm_add_close_handler() here. If we have both ctrl and * data FD's call ftpStateFree() upon close, then we have * to delete the close handler which did NOT get called * to prevent ftpStateFree() getting called twice. * Instead we'll always call comm_close() on the ctrl FD. */ ftpState->data.fd = fd; snprintf(cbuf, 1024, "PASV\r\n"); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_PASV;}static voidftpReadPasv(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; int h1, h2, h3, h4; int p1, p2; int n; u_short port; int fd = ftpState->data.fd; char *buf = ftpState->ctrl.last_reply; LOCAL_ARRAY(char, junk, 1024); debug(9, 3) ("This is ftpReadPasv\n"); if (code != 227) { debug(9, 3) ("PASV not supported by remote end\n"); comm_close(ftpState->data.fd); ftpState->data.fd = -1; ftpSendPort(ftpState); return; } if ((int) strlen(buf) > 1024) { debug(9, 1) ("ftpReadPasv: Avoiding potential buffer overflow\n"); ftpSendPort(ftpState); return; } /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */ debug(9, 5) ("scanning: %s\n", buf); n = sscanf(buf, "%[^0123456789]%d,%d,%d,%d,%d,%d", junk, &h1, &h2, &h3, &h4, &p1, &p2); if (n != 7 || p1 < 0 || p2 < 0 || p1 > 255 || p2 > 255) { debug(9, 3) ("Bad 227 reply\n"); debug(9, 3) ("n=%d, p1=%d, p2=%d\n", n, p1, p2); ftpSendPort(ftpState); return; } snprintf(junk, 1024, "%d.%d.%d.%d", h1, h2, h3, h4); if (!safe_inet_addr(junk, NULL)) { debug(9, 1) ("unsafe address (%s)\n", junk); ftpSendPort(ftpState); return; } port = ((p1 << 8) + p2); if (0 == port) { debug(9, 1) ("ftpReadPasv: Invalid PASV reply: %s\n", buf); ftpSendPort(ftpState); return; } debug(9, 5) ("ftpReadPasv: connecting to %s, port %d\n", junk, port); ftpState->data.port = port; ftpState->data.host = xstrdup(junk); commConnectStart(fd, junk, port, ftpPasvCallback, ftpState);}static voidftpPasvCallback(int fd, int status, void *data){ FtpStateData *ftpState = data; request_t *request = ftpState->request; ErrorState *err; debug(9, 3) ("ftpPasvCallback\n"); if (status != COMM_OK) { err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE); err->xerrno = errno; err->host = xstrdup(ftpState->data.host); err->port = ftpState->data.port; err->request = requestLink(request); errorAppendEntry(ftpState->entry, err); comm_close(ftpState->ctrl.fd); return; } ftpRestOrList(ftpState);}static intftpOpenListenSocket(FtpStateData * ftpState, int fallback){ int fd; struct sockaddr_in addr; socklen_t addr_len; int on = 1; u_short port = 0; /* * Set up a listen socket on the same local address as the * control connection. */ addr_len = sizeof(addr); if (getsockname(ftpState->ctrl.fd, (struct sockaddr *) &addr, &addr_len)) { debug(9, 0) ("ftpOpenListenSocket: getsockname(%d,..): %s\n", ftpState->ctrl.fd, xstrerror()); return -1; } /* * REUSEADDR is needed in fallback mode, since the same port is * used for both control and data. */ if (fallback) { setsockopt(ftpState->ctrl.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); port = ntohs(addr.sin_port); } fd = comm_open(SOCK_STREAM, 0, addr.sin_addr, port, COMM_NONBLOCKING | (fallback ? COMM_REUSEADDR : 0), storeUrl(ftpState->entry)); debug(9, 3) ("ftpOpenListenSocket: Unconnected data socket created on FD %d\n", fd); if (fd < 0) { debug(9, 0) ("ftpOpenListenSocket: comm_open failed\n"); return -1; } if (comm_listen(fd) < 0) { comm_close(fd); return -1; } ftpState->data.fd = fd; ftpState->data.port = comm_local_port(fd);; ftpState->data.host = NULL; return fd;}static voidftpSendPort(FtpStateData * ftpState){ int fd; struct sockaddr_in addr; socklen_t addr_len; unsigned char *addrptr; unsigned char *portptr; debug(9, 3) ("This is ftpSendPort\n"); ftpState->flags.pasv_supported = 0; fd = ftpOpenListenSocket(ftpState, 0); addr_len = sizeof(addr); if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) { debug(9, 0) ("ftpSendPort: getsockname(%d,..): %s\n", fd, xstrerror()); /* XXX Need to set error message */ ftpFail(ftpState); return; } addrptr = (unsigned char *) &addr.sin_addr.s_addr; portptr = (unsigned char *) &addr.sin_port; snprintf(cbuf, 1024, "PORT %d,%d,%d,%d,%d,%d\r\n", addrptr[0], addrptr[1], addrptr[2], addrptr[3], portptr[0], portptr[1]); ftpWriteCommand(cbuf, ftpState); ftpState->state = SENT_PORT;}static voidftpReadPort(FtpStateData * ftpState){ int code = ftpState->ctrl.replycode; debug(9, 3) ("This is ftpReadPort\n"); if (code != 200) { /* Fall back on using the same port as the control connection */ debug(9, 3) ("PORT not supported by remote end\n"); comm_close(ftpState->data.fd); ftpState->data.fd = -1; ftpOpenListenSocket(ftpState, 1); } ftpRestOrList(ftpState);}/* "read" handler to accept data connection */static voidftpAcceptDataConnection(int fd, void *data){ FtpStateData *ftpState = data; struct sockaddr_in peer, me; debug(9, 3) ("ftpAcceptDataConnection\n"); fd = comm_accept(fd, &peer, &me); if (fd < 0) { debug(9, 1) ("ftpHandleDataAccept: comm_accept(%d): %s", fd, xstrerror()); /* XXX Need to set error message */ ftpFail(ftpState); return; } /* Replace the Listen socket with the accepted data socket */ comm_close(ftpState->data.fd); debug(9, 3) ("ftpAcceptDataConnection: Connected data socket on FD %d\n", fd); ftpState->data.fd = fd; ftpState->data.port = ntohs(peer.sin_port); ftpState->data.host = xstrdup(inet_ntoa(peer.sin_addr)); commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL); commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); /* XXX We should have a flag to track connect state... * host NULL -> not connected, port == local port * host set -> connected, port == remote port */ /* Restart state (SENT_NLST/LIST/RETR) */ FTP_SM_FUNCS[ftpState->state] (ftpState);}static void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -