1 /* ftp.c
2  * ftp:// processing
3  * (c) 2002 Mikulas Patocka
4  * This file is a part of the Links program, released under GPL.
5  */
6 
7 #include "links.h"
8 
9 #define FTP_BUF	16384
10 
11 struct ftp_connection_info {
12 	int pending_commands;
13 	int opc;
14 	int pasv;
15 	int eprt_epsv;
16 	int dir;
17 	int rest_sent;
18 	int we_are_in_root;
19 	int conn_st;
20 	int d;
21 	int dpos;
22 	int buf_pos;
23 	unsigned char ftp_buffer[FTP_BUF];
24 	unsigned char cmdbuf[1];
25 };
26 
27 static void ftp_get_banner(struct connection *);
28 static void ftp_got_banner(struct connection *, struct read_buffer *);
29 static void ftp_login(struct connection *);
30 static void ftp_logged(struct connection *);
31 static void ftp_sent_passwd(struct connection *);
32 static void ftp_got_info(struct connection *, struct read_buffer *);
33 static void ftp_got_user_info(struct connection *, struct read_buffer *);
34 static void ftp_dummy_info(struct connection *, struct read_buffer *);
35 static void ftp_pass_info(struct connection *, struct read_buffer *);
36 static void ftp_send_retr_req(struct connection *, int, int);
37 static struct ftp_connection_info *add_file_cmd_to_str(struct connection *, int);
38 static void ftp_retr_1(struct connection *);
39 static void ftp_retr_file(struct connection *, struct read_buffer *);
40 static void ftp_got_final_response(struct connection *, struct read_buffer *);
41 static void created_data_connection(struct connection *);
42 static void got_something_from_data_connection(struct connection *);
43 static void ftp_end_request(struct connection *, int);
44 static int get_ftp_response(struct connection *, struct read_buffer *, int);
45 static int ftp_process_dirlist(struct cache_entry *, off_t *, int *, unsigned char *, int, int, int, int *);
46 
47 
get_ftp_response(struct connection * c,struct read_buffer * rb,int part)48 static int get_ftp_response(struct connection *c, struct read_buffer *rb, int part)
49 {
50 	int l;
51 	set_timeout(c);
52 	again:
53 	for (l = 0; l < rb->len; l++) if (rb->data[l] == 10) {
54 		unsigned char *e;
55 		long k = strtoul(cast_const_char rb->data, (char **)(void *)&e, 10);
56 		if (e != rb->data + 3 || k < 100 || k >= 1000) return -1;
57 		if (*e == '-') {
58 			int i;
59 			for (i = 0; i < rb->len - 5; i++) {
60 				if (rb->data[i] == 10 && !memcmp(rb->data+i+1, rb->data, 3) && rb->data[i+4] == ' ') {
61 					for (i++; i < rb->len; i++) if (rb->data[i] == 10) goto ok;
62 					return 0;
63 				}
64 			}
65 			return 0;
66 			ok:
67 			l = i;
68 		}
69 		if (!part && k >= 100 && k < 200) {
70 			kill_buffer_data(rb, l + 1);
71 			goto again;
72 		}
73 		if (part == 2) return (int)k;
74 		kill_buffer_data(rb, l + 1);
75 		return (int)k;
76 	}
77 	return 0;
78 }
79 
ftp_func(struct connection * c)80 void ftp_func(struct connection *c)
81 {
82 	int we_are_in_root;
83 	unsigned char *de, *d;
84 	int del, bad_url;
85 	d = get_url_data(c->url);
86 	de = init_str(), del = 0;
87 	add_conv_str(&de, &del, d, (int)strcspn(cast_const_char d, POST_CHAR_STRING), -2);
88 	bad_url = !!strchr(cast_const_char de, 10);
89 	mem_free(de);
90 	if (bad_url) {
91 		setcstate(c, S_BAD_URL);
92 		abort_connection(c);
93 		return;
94 	}
95 
96 	if (get_keepalive_socket(c, &we_are_in_root)) {
97 		int p;
98 		if ((p = get_port(c->url)) == -1) {
99 			setcstate(c, S_BAD_URL);
100 			abort_connection(c);
101 			return;
102 		}
103 		make_connection(c, p, &c->sock1, ftp_options.fast_ftp ? ftp_login : ftp_get_banner);
104 	} else {
105 		ftp_send_retr_req(c, S_SENT, we_are_in_root);
106 	}
107 }
108 
ftp_get_banner(struct connection * c)109 static void ftp_get_banner(struct connection *c)
110 {
111 	struct read_buffer *rb;
112 	set_timeout(c);
113 	setcstate(c, S_SENT);
114 	if (!(rb = alloc_read_buffer(c))) return;
115 	read_from_socket(c, c->sock1, rb, ftp_got_banner);
116 }
117 
ftp_got_banner(struct connection * c,struct read_buffer * rb)118 static void ftp_got_banner(struct connection *c, struct read_buffer *rb)
119 {
120 	int g = get_ftp_response(c, rb, 0);
121 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
122 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_banner); return; }
123 	if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
124 	ftp_login(c);
125 }
126 
ftp_login(struct connection * c)127 static void ftp_login(struct connection *c)
128 {
129 	unsigned char *login;
130 	unsigned char *u;
131 	int logl = 0;
132 	set_timeout(c);
133 	login = init_str();
134 	add_to_str(&login, &logl, cast_uchar "USER ");
135 	if ((u = get_user_name(c->url)) && *u) add_to_str(&login, &logl, u);
136 	else add_to_str(&login, &logl, cast_uchar "anonymous");
137 	if (u) mem_free(u);
138 	if (ftp_options.fast_ftp) {
139 		struct ftp_connection_info *fi;
140 		add_to_str(&login, &logl, cast_uchar "\r\nPASS ");
141 		if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u);
142 		else add_to_str(&login, &logl, ftp_options.anon_pass);
143 		if (u) mem_free(u);
144 		add_to_str(&login, &logl, cast_uchar "\r\n");
145 		if (!(fi = add_file_cmd_to_str(c, 0))) {
146 			mem_free(login);
147 			return;
148 		}
149 		add_to_str(&login, &logl, fi->cmdbuf);
150 	} else add_to_str(&login, &logl, cast_uchar "\r\n");
151 	write_to_socket(c, c->sock1, login, logl, ftp_logged);
152 	mem_free(login);
153 	setcstate(c, S_SENT);
154 }
155 
ftp_logged(struct connection * c)156 static void ftp_logged(struct connection *c)
157 {
158 	struct read_buffer *rb;
159 	if (!(rb = alloc_read_buffer(c))) return;
160 	if (!ftp_options.fast_ftp) {
161 		ftp_got_user_info(c, rb);
162 		return;
163 	}
164 	read_from_socket(c, c->sock1, rb, ftp_got_info);
165 }
166 
ftp_got_info(struct connection * c,struct read_buffer * rb)167 static void ftp_got_info(struct connection *c, struct read_buffer *rb)
168 {
169 	int g = get_ftp_response(c, rb, 0);
170 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
171 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_info); return; }
172 	if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
173 	ftp_got_user_info(c, rb);
174 }
175 
ftp_got_user_info(struct connection * c,struct read_buffer * rb)176 static void ftp_got_user_info(struct connection *c, struct read_buffer *rb)
177 {
178 	int g = get_ftp_response(c, rb, 0);
179 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
180 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_user_info); return; }
181 	if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); retry_connection(c); return; }
182 	if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
183 	if (g >= 200 && g < 300) {
184 		if (ftp_options.fast_ftp) ftp_dummy_info(c, rb);
185 		else ftp_send_retr_req(c, S_GETH, 0);
186 	} else {
187 		if (ftp_options.fast_ftp) ftp_pass_info(c, rb);
188 		else {
189 			unsigned char *login;
190 			unsigned char *u;
191 			int logl = 0;
192 			login = init_str();
193 			add_to_str(&login, &logl, cast_uchar "PASS ");
194 			if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u);
195 			else add_to_str(&login, &logl, ftp_options.anon_pass);
196 			if (u) mem_free(u);
197 			add_to_str(&login, &logl, cast_uchar "\r\n");
198 			write_to_socket(c, c->sock1, login, logl, ftp_sent_passwd);
199 			mem_free(login);
200 			setcstate(c, S_LOGIN);
201 		}
202 	}
203 }
204 
ftp_dummy_info(struct connection * c,struct read_buffer * rb)205 static void ftp_dummy_info(struct connection *c, struct read_buffer *rb)
206 {
207 	int g = get_ftp_response(c, rb, 0);
208 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
209 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_dummy_info); return; }
210 	ftp_retr_file(c, rb);
211 }
212 
ftp_sent_passwd(struct connection * c)213 static void ftp_sent_passwd(struct connection *c)
214 {
215 	struct read_buffer *rb;
216 	if (!(rb = alloc_read_buffer(c))) return;
217 	read_from_socket(c, c->sock1, rb, ftp_pass_info);
218 }
219 
ftp_pass_info(struct connection * c,struct read_buffer * rb)220 static void ftp_pass_info(struct connection *c, struct read_buffer *rb)
221 {
222 	int g = get_ftp_response(c, rb, 0);
223 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
224 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_pass_info); setcstate(c, S_LOGIN); return; }
225 	if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); abort_connection(c); return; }
226 	if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); abort_connection(c); return; }
227 	if (ftp_options.fast_ftp) ftp_retr_file(c, rb);
228 	else ftp_send_retr_req(c, S_GETH, 0);
229 }
230 
add_port_pasv(unsigned char ** s,int * l,struct ftp_connection_info * inf,unsigned char * port_string)231 static void add_port_pasv(unsigned char **s, int *l, struct ftp_connection_info *inf, unsigned char *port_string)
232 {
233 	if (!inf->pasv) {
234 		if (inf->eprt_epsv) {
235 			add_to_str(s, l, cast_uchar "EPRT ");
236 			add_to_str(s, l, port_string);
237 		} else {
238 			add_to_str(s, l, cast_uchar "PORT ");
239 			add_to_str(s, l, port_string);
240 		}
241 	} else {
242 		if (inf->eprt_epsv) {
243 			add_to_str(s, l, cast_uchar "EPSV");
244 		} else {
245 			add_to_str(s, l, cast_uchar "PASV");
246 		}
247 	}
248 	add_to_str(s, l, cast_uchar "\r\n");
249 }
250 
add_file_cmd_to_str(struct connection * c,int we_are_in_root)251 static struct ftp_connection_info *add_file_cmd_to_str(struct connection *c, int we_are_in_root)
252 {
253 	unsigned char *d = get_url_data(c->url);
254 	unsigned char *dd, *de;
255 	int del;
256 	unsigned char port_string[50];
257 	struct ftp_connection_info *inf, *inf2;
258 	unsigned char *s;
259 	int l;
260 	if (!d) {
261 		internal("get_url_data failed");
262 		setcstate(c, S_INTERNAL);
263 		abort_connection(c);
264 		return NULL;
265 	}
266 
267 	de = init_str(), del = 0;
268 	add_conv_str(&de, &del, d, (int)strlen(cast_const_char d), -2);
269 	d = de;
270 	inf = mem_alloc(sizeof(struct ftp_connection_info));
271 	memset(inf, 0, sizeof(struct ftp_connection_info));
272 	l = 0;
273 	s = init_str();
274 	inf->we_are_in_root = we_are_in_root;
275 	inf->pasv = ftp_options.passive_ftp;
276 #ifdef LINKS_2
277 	if (*c->socks_proxy) inf->pasv = 1;
278 	if (ftp_options.eprt_epsv || is_ipv6(c->sock1)) inf->eprt_epsv = 1;
279 #endif
280 	c->info = inf;
281 
282 	if (!inf->pasv) {
283 		int ps;
284 #ifdef SUPPORT_IPV6
285 		if (is_ipv6(c->sock1)) {
286 			ps = get_pasv_socket_ipv6(c, c->sock1, &c->sock2, port_string);
287 			if (ps) {
288 				mem_free(d);
289 				mem_free(s);
290 				return NULL;
291 			}
292 		} else
293 #endif
294 		{
295 			unsigned char pc[6];
296 			ps = get_pasv_socket(c, c->sock1, &c->sock2, pc);
297 			if (ps) {
298 				mem_free(d);
299 				mem_free(s);
300 				return NULL;
301 			}
302 			if (inf->eprt_epsv)
303 				sprintf(cast_char port_string, "|1|%d.%d.%d.%d|%d|", pc[0], pc[1], pc[2], pc[3], (pc[4] << 8) | pc[5]);
304 			else
305 				sprintf(cast_char port_string, "%d,%d,%d,%d,%d,%d", pc[0], pc[1], pc[2], pc[3], pc[4], pc[5]);
306 		}
307 		if (strlen(cast_const_char port_string) >= sizeof(port_string))
308 			internal("buffer overflow in get_pasv_socket_ipv6: %d > %d", (int)strlen(cast_const_char port_string), (int)sizeof(port_string));
309 	}
310 #ifdef HAVE_IPTOS
311 	if (ftp_options.set_tos) {
312 		int rx;
313 		int on = IPTOS_THROUGHPUT;
314 		EINTRLOOP(rx, setsockopt(c->sock2, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)));
315 	}
316 #endif
317 	dd = d;
318 	while (*dd == '/') dd++;
319 	de = cast_uchar strchr(cast_const_char dd, 0);
320 	if (dd == de || de[-1] == '/') {
321 		inf->dir = 1;
322 		inf->pending_commands = 3;
323 		add_to_str(&s, &l, cast_uchar "TYPE A\r\n");
324 		add_port_pasv(&s, &l, inf, port_string);
325 		if (!inf->we_are_in_root) {
326 			add_to_str(&s, &l, cast_uchar "CWD /\r\n");
327 			inf->we_are_in_root = 1;
328 			inf->pending_commands++;
329 		}
330 		if (dd != de) {
331 			add_to_str(&s, &l, cast_uchar "CWD ");
332 			add_bytes_to_str(&s, &l, dd, de - 1 - dd);
333 			add_to_str(&s, &l, cast_uchar "\r\n");
334 			inf->we_are_in_root = 0;
335 			inf->pending_commands++;
336 		}
337 		add_to_str(&s, &l, cast_uchar "LIST\r\n");
338 		c->from = 0;
339 	} else {
340 		inf->dir = 0;
341 		inf->pending_commands = 3;
342 		add_to_str(&s, &l, cast_uchar "TYPE I\r\n");
343 		add_port_pasv(&s, &l, inf, port_string);
344 		if (!inf->we_are_in_root) {
345 			add_to_str(&s, &l, cast_uchar "CWD /\r\n");
346 			inf->we_are_in_root = 1;
347 			inf->pending_commands++;
348 		}
349 		if (c->from && c->no_cache < NC_IF_MOD) {
350 			add_to_str(&s, &l, cast_uchar "REST ");
351 			add_num_to_str(&s, &l, c->from);
352 			add_to_str(&s, &l, cast_uchar "\r\n");
353 			inf->rest_sent = 1;
354 			inf->pending_commands++;
355 		} else {
356 			c->from = 0;
357 		}
358 		add_to_str(&s, &l, cast_uchar "RETR ");
359 		add_bytes_to_str(&s, &l, dd, de - dd);
360 		add_to_str(&s, &l, cast_uchar "\r\n");
361 	}
362 	inf->opc = inf->pending_commands;
363 	if ((unsigned)l > MAXINT - sizeof(struct ftp_connection_info) - 1) overalloc();
364 	inf2 = mem_realloc(inf, sizeof(struct ftp_connection_info) + l + 1);
365 	strcpy(cast_char (inf = inf2)->cmdbuf, cast_const_char s);
366 	mem_free(s);
367 	c->info = inf;
368 	mem_free(d);
369 	return inf;
370 }
371 
372 
ftp_send_retr_req(struct connection * c,int state,int we_are_in_root)373 static void ftp_send_retr_req(struct connection *c, int state, int we_are_in_root)
374 {
375 	struct ftp_connection_info *fi;
376 	unsigned char *login;
377 	int logl = 0;
378 	set_timeout(c);
379 	login = init_str();
380 	if (!c->info && !(fi = add_file_cmd_to_str(c, we_are_in_root))) {
381 		mem_free(login);
382 		return;
383 	} else {
384 		fi = c->info;
385 	}
386 	if (ftp_options.fast_ftp) {
387 		a:add_to_str(&login, &logl, fi->cmdbuf);
388 	} else {
389 		unsigned char *nl = cast_uchar strchr(cast_const_char fi->cmdbuf, '\n');
390 		if (!nl) goto a;
391 		nl++;
392 		add_bytes_to_str(&login, &logl, fi->cmdbuf, nl - fi->cmdbuf);
393 		memmove(fi->cmdbuf, nl, strlen(cast_const_char nl) + 1);
394 	}
395 	write_to_socket(c, c->sock1, login, logl, ftp_retr_1);
396 	mem_free(login);
397 	setcstate(c, state);
398 }
399 
ftp_retr_1(struct connection * c)400 static void ftp_retr_1(struct connection *c)
401 {
402 	struct read_buffer *rb;
403 	if (!(rb = alloc_read_buffer(c))) return;
404 	read_from_socket(c, c->sock1, rb, ftp_retr_file);
405 }
406 
ftp_retr_file(struct connection * c,struct read_buffer * rb)407 static void ftp_retr_file(struct connection *c, struct read_buffer *rb)
408 {
409 	int g;
410 	struct ftp_connection_info *inf = c->info;
411 	if (0) {
412 		rep:
413 		if (!ftp_options.fast_ftp) {
414 			ftp_send_retr_req(c, S_GETH, inf->we_are_in_root);
415 			return;
416 		}
417 	}
418 	if (inf->pending_commands > 1) {
419 		unsigned char pc[6];
420 		if (inf->pasv && inf->opc - (inf->pending_commands - 1) == 2) {
421 			int i, j;
422 			i = 3;
423 			if (!inf->eprt_epsv) while (i < rb->len) {
424 				if (rb->data[i] >= '0' && rb->data[i] <= '9') {
425 					for (j = 0; j < 6; j++) {
426 						int n = 0;
427 						while (rb->data[i] >= '0' && rb->data[i] <= '9') {
428 							n = n * 10 + rb->data[i] - '0';
429 							if (n >= 256) goto no_pasv;
430 							if (++i >= rb->len) goto no_pasv;
431 						}
432 						pc[j] = (unsigned char)n;
433 						if (j != 5) {
434 							if (rb->data[i] != ',') goto xa;
435 							if (++i >= rb->len) goto xa;
436 							if (rb->data[i] < '0' || rb->data[i] > '9') {
437 								xa:
438 								if (j != 1) goto no_pasv;
439 								pc[4] = pc[0];
440 								pc[5] = pc[1];
441 								pc[0] = pc[1] = pc[2] = pc[3] = 0;
442 								goto pasv_ok;
443 							}
444 						}
445 					}
446 					goto pasv_ok;
447 				}
448 				i++;
449 			}
450 			no_pasv:
451 			i = 3;
452 			while (i < rb->len - 5) {
453 				if (rb->data[i] == '(' && (rb->data[i + 1] < '0' || rb->data[i + 1] > '9') && rb->data[i + 1] == rb->data[i + 2] && rb->data[i + 2] == rb->data[i + 3]) {
454 					unsigned char delim = rb->data[i + 1];
455 					int n = 0;
456 					i += 4;
457 					while (rb->data[i] >= '0' && rb->data[i] <= '9') {
458 						n = n * 10 + rb->data[i] - '0';
459 						if (n >= 65536) goto no_epsv;
460 						if (++i >= rb->len) goto no_epsv;
461 					}
462 					if (rb->data[i] != delim) goto no_epsv;
463 					pc[4] = n >> 8;
464 					pc[5] = n & 0xff;
465 					pc[0] = pc[1] = pc[2] = pc[3] = 0;
466 					goto pasv_ok;
467 				}
468 				i++;
469 			}
470 			no_epsv:
471 			memset(pc, 0, sizeof pc);
472 			pasv_ok:;
473 		}
474 		g = get_ftp_response(c, rb, 0);
475 		if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
476 		if (!g) { read_from_socket(c, c->sock1, rb, ftp_retr_file); setcstate(c, S_GETH); return; }
477 		inf->pending_commands--;
478 		switch (inf->opc - inf->pending_commands) {
479 			case 1:		/* TYPE */
480 				goto rep;
481 			case 2:		/* PORT */
482 				if (g >= 400) { setcstate(c, S_FTP_PORT); abort_connection(c); return; }
483 				if (inf->pasv) {
484 					if (!pc[4] && !pc[5]) {
485 						setcstate(c, S_FTP_ERROR);
486 						retry_connection(c);
487 						return;
488 					}
489 					make_connection(c, (pc[4] << 8) + pc[5], &c->sock2, created_data_connection);
490 				}
491 				goto rep;
492 			case 3:		/* REST / CWD */
493 			case 4:
494 				if (g >= 400) {
495 					if (!inf->dir && c->from && inf->pending_commands == 1) c->from = 0;
496 					else { setcstate(c, S_FTP_NO_FILE); abort_connection(c); return; }
497 				}
498 				goto rep;
499 		}
500 		internal("WHAT???");
501 	}
502 	g = get_ftp_response(c, rb, 2);
503 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_retr_file); setcstate(c, S_GETH); return; }
504 	if (g >= 100 && g < 200) {
505 		unsigned char *d = rb->data;
506 		int i, p = 0;
507 		for (i = 0; i < rb->len && d[i] != 10; i++) if (d[i] == '(') p = i;
508 		if (!p || p == rb->len - 1) goto nol;
509 		p++;
510 		if (d[p] < '0' || d[p] > '9') goto nol;
511 		for (i = p; i < rb->len; i++) if (d[i] < '0' || d[i] > '9') goto quak;
512 		goto nol;
513 		quak:
514 		for (; i < rb->len; i++) if (d[i] != ' ') break;
515 		if (i + 4 > rb->len) goto nol;
516 		if (casecmp(&d[i], cast_uchar "byte", 4)) goto nol;
517 		/* when resuming file transfer, some servers return total size,
518 		   others return the number of remaining bytes. So it is not
519 		   reliable to guess file size in this case */
520 		if (c->from) goto nol;
521 		{
522 			my_strtoll_t est = my_strtoll(&d[p], NULL);
523 			if (est < 0 || (off_t)est < 0 || (off_t)est != est) est = 0;
524 			if (est) c->est_length = est + c->from;
525 		}
526 		nol:;
527 	}
528 	if (!inf->pasv)
529 		set_handlers(c->sock2, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
530 	/*read_from_socket(c, c->sock1, rb, ftp_got_final_response);*/
531 	ftp_got_final_response(c, rb);
532 }
533 
ftp_got_final_response(struct connection * c,struct read_buffer * rb)534 static void ftp_got_final_response(struct connection *c, struct read_buffer *rb)
535 {
536 	struct ftp_connection_info *inf = c->info;
537 	int g = get_ftp_response(c, rb, 0);
538 	if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
539 	if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_final_response); if (c->state != S_TRANS) setcstate(c, S_GETH); return; }
540 	if (g == 425 || g == 450 || g == 500 || g == 501 || g == 550) {
541 		if (c->url[strlen(cast_const_char c->url) - 1] == '/') goto skip_redir;
542 		if (!c->cache) {
543 			if (get_cache_entry(c->url, &c->cache)) {
544 				setcstate(c, S_OUT_OF_MEM);
545 				abort_connection(c);
546 				return;
547 			}
548 			c->cache->refcount--;
549 		}
550 		if (c->cache->redirect) mem_free(c->cache->redirect);
551 		c->cache->redirect = stracpy(c->url);
552 		c->cache->redirect_get = 1;
553 		add_to_strn(&c->cache->redirect, cast_uchar "/");
554 		c->cache->incomplete = 0;
555 		/*setcstate(c, S_FTP_NO_FILE);*/
556 		setcstate(c, S__OK);
557 		abort_connection(c);
558 		return;
559 	}
560 	skip_redir:
561 	if (g >= 400) { setcstate(c, S_FTP_FILE_ERROR); abort_connection(c); return; }
562 	if (inf->conn_st == 2) {
563 		ftp_end_request(c, S__OK);
564 	} else {
565 		inf->conn_st = 1;
566 		if (c->state != S_TRANS) setcstate(c, S_GETH);
567 	}
568 }
569 
is_date(unsigned char * data)570 static int is_date(unsigned char *data)	/* can touch at most data[-4] --- "n 12 "<--if fed with this --- if you change it, fix the caller */
571 {
572 	/* fix for ftp://ftp.su.se/ */
573 	if (*data == ' ') data--;
574 	if (data[0] >= '0' && data[0] <= '9' && data[-1] >= '0' && data[-1] <= '9') data -= 2;
575 	else if (data[0] >= '1' && data[0] <= '9' && data[-1] == ' ') data -= 1 + (data[-2] == ' ');
576 	else return 0;
577 	if (data[0] == ':') return 1;
578 	if (data[0] != ' ') return 0;
579 	if ((data[-1] < 'a' || data[-1] > 'z') && (data[-1] < 'A' || data[-1] > 'Z')) return 0;
580 	return 1;
581 }
582 
583 #define PARSE_MODE_VMS		-1
584 
ftp_process_dirlist(struct cache_entry * ce,off_t * pos,int * d,unsigned char * bf,int ln,int fin,int root,int * tr)585 static int ftp_process_dirlist(struct cache_entry *ce, off_t *pos, int *d, unsigned char *bf, int ln, int fin, int root, int *tr)
586 {
587 	unsigned char *str, *buf;
588 	int sl;
589 	int ret = 0;
590 	int p;
591 	int len;
592 	int f;
593 	int a;
594 	int dir;
595 	int sp;
596 	int ee;
597 	again:
598 	buf = bf + ret;
599 	len = ln - ret;
600 	for (p = 0; p < len; p++) if (buf[p] == '\n') goto lb;
601 	if (p && (fin || len >= FTP_BUF)) {
602 		ret += p;
603 		goto pl;
604 	}
605 	return ret;
606 	lb:
607 	ret += p + 1;
608 	if (p && buf[p - 1] == '\r') p--;
609 	pl:
610 	str = init_str();
611 	sl = 0;
612 	/*add_to_str(&str, &sl, cast_uchar "   ");*/
613 
614 	dir = 0;
615 	if (!*d || *d == PARSE_MODE_VMS) for (ee = 0; p - ee > 1 && !WHITECHAR(buf[ee]); ee++) {
616 		if (buf[ee] == ';' && buf[ee + 1] >= '1' && buf[ee + 1] <= '9') {
617 			if (!ee) goto skip_vms;
618 			if (ee >= 4 && buf[ee - 4] == '.' && buf[ee - 3] == 'D' && buf[ee - 2] == 'I' && buf[ee - 1] == 'R') {
619 				if (ee == 4) goto raw;
620 				dir = 1, ee -= 4;
621 			} else {
622 				/*if (buf[ee + 1] == '1' && (ee + 2 == p || buf[ee + 2] < '0' || buf[ee + 2] > '9')) goto no_version;
623 				ee += 2;
624 				while (ee < p && buf[ee] >= '0' && buf[ee] < '9') ee++;
625 				no_version:;*/
626 			}
627 			if (*d != PARSE_MODE_VMS && !root) {
628 				add_to_str(&str, &sl, cast_uchar "<a href=\"../\">..</a>\n");
629 			}
630 			sp = 0;
631 			*d = PARSE_MODE_VMS;
632 			goto put_entry;
633 		}
634 	}
635 	skip_vms:
636 
637 	if (*d < 0) goto raw;
638 
639 	f = *d;
640 	if (*d && *d < p && WHITECHAR(buf[*d - 1])) {
641 		sp = *d;
642 		ppp:
643 		for (ee = sp; ee <= p - 4; ee++)
644 			if (!memcmp(buf + ee, cast_uchar " -> ", 4)) goto syml;
645 		ee = p;
646 		syml:
647 		if (!f && !root) {
648 			if ((ee - sp != 1 || buf[sp] != '.') &&
649 			    (ee - sp != 2 || buf[sp] != '.' || buf[sp + 1] != '.')) {
650 				int i;
651 				for (i = 0; i < sp; i++) add_chr_to_str(&str, &sl, ' ');
652 				add_to_str(&str, &sl, cast_uchar "<a href=\"../\">..</a>\n");
653 			}
654 		}
655 		dir = buf[0] == 'd';
656 		if (!dir) {
657 			unsigned char *p = memacpy(buf, sp);
658 			if (strstr(cast_const_char p, "<DIR>")) dir = 1;
659 			mem_free(p);
660 		};
661 		put_entry:
662 		add_conv_str(&str, &sl, buf, sp, 0);
663 		add_to_str(&str, &sl, cast_uchar "<a href=\"./");
664 		add_conv_str(&str, &sl, buf + sp, ee - sp, 1);
665 		if (dir) add_chr_to_str(&str, &sl, '/');
666 		add_to_str(&str, &sl, cast_uchar "\">");
667 		add_conv_str(&str, &sl, buf + sp, ee - sp, 0);
668 		add_to_str(&str, &sl, cast_uchar "</a>");
669 		add_conv_str(&str, &sl, buf + ee, p - ee, 0);
670 	} else {
671 		int pp, ppos;
672 		int bp, bn;
673 		if (p > 5 && !casecmp(buf, cast_uchar "total", 5)) goto raw;
674 		if (p > 10 && !memcmp(buf, cast_uchar "Directory ", 10)) goto raw;
675 		for (pp = p - 1; pp >= 0; pp--) if (!WHITECHAR(buf[pp])) break;
676 		if (pp < 0) goto raw;
677 		if (pp < p - 1) pp++;
678 		ppos = -1;
679 		for (; pp >= 10; pp--) if (WHITECHAR(buf[pp])) {
680 			if (is_date(&buf[pp - 6]) &&
681 			    buf[pp - 5] == ' ' &&
682 			    ((buf[pp - 4] == '2' && buf[pp - 3] == '0') ||
683 			     (buf[pp - 4] == '1' && buf[pp - 3] == '9')) &&
684 			    buf[pp - 2] >= '0' && buf[pp - 2] <= '9' &&
685 			    buf[pp - 1] >= '0' && buf[pp - 1] <= '9') {
686 				if (pp < p - 2 && buf[pp + 1] == ' ' && buf[pp + 2] != ' ') ppos = pp + 1;
687 				else ppos = pp;
688 			}
689 			if (buf[pp - 6] == ' ' &&
690 			    ((buf[pp - 5] >= '0' && buf[pp - 5] <= '2') || buf[pp - 5] == ' ') &&
691 			    buf[pp - 4] >= '0' && buf[pp - 4] <= '9' &&
692 			    buf[pp - 3] == ':' &&
693 			    buf[pp - 2] >= '0' && buf[pp - 2] <= '5' &&
694 			    buf[pp - 1] >= '0' && buf[pp - 1] <= '9') {
695 				ppos = pp;
696 				if (p - pp > 2 && buf[pp + 1] == ' ' && buf[pp + 2] != ' ')
697 					ppos++;
698 			}
699 		}
700 		if (ppos != -1) {
701 			pp = ppos;
702 			goto done;
703 		}
704 
705 		for (pp = 0; p - pp >= 5; pp++)
706 			if (!casecmp(&buf[pp], cast_uchar "<DIR>", 5)) {
707 				pp += 4;
708 				while (p - pp > 1 && WHITECHAR(buf[pp + 1])) pp++;
709 				if (p - pp > 1) goto done;
710 			}
711 
712 		bn = -1;
713 		bp = 0;		/* warning, go away */
714 		for (pp = 0; pp < p; ) {
715 			if (buf[pp] >= '0' && buf[pp] <= '9') {
716 				int i;
717 				for (i = pp; i < p; i++)
718 					if (buf[i] < '0' || buf[i] > '9') break;
719 				if (i < p && WHITECHAR(buf[i])) {
720 					if (i - pp > bn) {
721 						bn = i - pp;
722 						bp = pp;
723 					}
724 				}
725 				pp = i;
726 			}
727 			while (pp < p && !WHITECHAR(buf[pp])) pp++;
728 			while (pp < p && WHITECHAR(buf[pp])) pp++;
729 		}
730 		if (bn >= 0) {
731 			pp = bp + bn;
732 			while (p - pp > 1 && WHITECHAR(buf[pp + 1])) pp++;
733 			if (p - pp > 1) goto done;
734 		}
735 
736 		for (pp = p - 1; pp >= 0; pp--) if (!WHITECHAR(buf[pp])) break;
737 		if (pp < 0) goto raw;
738 		for (; pp >= 0; pp--) if (WHITECHAR(buf[pp]) && (pp < 3 || memcmp(buf + pp - 3, cast_uchar " -> ", 4)) && (pp > p - 4 || memcmp(buf + pp, cast_uchar " -> ", 4))) break;
739 		done:
740 		sp = *d = pp + 1;
741 		goto ppp;
742 		raw:
743 		add_conv_str(&str, &sl, buf, p, 0);
744 	}
745 	add_chr_to_str(&str, &sl, '\n');
746 	a = add_fragment(ce, *pos, str, sl);
747 	if (a < 0) return a;
748 	if (a == 1) *tr = 0;
749 	*pos += sl;
750 	mem_free(str);
751 	goto again;
752 }
753 
created_data_connection(struct connection * c)754 static void created_data_connection(struct connection *c)
755 {
756 	struct ftp_connection_info *inf = c->info;
757 #ifdef HAVE_IPTOS
758 	if (ftp_options.set_tos) {
759 		int rx;
760 		int on = IPTOS_THROUGHPUT;
761 		EINTRLOOP(rx, setsockopt(c->sock2, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)));
762 	}
763 #endif
764 	inf->d = 1;
765 	set_handlers(c->sock2, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
766 }
767 
got_something_from_data_connection(struct connection * c)768 static void got_something_from_data_connection(struct connection *c)
769 {
770 	struct ftp_connection_info *inf = c->info;
771 	int l;
772 	int m;
773 	int rs;
774 	set_timeout(c);
775 	if (!inf->d) {
776 		int ns;
777 		inf->d = 1;
778 		set_handlers(c->sock2, NULL, NULL, NULL, NULL);
779 		EINTRLOOP(ns, accept(c->sock2, NULL, NULL));
780 		if (ns == -1) goto e;
781 		set_nonblock(ns);
782 		EINTRLOOP(rs, close(c->sock2));
783 		c->sock2 = ns;
784 		set_handlers(ns, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
785 		return;
786 	}
787 	if (!c->cache) {
788 		if (get_cache_entry(c->url, &c->cache)) {
789 			setcstate(c, S_OUT_OF_MEM);
790 			abort_connection(c);
791 			return;
792 		}
793 		c->cache->refcount--;
794 	}
795 	if (inf->dir && !c->from) {
796 		unsigned char *ud;
797 		unsigned char *s0;
798 		int s0l;
799 		int err = 0;
800 		static unsigned char ftp_head[] = "<html><head><title>/";
801 		static unsigned char ftp_head2[] = "</title></head><body><h2>Directory /";
802 		static unsigned char ftp_head3[] = "</h2><pre>";
803 #define A(s)							\
804 do {								\
805 	m = add_fragment(c->cache, c->from, s, strlen(cast_const_char s));	\
806 	if (m < 0 && !err) err = m;				\
807 	c->from += strlen(cast_const_char s);					\
808 } while (0)
809 		A(ftp_head);
810 		ud = stracpy(get_url_data(c->url));
811 		if (strchr(cast_const_char ud, POST_CHAR)) *strchr(cast_const_char ud, POST_CHAR) = 0;
812 		s0 = init_str();
813 		s0l = 0;
814 		add_conv_str(&s0, &s0l, ud, (int)strlen(cast_const_char ud), -1);
815 		mem_free(ud);
816 		A(s0);
817 		A(ftp_head2);
818 		A(s0);
819 		A(ftp_head3);
820 		mem_free(s0);
821 		if (!c->cache->head) c->cache->head = stracpy(cast_uchar "\r\n");
822 		add_to_strn(&c->cache->head, cast_uchar "Content-Type: text/html\r\n");
823 		if (err) {
824 			setcstate(c, err);
825 			abort_connection(c);
826 			return;
827 		}
828 #undef A
829 	}
830 	EINTRLOOP(l, (int)read(c->sock2, inf->ftp_buffer + inf->buf_pos, FTP_BUF - inf->buf_pos));
831 	if (l == -1) {
832 		e:
833 		if (inf->conn_st != 1 && !inf->dir && !c->from) {
834 			set_handlers(c->sock2, NULL, NULL, NULL, NULL);
835 			close_socket(&c->sock2);
836 			inf->conn_st = 2;
837 			return;
838 		}
839 		setcstate(c, get_error_from_errno(errno));
840 		retry_connection(c);
841 		return;
842 	}
843 	if (l > 0) {
844 		if (!inf->dir) {
845 			if ((off_t)(0UL + c->from + l) < 0) {
846 				setcstate(c, S_LARGE_FILE);
847 				abort_connection(c);
848 				return;
849 			}
850 			c->received += l;
851 			m = add_fragment(c->cache, c->from, inf->ftp_buffer, l);
852 			if (m < 0) {
853 				setcstate(c, m);
854 				abort_connection(c);
855 				return;
856 			}
857 			if (m == 1) c->tries = 0;
858 			c->from += l;
859 		} else {
860 			c->received += l;
861 			m = ftp_process_dirlist(c->cache, &c->from, &inf->dpos, inf->ftp_buffer, l + inf->buf_pos, 0, inf->we_are_in_root, &c->tries);
862 			if (m < 0) {
863 				setcstate(c, m);
864 				abort_connection(c);
865 				return;
866 			}
867 			memmove(inf->ftp_buffer, inf->ftp_buffer + m, inf->buf_pos + l - m);
868 			inf->buf_pos += l - m;
869 		}
870 		setcstate(c, S_TRANS);
871 		return;
872 	}
873 	m = ftp_process_dirlist(c->cache, &c->from, &inf->dpos, inf->ftp_buffer, inf->buf_pos, 1, inf->we_are_in_root, &c->tries);
874 	if (m < 0) {
875 		setcstate(c, m);
876 		abort_connection(c);
877 		return;
878 	}
879 	set_handlers(c->sock2, NULL, NULL, NULL, NULL);
880 	close_socket(&c->sock2);
881 	if (inf->conn_st == 1) {
882 		ftp_end_request(c, S__OK);
883 	} else {
884 		inf->conn_st = 2;
885 	}
886 }
887 
ftp_end_request(struct connection * c,int state)888 static void ftp_end_request(struct connection *c, int state)
889 {
890 	struct ftp_connection_info *inf = c->info;
891 	if (state == S__OK) {
892 		if (c->cache) {
893 			truncate_entry(c->cache, c->from, 1);
894 			c->cache->incomplete = 0;
895 		}
896 	}
897 	setcstate(c, state);
898 	add_keepalive_socket(c, FTP_KEEPALIVE_TIMEOUT, inf->we_are_in_root);
899 }
900 
901