blob: f66d4aeb5e22326169664f4936cab906e0625c18 [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 Middleditch29144852009-03-12 23:14:47 -04009#include "libtelnet.h"
10
11/* initialize a telnet state tracker */
Sean Middleditch51ad6792009-03-13 20:15:59 -040012void libtelnet_init(struct libtelnet_t *telnet) {
Sean Middleditch29144852009-03-12 23:14:47 -040013 telnet->state = LIBTELNET_TEXT;
14 telnet->buffer = 0;
15 telnet->size = 0;
16 telnet->length = 0;
17}
18
19/* free up any memory allocated by a state tracker */
Sean Middleditch51ad6792009-03-13 20:15:59 -040020void libtelnet_free(struct libtelnet_t *telnet) {
21 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -040022 free(telnet->buffer);
23 telnet->buffer = 0;
Sean Middleditch51ad6792009-03-13 20:15:59 -040024 telnet->size = 0;
25 telnet->length = 0;
Sean Middleditch29144852009-03-12 23:14:47 -040026 }
27}
28
Sean Middleditch51ad6792009-03-13 20:15:59 -040029/* push a byte into the telnet buffer */
30static enum libtelnet_error_t _libtelnet_buffer_byte(
31 struct libtelnet_t *telnet, unsigned char byte, void *user_data) {
32 /* check if we're out of room */
33 if (telnet->length == telnet->size) {
34 /* if we already have a large buffer, we're out of space, give up */
35 if (telnet->size == LIBTELNET_BUFFER_SIZE_LARGE) {
36 libtelnet_error_cb(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
37 libtelnet_free(telnet);
38 return LIBTELNET_ERROR_OVERFLOW;
39
40 /* if we have a small buffer, try to resize to a larger one */
41 } else if (telnet->size == LIBTELNET_BUFFER_SIZE_SMALL) {
42 unsigned char *new_buffer = (unsigned char *)realloc(
43 telnet->buffer, LIBTELNET_BUFFER_SIZE_LARGE);
44 if (new_buffer == 0) {
45 libtelnet_error_cb(telnet, LIBTELNET_ERROR_NOMEM,
46 user_data);
47 libtelnet_free(telnet);
48 return LIBTELNET_ERROR_NOMEM;
49 }
50
51 telnet->buffer = new_buffer;
52 telnet->size = LIBTELNET_BUFFER_SIZE_LARGE;
53
54 /* we have no buffer at all, so allocate one */
55 } else {
56 telnet->buffer = (unsigned char *)realloc(
57 telnet->buffer, LIBTELNET_BUFFER_SIZE_SMALL);
58 if (telnet->buffer == 0) {
59 libtelnet_error_cb(telnet, LIBTELNET_ERROR_NOMEM,
60 user_data);
61 libtelnet_free(telnet);
62 return LIBTELNET_ERROR_NOMEM;
63 }
64
65 telnet->size = LIBTELNET_BUFFER_SIZE_SMALL;
66 }
67 }
68
69 /* push the byte, all set */
70 telnet->buffer[telnet->length++] = byte;
71 return LIBTELNET_ERROR_OK;
72}
73
Sean Middleditch29144852009-03-12 23:14:47 -040074/* push a single byte into the state tracker */
75void libtelnet_push_byte(struct libtelnet_t *telnet, unsigned char byte,
76 void *user_data) {
77 switch (telnet->state) {
78 /* regular data */
79 case LIBTELNET_STATE_TEXT:
80 /* enter IAC state on IAC byte */
81 if (byte == LIBTELNET_IAC)
82 telnet->state = LIBTELNET_STATE_IAC;
83 /* regular input byte */
84 else
Sean Middleditchb1e452e2009-03-12 23:26:34 -040085 libtelnet_input_cb(telnet, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040086 break;
Sean Middleditchfddf1c42009-03-13 20:18:11 -040087
88 /* IAC command */
Sean Middleditch29144852009-03-12 23:14:47 -040089 case LIBTELNET_STATE_IAC:
90 switch (byte) {
91 /* subrequest */
92 case LIBTELNET_SB:
93 telnet->state = LIBTELNET_STATE_SB;
94 break;
95 /* negotiation commands */
96 case LIBTELNET_WILL:
97 telnet->state = LIBTELNET_STATE_WILL;
98 break;
99 case LIBTELNET_WONT:
100 telnet->state = LIBTELNET_STATE_WONT;
101 break;
102 case LIBTELNET_DO:
103 telnet->state = LIBTELNET_STATE_DO;
104 break;
105 case LIBTELNET_DONT:
106 telnet->state = LIBTELNET_STATE_DONT;
107 break;
108 /* IAC escaping */
109 case LIBTELNET_IAC:
110 telbet->input_cb(telnet, LIBTELNET_IAC, user_data);
111 libtelnet->state = LIBTELNET_STATE_TEXT;
112 break;
113 /* some other command */
114 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400115 libtelnet_command_cb(telnet, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400116 telnet->state = LIBTELNET_STATE_TEXT;
117 }
118 break;
Sean Middleditchfddf1c42009-03-13 20:18:11 -0400119
120 /* negotiation commands */
Sean Middleditch29144852009-03-12 23:14:47 -0400121 case LIBTELNET_STATE_DO:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400122 libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400123 telnet->state = LIBTELNET_STATE_TEXT;
124 break;
125 case LIBTELNET_STATE_DONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400126 libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400127 telnet->state = LIBTELNET_STATE_TEXT;
128 break;
129 case LIBTELNET_STATE_WILL:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400130 libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400131 telnet->state = LIBTELNET_STATE_TEXT;
132 break;
133 case LIBTELNET_STATE_WONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400134 libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400135 telnet->state = LIBTELNET_STATE_TEXT;
136 break;
Sean Middleditchfddf1c42009-03-13 20:18:11 -0400137
Sean Middleditch29144852009-03-12 23:14:47 -0400138 /* subrequest -- buffer bytes until end request */
139 case LIBTELNET_STATE_SB:
140 /* IAC command in subrequest -- either IAC SE or IAC IAC */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400141 if (byte == LIBTELNET_IAC)
Sean Middleditch29144852009-03-12 23:14:47 -0400142 telnet->state = LIBTELNET_STATE_SB_IAC;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400143 /* buffer the byte, or bail if we can't */
144 else if (_libtelnet_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
145 LIBTELNET_ERROR_OK)
146 telnet->state = LIBTELNET_STATE_TEXT;
147 else
148 telnet->state = LIBTELNET_STATE_SB;
Sean Middleditch29144852009-03-12 23:14:47 -0400149 break;
Sean Middleditchfddf1c42009-03-13 20:18:11 -0400150
Sean Middleditch29144852009-03-12 23:14:47 -0400151 /* IAC escaping inside a subrequest */
152 case LIBTELNET_STATE_SB_IAC:
153 switch (byte) {
154 /* end subrequest */
155 case LIBTELNET_SE:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400156 /* zero-size buffer is a protocol error */
157 if (telnet->length == 0) {
158 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
159 user_data);
160 /* process */
161 } else {
162 libtelnet_subrequest_cb(telnet, telnet->buffer[0],
163 telnet->buffer + 1, telnet->length - 1, user_data);
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400164 telnet->length = 0;
165 }
166
167 /* return to default state */
Sean Middleditch29144852009-03-12 23:14:47 -0400168 telnet->state = LIBTELNET_STATE_TEXT;
169 break;
170 /* escaped IAC byte */
171 case LIBTELNET_IAC:
Sean Middleditch51ad6792009-03-13 20:15:59 -0400172 /* push IAC into buffer */
173 if (_libtelnet_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
174 LIBTELNET_ERROR_OK)
175 telnet->state = LIBTELNET_STATE_TEXT;
176 else
177 telnet->state = LIBTELNET_STATE_SB;
Sean Middleditch29144852009-03-12 23:14:47 -0400178 break;
179 /* something else -- protocol error */
180 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400181 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400182 telnet->state = LIBTELNET_STATE_TEXT;
183 break;
184 }
185 break;
186 }
187}
188
189/* push a byte buffer into the state tracker */
190void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer,
191 size_t size, void *user_data) {
192 size_t i;
193 for (i = 0; i != size; ++i)
194 libtelnet_push_byte(telnet, buffer[i], user_data);
195}
196
197/* send an iac command */
198void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
199 void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400200 unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400201 libtelnet_output_cb(telnet, bytes, 2, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400202}
203
204/* send negotiation */
205void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
206 unsigned char opt, void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400207 unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400208 libtelnet_output_cb(telnet, bytes, 3, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400209}
210
211/* send non-command data (escapes IAC bytes) */
212void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
213 size_t size, void *user_data) {
214 size_t i, l;
215 for (l = i = 0; i != size; ++i) {
216 /* dump prior portion of text, send escaped bytes */
217 if (buffer[i] == LIBTELNET_IAC) {
218 /* dump prior text if any */
219 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400220 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400221 l = i + 1;
222
223 /* send escape */
224 libtelnet_send_command(telnet, LIBTELNET_IAC, user_data);
225 }
226 }
227
228 /* send whatever portion of buffer is left */
229 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400230 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400231}
232
233/* send sub-request */
234void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
235 unsigned char *buffer, size_t size, void *user_data) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400236 libtelnet_send_command(telnet, LIBTELNET_SB, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400237 libtelnet_send_data(telnet, &type, 1, user_data);
238 libtelnet_send_data(telnet, buffer, size, user_data);
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400239 libtelnet_send_command(telnet, LIBTELNET_SE, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400240}