File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / telnet.c
Revision 1.3: download - view: text, annotated - select for diffs - revision graph
Sun Jul 22 22:37:08 2012 UTC (11 years, 11 months ago) by misho
Branches: MAIN
CVS tags: cli3_0, cli2_3, HEAD, CLI2_3, CLI2_2
version 2.2

    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.3 2012/07/22 22:37:08 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
   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 = io_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 = io_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 = io_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: 				io_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 = io_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 = io_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 = io_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: 		io_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 io_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 = io_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>