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