]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/libntp/decodenetnum.c
Fix multiple denial of service in ntpd.
[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 #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 }