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