1 /*
2 * readcmd.c
3 * (C)1997-2011 Marc Huber <Marc.Huber@web.de>
4 * All rights reserved.
5 *
6 * $Id: readcmd.c,v 1.14 2019/03/30 14:57:51 marc Exp $
7 *
8 */
9
10 /*
11 * Standards compliance: We're ignoring the Telnet SYNCH/IP signals,
12 * but this really shouldn't matter as the daemon is perfectly capable
13 * of monitoring the control connection while transfering data.
14 */
15
16 #include "headers.h"
17 #include <arpa/telnet.h>
18
19 static const char rcsid[] __attribute__ ((used)) = "$Id: readcmd.c,v 1.14 2019/03/30 14:57:51 marc Exp $";
20
21 static void parsecmd(struct context *, int);
22
readcmd(struct context * ctx,int cur)23 void readcmd(struct context *ctx, int cur __attribute__ ((unused)))
24 {
25 int iac_state;
26 ssize_t i;
27 char *t, *u;
28 struct buffer *b = ctx->cbufi;
29
30 Debug((DEBUG_PROC, "+ %s(%d)\n", __func__, ctx->cfn));
31
32 io_clr_i(ctx->io, ctx->cfn);
33
34 if (b) {
35 while (b->next)
36 b = b->next;
37 if (b->size == b->length) {
38 b->next = buffer_get();
39 b = b->next;
40 }
41 } else
42 ctx->cbufi = b = buffer_get();
43
44 #ifdef WITH_SSL
45 if (ctx->ssl_c)
46 i = io_SSL_read(ctx->ssl_c, b->buf + b->length, b->size - b->length, ctx->io, ctx->cfn, (void *) readcmd);
47 else
48 #endif
49 i = read(ctx->cfn, b->buf + b->length, b->size - b->length);
50
51 if (i <= 0) {
52 if (i != -1 || errno != EAGAIN)
53 cleanup(ctx, ctx->cfn);
54
55 Debug((DEBUG_PROC, "- %s (<= 0)\n", __func__));
56 return;
57 }
58
59 b->offset = b->length;
60 b->length += i, ctx->traffic_total += i;
61
62 /* First, process telnet options */
63
64 iac_state = ctx->iac_state;
65
66 t = u = b->buf + b->offset;
67 for (; i; i--, t++)
68 switch (iac_state) {
69 case 0:
70 switch (*t & 0377) {
71 case IAC:
72 iac_state = 1;
73 break;
74 default:
75 *u++ = *t;
76 }
77 break;
78 case 1:
79 switch (*t & 0377) {
80 case IAC:
81 iac_state = 0;
82 *u++ = IAC;
83 break;
84 case WILL:
85 case WONT:
86 ctx->iac[1] = DONT;
87 iac_state = 2;
88 break;
89 case DO:
90 case DONT:
91 ctx->iac[1] = WONT;
92 iac_state = 2;
93 break;
94 default:
95 iac_state = 0;
96 }
97 break;
98 case 2:
99 ctx->iac[2] = *t;
100 iac_state = 0;
101 ctx->cbufo = buffer_write(ctx->cbufo, ctx->iac, 3);
102 io_set_o(ctx->io, ctx->cfn);
103 }
104
105 ctx->iac_state = iac_state;
106
107 b->length = u - b->buf;
108
109 parsecmd(ctx, ctx->cfn);
110
111 DebugOut(DEBUG_PROC);
112 }
113
parsecmd(struct context * ctx,int cfn)114 static void parsecmd(struct context *ctx, int cfn)
115 {
116 char *t, *u;
117 char lastchar = 0; /* Anything different from <CR> will do. */
118 DebugIn(DEBUG_PROC);
119
120 t = u = ctx->cbufi->buf + ctx->cbufi->offset;
121
122 again:
123
124 for (; t < ctx->cbufi->buf + ctx->cbufi->length; t++)
125 switch (*t) {
126 case '\0':
127 if (lastchar == '\r') /* <CR><NUL> => <CR> */
128 lastchar = *u++ = *t;
129 else {
130 Debug((DEBUG_PROC, " %s: Illegal character sequence \\%o\\%o\n", __func__, lastchar, *t));
131 cleanup(ctx, ctx->cfn);
132 DebugOut(DEBUG_PROC);
133 return;
134 }
135 break;
136 case '\n':
137 if (lastchar == '\r') { /* <CR><LF> => EOL */
138 struct io_context *io = ctx->io;
139 cfn = ctx->cfn;
140 *(u - 1) = 0;
141 checkcmd(ctx, ctx->cbufi->buf);
142
143 if (io_get_ctx(io, cfn))
144 /* need to check whether our context is still valid */
145 {
146 ctx->cbufi->offset = t - ctx->cbufi->buf + 1;
147 if (ctx->cbufi->offset == ctx->cbufi->length) {
148 ctx->cbufi = buffer_free(ctx->cbufi);
149 io_sched_del(ctx->io, ctx, (void *) parsecmd);
150 if (!ctx->cbufi && io_get_cb_i(ctx->io, ctx->cfn) == (void *) readcmd)
151 io_set_i(ctx->io, ctx->cfn);
152 } else if (io_sched_renew_proc(ctx->io, ctx, (void *) parsecmd))
153 io_sched_add(ctx->io, ctx, (void *) parsecmd, 0, 0);
154 }
155 DebugOut(DEBUG_PROC);
156 return;
157 }
158 default:
159 lastchar = *u++ = *t;
160 }
161
162 /*
163 * The FTP protocol doesn't support pipelining. No way this code can
164 * be reached with a well-behaving client.
165 */
166
167 /*
168 * Move content of input buffer to beginning of buffer.
169 */
170 if (ctx->cbufi->offset != ctx->cbufi->length) {
171 ctx->cbufi->length -= ctx->cbufi->offset;
172 memmove(ctx->cbufi->buf, ctx->cbufi->buf + ctx->cbufi->offset, ctx->cbufi->length);
173 t -= ctx->cbufi->offset, u -= ctx->cbufi->offset;
174 ctx->cbufi->offset = 0;
175 }
176
177 /*
178 * If we have more data, try to fill current buffer, then continue parsing.
179 */
180 if (ctx->cbufi->next && ctx->cbufi->length < ctx->cbufi->size) {
181 size_t len = MIN(ctx->cbufi->size - ctx->cbufi->length,
182 ctx->cbufi->next->length - ctx->cbufi->offset);
183 memcpy(ctx->cbufi->buf + ctx->cbufi->length, ctx->cbufi->next->buf + ctx->cbufi->offset, len);
184 ctx->cbufi->length += len, ctx->cbufi->next->offset += len;
185 if (ctx->cbufi->next->offset == ctx->cbufi->next->length)
186 ctx->cbufi->next = buffer_free(ctx->cbufi->next);
187 goto again;
188 }
189
190 /*
191 * Terminate connection if buffer filled but no <CR><LF> is found.
192 * Otherwise, accept more input.
193 */
194 if (ctx->cbufi->length == ctx->cbufi->size) {
195 logmsg("Found garbage in command buffer. Terminating session %.8lx", ctx->id);
196 cleanup(ctx, ctx->cfn);
197 } else
198 io_set_i(ctx->io, ctx->cfn);
199
200 DebugOut(DEBUG_PROC);
201 }
202