Fixing address resolution via getaddrinfo in libfreeswan.
[strongswan.git] / src / libfreeswan / atoaddr.c
1 /*
2 * conversion from ASCII forms of addresses to internal ones
3 * Copyright (C) 1998, 1999 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 * Define NOLEADINGZEROS to interpret 032 as an error, not as 32. There
22 * is deliberately no way to interpret it as 26 (i.e., as octal).
23 */
24
25 /*
26 * Legal characters in a domain name. Underscore technically is not,
27 * but is a common misunderstanding.
28 */
29 static const char namechars[] = "abcdefghijklmnopqrstuvwxyz0123456789"
30 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
31
32 static const char *try8hex(const char *, size_t, struct in_addr *);
33 static const char *try8hosthex(const char *, size_t, struct in_addr *);
34 static const char *trydotted(const char *, size_t, struct in_addr *);
35 static const char *getbyte(const char **, const char *, int *);
36
37 /*
38 - atoaddr - convert ASCII name or dotted-decimal address to binary address
39 */
40 const char * /* NULL for success, else string literal */
41 atoaddr(src, srclen, addrp)
42 const char *src;
43 size_t srclen; /* 0 means "apply strlen" */
44 struct in_addr *addrp;
45 {
46 struct addrinfo hints, *res;
47 struct netent *ne = NULL;
48 const char *oops;
49 # define HEXLEN 10 /* strlen("0x11223344") */
50 # ifndef ATOADDRBUF
51 # define ATOADDRBUF 100
52 # endif
53 char namebuf[ATOADDRBUF];
54 char *p = namebuf;
55 char *q;
56 int error;
57
58 if (srclen == 0)
59 srclen = strlen(src);
60 if (srclen == 0)
61 return "empty string";
62
63 /* might it be hex? */
64 if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'x'))
65 return try8hex(src+2, srclen-2, addrp);
66 if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'h'))
67 return try8hosthex(src+2, srclen-2, addrp);
68
69 /* try it as dotted decimal */
70 oops = trydotted(src, srclen, addrp);
71 if (oops == NULL)
72 return NULL; /* it worked */
73 if (*oops != '?')
74 return oops; /* it *was* probably meant as a d.q. */
75
76 /* try it as a name -- first, NUL-terminate it */
77 if (srclen > sizeof(namebuf)-1) {
78 p = (char *) MALLOC(srclen+1);
79 if (p == NULL)
80 return "unable to allocate temporary space for name";
81 }
82 p[0] = '\0';
83 strncat(p, src, srclen);
84
85 /* next, check that it's a vaguely legal name */
86 for (q = p; *q != '\0'; q++)
87 if (!isprint(*q))
88 return "unprintable character in name";
89 if (strspn(p, namechars) != srclen)
90 return "illegal (non-DNS-name) character in name";
91
92 /* try as host name, failing that as /etc/networks network name */
93 memset(&hints, 0, sizeof(hints));
94 hints.ai_family = AF_INET;
95 error = getaddrinfo(p, NULL, &hints, &res);
96 if (error != 0)
97 {
98 ne = getnetbyname(p);
99 if (ne == NULL)
100 {
101 if (p != namebuf)
102 {
103 FREE(p);
104 }
105 return "name lookup failed";
106 }
107 addrp->s_addr = htonl(ne->n_net);
108 }
109 else
110 {
111 struct sockaddr_in *in = (struct sockaddr_in*)res->ai_addr;
112 memcpy(&addrp->s_addr, &in->sin_addr.s_addr, sizeof(addrp->s_addr));
113 freeaddrinfo(res);
114 }
115
116 if (p != namebuf)
117 {
118 FREE(p);
119 }
120
121 return NULL;
122 }
123
124 /*
125 - try8hosthex - try conversion as an eight-digit host-order hex number
126 */
127 const char * /* NULL for success, else string literal */
128 try8hosthex(src, srclen, addrp)
129 const char *src;
130 size_t srclen; /* should be 8 */
131 struct in_addr *addrp;
132 {
133 const char *oops;
134 unsigned long addr;
135
136 if (srclen != 8)
137 return "internal error, try8hex called with bad length";
138
139 oops = atoul(src, srclen, 16, &addr);
140 if (oops != NULL)
141 return oops;
142
143 addrp->s_addr = addr;
144 return NULL;
145 }
146
147 /*
148 - try8hex - try conversion as an eight-digit network-order hex number
149 */
150 const char * /* NULL for success, else string literal */
151 try8hex(src, srclen, addrp)
152 const char *src;
153 size_t srclen; /* should be 8 */
154 struct in_addr *addrp;
155 {
156 const char *oops;
157
158 oops = try8hosthex(src, srclen, addrp);
159 if (oops != NULL)
160 return oops;
161
162 addrp->s_addr = htonl(addrp->s_addr);
163 return NULL;
164 }
165
166 /*
167 - trydotted - try conversion as dotted decimal
168 *
169 * If the first char of a complaint is '?', that means "didn't look like
170 * dotted decimal at all".
171 */
172 const char * /* NULL for success, else string literal */
173 trydotted(src, srclen, addrp)
174 const char *src;
175 size_t srclen;
176 struct in_addr *addrp;
177 {
178 const char *stop = src + srclen; /* just past end */
179 int byte;
180 const char *oops;
181 unsigned long addr;
182 int i;
183 # define NBYTES 4
184 # define BYTE 8
185
186 addr = 0;
187 for (i = 0; i < NBYTES && src < stop; i++) {
188 oops = getbyte(&src, stop, &byte);
189 if (oops != NULL) {
190 if (*oops != '?')
191 return oops; /* bad number */
192 if (i > 1)
193 return oops+1; /* failed number */
194 return oops; /* with leading '?' */
195 }
196 addr = (addr << BYTE) | byte;
197 if (i < 3 && src < stop && *src++ != '.') {
198 if (i == 0)
199 return "?syntax error in dotted-decimal address";
200 else
201 return "syntax error in dotted-decimal address";
202 }
203 }
204 addr <<= (NBYTES - i) * BYTE;
205 if (src != stop)
206 return "extra garbage on end of dotted-decimal address";
207
208 addrp->s_addr = htonl(addr);
209 return NULL;
210 }
211
212 /*
213 - getbyte - try to scan a byte in dotted decimal
214 * A subtlety here is that all this arithmetic on ASCII digits really is
215 * highly portable -- ANSI C guarantees that digits 0-9 are contiguous.
216 * It's easier to just do it ourselves than set up for a call to atoul().
217 *
218 * If the first char of a complaint is '?', that means "didn't look like a
219 * number at all".
220 */
221 const char * /* NULL for success, else string literal */
222 getbyte(srcp, stop, retp)
223 const char **srcp; /* *srcp is updated */
224 const char *stop; /* first untouchable char */
225 int *retp; /* return-value pointer */
226 {
227 char c;
228 const char *p;
229 int no;
230
231 if (*srcp >= stop)
232 return "?empty number in dotted-decimal address";
233
234 if (stop - *srcp >= 3 && **srcp == '0' && CIEQ(*(*srcp+1), 'x'))
235 return "hex numbers not supported in dotted-decimal addresses";
236 #ifdef NOLEADINGZEROS
237 if (stop - *srcp >= 2 && **srcp == '0' && isdigit(*(*srcp+1)))
238 return "octal numbers not supported in dotted-decimal addresses";
239 #endif /* NOLEADINGZEROS */
240
241 /* must be decimal, if it's numeric at all */
242 no = 0;
243 p = *srcp;
244 while (p < stop && no <= 255 && (c = *p) >= '0' && c <= '9') {
245 no = no*10 + (c - '0');
246 p++;
247 }
248 if (p == *srcp)
249 return "?non-numeric component in dotted-decimal address";
250 *srcp = p;
251 if (no > 255)
252 return "byte overflow in dotted-decimal address";
253 *retp = no;
254 return NULL;
255 }