Uniformly refer to RFCs as 'RFC xxxx' and not 'RFCxxxx' or 'RFC-xxxx'.
[dragonfly.git] / lib / libc / net / inet_net_pton.c
1 /*
2  * Copyright (c) 1996 by Internet Software Consortium.
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * $From Id: inet_net_pton.c,v 1.8 1996/11/21 10:28:12 vixie Exp $
18  * $FreeBSD: src/lib/libc/net/inet_net_pton.c,v 1.6 1999/08/28 00:00:10 peter Exp $
19  * $DragonFly: src/lib/libc/net/inet_net_pton.c,v 1.4 2005/11/13 02:04:47 swildner Exp $
20  */
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 #ifdef SPRINTF_CHAR
35 # define SPRINTF(x) strlen(sprintf/**/x)
36 #else
37 # define SPRINTF(x) ((size_t)sprintf x)
38 #endif
39
40 static int      inet_net_pton_ipv4 (const char *src, u_char *dst,
41                                         size_t size);
42
43 /*
44  * static 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 int
57 inet_net_pton(int af, const char *src, void *dst, size_t size)
58 {
59         switch (af) {
60         case AF_INET:
61                 return (inet_net_pton_ipv4(src, dst, size));
62         default:
63                 errno = EAFNOSUPPORT;
64                 return (-1);
65         }
66 }
67
68 /*
69  * static int
70  * inet_net_pton_ipv4(src, dst, size)
71  *      convert IPv4 network number from presentation to network format.
72  *      accepts hex octets, hex strings, decimal octets, and /CIDR.
73  *      "size" is in bytes and describes "dst".
74  * return:
75  *      number of bits, either imputed classfully or specified with /CIDR,
76  *      or -1 if some failure occurred (check errno).  ENOENT means it was
77  *      not an IPv4 network specification.
78  * note:
79  *      network byte order assumed.  this means 192.5.5.240/28 has
80  *      0x11110000 in its fourth octet.
81  * author:
82  *      Paul Vixie (ISC), June 1996
83  */
84 static int
85 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
86 {
87         static const char
88                 xdigits[] = "0123456789abcdef",
89                 digits[] = "0123456789";
90         int n, ch, tmp, dirty, bits;
91         const u_char *odst = dst;
92
93         ch = *src++;
94         if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
95             && isascii(src[1]) && isxdigit(src[1])) {
96                 /* Hexadecimal: Eat nybble string. */
97                 if (size <= 0)
98                         goto emsgsize;
99                 *dst = 0, dirty = 0;
100                 src++;  /* skip x or X. */
101                 while ((ch = *src++) != '\0' &&
102                        isascii(ch) && isxdigit(ch)) {
103                         if (isupper(ch))
104                                 ch = tolower(ch);
105                         n = strchr(xdigits, ch) - xdigits;
106                         assert(n >= 0 && n <= 15);
107                         *dst |= n;
108                         if (!dirty++)
109                                 *dst <<= 4;
110                         else if (size-- > 0)
111                                 *++dst = 0, dirty = 0;
112                         else
113                                 goto emsgsize;
114                 }
115                 if (dirty)
116                         size--;
117         } else if (isascii(ch) && isdigit(ch)) {
118                 /* Decimal: eat dotted digit string. */
119                 for (;;) {
120                         tmp = 0;
121                         do {
122                                 n = strchr(digits, ch) - digits;
123                                 assert(n >= 0 && n <= 9);
124                                 tmp *= 10;
125                                 tmp += n;
126                                 if (tmp > 255)
127                                         goto enoent;
128                         } while ((ch = *src++) != '\0' &&
129                                  isascii(ch) && isdigit(ch));
130                         if (size-- <= 0)
131                                 goto emsgsize;
132                         *dst++ = (u_char) tmp;
133                         if (ch == '\0' || ch == '/')
134                                 break;
135                         if (ch != '.')
136                                 goto enoent;
137                         ch = *src++;
138                         if (!isascii(ch) || !isdigit(ch))
139                                 goto enoent;
140                 }
141         } else
142                 goto enoent;
143
144         bits = -1;
145         if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
146                 /* CIDR width specifier.  Nothing can follow it. */
147                 ch = *src++;    /* Skip over the /. */
148                 bits = 0;
149                 do {
150                         n = strchr(digits, ch) - digits;
151                         assert(n >= 0 && n <= 9);
152                         bits *= 10;
153                         bits += n;
154                 } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
155                 if (ch != '\0')
156                         goto enoent;
157                 if (bits > 32)
158                         goto emsgsize;
159         }
160
161         /* Firey death and destruction unless we prefetched EOS. */
162         if (ch != '\0')
163                 goto enoent;
164
165         /* If nothing was written to the destination, we found no address. */
166         if (dst == odst)
167                 goto enoent;
168         /* If no CIDR spec was given, infer width from net class. */
169         if (bits == -1) {
170                 if (*odst >= 240)       /* Class E */
171                         bits = 32;
172                 else if (*odst >= 224)  /* Class D */
173                         bits = 4;
174                 else if (*odst >= 192)  /* Class C */
175                         bits = 24;
176                 else if (*odst >= 128)  /* Class B */
177                         bits = 16;
178                 else                    /* Class A */
179                         bits = 8;
180                 /* If imputed mask is narrower than specified octets, widen. */
181                 if (bits >= 8 && bits < ((dst - odst) * 8))
182                         bits = (dst - odst) * 8;
183         }
184         /* Extend network to cover the actual mask. */
185         while (bits > ((dst - odst) * 8)) {
186                 if (size-- <= 0)
187                         goto emsgsize;
188                 *dst++ = '\0';
189         }
190         return (bits);
191
192  enoent:
193         errno = ENOENT;
194         return (-1);
195
196  emsgsize:
197         errno = EMSGSIZE;
198         return (-1);
199 }
200
201 /*
202  * Weak aliases for applications that use certain private entry points,
203  * and fail to include <arpa/inet.h>.
204  */
205 #undef inet_net_pton
206 __weak_reference(__inet_net_pton, inet_net_pton);