summaryrefslogtreecommitdiff
path: root/socket-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'socket-util.c')
-rw-r--r--socket-util.c84
1 files changed, 51 insertions, 33 deletions
diff --git a/socket-util.c b/socket-util.c
index 407c9617b0..525c33474d 100644
--- a/socket-util.c
+++ b/socket-util.c
@@ -7,12 +7,13 @@
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
+#include <net/if.h>
#include "macro.h"
#include "util.h"
#include "socket-util.h"
-int address_parse(Address *a, const char *s) {
+int socket_address_parse(SocketAddress *a, const char *s) {
int r;
char *e, *n;
unsigned u;
@@ -21,6 +22,7 @@ int address_parse(Address *a, const char *s) {
assert(s);
memset(a, 0, sizeof(*a));
+ a->type = SOCK_STREAM;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
@@ -53,7 +55,6 @@ int address_parse(Address *a, const char *s) {
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->size = sizeof(struct sockaddr_in6);
- a->bind_ipv6_only = true;
} else if (*s == '/') {
/* AF_UNIX socket */
@@ -83,29 +84,46 @@ int address_parse(Address *a, const char *s) {
} else {
if ((e = strchr(s, ':'))) {
+ int r;
+
+ if ((r = safe_atou(e+1, &u)) < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
- /* IPv4 in w.x.y.z:p notation */
if (!(n = strndup(s, e-s)))
return -ENOMEM;
- errno = 0;
- if (inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr) <= 0) {
+ /* IPv4 in w.x.y.z:p notation? */
+ if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
free(n);
- return errno != 0 ? -errno : -EINVAL;
+ return -errno;
}
- free(n);
+ if (r > 0) {
+ /* Gotcha, it's a traditional IPv4 address */
+ free(n);
- e++;
- if ((r = safe_atou(e, &u)) < 0)
- return r;
+ a->sockaddr.in4.sin_family = AF_INET;
+ a->sockaddr.in4.sin_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in);
+ } else {
+ unsigned idx;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
+ /* Uh, our last resort, an interface name */
+ idx = if_nametoindex(n);
+ free(n);
+
+ if (n == 0)
+ return -EINVAL;
- a->sockaddr.in4.sin_family = AF_INET;
- a->sockaddr.in4.sin_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in);
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_scope_id = idx;
+ memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
+ a->size = sizeof(struct sockaddr_in6);
+ }
} else {
/* Just a port */
@@ -116,8 +134,8 @@ int address_parse(Address *a, const char *s) {
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
- memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
a->size = sizeof(struct sockaddr_in6);
}
}
@@ -125,10 +143,10 @@ int address_parse(Address *a, const char *s) {
return 0;
}
-int address_verify(const Address *a) {
+int socket_address_verify(const SocketAddress *a) {
assert(a);
- switch (address_family(a)) {
+ switch (socket_address_family(a)) {
case AF_INET:
if (a->size != sizeof(struct sockaddr_in))
return -EINVAL;
@@ -176,15 +194,15 @@ int address_verify(const Address *a) {
}
}
-int address_print(const Address *a, char **p) {
+int socket_address_print(const SocketAddress *a, char **p) {
int r;
assert(a);
assert(p);
- if ((r = address_verify(a)) < 0)
+ if ((r = socket_address_verify(a)) < 0)
return r;
- switch (address_family(a)) {
+ switch (socket_address_family(a)) {
case AF_INET: {
char *ret;
@@ -256,16 +274,25 @@ int address_print(const Address *a, char **p) {
}
}
-int address_listen(const Address *a, int backlog) {
+int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only) {
int r, fd;
assert(a);
- if ((r = address_verify(a)) < 0)
+ if ((r = socket_address_verify(a)) < 0)
return r;
- if ((fd = socket(address_family(a), a->type, 0)) < 0)
+ if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
return -errno;
+ if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
+ int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
+ close_nointr(fd);
+ return -errno;
+ }
+ }
+
if (bind(fd, &a->sockaddr.sa, a->size) < 0) {
close_nointr(fd);
return -errno;
@@ -277,14 +304,5 @@ int address_listen(const Address *a, int backlog) {
return -errno;
}
- if (address_family(a) == AF_INET6) {
- int flag = a->bind_ipv6_only;
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
- close_nointr(fd);
- return -errno;
- }
- }
-
return 0;
}