File: getaddrinfo.c

package info (click to toggle)
libflorist 2006-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 1,696 kB
  • ctags: 314
  • sloc: ada: 10,587; ansic: 7,869; makefile: 199
file content (797 lines) | stat: -rw-r--r-- 27,958 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
/*
 * Copyright (c) 1996 W. Richard Stevens.  All rights reserved. 
 *
 * Permission to use or modify this software for educational or
 * for commercial purposes, and without fee, is hereby granted,
 * provided that the above copyright notice appears in connection
 * with any and all uses, with clear indication as to any
 * modifications made.  The author RESERVES the sole rights of
 * reproduction, publication and distribution and hence permission
 * to print this source code in any book, reference manual,
 * magazine, or other type of publication, including any digital
 * medium, must be granted in writing by W. Richard Stevens.
 *
 * The author makes no representations about the suitability of this 
 * software for any purpose.  It is provided "as is" without express
 * or implied warranty. 
 */

#include   "pconfig.h"
#ifndef MAXDNAME
#include   <arpa/nameser.h>
#endif
#ifndef RES_INIT
#include <netinet/in.h>
#include <resolv.h>
#endif

/* Needed on Dec Unix. Doesn't hurt on other OS ??? */
#undef _OSF_SOURCE

#include <netdb.h>

/* NOTE: this code assumes you have the inet_pton() function as defined
 * in the BIND-4.9.4 release or later (ftp://ftp.vix.com/pub/bind).
 * If you don't have this in your resolver library, in this tar file is
 * a copy of it, along with inet_ntop(). */

/* We need a way to determine if the compiling host supports IPv4/IPv6.
 * Cannot test for AF_INET6, as some vendors #define it, even though
 * they don't support it. */

#ifdef   AF_INET
#define   IPV4   /* this is tested throughout the code that follows */
#endif

#ifdef   IPV6ADDR_ANY_INIT
#define   IPV6   /* this is tested throughout the code that follows */
#endif

/* There is no requirement that getaddrinfo() support Unix domain sockets,
 * but what the heck, it doesn't take too much code, and it's a real good
 * test whether an application is *really* protocol-independent. */

#ifdef   AF_UNIX
#define   LOCAL   /* this is tested throughout the code that follows */

#ifndef   AF_LOCAL
#define   AF_LOCAL   AF_UNIX      /* the Posix.1g correct term */
#endif
#ifndef   PF_LOCAL
#define   PF_LOCAL   PF_UNIX      /* the Posix.1g correct term */
#endif

#endif   /* AF_UNIX */

/* Define REENTRANT if the reentrant versions of get{host|serv}byname
 * are to be used.
 * Note that getaddrinfo{} *must* be reentrant if the underlying
 * get{host|serv}byname_r functions are provided.
 * This program has only been tested with the Solaris 2.x functions.
 * (I have no idea if other vendors have the same functions or not.)
 *
 * Long diatribe: Don't define REENTRANT.  At least not until you know
 * what your vendor's gethostbyname_r() function does with regard to
 * IPv4/IPv6 addresses.  If you really need a reentrant version of this
 * function, because you call it from different threads, then use a
 * mutex lock to protect the calls.
 * The problem at the time of this writing is the handling of IPv4/IPv6
 * addresses.  BIND-4.9.4 does it the "right" way :-), but this won't
 * be documented until a later revision of the IPv6 BSD API spec.  Also
 * BIND-4.9.4 doesn't provide the reentrant _r() functions, and I have
 * no idea what the vendors like Sun have done with these functions.
 * The code far below that calls gethostbyname() sets the resolver
 * RES_USE_INET6 option if the caller specifies an ai_family of AF_INET6.
 * This causes 16-byte addresses to be returned, regardless, either
 * "true" IPv6 address from AAAA records, or IPv4-mapped IPv6 addresses
 * from A records.  If the caller specifies an ai_family of AF_INET6,
 * then we should return 16-byte addresses.
 * With BIND-4.9.4 the caller can also force the return of 16-byte addresses
 * by setting the environment variable RES_OPTIONS, as in
 *    % RES_OPTIONS=inet6 ./a.out arguments...
 * This way the caller need not pollute the code with code like
 * ai_family = AF_INET6, making the code protocol-dependent.
 */

