/*****************************************************************************/ /* TCPIPalt.c Alternate TCP/IP functions, lookup by name and by address. Currently experimental. Both inline (synchronous) and by agent (DCL script, asynchronous). The agent can take on the role of a network agent and drop connections. This means that if using a lookup agent there is no need also to use a network agent. A drop connection is cached. These are (currently) controlled using logical names WASD_TCPIP_LOOKUP_INLINE and WASD_TCPIP_LOOKUP_AGENT. Without one or the other the (historic) TCP/IP $QIO INETACP_FUNC$C_GETHOSTBYADDR and INETACP_FUNC$C_GETHOSTBYNAME are used. VERSION HISTORY --------------- 01-JUN-2021 MGD initial */ /*****************************************************************************/ #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 /* standard C header files */ #include #include #include #include #include #include #include /* VMS related header files */ #include #include #include #include /* application-related header files */ #include "wasd.h" #include "tcpip.h" #define WASD_MODULE "TCPIPALT" #define FI_LI WASD_MODULE, __LINE__ #define DBUG 1 /******************/ /* global storage */ /******************/ char TcpIpAltAgentScript [] = "/cgiplus-bin/lookagent"; char ProblemTcpIpAgent [] = "Problem initiating TCP/IP DNS agent"; #define TCPIP_AGENT_ACTIVE_MAX 8 #define TCPIP_AGENT_BUSY_MAX 100 static int TcpIpAltAgentActiveCount, TcpIpAltAgentActiveMax = TCPIP_AGENT_ACTIVE_MAX, TcpIpAltAgentBusyMax = TCPIP_AGENT_BUSY_MAX, TcpIpAltAgentBusyCount, TcpIpAltAgentBusyLimit; /********************/ /* external storage */ /********************/ extern int HttpdTickSecond; TcpIpLookupAddrCount, TcpIpLookupNameCount; extern const int64 Delta100mSec; extern uint64 TcpIpLookupAddrDelta64, TcpIpLookupAddrMax64, TcpIpLookupAddrMin64, TcpIpLookupNameDelta64, TcpIpLookupNameMax64, TcpIpLookupNameMin64; extern char ErrorSanityCheck[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Not actually used :-) TcpIpNameToAddress() calls the relevant inline functions directly. */ #if 0 int TcpIpAltInlineLookup (TCPIP_HOST_LOOKUP *hlptr) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltInlineLookup()"); if (hlptr->AddrToName) status = TcpIpAltInlineAddressToName (hlptr); else status = TcpIpAltInlineNameToAddress (hlptr); return (status); } #endif /* 0 */ /*****************************************************************************/ /* Set up by TcpIpAddressToName(). */ int TcpIpAltInlineAddressToName (TCPIP_HOST_LOOKUP *hlptr) { int retval; char *cptr, *sptr, *zptr; char strerr [128]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltInlineAddressToName()"); retval = TcpIpAltGetNameInfo (hlptr); if (retval) { hlptr->LookupIOsb.Status = SS$_ENDOFFILE; *(USHORTPTR)hlptr->HostName = '?\0'; hlptr->HostNameLength = 1; } else { hlptr->LookupIOsb.Status = SS$_NORMAL; hlptr->HostNameLength = strlen(hlptr->HostName); } TcpIpLookupDelta (hlptr); TcpIpCacheSetEntry (hlptr); InstanceGblSecIncrLong (&AccountingPtr->LookupDnsAddressCount); if (WATCH_MODULE(WATCH_MOD_NET)) { sprintf (strerr, "%s", (char*)gai_strerror(retval)); WatchThis (WATCHALL, WATCH_MOD_NET, "host:!AZ retval:!UL (!AZ) !&S delta:!ULmS min:!ULmS max:!ULmS", &hlptr->HostName, retval, strerr, hlptr->LookupIOsb.Status, hlptr->LookupDeltaTime64 / 10000, TcpIpLookupNameMin64 / 10000, TcpIpLookupNameMax64 / 10000); } if (hlptr->AstFunction) SysDclAst (hlptr->AstFunction, hlptr->AstParam); return (hlptr->LookupIOsb.Status); } /*****************************************************************************/ /* Wrap the getnameinfo() name resolution function. SYNCHRONOUS! */ int TcpIpAltGetNameInfo (TCPIP_HOST_LOOKUP *hlptr) { static int flags = NI_NAMEREQD; int retval; struct sockaddr_in SockAddr; struct sockaddr_in6 Sock6Addr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltGetNameInfo() !&Z !UL", hlptr->HostName, IPADDRESS_SIZE(&hlptr->IpAddress)); *(ULONGPTR)hlptr->HostName = 0; if (IPADDRESS_IS_V4 (&hlptr->IpAddress)) { memset (&SockAddr, 0, sizeof(SockAddr)); SockAddr.sin_family = TCPIP$C_AF_INET; memcpy (&SockAddr.sin_addr, IPADDRESS_ADR4(&hlptr->IpAddress), sizeof(SockAddr.sin_addr)); retval = getnameinfo (&SockAddr, sizeof(SockAddr), hlptr->HostName, sizeof(hlptr->HostName), NULL, NULL, flags); } else if (IPADDRESS_IS_V6 (&hlptr->IpAddress)) { memset (&Sock6Addr, 0, sizeof(Sock6Addr)); Sock6Addr.sin6_family = TCPIP$C_AF_INET6; memcpy (&Sock6Addr.sin6_addr, IPADDRESS_ADR6(&hlptr->IpAddress), sizeof(Sock6Addr.sin6_addr)); retval = getnameinfo (&Sock6Addr, sizeof(Sock6Addr), hlptr->HostName, sizeof(hlptr->HostName), NULL, NULL, flags); } else return (false); if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "retval:!UL host:!&Z", retval, hlptr->HostName); return (retval); } /*****************************************************************************/ /* Set up by TcpIpNameToAddress(). */ int TcpIpAltInlineNameToAddress (TCPIP_HOST_LOOKUP *hlptr) { int retval; char *cptr, *sptr, *zptr; char strerr [128]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltInlineNameToAddress()"); if (retval = TcpIpAltGet4AddrInfo (hlptr)) retval = TcpIpAltGet6AddrInfo (hlptr); if (retval) { hlptr->LookupIOsb.Status = SS$_ENDOFFILE; IPADDRESS_SET_UNUSABLE (&hlptr->IpAddress); } else hlptr->LookupIOsb.Status = SS$_NORMAL; TcpIpLookupDelta (hlptr); InstanceGblSecIncrLong (&AccountingPtr->LookupDnsNameCount); if (WATCH_MODULE(WATCH_MOD_NET)) { sprintf (strerr, "%s", (char*)gai_strerror(retval)); WatchThis (WATCHALL, WATCH_MOD_NET, "IP:!&I retval:!UL (!AZ) !&S delta:!ULmS min:!ULmS max:!ULmS", &hlptr->IpAddress, retval, strerr, hlptr->LookupIOsb.Status, hlptr->LookupDeltaTime64 / 10000, TcpIpLookupNameMin64 / 10000, TcpIpLookupNameMax64 / 10000); } if (hlptr->AstFunction) SysDclAst (hlptr->AstFunction, hlptr->AstParam); return (hlptr->LookupIOsb.Status); } /*****************************************************************************/ /* Wrap the getaddrinfo() address resolution function. SYNCHRONOUS! */ int TcpIpAltGet4AddrInfo (TCPIP_HOST_LOOKUP *hlptr) { static struct addrinfo IPv4Hint = { AI_ALL, TCPIP$C_AF_INET, 0, 0, 0, NULL, NULL, NULL }; int retval; void *addptr; struct addrinfo *raiptr, *RetAddrInfo; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltGet4AddrInfo() !&Z !UL", hlptr->HostName, IPADDRESS_SIZE(&hlptr->IpAddress)); IPADDRESS_V4 (&hlptr->IpAddress); retval = getaddrinfo (hlptr->HostName, NULL, &IPv4Hint, &RetAddrInfo); if (retval) return (retval); for (raiptr = RetAddrInfo; raiptr; raiptr = raiptr->ai_next) { if (raiptr->ai_family == AF_INET) { addptr = &((struct sockaddr_in *)raiptr->ai_addr)->sin_addr; IPADDRESS_GET4 (&hlptr->IpAddress, addptr); TcpIpCacheSetEntry (hlptr); } } freeaddrinfo (&RetAddrInfo); return (retval); } /*****************************************************************************/ /* Wrap the getaddrinfo() address resolution function. SYNCHRONOUS! */ int TcpIpAltGet6AddrInfo (TCPIP_HOST_LOOKUP *hlptr) { static struct addrinfo IPv6Hint = { AI_ALL, TCPIP$C_AF_INET, 0, 0, 0, NULL, NULL, NULL }; int retval; void *addptr; struct addrinfo *raiptr, *RetAddrInfo; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltGet6AddrInfo() !&Z !UL", hlptr->HostName, IPADDRESS_SIZE(&hlptr->IpAddress)); IPADDRESS_V6 (&hlptr->IpAddress); retval = getaddrinfo (hlptr->HostName, NULL, &IPv6Hint, &RetAddrInfo); if (retval) return (retval); for (raiptr = RetAddrInfo; raiptr; raiptr = raiptr->ai_next) { if (raiptr->ai_family == AF_INET6) { addptr = &((struct sockaddr_in6 *)raiptr->ai_addr)->sin6_addr; IPADDRESS_GET6 (&hlptr->IpAddress, addptr); } } freeaddrinfo (&RetAddrInfo); return (retval); } /*****************************************************************************/ /* Activate a TCP/IP DNS agent (script) to perform the lookup. ASYNCHRONOUS! A TCP/IP agent does not have an associated request. */ int TcpIpAltAgentBegin (TCPIP_HOST_LOOKUP *hlptr) { int status; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAgentBegin()"); if (!hlptr->AstFunction) { /* no AST supplied then do it inline */ if (hlptr->AddrToName) status = TcpIpAltInlineAddressToName (hlptr); else status = TcpIpAltInlineNameToAddress (hlptr); return (status); } if (TcpIpAltAgentActiveCount >= TcpIpAltAgentActiveMax) { /* busy, busy, busy ... requeue for a delayed subsequent attempt */ if (hlptr->AgentBusyCount++ <= TcpIpAltAgentBusyMax) { if (hlptr->AgentBusyCount <= TcpIpAltAgentBusyMax / 4) SysDclAst (TcpIpAltAgentBegin, hlptr); else sys$setimr (0, &Delta100mSec, TcpIpAltAgentBegin, hlptr, 0); return (SS$_NORMAL); } else { ErrorNoticed (NULL, SS$_TOOMANYREDS, "!UL exceeded (!UL)", FI_LI, TcpIpAltAgentBusyMax, TcpIpAltAgentBusyLimit); TcpIpAltAgentBusyCount++; hlptr->LookupIOsb.Status = SS$_ABORT; SysDclAst (hlptr->AstFunction, hlptr->AstParam); return (SS$_ABORT); } } if (hlptr->AgentBusyCount) { if (hlptr->AgentBusyCount > TcpIpAltAgentBusyLimit) TcpIpAltAgentBusyLimit = hlptr->AgentBusyCount; hlptr->AgentBusyCount = 0; } /************/ /* continue */ /************/ rqptr = DclFauxRequest (NULL, NULL, NULL); rqptr->AgentAstParam = hlptr; rqptr->AgentRequestPtr = VmGetHeap (rqptr, 256); if (hlptr->AddrToName) FaoToBuffer (rqptr->AgentRequestPtr, 256, NULL, "!&I", &hlptr->IpAddress); else FaoToBuffer (rqptr->AgentRequestPtr, 256, NULL, "!AZ", hlptr->HostName); rqptr = DclFauxRequest (rqptr, TcpIpAltAgentScript, &TcpIpAltAgentEnd); if (!rqptr) { if (WATCHING (rqptr, WATCH_NETWORK)) WatchThis (WATCHITM(rqptr), WATCH_NETWORK, "LOOKUP AGENT !AZ", TcpIpAltAgentScript); ErrorNoticed (NULL, SS$_ABORT, ProblemTcpIpAgent, FI_LI); hlptr->LookupIOsb.Status = SS$_ABORT; SysDclAst (hlptr->AstFunction, hlptr->AstParam); return (SS$_ABORT); } TcpIpAltAgentActiveCount++; return (SS$_NORMAL); } /*****************************************************************************/ /* The agent (script) has completed. */ void TcpIpAltAgentEnd (REQUEST_STRUCT *rqptr) { int count, status; char *cptr, *sptr, *zptr; TCPIP_HOST_LOOKUP *hlptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_NET)) WatchThis (WATCHALL, WATCH_MOD_NET, "TcpIpAltAgentEnd()"); hlptr = (TCPIP_HOST_LOOKUP*)rqptr->AgentAstParam; TcpIpLookupDelta (hlptr); if (cptr = rqptr->AgentResponsePtr) { status = atoi(cptr); while (isdigit (*cptr)) cptr++; while (*cptr == ' ') cptr++; if (status >= 200 && status <= 299) { /***********/ /* success */ /***********/ if (hlptr->AddrToName) { hlptr->LookupIOsb.Status = SS$_NORMAL; zptr = (sptr = hlptr->HostName) + sizeof(hlptr->HostName)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; hlptr->HostNameLength = sptr - hlptr->HostName; TcpIpCacheSetEntry (hlptr); } else { /* possible multiple space-separated IP addresses */ hlptr->LookupIOsb.Status = SS$_NORMAL; count = 0; while (*cptr) { while (*cptr && *cptr == ' ') cptr++; if (!*cptr) break; status = TcpIpStringToAddress (cptr, &hlptr->IpAddress); if (VMSnok (status)) { if (!count) { /* primary host address */ hlptr->LookupIOsb.Status = status; IPADDRESS_SET_UNUSABLE (&hlptr->IpAddress); ErrorNoticed (NULL, SS$_ABORT, cptr, FI_LI); break; } } count++; TcpIpCacheSetEntry (hlptr); while (*cptr && *cptr != ' ') cptr++; } } } else if (status == 418) { /********/ /* drop */ /********/ hlptr->DropConnect = true; TcpIpCacheSetEntry (hlptr); } else { /**********/ /* failed */ /**********/ if (hlptr->AddrToName) { hlptr->LookupIOsb.Status = SS$_ENDOFFILE; *(USHORTPTR)hlptr->HostName = '?\0'; hlptr->HostNameLength = 1; } else IPADDRESS_SET_UNUSABLE (&hlptr->IpAddress); TcpIpCacheSetEntry (hlptr); if (MATCH3 (cptr, "[%X")) { /* if agent reporting VMS error */ status = strtol (cptr+3, NULL, 16); ErrorNoticed (NULL, status, cptr, FI_LI); hlptr->LookupIOsb.Status = status; } } } else { ErrorNoticed (NULL, SS$_ABORT, ProblemTcpIpAgent, FI_LI); hlptr->LookupIOsb.Status = SS$_ABORT; } if (TcpIpAltAgentActiveCount) TcpIpAltAgentActiveCount--; SysDclAst (hlptr->AstFunction, hlptr->AstParam); /* remove from any supervisory list */ HttpdSupervisorList (rqptr, -1); VmFreeRequest (rqptr, FI_LI); } /*****************************************************************************/