~ubuntu-branches/ubuntu/hardy/postgresql-8.4/hardy-backports

« back to all changes in this revision

Viewing changes to src/port/getaddrinfo.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * getaddrinfo.c
 
4
 *        Support getaddrinfo() on platforms that don't have it.
 
5
 *
 
6
 * We also supply getnameinfo() here, assuming that the platform will have
 
7
 * it if and only if it has getaddrinfo().      If this proves false on some
 
8
 * platform, we'll need to split this file and provide a separate configure
 
9
 * test for getnameinfo().
 
10
 *
 
11
 * Windows may or may not have these routines, so we handle Windows specially
 
12
 * by dynamically checking for their existence.  If they already exist, we
 
13
 * use the Windows native routines, but if not, we use our own.
 
14
 *
 
15
 *
 
16
 * Copyright (c) 2003-2009, PostgreSQL Global Development Group
 
17
 *
 
18
 * IDENTIFICATION
 
19
 *        $PostgreSQL$
 
20
 *
 
21
 *-------------------------------------------------------------------------
 
22
 */
 
23
 
 
24
/* This is intended to be used in both frontend and backend, so use c.h */
 
25
#include "c.h"
 
26
 
 
27
#include <sys/socket.h>
 
28
#include <netdb.h>
 
29
#include <netinet/in.h>
 
30
#include <arpa/inet.h>
 
31
 
 
32
#include "getaddrinfo.h"
 
33
#include "libpq/pqcomm.h"               /* needed for struct sockaddr_storage */
 
34
 
 
35
 
 
36
#ifdef WIN32
 
37
/*
 
38
 * The native routines may or may not exist on the Windows platform we are on,
 
39
 * so we dynamically look up the routines, and call them via function pointers.
 
40
 * Here we need to declare what the function pointers look like
 
41
 */
 
42
typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename,
 
43
                                                                                                                 const char *servname,
 
44
                                                                                           const struct addrinfo * hints,
 
45
                                                                                                         struct addrinfo ** res);
 
46
 
 
47
typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai);
 
48
 
 
49
typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa,
 
50
                                                                                                                 int salen,
 
51
                                                                                                         char *host, int hostlen,
 
52
                                                                                                         char *serv, int servlen,
 
53
                                                                                                                 int flags);
 
54
 
 
55
/* static pointers to the native routines, so we only do the lookup once. */
 
56
static getaddrinfo_ptr_t getaddrinfo_ptr = NULL;
 
57
static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL;
 
58
static getnameinfo_ptr_t getnameinfo_ptr = NULL;
 
59
 
 
60
 
 
61
static bool
 
62
haveNativeWindowsIPv6routines(void)
 
63
{
 
64
        void       *hLibrary = NULL;
 
65
        static bool alreadyLookedForIpv6routines = false;
 
66
 
 
67
        if (alreadyLookedForIpv6routines)
 
68
                return (getaddrinfo_ptr != NULL);
 
69
 
 
70
        /*
 
71
         * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines
 
72
         * are present in the WinSock 2 library (ws2_32.dll). Try that first
 
73
         */
 
74
 
 
75
        hLibrary = LoadLibraryA("ws2_32");
 
76
 
 
77
        if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL)
 
78
        {
 
79
                /*
 
80
                 * Well, ws2_32 doesn't exist, or more likely doesn't have
 
81
                 * getaddrinfo.
 
82
                 */
 
83
                if (hLibrary != NULL)
 
84
                        FreeLibrary(hLibrary);
 
85
 
 
86
                /*
 
87
                 * In Windows 2000, there was only the IPv6 Technology Preview look in
 
88
                 * the IPv6 WinSock library (wship6.dll).
 
89
                 */
 
90
 
 
91
                hLibrary = LoadLibraryA("wship6");
 
92
        }
 
93
 
 
94
        /* If hLibrary is null, we couldn't find a dll with functions */
 
95
        if (hLibrary != NULL)
 
