File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / telnet.c
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Thu May 30 09:16:42 2013 UTC (11 years, 1 month ago) by misho
Branches: MAIN
CVS tags: cli4_1, cli4_0, cli3_9, cli3_8, cli3_7, cli3_6, cli3_5, cli3_4, cli3_3, cli3_2, cli3_1, HEAD, CLI4_0, CLI3_9, CLI3_8, CLI3_7, CLI3_6, CLI3_5, CLI3_4, CLI3_3, CLI3_2, CLI3_1, CLI3_0
version 3.0

    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.4 2013/05/30 09:16:42 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, 2012, 2013
   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:  *
   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)++;
  141: 				*attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
  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)++;
  171: 				*attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
  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)++;
  196: 					*attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
  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);
  207: 				e_free(*attr);
  208: 				*nAttr = 0;
  209: 				return -1;
  210: 			}
  211: 		}
  212: 	}
  213: 
  214: 	return pos;
  215: }
  216: 
  217: #ifndef NDEBUG
  218: 
  219: /*
  220:  * cli_telnet_DumpAttrs() - Telnet debug attributes list, if NDEBUG defined not include
  221:  *
  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: /*
  241:  * cli_telnetSend() - Telnet send commands, negotiate with telnet peer
  242:  *
  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: 
  272: 			buf = e_realloc(buf, pos + len);
  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) {
  296: 		buf = e_realloc(buf, pos + datLen);
  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) {
  309: 		buf = e_realloc(buf, pos + 2);
  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)
  324: 		e_free(buf);
  325: 	return writeLen;
  326: }
  327: 
  328: 
  329: /*
  330:  * cli_telnet_Get_SubOpt() - Telnet get sub option function
  331:  *
  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: */
  338: int
  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: /*
  366:  * cli_telnet_Set_SubOpt() - Telnet set sub option function
  367:  *
  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: */
  375: int
  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: /*
  406:  * cli_telnet_GetCmd() - Telnet get command
  407:  *
  408:  * @attr = input attribute
  409:  * return: -1 can`t get command; !=-1 command <<24 return sublen, <<8 return option, <<0 command
  410: */
  411: u_int
  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: /*
  435:  * cli_telnet_SetCmd() - Telnet set command
  436:  *
  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: */
  445: int
  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: /*
  490:  * cli_telnet_Answer() - Automatic generate commands answer to send from telnet
  491:  *
  492:  * @caps = Array of capability options
  493:  * @nCaps = number of capability options
  494:  * @attr = input attribute
  495:  * @nAttr = number of input attributes
  496:  * @ans = output answered attributes, must be e_free() after use
  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)++;
  532: 			*ans = e_realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
  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>