Annotation of nono/host/comdriver_tcp.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2021 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // シリアルポートの TCP ドライバ
        !             9: //
        !            10: 
        !            11: #include "comdriver_tcp.h"
        !            12: #include "config.h"
        !            13: #include "hostcom.h"
        !            14: #include <sys/socket.h>
        !            15: #include <netdb.h>
        !            16: #include <arpa/telnet.h>
        !            17: 
        !            18: // コンストラクタ
        !            19: COMDriverTCP::COMDriverTCP(HostDevice *hostdev_)
        !            20:        : inherited(hostdev_, "TCP")
        !            21: {
        !            22: }
        !            23: 
        !            24: // デストラクタ
        !            25: COMDriverTCP::~COMDriverTCP()
        !            26: {
        !            27: }
        !            28: 
        !            29: // ドライバ初期化
        !            30: bool
        !            31: COMDriverTCP::InitDriver()
        !            32: {
        !            33:        struct addrinfo hints;
        !            34:        struct addrinfo *res, *ai;
        !            35:        int r;
        !            36: 
        !            37:        // host, port を取得
        !            38:        const char *host = "::1";
        !            39:        std::string key = hostdev->GetConfigKey();
        !            40:        const ConfigItem& item = gConfig->Find(key + "-tcp-port");
        !            41:        std::string port = item.AsString();
        !            42:        int portnum = atoi(port.c_str());
        !            43:        if (portnum < 1 || portnum > 65535) {
        !            44:                item.Err();
        !            45:                return false;
        !            46:        }
        !            47: 
        !            48:        // hints を用意
        !            49:        memset(&hints, 0, sizeof(hints));
        !            50:        hints.ai_family = AF_UNSPEC;
        !            51:        hints.ai_socktype = SOCK_STREAM;
        !            52:        hints.ai_flags = AI_PASSIVE;
        !            53: 
        !            54:        r = getaddrinfo(host, port.c_str(), &hints, &res);
        !            55:        if (r != 0) {
        !            56:                const char *errmsg;
        !            57:                if (r == EAI_SYSTEM) {
        !            58:                        errmsg = strerror(errno);
        !            59:                } else {
        !            60:                        errmsg = gai_strerror(r);
        !            61:                }
        !            62:                putmsg(0, "getaddrinfo: %s: %s", host, errmsg);
        !            63:                return false;
        !            64:        }
        !            65:        if (res == NULL) {
        !            66:                putmsg(0, "getaddrinfo: %s: no address found", host);
        !            67:                return false;
        !            68:        }
        !            69: 
        !            70:        // res を順番に試して成功したらそれで待ち受ける
        !            71:        ls = -1;
        !            72:        for (ai = res; ai && ls == -1; ai = ai->ai_next) {
        !            73:                int on = 1;
        !            74: 
        !            75:                ls = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        !            76:                if (ls == -1) {
        !            77:                        putmsg(0, "socket(family=%d): %s", (int)ai->ai_family,
        !            78:                                strerror(errno));
        !            79:                        continue;
        !            80:                }
        !            81: 
        !            82:                if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
        !            83:                        putmsg(0, "setsockopt(SO_REUSEADDR): %s", strerror(errno));
        !            84:                        ls.Close();
        !            85:                        continue;
        !            86:                }
        !            87: 
        !            88:                if (setsockopt(ls, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) {
        !            89:                        putmsg(0, "setsockopt(SO_REUSEPORT): %s", strerror(errno));
        !            90:                        ls.Close();
        !            91:                        continue;
        !            92:                }
        !            93: 
        !            94:                if (bind(ls, ai->ai_addr, ai->ai_addrlen) == -1) {
        !            95:                        putmsg(0, "bind: %s", strerror(errno));
        !            96:                        ls.Close();
        !            97:                        continue;
        !            98:                }
        !            99: 
        !           100:                if (listen(ls, 0) == -1) {
        !           101:                        putmsg(0, "listen: %s", strerror(errno));
        !           102:                        ls.Close();
        !           103:                        continue;
        !           104:                }
        !           105: 
        !           106:                // ここまで来たら成功
        !           107:                break;
        !           108:        }
        !           109: 
        !           110:        freeaddrinfo(res);
        !           111: 
        !           112:        if (ls == -1) {
        !           113:                return false;
        !           114:        }
        !           115:        if (hostdev->AddListen(ls, EV_ADD) < 0) {
        !           116:                putmsg(0, "AddListen(ADD): %s", strerror(errno));
        !           117:                return false;
        !           118:        }
        !           119: 
        !           120:        return true;
        !           121: }
        !           122: 
        !           123: // Driver の Dispatch() は udata を返す。
        !           124: int
        !           125: COMDriverTCP::Dispatch(int udata)
        !           126: {
        !           127:        if (udata == HostDevice::LISTEN_SOCKET) {
        !           128:                if (Accept()) {
        !           129:                        // 呼び出し側に戻って通知を出したいので LISTEN_SOCKET のまま帰る
        !           130:                        return udata;
        !           131:                } else {
        !           132:                        // 失敗時は何もしない
        !           133:                        return HostDevice::DONE;
        !           134:                }
        !           135:        }
        !           136: 
        !           137:        return udata;
        !           138: }
        !           139: 
        !           140: // 待ち受けソケットに着信した。
        !           141: // 成功すれば true を返す。
        !           142: bool
        !           143: COMDriverTCP::Accept()
        !           144: {
        !           145:        struct sockaddr addr;
        !           146:        socklen_t addrlen;
        !           147: 
        !           148:        for (;;) {
        !           149:                addrlen = sizeof(addr);
        !           150:                sock = accept(ls, &addr, &addrlen);
        !           151:                if (sock < 0) {
        !           152:                        if (errno == EINTR) {
        !           153:                                continue;
        !           154:                        }
        !           155:                        // XXX どうする?
        !           156:                        putmsg(0, "accept: %s", strerror(errno));
        !           157:                        return false;
        !           158:                }
        !           159:                break;
        !           160:        }
        !           161: 
        !           162:        // 待ち受けソケットのほうは一旦通知オフにする?
        !           163:        if (hostdev->AddListen(ls, EV_DISABLE) < 0) {
        !           164:                putmsg(0, "AddListen(DISABLE): %s", strerror(errno));
        !           165:                return false;
        !           166:        }
        !           167: 
        !           168:        putmsg(1, "Accept a connection");
        !           169: 
        !           170:        // エコーオフ
        !           171:        WriteCmd({ WILL, TELOPT_ECHO });
        !           172:        negotiation++;
        !           173: 
        !           174:        // 全二重を使用
        !           175:        WriteCmd({ WILL, TELOPT_SGA });
        !           176:        negotiation++;
        !           177: 
        !           178:        // コマンド送信中にエラーが起きたら終了
        !           179:        if (sock.Valid() == false) {
        !           180:                return false;
        !           181:        }
        !           182: 
        !           183:        // 新しいソケットを kevent に登録
        !           184:        if (hostdev->AddOuter(sock) < 0) {
        !           185:                putmsg(0, "AddOuter: %s", strerror(errno));
        !           186:                Close();
        !           187:                return false;
        !           188:        }
        !           189: 
        !           190:        return true;
        !           191: }
        !           192: 
        !           193: // 外部への1バイト送信
        !           194: void
        !           195: COMDriverTCP::Write(uint32 data)
        !           196: {
        !           197:        uint8 buf[1];
        !           198:        int n;
        !           199: 
        !           200:        if (sock.Valid()) {
        !           201:                buf[0] = data;
        !           202: 
        !           203:                n = write(sock, buf, sizeof(buf));
        !           204:                if (n < 0) {
        !           205:                        putmsg(0, "write: %s", strerror(errno));
        !           206:                        // エラーならソケットをクローズ
        !           207:                        Close();
        !           208:                        return;
        !           209:                }
        !           210:                if (n < sizeof(buf)) {
        !           211:                        putmsg(0, "write: short");
        !           212:                        return;
        !           213:                }
        !           214:        }
        !           215: }
        !           216: 
        !           217: // TELNET コマンドを送信。
        !           218: // cmd には IAC を除いたデータ列を指定する。
        !           219: void
        !           220: COMDriverTCP::WriteCmd(const std::vector<uint8>& cmd)
        !           221: {
        !           222:        if (loglevel >= 1) {
        !           223:                std::string str;
        !           224:                for (auto data : cmd) {
        !           225:                        str += ' ';
        !           226:                        str += GetCommandName(data);
        !           227:                }
        !           228:                putmsgn("Send %s", str.c_str() + 1);
        !           229:        }
        !           230: 
        !           231:        Write(IAC);
        !           232:        for (auto data : cmd) {
        !           233:                Write(data);
        !           234:        }
        !           235: }
        !           236: 
        !           237: // 外部からの1バイト受信
        !           238: int
        !           239: COMDriverTCP::Read()
        !           240: {
        !           241:        uint8 buf[1];
        !           242:        int r;
        !           243: 
        !           244:        r = read(sock, buf, sizeof(buf));
        !           245:        if (r < 0) {
        !           246:                putmsg(0, "read: %s", strerror(errno));
        !           247:                Close();
        !           248:                return NODATA;
        !           249:        }
        !           250:        if (r == 0) {
        !           251:                Close();
        !           252:                return NODATA;
        !           253:        }
        !           254:        uint8 data = buf[0];
        !           255: 
        !           256:        if (__predict_false(recvcmd.empty() == false || data == IAC)) {
        !           257:                // TELNET コマンド
        !           258:                return Command(data);
        !           259:        } else {
        !           260:                // 平文
        !           261:                return data;
        !           262:        }
        !           263: }
        !           264: 
        !           265: // データソケットをクローズして、待ち受けソケットの通知を再開する。
        !           266: void
        !           267: COMDriverTCP::Close()
        !           268: {
        !           269:        putmsg(1, "Close the connection");
        !           270: 
        !           271:        // データソケットをクローズ
        !           272:        if (sock.Valid()) {
        !           273:                hostdev->DelOuter(sock);
        !           274:                sock.Close();
        !           275:        }
        !           276: 
        !           277:        // 待ち受けソケットを再開
        !           278:        if (hostdev->AddListen(ls, EV_ENABLE) < 0) {
        !           279:                putmsg(0, "AddListen(ENABLE): %s", strerror(errno));
        !           280:                return;
        !           281:        }
        !           282: }
        !           283: 
        !           284: // TELNET コマンド受信。
        !           285: // VM 側に文字を渡す場合は 0..255 を返す。
        !           286: // VM に文字を渡さない場合は NODATA を返す。
        !           287: int
        !           288: COMDriverTCP::Command(uint8 code)
        !           289: {
        !           290:        recvcmd.push_back(code);
        !           291:        putmsg(2, "Read cmd $%02x %s", code, GetCommandName(code).c_str());
        !           292: 
        !           293:        if (cmdstate == WILL) {
        !           294:                // WILL: 送信側がこのオプション使用を宣言
        !           295:                // DONT: 送信側がこのオプション無効を宣言
        !           296:                // (今の所動作が同じなので同じ state を使う)
        !           297: 
        !           298:                if (negotiation > 0) {
        !           299:                        // 送ったコマンドに対する応答を受信したのなら、何もしない
        !           300:                        putmsg(1, "Recv response %s %s",
        !           301:                                GetCommandName(recvcmd[1]).c_str(),
        !           302:                                GetCommandName(recvcmd[2]).c_str());
        !           303:                        negotiation--;
        !           304:                } else {
        !           305:                        // 向こう発のコマンドを受信したのなら、応答する
        !           306:                        putmsg(1, "Recv command %s %s",
        !           307:                                GetCommandName(recvcmd[1]).c_str(),
        !           308:                                GetCommandName(recvcmd[2]).c_str());
        !           309: 
        !           310:                        // どのコマンドもサポートしていないので、
        !           311:                        // WILL に対しては DONT で無視。
        !           312:                        // DONT に対しては DONT で応答。
        !           313:                        WriteCmd({ DONT, recvcmd[2] });
        !           314:                }
        !           315: 
        !           316:                recvcmd.clear();
        !           317:                cmdstate = 0;
        !           318: 
        !           319:        } else if (cmdstate == DO) {
        !           320:                // DO:   送信側がこちらにこのオプション使用を要求
        !           321:                // WONT: 送信側がこちらにこのオプション無効を要求
        !           322:                // (今の所動作が同じなので同じ state を使う)
        !           323: 
        !           324:                if (negotiation > 0) {
        !           325:                        // 送ったコマンドに対する応答を受信したのなら、何もしない
        !           326:                        putmsg(1, "Recv response %s %s",
        !           327:                                GetCommandName(recvcmd[1]).c_str(),
        !           328:                                GetCommandName(recvcmd[2]).c_str());
        !           329:                        negotiation--;
        !           330:                } else {
        !           331:                        // 向こう発のコマンドを受信したのなら、応答する
        !           332:                        putmsg(1, "Recv command %s %s",
        !           333:                                GetCommandName(recvcmd[1]).c_str(),
        !           334:                                GetCommandName(recvcmd[2]).c_str());
        !           335: 
        !           336:                        // DO   に対しては WONT で拒否。
        !           337:                        // WONT に対しては WONT で応答。
        !           338:                        WriteCmd({ WONT, recvcmd[2] });
        !           339:                }
        !           340: 
        !           341:                recvcmd.clear();
        !           342:                cmdstate = 0;
        !           343: 
        !           344:        } else if (cmdstate == SB) {
        !           345:                // IAC, SE で終端
        !           346:                if (recvcmd[recvcmd.size() - 2] == IAC &&
        !           347:                    recvcmd[recvcmd.size() - 1] == SE    )
        !           348:                {
        !           349:                        // 未知のシーケンスを読み飛ばすだけで、何もしない
        !           350:                        recvcmd.clear();
        !           351:                        cmdstate = 0;
        !           352:                }
        !           353: 
        !           354:        } else if (recvcmd.size() < 2) {
        !           355:                // 1バイト目は IAC のはず
        !           356: 
        !           357:        } else if (recvcmd.size() == 2) {
        !           358:                // 2バイト目がコマンド
        !           359:                cmdstate = 0;
        !           360:                switch (recvcmd[1]) {
        !           361:                 case SE:       // SE はここに来ないはずだが、来てもここで終了でいい。
        !           362:                 case NOP:      // NOP はたぶん何もしない。
        !           363:                        FALLTHROUGH;
        !           364:                 case DM:
        !           365:                        // XXX 未確認
        !           366:                        FALLTHROUGH;
        !           367:                 case BREAK:
        !           368:                        // XXX Break 文字を送る
        !           369:                        FALLTHROUGH;
        !           370:                 case IP:
        !           371:                 case AO:
        !           372:                 case AYT:
        !           373:                 case EC:
        !           374:                 case EL:
        !           375:                 case GA:
        !           376:                        // XXX 未実装、たぶん引数はない
        !           377:                        putmsg(1, "Recv command %s", GetCommandName(recvcmd[1]).c_str());
        !           378:                        recvcmd.clear();
        !           379:                        break;
        !           380: 
        !           381:                 case SB:
        !           382:                        // この後 SE までがサブネゴシエーション
        !           383:                        putmsg(1, "Recv command %s", GetCommandName(recvcmd[1]).c_str());
        !           384:                        cmdstate = SB;
        !           385:                        break;
        !           386: 
        !           387:                 case WILL:
        !           388:                 case DONT:
        !           389:                        // 次の1文字がオプション
        !           390:                        cmdstate = WILL;
        !           391:                        break;
        !           392:                 case DO:
        !           393:                 case WONT:
        !           394:                        // 次の1文字がオプション
        !           395:                        cmdstate = DO;
        !           396:                        break;
        !           397: 
        !           398:                 default:
        !           399:                        // それ以外なら、その文字
        !           400:                        recvcmd.clear();
        !           401:                        return code;
        !           402:                }
        !           403:        }
        !           404: 
        !           405:        return NODATA;
        !           406: }
        !           407: 
        !           408: // TELNET のコマンド、オプション名を返す
        !           409: /*static*/ std::string
        !           410: COMDriverTCP::GetCommandName(uint8 code)
        !           411: {
        !           412:        switch (code) {
        !           413:         case 0x00:     return "Transmit-Binary";
        !           414:         case 0x01:     return "Echo";
        !           415:         case 0x03:     return "Suppress-Go-Ahead";
        !           416:         case 0x05:     return "Telnet-Status-Option";
        !           417:         case 0x06:     return "Telnet-Timing-Mark";
        !           418:         case 0x18:     return "Terminal-Type";
        !           419:         case 0x22:     return "Telnet-Line-Mode";
        !           420: 
        !           421:         case 0xf0:     return "SE";
        !           422:         case 0xf1:     return "NOP";
        !           423:         case 0xf2:     return "DataMark";
        !           424:         case 0xf3:     return "Break";
        !           425:         case 0xf4:     return "IP";
        !           426:         case 0xf5:     return "AO";    // Abort Output
        !           427:         case 0xf6:     return "AYT";   // Are You There
        !           428:         case 0xf7:     return "EC";    // Erase Character
        !           429:         case 0xf8:     return "EL";    // Erase Line
        !           430:         case 0xf9:     return "GA";    // Go Ahead
        !           431:         case 0xfa:     return "SB";
        !           432:         case 0xfb:     return "WILL";
        !           433:         case 0xfc:     return "WONT";
        !           434:         case 0xfd:     return "DO";
        !           435:         case 0xfe:     return "DONT";
        !           436:         case 0xff:     return "IAC";   // Interpret As Command
        !           437: 
        !           438:         default:
        !           439:                return string_format("(unknown $%02x)", code);
        !           440:        }
        !           441: }
        !           442: 
        !           443: // モニタ (ドライバ依存情報のみ)
        !           444: void
        !           445: COMDriverTCP::MonitorUpdateMD(TextScreen& screen)
        !           446: {
        !           447:        // 何を表示したもんか
        !           448: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.