Annotation of libaitcli/src/telnet.c, revision 1.3

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     ! misho       6: * $Id: telnet.c,v 1.2.2.2 2012/07/22 22:36:21 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     ! misho      15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
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: /*
                     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)++;
1.3     ! misho     140:                                *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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)++;
1.3     ! misho     170:                                *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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)++;
1.3     ! misho     195:                                        *attr = io_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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);
1.3     ! misho     206:                                io_free(*attr);
1.2       misho     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: 
1.3     ! misho     269:                        buf = io_realloc(buf, pos + len);
1.2       misho     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) {
1.3     ! misho     293:                buf = io_realloc(buf, pos + datLen);
1.2       misho     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) {
1.3     ! misho     306:                buf = io_realloc(buf, pos + 2);
1.2       misho     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)
1.3     ! misho     321:                io_free(buf);
1.2       misho     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
1.3     ! misho     488:  * @ans = output answered attributes, must be io_free() after use
1.2       misho     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)++;
1.3     ! misho     524:                        *ans = io_realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
1.2       misho     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>