xref: /freebsd/contrib/ntp/libntp/decodenetnum.c (revision 4e99f45480598189d49d45a825533a6c9e12f02c)
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 
21 static int
22 isnumstr(
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  */
36 int
37 decodenetnum(
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