]> git.zerfleddert.de Git - amt/blob - redir.c
9731715b62d96b455df60415f96bd7ce925778ae
[amt] / redir.c
1 /*
2 * Intel AMT tcp redirection protocol helper functions.
3 *
4 * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27
28 #include "tcp.h"
29 #include "redir.h"
30
31 static const char *state_name[] = {
32 [ REDIR_NONE ] = "NONE",
33 [ REDIR_CONNECT ] = "CONNECT",
34 [ REDIR_INIT ] = "INIT",
35 [ REDIR_AUTH ] = "AUTH",
36 [ REDIR_INIT_SOL ] = "INIT_SOL",
37 [ REDIR_RUN_SOL ] = "RUN_SOL",
38 [ REDIR_INIT_IDER ] = "INIT_IDER",
39 [ REDIR_RUN_IDER ] = "RUN_IDER",
40 [ REDIR_CLOSING ] = "CLOSING",
41 [ REDIR_CLOSED ] = "CLOSED",
42 [ REDIR_ERROR ] = "ERROR",
43 };
44
45 static const char *state_desc[] = {
46 [ REDIR_NONE ] = "disconnected",
47 [ REDIR_CONNECT ] = "connection to host",
48 [ REDIR_INIT ] = "redirection initialization",
49 [ REDIR_AUTH ] = "session authentication",
50 [ REDIR_INIT_SOL ] = "serial-over-lan initialization",
51 [ REDIR_RUN_SOL ] = "serial-over-lan active",
52 [ REDIR_INIT_IDER ] = "IDE redirect initialization",
53 [ REDIR_RUN_IDER ] = "IDE redirect active",
54 [ REDIR_CLOSING ] = "redirection shutdown",
55 [ REDIR_CLOSED ] = "connection closed",
56 [ REDIR_ERROR ] = "failure",
57 };
58
59 /* ------------------------------------------------------------------ */
60
61 static void hexdump(const char *prefix, const unsigned char *data, size_t size)
62 {
63 char ascii[17];
64 int i;
65
66 for (i = 0; i < size; i++) {
67 if (0 == (i%16)) {
68 fprintf(stderr,"%s%s%04x:",
69 prefix ? prefix : "",
70 prefix ? ": " : "",
71 i);
72 memset(ascii,0,sizeof(ascii));
73 }
74 if (0 == (i%4))
75 fprintf(stderr," ");
76 fprintf(stderr," %02x",data[i]);
77 ascii[i%16] = isprint(data[i]) ? data[i] : '.';
78 if (15 == (i%16))
79 fprintf(stderr," %s\n",ascii);
80 }
81 if (0 != (i%16)) {
82 while (0 != (i%16)) {
83 if (0 == (i%4))
84 fprintf(stderr," ");
85 fprintf(stderr," ");
86 i++;
87 };
88 fprintf(stderr," %s\n",ascii);
89 }
90 }
91
92 static ssize_t redir_write(struct redir *r, const char *buf, size_t count)
93 {
94 int rc;
95
96 if (r->trace)
97 hexdump("out", buf, count);
98 rc = write(r->sock, buf, count);
99 if (-1 == rc)
100 snprintf(r->err, sizeof(r->err), "write(socket): %s", strerror(errno));
101 return rc;
102 }
103
104 static void redir_state(struct redir *r, enum redir_state new)
105 {
106 enum redir_state old = r->state;
107
108 r->state = new;
109 if (r->cb_state)
110 r->cb_state(r->cb_data, old, new);
111 }
112
113 /* ------------------------------------------------------------------ */
114
115 const char *redir_state_name(enum redir_state state)
116 {
117 const char *name = NULL;
118
119 if (state < sizeof(state_name)/sizeof(state_name[0]))
120 name = state_name[state];
121 if (NULL == name)
122 name = "unknown";
123 return name;
124 }
125
126 const char *redir_state_desc(enum redir_state state)
127 {
128 const char *desc = NULL;
129
130 if (state < sizeof(state_desc)/sizeof(state_desc[0]))
131 desc = state_desc[state];
132 if (NULL == desc)
133 desc = "unknown";
134 return desc;
135 }
136
137 int redir_connect(struct redir *r)
138 {
139 static unsigned char *defport = "16994";
140 struct addrinfo ai;
141
142 memset(&ai, 0, sizeof(ai));
143 ai.ai_socktype = SOCK_STREAM;
144 ai.ai_family = PF_UNSPEC;
145 tcp_verbose = r->verbose;
146 redir_state(r, REDIR_CONNECT);
147 r->sock = tcp_connect(&ai, NULL, NULL, r->host,
148 strlen(r->port) ? r->port : defport);
149 if (-1 == r->sock) {
150 redir_state(r, REDIR_ERROR);
151 return -1;
152 }
153 return 0;
154 }
155
156 int redir_start(struct redir *r)
157 {
158 unsigned char request[START_REDIRECTION_SESSION_LENGTH] = {
159 START_REDIRECTION_SESSION, 0, 0, 0, 0, 0, 0, 0
160 };
161
162 memcpy(request+4, r->type, 4);
163 redir_state(r, REDIR_INIT);
164 return redir_write(r, request, sizeof(request));
165 }
166
167 int redir_stop(struct redir *r)
168 {
169 unsigned char request[END_REDIRECTION_SESSION_LENGTH] = {
170 END_REDIRECTION_SESSION, 0, 0, 0
171 };
172
173 redir_state(r, REDIR_CLOSED);
174 redir_write(r, request, sizeof(request));
175 close(r->sock);
176 return 0;
177 }
178
179 int redir_auth(struct redir *r)
180 {
181 int ulen = strlen(r->user);
182 int plen = strlen(r->pass);
183 int len = 11+ulen+plen;
184 int rc;
185 unsigned char *request = malloc(len);
186
187 memset(request, 0, len);
188 request[0] = AUTHENTICATE_SESSION;
189 request[4] = 0x01;
190 request[5] = ulen+plen+2;
191 request[9] = ulen;
192 memcpy(request + 10, r->user, ulen);
193 request[10 + ulen] = plen;
194 memcpy(request + 11 + ulen, r->pass, plen);
195 redir_state(r, REDIR_AUTH);
196 rc = redir_write(r, request, len);
197 free(request);
198 return rc;
199 }
200
201 int redir_sol_start(struct redir *r)
202 {
203 unsigned char request[START_SOL_REDIRECTION_LENGTH] = {
204 START_SOL_REDIRECTION, 0, 0, 0,
205 0, 0, 0, 0,
206 MAX_TRANSMIT_BUFFER & 0xff,
207 MAX_TRANSMIT_BUFFER >> 8,
208 TRANSMIT_BUFFER_TIMEOUT & 0xff,
209 TRANSMIT_BUFFER_TIMEOUT >> 8,
210 TRANSMIT_OVERFLOW_TIMEOUT & 0xff, TRANSMIT_OVERFLOW_TIMEOUT >> 8,
211 HOST_SESSION_RX_TIMEOUT & 0xff,
212 HOST_SESSION_RX_TIMEOUT >> 8,
213 HOST_FIFO_RX_FLUSH_TIMEOUT & 0xff,
214 HOST_FIFO_RX_FLUSH_TIMEOUT >> 8,
215 HEARTBEAT_INTERVAL & 0xff,
216 HEARTBEAT_INTERVAL >> 8,
217 0, 0, 0, 0
218 };
219
220 redir_state(r, REDIR_INIT_SOL);
221 return redir_write(r, request, sizeof(request));
222 }
223
224 int redir_sol_stop(struct redir *r)
225 {
226 unsigned char request[END_SOL_REDIRECTION_LENGTH] = {
227 END_SOL_REDIRECTION, 0, 0, 0,
228 0, 0, 0, 0,
229 };
230
231 redir_state(r, REDIR_CLOSING);
232 return redir_write(r, request, sizeof(request));
233 }
234
235 int redir_sol_send(struct redir *r, unsigned char *buf, int blen)
236 {
237 int len = 10+blen;
238 int rc;
239 unsigned char *request = malloc(len);
240
241 memset(request, 0, len);
242 request[0] = SOL_DATA_TO_HOST;
243 request[8] = blen & 0xff;
244 request[9] = blen >> 8;
245 memcpy(request + 10, buf, blen);
246 rc = redir_write(r, request, len);
247 free(request);
248 return rc;
249 }
250
251 int redir_sol_recv(struct redir *r)
252 {
253 unsigned char msg[64];
254 int count, len, bshift;
255
256 len = r->buf[8] + (r->buf[9] << 8);
257 count = r->blen - 10;
258 if (count > len)
259 count = len;
260 bshift = count + 10;
261 if (r->cb_recv)
262 r->cb_recv(r->cb_data, r->buf + 10, count);
263 len -= count;
264
265 while (len) {
266 if (r->trace)
267 fprintf(stderr, "in+: need %d more data bytes\n", len);
268 count = sizeof(msg);
269 if (count > len)
270 count = len;
271 count = read(r->sock, msg, count);
272 switch (count) {
273 case -1:
274 snprintf(r->err, sizeof(r->err), "read(socket): %s", strerror(errno));
275 return -1;
276 case 0:
277 snprintf(r->err, sizeof(r->err), "EOF from socket");
278 return -1;
279 default:
280 if (r->trace)
281 hexdump("in+", msg, count);
282 if (r->cb_recv)
283 r->cb_recv(r->cb_data, msg, count);
284 len -= count;
285 }
286 }
287
288 return bshift;
289 }
290
291 int redir_data(struct redir *r)
292 {
293 int rc, bshift;
294
295 if (r->trace) {
296 fprintf(stderr, "in --\n");
297 if (r->blen)
298 fprintf(stderr, "in : already have %d\n", r->blen);
299 }
300 rc = read(r->sock, r->buf + r->blen, sizeof(r->buf) - r->blen);
301 switch (rc) {
302 case -1:
303 snprintf(r->err, sizeof(r->err), "read(socket): %s", strerror(errno));
304 goto err;
305 case 0:
306 snprintf(r->err, sizeof(r->err), "EOF from socket");
307 goto err;
308 default:
309 if (r->trace)
310 hexdump("in ", r->buf + r->blen, rc);
311 r->blen += rc;
312 }
313
314 for (;;) {
315 if (r->blen < 4)
316 goto again;
317 bshift = 0;
318
319 switch (r->buf[0]) {
320 case START_REDIRECTION_SESSION_REPLY:
321 bshift = START_REDIRECTION_SESSION_REPLY_LENGTH;
322 if (r->blen < bshift)
323 goto again;
324 if (r->buf[1] != STATUS_SUCCESS) {
325 snprintf(r->err, sizeof(r->err), "redirection session start failed");
326 goto err;
327 }
328 if (-1 == redir_auth(r))
329 goto err;
330 break;
331 case AUTHENTICATE_SESSION_REPLY:
332 bshift = r->blen; /* FIXME */
333 if (r->blen < bshift)
334 goto again;
335 if (r->buf[1] != STATUS_SUCCESS) {
336 snprintf(r->err, sizeof(r->err), "session authentication failed");
337 goto err;
338 }
339 if (-1 == redir_sol_start(r))
340 goto err;
341 break;
342 case START_SOL_REDIRECTION_REPLY:
343 bshift = r->blen; /* FIXME */
344 if (r->blen < bshift)
345 goto again;
346 if (r->buf[1] != STATUS_SUCCESS) {
347 snprintf(r->err, sizeof(r->err), "serial-over-lan redirection failed");
348 goto err;
349 }
350 redir_state(r, REDIR_RUN_SOL);
351 break;
352 case SOL_HEARTBEAT:
353 case SOL_KEEP_ALIVE_PING:
354 case IDER_HEARTBEAT:
355 case IDER_KEEP_ALIVE_PING:
356 bshift = HEARTBEAT_LENGTH;
357 if (r->blen < bshift)
358 goto again;
359 if (HEARTBEAT_LENGTH != redir_write(r, r->buf, HEARTBEAT_LENGTH))
360 goto err;
361 break;
362 case SOL_DATA_FROM_HOST:
363 if (r->blen < 10) /* header length */
364 goto again;
365 bshift = redir_sol_recv(r);
366 if (bshift < 0)
367 goto err;
368 break;
369 case END_SOL_REDIRECTION_REPLY:
370 bshift = r->blen; /* FIXME */
371 if (r->blen < bshift)
372 goto again;
373 redir_stop(r);
374 break;
375 default:
376 snprintf(r->err, sizeof(r->err), "%s: unknown r->buf 0x%02x",
377 __FUNCTION__, r->buf[0]);
378 goto err;
379 }
380
381 if (bshift == r->blen) {
382 r->blen = 0;
383 break;
384 }
385
386 /* have more data, shift by bshift */
387 if (r->trace)
388 fprintf(stderr, "in : shift by %d\n", bshift);
389 memmove(r->buf, r->buf + bshift, r->blen - bshift);
390 r->blen -= bshift;
391 }
392 return 0;
393
394 again:
395 /* need more data, jump back into poll/select loop */
396 if (r->trace)
397 fprintf(stderr, "in : need more data\n");
398 return 0;
399
400 err:
401 if (r->trace)
402 fprintf(stderr, "in : ERROR (%s)\n", r->err);
403 redir_state(r, REDIR_ERROR);
404 close(r->sock);
405 return -1;
406 }
Impressum, Datenschutz