96
        {
 
97
                /* We found a dll, so now get the addresses of the routines */
 
98
 
 
99
                getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary,
 
100
                                                                                                                         "getaddrinfo");
 
101
                freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary,
 
102
                                                                                                                         "freeaddrinfo");
 
103
                getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary,
 
104
                                                                                                                         "getnameinfo");
 
105
 
 
106
                /*
 
107
                 * If any one of the routines is missing, let's play it safe and
 
108
                 * ignore them all
 
109
                 */
 
110
                if (getaddrinfo_ptr == NULL ||
 
111
                        freeaddrinfo_ptr == NULL ||
 
112
                        getnameinfo_ptr == NULL)
 
113
                {
 
114
                        FreeLibrary(hLibrary);
 
115
                        hLibrary = NULL;
 
116
                        getaddrinfo_ptr = NULL;
 
117
                        freeaddrinfo_ptr = NULL;
 
118
                        getnameinfo_ptr = NULL;
 
119
                }
 
120
        }
 
121
 
 
122
        alreadyLookedForIpv6routines = true;
 
123
        return (getaddrinfo_ptr != NULL);
 
124
}
 
125
#endif
 
126
 
 
127
 
 
128
/*
 
129
 * get address info for ipv4 sockets.
 
130
 *
 
131
 *      Bugs:   - only one addrinfo is set even though hintp is NULL or
 
132
 *                ai_socktype is 0
 
133
 *              - AI_CANONNAME is not supported.
 
134
 *              - servname can only be a number, not text.
 
135
 */
 
136
int
 
137
getaddrinfo(const char *node, const char *service,
 
138
                        const struct addrinfo * hintp,
 
139
                        struct addrinfo ** res)
 