/* #define   REENTRANT  */

/* Define following function prototype if your headers don't define it */

int    inet_pton(int, const char *, void *);

#define   HENTBUFSIZ   8*1024
#define   HENTMAXADDR  32 /* max binary address: 16 for IPv4, 24 for IPv6 */

      /* following internal flags cannot overlap with other AI_xxx flags */
#define   AI_CLONE  4   /* clone this entry for other socket types */

      /* function prototypes for our own internal functions */
static int   getaddrinfo_host(const char *, struct hostent *,
                      struct hostent **, char *, int, int);
static int   getaddrinfo_serv(struct addrinfo *,
                      const struct addrinfo *, const char *,
                      struct servent *, char *, int);
static int   getaddrinfo_port(struct addrinfo *, int , int);
static int   addrinfo_local(const char *, struct addrinfo *,
                     struct addrinfo **);
static struct addrinfo   *getaddrinfo_clone(struct addrinfo *);

      /* globals for all functions in this file; these *must* be
         read-only if this function is to be reentrant */
static struct addrinfo   hints_default;

int
getaddrinfo(const char *host, const char *serv,
         const struct addrinfo *hintsptr, struct addrinfo **result)
{
   int               rc, error;
   struct hostent      *hptr, hent;
   struct servent      sent;
   char            hentbuf[HENTBUFSIZ], hent_addr[HENTMAXADDR];
   char            *hent_aliases[1], *hent_addr_list[2];
   char            **ap;
   struct addrinfo      hints, *ai, *aihead, **aipnext;

#ifdef   IPV4
   struct sockaddr_in   *sinptr;
#endif

#ifdef   IPV6
   struct sockaddr_in6   *sin6ptr;
#endif

/* If we encounter an error we want to free() any dynamic memory
   that we've allocated.  This is our hack to simplify the code. */
#define   error(e) { error = (e); goto bad; }


/* debug -- see what the Ada binding passed in                     
 printf (" getaddrinfo(): hptr=%d\n", host);                     
 if (host != NULL) {printf ("                host=%s\n", host);} 
 printf ("                sptr=%d\n", serv);                     
 if (serv != NULL) {printf ("                serv=%s\n", serv);} 
 printf ("                hptr=%d\n", hintsptr);                 
 if (hintsptr != NULL) {                                         
 printf ("         hints.flags=%d\n", hintsptr->ai_flags);       
 printf ("             .family=%d\n", hintsptr->ai_family);      
 printf ("           .socktype=%d\n", hintsptr->ai_socktype);    
 printf ("           .protocol=%d\n", hintsptr->ai_protocol);}
*/
   /*
    * We must make a copy of the caller's hints structure, so we can
    * modify ai_family.  If the caller doesn't provide a hints structure,
    * use a default one.  This simplifies all the following code.
    * In the default one, ai_flags, ai_socktype, and ai_protocol are all 0,
    * but we have to set ai_family to PF_UNSPEC, which isn't guaranteed to
    * be 0.
    */ 
   if (hintsptr == NULL) {
      hints_default.ai_family = PF_UNSPEC;
      hints = hints_default;   /* struct copy */
   } else
      hints = *hintsptr;      /* struct copy */

   /*
    * First some error checking.
    */
   if (hints.ai_flags & ~(AI_PASSIVE | AI_CANONNAME))
      error(EAI_BADFLAGS);   /* unknown flag bits */

   /*
    * Check that the family is valid, and if a socket type is also
    * specified, check that it's valid for the family.
    */
   if (hints.ai_family != 0) {
      switch(hints.ai_family) {
         case PF_UNSPEC:   break;
            /* Actually, PF_UNSPEC is normally defined as 0,
               but Posix does not require this. */
#ifdef   IPV4
         case PF_INET:
            if (hints.ai_socktype != 0 &&
               (hints.ai_socktype != SOCK_STREAM &&
                hints.ai_socktype != SOCK_DGRAM &&
                hints.ai_socktype != SOCK_RAW))
               error(EAI_SOCKTYPE);   /* invalid socket type */
            break;
#endif
#ifdef   IPV6
         case PF_INET6:
            if (hints.ai_socktype != 0 &&
               (hints.ai_socktype != SOCK_STREAM &&
                hints.ai_socktype != SOCK_DGRAM &&
                hints.ai_socktype != SOCK_RAW))
               error(EAI_SOCKTYPE);   /* invalid socket type */
            break;
#endif
#ifdef   LOCAL
         case PF_LOCAL:
            if (hints.ai_socktype != 0 &&
               (hints.ai_socktype != SOCK_STREAM &&
                hints.ai_socktype != SOCK_DGRAM))
               error(EAI_SOCKTYPE);   /* invalid socket type */
            break;
#endif
      default:
         error(EAI_FAMILY);      /* unknown protocol family */
      }
   }

   if (host == NULL || host[0] == '\0') {
      if (serv == NULL || serv[0] == '\0')
         error(EAI_NONAME);   /* either host or serv must be specified */

      if (hints.ai_flags & AI_PASSIVE) {
         /*
          * No "host" and AI_PASSIVE: the returned address must be
          * ready for bind(): 0.0.0.0 for IPv4 or 0::0 for IPv6.
          */
         switch (hints.ai_family) {
#ifdef   IPV4
         case PF_INET:   host = "0.0.0.0"; break;
#endif
#ifdef   IPV6
         case PF_INET6:   host = "0::0"; break;
#endif
#ifdef   LOCAL
         case PF_LOCAL:
            if (serv[0] != '/')      /* allow service to specify path */
               error(EAI_ADDRFAMILY);
            break;
#endif
         case 0:         error(EAI_ADDRFAMILY);
               /* How can we initialize a socket address structure
                  for a passive open if we don't even know the family? */
         }
      } else {
         /*
          * No host and not AI_PASSIVE: caller implies connect() to
          * local host.
          */

         host = "localhost";
      }
   } else if (hints.ai_family == 0) {
      /*
       * Caller specifies a host but no address family.
       * If the host string is really a valid IPv4 dotted-decimal address,
       * set family to IPv4.  Similarly for IPv6 strings.
       * This allows server applications to be protocol independent
       * (not having to hard code a protocol family), allowing the
       * user who starts the program to specify either 0.0.0.0 or 0::0.
       *
       * Assumed below is that inet_pton() allows only "valid" strings,
       * which Paul Vixie put into the BIND-4.9.4 version of this function.
       */

      char   temp[16];

#ifdef   IPV4
      if (inet_pton(AF_INET, host, temp) == 1)
         hints.ai_family = AF_INET;
#endif
#ifdef   IPV6
      if (inet_pton(AF_INET6, host, temp) == 1)
         hints.ai_family = AF_INET6;
#endif
      /*
       * Note that we could bypass some of the testing done in
       * getaddrinfo_host(), but it doesn't seem worth complicating
       * this (already long) function.
       */
   }

#ifdef   LOCAL
   /*
    * For a Unix domain socket only one string can be provided and we
    * require it to be an absolute pathname.  (Using relative pathnames
    * is asking for trouble.)  We allow this string to be specified as
    * either the hostname or the service name, in which case we ignore
    * the other string.  Notice that a slash is not allowed in a DNS
    * hostname (see RFC 1912) and a slash does not appear in any of the
    * service names in /etc/services either.  Hence no conflict.
    * For example, often a protocol-independent server will allow an
    * argument to specify the service (e.g., port number) and let the
    * hostname be wildcarded.  Similarly, a protocol-independent client
    * often allows only the hostname as a command-line argument, hardcoding
    * a service name in the program (which we ignore).
    */

   if ((host != NULL && host[0] == '/'))
      return(addrinfo_local(host, &hints, result));

   if ((serv != NULL && serv[0] == '/'))
      return(addrinfo_local(serv, &hints, result));
#endif

   /*
    * Look up the host.  The code above guarantees that "host"
    * is a nonnull pointer to a nonull string.
    *
    * We first initialize "hent" assuming "host" is an IPv4/IPv6 address
    * (instead of a name).  This saves passing lots of additional
    * arguments to getaddrinfo_host().
    */

   hent.h_name = hentbuf;      /* char string specifying address goes here */
   hent.h_aliases = hent_aliases;
   hent_aliases[0] = NULL;         /* no aliases */
   hent.h_addr_list = hent_addr_list;
   hent_addr_list[0] = hent_addr;   /* one binary address in [0] */
   hent_addr_list[1] = NULL;
   hptr = &hent;

   if ( (rc = getaddrinfo_host(host, &hent, &hptr, hentbuf, HENTBUFSIZ,
                        hints.ai_family)) != 0) {
     /*   printf ("Error: getaddrinfo_host\n"); */
      error(rc);
   }

   /*
    * "hptr" now points to a filled in hostent{}.
    * If "host" was an IPv4/IPv6 address, instead of a name, then
    * "hptr" points to our own "hent" structure.
    * If gethostbyname_r() was called, then "hptr" points to our own
    * "hent" structure, which was passed as as an argument to the
    * reentrant function.
    * If gethostbyname() was called, then "hptr" points to the static
    * hostent{} that it returned.
    *
    * Check for address family mismatch if the caller specified one.
    * Note that Posix assumes that AF_foo == PF_foo.
    */
   if (hints.ai_family != PF_UNSPEC && hints.ai_family != hptr->h_addrtype)
      error(EAI_ADDRFAMILY);

   /*
    * Go through the list of returned addresses and create one
    * addrinfo{} for each one, linking all the structures together.
    * We still have not looked at the service--that comes after this.
    */

   aihead = NULL;
   aipnext = &aihead;
   for (ap = hptr->h_addr_list; *ap != NULL; ap++) {
      if ( (ai = (struct addrinfo *)
        calloc(1, sizeof(struct addrinfo))) == NULL)
         error(EAI_MEMORY);
      *aipnext = ai;         /* prev points to this new one */
      aipnext = &ai->ai_next;   /* pointer to next one goes here */

         /* initialize from hints; could be 0 */
      if ( (ai->ai_socktype = hints.ai_socktype) == 0)
         ai->ai_flags |= AI_CLONE;
      ai->ai_protocol = hints.ai_protocol;

      ai->ai_family = hptr->h_addrtype;
      switch (ai->ai_family) {
         /*
          * Allocate a socket address structure and fill it in.
          * The port number will be filled in later, when the service
          * is processed.
          */
#ifdef   IPV4
         case PF_INET:
            if ( (sinptr = (struct sockaddr_in *) 
              calloc(1, sizeof(struct sockaddr_in))) == NULL)
               error(EAI_MEMORY);
#ifdef   _CMSG_DATA   /* 4.3BSD Reno and later */
            sinptr->sin_len = sizeof(struct sockaddr_in);
#endif
            sinptr->sin_family = AF_INET;
            memcpy(&sinptr->sin_addr, *ap, sizeof(struct in_addr));
            ai->ai_addr = (struct sockaddr *) sinptr;
            ai->ai_addrlen = sizeof(struct sockaddr_in);
            break;
#endif   /* IPV4 */

#ifdef   IPV6
         case PF_INET6:
            if ( (sin6ptr = calloc(1, sizeof(struct sockaddr_in6))) == NULL)
               error(EAI_MEMORY);
#ifdef   SIN6_LEN
            sin6ptr->sin6_len = sizeof(struct sockaddr_in6);
#endif
            sin6ptr->sin6_family = AF_INET6;
            memcpy(&sin6ptr->sin6_addr, *ap, sizeof(struct in6_addr));
            ai->ai_addr = (struct sockaddr *) sin6ptr;
            ai->ai_addrlen = sizeof(struct sockaddr_in6);
            break;
#endif   /* IPV6 */
      }
   }
   /* "aihead" points to the first structure in the linked list */

   if (hints.ai_flags & AI_CANONNAME) {
      /*
       * Posix doesn't say what to do if this flag is set and
       * multiple addrinfo structures are returned.
       * We return the canonical name only in the first addrinfo{}.
       */
      if (hptr->h_name != NULL) {
         if ( (aihead->ai_canonname = (char *) strdup(hptr->h_name)) == NULL)
            error(EAI_MEMORY);
      } else {
         /*
          * Posix says we can just set ai_canonname to point to the
          * "host" argument, but that makes freeaddrinfo() harder.
          * We dynamically allocate room for a copy of "host".
          */
         if ( (aihead->ai_canonname = (char *) strdup(host)) == NULL)
            error(EAI_MEMORY);
      }
   }

   /*
    * Now look up the service, if specified.
    */
   if (serv != NULL && serv[0] != '\0') {
      if ( (rc = getaddrinfo_serv(aihead, &hints, serv, &sent,
                           hentbuf, HENTBUFSIZ)) != 0)
         error(rc);
   }

   *result = aihead;   /* pointer to first structure in linked list */

/* printf ("        result.flags=%d\n", aihead->ai_flags);      */
/* printf ("             .family=%d\n", aihead->ai_family);     */
/* printf ("           .socktype=%d\n", aihead->ai_socktype);   */
/* printf ("           .protocol=%d\n", aihead->ai_protocol);   */
/* printf ("            .addrlen=%d\n", aihead->ai_addrlen);    */
/* printf ("              .^addr=%d\n", aihead->ai_addr);       */
/* printf ("              .^name=%d\n", aihead->ai_canonname);  */
/* if (aihead->ai_canonname != NULL) {                          */
/* printf ("          .canonname=%s\n", aihead->ai_canonname);} */
/* printf ("              .^next=%d\n", aihead->ai_next);       */

   return(0);

bad:
   /*   printf ("About to free any allocated memory\n");  */
   freeaddrinfo(aihead);   /* free any alloc'ed memory */
   /*   printf ("Memory freed, error is %d\n", error);  */
   fflush (stdout);
   return(error);
}

