~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/inet_net_pton.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 
3
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 
4
 *
 
5
 * Permission to use, copy, modify, and distribute this software for any
 
6
 * purpose with or without fee is hereby granted, provided that the above
 
7
 * copyright notice and this permission notice appear in all copies.
 
8
 *
 
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
11
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 
15
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
16
 *
 
17
 *        $PostgreSQL: pgsql/src/backend/utils/adt/inet_net_pton.c,v 1.19.4.1 2005-02-01 00:59:53 tgl Exp $
 
18
 */
 
19
 
 
20
#if defined(LIBC_SCCS) && !defined(lint)
 
21
static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
 
22
#endif
 
23
 
 
24
#include "postgres.h"
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/socket.h>
 
28
#include <netinet/in.h>
 
29
#include <arpa/inet.h>
 
30
#include <assert.h>
 
31
#include <ctype.h>
 
32
 
 
33
#include "utils/inet.h"
 
34
#include "utils/builtins.h"
 
35
 
 
36
 
 
37
static int      inet_net_pton_ipv4(const char *src, u_char *dst);
 
38
static int      inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
 
39
static int      inet_net_pton_ipv6(const char *src, u_char *dst);
 
40
static int      inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
 
41
 
 
42
 
 
43
/*
 
44
 * int
 
45
 * inet_net_pton(af, src, dst, size)
 
46
 *      convert network number from presentation to network format.
 
47
 *      accepts hex octets, hex strings, decimal octets, and /CIDR.
 
48
 *      "size" is in bytes and describes "dst".
 
49
 * return:
 
50
 *      number of bits, either imputed classfully or specified with /CIDR,
 
51
 *      or -1 if some failure occurred (check errno).  ENOENT means it was
 
52
 *      not a valid network specification.
 
53
 * author:
 
54
 *      Paul Vixie (ISC), June 1996
 
55
 *
 
56
 * Changes:
 
57
 *      I added the inet_cidr_pton function (also from Paul) and changed
 
58
 *      the names to reflect their current use.
 
59
 *
 
60
 */
 
61
int
 
62
inet_net_pton(int af, const char *src, void *dst, size_t size)
 
63
{
 
64
        switch (af)
 
65
        {
 
66
                case PGSQL_AF_INET:
 
67
                        return size == -1 ?
 
68
                                inet_net_pton_ipv4(src, dst) :
 
69
                                inet_cidr_pton_ipv4(src, dst, size);
 
70
                case PGSQL_AF_INET6:
 
71
                        return size == -1 ?
 
72
                                inet_net_pton_ipv6(src, dst) :
 
73
                                inet_cidr_pton_ipv6(src, dst, size);
 
74
                default:
 
75
                        errno = EAFNOSUPPORT;
 
76
                        return (-1);
 
77
        }
 
78
}
 
79
 
 
80
/*
 
81
 * static int
 
82
 * inet_cidr_pton_ipv4(src, dst, size)
 
83
 *      convert IPv4 network number from presentation to network format.
 
84
 *      accepts hex octets, hex strings, decimal octets, and /CIDR.
 
85
 *      "size" is in bytes and describes "dst".
 
86
 * return:
 
87
 *      number of bits, either imputed classfully or specified with /CIDR,
 
88
 *      or -1 if some failure occurred (check errno).  ENOENT means it was
 
89
 *      not an IPv4 network specification.
 
90
 * note:
 
91
 *      network byte order assumed.  this means 192.5.5.240/28 has
 
92
 *      0b11110000 in its fourth octet.
 
93
 * author:
 
94
 *      Paul Vixie (ISC), June 1996
 
95
 */
 
96
static int
 
97
inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
 
98
{
 
99
        static const char xdigits[] = "0123456789abcdef";
 
100
        static const char digits[] = "0123456789";
 
101
        int                     n,
 
102
                                ch,
 
103
                                tmp = 0,
 
104
                                dirty,
 
105
                                bits;
 
106
        const u_char *odst = dst;
 
107
 
 
108
        ch = *src++;
 
109
        if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
 
110
                && isxdigit((unsigned char) src[1]))
 
