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 <arpa/inet.h>
39 #include <tr_inet_util.h>
42 * Determine whether a string is a valid address of a given family
44 * @param s string to check
45 * @param af address family (probably AF_INET or AF_INET6)
46 * @return 1 if string is a valid address in the given family, 0 if not, -1 on error (errno set)
48 static int is_valid_address(int af, const char *s)
50 unsigned char buf[sizeof(struct in6_addr)];
54 return inet_pton(af, s, buf);
58 * Determine whether a string is a valid IPv4 address
59 * @param s string to validate
60 * @return 1 if a valid reference, 0 otherwise
62 int is_valid_ipv4_address(const char *s)
64 return is_valid_address(AF_INET, s);
68 * Determine whether a string is a valid IPv6 address reference
70 * I.e., an IPv6 address in brackets
72 * @param s string to validate
73 * @return 1 if a valid reference, 0 otherwise
75 int is_valid_ipv6_reference(const char *s)
81 /* check that it starts with an open bracket */
85 /* check that it ends with a close bracket */
87 if (*(s+len-1) != ']')
90 /* make a null-terminated copy of the string omitting the brackets */
91 cpy = strndup(s+1, len-2);
95 valid_ipv6 = is_valid_address(AF_INET6, cpy);
101 static int is_valid_dns_char(char c)
104 if ( ('0' <= c) && ('9' >= c))
108 if ( (('a' <= c) && ('z' >= c))
109 || (('A' <= c) && ('Z' >= c)) )
116 /* everything else illegal */
121 * Helper to validate a DNS label
123 * Checks whether the string starting at s[start] and ending at s[end-1]
124 * is a valid DNS label.
126 * Does not check the length of the label.
133 static int is_valid_dns_label(const char *s, size_t start, size_t end)
137 /* Check that there is at least one character.
138 * Be careful - size_t is unsigned */
142 /* must neither start nor end with '-' */
143 if ((s[start] == '-')
147 /* make sure all characters are valid */
148 for (ii=start; ii<end; ii++) {
149 if (! is_valid_dns_char(s[ii]))
157 * Determine whether a string is a valid DNS name
159 * Does not check the length of the name or of the indivdiual
162 * @param s string to validate
163 * @return 1 if a valid DNS name, 0 otherwise
165 int is_valid_dns_name(const char *s)
170 /* reject some trivial cases */
175 /* Walk along with the end counter until we encounter a '.'. When that
176 * happens, we have a complete DNS label. Validate that, then set the start
177 * counter to one character past the end pointer, which will either be the
178 * next character in the DNS name or the null terminator. Since we stop as
179 * soon as the end counter reaches a null character, this will never refer
180 * to an invalid address. */
181 for (label_start = 0, label_end = 0;
182 s[label_end] != '\0';
184 if (s[label_end] == '.') {
185 if (! is_valid_dns_label(s, label_start, label_end))
188 label_start = label_end+1;
192 if (s[label_start] == '\0')
193 return 1; /* we must have ended on a '.' */
195 /* There was one more label to validate */
196 return is_valid_dns_label(s, label_start, label_end);
200 * Validate a host string
203 * IPv4 address (dotted quad)
204 * IPv6 address in brackets (e.g., [::1])
205 * DNS hostname (labels made of alphanumerics or "-", separated by dots)
207 * @param s string to validate
208 * @return 1 if a valid host specification, 0 otherwise
210 int is_valid_host(const char *s)
212 if (is_valid_ipv4_address(s))
215 if (is_valid_ipv6_reference(s))
218 if (is_valid_dns_name(s))