/*
 * This function handles the host string.
 */

static int
getaddrinfo_host(const char *host,
       struct hostent *hptr, struct hostent **hptrptr,
       char *buf, int bufsiz, int family)
{

#ifdef   REENTRANT
   int      h_errno;
#endif   /* REENTRANT */

#ifdef   IPV4
   /*
    * We explicitly check for an IPv4 dotted-decimal string.
    * Recent versions of gethostbyname(), starting around BIND 4.9.2
    * do this too, but we have the check here so we don't depend on
    * this newer feature.  (You wouldn't believe the ancient versions
    * of BIND that some vendors ship.)
    */
   if (isdigit(host[0])) {
      if (inet_pton(AF_INET, host, hptr->h_addr_list[0]) == 1) {
            /* Success.  Finish making up the hostent{} as though
               we had called gethostbyname(). */
         strncpy(hptr->h_name, host, bufsiz-1);
         buf[bufsiz-1] = '\0';
         hptr->h_addrtype = AF_INET;
         hptr->h_length = sizeof(struct in_addr);
         return(0);
      }
   }
#endif   /* IPV4 */

#ifdef   IPV6
   /*
    * Check for an IPv6 hex string.
    */
   if (isxdigit(host[0]) || host[0] == ':') {
      if (inet_pton(AF_INET6, host, hptr->h_addr_list[0]) == 1) {
            /* Success.  Finish making up a hostent{} as though
               we had called gethostbyname(). */
         strncpy(buf, host, bufsiz-1);
         buf[bufsiz-1] = '\0';
         hptr->h_addrtype = AF_INET6;
         hptr->h_length = sizeof(struct in6_addr);
         return(0);
      }
   }
#endif   /* IPV6 */

   /*
    * Not an address, must be a hostname, try the DNS.
    * Initialize the resolver, if not already initialized.
    */

   if ((_res.options & RES_INIT) == 0)
      res_init();         /* need this to set _res.options below */

#ifdef   IPV6
   /*
    * Notice that the following might be considered optional, and
    * could be #ifdef'ed out if your <resolv.h> does not define
    * RES_USE_INET6.  But I am assuming you have BIND-4.9.4 installed
    * and want the IPv4/IPv6 semantics that it defines for gethostbyname().
    */

   if (family == AF_INET6)
      _res.options |= RES_USE_INET6;
#endif   /* IPV6 */

   /*   printf ("Calling gethostbyname with host = %s\n", host);  */

#ifdef   REENTRANT
   hptr = gethostbyname_r(host, hptr, buf, bufsiz, &h_errno);
#else
   hptr = gethostbyname(host);
#endif   /* REENTRANT */
   if (hptr == NULL) {
      switch (h_errno) {
      case HOST_NOT_FOUND:   return(EAI_NONAME);
      case TRY_AGAIN:      return(EAI_AGAIN);
      case NO_RECOVERY:   return(EAI_FAIL);
      case NO_DATA:      return(EAI_NODATA);
      default:      return(EAI_NONAME);
       }
   }
   *hptrptr = hptr;
   return(0);
}

