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