154359Sroberto/*
254359Sroberto * decodenetnum - return a net number (this is crude, but careful)
354359Sroberto */
4280849Scy#include <config.h>
554359Sroberto#include <sys/types.h>
654359Sroberto#include <ctype.h>
7280849Scy#ifdef HAVE_SYS_SOCKET_H
854359Sroberto#include <sys/socket.h>
9280849Scy#endif
10280849Scy#ifdef HAVE_NETINET_IN_H
1154359Sroberto#include <netinet/in.h>
12280849Scy#endif
1354359Sroberto
14280849Scy#include "ntp.h"
1554359Sroberto#include "ntp_stdlib.h"
1654359Sroberto
17358659Scy
18362716Scy/* If the given string position points to a decimal digit, parse the
19362716Scy * number. If this is not possible, or the parsing did not consume the
20362716Scy * whole string, or if the result exceeds the maximum value, return the
21362716Scy * default value.
22362716Scy */
23362716Scystatic unsigned long
24362716Scy_num_or_dflt(
25362716Scy	char *		sval,
26362716Scy	unsigned long	maxval,
27362716Scy	unsigned long	defval
28358659Scy	)
29358659Scy{
30362716Scy	char *		ep;
31362716Scy	unsigned long	num;
32362716Scy
33362716Scy	if (!(sval && isdigit(*(unsigned char*)sval)))
34362716Scy		return defval;
35362716Scy
36362716Scy	num = strtoul(sval, &ep, 10);
37362716Scy	if (!*ep && num <= maxval)
38362716Scy		return num;
39362716Scy
40362716Scy	return defval;
41358659Scy}
42358659Scy
43362716Scy/* If the given string position is not NULL and does not point to the
44362716Scy * terminator, replace the character with NUL and advance the pointer.
45362716Scy * Return the resulting position.
46362716Scy */
47362716Scystatic inline char*
48362716Scy_chop(
49362716Scy	char * sp)
50362716Scy{
51362716Scy	if (sp && *sp)
52362716Scy		*sp++ = '\0';
53362716Scy	return sp;
54362716Scy}
55362716Scy
56362716Scy/* If the given string position points to the given char, advance the
57362716Scy * pointer and return the result. Otherwise, return NULL.
58362716Scy */
59362716Scystatic inline char*
60362716Scy_skip(
61362716Scy	char * sp,
62362716Scy	int    ch)
63362716Scy{
64362716Scy	if (sp && *(unsigned char*)sp == ch)
65362716Scy		return (sp + 1);
66362716Scy	return NULL;
67362716Scy}
68362716Scy
69280849Scy/*
70280849Scy * decodenetnum		convert text IP address and port to sockaddr_u
71280849Scy *
72362716Scy * Returns FALSE (->0) for failure, TRUE (->1) for success.
73280849Scy */
7454359Srobertoint
7554359Srobertodecodenetnum(
7654359Sroberto	const char *num,
77362716Scy	sockaddr_u *net
7854359Sroberto	)
7954359Sroberto{
80362716Scy	/* Building a parser is more fun in Haskell, but here we go...
81362716Scy	 *
82362716Scy	 * This works through 'inet_pton()' taking the brunt of the
83362716Scy	 * work, after some string manipulations to split off URI
84362716Scy	 * brackets, ports and scope identifiers. The heuristics are
85362716Scy	 * simple but must hold for all _VALID_ addresses. inet_pton()
86362716Scy	 * will croak on bad ones later, but replicating the whole
87362716Scy	 * parser logic to detect errors is wasteful.
88362716Scy	 */
89358659Scy
90362716Scy	sockaddr_u	netnum;
91362716Scy	char		buf[64];	/* working copy of input */
92362716Scy	char		*haddr=buf;
93362716Scy	unsigned int	port=NTP_PORT, scope=0;
94362716Scy	unsigned short	afam=AF_UNSPEC;
95362716Scy
96362716Scy	/* copy input to working buffer with length check */
97362716Scy	if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf))
98358659Scy		return FALSE;
99289764Sglebius
100362716Scy	/* Identify address family and possibly the port, if given.  If
101362716Scy	 * this results in AF_UNSPEC, we will fail in the next step.
102362716Scy	 */
103362716Scy	if (*haddr == '[') {
104362716Scy		char * endp = strchr(++haddr, ']');
105362716Scy		if (endp) {
106362716Scy			port = _num_or_dflt(_skip(_chop(endp), ':'),
107362716Scy					      0xFFFFu, port);
108362716Scy			afam = strchr(haddr, ':') ? AF_INET6 : AF_INET;
109280849Scy		}
110280849Scy	} else {
111362716Scy		char *col = strchr(haddr, ':');
112362716Scy		char *dot = strchr(haddr, '.');
113362716Scy		if (col == dot) {
114362716Scy			/* no dot, no colon: bad! */
115362716Scy			afam = AF_UNSPEC;
116362716Scy		} else if (!col) {
117362716Scy			/* no colon, only dot: IPv4! */
118362716Scy			afam = AF_INET;
119362716Scy		} else if (!dot || col < dot) {
120362716Scy			/* no dot or 1st colon before 1st dot: IPv6! */
121362716Scy			afam = AF_INET6;
122362716Scy		} else {
123362716Scy			/* 1st dot before 1st colon: must be IPv4 with port */
124362716Scy			afam = AF_INET;
125362716Scy			port = _num_or_dflt(_chop(col), 0xFFFFu, port);
126362716Scy		}
12754359Sroberto	}
128362716Scy
129362716Scy	/* Since we don't know about additional members in the address
130362716Scy	 * structures, we wipe the result buffer thoroughly:
131362716Scy	 */
132362716Scy	memset(&netnum, 0, sizeof(netnum));
133362716Scy
134362716Scy	/* For AF_INET6, evaluate and remove any scope suffix. Have
135362716Scy	 * inet_pton() do the real work for AF_INET and AF_INET6, bail
136362716Scy	 * out otherwise:
137362716Scy	 */
138362716Scy	switch (afam) {
139362716Scy	case AF_INET:
140362716Scy		if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0)
141362716Scy			return FALSE;
142362716Scy		netnum.sa4.sin_port = htons((unsigned short)port);
143362716Scy		break;
144362716Scy
145362716Scy	case AF_INET6:
146362716Scy		scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope);
147362716Scy		if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0)
148362716Scy			return FALSE;
149362716Scy		netnum.sa6.sin6_port = htons((unsigned short)port);
150362716Scy		netnum.sa6.sin6_scope_id = scope;
151362716Scy		break;
152362716Scy
153362716Scy	case AF_UNSPEC:
154362716Scy	default:
155358659Scy		return FALSE;
156358659Scy	}
157358659Scy
158362716Scy	/* Collect the remaining pieces and feed the output, which was
159362716Scy	 * not touched so far:
160362716Scy	 */
161362716Scy	netnum.sa.sa_family = afam;
162362716Scy	memcpy(net, &netnum, sizeof(netnum));
163358659Scy	return TRUE;
16454359Sroberto}
165