~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to lib/replace/getaddrinfo.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
PostgreSQL Database Management System
 
3
(formerly known as Postgres, then as Postgres95)
 
4
 
 
5
Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
 
6
 
 
7
Portions Copyright (c) 1994, The Regents of the University of California
 
8
 
 
9
Permission to use, copy, modify, and distribute this software and its
 
10
documentation for any purpose, without fee, and without a written agreement
 
11
is hereby granted, provided that the above copyright notice and this paragraph
 
12
and the following two paragraphs appear in all copies.
 
13
 
 
14
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 
15
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 
16
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
 
17
EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 
18
SUCH DAMAGE.
 
19
 
 
20
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 
21
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 
22
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
 
23
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
 
24
TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
25
 
 
26
*/
 
27
 
 
28
/*-------------------------------------------------------------------------
 
29
 *
 
30
 * getaddrinfo.c
 
31
 *        Support getaddrinfo() on platforms that don't have it.
 
32
 *
 
33
 * We also supply getnameinfo() here, assuming that the platform will have
 
34
 * it if and only if it has getaddrinfo().      If this proves false on some
 
35
 * platform, we'll need to split this file and provide a separate configure
 
36
 * test for getnameinfo().
 
37
 *
 
38
 * Copyright (c) 2003-2007, PostgreSQL Global Development Group
 
39
 *
 
40
 * Copyright (C) 2007 Jeremy Allison.
 
41
 * Modified to return multiple IPv4 addresses for Samba.
 
42
 *
 
43
 *-------------------------------------------------------------------------
 
44
 */
 
45
 
 
46
#include "replace.h"
 
47
#include "system/network.h"
 
48
 
 
49
#ifndef SMB_MALLOC
 
50
#define SMB_MALLOC(s) malloc(s)
 
51
#endif
 
52
 
 
53
#ifndef SMB_STRDUP
 
54
#define SMB_STRDUP(s) strdup(s)
 
55
#endif
 
56
 
 
57
static int check_hostent_err(struct hostent *hp)
 
58
{
 
59
        if (!hp) {
 
60
                switch (h_errno) {
 
61
                        case HOST_NOT_FOUND:
 
62
                        case NO_DATA:
 
63
                                return EAI_NONAME;
 
64
                        case TRY_AGAIN:
 
65
                                return EAI_AGAIN;
 
66
                        case NO_RECOVERY:
 
67
                        default:
 
68
                                return EAI_FAIL;
 
69
                }
 
70
        }
 
71
        if (!hp->h_name || hp->h_addrtype != AF_INET) {
 
72
                return EAI_FAIL;
 
73
        }
 
74
        return 0;
 
75
}
 
76
 
 
77
static char *canon_name_from_hostent(struct hostent *hp,
 
78
                                int *perr)
 
79
{
 
80
        char *ret = NULL;
 
81
 
 
82
        *perr = check_hostent_err(hp);
 
83
        if (*perr) {
 
84
                return NULL;
 
85
        }
 
86
        ret = SMB_STRDUP(hp->h_name);
 
87
        if (!ret) {
 
88
                *perr = EAI_MEMORY;
 
89
        }
 
90
        return ret;
 
91
}
 
92
 
 
93
static char *get_my_canon_name(int *perr)
 
94
{
 
95
        char name[HOST_NAME_MAX+1];
 
96
 
 
97
        if (gethostname(name, HOST_NAME_MAX) == -1) {
 
98
                *perr = EAI_FAIL;
 
99
                return NULL;
 
100
        }
 
101
        /* Ensure null termination. */
 
102
        name[HOST_NAME_MAX] = '\0';
 
103
        return canon_name_from_hostent(gethostbyname(name), perr);
 
104
}
 
105
 
 
106
static char *get_canon_name_from_addr(struct in_addr ip,
 
107
                                int *perr)
 
108
{
 
109
        return canon_name_from_hostent(
 
110
                        gethostbyaddr(&ip, sizeof(ip), AF_INET),
 
111
                        perr);
 
112
}
 
113
 
 
114
static struct addrinfo *alloc_entry(const struct addrinfo *hints,
 
115
                                struct in_addr ip,
 
116
                                unsigned short port)
 