/*
 * This function handles the service string.
 */

static int
getaddrinfo_serv(struct addrinfo *aihead,
             const struct addrinfo *hintsptr, const char *serv,
             struct servent *sptrarg, char *buf, int bufsiz)
{
   int            port, rc;
   int            nfound = 0;
   struct servent   *sptr;

   /*
    * We allow the service to be a numeric string, which we
    * interpret as a decimal port number.  Posix doesn't
    * explicitly say to do this, but it just makes sense.
    * But to do this the caller must specify a socket type,
    * else there's no way to return values for socket().
    */

   if (isdigit(serv[0]) && hintsptr->ai_socktype != 0) {
      port = htons(atoi(serv));
      if ( (rc = getaddrinfo_port(aihead, port, hintsptr->ai_socktype)) == 0)
         return(EAI_NONAME);
      else if (rc < 0)
         return(EAI_MEMORY);
      else
         return(0);
   }

   /*
    * Not a special case, try the "/etc/services" file (or whatever).
    * We first try TCP, if applicable.
    */

   if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_STREAM) {
#ifdef   REENTRANT
      sptr = getservbyname_r(serv, "tcp", sptrarg, buf, bufsiz);
#else
      sptr = getservbyname(serv, "tcp");
#endif   /* REENTRANT */
      if (sptr != NULL) {
         rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_STREAM);
         if (rc < 0)
            return(EAI_MEMORY);
         nfound += rc;
      }
   }

   /*
    * Now try UDP, if applicable.
    */
   if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_DGRAM) {
#ifdef   REENTRANT
      sptr = getservbyname_r(serv, "udp", sptrarg, buf, bufsiz);
#else
      sptr = getservbyname(serv, "udp");
#endif   /* REENTRANT */
      if (sptr != NULL) {
         rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_DGRAM);
         if (rc < 0)
            return(EAI_MEMORY);
         nfound += rc;
      }
   }

   if (nfound == 0) {
         /* You could call getservbyname() one more time, with no
            protocol specified, but "tcp" and "udp" are all that
            are supported today. */

      if (hintsptr->ai_socktype == 0)
         return(EAI_NONAME);   /* all calls to getservbyname() failed */
      else
         return(EAI_SERVICE);/* service not supported for socket type */
   }
   return(0);
}

