2 * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
17 static void freehostport(struct hostportres *hp) {
22 freeaddrinfo(hp->addrinfo);
27 static int parsehostport(struct hostportres *hp, char *hostport, char *default_port) {
32 /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
36 for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
38 debug(DBG_ERR, "no ] matching initial [");
44 for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
47 debug(DBG_ERR, "missing host/address");
51 hp->host = stringcopy(field, p - field);
54 if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n') {
55 debug(DBG_ERR, "unexpected character after ]");
60 /* port number or service name is specified */;
62 for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
64 debug(DBG_ERR, "syntax error, : but no following port");
67 hp->port = stringcopy(field, p - field);
69 hp->port = default_port ? stringcopy(default_port, 0) : NULL;
73 static struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
74 struct hostportres *hp;
78 hp = malloc(sizeof(struct hostportres));
80 debug(DBG_ERR, "resolve_newhostport: malloc failed");
83 memset(hp, 0, sizeof(struct hostportres));
85 if (!parsehostport(hp, hostport, default_port))
88 if (!strcmp(hp->host, "*")) {
93 slash = hp->host ? strchr(hp->host, '/') : NULL;
96 debug(DBG_WARN, "newhostport: prefix not allowed here", hp->host);
101 debug(DBG_WARN, "newhostport: prefix length must be specified after the / in %s", hp->host);
105 if (*s < '0' || *s > '9') {
106 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
109 plen = atoi(slash + 1);
110 if (plen < 0 || plen > 128) {
111 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
114 hp->prefixlen = plen;
125 static int resolvehostport(struct hostportres *hp, int socktype, uint8_t passive) {
126 struct addrinfo hints, *res;
128 memset(&hints, 0, sizeof(hints));
129 hints.ai_socktype = socktype;
130 hints.ai_family = AF_UNSPEC;
132 hints.ai_flags = AI_PASSIVE;
134 if (!hp->host && !hp->port) {
135 /* getaddrinfo() doesn't like host and port to be NULL */
136 if (getaddrinfo(hp->host, "1812" /* can be anything */, &hints, &hp->addrinfo)) {
137 debug(DBG_WARN, "resolvehostport: can't resolve (null) port (null)");
140 for (res = hp->addrinfo; res; res = res->ai_next)
141 port_set(res->ai_addr, 0);
143 if (hp->prefixlen != 255)
144 hints.ai_flags |= AI_NUMERICHOST;
145 if (getaddrinfo(hp->host, hp->port, &hints, &hp->addrinfo)) {
146 debug(DBG_WARN, "resolvehostport: can't resolve %s port %s", hp->host ? hp->host : "(null)", hp->port ? hp->port : "(null)");
149 if (hp->prefixlen != 255) {
150 switch (hp->addrinfo->ai_family) {
152 if (hp->prefixlen > 32) {
153 debug(DBG_WARN, "resolvehostport: prefix length must be <= 32 in %s", hp->host);
160 debug(DBG_WARN, "resolvehostport: prefix must be IPv4 or IPv6 in %s", hp->host);
169 freeaddrinfo(hp->addrinfo);
173 int addhostport(struct list **hostports, char *hostport, char *portdefault, uint8_t prefixok) {
174 struct hostportres *hp;
176 hp = newhostport(hostport, portdefault, prefixok);
180 *hostports = list_create();
181 if (!*hostports || !list_push(*hostports, hp)) {
183 debug(DBG_ERR, "addhostport: malloc failed");
189 void freehostports(struct list *hostports) {
190 struct hostportres *hp;
192 while ((hp = (struct hostportres *)list_shift(hostports)))
194 list_destroy(hostports);
197 int resolvehostports(struct list *hostports, int socktype) {
198 struct list_node *entry;
199 struct hostportres *hp;
201 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
202 hp = (struct hostportres *)entry->data;
203 if (!hp->addrinfo && !resolvehostport(hp, socktype, 0))
209 struct addrinfo *resolvepassiveaddrinfo(char *hostport, char *default_port, int socktype) {
210 struct addrinfo *ai = NULL;
211 struct hostportres *hp = newhostport(hostport, default_port, 0);
212 if (hp && resolvehostport(hp, socktype, 1)) {
220 /* returns 1 if the len first bits are equal, else 0 */
221 static int prefixmatch(void *a1, void *a2, uint8_t len) {
222 static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
223 uint8_t r, l = len / 8;
224 if (l && memcmp(a1, a2, l))
229 return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
232 int addressmatches(struct list *hostports, struct sockaddr *addr) {
233 struct sockaddr_in6 *sa6 = NULL;
234 struct in_addr *a4 = NULL;
235 struct addrinfo *res;
236 struct list_node *entry;
237 struct hostportres *hp = NULL;
239 if (addr->sa_family == AF_INET6) {
240 sa6 = (struct sockaddr_in6 *)addr;
241 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
242 a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
246 a4 = &((struct sockaddr_in *)addr)->sin_addr;
248 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
249 hp = (struct hostportres *)entry->data;
250 for (res = hp->addrinfo; res; res = res->ai_next)
251 if (hp->prefixlen == 255) {
252 if ((a4 && res->ai_family == AF_INET &&
253 !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
254 (sa6 && res->ai_family == AF_INET6 &&
255 !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
258 if ((a4 && res->ai_family == AF_INET &&
259 prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, hp->prefixlen)) ||
260 (sa6 && res->ai_family == AF_INET6 &&
261 prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, hp->prefixlen)))
268 int connecttcphostlist(struct list *hostports, struct addrinfo *src) {
270 struct list_node *entry;
271 struct hostportres *hp = NULL;
273 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
274 hp = (struct hostportres *)entry->data;
275 debug(DBG_WARN, "connecttcphostlist: trying to open TCP connection to %s port %s", hp->host, hp->port);
276 if ((s = connecttcp(hp->addrinfo, src)) >= 0) {
277 debug(DBG_WARN, "connecttcphostlist: TCP connection to %s port %s up", hp->host, hp->port);
281 debug(DBG_ERR, "connecttcphostlist: failed");