decodenetnum.c revision 358659
1/*
2 * decodenetnum - return a net number (this is crude, but careful)
3 */
4#include <config.h>
5#include <sys/types.h>
6#include <ctype.h>
7#ifdef HAVE_SYS_SOCKET_H
8#include <sys/socket.h>
9#endif
10#ifdef HAVE_NETINET_IN_H
11#include <netinet/in.h>
12#endif
13
14#include "ntp.h"
15#include "ntp_stdlib.h"
16#include "ntp_assert.h"
17
18#define PORTSTR(x) _PORTSTR(x)
19#define _PORTSTR(x) #x
20
21static int
22isnumstr(
23	const char *s
24	)
25{
26	while (*s >= '0' && *s <= '9')
27		++s;
28	return !*s;
29}
30
31/*
32 * decodenetnum		convert text IP address and port to sockaddr_u
33 *
34 * Returns 0 for failure, 1 for success.
35 */
36int
37decodenetnum(
38	const char *num,
39	sockaddr_u *netnum
40	)
41{
42	static const char * const servicename = "ntp";
43	static const char * const serviceport = PORTSTR(NTP_PORT);
44
45	struct addrinfo hints, *ai = NULL;
46	int err;
47	const char *host_str;
48	const char *port_str;
49	char *pp;
50	char *np;
51	char nbuf[80];
52
53	REQUIRE(num != NULL);
54
55	if (strlen(num) >= sizeof(nbuf)) {
56		printf("length error\n");
57		return FALSE;
58	}
59
60	port_str = servicename;
61	if ('[' != num[0]) {
62		/*
63		 * to distinguish IPv6 embedded colons from a port
64		 * specification on an IPv4 address, assume all
65		 * legal IPv6 addresses have at least two colons.
66		 */
67		pp = strchr(num, ':');
68		if (NULL == pp)
69			host_str = num;	/* no colons */
70		else if (NULL != strchr(pp + 1, ':'))
71			host_str = num;	/* two or more colons */
72		else {			/* one colon */
73			strlcpy(nbuf, num, sizeof(nbuf));
74			host_str = nbuf;
75			pp = strchr(nbuf, ':');
76			*pp = '\0';
77			port_str = pp + 1;
78		}
79	} else {
80		host_str = np = nbuf;
81		while (*++num && ']' != *num)
82			*np++ = *num;
83		*np = 0;
84		if (']' == num[0] && ':' == num[1] && '\0' != num[2])
85			port_str = &num[2];
86	}
87	if ( ! *host_str)
88		return FALSE;
89	if ( ! *port_str)
90		port_str = servicename;
91
92	ZERO(hints);
93	hints.ai_flags |= Z_AI_NUMERICHOST;
94	if (isnumstr(port_str))
95		hints.ai_flags |= Z_AI_NUMERICSERV;
96	err = getaddrinfo(host_str, port_str, &hints, &ai);
97	/* retry with default service name if the service lookup failed */
98	if (err == EAI_SERVICE && strcmp(port_str, servicename)) {
99		hints.ai_flags &= ~Z_AI_NUMERICSERV;
100		port_str = servicename;
101		err = getaddrinfo(host_str, port_str, &hints, &ai);
102	}
103	/* retry another time with default service port if the service lookup failed */
104	if (err == EAI_SERVICE && strcmp(port_str, serviceport)) {
105		hints.ai_flags |= Z_AI_NUMERICSERV;
106		port_str = serviceport;
107		err = getaddrinfo(host_str, port_str, &hints, &ai);
108	}
109	if (err != 0)
110		return FALSE;
111
112	INSIST(ai->ai_addrlen <= sizeof(*netnum));
113	ZERO(*netnum);
114	memcpy(netnum, ai->ai_addr, ai->ai_addrlen);
115	freeaddrinfo(ai);
116
117	return TRUE;
118}
119