sockets: helper functions for qemu (Gerd Hoffman)
[qemu] / qemu-sockets.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <unistd.h>
7
8 #include "qemu_socket.h"
9
10 #ifndef AI_ADDRCONFIG
11 # define AI_ADDRCONFIG 0
12 #endif
13
14 static int sockets_debug = 0;
15 static const int on=1, off=0;
16
17 static int inet_getport(struct addrinfo *e)
18 {
19     struct sockaddr_in *i4;
20     struct sockaddr_in6 *i6;
21
22     switch (e->ai_family) {
23     case PF_INET6:
24         i6 = (void*)e->ai_addr;
25         return ntohs(i6->sin6_port);
26     case PF_INET:
27         i4 = (void*)e->ai_addr;
28         return ntohs(i4->sin_port);
29     default:
30         return 0;
31     }
32 }
33
34 static void inet_setport(struct addrinfo *e, int port)
35 {
36     struct sockaddr_in *i4;
37     struct sockaddr_in6 *i6;
38
39     switch (e->ai_family) {
40     case PF_INET6:
41         i6 = (void*)e->ai_addr;
42         i6->sin6_port = htons(port);
43         break;
44     case PF_INET:
45         i4 = (void*)e->ai_addr;
46         i4->sin_port = htons(port);
47         break;
48     }
49 }
50
51 static const char *inet_strfamily(int family)
52 {
53     switch (family) {
54     case PF_INET6: return "ipv6";
55     case PF_INET:  return "ipv4";
56     case PF_UNIX:  return "unix";
57     }
58     return "????";
59 }
60
61 static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
62 {
63     struct addrinfo *e;
64     char uaddr[INET6_ADDRSTRLEN+1];
65     char uport[33];
66
67     for (e = res; e != NULL; e = e->ai_next) {
68         getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
69                     uaddr,INET6_ADDRSTRLEN,uport,32,
70                     NI_NUMERICHOST | NI_NUMERICSERV);
71         fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
72                 tag, inet_strfamily(e->ai_family), uaddr, uport);
73     }
74 }
75
76 int inet_listen(const char *str, char *ostr, int olen,
77                 int socktype, int port_offset)
78 {
79     struct addrinfo ai,*res,*e;
80     char addr[64];
81     char port[33];
82     char uaddr[INET6_ADDRSTRLEN+1];
83     char uport[33];
84     const char *opts, *h;
85     int slisten,rc,pos,to,try_next;
86
87     memset(&ai,0, sizeof(ai));
88     ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
89     ai.ai_family = PF_UNSPEC;
90     ai.ai_socktype = socktype;
91
92     /* parse address */
93     if (str[0] == ':') {
94         /* no host given */
95         strcpy(addr,"");
96         if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
97             fprintf(stderr, "%s: portonly parse error (%s)\n",
98                     __FUNCTION__, str);
99             return -1;
100         }
101     } else if (str[0] == '[') {
102         /* IPv6 addr */
103         if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
104             fprintf(stderr, "%s: ipv6 parse error (%s)\n",
105                     __FUNCTION__, str);
106             return -1;
107         }
108         ai.ai_family = PF_INET6;
109     } else if (isdigit(str[0])) {
110         /* IPv4 addr */
111         if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
112             fprintf(stderr, "%s: ipv4 parse error (%s)\n",
113                     __FUNCTION__, str);
114             return -1;
115         }
116         ai.ai_family = PF_INET;
117     } else {
118         /* hostname */
119         if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
120             fprintf(stderr, "%s: hostname parse error (%s)\n",
121                     __FUNCTION__, str);
122             return -1;
123         }
124     }
125
126     /* parse options */
127     opts = str + pos;
128     h = strstr(opts, ",to=");
129     to = h ? atoi(h+4) : 0;
130     if (strstr(opts, ",ipv4"))
131         ai.ai_family = PF_INET;
132     if (strstr(opts, ",ipv6"))
133         ai.ai_family = PF_INET6;
134
135     /* lookup */
136     if (port_offset)
137         snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
138     rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
139     if (rc != 0) {
140         fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
141                 addr, port, gai_strerror(rc));
142         return -1;
143     }
144     if (sockets_debug)
145         inet_print_addrinfo(__FUNCTION__, res);
146
147     /* create socket + bind */
148     for (e = res; e != NULL; e = e->ai_next) {
149         getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
150                     uaddr,INET6_ADDRSTRLEN,uport,32,
151                     NI_NUMERICHOST | NI_NUMERICSERV);
152         slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
153         if (slisten < 0) {
154             fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
155                     inet_strfamily(e->ai_family), strerror(errno));
156             continue;
157         }
158
159         setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
160 #ifdef IPV6_V6ONLY
161         if (e->ai_family == PF_INET6) {
162             /* listen on both ipv4 and ipv6 */
163             setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
164         }
165 #endif
166
167         for (;;) {
168             if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
169                 if (sockets_debug)
170                     fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
171                             inet_strfamily(e->ai_family), uaddr, inet_getport(e));
172                 goto listen;
173             }
174             try_next = to && (inet_getport(e) <= to + port_offset);
175             if (!try_next || sockets_debug)
176                 fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
177                         inet_strfamily(e->ai_family), uaddr, inet_getport(e),
178                         strerror(errno));
179             if (try_next) {
180                 inet_setport(e, inet_getport(e) + 1);
181                 continue;
182             }
183             break;
184         }
185         closesocket(slisten);
186     }
187     fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
188     freeaddrinfo(res);
189     return -1;
190
191 listen:
192     if (listen(slisten,1) != 0) {
193         perror("listen");
194         closesocket(slisten);
195         return -1;
196     }
197     if (ostr) {
198         if (e->ai_family == PF_INET6) {
199             snprintf(ostr, olen, "[%s]:%d%s", uaddr,
200                      inet_getport(e) - port_offset, opts);
201         } else {
202             snprintf(ostr, olen, "%s:%d%s", uaddr,
203                      inet_getport(e) - port_offset, opts);
204         }
205     }
206     freeaddrinfo(res);
207     return slisten;
208 }
209
210 int inet_connect(const char *str, int socktype)
211 {
212     struct addrinfo ai,*res,*e;
213     char addr[64];
214     char port[33];
215     char uaddr[INET6_ADDRSTRLEN+1];
216     char uport[33];
217     int sock,rc;
218
219     memset(&ai,0, sizeof(ai));
220     ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
221     ai.ai_family = PF_UNSPEC;
222     ai.ai_socktype = socktype;
223
224     /* parse address */
225     if (str[0] == '[') {
226         /* IPv6 addr */
227         if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
228             fprintf(stderr, "%s: ipv6 parse error (%s)\n",
229                     __FUNCTION__, str);
230             return -1;
231         }
232         ai.ai_family = PF_INET6;
233     } else if (isdigit(str[0])) {
234         /* IPv4 addr */
235         if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
236             fprintf(stderr, "%s: ipv4 parse error (%s)\n",
237                     __FUNCTION__, str);
238             return -1;
239         }
240         ai.ai_family = PF_INET;
241     } else {
242         /* hostname */
243         if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
244             fprintf(stderr, "%s: hostname parse error (%s)\n",
245                     __FUNCTION__, str);
246             return -1;
247         }
248     }
249
250     /* parse options */
251     if (strstr(str, ",ipv4"))
252         ai.ai_family = PF_INET;
253     if (strstr(str, ",ipv6"))
254         ai.ai_family = PF_INET6;
255
256     /* lookup */
257     if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
258         fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
259                 addr, port);
260         return -1;
261     }
262     if (sockets_debug)
263         inet_print_addrinfo(__FUNCTION__, res);
264
265     for (e = res; e != NULL; e = e->ai_next) {
266         if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
267                         uaddr,INET6_ADDRSTRLEN,uport,32,
268                         NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
269             fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
270             continue;
271         }
272         sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
273         if (sock < 0) {
274             fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
275                     inet_strfamily(e->ai_family), strerror(errno));
276             continue;
277         }
278         setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
279
280         /* connect to peer */
281         if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
282             if (sockets_debug || NULL == e->ai_next)
283                 fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
284                         inet_strfamily(e->ai_family),
285                         e->ai_canonname, uaddr, uport, strerror(errno));
286             closesocket(sock);
287             continue;
288         }
289         if (sockets_debug)
290             fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
291                     inet_strfamily(e->ai_family),
292                     e->ai_canonname, uaddr, uport);
293         freeaddrinfo(res);
294         return sock;
295     }
296     freeaddrinfo(res);
297     return -1;
298 }
299
300 #ifndef _WIN32
301
302 int unix_listen(const char *str, char *ostr, int olen)
303 {
304     struct sockaddr_un un;
305     char *path, *opts;
306     int sock, fd, len;
307
308     sock = socket(PF_UNIX, SOCK_STREAM, 0);
309     if (sock < 0) {
310         perror("socket(unix)");
311         return -1;
312     }
313
314     opts = strchr(str, ',');
315     if (opts) {
316         len = opts - str;
317         path = malloc(len+1);
318         snprintf(path, len+1, "%.*s", len, str);
319     } else
320         path = strdup(str);
321
322     memset(&un, 0, sizeof(un));
323     un.sun_family = AF_UNIX;
324     if (path && strlen(path)) {
325         snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
326     } else {
327         char *tmpdir = getenv("TMPDIR");
328         snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
329                  tmpdir ? tmpdir : "/tmp");
330         /*
331          * This dummy fd usage silences the mktemp() unsecure warning.
332          * Using mkstemp() doesn't make things more secure here
333          * though.  bind() complains about existing files, so we have
334          * to unlink first and thus re-open the race window.  The
335          * worst case possible is bind() failing, i.e. a DoS attack.
336          */
337         fd = mkstemp(un.sun_path); close(fd);
338     }
339     snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
340
341     unlink(un.sun_path);
342     if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
343         fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
344         goto err;
345     }
346     if (listen(sock, 1) < 0) {
347         fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
348         goto err;
349     }
350
351     if (sockets_debug)
352         fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
353     free(path);
354     return sock;
355
356 err:
357     free(path);
358     closesocket(sock);
359     return -1;
360 }
361
362 int unix_connect(const char *path)
363 {
364     struct sockaddr_un un;
365     int sock;
366
367     sock = socket(PF_UNIX, SOCK_STREAM, 0);
368     if (sock < 0) {
369         perror("socket(unix)");
370         return -1;
371     }
372
373     memset(&un, 0, sizeof(un));
374     un.sun_family = AF_UNIX;
375     snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
376     if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
377         fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
378         return -1;
379     }
380
381     if (sockets_debug)
382         fprintf(stderr, "connect(unix:%s): OK\n", path);
383     return sock;
384 }
385
386 #else
387
388 int unix_listen(const char *path, char *ostr, int olen)
389 {
390     fprintf(stderr, "unix sockets are not available on windows\n");
391     return -1;
392 }
393
394 int unix_connect(const char *path)
395 {
396     fprintf(stderr, "unix sockets are not available on windows\n");
397     return -1;
398 }
399
400 #endif