rewriting code for hosts,ports,resolving,currently does not build
[libradsec.git] / hostport.c
1 /*
2  * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
3  *
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.
7  */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <netdb.h>
12 #include "debug.h"
13 #include "util.h"
14 #include "list.h"
15 #include "hostport.h"
16
17 static void freehostport(struct hostportres *hp) {
18     if (hp) {
19         free(hp->host);
20         free(hp->port);
21         if (hp->addrinfo)
22             freeaddrinfo(hp->addrinfo);
23         free(hp);
24     }
25 }
26
27 static int parsehostport(struct hostportres *hp, char *hostport, char *default_port) {
28     char *p, *field;
29     int ipv6 = 0;
30
31     p = hostport;
32     /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
33     if (*p == '[') {
34         p++;
35         field = p;
36         for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
37         if (*p != ']') {
38             debug(DBG_ERR, "no ] matching initial [");
39             return 0;
40         }
41         ipv6 = 1;
42     } else {
43         field = p;
44         for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
45     }
46     if (field == p) {
47         debug(DBG_ERR, "missing host/address");
48         return 0;
49     }
50
51     hp->host = stringcopy(field, p - field);
52     if (ipv6) {
53         p++;
54         if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n') {
55             debug(DBG_ERR, "unexpected character after ]");
56             return 0;
57         }
58     }
59     if (*p == ':') {
60             /* port number or service name is specified */;
61             field = ++p;
62             for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
63             if (field == p) {
64                 debug(DBG_ERR, "syntax error, : but no following port");
65                 return 0;
66             }
67             hp->port = stringcopy(field, p - field);
68     } else
69         hp->port = default_port ? stringcopy(default_port, 0) : NULL;
70     return 1;
71 }
72     
73 static struct hostportres *newhostport(char *hostport, char *default_port, uint8_t prefixok) {
74     struct hostportres *hp;
75     char *slash, *s;
76     int plen;
77     
78     hp = malloc(sizeof(struct hostportres));
79     if (!hp) {
80         debug(DBG_ERR, "resolve_newhostport: malloc failed");
81         goto errexit;
82     }
83     memset(hp, 0, sizeof(struct hostportres));
84
85     if (!parsehostport(hp, hostport, default_port))
86         goto errexit;
87
88     if (!strcmp(hp->host, "*")) {
89         free(hp->host);
90         hp->host = NULL;
91     }
92
93     slash = hp->host ? strchr(hp->host, '/') : NULL;
94     if (slash) {
95         if (!prefixok) {
96             debug(DBG_WARN, "newhostport: prefix not allowed here", hp->host);
97             goto errexit;
98         }
99         s = slash + 1;
100         if (!*s) {
101             debug(DBG_WARN, "newhostport: prefix length must be specified after the / in %s", hp->host);
102             goto errexit;
103         }
104         for (; *s; s++)
105             if (*s < '0' || *s > '9') {
106                 debug(DBG_WARN, "newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
107                 goto errexit;
108             }
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);
112             goto errexit;
113         }
114         hp->prefixlen = plen;
115         *slash = '\0';
116     } else
117         hp->prefixlen = 255;
118     return hp;
119
120  errexit:
121     freehostport(hp);
122     return NULL;
123 }
124
125 static int resolvehostport(struct hostportres *hp, int socktype, uint8_t passive) {
126     struct addrinfo hints, *res;
127
128     memset(&hints, 0, sizeof(hints));
129     hints.ai_socktype = socktype;
130     hints.ai_family = AF_UNSPEC;
131     if (passive)
132         hints.ai_flags = AI_PASSIVE;
133     
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)");
138             goto errexit;
139         }
140         for (res = hp->addrinfo; res; res = res->ai_next)
141             port_set(res->ai_addr, 0);
142     } else {
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)");
147             goto errexit;
148         }
149         if (hp->prefixlen != 255) {
150             switch (hp->addrinfo->ai_family) {
151             case AF_INET:
152                 if (hp->prefixlen > 32) {
153                     debug(DBG_WARN, "resolvehostport: prefix length must be <= 32 in %s", hp->host);
154                     goto errexit;
155                 }
156                 break;
157             case AF_INET6:
158                 break;
159             default:
160                 debug(DBG_WARN, "resolvehostport: prefix must be IPv4 or IPv6 in %s", hp->host);
161                 goto errexit;
162             }
163         }
164     }
165     return 1;
166
167  errexit:
168     if (hp->addrinfo)
169         freeaddrinfo(hp->addrinfo);
170     return 0;
171 }         
172
173 int addhostport(struct list **hostports, char *hostport, char *portdefault, uint8_t prefixok) {
174     struct hostportres *hp;
175
176     hp = newhostport(hostport, portdefault, prefixok);
177     if (!hp)
178         return 0;
179     if (!*hostports)
180         *hostports = list_create();
181     if (!*hostports || !list_push(*hostports, hp)) {
182         freehostport(hp);
183         debug(DBG_ERR, "addhostport: malloc failed");
184         return 0;
185     }
186     return 1;
187 }
188
189 void freehostports(struct list *hostports) {
190     struct hostportres *hp;
191
192     while ((hp = (struct hostportres *)list_shift(hostports)))
193         freehostport(hp);
194     list_destroy(hostports);
195 }
196
197 int resolvehostports(struct list *hostports, int socktype) {
198     struct list_node *entry;
199     struct hostportres *hp;
200     
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))
204             return 0;
205     }
206     return 1;
207 }
208
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)) {
213         ai = hp->addrinfo;
214         hp->addrinfo = NULL;
215     }
216     freehostport(hp);
217     return ai;
218 }
219
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))
225         return 0;
226     r = len % 8;
227     if (!r)
228         return 1;
229     return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
230 }
231
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;
238     
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];
243             sa6 = NULL;
244         }
245     } else
246         a4 = &((struct sockaddr_in *)addr)->sin_addr;
247
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)))
256                     return 1;
257             } else {
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)))
262                     return 1;
263             }
264     }
265     return 0;
266 }