blob: badffaf5c2325d196a77fd338b257331b80044b0 [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 Middleditchb1e452e2009-03-12 23:26:34 -040012libtelnet_error_t 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;
Sean Middleditchb1e452e2009-03-12 23:26:34 -040017
18 /* attempt to allocate default buffer */
19 telnet->buffer = (unsigned char *)malloc(LIBTELNET_BUFFER_SIZE);
20 if (telnet->buffer == 0)
21 return LIBTELNET_ERROR_NOMEM;
22
23 return LIBTELNET_ERROR_OK;
Sean Middleditch29144852009-03-12 23:14:47 -040024}
25
26/* free up any memory allocated by a state tracker */
27void libtelnet_close(struct libtelnet_t *telnet) {
28 if (telnet->buffer) {
29 free(telnet->buffer);
30 telnet->buffer = 0;
31 }
32}
33
34/* push a single byte into the state tracker */
35void libtelnet_push_byte(struct libtelnet_t *telnet, unsigned char byte,
36 void *user_data) {
37 switch (telnet->state) {
38 /* regular data */
39 case LIBTELNET_STATE_TEXT:
40 /* enter IAC state on IAC byte */
41 if (byte == LIBTELNET_IAC)
42 telnet->state = LIBTELNET_STATE_IAC;
43 /* regular input byte */
44 else
Sean Middleditchb1e452e2009-03-12 23:26:34 -040045 libtelnet_input_cb(telnet, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040046 break;
47 case LIBTELNET_STATE_IAC:
48 switch (byte) {
49 /* subrequest */
50 case LIBTELNET_SB:
51 telnet->state = LIBTELNET_STATE_SB;
52 break;
53 /* negotiation commands */
54 case LIBTELNET_WILL:
55 telnet->state = LIBTELNET_STATE_WILL;
56 break;
57 case LIBTELNET_WONT:
58 telnet->state = LIBTELNET_STATE_WONT;
59 break;
60 case LIBTELNET_DO:
61 telnet->state = LIBTELNET_STATE_DO;
62 break;
63 case LIBTELNET_DONT:
64 telnet->state = LIBTELNET_STATE_DONT;
65 break;
66 /* IAC escaping */
67 case LIBTELNET_IAC:
68 telbet->input_cb(telnet, LIBTELNET_IAC, user_data);
69 libtelnet->state = LIBTELNET_STATE_TEXT;
70 break;
71 /* some other command */
72 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -040073 libtelnet_command_cb(telnet, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040074 telnet->state = LIBTELNET_STATE_TEXT;
75 }
76 break;
77 /* DO negotiation */
78 case LIBTELNET_STATE_DO:
Sean Middleditchb1e452e2009-03-12 23:26:34 -040079 libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040080 telnet->state = LIBTELNET_STATE_TEXT;
81 break;
82 case LIBTELNET_STATE_DONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -040083 libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040084 telnet->state = LIBTELNET_STATE_TEXT;
85 break;
86 case LIBTELNET_STATE_WILL:
Sean Middleditchb1e452e2009-03-12 23:26:34 -040087 libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040088 telnet->state = LIBTELNET_STATE_TEXT;
89 break;
90 case LIBTELNET_STATE_WONT:
Sean Middleditchb1e452e2009-03-12 23:26:34 -040091 libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -040092 telnet->state = LIBTELNET_STATE_TEXT;
93 break;
94 /* subrequest -- buffer bytes until end request */
95 case LIBTELNET_STATE_SB:
96 /* IAC command in subrequest -- either IAC SE or IAC IAC */
97 if (byte == LIBTELNET_IAC) {
98 telnet->state = LIBTELNET_STATE_SB_IAC;
99 } else {
100 /* FIXME: buffer byte */
101 }
102 break;
103 /* IAC escaping inside a subrequest */
104 case LIBTELNET_STATE_SB_IAC:
105 switch (byte) {
106 /* end subrequest */
107 case LIBTELNET_SE:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400108 /* zero-size buffer is a protocol error */
109 if (telnet->length == 0) {
110 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
111 user_data);
112 /* process */
113 } else {
114 libtelnet_subrequest_cb(telnet, telnet->buffer[0],
115 telnet->buffer + 1, telnet->length - 1, user_data);
116
117 /* unallocate free buffer */
118 free(telnet->buffer);
119 telnet->size = 0;
120 telnet->length = 0;
121 }
122
123 /* return to default state */
Sean Middleditch29144852009-03-12 23:14:47 -0400124 telnet->state = LIBTELNET_STATE_TEXT;
125 break;
126 /* escaped IAC byte */
127 case LIBTELNET_IAC:
128 /* FIXME: buffer byte */
129 telnet->state = LIBTELNET_STATE_SB;
130 break;
131 /* something else -- protocol error */
132 default:
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400133 libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400134 telnet->state = LIBTELNET_STATE_TEXT;
135 break;
136 }
137 break;
138 }
139}
140
141/* push a byte buffer into the state tracker */
142void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer,
143 size_t size, void *user_data) {
144 size_t i;
145 for (i = 0; i != size; ++i)
146 libtelnet_push_byte(telnet, buffer[i], user_data);
147}
148
149/* send an iac command */
150void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
151 void *user_data) {
152 unsigned char bytes[2] = { IAC, cmd };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400153 libtelnet_output_cb(telnet, bytes, 2, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400154}
155
156/* send negotiation */
157void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
158 unsigned char opt, void *user_data) {
159 unsigned char bytes[3] = { IAC, cmd, opt };
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400160 libtelnet_output_cb(telnet, bytes, 3, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400161}
162
163/* send non-command data (escapes IAC bytes) */
164void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
165 size_t size, void *user_data) {
166 size_t i, l;
167 for (l = i = 0; i != size; ++i) {
168 /* dump prior portion of text, send escaped bytes */
169 if (buffer[i] == LIBTELNET_IAC) {
170 /* dump prior text if any */
171 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400172 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400173 l = i + 1;
174
175 /* send escape */
176 libtelnet_send_command(telnet, LIBTELNET_IAC, user_data);
177 }
178 }
179
180 /* send whatever portion of buffer is left */
181 if (i != l)
Sean Middleditchb1e452e2009-03-12 23:26:34 -0400182 libtelnet_output_cb(telnet, buffer + l, i - l, user_data);
Sean Middleditch29144852009-03-12 23:14:47 -0400183}
184
185/* send sub-request */
186void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
187 unsigned char *buffer, size_t size, void *user_data) {
188 libtelnet_send_command(telnet, SB, user_data);
189 libtelnet_send_data(telnet, &type, 1, user_data);
190 libtelnet_send_data(telnet, buffer, size, user_data);
191 libtelnet_send_command(telnet, SE, user_data);
192}