#include "system/filesys.h"
#include "lib/socket/socket.h"
#include "system/network.h"
+#include "lib/util/util_net.h"
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type);
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type);
static NTSTATUS ipv4_init(struct socket_context *sock)
{
sock->fd = socket(PF_INET, type, 0);
if (sock->fd == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
+ smb_set_close_on_exec(sock->fd);
+
sock->backend_name = "ipv4";
sock->family = AF_INET;
static void ip_close(struct socket_context *sock)
{
- close(sock->fd);
+ if (sock->fd != -1) {
+ close(sock->fd);
+ sock->fd = -1;
+ }
}
static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
for non-blocking connect */
ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
if (error != 0) {
- return map_nt_error_from_unix(error);
+ return map_nt_error_from_unix_common(error);
}
if (!(flags & SOCKET_FLAG_BLOCK)) {
ret = set_blocking(sock->fd, false);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
if (my_address && my_address->sockaddr) {
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
} else if (my_address) {
my_ip = interpret_addr2(my_address->addr);
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
}
if (srv_address->sockaddr) {
ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
} else {
srv_ip = interpret_addr2(srv_address->addr);
ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
}
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
if (sock->type == SOCKET_TYPE_STREAM) {
ret = listen(sock->fd, queue_size);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
if (!(flags & SOCKET_FLAG_BLOCK)) {
ret = set_blocking(sock->fd, false);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
if (new_fd == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
int ret = set_blocking(new_fd, false);
if (ret == -1) {
close(new_fd);
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
if (gotlen == 0) {
return NT_STATUS_END_OF_FILE;
} else if (gotlen == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
*nread = gotlen;
return NT_STATUS_END_OF_FILE;
} else if (gotlen == -1) {
talloc_free(src);
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
src->sockaddrlen = from_len;
len = send(sock->fd, blob->data, blob->length, 0);
if (len == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
*sendlen = len;
(struct sockaddr *)&srv_addr, sizeof(srv_addr));
}
if (len == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
*sendlen = len;
*npending = value;
return NT_STATUS_OK;
}
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
static const struct socket_ops ipv4_ops = {
static struct in6_addr interpret_addr6(const char *name)
{
- struct hostent *he;
-
- if (name == NULL) return in6addr_any;
+ char addr[INET6_ADDRSTRLEN];
+ struct in6_addr dest6;
+ const char *sp = name;
+ char *p;
+ int ret;
+
+ if (sp == NULL) return in6addr_any;
- if (strcasecmp(name, "localhost") == 0) {
- name = "::1";
+ p = strchr_m(sp, '%');
+
+ if (strcasecmp(sp, "localhost") == 0) {
+ sp = "::1";
}
- he = gethostbyname2(name, PF_INET6);
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+
+ if (p && (p > sp) && (if_nametoindex(p+1) != 0)) {
+ strlcpy(addr, sp,
+ MIN(PTR_DIFF(p,sp)+1,
+ sizeof(addr)));
+ sp = addr;
+ }
- if (he == NULL) return in6addr_any;
+ ret = inet_pton(AF_INET6, sp, &dest6);
+ if (ret > 0) {
+ return dest6;
+ }
- return *((struct in6_addr *)he->h_addr);
+ return in6addr_any;
}
static NTSTATUS ipv6_init(struct socket_context *sock)
sock->fd = socket(PF_INET6, type, 0);
if (sock->fd == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
+ smb_set_close_on_exec(sock->fd);
+
sock->backend_name = "ipv6";
sock->family = AF_INET6;
if (my_address && my_address->sockaddr) {
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
} else if (my_address) {
struct in6_addr my_ip;
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
}
ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
}
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
return ip_connect_complete(sock, flags);
}
+/*
+ fix the sin6_scope_id based on the address interface
+ */
+static void fix_scope_id(struct sockaddr_in6 *in6,
+ const char *address)
+{
+ const char *p = strchr(address, '%');
+ if (p != NULL) {
+ in6->sin6_scope_id = if_nametoindex(p+1);
+ }
+}
+
+
static NTSTATUS ipv6_listen(struct socket_context *sock,
- const struct socket_address *my_address,
- int queue_size, uint32_t flags)
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
{
struct sockaddr_in6 my_addr;
struct in6_addr ip_addr;
if (my_address->sockaddr) {
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
} else {
+ int one = 1;
ip_addr = interpret_addr6(my_address->addr);
ZERO_STRUCT(my_addr);
my_addr.sin6_addr = ip_addr;
my_addr.sin6_port = htons(my_address->port);
my_addr.sin6_family = PF_INET6;
-
- ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ fix_scope_id(&my_addr, my_address->addr);
+
+ /* when binding on ipv6 we always want to only bind on v6 */
+ ret = setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *)&one, sizeof(one));
+ if (ret != -1) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
}
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
if (sock->type == SOCKET_TYPE_STREAM) {
ret = listen(sock->fd, queue_size);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
if (!(flags & SOCKET_FLAG_BLOCK)) {
ret = set_blocking(sock->fd, false);
if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
{
- struct sockaddr_in cli_addr;
+ struct sockaddr_in6 cli_addr;
socklen_t cli_addr_len = sizeof(cli_addr);
int new_fd;
new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
if (new_fd == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
int ret = set_blocking(new_fd, false);
if (ret == -1) {
close(new_fd);
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
}
return NT_STATUS_END_OF_FILE;
} else if (gotlen == -1) {
talloc_free(src);
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
src->sockaddrlen = from_len;
(struct sockaddr *)&srv_addr, sizeof(srv_addr));
}
if (len == -1) {
- return map_nt_error_from_unix(errno);
+ return map_nt_error_from_unix_common(errno);
}
*sendlen = len;