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