|
|
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
1.1.1.2 ! root 445: COMDriverTCP::MonitorUpdateMD(TextScreen& screen, int y)
1.1 root 446: {
447: // 何を表示したもんか
448: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.