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

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

unix.superglobalmegacorp.com

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