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>