summaryrefslogtreecommitdiff
path: root/source4/lib/socket
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/socket')
-rw-r--r--source4/lib/socket/access.c361
-rw-r--r--source4/lib/socket/config.m419
-rw-r--r--source4/lib/socket/config.mk43
-rw-r--r--source4/lib/socket/connect.c216
-rw-r--r--source4/lib/socket/connect_multi.c282
-rw-r--r--source4/lib/socket/interface.c335
-rw-r--r--source4/lib/socket/netif.c126
-rw-r--r--source4/lib/socket/netif.h36
-rw-r--r--source4/lib/socket/socket.c554
-rw-r--r--source4/lib/socket/socket.h211
-rw-r--r--source4/lib/socket/socket_ip.c985
-rw-r--r--source4/lib/socket/socket_unix.c420
-rw-r--r--source4/lib/socket/testsuite.c198
13 files changed, 3786 insertions, 0 deletions
diff --git a/source4/lib/socket/access.c b/source4/lib/socket/access.c
new file mode 100644
index 0000000000..42c42db365
--- /dev/null
+++ b/source4/lib/socket/access.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ check access rules for socket connections
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ This module is an adaption of code from the tcpd-1.4 package written
+ by Wietse Venema, Eindhoven University of Technology, The Netherlands.
+
+ The code is used here with permission.
+
+ The code has been considerably changed from the original. Bug reports
+ should be sent to samba@samba.org
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "system/locale.h"
+
+#define FAIL (-1)
+#define ALLONES ((uint32_t)0xFFFFFFFF)
+
+/* masked_match - match address against netnumber/netmask */
+static bool masked_match(TALLOC_CTX *mem_ctx, const char *tok, const char *slash, const char *s)
+{
+ uint32_t net;
+ uint32_t mask;
+ uint32_t addr;
+ char *tok_cpy;
+
+ if ((addr = interpret_addr(s)) == INADDR_NONE)
+ return false;
+
+ tok_cpy = talloc_strdup(mem_ctx, tok);
+ tok_cpy[PTR_DIFF(slash,tok)] = '\0';
+ net = interpret_addr(tok_cpy);
+ talloc_free(tok_cpy);
+
+ if (strlen(slash + 1) > 2) {
+ mask = interpret_addr(slash + 1);
+ } else {
+ mask = (uint32_t)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
+ /* convert to network byte order */
+ mask = htonl(mask);
+ }
+
+ if (net == INADDR_NONE || mask == INADDR_NONE) {
+ DEBUG(0,("access: bad net/mask access control: %s\n", tok));
+ return false;
+ }
+
+ return (addr & mask) == (net & mask);
+}
+
+/* string_match - match string against token */
+static bool string_match(TALLOC_CTX *mem_ctx, const char *tok,const char *s, char *invalid_char)
+{
+ size_t tok_len;
+ size_t str_len;
+ const char *cut;
+
+ *invalid_char = '\0';
+
+ /* Return true if a token has the magic value "ALL". Return
+ * FAIL if the token is "FAIL". If the token starts with a "."
+ * (domain name), return true if it matches the last fields of
+ * the string. If the token has the magic value "LOCAL",
+ * return true if the string does not contain a "."
+ * character. If the token ends on a "." (network number),
+ * return true if it matches the first fields of the
+ * string. If the token begins with a "@" (netgroup name),
+ * return true if the string is a (host) member of the
+ * netgroup. Return true if the token fully matches the
+ * string. If the token is a netnumber/netmask pair, return
+ * true if the address is a member of the specified subnet.
+ */
+
+ if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(s)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, s + str_len - tok_len)==0) {
+ return true;
+ }
+ } else if (tok[0] == '@') { /* netgroup: look it up */
+ DEBUG(0,("access: netgroup support is not available\n"));
+ return false;
+ } else if (strcmp(tok, "ALL")==0) { /* all: match any */
+ return true;
+ } else if (strcmp(tok, "FAIL")==0) { /* fail: match any */
+ return FAIL;
+ } else if (strcmp(tok, "LOCAL")==0) { /* local: no dots */
+ if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) {
+ return true;
+ }
+ } else if (strcasecmp(tok, s)==0) { /* match host name or address */
+ return true;
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */
+ if (strncmp(tok, s, tok_len) == 0)
+ return true;
+ } else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */
+ if (isdigit((int)s[0]) && masked_match(mem_ctx, tok, cut, s))
+ return true;
+ } else if (strchr(tok, '*') != 0) {
+ *invalid_char = '*';
+ } else if (strchr(tok, '?') != 0) {
+ *invalid_char = '?';
+ }
+ return false;
+}
+
+struct client_addr {
+ const char *cname;
+ const char *caddr;
+};
+
+/* client_match - match host name and address against token */
+static bool client_match(TALLOC_CTX *mem_ctx, const char *tok, struct client_addr *client)
+{
+ bool match;
+ char invalid_char = '\0';
+
+ /*
+ * Try to match the address first. If that fails, try to match the host
+ * name if available.
+ */
+
+ if ((match = string_match(mem_ctx, tok, client->caddr, &invalid_char)) == 0) {
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+
+ if (client->cname[0] != 0)
+ match = string_match(mem_ctx, tok, client->cname, &invalid_char);
+
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+ }
+
+ return (match);
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+static bool list_match(TALLOC_CTX *mem_ctx, const char **list, struct client_addr *client)
+{
+ bool match = false;
+
+ if (!list)
+ return false;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (; *list ; list++) {
+ if (strcmp(*list, "EXCEPT")==0) /* EXCEPT: give up */
+ break;
+ if ((match = client_match(mem_ctx, *list, client))) /* true or FAIL */
+ break;
+ }
+
+ /* Process exceptions to true or FAIL matches. */
+ if (match != false) {
+ while (*list && strcmp(*list, "EXCEPT")!=0)
+ list++;
+
+ for (; *list; list++) {
+ if (client_match(mem_ctx, *list, client)) /* Exception Found */
+ return false;
+ }
+ }
+
+ return match;
+}
+
+/* return true if access should be allowed */
+static bool allow_access_internal(TALLOC_CTX *mem_ctx,
+ const char **deny_list,const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ struct client_addr client;
+
+ client.cname = cname;
+ client.caddr = caddr;
+
+ /* if it is loopback then always allow unless specifically denied */
+ if (strcmp(caddr, "127.0.0.1") == 0) {
+ /*
+ * If 127.0.0.1 matches both allow and deny then allow.
+ * Patch from Steve Langasek vorlon@netexpress.net.
+ */
+ if (deny_list &&
+ list_match(mem_ctx, deny_list, &client) &&
+ (!allow_list ||
+ !list_match(mem_ctx, allow_list, &client))) {
+ return false;
+ }
+ return true;
+ }
+
+ /* if theres no deny list and no allow list then allow access */
+ if ((!deny_list || *deny_list == 0) &&
+ (!allow_list || *allow_list == 0)) {
+ return true;
+ }
+
+ /* if there is an allow list but no deny list then allow only hosts
+ on the allow list */
+ if (!deny_list || *deny_list == 0)
+ return list_match(mem_ctx, allow_list, &client);
+
+ /* if theres a deny list but no allow list then allow
+ all hosts not on the deny list */
+ if (!allow_list || *allow_list == 0)
+ return !list_match(mem_ctx, deny_list, &client);
+
+ /* if there are both types of list then allow all hosts on the
+ allow list */
+ if (list_match(mem_ctx, allow_list, &client))
+ return true;
+
+ /* if there are both types of list and it's not on the allow then
+ allow it if its not on the deny */
+ if (list_match(mem_ctx, deny_list, &client))
+ return false;
+
+ return true;
+}
+
+/* return true if access should be allowed */
+bool allow_access(TALLOC_CTX *mem_ctx,
+ const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ bool ret;
+ char *nc_cname = talloc_strdup(mem_ctx, cname);
+ char *nc_caddr = talloc_strdup(mem_ctx, caddr);
+
+ if (!nc_cname || !nc_caddr) {
+ return false;
+ }
+
+ ret = allow_access_internal(mem_ctx, deny_list, allow_list, nc_cname, nc_caddr);
+
+ talloc_free(nc_cname);
+ talloc_free(nc_caddr);
+
+ return ret;
+}
+
+/* return true if the char* contains ip addrs only. Used to avoid
+gethostbyaddr() calls */
+
+static bool only_ipaddrs_in_list(const char** list)
+{
+ bool only_ip = true;
+
+ if (!list)
+ return true;
+
+ for (; *list ; list++) {
+ /* factor out the special strings */
+ if (strcmp(*list, "ALL")==0 ||
+ strcmp(*list, "FAIL")==0 ||
+ strcmp(*list, "EXCEPT")==0) {
+ continue;
+ }
+
+ if (!is_ipaddress(*list)) {
+ /*
+ * if we failed, make sure that it was not because the token
+ * was a network/netmask pair. Only network/netmask pairs
+ * have a '/' in them
+ */
+ if ((strchr(*list, '/')) == NULL) {
+ only_ip = false;
+ DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
+ break;
+ }
+ }
+ }
+
+ return only_ip;
+}
+
+/* return true if access should be allowed to a service for a socket */
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list)
+{
+ bool ret;
+ const char *name="";
+ struct socket_address *addr;
+ TALLOC_CTX *mem_ctx;
+
+ if ((!deny_list || *deny_list==0) &&
+ (!allow_list || *allow_list==0)) {
+ return true;
+ }
+
+ mem_ctx = talloc_init("socket_check_access");
+ if (!mem_ctx) {
+ return false;
+ }
+
+ addr = socket_get_peer_addr(sock, mem_ctx);
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+ if (!only_ipaddrs_in_list(allow_list) ||
+ !only_ipaddrs_in_list(deny_list)) {
+ name = socket_get_peer_name(sock, mem_ctx);
+ if (!name) {
+ name = addr->addr;
+ }
+ }
+
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ret = allow_access(mem_ctx, deny_list, allow_list, name, addr->addr);
+
+ if (ret) {
+ DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ } else {
+ DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/lib/socket/config.m4 b/source4/lib/socket/config.m4
new file mode 100644
index 0000000000..9c0072dd8b
--- /dev/null
+++ b/source4/lib/socket/config.m4
@@ -0,0 +1,19 @@
+AC_CHECK_FUNCS(writev)
+AC_CHECK_FUNCS(readv)
+AC_CHECK_FUNCS(gethostbyname2)
+
+############################################
+# check for unix domain sockets
+# done by AC_LIBREPLACE_NETWORK_CHECKS
+SMB_ENABLE(socket_unix, NO)
+if test x"$libreplace_cv_HAVE_UNIXSOCKET" = x"yes"; then
+ SMB_ENABLE(socket_unix, YES)
+fi
+
+############################################
+# check for ipv6
+# done by AC_LIBREPLACE_NETWORK_CHECKS
+SMB_ENABLE(socket_ipv6, NO)
+if test x"$libreplace_cv_HAVE_IPV6" = x"yes"; then
+ SMB_ENABLE(socket_ipv6, YES)
+fi
diff --git a/source4/lib/socket/config.mk b/source4/lib/socket/config.mk
new file mode 100644
index 0000000000..18aa806e41
--- /dev/null
+++ b/source4/lib/socket/config.mk
@@ -0,0 +1,43 @@
+##############################
+# Start SUBSYSTEM LIBNETIF
+[SUBSYSTEM::LIBNETIF]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBREPLACE_NETWORK
+# End SUBSYSTEM LIBNETIF
+##############################
+
+LIBNETIF_OBJ_FILES = $(addprefix $(libsocketsrcdir)/, interface.o netif.o)
+
+$(eval $(call proto_header_template,$(libsocketsrcdir)/netif_proto.h,$(LIBNETIF_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE socket_ip
+[MODULE::socket_ip]
+SUBSYSTEM = samba-socket
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = LIBSAMBA-ERRORS LIBREPLACE_NETWORK
+# End MODULE socket_ip
+################################################
+
+socket_ip_OBJ_FILES = $(libsocketsrcdir)/socket_ip.o
+
+################################################
+# Start MODULE socket_unix
+[MODULE::socket_unix]
+SUBSYSTEM = samba-socket
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = LIBREPLACE_NETWORK
+# End MODULE socket_unix
+################################################
+
+socket_unix_OBJ_FILES = $(libsocketsrcdir)/socket_unix.o
+
+################################################
+# Start SUBSYSTEM SOCKET
+[SUBSYSTEM::samba-socket]
+PUBLIC_DEPENDENCIES = LIBTALLOC
+PRIVATE_DEPENDENCIES = SOCKET_WRAPPER LIBCLI_COMPOSITE LIBCLI_RESOLVE
+# End SUBSYSTEM SOCKET
+################################################
+
+samba-socket_OBJ_FILES = $(addprefix $(libsocketsrcdir)/, socket.o access.o connect_multi.o connect.o)
+
diff --git a/source4/lib/socket/connect.c b/source4/lib/socket/connect.c
new file mode 100644
index 0000000000..773bf41873
--- /dev/null
+++ b/source4/lib/socket/connect.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implements a non-blocking connect operation that is aware of the samba4
+ events system
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+
+struct connect_state {
+ struct socket_context *sock;
+ const struct socket_address *my_address;
+ const struct socket_address *server_address;
+ uint32_t flags;
+};
+
+static void socket_connect_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *private);
+static void continue_resolve_name(struct composite_context *ctx);
+static void continue_socket_connect(struct composite_context *creq);
+
+/*
+ call the real socket_connect() call, and setup event handler
+*/
+static void socket_send_connect(struct composite_context *result)
+{
+ struct composite_context *creq;
+ struct fd_event *fde;
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ creq = talloc_zero(state, struct composite_context);
+ if (composite_nomem(creq, result)) return;
+ creq->state = COMPOSITE_STATE_IN_PROGRESS;
+ creq->event_ctx = result->event_ctx;
+ creq->async.fn = continue_socket_connect;
+ creq->async.private_data = result;
+
+ result->status = socket_connect(state->sock,
+ state->my_address,
+ state->server_address,
+ state->flags);
+ if (NT_STATUS_IS_ERR(result->status) &&
+ !NT_STATUS_EQUAL(result->status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(result, result->status);
+ return;
+ }
+
+ fde = event_add_fd(result->event_ctx, result,
+ socket_get_fd(state->sock),
+ EVENT_FD_READ|EVENT_FD_WRITE,
+ socket_connect_handler, result);
+ composite_nomem(fde, result);
+}
+
+
+/*
+ send a socket connect, potentially doing some name resolution first
+*/
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_state *state;
+
+ result = talloc_zero(sock, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = event_ctx;
+
+ state = talloc_zero(result, struct connect_state);
+ if (composite_nomem(state, result)) return result;
+ result->private_data = state;
+
+ state->sock = talloc_reference(state, sock);
+ if (composite_nomem(state->sock, result)) return result;
+
+ if (my_address) {
+ void *ref = talloc_reference(state, my_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->my_address = my_address;
+ }
+
+ {
+ void *ref = talloc_reference(state, server_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->server_address = server_address;
+ }
+
+ state->flags = flags;
+
+ set_blocking(socket_get_fd(sock), false);
+
+ if (resolve_ctx != NULL && server_address->addr && strcmp(sock->backend_name, "ipv4") == 0) {
+ struct nbt_name name;
+ struct composite_context *creq;
+ make_nbt_name_client(&name, server_address->addr);
+ creq = resolve_name_send(resolve_ctx, &name, result->event_ctx);
+ if (composite_nomem(creq, result)) return result;
+ composite_continue(result, creq, continue_resolve_name, result);
+ return result;
+ }
+
+ socket_send_connect(result);
+
+ return result;
+}
+
+/*
+ handle write events on connect completion
+*/
+static void socket_connect_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *private)
+{
+ struct composite_context *result =
+ talloc_get_type(private, struct composite_context);
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ result->status = socket_connect_complete(state->sock, state->flags);
+ if (!composite_is_ok(result)) return;
+
+ composite_done(result);
+}
+
+/*
+ recv name resolution reply then send the connect
+*/
+static void continue_resolve_name(struct composite_context *creq)
+{
+ struct composite_context *result = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct connect_state *state = talloc_get_type(result->private_data, struct connect_state);
+ const char *addr;
+
+ result->status = resolve_name_recv(creq, state, &addr);
+ if (!composite_is_ok(result)) return;
+
+ state->server_address = socket_address_from_strings(state, state->sock->backend_name,
+ addr, state->server_address->port);
+ if (composite_nomem(state->server_address, result)) return;
+
+ socket_send_connect(result);
+}
+
+/*
+ called when a connect has finished. Complete the top level composite context
+*/
+static void continue_socket_connect(struct composite_context *creq)
+{
+ struct composite_context *result = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ result->status = creq->status;
+ if (!composite_is_ok(result)) return;
+ composite_done(result);
+}
+
+
+/*
+ wait for a socket_connect_send() to finish
+*/
+NTSTATUS socket_connect_recv(struct composite_context *result)
+{
+ NTSTATUS status = composite_wait(result);
+ talloc_free(result);
+ return status;
+}
+
+
+/*
+ like socket_connect() but takes an event context, doing a semi-async connect
+*/
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags, struct resolve_context *resolve_ctx,
+ struct event_context *ev)
+{
+ struct composite_context *ctx;
+ ctx = socket_connect_send(sock, my_address,
+ server_address, flags, resolve_ctx, ev);
+ return socket_connect_recv(ctx);
+}
diff --git a/source4/lib/socket/connect_multi.c b/source4/lib/socket/connect_multi.c
new file mode 100644
index 0000000000..2f736a4b05
--- /dev/null
+++ b/source4/lib/socket/connect_multi.c
@@ -0,0 +1,282 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Fire connect requests to a host and a number of ports, with a timeout
+ between the connect request. Return if the first connect comes back
+ successfully or return the last error.
+
+ Copyright (C) Volker Lendecke 2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+#define MULTI_PORT_DELAY 2000 /* microseconds */
+
+/*
+ overall state
+*/
+struct connect_multi_state {
+ const char *server_address;
+ int num_ports;
+ uint16_t *ports;
+
+ struct resolve_context *resolve_ctx;
+
+ struct socket_context *sock;
+ uint16_t result_port;
+
+ int num_connects_sent, num_connects_recv;
+};
+
+/*
+ state of an individual socket_connect_send() call
+*/
+struct connect_one_state {
+ struct composite_context *result;
+ struct socket_context *sock;
+ struct socket_address *addr;
+};
+
+static void continue_resolve_name(struct composite_context *creq);
+static void connect_multi_timer(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *p);
+static void connect_multi_next_socket(struct composite_context *result);
+static void continue_one(struct composite_context *creq);
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_multi_state *multi;
+ int i;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = event_ctx;
+
+ multi = talloc_zero(result, struct connect_multi_state);
+ if (composite_nomem(multi, result)) goto failed;
+ result->private_data = multi;
+
+ multi->server_address = talloc_strdup(multi, server_address);
+ if (composite_nomem(multi->server_address, result)) goto failed;
+
+ multi->num_ports = num_server_ports;
+ multi->resolve_ctx = talloc_reference(multi, resolve_ctx);
+ multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
+ if (composite_nomem(multi->ports, result)) goto failed;
+
+ for (i=0; i<multi->num_ports; i++) {
+ multi->ports[i] = server_ports[i];
+ }
+
+ if (!is_ipaddress(server_address)) {
+ /*
+ we don't want to do the name resolution separately
+ for each port, so start it now, then only start on
+ the real sockets once we have an IP
+ */
+ struct nbt_name name;
+ struct composite_context *creq;
+ make_nbt_name_client(&name, server_address);
+ creq = resolve_name_send(resolve_ctx, &name, result->event_ctx);
+ if (composite_nomem(creq, result)) goto failed;
+ composite_continue(result, creq, continue_resolve_name, result);
+ return result;
+ }
+
+ /* now we've setup the state we can process the first socket */
+ connect_multi_next_socket(result);
+
+ if (!NT_STATUS_IS_OK(result->status)) {
+ goto failed;
+ }
+
+ return result;
+
+ failed:
+ composite_error(result, result->status);
+ return result;
+}
+
+/*
+ start connecting to the next socket/port in the list
+*/
+static void connect_multi_next_socket(struct composite_context *result)
+{
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ struct connect_one_state *state;
+ struct composite_context *creq;
+ int next = multi->num_connects_sent;
+
+ if (next == multi->num_ports) {
+ /* don't do anything, just wait for the existing ones to finish */
+ return;
+ }
+
+ multi->num_connects_sent += 1;
+
+ state = talloc(multi, struct connect_one_state);
+ if (composite_nomem(state, result)) return;
+
+ state->result = result;
+ result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
+ if (!composite_is_ok(result)) return;
+
+ /* Form up the particular address we are interested in */
+ state->addr = socket_address_from_strings(state, state->sock->backend_name,
+ multi->server_address, multi->ports[next]);
+ if (composite_nomem(state->addr, result)) return;
+
+ talloc_steal(state, state->sock);
+
+ creq = socket_connect_send(state->sock, NULL,
+ state->addr, 0, multi->resolve_ctx,
+ result->event_ctx);
+ if (composite_nomem(creq, result)) return;
+ talloc_steal(state, creq);
+
+ composite_continue(result, creq, continue_one, state);
+
+ /* if there are more ports to go then setup a timer to fire when we have waited
+ for a couple of milli-seconds, when that goes off we try the next port regardless
+ of whether this port has completed */
+ if (multi->num_ports > multi->num_connects_sent) {
+ /* note that this timer is a child of the single
+ connect attempt state, so it will go away when this
+ request completes */
+ event_add_timed(result->event_ctx, state,
+ timeval_current_ofs(0, MULTI_PORT_DELAY),
+ connect_multi_timer, result);
+ }
+}
+
+/*
+ a timer has gone off telling us that we should try the next port
+*/
+static void connect_multi_timer(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *p)
+{
+ struct composite_context *result = talloc_get_type(p, struct composite_context);
+ connect_multi_next_socket(result);
+}
+
+
+/*
+ recv name resolution reply then send the next connect
+*/
+static void continue_resolve_name(struct composite_context *creq)
+{
+ struct composite_context *result = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ const char *addr;
+
+ result->status = resolve_name_recv(creq, multi, &addr);
+ if (!composite_is_ok(result)) return;
+
+ multi->server_address = addr;
+
+ connect_multi_next_socket(result);
+}
+
+/*
+ one of our socket_connect_send() calls hash finished. If it got a
+ connection or there are none left then we are done
+*/
+static void continue_one(struct composite_context *creq)
+{
+ struct connect_one_state *state = talloc_get_type(creq->async.private_data,
+ struct connect_one_state);
+ struct composite_context *result = state->result;
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ NTSTATUS status;
+ multi->num_connects_recv++;
+
+ status = socket_connect_recv(creq);
+
+ if (NT_STATUS_IS_OK(status)) {
+ multi->sock = talloc_steal(multi, state->sock);
+ multi->result_port = state->addr->port;
+ }
+
+ talloc_free(state);
+
+ if (NT_STATUS_IS_OK(status) ||
+ multi->num_connects_recv == multi->num_ports) {
+ result->status = status;
+ composite_done(result);
+ return;
+ }
+
+ /* try the next port */
+ connect_multi_next_socket(result);
+}
+
+/*
+ async recv routine for socket_connect_multi()
+ */
+_PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **sock,
+ uint16_t *port)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_multi_state *multi =
+ talloc_get_type(ctx->private_data,
+ struct connect_multi_state);
+ *sock = talloc_steal(mem_ctx, multi->sock);
+ *port = multi->result_port;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ struct composite_context *ctx =
+ socket_connect_multi_send(mem_ctx, server_address,
+ num_server_ports, server_ports,
+ resolve_ctx,
+ event_ctx);
+ return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);
+}
diff --git a/source4/lib/socket/interface.c b/source4/lib/socket/interface.c
new file mode 100644
index 0000000000..c327f02bbd
--- /dev/null
+++ b/source4/lib/socket/interface.c
@@ -0,0 +1,335 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ multiple interface handling
+
+ Copyright (C) Andrew Tridgell 1992-2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "lib/util/dlinklist.h"
+#include "param/param.h"
+
+/** used for network interfaces */
+struct interface {
+ struct interface *next, *prev;
+ struct in_addr ip;
+ struct in_addr nmask;
+ const char *ip_s;
+ const char *bcast_s;
+ const char *nmask_s;
+};
+
+#define ALLONES ((uint32_t)0xFFFFFFFF)
+/*
+ address construction based on a patch from fred@datalync.com
+*/
+#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
+#define MKNETADDR(_IP, _NM) (_IP & _NM)
+
+/****************************************************************************
+Try and find an interface that matches an ip. If we cannot, return NULL
+ **************************************************************************/
+static struct interface *iface_find(struct interface *interfaces,
+ struct in_addr ip, bool CheckMask)
+{
+ struct interface *i;
+ if (is_zero_ip(ip)) return interfaces;
+
+ for (i=interfaces;i;i=i->next)
+ if (CheckMask) {
+ if (same_net(i->ip,ip,i->nmask)) return i;
+ } else if (i->ip.s_addr == ip.s_addr) return i;
+
+ return NULL;
+}
+
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(TALLOC_CTX *mem_ctx, struct in_addr ip, struct in_addr nmask, struct interface **interfaces)
+{
+ struct interface *iface;
+ struct in_addr bcast;
+
+ if (iface_find(*interfaces, ip, false)) {
+ DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ iface = talloc(*interfaces == NULL ? mem_ctx : *interfaces, struct interface);
+ if (iface == NULL)
+ return;
+
+ ZERO_STRUCTPN(iface);
+
+ iface->ip = ip;
+ iface->nmask = nmask;
+ bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr);
+
+ /* keep string versions too, to avoid people tripping over the implied
+ static in inet_ntoa() */
+ iface->ip_s = talloc_strdup(iface, inet_ntoa(iface->ip));
+ iface->nmask_s = talloc_strdup(iface, inet_ntoa(iface->nmask));
+
+ if (nmask.s_addr != ~0) {
+ iface->bcast_s = talloc_strdup(iface, inet_ntoa(bcast));
+ }
+
+ DLIST_ADD_END(*interfaces, iface, struct interface *);
+
+ DEBUG(2,("added interface ip=%s nmask=%s\n", iface->ip_s, iface->nmask_s));
+}
+
+
+
+/**
+interpret a single element from a interfaces= config line
+
+This handles the following different forms:
+
+1) wildcard interface name
+2) DNS name
+3) IP/masklen
+4) ip/mask
+5) bcast/mask
+**/
+static void interpret_interface(TALLOC_CTX *mem_ctx,
+ const char *token,
+ struct iface_struct *probed_ifaces,
+ int total_probed,
+ struct interface **local_interfaces)
+{
+ struct in_addr ip, nmask;
+ char *p;
+ char *address;
+ int i, added=0;
+
+ ip.s_addr = 0;
+ nmask.s_addr = 0;
+
+ /* first check if it is an interface name */
+ for (i=0;i<total_probed;i++) {
+ if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
+ add_interface(mem_ctx, probed_ifaces[i].ip,
+ probed_ifaces[i].netmask,
+ local_interfaces);
+ added = 1;
+ }
+ }
+ if (added) return;
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (!p) {
+ /* don't try to do dns lookups on wildcard names */
+ if (strpbrk(token, "*?") != NULL) {
+ return;
+ }
+ ip.s_addr = interpret_addr2(token).s_addr;
+ for (i=0;i<total_probed;i++) {
+ if (ip.s_addr == probed_ifaces[i].ip.s_addr) {
+ add_interface(mem_ctx, probed_ifaces[i].ip,
+ probed_ifaces[i].netmask,
+ local_interfaces);
+ return;
+ }
+ }
+ DEBUG(2,("can't determine netmask for %s\n", token));
+ return;
+ }
+
+ address = talloc_strdup(mem_ctx, token);
+ p = strchr_m(address,'/');
+
+ /* parse it into an IP address/netmasklength pair */
+ *p++ = 0;
+
+ ip.s_addr = interpret_addr2(address).s_addr;
+
+ if (strlen(p) > 2) {
+ nmask.s_addr = interpret_addr2(p).s_addr;
+ } else {
+ nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES));
+ }
+
+ /* maybe the first component was a broadcast address */
+ if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) ||
+ ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) {
+ for (i=0;i<total_probed;i++) {
+ if (same_net(ip, probed_ifaces[i].ip, nmask)) {
+ add_interface(mem_ctx, probed_ifaces[i].ip, nmask,
+ local_interfaces);
+ talloc_free(address);
+ return;
+ }
+ }
+ DEBUG(2,("Can't determine ip for broadcast address %s\n", address));
+ talloc_free(address);
+ return;
+ }
+
+ add_interface(mem_ctx, ip, nmask, local_interfaces);
+ talloc_free(address);
+}
+
+
+/**
+load the list of network interfaces
+**/
+void load_interfaces(TALLOC_CTX *mem_ctx, const char **interfaces, struct interface **local_interfaces)
+{
+ const char **ptr = interfaces;
+ int i;
+ struct iface_struct ifaces[MAX_INTERFACES];
+ struct in_addr loopback_ip;
+ int total_probed;
+
+ *local_interfaces = NULL;
+
+ loopback_ip = interpret_addr2("127.0.0.1");
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(ifaces, MAX_INTERFACES);
+
+ /* if we don't have a interfaces line then use all interfaces
+ except loopback */
+ if (!ptr || !*ptr || !**ptr) {
+ if (total_probed <= 0) {
+ DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
+ }
+ for (i=0;i<total_probed;i++) {
+ if (ifaces[i].ip.s_addr != loopback_ip.s_addr) {
+ add_interface(mem_ctx, ifaces[i].ip,
+ ifaces[i].netmask, local_interfaces);
+ }
+ }
+ }
+
+ while (ptr && *ptr) {
+ interpret_interface(mem_ctx, *ptr, ifaces, total_probed, local_interfaces);
+ ptr++;
+ }
+
+ if (!*local_interfaces) {
+ DEBUG(0,("WARNING: no network interfaces found\n"));
+ }
+}
+
+/**
+ how many interfaces do we have
+ **/
+int iface_count(struct interface *ifaces)
+{
+ int ret = 0;
+ struct interface *i;
+
+ for (i=ifaces;i;i=i->next)
+ ret++;
+ return ret;
+}
+
+/**
+ return IP of the Nth interface
+ **/
+const char *iface_n_ip(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->ip_s;
+ }
+ return NULL;
+}
+
+/**
+ return bcast of the Nth interface
+ **/
+const char *iface_n_bcast(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->bcast_s;
+ }
+ return NULL;
+}
+
+/**
+ return netmask of the Nth interface
+ **/
+const char *iface_n_netmask(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->nmask_s;
+ }
+ return NULL;
+}
+
+/**
+ return the local IP address that best matches a destination IP, or
+ our first interface if none match
+*/
+const char *iface_best_ip(struct interface *ifaces, const char *dest)
+{
+ struct interface *iface;
+ struct in_addr ip;
+
+ ip.s_addr = interpret_addr(dest);
+ iface = iface_find(ifaces, ip, true);
+ if (iface) {
+ return iface->ip_s;
+ }
+ return iface_n_ip(ifaces, 0);
+}
+
+/**
+ return true if an IP is one one of our local networks
+*/
+bool iface_is_local(struct interface *ifaces, const char *dest)
+{
+ struct in_addr ip;
+
+ ip.s_addr = interpret_addr(dest);
+ if (iface_find(ifaces, ip, true)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ return true if a IP matches a IP/netmask pair
+*/
+bool iface_same_net(const char *ip1, const char *ip2, const char *netmask)
+{
+ return same_net(interpret_addr2(ip1),
+ interpret_addr2(ip2),
+ interpret_addr2(netmask));
+}
diff --git a/source4/lib/socket/netif.c b/source4/lib/socket/netif.c
new file mode 100644
index 0000000000..bf410af441
--- /dev/null
+++ b/source4/lib/socket/netif.c
@@ -0,0 +1,126 @@
+/*
+ Unix SMB/CIFS implementation.
+ return a list of network interfaces
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Jeremy Allison 2007
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/* working out the interfaces for a OS is an incredibly non-portable
+ thing. We have several possible implementations below, and autoconf
+ tries each of them to see what works
+
+ Note that this file does _not_ include includes.h. That is so this code
+ can be called directly from the autoconf tests. That also means
+ this code cannot use any of the normal Samba debug stuff or defines.
+ This is standalone code.
+
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "netif.h"
+
+/****************************************************************************
+ Try the "standard" getifaddrs/freeifaddrs interfaces.
+ Also gets IPv6 interfaces.
+****************************************************************************/
+
+/****************************************************************************
+ Get the netmask address for a local interface.
+****************************************************************************/
+
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifaddrs *iflist = NULL;
+ struct ifaddrs *ifptr = NULL;
+ int total = 0;
+
+ if (getifaddrs(&iflist) < 0) {
+ return -1;
+ }
+
+ /* Loop through interfaces, looking for given IP address */
+ for (ifptr = iflist, total = 0;
+ ifptr != NULL && total < max_interfaces;
+ ifptr = ifptr->ifa_next) {
+
+ memset(&ifaces[total], '\0', sizeof(ifaces[total]));
+
+ if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
+ continue;
+ }
+
+ /* Check the interface is up. */
+ if (!(ifptr->ifa_flags & IFF_UP)) {
+ continue;
+ }
+
+ /* We don't support IPv6 *yet* */
+ if (ifptr->ifa_addr->sa_family != AF_INET) {
+ continue;
+ }
+
+ ifaces[total].ip = ((struct sockaddr_in *)ifptr->ifa_addr)->sin_addr;
+ ifaces[total].netmask = ((struct sockaddr_in *)ifptr->ifa_netmask)->sin_addr;
+
+ strlcpy(ifaces[total].name, ifptr->ifa_name,
+ sizeof(ifaces[total].name));
+ total++;
+ }
+
+ freeifaddrs(iflist);
+
+ return total;
+}
+
+static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
+{
+ int r;
+ r = strcmp(i1->name, i2->name);
+ if (r) return r;
+ r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
+ if (r) return r;
+ r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
+ return r;
+}
+
+/* this wrapper is used to remove duplicates from the interface list generated
+ above */
+int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ int total, i, j;
+
+ total = _get_interfaces(ifaces, max_interfaces);
+ if (total <= 0) return total;
+
+ /* now we need to remove duplicates */
+ qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
+
+ for (i=1;i<total;) {
+ if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
+ for (j=i-1;j<total-1;j++) {
+ ifaces[j] = ifaces[j+1];
+ }
+ total--;
+ } else {
+ i++;
+ }
+ }
+
+ return total;
+}
diff --git a/source4/lib/socket/netif.h b/source4/lib/socket/netif.h
new file mode 100644
index 0000000000..417c6e074f
--- /dev/null
+++ b/source4/lib/socket/netif.h
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for lib/netif/
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "system/network.h"
+
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
+
+struct interface;
+
+#define MAX_INTERFACES 128
+
+#ifndef AUTOCONF_TEST
+#include "lib/socket/netif_proto.h"
+#endif
diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c
new file mode 100644
index 0000000000..92f0a44005
--- /dev/null
+++ b/source4/lib/socket/socket.c
@@ -0,0 +1,554 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "param/param.h"
+
+/*
+ auto-close sockets on free
+*/
+static int socket_destructor(struct socket_context *sock)
+{
+ if (sock->ops->fn_close &&
+ !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
+ sock->ops->fn_close(sock);
+ }
+ return 0;
+}
+
+_PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags)
+{
+ NTSTATUS status;
+
+ (*new_sock) = talloc(mem_ctx, struct socket_context);
+ if (!(*new_sock)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*new_sock)->type = type;
+ (*new_sock)->state = SOCKET_STATE_UNDEFINED;
+ (*new_sock)->flags = flags;
+
+ (*new_sock)->fd = -1;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = ops;
+ (*new_sock)->backend_name = NULL;
+
+ status = (*new_sock)->ops->fn_init((*new_sock));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*new_sock);
+ return status;
+ }
+
+ /* by enabling "testnonblock" mode, all socket receive and
+ send calls on non-blocking sockets will randomly recv/send
+ less data than requested */
+
+ if (!(flags & SOCKET_FLAG_BLOCK) &&
+ type == SOCKET_TYPE_STREAM &&
+ lp_parm_bool(global_loadparm, NULL, "socket", "testnonblock", false)) {
+ (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
+ }
+
+ /* we don't do a connect() on dgram sockets, so need to set
+ non-blocking at socket create time */
+ if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
+ set_blocking(socket_get_fd(*new_sock), false);
+ }
+
+ talloc_set_destructor(*new_sock, socket_destructor);
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags)
+{
+ const struct socket_ops *ops;
+
+ ops = socket_getops_byname(name, type);
+ if (!ops) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return socket_create_with_ops(NULL, ops, new_sock, type, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_connect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_connect(sock, my_address, server_address, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ if (!sock->ops->fn_connect_complete) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_connect_complete(sock, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_listen) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_listen(sock, my_address, queue_size, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ NTSTATUS status;
+
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_accept) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ status = sock->ops->fn_accept(sock, new_sock);
+
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_set_destructor(*new_sock, socket_destructor);
+ (*new_sock)->flags = 0;
+ }
+
+ return status;
+}
+
+_PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED &&
+ sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recv) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && wantlen > 1) {
+
+ if (random() % 10 == 0) {
+ *nread = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
+ }
+ return sock->ops->fn_recv(sock, buf, wantlen, nread);
+}
+
+_PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recvfrom) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
+ mem_ctx, src_addr);
+}
+
+_PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_send) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && blob->length > 1) {
+ DATA_BLOB blob2 = *blob;
+ if (random() % 10 == 0) {
+ *sendlen = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ /* The random size sends are incompatible with TLS and SASL
+ * sockets, which require re-sends to be consistant */
+ if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
+ blob2.length = 1+(random() % blob2.length);
+ } else {
+ /* This is particularly stressful on buggy
+ * LDAP clients, that don't expect on LDAP
+ * packet in many SASL packets */
+ blob2.length = 1 + blob2.length/2;
+ }
+ return sock->ops->fn_send(sock, &blob2, sendlen);
+ }
+ return sock->ops->fn_send(sock, blob, sendlen);
+}
+
+
+_PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
+ sock->state == SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_sendto) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
+}
+
+
+/*
+ ask for the number of bytes in a pending incoming packet
+*/
+_PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_pending) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_pending(sock, npending);
+}
+
+
+_PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_set_option) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_set_option(sock, option, val);
+}
+
+_PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_name) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_name(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_my_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_my_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ int socket_get_fd(struct socket_context *sock)
+{
+ if (!sock->ops->fn_get_fd) {
+ return -1;
+ }
+
+ return sock->ops->fn_get_fd(sock);
+}
+
+/*
+ call dup() on a socket, and close the old fd. This is used to change
+ the fd to the lowest available number, to make select() more
+ efficient (select speed depends on the maxiumum fd number passed to
+ it)
+*/
+_PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
+{
+ int fd;
+ if (sock->fd == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fd = dup(sock->fd);
+ if (fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ close(sock->fd);
+ sock->fd = fd;
+ return NT_STATUS_OK;
+
+}
+
+/* Create a new socket_address. The type must match the socket type.
+ * The host parameter may be an IP or a hostname
+ */
+
+_PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *family,
+ const char *host,
+ int port)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+
+ addr->family = family;
+ addr->addr = talloc_strdup(addr, host);
+ if (!addr->addr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->port = port;
+ addr->sockaddr = NULL;
+ addr->sockaddrlen = 0;
+
+ return addr;
+}
+
+/* Create a new socket_address. Copy the struct sockaddr into the new
+ * structure. Used for hooks in the kerberos libraries, where they
+ * supply only a struct sockaddr */
+
+_PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t sockaddrlen)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ addr->family = NULL;
+ addr->addr = NULL;
+ addr->port = 0;
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
+ if (!addr->sockaddr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->sockaddrlen = sockaddrlen;
+ return addr;
+}
+
+_PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
+{
+ extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
+ extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
+ extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
+
+ if (strcmp("ip", family) == 0 ||
+ strcmp("ipv4", family) == 0) {
+ return socket_ipv4_ops(type);
+ }
+
+#if HAVE_IPV6
+ if (strcmp("ipv6", family) == 0) {
+ return socket_ipv6_ops(type);
+ }
+#endif
+
+ if (strcmp("unix", family) == 0) {
+ return socket_unixdom_ops(type);
+ }
+
+ return NULL;
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+static const struct {
+ const char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} socket_options[] = {
+ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
+ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
+ {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
+#ifdef TCP_NODELAY
+ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+ {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+ {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
+#endif
+#ifdef SO_REUSEPORT
+ {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
+#endif
+#ifdef SO_SNDBUF
+ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
+#endif
+ {NULL,0,0,0,0}};
+
+
+/**
+ Set user socket options.
+**/
+_PUBLIC_ void set_socket_options(int fd, const char *options)
+{
+ const char **options_list = str_list_make(NULL, options, " \t,");
+ int j;
+
+ if (!options_list)
+ return;
+
+ for (j = 0; options_list[j]; j++) {
+ const char *tok = options_list[j];
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ bool got_value = false;
+
+ if ((p = strchr(tok,'='))) {
+ *p = 0;
+ value = atoi(p+1);
+ got_value = true;
+ }
+
+ for (i=0;socket_options[i].name;i++)
+ if (strequal(socket_options[i].name,tok))
+ break;
+
+ if (!socket_options[i].name) {
+ DEBUG(0,("Unknown socket option %s\n",tok));
+ continue;
+ }
+
+ switch (socket_options[i].opttype) {
+ case OPT_BOOL:
+ case OPT_INT:
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&value,sizeof(int));
+ break;
+
+ case OPT_ON:
+ if (got_value)
+ DEBUG(0,("syntax error - %s does not take a value\n",tok));
+
+ {
+ int on = socket_options[i].value;
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&on,sizeof(int));
+ }
+ break;
+ }
+
+ if (ret != 0)
+ DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
+ }
+
+ talloc_free(options_list);
+}
+
+/*
+ set some flags on a socket
+ */
+void socket_set_flags(struct socket_context *sock, unsigned flags)
+{
+ sock->flags |= flags;
+}
diff --git a/source4/lib/socket/socket.h b/source4/lib/socket/socket.h
new file mode 100644
index 0000000000..4baa0cfbb1
--- /dev/null
+++ b/source4/lib/socket/socket.h
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA_SOCKET_H
+#define _SAMBA_SOCKET_H
+
+#include "lib/events/events.h"
+
+struct socket_context;
+
+enum socket_type {
+ SOCKET_TYPE_STREAM,
+ SOCKET_TYPE_DGRAM
+};
+
+struct socket_address {
+ const char *family;
+ char *addr;
+ int port;
+ struct sockaddr *sockaddr;
+ size_t sockaddrlen;
+};
+
+struct socket_ops {
+ const char *name;
+
+ NTSTATUS (*fn_init)(struct socket_context *sock);
+
+ /* client ops */
+ NTSTATUS (*fn_connect)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+
+ /* complete a non-blocking connect */
+ NTSTATUS (*fn_connect_complete)(struct socket_context *sock,
+ uint32_t flags);
+
+ /* server ops */
+ NTSTATUS (*fn_listen)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+ NTSTATUS (*fn_accept)(struct socket_context *sock,
+ struct socket_context **new_sock);
+
+ /* general ops */
+ NTSTATUS (*fn_recv)(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+ NTSTATUS (*fn_send)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+
+ NTSTATUS (*fn_sendto)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+ NTSTATUS (*fn_recvfrom)(struct socket_context *sock,
+ void *buf, size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+ NTSTATUS (*fn_pending)(struct socket_context *sock, size_t *npending);
+
+ void (*fn_close)(struct socket_context *sock);
+
+ NTSTATUS (*fn_set_option)(struct socket_context *sock, const char *option, const char *val);
+
+ char *(*fn_get_peer_name)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_peer_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_my_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+
+ int (*fn_get_fd)(struct socket_context *sock);
+};
+
+enum socket_state {
+ SOCKET_STATE_UNDEFINED,
+
+ SOCKET_STATE_CLIENT_START,
+ SOCKET_STATE_CLIENT_CONNECTED,
+ SOCKET_STATE_CLIENT_STARTTLS,
+ SOCKET_STATE_CLIENT_ERROR,
+
+ SOCKET_STATE_SERVER_LISTEN,
+ SOCKET_STATE_SERVER_CONNECTED,
+ SOCKET_STATE_SERVER_STARTTLS,
+ SOCKET_STATE_SERVER_ERROR
+};
+
+#define SOCKET_FLAG_BLOCK 0x00000001
+#define SOCKET_FLAG_PEEK 0x00000002
+#define SOCKET_FLAG_TESTNONBLOCK 0x00000004
+#define SOCKET_FLAG_ENCRYPT 0x00000008 /* This socket
+ * implementation requires
+ * that re-sends be
+ * consistant, because it
+ * is encrypting data.
+ * This modifies the
+ * TESTNONBLOCK case */
+#define SOCKET_FLAG_NOCLOSE 0x00000010 /* don't auto-close on free */
+
+
+struct socket_context {
+ enum socket_type type;
+ enum socket_state state;
+ uint32_t flags;
+
+ int fd;
+
+ void *private_data;
+ const struct socket_ops *ops;
+ const char *backend_name;
+
+ /* specific to the ip backend */
+ int family;
+};
+
+struct resolve_context;
+
+/* prototypes */
+NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags);
+NTSTATUS socket_create(const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags);
+NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags);
+NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock);
+NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+NTSTATUS socket_pending(struct socket_context *sock, size_t *npending);
+NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val);
+char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+int socket_get_fd(struct socket_context *sock);
+NTSTATUS socket_dup(struct socket_context *sock);
+struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *type,
+ const char *host,
+ int port);
+struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t addrlen);
+const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type);
+bool allow_access(TALLOC_CTX *mem_ctx,
+ const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr);
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list);
+
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx);
+NTSTATUS socket_connect_recv(struct composite_context *ctx);
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct resolve_context *resolve_ctx,
+ struct event_context *ev);
+
+struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx);
+NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct event_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+void set_socket_options(int fd, const char *options);
+void socket_set_flags(struct socket_context *socket, unsigned flags);
+
+#endif /* _SAMBA_SOCKET_H */
diff --git a/source4/lib/socket/socket_ip.c b/source4/lib/socket/socket_ip.c
new file mode 100644
index 0000000000..bca0aab924
--- /dev/null
+++ b/source4/lib/socket/socket_ip.c
@@ -0,0 +1,985 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Socket IPv4/IPv6 functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+
+static NTSTATUS ipv4_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ sock->backend_name = "ipv4";
+ sock->family = AF_INET;
+
+ return NT_STATUS_OK;
+}
+
+static void ip_close(struct socket_context *sock)
+{
+ close(sock->fd);
+}
+
+static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix(error);
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ struct sockaddr_in srv_addr;
+ struct in_addr my_ip;
+ struct in_addr srv_ip;
+ int ret;
+
+ 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);
+ }
+ } else if (my_address) {
+ my_ip = interpret_addr2(my_address->addr);
+
+ if (my_ip.s_addr != 0 || my_address->port != 0) {
+ struct sockaddr_in my_addr;
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = my_ip.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix(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);
+ }
+ } else {
+ srv_ip = interpret_addr2(srv_address->addr);
+ if (!srv_ip.s_addr) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ SMB_ASSERT(srv_address->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ srv_addr.sin_addr.s_addr= srv_ip.s_addr;
+ srv_addr.sin_port = htons(srv_address->port);
+ srv_addr.sin_family = PF_INET;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+
+/*
+ note that for simplicity of the API, socket_listen() is also
+ use for DGRAM sockets, but in reality only a bind() is done
+*/
+static NTSTATUS ipv4_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in my_addr;
+ struct in_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ ip_addr = interpret_addr2(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = ip_addr.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix(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);
+ }
+ }
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET, &from_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in srv_addr;
+ struct in_addr addr;
+
+ SMB_ASSERT(dest_addr->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ addr = interpret_addr2(dest_addr->addr);
+ if (addr.s_addr == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin_addr.s_addr = addr.s_addr;
+ srv_addr.sin_port = htons(dest_addr->port);
+ srv_addr.sin_family = PF_INET;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &peer_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->addr = talloc_strdup(peer, addrstring);
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->port = ntohs(peer_addr->sin_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &local_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->addr = talloc_strdup(local, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin_port);
+
+ return local;
+}
+static int ip_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+}
+
+static const struct socket_ops ipv4_ops = {
+ .name = "ipv4",
+ .fn_init = ipv4_init,
+ .fn_connect = ipv4_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv4_listen,
+ .fn_accept = ipv4_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv4_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv4_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv4_set_option,
+
+ .fn_get_peer_name = ipv4_get_peer_name,
+ .fn_get_peer_addr = ipv4_get_peer_addr,
+ .fn_get_my_addr = ipv4_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type)
+{
+ return &ipv4_ops;
+}
+
+#if HAVE_IPV6
+
+static struct in6_addr interpret_addr6(const char *name)
+{
+ struct hostent *he;
+
+ if (name == NULL) return in6addr_any;
+
+ if (strcasecmp(name, "localhost") == 0) {
+ name = "::1";
+ }
+
+ he = gethostbyname2(name, PF_INET6);
+
+ if (he == NULL) return in6addr_any;
+
+ return *((struct in6_addr *)he->h_addr);
+}
+
+static NTSTATUS ipv6_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET6, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ sock->backend_name = "ipv6";
+ sock->family = AF_INET6;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ 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);
+ }
+ } else if (my_address) {
+ struct in6_addr my_ip;
+ my_ip = interpret_addr6(my_address->addr);
+
+ if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
+ struct sockaddr_in6 my_addr;
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = my_ip;
+ 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));
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct in6_addr srv_ip;
+ struct sockaddr_in6 srv_addr;
+ srv_ip = interpret_addr6(srv_address->addr);
+ if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sin6_addr = srv_ip;
+ srv_addr.sin6_port = htons(srv_address->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+static NTSTATUS ipv6_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in6 my_addr;
+ struct in6_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ 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));
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix(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);
+ }
+ }
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in6 *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in6);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET6, &from_addr->sin6_addr, addrstring, sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n", strerror(errno)));
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin6_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in6 srv_addr;
+ struct in6_addr addr;
+
+ ZERO_STRUCT(srv_addr);
+ addr = interpret_addr6(dest_addr->addr);
+ if (addr.s6_addr == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin6_addr = addr;
+ srv_addr.sin6_port = htons(dest_addr->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+ char addr[128];
+ const char *addr_ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in6);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
+ if (addr_ret == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->addr = talloc_strdup(peer, addr_ret);
+ if (peer->addr == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->port = ntohs(peer_addr->sin6_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in6);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET6, &local_addr->sin6_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n",
+ strerror(errno)));
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->addr = talloc_strdup(mem_ctx, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin6_port);
+
+ return local;
+}
+
+static const struct socket_ops ipv6_tcp_ops = {
+ .name = "ipv6",
+ .fn_init = ipv6_init,
+ .fn_connect = ipv6_tcp_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv6_listen,
+ .fn_accept = ipv6_tcp_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv6_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv6_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv6_set_option,
+
+ .fn_get_peer_name = ipv6_tcp_get_peer_name,
+ .fn_get_peer_addr = ipv6_tcp_get_peer_addr,
+ .fn_get_my_addr = ipv6_tcp_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type)
+{
+ return &ipv6_tcp_ops;
+}
+
+#endif
diff --git a/source4/lib/socket/socket_unix.c b/source4/lib/socket/socket_unix.c
new file mode 100644
index 0000000000..af7d2bb79f
--- /dev/null
+++ b/source4/lib/socket/socket_unix.c
@@ -0,0 +1,420 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ unix domain socket functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004-2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+
+
+/*
+ approximate errno mapping
+*/
+static NTSTATUS unixdom_error(int ernum)
+{
+ return map_nt_error_from_unix(ernum);
+}
+
+static NTSTATUS unixdom_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_UNIX, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ sock->private_data = NULL;
+
+ sock->backend_name = "unix";
+
+ return NT_STATUS_OK;
+}
+
+static void unixdom_close(struct socket_context *sock)
+{
+ close(sock->fd);
+}
+
+static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix(error);
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct sockaddr_un srv_addr;
+ if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ strncpy(srv_addr.sun_path, srv_address->addr, sizeof(srv_addr.sun_path));
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ return unixdom_connect_complete(sock, flags);
+}
+
+static NTSTATUS unixdom_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_un my_addr;
+ int ret;
+
+ /* delete if it already exists */
+ if (my_address->addr) {
+ unlink(my_address->addr);
+ }
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ } else if (my_address->addr == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, my_address->addr, sizeof(my_addr.sun_path));
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_SERVER_LISTEN;
+ sock->private_data = (void *)talloc_strdup(sock, my_address->addr);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_accept(struct socket_context *sock,
+ struct socket_context **new_sock)
+{
+ struct sockaddr_un cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return unixdom_error(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);
+ }
+ }
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return unixdom_error(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return unixdom_error(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest)
+{
+ ssize_t len;
+ *sendlen = 0;
+
+ if (dest->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest->sockaddr, dest->sockaddrlen);
+ } else {
+ struct sockaddr_un srv_addr;
+
+ if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ strncpy(srv_addr.sun_path, dest->addr, sizeof(srv_addr.sun_path));
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_set_option(struct socket_context *sock,
+ const char *option, const char *val)
+{
+ return NT_STATUS_OK;
+}
+
+static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ return talloc_strdup(mem_ctx, "LOCAL/unixdom");
+}
+
+static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ peer->port = 0;
+ peer->addr = talloc_strdup(peer, "LOCAL/unixdom");
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ return peer;
+}
+
+static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ local->port = 0;
+ local->addr = talloc_strdup(local, "LOCAL/unixdom");
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ return local;
+}
+
+static int unixdom_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+}
+
+static const struct socket_ops unixdom_ops = {
+ .name = "unix",
+ .fn_init = unixdom_init,
+ .fn_connect = unixdom_connect,
+ .fn_connect_complete = unixdom_connect_complete,
+ .fn_listen = unixdom_listen,
+ .fn_accept = unixdom_accept,
+ .fn_recv = unixdom_recv,
+ .fn_send = unixdom_send,
+ .fn_sendto = unixdom_sendto,
+ .fn_close = unixdom_close,
+ .fn_pending = unixdom_pending,
+
+ .fn_set_option = unixdom_set_option,
+
+ .fn_get_peer_name = unixdom_get_peer_name,
+ .fn_get_peer_addr = unixdom_get_peer_addr,
+ .fn_get_my_addr = unixdom_get_my_addr,
+
+ .fn_get_fd = unixdom_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type)
+{
+ return &unixdom_ops;
+}
diff --git a/source4/lib/socket/testsuite.c b/source4/lib/socket/testsuite.c
new file mode 100644
index 0000000000..2c25d8f491
--- /dev/null
+++ b/source4/lib/socket/testsuite.c
@@ -0,0 +1,198 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of socket routines.
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "torture/torture.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+ basic testing of udp routines
+*/
+static bool test_udp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct interface *ifaces;
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+ talloc_steal(mem_ctx, sock1);
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+ talloc_steal(mem_ctx, sock2);
+
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_best_ip(ifaces, "127.0.0.1"), 0);
+
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr != NULL &&
+ strcmp(srv_addr->addr, iface_best_ip(ifaces, "127.0.0.1")) == 0,
+ talloc_asprintf(tctx,
+ "Expected server address of %s but got %s",
+ iface_best_ip(ifaces, "127.0.0.1"), srv_addr ? srv_addr->addr : NULL));
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_sendto(sock2, &blob, &sent, srv_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 2");
+
+ status = socket_recvfrom(sock1, blob2.data, size, &nread,
+ sock1, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 1");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "different address");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ generate_random_buffer(blob.data, blob.length);
+ status = socket_sendto(sock1, &blob, &sent, from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 1");
+
+ status = socket_recvfrom(sock2, blob2.data, size, &nread,
+ sock2, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 2");
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_int_equal(tctx, from_addr->port, srv_addr->port,
+ "Unexpected recvfrom port");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ talloc_free(sock1);
+ talloc_free(sock2);
+ return true;
+}
+
+/*
+ basic testing of tcp routines
+*/
+static bool test_tcp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2, *sock3;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct event_context *ev = tctx->ev;
+ struct interface *ifaces;
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+ talloc_steal(mem_ctx, sock1);
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+ talloc_steal(mem_ctx, sock2);
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_best_ip(ifaces, "127.0.0.1"), 0);
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr && srv_addr->addr,
+ "Unexpected socket_get_my_addr NULL\n");
+
+ torture_assert_str_equal(tctx, srv_addr->addr, iface_best_ip(ifaces, "127.0.0.1"),
+ "Unexpected server address");
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ status = socket_connect_ev(sock2, NULL, srv_addr, 0, NULL, ev);
+ torture_assert_ntstatus_ok(tctx, status, "connect() on socket 2");
+
+ status = socket_accept(sock1, &sock3);
+ torture_assert_ntstatus_ok(tctx, status, "accept() on socket 1");
+ talloc_steal(mem_ctx, sock3);
+ talloc_free(sock1);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_send(sock2, &blob, &sent);
+ torture_assert_ntstatus_ok(tctx, status, "send() on socket 2");
+
+ status = socket_recv(sock3, blob2.data, size, &nread);
+ torture_assert_ntstatus_ok(tctx, status, "recv() on socket 3");
+
+ from_addr = socket_get_peer_addr(sock3, mem_ctx);
+
+ torture_assert(tctx, from_addr && from_addr->addr,
+ "Unexpected recvfrom addr NULL");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recv");
+ return true;
+}
+
+struct torture_suite *torture_local_socket(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "SOCKET");
+
+ torture_suite_add_simple_test(suite, "udp", test_udp);
+ torture_suite_add_simple_test(suite, "tcp", test_tcp);
+
+ return suite;
+}