/*
 * Go through all the addrinfo structures, checking for a match of the
 * socket type and filling in the socket type, and then the port number
 * in the corresponding socket address structures.
 *
 * The AI_CLONE flag works as follows.  Consider a multihomed host with
 * two IP addresses and no socket type specified by the caller.  After
 * the "host" search there are two addrinfo structures, one per IP address.
 * Assuming a service supported by both TCP and UDP (say the daytime
 * service) we need to return *four* addrinfo structures:
 *      IP#1, SOCK_STREAM, TCP port,
 *      IP#1, SOCK_DGRAM, UDP port,
 *      IP#2, SOCK_STREAM, TCP port,
 *      IP#2, SOCK_DGRAM, UDP port.
 * To do this, when the "host" loop creates an addrinfo structure, if the
 * caller has not specified a socket type (hints->ai_socktype == 0), the
 * AI_CLONE flag is set.  When the following function finds an entry like
 * this it is handled as follows: If the entry's ai_socktype is still 0,
 * this is the first use of the structure, and the ai_socktype field is set.
 * But, if the entry's ai_socktype is nonzero, then we clone a new addrinfo
 * structure and set it's ai_socktype to the new value.  Although we only
 * need two socket types today (SOCK_STREAM and SOCK_DGRAM) this algorithm
 * will handle any number.  Also notice that Posix requires all socket
 * types to be nonzero.
 */