117
{
 
118
        struct sockaddr_in *psin = NULL;
 
119
        struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
 
120
 
 
121
        if (!ai) {
 
122
                return NULL;
 
123
        }
 
124
        memset(ai, '\0', sizeof(*ai));
 
125
 
 
126
        psin = SMB_MALLOC(sizeof(*psin));
 
127
        if (!psin) {
 
128
                free(ai);
 
129
                return NULL;
 
130
        }
 
131
 
 
132
        memset(psin, '\0', sizeof(*psin));
 
133
 
 
134
        psin->sin_family = AF_INET;
 
135
        psin->sin_port = htons(port);
 
136
        psin->sin_addr = ip;
 
137
 
 
138
        ai->ai_flags = 0;
 
139
        ai->ai_family = AF_INET;
 
140
        ai->ai_socktype = hints->ai_socktype;
 
141
        ai->ai_protocol = hints->ai_protocol;
 
142
        ai->ai_addrlen = sizeof(*psin);
 
143
        ai->ai_addr = (struct sockaddr *) psin;
 
144
        ai->ai_canonname = NULL;
 
145
        ai->ai_next = NULL;
 
146
 
 
147
        return ai;
 
148
}
 
149
 
 
150
/*
 
151
 * get address info for a single ipv4 address.
 
152
 *
 
153
 *      Bugs:   - servname can only be a number, not text.
 
154
 */
 
155
 
 
156
static int getaddr_info_single_addr(const char *service,
 
157
                                uint32_t addr,
 
158
                                const struct addrinfo *hints,
 
159
                                struct addrinfo **res)
 
160
{
 
161
 
 
162
        struct addrinfo *ai = NULL;
 
163
        struct in_addr ip;
 
164
        unsigned short port = 0;
 
165
 
 
166
        if (service) {
 
167
                port = (unsigned short)atoi(service);
 
168
        }
 
169
        ip.s_addr = htonl(addr);
 
170
 
 
171
        ai = alloc_entry(hints, ip, port);
 
172
        if (!ai) {
 
173
                return EAI_MEMORY;
 
174
        }
 
175
 
 
176
        /* If we're asked for the canonical name,
 
177
         * make sure it returns correctly. */
 
178
        if (!(hints->ai_flags & AI_NUMERICSERV) &&
 
179
                        hints->ai_flags & AI_CANONNAME) {
 
180
                int err;
 
181
                if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
 
182
                        ai->ai_canonname = get_my_canon_name(&err);
 
183
                } else {
 
184
                        ai->ai_canonname =
 
185
                        get_canon_name_from_addr(ip,&err);
 
186
                }
 
187
                if (ai->ai_canonname == NULL) {
 
188
                        freeaddrinfo(ai);
 
189
                        return err;
 
190
                }
 
191
        }
 
192
 
 
193
        *res = ai;
 
194
        return 0;
 
195
}
 
196
 
 
197
/*
 
198
 * get address info for multiple ipv4 addresses.
 
199
 *
 
200
 *      Bugs:   - servname can only be a number, not text.
 
201
 */
 
202
 
 
203
static int getaddr_info_name(const char *node,
 
204
                                const char *service,
 
205
                                const struct addrinfo *hints,
 
206
                                struct addrinfo **res)
 
207
{
 
208
        struct addrinfo *listp = NULL, *prevp = NULL;
 
209
        char **pptr = NULL;
 
210
        int err;
 
211
        struct hostent *hp = NULL;
 
212
        unsigned short port = 0;
 
213
 
 
214
        if (service) {
 
215
                port = (unsigned short)atoi(service);
 
216
        }
 
217
 
 
218
        hp = gethostbyname(node);
 
219
        err = check_hostent_err(hp);
 
220
        if (err) {
 
221
                return err;
 
222
        }
 
223
 
 
224
        for(pptr = hp->h_addr_list; *pptr; pptr++) {
 
225
                struct in_addr ip = *(struct in_addr *)*pptr;
 
226
                struct addrinfo *ai = alloc_entry(hints, ip, port);
 
227
 
 
228
                if (!ai) {
 
229
                        freeaddrinfo(listp);
 
230
                        return EAI_MEMORY;
 
231
                }
 
232
 
 
233
                if (!listp) {
 
234
                        listp = ai;
 
235
                        prevp = ai;
 
236
                        ai->ai_canonname = SMB_STRDUP(hp->h_name);
 
237
                        if (!ai->ai_canonname) {
 
238
                                freeaddrinfo(listp);
 
239
                                return EAI_MEMORY;
 
240
                        }
 
241
                } else {
 
242
                        prevp->ai_next = ai;
 
243
                        prevp = ai;
 
244
                }
 
245
        }
 
246
        *res = listp;
 
247
        return 0;
 
248
}
 
