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