blob: b148d8e6f862b280f1e1319a64a46647e8d36907 [file] [log] [blame]
Sean Middleditch29144852009-03-12 23:14:47 -04001#include "libtelnet.h"
2
3/* initialize a telnet state tracker */
4void libtelnet_init(struct libtelnet_t *telnet) {
5 telnet->state = LIBTELNET_TEXT;
6 telnet->buffer = 0;
7 telnet->size = 0;
8 telnet->length = 0;
9}
10
11/* free up any memory allocated by a state tracker */
12void libtelnet_close(struct libtelnet_t *telnet) {
13 if (telnet->buffer) {
14 free(telnet->buffer);
15 telnet->buffer = 0;
16 }
17}
18
19/* push a single byte into the state tracker */
20void libtelnet_push_byte(struct libtelnet_t *telnet, unsigned char byte,
21 void *user_data) {
22 switch (telnet->state) {
23 /* regular data */
24 case LIBTELNET_STATE_TEXT:
25 /* enter IAC state on IAC byte */
26 if (byte == LIBTELNET_IAC)
27 telnet->state = LIBTELNET_STATE_IAC;
28 /* regular input byte */
29 else
30 telnet->input_cb(telnet, byte, user_data);
31 break;
32 case LIBTELNET_STATE_IAC:
33 switch (byte) {
34 /* subrequest */
35 case LIBTELNET_SB:
36 telnet->state = LIBTELNET_STATE_SB;
37 break;
38 /* negotiation commands */
39 case LIBTELNET_WILL:
40 telnet->state = LIBTELNET_STATE_WILL;
41 break;
42 case LIBTELNET_WONT:
43 telnet->state = LIBTELNET_STATE_WONT;
44 break;
45 case LIBTELNET_DO:
46 telnet->state = LIBTELNET_STATE_DO;
47 break;
48 case LIBTELNET_DONT:
49 telnet->state = LIBTELNET_STATE_DONT;
50 break;
51 /* IAC escaping */
52 case LIBTELNET_IAC:
53 telbet->input_cb(telnet, LIBTELNET_IAC, user_data);
54 libtelnet->state = LIBTELNET_STATE_TEXT;
55 break;
56 /* some other command */
57 default:
58 telnet->command_cb(telnet, byte, user_data);
59 telnet->state = LIBTELNET_STATE_TEXT;
60 }
61 break;
62 /* DO negotiation */
63 case LIBTELNET_STATE_DO:
64 telnet->negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
65 telnet->state = LIBTELNET_STATE_TEXT;
66 break;
67 case LIBTELNET_STATE_DONT:
68 telnet->negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
69 telnet->state = LIBTELNET_STATE_TEXT;
70 break;
71 case LIBTELNET_STATE_WILL:
72 telnet->negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
73 telnet->state = LIBTELNET_STATE_TEXT;
74 break;
75 case LIBTELNET_STATE_WONT:
76 telnet->negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
77 telnet->state = LIBTELNET_STATE_TEXT;
78 break;
79 /* subrequest -- buffer bytes until end request */
80 case LIBTELNET_STATE_SB:
81 /* IAC command in subrequest -- either IAC SE or IAC IAC */
82 if (byte == LIBTELNET_IAC) {
83 telnet->state = LIBTELNET_STATE_SB_IAC;
84 } else {
85 /* FIXME: buffer byte */
86 }
87 break;
88 /* IAC escaping inside a subrequest */
89 case LIBTELNET_STATE_SB_IAC:
90 switch (byte) {
91 /* end subrequest */
92 case LIBTELNET_SE:
93 /* FIXME: process */
94 telnet->state = LIBTELNET_STATE_TEXT;
95 break;
96 /* escaped IAC byte */
97 case LIBTELNET_IAC:
98 /* FIXME: buffer byte */
99 telnet->state = LIBTELNET_STATE_SB;
100 break;
101 /* something else -- protocol error */
102 default:
103 telnet->error_cb(telnet, LIBTELNET_ERROR_PROTOCOL, user_data);
104 telnet->state = LIBTELNET_STATE_TEXT;
105 break;
106 }
107 break;
108 }
109}
110
111/* push a byte buffer into the state tracker */
112void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer,
113 size_t size, void *user_data) {
114 size_t i;
115 for (i = 0; i != size; ++i)
116 libtelnet_push_byte(telnet, buffer[i], user_data);
117}
118
119/* send an iac command */
120void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
121 void *user_data) {
122 unsigned char bytes[2] = { IAC, cmd };
123 telnet->output_cb(telnet, bytes, 2, user_data);
124}
125
126/* send negotiation */
127void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
128 unsigned char opt, void *user_data) {
129 unsigned char bytes[3] = { IAC, cmd, opt };
130 telnet->output_cb(telnet, bytes, 3, user_data);
131}
132
133/* send non-command data (escapes IAC bytes) */
134void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
135 size_t size, void *user_data) {
136 size_t i, l;
137 for (l = i = 0; i != size; ++i) {
138 /* dump prior portion of text, send escaped bytes */
139 if (buffer[i] == LIBTELNET_IAC) {
140 /* dump prior text if any */
141 if (i != l)
142 telnet->output_cb(telnet, buffer + l, i - l, user_data);
143 l = i + 1;
144
145 /* send escape */
146 libtelnet_send_command(telnet, LIBTELNET_IAC, user_data);
147 }
148 }
149
150 /* send whatever portion of buffer is left */
151 if (i != l)
152 telnet->output_cb(telnet, buffer + l, i - l, user_data);
153}
154
155/* send sub-request */
156void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
157 unsigned char *buffer, size_t size, void *user_data) {
158 libtelnet_send_command(telnet, SB, user_data);
159 libtelnet_send_data(telnet, &type, 1, user_data);
160 libtelnet_send_data(telnet, buffer, size, user_data);
161 libtelnet_send_command(telnet, SE, user_data);
162}