static int
getaddrinfo_port(struct addrinfo *aihead, int port, int socktype)
      /* port must be in network byte order */
{
   int            nfound = 0;
   struct addrinfo   *ai;

   for (ai = aihead; ai != NULL; ai = ai->ai_next) {
      /*
       * We set the socket type but not the protocol, because if a
       * port number is specified, the protocol must be TCP or UDP,
       * and a protocol of 0 for socket() is fine for TCP and UDP.
       * The only time a nonzero protocol argument is required by
       * socket() is for a raw socket, in which case a service will
       * not be specified to getaddrinfo().
       */

      if (ai->ai_flags & AI_CLONE) {
         if (ai->ai_socktype != 0) {
            if ( (ai = getaddrinfo_clone(ai)) == NULL)
               return(-1);   /* tell caller it's a memory allocation error */
            /* ai points to newly cloned entry, which is what we want */
         }
      } else if (ai->ai_socktype != socktype)
         continue;      /* ignore if mismatch on socket type */

      ai->ai_socktype = socktype;

      switch (ai->ai_family) {
#ifdef   IPV4
         case PF_INET:
            ((struct sockaddr_in *) ai->ai_addr)->sin_port = port;
            nfound++;
            break;
#endif
#ifdef   IPV6
         case PF_INET6:
            ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port = port;
            nfound++;
            break;
#endif
      }
   }
   return(nfound);
}

