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