|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.