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