vsftpd 3.0.2
[tomato/tomato-dir865l.git] / release / src / router / vsftpd / postlogin.c
blob8a8316407adcd015d699ec521cdb348f066a752b
1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * postlogin.c
6 */
8 #include "postlogin.h"
9 #include "session.h"
10 #include "oneprocess.h"
11 #include "twoprocess.h"
12 #include "ftpcodes.h"
13 #include "ftpcmdio.h"
14 #include "ftpdataio.h"
15 #include "utility.h"
16 #include "tunables.h"
17 #include "defs.h"
18 #include "str.h"
19 #include "sysstr.h"
20 #include "banner.h"
21 #include "sysutil.h"
22 #include "logging.h"
23 #include "sysdeputil.h"
24 #include "ipaddrparse.h"
25 #include "access.h"
26 #include "features.h"
27 #include "ssl.h"
28 #include "vsftpver.h"
29 #include "opts.h"
31 /* Private local functions */
32 static void handle_pwd(struct vsf_session* p_sess);
33 static void handle_cwd(struct vsf_session* p_sess);
34 static void handle_pasv(struct vsf_session* p_sess, int is_epsv);
35 static void handle_retr(struct vsf_session* p_sess, int is_http);
36 static void handle_cdup(struct vsf_session* p_sess);
37 static void handle_list(struct vsf_session* p_sess);
38 static void handle_type(struct vsf_session* p_sess);
39 static void handle_port(struct vsf_session* p_sess);
40 static void handle_stor(struct vsf_session* p_sess);
41 static void handle_mkd(struct vsf_session* p_sess);
42 static void handle_rmd(struct vsf_session* p_sess);
43 static void handle_dele(struct vsf_session* p_sess);
44 static void handle_rest(struct vsf_session* p_sess);
45 static void handle_rnfr(struct vsf_session* p_sess);
46 static void handle_rnto(struct vsf_session* p_sess);
47 static void handle_nlst(struct vsf_session* p_sess);
48 static void handle_size(struct vsf_session* p_sess);
49 static void handle_site(struct vsf_session* p_sess);
50 static void handle_appe(struct vsf_session* p_sess);
51 static void handle_mdtm(struct vsf_session* p_sess);
52 static void handle_site_chmod(struct vsf_session* p_sess,
53 struct mystr* p_arg_str);
54 static void handle_site_umask(struct vsf_session* p_sess,
55 struct mystr* p_arg_str);
56 static void handle_eprt(struct vsf_session* p_sess);
57 static void handle_help(struct vsf_session* p_sess);
58 static void handle_stou(struct vsf_session* p_sess);
59 static void handle_stat(struct vsf_session* p_sess);
60 static void handle_stat_file(struct vsf_session* p_sess);
61 static void handle_logged_in_user(struct vsf_session* p_sess);
62 static void handle_logged_in_pass(struct vsf_session* p_sess);
63 static void handle_http(struct vsf_session* p_sess);
65 static int pasv_active(struct vsf_session* p_sess);
66 static int port_active(struct vsf_session* p_sess);
67 static void pasv_cleanup(struct vsf_session* p_sess);
68 static void port_cleanup(struct vsf_session* p_sess);
69 static void handle_dir_common(struct vsf_session* p_sess, int full_details,
70 int stat_cmd);
71 static void prepend_path_to_filename(struct mystr* p_str);
72 static int get_remote_transfer_fd(struct vsf_session* p_sess,
73 const char* p_status_msg);
74 static void check_abor(struct vsf_session* p_sess);
75 static void handle_sigurg(void* p_private);
76 static void handle_upload_common(struct vsf_session* p_sess, int is_append,
77 int is_unique);
78 static void get_unique_filename(struct mystr* p_outstr,
79 const struct mystr* p_base);
80 static int data_transfer_checks_ok(struct vsf_session* p_sess);
81 static void resolve_tilde(struct mystr* p_str, struct vsf_session* p_sess);
83 void
84 process_post_login(struct vsf_session* p_sess)
86 str_getcwd(&p_sess->home_str);
87 if (p_sess->is_anonymous)
89 vsf_sysutil_set_umask(tunable_anon_umask);
90 p_sess->bw_rate_max = tunable_anon_max_rate;
92 else
94 vsf_sysutil_set_umask(tunable_local_umask);
95 p_sess->bw_rate_max = tunable_local_max_rate;
97 if (p_sess->is_http)
99 handle_http(p_sess);
100 bug("should not be reached");
103 /* Don't support async ABOR if we have an SSL channel. The spec says SHOULD
104 * NOT, and I think there are synchronization issues between command and
105 * data reads.
107 if (tunable_async_abor_enable && !p_sess->control_use_ssl)
109 vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess, 0);
110 vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD);
112 /* Handle any login message */
113 vsf_banner_dir_changed(p_sess, FTP_LOGINOK);
114 vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful.");
116 while(1)
118 int cmd_ok = 1;
119 if (tunable_setproctitle_enable)
121 vsf_sysutil_setproctitle("IDLE");
123 /* Blocks */
124 vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
125 &p_sess->ftp_arg_str, 1);
126 if (tunable_setproctitle_enable)
128 struct mystr proctitle_str = INIT_MYSTR;
129 str_copy(&proctitle_str, &p_sess->ftp_cmd_str);
130 if (!str_isempty(&p_sess->ftp_arg_str))
132 str_append_char(&proctitle_str, ' ');
133 str_append_str(&proctitle_str, &p_sess->ftp_arg_str);
135 /* Suggestion from Solar */
136 str_replace_unprintable(&proctitle_str, '?');
137 vsf_sysutil_setproctitle_str(&proctitle_str);
138 str_free(&proctitle_str);
140 /* Test command against the allowed lists.. */
141 if (tunable_cmds_allowed)
143 static struct mystr s_src_str;
144 static struct mystr s_rhs_str;
145 str_alloc_text(&s_src_str, tunable_cmds_allowed);
146 while (1)
148 str_split_char(&s_src_str, &s_rhs_str, ',');
149 if (str_isempty(&s_src_str))
151 cmd_ok = 0;
152 break;
154 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
156 break;
158 str_copy(&s_src_str, &s_rhs_str);
161 if (tunable_cmds_denied)
163 static struct mystr s_src_str;
164 static struct mystr s_rhs_str;
165 str_alloc_text(&s_src_str, tunable_cmds_denied);
166 while (1)
168 str_split_char(&s_src_str, &s_rhs_str, ',');
169 if (str_isempty(&s_src_str))
171 break;
173 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
175 cmd_ok = 0;
176 break;
178 str_copy(&s_src_str, &s_rhs_str);
181 if (!cmd_ok)
183 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
185 else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
187 vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.", 0);
189 else if (str_equal_text(&p_sess->ftp_cmd_str, "PWD") ||
190 str_equal_text(&p_sess->ftp_cmd_str, "XPWD"))
192 handle_pwd(p_sess);
194 else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
195 str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
197 handle_cwd(p_sess);
199 else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") ||
200 str_equal_text(&p_sess->ftp_cmd_str, "XCUP"))
202 handle_cdup(p_sess);
204 else if (tunable_pasv_enable &&
205 !p_sess->epsv_all &&
206 (str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
207 str_equal_text(&p_sess->ftp_cmd_str, "P@SW")))
209 handle_pasv(p_sess, 0);
211 else if (tunable_pasv_enable &&
212 str_equal_text(&p_sess->ftp_cmd_str, "EPSV"))
214 handle_pasv(p_sess, 1);
216 else if (tunable_download_enable &&
217 str_equal_text(&p_sess->ftp_cmd_str, "RETR"))
219 handle_retr(p_sess, 0);
221 else if (str_equal_text(&p_sess->ftp_cmd_str, "NOOP"))
223 vsf_cmdio_write(p_sess, FTP_NOOPOK, "NOOP ok.");
225 else if (str_equal_text(&p_sess->ftp_cmd_str, "SYST"))
227 vsf_cmdio_write(p_sess, FTP_SYSTOK, "UNIX Type: L8");
229 else if (str_equal_text(&p_sess->ftp_cmd_str, "HELP"))
231 handle_help(p_sess);
233 else if (tunable_dirlist_enable &&
234 str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
236 handle_list(p_sess);
238 else if (str_equal_text(&p_sess->ftp_cmd_str, "TYPE"))
240 handle_type(p_sess);
242 else if (tunable_port_enable &&
243 !p_sess->epsv_all &&
244 str_equal_text(&p_sess->ftp_cmd_str, "PORT"))
246 handle_port(p_sess);
248 else if (tunable_write_enable &&
249 (tunable_anon_upload_enable || !p_sess->is_anonymous) &&
250 str_equal_text(&p_sess->ftp_cmd_str, "STOR"))
252 handle_stor(p_sess);
254 else if (tunable_write_enable &&
255 (tunable_anon_mkdir_write_enable || !p_sess->is_anonymous) &&
256 (str_equal_text(&p_sess->ftp_cmd_str, "MKD") ||
257 str_equal_text(&p_sess->ftp_cmd_str, "XMKD")))
259 handle_mkd(p_sess);
261 else if (tunable_write_enable &&
262 (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
263 (str_equal_text(&p_sess->ftp_cmd_str, "RMD") ||
264 str_equal_text(&p_sess->ftp_cmd_str, "XRMD")))
266 handle_rmd(p_sess);
268 else if (tunable_write_enable &&
269 (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
270 str_equal_text(&p_sess->ftp_cmd_str, "DELE"))
272 handle_dele(p_sess);
274 else if (str_equal_text(&p_sess->ftp_cmd_str, "REST"))
276 handle_rest(p_sess);
278 else if (tunable_write_enable &&
279 (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
280 str_equal_text(&p_sess->ftp_cmd_str, "RNFR"))
282 handle_rnfr(p_sess);
284 else if (tunable_write_enable &&
285 (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
286 str_equal_text(&p_sess->ftp_cmd_str, "RNTO"))
288 handle_rnto(p_sess);
290 else if (tunable_dirlist_enable &&
291 str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
293 handle_nlst(p_sess);
295 else if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE"))
297 handle_size(p_sess);
299 else if (!p_sess->is_anonymous &&
300 str_equal_text(&p_sess->ftp_cmd_str, "SITE"))
302 handle_site(p_sess);
304 /* Note - the weird ABOR string is checking for an async ABOR arriving
305 * without a SIGURG condition.
307 else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR") ||
308 str_equal_text(&p_sess->ftp_cmd_str, "\377\364\377\362ABOR"))
310 vsf_cmdio_write(p_sess, FTP_ABOR_NOCONN, "No transfer to ABOR.");
312 else if (tunable_write_enable &&
313 (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
314 str_equal_text(&p_sess->ftp_cmd_str, "APPE"))
316 handle_appe(p_sess);
318 else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM"))
320 handle_mdtm(p_sess);
322 else if (tunable_port_enable &&
323 str_equal_text(&p_sess->ftp_cmd_str, "EPRT"))
325 handle_eprt(p_sess);
327 else if (str_equal_text(&p_sess->ftp_cmd_str, "STRU"))
329 str_upper(&p_sess->ftp_arg_str);
330 if (str_equal_text(&p_sess->ftp_arg_str, "F"))
332 vsf_cmdio_write(p_sess, FTP_STRUOK, "Structure set to F.");
334 else
336 vsf_cmdio_write(p_sess, FTP_BADSTRU, "Bad STRU command.");
339 else if (str_equal_text(&p_sess->ftp_cmd_str, "MODE"))
341 str_upper(&p_sess->ftp_arg_str);
342 if (str_equal_text(&p_sess->ftp_arg_str, "S"))
344 vsf_cmdio_write(p_sess, FTP_MODEOK, "Mode set to S.");
346 else
348 vsf_cmdio_write(p_sess, FTP_BADMODE, "Bad MODE command.");
351 else if (tunable_write_enable &&
352 (tunable_anon_upload_enable || !p_sess->is_anonymous) &&
353 str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
355 handle_stou(p_sess);
357 else if (str_equal_text(&p_sess->ftp_cmd_str, "ALLO"))
359 vsf_cmdio_write(p_sess, FTP_ALLOOK, "ALLO command ignored.");
361 else if (str_equal_text(&p_sess->ftp_cmd_str, "REIN"))
363 vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "REIN not implemented.");
365 else if (str_equal_text(&p_sess->ftp_cmd_str, "ACCT"))
367 vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "ACCT not implemented.");
369 else if (str_equal_text(&p_sess->ftp_cmd_str, "SMNT"))
371 vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "SMNT not implemented.");
373 else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT"))
375 handle_feat(p_sess);
377 else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
379 handle_opts(p_sess);
381 else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
382 str_isempty(&p_sess->ftp_arg_str))
384 handle_stat(p_sess);
386 else if (tunable_dirlist_enable &&
387 str_equal_text(&p_sess->ftp_cmd_str, "STAT"))
389 handle_stat_file(p_sess);
391 else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PBSZ"))
393 handle_pbsz(p_sess);
395 else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
397 handle_prot(p_sess);
399 else if (str_equal_text(&p_sess->ftp_cmd_str, "USER"))
401 handle_logged_in_user(p_sess);
403 else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS"))
405 handle_logged_in_pass(p_sess);
407 else if (str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
408 str_equal_text(&p_sess->ftp_cmd_str, "PORT") ||
409 str_equal_text(&p_sess->ftp_cmd_str, "STOR") ||
410 str_equal_text(&p_sess->ftp_cmd_str, "MKD") ||
411 str_equal_text(&p_sess->ftp_cmd_str, "XMKD") ||
412 str_equal_text(&p_sess->ftp_cmd_str, "RMD") ||
413 str_equal_text(&p_sess->ftp_cmd_str, "XRMD") ||
414 str_equal_text(&p_sess->ftp_cmd_str, "DELE") ||
415 str_equal_text(&p_sess->ftp_cmd_str, "RNFR") ||
416 str_equal_text(&p_sess->ftp_cmd_str, "RNTO") ||
417 str_equal_text(&p_sess->ftp_cmd_str, "SITE") ||
418 str_equal_text(&p_sess->ftp_cmd_str, "APPE") ||
419 str_equal_text(&p_sess->ftp_cmd_str, "EPSV") ||
420 str_equal_text(&p_sess->ftp_cmd_str, "EPRT") ||
421 str_equal_text(&p_sess->ftp_cmd_str, "RETR") ||
422 str_equal_text(&p_sess->ftp_cmd_str, "LIST") ||
423 str_equal_text(&p_sess->ftp_cmd_str, "NLST") ||
424 str_equal_text(&p_sess->ftp_cmd_str, "STOU") ||
425 str_equal_text(&p_sess->ftp_cmd_str, "ALLO") ||
426 str_equal_text(&p_sess->ftp_cmd_str, "REIN") ||
427 str_equal_text(&p_sess->ftp_cmd_str, "ACCT") ||
428 str_equal_text(&p_sess->ftp_cmd_str, "SMNT") ||
429 str_equal_text(&p_sess->ftp_cmd_str, "FEAT") ||
430 str_equal_text(&p_sess->ftp_cmd_str, "OPTS") ||
431 str_equal_text(&p_sess->ftp_cmd_str, "STAT") ||
432 str_equal_text(&p_sess->ftp_cmd_str, "PBSZ") ||
433 str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
435 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
437 else if (str_isempty(&p_sess->ftp_cmd_str) &&
438 str_isempty(&p_sess->ftp_arg_str))
440 /* Deliberately ignore to avoid NAT device bugs. ProFTPd does the same. */
442 else
444 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command.");
446 if (vsf_log_entry_pending(p_sess))
448 vsf_log_do_log(p_sess, 0);
450 if (p_sess->data_timeout)
452 vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT,
453 "Data timeout. Reconnect. Sorry.", 1);
458 static void
459 handle_pwd(struct vsf_session* p_sess)
461 static struct mystr s_cwd_buf_mangle_str;
462 static struct mystr s_pwd_res_str;
463 str_getcwd(&s_cwd_buf_mangle_str);
464 /* Double up any double-quotes in the pathname! */
465 str_replace_text(&s_cwd_buf_mangle_str, "\"", "\"\"");
466 /* Enclose pathname in quotes */
467 str_alloc_text(&s_pwd_res_str, "\"");
468 str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
469 str_append_text(&s_pwd_res_str, "\"");
470 vsf_cmdio_write_str(p_sess, FTP_PWDOK, &s_pwd_res_str);
473 static void
474 handle_cwd(struct vsf_session* p_sess)
476 int retval;
477 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
478 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
480 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
481 return;
483 retval = str_chdir(&p_sess->ftp_arg_str);
484 if (retval == 0)
486 /* Handle any messages */
487 vsf_banner_dir_changed(p_sess, FTP_CWDOK);
488 vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
490 else
492 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
496 static void
497 handle_cdup(struct vsf_session* p_sess)
499 str_alloc_text(&p_sess->ftp_arg_str, "..");
500 handle_cwd(p_sess);
503 static int
504 port_active(struct vsf_session* p_sess)
506 int ret = 0;
507 if (p_sess->p_port_sockaddr != 0)
509 ret = 1;
510 if (pasv_active(p_sess))
512 bug("port and pasv both active");
515 return ret;
518 static int
519 pasv_active(struct vsf_session* p_sess)
521 int ret = 0;
522 if (tunable_one_process_model)
524 ret = vsf_one_process_pasv_active(p_sess);
526 else
528 ret = vsf_two_process_pasv_active(p_sess);
530 if (ret)
532 if (port_active(p_sess))
534 bug("pasv and port both active");
537 return ret;
540 static void
541 port_cleanup(struct vsf_session* p_sess)
543 vsf_sysutil_sockaddr_clear(&p_sess->p_port_sockaddr);
546 static void
547 pasv_cleanup(struct vsf_session* p_sess)
549 if (tunable_one_process_model)
551 vsf_one_process_pasv_cleanup(p_sess);
553 else
555 vsf_two_process_pasv_cleanup(p_sess);
559 static void
560 handle_pasv(struct vsf_session* p_sess, int is_epsv)
562 unsigned short the_port;
563 static struct mystr s_pasv_res_str;
564 static struct vsf_sysutil_sockaddr* s_p_sockaddr;
565 int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
566 if (is_epsv && !str_isempty(&p_sess->ftp_arg_str))
568 int argval;
569 str_upper(&p_sess->ftp_arg_str);
570 if (str_equal_text(&p_sess->ftp_arg_str, "ALL"))
572 p_sess->epsv_all = 1;
573 vsf_cmdio_write(p_sess, FTP_EPSVALLOK, "EPSV ALL ok.");
574 return;
576 argval = vsf_sysutil_atoi(str_getbuf(&p_sess->ftp_arg_str));
577 if (argval < 1 || argval > 2 || (!is_ipv6 && argval == 2))
579 vsf_cmdio_write(p_sess, FTP_EPSVBAD, "Bad network protocol.");
580 return;
583 pasv_cleanup(p_sess);
584 port_cleanup(p_sess);
585 if (tunable_one_process_model)
587 the_port = vsf_one_process_listen(p_sess);
589 else
591 the_port = vsf_two_process_listen(p_sess);
593 if (is_epsv)
595 str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
596 str_append_ulong(&s_pasv_res_str, (unsigned long) the_port);
597 str_append_text(&s_pasv_res_str, "|).");
598 vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str);
599 return;
601 if (tunable_pasv_address != 0)
603 vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
604 /* Report passive address as specified in configuration */
605 if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
607 die("invalid pasv_address");
610 else
612 vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
614 str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
615 if (!is_ipv6)
617 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
619 else
621 const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
622 if (p_v4addr)
624 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
626 else
628 str_append_text(&s_pasv_res_str, "0,0,0,0");
631 str_replace_char(&s_pasv_res_str, '.', ',');
632 str_append_text(&s_pasv_res_str, ",");
633 str_append_ulong(&s_pasv_res_str, the_port >> 8);
634 str_append_text(&s_pasv_res_str, ",");
635 str_append_ulong(&s_pasv_res_str, the_port & 255);
636 str_append_text(&s_pasv_res_str, ").");
637 vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
640 static void
641 handle_retr(struct vsf_session* p_sess, int is_http)
643 static struct mystr s_mark_str;
644 static struct vsf_sysutil_statbuf* s_p_statbuf;
645 struct vsf_transfer_ret trans_ret;
646 int remote_fd;
647 int opened_file;
648 int is_ascii = 0;
649 filesize_t offset = p_sess->restart_pos;
650 p_sess->restart_pos = 0;
651 if (!is_http && !data_transfer_checks_ok(p_sess))
653 return;
655 if (p_sess->is_ascii && offset != 0)
657 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
658 "No support for resume of ASCII transfer.");
659 return;
661 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
662 vsf_log_start_entry(p_sess, kVSFLogEntryDownload);
663 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
664 prepend_path_to_filename(&p_sess->log_str);
665 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
667 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
668 return;
670 opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
671 if (vsf_sysutil_retval_is_error(opened_file))
673 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
674 return;
676 /* Lock file if required */
677 if (tunable_lock_upload_files)
679 vsf_sysutil_lock_file_read(opened_file);
681 vsf_sysutil_fstat(opened_file, &s_p_statbuf);
682 /* No games please */
683 if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
685 /* Note - pretend open failed */
686 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
687 /* Irritating FireFox does RETR on directories, so avoid logging this
688 * very common and noisy case.
690 if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
692 vsf_log_clear_entry(p_sess);
694 goto file_close_out;
696 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
697 * such as XFS DMAPI.
699 vsf_sysutil_deactivate_noblock(opened_file);
700 /* Optionally, we'll be paranoid and only serve publicly readable stuff */
701 if (p_sess->is_anonymous && tunable_anon_world_readable_only &&
702 !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf))
704 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
705 goto file_close_out;
707 /* Set the download offset (from REST) if any */
708 if (offset != 0)
710 vsf_sysutil_lseek_to(opened_file, offset);
712 str_alloc_text(&s_mark_str, "Opening ");
713 if (tunable_ascii_download_enable && p_sess->is_ascii)
715 str_append_text(&s_mark_str, "ASCII");
716 is_ascii = 1;
718 else
720 str_append_text(&s_mark_str, "BINARY");
722 str_append_text(&s_mark_str, " mode data connection for ");
723 str_append_str(&s_mark_str, &p_sess->ftp_arg_str);
724 str_append_text(&s_mark_str, " (");
725 str_append_filesize_t(&s_mark_str,
726 vsf_sysutil_statbuf_get_size(s_p_statbuf));
727 str_append_text(&s_mark_str, " bytes).");
728 if (is_http)
730 remote_fd = VSFTP_COMMAND_FD;
732 else
734 remote_fd = get_remote_transfer_fd(p_sess, str_getbuf(&s_mark_str));
735 if (vsf_sysutil_retval_is_error(remote_fd))
737 goto port_pasv_cleanup_out;
740 trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
741 opened_file, 0, is_ascii);
742 if (!is_http &&
743 vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 &&
744 trans_ret.retval == 0)
746 trans_ret.retval = -2;
748 p_sess->transfer_size = trans_ret.transferred;
749 /* Log _after_ the blocking dispose call, so we get transfer times right */
750 if (trans_ret.retval == 0)
752 vsf_log_do_log(p_sess, 1);
754 if (is_http)
756 goto file_close_out;
758 /* Emit status message _after_ blocking dispose call to avoid buggy FTP
759 * clients truncating the transfer.
761 if (trans_ret.retval == -1)
763 vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure reading local file.");
765 else if (trans_ret.retval == -2)
767 if (!p_sess->data_timeout)
769 vsf_cmdio_write(p_sess, FTP_BADSENDNET,
770 "Failure writing network stream.");
773 else
775 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
777 check_abor(p_sess);
778 port_pasv_cleanup_out:
779 port_cleanup(p_sess);
780 pasv_cleanup(p_sess);
781 file_close_out:
782 vsf_sysutil_close(opened_file);
785 static void
786 handle_list(struct vsf_session* p_sess)
788 handle_dir_common(p_sess, 1, 0);
791 static void
792 handle_dir_common(struct vsf_session* p_sess, int full_details, int stat_cmd)
794 static struct mystr s_option_str;
795 static struct mystr s_filter_str;
796 static struct mystr s_dir_name_str;
797 static struct vsf_sysutil_statbuf* s_p_dirstat;
798 int dir_allow_read = 1;
799 struct vsf_sysutil_dir* p_dir = 0;
800 int retval = 0;
801 int use_control = 0;
802 str_empty(&s_option_str);
803 str_empty(&s_filter_str);
804 /* By default open the current directory */
805 str_alloc_text(&s_dir_name_str, ".");
806 if (!stat_cmd && !data_transfer_checks_ok(p_sess))
808 return;
810 /* Do we have an option? Going to be strict here - the option must come
811 * first. e.g. "ls -a .." fine, "ls .. -a" not fine
813 if (!str_isempty(&p_sess->ftp_arg_str) &&
814 str_get_char_at(&p_sess->ftp_arg_str, 0) == '-')
816 /* Chop off the '-' */
817 str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1);
818 /* A space will separate options from filter (if any) */
819 str_split_char(&s_option_str, &s_filter_str, ' ');
821 else
823 /* The argument, if any, is just a filter */
824 str_copy(&s_filter_str, &p_sess->ftp_arg_str);
826 if (!str_isempty(&s_filter_str))
828 resolve_tilde(&s_filter_str, p_sess);
829 if (!vsf_access_check_file(&s_filter_str))
831 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
832 return;
834 /* First check - is it an outright directory, as in "ls /pub" */
835 p_dir = str_opendir(&s_filter_str);
836 if (p_dir != 0)
838 /* Listing a directory! */
839 str_copy(&s_dir_name_str, &s_filter_str);
840 str_free(&s_filter_str);
842 else
844 struct str_locate_result locate_result =
845 str_locate_char(&s_filter_str, '/');
846 if (locate_result.found)
848 /* Includes a path! Reverse scan for / in the arg, to get the
849 * base directory and filter (if any)
851 str_copy(&s_dir_name_str, &s_filter_str);
852 str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/');
853 /* If we have e.g. "ls /.message", we just ripped off the leading
854 * slash because it is the only one!
856 if (str_isempty(&s_dir_name_str))
858 str_alloc_text(&s_dir_name_str, "/");
863 if (p_dir == 0)
865 /* NOTE - failure check done below, it's not forgotten */
866 p_dir = str_opendir(&s_dir_name_str);
868 /* Fine, do it */
869 if (stat_cmd)
871 use_control = 1;
872 str_append_char(&s_option_str, 'a');
873 vsf_cmdio_write_hyphen(p_sess, FTP_STATFILE_OK, "Status follows:");
875 else
877 int remote_fd = get_remote_transfer_fd(
878 p_sess, "Here comes the directory listing.");
879 if (vsf_sysutil_retval_is_error(remote_fd))
881 goto dir_close_out;
884 if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only)
886 vsf_sysutil_dir_stat(p_dir, &s_p_dirstat);
887 if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat))
889 dir_allow_read = 0;
892 if (p_dir != 0 && dir_allow_read)
894 retval = vsf_ftpdataio_transfer_dir(p_sess, use_control, p_dir,
895 &s_dir_name_str, &s_option_str,
896 &s_filter_str, full_details);
898 if (!stat_cmd)
900 if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && retval == 0)
902 retval = -1;
905 if (stat_cmd)
907 vsf_cmdio_write(p_sess, FTP_STATFILE_OK, "End of status");
909 else if (retval != 0)
911 if (!p_sess->data_timeout)
913 vsf_cmdio_write(p_sess, FTP_BADSENDNET,
914 "Failure writing network stream.");
917 else if (p_dir == 0 || !dir_allow_read)
919 vsf_cmdio_write(p_sess, FTP_TRANSFEROK,
920 "Transfer done (but failed to open directory).");
922 else
924 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
926 check_abor(p_sess);
927 dir_close_out:
928 if (p_dir)
930 vsf_sysutil_closedir(p_dir);
932 if (!stat_cmd)
934 port_cleanup(p_sess);
935 pasv_cleanup(p_sess);
939 static void
940 handle_type(struct vsf_session* p_sess)
942 str_upper(&p_sess->ftp_arg_str);
943 if (str_equal_text(&p_sess->ftp_arg_str, "I") ||
944 str_equal_text(&p_sess->ftp_arg_str, "L8") ||
945 str_equal_text(&p_sess->ftp_arg_str, "L 8"))
947 p_sess->is_ascii = 0;
948 vsf_cmdio_write(p_sess, FTP_TYPEOK, "Switching to Binary mode.");
950 else if (str_equal_text(&p_sess->ftp_arg_str, "A") ||
951 str_equal_text(&p_sess->ftp_arg_str, "A N"))
953 p_sess->is_ascii = 1;
954 vsf_cmdio_write(p_sess, FTP_TYPEOK, "Switching to ASCII mode.");
956 else
958 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unrecognised TYPE command.");
962 static void
963 handle_port(struct vsf_session* p_sess)
965 unsigned short the_port;
966 unsigned char vals[6];
967 const unsigned char* p_raw;
968 pasv_cleanup(p_sess);
969 port_cleanup(p_sess);
970 p_raw = vsf_sysutil_parse_uchar_string_sep(&p_sess->ftp_arg_str, ',', vals,
971 sizeof(vals));
972 if (p_raw == 0)
974 vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
975 return;
977 the_port = (unsigned short) ((vals[4] << 8) | vals[5]);
978 vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
979 vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, vals);
980 vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, the_port);
981 /* SECURITY:
982 * 1) Reject requests not connecting to the control socket IP
983 * 2) Reject connects to privileged ports
985 if (!tunable_port_promiscuous)
987 if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
988 p_sess->p_port_sockaddr) ||
989 vsf_sysutil_is_port_reserved(the_port))
991 vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
992 port_cleanup(p_sess);
993 return;
996 vsf_cmdio_write(p_sess, FTP_PORTOK,
997 "PORT command successful. Consider using PASV.");
1000 static void
1001 handle_stor(struct vsf_session* p_sess)
1003 handle_upload_common(p_sess, 0, 0);
1006 static void
1007 handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
1009 static struct vsf_sysutil_statbuf* s_p_statbuf;
1010 static struct mystr s_filename;
1011 struct mystr* p_filename;
1012 struct vsf_transfer_ret trans_ret;
1013 int new_file_fd;
1014 int remote_fd;
1015 int success = 0;
1016 int created = 0;
1017 int do_truncate = 0;
1018 filesize_t offset = p_sess->restart_pos;
1019 p_sess->restart_pos = 0;
1020 if (!data_transfer_checks_ok(p_sess))
1022 return;
1024 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1025 p_filename = &p_sess->ftp_arg_str;
1026 if (is_unique)
1028 get_unique_filename(&s_filename, p_filename);
1029 p_filename = &s_filename;
1031 vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
1032 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1033 prepend_path_to_filename(&p_sess->log_str);
1034 if (!vsf_access_check_file(p_filename))
1036 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1037 return;
1039 /* NOTE - actual file permissions will be governed by the tunable umask */
1040 /* XXX - do we care about race between create and chown() of anonymous
1041 * upload?
1043 if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
1045 new_file_fd = str_create_exclusive(p_filename);
1047 else
1049 /* For non-anonymous, allow open() to overwrite or append existing files */
1050 new_file_fd = str_create(p_filename);
1051 if (!is_append && offset == 0)
1053 do_truncate = 1;
1056 if (vsf_sysutil_retval_is_error(new_file_fd))
1058 vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
1059 return;
1061 created = 1;
1062 vsf_sysutil_fstat(new_file_fd, &s_p_statbuf);
1063 if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
1065 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
1066 * such as XFS DMAPI.
1068 vsf_sysutil_deactivate_noblock(new_file_fd);
1070 /* Are we required to chown() this file for security? */
1071 if (p_sess->is_anonymous && tunable_chown_uploads)
1073 vsf_sysutil_fchmod(new_file_fd, tunable_chown_upload_mode);
1074 if (tunable_one_process_model)
1076 vsf_one_process_chown_upload(p_sess, new_file_fd);
1078 else
1080 vsf_two_process_chown_upload(p_sess, new_file_fd);
1083 /* Are we required to lock this file? */
1084 if (tunable_lock_upload_files)
1086 vsf_sysutil_lock_file_write(new_file_fd);
1088 /* Must truncate the file AFTER locking it! */
1089 if (do_truncate)
1091 vsf_sysutil_ftruncate(new_file_fd);
1092 vsf_sysutil_lseek_to(new_file_fd, 0);
1094 if (!is_append && offset != 0)
1096 /* XXX - warning, allows seek past end of file! Check for seek > size? */
1097 vsf_sysutil_lseek_to(new_file_fd, offset);
1099 else if (is_append)
1101 vsf_sysutil_lseek_end(new_file_fd);
1103 if (is_unique)
1105 struct mystr resp_str = INIT_MYSTR;
1106 str_alloc_text(&resp_str, "FILE: ");
1107 str_append_str(&resp_str, p_filename);
1108 remote_fd = get_remote_transfer_fd(p_sess, str_getbuf(&resp_str));
1109 str_free(&resp_str);
1111 else
1113 remote_fd = get_remote_transfer_fd(p_sess, "Ok to send data.");
1115 if (vsf_sysutil_retval_is_error(remote_fd))
1117 goto port_pasv_cleanup_out;
1119 if (tunable_ascii_upload_enable && p_sess->is_ascii)
1121 trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
1122 new_file_fd, 1, 1);
1124 else
1126 trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
1127 new_file_fd, 1, 0);
1129 if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && trans_ret.retval == 0)
1131 trans_ret.retval = -2;
1133 p_sess->transfer_size = trans_ret.transferred;
1134 if (trans_ret.retval == 0)
1136 success = 1;
1137 vsf_log_do_log(p_sess, 1);
1139 if (trans_ret.retval == -1)
1141 vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure writing to local file.");
1143 else if (trans_ret.retval == -2)
1145 if (!p_sess->data_timeout)
1147 vsf_cmdio_write(p_sess, FTP_BADSENDNET,
1148 "Failure reading network stream.");
1151 else
1153 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
1155 check_abor(p_sess);
1156 port_pasv_cleanup_out:
1157 port_cleanup(p_sess);
1158 pasv_cleanup(p_sess);
1159 if (tunable_delete_failed_uploads && created && !success)
1161 str_unlink(p_filename);
1163 vsf_sysutil_close(new_file_fd);
1166 static void
1167 handle_mkd(struct vsf_session* p_sess)
1169 int retval;
1170 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1171 vsf_log_start_entry(p_sess, kVSFLogEntryMkdir);
1172 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1173 prepend_path_to_filename(&p_sess->log_str);
1174 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1176 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1177 return;
1179 /* NOTE! Actual permissions will be governed by the tunable umask */
1180 retval = str_mkdir(&p_sess->ftp_arg_str, 0777);
1181 if (retval != 0)
1183 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1184 "Create directory operation failed.");
1185 return;
1187 vsf_log_do_log(p_sess, 1);
1189 static struct mystr s_mkd_res;
1190 static struct mystr s_tmp_str;
1191 str_copy(&s_tmp_str, &p_sess->ftp_arg_str);
1192 prepend_path_to_filename(&s_tmp_str);
1193 /* Double up double quotes */
1194 str_replace_text(&s_tmp_str, "\"", "\"\"");
1195 /* Build result string */
1196 str_alloc_text(&s_mkd_res, "\"");
1197 str_append_str(&s_mkd_res, &s_tmp_str);
1198 str_append_text(&s_mkd_res, "\" created");
1199 vsf_cmdio_write_str(p_sess, FTP_MKDIROK, &s_mkd_res);
1203 static void
1204 handle_rmd(struct vsf_session* p_sess)
1206 int retval;
1207 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1208 vsf_log_start_entry(p_sess, kVSFLogEntryRmdir);
1209 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1210 prepend_path_to_filename(&p_sess->log_str);
1211 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1213 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1214 return;
1216 retval = str_rmdir(&p_sess->ftp_arg_str);
1217 if (retval != 0)
1219 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1220 "Remove directory operation failed.");
1222 else
1224 vsf_log_do_log(p_sess, 1);
1225 vsf_cmdio_write(p_sess, FTP_RMDIROK,
1226 "Remove directory operation successful.");
1230 static void
1231 handle_dele(struct vsf_session* p_sess)
1233 int retval;
1234 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1235 vsf_log_start_entry(p_sess, kVSFLogEntryDelete);
1236 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1237 prepend_path_to_filename(&p_sess->log_str);
1238 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1240 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1241 return;
1243 retval = str_unlink(&p_sess->ftp_arg_str);
1244 if (retval != 0)
1246 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
1248 else
1250 vsf_log_do_log(p_sess, 1);
1251 vsf_cmdio_write(p_sess, FTP_DELEOK, "Delete operation successful.");
1255 static void
1256 handle_rest(struct vsf_session* p_sess)
1258 static struct mystr s_rest_str;
1259 filesize_t val = str_a_to_filesize_t(&p_sess->ftp_arg_str);
1260 if (val < 0)
1262 val = 0;
1264 p_sess->restart_pos = val;
1265 str_alloc_text(&s_rest_str, "Restart position accepted (");
1266 str_append_filesize_t(&s_rest_str, val);
1267 str_append_text(&s_rest_str, ").");
1268 vsf_cmdio_write_str(p_sess, FTP_RESTOK, &s_rest_str);
1271 static void
1272 handle_rnfr(struct vsf_session* p_sess)
1274 static struct vsf_sysutil_statbuf* p_statbuf;
1275 int retval;
1276 /* Clear old value */
1277 str_free(&p_sess->rnfr_filename_str);
1278 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1279 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1281 vsf_log_start_entry(p_sess, kVSFLogEntryRename);
1282 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1283 prepend_path_to_filename(&p_sess->log_str);
1284 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1285 return;
1287 /* Does it exist? */
1288 retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
1289 if (retval == 0)
1291 /* Yes */
1292 str_copy(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
1293 vsf_cmdio_write(p_sess, FTP_RNFROK, "Ready for RNTO.");
1295 else
1297 vsf_log_start_entry(p_sess, kVSFLogEntryRename);
1298 str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
1299 prepend_path_to_filename(&p_sess->log_str);
1300 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "RNFR command failed.");
1304 static void
1305 handle_rnto(struct vsf_session* p_sess)
1307 static struct mystr s_tmp_str;
1308 int retval;
1309 /* If we didn't get a RNFR, throw a wobbly */
1310 if (str_isempty(&p_sess->rnfr_filename_str))
1312 vsf_cmdio_write(p_sess, FTP_NEEDRNFR,
1313 "RNFR required first.");
1314 return;
1316 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1317 vsf_log_start_entry(p_sess, kVSFLogEntryRename);
1318 str_copy(&p_sess->log_str, &p_sess->rnfr_filename_str);
1319 prepend_path_to_filename(&p_sess->log_str);
1320 str_append_char(&p_sess->log_str, ' ');
1321 str_copy(&s_tmp_str, &p_sess->ftp_arg_str);
1322 prepend_path_to_filename(&s_tmp_str);
1323 str_append_str(&p_sess->log_str, &s_tmp_str);
1324 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1326 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1327 return;
1329 /* NOTE - might overwrite destination file. Not a concern because the same
1330 * could be accomplished with DELE.
1332 retval = str_rename(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
1333 /* Clear the RNFR filename; start the two stage process again! */
1334 str_free(&p_sess->rnfr_filename_str);
1335 if (retval == 0)
1337 vsf_log_do_log(p_sess, 1);
1338 vsf_cmdio_write(p_sess, FTP_RENAMEOK, "Rename successful.");
1340 else
1342 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Rename failed.");
1346 static void
1347 handle_nlst(struct vsf_session* p_sess)
1349 handle_dir_common(p_sess, 0, 0);
1352 static void
1353 prepend_path_to_filename(struct mystr* p_str)
1355 static struct mystr s_tmp_str;
1356 /* Only prepend current working directory if the incoming filename is
1357 * relative
1359 str_empty(&s_tmp_str);
1360 if (str_isempty(p_str) || str_get_char_at(p_str, 0) != '/')
1362 str_getcwd(&s_tmp_str);
1363 /* Careful to not emit // if we are in directory / (common with chroot) */
1364 if (str_isempty(&s_tmp_str) ||
1365 str_get_char_at(&s_tmp_str, str_getlen(&s_tmp_str) - 1) != '/')
1367 str_append_char(&s_tmp_str, '/');
1370 str_append_str(&s_tmp_str, p_str);
1371 str_copy(p_str, &s_tmp_str);
1375 static void
1376 handle_sigurg(void* p_private)
1378 struct mystr async_cmd_str = INIT_MYSTR;
1379 struct mystr async_arg_str = INIT_MYSTR;
1380 struct mystr real_cmd_str = INIT_MYSTR;
1381 unsigned int len;
1382 struct vsf_session* p_sess = (struct vsf_session*) p_private;
1383 /* Did stupid client sent something OOB without a data connection? */
1384 if (p_sess->data_fd == -1)
1386 return;
1388 /* Get the async command - blocks (use data timeout alarm) */
1389 vsf_cmdio_get_cmd_and_arg(p_sess, &async_cmd_str, &async_arg_str, 0);
1390 /* Chop off first four characters; they are telnet characters. The client
1391 * should have sent the first two normally and the second two as urgent
1392 * data.
1394 len = str_getlen(&async_cmd_str);
1395 if (len >= 4)
1397 str_right(&async_cmd_str, &real_cmd_str, len - 4);
1399 if (str_equal_text(&real_cmd_str, "ABOR"))
1401 p_sess->abor_received = 1;
1402 /* This is failok because of a small race condition; the SIGURG might
1403 * be raised after the data socket is closed, but before data_fd is
1404 * set to -1.
1406 vsf_sysutil_shutdown_failok(p_sess->data_fd);
1408 else
1410 /* Sorry! */
1411 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command.");
1413 str_free(&async_cmd_str);
1414 str_free(&async_arg_str);
1415 str_free(&real_cmd_str);
1418 static int
1419 get_remote_transfer_fd(struct vsf_session* p_sess, const char* p_status_msg)
1421 int remote_fd;
1422 if (!pasv_active(p_sess) && !port_active(p_sess))
1424 bug("neither PORT nor PASV active in get_remote_transfer_fd");
1426 p_sess->abor_received = 0;
1427 if (pasv_active(p_sess))
1429 remote_fd = vsf_ftpdataio_get_pasv_fd(p_sess);
1431 else
1433 remote_fd = vsf_ftpdataio_get_port_fd(p_sess);
1435 if (vsf_sysutil_retval_is_error(remote_fd))
1437 return remote_fd;
1439 vsf_cmdio_write(p_sess, FTP_DATACONN, p_status_msg);
1440 if (vsf_ftpdataio_post_mark_connect(p_sess) != 1)
1442 vsf_ftpdataio_dispose_transfer_fd(p_sess);
1443 return -1;
1445 return remote_fd;
1448 static void
1449 check_abor(struct vsf_session* p_sess)
1451 /* If the client sent ABOR, respond to it here */
1452 if (p_sess->abor_received)
1454 p_sess->abor_received = 0;
1455 vsf_cmdio_write(p_sess, FTP_ABOROK, "ABOR successful.");
1459 static void
1460 handle_size(struct vsf_session* p_sess)
1462 /* Note - in ASCII mode, are supposed to return the size after taking into
1463 * account ASCII linefeed conversions. At least this is what wu-ftpd does in
1464 * version 2.6.1. Proftpd-1.2.0pre fails to do this.
1465 * I will not do it because it is a potential I/O DoS.
1467 static struct vsf_sysutil_statbuf* s_p_statbuf;
1468 int retval;
1469 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1470 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1472 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1473 return;
1475 retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
1476 if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
1478 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Could not get file size.");
1480 else
1482 static struct mystr s_size_res_str;
1483 str_alloc_filesize_t(&s_size_res_str,
1484 vsf_sysutil_statbuf_get_size(s_p_statbuf));
1485 vsf_cmdio_write_str(p_sess, FTP_SIZEOK, &s_size_res_str);
1489 static void
1490 handle_site(struct vsf_session* p_sess)
1492 static struct mystr s_site_args_str;
1493 /* What SITE sub-command is it? */
1494 str_split_char(&p_sess->ftp_arg_str, &s_site_args_str, ' ');
1495 str_upper(&p_sess->ftp_arg_str);
1496 if (tunable_write_enable &&
1497 tunable_chmod_enable &&
1498 str_equal_text(&p_sess->ftp_arg_str, "CHMOD"))
1500 handle_site_chmod(p_sess, &s_site_args_str);
1502 else if (str_equal_text(&p_sess->ftp_arg_str, "UMASK"))
1504 handle_site_umask(p_sess, &s_site_args_str);
1506 else if (str_equal_text(&p_sess->ftp_arg_str, "HELP"))
1508 if (tunable_write_enable &&
1509 tunable_chmod_enable)
1511 vsf_cmdio_write(p_sess, FTP_SITEHELP, "CHMOD UMASK HELP");
1513 else
1515 vsf_cmdio_write(p_sess, FTP_SITEHELP, "UMASK HELP");
1518 else
1520 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command.");
1524 static void
1525 handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str)
1527 static struct mystr s_chmod_file_str;
1528 unsigned int perms;
1529 int retval;
1530 if (str_isempty(p_arg_str))
1532 vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
1533 return;
1535 str_split_char(p_arg_str, &s_chmod_file_str, ' ');
1536 if (str_isempty(&s_chmod_file_str))
1538 vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
1539 return;
1541 resolve_tilde(&s_chmod_file_str, p_sess);
1542 vsf_log_start_entry(p_sess, kVSFLogEntryChmod);
1543 str_copy(&p_sess->log_str, &s_chmod_file_str);
1544 prepend_path_to_filename(&p_sess->log_str);
1545 str_append_char(&p_sess->log_str, ' ');
1546 str_append_str(&p_sess->log_str, p_arg_str);
1547 if (!vsf_access_check_file(&s_chmod_file_str))
1549 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1550 return;
1552 /* Don't worry - our chmod() implementation only allows 0 - 0777 */
1553 perms = str_octal_to_uint(p_arg_str);
1554 retval = str_chmod(&s_chmod_file_str, perms);
1555 if (vsf_sysutil_retval_is_error(retval))
1557 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "SITE CHMOD command failed.");
1559 else
1561 vsf_log_do_log(p_sess, 1);
1562 vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok.");
1566 static void
1567 handle_site_umask(struct vsf_session* p_sess, struct mystr* p_arg_str)
1569 static struct mystr s_umask_resp_str;
1570 if (str_isempty(p_arg_str))
1572 /* Empty arg => report current umask */
1573 str_alloc_text(&s_umask_resp_str, "Your current UMASK is ");
1574 str_append_text(&s_umask_resp_str,
1575 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1577 else
1579 /* Set current umask */
1580 unsigned int new_umask = str_octal_to_uint(p_arg_str);
1581 vsf_sysutil_set_umask(new_umask);
1582 str_alloc_text(&s_umask_resp_str, "UMASK set to ");
1583 str_append_text(&s_umask_resp_str,
1584 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1586 vsf_cmdio_write_str(p_sess, FTP_UMASKOK, &s_umask_resp_str);
1589 static void
1590 handle_appe(struct vsf_session* p_sess)
1592 handle_upload_common(p_sess, 1, 0);
1595 static void
1596 handle_mdtm(struct vsf_session* p_sess)
1598 static struct mystr s_filename_str;
1599 static struct vsf_sysutil_statbuf* s_p_statbuf;
1600 int do_write = 0;
1601 long modtime = 0;
1602 struct str_locate_result loc = str_locate_char(&p_sess->ftp_arg_str, ' ');
1603 int retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
1604 if (tunable_mdtm_write && retval != 0 && loc.found &&
1605 vsf_sysutil_isdigit(str_get_char_at(&p_sess->ftp_arg_str, 0)))
1607 if (loc.index == 8 || loc.index == 14 ||
1608 (loc.index > 15 && str_get_char_at(&p_sess->ftp_arg_str, 14) == '.'))
1610 do_write = 1;
1613 if (do_write != 0)
1615 str_split_char(&p_sess->ftp_arg_str, &s_filename_str, ' ');
1616 modtime = vsf_sysutil_parse_time(str_getbuf(&p_sess->ftp_arg_str));
1617 str_copy(&p_sess->ftp_arg_str, &s_filename_str);
1619 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1620 if (!vsf_access_check_file(&p_sess->ftp_arg_str))
1622 vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
1623 return;
1625 if (do_write && tunable_write_enable &&
1626 (tunable_anon_other_write_enable || !p_sess->is_anonymous))
1628 retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
1629 if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
1631 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1632 "Could not set file modification time.");
1634 else
1636 retval = vsf_sysutil_setmodtime(
1637 str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
1638 if (retval != 0)
1640 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1641 "Could not set file modification time.");
1643 else
1645 vsf_cmdio_write(p_sess, FTP_MDTMOK,
1646 "File modification time set.");
1650 else
1652 if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
1654 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1655 "Could not get file modification time.");
1657 else
1659 static struct mystr s_mdtm_res_str;
1660 str_alloc_text(&s_mdtm_res_str,
1661 vsf_sysutil_statbuf_get_numeric_date(
1662 s_p_statbuf, tunable_use_localtime));
1663 vsf_cmdio_write_str(p_sess, FTP_MDTMOK, &s_mdtm_res_str);
1668 static void
1669 handle_eprt(struct vsf_session* p_sess)
1671 static struct mystr s_part1_str;
1672 static struct mystr s_part2_str;
1673 static struct mystr s_scopeid_str;
1674 int proto;
1675 int port;
1676 const unsigned char* p_raw_addr;
1677 int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
1678 port_cleanup(p_sess);
1679 pasv_cleanup(p_sess);
1680 str_copy(&s_part1_str, &p_sess->ftp_arg_str);
1681 str_split_char(&s_part1_str, &s_part2_str, '|');
1682 if (!str_isempty(&s_part1_str))
1684 goto bad_eprt;
1686 /* Split out the protocol and check it */
1687 str_split_char(&s_part2_str, &s_part1_str, '|');
1688 proto = str_atoi(&s_part2_str);
1689 if (proto < 1 || proto > 2 || (!is_ipv6 && proto == 2))
1691 vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT protocol.");
1692 return;
1694 /* Split out address and parse it */
1695 str_split_char(&s_part1_str, &s_part2_str, '|');
1696 if (proto == 2)
1698 str_split_char(&s_part1_str, &s_scopeid_str, '%');
1699 p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str);
1701 else
1703 p_raw_addr = vsf_sysutil_parse_ipv4(&s_part1_str);
1705 if (!p_raw_addr)
1707 goto bad_eprt;
1709 /* Split out port and parse it */
1710 str_split_char(&s_part2_str, &s_part1_str, '|');
1711 if (!str_isempty(&s_part1_str) || str_isempty(&s_part2_str))
1713 goto bad_eprt;
1715 port = str_atoi(&s_part2_str);
1716 if (port < 0 || port > 65535)
1718 goto bad_eprt;
1720 vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
1721 if (proto == 2)
1723 vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr);
1725 else
1727 vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, p_raw_addr);
1729 vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, (unsigned short) port);
1730 /* SECURITY:
1731 * 1) Reject requests not connecting to the control socket IP
1732 * 2) Reject connects to privileged ports
1734 if (!tunable_port_promiscuous)
1736 if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
1737 p_sess->p_port_sockaddr) ||
1738 vsf_sysutil_is_port_reserved((unsigned short) port))
1740 vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal EPRT command.");
1741 port_cleanup(p_sess);
1742 return;
1745 vsf_cmdio_write(p_sess, FTP_EPRTOK,
1746 "EPRT command successful. Consider using EPSV.");
1747 return;
1748 bad_eprt:
1749 vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command.");
1752 /* XXX - add AUTH etc. */
1753 static void
1754 handle_help(struct vsf_session* p_sess)
1756 vsf_cmdio_write_hyphen(p_sess, FTP_HELP,
1757 "The following commands are recognized.");
1758 vsf_cmdio_write_raw(p_sess,
1759 " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
1760 vsf_cmdio_write_raw(p_sess,
1761 " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
1762 vsf_cmdio_write_raw(p_sess,
1763 " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
1764 vsf_cmdio_write_raw(p_sess,
1765 " XPWD XRMD\r\n");
1766 vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
1769 static void
1770 handle_stou(struct vsf_session* p_sess)
1772 handle_upload_common(p_sess, 0, 1);
1775 static void
1776 get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
1778 /* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
1779 * two sessions are using the same file prefix at the same time.
1781 static struct vsf_sysutil_statbuf* s_p_statbuf;
1782 static struct mystr s_stou_str;
1783 unsigned int suffix = 1;
1784 const struct mystr* p_real_base_str = p_base_str;
1785 int retval;
1786 if (str_isempty(p_real_base_str))
1788 str_alloc_text(&s_stou_str, "STOU");
1789 p_real_base_str = &s_stou_str;
1791 else
1793 /* Do not add any suffix at all if the name is not taken. */
1794 retval = str_stat(p_real_base_str, &s_p_statbuf);
1795 if (vsf_sysutil_retval_is_error(retval))
1797 str_copy(p_outstr, p_real_base_str);
1798 return;
1801 while (1)
1803 str_copy(p_outstr, p_real_base_str);
1804 str_append_char(p_outstr, '.');
1805 str_append_ulong(p_outstr, suffix);
1806 retval = str_stat(p_outstr, &s_p_statbuf);
1807 if (vsf_sysutil_retval_is_error(retval))
1809 return;
1811 ++suffix;
1815 static void
1816 handle_stat(struct vsf_session* p_sess)
1818 vsf_cmdio_write_hyphen(p_sess, FTP_STATOK, "FTP server status:");
1819 vsf_cmdio_write_raw(p_sess, " Connected to ");
1820 vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->remote_ip_str));
1821 vsf_cmdio_write_raw(p_sess, "\r\n");
1822 vsf_cmdio_write_raw(p_sess, " Logged in as ");
1823 vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->user_str));
1824 vsf_cmdio_write_raw(p_sess, "\r\n");
1825 vsf_cmdio_write_raw(p_sess, " TYPE: ");
1826 if (p_sess->is_ascii)
1828 vsf_cmdio_write_raw(p_sess, "ASCII\r\n");
1830 else
1832 vsf_cmdio_write_raw(p_sess, "BINARY\r\n");
1834 if (p_sess->bw_rate_max == 0)
1836 vsf_cmdio_write_raw(p_sess, " No session bandwidth limit\r\n");
1838 else
1840 vsf_cmdio_write_raw(p_sess, " Session bandwidth limit in byte/s is ");
1841 vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->bw_rate_max));
1842 vsf_cmdio_write_raw(p_sess, "\r\n");
1844 if (tunable_idle_session_timeout == 0)
1846 vsf_cmdio_write_raw(p_sess, " No session timeout\r\n");
1848 else
1850 vsf_cmdio_write_raw(p_sess, " Session timeout in seconds is ");
1851 vsf_cmdio_write_raw(p_sess,
1852 vsf_sysutil_ulong_to_str(tunable_idle_session_timeout));
1853 vsf_cmdio_write_raw(p_sess, "\r\n");
1855 if (p_sess->control_use_ssl)
1857 vsf_cmdio_write_raw(p_sess, " Control connection is encrypted\r\n");
1859 else
1861 vsf_cmdio_write_raw(p_sess, " Control connection is plain text\r\n");
1863 if (p_sess->data_use_ssl)
1865 vsf_cmdio_write_raw(p_sess, " Data connections will be encrypted\r\n");
1867 else
1869 vsf_cmdio_write_raw(p_sess, " Data connections will be plain text\r\n");
1871 if (p_sess->num_clients > 0)
1873 vsf_cmdio_write_raw(p_sess, " At session startup, client count was ");
1874 vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->num_clients));
1875 vsf_cmdio_write_raw(p_sess, "\r\n");
1877 vsf_cmdio_write_raw(p_sess,
1878 " vsFTPd " VSF_VERSION " - secure, fast, stable\r\n");
1879 vsf_cmdio_write(p_sess, FTP_STATOK, "End of status");
1882 static void
1883 handle_stat_file(struct vsf_session* p_sess)
1885 handle_dir_common(p_sess, 1, 1);
1888 static int
1889 data_transfer_checks_ok(struct vsf_session* p_sess)
1891 if (!pasv_active(p_sess) && !port_active(p_sess))
1893 vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
1894 return 0;
1896 if (tunable_ssl_enable && !p_sess->data_use_ssl &&
1897 ((tunable_force_local_data_ssl && !p_sess->is_anonymous) ||
1898 (tunable_force_anon_data_ssl && p_sess->is_anonymous)))
1900 vsf_cmdio_write(
1901 p_sess, FTP_NEEDENCRYPT, "Data connections must be encrypted.");
1902 return 0;
1904 return 1;
1907 static void
1908 resolve_tilde(struct mystr* p_str, struct vsf_session* p_sess)
1910 unsigned int len = str_getlen(p_str);
1911 if (len > 0 && str_get_char_at(p_str, 0) == '~')
1913 static struct mystr s_rhs_str;
1914 if (len == 1 || str_get_char_at(p_str, 1) == '/')
1916 str_split_char(p_str, &s_rhs_str, '~');
1917 str_copy(p_str, &p_sess->home_str);
1918 str_append_str(p_str, &s_rhs_str);
1920 else if (tunable_tilde_user_enable && len > 1)
1922 static struct mystr s_user_str;
1923 struct vsf_sysutil_user* p_user;
1924 str_copy(&s_rhs_str, p_str);
1925 str_split_char(&s_rhs_str, &s_user_str, '~');
1926 str_split_char(&s_user_str, &s_rhs_str, '/');
1927 p_user = str_getpwnam(&s_user_str);
1928 if (p_user != 0)
1930 str_alloc_text(p_str, vsf_sysutil_user_get_homedir(p_user));
1931 if (!str_isempty(&s_rhs_str))
1933 str_append_char(p_str, '/');
1934 str_append_str(p_str, &s_rhs_str);
1941 static void handle_logged_in_user(struct vsf_session* p_sess)
1943 if (p_sess->is_anonymous)
1945 vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change from guest user.");
1947 else if (str_equal(&p_sess->user_str, &p_sess->ftp_arg_str))
1949 vsf_cmdio_write(p_sess, FTP_GIVEPWORD, "Any password will do.");
1951 else
1953 vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change to another user.");
1957 static void handle_logged_in_pass(struct vsf_session* p_sess)
1959 vsf_cmdio_write(p_sess, FTP_LOGINOK, "Already logged in.");
1962 static void
1963 handle_http(struct vsf_session* p_sess)
1965 /* Warning: Doesn't respect cmds_allowed etc. because there is currently only
1966 * one command (GET)!
1967 * HTTP likely doesn't respect other important FTP options. I don't think
1968 * logging works.
1970 if (!tunable_download_enable)
1972 bug("HTTP needs download - fix your config");
1974 /* Eat the HTTP headers, which we don't care about. */
1977 vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
1978 &p_sess->ftp_arg_str, 1);
1980 while (!str_isempty(&p_sess->ftp_cmd_str) ||
1981 !str_isempty(&p_sess->ftp_arg_str));
1982 vsf_cmdio_write_raw(p_sess, "HTTP/1.1 200 OK\r\n");
1983 vsf_cmdio_write_raw(p_sess, "Server: vsftpd\r\n");
1984 vsf_cmdio_write_raw(p_sess, "Connection: close\r\n");
1985 vsf_cmdio_write_raw(p_sess, "X-Frame-Options: SAMEORIGIN\r\n");
1986 vsf_cmdio_write_raw(p_sess, "X-Content-Type-Options: nosniff\r\n");
1987 /* Split the path from the HTTP/1.x */
1988 str_split_char(&p_sess->http_get_arg, &p_sess->ftp_arg_str, ' ');
1989 str_copy(&p_sess->ftp_arg_str, &p_sess->http_get_arg);
1990 str_split_char(&p_sess->http_get_arg, &p_sess->ftp_cmd_str, '.');
1991 str_upper(&p_sess->ftp_cmd_str);
1992 if (str_equal_text(&p_sess->ftp_cmd_str, "HTML") ||
1993 str_equal_text(&p_sess->ftp_cmd_str, "HTM"))
1995 vsf_cmdio_write_raw(p_sess, "Content-Type: text/html\r\n");
1997 else
1999 vsf_cmdio_write_raw(p_sess, "Content-Type: dunno\r\n");
2001 vsf_cmdio_write_raw(p_sess, "\r\n");
2002 p_sess->is_ascii = 0;
2003 p_sess->restart_pos = 0;
2004 handle_retr(p_sess, 1);
2005 if (vsf_log_entry_pending(p_sess))
2007 vsf_log_do_log(p_sess, 0);
2009 vsf_sysutil_exit(0);