home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xhost / xhost.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-19  |  15.7 KB  |  679 lines

  1. /* $XConsortium: xhost.c,v 11.48 91/07/19 18:41:15 rws Exp $ */
  2.  
  3. /*
  4.  
  5. Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
  6.  
  7. Permission to use, copy, modify, and distribute this
  8. software and its documentation for any purpose and without
  9. fee is hereby granted, provided that the above copyright
  10. notice appear in all copies and that both that copyright
  11. notice and this permission notice appear in supporting
  12. documentation, and that the name of M.I.T. not be used in
  13. advertising or publicity pertaining to distribution of the
  14. software without specific, written prior permission.
  15. M.I.T. makes no representations about the suitability of
  16. this software for any purpose.  It is provided "as is"
  17. without express or implied warranty.
  18.  
  19. */
  20.  
  21. /* sorry, streams support does not really work yet */
  22. #if defined(STREAMSCONN) && defined(SVR4)
  23. #undef STREAMSCONN
  24. #define TCPCONN
  25. #endif
  26.  
  27. #ifdef TCPCONN
  28. #define NEEDSOCKETS
  29. #endif
  30. #ifdef UNIXCONN
  31. #define NEEDSOCKETS
  32. #endif
  33. #ifdef DNETCONN
  34. #define NEEDSOCKETS
  35. #endif
  36.  
  37. #include <X11/Xlib.h>
  38. #include <X11/Xos.h>
  39. #include <X11/Xproto.h>
  40. #include <X11/Xfuncs.h>
  41. #include <stdio.h>
  42. #include <signal.h>
  43. #include <setjmp.h>
  44. #include <ctype.h>
  45. #include <X11/Xauth.h>
  46. #include <X11/Xmu/Error.h>
  47.  
  48. #ifdef NEEDSOCKETS
  49. #ifdef att
  50. typedef unsigned short unsign16;
  51. typedef unsigned long unsign32;
  52. typedef short sign16;
  53. typedef long sign32;
  54. #include <interlan/socket.h>
  55. #include <interlan/netdb.h>
  56. #include <interlan/in.h>
  57. #else
  58. #include <sys/socket.h>
  59. #include <netdb.h>
  60. #include <netinet/in.h>
  61. #endif
  62. #endif /* NEEDSOCKETS */
  63.  
  64. #ifdef notdef
  65. #include <arpa/inet.h>
  66.     bogus definition of inet_makeaddr() in BSD 4.2 and Ultrix
  67. #else
  68. #ifndef hpux
  69. extern unsigned long inet_makeaddr();
  70. #endif
  71. #endif
  72. #ifdef DNETCONN
  73. #include <netdnet/dn.h>
  74. #include <netdnet/dnetdb.h>
  75. #endif
  76. #ifdef STREAMSCONN
  77. #include <Xstreams.h>
  78. extern char _XsTypeOfStream[];
  79. #endif /* STREAMSCONN */
  80.  
  81. #ifdef SECURE_RPC
  82. #include <pwd.h>
  83. #include <rpc/rpc.h>
  84. #ifndef X_NOT_POSIX
  85. #ifdef _POSIX_SOURCE
  86. #include <limits.h>
  87. #else
  88. #define _POSIX_SOURCE
  89. #include <limits.h>
  90. #undef _POSIX_SOURCE
  91. #endif
  92. #endif
  93. #ifndef NGROUPS_MAX
  94. #include <sys/param.h>
  95. #define NGROUPS_MAX NGROUPS
  96. #endif
  97. #endif
  98.  
  99. static int local_xerror();
  100. static char *get_hostname();
  101. #ifdef STREAMSCONN
  102. static char *get_streams_hostname();
  103. static Bool get_streams_address();
  104. #endif
  105.  
  106. #ifdef SIGNALRETURNSINT
  107. #define signal_t int
  108. #else
  109. #define signal_t void
  110. #endif
  111. static signal_t nameserver_lost();
  112.  
  113. #define NAMESERVER_TIMEOUT 5    /* time to wait for nameserver */
  114.  
  115. int nameserver_timedout;
  116.  
  117. char *ProgramName;
  118.  
  119. #ifdef NEEDSOCKETS
  120. static int XFamily(af)
  121.     int af;
  122. {
  123.     int i;
  124.     static struct _familyMap {
  125.     int af, xf;
  126.     } familyMap[] = {
  127. #ifdef    AF_DECnet
  128.         { AF_DECnet, FamilyDECnet },
  129. #endif
  130. #ifdef    AF_CHAOS
  131.         { AF_CHAOS, FamilyChaos },
  132. #endif
  133. #ifdef    AF_INET
  134.         { AF_INET, FamilyInternet },
  135. #endif
  136. };
  137.  
  138. #define FAMILIES ((sizeof familyMap)/(sizeof familyMap[0]))
  139.  
  140.     for (i = 0; i < FAMILIES; i++)
  141.     if (familyMap[i].af == af) return familyMap[i].xf;
  142.     return -1;
  143. }
  144. #endif /* NEEDSOCKETS */
  145.  
  146. Display *dpy;
  147.  
  148. main(argc, argv)
  149.     int argc;
  150.     char **argv;
  151. {
  152.     register char *arg;
  153.     int i, nhosts;
  154.     char *hostname;
  155.     XHostAddress *list;
  156.     Bool enabled = False;
  157. #ifdef DNETCONN
  158.     char *dnet_htoa();
  159.     struct nodeent *np;
  160.     struct dn_naddr *nlist, dnaddr, *dnaddrp, *dnet_addr();
  161.     char *cp;
  162. #endif
  163.  
  164.     ProgramName = argv[0];
  165.  
  166.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  167.         fprintf(stderr, "%s:  unable to open display \"%s\"\n",
  168.             ProgramName, XDisplayName (NULL));
  169.         exit(1);
  170.     }
  171.  
  172.     XSetErrorHandler(local_xerror);
  173.  
  174.  
  175.     if (argc == 1) {
  176. #ifdef DNETCONN
  177.         setnodeent(1); /* keep the database accessed */
  178. #endif
  179.         sethostent(1); /* don't close the data base each time */
  180.         list = XListHosts(dpy, &nhosts, &enabled);
  181.         if (enabled)
  182.             printf ("access control enabled, only authorized clients can connect\n");
  183.         else
  184.             printf ("access control disabled, clients can connect from any host\n");
  185.  
  186.         if (nhosts != 0) {
  187.             for (i = 0; i < nhosts; i++ )  {
  188.               hostname = get_hostname(&list[i]);
  189.               if (hostname) {
  190.               printf ("%s", hostname);
  191.               } else {
  192. #ifdef STREAMSCONN
  193.               print_streams_hostnames (list, nhosts);
  194. #else
  195.               printf ("<unknown address in family %d>",
  196.                   list[i].family);
  197. #endif
  198.               }
  199.               if (nameserver_timedout)
  200.             printf("\t(no nameserver response within %d seconds)\n",
  201.                     NAMESERVER_TIMEOUT);
  202.               else printf("\n");
  203.             }
  204.             free(list);
  205.             endhostent();
  206.         }
  207.         exit(0);
  208.     }
  209.  
  210.     if (argc == 2 && !strcmp(argv[1], "-help")) {
  211.         fprintf(stderr, "usage: %s [[+-]hostname ...]\n", argv[0]);
  212.         exit(1);
  213.     }
  214.  
  215.     for (i = 1; i < argc; i++) {
  216.         arg = argv[i];
  217.         if (*arg == '-') {
  218.         
  219.             if (!argv[i][1] && ((i+1) == argc)) {
  220.             printf ("access control enabled, only authorized clients can connect\n");
  221.             XEnableAccessControl(dpy);
  222.         } else {
  223.             arg = argv[i][1]? &argv[i][1] : argv[++i];
  224.             if (!change_host (dpy, arg, False)) {
  225.             fprintf (stderr, "%s:  bad hostname \"%s\"\n",
  226.                  ProgramName, arg);
  227.             }
  228.         }
  229.         } else {
  230.             if (*arg == '+' && !argv[i][1] && ((i+1) == argc)) {
  231.             printf ("access control disabled, clients can connect from any host\n");
  232.             XDisableAccessControl(dpy);
  233.         } else {
  234.             if (*arg == '+') {
  235.               arg = argv[i][1]? &argv[i][1] : argv[++i];
  236.             }
  237.             if (!change_host (dpy, arg, True)) {
  238.             fprintf (stderr, "%s:  bad hostname \"%s\"\n",
  239.                  ProgramName, arg);
  240.             }
  241.         }
  242.         }
  243.     }
  244.     XCloseDisplay (dpy);  /* does an XSync first */
  245.     exit(0);
  246. }
  247.  
  248.  
  249.  
  250. /*
  251.  * change_host - edit the list of hosts that may connect to the server;
  252.  * it parses DECnet names (expo::), Internet addresses (18.30.0.212), or
  253.  * Internet names (expo.lcs.mit.edu); if 4.3bsd macro h_addr is defined
  254.  * (from <netdb.h>), it will add or remove all addresses with the given
  255.  * address.
  256.  */
  257.  
  258. int change_host (dpy, name, add)
  259.     Display *dpy;
  260.     char *name;
  261.     Bool add;
  262. {
  263.   struct hostent *hp;
  264.   XHostAddress ha;
  265. #ifdef NEEDSOCKETS
  266.   static struct in_addr addr;    /* so we can point at it */
  267. #endif
  268.   char *cp;
  269. #ifdef DNETCONN
  270.   struct dn_naddr *dnaddrp;
  271.   struct nodeent *np;
  272.   static struct dn_naddr dnaddr;
  273. #endif /* DNETCONN */
  274.   static char *add_msg = "being added to access control list";
  275.   static char *remove_msg = "being removed from access control list";
  276.  
  277. #ifdef DNETCONN
  278.   if ((cp = index (name, ':')) && (*(cp + 1) == ':')) {
  279.     *cp = '\0';
  280.     ha.family = FamilyDECnet;
  281.     if (dnaddrp = dnet_addr(name)) {
  282.       dnaddr = *dnaddrp;
  283.     } else {
  284.       if ((np = getnodebyname (name)) == NULL) {
  285.       fprintf (stderr, "%s:  unble to get node name for \"%s::\"\n",
  286.            ProgramName, name);
  287.       return 0;
  288.       }
  289.       dnaddr.a_len = np->n_length;
  290.       bcopy (np->n_addr, dnaddr.a_addr, np->n_length);
  291.     }
  292.     ha.length = sizeof(struct dn_naddr);
  293.     ha.address = (char *)&dnaddr;
  294.     if (add) {
  295.     XAddHost (dpy, &ha);
  296.     printf ("%s:: %s\n", name, add_msg);
  297.     } else {
  298.     XRemoveHost (dpy, &ha);
  299.     printf ("%s:: %s\n", name, remove_msg);
  300.     }
  301.     return 1;
  302.   }
  303. #endif /* DNETCONN */
  304.     /*
  305.      * If it has an '@',  its a netname
  306.      */
  307.     if (cp = index(name, '@')) {
  308.     char *netname = name;
  309. #ifdef SECURE_RPC
  310.     static char username[MAXNETNAMELEN];
  311.  
  312.     if (!cp[1]) {
  313.         struct passwd *pwd;
  314.         static char domainname[128];
  315.  
  316.         *cp = '\0';
  317.         pwd = getpwnam(name);
  318.         if (!pwd) {
  319.         fprintf(stderr, "no such user \"%s\"\n", name);
  320.         return 0;
  321.         }
  322.         getdomainname(domainname, sizeof(domainname));
  323.         if (!user2netname(username, pwd->pw_uid, domainname)) {
  324.         fprintf(stderr, "failed to get netname for \"%s\"\n", name);
  325.         return 0;
  326.         }
  327.         netname = username;
  328.     }
  329. #endif
  330.     ha.family = FamilyNetname;
  331.     ha.length = strlen(netname);
  332.     ha.address = netname;
  333.     if (add)
  334.         XAddHost (dpy, &ha);
  335.     else
  336.         XRemoveHost (dpy, &ha);
  337.     if (netname != name)
  338.         printf ("%s@ (%s) %s\n", name, netname, add ? add_msg : remove_msg);
  339.     else
  340.         printf ("%s %s\n", netname, add ? add_msg : remove_msg);
  341.         return 1;
  342.     }
  343. #ifdef STREAMSCONN
  344.   if (get_streams_address (name, &ha)) {
  345.     if (add) {
  346.     XAddHost (dpy, &ha);
  347.     printf ("%s %s\n", name, add_msg);
  348.     } else {
  349.     XRemoveHost (dpy, &ha);
  350.     printf ("%s %s\n", name, remove_msg);
  351.     }
  352.     return 1;
  353.   }
  354. #endif
  355. #ifdef NEEDSOCKETS
  356.   /*
  357.    * First see if inet_addr() can grok the name; if so, then use it.
  358.    */
  359.   if ((addr.s_addr = inet_addr(name)) != -1) {
  360.     ha.family = FamilyInternet;
  361.     ha.length = sizeof(addr.s_addr);
  362.     ha.address = (char *)&addr.s_addr;
  363.     if (add) {
  364.     XAddHost (dpy, &ha);
  365.     printf ("%s %s\n", name, add_msg);
  366.     } else {
  367.     XRemoveHost (dpy, &ha);
  368.     printf ("%s %s\n", name, remove_msg);
  369.     }
  370.     return 1;
  371.   } 
  372.   /*
  373.    * Is it in the namespace?
  374.    */
  375.   else if (((hp = gethostbyname(name)) == (struct hostent *)NULL)
  376.        || hp->h_addrtype != AF_INET) {
  377.     return 0;
  378.   } else {
  379.     ha.family = XFamily(hp->h_addrtype);
  380.     ha.length = hp->h_length;
  381. #ifdef h_addr                /* new 4.3bsd version of gethostent */
  382.     {
  383.     char **list;
  384.  
  385.     /* iterate over the hosts */
  386.     for (list = hp->h_addr_list; *list; list++) {
  387.         ha.address = *list;
  388.         if (add) {
  389.         XAddHost (dpy, &ha);
  390.         } else {
  391.         XRemoveHost (dpy, &ha);
  392.         }
  393.     }
  394.     }
  395. #else
  396.     ha.address = hp->h_addr;
  397.     if (add) {
  398.     XAddHost (dpy, &ha);
  399.     } else {
  400.     XRemoveHost (dpy, &ha);
  401.     }
  402. #endif
  403.     printf ("%s %s\n", name, add ? add_msg : remove_msg);
  404.     return 1;
  405.   }
  406. #endif /* NEEDSOCKETS */
  407. }
  408.  
  409.  
  410. /*
  411.  * get_hostname - Given an internet address, return a name (CHARON.MIT.EDU)
  412.  * or a string representing the address (18.58.0.13) if the name cannot
  413.  * be found.
  414.  */
  415.  
  416. jmp_buf env;
  417.  
  418. static char *get_hostname (ha)
  419.     XHostAddress *ha;
  420. {
  421. #ifdef TCPCONN
  422.   struct hostent *hp = NULL;
  423.   char *inet_ntoa();
  424. #endif
  425. #ifdef DNETCONN
  426.   struct nodeent *np;
  427.   static char nodeaddr[16];
  428. #endif /* DNETCONN */
  429.  
  430. #ifdef TCPCONN
  431.   if (ha->family == FamilyInternet) {
  432.     /* gethostbyaddr can take a LONG time if the host does not exist.
  433.        Assume that if it does not respond in NAMESERVER_TIMEOUT seconds
  434.        that something is wrong and do not make the user wait.
  435.        gethostbyaddr will continue after a signal, so we have to
  436.        jump out of it. 
  437.        */
  438.     nameserver_timedout = 0;
  439.     signal(SIGALRM, nameserver_lost);
  440.     alarm(4);
  441.     if (setjmp(env) == 0) {
  442.       hp = gethostbyaddr (ha->address, ha->length, AF_INET);
  443.     }
  444.     alarm(0);
  445.     if (hp)
  446.       return (hp->h_name);
  447.     else return (inet_ntoa(*((struct in_addr *)(ha->address))));
  448.   }
  449. #endif
  450.   if (ha->family == FamilyNetname) {
  451.     static char netname[512];
  452.     int len;
  453. #ifdef SECURE_RPC
  454.     int uid, gid, gidlen, gidlist[NGROUPS_MAX];
  455. #endif
  456.  
  457.     if (ha->length < sizeof(netname) - 1)
  458.         len = ha->length;
  459.     else
  460.         len = sizeof(netname) - 1;
  461.     bcopy(ha->address, netname, len);
  462.     netname[len] = '\0';
  463. #ifdef SECURE_RPC
  464.     if (netname2user(netname, &uid, &gid, &gidlen, gidlist)) {
  465.     struct passwd *pwd;
  466.     char *cp;
  467.  
  468.     pwd = getpwuid(uid);
  469.     if (pwd)
  470.         sprintf(netname, "%s@ (%*.*s)", pwd->pw_name,
  471.             ha->length, ha->length, ha->address);
  472.     }
  473. #endif
  474.     return (netname);
  475.   }
  476. #ifdef DNETCONN
  477.   if (ha->family == FamilyDECnet) {
  478.     if (np = getnodebyaddr(ha->address, ha->length, AF_DECnet)) {
  479.       sprintf(nodeaddr, "%s::", np->n_name);
  480.     } else {
  481.       sprintf(nodeaddr, "%s::", dnet_htoa(ha->address));
  482.     }
  483.     return(nodeaddr);
  484.   }
  485. #endif
  486. #ifdef STREAMSCONN
  487.   return get_streams_hostname (ha);
  488. #else
  489.   return (NULL);
  490. #endif
  491. }
  492.  
  493. static signal_t nameserver_lost()
  494. {
  495.   nameserver_timedout = 1;
  496.   longjmp(env, -1);
  497. }
  498.  
  499. /*
  500.  * local_xerror - local non-fatal error handling routine. If the error was
  501.  * that an X_GetHosts request for an unknown address format was received, just
  502.  * return, otherwise print the normal error message and continue.
  503.  */
  504. static int local_xerror (dpy, rep)
  505.     Display *dpy;
  506.     XErrorEvent *rep;
  507. {
  508.     if ((rep->error_code == BadAccess) && (rep->request_code == X_ChangeHosts)) {
  509.     fprintf (stderr, 
  510.          "%s:  must be on local machine to add or remove hosts.\n",
  511.          ProgramName);
  512.     return 1;
  513.     } else if ((rep->error_code == BadAccess) && 
  514.            (rep->request_code == X_SetAccessControl)) {
  515.     fprintf (stderr, 
  516.     "%s:  must be on local machine to enable or disable access control.\n",
  517.          ProgramName);
  518.     return 1;
  519.     } else if ((rep->error_code == BadValue) && 
  520.            (rep->request_code == X_ListHosts)) {
  521.     return 1;
  522.     }
  523.  
  524.     XmuPrintDefaultErrorMessage (dpy, rep, stderr);
  525.     return 0;
  526. }
  527.  
  528.  
  529. #ifdef STREAMSCONN
  530. static Bool get_streams_address (name, hap) 
  531.     char *name;
  532.     XHostAddress *hap;
  533. {
  534.   static char buf[128];
  535.   char     *ptr, *packet, *retptr, pktbuf[128];
  536.   int     n;
  537.  
  538.  
  539.   if(_XsTypeOfStream[ConnectionNumber(dpy)]  == X_LOCAL_STREAM)
  540.   {
  541.     hap->family = FamilyUname;
  542.     hap->length = strlen(name) +1;
  543.     hap->address = name;
  544.     return True;
  545.   }
  546.  
  547.   packet = pktbuf;
  548.   ptr = &packet[2*sizeof(int)];
  549.  
  550.   n = strlen(name) + 1;
  551.   ((xHostEntry *) ptr)->length = n;
  552.   ptr += sizeof(xHostEntry);
  553.   memcpy(ptr, name, n);
  554.  
  555.   retptr = packet;
  556.    *(int *) retptr = n+sizeof(xHostEntry);
  557.    *(int *) (retptr + sizeof(int)) = 1;
  558.  
  559.   if(GetNetworkInfo (ConnectionNumber(dpy), NULL, ConvertNameToNetAddr, &packet, &retptr, NULL)<0)
  560.            {
  561.         return False;
  562.            }
  563.    hap->family = ((xHostEntry *) retptr)->family;
  564.    hap->length = ((xHostEntry *) retptr)->length;
  565.    hap->address = buf;
  566.   
  567.    if(hap->length > 127)
  568.        hap->length = 127;
  569.  
  570.    /* trim internet address to four */
  571.    if (hap->family == FamilyInternet)
  572.     hap->length = 4;
  573.  
  574.    ptr = &retptr[sizeof(xHostEntry)];
  575.    memcpy(buf, ptr, hap->length);
  576.    buf[hap->length] = '\0';
  577.    return True;
  578. }
  579.  
  580.  
  581. print_streams_hostnames (list, nhosts)
  582.     XHostAddress *list;
  583.     int nhosts;
  584. {
  585.     int  m, n, i;
  586.     char *ptr, *retptr;
  587.     static char *packet = NULL;
  588.     static int buflen = 0;
  589.  
  590.     if(buflen == 0)
  591.         buflen = 512;
  592.  
  593.     m = 2 * sizeof(int);
  594.     packet = (char *) malloc (buflen);
  595.     if(packet == NULL){
  596.     fprintf(stderr, "Cannot malloc %d chars \n", buflen);
  597.     return;
  598.     }
  599.     ptr = &packet[m];
  600.  
  601.     for (i=0; i< nhosts; i++)
  602.     {
  603.     n = (((list[i].length + 3) >> 2) << 2) + sizeof(xHostEntry);
  604.     m += n;
  605.     if(m > buflen){
  606.         buflen = m + 128;
  607.         packet = (char *) realloc(packet, buflen);
  608.             if(packet == NULL){
  609.             fprintf(stderr, "Cannot realloc %d chars \n", buflen);
  610.             return;
  611.             }
  612.         }
  613.     ptr = &packet[m - n];
  614.     ((xHostEntry *) ptr)->length  = list[i].length;
  615.     ((xHostEntry *) ptr)->family  = list[i].family;
  616.     ptr += sizeof(xHostEntry);
  617.     bcopy (list[i].address, ptr, list[i].length);
  618.     }
  619.     *(int *) packet = m;
  620.     *(int *) (packet + sizeof(int)) = nhosts;
  621.     if(_XsTypeOfStream[ConnectionNumber(dpy)] != X_LOCAL_STREAM){
  622.         n =
  623.  GetNetworkInfo (ConnectionNumber(dpy), NULL,ConvertNetAddrToName, &packet, &retptr, &nhosts);
  624.     if( n <= 0){
  625.         fprintf(stderr, "No reply from the nameserver\n");
  626.         return;
  627.         }
  628.     }
  629.       else retptr = &packet[2*sizeof(int)];
  630.      m = 0;
  631.      for(i=0; i<nhosts; i++){
  632.     ptr = &retptr[m];
  633.          n = ((xHostEntry *) ptr)->length;
  634.     n = (((n + 3) >> 2) << 2) + sizeof(xHostEntry);
  635.     m += n;
  636.          ptr += sizeof(xHostEntry);
  637.      fprintf(stderr, "%s\n", ptr);    
  638.      }        
  639.      free(retptr);
  640. }
  641.  
  642. static char *get_streams_hostname (ha)
  643.     XHostAddress *ha;
  644. {
  645.   static char buf[128];
  646.   char     *ptr, *packet, pktbuf[128], *retptr;
  647.   int     n, len;
  648.  
  649.    if(_XsTypeOfStream[ConnectionNumber(dpy)] == X_LOCAL_STREAM || ha->family == FamilyUname){
  650.     return(ha->address);
  651.   }
  652.  
  653.   packet = pktbuf;
  654.   ptr = &packet[2*sizeof(int)];
  655.  
  656.   ((xHostEntry *) ptr)->length = ha->length;
  657.   ((xHostEntry *) ptr)->family = ha->family;
  658.  
  659.   ptr += sizeof(xHostEntry);
  660.   memcpy(ptr, ha->address, ha->length);
  661.  
  662.    retptr = packet;
  663.    *(int *) retptr = ha->length+sizeof(xHostEntry);
  664.    *(int *) (retptr + sizeof(int)) = 1;
  665.  
  666.   if(GetNetworkInfo (ConnectionNumber(dpy), NULL, ConvertNetAddrToName, &packet, &retptr, NULL)<0)
  667.            {
  668.         ha->address[ha->length] = '\0';
  669.         return(ha->address);
  670.            }
  671.    ptr = &retptr[sizeof(xHostEntry)];
  672.    len = ((xHostEntry *) retptr)->length;
  673.    memcpy(buf, ptr, len);
  674.    buf[len] = '\0';
  675.    return(buf);
  676. }
  677.  
  678. #endif /* STREAMSCONN */
  679.