/*
 * Clone a new addrinfo structure from an existing one.
 */

static struct addrinfo *
getaddrinfo_clone(struct addrinfo *ai)
{
   struct addrinfo   *new;

   if ( (new = (struct addrinfo *) calloc(1, sizeof(struct addrinfo))) == NULL)
      return(NULL);

   new->ai_next = ai->ai_next;
   ai->ai_next = new;

   new->ai_flags = 0;            /* make sure AI_CLONE is off */
   new->ai_family = ai->ai_family;
   new->ai_socktype = ai->ai_socktype;
   new->ai_protocol = ai->ai_protocol;
   new->ai_canonname = NULL;
   new->ai_addrlen = ai->ai_addrlen;
   if ( (new->ai_addr = (struct sockaddr *) malloc(ai->ai_addrlen)) == NULL)
      return(NULL);
   memcpy(new->ai_addr, ai->ai_addr, ai->ai_addrlen);

   return(new);
}

#ifdef   LOCAL
/*
 * Do everything for a Unix domain socket.
 * Only one addrinfo{} is returned.
 */

static int
addrinfo_local(const char *path, struct addrinfo *hints,
            struct addrinfo **result)
{
   struct addrinfo      *ai;
   struct sockaddr_un   *unp;

   if (hints->ai_socktype == 0)
      return(EAI_SOCKTYPE);   /* we cannot tell socket type from service */

   if ( (ai = (struct addrinfo *) calloc(1, sizeof(struct addrinfo))) == NULL)
      return 0;

   ai->ai_flags = 0;
   ai->ai_family = AF_LOCAL;
   ai->ai_socktype = hints->ai_socktype;
   ai->ai_protocol = 0;

      /* allocate and fill in a socket address structure */
   ai->ai_addrlen = sizeof(struct sockaddr_un);
   if ( (ai->ai_addr = (struct sockaddr * ) malloc(ai->ai_addrlen)) == NULL)
      return(EAI_MEMORY);
   unp = (struct sockaddr_un *) ai->ai_addr;
   unp->sun_family = AF_UNIX;
   strncpy(unp->sun_path, path, sizeof(unp->sun_path));

   ai->ai_canonname = NULL;   /* maybe return the i-node number :-) */
   ai->ai_next = NULL;
   *result = ai;

   if (hints->ai_flags & AI_PASSIVE)
      unlink(path);      /* OK if this fails */

   return(0);      /* success */
}
#endif   /* LOCAL */

void
freeaddrinfo(struct addrinfo *aihead)
{
   struct addrinfo   *ai, *ainext;

   for (ai = aihead; ai != NULL; ai = ainext) {
     /*      printf ("in the free loop\n");  */
      fflush (stdout);
      if (ai->ai_addr != NULL)
      {
	/*         printf ("about to free ai_addr\n"); */
         fflush (stdout);
         free(ai->ai_addr);      /* the socket address structure */
      }
      /*      printf ("freed ai_addr\n"); */
      fflush (stdout);
      if (ai->ai_canonname != NULL)
         free(ai->ai_canonname);   /* the canonical name */
      /*      printf ("freed ai_cannoname\n"); */
      fflush (stdout);
      ainext = ai->ai_next;      /* can't fetch ai_next after free() */
      /*      printf ("just got the next one to free\n");  */
      fflush (stdout);
      free(ai);               /* the addrinfo{} itself */
   }
}