2 * Copyright (c) 2018, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
35 #include <tr_name_internal.h>
36 #include <arpa/inet.h>
41 #include <tr_inet_util.h>
45 * Determine whether a string is a valid address of a given family
47 * @param s string to check
48 * @param af address family (probably AF_INET or AF_INET6)
49 * @return 1 if string is a valid address in the given family, 0 if not, -1 on error (errno set)
51 static int is_valid_address(int af, const char *s)
53 unsigned char buf[sizeof(struct in6_addr)];
57 return inet_pton(af, s, buf);
61 * Determine whether a string is a valid IPv6 address reference
63 * I.e., an IPv6 address in brackets
65 * @param s string to validate
66 * @return 1 if a valid reference, 0 otherwise
68 static int tr_valid_ipv6_reference(const char *s)
74 /* check that it starts with an open bracket */
78 /* check that it ends with a close bracket */
80 if (*(s+len-1) != ']')
83 /* make a null-terminated copy of the string omitting the brackets */
84 cpy = talloc_strndup(NULL, s+1, len-2);
86 return 0; /* an error occurred - fail safe */
88 valid_ipv6 = is_valid_address(AF_INET6, cpy);
95 * Validate a host string
97 * The intention is to reject strings that may appear to contain a ':port' spec.
98 * Takes a permissive view of valid: a hostname is valid if either it is a
99 * bracketed IPv6 address reference ([address]) or has no brackets or colons.
100 * This accepts all valid DNS names and IPv4 addresses, as well as many invalid
101 * hostnames. This is ok for accepting a hostname that will later be resolved
102 * because invalid names will fail to resolve. It should *not* be used to ensure
103 * a hostname is compliant with RFC!
105 * Ignores a trailing colon followed by decimal digits.
107 * @param s string to validate
108 * @return 1 if a valid host specification, 0 otherwise
110 static int tr_valid_host(const char *s)
112 if (strchr(s, '[') || strchr(s, ']') || strchr(s, ':'))
113 return tr_valid_ipv6_reference(s);
119 * Check that all characters are decimal digits
122 * @return 1 if all digits, 0 otherwise
124 static int tr_str_all_digits(const char *s)
130 if ( (*s < '0') || (*s > '9'))
138 * Validate and parse a hostname or hostname/port
140 * If port_out is not null, accepts a port as well. This is
141 * stored in *port_out. If no port is given, a 0 is stored.
142 * If an invalid port is given, -1 is stored.
144 * If the hostname is invalid, null is returned and no value
145 * is written to *port_out.
147 * If port_out is null, null will be returned if the string
150 * The return value must be freed with talloc_free unless
153 * @param mem_ctx talloc context for hostname result
154 * @param s string to parse
155 * @param port_out pointer to an allocated integer, or NULL
156 * @return pointer to the hostname or null on error
158 char *tr_parse_host(TALLOC_CTX *mem_ctx, const char *s, int *port_out)
167 /* If we are accepting a port, find the last colon. */
168 if (port_out == NULL)
171 colon = strrchr(s, ':');
173 /* Get a copy of the hostname portion, which may be the entire string. */
175 hostname = talloc_strdup(NULL, s);
177 hostname = talloc_strndup(NULL, s, colon-s);
179 if (hostname == NULL)
180 return NULL; /* failed to dup the hostname */
182 /* Check that the hostname is valid; if not, return null and ignore the port. */
183 if (! tr_valid_host(hostname)) {
184 talloc_free(hostname);
188 /* If we are accepting a port, parse and validate it. */
189 if (port_out != NULL) {
193 port = tr_parse_port(colon+1);
194 if ((port > 0) && tr_str_all_digits(colon+1))
204 TR_NAME *tr_hostname_and_port_to_name(TR_NAME *hn, int port)
206 TR_NAME *retval = NULL;
208 char *hn_s = tr_name_strdup(hn);
213 s = talloc_asprintf(NULL, "%s:%d", hn_s, port);
217 retval = tr_new_name(s);
225 * Parse a string containing a port
227 * Returns the port number, which is always in the range 1-65535.
228 * On error, returns < 0. The absolute value is an error code from errno.h
231 * @return port number or < 0 on error
233 int tr_parse_port(const char *s)
238 errno = 0; /* strtol sets this, make sure it's zero to avoid false positives */
239 port = strtol(s, &end, 10);
248 if ((port <= 0) || (port > 65535)) {