140
{
 
141
        struct addrinfo *ai;
 
142
        struct sockaddr_in sin,
 
143
                           *psin;
 
144
        struct addrinfo hints;
 
145
 
 
146
#ifdef WIN32
 
147
 
 
148
        /*
 
149
         * If Windows has native IPv6 support, use the native Windows routine.
 
150
         * Otherwise, fall through and use our own code.
 
151
         */
 
152
        if (haveNativeWindowsIPv6routines())
 
153
                return (*getaddrinfo_ptr) (node, service, hintp, res);
 
154
#endif
 
155
 
 
156
        if (hintp == NULL)
 
157
        {
 
158
                memset(&hints, 0, sizeof(hints));
 
159
                hints.ai_family = AF_INET;
 
160
                hints.ai_socktype = SOCK_STREAM;
 
161
        }
 
162
        else
 
163
                memcpy(&hints, hintp, sizeof(hints));
 
164
 
 
165
        if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
 
166
                return EAI_FAMILY;
 
167
 
 
168
        if (hints.ai_socktype == 0)
 
169
                hints.ai_socktype = SOCK_STREAM;
 
170
 
 
171
        if (!node && !service)
 
172
                return EAI_NONAME;
 
173
 
 
174
        memset(&sin, 0, sizeof(sin));
 
175
 
 
176
        sin.sin_family = AF_INET;
 
177
 
 
178
        if (node)
 
179
        {
 
180
                if (node[0] == '\0')
 
181
                        sin.sin_addr.s_addr = htonl(INADDR_ANY);
 
182
                else if (hints.ai_flags & AI_NUMERICHOST)
 
183
                {
 
184
                        if (!inet_aton(node, &sin.sin_addr))
 
185
                                return EAI_FAIL;
 
186
                }
 
187
                else
 
188
                {
 
189
                        struct hostent *hp;
 
190
 
 
191
#ifdef FRONTEND
 
192
                        struct hostent hpstr;
 
193
                        char            buf[BUFSIZ];
 
194
                        int                     herrno = 0;
 
195
 
 
196
                        pqGethostbyname(node, &hpstr, buf, sizeof(buf),
 
197
                                                        &hp, &herrno);
 
198
#else
 
199
                        hp = gethostbyname(node);
 
200
#endif
 
201
                        if (hp == NULL)
 
202
                        {
 
203
                                switch (h_errno)
 
204
                                {
 
205
                                        case HOST_NOT_FOUND:
 
206
                                        case NO_DATA:
 
207
                                                return EAI_NONAME;
 
208
                                        case TRY_AGAIN:
 
209
                                                return EAI_AGAIN;
 
210
                                        case NO_RECOVERY:
 
211
                                        default:
 
212
                                                return EAI_FAIL;
 
213
                                }
 
214
                        }
 
215
                        if (hp->h_addrtype != AF_INET)
 
216
                                return EAI_FAIL;
 
217
 
 
218
                        memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
 
219
                }
 
220
        }
 
221
        else
 
222
        {
 
223
                if (hints.ai_flags & AI_PASSIVE)
 
224
                        sin.sin_addr.s_addr = htonl(INADDR_ANY);
 
225
                else
 
226
                        sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
227
        }
 
228
 
 
229
        if (service)
 
230
                sin.sin_port = htons((unsigned short) atoi(service));
 
231
 
 
232
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
 
233
        sin.sin_len = sizeof(sin);
 
234
#endif
 
235
 
 
236
        ai = malloc(sizeof(*ai));
 
237
        if (!ai)
 
238
                return EAI_MEMORY;
 
239
 
 
240
        psin = malloc(sizeof(*psin));
 
241
        if (!psin)
 
242
        {
 
243
                free(ai);
 
244
                return EAI_MEMORY;
 
245
        }
 
246
 
 
247
        memcpy(psin, &sin, sizeof(*psin));
 
248
 
 
249
        ai->ai_flags = 0;
 
250
        ai->ai_family = AF_INET;
 
251
        ai->ai_socktype = hints.ai_socktype;
 
252
        ai->ai_protocol = hints.ai_protocol;
 
253
        ai->ai_addrlen = sizeof(*psin);
 
254
        ai->ai_addr = (struct sockaddr *) psin;
 
255
        ai->ai_canonname = NULL;
 
256
        ai->ai_next = NULL;
 
257
 
 
258
        *res = ai;
 
259
 
 
260
        return 0;
 
261
}
 
262
 
 
263
 
 
264
void
 
265
freeaddrinfo(struct addrinfo * res)
 
266
{
 
267
        if (res)
 
268
        {
 
269
#ifdef WIN32
 
270
 
 
271
                /*
 
272
                 * If Windows has native IPv6 support, use the native Windows routine.
 
273
                 * Otherwise, fall through and use our own code.
 
274
                 */
 
275
                if (haveNativeWindowsIPv6routines())
 
276
                {
 
277
                        (*freeaddrinfo_ptr) (res);
 
278
                        return;
 
279
                }
 
280
#endif
 
281
 
 
282
                if (res->ai_addr)
 
283
                        free(res->ai_addr);
 
284
                free(res);
 
285
        }
 
286
}
 
287
 
 
288
 
 
289
const char *
 
290
gai_strerror(int errcode)
 
