📄 ftp.c
字号:
/* ftp.c * ftp:// processing * (c) 2002 Mikulas Patocka * This file is a part of the Links program, released under GPL. */#include "links.h"#define FTP_BUF 16384struct ftp_connection_info { int pending_commands; int opc; int pasv; int dir; int rest_sent; int conn_st; int d; int dpos; int buf_pos; unsigned char ftp_buffer[FTP_BUF]; unsigned char cmdbuf[1];};void ftp_get_banner(struct connection *);void ftp_got_banner(struct connection *, struct read_buffer *);void ftp_login(struct connection *);void ftp_logged(struct connection *);void ftp_sent_passwd(struct connection *);void ftp_got_reply(struct connection *, struct read_buffer *);void ftp_got_info(struct connection *, struct read_buffer *);void ftp_got_user_info(struct connection *, struct read_buffer *);void ftp_dummy_info(struct connection *, struct read_buffer *);void ftp_pass_info(struct connection *, struct read_buffer *);void ftp_send_retr_req(struct connection *, int);struct ftp_connection_info *add_file_cmd_to_str(struct connection *);void ftp_retr_1(struct connection *);void ftp_retr_file(struct connection *, struct read_buffer *);void ftp_got_final_response(struct connection *, struct read_buffer *);void created_data_connection(struct connection *);void got_something_from_data_connection(struct connection *);void ftp_end_request(struct connection *);int get_ftp_response(struct connection *, struct read_buffer *, int);int ftp_process_dirlist(struct cache_entry *, off_t *, int *, unsigned char *, int, int, int *);int get_ftp_response(struct connection *c, struct read_buffer *rb, int part){ int l; set_timeout(c); again: for (l = 0; l < rb->len; l++) if (rb->data[l] == 10) { unsigned char *e; long k = strtoul(rb->data, (char **)(void *)&e, 10); if (e != rb->data + 3 || k < 100 || k >= 1000) return -1; if (*e == '-') { int i; for (i = 0; i < rb->len - 5; i++) { if (rb->data[i] == 10 && !memcmp(rb->data+i+1, rb->data, 3) && rb->data[i+4] == ' ') { for (i++; i < rb->len; i++) if (rb->data[i] == 10) goto ok; return 0; } } return 0; ok: l = i; } if (!part && k >= 100 && k < 200) { kill_buffer_data(rb, l + 1); goto again; } if (part == 2) return k; kill_buffer_data(rb, l + 1); return k; } return 0;}void ftp_func(struct connection *c){ /*setcstate(c, S_CONN);*/ /*set_timeout(c);*/ if (get_keepalive_socket(c)) { int p; if ((p = get_port(c->url)) == -1) { setcstate(c, S_INTERNAL); abort_connection(c); return; } make_connection(c, p, &c->sock1, ftp_options.fast_ftp ? ftp_login : ftp_get_banner); } else ftp_send_retr_req(c, S_SENT);}void ftp_get_banner(struct connection *c){ struct read_buffer *rb; set_timeout(c); setcstate(c, S_SENT); if (!(rb = alloc_read_buffer(c))) return; read_from_socket(c, c->sock1, rb, ftp_got_banner);}void ftp_got_banner(struct connection *c, struct read_buffer *rb){ int g = get_ftp_response(c, rb, 0); if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; } if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_banner); return; } if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; } ftp_login(c);}void ftp_login(struct connection *c){ unsigned char *login; unsigned char *u; int logl = 0; set_timeout(c); login = init_str(); add_to_str(&login, &logl, "USER "); if ((u = get_user_name(c->url)) && *u) add_to_str(&login, &logl, u); else add_to_str(&login, &logl, "anonymous"); if (u) mem_free(u); if (ftp_options.fast_ftp) { struct ftp_connection_info *fi; add_to_str(&login, &logl, "\r\nPASS "); if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u); else add_to_str(&login, &logl, ftp_options.anon_pass); if (u) mem_free(u); add_to_str(&login, &logl, "\r\n"); if (!(fi = add_file_cmd_to_str(c))) { mem_free(login); return; } add_to_str(&login, &logl, fi->cmdbuf); } else add_to_str(&login, &logl, "\r\n"); write_to_socket(c, c->sock1, login, strlen(login), ftp_logged); mem_free(login); setcstate(c, S_SENT);}void ftp_logged(struct connection *c){ struct read_buffer *rb; if (!(rb = alloc_read_buffer(c))) return; if (!ftp_options.fast_ftp) { ftp_got_user_info(c, rb); return; } read_from_socket(c, c->sock1, rb, ftp_got_info);}void ftp_got_info(struct connection *c, struct read_buffer *rb){ int g = get_ftp_response(c, rb, 0); if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; } if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_info); return; } if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; } ftp_got_user_info(c, rb);}void ftp_got_user_info(struct connection *c, struct read_buffer *rb){ int g = get_ftp_response(c, rb, 0); if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; } if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_user_info); return; } if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); retry_connection(c); return; } if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; } if (g >= 200 && g < 300) { if (ftp_options.fast_ftp) ftp_dummy_info(c, rb); else ftp_send_retr_req(c, S_GETH); } else { if (ftp_options.fast_ftp) ftp_pass_info(c, rb); else { unsigned char *login; unsigned char *u; int logl = 0; login = init_str(); add_to_str(&login, &logl, "PASS "); if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u); else add_to_str(&login, &logl, ftp_options.anon_pass); if (u) mem_free(u); add_to_str(&login, &logl, "\r\n"); write_to_socket(c, c->sock1, login, strlen(login), ftp_sent_passwd); mem_free(login); setcstate(c, S_LOGIN); } }}void ftp_dummy_info(struct connection *c, struct read_buffer *rb){ int g = get_ftp_response(c, rb, 0); if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; } if (!g) { read_from_socket(c, c->sock1, rb, ftp_dummy_info); return; } ftp_retr_file(c, rb);}void ftp_sent_passwd(struct connection *c){ struct read_buffer *rb; if (!(rb = alloc_read_buffer(c))) return; read_from_socket(c, c->sock1, rb, ftp_pass_info);}void ftp_pass_info(struct connection *c, struct read_buffer *rb){ int g = get_ftp_response(c, rb, 0); if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; } if (!g) { read_from_socket(c, c->sock1, rb, ftp_pass_info); setcstate(c, S_LOGIN); return; } if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); abort_connection(c); return; } if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); abort_connection(c); return; } if (ftp_options.fast_ftp) ftp_retr_file(c, rb); else ftp_send_retr_req(c, S_GETH);}struct ftp_connection_info *add_file_cmd_to_str(struct connection *c){ unsigned char *d = get_url_data(c->url); unsigned char *de; int del; unsigned char pc[6]; int ps; struct ftp_connection_info *inf, *inf2; unsigned char *s; int l; if (!d) { internal("get_url_data failed"); setcstate(c, S_INTERNAL); abort_connection(c); return NULL; } de = init_str(), del = 0; add_conv_str(&de, &del, d, strlen(d), -2); d = de; inf = mem_alloc(sizeof(struct ftp_connection_info)); memset(inf, 0, sizeof(struct ftp_connection_info)); l = 0; s = init_str(); inf->pasv = ftp_options.passive_ftp; if (*c->socks_proxy) inf->pasv = 1; c->info = inf; if (!inf->pasv) if ((ps = get_pasv_socket(c, c->sock1, &c->sock2, pc))) { mem_free(d); return NULL; }#ifdef HAVE_IPTOS if (ftp_options.set_tos) { int on = IPTOS_THROUGHPUT; setsockopt(c->sock2, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)); }#endif if (!(de = strchr(d, POST_CHAR))) de = d + strlen(d); if (d == de || de[-1] == '/') { inf->dir = 1; inf->pending_commands = 4; add_to_str(&s, &l, "TYPE A\r\n"); if (!inf->pasv) { add_to_str(&s, &l, "PORT "); add_num_to_str(&s, &l, pc[0]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[1]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[2]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[3]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[4]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[5]); add_to_str(&s, &l, "\r\n"); } else { add_to_str(&s, &l, "PASV\r\n"); } add_to_str(&s, &l, "CWD /"); add_bytes_to_str(&s, &l, d, de - d); add_to_str(&s, &l, "\r\nLIST\r\n"); c->from = 0; } else { inf->dir = 0; inf->pending_commands = 3; add_to_str(&s, &l, "TYPE I\r\n"); if (!inf->pasv) { add_to_str(&s, &l, "PORT "); add_num_to_str(&s, &l, pc[0]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[1]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[2]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[3]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[4]); add_chr_to_str(&s, &l, ','); add_num_to_str(&s, &l, pc[5]); add_to_str(&s, &l, "\r\n"); } else { add_to_str(&s, &l, "PASV\r\n"); } if (c->from && c->no_cache < NC_IF_MOD) { add_to_str(&s, &l, "REST "); add_num_to_str(&s, &l, c->from); add_to_str(&s, &l, "\r\n"); inf->rest_sent = 1; inf->pending_commands++; } else c->from = 0; add_to_str(&s, &l, "RETR /"); add_bytes_to_str(&s, &l, d, de - d); add_to_str(&s, &l, "\r\n"); } inf->opc = inf->pending_commands; if ((unsigned)l > MAXINT - sizeof(struct ftp_connection_info) - 1) overalloc(); inf2 = mem_realloc(inf, sizeof(struct ftp_connection_info) + l + 1); strcpy((inf = inf2)->cmdbuf, s); mem_free(s); c->info = inf; mem_free(d); return inf;}void ftp_send_retr_req(struct connection *c, int state){ struct ftp_connection_info *fi; unsigned char *login; int logl = 0; set_timeout(c); login = init_str(); if (!c->info && !(fi = add_file_cmd_to_str(c))) { mem_free(login); return; } else fi = c->info; if (ftp_options.fast_ftp) a:add_to_str(&login, &logl, fi->cmdbuf); else { unsigned char *nl = strchr(fi->cmdbuf, '\n'); if (!nl) goto a; nl++; add_bytes_to_str(&login, &logl, fi->cmdbuf, nl - fi->cmdbuf); memmove(fi->cmdbuf, nl, strlen(nl) + 1); } write_to_socket(c, c->sock1, login, strlen(login), ftp_retr_1); mem_free(login); setcstate(c, state);}void ftp_retr_1(struct connection *c){ struct read_buffer *rb; if (!(rb = alloc_read_buffer(c))) return; read_from_socket(c, c->sock1, rb, ftp_retr_file);}void ftp_retr_file(struct connection *c, struct read_buffer *rb){ int g; struct ftp_connection_info *inf = c->info; if (0) { rep: if (!ftp_options.fast_ftp) { ftp_send_retr_req(c, S_GETH); return; } } if (inf->pending_commands > 1) { unsigned char pc[6]; if (inf->pasv && inf->opc - (inf->pending_commands - 1) == 2) { int i = 3, j; while (i < rb->len) { if (rb->data[i] >= '0' && rb->data[i] <= '9') { for (j = 0; j < 6; j++) { int n = 0; while (rb->data[i] >= '0' && rb->data[i] <= '9') { n = n * 10 + rb->data[i] - '0'; if (n >= 256) goto no_pasv; if (++i >= rb->len) goto no_pasv; } pc[j] = n; if (j != 5) { if (rb->data[i] != ',') goto xa; if (++i >= rb->len) goto xa; if (rb->data[i] < '0' || rb->data[i] > '9') { xa: if (j != 1) goto no_pasv; pc[4] = pc[0]; pc[5] = pc[1]; { unsigned a; struct sockaddr_in sa; socklen_t nl = sizeof(sa); if (getpeername(c->sock1, (struct sockaddr *)(void *)&sa, &nl)) goto no_pasv; if (nl != sizeof(sa)) goto no_pasv; a = ntohl(sa.sin_addr.s_addr); pc[0] = a >> 24; pc[1] = a >> 16; pc[2] = a >> 8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -