* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
+#include "qemu/osdep.h"
#include "monitor/monitor.h"
+#include "qapi/error.h"
#include "qemu/sockets.h"
#include "qemu/main-loop.h"
#include "qapi/qmp-input-visitor.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi-visit.h"
+#include "qemu/cutils.h"
#ifndef AI_ADDRCONFIG
# define AI_ADDRCONFIG 0
#endif
+
#ifndef AI_V4MAPPED
# define AI_V4MAPPED 0
#endif
* t f PF_INET
* t t PF_INET6
*
- * NB, this matrix is only about getting the neccessary results
+ * NB, this matrix is only about getting the necessary results
* from getaddrinfo(). Some of the cases require further work
* after reading results from getaddrinfo in order to fully
* apply the logic the end user wants. eg with the last case
do {
rc = qemu_getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize);
- } while (rc == -1 && socket_error() == EINTR);
+ } while (rc == -1 && errno == EINTR);
/* update rc to contain error */
if (!rc && val) {
do {
rc = 0;
if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) {
- rc = -socket_error();
+ rc = -errno;
}
} while (rc == -EINTR);
struct addrinfo ai, *res;
int rc;
Error *err = NULL;
+ static int useV4Mapped = 1;
memset(&ai, 0, sizeof(ai));
- ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG;
+ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+ if (atomic_read(&useV4Mapped)) {
+ ai.ai_flags |= AI_V4MAPPED;
+ }
ai.ai_family = inet_ai_family_from_address(saddr, &err);
ai.ai_socktype = SOCK_STREAM;
/* lookup */
rc = getaddrinfo(saddr->host, saddr->port, &ai, &res);
+
+ /* At least FreeBSD and OS-X 10.6 declare AI_V4MAPPED but
+ * then don't implement it in their getaddrinfo(). Detect
+ * this and retry without the flag since that's preferrable
+ * to a fatal error
+ */
+ if (rc == EAI_BADFLAGS &&
+ (ai.ai_flags & AI_V4MAPPED)) {
+ atomic_set(&useV4Mapped, 0);
+ ai.ai_flags &= ~AI_V4MAPPED;
+ rc = getaddrinfo(saddr->host, saddr->port, &ai, &res);
+ }
if (rc != 0) {
error_setg(errp, "address resolution failed for %s:%s: %s",
saddr->host, saddr->port, gai_strerror(rc));
if (err) {
error_propagate(errp, err);
- return -1;
+ goto err;
}
addr = sraddr->host;
}
if (port == NULL || strlen(port) == 0) {
error_setg(errp, "remote port not specified");
- return -1;
+ goto err;
}
if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) {
error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
gai_strerror(rc));
- return -1;
+ goto err;
}
/* lookup local addr */
do {
rc = 0;
if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) {
- rc = -socket_error();
+ rc = -errno;
}
} while (rc == -EINTR);
goto fail;
} else {
addr->type = SOCKET_ADDRESS_KIND_UNIX;
- addr->u.q_unix = g_new(UnixSocketAddress, 1);
- addr->u.q_unix->path = g_strdup(str + 5);
+ addr->u.q_unix.data = g_new(UnixSocketAddress, 1);
+ addr->u.q_unix.data->path = g_strdup(str + 5);
}
} else if (strstart(str, "fd:", NULL)) {
if (str[3] == '\0') {
goto fail;
} else {
addr->type = SOCKET_ADDRESS_KIND_FD;
- addr->u.fd = g_new(String, 1);
- addr->u.fd->str = g_strdup(str + 3);
+ addr->u.fd.data = g_new(String, 1);
+ addr->u.fd.data->str = g_strdup(str + 3);
}
} else {
addr->type = SOCKET_ADDRESS_KIND_INET;
- addr->u.inet = inet_parse(str, errp);
- if (addr->u.inet == NULL) {
+ addr->u.inet.data = inet_parse(str, errp);
+ if (addr->u.inet.data == NULL) {
goto fail;
}
}
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
- fd = inet_connect_saddr(addr->u.inet, errp, callback, opaque);
+ fd = inet_connect_saddr(addr->u.inet.data, errp, callback, opaque);
break;
case SOCKET_ADDRESS_KIND_UNIX:
- fd = unix_connect_saddr(addr->u.q_unix, errp, callback, opaque);
+ fd = unix_connect_saddr(addr->u.q_unix.data, errp, callback, opaque);
break;
case SOCKET_ADDRESS_KIND_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp);
+ fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp);
if (fd >= 0 && callback) {
qemu_set_nonblock(fd);
callback(fd, NULL, opaque);
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
- fd = inet_listen_saddr(addr->u.inet, 0, false, errp);
+ fd = inet_listen_saddr(addr->u.inet.data, 0, false, errp);
break;
case SOCKET_ADDRESS_KIND_UNIX:
- fd = unix_listen_saddr(addr->u.q_unix, false, errp);
+ fd = unix_listen_saddr(addr->u.q_unix.data, false, errp);
break;
case SOCKET_ADDRESS_KIND_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp);
+ fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp);
break;
default:
return fd;
}
+void socket_listen_cleanup(int fd, Error **errp)
+{
+ SocketAddress *addr;
+
+ addr = socket_local_address(fd, errp);
+
+ if (addr->type == SOCKET_ADDRESS_KIND_UNIX
+ && addr->u.q_unix.data->path) {
+ if (unlink(addr->u.q_unix.data->path) < 0 && errno != ENOENT) {
+ error_setg_errno(errp, errno,
+ "Failed to unlink socket %s",
+ addr->u.q_unix.data->path);
+ }
+ }
+
+ g_free(addr);
+}
+
int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
{
int fd;
switch (remote->type) {
case SOCKET_ADDRESS_KIND_INET:
- fd = inet_dgram_saddr(remote->u.inet, local ? local->u.inet : NULL, errp);
+ fd = inet_dgram_saddr(remote->u.inet.data,
+ local ? local->u.inet.data : NULL, errp);
break;
default:
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
SocketAddress *addr;
+ InetSocketAddress *inet;
int ret;
ret = getnameinfo((struct sockaddr *)sa, salen,
addr = g_new0(SocketAddress, 1);
addr->type = SOCKET_ADDRESS_KIND_INET;
- addr->u.inet = g_new0(InetSocketAddress, 1);
- addr->u.inet->host = g_strdup(host);
- addr->u.inet->port = g_strdup(serv);
+ inet = addr->u.inet.data = g_new0(InetSocketAddress, 1);
+ inet->host = g_strdup(host);
+ inet->port = g_strdup(serv);
if (sa->ss_family == AF_INET) {
- addr->u.inet->has_ipv4 = addr->u.inet->ipv4 = true;
+ inet->has_ipv4 = inet->ipv4 = true;
} else {
- addr->u.inet->has_ipv6 = addr->u.inet->ipv6 = true;
+ inet->has_ipv6 = inet->ipv6 = true;
}
return addr;
addr = g_new0(SocketAddress, 1);
addr->type = SOCKET_ADDRESS_KIND_UNIX;
- addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+ addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
if (su->sun_path[0]) {
- addr->u.q_unix->path = g_strndup(su->sun_path,
- sizeof(su->sun_path));
+ addr->u.q_unix.data->path = g_strndup(su->sun_path,
+ sizeof(su->sun_path));
}
return addr;
socklen_t sslen = sizeof(ss);
if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) {
- error_setg_errno(errp, socket_error(), "%s",
+ error_setg_errno(errp, errno, "%s",
"Unable to query local socket address");
return NULL;
}
socklen_t sslen = sizeof(ss);
if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) {
- error_setg_errno(errp, socket_error(), "%s",
+ error_setg_errno(errp, errno, "%s",
"Unable to query remote socket address");
return NULL;
}
void qapi_copy_SocketAddress(SocketAddress **p_dest,
SocketAddress *src)
{
- QmpOutputVisitor *qov;
- QmpInputVisitor *qiv;
Visitor *ov, *iv;
QObject *obj;
*p_dest = NULL;
- qov = qmp_output_visitor_new();
- ov = qmp_output_get_visitor(qov);
- visit_type_SocketAddress(ov, &src, NULL, &error_abort);
- obj = qmp_output_get_qobject(qov);
- qmp_output_visitor_cleanup(qov);
+ ov = qmp_output_visitor_new(&obj);
+ visit_type_SocketAddress(ov, NULL, &src, &error_abort);
+ visit_complete(ov, &obj);
+ visit_free(ov);
if (!obj) {
return;
}
- qiv = qmp_input_visitor_new(obj);
- iv = qmp_input_get_visitor(qiv);
- visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
- qmp_input_visitor_cleanup(qiv);
+ iv = qmp_input_visitor_new(obj, true);
+ visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
+ visit_free(iv);
qobject_decref(obj);
}
+
+char *socket_address_to_string(struct SocketAddress *addr, Error **errp)
+{
+ char *buf;
+ InetSocketAddress *inet;
+ char host_port[INET6_ADDRSTRLEN + 5 + 4];
+
+ switch (addr->type) {
+ case SOCKET_ADDRESS_KIND_INET:
+ inet = addr->u.inet.data;
+ if (strchr(inet->host, ':') == NULL) {
+ snprintf(host_port, sizeof(host_port), "%s:%s", inet->host,
+ inet->port);
+ buf = g_strdup(host_port);
+ } else {
+ snprintf(host_port, sizeof(host_port), "[%s]:%s", inet->host,
+ inet->port);
+ buf = g_strdup(host_port);
+ }
+ break;
+
+ case SOCKET_ADDRESS_KIND_UNIX:
+ buf = g_strdup(addr->u.q_unix.data->path);
+ break;
+
+ case SOCKET_ADDRESS_KIND_FD:
+ buf = g_strdup(addr->u.fd.data->str);
+ break;
+
+ default:
+ error_setg(errp, "socket family %d unsupported",
+ addr->type);
+ return NULL;
+ }
+ return buf;
+}