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