111
        {
 
112
                /* Hexadecimal: Eat nybble string. */
 
113
                if (size <= 0U)
 
114
                        goto emsgsize;
 
115
                dirty = 0;
 
116
                src++;                                  /* skip x or X. */
 
117
                while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
 
118
                {
 
119
                        if (isupper((unsigned char) ch))
 
120
                                ch = tolower((unsigned char) ch);
 
121
                        n = strchr(xdigits, ch) - xdigits;
 
122
                        assert(n >= 0 && n <= 15);
 
123
                        if (dirty == 0)
 
124
                                tmp = n;
 
125
                        else
 
126
                                tmp = (tmp << 4) | n;
 
127
                        if (++dirty == 2)
 
128
                        {
 
129
                                if (size-- <= 0U)
 
130
                                        goto emsgsize;
 
131
                                *dst++ = (u_char) tmp;
 
132
                                dirty = 0;
 
133
                        }
 
134
                }
 
135
                if (dirty)
 
136
                {                                               /* Odd trailing nybble? */
 
137
                        if (size-- <= 0U)
 
138
                                goto emsgsize;
 
139
                        *dst++ = (u_char) (tmp << 4);
 
140
                }
 
141
        }
 
142
        else if (isdigit((unsigned char) ch))
 
143
        {
 
144
                /* Decimal: eat dotted digit string. */
 
145
                for (;;)
 
146
                {
 
147
                        tmp = 0;
 
148
                        do
 
149
                        {
 
150
                                n = strchr(digits, ch) - digits;
 
151
                                assert(n >= 0 && n <= 9);
 
152
                                tmp *= 10;
 
153
                                tmp += n;
 
154
                                if (tmp > 255)
 
155
                                        goto enoent;
 
156
                        } while ((ch = *src++) != '\0' &&
 
157
                                         isdigit((unsigned char) ch));
 
158
                        if (size-- <= 0U)
 
159
                                goto emsgsize;
 
160
                        *dst++ = (u_char) tmp;
 
161
                        if (ch == '\0' || ch == '/')
 
162
                                break;
 
163
                        if (ch != '.')
 
164
                                goto enoent;
 
165
                        ch = *src++;
 
166
                        if (!isdigit((unsigned char) ch))
 
167
                                goto enoent;
 
168
                }
 
169
        }
 
170
        else
 
171
                goto enoent;
 
172
 
 
173
        bits = -1;
 
174
        if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
 
175
        {
 
176
                /* CIDR width specifier.  Nothing can follow it. */
 
177
                ch = *src++;                    /* Skip over the /. */
 
178
                bits = 0;
 
179
                do
 
180
                {
 
181
                        n = strchr(digits, ch) - digits;
 
182
                        assert(n >= 0 && n <= 9);
 
183
                        bits *= 10;
 
184
                        bits += n;
 
185
                } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
 
186
                if (ch != '\0')
 
187
                        goto enoent;
 
188
                if (bits > 32)
 
189
                        goto emsgsize;
 
190
        }
 
191
 
 
192
        /* Firey death and destruction unless we prefetched EOS. */
 
193
        if (ch != '\0')
 
194
                goto enoent;
 
195
 
 
196
        /* If nothing was written to the destination, we found no address. */
 
197
        if (dst == odst)
 
198
                goto enoent;
 
199
        /* If no CIDR spec was given, infer width from net class. */
 
200
        if (bits == -1)
 
201
        {
 
202
                if (*odst >= 240)               /* Class E */
 
203
                        bits = 32;
 
204
                else if (*odst >= 224)  /* Class D */
 
205
                        bits = 8;
 
206
                else if (*odst >= 192)  /* Class C */
 
207
                        bits = 24;
 
208
                else if (*odst >= 128)  /* Class B */
 
209
                        bits = 16;
 
210
                else                                    /* Class A */
 
211
                        bits = 8;
 
212
                /* If imputed mask is narrower than specified octets, widen. */
 
213
                if (bits < ((dst - odst) * 8))
 
214
                        bits = (dst - odst) * 8;
 
215
 
 
216
                /*
 
217
                 * If there are no additional bits specified for a class D address
 
218
                 * adjust bits to 4.
 
219
                 */
 
220
                if (bits == 8 && *odst == 224)
 
221
                        bits = 4;
 
222
        }
 
