xref: /freebsd/crypto/openssl/apps/lib/vms_term_sock.c (revision 78e936b2d0b5e6554425009199be31e76bc67c10)
1 /*
2  * Copyright 2016-2026 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 /*----------------------------------------------------------------------------*/
main(int argc,char * argv[],char * envp[])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 /*----------------------------------------------------------------------------*/
TerminalSocket(int FunctionCode,int * ReturnSocket)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             sys$dassgn(TerminalDeviceChan);
234             return TERM_SOCK_FAILURE;
235         }
236 
237         /*
238         ** Return the input side of the socket pair
239         */
240         *ReturnSocket = TerminalSocketPair[1];
241         break;
242 
243     case TERM_SOCK_DELETE:
244         /*
245         ** Cancel any pending IO on the terminal channel
246         */
247         status = sys$cancel(TerminalDeviceChan);
248         if (!(status & 1)) {
249             LogMessage("TerminalSocket: SYS$CANCEL () - %08X", status);
250             close(TerminalSocketPair[0]);
251             close(TerminalSocketPair[1]);
252             sys$dassgn(TerminalDeviceChan);
253             return TERM_SOCK_FAILURE;
254         }
255 
256         /*
257         ** Deassign the terminal channel
258         */
259         status = sys$dassgn(TerminalDeviceChan);
260         if (!(status & 1)) {
261             LogMessage("TerminalSocket: SYS$DASSGN () - %08X", status);
262             close(TerminalSocketPair[0]);
263             close(TerminalSocketPair[1]);
264             return TERM_SOCK_FAILURE;
265         }
266 
267         /*
268         ** Close the terminal socket pair
269         */
270         close(TerminalSocketPair[0]);
271         close(TerminalSocketPair[1]);
272 
273         /*
274         ** Return the initialized socket
275         */
276         *ReturnSocket = 0;
277         break;
278 
279     default:
280         /*
281         ** Invalid function code
282         */
283         LogMessage("TerminalSocket: Invalid Function Code - %d", FunctionCode);
284         return TERM_SOCK_FAILURE;
285         break;
286     }
287 
288     /*
289     ** Return success
290     */
291     return TERM_SOCK_SUCCESS;
292 }
293 
294 /*----------------------------------------------------------------------------*/
295 /*                                                                            */
296 /*----------------------------------------------------------------------------*/
CreateSocketPair(int SocketFamily,int SocketType,int SocketProtocol,int * SocketPair)297 static int CreateSocketPair(int SocketFamily,
298     int SocketType,
299     int SocketProtocol,
300     int *SocketPair)
301 {
302     struct dsc$descriptor AscTimeDesc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
303     static const char *LocalHostAddr = { "127.0.0.1" };
304     unsigned short TcpAcceptChan = 0,
305                    TcpDeviceChan = 0;
306     unsigned long BinTimeBuff[2];
307     struct sockaddr_in sin;
308     char AscTimeBuff[32];
309     short LocalHostPort;
310     int status;
311     unsigned int slen;
312 
313 #ifdef __alpha
314     struct _iosb iosb;
315 #else
316     IOSB iosb;
317 #endif
318 
319     int SockDesc1 = 0,
320         SockDesc2 = 0;
321     SPTB sptb;
322     $DESCRIPTOR(TcpDeviceDesc, "TCPIP$DEVICE");
323 
324     /*
325     ** Create a socket
326     */
327     SockDesc1 = socket(SocketFamily, SocketType, 0);
328     if (SockDesc1 < 0) {
329         LogMessage("CreateSocketPair: socket () - %d", errno);
330         return -1;
331     }
332 
333     /*
334     ** Initialize the socket information
335     */
336     slen = sizeof(sin);
337     memset((char *)&sin, 0, slen);
338     sin.sin_family = SocketFamily;
339     sin.sin_addr.s_addr = inet_addr(LocalHostAddr);
340     sin.sin_port = 0;
341 
342     /*
343     ** Bind the socket to the local IP
344     */
345     status = bind(SockDesc1, (struct sockaddr *)&sin, slen);
346     if (status < 0) {
347         LogMessage("CreateSocketPair: bind () - %d", errno);
348         close(SockDesc1);
349         return -1;
350     }
351 
352     /*
353     ** Get the socket name so we can save the port number
354     */
355     status = getsockname(SockDesc1, (struct sockaddr *)&sin, &slen);
356     if (status < 0) {
357         LogMessage("CreateSocketPair: getsockname () - %d", errno);
358         close(SockDesc1);
359         return -1;
360     } else
361         LocalHostPort = sin.sin_port;
362 
363     /*
364     ** Setup a listen for the socket
365     */
366     listen(SockDesc1, 5);
367 
368     /*
369     ** Get the binary (64-bit) time of the specified timeout value
370     */
371     BIO_snprintf(AscTimeBuff, sizeof(AscTimeBuff), "0 0:0:%02d.00", SOCKET_PAIR_TIMEOUT_VALUE);
372     AscTimeDesc.dsc$w_length = strlen(AscTimeBuff);
373     AscTimeDesc.dsc$a_pointer = AscTimeBuff;
374     status = sys$bintim(&AscTimeDesc, BinTimeBuff);
375     if (!(status & 1)) {
376         LogMessage("CreateSocketPair: SYS$BINTIM () - %08X", status);
377         close(SockDesc1);
378         return -1;
379     }
380 
381     /*
382     ** Assign another channel to the TCP/IP device for the accept.
383     ** This is the channel that ends up being connected to.
384     */
385     status = sys$assign(&TcpDeviceDesc, &TcpDeviceChan, 0, 0, 0);
386     if (!(status & 1)) {
387         LogMessage("CreateSocketPair: SYS$ASSIGN () - %08X", status);
388         close(SockDesc1);
389         return -1;
390     }
391 
392     /*
393     ** Get the channel of the first socket for the accept
394     */
395     TcpAcceptChan = decc$get_sdc(SockDesc1);
396 
397     /*
398     ** Perform the accept using $QIO so we can do this asynchronously
399     */
400     status = sys$qio(EFN$C_ENF,
401         TcpAcceptChan,
402         IO$_ACCESS | IO$M_ACCEPT,
403         &iosb,
404         0, 0, 0, 0, 0,
405         &TcpDeviceChan,
406         0, 0);
407     if (!(status & 1)) {
408         LogMessage("CreateSocketPair: SYS$QIO () - %08X", status);
409         close(SockDesc1);
410         sys$dassgn(TcpDeviceChan);
411         return -1;
412     }
413 
414     /*
415     ** Create the second socket to do the connect
416     */
417     SockDesc2 = socket(SocketFamily, SocketType, 0);
418     if (SockDesc2 < 0) {
419         LogMessage("CreateSocketPair: socket () - %d", errno);
420         sys$cancel(TcpAcceptChan);
421         close(SockDesc1);
422         sys$dassgn(TcpDeviceChan);
423         return (-1);
424     }
425 
426     /*
427     ** Setup the Socket Pair Timeout Block
428     */
429     sptb.SockChan1 = TcpAcceptChan;
430     sptb.SockChan2 = decc$get_sdc(SockDesc2);
431 
432     /*
433     ** Before we block on the connect, set a timer that can cancel I/O on our
434     ** two sockets if it never connects.
435     */
436     status = sys$setimr(EFN$C_ENF,
437         BinTimeBuff,
438         SocketPairTimeoutAst,
439         &sptb,
440         0);
441     if (!(status & 1)) {
442         LogMessage("CreateSocketPair: SYS$SETIMR () - %08X", status);
443         sys$cancel(TcpAcceptChan);
444         close(SockDesc1);
445         close(SockDesc2);
446         sys$dassgn(TcpDeviceChan);
447         return -1;
448     }
449 
450     /*
451     ** Now issue the connect
452     */
453     memset((char *)&sin, 0, sizeof(sin));
454     sin.sin_family = SocketFamily;
455     sin.sin_addr.s_addr = inet_addr(LocalHostAddr);
456     sin.sin_port = LocalHostPort;
457 
458     status = connect(SockDesc2, (struct sockaddr *)&sin, sizeof(sin));
459     if (status < 0) {
460         LogMessage("CreateSocketPair: connect () - %d", errno);
461         sys$cantim(&sptb, 0);
462         sys$cancel(TcpAcceptChan);
463         close(SockDesc1);
464         close(SockDesc2);
465         sys$dassgn(TcpDeviceChan);
466         return -1;
467     }
468 
469     /*
470     ** Wait for the asynch $QIO to finish.  Note that if the I/O was aborted
471     ** (SS$_ABORT), then we probably canceled it from the AST routine - so log
472     ** a timeout.
473     */
474     status = sys$synch(EFN$C_ENF, &iosb);
475     if (!(iosb.iosb$w_status & 1)) {
476         if (iosb.iosb$w_status == SS$_ABORT)
477             LogMessage("CreateSocketPair: SYS$QIO(iosb) timeout");
478         else {
479             LogMessage("CreateSocketPair: SYS$QIO(iosb) - %d",
480                 iosb.iosb$w_status);
481             sys$cantim(&sptb, 0);
482         }
483         close(SockDesc1);
484         close(SockDesc2);
485         sys$dassgn(TcpDeviceChan);
486         return -1;
487     }
488 
489     /*
490     ** Here we're successfully connected, so cancel the timer, convert the
491     ** I/O channel to a socket fd, close the listener socket and return the
492     ** connected pair.
493     */
494     sys$cantim(&sptb, 0);
495 
496     close(SockDesc1);
497     SocketPair[0] = SockDesc2;
498     SocketPair[1] = socket_fd(TcpDeviceChan);
499 
500     return (0);
501 }
502 
503 /*----------------------------------------------------------------------------*/
504 /*                                                                            */
505 /*----------------------------------------------------------------------------*/
SocketPairTimeoutAst(int astparm)506 static void SocketPairTimeoutAst(int astparm)
507 {
508     SPTB *sptb = (SPTB *)astparm;
509 
510     sys$cancel(sptb->SockChan2); /* Cancel the connect() */
511     sys$cancel(sptb->SockChan1); /* Cancel the accept() */
512 
513     return;
514 }
515 
516 /*----------------------------------------------------------------------------*/
517 /*                                                                            */
518 /*----------------------------------------------------------------------------*/
TerminalDeviceAst(int astparm)519 static int TerminalDeviceAst(int astparm)
520 {
521     int status;
522 
523     /*
524     ** Terminate the terminal buffer
525     */
526     TerminalDeviceBuff[TerminalDeviceIosb.iosb$w_bcnt] = '\0';
527     strcat(TerminalDeviceBuff, "\n");
528 
529     /*
530     ** Send the data read from the terminal device through the socket pair
531     */
532     send(TerminalSocketPair[0], TerminalDeviceBuff,
533         TerminalDeviceIosb.iosb$w_bcnt + 1, 0);
534 
535     /*
536     ** Queue another async IO to the terminal device
537     */
538     status = sys$qio(EFN$C_ENF,
539         TerminalDeviceChan,
540         IO$_READVBLK,
541         &TerminalDeviceIosb,
542         TerminalDeviceAst,
543         0,
544         TerminalDeviceBuff,
545         sizeof(TerminalDeviceBuff) - 2,
546         0, 0, 0, 0);
547 
548     /*
549     ** Return status
550     */
551     return status;
552 }
553 
554 /*----------------------------------------------------------------------------*/
555 /*                                                                            */
556 /*----------------------------------------------------------------------------*/
LogMessage(char * msg,...)557 static void LogMessage(char *msg, ...)
558 {
559     char *Month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
560         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
561     static unsigned int pid = 0;
562     va_list args;
563     time_t CurTime;
564     struct tm *LocTime;
565     char MsgBuff[256];
566 
567     /*
568     ** Get the process pid
569     */
570     if (pid == 0)
571         pid = getpid();
572 
573     /*
574     ** Convert the current time into local time
575     */
576     CurTime = time(NULL);
577     LocTime = localtime(&CurTime);
578 
579     /*
580     ** Format the message buffer
581     */
582     BIO_snprintf(MsgBuff, sizeof(MsgBuff), "%02d-%s-%04d %02d:%02d:%02d [%08X] %s\n",
583         LocTime->tm_mday, Month[LocTime->tm_mon],
584         (LocTime->tm_year + 1900), LocTime->tm_hour, LocTime->tm_min,
585         LocTime->tm_sec, pid, msg);
586 
587     /*
588     ** Get any variable arguments and add them to the print of the message
589     ** buffer
590     */
591     va_start(args, msg);
592     vfprintf(stderr, MsgBuff, args);
593     va_end(args);
594 
595     /*
596     ** Flush standard error output
597     */
598     fsync(fileno(stderr));
599 
600     return;
601 }
602 #endif
603