chiark / gitweb /
More general error handling for sinks
[disorder] / lib / addr.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2007, 2008, 2013 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/addr.c
19  * @brief Socket address support */
20
21 #include "common.h"
22
23 #include <sys/types.h>
24 #if HAVE_SYS_SOCKET_H
25 # include <sys/socket.h>
26 #endif
27 #if HAVE_NETINET_IN_H
28 # include <netinet/in.h>
29 #endif
30 #if HAVE_ARPA_INET_H
31 # include <arpa/inet.h>
32 #endif
33 #if HAVE_SYS_UN_H
34 # include <sys/un.h>
35 #endif
36
37 #include "log.h"
38 #include "printf.h"
39 #include "addr.h"
40 #include "mem.h"
41 #include "syscalls.h"
42 #include "configuration.h"
43 #include "vector.h"
44
45 /** @brief Convert a pair of strings to an address
46  * @param a Pointer to string list
47  * @param pref Hints structure for getaddrinfo, or NULL
48  * @param namep Where to store address description, or NULL
49  * @return Address info structure or NULL on error
50  *
51  * This converts one or two strings into an address specification suitable
52  * for passing to socket(), bind() etc.
53  *
54  * If there is only one string then it is assumed to be the service
55  * name (port number).  If there are two then the first is the host
56  * name and the second the service name.
57  *
58  * @p namep is used to return a description of the address suitable
59  * for use in log messages.
60  *
61  * If an error occurs a message is logged and a null pointer returned.
62  */
63 struct addrinfo *get_address(const struct stringlist *a,
64                              const struct addrinfo *pref,
65                              char **namep) {
66   struct addrinfo *res;
67   char *name;
68   int rc;
69   char errbuf[1024];
70
71   switch(a->n) {  
72   case 1:
73     byte_xasprintf(&name, "host * service %s", a->s[0]);
74     if((rc = getaddrinfo(0, a->s[0], pref, &res))) {
75       disorder_error(0, "getaddrinfo %s: %s", a->s[0],
76                      format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
77       return 0;
78     }
79     break;
80   case 2:
81     byte_xasprintf(&name, "host %s service %s", a->s[0], a->s[1]);
82     if((rc = getaddrinfo(a->s[0], a->s[1], pref, &res))) {
83       disorder_error(0, "getaddrinfo %s %s: %s",
84                      a->s[0], a->s[1],
85                      format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
86       return 0;
87     }
88     break;
89   default:
90     disorder_error(0, "invalid network address specification (n=%d)", a->n);
91     return 0;
92   }
93   if(!res || (pref && res->ai_socktype != pref->ai_socktype)) {
94     disorder_error(0, "getaddrinfo didn't give us a suitable socket address");
95     if(res)
96       freeaddrinfo(res);
97     return 0;
98   }
99   if(namep)
100     *namep = name;
101   return res;
102 }
103
104 /** @brief Comparison function for address information
105  *
106  * Suitable for qsort().
107  */
108 int addrinfocmp(const struct addrinfo *a,
109                 const struct addrinfo *b) {
110   if(a->ai_family != b->ai_family) return a->ai_family - b->ai_family;
111   if(a->ai_socktype != b->ai_socktype) return a->ai_socktype - b->ai_socktype;
112   if(a->ai_protocol != b->ai_protocol) return a->ai_protocol - b->ai_protocol;
113   return sockaddrcmp(a->ai_addr, b->ai_addr);
114 }
115
116 /** @brief Comparison function for socket addresses
117  *
118  * Suitable for qsort().
119  */
120 int sockaddrcmp(const struct sockaddr *a,
121                 const struct sockaddr *b) {
122   const struct sockaddr_in *ina, *inb;
123   const struct sockaddr_in6 *in6a, *in6b;
124   
125   if(a->sa_family != b->sa_family) return a->sa_family - b->sa_family;
126   switch(a->sa_family) {
127   case PF_INET:
128     ina = (const struct sockaddr_in *)a;
129     inb = (const struct sockaddr_in *)b;
130     if(ina->sin_port != inb->sin_port) return ina->sin_port - inb->sin_port;
131     return ina->sin_addr.s_addr - inb->sin_addr.s_addr;
132     break;
133   case PF_INET6:
134     in6a = (const struct sockaddr_in6 *)a;
135     in6b = (const struct sockaddr_in6 *)b;
136     if(in6a->sin6_port != in6b->sin6_port)
137       return in6a->sin6_port - in6b->sin6_port;
138     return memcmp(&in6a->sin6_addr, &in6b->sin6_addr,
139                   sizeof (struct in6_addr));
140   default:
141     disorder_fatal(0, "unsupported protocol family %d", a->sa_family);
142   }
143 }
144
145 /** @brief Return nonzero if @p sin4 is an IPv4 multicast address */
146 static inline int multicast4(const struct sockaddr_in *sin4) {
147   return IN_MULTICAST(ntohl(sin4->sin_addr.s_addr));
148 }
149
150 /** @brief Return nonzero if @p sin6 is an IPv6 multicast address */
151 static inline int multicast6(const struct sockaddr_in6 *sin6) {
152   return IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
153 }
154
155 /** @brief Return true if @p sa represents a multicast address */
156 int multicast(const struct sockaddr *sa) {
157   switch(sa->sa_family) {
158   case AF_INET:
159     return multicast4((const struct sockaddr_in *)sa);
160   case AF_INET6:
161     return multicast6((const struct sockaddr_in6 *)sa);
162   default:
163     return 0;
164   }
165 }
166
167 /** @brief Format an IPv4 address */
168 static inline char *format_sockaddr4(const struct sockaddr_in *sin4) {
169   char buffer[1024], *r;
170
171   if(sin4->sin_port)
172     byte_xasprintf(&r, "%s port %u",
173                    inet_ntop(sin4->sin_family, &sin4->sin_addr,
174                              buffer, sizeof buffer),
175                    ntohs(sin4->sin_port));
176   else
177     byte_xasprintf(&r, "%s",
178                    inet_ntop(sin4->sin_family, &sin4->sin_addr,
179                              buffer, sizeof buffer));
180   return r;
181 }
182
183 /** @brief Format an IPv6 address */
184 static inline char *format_sockaddr6(const struct sockaddr_in6 *sin6) {
185   char buffer[1024], *r;
186
187   if(sin6->sin6_port)
188     byte_xasprintf(&r, "%s port %u",
189                    inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
190                              buffer, sizeof buffer),
191                    ntohs(sin6->sin6_port));
192   else
193     byte_xasprintf(&r, "%s",
194                    inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
195                              buffer, sizeof buffer));
196   return r;
197 }
198
199 #if HAVE_SYS_UN_H
200 /** @brief Format a UNIX socket address */
201 static inline char *format_sockaddrun(const struct sockaddr_un *sun) {
202   return xstrdup(sun->sun_path);
203 }
204 #endif
205     
206 /** @brief Construct a text description a sockaddr
207  * @param sa Socket address
208  * @return Human-readable form of address
209  */
210 char *format_sockaddr(const struct sockaddr *sa) {
211   switch(sa->sa_family) {
212   case AF_INET:
213     return format_sockaddr4((const struct sockaddr_in *)sa);
214   case AF_INET6:
215     return format_sockaddr6((const struct sockaddr_in6 *)sa);
216 #if HAVE_SYS_UN_H
217   case AF_UNIX:
218     return format_sockaddrun((const struct sockaddr_un *)sa);
219 #endif
220   default:
221     return 0;
222   }
223 }
224
225 /** @brief Parse the text form of a network address
226  * @param na Where to store result
227  * @param nvec Number of strings
228  * @param vec List of strings
229  * @return 0 on success, -1 on syntax error
230  */
231 int netaddress_parse(struct netaddress *na,
232                      int nvec,
233                      char **vec) {
234   const char *port;
235   long p;
236   int e;
237
238   na->af = AF_UNSPEC;
239   if(nvec > 0 && vec[0][0] == '-') {
240     if(!strcmp(vec[0], "-4"))
241       na->af = AF_INET;
242     else if(!strcmp(vec[0], "-6")) 
243       na->af = AF_INET6;
244     else if(!strcmp(vec[0], "-unix"))
245       na->af = AF_UNIX;
246     else if(!strcmp(vec[0], "-"))
247       na->af = AF_UNSPEC;
248     else
249       return -1;
250     --nvec;
251     ++vec;
252   }
253   if(nvec == 0)
254     return -1;
255   /* Possibilities are:
256    *
257    *       /path/to/unix/socket      an AF_UNIX socket
258    *       * PORT                    any address, specific port
259    *       PORT                      any address, specific port
260    *       ADDRESS PORT              specific address, specific port
261    */
262   if(vec[0][0] == '/' && na->af == AF_UNSPEC)
263     na->af = AF_UNIX;
264   if(na->af == AF_UNIX) {
265     if(nvec != 1)
266       return -1;
267     na->address = xstrdup(vec[0]);
268     na->port = -1;                      /* makes no sense */
269   } else {
270     switch(nvec) {
271     case 1:
272       na->address = NULL;
273       port = vec[0];
274       break;
275     case 2:
276       if(!strcmp(vec[0], "*"))
277         na->address = NULL;
278       else
279         na->address = xstrdup(vec[0]);
280       port = vec[1];
281       break;
282     default:
283       return -1;
284     }
285     if(port[strspn(port, "0123456789")])
286       return -1;
287     e = xstrtol(&p, port, NULL, 10);
288
289     if(e)
290       return -1;
291     if(p > 65535)
292       return -1;
293     na->port = (int)p;
294   }
295   return 0;
296 }
297
298 /** @brief Format a @ref netaddress structure
299  * @param na Network address to format
300  * @param nvecp Where to put string count, or NULL
301  * @param vecp Where to put strings
302  *
303  * The formatted form is suitable for passing to netaddress_parse().
304  */
305 void netaddress_format(const struct netaddress *na,
306                        int *nvecp,
307                        char ***vecp) {
308   struct vector v[1];
309
310   vector_init(v);
311   switch(na->af) {
312   case AF_UNSPEC: vector_append(v, xstrdup("-")); break;
313   case AF_INET: vector_append(v, xstrdup("-4")); break;
314   case AF_INET6: vector_append(v, xstrdup("-6")); break;
315   case AF_UNIX: vector_append(v, xstrdup("-unix")); break;
316   }
317   if(na->address)
318     vector_append(v, xstrdup(na->address));
319   else
320     vector_append(v, xstrdup("*"));
321   if(na->port != -1) {
322     char buffer[64];
323
324     byte_snprintf(buffer, sizeof buffer, "%d", na->port);
325     vector_append(v, xstrdup(buffer));
326   }
327   vector_terminate(v);
328   if(nvecp)
329     *nvecp = v->nvec;
330   if(vecp)
331     *vecp = v->vec;
332 }
333
334 /** @brief Resolve a network address
335  * @param na Address structure
336  * @param passive True if passive (bindable) address is desired
337  * @param protocol Protocol number desired (e.g. @c IPPROTO_TCP)
338  * @return List of suitable addresses or NULL
339  */
340 struct addrinfo *netaddress_resolve(const struct netaddress *na,
341                                     int passive,
342                                     int protocol) {
343   struct addrinfo *res, hints[1];
344   char service[64];
345   int rc;
346   char errbuf[1024];
347
348   memset(hints, 0, sizeof hints);
349   hints->ai_family = na->af;
350   hints->ai_protocol = protocol;
351   hints->ai_flags = passive ? AI_PASSIVE : 0;
352   byte_snprintf(service, sizeof service, "%d", na->port);
353   rc = getaddrinfo(na->address, service, hints, &res);
354   if(rc) {
355     disorder_error(0, "getaddrinfo %s %d: %s",
356                    na->address ? na->address : "*",
357                    na->port,
358                    format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
359     return NULL;
360   }
361   return res;
362 }
363
364 /*
365 Local Variables:
366 c-basic-offset:2
367 comment-column:40
368 End:
369 */