mirror of
https://github.com/qemu/qemu.git
synced 2024-11-23 19:03:38 +08:00
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
This commit is contained in:
parent
065e281356
commit
d247d25f18
@ -589,7 +589,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_KVM
|
||||
OBJS+=kvm.o kvm-all.o
|
||||
endif
|
||||
|
@ -442,7 +442,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
int send_all(int fd, const uint8_t *buf, int len1)
|
||||
int send_all(int fd, const void *buf, int len1)
|
||||
{
|
||||
int ret, len;
|
||||
|
||||
@ -487,7 +487,7 @@ static int unix_write(int fd, const uint8_t *buf, int len1)
|
||||
return len1 - len;
|
||||
}
|
||||
|
||||
int send_all(int fd, const uint8_t *buf, int len1)
|
||||
int send_all(int fd, const void *buf, int len1)
|
||||
{
|
||||
return unix_write(fd, buf, len1);
|
||||
}
|
||||
|
@ -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
|
||||
|
400
qemu-sockets.c
Normal file
400
qemu-sockets.c
Normal file
@ -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
|
@ -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 void *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 */
|
||||
|
Loading…
Reference in New Issue
Block a user