/*****************************************************************************/ /* lookAgent.c This is a WASD agent for resolving IP address->name and name->address. Pronouced: looker-jent (also for no particular reason). It executes as a CGIplus script though does not use CGI variables or provide a CGI compliant response. Performs its job via callouts. It relies on the WASD v12 and later AGENT-BEGIN: and AGENT-END: callouts to provide the agent request parameter and response. AGENT-BEGIN: (reply) to resolve address->name (reply) to resolve name->address !AGENT-END: 200 to return resolved address->name !AGENT-END: 200 to return resolved name->address !AGENT-END: 418 to drop the connection !AGENT-END: 5nn to the agent functionality only !AGENT-END: 5nn %X if a to-be -E-NOTICED error As can be seen, a lookup agent can also be used to determine whether a connection should be dropped. If using a lookup agent, it can double as a connection agent, removing the need to run two agents. This agent contains a test/demonstration connection dropper. COPYRIGHT --------- Copyright (C) 2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION LOG ----------- 01-JUN-2021 MGD initial */ /*****************************************************************************/ #define SOFTWAREVN "1.0.0" #define SOFTWARENM "LOOKAGENT" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # error VAX not implemented #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* ensure BSD 4.4 structures */ #define _SOCKADDR_LEN /* BUT MultiNet BG driver does not support BSD 4.4 AF_INET addresses */ #define NO_SOCKADDR_LEN_4 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int CgiPlusUsageCount; char *dropper; char SoftwareId[] = SOFTWAREID; #define LOOKUP_RETRY 10 char* TcpIpLookup (char*, char*); /*****************************************************************************/ /* */ int main (int argc, char *argv[]) { int status; char *cptr, *sptr; if (argc > 1) { if (!strcasecmp (argv[1], "/version")) fprintf (stdout, "%%%s-I-VERSION, %s (%s)\n", SOFTWARENM, SOFTWAREID, CgiLibEnvironmentVersion()); exit (SS$_NORMAL); } CgiLibEnvironmentInit (0, NULL, 0); /* MUST only be executed in a CGIplus environment! */ if (!CgiLibEnvironmentIsCgiPlus ()) { CgiLibResponseHeader (502, "text/plain"); fputs ("CGIplus!\n", stdout); exit (SS$_NORMAL); } /* this is to test/demonstrate the connection dropper */ dropper = getenv("WASD_LOOKAGENT_DROPPER"); for (;;) { /* block waiting for the next request */ CgiLibVar (""); CgiPlusUsageCount++; if (cptr = CgiLibVarNull("REQUEST_METHOD")) if (!*cptr) { /* proctored into existance */ CgiLibResponseHeader (204, "application/proctor"); CgiLibCgiPlusEOF (); continue; } cptr = CgiLibCgiPlusCallout ("AGENT-BEGIN: %s (%s) usage:%d", SoftwareId, CgiLibEnvironmentVersion(), CgiPlusUsageCount); if (*cptr != '2') { /* if callout response not "200 ..." */ CgiLibCgiPlusCallout ("!AGENT-END: 500 %s", cptr); CgiLibCgiPlusEOF (); continue; } /* skip over response status (e.g. "200") */ for (sptr = cptr; isdigit(*sptr); sptr++); while (*sptr && *sptr == ' ') sptr++; if (!*sptr) { /* empty parameter */ CgiLibCgiPlusCallout ("!AGENT-END: 500 %s", cptr); CgiLibCgiPlusEOF (); continue; } cptr = sptr; while (isdigit(*sptr) || *sptr == '.') sptr++; if (*sptr) { /* not an IPv4 address */ for (sptr = cptr; isxdigit(*sptr) || *sptr == ':' || *sptr == '-' || *sptr == '.'; sptr++); } if (*sptr) { /* not an IPv4 or IPv6 address, assume host name */ cptr = TcpIpLookup (cptr, NULL); } else cptr = TcpIpLookup (NULL, cptr); if (dropper && strstr (dropper, cptr)) CgiLibCgiPlusCallout ("!AGENT-END: 418 %s", cptr); else if (*cptr == '[') { for (sptr = ++cptr; *sptr; sptr++); if (sptr > cptr && *(sptr-1) == ']') *(--sptr) = '\0'; CgiLibCgiPlusCallout ("!AGENT-END: 502 %s", cptr); } else CgiLibCgiPlusCallout ("!AGENT-END: 200 %s", cptr); CgiLibCgiPlusEOF (); } } /*****************************************************************************/ /* If |name| is non-NULL lookup the IP address using the host name. If |addr| is non-NULL lookup the host name using the address. */ char* TcpIpLookup ( char *name, char *addr ) { static char buf [1024]; int retry, retval; char *cptr, *sptr, *zptr; void *addptr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; struct addrinfo hints; struct addrinfo *aiptr, *resaiptr; /*********/ /* begin */ /*********/ if (addr) { retval = 0; memset (&addr4, 0, sizeof(addr4)); if (inet_pton (AF_INET, addr, &addr4.sin_addr) > 0) { /* MultiNet does not support BSD 4.4 AF_INET addresses */ #ifdef NO_SOCKADDR_LEN_4 /* this kludge seems to work for both! */ *(USHORTPTR)&addr4 = AF_INET; #else addr4.sin_len = sizeof(struct sockaddr_in); addr4.sin_family = AF_INET; #endif for (retry = LOOKUP_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr4, sizeof(addr4), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) strcpy (buf, "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) strcpy (buf, "[failed]"); else sprintf (buf, "[%s]", gai_strerror(retval)); } return (buf); } else { memset (&addr6, 0, sizeof(addr6)); if (inet_pton (AF_INET6, addr, &addr6.sin6_addr) > 0) { addr6.sin6_len = sizeof(struct sockaddr_in6); addr6.sin6_family = AF_INET6; for (retry = LOOKUP_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr6, addr6.sin6_len, buf, sizeof(buf), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) strcpy (buf, "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) strcpy (buf, "[failed]"); else sprintf (buf, "[%s]", gai_strerror(retval)); } } return (buf); } } if (name) { aiptr = NULL; memset (&hints, 0, sizeof(hints)); hints.ai_flags |= AI_CANONNAME; retval = 0; for (retry = LOOKUP_RETRY; retry; retry--) { retval = getaddrinfo (name, NULL, &hints, &resaiptr); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (buf, "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (buf, "[failed]"); else sprintf (buf, "[%s]", gai_strerror(retval)); return (buf); } else { /* potentially multiple addresses for the one host name */ zptr = (sptr = buf) + sizeof(buf)-8; for (aiptr = resaiptr; aiptr; aiptr = aiptr->ai_next) { if (aiptr->ai_family == AF_INET) { /* IPv4 */ addptr = &((struct sockaddr_in *)aiptr->ai_addr)->sin_addr; } else { /* must be IPv6 */ addptr = &((struct sockaddr_in6 *)aiptr->ai_addr)->sin6_addr; } if (sptr > buf) *sptr++ = ' '; if (!inet_ntop (aiptr->ai_family, addptr, sptr, sptr - buf)) { sprintf (buf, "[%s]", strerror(errno)); break; } while (*sptr) sptr++; } } /* free the addrinfo */ freeaddrinfo(aiptr); return (buf); } return ("[bugcheck]"); } /*****************************************************************************/