blob: 3921b1617983ebc0ed3e6e6b02102da3a7d228df [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
2 * The author or authors of this code dedicate any and all copyright interest
3 * in this code to the public domain. We make this dedication for the benefit
4 * of the public at large and to the detriment of our heirs and successors. We
5 * intend this dedication to be an overt act of relinquishment in perpetuity of
6 * all present and future rights to this code under copyright law.
7 */
8
Sean Middleditch4d9444d2009-03-13 22:48:05 -04009#include <malloc.h>
Sean Middleditch29144852009-03-12 23:14:47 -040010#include "libtelnet.h"
11
Sean Middleditch4d9444d2009-03-13 22:48:05 -040012/* buffer sizes */
13static const unsigned int _buffer_sizes[] = {
14 0,
15 512,
16 2048,
17 8192,
18 16384,
19};
20static const unsigned int _buffer_sizes_count =
21 sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
22
Sean Middleditch29144852009-03-12 23:14:47 -040023/* initialize a telnet state tracker */
Sean Middleditch51ad6792009-03-13 20:15:59 -040024void libtelnet_init(struct libtelnet_t *telnet) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040025 telnet->state = LIBTELNET_STATE_TEXT;
Sean Middleditch29144852009-03-12 23:14:47 -040026 telnet->buffer = 0;
27 telnet->size = 0;
28 telnet->length = 0;
29}
30
31/* free up any memory allocated by a state tracker */
Sean Middleditch51ad6792009-03-13 20:15:59 -040032void libtelnet_free(struct libtelnet_t *telnet) {
33 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -040034 free(telnet->buffer);
35 telnet->buffer = 0;
Sean Middleditch51ad6792009-03-13 20:15:59 -040036 telnet->size = 0;
37 telnet->length = 0;
Sean Middleditch29144852009-03-12 23:14:47 -040038 }
39}
40
Sean Middleditch51ad6792009-03-13 20:15:59 -040041/* push a byte into the telnet buffer */
Sean Middleditch4d9444d2009-03-13 22:48:05 -040042static enum libtelnet_error_t _buffer_byte(
Sean Middleditch51ad6792009-03-13 20:15:59 -040043 struct libtelnet_t *telnet, unsigned char byte, void *user_data) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040044 unsigned char *new_buffer;
45 int i;
46
Sean Middleditch51ad6792009-03-13 20:15:59 -040047 /* check if we're out of room */
48 if (telnet->length == telnet->size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040049 /* find the next buffer size */
50 for (i = 0; i != _buffer_sizes_count; ++i) {
51 if (_buffer_sizes[i] == telnet->size)
52 break;
53 }
54
55 /* overflow -- can't grow any more */
56 if (i >= _buffer_sizes_count - 1) {
Sean Middleditch51ad6792009-03-13 20:15:59 -040057 libtelnet_error_cb(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
58 libtelnet_free(telnet);
59 return LIBTELNET_ERROR_OVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -040060 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -040061
62 /* (re)allocate buffer */
63 new_buffer = (unsigned char *)realloc(telnet->buffer,
64 _buffer_sizes[i + 1]);
65 if (new_buffer == 0) {
66 libtelnet_error_cb(telnet, LIBTELNET_ERROR_NOMEM,
67 user_data);
68 libtelnet_free(telnet);
69 return LIBTELNET_ERROR_NOMEM;
70 }
71
72 telnet->buffer = new_buffer;
73 telnet->size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -040074 }
75
76 /* push the byte, all set */
77 telnet->buffer[telnet->length++] = byte;
78 return LIBTELNET_ERROR_OK;
79}
80
Sean Middleditch29144852009-03-12 23:14:47 -040081/* push a single byte into the state tracker */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -040082void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch4d9444d2009-03-13 22:48:05 -040083 unsigned int size, void *user_data) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -040084 unsigned char byte;
85 unsigned int i, start;
86 for (i = start = 0; i != size; ++i) {
87 byte = buffer[i];
88 switch (telnet->state) {
89 /* regular data */
90 case LIBTELNET_STATE_TEXT:
91 /* on an IAC byte, pass through all pending bytes and
92 * switch states */
93 if (byte == LIBTELNET_IAC) {
94 if (i != start)
Sean Middleditch78f400f2009-03-14 01:26:43 -040095 libtelnet_data_cb(telnet, &buffer[start], i - start,
Sean Middleditch8b5e2b12009-03-13 23:39:18 -040096 user_data);
97 telnet->state = LIBTELNET_STATE_IAC;
98 }
99 break;
100
101 /* IAC command */
102 case LIBTELNET_STATE_IAC:
103 switch (byte) {
104 /* subrequest */
105 case LIBTELNET_SB:
106 telnet->state = LIBTELNET_STATE_SB;
107 break;
108 /* negotiation commands */
109 case LIBTELNET_WILL:
110 telnet->state = LIBTELNET_STATE_WILL;
111 break;
112 case LIBTELNET_WONT:
113 telnet->state = LIBTELNET_STATE_WONT;
114 break;
115 case LIBTELNET_DO:
116 telnet->state = LIBTELNET_STATE_DO;
117 break;
118 case LIBTELNET_DONT:
119 telnet->state = LIBTELNET_STATE_DONT;
120 break;
121 /* IAC escaping */
122 case LIBTELNET_IAC:
Sean Middleditch78f400f2009-03-14 01:26:43 -0400123 libtelnet_data_cb(telnet, &byte, 1, user_data);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400124 start = i + 1;
125 telnet->state = LIBTELNET_STATE_TEXT;
126 break;
127 /* some other command */
128 default:
129 libtelnet_command_cb(telnet, byte, user_data);
130 start = i + 1;
131 telnet->state = LIBTELNET_STATE_TEXT;
132 }
133 break;
134
135 /* negotiation commands */
136 case LIBTELNET_STATE_DO:
137 libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
138 start = i + 1;
139 telnet->state = LIBTELNET_STATE_TEXT;
140 break;
141 case LIBTELNET_STATE_DONT:
142 libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
143 start = i + 1;
144 telnet->state = LIBTELNET_STATE_TEXT;
145 break;
146 case LIBTELNET_STATE_WILL:
147 libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
148 start = i + 1;
149 telnet->state = LIBTELNET_STATE_TEXT;
150 break;
151 case LIBTELNET_STATE_WONT:
152 libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
153 start = i + 1;
154 telnet->state = LIBTELNET_STATE_TEXT;
155 break;
156
157 /* subrequest -- buffer bytes until end request */
158 case LIBTELNET_STATE_SB:
159 /* IAC command in subrequest -- either IAC SE or IAC IAC */
160 if (byte == LIBTELNET_IAC) {
161 telnet->state = LIBTELNET_STATE_SB_IAC;
162 /* buffer the byte, or bail if we can't */
Sean Middleditch7e517ec2009-03-14 00:18:30 -0400163 } else if (_buffer_byte(telnet, byte, user_data) !=
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400164 LIBTELNET_ERROR_OK) {
165 start = i + 1;
166 telnet->state = LIBTELNET_STATE_TEXT;
167 } else {
168 telnet->state = LIBTELNET_STATE_SB;
169 }
170 break;
171
172 /* IAC escaping inside a subrequest */
173 case LIBTELNET_STATE_SB_IAC:
174 switch (byte) {
175 /* end subrequest */
176 case LIBTELNET_SE:
177 /* zero-size buffer is a protocol error */
178 if (telnet->length == 0) {
179 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
180 user_data);
181 /* process */
182 } else {
183 libtelnet_subrequest_cb(telnet, telnet->buffer[0],
184 telnet->buffer + 1, telnet->length - 1, user_data);
185 telnet->length = 0;
186 }
187
188 /* return to default state */
189 start = i + 1;
190 telnet->state = LIBTELNET_STATE_TEXT;
191 break;
192 /* escaped IAC byte */
193 case LIBTELNET_IAC:
194 /* push IAC into buffer */
195 if (_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
196 LIBTELNET_ERROR_OK) {
197 start = i + 1;
198 telnet->state = LIBTELNET_STATE_TEXT;
199 } else {
200 telnet->state = LIBTELNET_STATE_SB;
201 }
202 break;
203 /* something else -- protocol error */
204 default:
205 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
206 user_data);
207 start = i + 1;
208 telnet->state = LIBTELNET_STATE_TEXT;
209 break;
210 }
211 break;
212 }
213 }
214
215 /* pass through any remaining bytes */
216 if (i != start)
Sean Middleditch78f400f2009-03-14 01:26:43 -0400217 libtelnet_data_cb(telnet, &buffer[start], i - start, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400218}
219
220/* send an iac command */
221void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
222 void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400223 unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
Sean Middleditch78f400f2009-03-14 01:26:43 -0400224 libtelnet_send_cb(telnet, bytes, 2, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400225}
226
227/* send negotiation */
228void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
229 unsigned char opt, void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400230 unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
Sean Middleditch78f400f2009-03-14 01:26:43 -0400231 libtelnet_send_cb(telnet, bytes, 3, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400232}
233
234/* send non-command data (escapes IAC bytes) */
235void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400236 unsigned int size, void *user_data) {
237 unsigned int i, l;
Sean Middleditch29144852009-03-12 23:14:47 -0400238 for (l = i = 0; i != size; ++i) {
239 /* dump prior portion of text, send escaped bytes */
240 if (buffer[i] == LIBTELNET_IAC) {
241 /* dump prior text if any */
242 if (i != l)
Sean Middleditch78f400f2009-03-14 01:26:43 -0400243 libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400244 l = i + 1;
245
246 /* send escape */
247 libtelnet_send_command(telnet, LIBTELNET_IAC, user_data);
248 }
249 }
250
251 /* send whatever portion of buffer is left */
252 if (i != l)
Sean Middleditch78f400f2009-03-14 01:26:43 -0400253 libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400254}
255
256/* send sub-request */
257void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400258 unsigned char *buffer, unsigned int size, void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400259 libtelnet_send_command(telnet, LIBTELNET_SB, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400260 libtelnet_send_data(telnet, &type, 1, user_data);
261 libtelnet_send_data(telnet, buffer, size, user_data);
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400262 libtelnet_send_command(telnet, LIBTELNET_SE, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400263}