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