Replacing gethostbyname, gethostbyname2 and their _r variants with getaddrinfo to...
[strongswan.git] / src / libfreeswan / ttoaddr.c
1 /*
2 * conversion from text forms of addresses to internal ones
3 * Copyright (C) 2000 Henry Spencer.
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Library General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 * License for more details.
14 */
15 #include <sys/socket.h>
16
17 #include "internal.h"
18 #include "freeswan.h"
19
20 /*
21 * Legal ASCII characters in a domain name. Underscore technically is not,
22 * but is a common misunderstanding. Non-ASCII characters are simply
23 * exempted from checking at the moment, to allow for UTF-8 encoded stuff;
24 * the purpose of this check is merely to catch blatant errors.
25 */
26 static const char namechars[] = "abcdefghijklmnopqrstuvwxyz0123456789"
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
28 #define ISASCII(c) (((c) & 0x80) == 0)
29
30 static err_t tryname(const char *, size_t, int, int, ip_address *);
31 static err_t tryhex(const char *, size_t, int, ip_address *);
32 static err_t trydotted(const char *, size_t, ip_address *);
33 static err_t getbyte(const char **, const char *, int *);
34 static err_t colon(const char *, size_t, ip_address *);
35 static err_t getpiece(const char **, const char *, unsigned *);
36
37 /*
38 - ttoaddr - convert text name or dotted-decimal address to binary address
39 */
40 err_t /* NULL for success, else string literal */
41 ttoaddr(src, srclen, af, dst)
42 const char *src;
43 size_t srclen; /* 0 means "apply strlen" */
44 int af; /* address family */
45 ip_address *dst;
46 {
47 err_t oops;
48 # define HEXLEN 10 /* strlen("0x11223344") */
49 int nultermd;
50
51 if (srclen == 0) {
52 srclen = strlen(src);
53 if (srclen == 0)
54 return "empty string";
55 nultermd = 1;
56 } else
57 nultermd = 0; /* at least, not *known* to be terminated */
58
59 switch (af) {
60 case AF_INET:
61 case AF_INET6:
62 case 0: /* guess */
63 break;
64
65 default:
66 return "invalid address family";
67 }
68
69 if (af == AF_INET && srclen == HEXLEN && *src == '0') {
70 if (*(src+1) == 'x' || *(src+1) == 'X')
71 return tryhex(src+2, srclen-2, 'x', dst);
72 if (*(src+1) == 'h' || *(src+1) == 'H')
73 return tryhex(src+2, srclen-2, 'h', dst);
74 }
75
76 if (memchr(src, ':', srclen) != NULL) {
77 if(af == 0)
78 {
79 af = AF_INET6;
80 }
81
82 if (af != AF_INET6)
83 return "non-ipv6 address may not contain `:'";
84 return colon(src, srclen, dst);
85 }
86
87 if (af == 0 || af == AF_INET) {
88 oops = trydotted(src, srclen, dst);
89 if (oops == NULL)
90 return NULL; /* it worked */
91 if (*oops != '?')
92 return oops; /* probably meant as d-d */
93 }
94
95 return tryname(src, srclen, nultermd, af, dst);
96 }
97
98 /*
99 - tnatoaddr - convert text numeric address (only) to binary address
100 */
101 err_t /* NULL for success, else string literal */
102 tnatoaddr(src, srclen, af, dst)
103 const char *src;
104 size_t srclen; /* 0 means "apply strlen" */
105 int af; /* address family */
106 ip_address *dst;
107 {
108 err_t oops;
109
110 if (srclen == 0) {
111 srclen = strlen(src);
112 if (srclen == 0)
113 return "empty string";
114 }
115
116 switch (af) {
117 case 0: /* guess */
118 oops = colon(src, srclen, dst);
119 if(oops == NULL)
120 {
121 return NULL;
122 }
123 oops = trydotted(src, srclen, dst);
124 if(oops == NULL)
125 {
126 return NULL;
127 }
128 return "does not appear to be either IPv4 or IPv6 numeric address";
129 break;
130
131 case AF_INET6:
132 return colon(src, srclen, dst);
133 break;
134 case AF_INET:
135 oops = trydotted(src, srclen, dst);
136 if (oops == NULL)
137 return NULL; /* it worked */
138 if (*oops != '?')
139 return oops; /* probably meant as d-d */
140 return "does not appear to be numeric address";
141 break;
142 default:
143 return "unknown address family in tnatoaddr";
144 break;
145 }
146 }
147
148 /*
149 - tryname - try it as a name
150 * Slightly complicated by lack of reliable NUL termination in source.
151 */
152 static err_t
153 tryname(src, srclen, nultermd, af, dst)
154 const char *src;
155 size_t srclen;
156 int nultermd; /* is it known to be NUL-terminated? */
157 int af;
158 ip_address *dst;
159 {
160 struct addrinfo hints, *res;
161 struct netent *ne = NULL;
162 char namebuf[100]; /* enough for most DNS names */
163 const char *cp;
164 char *p = namebuf;
165 unsigned char *addr = NULL;
166 size_t n;
167 int error;
168 err_t err = NULL;
169
170 for (cp = src, n = srclen; n > 0; cp++, n--)
171 if (ISASCII(*cp) && strchr(namechars, *cp) == NULL)
172 return "illegal (non-DNS-name) character in name";
173
174 if (nultermd)
175 cp = src;
176 else {
177 if (srclen+1 > sizeof(namebuf)) {
178 p = (char *) MALLOC(srclen+1);
179 if (p == NULL)
180 return "unable to get temporary space for name";
181 }
182 p[0] = '\0'; /* strncpy semantics are wrong */
183 strncat(p, src, srclen);
184 cp = (const char *)p;
185 }
186
187 memset(&hints, 0, sizeof(hints));
188 hints.ai_family = af;
189 error = getaddrinfo(cp, NULL, &hints, &res);
190 if (error != 0)
191 { /* getaddrinfo failed, try getnetbyname */
192 if (af == AF_INET)
193 {
194 ne = getnetbyname(cp);
195 if (ne != NULL)
196 {
197 ne->n_net = htonl(ne->n_net);
198 addr = (unsigned char*)&ne->n_net;
199 err = initaddr(addr, sizeof(ne->n_net), af, dst);
200 }
201 }
202 }
203 else
204 {
205 addr = res->ai_addr->sa_data;
206 err = initaddr(addr, res->ai_addrlen, af, dst);
207 freeaddrinfo(res);
208 }
209
210 if (p != namebuf)
211 {
212 FREE(p);
213 }
214
215 if (addr == NULL)
216 {
217 return "does not look numeric and name lookup failed";
218 }
219
220 return err;
221 }
222
223 /*
224 - tryhex - try conversion as an eight-digit hex number (AF_INET only)
225 */
226 static err_t
227 tryhex(src, srclen, flavor, dst)
228 const char *src;
229 size_t srclen; /* should be 8 */
230 int flavor; /* 'x' for network order, 'h' for host order */
231 ip_address *dst;
232 {
233 err_t oops;
234 unsigned long ul;
235 union {
236 uint32_t addr;
237 unsigned char buf[4];
238 } u;
239
240 if (srclen != 8)
241 return "internal error, tryhex called with bad length";
242
243 oops = ttoul(src, srclen, 16, &ul);
244 if (oops != NULL)
245 return oops;
246
247 u.addr = (flavor == 'h') ? ul : htonl(ul);
248 return initaddr(u.buf, sizeof(u.buf), AF_INET, dst);
249 }
250
251 /*
252 - trydotted - try conversion as dotted decimal (AF_INET only)
253 *
254 * If the first char of a complaint is '?', that means "didn't look like
255 * dotted decimal at all".
256 */
257 static err_t
258 trydotted(src, srclen, dst)
259 const char *src;
260 size_t srclen;
261 ip_address *dst;
262 {
263 const char *stop = src + srclen; /* just past end */
264 int byte;
265 err_t oops;
266 # define NBYTES 4
267 unsigned char buf[NBYTES];
268 int i;
269
270 memset(buf, 0, sizeof(buf));
271 for (i = 0; i < NBYTES && src < stop; i++) {
272 oops = getbyte(&src, stop, &byte);
273 if (oops != NULL) {
274 if (*oops != '?')
275 return oops; /* bad number */
276 if (i > 1)
277 return oops+1; /* failed number */
278 return oops; /* with leading '?' */
279 }
280 buf[i] = byte;
281 if (i < 3 && src < stop && *src++ != '.') {
282 if (i == 0)
283 return "?syntax error in dotted-decimal address";
284 else
285 return "syntax error in dotted-decimal address";
286 }
287 }
288 if (src != stop)
289 return "extra garbage on end of dotted-decimal address";
290
291 return initaddr(buf, sizeof(buf), AF_INET, dst);
292 }
293
294 /*
295 - getbyte - try to scan a byte in dotted decimal
296 * A subtlety here is that all this arithmetic on ASCII digits really is
297 * highly portable -- ANSI C guarantees that digits 0-9 are contiguous.
298 * It's easier to just do it ourselves than set up for a call to ttoul().
299 *
300 * If the first char of a complaint is '?', that means "didn't look like a
301 * number at all".
302 */
303 err_t
304 getbyte(srcp, stop, retp)
305 const char **srcp; /* *srcp is updated */
306 const char *stop; /* first untouchable char */
307 int *retp; /* return-value pointer */
308 {
309 char c;
310 const char *p;
311 int no;
312
313 if (*srcp >= stop)
314 return "?empty number in dotted-decimal address";
315
316 no = 0;
317 p = *srcp;
318 while (p < stop && no <= 255 && (c = *p) >= '0' && c <= '9') {
319 no = no*10 + (c - '0');
320 p++;
321 }
322 if (p == *srcp)
323 return "?non-numeric component in dotted-decimal address";
324 *srcp = p;
325 if (no > 255)
326 return "byte overflow in dotted-decimal address";
327 *retp = no;
328 return NULL;
329 }
330
331 /*
332 - colon - convert IPv6 "numeric" address
333 */
334 static err_t
335 colon(src, srclen, dst)
336 const char *src;
337 size_t srclen; /* known to be >0 */
338 ip_address *dst;
339 {
340 const char *stop = src + srclen; /* just past end */
341 unsigned piece = 0;
342 int gapat; /* where was empty piece seen */
343 err_t oops;
344 # define NPIECES 8
345 unsigned char buf[NPIECES*2]; /* short may have wrong byte order */
346 int i;
347 int j;
348 # define IT "IPv6 numeric address"
349 int naftergap;
350
351 /* leading or trailing :: becomes single empty field */
352 if (*src == ':') { /* legal only if leading :: */
353 if (srclen == 1 || *(src+1) != ':')
354 return "illegal leading `:' in " IT;
355 if (srclen == 2) {
356 unspecaddr(AF_INET6, dst);
357 return NULL;
358 }
359 src++; /* past first but not second */
360 srclen--;
361 }
362 if (*(stop-1) == ':') { /* legal only if trailing :: */
363 if (srclen == 1 || *(stop-2) != ':')
364 return "illegal trailing `:' in " IT;
365 srclen--; /* leave one */
366 }
367
368 gapat = -1;
369 for (i = 0; i < NPIECES && src < stop; i++) {
370 oops = getpiece(&src, stop, &piece);
371 if (oops != NULL && *oops == ':') { /* empty field */
372 if (gapat >= 0)
373 return "more than one :: in " IT;
374 gapat = i;
375 } else if (oops != NULL)
376 return oops;
377 buf[2*i] = piece >> 8;
378 buf[2*i + 1] = piece & 0xff;
379 if (i < NPIECES-1) { /* there should be more input */
380 if (src == stop && gapat < 0)
381 return IT " ends prematurely";
382 if (src != stop && *src++ != ':')
383 return "syntax error in " IT;
384 }
385 }
386 if (src != stop)
387 return "extra garbage on end of " IT;
388
389 if (gapat < 0 && i < NPIECES) /* should have been caught earlier */
390 return "incomplete " IT " (internal error)";
391 if (gapat >= 0 && i == NPIECES)
392 return "non-abbreviating empty field in " IT;
393 if (gapat >= 0) {
394 naftergap = i - (gapat + 1);
395 for (i--, j = NPIECES-1; naftergap > 0; i--, j--, naftergap--) {
396 buf[2*j] = buf[2*i];
397 buf[2*j + 1] = buf[2*i + 1];
398 }
399 for (; j >= gapat; j--)
400 buf[2*j] = buf[2*j + 1] = 0;
401 }
402
403 return initaddr(buf, sizeof(buf), AF_INET6, dst);
404 }
405
406 /*
407 - getpiece - try to scan one 16-bit piece of an IPv6 address
408 */
409 err_t /* ":" means "empty field seen" */
410 getpiece(srcp, stop, retp)
411 const char **srcp; /* *srcp is updated */
412 const char *stop; /* first untouchable char */
413 unsigned *retp; /* return-value pointer */
414 {
415 const char *p;
416 # define NDIG 4
417 int d;
418 unsigned long ret;
419 err_t oops;
420
421 if (*srcp >= stop || **srcp == ':') { /* empty field */
422 *retp = 0;
423 return ":";
424 }
425
426 p = *srcp;
427 d = 0;
428 while (p < stop && d < NDIG && isxdigit(*p)) {
429 p++;
430 d++;
431 }
432 if (d == 0)
433 return "non-hex field in IPv6 numeric address";
434 if (p < stop && d == NDIG && isxdigit(*p))
435 return "field in IPv6 numeric address longer than 4 hex digits";
436
437 oops = ttoul(*srcp, d, 16, &ret);
438 if (oops != NULL) /* shouldn't happen, really... */
439 return oops;
440
441 *srcp = p;
442 *retp = ret;
443 return NULL;
444 }