chiark / gitweb /
log: more general error message formatting
[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
236   na->af = AF_UNSPEC;
237   if(nvec > 0 && vec[0][0] == '-') {
238     if(!strcmp(vec[0], "-4"))
239       na->af = AF_INET;
240     else if(!strcmp(vec[0], "-6")) 
241       na->af = AF_INET6;
242     else if(!strcmp(vec[0], "-unix"))
243       na->af = AF_UNIX;
244     else if(!strcmp(vec[0], "-"))
245       na->af = AF_UNSPEC;
246     else
247       return -1;
248     --nvec;
249     ++vec;
250   }
251   if(nvec == 0)
252     return -1;
253   /* Possibilities are:
254    *
255    *       /path/to/unix/socket      an AF_UNIX socket
256    *       * PORT                    any address, specific port
257    *       PORT                      any address, specific port
258    *       ADDRESS PORT              specific address, specific port
259    */
260   if(vec[0][0] == '/' && na->af == AF_UNSPEC)
261     na->af = AF_UNIX;
262   if(na->af == AF_UNIX) {
263     if(nvec != 1)
264       return -1;
265     na->address = xstrdup(vec[0]);
266     na->port = -1;                      /* makes no sense */
267   } else {
268     switch(nvec) {
269     case 1:
270       na->address = NULL;
271       port = vec[0];
272       break;
273     case 2:
274       if(!strcmp(vec[0], "*"))
275         na->address = NULL;
276       else
277         na->address = xstrdup(vec[0]);
278       port = vec[1];
279       break;
280     default:
281       return -1;
282     }
283     if(port[strspn(port, "0123456789")])
284       return -1;
285     long p;
286     int e = xstrtol(&p, port, NULL, 10);
287
288     if(e)
289       return -1;
290     if(p > 65535)
291       return -1;
292     na->port = (int)p;
293   }
294   return 0;
295 }
296
297 /** @brief Format a @ref netaddress structure
298  * @param na Network address to format
299  * @param nvecp Where to put string count, or NULL
300  * @param vecp Where to put strings
301  *
302  * The formatted form is suitable for passing to netaddress_parse().
303  */
304 void netaddress_format(const struct netaddress *na,
305                        int *nvecp,
306                        char ***vecp) {
307   struct vector v[1];
308
309   vector_init(v);
310   switch(na->af) {
311   case AF_UNSPEC: vector_append(v, xstrdup("-")); break;
312   case AF_INET: vector_append(v, xstrdup("-4")); break;
313   case AF_INET6: vector_append(v, xstrdup("-6")); break;
314   case AF_UNIX: vector_append(v, xstrdup("-unix")); break;
315   }
316   if(na->address)
317     vector_append(v, xstrdup(na->address));
318   else
319     vector_append(v, xstrdup("*"));
320   if(na->port != -1) {
321     char buffer[64];
322
323     snprintf(buffer, sizeof buffer, "%d", na->port);
324     vector_append(v, xstrdup(buffer));
325   }
326   vector_terminate(v);
327   if(nvecp)
328     *nvecp = v->nvec;
329   if(vecp)
330     *vecp = v->vec;
331 }
332
333 /** @brief Resolve a network address
334  * @param na Address structure
335  * @param passive True if passive (bindable) address is desired
336  * @param protocol Protocol number desired (e.g. @c IPPROTO_TCP)
337  * @return List of suitable addresses or NULL
338  */
339 struct addrinfo *netaddress_resolve(const struct netaddress *na,
340                                     int passive,
341                                     int protocol) {
342   struct addrinfo *res, hints[1];
343   char service[64];
344   int rc;
345   char errbuf[1024];
346
347   memset(hints, 0, sizeof hints);
348   hints->ai_family = na->af;
349   hints->ai_protocol = protocol;
350   hints->ai_flags = passive ? AI_PASSIVE : 0;
351   snprintf(service, sizeof service, "%d", na->port);
352   rc = getaddrinfo(na->address, service, hints, &res);
353   if(rc) {
354     disorder_error(0, "getaddrinfo %s %d: %s",
355                    na->address ? na->address : "*",
356                    na->port,
357                    format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
358     return NULL;
359   }
360   return res;
361 }
362
363 /*
364 Local Variables:
365 c-basic-offset:2
366 comment-column:40
367 End:
368 */