223
        /* Extend network to cover the actual mask. */
 
224
        while (bits > ((dst - odst) * 8))
 
225
        {
 
226
                if (size-- <= 0U)
 
227
                        goto emsgsize;
 
228
                *dst++ = '\0';
 
229
        }
 
230
        return (bits);
 
231
 
 
232
enoent:
 
233
        errno = ENOENT;
 
234
        return (-1);
 
235
 
 
236
emsgsize:
 
237
        errno = EMSGSIZE;
 
238
        return (-1);
 
239
}
 
240
 
 
241
/*
 
242
 * int
 
243
 * inet_net_pton(af, src, dst, *bits)
 
244
 *      convert network address from presentation to network format.
 
245
 *      accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
 
246
 *      "dst" is assumed large enough for its "af".  "bits" is set to the
 
247
 *      /CIDR prefix length, which can have defaults (like /32 for IPv4).
 
248
 * return:
 
249
 *      -1 if an error occurred (inspect errno; ENOENT means bad format).
 
250
 *      0 if successful conversion occurred.
 
251
 * note:
 
252
 *      192.5.5.1/28 has a nonzero host part, which means it isn't a network
 
253
 *      as called for by inet_cidr_pton() but it can be a host address with
 
254
 *      an included netmask.
 
255
 * author:
 
256
 *      Paul Vixie (ISC), October 1998
 
257
 */
 
258
static int
 
259
inet_net_pton_ipv4(const char *src, u_char *dst)
 
260
{
 
261
        static const char digits[] = "0123456789";
 
262
        const u_char *odst = dst;
 
263
        int                     n,
 
264
                                ch,
 
265
                                tmp,
 
266
                                bits;
 
267
        size_t          size = 4;
 
268
 
 
269
        /* Get the mantissa. */
 
270
        while (ch = *src++, isdigit((unsigned char) ch))
 
271
        {
 
272
                tmp = 0;
 
273
                do
 
274
                {
 
275
                        n = strchr(digits, ch) - digits;
 
276
                        assert(n >= 0 && n <= 9);
 
277
                        tmp *= 10;
 
278
                        tmp += n;
 
279
                        if (tmp > 255)
 
280
                                goto enoent;
 
281
                } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
 
282
                if (size-- == 0)
 
283
                        goto emsgsize;
 
284
                *dst++ = (u_char) tmp;
 
285
                if (ch == '\0' || ch == '/')
 
286
                        break;
 
287
                if (ch != '.')
 
288
                        goto enoent;
 
289
        }
 
290
 
 
291
        /* Get the prefix length if any. */
 
292
        bits = -1;
 
293
        if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
 
294
        {
 
295
                /* CIDR width specifier.  Nothing can follow it. */
 
296
                ch = *src++;                    /* Skip over the /. */
 
297
                bits = 0;
 
298
                do
 
299
                {
 
300
                        n = strchr(digits, ch) - digits;
 
301
                        assert(n >= 0 && n <= 9);
 
302
                        bits *= 10;
 
303
                        bits += n;
 
304
                } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
 
305
                if (ch != '\0')
 
306
                        goto enoent;
 
307
                if (bits > 32)
 
308
                        goto emsgsize;
 
309
        }
 
310
 
 
311
        /* Firey death and destruction unless we prefetched EOS. */
 
312
        if (ch != '\0')
 
313
                goto enoent;
 
314
 
 
315
        /* Prefix length can default to /32 only if all four octets spec'd. */
 
316
        if (bits == -1)
 
317
        {
 
318
                if (dst - odst == 4)
 
319
                        bits = 32;
 
320
                else
 
321
                        goto enoent;
 
322
        }
 
323
 
 
324
        /* If nothing was written to the destination, we found no address. */
 
325
        if (dst == odst)
 
326
                goto enoent;
 
327
 
 
328
        /* If prefix length overspecifies mantissa, life is bad. */
 
329
        if ((bits / 8) > (dst - odst))
 
330
                goto enoent;
 
331
 
 
332
        /* Extend address to four octets. */
 
333
        while (size-- > 0)
 
334
                *dst++ = 0;
 
335
 
 
336
        return bits;
 
337
 
 
338
enoent:
 
339
        errno = ENOENT;
 
340
        return (-1);
 
341
 
 
342
emsgsize:
 
343
        errno = EMSGSIZE;
 
344
        return (-1);
 
345
}
 
