summaryrefslogtreecommitdiff
path: root/src/libfreeswan/atoaddr.c
blob: 8f1be0a842ec935feb24a072b98a7fdd076ca24c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
 * conversion from ASCII forms of addresses to internal ones
 * Copyright (C) 1998, 1999  Henry Spencer.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/lgpl.txt>.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
 * License for more details.
 */
#include <sys/socket.h>

#include "internal.h"
#include "freeswan.h"

/*
 * Define NOLEADINGZEROS to interpret 032 as an error, not as 32.  There
 * is deliberately no way to interpret it as 26 (i.e., as octal).
 */

/*
 * Legal characters in a domain name.  Underscore technically is not,
 * but is a common misunderstanding.
 */
static const char namechars[] = "abcdefghijklmnopqrstuvwxyz0123456789"
				"ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";

static const char *try8hex(const char *, size_t, struct in_addr *);
static const char *try8hosthex(const char *, size_t, struct in_addr *);
static const char *trydotted(const char *, size_t, struct in_addr *);
static const char *getbyte(const char **, const char *, int *);

/*
 - atoaddr - convert ASCII name or dotted-decimal address to binary address
 */
const char *			/* NULL for success, else string literal */
atoaddr(src, srclen, addrp)
const char *src;
size_t srclen;			/* 0 means "apply strlen" */
struct in_addr *addrp;
{
	struct addrinfo hints, *res;
	struct netent *ne = NULL;
	const char *oops;
#	define	HEXLEN	10	/* strlen("0x11223344") */
#	ifndef ATOADDRBUF
#	define	ATOADDRBUF	100
#	endif
	char namebuf[ATOADDRBUF];
	char *p = namebuf;
	char *q;
	int error;

	if (srclen == 0)
		srclen = strlen(src);
	if (srclen == 0)
		return "empty string";

	/* might it be hex? */
	if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'x'))
		return try8hex(src+2, srclen-2, addrp);
	if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'h'))
		return try8hosthex(src+2, srclen-2, addrp);

	/* try it as dotted decimal */
	oops = trydotted(src, srclen, addrp);
	if (oops == NULL)
		return NULL;		/* it worked */
	if (*oops != '?')
		return oops;		/* it *was* probably meant as a d.q. */

	/* try it as a name -- first, NUL-terminate it */
	if (srclen > sizeof(namebuf)-1) {
		p = (char *) MALLOC(srclen+1);
		if (p == NULL)
			return "unable to allocate temporary space for name";
	}
	p[0] = '\0';
	strncat(p, src, srclen);

	/* next, check that it's a vaguely legal name */
	for (q = p; *q != '\0'; q++)
		if (!isprint(*q))
			return "unprintable character in name";
	if (strspn(p, namechars) != srclen)
		return "illegal (non-DNS-name) character in name";

	/* try as host name, failing that as /etc/networks network name */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_INET;
	error = getaddrinfo(p, NULL, &hints, &res);
	if (error != 0)
	{
		ne = getnetbyname(p);
		if (ne == NULL)
		{
			if (p != namebuf)
			{
				FREE(p);
			}
			return "name lookup failed";
		}
		addrp->s_addr = htonl(ne->n_net);
	}
	else
	{
		struct sockaddr_in *in = (struct sockaddr_in*)res->ai_addr;
		memcpy(&addrp->s_addr, &in->sin_addr.s_addr, sizeof(addrp->s_addr));
		freeaddrinfo(res);
	}

	if (p != namebuf)
	{
		FREE(p);
	}

	return NULL;
}

/*
 - try8hosthex - try conversion as an eight-digit host-order hex number
 */
const char *			/* NULL for success, else string literal */
try8hosthex(src, srclen, addrp)
const char *src;
size_t srclen;			/* should be 8 */
struct in_addr *addrp;
{
	const char *oops;
	unsigned long addr;

	if (srclen != 8)
		return "internal error, try8hex called with bad length";

	oops = atoul(src, srclen, 16, &addr);
	if (oops != NULL)
		return oops;

	addrp->s_addr = addr;
	return NULL;
}

/*
 - try8hex - try conversion as an eight-digit network-order hex number
 */
const char *			/* NULL for success, else string literal */
try8hex(src, srclen, addrp)
const char *src;
size_t srclen;			/* should be 8 */
struct in_addr *addrp;
{
	const char *oops;

	oops = try8hosthex(src, srclen, addrp);
	if (oops != NULL)
		return oops;

	addrp->s_addr = htonl(addrp->s_addr);
	return NULL;
}

/*
 - trydotted - try conversion as dotted decimal
 *
 * If the first char of a complaint is '?', that means "didn't look like
 * dotted decimal at all".
 */
const char *			/* NULL for success, else string literal */
trydotted(src, srclen, addrp)
const char *src;
size_t srclen;
struct in_addr *addrp;
{
	const char *stop = src + srclen;	/* just past end */
	int byte;
	const char *oops;
	unsigned long addr;
	int i;
#	define	NBYTES	4
#	define	BYTE	8

	addr = 0;
	for (i = 0; i < NBYTES && src < stop; i++) {
		oops = getbyte(&src, stop, &byte);
		if (oops != NULL) {
			if (*oops != '?')
				return oops;	/* bad number */
			if (i > 1)
				return oops+1;	/* failed number */
			return oops;		/* with leading '?' */
		}
		addr = (addr << BYTE) | byte;
		if (i < 3 && src < stop && *src++ != '.') {
			if (i == 0)
				return "?syntax error in dotted-decimal address";
			else
				return "syntax error in dotted-decimal address";
		}
	}
	addr <<= (NBYTES - i) * BYTE;
	if (src != stop)
		return "extra garbage on end of dotted-decimal address";

	addrp->s_addr = htonl(addr);
	return NULL;
}

/*
 - getbyte - try to scan a byte in dotted decimal
 * A subtlety here is that all this arithmetic on ASCII digits really is
 * highly portable -- ANSI C guarantees that digits 0-9 are contiguous.
 * It's easier to just do it ourselves than set up for a call to atoul().
 *
 * If the first char of a complaint is '?', that means "didn't look like a
 * number at all".
 */
const char *			/* NULL for success, else string literal */
getbyte(srcp, stop, retp)
const char **srcp;		/* *srcp is updated */
const char *stop;		/* first untouchable char */
int *retp;			/* return-value pointer */
{
	char c;
	const char *p;
	int no;

	if (*srcp >= stop)
		return "?empty number in dotted-decimal address";

	if (stop - *srcp >= 3 && **srcp == '0' && CIEQ(*(*srcp+1), 'x'))
		return "hex numbers not supported in dotted-decimal addresses";
#ifdef NOLEADINGZEROS
	if (stop - *srcp >= 2 && **srcp == '0' && isdigit(*(*srcp+1)))
		return "octal numbers not supported in dotted-decimal addresses";
#endif /* NOLEADINGZEROS */

	/* must be decimal, if it's numeric at all */
	no = 0;
	p = *srcp;
	while (p < stop && no <= 255 && (c = *p) >= '0' && c <= '9') {
		no = no*10 + (c - '0');
		p++;
	}
	if (p == *srcp)
		return "?non-numeric component in dotted-decimal address";
	*srcp = p;
	if (no > 255)
		return "byte overflow in dotted-decimal address";
	*retp = no;
	return NULL;
}