This source file includes following definitions.
- alloc_buffer
- accessWriteCallback
- accessReadCallback
- accessReadStart
- accessStartWriteActual
- accessStartWrite
- accessPutCharForce
- accessPutStrForce
- accessStartWriteStr
- accessLogon
- accessCloseCallback
- accessCloseConnection
- accessProcessInput
- accessTelnetReadCallback
- evHandler
- accessTelnetConnect
- onNewAccess
- uv_open_access
- accessPutChar
- accessGetChar
- accessPutStr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 #include <uv.h>
19 #include <ctype.h>
20 #include <signal.h>
21 #include "dps8.h"
22 #include "dps8_sys.h"
23 #include "dps8_cpu.h"
24 #include "dps8_utils.h"
25 #include "libtelnet.h"
26 #include "uvutil.h"
27
28 #if defined(FREE)
29 # undef FREE
30 #endif
31 #define FREE(p) do \
32 { \
33 free((p)); \
34 (p) = NULL; \
35 } while(0)
36
37 #define USE_REQ_DATA
38
39 static void accessTelnetReadCallback (uv_tcp_t * client,
40 ssize_t nread,
41 unsigned char * buf);
42
43
44
45
46
47 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
48 uv_buf_t * buf)
49 {
50
51 #if !defined(__clang_analyzer__)
52 * buf = uv_buf_init ((char *) malloc (suggested_size),
53 (uint) suggested_size);
54 #endif
55 }
56
57 static void accessWriteCallback (uv_write_t * req, int status)
58 {
59 if (status < 0)
60 {
61 if (status == -ECONNRESET || status == -ECANCELED ||
62 status == -EPIPE)
63 {
64
65 }
66 else
67 {
68 sim_warn ("accessWriteCallback status %d (%s)\n", -status,
69 xstrerror_l(-status));
70 }
71
72
73 accessCloseConnection (req->handle);
74 }
75
76 #if defined(USE_REQ_DATA)
77 FREE (req->data);
78 #else
79 unsigned int nbufs = req->nbufs;
80 uv_buf_t * bufs = req->bufs;
81 for (unsigned int i = 0; i < nbufs; i ++)
82 {
83 if (bufs && bufs[i].base)
84 {
85 FREE (bufs[i].base);
86
87 }
88 if (req->bufsml[i].base)
89 {
90 FREE (req->bufsml[i].base);
91 }
92 }
93 #endif
94
95
96
97 FREE (req);
98 }
99
100
101
102
103
104
105
106 static void accessReadCallback (uv_stream_t* stream,
107 ssize_t nread,
108 const uv_buf_t* buf)
109 {
110 uv_access * access = (uv_access *) stream->data;
111 if (nread < 0)
112 {
113
114 {
115 accessCloseConnection (stream);
116 }
117 }
118 else if (nread > 0)
119 {
120 if (access->telnetp)
121 {
122 telnet_recv (access->telnetp, buf->base, (size_t) nread);
123 }
124 else
125 {
126 accessTelnetReadCallback ((uv_tcp_t *) stream,
127 (ssize_t) nread,
128 (unsigned char *) buf->base);
129 }
130 }
131 if (buf->base)
132 free (buf->base);
133 }
134
135
136
137
138
139 static void accessReadStart (uv_tcp_t * client)
140 {
141 if (! client || uv_is_closing ((uv_handle_t *) client))
142 return;
143 uv_read_start ((uv_stream_t *) client, alloc_buffer, accessReadCallback);
144 }
145
146
147
148 static void accessStartWriteActual (uv_tcp_t * client, char * data,
149 ssize_t datalen)
150 {
151 if (! client || uv_is_closing ((uv_handle_t *) client))
152 return;
153
154 #if !defined(__clang_analyzer__)
155 uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
156 if (!req)
157 {
158 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
159 __func__, __FILE__, __LINE__);
160 # if defined(USE_BACKTRACE)
161 # if defined(SIGUSR2)
162 (void)raise(SIGUSR2);
163
164 # endif
165 # endif
166 abort();
167 }
168
169 (void)memset (req, 0, sizeof (uv_write_t));
170 uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen),
171 (uint) datalen);
172 # if defined(USE_REQ_DATA)
173 req->data = buf.base;
174 # endif
175 memcpy (buf.base, data, (unsigned long) datalen);
176
177 int ret = uv_write (req, (uv_stream_t *) client, & buf, 1,
178 accessWriteCallback);
179
180
181
182
183 if (ret < 0 && ret != -EBADF)
184 sim_printf ("uv_write returns %d\n", ret);
185 #endif
186 }
187
188 void accessStartWrite (uv_tcp_t * client, char * data, ssize_t datalen)
189 {
190 if ((client == NULL) || uv_is_closing ((uv_handle_t *) client))
191 return;
192 uv_access * access = (uv_access *) client->data;
193 if (access->telnetp)
194 telnet_send (access->telnetp, data, (size_t) datalen);
195 else
196 accessStartWriteActual (client, data, (ssize_t) datalen);
197 }
198
199 static void accessPutCharForce (uv_access * access, char ch)
200 {
201
202 accessStartWrite (access->client, & ch, 1);
203 }
204
205 static void accessPutStrForce (uv_access * access, char * str)
206 {
207 size_t l = strlen (str);
208 accessStartWrite (access->client, str, (ssize_t) l);
209 }
210
211 void accessStartWriteStr (uv_tcp_t * client, char * data)
212 {
213 accessStartWrite (client, data, (ssize_t) strlen (data));
214 }
215
216 static void accessLogon (uv_access * access, unsigned char * buf, ssize_t nread)
217 {
218 for (ssize_t nchar = 0; nchar < nread; nchar ++)
219 {
220 unsigned char kar = buf[nchar];
221
222 if ((unsigned long) access->pwPos >= sizeof (access->pwBuffer) - 1)
223 {
224
225 switch (kar)
226 {
227 case '\b':
228 case 127:
229 {
230
231 accessPutStrForce (access, "\b \b");
232 access->pwBuffer[access->pwPos] = 0;
233 if (access->pwPos > 0)
234 access->pwPos -= 1;
235 }
236 break;
237
238 case '\n':
239 case '\r':
240 {
241 access->pwBuffer[access->pwPos] = 0;
242 goto check;
243 }
244
245 case 0x12:
246 {
247 accessPutStrForce (access, "^R\r\n");
248 access->connectPrompt (access->client);
249 accessPutStrForce (access, access->pwBuffer);
250 }
251 break;
252
253 default:
254 break;
255 }
256 continue;
257 }
258
259 if (isprint (kar))
260 {
261 accessPutCharForce (access, '*');
262 access->pwBuffer[access->pwPos++] = (char) kar;
263 access->pwBuffer[access->pwPos] = 0;
264 }
265 else
266 {
267 switch (kar)
268 {
269 case '\b':
270 case 127:
271 {
272
273 accessPutStrForce (access, "\b \b");
274
275 access->pwBuffer[access->pwPos] = 0;
276 if (access->pwPos > 0)
277 access->pwPos -= 1;
278 }
279 break;
280
281 case '\n':
282 case '\r':
283 {
284 access->pwBuffer[access->pwPos] = 0;
285 goto check;
286 }
287
288 case 0x12:
289 {
290 accessPutStrForce (access, "^R\r\n");
291 access->connectPrompt (access->client);
292 accessPutStrForce (access, access->pwBuffer);
293 }
294 break;
295
296 default:
297 break;
298 }
299 }
300 }
301 return;
302
303 check:;
304 char cpy[access->pwPos + 1];
305 memcpy (cpy, access->pwBuffer, (unsigned long) access->pwPos);
306 cpy[access->pwPos] = 0;
307 trim (cpy);
308
309 access->pwPos = 0;
310 accessPutStrForce (access, "\r\n");
311
312 if (strcmp (cpy, access->pw) == 0)
313 {
314 accessPutStrForce (access, "ok\r\n");
315 sim_printf ("\r[OPC emulation: ACCESS GRANTED]\r\n");
316 goto associate;
317 }
318 else
319 {
320
321
322
323 accessPutStrForce (access, "nope\r\n");
324 sim_printf ("\r[OPC emulation: INVALID PASSWORD]\r\n");
325 goto reprompt;
326 }
327
328 reprompt:;
329 access->connectPrompt (access->client);
330 return;
331
332 associate:;
333 access->loggedOn = true;
334 if (access->connected)
335 access->connected (access->client);
336 }
337
338 static void accessCloseCallback (uv_handle_t * stream)
339 {
340 FREE (stream);
341
342 }
343
344 void accessCloseConnection (uv_stream_t* stream)
345 {
346 uv_access * access = (uv_access *) stream->data;
347 sim_printf ("\r[OPC emulation: DISCONNECT]\r\n");
348
349 if (access->telnetp)
350 {
351 telnet_free (access->telnetp);
352 access->telnetp = NULL;
353 }
354 if (! uv_is_closing ((uv_handle_t *) stream))
355 uv_close ((uv_handle_t *) stream, accessCloseCallback);
356 access->client = NULL;
357 }
358
359 static void accessProcessInput (uv_access * access, unsigned char * buf,
360 ssize_t nread)
361 {
362 if (access->inBuffer)
363 {
364
365 unsigned char * new =
366 realloc (access->inBuffer,
367 (unsigned long) (access->inSize + nread));
368 if (! new)
369 {
370 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
371 __func__, __FILE__, __LINE__);
372 #if defined(USE_BACKTRACE)
373 # if defined(SIGUSR2)
374 (void)raise(SIGUSR2);
375
376 # endif
377 #endif
378 abort();
379 }
380 memcpy (new + access->inSize, buf, (unsigned long) nread);
381 access->inSize += nread;
382 access->inBuffer = new;
383 }
384 else
385 {
386 access->inBuffer = malloc ((unsigned long) nread);
387 if (! access->inBuffer)
388 {
389 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
390 __func__, __FILE__, __LINE__);
391 #if defined(USE_BACKTRACE)
392 # if defined(SIGUSR2)
393 (void)raise(SIGUSR2);
394
395 # endif
396 #endif
397 abort();
398 }
399 memcpy (access->inBuffer, buf, (unsigned long) nread);
400 access->inSize = (uint) nread;
401 access->inUsed = 0;
402 }
403
404
405
406
407
408
409 }
410
411 static void accessTelnetReadCallback (uv_tcp_t * client,
412 ssize_t nread,
413 unsigned char * buf)
414 {
415 uv_access * access = (uv_access *) client->data;
416 if (access->loggedOn)
417 accessProcessInput (access, buf, nread);
418 else
419 accessLogon (access, buf, nread);
420 }
421
422 static void evHandler (UNUSED telnet_t *telnet, telnet_event_t *event,
423 void *user_data)
424 {
425 uv_tcp_t * client = (uv_tcp_t *) user_data;
426
427 switch (event->type)
428 {
429 case TELNET_EV_DATA:
430 {
431 accessTelnetReadCallback (client, (ssize_t) event->data.size,
432 (unsigned char *)event->data.buffer);
433 }
434 break;
435
436 case TELNET_EV_SEND:
437 {
438 accessStartWriteActual (client, (char *) event->data.buffer,
439 (ssize_t) event->data.size);
440 }
441 break;
442
443 case TELNET_EV_DO:
444 {
445 if (event->neg.telopt == TELNET_TELOPT_BINARY)
446 {
447
448 }
449 else if (event->neg.telopt == TELNET_TELOPT_SGA)
450 {
451
452 }
453 else if (event->neg.telopt == TELNET_TELOPT_ECHO)
454 {
455
456 }
457 else
458 {
459 sim_printf ("evHandler DO %d\n", event->neg.telopt);
460 }
461 }
462 break;
463
464 case TELNET_EV_DONT:
465 {
466 sim_printf ("evHandler DONT %d\n", event->neg.telopt);
467 }
468 break;
469
470 case TELNET_EV_WILL:
471 {
472 if (event->neg.telopt != 3)
473 sim_printf ("evHandler WILL %d\n", event->neg.telopt);
474 }
475 break;
476
477 case TELNET_EV_WONT:
478 {
479 sim_printf ("evHandler WONT %d\n", event->neg.telopt);
480 }
481 break;
482
483 case TELNET_EV_ERROR:
484 {
485 sim_warn ("libtelnet evHandler error <%s>\n", event->error.msg);
486 }
487 break;
488
489 case TELNET_EV_IAC:
490 {
491 if (event->iac.cmd == 243 ||
492 event->iac.cmd == 244)
493 {
494 sim_warn ("libtelnet dropping unassociated BRK/IP\n");
495 }
496 else
497 if ((!sim_quiet) || (event->iac.cmd != 241))
498 sim_warn ("libtelnet unhandled IAC event %d\n", event->iac.cmd);
499 }
500 break;
501
502 default:
503 sim_printf ("evHandler: unhandled event %d\n", event->type);
504 break;
505 }
506
507 }
508
509 static const telnet_telopt_t my_telopts[] =
510 {
511 { TELNET_TELOPT_SGA, TELNET_WILL, TELNET_DO },
512 { TELNET_TELOPT_ECHO, TELNET_WILL, TELNET_DONT },
513
514
515 { TELNET_TELOPT_BINARY, TELNET_WONT, TELNET_DONT },
516
517 { -1, 0, 0 }
518 };
519
520 static void * accessTelnetConnect (uv_tcp_t * client)
521 {
522 void * p = (void *) telnet_init (my_telopts, evHandler, 0, client);
523 if (!p)
524 {
525 (void)fprintf(stderr, "\rtelnet_init failed at %s[%s:%d]\r\n",
526 __func__, __FILE__, __LINE__);
527 return NULL;
528 }
529 const telnet_telopt_t * q = my_telopts;
530 while (q->telopt != -1)
531 {
532 telnet_negotiate (p, q->us, (unsigned char) q->telopt);
533 q ++;
534 }
535 return p;
536 }
537
538
539
540
541
542 static void onNewAccess (uv_stream_t * server, int status)
543 {
544 if (status < 0)
545 {
546 (void)fprintf (stderr, "\r[OPC emulation: new connection error %s]\r\n", uv_strerror(status));
547
548 return;
549 }
550
551 uv_access * access = (uv_access *) server->data;
552
553 uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
554 if (!client)
555 {
556 (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
557 __func__, __FILE__, __LINE__);
558 #if defined(USE_BACKTRACE)
559 # if defined(SIGUSR2)
560 (void)raise(SIGUSR2);
561
562 # endif
563 #endif
564 abort();
565 }
566 uv_tcp_init (access->loop, client);
567 client->data = server->data;
568 if (uv_accept (server, (uv_stream_t *) client) == 0)
569 {
570
571 if (access->client)
572 {
573 sim_printf ("\r\n[OPC emulation: BUMPED]\r\n\r\n");
574 accessPutStrForce (access, "\r[OPC emulation: BUMPED]\r\n");
575 access->loggedOn = false;
576 sim_printf ("\rMultics has disconnected you\r\n");
577 accessCloseConnection ((uv_stream_t *) access->client);
578 }
579 access->client = client;
580 uv_tcp_nodelay (client, 1);
581 struct sockaddr name;
582 int namelen = sizeof (name);
583 int ret = uv_tcp_getpeername (access->client, & name, & namelen);
584 if (ret < 0)
585 {
586 sim_printf ("\r[OPC emulation: CONNECT; addr err %d]\r\n", ret);
587 }
588 else
589 {
590 struct sockaddr_in * p = (struct sockaddr_in *) & name;
591 sim_printf ("\r[OPC emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
592 }
593
594 if (access->useTelnet)
595 {
596 access->telnetp = accessTelnetConnect (access->client);
597 if (!access->telnetp)
598 {
599 sim_warn ("ltnConnect failed\n");
600 return;
601 }
602 }
603 else
604 {
605 access->telnetp = NULL;
606 }
607 access->loggedOn =
608 ! strlen (access->pw);
609 if (access->loggedOn)
610 access->connected (access->client);
611 else
612 access->connectPrompt (access->client);
613 accessReadStart (access->client);
614 }
615 else
616 {
617 uv_close ((uv_handle_t *) client, accessCloseCallback);
618 }
619 }
620
621 void uv_open_access (uv_access * access)
622 {
623 if (access->open == true)
624 {
625 sim_printf ("\r[OPC emulation: OPC already initialized]\r\n");
626 }
627
628 if (! access->port)
629 {
630
631 return;
632 }
633
634 if (! access->loop)
635 access->loop = uv_default_loop ();
636
637
638 if (access->open)
639 return;
640
641 uv_tcp_init (access->loop, & access->server);
642 access->server.data = (void *) access;
643 struct sockaddr_in addr;
644 uv_ip4_addr (access->address, access->port, & addr);
645 uv_tcp_bind (& access->server, (const struct sockaddr *) & addr, 0);
646 #define DEFAULT_BACKLOG 1024
647 int r = uv_listen ((uv_stream_t *) & access->server,
648 DEFAULT_BACKLOG,
649 onNewAccess);
650 if (r)
651 {
652 (void)fprintf (stderr, "\r[OPC emulation: listen error: %s:%ld: %s]\r\n",
653 access->address, (long) access->port, uv_strerror(r));
654 }
655 access->open = true;
656 if (access->address != NULL)
657 sim_printf ("\r[OPC emulation: TELNET server listening on %s:%ld]\r\n",
658 access->address, (long) access->port);
659 else
660 sim_printf ("\r[OPC emulation: TELNET server listening on 0.0.0.0:%ld]\r\n",
661 (long) access->port);
662 }
663
664 #if !defined(QUIET_UNUSED)
665 void accessPutChar (uv_access * access, char ch)
666 {
667
668 if (access->loggedOn)
669 accessStartWrite (access->client, & ch, 1);
670 }
671 #endif
672
673 int accessGetChar (uv_access * access)
674 {
675
676 if (! access->client)
677 {
678 if (access->inBuffer)
679 FREE (access->inBuffer);
680 access->inBuffer = NULL;
681 access->inSize = 0;
682 access->inUsed = 0;
683 return SCPE_OK;
684 }
685
686 if (access->inBuffer && access->inUsed < access->inSize)
687 {
688 unsigned char c = access->inBuffer[access->inUsed ++];
689 if (access->inUsed >= access->inSize)
690 {
691 FREE (access->inBuffer);
692 access->inBuffer = NULL;
693 access->inSize = 0;
694 access->inUsed = 0;
695
696
697
698 }
699 return (int) c + SCPE_KFLAG;
700 }
701 return SCPE_OK;
702
703 }
704
705 #if !defined(QUIET_UNUSED)
706 void accessPutStr (uv_access * access, char * str)
707 {
708 size_t l = strlen (str);
709
710
711 if (access->loggedOn)
712 accessStartWrite (access->client, str, (ssize_t) l);
713 }
714 #endif