From: Anthony Liguori <anthony@codemonkey.ws>
To: qemu-devel@nongnu.org
Cc: Gerd Hoffmann <kraxel@redhat.com>
Subject: Re: [Qemu-devel] [PATCH 1/3] sockets: helper functions for qemu.
Date: Tue, 11 Nov 2008 14:41:17 -0600 [thread overview]
Message-ID: <4919EDED.7070605@codemonkey.ws> (raw)
In-Reply-To: <1225730550-31941-2-git-send-email-kraxel@redhat.com>
Gerd Hoffmann wrote:
> 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.
>
I'm going to apply this series but can you follow up with
copyright/licenses for each of the files you add?
Regards,
Anthony Liguori
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> Makefile.target | 2 +-
> qemu-common.h | 1 +
> qemu-sockets.c | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> qemu_socket.h | 16 ++-
> 4 files changed, 415 insertions(+), 4 deletions(-)
> create mode 100644 qemu-sockets.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 3bad292..06ea1da 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -580,7 +580,7 @@ ifndef CONFIG_USER_ONLY
>
> OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
> OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o qemu-char.o
> -OBJS+=net.o
> +OBJS+=net.o qemu-sockets.o
> ifdef CONFIG_WIN32
> OBJS+=block-raw-win32.o
> else
> diff --git a/qemu-common.h b/qemu-common.h
> index f23d7b4..142182b 100644
> --- a/qemu-common.h
> +++ b/qemu-common.h
> @@ -29,6 +29,7 @@
>
> #ifdef _WIN32
> #define WIN32_LEAN_AND_MEAN
> +#define WINVER 0x0501 /* needed for ipv6 bits */
> #include <windows.h>
> #define fsync _commit
> #define lseek _lseeki64
> diff --git a/qemu-sockets.c b/qemu-sockets.c
> new file mode 100644
> index 0000000..a5499a6
> --- /dev/null
> +++ b/qemu-sockets.c
> @@ -0,0 +1,400 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <ctype.h>
> +#include <errno.h>
> +#include <unistd.h>
> +
> +#include "qemu_socket.h"
> +
> +#ifndef AI_ADDRCONFIG
> +# define AI_ADDRCONFIG 0
> +#endif
> +
> +static int sockets_debug = 0;
> +static const int on=1, off=0;
> +
> +static int inet_getport(struct addrinfo *e)
> +{
> + struct sockaddr_in *i4;
> + struct sockaddr_in6 *i6;
> +
> + switch (e->ai_family) {
> + case PF_INET6:
> + i6 = (void*)e->ai_addr;
> + return ntohs(i6->sin6_port);
> + case PF_INET:
> + i4 = (void*)e->ai_addr;
> + return ntohs(i4->sin_port);
> + default:
> + return 0;
> + }
> +}
> +
> +static void inet_setport(struct addrinfo *e, int port)
> +{
> + struct sockaddr_in *i4;
> + struct sockaddr_in6 *i6;
> +
> + switch (e->ai_family) {
> + case PF_INET6:
> + i6 = (void*)e->ai_addr;
> + i6->sin6_port = htons(port);
> + break;
> + case PF_INET:
> + i4 = (void*)e->ai_addr;
> + i4->sin_port = htons(port);
> + break;
> + }
> +}
> +
> +static const char *inet_strfamily(int family)
> +{
> + switch (family) {
> + case PF_INET6: return "ipv6";
> + case PF_INET: return "ipv4";
> + case PF_UNIX: return "unix";
> + }
> + return "????";
> +}
> +
> +static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
> +{
> + struct addrinfo *e;
> + char uaddr[INET6_ADDRSTRLEN+1];
> + char uport[33];
> +
> + for (e = res; e != NULL; e = e->ai_next) {
> + getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> + uaddr,INET6_ADDRSTRLEN,uport,32,
> + NI_NUMERICHOST | NI_NUMERICSERV);
> + fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
> + tag, inet_strfamily(e->ai_family), uaddr, uport);
> + }
> +}
> +
> +int inet_listen(const char *str, char *ostr, int olen,
> + int socktype, int port_offset)
> +{
> + struct addrinfo ai,*res,*e;
> + char addr[64];
> + char port[33];
> + char uaddr[INET6_ADDRSTRLEN+1];
> + char uport[33];
> + const char *opts, *h;
> + int slisten,rc,pos,to,try_next;
> +
> + memset(&ai,0, sizeof(ai));
> + ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
> + ai.ai_family = PF_UNSPEC;
> + ai.ai_socktype = socktype;
> +
> + /* parse address */
> + if (str[0] == ':') {
> + /* no host given */
> + strcpy(addr,"");
> + if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
> + fprintf(stderr, "%s: portonly parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + } else if (str[0] == '[') {
> + /* IPv6 addr */
> + if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: ipv6 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET6;
> + } else if (isdigit(str[0])) {
> + /* IPv4 addr */
> + if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: ipv4 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET;
> + } else {
> + /* hostname */
> + if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
> + fprintf(stderr, "%s: hostname parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + }
> +
> + /* parse options */
> + opts = str + pos;
> + h = strstr(opts, ",to=");
> + to = h ? atoi(h+4) : 0;
> + if (strstr(opts, ",ipv4"))
> + ai.ai_family = PF_INET;
> + if (strstr(opts, ",ipv6"))
> + ai.ai_family = PF_INET6;
> +
> + /* lookup */
> + if (port_offset)
> + snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
> + rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
> + if (rc != 0) {
> + fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
> + addr, port, gai_strerror(rc));
> + return -1;
> + }
> + if (sockets_debug)
> + inet_print_addrinfo(__FUNCTION__, res);
> +
> + /* create socket + bind */
> + for (e = res; e != NULL; e = e->ai_next) {
> + getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> + uaddr,INET6_ADDRSTRLEN,uport,32,
> + NI_NUMERICHOST | NI_NUMERICSERV);
> + slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
> + if (slisten < 0) {
> + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), strerror(errno));
> + continue;
> + }
> +
> + setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
> +#ifdef IPV6_V6ONLY
> + if (e->ai_family == PF_INET6) {
> + /* listen on both ipv4 and ipv6 */
> + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
> + }
> +#endif
> +
> + for (;;) {
> + if (bind(slisten, e->ai_addr, e->ai_addrlen) != 0) {
> + if (sockets_debug)
> + fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), uaddr, inet_getport(e));
> + goto listen;
> + }
> + try_next = to && (inet_getport(e) <= to + port_offset);
> + if (!try_next || sockets_debug)
> + fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), uaddr, inet_getport(e),
> + strerror(errno));
> + if (try_next) {
> + inet_setport(e, inet_getport(e) + 1);
> + continue;
> + }
> + break;
> + }
> + closesocket(slisten);
> + }
> + fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
> + freeaddrinfo(res);
> + return -1;
> +
> +listen:
> + if (listen(slisten,1) != 0) {
> + perror("listen");
> + closesocket(slisten);
> + return -1;
> + }
> + if (ostr) {
> + if (e->ai_family == PF_INET6) {
> + snprintf(ostr, olen, "[%s]:%d%s", uaddr,
> + inet_getport(e) - port_offset, opts);
> + } else {
> + snprintf(ostr, olen, "%s:%d%s", uaddr,
> + inet_getport(e) - port_offset, opts);
> + }
> + }
> + freeaddrinfo(res);
> + return slisten;
> +}
> +
> +int inet_connect(const char *str, int socktype)
> +{
> + struct addrinfo ai,*res,*e;
> + char addr[64];
> + char port[33];
> + char uaddr[INET6_ADDRSTRLEN+1];
> + char uport[33];
> + int sock,rc;
> +
> + memset(&ai,0, sizeof(ai));
> + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
> + ai.ai_family = PF_UNSPEC;
> + ai.ai_socktype = socktype;
> +
> + /* parse address */
> + if (str[0] == '[') {
> + /* IPv6 addr */
> + if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: ipv6 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET6;
> + } else if (isdigit(str[0])) {
> + /* IPv4 addr */
> + if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: ipv4 parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + ai.ai_family = PF_INET;
> + } else {
> + /* hostname */
> + if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
> + fprintf(stderr, "%s: hostname parse error (%s)\n",
> + __FUNCTION__, str);
> + return -1;
> + }
> + }
> +
> + /* parse options */
> + if (strstr(str, ",ipv4"))
> + ai.ai_family = PF_INET;
> + if (strstr(str, ",ipv6"))
> + ai.ai_family = PF_INET6;
> +
> + /* lookup */
> + if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
> + fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
> + addr, port);
> + return -1;
> + }
> + if (sockets_debug)
> + inet_print_addrinfo(__FUNCTION__, res);
> +
> + for (e = res; e != NULL; e = e->ai_next) {
> + if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> + uaddr,INET6_ADDRSTRLEN,uport,32,
> + NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
> + fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
> + continue;
> + }
> + sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
> + if (sock < 0) {
> + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family), strerror(errno));
> + continue;
> + }
> + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
> +
> + /* connect to peer */
> + if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
> + if (sockets_debug || NULL == e->ai_next)
> + fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
> + inet_strfamily(e->ai_family),
> + e->ai_canonname, uaddr, uport, strerror(errno));
> + closesocket(sock);
> + continue;
> + }
> + if (sockets_debug)
> + fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
> + inet_strfamily(e->ai_family),
> + e->ai_canonname, uaddr, uport);
> + freeaddrinfo(res);
> + return sock;
> + }
> + freeaddrinfo(res);
> + return -1;
> +}
> +
> +#ifndef _WIN32
> +
> +int unix_listen(const char *str, char *ostr, int olen)
> +{
> + struct sockaddr_un un;
> + char *path, *opts;
> + int sock, fd, len;
> +
> + sock = socket(PF_UNIX, SOCK_STREAM, 0);
> + if (sock < 0) {
> + perror("socket(unix)");
> + return -1;
> + }
> +
> + opts = strchr(str, ',');
> + if (opts) {
> + len = opts - str;
> + path = malloc(len+1);
> + snprintf(path, len+1, "%.*s", len, str);
> + } else
> + path = strdup(str);
> +
> + memset(&un, 0, sizeof(un));
> + un.sun_family = AF_UNIX;
> + if (path && strlen(path)) {
> + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
> + } else {
> + char *tmpdir = getenv("TMPDIR");
> + snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
> + tmpdir ? tmpdir : "/tmp");
> + /*
> + * This dummy fd usage silences the mktemp() unsecure warning.
> + * Using mkstemp() doesn't make things more secure here
> + * though. bind() complains about existing files, so we have
> + * to unlink first and thus re-open the race window. The
> + * worst case possible is bind() failing, i.e. a DoS attack.
> + */
> + fd = mkstemp(un.sun_path); close(fd);
> + }
> + snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
> +
> + unlink(un.sun_path);
> + if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
> + fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
> + goto err;
> + }
> + if (listen(sock, 1) < 0) {
> + fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
> + goto err;
> + }
> +
> + if (sockets_debug)
> + fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
> + free(path);
> + return sock;
> +
> +err:
> + free(path);
> + closesocket(sock);
> + return -1;
> +}
> +
> +int unix_connect(const char *path)
> +{
> + struct sockaddr_un un;
> + int sock;
> +
> + sock = socket(PF_UNIX, SOCK_STREAM, 0);
> + if (sock < 0) {
> + perror("socket(unix)");
> + return -1;
> + }
> +
> + memset(&un, 0, sizeof(un));
> + un.sun_family = AF_UNIX;
> + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
> + if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
> + fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
> + return -1;
> + }
> +
> + if (sockets_debug)
> + fprintf(stderr, "connect(unix:%s): OK\n", path);
> + return sock;
> +}
> +
> +#else
> +
> +int unix_listen(const char *path, char *ostr, int olen)
> +{
> + fprintf(stderr, "unix sockets are not available on windows\n");
> + return -1;
> +}
> +
> +int unix_connect(const char *path)
> +{
> + fprintf(stderr, "unix sockets are not available on windows\n");
> + return -1;
> +}
> +
> +#endif
> diff --git a/qemu_socket.h b/qemu_socket.h
> index 18488dd..43a27b0 100644
> --- a/qemu_socket.h
> +++ b/qemu_socket.h
> @@ -4,6 +4,7 @@
>
> #ifdef _WIN32
> #define WIN32_LEAN_AND_MEAN
> +#define WINVER 0x0501 /* needed for ipv6 bits */
> #include <windows.h>
> #include <winsock2.h>
> #include <ws2tcpip.h>
> @@ -28,15 +29,24 @@ int inet_aton(const char *cp, struct in_addr *ia);
> #define socket_error() errno
> #define closesocket(s) close(s)
>
> -int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
> -
> #endif /* !_WIN32 */
>
> +/* misc helpers */
> void socket_set_nonblock(int fd);
> +int send_all(int fd, const uint8_t *buf, int len1);
> +
> +/* New, ipv6-ready socket helper functions, see qemu-sockets.c */
> +int inet_listen(const char *str, char *ostr, int olen,
> + int socktype, int port_offset);
> +int inet_connect(const char *str, int socktype);
> +
> +int unix_listen(const char *path, char *ostr, int olen);
> +int unix_connect(const char *path);
> +
> +/* Old, ipv4 only bits. Don't use for new code. */
> int parse_host_port(struct sockaddr_in *saddr, const char *str);
> int parse_host_src_port(struct sockaddr_in *haddr,
> struct sockaddr_in *saddr,
> const char *str);
> -int send_all(int fd, const uint8_t *buf, int len1);
>
> #endif /* QEMU_SOCKET_H */
>
next prev parent reply other threads:[~2008-11-11 20:41 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-11-03 16:42 [Qemu-devel] [PATCH v3 0/3] ipv6 and autoport patches Gerd Hoffmann
2008-11-03 16:42 ` [Qemu-devel] [PATCH 1/3] sockets: helper functions for qemu Gerd Hoffmann
2008-11-04 12:42 ` [Qemu-devel] " Gerd Hoffmann
2008-11-11 20:41 ` Anthony Liguori [this message]
2008-11-11 21:14 ` [Qemu-devel] " Gerd Hoffmann
2008-11-11 20:47 ` Anthony Liguori
2008-11-03 16:42 ` [Qemu-devel] [PATCH 2/3] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
2008-11-11 20:52 ` Anthony Liguori
2008-11-26 23:25 ` Ryan Harper
2008-11-27 9:11 ` Gerd Hoffmann
2008-11-03 16:42 ` [Qemu-devel] [PATCH 3/3] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
2008-11-11 20:54 ` Anthony Liguori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4919EDED.7070605@codemonkey.ws \
--to=anthony@codemonkey.ws \
--cc=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.