aboutsummaryrefslogtreecommitdiffstats
path: root/qemu-sockets-android.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu-sockets-android.c')
-rw-r--r--qemu-sockets-android.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/qemu-sockets-android.c b/qemu-sockets-android.c
new file mode 100644
index 0000000..c22f352
--- /dev/null
+++ b/qemu-sockets-android.c
@@ -0,0 +1,357 @@
+/*
+ * inet and unix socket functions for qemu
+ *
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu_socket.h"
+#include "qemu-common.h" /* for qemu_isdigit */
+
+#ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+#endif
+
+static int sockets_debug = 0;
+static const int on=1, off=0;
+
+static const char *sock_address_strfamily(SockAddress *s)
+{
+ switch (sock_address_get_family(s)) {
+ case SOCKET_IN6: return "ipv6";
+ case SOCKET_INET: return "ipv4";
+ case SOCKET_UNIX: return "unix";
+ default: return "????";
+ }
+}
+
+int inet_listen(const char *str, char *ostr, int olen,
+ SocketType socktype, int port_offset)
+{
+ SockAddress** list;
+ SockAddress* e;
+ unsigned flags = SOCKET_LIST_PASSIVE;
+ char addr[64];
+ char port[33];
+ char uaddr[256+1];
+ const char *opts, *h;
+ int slisten,pos,to,try_next,nn;
+
+ /* parse address */
+ if (str[0] == ':') {
+ /* no host given */
+ addr[0] = '\0';
+ 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;
+ }
+ flags |= SOCKET_LIST_FORCE_IN6;
+ } else if (qemu_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;
+ }
+ flags |= SOCKET_LIST_FORCE_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")) {
+ flags &= ~SOCKET_LIST_FORCE_IN6;
+ flags |= SOCKET_LIST_FORCE_INET;
+ }
+ if (strstr(opts, ",ipv6")) {
+ flags &= SOCKET_LIST_FORCE_INET;
+ flags |= SOCKET_LIST_FORCE_IN6;
+ }
+
+ /* lookup */
+ if (port_offset)
+ snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
+
+ list = sock_address_list_create( strlen(addr) ? addr : NULL,
+ port,
+ flags );
+ if (list == NULL) {
+ fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
+ addr, port, errno_str);
+ return -1;
+ }
+
+ /* create socket + bind */
+ for (nn = 0; list[nn] != NULL; nn++) {
+ SocketFamily family;
+
+ e = list[nn];
+ family = sock_address_get_family(e);
+ slisten = socket_create(family, socktype);
+ if (slisten < 0) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ sock_address_strfamily(e), errno_str);
+ continue;
+ }
+
+ socket_set_xreuseaddr(slisten);
+#ifdef IPV6_V6ONLY
+ /* listen on both ipv4 and ipv6 */
+ if (family == PF_INET6) {
+ socket_set_ipv6only(slisten);
+ }
+#endif
+
+ for (;;) {
+ if (socket_bind(slisten, e) == 0) {
+ if (sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+ sock_address_strfamily(e), uaddr, sock_address_get_port(e));
+ goto listen;
+ }
+ socket_close(slisten);
+ try_next = to && (sock_address_get_port(e) <= to + port_offset);
+ if (!try_next || sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
+ sock_address_strfamily(e), uaddr, sock_address_get_port(e),
+ strerror(errno));
+ if (try_next) {
+ sock_address_set_port(e, sock_address_get_port(e) + 1);
+ continue;
+ }
+ break;
+ }
+ }
+ sock_address_list_free(list);
+ fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
+ return -1;
+
+listen:
+ if (socket_listen(slisten,1) != 0) {
+ perror("listen");
+ socket_close(slisten);
+ return -1;
+ }
+ if (ostr) {
+ if (flags & SOCKET_LIST_FORCE_IN6) {
+ snprintf(ostr, olen, "[%s]:%d%s", uaddr,
+ sock_address_get_port(e) - port_offset, opts);
+ } else {
+ snprintf(ostr, olen, "%s:%d%s", uaddr,
+ sock_address_get_port(e) - port_offset, opts);
+ }
+ }
+ sock_address_list_free(list);
+ return slisten;
+}
+
+int inet_connect(const char *str, SocketType socktype)
+{
+ SockAddress** list;
+ SockAddress* e;
+ unsigned flags = 0;
+ char addr[64];
+ char port[33];
+ int sock, nn;
+
+ /* 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;
+ }
+ flags |= SOCKET_LIST_FORCE_IN6;
+ } else if (qemu_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;
+ }
+ flags |= SOCKET_LIST_FORCE_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")) {
+ flags &= SOCKET_LIST_FORCE_IN6;
+ flags |= SOCKET_LIST_FORCE_INET;
+ }
+ if (strstr(str, ",ipv6")) {
+ flags &= SOCKET_LIST_FORCE_INET;
+ flags |= SOCKET_LIST_FORCE_IN6;
+ }
+
+ /* lookup */
+ list = sock_address_list_create(addr, port, flags);
+ if (list == NULL) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n",
+ addr, port, errno_str);
+ return -1;
+ }
+
+ for (nn = 0; list[nn] != NULL; nn++) {
+ e = list[nn];
+ sock = socket_create(sock_address_get_family(e), socktype);
+ if (sock < 0) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ sock_address_strfamily(e), errno_str);
+ continue;
+ }
+ socket_set_xreuseaddr(sock);
+
+ /* connect to peer */
+ if (socket_connect(sock,e) < 0) {
+ if (sockets_debug)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+ sock_address_strfamily(e),
+ sock_address_to_string(e), addr, port, strerror(errno));
+ socket_close(sock);
+ continue;
+ }
+ if (sockets_debug)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
+ sock_address_strfamily(e),
+ sock_address_to_string(e), addr, port);
+
+ goto EXIT;
+ }
+ sock = -1;
+EXIT:
+ sock_address_list_free(list);
+ return sock;
+}
+
+#ifndef _WIN32
+
+int unix_listen(const char *str, char *ostr, int olen)
+{
+ SockAddress un;
+ char unpath[PATH_MAX];
+ char *path, *upath, *opts;
+ int sock, fd, len;
+
+ opts = strchr(str, ',');
+ if (opts) {
+ len = opts - str;
+ path = qemu_malloc(len+1);
+ snprintf(path, len+1, "%.*s", len, str);
+ } else
+ path = qemu_strdup(str);
+
+ if (path || strlen(path) > 0) {
+ upath = path;
+ } else {
+ char *tmpdir = getenv("TMPDIR");
+ snprintf(unpath, sizeof(unpath), "%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(unpath); close(fd);
+ upath = unpath;
+ }
+ snprintf(ostr, olen, "%s%s", path, opts ? opts : "");
+
+ sock = socket_unix_server(upath, SOCKET_STREAM);
+ sock_address_done(&un);
+
+ if (sock < 0) {
+ fprintf(stderr, "bind(unix:%s): %s\n", upath, errno_str);
+ goto err;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "bind(unix:%s): OK\n", upath);
+
+ qemu_free(path);
+ return sock;
+
+err:
+ qemu_free(path);
+ socket_close(sock);
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ SockAddress un;
+ int ret, sock;
+
+ sock = socket_create_unix(SOCKET_STREAM);
+ if (sock < 0) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ sock_address_init_unix(&un, path);
+ ret = socket_connect(sock, &un);
+ sock_address_done(&un);
+ if (ret < 0) {
+ fprintf(stderr, "connect(unix:%s): %s\n", path, errno_str);
+ 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