249
 
 
250
/*
 
251
 * get address info for ipv4 sockets.
 
252
 *
 
253
 *      Bugs:   - servname can only be a number, not text.
 
254
 */
 
255
 
 
256
int rep_getaddrinfo(const char *node,
 
257
                const char *service,
 
258
                const struct addrinfo * hintp,
 
259
                struct addrinfo ** res)
 
260
{
 
261
        struct addrinfo hints;
 
262
 
 
263
        /* Setup the hints struct. */
 
264
        if (hintp == NULL) {
 
265
                memset(&hints, 0, sizeof(hints));
 
266
                hints.ai_family = AF_INET;
 
267
                hints.ai_socktype = SOCK_STREAM;
 
268
        } else {
 
269
                memcpy(&hints, hintp, sizeof(hints));
 
270
        }
 
271
 
 
272
        if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
 
273
                return EAI_FAMILY;
 
274
        }
 
275
 
 
276
        if (hints.ai_socktype == 0) {
 
277
                hints.ai_socktype = SOCK_STREAM;
 
278
        }
 
279
 
 
280
        if (!node && !service) {
 
281
                return EAI_NONAME;
 
282
        }
 
283
 
 
284
        if (node) {
 
285
                if (node[0] == '\0') {
 
286
                        return getaddr_info_single_addr(service,
 
287
                                        INADDR_ANY,
 
288
                                        &hints,
 
289
                                        res);
 
290
                } else if (hints.ai_flags & AI_NUMERICHOST) {
 
291
                        struct in_addr ip;
 
292
                        if (!inet_aton(node, &ip)) {
 
293
                                return EAI_FAIL;
 
294
                        }
 
295
                        return getaddr_info_single_addr(service,
 
296
                                        ntohl(ip.s_addr),
 
297
                                        &hints,
 
298
                                        res);
 
299
                } else {
 
300
                        return getaddr_info_name(node,
 
301
                                                service,
 
302
                                                &hints,
 
303
                                                res);
 
304
                }
 
305
        } else if (hints.ai_flags & AI_PASSIVE) {
 
306
                return getaddr_info_single_addr(service,
 
307
                                        INADDR_ANY,
 
308
                                        &hints,
 
309
                                        res);
 
310
        }
 
311
        return getaddr_info_single_addr(service,
 
312
                                        INADDR_LOOPBACK,
 
313
                                        &hints,
 
314
                                        res);
 
315
}
 
316
 
 
317
 
 
318
void rep_freeaddrinfo(struct addrinfo *res)
 
319
{
 
320
        struct addrinfo *next = NULL;
 
321
 
 
322
        for (;res; res = next) {
 
323
                next = res->ai_next;
 
324
                if (res->ai_canonname) {
 
325
                        free(res->ai_canonname);
 
326
                }
 
327
                if (res->ai_addr) {
 
328
                        free(res->ai_addr);
 
329
                }
 
330
                free(res);
 
331
        }
 
332
}
 
333
 
 
334
 
 
335
const char *rep_gai_strerror(int errcode)
 
336
{
 
337
#ifdef HAVE_HSTRERROR
 
338
        int                     hcode;
 
339
 
 
340
        switch (errcode)
 
341
        {
 
342
                case EAI_NONAME:
 
343
                        hcode = HOST_NOT_FOUND;
 
344
                        break;
 
345
                case EAI_AGAIN:
 
346
                        hcode = TRY_AGAIN;
 
347
                        break;
 
348
                case EAI_FAIL:
 
349
                default:
 
350
                        hcode = NO_RECOVERY;
 
351
                        break;
 
352
        }
 
353
 
 
354
        return hstrerror(hcode);
 
355
#else                                                   /* !HAVE_HSTRERROR */
 
356
 
 
357
        switch (errcode)
 
358
        {
 
359
                case EAI_NONAME:
 
360
                        return "Unknown host";
 
361
                case EAI_AGAIN:
 
362
                        return "Host name lookup failure";
 
363
#ifdef EAI_BADFLAGS
 
364
                case EAI_BADFLAGS:
 
365
                        return "Invalid argument";
 
366
#endif
 
367
#ifdef EAI_FAMILY
 
368
                case EAI_FAMILY:
 
369
                        return "Address family not supported";
 
370
#endif
 
371
#ifdef EAI_MEMORY
 
372
                case EAI_MEMORY:
 
373
                        return "Not enough memory";
 
374
#endif
 
375
#ifdef EAI_NODATA
 
376
                case EAI_NODATA:
 
377
                        return "No host data of that type was found";
 
378
#endif
 
379
#ifdef EAI_SERVICE
 
380
                case EAI_SERVICE:
 
381
                        return "Class type not found";
 
382
#endif
 
383
#ifdef EAI_SOCKTYPE
 
384
                case EAI_SOCKTYPE:
 
385
                        return "Socket type not supported";
 
386
#endif
 
387
                default:
 
388
                        return "Unknown server error";
 
389
        }
 
390
#endif   /* HAVE_HSTRERROR */
 
391
}
 
