Annotation of libaitcli/src/telnet.c, revision 1.3.4.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.3.4.2 ! misho 6: * $Id: telnet.c,v 1.3.4.1 2013/01/17 16:04:31 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.3.4.1 misho 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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: /*
1.3.4.1 misho 54: * cli_telnetRecv() - Telnet receive commands, negotiate with telnet peer
55: *
1.2 misho 56: * @sock = socket for communication
57: * @attr = received attributes list, must be free after use, but if NULL receive in binary mode
58: * @nAttr = received attributes list size, if is NULL receive in binary mode
59: * @pdata = received data in supplied buffer
60: * @datLen = buffer pdata size
61: * return: 0 not present data; -1 error:: can`t read; -2 timeout; -3 EOF; >0 number of received bytes
62: */
63: int
64: cli_telnetRecv(int sock, struct telnetAttrs **attr, int *nAttr, void *pdata, int datLen)
65: {
66: int readLen, ret, pos;
67: u_char buf[BUFSIZ], *data = NULL;
68: struct telnetAttrs ta;
69: register int i;
70: #ifdef SELECT_WAIT_FOR_READ
71: fd_set fds;
72: struct timeval tv = { DEFAULT_TELNET_TIMEOUT, 0 };
73: #endif
74:
75: if (attr && nAttr) {
76: *attr = NULL;
77: *nAttr = 0;
78: }
79:
80: data = pdata ? pdata : NULL;
81: if (!data || !datLen || BUFSIZ < datLen) {
82: cli_SetErr(EINVAL, "Data buffer or size is not valid!");
83: return -1;
84: } else
85: memset(data, 0, datLen);
86:
87: #ifdef SELECT_WAIT_FOR_READ
88: FD_ZERO(&fds);
89: FD_SET(sock, &fds);
90: if ((ret = select(sock + 1, &fds, NULL, NULL, &tv)) == -1) {
91: LOGERR;
92: return -1;
93: }
94: if (!ret) {
95: cli_SetErr(ETIMEDOUT, "Timeout!");
96: return -2; // Timeout
97: } else
98: ret = 0;
99: #endif
100:
101: memset(buf, 0, BUFSIZ);
102: readLen = read(sock, buf, BUFSIZ);
103: if (-1 == readLen) {
104: LOGERR;
105: return -1;
106: } else
107: if (!readLen)
108: return -3; // EOF
109:
110: // receive in binary mode ...
111: if (!attr || !nAttr) {
112: memcpy(data, buf, readLen > datLen ? datLen : readLen);
113: return readLen;
114: }
115:
116: // telnet ascii mode ...
117: for (ret = pos = i = 0; i < readLen && pos < datLen; i++) {
118: // raw(clear) mode
119: if (!ret) {
120: if (IAC != buf[i]) {
121: data[pos++] = buf[i];
122: } else {
123: ret = 1;
124: memset(&ta, 0, sizeof ta);
125: }
126:
127: continue;
128: }
129:
130: // check for command
131: if (1 == ret) {
132: if (xEOF > buf[i]) {
133: data[pos++] = buf[i - 1];
134: data[pos++] = buf[i];
135:
136: goto cmd_exit;
137: } else
138: ta.ta_cmd = buf[i];
139: if (SB > ta.ta_cmd) {
140: (*nAttr)++;
1.3.4.1 misho 141: *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 142: if (!*attr) {
143: LOGERR;
144: return -1;
145: } else
146: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
147:
148: goto cmd_exit;
149: }
150: if (IAC > ta.ta_cmd) {
151: ret = 2;
152: continue;
153: }
154: cmd_exit:
155: ret = 0;
156: continue;
157: }
158:
159: // check for option
160: if (2 == ret) {
161: if (!(TELOPT_KERMIT >= buf[i]) && TELOPT_EXOPL != buf[i]) {
162: data[pos++] = buf[i - 2];
163: data[pos++] = buf[i - 1];
164: data[pos++] = buf[i];
165:
166: goto opt_exit;
167: } else
168: ta.ta_opt = buf[i];
169: if (SB != ta.ta_cmd) {
170: (*nAttr)++;
1.3.4.1 misho 171: *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 172: if (!*attr) {
173: LOGERR;
174: return -1;
175: } else
176: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
177:
178: goto opt_exit;
179: }
180:
181: ret = 3;
182: continue;
183: opt_exit:
184: ret = 0;
185: continue;
186: }
187:
188: // sub-option
189: if (3 == ret) {
190: if (ta.ta_sublen < MAX_SUB_LEN) {
191: if (SE == buf[i] && IAC == ta.ta_sub[ta.ta_sublen - 1]) {
192: ta.ta_sublen--;
193: ta.ta_sub[ta.ta_sublen] = 0;
194:
195: (*nAttr)++;
1.3.4.1 misho 196: *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2 misho 197: if (!*attr) {
198: LOGERR;
199: return -1;
200: } else
201: memcpy(&(*attr)[*nAttr - 1], &ta, sizeof(struct telnetAttrs));
202: ret = 0;
203: } else
204: ta.ta_sub[ta.ta_sublen++] = buf[i];
205: } else {
206: cli_SetErr(EPROTONOSUPPORT, "Protocol limitation in sub-option to %d!", MAX_SUB_LEN);
1.3.4.1 misho 207: e_free(*attr);
1.2 misho 208: *nAttr = 0;
209: return -1;
210: }
211: }
212: }
213:
214: return pos;
215: }
216:
217: #ifndef NDEBUG
218:
219: /*
1.3.4.1 misho 220: * cli_telnet_DumpAttrs() - Telnet debug attributes list, if NDEBUG defined not include
221: *
1.2 misho 222: * @attr = attributes list
223: * @nAttr = attributes list size
224: * return: none
225: */
226: void
227: cli_telnet_DumpAttrs(struct telnetAttrs *attr, int nAttr)
228: {
229: register int i;
230:
231: for (i = 0; i < nAttr; i++) {
232: printf("DUMP:: Attribute(%d) = %s %s Sub(%d) => %s\n", i,
233: TELCMD(attr[i].ta_cmd), TELOPT(attr[i].ta_opt),
234: attr[i].ta_sublen, attr[i].ta_sub);
235: }
236: }
237:
238: #endif
239:
240: /*
1.3.4.1 misho 241: * cli_telnetSend() - Telnet send commands, negotiate with telnet peer
242: *
1.2 misho 243: * @sock = socket for communication
244: * @attr = send attributes list
245: * @nAttr = send attributes list size
246: * @data = data for send
247: * @datLen = data size
248: * @Term = Terminate with GA (Go Ahead), 1 send after data GA command
249: * return: 0 not sended commands; -1 error:: can`t send; >0 number of sended bytes
250: */
251: int
252: cli_telnetSend(int sock, struct telnetAttrs *attr, int nAttr, void *data, int datLen, int Term)
253: {
254: register int i;
255: int writeLen, pos = 0, len = 0;
256: u_char *buf = NULL;
257:
258: /* add commands */
259:
260: if (attr && nAttr) {
261: for (i = 0; i < nAttr; i++) {
262: len = 2; // IAC CMD
263: if (attr[i].ta_cmd > GA && attr[i].ta_cmd < IAC) {
264: len++; // added OPT
265:
266: if (SB == attr[i].ta_cmd) {
267: len += 2; // added IAC SE to end ;)
268: len += attr[i].ta_sublen;
269: }
270: }
271:
1.3.4.1 misho 272: buf = e_realloc(buf, pos + len);
1.2 misho 273: if (!buf) {
274: LOGERR;
275: return -1;
276: }
277:
278: buf[pos++] = IAC;
279: buf[pos++] = attr[i].ta_cmd;
280: if (attr[i].ta_cmd > GA && attr[i].ta_cmd < IAC) {
281: buf[pos++] = attr[i].ta_opt;
282:
283: if (SB == attr[i].ta_cmd) {
284: memcpy(buf + pos, attr[i].ta_sub, attr[i].ta_sublen);
285: pos += attr[i].ta_sublen;
286: buf[pos++] = IAC;
287: buf[pos++] = SE;
288: }
289: }
290: }
291: }
292:
293: /* add data */
294:
295: if (data && datLen) {
1.3.4.1 misho 296: buf = e_realloc(buf, pos + datLen);
1.2 misho 297: if (!buf) {
298: LOGERR;
299: return -1;
300: }
301:
302: memcpy(buf + pos, data, datLen);
303: pos += datLen;
304: }
305:
306: /* add GA after end of all */
307:
308: if (Term) {
1.3.4.1 misho 309: buf = e_realloc(buf, pos + 2);
1.2 misho 310: if (!buf) {
311: LOGERR;
312: return -1;
313: }
314:
315: buf[pos++] = IAC;
316: buf[pos++] = GA;
317: }
318:
319: writeLen = write(sock, buf, pos);
320: if (-1 == writeLen)
321: LOGERR;
322:
323: if (buf)
1.3.4.1 misho 324: e_free(buf);
1.2 misho 325: return writeLen;
326: }
327:
328:
329: /*
1.3.4.1 misho 330: * cli_telnet_Get_SubOpt() - Telnet get sub option function
331: *
1.2 misho 332: * @attr = input attribute
333: * @code = sub-option code for opt
334: * @data = sub-option data
335: * @datLen = data size set max size in input, output return copy size
336: * return: -1 can`t get option; !=-1 option code
337: */
1.3.4.2 ! misho 338: int
1.2 misho 339: cli_telnet_Get_SubOpt(struct telnetAttrs *attr, u_char *code, void *data, u_char *datLen)
340: {
341: u_char *pos, len;
342:
343: if (!attr || !data || !datLen || !*datLen)
344: return -1;
345: if (SB != attr->ta_cmd || !attr->ta_sublen) {
346: cli_SetErr(ENOTSUP, "Wrong attribute argument!");
347: return -1;
348: } else {
349: pos = attr->ta_sub;
350: len = attr->ta_sublen;
351: }
352:
353: memset(data, 0, *datLen);
354: if (0xFF != *code) {
355: *code = *pos++;
356: len--;
357: }
358:
359: *datLen = len > *datLen ? *datLen : len;
360: memcpy(data, pos, *datLen);
361:
362: return attr->ta_opt;
363: }
364:
365: /*
1.3.4.1 misho 366: * cli_telnet_Set_SubOpt() - Telnet set sub option function
367: *
1.2 misho 368: * @attr = output attribute
369: * @opt = attribute option
370: * @code = sub-option code for opt, if 0xff not specified
371: * @data = sub-option data, if NULL not specified
372: * @datLen = data size, if 0 not specified
373: * return: -1 can`t set sub-otion; 0 ok
374: */
1.3.4.2 ! misho 375: int
1.2 misho 376: cli_telnet_Set_SubOpt(struct telnetAttrs *attr, u_char opt, u_char code, void *data, u_char datLen)
377: {
378: u_char len;
379:
380: if (!attr)
381: return -1;
382: if (!(TELOPT_KERMIT >= opt) && TELOPT_EXOPL != opt) {
383: cli_SetErr(EINVAL, "Invalid option argument!");
384: return -1;
385: }
386:
387: memset(attr, 0, sizeof(struct telnetAttrs));
388: attr->ta_cmd = SB;
389: attr->ta_opt = opt;
390:
391: if (0xFF != code) {
392: attr->ta_sublen++;
393: attr->ta_sub[0] = code;
394: }
395:
396: if (data && datLen) {
397: len = MAX_SUB_LEN > datLen ? datLen : MAX_SUB_LEN - 1;
398: attr->ta_sublen += len;
399: memcpy(attr->ta_sub + 1, data, len);
400: }
401:
402: return 0;
403: }
404:
405: /*
1.3.4.1 misho 406: * cli_telnet_GetCmd() - Telnet get command
407: *
1.2 misho 408: * @attr = input attribute
409: * return: -1 can`t get command; !=-1 command <<24 return sublen, <<8 return option, <<0 command
410: */
1.3.4.2 ! misho 411: u_int
1.2 misho 412: cli_telnet_GetCmd(struct telnetAttrs *attr)
413: {
414: u_int ret = 0;
415:
416: if (!attr)
417: return -1;
418: if (xEOF > attr->ta_cmd) {
419: cli_SetErr(ENOTSUP, "Wrong attribute command argument!");
420: return -1;
421: }
422: if (GA < attr->ta_cmd && !(TELOPT_KERMIT >= attr->ta_opt) && TELOPT_EXOPL != attr->ta_opt) {
423: cli_SetErr(ENOTSUP, "Wrong attribute option argument!");
424: return -1;
425: }
426:
427: ret = attr->ta_sublen << 24;
428: ret |= attr->ta_opt << 8;
429: ret |= attr->ta_cmd;
430:
431: return ret;
432: }
433:
434: /*
1.3.4.1 misho 435: * cli_telnet_SetCmd() - Telnet set command
436: *
1.2 misho 437: * @attr = input attribute
438: * @cmd = command
439: * @opt = option, if 0xff not specified
440: * @arg1 = sub-option code, if 0xff not specified
441: * @arg2 = sub-option data, if NULL not specified
442: * @arg3 = sub-option data size, if 0 not specified data
443: * return: -1 can`t set command; !=-1 ok
444: */
1.3.4.2 ! misho 445: int
1.2 misho 446: cli_telnet_SetCmd(struct telnetAttrs *attr, u_char cmd, u_char opt, ...)
447: {
448: va_list lst;
449: u_char res;
450: void *ptr;
451:
452: if (!attr)
453: return -1;
454: else
455: memset(attr, 0, sizeof(struct telnetAttrs));
456:
457: if (xEOF > cmd) {
458: cli_SetErr(EINVAL, "Invalid command argument!");
459: return -1;
460: } else
461: attr->ta_cmd = cmd;
462: if (GA < attr->ta_cmd) {
463: if (!(TELOPT_KERMIT >= attr->ta_opt) && TELOPT_EXOPL != attr->ta_opt) {
464: cli_SetErr(EINVAL, "Invalid option argument!");
465: return -1;
466: } else
467: attr->ta_opt = opt;
468: }
469: if (SB == attr->ta_cmd) {
470: va_start(lst, opt);
471: res = (u_char) va_arg(lst, int);
472: if (0xff != res) {
473: *attr->ta_sub = res;
474: attr->ta_sublen++;
475: }
476: ptr = va_arg(lst, void*);
477: res = (u_char) va_arg(lst, int);
478: if (ptr && MAX_SUB_LEN > res) {
479: memcpy(attr->ta_sub + 1, ptr, res);
480: attr->ta_sublen += res;
481: }
482: va_end(lst);
483: }
484:
485: return 0;
486: }
487:
488:
489: /*
1.3.4.1 misho 490: * cli_telnet_Answer() - Automatic generate commands answer to send from telnet
491: *
1.2 misho 492: * @caps = Array of capability options
493: * @nCaps = number of capability options
494: * @attr = input attribute
495: * @nAttr = number of input attributes
1.3.4.1 misho 496: * @ans = output answered attributes, must be e_free() after use
1.2 misho 497: * @Ans = number of output answered attributes
498: * return: -1 can`t answer; !=-1 ok
499: */
500: int
501: cli_telnet_Answer(u_char *caps, int nCaps, struct telnetAttrs *attr, int nAttr,
502: struct telnetAttrs **ans, int *Ans)
503: {
504: register int i, j;
505: int flg;
506: struct telnetAttrs ta;
507:
508: if (!caps || !nCaps || !attr || !nAttr || !ans || !Ans)
509: return -1;
510: else {
511: *ans = NULL;
512: *Ans = 0;
513: }
514:
515: for (i = 0; i < nAttr; i++) {
516: if (SB > attr[i].ta_cmd || IAC == attr[i].ta_cmd || DONT == attr[i].ta_cmd || WONT == attr[i].ta_cmd)
517: continue;
518:
519: for (flg = -1, j = 0; j < nCaps; j++) {
520: if (!(TELOPT_KERMIT >= CAP(caps[j])) && TELOPT_EXOPL != CAP(caps[j]))
521: continue;
522:
523: if (attr[i].ta_opt == CAP(caps[j])) {
524: flg = j;
525: break;
526: }
527: }
528:
529: // make attribute ...
530: if (flg > -1) {
531: (*Ans)++;
1.3.4.1 misho 532: *ans = e_realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
1.2 misho 533: if (!*ans) {
534: LOGERR;
535: return -1;
536: } else
537: memset(&ta, 0, sizeof ta);
538:
539: ta.ta_opt = attr[i].ta_opt;
540: switch (attr[i].ta_cmd) {
541: case DO:
542: ta.ta_cmd = SUP_CAPS(caps[flg]) ? WILL : WONT;
543: break;
544: case WILL:
545: ta.ta_cmd = SUP_CAPS(caps[flg]) ? DO : DONT;
546: break;
547: case SB:
548: ta.ta_cmd = SB;
549: break;
550: }
551:
552: memcpy(&(*ans)[*Ans - 1], &ta, sizeof(struct telnetAttrs));
553: }
554: }
555:
556: return 0;
557: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>