1 /* Copyright (c) 2007-2009, UNINETT AS
2 * Copyright (c) 2012,2016 NORDUnet A/S */
3 /* See LICENSE for licensing information. */
4
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <netdb.h>
9 #include <netinet/in.h>
10 #include "debug.h"
11 #include "util.h"
12 #include "list.h"
13 #include "hostport.h"
14
freehostport(struct hostportres * hp)15 void freehostport(struct hostportres *hp) {
16 if (hp) {
17 free(hp->host);
18 free(hp->port);
19 if (hp->addrinfo)
20 freeaddrinfo(hp->addrinfo);
21 free(hp);
22 }
23 }
24
parsehostport(struct hostportres * hp,char * hostport,char * default_port)25 static int parsehostport(struct hostportres *hp, char *hostport, char *default_port) {
26 char *p, *field;
27 int ipv6 = 0;
28
29 if (!hostport) {
30 hp->port = default_port ? stringcopy(default_port, 0) : NULL;
31 return 1;
32 }
33 p = hostport;
34 /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
35 if (*p == '[') {
36 p++;
37 field = p;
38 for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
39 if (*p != ']') {
40 debug(DBG_ERR, "no ] matching initial [");
41 return 0;
42 }
43 ipv6 = 1;
44 } else {
45 field = p;
46 for (; *p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
47 }
48 if (field == p) {
49 debug(DBG_ERR, "missing host/address");
50 return 0;
51 }
52
53 hp->host = stringcopy(field, p - field);
54 if (ipv6) {
55 p++;
56 if (*p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n') {
57 debug(DBG_ERR, "unexpected character after ]");
58 return 0;
59 }
60 }
61 if (*p == ':') {
62 /* port number or service name is specified */;
63 field = ++p;
64 for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
65 if (field == p) {
66 debug(DBG_ERR, "syntax error, : but no following port");
67 return 0;
68 }
69 hp->port = stringcopy(field, p - field);
70 } else
71 hp->port = default_port ? stringcopy(default_port, 0) : NULL;
72 return 1;
73 }
74
newhostport(char * hostport,char * default_port,uint8_t prefixok)75 struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
76 struct hostportres *hp;
77 char *slash, *s;
78 int plen;
79
80 hp = malloc(sizeof(struct hostportres));
81 if (!hp) {
82 debug(DBG_ERR, "resolve_newhostport: malloc failed");
83 goto errexit;
84 }
85 memset(hp, 0, sizeof(struct hostportres));
86
87 if (!parsehostport(hp, hostport, default_port))
88 goto errexit;
89
90 if (hp->host && !strcmp(hp->host, "*")) {
91 free(hp->host);
92 hp->host = NULL;
93 }
94
95 slash = hostport ? strchr(hostport, '/') : NULL;
96 if (slash) {
97 if (!prefixok) {
98 debug(DBG_WARN, "newhostport: prefix not allowed here", hp->host);
99 goto errexit;
100 }
101 s = slash + 1;
102 if (!*s) {
103 debug(DBG_WARN, "newhostport: prefix length must be specified after the / in %s", hp->host);
104 goto errexit;
105 }
106 for (; *s; s++)
107 if (*s < '0' || *s > '9') {
108 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
109 goto errexit;
110 }
111 plen = atoi(slash + 1);
112 if (plen < 0 || plen > 128) {
113 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
114 goto errexit;
115 }
116 hp->prefixlen = plen;
117 } else
118 hp->prefixlen = 255;
119 return hp;
120
121 errexit:
122 freehostport(hp);
123 return NULL;
124 }
125
resolvehostport(struct hostportres * hp,int af,int socktype,uint8_t passive)126 int resolvehostport(struct hostportres *hp, int af, int socktype, uint8_t passive) {
127 struct addrinfo hints, *res;
128 char tmp[INET6_ADDRSTRLEN];
129
130 memset(&hints, 0, sizeof(hints));
131 hints.ai_socktype = socktype;
132 hints.ai_family = af;
133 if (passive)
134 hints.ai_flags = AI_PASSIVE;
135
136 if (!hp->host && !hp->port) {
137 /* getaddrinfo() doesn't like host and port to be NULL */
138 if (getaddrinfo(hp->host, "1812" /* can be anything */, &hints, &hp->addrinfo)) {
139 debug(DBG_WARN, "resolvehostport: can't resolve (null) port (null)");
140 goto errexit;
141 }
142 for (res = hp->addrinfo; res; res = res->ai_next)
143 port_set(res->ai_addr, 0);
144 } else {
145 if (hp->prefixlen != 255)
146 hints.ai_flags |= AI_NUMERICHOST;
147 if (getaddrinfo(hp->host, hp->port, &hints, &hp->addrinfo)) {
148 debug(DBG_WARN, "resolvehostport: can't resolve %s port %s", hp->host ? hp->host : "(null)", hp->port ? hp->port : "(null)");
149 goto errexit;
150 }
151 if (hp->prefixlen != 255) {
152 switch (hp->addrinfo->ai_family) {
153 case AF_INET:
154 if (hp->prefixlen > 32) {
155 debug(DBG_WARN, "resolvehostport: prefix length must be <= 32 in %s", hp->host);
156 goto errexit;
157 }
158 break;
159 case AF_INET6:
160 break;
161 default:
162 debug(DBG_WARN, "resolvehostport: prefix must be IPv4 or IPv6 in %s", hp->host);
163 goto errexit;
164 }
165 }
166 }
167 debug(DBG_DBG, "%s: %s -> %s", __func__,
168 (hp->host ? hp->host : "(src info not available)"),
169 ((hp->addrinfo && hp->addrinfo->ai_addr) ?
170 addr2string(hp->addrinfo->ai_addr, tmp, sizeof(tmp)) : "(dst info not available)"));
171 return 1;
172
173 errexit:
174 if (hp->addrinfo)
175 freeaddrinfo(hp->addrinfo);
176 return 0;
177 }
178
addhostport(struct list ** hostports,char ** hostport,char * portdefault,uint8_t prefixok)179 int addhostport(struct list **hostports, char **hostport, char *portdefault, uint8_t prefixok) {
180 struct hostportres *hp;
181 int i;
182
183 if (!*hostports) {
184 *hostports = list_create();
185 if (!*hostports) {
186 debug(DBG_ERR, "addhostport: malloc failed");
187 return 0;
188 }
189 }
190
191 for (i = 0; hostport[i]; i++) {
192 hp = newhostport(hostport[i], portdefault, prefixok);
193 if (!hp)
194 return 0;
195 if (!list_push(*hostports, hp)) {
196 freehostport(hp);
197 debug(DBG_ERR, "addhostport: malloc failed");
198 return 0;
199 }
200 }
201 return 1;
202 }
203
freehostports(struct list * hostports)204 void freehostports(struct list *hostports) {
205 struct hostportres *hp;
206
207 while ((hp = (struct hostportres *)list_shift(hostports)))
208 freehostport(hp);
209 list_destroy(hostports);
210 }
211
resolvehostports(struct list * hostports,int af,int socktype)212 int resolvehostports(struct list *hostports, int af, int socktype) {
213 struct list_node *entry;
214 struct hostportres *hp;
215
216 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
217 hp = (struct hostportres *)entry->data;
218 if (!hp->addrinfo && !resolvehostport(hp, af, socktype, 0))
219 return 0;
220 }
221 return 1;
222 }
223
resolvepassiveaddrinfo(char * hostport,int af,char * default_port,int socktype)224 struct addrinfo *resolvepassiveaddrinfo(char *hostport, int af, char *default_port, int socktype) {
225 struct addrinfo *ai = NULL;
226 struct hostportres *hp = newhostport(hostport, default_port, 0);
227 if (hp && resolvehostport(hp, af, socktype, 1)) {
228 ai = hp->addrinfo;
229 hp->addrinfo = NULL;
230 }
231 freehostport(hp);
232 return ai;
233 }
234
235 /* returns 1 if the len first bits are equal, else 0 */
prefixmatch(void * a1,void * a2,uint8_t len)236 static int prefixmatch(void *a1, void *a2, uint8_t len) {
237 static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
238 uint8_t r, l = len / 8;
239 if (l && memcmp(a1, a2, l))
240 return 0;
241 r = len % 8;
242 if (!r)
243 return 1;
244 return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
245 }
246
_internal_addressmatches(struct list * hostports,struct sockaddr * addr,uint8_t prefixlen,uint8_t checkport)247 int _internal_addressmatches(struct list *hostports, struct sockaddr *addr, uint8_t prefixlen, uint8_t checkport) {
248 struct sockaddr_in6 *sa6 = NULL;
249 struct in_addr *a4 = NULL;
250 struct addrinfo *res;
251 struct list_node *entry;
252 struct hostportres *hp = NULL;
253
254 if (addr->sa_family == AF_INET6) {
255 sa6 = (struct sockaddr_in6 *)addr;
256 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
257 a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
258 sa6 = NULL;
259 }
260 } else
261 a4 = &((struct sockaddr_in *)addr)->sin_addr;
262
263 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
264 hp = (struct hostportres *)entry->data;
265 for (res = hp->addrinfo; res; res = res->ai_next) {
266 if (hp->prefixlen >= (res->ai_family == AF_INET ? 32 : 128) && prefixlen >= (a4 ? 32 : 128)) {
267 if ((a4 && res->ai_family == AF_INET &&
268 !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4) &&
269 (!checkport || ((struct sockaddr_in *)res->ai_addr)->sin_port ==
270 ((struct sockaddr_in *)addr)->sin_port)) ||
271
272 (sa6 && res->ai_family == AF_INET6 &&
273 !memcmp(&sa6->sin6_addr,
274 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16) &&
275 (!checkport || ((struct sockaddr_in6 *)res->ai_addr)->sin6_port ==
276 ((struct sockaddr_in6 *)addr)->sin6_port)))
277
278 return 1;
279 } else if (hp->prefixlen <= prefixlen) {
280 if ((a4 && res->ai_family == AF_INET &&
281 prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, hp->prefixlen)) ||
282 (sa6 && res->ai_family == AF_INET6 &&
283 prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, hp->prefixlen)))
284
285 return 1;
286 }
287 }
288 }
289 return 0;
290 }
291
hostportmatches(struct list * hostports,struct list * matchhostports,uint8_t checkport)292 int hostportmatches(struct list *hostports, struct list *matchhostports, uint8_t checkport) {
293 struct list_node *entry;
294 struct hostportres *match;
295 struct addrinfo *res;
296
297 for (entry = list_first(matchhostports); entry; entry = list_next(entry)) {
298 match = (struct hostportres *)entry->data;
299
300 for (res = match->addrinfo; res; res = res->ai_next) {
301 if (_internal_addressmatches(hostports, res->ai_addr, match->prefixlen, checkport))
302 return 1;
303 }
304 }
305 return 0;
306 }
307
addressmatches(struct list * hostports,struct sockaddr * addr,uint8_t checkport)308 int addressmatches(struct list *hostports, struct sockaddr *addr, uint8_t checkport) {
309 return _internal_addressmatches(hostports, addr, 255, checkport);
310 }
311
connecttcphostlist(struct list * hostports,struct addrinfo * src)312 int connecttcphostlist(struct list *hostports, struct addrinfo *src) {
313 int s;
314 struct list_node *entry;
315 struct hostportres *hp = NULL;
316
317 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
318 hp = (struct hostportres *)entry->data;
319 debug(DBG_WARN, "connecttcphostlist: trying to open TCP connection to %s port %s", hp->host, hp->port);
320 if ((s = connecttcp(hp->addrinfo, src, list_count(hostports) > 1 ? 5 : 30)) >= 0) {
321 debug(DBG_WARN, "connecttcphostlist: TCP connection to %s port %s up", hp->host, hp->port);
322 return s;
323 }
324 }
325 debug(DBG_ERR, "connecttcphostlist: failed");
326 return -1;
327 }
328
329 /* Local Variables: */
330 /* c-file-style: "stroustrup" */
331 /* End: */
332