392
 
 
393
static int gethostnameinfo(const struct sockaddr *sa,
 
394
                        char *node,
 
395
                        size_t nodelen,
 
396
                        int flags)
 
397
{
 
398
        int ret = -1;
 
399
        char *p = NULL;
 
400
 
 
401
        if (!(flags & NI_NUMERICHOST)) {
 
402
                struct hostent *hp = gethostbyaddr(
 
403
                                &((struct sockaddr_in *)sa)->sin_addr,
 
404
                                sizeof(struct in_addr),
 
405
                                sa->sa_family);
 
406
                ret = check_hostent_err(hp);
 
407
                if (ret == 0) {
 
408
                        /* Name looked up successfully. */
 
409
                        ret = snprintf(node, nodelen, "%s", hp->h_name);
 
410
                        if (ret < 0 || (size_t)ret >= nodelen) {
 
411
                                return EAI_MEMORY;
 
412
                        }
 
413
                        if (flags & NI_NOFQDN) {
 
414
                                p = strchr(node,'.');
 
415
                                if (p) {
 
416
                                        *p = '\0';
 
417
                                }
 
418
                        }
 
419
                        return 0;
 
420
                }
 
421
 
 
422
                if (flags & NI_NAMEREQD) {
 
423
                        /* If we require a name and didn't get one,
 
424
                         * automatically fail. */
 
425
                        return ret;
 
426
                }
 
427
                /* Otherwise just fall into the numeric host code... */
 
428
        }
 
429
        p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
 
430
        ret = snprintf(node, nodelen, "%s", p);
 
431
        if (ret < 0 || (size_t)ret >= nodelen) {
 
432
                return EAI_MEMORY;
 
433
        }
 
434
        return 0;
 
435
}
 
436
 
 
437
static int getservicenameinfo(const struct sockaddr *sa,
 
438
                        char *service,
 
439
                        size_t servicelen,
 
440
                        int flags)
 
441
{
 
442
        int ret = -1;
 
443
        int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
 
444
 
 
445
        if (!(flags & NI_NUMERICSERV)) {
 
446
                struct servent *se = getservbyport(
 
447
                                port,
 
448
                                (flags & NI_DGRAM) ? "udp" : "tcp");
 
449
                if (se && se->s_name) {
 
450
                        /* Service name looked up successfully. */
 
451
                        ret = snprintf(service, servicelen, "%s", se->s_name);
 
452
                        if (ret < 0 || (size_t)ret >= servicelen) {
 
453
                                return EAI_MEMORY;
 
454
                        }
 
455
                        return 0;
 
456
                }
 
457
                /* Otherwise just fall into the numeric service code... */
 
458
        }
 
459
        ret = snprintf(service, servicelen, "%d", port);
 
460
        if (ret < 0 || (size_t)ret >= servicelen) {
 
461
                return EAI_MEMORY;
 
462
        }
 
463
        return 0;
 
464
}
 
465
 
 
466
/*
 
467
 * Convert an ipv4 address to a hostname.
 
468
 *
 
469
 * Bugs:        - No IPv6 support.
 
470
 */
 
471
int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
 
472
                        char *node, size_t nodelen,
 
473
                        char *service, size_t servicelen, int flags)
 
474
{
 
475
 
 
476
        /* Invalid arguments. */
 
477
        if (sa == NULL || (node == NULL && service == NULL)) {
 
478
                return EAI_FAIL;
 
479
        }
 
480
 
 
481
        if (sa->sa_family != AF_INET) {
 
482
                return EAI_FAIL;
 
483
        }
 
484
 
 
485
        if (salen < sizeof(struct sockaddr_in)) {
 
486
                return EAI_FAIL;
 
487
        }
 
488
 
 
489
        if (node) {
 
490
                return gethostnameinfo(sa, node, nodelen, flags);
 
491
        }
 
492
 
 
493
        if (service) {
 
494
                return getservicenameinfo(sa, service, servicelen, flags);
 
495
        }
 
496
        return 0;
 
497
}