1 /* Copyright (c) 2006-2010, UNINETT AS.
2 * Copyright (c) 2010, UNINETT AS, NORDUnet A/S.
3 * Copyright (c) 2010-2012, NORDUnet A/S. */
4 /* See LICENSE for licensing information. */
6 /* Code contributions from:
8 * Simon Leinen <simon.leinen@switch.ch>
15 #include <netinet/in.h>
21 void freehostport(struct hostportres *hp) {
26 freeaddrinfo(hp->addrinfo);
31 static int parsehostport(struct hostportres *hp, char *hostport, char *default_port) {
36 hp->port = default_port ? stringcopy(default_port, 0) : NULL;
40 /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
44 for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
46 debug(DBG_ERR, "no ] matching initial [");
52 for (; *p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
55 debug(DBG_ERR, "missing host/address");
59 hp->host = stringcopy(field, p - field);
62 if (*p && *p != ':' && *p != '/' && *p != ' ' && *p != '\t' && *p != '\n') {
63 debug(DBG_ERR, "unexpected character after ]");
68 /* port number or service name is specified */;
70 for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
72 debug(DBG_ERR, "syntax error, : but no following port");
75 hp->port = stringcopy(field, p - field);
77 hp->port = default_port ? stringcopy(default_port, 0) : NULL;
81 struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
82 struct hostportres *hp;
86 hp = malloc(sizeof(struct hostportres));
88 debug(DBG_ERR, "resolve_newhostport: malloc failed");
91 memset(hp, 0, sizeof(struct hostportres));
93 if (!parsehostport(hp, hostport, default_port))
96 if (hp->host && !strcmp(hp->host, "*")) {
101 slash = hostport ? strchr(hostport, '/') : NULL;
104 debug(DBG_WARN, "newhostport: prefix not allowed here", hp->host);
109 debug(DBG_WARN, "newhostport: prefix length must be specified after the / in %s", hp->host);
113 if (*s < '0' || *s > '9') {
114 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
117 plen = atoi(slash + 1);
118 if (plen < 0 || plen > 128) {
119 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
122 hp->prefixlen = plen;
132 int resolvehostport(struct hostportres *hp, int af, int socktype, uint8_t passive) {
133 struct addrinfo hints, *res;
135 memset(&hints, 0, sizeof(hints));
136 hints.ai_socktype = socktype;
137 hints.ai_family = af;
139 hints.ai_flags = AI_PASSIVE;
141 if (!hp->host && !hp->port) {
142 /* getaddrinfo() doesn't like host and port to be NULL */
143 if (getaddrinfo(hp->host, "1812" /* can be anything */, &hints, &hp->addrinfo)) {
144 debug(DBG_WARN, "resolvehostport: can't resolve (null) port (null)");
147 for (res = hp->addrinfo; res; res = res->ai_next)
148 port_set(res->ai_addr, 0);
150 if (hp->prefixlen != 255)
151 hints.ai_flags |= AI_NUMERICHOST;
152 if (getaddrinfo(hp->host, hp->port, &hints, &hp->addrinfo)) {
153 debug(DBG_WARN, "resolvehostport: can't resolve %s port %s", hp->host ? hp->host : "(null)", hp->port ? hp->port : "(null)");
156 if (hp->prefixlen != 255) {
157 switch (hp->addrinfo->ai_family) {
159 if (hp->prefixlen > 32) {
160 debug(DBG_WARN, "resolvehostport: prefix length must be <= 32 in %s", hp->host);
167 debug(DBG_WARN, "resolvehostport: prefix must be IPv4 or IPv6 in %s", hp->host);
172 debug(DBG_DBG, "%s: %s -> %s", __func__, hp->host, addr2string(hp->addrinfo->ai_addr));
177 freeaddrinfo(hp->addrinfo);
181 int addhostport(struct list **hostports, char **hostport, char *portdefault, uint8_t prefixok) {
182 struct hostportres *hp;
186 *hostports = list_create();
188 debug(DBG_ERR, "addhostport: malloc failed");
193 for (i = 0; hostport[i]; i++) {
194 hp = newhostport(hostport[i], portdefault, prefixok);
197 if (!list_push(*hostports, hp)) {
199 debug(DBG_ERR, "addhostport: malloc failed");
206 void freehostports(struct list *hostports) {
207 struct hostportres *hp;
209 while ((hp = (struct hostportres *)list_shift(hostports)))
211 list_destroy(hostports);
214 int resolvehostports(struct list *hostports, int af, int socktype) {
215 struct list_node *entry;
216 struct hostportres *hp;
218 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
219 hp = (struct hostportres *)entry->data;
220 if (!hp->addrinfo && !resolvehostport(hp, af, socktype, 0))
226 struct addrinfo *resolvepassiveaddrinfo(char *hostport, int af, char *default_port, int socktype) {
227 struct addrinfo *ai = NULL;
228 struct hostportres *hp = newhostport(hostport, default_port, 0);
229 if (hp && resolvehostport(hp, af, socktype, 1)) {
237 /* returns 1 if the len first bits are equal, else 0 */
238 static int prefixmatch(void *a1, void *a2, uint8_t len) {
239 static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
240 uint8_t r, l = len / 8;
241 if (l && memcmp(a1, a2, l))
246 return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
249 int addressmatches(struct list *hostports, struct sockaddr *addr, uint8_t checkport) {
250 struct sockaddr_in6 *sa6 = NULL;
251 struct in_addr *a4 = NULL;
252 struct addrinfo *res;
253 struct list_node *entry;
254 struct hostportres *hp = NULL;
256 if (addr->sa_family == AF_INET6) {
257 sa6 = (struct sockaddr_in6 *)addr;
258 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
259 a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
263 a4 = &((struct sockaddr_in *)addr)->sin_addr;
265 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
266 hp = (struct hostportres *)entry->data;
267 for (res = hp->addrinfo; res; res = res->ai_next)
268 if (hp->prefixlen == 255) {
269 if ((a4 && res->ai_family == AF_INET &&
270 !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4) &&
271 (!checkport || ((struct sockaddr_in *)res->ai_addr)->sin_port ==
272 ((struct sockaddr_in *)addr)->sin_port)) ||
273 (sa6 && res->ai_family == AF_INET6 &&
274 !memcmp(&sa6->sin6_addr,
275 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16) &&
276 (!checkport || ((struct sockaddr_in6 *)res->ai_addr)->sin6_port ==
277 ((struct sockaddr_in6 *)addr)->sin6_port)))
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)))
290 int connecttcphostlist(struct list *hostports, struct addrinfo *src) {
292 struct list_node *entry;
293 struct hostportres *hp = NULL;
295 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
296 hp = (struct hostportres *)entry->data;
297 debug(DBG_WARN, "connecttcphostlist: trying to open TCP connection to %s port %s", hp->host, hp->port);
298 if ((s = connecttcp(hp->addrinfo, src, list_count(hostports) > 1 ? 5 : 30)) >= 0) {
299 debug(DBG_WARN, "connecttcphostlist: TCP connection to %s port %s up", hp->host, hp->port);
303 debug(DBG_ERR, "connecttcphostlist: failed");
307 /* Local Variables: */
308 /* c-file-style: "stroustrup" */