291
{
 
292
#ifdef HAVE_HSTRERROR
 
293
        int                     hcode;
 
294
 
 
295
        switch (errcode)
 
296
        {
 
297
                case EAI_NONAME:
 
298
                        hcode = HOST_NOT_FOUND;
 
299
                        break;
 
300
                case EAI_AGAIN:
 
301
                        hcode = TRY_AGAIN;
 
302
                        break;
 
303
                case EAI_FAIL:
 
304
                default:
 
305
                        hcode = NO_RECOVERY;
 
306
                        break;
 
307
        }
 
308
 
 
309
        return hstrerror(hcode);
 
310
#else                                                   /* !HAVE_HSTRERROR */
 
311
 
 
312
        switch (errcode)
 
313
        {
 
314
                case EAI_NONAME:
 
315
                        return "Unknown host";
 
316
                case EAI_AGAIN:
 
317
                        return "Host name lookup failure";
 
318
                        /* Errors below are probably WIN32 only */
 
319
#ifdef EAI_BADFLAGS
 
320
                case EAI_BADFLAGS:
 
321
                        return "Invalid argument";
 
322
#endif
 
323
#ifdef EAI_FAMILY
 
324
                case EAI_FAMILY:
 
325
                        return "Address family not supported";
 
326
#endif
 
327
#ifdef EAI_MEMORY
 
328
                case EAI_MEMORY:
 
329
                        return "Not enough memory";
 
330
#endif
 
331
#ifdef EAI_NODATA
 
332
#ifndef WIN32_ONLY_COMPILER             /* MSVC complains because another case has the
 
333
                                                                 * same value */
 
334
                case EAI_NODATA:
 
335
                        return "No host data of that type was found";
 
336
#endif
 
337
#endif
 
338
#ifdef EAI_SERVICE
 
339
                case EAI_SERVICE:
 
340
                        return "Class type not found";
 
341
#endif
 
342
#ifdef EAI_SOCKTYPE
 
343
                case EAI_SOCKTYPE:
 
344
                        return "Socket type not supported";
 
345
#endif
 
346
                default:
 
347
                        return "Unknown server error";
 
348
        }
 
349
#endif   /* HAVE_HSTRERROR */
 
350
}
 
351
 
 
352
/*
 
353
 * Convert an ipv4 address to a hostname.
 
354
 *
 
355
 * Bugs:        - Only supports NI_NUMERICHOST and NI_NUMERICSERV
 
356
 *                It will never resolv a hostname.
 
357
 *              - No IPv6 support.
 
358
 */
 
359
int
 
360
getnameinfo(const struct sockaddr * sa, int salen,
 
361
                        char *node, int nodelen,
 
362
                        char *service, int servicelen, int flags)
 
363
{
 
364
#ifdef WIN32
 
365
 
 
366
        /*
 
367
         * If Windows has native IPv6 support, use the native Windows routine.
 
368
         * Otherwise, fall through and use our own code.
 
369
         */
 
370
        if (haveNativeWindowsIPv6routines())
 
371
                return (*getnameinfo_ptr) (sa, salen, node, nodelen,
 
372
                                                                   service, servicelen, flags);
 
373
#endif
 
374
 
 
375
        /* Invalid arguments. */
 
376
        if (sa == NULL || (node == NULL && service == NULL))
 
377
                return EAI_FAIL;
 
378
 
 
379
        /* We don't support those. */
 
380
        if ((node && !(flags & NI_NUMERICHOST))
 
381
                || (service && !(flags & NI_NUMERICSERV)))
 
382
                return EAI_FAIL;
 
383
 
 
384
#ifdef  HAVE_IPV6
 
385
        if (sa->sa_family == AF_INET6)
 
386
                return EAI_FAMILY;
 
387
#endif
 
388
 
 
389
        if (node)
 
390
        {
 
391
                int                     ret = -1;
 
392
 
 
393
                if (sa->sa_family == AF_INET)
 
394
                {
 
395
                        char       *p;
 
396
 
 
397
                        p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
 
398
                        ret = snprintf(node, nodelen, "%s", p);
 
399
                }
 
400
                if (ret == -1 || ret > nodelen)
 
401
                        return EAI_MEMORY;
 
402
        }
 
403
 
 
404
        if (service)
 
405
        {
 
406
                int                     ret = -1;
 
407
 
 
408
                if (sa->sa_family == AF_INET)
 
409
                {
 
410
                        ret = snprintf(service, servicelen, "%d",
 
411
                                                   ntohs(((struct sockaddr_in *) sa)->sin_port));
 
412
                }
 
413
                if (ret == -1 || ret > servicelen)
 
414
                        return EAI_MEMORY;
 
415
        }
 
416
 
 
417
        return 0;
 
418
}