Annotation of libaitcli/src/telnet.c, revision 1.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 $
! 6: * $Id: telnet.c,v 1.1.2.2 2011/05/29 22:48:23 misho Exp $
! 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:
! 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
! 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)++;
! 140: *attr = realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
! 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)++;
! 170: *attr = realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
! 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)++;
! 195: *attr = realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
! 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);
! 206: free(*attr);
! 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:
! 269: buf = realloc(buf, pos + len);
! 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) {
! 293: buf = realloc(buf, pos + datLen);
! 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) {
! 306: buf = realloc(buf, pos + 2);
! 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)
! 321: free(buf);
! 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
! 488: * @ans = output answered attributes, must be free() after use
! 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)++;
! 524: *ans = realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
! 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>