6d84918995c4802cabca9576128cb86fdea217d5
[trust_router.git] / common / tr_inet_util.c
1 /*
2  * Copyright (c) 2018, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <tr_name_internal.h>
36 #include <arpa/inet.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <talloc.h>
40
41 #include <tr_inet_util.h>
42
43 /**
44  * Determine whether a string is a valid address of a given family
45  *
46  * @param s string to check
47  * @param af address family (probably AF_INET or AF_INET6)
48  * @return 1 if string is a valid address in the given family, 0 if not, -1 on error (errno set)
49  */
50 static int is_valid_address(int af, const char *s)
51 {
52   unsigned char buf[sizeof(struct in6_addr)];
53
54   if (s == NULL)
55     return 0;
56   return inet_pton(af, s, buf);
57 }
58
59 /**
60  * Determine whether a string is a valid IPv6 address reference
61  *
62  * I.e., an IPv6 address in brackets
63  *
64  * @param s string to validate
65  * @return 1 if a valid reference, 0 otherwise
66  */
67 static int tr_valid_ipv6_reference(const char *s)
68 {
69   char *cpy;
70   size_t len;
71   int valid_ipv6;
72
73   /* check that it starts with an open bracket */
74   if (*s != '[')
75     return 0;
76
77   /* check that it ends with a close bracket */
78   len = strlen(s);
79   if (*(s+len-1) != ']')
80     return 0;
81
82   /* make a null-terminated copy of the string omitting the brackets */
83   cpy = talloc_strndup(NULL, s+1, len-2);
84   if (cpy == NULL)
85     return 0; /* an error occurred - fail safe */
86
87   valid_ipv6 = is_valid_address(AF_INET6, cpy);
88   talloc_free(cpy);
89
90   return valid_ipv6;
91 }
92
93 /**
94  * Validate a host string
95  *
96  * The intention is to reject strings that may appear to contain a ':port' spec.
97  * Takes a permissive view of valid: a hostname is valid if either it is a
98  * bracketed IPv6 address reference ([address]) or has no brackets or colons.
99  * This accepts all valid DNS names and IPv4 addresses, as well as many invalid
100  * hostnames. This is ok for accepting a hostname that will later be resolved
101  * because invalid names will fail to resolve. It should *not* be used to ensure
102  * a hostname is compliant with RFC!
103  *
104  * Ignores a trailing colon followed by decimal digits.
105  *
106  * @param s string to validate
107  * @return 1 if a valid host specification, 0 otherwise
108  */
109 static int tr_valid_host(const char *s)
110 {
111   if (strchr(s, '[') || strchr(s, ']') || strchr(s, ':'))
112     return tr_valid_ipv6_reference(s);
113
114   return 1;
115 }
116
117 /**
118  * Check that all characters are decimal digits
119  *
120  * @param s
121  * @return 1 if all digits, 0 otherwise
122  */
123 static int tr_str_all_digits(const char *s)
124 {
125   if (s == NULL)
126     return 0;
127
128   for( ; *s; s++) {
129     if ( (*s < '0') || (*s > '9'))
130       return 0;
131   }
132
133   return 1;
134 }
135
136 /**
137  * Validate and parse a hostname or hostname/port
138  *
139  * If port_out is not null, accepts a port as well. This is
140  * stored in *port_out. If no port is given, a 0 is stored.
141  * If an invalid port is given, -1 is stored.
142  *
143  * If the hostname is invalid, null is returned and no value
144  * is written to *port_out.
145  *
146  * If port_out is null, null will be returned if the string
147  * contains a port.
148  *
149  * The return value must be freed with talloc_free unless
150  * it is null.
151  *
152  * @param mem_ctx talloc context for hostname result
153  * @param s string to parse
154  * @param port_out pointer to an allocated integer, or NULL
155  * @return pointer to the hostname or null on error
156  */
157 char *tr_parse_host(TALLOC_CTX *mem_ctx, const char *s, int *port_out)
158 {
159   const char *colon;
160   char *hostname;
161   long int port;
162
163   if (s == NULL)
164     return NULL;
165
166   /* If we are accepting a port, find the last colon. */
167   if (port_out == NULL)
168     colon = NULL;
169   else
170     colon = strrchr(s, ':');
171
172   /* Get a copy of the hostname portion, which may be the entire string. */
173   if (colon == NULL)
174     hostname = talloc_strdup(NULL, s);
175   else
176     hostname = talloc_strndup(NULL, s, colon-s);
177
178   if (hostname == NULL)
179     return NULL; /* failed to dup the hostname */
180
181   /* Check that the hostname is valid; if not, return null and ignore the port. */
182   if (! tr_valid_host(hostname)) {
183     talloc_free(hostname);
184     return NULL;
185   }
186
187   /* If we are accepting a port, parse and validate it. */
188   if (port_out != NULL) {
189     if (colon == NULL) {
190       *port_out = 0;
191     } else {
192       port = strtol(colon+1, NULL, 10);
193       if (tr_str_all_digits(colon+1) && (port > 0) && (port <= 65535))
194         *port_out = (int) port;
195       else
196         *port_out = -1;
197     }
198   }
199
200   return hostname;
201 }
202
203 TR_NAME *tr_hostname_and_port_to_name(TR_NAME *hn, int port)
204 {
205   TR_NAME *retval = NULL;
206   char *s = NULL;
207   char *hn_s = tr_name_strdup(hn);
208
209   if (!hn_s)
210     return NULL;
211
212   s = talloc_asprintf(NULL, "%s:%d", hn_s, port);
213   free(hn_s);
214
215   if (s) {
216     retval = tr_new_name(s);
217     talloc_free(s);
218   }
219
220   return retval;
221 }