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