346
 
 
347
static int
 
348
getbits(const char *src, int *bitsp)
 
349
{
 
350
        static const char digits[] = "0123456789";
 
351
        int                     n;
 
352
        int                     val;
 
353
        char            ch;
 
354
 
 
355
        val = 0;
 
356
        n = 0;
 
357
        while ((ch = *src++) != '\0')
 
358
        {
 
359
                const char *pch;
 
360
 
 
361
                pch = strchr(digits, ch);
 
362
                if (pch != NULL)
 
363
                {
 
364
                        if (n++ != 0 && val == 0)       /* no leading zeros */
 
365
                                return (0);
 
366
                        val *= 10;
 
367
                        val += (pch - digits);
 
368
                        if (val > 128)          /* range */
 
369
                                return (0);
 
370
                        continue;
 
371
                }
 
372
                return (0);
 
373
        }
 
374
        if (n == 0)
 
375
                return (0);
 
376
        *bitsp = val;
 
377
        return (1);
 
378
}
 
379
 
 
380
static int
 
381
getv4(const char *src, u_char *dst, int *bitsp)
 
382
{
 
383
        static const char digits[] = "0123456789";
 
384
        u_char     *odst = dst;
 
385
        int                     n;
 
386
        u_int           val;
 
387
        char            ch;
 
388
 
 
389
        val = 0;
 
390
        n = 0;
 
391
        while ((ch = *src++) != '\0')
 
392
        {
 
393
                const char *pch;
 
394
 
 
395
                pch = strchr(digits, ch);
 
396
                if (pch != NULL)
 
397
                {
 
398
                        if (n++ != 0 && val == 0)       /* no leading zeros */
 
399
                                return (0);
 
400
                        val *= 10;
 
401
                        val += (pch - digits);
 
402
                        if (val > 255)          /* range */
 
403
                                return (0);
 
404
                        continue;
 
405
                }
 
406
                if (ch == '.' || ch == '/')
 
407
                {
 
408
                        if (dst - odst > 3) /* too many octets? */
 
409
                                return (0);
 
410
                        *dst++ = val;
 
411
                        if (ch == '/')
 
412
                                return (getbits(src, bitsp));
 
413
                        val = 0;
 
414
                        n = 0;
 
415
                        continue;
 
416
                }
 
417
                return (0);
 
418
        }
 
419
        if (n == 0)
 
420
                return (0);
 
421
        if (dst - odst > 3)                     /* too many octets? */
 
422
                return (0);
 
423
        *dst++ = val;
 
424
        return (1);
 
425
}
 
426
 
 
427
static int
 
428
inet_net_pton_ipv6(const char *src, u_char *dst)
 
429
{
 
430
        return inet_cidr_pton_ipv6(src, dst, 16);
 
431
}
 
432
 
 
433
#define NS_IN6ADDRSZ 16
 
434
#define NS_INT16SZ 2
 
435
#define NS_INADDRSZ 4
 
436
 
 
437
static int
 
438
inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
 
