6360cbdd6b0ff6deb531876e5b8e1fa325802d7f
[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 "resolve.h"
15
16 void resolve_freehostport(struct hostportres *hp) {
17     if (hp) {
18         free(hp->host);
19         free(hp->port);
20         if (hp->addrinfo)
21             freeaddrinfo(hp->addrinfo);
22         free(hp);
23     }
24 }
25
26 static int resolve_parsehostport(struct hostportres *hp, char *hostport, char *default_port) {
27     char *p, *field;
28     int ipv6 = 0;
29
30     p = hostport;
31     /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
32     if (*p == '[') {
33         p++;
34         field = p;
35         for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
36         if (*p != ']') {
37             debug(DBG_ERR, "no ] matching initial [");
38             return 0;
39         }
40         ipv6 = 1;
41     } else {
42         field = p;
43         for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
44     }
45     if (field == p) {
46         debug(DBG_ERR, "missing host/address");
47         return 0;
48     }
49
50     hp->host = stringcopy(field, p - field);
51     if (ipv6) {
52         p++;
53         if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n') {
54             debug(DBG_ERR, "unexpected character after ]");
55             return 0;
56         }
57     }
58     if (*p == ':') {
59             /* port number or service name is specified */;
60             field = ++p;
61             for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
62             if (field == p) {
63                 debug(DBG_ERR, "syntax error, : but no following port");
64                 return 0;
65             }
66             hp->port = stringcopy(field, p - field);
67     } else
68         hp->port = default_port ? stringcopy(default_port, 0) : NULL;
69     return 1;
70 }
71     
72 struct hostportres *resolve_newhostport(char *hostport, char *default_port, uint8_t prefixok) {
73     struct hostportres *hp;
74     char *slash, *s;
75     int plen;
76     
77     hp = malloc(sizeof(struct hostportres));
78     if (!hp) {
79         debug(DBG_ERR, "resolve_newhostport: malloc failed");
80         goto errexit;
81     }
82     memset(hp, 0, sizeof(struct hostportres));
83
84     if (!resolve_parsehostport(hp, hostport, default_port))
85         goto errexit;
86
87     if (!strcmp(hp->host, "*")) {
88         free(hp->host);
89         hp->host = NULL;
90     }
91
92     slash = hp->host ? strchr(hp->host, '/') : NULL;
93     if (slash) {
94         if (!prefixok) {
95             debug(DBG_WARN, "resolve_newhostport: prefix not allowed here", hp->host);
96             goto errexit;
97         }
98         s = slash + 1;
99         if (!*s) {
100             debug(DBG_WARN, "resolve_newhostport: prefix length must be specified after the / in %s", hp->host);
101             goto errexit;
102         }
103         for (; *s; s++)
104             if (*s < '0' || *s > '9') {
105                 debug(DBG_WARN, "resolve_newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
106                 goto errexit;
107             }
108         plen = atoi(slash + 1);
109         if (plen < 0 || plen > 128) {
110             debug(DBG_WARN, "resolve_newhostport: %s in %s is not a valid prefix length", slash + 1, hp->host);
111             goto errexit;
112         }
113         hp->prefixlen = plen;
114         *slash = '\0';
115     } else
116         hp->prefixlen = 255;
117     return hp;
118
119  errexit:
120     resolve_freehostport(hp);
121     return NULL;
122 }
123
124 static int resolve_resolve(struct hostportres *hp, int socktype, uint8_t passive) {
125     struct addrinfo hints, *res;
126
127     memset(&hints, 0, sizeof(hints));
128     hints.ai_socktype = socktype;
129     hints.ai_family = AF_UNSPEC;
130     if (passive)
131         hints.ai_flags = AI_PASSIVE;
132     
133     if (!hp->host && !hp->port) {
134         /* getaddrinfo() doesn't like host and port to be NULL */
135         if (getaddrinfo(hp->host, "1812" /* can be anything */, &hints, &hp->addrinfo)) {
136             debug(DBG_WARN, "resolve_resolve: can't resolve (null) port (null)");
137             goto errexit;
138         }
139         for (res = hp->addrinfo; res; res = res->ai_next)
140             port_set(res->ai_addr, 0);
141     } else {
142         if (hp->prefixlen != 255)
143             hints.ai_flags |= AI_NUMERICHOST;
144         if (getaddrinfo(hp->host, hp->port, &hints, &hp->addrinfo)) {
145             debug(DBG_WARN, "resolve_resolve: can't resolve %s port %s", hp->host ? hp->host : "(null)", hp->port ? hp->port : "(null)");
146             goto errexit;
147         }
148         if (hp->prefixlen != 255) {
149             switch (hp->addrinfo->ai_family) {
150             case AF_INET:
151                 if (hp->prefixlen > 32) {
152                     debug(DBG_WARN, "resolve_resolve: prefix length must be <= 32 in %s", hp->host);
153                     goto errexit;
154                 }
155                 break;
156             case AF_INET6:
157                 break;
158             default:
159                 debug(DBG_WARN, "resolve_resolve: prefix must be IPv4 or IPv6 in %s", hp->host);
160                 goto errexit;
161             }
162         }
163     }
164     return 1;
165
166  errexit:
167     if (hp->addrinfo)
168         freeaddrinfo(hp->addrinfo);
169     return 0;
170 }         
171
172 int resolve_hostports(struct list *hostports, int socktype) {
173     struct list_node *entry;
174     struct hostportres *hp;
175     
176     for (entry = list_first(hostports); entry; entry = list_next(entry)) {
177         hp = (struct hostportres *)entry->data;
178         if (!hp->addrinfo && !resolve_resolve(hp, socktype, 0))
179             return 0;
180     }
181     return 1;
182 }
183
184 struct addrinfo *resolve_passiveaddrinfo(char *hostport, char *default_port, int socktype) {
185     struct addrinfo *ai = NULL;
186     struct hostportres *hp = resolve_newhostport(hostport, default_port, 0);
187     if (hp && resolve_resolve(hp, socktype, 1)) {
188         ai = hp->addrinfo;
189         hp->addrinfo = NULL;
190     }
191     resolve_freehostport(hp);
192     return ai;
193 }
194
195 /* returns 1 if the len first bits are equal, else 0 */
196 static int prefixmatch(void *a1, void *a2, uint8_t len) {
197     static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
198     uint8_t r, l = len / 8;
199     if (l && memcmp(a1, a2, l))
200         return 0;
201     r = len % 8;
202     if (!r)
203         return 1;
204     return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
205 }