📄 ftp.c
字号:
add_to_string(&cmd, "PASS "); if (conn->uri->passwordlen) { struct uri *uri = conn->uri; 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 connection *conn, struct read_buffer *rb){ int response = get_ftp_response(conn, rb, 0, NULL); if (response == -1) { abort_conn_with_state(conn, S_FTP_ERROR); return; } if (!response) { read_from_socket(conn, &conn->socket, rb, ftp_pass_info); set_connection_state(conn, S_LOGIN); 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_conn_with_state(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/* 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){#ifdef CONFIG_IPV6 struct sockaddr_storage data_addr;#endif struct ftp_connection_info *c_i; struct string command; int data_sock; unsigned char pc[6]; c_i = mem_calloc(1, sizeof(*c_i)); if (!c_i) { abort_conn_with_state(conn, S_OUT_OF_MEM); return NULL; } if (!init_string(&command)) { mem_free(c_i); abort_conn_with_state(conn, S_OUT_OF_MEM); return NULL; } conn->info = c_i;#ifdef CONFIG_IPV6 memset(&data_addr, 0, sizeof(data_addr));#endif memset(pc, 0, 6); if (get_opt_bool("protocol.ftp.use_pasv")) c_i->use_pasv = 1;#ifdef CONFIG_IPV6 if (get_opt_bool("protocol.ftp.use_epsv")) c_i->use_epsv = 1; if (!c_i->use_epsv && conn->protocol_family == 1) { data_sock = get_pasv6_socket(conn, conn->socket.fd, &data_addr); if (data_sock < 0) return NULL; conn->data_socket.fd = data_sock; }#endif if (!c_i->use_pasv && conn->protocol_family != 1) { data_sock = get_pasv_socket(conn, conn->socket.fd, pc); if (data_sock < 0) return NULL; conn->data_socket.fd = data_sock; } if (!conn->uri->data) { INTERNAL("conn->uri->data empty"); abort_conn_with_state(conn, S_INTERNAL); return NULL; } if (!conn->uri->datalen || conn->uri->data[conn->uri->datalen - 1] == '/') { /* Commands to get directory listing. */ c_i->dir = 1; c_i->pending_commands = 4; /* ASCII */ add_to_string(&command, "TYPE A"); add_crlf_to_string(&command);#ifdef CONFIG_IPV6 if (conn->protocol_family == 1) if (c_i->use_epsv) add_to_string(&command, "EPSV"); else add_eprtcmd_to_string(&command, (struct sockaddr_in6 *) &data_addr); else#endif if (c_i->use_pasv) add_to_string(&command, "PASV"); else add_portcmd_to_string(&command, pc); add_crlf_to_string(&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. */ c_i->dir = 0; c_i->pending_commands = 3; /* BINARY */ add_to_string(&command, "TYPE I"); add_crlf_to_string(&command);#ifdef CONFIG_IPV6 if (conn->protocol_family == 1) if (c_i->use_epsv) add_to_string(&command, "EPSV"); else add_eprtcmd_to_string(&command, (struct sockaddr_in6 *) &data_addr); else#endif if (c_i->use_pasv) add_to_string(&command, "PASV"); else add_portcmd_to_string(&command, pc); add_crlf_to_string(&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); c_i->rest_sent = 1; c_i->pending_commands++; } add_to_string(&command, "RETR "); add_uri_to_string(&command, conn->uri, URI_PATH); add_crlf_to_string(&command); } c_i->opc = c_i->pending_commands; /* 1 byte is already reserved for cmd_buffer in struct ftp_connection_info. */ c_i = mem_realloc(c_i, sizeof(*c_i) + command.length); if (!c_i) { done_string(&command); abort_conn_with_state(conn, S_OUT_OF_MEM); return NULL; } memcpy(c_i->cmd_buffer, command.source, command.length + 1); done_string(&command); conn->info = c_i; return c_i;}static voidsend_it_line_by_line(struct connection *conn, struct string *cmd){ struct ftp_connection_info *c_i = conn->info; unsigned char *nl = strchr(c_i->cmd_buffer, '\n'); if (!nl) { add_to_string(cmd, c_i->cmd_buffer); return; } nl++; add_bytes_to_string(cmd, c_i->cmd_buffer, nl - c_i->cmd_buffer); memmove(c_i->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_conn_with_state(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 long intget_filesize_from_RETR(unsigned char *data, int data_len){ long int 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 = strtol(&data[pos_file_len], NULL, 10); if (errno) return -1; return file_len;}static intftp_data_connect(struct connection *conn, int family, struct sockaddr_storage *sa, int size_of_sockaddr){ int fd = socket(family, SOCK_STREAM, 0); if (fd < 0 || set_nonblocking_fd(fd) < 0) { abort_conn_with_state(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 connection *conn, struct read_buffer *rb){ struct ftp_connection_info *c_i = conn->info; struct sockaddr_storage sa; int response; if (c_i->pending_commands > 1) { response = get_ftp_response(conn, rb, 0, &sa); if (response == -1) { abort_conn_with_state(conn, S_FTP_ERROR); return; } if (!response) { read_from_socket(conn, &conn->socket, rb, ftp_retr_file); set_connection_state(conn, S_GETH); 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 c_i->pending_commands--; /* XXX: The case values are order numbers of commands. */ switch (c_i->opc - c_i->pending_commands) { case 1: /* TYPE */ break; case 2: /* PORT */ if (response >= 400) { abort_conn_with_state(conn, S_FTP_PORT); return; } break; case 3: /* REST / CWD */ if (response >= 400) { if (c_i->dir) { abort_conn_with_state(conn, S_FTP_NO_FILE); return; } conn->from = 0; } else if (c_i->rest_sent) { /* Following code is related to resume * feature. */ if (response == 350) conn->from = conn->progress.start; /* Come on, don't be nervous ;-). */ if (conn->progress.start >= 0) { /* Update to the real value * which we've got from * Content-Range. */ conn->progress.seek = conn->from; } conn->progress.start = conn->from; } break; default: INTERNAL("WHAT???"); } ftp_send_retr_req(conn, S_GETH); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -