[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
/*****************************************************************************/
/*
                                 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: <software-id>
        (reply) <IPv4-or-IPv6>          to resolve address->name
        (reply) <host-name>             to resolve name->address

   !AGENT-END: 200 <host-name>          to return resolved address->name
   !AGENT-END: 200 <IPv4-or-IPv6>       to return resolved name->address
   !AGENT-END: 418 <string>             to drop the connection
   !AGENT-END: 5nn <error-string>       to the agent functionality only
   !AGENT-END: 5nn %X<xxxxxxxx>         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 <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <types.h>
#include <unixlib.h>
#include <unistd.h>

#include <ssdef.h>

#include <socket.h>
#include <in.h>
#include <netdb.h>
#include <inet.h>

#include <cgilib.h>

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]");
}

/*****************************************************************************/