X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fsocket-util.c;h=12bbc716d91dcff972d06a2472be1dd949406c36;hb=a328a943f173391cd9a9a54e257c8dabcd463402;hp=8d291edb4c2e56e42a6e99a2aa6898379fb92e5e;hpb=e61c056cb94aeffc86cb314ea32726be9b666724;p=sliver-openvswitch.git diff --git a/lib/socket-util.c b/lib/socket-util.c index 8d291edb4..12bbc716d 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,10 @@ VLOG_DEFINE_THIS_MODULE(socket_util); #define LINUX 0 #endif +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif + /* Sets 'fd' to non-blocking mode. Returns 0 if successful, otherwise a * positive errno value. */ int @@ -110,13 +114,27 @@ int lookup_ip(const char *host_name, struct in_addr *addr) { if (!inet_aton(host_name, addr)) { - struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name); return ENOENT; } return 0; } +/* Translates 'host_name', which must be a string representation of an IPv6 + * address, into a numeric IPv6 address in '*addr'. Returns 0 if successful, + * otherwise a positive errno value. */ +int +lookup_ipv6(const char *host_name, struct in6_addr *addr) +{ + if (inet_pton(AF_INET6, host_name, addr) != 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_ERR_RL(&rl, "\"%s\" is not a valid IPv6 address", host_name); + return ENOENT; + } + return 0; +} + /* Returns the error condition associated with socket 'fd' and resets the * socket's error status. */ int @@ -125,7 +143,7 @@ get_socket_error(int fd) int error; socklen_t len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10); error = errno; VLOG_ERR_RL(&rl, "getsockopt(SO_ERROR): %s", strerror(error)); } @@ -212,15 +230,78 @@ drain_fd(int fd, size_t n_packets) /* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in * '*un_len' the size of the sockaddr_un. */ static void -make_sockaddr_un(const char *name, struct sockaddr_un* un, socklen_t *un_len) +make_sockaddr_un__(const char *name, struct sockaddr_un *un, socklen_t *un_len) { un->sun_family = AF_UNIX; - strncpy(un->sun_path, name, sizeof un->sun_path); - un->sun_path[sizeof un->sun_path - 1] = '\0'; + ovs_strzcpy(un->sun_path, name, sizeof un->sun_path); *un_len = (offsetof(struct sockaddr_un, sun_path) + strlen (un->sun_path) + 1); } +/* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in + * '*un_len' the size of the sockaddr_un. + * + * Returns 0 on success, otherwise a positive errno value. On success, + * '*dirfdp' is either -1 or a nonnegative file descriptor that the caller + * should close after using '*un' to bind or connect. On failure, '*dirfdp' is + * -1. */ +static int +make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len, + int *dirfdp) +{ + enum { MAX_UN_LEN = sizeof un->sun_path - 1 }; + + *dirfdp = -1; + if (strlen(name) > MAX_UN_LEN) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + if (LINUX) { + /* 'name' is too long to fit in a sockaddr_un, but we have a + * workaround for that on Linux: shorten it by opening a file + * descriptor for the directory part of the name and indirecting + * through /proc/self/fd//. */ + char *dir, *base; + char *short_name; + int dirfd; + + dir = dir_name(name); + base = base_name(name); + + dirfd = open(dir, O_DIRECTORY | O_RDONLY); + if (dirfd < 0) { + free(base); + free(dir); + return errno; + } + + short_name = xasprintf("/proc/self/fd/%d/%s", dirfd, base); + free(dir); + free(base); + + if (strlen(short_name) <= MAX_UN_LEN) { + make_sockaddr_un__(short_name, un, un_len); + free(short_name); + *dirfdp = dirfd; + return 0; + } + free(short_name); + close(dirfd); + + VLOG_WARN_RL(&rl, "Unix socket name %s is longer than maximum " + "%d bytes (even shortened)", name, MAX_UN_LEN); + } else { + /* 'name' is too long and we have no workaround. */ + VLOG_WARN_RL(&rl, "Unix socket name %s is longer than maximum " + "%d bytes", name, MAX_UN_LEN); + } + + return ENAMETOOLONG; + } else { + make_sockaddr_un__(name, un, un_len); + return 0; + } +} + /* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or * SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and * connected to '*connect_path' (if 'connect_path' is non-null). If 'nonblock' @@ -247,9 +328,11 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, if (nonblock) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { + error = errno; goto error; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + error = errno; goto error; } } @@ -257,13 +340,22 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, if (bind_path) { struct sockaddr_un un; socklen_t un_len; - make_sockaddr_un(bind_path, &un, &un_len); - if (unlink(un.sun_path) && errno != ENOENT) { - VLOG_WARN("unlinking \"%s\": %s\n", un.sun_path, strerror(errno)); + int dirfd; + + if (unlink(bind_path) && errno != ENOENT) { + VLOG_WARN("unlinking \"%s\": %s\n", bind_path, strerror(errno)); } fatal_signal_add_file_to_unlink(bind_path); - if (bind(fd, (struct sockaddr*) &un, un_len) - || fchmod(fd, S_IRWXU)) { + + error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd); + if (!error && (bind(fd, (struct sockaddr*) &un, un_len) + || fchmod(fd, S_IRWXU))) { + error = errno; + } + if (dirfd >= 0) { + close(dirfd); + } + if (error) { goto error; } } @@ -271,9 +363,18 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, if (connect_path) { struct sockaddr_un un; socklen_t un_len; - make_sockaddr_un(connect_path, &un, &un_len); - if (connect(fd, (struct sockaddr*) &un, un_len) + int dirfd; + + error = make_sockaddr_un(connect_path, &un, &un_len, &dirfd); + if (!error + && connect(fd, (struct sockaddr*) &un, un_len) && errno != EINPROGRESS) { + error = errno; + } + if (dirfd >= 0) { + close(dirfd); + } + if (error) { goto error; } } @@ -282,6 +383,7 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, if (passcred) { int enable = 1; if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) { + error = errno; goto error; } } @@ -290,7 +392,9 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, return fd; error: - error = errno == EAGAIN ? EPROTO : errno; + if (error == EAGAIN) { + error = EPROTO; + } if (bind_path) { fatal_signal_remove_file_to_unlink(bind_path); } @@ -663,3 +767,10 @@ get_mtime(const char *file_name, struct timespec *mtime) } } +void +xpipe(int fds[2]) +{ + if (pipe(fds)) { + VLOG_FATAL("failed to create pipe (%s)", strerror(errno)); + } +}