439
{
 
440
        static const char xdigits_l[] = "0123456789abcdef",
 
441
                                xdigits_u[] = "0123456789ABCDEF";
 
442
        u_char          tmp[NS_IN6ADDRSZ],
 
443
                           *tp,
 
444
                           *endp,
 
445
                           *colonp;
 
446
        const char *xdigits,
 
447
                           *curtok;
 
448
        int                     ch,
 
449
                                saw_xdigit;
 
450
        u_int           val;
 
451
        int                     digits;
 
452
        int                     bits;
 
453
 
 
454
        if (size < NS_IN6ADDRSZ)
 
455
                goto emsgsize;
 
456
 
 
457
        memset((tp = tmp), '\0', NS_IN6ADDRSZ);
 
458
        endp = tp + NS_IN6ADDRSZ;
 
459
        colonp = NULL;
 
460
        /* Leading :: requires some special handling. */
 
461
        if (*src == ':')
 
462
                if (*++src != ':')
 
463
                        goto enoent;
 
464
        curtok = src;
 
465
        saw_xdigit = 0;
 
466
        val = 0;
 
467
        digits = 0;
 
468
        bits = -1;
 
469
        while ((ch = *src++) != '\0')
 
470
        {
 
471
                const char *pch;
 
472
 
 
473
                if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
 
474
                        pch = strchr((xdigits = xdigits_u), ch);
 
475
                if (pch != NULL)
 
476
                {
 
477
                        val <<= 4;
 
478
                        val |= (pch - xdigits);
 
479
                        if (++digits > 4)
 
480
                                goto enoent;
 
481
                        saw_xdigit = 1;
 
482
                        continue;
 
483
                }
 
484
                if (ch == ':')
 
485
                {
 
486
                        curtok = src;
 
487
                        if (!saw_xdigit)
 
488
                        {
 
489
                                if (colonp)
 
490
                                        goto enoent;
 
491
                                colonp = tp;
 
492
                                continue;
 
493
                        }
 
494
                        else if (*src == '\0')
 
495
                                goto enoent;
 
496
                        if (tp + NS_INT16SZ > endp)
 
497
                                return (0);
 
498
                        *tp++ = (u_char) (val >> 8) & 0xff;
 
499
                        *tp++ = (u_char) val & 0xff;
 
500
                        saw_xdigit = 0;
 
501
                        digits = 0;
 
502
                        val = 0;
 
503
                        continue;
 
504
                }
 
505
                if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
 
506
                        getv4(curtok, tp, &bits) > 0)
 
507
                {
 
508
                        tp += NS_INADDRSZ;
 
509
                        saw_xdigit = 0;
 
510
                        break;                          /* '\0' was seen by inet_pton4(). */
 
511
                }
 
512
                if (ch == '/' && getbits(src, &bits) > 0)
 
513
                        break;
 
514
                goto enoent;
 
515
        }
 
516
        if (saw_xdigit)
 
517
        {
 
518
                if (tp + NS_INT16SZ > endp)
 
519
                        goto enoent;
 
520
                *tp++ = (u_char) (val >> 8) & 0xff;
 
521
                *tp++ = (u_char) val & 0xff;
 
522
        }
 
523
        if (bits == -1)
 
524
                bits = 128;
 
525
 
 
526
        endp = tmp + 16;
 
527
 
 
528
        if (colonp != NULL)
 
529
        {
 
530
                /*
 
531
                 * Since some memmove()'s erroneously fail to handle overlapping
 
532
                 * regions, we'll do the shift by hand.
 
533
                 */
 
534
                const int       n = tp - colonp;
 
535
                int                     i;
 
536
 
 
537
                if (tp == endp)
 
538
                        goto enoent;
 
539
                for (i = 1; i <= n; i++)
 
540
                {
 
541
                        endp[-i] = colonp[n - i];
 
542
                        colonp[n - i] = 0;
 
543
                }
 
544
                tp = endp;
 
545
        }
 
546
        if (tp != endp)
 
547
                goto enoent;
 
548
 
 
549
        /*
 
550
         * Copy out the result.
 
551
         */
 
552
        memcpy(dst, tmp, NS_IN6ADDRSZ);
 
553
 
 
554
        return (bits);
 
555
 
 
556
enoent:
 
557
        errno = ENOENT;
 
558
        return (-1);
 
559
 
 
560
emsgsize:
 
561
        errno = EMSGSIZE;
 
562
        return (-1);
 
563
}