1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert * decodenetnum - return a net number (this is crude, but careful)
3c0b746e5SOllivier Robert */
42b15cb3dSCy Schubert #include <config.h>
5c0b746e5SOllivier Robert #include <sys/types.h>
6c0b746e5SOllivier Robert #include <ctype.h>
72b15cb3dSCy Schubert #ifdef HAVE_SYS_SOCKET_H
8c0b746e5SOllivier Robert #include <sys/socket.h>
92b15cb3dSCy Schubert #endif
102b15cb3dSCy Schubert #ifdef HAVE_NETINET_IN_H
11c0b746e5SOllivier Robert #include <netinet/in.h>
122b15cb3dSCy Schubert #endif
13c0b746e5SOllivier Robert
142b15cb3dSCy Schubert #include "ntp.h"
15c0b746e5SOllivier Robert #include "ntp_stdlib.h"
16c0b746e5SOllivier Robert
172d4e511cSCy Schubert
18*767173ceSCy Schubert /* If the given string position points to a decimal digit, parse the
19*767173ceSCy Schubert * number. If this is not possible, or the parsing did not consume the
20*767173ceSCy Schubert * whole string, or if the result exceeds the maximum value, return the
21*767173ceSCy Schubert * default value.
22*767173ceSCy Schubert */
23*767173ceSCy Schubert static unsigned long
_num_or_dflt(char * sval,unsigned long maxval,unsigned long defval)24*767173ceSCy Schubert _num_or_dflt(
25*767173ceSCy Schubert char * sval,
26*767173ceSCy Schubert unsigned long maxval,
27*767173ceSCy Schubert unsigned long defval
282d4e511cSCy Schubert )
292d4e511cSCy Schubert {
30*767173ceSCy Schubert char * ep;
31*767173ceSCy Schubert unsigned long num;
32*767173ceSCy Schubert
33*767173ceSCy Schubert if (!(sval && isdigit(*(unsigned char*)sval)))
34*767173ceSCy Schubert return defval;
35*767173ceSCy Schubert
36*767173ceSCy Schubert num = strtoul(sval, &ep, 10);
37*767173ceSCy Schubert if (!*ep && num <= maxval)
38*767173ceSCy Schubert return num;
39*767173ceSCy Schubert
40*767173ceSCy Schubert return defval;
41*767173ceSCy Schubert }
42*767173ceSCy Schubert
43*767173ceSCy Schubert /* If the given string position is not NULL and does not point to the
44*767173ceSCy Schubert * terminator, replace the character with NUL and advance the pointer.
45*767173ceSCy Schubert * Return the resulting position.
46*767173ceSCy Schubert */
47*767173ceSCy Schubert static inline char*
_chop(char * sp)48*767173ceSCy Schubert _chop(
49*767173ceSCy Schubert char * sp)
50*767173ceSCy Schubert {
51*767173ceSCy Schubert if (sp && *sp)
52*767173ceSCy Schubert *sp++ = '\0';
53*767173ceSCy Schubert return sp;
54*767173ceSCy Schubert }
55*767173ceSCy Schubert
56*767173ceSCy Schubert /* If the given string position points to the given char, advance the
57*767173ceSCy Schubert * pointer and return the result. Otherwise, return NULL.
58*767173ceSCy Schubert */
59*767173ceSCy Schubert static inline char*
_skip(char * sp,int ch)60*767173ceSCy Schubert _skip(
61*767173ceSCy Schubert char * sp,
62*767173ceSCy Schubert int ch)
63*767173ceSCy Schubert {
64*767173ceSCy Schubert if (sp && *(unsigned char*)sp == ch)
65*767173ceSCy Schubert return (sp + 1);
66*767173ceSCy Schubert return NULL;
672d4e511cSCy Schubert }
682d4e511cSCy Schubert
692b15cb3dSCy Schubert /*
702b15cb3dSCy Schubert * decodenetnum convert text IP address and port to sockaddr_u
712b15cb3dSCy Schubert *
72*767173ceSCy Schubert * Returns FALSE (->0) for failure, TRUE (->1) for success.
732b15cb3dSCy Schubert */
74c0b746e5SOllivier Robert int
decodenetnum(const char * num,sockaddr_u * net)75c0b746e5SOllivier Robert decodenetnum(
76c0b746e5SOllivier Robert const char *num,
77*767173ceSCy Schubert sockaddr_u *net
78c0b746e5SOllivier Robert )
79c0b746e5SOllivier Robert {
80*767173ceSCy Schubert /* Building a parser is more fun in Haskell, but here we go...
81*767173ceSCy Schubert *
82*767173ceSCy Schubert * This works through 'inet_pton()' taking the brunt of the
83*767173ceSCy Schubert * work, after some string manipulations to split off URI
84*767173ceSCy Schubert * brackets, ports and scope identifiers. The heuristics are
85*767173ceSCy Schubert * simple but must hold for all _VALID_ addresses. inet_pton()
86*767173ceSCy Schubert * will croak on bad ones later, but replicating the whole
87*767173ceSCy Schubert * parser logic to detect errors is wasteful.
882b15cb3dSCy Schubert */
89*767173ceSCy Schubert
90*767173ceSCy Schubert sockaddr_u netnum;
91*767173ceSCy Schubert char buf[64]; /* working copy of input */
92*767173ceSCy Schubert char *haddr=buf;
93*767173ceSCy Schubert unsigned int port=NTP_PORT, scope=0;
94*767173ceSCy Schubert unsigned short afam=AF_UNSPEC;
95*767173ceSCy Schubert
96*767173ceSCy Schubert /* copy input to working buffer with length check */
97*767173ceSCy Schubert if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf))
98*767173ceSCy Schubert return FALSE;
99*767173ceSCy Schubert
100*767173ceSCy Schubert /* Identify address family and possibly the port, if given. If
101*767173ceSCy Schubert * this results in AF_UNSPEC, we will fail in the next step.
102*767173ceSCy Schubert */
103*767173ceSCy Schubert if (*haddr == '[') {
104*767173ceSCy Schubert char * endp = strchr(++haddr, ']');
105*767173ceSCy Schubert if (endp) {
106*767173ceSCy Schubert port = _num_or_dflt(_skip(_chop(endp), ':'),
107*767173ceSCy Schubert 0xFFFFu, port);
108*767173ceSCy Schubert afam = strchr(haddr, ':') ? AF_INET6 : AF_INET;
109c0b746e5SOllivier Robert }
1102b15cb3dSCy Schubert } else {
111*767173ceSCy Schubert char *col = strchr(haddr, ':');
112*767173ceSCy Schubert char *dot = strchr(haddr, '.');
113*767173ceSCy Schubert if (col == dot) {
114*767173ceSCy Schubert /* no dot, no colon: bad! */
115*767173ceSCy Schubert afam = AF_UNSPEC;
116*767173ceSCy Schubert } else if (!col) {
117*767173ceSCy Schubert /* no colon, only dot: IPv4! */
118*767173ceSCy Schubert afam = AF_INET;
119*767173ceSCy Schubert } else if (!dot || col < dot) {
120*767173ceSCy Schubert /* no dot or 1st colon before 1st dot: IPv6! */
121*767173ceSCy Schubert afam = AF_INET6;
122*767173ceSCy Schubert } else {
123*767173ceSCy Schubert /* 1st dot before 1st colon: must be IPv4 with port */
124*767173ceSCy Schubert afam = AF_INET;
125*767173ceSCy Schubert port = _num_or_dflt(_chop(col), 0xFFFFu, port);
1262b15cb3dSCy Schubert }
127*767173ceSCy Schubert }
128*767173ceSCy Schubert
129*767173ceSCy Schubert /* Since we don't know about additional members in the address
130*767173ceSCy Schubert * structures, we wipe the result buffer thoroughly:
131*767173ceSCy Schubert */
132*767173ceSCy Schubert memset(&netnum, 0, sizeof(netnum));
133*767173ceSCy Schubert
134*767173ceSCy Schubert /* For AF_INET6, evaluate and remove any scope suffix. Have
135*767173ceSCy Schubert * inet_pton() do the real work for AF_INET and AF_INET6, bail
136*767173ceSCy Schubert * out otherwise:
137*767173ceSCy Schubert */
138*767173ceSCy Schubert switch (afam) {
139*767173ceSCy Schubert case AF_INET:
140*767173ceSCy Schubert if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0)
1412d4e511cSCy Schubert return FALSE;
142*767173ceSCy Schubert netnum.sa4.sin_port = htons((unsigned short)port);
143*767173ceSCy Schubert break;
1442d4e511cSCy Schubert
145*767173ceSCy Schubert case AF_INET6:
146*767173ceSCy Schubert scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope);
147*767173ceSCy Schubert if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0)
1482d4e511cSCy Schubert return FALSE;
149*767173ceSCy Schubert netnum.sa6.sin6_port = htons((unsigned short)port);
150*767173ceSCy Schubert netnum.sa6.sin6_scope_id = scope;
151*767173ceSCy Schubert break;
1522d4e511cSCy Schubert
153*767173ceSCy Schubert case AF_UNSPEC:
154*767173ceSCy Schubert default:
155*767173ceSCy Schubert return FALSE;
156*767173ceSCy Schubert }
1572d4e511cSCy Schubert
158*767173ceSCy Schubert /* Collect the remaining pieces and feed the output, which was
159*767173ceSCy Schubert * not touched so far:
160*767173ceSCy Schubert */
161*767173ceSCy Schubert netnum.sa.sa_family = afam;
162*767173ceSCy Schubert memcpy(net, &netnum, sizeof(netnum));
1632d4e511cSCy Schubert return TRUE;
164c0b746e5SOllivier Robert }
165