📄 ftp.c
字号:
add_bytes_to_string(&cmd, uri->password, uri->passwordlen); } else if (auth && auth->valid) { if (!auth_user_matching_uri(auth, conn->uri)) { prompt_username_pw(conn); return; } add_to_string(&cmd, auth->password); } else { add_to_string(&cmd, get_opt_str("protocol.ftp.anon_passwd")); } add_crlf_to_string(&cmd); send_cmd(conn, &cmd, (void *) ftp_pass_info, S_LOGIN);}/* Parse PASS command response. */static voidftp_pass_info(struct socket *socket, struct read_buffer *rb){ struct connection *conn = socket->conn; int response = get_ftp_response(conn, rb, 0, NULL); if (response == -1) { abort_connection(conn, S_FTP_ERROR); return; } if (!response) { read_from_socket(conn->socket, rb, S_LOGIN, ftp_pass_info); return; } /* RFC959 says that possible response codes for PASS are: * 202 Command not implemented, superfluous at this site. * 230 User logged in, proceed. * 332 Need account for login. * 421 Service not available, closing control connection. * 500 Syntax error, command unrecognized. * 501 Syntax error in parameters or arguments. * 503 Bad sequence of commands. * 530 Not logged in. */ if (response == 332 || response >= 500) { /* If we didn't have a user, we tried anonymous. But it failed, so ask for a * user and password */ prompt_username_pw(conn); return; } if (response >= 400) { abort_connection(conn, S_FTP_UNAVAIL); return; } ftp_send_retr_req(conn, S_GETH);}/* Construct PORT command. */static voidadd_portcmd_to_string(struct string *string, unsigned char *pc){ /* From RFC 959: DATA PORT (PORT) * * The argument is a HOST-PORT specification for the data port * to be used in data connection. There are defaults for both * the user and server data ports, and under normal * circumstances this command and its reply are not needed. If * this command is used, the argument is the concatenation of a * 32-bit internet host address and a 16-bit TCP port address. * This address information is broken into 8-bit fields and the * value of each field is transmitted as a decimal number (in * character string representation). The fields are separated * by commas. A port command would be: * * PORT h1,h2,h3,h4,p1,p2 * * where h1 is the high order 8 bits of the internet host * address. */ add_to_string(string, "PORT "); add_long_to_string(string, pc[0]); add_char_to_string(string, ','); add_long_to_string(string, pc[1]); add_char_to_string(string, ','); add_long_to_string(string, pc[2]); add_char_to_string(string, ','); add_long_to_string(string, pc[3]); add_char_to_string(string, ','); add_long_to_string(string, pc[4]); add_char_to_string(string, ','); add_long_to_string(string, pc[5]);}#ifdef CONFIG_IPV6/* Construct EPRT command. */static voidadd_eprtcmd_to_string(struct string *string, struct sockaddr_in6 *addr){ unsigned char addr_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN); /* From RFC 2428: EPRT * * The format of EPRT is: * * EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d> * * <net-prt>: * AF Number Protocol * --------- -------- * 1 Internet Protocol, Version 4 [Pos81a] * 2 Internet Protocol, Version 6 [DH96] */ add_to_string(string, "EPRT |2|"); add_to_string(string, addr_str); add_char_to_string(string, '|'); add_long_to_string(string, ntohs(addr->sin6_port)); add_char_to_string(string, '|');}#endif/* Depending on options, get proper ftp data socket and command. * It appends ftp command (either PASV,PORT,EPSV or EPRT) to @command * string. * When PORT or EPRT are used, related sockets are created. * It returns 0 on error (data socket creation failure). */static intget_ftp_data_socket(struct connection *conn, struct string *command){ struct ftp_connection_info *ftp = conn->info; ftp->use_pasv = get_opt_bool("protocol.ftp.use_pasv");#ifdef CONFIG_IPV6 ftp->use_epsv = get_opt_bool("protocol.ftp.use_epsv"); if (conn->socket->protocol_family == EL_PF_INET6) { if (ftp->use_epsv) { add_to_string(command, "EPSV"); } else { struct sockaddr_storage data_addr; int data_sock; memset(&data_addr, 0, sizeof(data_addr)); data_sock = get_pasv_socket(conn->socket, &data_addr); if (data_sock < 0) return 0; conn->data_socket->fd = data_sock; add_eprtcmd_to_string(command, (struct sockaddr_in6 *) &data_addr); } } else#endif { if (ftp->use_pasv) { add_to_string(command, "PASV"); } else { struct sockaddr_in sa; unsigned char pc[6]; int data_sock; memset(pc, 0, sizeof(pc)); data_sock = get_pasv_socket(conn->socket, (struct sockaddr_storage *) &sa); if (data_sock < 0) return 0; memcpy(pc, &sa.sin_addr.s_addr, 4); memcpy(pc + 4, &sa.sin_port, 2); conn->data_socket->fd = data_sock; add_portcmd_to_string(command, pc); } } add_crlf_to_string(command); return 1;}/* Create passive socket and add appropriate announcing commands to str. Then * go and retrieve appropriate object from server. * Returns NULL if error. */static struct ftp_connection_info *add_file_cmd_to_str(struct connection *conn){ struct ftp_connection_info *ftp; struct string command, ftp_data_command; if (!conn->uri->data) { INTERNAL("conn->uri->data empty"); abort_connection(conn, S_INTERNAL); return NULL; } ftp = mem_calloc(1, sizeof(*ftp)); if (!ftp) { abort_connection(conn, S_OUT_OF_MEM); return NULL; } conn->info = ftp; /* Freed when connection is destroyed. */ if (!init_string(&command)) { abort_connection(conn, S_OUT_OF_MEM); return NULL; } if (!init_string(&ftp_data_command)) { done_string(&command); abort_connection(conn, S_OUT_OF_MEM); return NULL; } if (!get_ftp_data_socket(conn, &ftp_data_command)) { done_string(&command); done_string(&ftp_data_command); INTERNAL("Ftp data socket failure"); abort_connection(conn, S_INTERNAL); return NULL; } if (!conn->uri->datalen || conn->uri->data[conn->uri->datalen - 1] == '/') { /* Commands to get directory listing. */ ftp->dir = 1; ftp->pending_commands = 4; /* ASCII */ add_to_string(&command, "TYPE A"); add_crlf_to_string(&command); add_string_to_string(&command, &ftp_data_command); add_to_string(&command, "CWD "); add_uri_to_string(&command, conn->uri, URI_PATH); add_crlf_to_string(&command); add_to_string(&command, "LIST"); add_crlf_to_string(&command); conn->from = 0; } else { /* Commands to get a file. */ ftp->dir = 0; ftp->pending_commands = 3; /* BINARY */ add_to_string(&command, "TYPE I"); add_crlf_to_string(&command); add_string_to_string(&command, &ftp_data_command); if (conn->from || conn->progress->start > 0) { add_to_string(&command, "REST "); add_long_to_string(&command, conn->from ? conn->from : conn->progress->start); add_crlf_to_string(&command); ftp->rest_sent = 1; ftp->pending_commands++; } add_to_string(&command, "RETR "); add_uri_to_string(&command, conn->uri, URI_PATH); add_crlf_to_string(&command); } done_string(&ftp_data_command); ftp->opc = ftp->pending_commands; /* 1 byte is already reserved for cmd_buffer in struct ftp_connection_info. */ ftp = mem_realloc(ftp, sizeof(*ftp) + command.length); if (!ftp) { done_string(&command); abort_connection(conn, S_OUT_OF_MEM); return NULL; } memcpy(ftp->cmd_buffer, command.source, command.length + 1); done_string(&command); conn->info = ftp; return ftp;}static voidsend_it_line_by_line(struct connection *conn, struct string *cmd){ struct ftp_connection_info *ftp = conn->info; unsigned char *nl = strchr(ftp->cmd_buffer, '\n'); if (!nl) { add_to_string(cmd, ftp->cmd_buffer); return; } nl++; add_bytes_to_string(cmd, ftp->cmd_buffer, nl - ftp->cmd_buffer); memmove(ftp->cmd_buffer, nl, strlen(nl) + 1);}/* Send commands to retrieve file or directory. */static voidftp_send_retr_req(struct connection *conn, int state){ struct string cmd; if (!init_string(&cmd)) { abort_connection(conn, S_OUT_OF_MEM); return; } /* We don't save return value from add_file_cmd_to_str(), as it's saved * in conn->info as well. */ if (!conn->info && !add_file_cmd_to_str(conn)) { done_string(&cmd); return; } /* Send it line-by-line. */ send_it_line_by_line(conn, &cmd); send_cmd(conn, &cmd, (void *) ftp_retr_file, state);}/* Parse RETR response and return file size or -1 on error. */static off_tget_filesize_from_RETR(unsigned char *data, int data_len){ off_t file_len; int pos; int pos_file_len = 0; /* Getting file size from text response.. */ /* 150 Opening BINARY mode data connection for hello-1.0-1.1.diff.gz (16452 bytes). */ for (pos = 0; pos < data_len && data[pos] != ASCII_LF; pos++) if (data[pos] == '(') pos_file_len = pos; if (!pos_file_len || pos_file_len == data_len - 1) return -1; pos_file_len++; if (!isdigit(data[pos_file_len])) return -1; for (pos = pos_file_len; pos < data_len; pos++) if (!isdigit(data[pos])) goto next; return -1;next: for (; pos < data_len; pos++) if (data[pos] != ' ') break; if (pos + 4 > data_len) return -1; if (strncasecmp(&data[pos], "byte", 4)) return -1; errno = 0; file_len = (off_t) strtol(&data[pos_file_len], NULL, 10); if (errno) return -1; return file_len;}static intftp_data_connect(struct connection *conn, int pf, struct sockaddr_storage *sa, int size_of_sockaddr){ int fd = socket(pf, SOCK_STREAM, 0); if (fd < 0 || set_nonblocking_fd(fd) < 0) { abort_connection(conn, S_FTP_ERROR); return -1; } set_ip_tos_throughput(fd); conn->data_socket->fd = fd; /* XXX: We ignore connect() errors here. */ connect(fd, (struct sockaddr *) sa, size_of_sockaddr); return 0;}static voidftp_retr_file(struct socket *socket, struct read_buffer *rb){ struct connection *conn = socket->conn; struct ftp_connection_info *ftp = conn->info; int response; if (ftp->pending_commands > 1) { struct sockaddr_storage sa; response = get_ftp_response(conn, rb, 0, &sa); if (response == -1) { abort_connection(conn, S_FTP_ERROR); return; } if (!response) { read_from_socket(conn->socket, rb, S_GETH, ftp_retr_file); return; } if (response == 227) { if (ftp_data_connect(conn, PF_INET, &sa, sizeof(struct sockaddr_in))) return; }#ifdef CONFIG_IPV6 if (response == 229) { if (ftp_data_connect(conn, PF_INET6, &sa, sizeof(struct sockaddr_in6))) return; }#endif ftp->pending_commands--; /* XXX: The case values are order numbers of commands. */ switch (ftp->opc - ftp->pending_commands) { case 1: /* TYPE */ break; case 2: /* PORT */ if (response >= 400) { abort_connection(conn, S_FTP_PORT); return; } break; case 3: /* REST / CWD */ if (response >= 400) { if (ftp->dir) { abort_connection(conn, S_FTP_NO_FILE); return; } conn->from = 0; } else if (ftp->rest_sent) { /* Following code is related to resume
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -