blob: 20941b72e457de2e83482a56b6b6963ec8bad6d9 [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;
87 case LIBTELNET_STATE_IAC:
88 switch (byte) {
89 /* subrequest */
90 case LIBTELNET_SB:
91 telnet->state = LIBTELNET_STATE_SB;
92 break;
93 /* negotiation commands */
94 case LIBTELNET_WILL:
95 telnet->state = LIBTELNET_STATE_WILL;
96 break;
97 case LIBTELNET_WONT:
98 telnet->state = LIBTELNET_STATE_WONT;
99 break;
100 case LIBTELNET_DO:
101 telnet->state = LIBTELNET_STATE_DO;
102 break;
103 case LIBTELNET_DONT:
104 telnet->state = LIBTELNET_STATE_DONT;
105 break;
106 /* IAC escaping */
107 case LIBTELNET_IAC:
108 telbet->input_cb(telnet, LIBTELNET_IAC, user_data);
109 libtelnet->state = LIBTELNET_STATE_TEXT;
110 break;
111 /* some other command */
112 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400113 libtelnet_command_cb(telnet, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400114 telnet->state = LIBTELNET_STATE_TEXT;
115 }
116 break;
117 /* DO negotiation */
118 case LIBTELNET_STATE_DO:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400119 libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400120 telnet->state = LIBTELNET_STATE_TEXT;
121 break;
122 case LIBTELNET_STATE_DONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400123 libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400124 telnet->state = LIBTELNET_STATE_TEXT;
125 break;
126 case LIBTELNET_STATE_WILL:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400127 libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400128 telnet->state = LIBTELNET_STATE_TEXT;
129 break;
130 case LIBTELNET_STATE_WONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400131 libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400132 telnet->state = LIBTELNET_STATE_TEXT;
133 break;
134 /* subrequest -- buffer bytes until end request */
135 case LIBTELNET_STATE_SB:
136 /* IAC command in subrequest -- either IAC SE or IAC IAC */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400137 if (byte == LIBTELNET_IAC)
Sean Middleditch29144852009-03-12 23:14:47 -0400138 telnet->state = LIBTELNET_STATE_SB_IAC;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400139 /* buffer the byte, or bail if we can't */
140 else if (_libtelnet_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
141 LIBTELNET_ERROR_OK)
142 telnet->state = LIBTELNET_STATE_TEXT;
143 else
144 telnet->state = LIBTELNET_STATE_SB;
Sean Middleditch29144852009-03-12 23:14:47 -0400145 break;
146 /* IAC escaping inside a subrequest */
147 case LIBTELNET_STATE_SB_IAC:
148 switch (byte) {
149 /* end subrequest */
150 case LIBTELNET_SE:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400151 /* zero-size buffer is a protocol error */
152 if (telnet->length == 0) {
153 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
154 user_data);
155 /* process */
156 } else {
157 libtelnet_subrequest_cb(telnet, telnet->buffer[0],
158 telnet->buffer + 1, telnet->length - 1, user_data);
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400159 telnet->length = 0;
160 }
161
162 /* return to default state */
Sean Middleditch29144852009-03-12 23:14:47 -0400163 telnet->state = LIBTELNET_STATE_TEXT;
164 break;
165 /* escaped IAC byte */
166 case LIBTELNET_IAC:
Sean Middleditch51ad6792009-03-13 20:15:59 -0400167 /* push IAC into buffer */
168 if (_libtelnet_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
169 LIBTELNET_ERROR_OK)
170 telnet->state = LIBTELNET_STATE_TEXT;
171 else
172 telnet->state = LIBTELNET_STATE_SB;
Sean Middleditch29144852009-03-12 23:14:47 -0400173 break;
174 /* something else -- protocol error */
175 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400176 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400177 telnet->state = LIBTELNET_STATE_TEXT;
178 break;
179 }
180 break;
181 }
182}
183
184/* push a byte buffer into the state tracker */
185void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer,
186 size_t size, void *user_data) {
187 size_t i;
188 for (i = 0; i != size; ++i)
189 libtelnet_push_byte(telnet, buffer[i], user_data);
190}
191
192/* send an iac command */
193void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
194 void *user_data) {
195 unsigned char bytes[2] = { IAC, cmd };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400196 libtelnet_output_cb(telnet, bytes, 2, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400197}
198
199/* send negotiation */
200void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
201 unsigned char opt, void *user_data) {
202 unsigned char bytes[3] = { IAC, cmd, opt };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400203 libtelnet_output_cb(telnet, bytes, 3, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400204}
205
206/* send non-command data (escapes IAC bytes) */
207void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
208 size_t size, void *user_data) {
209 size_t i, l;
210 for (l = i = 0; i != size; ++i) {
211 /* dump prior portion of text, send escaped bytes */
212 if (buffer[i] == LIBTELNET_IAC) {
213 /* dump prior text if any */
214 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400215 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400216 l = i + 1;
217
218 /* send escape */
219 libtelnet_send_command(telnet, LIBTELNET_IAC, user_data);
220 }
221 }
222
223 /* send whatever portion of buffer is left */
224 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400225 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400226}
227
228/* send sub-request */
229void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
230 unsigned char *buffer, size_t size, void *user_data) {
231 libtelnet_send_command(telnet, SB, user_data);
232 libtelnet_send_data(telnet, &type, 1, user_data);
233 libtelnet_send_data(telnet, buffer, size, user_data);
234 libtelnet_send_command(telnet, SE, user_data);
235}