/*****************************************************************************/ /* TCPIP6.c IPv6 address resolution functions only available non-VAX VMS V7.0 and later. If the logical name WASD_TCPIP6_IPNODES is not defined none of this happens. IPv6 name/address resolution is a little problematic for WASD because there is (currently) no native asynchronous interface to it in the same way as there is with $QIO ACPCONTROL for IPv4. Fortunately IPv6 is somewhat of a niche environment! The function TcpIp6Nodes() allows a WASD-specific logical name be used to map IPv6 addresses and host names back and forth without recourse to the blocking functions getaddrinfo() and getnameinfo(). The logical name WASD_TCPIP6_IPNODES allows multiple values to be defined each of which can represent an IPv4 or IPv6 and then qualified host name (similar to the content of the IPNODES.DAT file). The logical name must exist at startup but after than can have its value(s) changed dynamically. This is an example: $ DEFINE /SYSTEM /EXEC WASD_TCPIP6_IPNODES - "fe80::200:f8ff:fe75:c062 klaatu6.private.net",- "fe80::21d:7dff:fed3:ae62 gort6.private.net" If a logical value is "*" (first, last, in-between or only) then the logical name processing stops and getaddrinfo() getnameinfo() are used allowing AAAA records (IPv6 DNS host entries) to be processed. NOTE: the "*" causes name/address resolution to become blocking adding significant granularity to server processing. Of course after resolution, and until the lifetime expires, the resolved name/address is cached by the calling TCPIP.C functions. In reality this should have little impact for the average IPv6 server. It is a general recommendation not to have client host address-to-name lookup enabled on a web server (this can be handled during access log processing for example). For name-to-address resolution there are two occasions when it is used. Firstly, during startup, resolution of service names to addresses. These are made using explicit blocking anyway (serialises the startup). The second usage is with proxy processing. The proxy client's proxied-to host name must be resolved to an address. For host names that don't resolve, or not quickly, this has the potential to add significant granularity. Fortunately (again) IPv6 proxy serving is a niche environment. VERSION HISTORY --------------- 18-NOV-2018 MGD TcpIp6_getaddrinfo() works around unresolved getaddrinfo() TcpIp6_getnameinfo() works around unresolved getnameinfo() 30-AUG-2010 MGD initial (needed *something* for IPv6 host resolution) */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif #ifdef __VAX # define TCPIP6_STUB #endif #include #include #ifndef TCPIP6_STUB /* VAX and some earlier Alpha TCP/IP don't support these functions */ extern int getaddrinfo (...); int (*TcpIp6_getaddrinfo)() = getaddrinfo; extern int getnameinfo (...); int (*TcpIp6_getnameinfo)() = getnameinfo; /* and under HP C V7.3-009 on OpenVMS Alpha V8.4 netdb.h breaks the above */ #include #ifndef AI_ALL /* at least Compaq C V6.4-005 on OpenVMS VAX V7.2 */ #define AI_ALL 0 #endif #endif #include #include "wasd.h" #include "tcpip.h" #define WASD_MODULE "TCPIP6" /********************/ /* external storage */ /********************/ extern WATCH_STRUCT Watch; /*****************************************************************************/ /* This is somewhat of a KLUDGE function! */ BOOL TcpIp6Nodes ( char *hnaptr, IPADDRESS *ipaptr ) { static BOOL IpNodes = true; static unsigned long LnmIndex; static $DESCRIPTOR (LogNameDsc, "WASD_TCPIP6_IPNODES"); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static unsigned long LnmAttributes; static VMS_ITEM_LIST3 LnmItems [] = { { sizeof(LnmIndex), LNM$_INDEX, &LnmIndex, 0 }, { sizeof(LnmAttributes), LNM$_ATTRIBUTES, &LnmAttributes, 0 }, { 0, LNM$_STRING, 0, 0 }, { 0,0,0,0 } }; int status; unsigned short LogValueLength; unsigned long LnmCount; char *asptr, *cptr, *sptr; char LogValue [256]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIp6Nodes() !&B !&Z !UL", IpNodes, hnaptr, IPADDRESS_SIZE(ipaptr)); if (!IpNodes) return (false); LnmItems[2].buf_len = sizeof(LogValue)-1; LnmItems[2].buf_addr = LogValue; LnmItems[2].ret_len = &LogValueLength; if (!*hnaptr) asptr = TcpIpAddressToString (ipaptr, 0); for (LnmIndex = 0; LnmIndex <= 127; LnmIndex++) { status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems); if (VMSnok (status) || !(LnmAttributes & LNM$M_EXISTS)) { if (LnmIndex) break; IpNodes = false; return (false); } LogValue[LogValueLength] = '\0'; if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "!UL !AZ", LnmIndex, LogValue); if (LogValue[0] == '*') if (*hnaptr) return (TcpIp6GetAddrInfo (hnaptr, ipaptr)); else return (TcpIp6GetNameInfo (hnaptr, ipaptr)); for (cptr = LogValue; *cptr && !ISLWS(*cptr); cptr++); *cptr++ = '\0'; while (*cptr && ISLWS(*cptr)) cptr++; if (*hnaptr) { /* name to address */ if (!strsame (hnaptr, cptr, -1)) continue; if (VMSok (TcpIpStringToAddress (LogValue, ipaptr))) return (true); else return (false); } else { /* address to name */ if (!strsame (asptr, LogValue, -1)) continue; for (sptr = hnaptr; *cptr; *sptr++ = *cptr++); *sptr = '\0'; return (true); } } return (false); } /*****************************************************************************/ /* Wrap the IPv6-capable getaddrinfo() address resolution function. SYNCHRONOUS! */ BOOL TcpIp6GetAddrInfo ( char *hnaptr, IPADDRESS *ipaptr ) { #ifndef TCPIP6_STUB static struct addrinfo IPv6Hint = { AI_ALL, AF_INET6, 0, 0, 0, NULL, NULL, NULL }; int retval; struct addrinfo *raiptr, *RetAddrInfo; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIp6GetAddrInfo() !&Z !UL", hnaptr, IPADDRESS_SIZE(ipaptr)); if (TcpIp6_getaddrinfo == 0) { static int reported = 0; if (!reported) FaoToStdout ("%HTTPD-W-TCPIP6, getaddrinfo() unresolved and disabled\n"); retval = reported = -1; } else retval = TcpIp6_getaddrinfo (hnaptr, NULL, &IPv6Hint, &RetAddrInfo); if (retval) return (false); /* just use the first of this (potential) list of addresses */ raiptr = RetAddrInfo; if (raiptr->ai_family == AF_INET) IPADDRESS_GET4 (ipaptr, &((struct sockaddr_in*)(raiptr->ai_addr))->sin_addr) else if (raiptr->ai_family == AF_INET6) IPADDRESS_GET6 (ipaptr, &((struct sockaddr_in6*)(raiptr->ai_addr))->sin6_addr) freeaddrinfo (&RetAddrInfo); return (true); #else if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIp6GetAddrInfo() NOT SUPPORTED"); return (false); #endif } /*****************************************************************************/ /* Wrap the IPv6-capable getnameinfo() name resolution function. SYNCHRONOUS! */ BOOL TcpIp6GetNameInfo ( char *hnaptr, IPADDRESS *ipaptr ) { #ifndef TCPIP6_STUB static int flags = NI_NAMEREQD; int nodelen, retval; struct sockaddr_in6 Sock6Addr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIp6GetNameInfo() !&Z !UL", hnaptr, IPADDRESS_SIZE(ipaptr)); /* only used for IPv6 addresses */ if (!IPADDRESS_IS_V6 (ipaptr)) return (false); memset (&Sock6Addr, 0, sizeof(Sock6Addr)); Sock6Addr.sin6_family = AF_INET6; memcpy (Sock6Addr.sin6_addr, IPADDRESS_ADR6(ipaptr), sizeof(Sock6Addr.sin6_addr)); if (TcpIp6_getnameinfo == 0) { static int reported = 0; if (!reported) FaoToStdout ("%HTTPD-W-TCPIP6, getnameinfo() unresolved and disabled\n"); retval = reported = 1; /* EPERM errno */ } else retval = TcpIp6_getnameinfo (&Sock6Addr, sizeof(Sock6Addr), hnaptr, &nodelen, NULL, NULL, flags); if (retval) return (false); return (true); #else if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIp6GetNameInfo() NOT SUPPORTED"); return (false); #endif } /*****************************************************************************/