Commit d247d25f18764402899b37c381bb696a79000b4e

Authored by aliguori
1 parent 065e2813

sockets: helper functions for qemu (Gerd Hoffman)

This patch creates a new source file qemu-sockets.c with a bunch of
helper functions to create listening and connected sockets.

New features of this code are (a) support for searching for a free
port in a given range and (b) support for IPv6.

The following patches put that code into use.

Compile fixes for Windows added by Anthony Liguori

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5695 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile.target
... ... @@ -589,7 +589,7 @@ ifndef CONFIG_USER_ONLY
589 589  
590 590 OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
591 591 OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o qemu-char.o
592   -OBJS+=net.o
  592 +OBJS+=net.o qemu-sockets.o
593 593 ifdef CONFIG_KVM
594 594 OBJS+=kvm.o kvm-all.o
595 595 endif
... ...
qemu-char.c
... ... @@ -442,7 +442,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
442 442  
443 443  
444 444 #ifdef _WIN32
445   -int send_all(int fd, const uint8_t *buf, int len1)
  445 +int send_all(int fd, const void *buf, int len1)
446 446 {
447 447 int ret, len;
448 448  
... ... @@ -487,7 +487,7 @@ static int unix_write(int fd, const uint8_t *buf, int len1)
487 487 return len1 - len;
488 488 }
489 489  
490   -int send_all(int fd, const uint8_t *buf, int len1)
  490 +int send_all(int fd, const void *buf, int len1)
491 491 {
492 492 return unix_write(fd, buf, len1);
493 493 }
... ...
qemu-common.h
... ... @@ -29,6 +29,7 @@
29 29  
30 30 #ifdef _WIN32
31 31 #define WIN32_LEAN_AND_MEAN
  32 +#define WINVER 0x0501 /* needed for ipv6 bits */
32 33 #include <windows.h>
33 34 #define fsync _commit
34 35 #define lseek _lseeki64
... ...
qemu-sockets.c 0 → 100644
  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
... ...
qemu_socket.h
... ... @@ -4,6 +4,7 @@
4 4  
5 5 #ifdef _WIN32
6 6 #define WIN32_LEAN_AND_MEAN
  7 +#define WINVER 0x0501 /* needed for ipv6 bits */
7 8 #include <windows.h>
8 9 #include <winsock2.h>
9 10 #include <ws2tcpip.h>
... ... @@ -28,15 +29,24 @@ int inet_aton(const char *cp, struct in_addr *ia);
28 29 #define socket_error() errno
29 30 #define closesocket(s) close(s)
30 31  
31   -int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
32   -
33 32 #endif /* !_WIN32 */
34 33  
  34 +/* misc helpers */
35 35 void socket_set_nonblock(int fd);
  36 +int send_all(int fd, const void *buf, int len1);
  37 +
  38 +/* New, ipv6-ready socket helper functions, see qemu-sockets.c */
  39 +int inet_listen(const char *str, char *ostr, int olen,
  40 + int socktype, int port_offset);
  41 +int inet_connect(const char *str, int socktype);
  42 +
  43 +int unix_listen(const char *path, char *ostr, int olen);
  44 +int unix_connect(const char *path);
  45 +
  46 +/* Old, ipv4 only bits. Don't use for new code. */
36 47 int parse_host_port(struct sockaddr_in *saddr, const char *str);
37 48 int parse_host_src_port(struct sockaddr_in *haddr,
38 49 struct sockaddr_in *saddr,
39 50 const char *str);
40   -int send_all(int fd, const uint8_t *buf, int len1);
41 51  
42 52 #endif /* QEMU_SOCKET_H */
... ...