Annotation of libaitcli/src/telnet.c, revision 1.2.2.2
1.2 misho 1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.2.2.2 ! misho 6: * $Id: telnet.c,v 1.2.2.1 2012/05/23 15:04:11 misho Exp $
1.2 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
1.2.2.2 ! misho 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
1.2 misho 16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #ifndef NDEBUG
47: #define TELOPTS
48: #define TELCMDS
49: #endif
50: #include "global.h"
51:
52:
53: /*
54: * cli_telnetRecv() Telnet receive commands, negotiate with telnet peer
55: * @sock = socket for communication
56: * @attr = received attributes list, must be free after use, but if NULL receive in binary mode
57: * @nAttr = received attributes list size, if is NULL receive in binary mode
58: * @pdata = received data in supplied buffer
59: * @datLen = buffer pdata size
60: * return: 0 not present data; -1 error:: can`t read; -2 timeout; -3 EOF; >0 number of received bytes
61: */
62: int
63: cli_telnetRecv(int sock, struct telnetAttrs **attr, int *nAttr, void *pdata, int datLen)
64: {
65: int readLen, ret, pos;
66: u_char buf[BUFSIZ], *data = NULL;
67: struct telnetAttrs ta;
68: register int i;
69: #ifdef SELECT_WAIT_FOR_READ
70: fd_set fds;
71: struct timeval tv = { DEFAULT_TELNET_TIMEOUT, 0 };
72: #endif
73:
74: if (attr && nAttr) {
75: *attr = NULL;
76: *nAttr = 0;
77: }
78:
79: data = pdata ? pdata : NULL;
80: if (!data || !datLen || BUFSIZ < datLen) {
81: cli_SetErr(EINVAL, "Data buffer or size is not valid!");
82: return -1;
83: } else
84: memset(data, 0, datLen);
85:
86: #ifdef SELECT_WAIT_FOR_READ
87: FD_ZERO(&fds);
88: FD_SET(sock, &fds);
89: if ((ret = select(sock + 1, &fds, NULL, NULL, &tv)) == -1) {
90: LOGERR;
91: return -1;
92: }
93: if (!ret) {
94: cli_SetErr(ETIMEDOUT, "Timeout!");
95: return -2; // Timeout
96: } else
97: ret = 0;
98: #endif
99:
100: memset(buf, 0, BUFSIZ);
101: readLen = read(sock, buf, BUFSIZ);
102: if (-1 == readLen) {
103: LOGERR;
104: return -1;
105: } else
106: if (!readLen)
107: return -3; // EOF
108:
109: // receive in binary mode ...
110: if (!attr || !nAttr) {
111: memcpy(data, buf, readLen > datLen ? datLen : readLen);
112: return readLen;
113: }
114:
115: // telnet ascii mode ...
116: for (ret = pos = i = 0; i < readLen && pos < datLen; i++) {
117: // raw(clear) mode
118: if (!ret) {
119: if (IAC != buf[i]) {
120: data[pos++] = buf[i];
121: } else {
122: ret = 1;
123: memset(&ta, 0, sizeof ta);
124: }
125:
126: continue;
127: }
128:
129: // check for command
130: if (1 == ret) {
131: if (xEOF > buf[i]) {
132: data[pos++] = buf[i - 1];
133: data[pos++] = buf[i];
134:
135: goto cmd_exit;
136: } else
137: ta.ta_cmd = buf[i];
138: if (SB > ta.ta_cmd) {
139: (*nAttr)++;
1.2.2.1 misho 140: *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 141: if (!*attr) {
142: LOGERR;
143: return -1;
144: } else
145: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
146:
147: goto cmd_exit;
148: }
149: if (IAC > ta.ta_cmd) {
150: ret = 2;
151: continue;
152: }
153: cmd_exit:
154: ret = 0;
155: continue;
156: }
157:
158: // check for option
159: if (2 == ret) {
160: if (!(TELOPT_KERMIT >= buf[i]) && TELOPT_EXOPL != buf[i]) {
161: data[pos++] = buf[i - 2];
162: data[pos++] = buf[i - 1];
163: data[pos++] = buf[i];
164:
165: goto opt_exit;
166: } else
167: ta.ta_opt = buf[i];
168: if (SB != ta.ta_cmd) {
169: (*nAttr)++;
1.2.2.1 misho 170: *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 171: if (!*attr) {
172: LOGERR;
173: return -1;
174: } else
175: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
176:
177: goto opt_exit;
178: }
179:
180: ret = 3;
181: continue;
182: opt_exit:
183: ret = 0;
184: continue;
185: }
186:
187: // sub-option
188: if (3 == ret) {
189: if (ta.ta_sublen < MAX_SUB_LEN) {
190: if (SE == buf[i] && IAC == ta.ta_sub[ta.ta_sublen - 1]) {
191: ta.ta_sublen--;
192: ta.ta_sub[ta.ta_sublen] = 0;
193:
194: (*nAttr)++;
1.2.2.1 misho 195: *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 196: if (!*attr) {
197: LOGERR;
198: return -1;
199: } else
200: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
201: ret = 0;
202: } else
203: ta.ta_sub[ta.ta_sublen++] = buf[i];
204: } else {
205: cli_SetErr(EPROTONOSUPPORT, "Protocol limitation in sub-option to %d!", MAX_SUB_LEN);
1.2.2.1 misho 206: io_free(*attr);
1.2 misho 207: *nAttr = 0;
208: return -1;
209: }
210: }
211: }
212:
213: return pos;
214: }
215:
216: #ifndef NDEBUG
217:
218: /*
219: * cli_telnet_DumpAttrs() Telnet debug attributes list, if NDEBUG defined not include
220: * @attr = attributes list
221: * @nAttr = attributes list size
222: * return: none
223: */
224: void
225: cli_telnet_DumpAttrs(struct telnetAttrs *attr, int nAttr)
226: {
227: register int i;
228:
229: for (i = 0; i < nAttr; i++) {
230: printf("DUMP:: Attribute(%d) = %s %s Sub(%d) => %s\n", i,
231: TELCMD(attr[i].ta_cmd), TELOPT(attr[i].ta_opt),
232: attr[i].ta_sublen, attr[i].ta_sub);
233: }
234: }
235:
236: #endif
237:
238: /*
239: * cli_telnetSend() Telnet send commands, negotiate with telnet peer
240: * @sock = socket for communication
241: * @attr = send attributes list
242: * @nAttr = send attributes list size
243: * @data = data for send
244: * @datLen = data size
245: * @Term = Terminate with GA (Go Ahead), 1 send after data GA command
246: * return: 0 not sended commands; -1 error:: can`t send; >0 number of sended bytes
247: */
248: int
249: cli_telnetSend(int sock, struct telnetAttrs *attr, int nAttr, void *data, int datLen, int Term)
250: {
251: register int i;
252: int writeLen, pos = 0, len = 0;
253: u_char *buf = NULL;
254:
255: /* add commands */
256:
257: if (attr && nAttr) {
258: for (i = 0; i < nAttr; i++) {
259: len = 2; // IAC CMD
260: if (attr[i].ta_cmd > GA && attr[i].ta_cmd < IAC) {
261: len++; // added OPT
262:
263: if (SB == attr[i].ta_cmd) {
264: len += 2; // added IAC SE to end ;)
265: len += attr[i].ta_sublen;
266: }
267: }
268:
1.2.2.1 misho 269: buf = io_realloc(buf, pos + len);
1.2 misho 270: if (!buf) {
271: LOGERR;
272: return -1;
273: }
274:
275: buf[pos++] = IAC;
276: buf[pos++] = attr[i].ta_cmd;
277: if (attr[i].ta_cmd > GA && attr[i].ta_cmd < IAC) {
278: buf[pos++] = attr[i].ta_opt;
279:
280: if (SB == attr[i].ta_cmd) {
281: memcpy(buf + pos, attr[i].ta_sub, attr[i].ta_sublen);
282: pos += attr[i].ta_sublen;
283: buf[pos++] = IAC;
284: buf[pos++] = SE;
285: }
286: }
287: }
288: }
289:
290: /* add data */
291:
292: if (data && datLen) {
1.2.2.1 misho 293: buf = io_realloc(buf, pos + datLen);
1.2 misho 294: if (!buf) {
295: LOGERR;
296: return -1;
297: }
298:
299: memcpy(buf + pos, data, datLen);
300: pos += datLen;
301: }
302:
303: /* add GA after end of all */
304:
305: if (Term) {
1.2.2.1 misho 306: buf = io_realloc(buf, pos + 2);
1.2 misho 307: if (!buf) {
308: LOGERR;
309: return -1;
310: }
311:
312: buf[pos++] = IAC;
313: buf[pos++] = GA;
314: }
315:
316: writeLen = write(sock, buf, pos);
317: if (-1 == writeLen)
318: LOGERR;
319:
320: if (buf)
1.2.2.1 misho 321: io_free(buf);
1.2 misho 322: return writeLen;
323: }
324:
325:
326: /*
327: * cli_telnet_Get_SubOpt() Telnet get sub option function
328: * @attr = input attribute
329: * @code = sub-option code for opt
330: * @data = sub-option data
331: * @datLen = data size set max size in input, output return copy size
332: * return: -1 can`t get option; !=-1 option code
333: */
334: inline int
335: cli_telnet_Get_SubOpt(struct telnetAttrs *attr, u_char *code, void *data, u_char *datLen)
336: {
337: u_char *pos, len;
338:
339: if (!attr || !data || !datLen || !*datLen)
340: return -1;
341: if (SB != attr->ta_cmd || !attr->ta_sublen) {
342: cli_SetErr(ENOTSUP, "Wrong attribute argument!");
343: return -1;
344: } else {
345: pos = attr->ta_sub;
346: len = attr->ta_sublen;
347: }
348:
349: memset(data, 0, *datLen);
350: if (0xFF != *code) {
351: *code = *pos++;
352: len--;
353: }
354:
355: *datLen = len > *datLen ? *datLen : len;
356: memcpy(data, pos, *datLen);
357:
358: return attr->ta_opt;
359: }
360:
361: /*
362: * cli_telnet_Set_SubOpt() Telnet set sub option function
363: * @attr = output attribute
364: * @opt = attribute option
365: * @code = sub-option code for opt, if 0xff not specified
366: * @data = sub-option data, if NULL not specified
367: * @datLen = data size, if 0 not specified
368: * return: -1 can`t set sub-otion; 0 ok
369: */
370: inline int
371: cli_telnet_Set_SubOpt(struct telnetAttrs *attr, u_char opt, u_char code, void *data, u_char datLen)
372: {
373: u_char len;
374:
375: if (!attr)
376: return -1;
377: if (!(TELOPT_KERMIT >= opt) && TELOPT_EXOPL != opt) {
378: cli_SetErr(EINVAL, "Invalid option argument!");
379: return -1;
380: }
381:
382: memset(attr, 0, sizeof(struct telnetAttrs));
383: attr->ta_cmd = SB;
384: attr->ta_opt = opt;
385:
386: if (0xFF != code) {
387: attr->ta_sublen++;
388: attr->ta_sub[0] = code;
389: }
390:
391: if (data && datLen) {
392: len = MAX_SUB_LEN > datLen ? datLen : MAX_SUB_LEN - 1;
393: attr->ta_sublen += len;
394: memcpy(attr->ta_sub + 1, data, len);
395: }
396:
397: return 0;
398: }
399:
400: /*
401: * cli_telnet_GetCmd() Telnet get command
402: * @attr = input attribute
403: * return: -1 can`t get command; !=-1 command <<24 return sublen, <<8 return option, <<0 command
404: */
405: inline u_int
406: cli_telnet_GetCmd(struct telnetAttrs *attr)
407: {
408: u_int ret = 0;
409:
410: if (!attr)
411: return -1;
412: if (xEOF > attr->ta_cmd) {
413: cli_SetErr(ENOTSUP, "Wrong attribute command argument!");
414: return -1;
415: }
416: if (GA < attr->ta_cmd && !(TELOPT_KERMIT >= attr->ta_opt) && TELOPT_EXOPL != attr->ta_opt) {
417: cli_SetErr(ENOTSUP, "Wrong attribute option argument!");
418: return -1;
419: }
420:
421: ret = attr->ta_sublen << 24;
422: ret |= attr->ta_opt << 8;
423: ret |= attr->ta_cmd;
424:
425: return ret;
426: }
427:
428: /*
429: * cli_telnet_SetCmd() Telnet set command
430: * @attr = input attribute
431: * @cmd = command
432: * @opt = option, if 0xff not specified
433: * @arg1 = sub-option code, if 0xff not specified
434: * @arg2 = sub-option data, if NULL not specified
435: * @arg3 = sub-option data size, if 0 not specified data
436: * return: -1 can`t set command; !=-1 ok
437: */
438: inline int
439: cli_telnet_SetCmd(struct telnetAttrs *attr, u_char cmd, u_char opt, ...)
440: {
441: va_list lst;
442: u_char res;
443: void *ptr;
444:
445: if (!attr)
446: return -1;
447: else
448: memset(attr, 0, sizeof(struct telnetAttrs));
449:
450: if (xEOF > cmd) {
451: cli_SetErr(EINVAL, "Invalid command argument!");
452: return -1;
453: } else
454: attr->ta_cmd = cmd;
455: if (GA < attr->ta_cmd) {
456: if (!(TELOPT_KERMIT >= attr->ta_opt) && TELOPT_EXOPL != attr->ta_opt) {
457: cli_SetErr(EINVAL, "Invalid option argument!");
458: return -1;
459: } else
460: attr->ta_opt = opt;
461: }
462: if (SB == attr->ta_cmd) {
463: va_start(lst, opt);
464: res = (u_char) va_arg(lst, int);
465: if (0xff != res) {
466: *attr->ta_sub = res;
467: attr->ta_sublen++;
468: }
469: ptr = va_arg(lst, void*);
470: res = (u_char) va_arg(lst, int);
471: if (ptr && MAX_SUB_LEN > res) {
472: memcpy(attr->ta_sub + 1, ptr, res);
473: attr->ta_sublen += res;
474: }
475: va_end(lst);
476: }
477:
478: return 0;
479: }
480:
481:
482: /*
483: * cli_telnet_Answer() Automatic generate commands answer to send from telnet
484: * @caps = Array of capability options
485: * @nCaps = number of capability options
486: * @attr = input attribute
487: * @nAttr = number of input attributes
1.2.2.1 misho 488: * @ans = output answered attributes, must be io_free() after use
1.2 misho 489: * @Ans = number of output answered attributes
490: * return: -1 can`t answer; !=-1 ok
491: */
492: int
493: cli_telnet_Answer(u_char *caps, int nCaps, struct telnetAttrs *attr, int nAttr,
494: struct telnetAttrs **ans, int *Ans)
495: {
496: register int i, j;
497: int flg;
498: struct telnetAttrs ta;
499:
500: if (!caps || !nCaps || !attr || !nAttr || !ans || !Ans)
501: return -1;
502: else {
503: *ans = NULL;
504: *Ans = 0;
505: }
506:
507: for (i = 0; i < nAttr; i++) {
508: if (SB > attr[i].ta_cmd || IAC == attr[i].ta_cmd || DONT == attr[i].ta_cmd || WONT == attr[i].ta_cmd)
509: continue;
510:
511: for (flg = -1, j = 0; j < nCaps; j++) {
512: if (!(TELOPT_KERMIT >= CAP(caps[j])) && TELOPT_EXOPL != CAP(caps[j]))
513: continue;
514:
515: if (attr[i].ta_opt == CAP(caps[j])) {
516: flg = j;
517: break;
518: }
519: }
520:
521: // make attribute ...
522: if (flg > -1) {
523: (*Ans)++;
1.2.2.1 misho 524: *ans = io_realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
1.2 misho 525: if (!*ans) {
526: LOGERR;
527: return -1;
528: } else
529: memset(&ta, 0, sizeof ta);
530:
531: ta.ta_opt = attr[i].ta_opt;
532: switch (attr[i].ta_cmd) {
533: case DO:
534: ta.ta_cmd = SUP_CAPS(caps[flg]) ? WILL : WONT;
535: break;
536: case WILL:
537: ta.ta_cmd = SUP_CAPS(caps[flg]) ? DO : DONT;
538: break;
539: case SB:
540: ta.ta_cmd = SB;
541: break;
542: }
543:
544: memcpy(&(*ans)[*Ans - 1], &ta, sizeof(struct telnetAttrs));
545: }
546: }
547:
548: return 0;
549: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>