/* * Copyright (C) 1988-1992 by CERN * All rights reserved */ #ifndef lint static char sccsid[] = "%W% %G% CERN CN-SW/DC Frederic Hemmer"; #endif /* socket.c A simple VMS socket TCP/Decnet interface */ /* * Note : The multi-protocol (TCP/IP, DECNET) is historical, dated * from the time there was only Wollongong TCP/IP without * socket interface. At this time only QIO's were allowed. * In order not to confuse the linker, only one call per * socket I/F was implemented, multipexing TCP/IP and Decnet * In principle QIO calls are still available for Wollongong * and Multinet, but maybe the IO codes ahve changed. */ static int socket_debug = 0; /* debugging flag */ #include /* error numbers and codes */ #include /* standard input/output */ #include /* system services definitions */ #include /* VMS I/O definitions */ #include /* DECnet networking blocks */ #include /* mailbox message types */ #include /* system types */ #include /* socket type definitions */ #include /* denet networkingg */ #include /* internet IO definitions */ #define PUBLIC #define PRIVATE static #define to_unix(x) ((0x7fff & x) >> 3) /* Win/TCP error numbers */ #define MAXMBXMSG 128 /* Mailbox size */ #define MAXBUFQUO 128 /* Only one message */ static struct Channel { unsigned short Channel; int Type; unsigned short mbx; unsigned short Backlog; /* For Decnet */ struct Channel *Next; }; static struct Channel *Head; static struct { /* Win/TCP INET descriptor */ int size; char *ptr; } inetdsc = { 7, "_INET0:"}; static struct { /* Decnet NET descriptor */ int size; char *ptr; } dnetdsc = { 5, "_NET:"}; static struct { /* Decnet mailbox name */ int size; char *ptr; } mbxnamdsc = { 6, "SOCKET"}; typedef struct { unsigned short msgtype; unsigned short unit; unsigned char count; char info[MAXMBXMSG-5]; } mbx; typedef struct { int size; char *ptr; } ncbdsc; /* Decnet NCB descriptor */ typedef struct { /* Decnet Network Function block*/ unsigned char type; unsigned long objnum; } nfb; typedef struct { /* Decnet NFB Descriptor */ int size; nfb *ptr; } nfb_dsc; typedef struct { /* VMS I/O Status Block for QIO */ short status; int bytecount; short unused; } IOSB; #if defined(DEBUG) static int PrintChannelTable() { struct Channel *s; if ((s = Head) == 0) { fprintf(stdout,"Channel Table empty\n"); return(0); } for (;s != 0;s = s->Next) { fprintf(stdout,"Channel %x : %d %x %d %x\n", s->Channel, s->Type, s->mbx, s->Backlog, s->Next); } return(0); } #endif /* DEBUG */ /* * Allocate channel for address family and insert in a linked list * a mailbox is associated for the channel for AF_DECnet */ static int AllocateChannel(type, channel) int type; int *channel; { struct Channel *s; int rc; if ((s = (struct Channel *) malloc(sizeof(struct Channel)))== 0) { return(errno); } switch (type) { case AF_INET: rc = sys$assign(&inetdsc, &(s->Channel), 0, 0); if (socket_debug) { fprintf(stdout,"AllocateChannel(%d, %x): ", type, channel); fprintf(stdout,"assign returned %x (%d)\n", s->Channel, rc); } break; case AF_DECnet: rc = lib$asn_wth_mbx(&dnetdsc, 0, 0, &(s->Channel), &(s->mbx)); if (socket_debug) { fprintf(stdout,"AllocateChannel(%d, %x): ", type, channel); fprintf(stdout,"asn_wth_mbx returned %x %x (%d)\n", s->Channel, s->mbx, rc); } break; } if (rc != SS$_NORMAL) { free(s); return(rc); } s->Type = type; s->Backlog = 0; s->Next = Head; Head = s; *channel = s->Channel; return (SS$_NORMAL); } /* * Find a channel in the linked list */ static struct Channel * FindChannel(channel) { struct Channel *s; if ((s = Head) == 0) return(0); for (;s != 0;s = s->Next) { if (s->Channel == channel) { return(s); } } return(0); } int accept(s, addr, addrlen) int s; struct sockaddr *addr; int *addrlen; { struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EBADF; return(-1); } switch (C->Type) { case AF_INET: return(TCPaccept(s, addr, addrlen)); case AF_DECnet: return(DNPaccept(s, addr, addrlen)); } } int bind(s, name, namelen) int s; struct sockaddr *name; int namelen; { struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: return(TCPbind(s, name, namelen)); case AF_DECnet: return(DNPbind(s, name, namelen)); } } int socket_close(s) int s; { IOSB iosb; int rc; struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: rc = sys$dassgn(C->Channel); (void) free(C); break; case AF_DECnet: rc = sys$dassgn(C->Channel); if (rc) (void) sys$dassgn(C->mbx); else rc = sys$dassgn(C->mbx); (void) free(C); break; } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } } int connect (s, name, namelen) int s; char *name; /* could be *sockaddr_in or *sockaddr_dn */ int namelen; { struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: return(TCPconnect(s, name, namelen)); case AF_DECnet: return(DNPconnect(s, name, namelen)); } } int listen(s, backlog) int s, backlog; { IOSB iosb; int rc; struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: return(TCPlisten(s, backlog)); case AF_DECnet: return(DNPlisten(s, backlog)); } } int recv (s, buf, len, flags) int s; char *buf; int len, flags; { IOSB iosb; struct Channel *C; int rc; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: rc = sys$qiow(0, s, IO$_RECEIVE, &iosb, 0, 0, buf, len, flags, 0, 0, 0); if (socket_debug) { fprintf(stdout,"recv(%x,%x,%d,%d): qiow returned %d (iosb.status: %d)(iosb[2]: %d)\n", s, buf, len, flags, rc, iosb.status, iosb.bytecount); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (iosb.bytecount); case AF_DECnet: rc = sys$qiow(0, s, IO$_READVBLK|IO$M_MULTIPLE, &iosb, 0, 0, buf, len, flags, 0, 0, 0); if (socket_debug) { fprintf(stdout,"recv(%x,%x,%d,%d): qiow returned %d (iosb.status: %d)(iosb[2]: %d)\n", s, buf, len, flags, rc, iosb.status, iosb.bytecount); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } return (iosb.bytecount); } } int send (s, msg, len, flags) int s; char *msg; int len, flags; { IOSB iosb; struct Channel *C; int rc; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: rc = sys$qiow(0, s, IO$_SEND, &iosb, 0, 0, msg, len, flags, 0, 0, 0); if (socket_debug) { fprintf(stdout, "send(%x,%x,%d,%d): qiow returned %d (iosb.status: %d)(iosb[2]: %d)\n", s, msg, len, flags, rc, iosb.status, iosb.bytecount); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (iosb.bytecount); case AF_DECnet: rc = sys$qiow(0, s, IO$_SEND, &iosb, 0, 0, msg, len, flags, 0, 0, 0); if (socket_debug) { fprintf(stdout,"send(%x,%x,%d,%d): qiow returned %d (iosb.status: %d)(iosb[2]: %d)\n", s, msg, len, flags, rc, iosb.status, iosb.bytecount); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } iosb.bytecount = 0x0000FFFF & iosb.bytecount; return (iosb.bytecount); } } int shutdown (s, how) int s, how; { IOSB iosb; int rc; struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EINVAL; return(-1); } switch (C->Type) { case AF_INET: return(TCPshutdown(s, how)); case AF_DECnet: return(DNPshutdown(s, how)); } } int socket (af, type, protocol) int af, type, protocol; { if ((getenv("SOCKET_DEBUG")) != NULL) { socket_debug = 1; } switch (af) { case AF_INET: return(TCPsocket(af, type, protocol)); case AF_DECnet: return(DNPsocket(af, type, protocol)); default: if (socket_debug) { fprintf(stdout,"socket(%d,%d,%d): Bad address family\n", af, type, protocol); fprintf(stdout," only (%d, %d) are supported\n", AF_INET, AF_DECnet); } errno = EAFNOSUPPORT; return(-1); } } static int TCPaccept(s, addr, addrlen) int s; struct sockaddr *addr; int *addrlen; { IOSB iosb; int ns; int rc; rc = AllocateChannel(AF_INET, &ns); if (socket_debug) { fprintf(stdout,"accept(%x,%x,%d) (INET): AllocateChannel returned %d\n", s, addr, *addrlen, rc); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } rc = sys$qiow(0, ns, IO$_ACCEPT, &iosb, 0, 0, addrlen, sizeof(addr)+sizeof(int), s, 0, 0, 0); if (socket_debug) { fprintf(stdout,"accept(%x,%x,%d): qiow returned %d (iosb.status: %d)\n", s, addr, *addrlen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (ns); } static int DNPaccept(s, addr, addrlen) int s; struct sockaddr *addr; int *addrlen; { IOSB iosb; int ns; int rc; mbx mailbox; ncbdsc ncb; char ncbstr[100]; struct Channel *C; if ((C = FindChannel(s)) == 0) { errno = EBADF; return(-1); } if (socket_debug) { fprintf(stdout,"accept(%x,%x,%d) (DECnet)\n", s, addr, *addrlen); } rc = sys$qiow(0, (C->mbx), IO$_READVBLK, &iosb, 0, 0, &mailbox, MAXMBXMSG, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"accept(%x,%x,%d) (DECnet): qiow (mbx: %x) returned: %d \n", s, addr, *addrlen ,C->mbx, rc); fprintf(stdout," mailbox message type code: %d\n",mailbox.msgtype); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } switch (mailbox.msgtype) { case MSG$_CONNECT: rc = AllocateChannel(AF_DECnet ,&ns); if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } ncb.ptr = ncbstr; ncb.size= (int) (* (&mailbox.info + mailbox.count)); bcopy ((char *) (&mailbox.info + mailbox.count + 1), ncb.ptr,ncb.size); rc = sys$qiow( 0, ns, IO$_ACCESS, &iosb, 0, 0, 0, &ncb, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"accept(%x,%x,%d) (DECnet): qiow returned %d (iosb.status: %d)\n", s, addr, addrlen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } switch (iosb.status) { case SS$_NORMAL: break; case SS$_LINKABORT: case SS$_LINKDISCON: case SS$_LINKEXIT: case SS$_TIMEOUT: case SS$_PATHLOST: errno = EINTR; return(-1); default : vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } break; case MSG$_NETSHUT: fprintf(stderr, "accept: network shutting down, exiting\n"); exit(0); default: /* we ignore all others */ if (socket_debug) { fprintf(stdout, "accept: unexpected mailbox message type %x\n", mailbox.msgtype); } errno = EINTR; return(-1); } return (ns); } static int TCPbind(s, name, namelen) int s; struct sockaddr *name; int namelen; { IOSB iosb; int rc; rc = sys$qiow(0, s, IO$_BIND, &iosb, 0, 0, name, sizeof(struct sockaddr), 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"bind(%x,%x,%d) (INET): qiow returned %d (iosb.status: %d)\n", s, name, namelen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (0); } static int DNPbind(s, name, namelen) int s; struct sockaddr_dn *name; int namelen; { IOSB iosb; int rc; nfb dnp_object; nfb_dsc dnp_obj_dsc; if (socket_debug) { fprintf(stdout,"bind(%x, %x, %d) (DECnet): Node (%d.%d) Object(%d)\n", s, name, namelen, ((*(unsigned short *) name->sdn_nodeaddr) >> 10) & 0x003F, (*(unsigned short *) name->sdn_nodeaddr) & 0x03FF, name->sdn_objnum); } dnp_obj_dsc.size = 5; dnp_obj_dsc.ptr = &dnp_object; dnp_object.type = NFB$C_DECLOBJ; dnp_object.objnum = name->sdn_objnum; rc = sys$qiow(0, s, IO$_ACPCONTROL, &iosb, 0, 0, &dnp_obj_dsc, 0, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"bind(%x,%x,%d) (DECnet): qiow returned %d (iosb.status: %d)\n", s, name, namelen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } return (0); } static int TCPconnect (s, name, namelen) int s; struct sockaddr_in *name; int namelen; { IOSB iosb; int rc; rc = sys$qiow(0, s, IO$_CONNECT, &iosb, 0, 0, name, sizeof(struct sockaddr), 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"connect(%x,%x,%d) (INET): qiow returned %d (iosb.status: %d)\n", s, name, namelen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (0); } static int DNPconnect (s, name, namelen) int s; struct sockaddr_dn *name; int namelen; { IOSB iosb; int rc; mbx mailbox; ncbdsc ncb; char ncbstr[100]; struct Channel *C; /* map the name sock_addr_dn to NCB */ if (socket_debug) { fprintf(stdout,"connect(%x, %x, %d) (DECnet): Node (%d.%d) %s\n", s, name, namelen, ((*(unsigned short *) name->sdn_nodeaddr) >> 10) & 0x003F, (*(unsigned short *) name->sdn_nodeaddr) & 0x03FF, name->sdn_nodename); } sprintf(ncbstr,"%s::\"%d=\"\0", name->sdn_nodename, name->sdn_objnum); ncb.size = strlen(ncbstr); ncb.ptr = ncbstr; rc = sys$qiow(0, s, IO$_ACCESS, &iosb, 0, 0, 0, &ncb, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"connect(%x,%x,%d): qiow returned %d (iosb.status: %d)\n", s, name, namelen, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { vaxc$errno = iosb.status; errno = EVMSERR; return(-1); } /* confirm the connection */ if ((C = FindChannel(s)) == 0) { errno = EBADF; return(-1); } rc = sys$qiow(0, (C->mbx), IO$_READVBLK, &iosb, 0, 0, &mailbox, MAXMBXMSG, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"connect(%x) (DECnet): qiow (mbx: %x) returned: %d \n", s ,C->mbx, rc); fprintf(stdout," mailbox message type: %d\n",mailbox.msgtype); } switch (mailbox.msgtype) { case MSG$_CONFIRM: fprintf(stderr, "connect: logical link connection confirmed\n"); return(0); default: if (socket_debug) { fprintf(stdout, "accept: unexpected mailbox message type %x\n", mailbox.msgtype); } vaxc$errno = mailbox.msgtype; errno = EVMSERR; return(-1); } } static int TCPsocket (af, type, protocol) int af, type, protocol; { IOSB iosb; int rc; int s; rc = AllocateChannel(AF_INET, &s); if (socket_debug) { fprintf(stdout,"socket(%d,%d,%d): (INET) AllocateChannel returned %x (%d)\n", af, type, protocol, s, rc); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } rc = sys$qiow(0, s, IO$_SOCKET, &iosb, 0, 0, af, type, protocol, 0, 0, 0); if (socket_debug) { fprintf(stdout,"socket(%d,%d,%d): qiow returned %d (iosb.status: %d)\n", af, type, protocol, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (s); } static int DNPsocket (af, type, protocol) int af, type, protocol; { IOSB iosb; int rc; int s; if (type != SOCK_STREAM) { errno = ESOCKTNOSUPPORT; return(-1); } rc = AllocateChannel(AF_DECnet, &s); if (socket_debug) { fprintf(stdout,"socket(%d,%d,%d): (DECnet) AllocateChannel returned %x (%d)\n", af, type, protocol, s, rc); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } return (s); } static int TCPlisten(s, backlog) int s, backlog; { IOSB iosb; int rc; rc = sys$qiow(0, s, IO$_LISTEN, &iosb, 0, 0, backlog, 0, 0, 0, 0, 0); if (socket_debug) { fprintf(stdout,"listen(%x,%d) (INET): qiow returned %d (iosb.status: %d)\n", s, backlog, rc, iosb.status); } if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (0); } static int DNPlisten(s, backlog) int s, backlog; { IOSB iosb; int rc; struct Channel *C; /* we don't do very much, just establishing the backlog */ if (socket_debug) { fprintf(stdout,"listen(%x,%d) (DECnet)\n",s, backlog); } if ((C = FindChannel(s)) == 0) { errno = EBADF; return(-1); } C->Backlog = backlog; return(0); } int TCPshutdown (s, how) int s, how; { IOSB iosb; int rc; if (socket_debug) { fprintf(stdout,"shutdown(%x,%d) (INET)\n",s, how); } rc = sys$qiow(0, s, IO$_SHUTDOWN, &iosb, 0, 0, how, 0, 0, 0, 0, 0); if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (-1); } int DNPshutdown (s, how) int s, how; { IOSB iosb; int rc; if (socket_debug) { fprintf(stdout,"shutdown(%x,%d) (DECnet)\n",s, how); } rc = sys$qiow(0, s, IO$_DEACCESS|IO$M_SYNCH, &iosb, 0, 0, how, 0, 0, 0, 0, 0); if (rc != SS$_NORMAL) { vaxc$errno = rc; errno = EVMSERR; return(-1); } if (iosb.status != SS$_NORMAL) { errno=to_unix(iosb.status); return(-1); } return (-1); }