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

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.5     ! misho       6: * $Id: telnet.c,v 1.4.22.2 2017/10/08 23:10:30 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.5     ! misho      15: Copyright 2004 - 2017
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: /*
1.4       misho      54:  * cli_telnetRecv() - Telnet receive commands, negotiate with telnet peer
                     55:  *
1.2       misho      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)++;
1.4       misho     141:                                *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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)++;
1.4       misho     171:                                *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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)++;
1.4       misho     196:                                        *attr = e_realloc(*attr, sizeof(struct telnetAttrs) * *nAttr);
1.2       misho     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);
1.4       misho     207:                                e_free(*attr);
1.2       misho     208:                                *nAttr = 0;
                    209:                                return -1;
                    210:                        }
                    211:                }
                    212:        }
                    213: 
                    214:        return pos;
                    215: }
                    216: 
                    217: #ifndef NDEBUG
                    218: 
                    219: /*
1.4       misho     220:  * cli_telnet_DumpAttrs() - Telnet debug attributes list, if NDEBUG defined not include
                    221:  *
1.2       misho     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: /*
1.4       misho     241:  * cli_telnetSend() - Telnet send commands, negotiate with telnet peer
                    242:  *
1.2       misho     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: 
1.4       misho     272:                        buf = e_realloc(buf, pos + len);
1.2       misho     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) {
1.4       misho     296:                buf = e_realloc(buf, pos + datLen);
1.2       misho     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) {
1.4       misho     309:                buf = e_realloc(buf, pos + 2);
1.2       misho     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)
1.4       misho     324:                e_free(buf);
1.2       misho     325:        return writeLen;
                    326: }
                    327: 
                    328: 
                    329: /*
1.4       misho     330:  * cli_telnet_Get_SubOpt() - Telnet get sub option function
                    331:  *
1.2       misho     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: */
1.4       misho     338: int
1.2       misho     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: /*
1.4       misho     366:  * cli_telnet_Set_SubOpt() - Telnet set sub option function
                    367:  *
1.2       misho     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: */
1.4       misho     375: int
1.2       misho     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: /*
1.4       misho     406:  * cli_telnet_GetCmd() - Telnet get command
                    407:  *
1.2       misho     408:  * @attr = input attribute
                    409:  * return: -1 can`t get command; !=-1 command <<24 return sublen, <<8 return option, <<0 command
                    410: */
1.4       misho     411: u_int
1.2       misho     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: /*
1.4       misho     435:  * cli_telnet_SetCmd() - Telnet set command
                    436:  *
1.2       misho     437:  * @attr = input attribute
                    438:  * @cmd = command
1.5     ! misho     439:  * @optz = option, if 0xff not specified
1.2       misho     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: */
1.4       misho     445: int
1.5     ! misho     446: cli_telnet_SetCmd(struct telnetAttrs *attr, u_char cmd, int optz, ...)
1.2       misho     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
1.5     ! misho     467:                        attr->ta_opt = (u_char) optz;
1.2       misho     468:        }
                    469:        if (SB == attr->ta_cmd) {
1.5     ! misho     470:                va_start(lst, optz);
1.2       misho     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: /*
1.4       misho     490:  * cli_telnet_Answer() - Automatic generate commands answer to send from telnet
                    491:  *
1.2       misho     492:  * @caps = Array of capability options
                    493:  * @nCaps = number of capability options
                    494:  * @attr = input attribute
                    495:  * @nAttr = number of input attributes
1.4       misho     496:  * @ans = output answered attributes, must be e_free() after use
1.2       misho     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)++;
1.4       misho     532:                        *ans = e_realloc(*ans, sizeof(struct telnetAttrs) * *Ans);
1.2       misho     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>