1/* 2 * Part of Very Secure FTPd 3 * Licence: GPL v2 4 * Author: Chris Evans 5 * ftpcmdio.c 6 * 7 * Routines applicable to reading and writing the FTP command stream. 8 */ 9 10#include "ftpcmdio.h" 11#include "ftpcodes.h" 12#include "str.h" 13#include "netstr.h" 14#include "sysutil.h" 15#include "tunables.h" 16#include "defs.h" 17#include "secbuf.h" 18#include "utility.h" 19#include "logging.h" 20#include "session.h" 21#include "readwrite.h" 22#include "utility.h" // Jiahao 23#include <stdlib.h> 24#include <string.h> 25 26/* Internal functions */ 27static void control_getline(struct mystr* p_str, struct vsf_session* p_sess); 28static void ftp_write_text_common(struct vsf_session* p_sess, int status, 29 const char* p_text, int noblock, char sep); 30static void ftp_write_str_common(struct vsf_session* p_sess, int status, 31 char sep, const struct mystr* p_str, 32 int noblock); 33static void handle_alarm_timeout(void* p_private); 34 35void 36vsf_cmdio_sock_setup(void) 37{ 38 vsf_sysutil_activate_keepalive(VSFTP_COMMAND_FD); 39 vsf_sysutil_set_nodelay(VSFTP_COMMAND_FD); 40 vsf_sysutil_activate_oobinline(VSFTP_COMMAND_FD); 41} 42 43static void 44handle_alarm_timeout(void* p_private) 45{ 46 struct vsf_session* p_sess = (struct vsf_session*) p_private; 47 vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout."); 48} 49 50void 51vsf_cmdio_write(struct vsf_session* p_sess, int status, const char* p_text) 52{ 53 ftp_write_text_common(p_sess, status, p_text, 0, ' '); 54} 55 56void 57vsf_cmdio_write_hyphen(struct vsf_session* p_sess, int status, 58 const char* p_text) 59{ 60 ftp_write_text_common(p_sess, status, p_text, 0, '-'); 61} 62 63void 64vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text) 65{ 66 static struct mystr s_the_str; 67 int retval; 68 str_alloc_text(&s_the_str, p_text); 69 if (tunable_log_ftp_protocol) 70 { 71 vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_the_str); 72 } 73 retval = ftp_write_str(p_sess, &s_the_str, kVSFRWControl); 74 if (retval != 0) 75 { 76 die("ftp_write_str"); 77 } 78} 79 80void 81vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text) 82{ 83 /* Unblock any readers on the dying control channel. This is needed for SSL 84 * connections, where the SSL control channel slave is in a separate 85 * process. 86 */ 87 vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD); 88 ftp_write_text_common(p_sess, status, p_text, 1, ' '); 89 vsf_sysutil_exit(0); 90} 91 92static void 93ftp_write_text_common(struct vsf_session* p_sess, int status, 94 const char* p_text, int noblock, char sep) 95{ 96 /* XXX - could optimize */ 97 static struct mystr s_the_str; 98 str_alloc_text(&s_the_str, p_text); 99 ftp_write_str_common(p_sess, status, sep, &s_the_str, noblock); 100} 101 102void 103vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status, 104 const struct mystr* p_str) 105{ 106 ftp_write_str_common(p_sess, status, '-', p_str, 0); 107} 108 109void 110vsf_cmdio_write_str(struct vsf_session* p_sess, int status, 111 const struct mystr* p_str) 112{ 113 ftp_write_str_common(p_sess, status, ' ', p_str, 0); 114} 115 116static void 117ftp_write_str_common(struct vsf_session* p_sess, int status, char sep, 118 const struct mystr* p_str, int noblock) 119{ 120 static struct mystr s_write_buf_str; 121 static struct mystr s_text_mangle_str; 122 int retval; 123 if (tunable_log_ftp_protocol) 124 { 125 str_alloc_ulong(&s_write_buf_str, (unsigned long) status); 126 str_append_char(&s_write_buf_str, sep); 127 str_append_str(&s_write_buf_str, p_str); 128 vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_write_buf_str); 129 } 130 str_copy(&s_text_mangle_str, p_str); 131 /* Process the output response according to the specifications.. */ 132 /* Escape telnet characters properly */ 133 str_replace_text(&s_text_mangle_str, "\377", "\377\377"); 134 /* Change \n for \0 in response */ 135 str_replace_char(&s_text_mangle_str, '\n', '\0'); 136 /* Build string to squirt down network */ 137 str_alloc_ulong(&s_write_buf_str, (unsigned long) status); 138 str_append_char(&s_write_buf_str, sep); 139 str_append_str(&s_write_buf_str, &s_text_mangle_str); 140 str_append_text(&s_write_buf_str, "\r\n"); 141 if (noblock) 142 { 143 vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD); 144 } 145 retval = ftp_write_str(p_sess, &s_write_buf_str, kVSFRWControl); 146 if (retval != 0 && !noblock) 147 { 148 die("ftp_write"); 149 } 150 if (noblock) 151 { 152 vsf_sysutil_deactivate_noblock(VSFTP_COMMAND_FD); 153 } 154} 155 156void 157vsf_cmdio_set_alarm(struct vsf_session* p_sess) 158{ 159 if (tunable_idle_session_timeout > 0) 160 { 161 vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_alarm_timeout, 162 p_sess); 163 vsf_sysutil_set_alarm(tunable_idle_session_timeout); 164 } 165} 166 167void 168vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str, 169 struct mystr* p_arg_str, int set_alarm) 170{ 171 /* Prepare an alarm to timeout the session.. */ 172 if (set_alarm) 173 { 174 vsf_cmdio_set_alarm(p_sess); 175 } 176 /* Blocks */ 177 control_getline(p_cmd_str, p_sess); 178 str_split_char(p_cmd_str, p_arg_str, ' '); 179 str_upper(p_cmd_str); 180 if (!str_isempty(p_arg_str)) { // Jiahao 181 char *tmp_str; 182 tmp_str = remote2local(str_getbuf(p_arg_str)); 183 if (tmp_str != NULL) { 184 str_empty(p_arg_str); 185 str_append_text(p_arg_str, tmp_str); 186 vsf_sysutil_free(tmp_str); 187 } 188 } 189 if (tunable_log_ftp_protocol) 190 { 191 static struct mystr s_log_str; 192 if (str_equal_text(p_cmd_str, "PASS")) 193 { 194 str_alloc_text(&s_log_str, "PASS <password>"); 195 } 196 else 197 { 198 str_copy(&s_log_str, p_cmd_str); 199 if (!str_isempty(p_arg_str)) 200 { 201 str_append_char(&s_log_str, ' '); 202 str_append_str(&s_log_str, p_arg_str); 203 } 204 } 205 vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str); 206 } 207} 208 209static void 210control_getline(struct mystr* p_str, struct vsf_session* p_sess) 211{ 212 if (p_sess->p_control_line_buf == 0) 213 { 214 vsf_secbuf_alloc(&p_sess->p_control_line_buf, VSFTP_MAX_COMMAND_LINE); 215 } 216 ftp_getline(p_sess, p_str, p_sess->p_control_line_buf); 217 /* As mandated by the FTP specifications.. */ 218 str_replace_char(p_str, '\0', '\n'); 219 /* If the last character is a \r, strip it */ 220 { 221 unsigned int len = str_getlen(p_str); 222 while (len > 0 && str_get_char_at(p_str, len - 1) == '\r') 223 { 224 str_trunc(p_str, len - 1); 225 --len; 226 } 227 } 228} 229 230