xref: /freebsd/crypto/openssl/apps/lib/vms_term_sock.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright 2016 VMS Software, Inc. All Rights Reserved.
4  *
5  * Licensed under the Apache License 2.0 (the "License").  You may not use
6  * this file except in compliance with the License.  You can obtain a copy
7  * in the file LICENSE in the source distribution or at
8  * https://www.openssl.org/source/license.html
9  */
10 
11 #ifdef __VMS
12 #define OPENSSL_SYS_VMS
13 #pragma message disable DOLLARID
14 
15 #include <openssl/opensslconf.h>
16 
17 #if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
18 /*
19  * On VMS, you need to define this to get the declaration of fileno().  The
20  * value 2 is to make sure no function defined in POSIX-2 is left undefined.
21  */
22 #define _POSIX_C_SOURCE 2
23 #endif
24 
25 #include <stdio.h>
26 
27 #undef _POSIX_C_SOURCE
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <inet.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <starlet.h>
37 #include <iodef.h>
38 #ifdef __alpha
39 #include <iosbdef.h>
40 #else
41 typedef struct _iosb { /* Copied from IOSBDEF.H for Alpha  */
42 #pragma __nomember_alignment
43     __union
44     {
45         __struct
46         {
47             unsigned short int iosb$w_status; /* Final I/O status           */
48             __union
49             {
50                 __struct
51                 { /* 16-bit byte count variant        */
52                     unsigned short int iosb$w_bcnt; /* 16-bit byte count    */
53                     __union
54                     {
55                         unsigned int iosb$l_dev_depend; /* 32-bit device dependent info */
56                         unsigned int iosb$l_pid; /* 32-bit pid              */
57                     }
58                     iosb$r_l;
59                 }
60                 iosb$r_bcnt_16;
61                 __struct
62                 { /* 32-bit byte count variant        */
63                     unsigned int iosb$l_bcnt; /* 32-bit byte count (unaligned) */
64                     unsigned short int iosb$w_dev_depend_high; /* 16-bit device dependent info */
65                 }
66                 iosb$r_bcnt_32;
67             }
68             iosb$r_devdepend;
69         }
70         iosb$r_io_64;
71         __struct
72         {
73             __union
74             {
75                 unsigned int iosb$l_getxxi_status; /* Final GETxxI status   */
76                 unsigned int iosb$l_reg_status; /* Final $Registry status   */
77             }
78             iosb$r_l_status;
79             unsigned int iosb$l_reserved; /* Reserved field                 */
80         }
81         iosb$r_get_64;
82     }
83     iosb$r_io_get;
84 } IOSB;
85 
86 #if !defined(__VAXC)
87 #define iosb$w_status iosb$r_io_get.iosb$r_io_64.iosb$w_status
88 #define iosb$w_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$w_bcnt
89 #define iosb$r_l iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$r_l
90 #define iosb$l_dev_depend iosb$r_l.iosb$l_dev_depend
91 #define iosb$l_pid iosb$r_l.iosb$l_pid
92 #define iosb$l_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$l_bcnt
93 #define iosb$w_dev_depend_high iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$w_dev_depend_high
94 #define iosb$l_getxxi_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_getxxi_status
95 #define iosb$l_reg_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_reg_status
96 #endif /* #if !defined(__VAXC) */
97 
98 #endif /* End of IOSBDEF */
99 
100 #include <efndef.h>
101 #include <stdlib.h>
102 #include <ssdef.h>
103 #include <time.h>
104 #include <stdarg.h>
105 #include <descrip.h>
106 
107 #include "vms_term_sock.h"
108 
109 #ifdef __alpha
110 static struct _iosb TerminalDeviceIosb;
111 #else
112 IOSB TerminalDeviceIosb;
113 #endif
114 
115 static char TerminalDeviceBuff[255 + 2];
116 static int TerminalSocketPair[2] = { 0, 0 };
117 static unsigned short TerminalDeviceChan = 0;
118 
119 static int CreateSocketPair(int, int, int, int *);
120 static void SocketPairTimeoutAst(int);
121 static int TerminalDeviceAst(int);
122 static void LogMessage(char *, ...);
123 
124 /*
125 ** Socket Pair Timeout Value (must be 0-59 seconds)
126 */
127 #define SOCKET_PAIR_TIMEOUT_VALUE 20
128 
129 /*
130 ** Socket Pair Timeout Block which is passed to timeout AST
131 */
132 typedef struct _SocketPairTimeoutBlock {
133     unsigned short SockChan1;
134     unsigned short SockChan2;
135 } SPTB;
136 
137 #ifdef TERM_SOCK_TEST
138 
139 /*----------------------------------------------------------------------------*/
140 /*                                                                            */
141 /*----------------------------------------------------------------------------*/
142 int main(int argc, char *argv[], char *envp[])
143 {
144     char TermBuff[80];
145     int TermSock,
146         status,
147         len;
148 
149     LogMessage("Enter 'q' or 'Q' to quit ...");
150     while (OPENSSL_strcasecmp(TermBuff, "Q")) {
151         /*
152         ** Create the terminal socket
153         */
154         status = TerminalSocket(TERM_SOCK_CREATE, &TermSock);
155         if (status != TERM_SOCK_SUCCESS)
156             exit(1);
157 
158         /*
159         ** Process the terminal input
160         */
161         LogMessage("Waiting on terminal I/O ...\n");
162         len = recv(TermSock, TermBuff, sizeof(TermBuff), 0);
163         TermBuff[len] = '\0';
164         LogMessage("Received terminal I/O [%s]", TermBuff);
165 
166         /*
167         ** Delete the terminal socket
168         */
169         status = TerminalSocket(TERM_SOCK_DELETE, &TermSock);
170         if (status != TERM_SOCK_SUCCESS)
171             exit(1);
172     }
173 
174     return 1;
175 }
176 #endif
177 
178 /*----------------------------------------------------------------------------*/
179 /*                                                                            */
180 /*----------------------------------------------------------------------------*/
181 int TerminalSocket(int FunctionCode, int *ReturnSocket)
182 {
183     int status;
184     $DESCRIPTOR(TerminalDeviceDesc, "SYS$COMMAND");
185 
186     /*
187     ** Process the requested function code
188     */
189     switch (FunctionCode) {
190     case TERM_SOCK_CREATE:
191         /*
192         ** Create a socket pair
193         */
194         status = CreateSocketPair(AF_INET, SOCK_STREAM, 0, TerminalSocketPair);
195         if (status == -1) {
196             LogMessage("TerminalSocket: CreateSocketPair () - %08X", status);
197             if (TerminalSocketPair[0])
198                 close(TerminalSocketPair[0]);
199             if (TerminalSocketPair[1])
200                 close(TerminalSocketPair[1]);
201             return TERM_SOCK_FAILURE;
202         }
203 
204         /*
205         ** Assign a channel to the terminal device
206         */
207         status = sys$assign(&TerminalDeviceDesc,
208             &TerminalDeviceChan,
209             0, 0, 0);
210         if (!(status & 1)) {
211             LogMessage("TerminalSocket: SYS$ASSIGN () - %08X", status);
212             close(TerminalSocketPair[0]);
213             close(TerminalSocketPair[1]);
214             return TERM_SOCK_FAILURE;
215         }
216 
217         /*
218         ** Queue an async IO to the terminal device
219         */
220         status = sys$qio(EFN$C_ENF,
221             TerminalDeviceChan,
222             IO$_READVBLK,
223             &TerminalDeviceIosb,
224             TerminalDeviceAst,
225             0,
226             TerminalDeviceBuff,
227             sizeof(TerminalDeviceBuff) - 2,
228             0, 0, 0, 0);
229         if (!(status & 1)) {
230             LogMessage("TerminalSocket: SYS$QIO () - %08X", status);
231             close(TerminalSocketPair[0]);
232             close(TerminalSocketPair[1]);
233             return TERM_SOCK_FAILURE;
234         }
235 
236         /*
237         ** Return the input side of the socket pair
238         */
239         *ReturnSocket = TerminalSocketPair[1];
240         break;
241 
242     case TERM_SOCK_DELETE:
243         /*
244         ** Cancel any pending IO on the terminal channel
245         */
246         status = sys$cancel(TerminalDeviceChan);
247         if (!(status & 1)) {
248             LogMessage("TerminalSocket: SYS$CANCEL () - %08X", status);
249             close(TerminalSocketPair[0]);
250             close(TerminalSocketPair[1]);
251             return TERM_SOCK_FAILURE;
252         }
253 
254         /*
255         ** Deassign the terminal channel
256         */
257         status = sys$dassgn(TerminalDeviceChan);
258         if (!(status & 1)) {
259             LogMessage("TerminalSocket: SYS$DASSGN () - %08X", status);
260             close(TerminalSocketPair[0]);
261             close(TerminalSocketPair[1]);
262             return TERM_SOCK_FAILURE;
263         }
264 
265         /*
266         ** Close the terminal socket pair
267         */
268         close(TerminalSocketPair[0]);
269         close(TerminalSocketPair[1]);
270 
271         /*
272         ** Return the initialized socket
273         */
274         *ReturnSocket = 0;
275         break;
276 
277     default:
278         /*
279         ** Invalid function code
280         */
281         LogMessage("TerminalSocket: Invalid Function Code - %d", FunctionCode);
282         return TERM_SOCK_FAILURE;
283         break;
284     }
285 
286     /*
287     ** Return success
288     */
289     return TERM_SOCK_SUCCESS;
290 }
291 
292 /*----------------------------------------------------------------------------*/
293 /*                                                                            */
294 /*----------------------------------------------------------------------------*/
295 static int CreateSocketPair(int SocketFamily,
296     int SocketType,
297     int SocketProtocol,
298     int *SocketPair)
299 {
300     struct dsc$descriptor AscTimeDesc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
301     static const char *LocalHostAddr = { "127.0.0.1" };
302     unsigned short TcpAcceptChan = 0,
303                    TcpDeviceChan = 0;
304     unsigned long BinTimeBuff[2];
305     struct sockaddr_in sin;
306     char AscTimeBuff[32];
307     short LocalHostPort;
308     int status;
309     unsigned int slen;
310 
311 #ifdef __alpha
312     struct _iosb iosb;
313 #else
314     IOSB iosb;
315 #endif
316 
317     int SockDesc1 = 0,
318         SockDesc2 = 0;
319     SPTB sptb;
320     $DESCRIPTOR(TcpDeviceDesc, "TCPIP$DEVICE");
321 
322     /*
323     ** Create a socket
324     */
325     SockDesc1 = socket(SocketFamily, SocketType, 0);
326     if (SockDesc1 < 0) {
327         LogMessage("CreateSocketPair: socket () - %d", errno);
328         return -1;
329     }
330 
331     /*
332     ** Initialize the socket information
333     */
334     slen = sizeof(sin);
335     memset((char *)&sin, 0, slen);
336     sin.sin_family = SocketFamily;
337     sin.sin_addr.s_addr = inet_addr(LocalHostAddr);
338     sin.sin_port = 0;
339 
340     /*
341     ** Bind the socket to the local IP
342     */
343     status = bind(SockDesc1, (struct sockaddr *)&sin, slen);
344     if (status < 0) {
345         LogMessage("CreateSocketPair: bind () - %d", errno);
346         close(SockDesc1);
347         return -1;
348     }
349 
350     /*
351     ** Get the socket name so we can save the port number
352     */
353     status = getsockname(SockDesc1, (struct sockaddr *)&sin, &slen);
354     if (status < 0) {
355         LogMessage("CreateSocketPair: getsockname () - %d", errno);
356         close(SockDesc1);
357         return -1;
358     } else
359         LocalHostPort = sin.sin_port;
360 
361     /*
362     ** Setup a listen for the socket
363     */
364     listen(SockDesc1, 5);
365 
366     /*
367     ** Get the binary (64-bit) time of the specified timeout value
368     */
369     BIO_snprintf(AscTimeBuff, sizeof(AscTimeBuff), "0 0:0:%02d.00", SOCKET_PAIR_TIMEOUT_VALUE);
370     AscTimeDesc.dsc$w_length = strlen(AscTimeBuff);
371     AscTimeDesc.dsc$a_pointer = AscTimeBuff;
372     status = sys$bintim(&AscTimeDesc, BinTimeBuff);
373     if (!(status & 1)) {
374         LogMessage("CreateSocketPair: SYS$BINTIM () - %08X", status);
375         close(SockDesc1);
376         return -1;
377     }
378 
379     /*
380     ** Assign another channel to the TCP/IP device for the accept.
381     ** This is the channel that ends up being connected to.
382     */
383     status = sys$assign(&TcpDeviceDesc, &TcpDeviceChan, 0, 0, 0);
384     if (!(status & 1)) {
385         LogMessage("CreateSocketPair: SYS$ASSIGN () - %08X", status);
386         close(SockDesc1);
387         return -1;
388     }
389 
390     /*
391     ** Get the channel of the first socket for the accept
392     */
393     TcpAcceptChan = decc$get_sdc(SockDesc1);
394 
395     /*
396     ** Perform the accept using $QIO so we can do this asynchronously
397     */
398     status = sys$qio(EFN$C_ENF,
399         TcpAcceptChan,
400         IO$_ACCESS | IO$M_ACCEPT,
401         &iosb,
402         0, 0, 0, 0, 0,
403         &TcpDeviceChan,
404         0, 0);
405     if (!(status & 1)) {
406         LogMessage("CreateSocketPair: SYS$QIO () - %08X", status);
407         close(SockDesc1);
408         sys$dassgn(TcpDeviceChan);
409         return -1;
410     }
411 
412     /*
413     ** Create the second socket to do the connect
414     */
415     SockDesc2 = socket(SocketFamily, SocketType, 0);
416     if (SockDesc2 < 0) {
417         LogMessage("CreateSocketPair: socket () - %d", errno);
418         sys$cancel(TcpAcceptChan);
419         close(SockDesc1);
420         sys$dassgn(TcpDeviceChan);
421         return (-1);
422     }
423 
424     /*
425     ** Setup the Socket Pair Timeout Block
426     */
427     sptb.SockChan1 = TcpAcceptChan;
428     sptb.SockChan2 = decc$get_sdc(SockDesc2);
429 
430     /*
431     ** Before we block on the connect, set a timer that can cancel I/O on our
432     ** two sockets if it never connects.
433     */
434     status = sys$setimr(EFN$C_ENF,
435         BinTimeBuff,
436         SocketPairTimeoutAst,
437         &sptb,
438         0);
439     if (!(status & 1)) {
440         LogMessage("CreateSocketPair: SYS$SETIMR () - %08X", status);
441         sys$cancel(TcpAcceptChan);
442         close(SockDesc1);
443         close(SockDesc2);
444         sys$dassgn(TcpDeviceChan);
445         return -1;
446     }
447 
448     /*
449     ** Now issue the connect
450     */
451     memset((char *)&sin, 0, sizeof(sin));
452     sin.sin_family = SocketFamily;
453     sin.sin_addr.s_addr = inet_addr(LocalHostAddr);
454     sin.sin_port = LocalHostPort;
455 
456     status = connect(SockDesc2, (struct sockaddr *)&sin, sizeof(sin));
457     if (status < 0) {
458         LogMessage("CreateSocketPair: connect () - %d", errno);
459         sys$cantim(&sptb, 0);
460         sys$cancel(TcpAcceptChan);
461         close(SockDesc1);
462         close(SockDesc2);
463         sys$dassgn(TcpDeviceChan);
464         return -1;
465     }
466 
467     /*
468     ** Wait for the asynch $QIO to finish.  Note that if the I/O was aborted
469     ** (SS$_ABORT), then we probably canceled it from the AST routine - so log
470     ** a timeout.
471     */
472     status = sys$synch(EFN$C_ENF, &iosb);
473     if (!(iosb.iosb$w_status & 1)) {
474         if (iosb.iosb$w_status == SS$_ABORT)
475             LogMessage("CreateSocketPair: SYS$QIO(iosb) timeout");
476         else {
477             LogMessage("CreateSocketPair: SYS$QIO(iosb) - %d",
478                 iosb.iosb$w_status);
479             sys$cantim(&sptb, 0);
480         }
481         close(SockDesc1);
482         close(SockDesc2);
483         sys$dassgn(TcpDeviceChan);
484         return -1;
485     }
486 
487     /*
488     ** Here we're successfully connected, so cancel the timer, convert the
489     ** I/O channel to a socket fd, close the listener socket and return the
490     ** connected pair.
491     */
492     sys$cantim(&sptb, 0);
493 
494     close(SockDesc1);
495     SocketPair[0] = SockDesc2;
496     SocketPair[1] = socket_fd(TcpDeviceChan);
497 
498     return (0);
499 }
500 
501 /*----------------------------------------------------------------------------*/
502 /*                                                                            */
503 /*----------------------------------------------------------------------------*/
504 static void SocketPairTimeoutAst(int astparm)
505 {
506     SPTB *sptb = (SPTB *)astparm;
507 
508     sys$cancel(sptb->SockChan2); /* Cancel the connect() */
509     sys$cancel(sptb->SockChan1); /* Cancel the accept() */
510 
511     return;
512 }
513 
514 /*----------------------------------------------------------------------------*/
515 /*                                                                            */
516 /*----------------------------------------------------------------------------*/
517 static int TerminalDeviceAst(int astparm)
518 {
519     int status;
520 
521     /*
522     ** Terminate the terminal buffer
523     */
524     TerminalDeviceBuff[TerminalDeviceIosb.iosb$w_bcnt] = '\0';
525     strcat(TerminalDeviceBuff, "\n");
526 
527     /*
528     ** Send the data read from the terminal device through the socket pair
529     */
530     send(TerminalSocketPair[0], TerminalDeviceBuff,
531         TerminalDeviceIosb.iosb$w_bcnt + 1, 0);
532 
533     /*
534     ** Queue another async IO to the terminal device
535     */
536     status = sys$qio(EFN$C_ENF,
537         TerminalDeviceChan,
538         IO$_READVBLK,
539         &TerminalDeviceIosb,
540         TerminalDeviceAst,
541         0,
542         TerminalDeviceBuff,
543         sizeof(TerminalDeviceBuff) - 2,
544         0, 0, 0, 0);
545 
546     /*
547     ** Return status
548     */
549     return status;
550 }
551 
552 /*----------------------------------------------------------------------------*/
553 /*                                                                            */
554 /*----------------------------------------------------------------------------*/
555 static void LogMessage(char *msg, ...)
556 {
557     char *Month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
558         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
559     static unsigned int pid = 0;
560     va_list args;
561     time_t CurTime;
562     struct tm *LocTime;
563     char MsgBuff[256];
564 
565     /*
566     ** Get the process pid
567     */
568     if (pid == 0)
569         pid = getpid();
570 
571     /*
572     ** Convert the current time into local time
573     */
574     CurTime = time(NULL);
575     LocTime = localtime(&CurTime);
576 
577     /*
578     ** Format the message buffer
579     */
580     BIO_snprintf(MsgBuff, sizeof(MsgBuff), "%02d-%s-%04d %02d:%02d:%02d [%08X] %s\n",
581         LocTime->tm_mday, Month[LocTime->tm_mon],
582         (LocTime->tm_year + 1900), LocTime->tm_hour, LocTime->tm_min,
583         LocTime->tm_sec, pid, msg);
584 
585     /*
586     ** Get any variable arguments and add them to the print of the message
587     ** buffer
588     */
589     va_start(args, msg);
590     vfprintf(stderr, MsgBuff, args);
591     va_end(args);
592 
593     /*
594     ** Flush standard error output
595     */
596     fsync(fileno(stderr));
597 
598     return;
599 }
600 #endif
601