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