summaryrefslogtreecommitdiffstats
path: root/source4/lib/socket
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source4/lib/socket/access.c129
-rw-r--r--source4/lib/socket/connect.c158
-rw-r--r--source4/lib/socket/connect_multi.c392
-rw-r--r--source4/lib/socket/interface.c525
-rw-r--r--source4/lib/socket/netif.h24
-rw-r--r--source4/lib/socket/socket.c640
-rw-r--r--source4/lib/socket/socket.h256
-rw-r--r--source4/lib/socket/socket_ip.c1033
-rw-r--r--source4/lib/socket/socket_unix.c436
-rw-r--r--source4/lib/socket/testsuite.c194
-rw-r--r--source4/lib/socket/wscript_build29
11 files changed, 3816 insertions, 0 deletions
diff --git a/source4/lib/socket/access.c b/source4/lib/socket/access.c
new file mode 100644
index 0000000..c019fd6
--- /dev/null
+++ b/source4/lib/socket/access.c
@@ -0,0 +1,129 @@
+/*
+ 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-technical@lists.samba.org
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/util/util_net.h"
+#include "lib/util/access.h"
+
+/* 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(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/connect.c b/source4/lib/socket/connect.c
new file mode 100644
index 0000000..1da8b41
--- /dev/null
+++ b/source4/lib/socket/connect.c
@@ -0,0 +1,158 @@
+/*
+ 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"
+
+
+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 tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data);
+
+/*
+ call the real socket_connect() call, and setup event handler
+*/
+static void socket_send_connect(struct composite_context *result)
+{
+ struct tevent_fd *fde;
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ 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 = tevent_add_fd(result->event_ctx, result,
+ socket_get_fd(state->sock),
+ TEVENT_FD_READ|TEVENT_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 tevent_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_state *state;
+
+ result = composite_create(sock, event_ctx);
+ if (result == NULL) return NULL;
+
+ 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);
+
+ socket_send_connect(result);
+
+ return result;
+}
+
+/*
+ handle write events on connect completion
+*/
+static void socket_connect_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct composite_context *result =
+ talloc_get_type(private_data, 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);
+}
+
+/*
+ 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 tevent_context *ev)
+{
+ struct composite_context *ctx;
+ ctx = socket_connect_send(sock, my_address,
+ server_address, flags, 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 0000000..b29fffb
--- /dev/null
+++ b/source4/lib/socket/connect_multi.c
@@ -0,0 +1,392 @@
+/*
+ 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"
+
+#define MULTI_PORT_DELAY 2000 /* microseconds */
+
+/*
+ overall state
+*/
+struct connect_multi_state {
+ struct socket_address **server_address;
+ unsigned num_address, current_address, current_port;
+ int num_ports;
+ uint16_t *ports;
+
+ struct socket_context *sock;
+ uint16_t result_port;
+
+ int num_connects_sent, num_connects_recv;
+
+ struct socket_connect_multi_ex *ex;
+};
+
+/*
+ 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 tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *p);
+static void connect_multi_next_socket(struct composite_context *result);
+static void continue_one(struct composite_context *creq);
+static void continue_one_ex(struct tevent_req *subreq);
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_ex_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_name,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex)
+{
+ struct composite_context *result;
+ struct connect_multi_state *multi;
+ int i;
+
+ struct nbt_name name;
+ struct composite_context *creq;
+
+ 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->num_ports = num_server_ports;
+ 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];
+ }
+
+ multi->ex = ex;
+
+ /*
+ 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
+ */
+ make_nbt_name_server(&name, server_name);
+
+ creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx);
+ if (composite_nomem(creq, result)) goto failed;
+
+ composite_continue(result, creq, continue_resolve_name, result);
+
+ 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_address * multi->num_ports) {
+ /* don't do anything, just wait for the existing ones to finish */
+ return;
+ }
+
+ if (multi->current_address == multi->num_address) {
+ multi->current_address = 0;
+ multi->current_port += 1;
+ }
+ multi->num_connects_sent += 1;
+
+ if (multi->server_address == NULL || multi->server_address[multi->current_address] == NULL) {
+ composite_error(result, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ state = talloc(multi, struct connect_one_state);
+ if (composite_nomem(state, result)) return;
+
+ state->result = result;
+ result->status = socket_create(
+ state, multi->server_address[multi->current_address]->family,
+ SOCKET_TYPE_STREAM, &state->sock, 0);
+ if (!composite_is_ok(result)) return;
+
+ state->addr = socket_address_copy(state, multi->server_address[multi->current_address]);
+ if (composite_nomem(state->addr, result)) return;
+
+ socket_address_set_port(state->addr, multi->ports[multi->current_port]);
+
+ creq = socket_connect_send(state->sock, NULL,
+ state->addr, 0,
+ result->event_ctx);
+ if (composite_nomem(creq, result)) return;
+ talloc_steal(state, creq);
+
+ multi->current_address++;
+ composite_continue(result, creq, continue_one, state);
+
+ /* if there are more ports / addresses 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_address > 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 */
+ tevent_add_timer(result->event_ctx, state,
+ timeval_current_ofs_usec(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 tevent_context *ev,
+ struct tevent_timer *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);
+ struct socket_address **addr;
+ unsigned i;
+
+ result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
+ if (!composite_is_ok(result)) return;
+
+ for(i=0; addr[i]; i++);
+ multi->num_address = i;
+ multi->server_address = talloc_steal(multi, 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;
+
+ status = socket_connect_recv(creq);
+
+ if (multi->ex) {
+ struct tevent_req *subreq;
+
+ subreq = multi->ex->establish_send(state,
+ result->event_ctx,
+ state->sock,
+ state->addr,
+ multi->ex->private_data);
+ if (composite_nomem(subreq, result)) return;
+ tevent_req_set_callback(subreq, continue_one_ex, state);
+ return;
+ }
+
+ multi->num_connects_recv++;
+
+ 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_address * multi->num_ports)) {
+ result->status = status;
+ composite_done(result);
+ return;
+ }
+
+ /* try the next port */
+ connect_multi_next_socket(result);
+}
+
+/*
+ one of our multi->ex->establish_send() calls hash finished. If it got a
+ connection or there are none left then we are done
+*/
+static void continue_one_ex(struct tevent_req *subreq)
+{
+ struct connect_one_state *state =
+ tevent_req_callback_data(subreq,
+ struct connect_one_state);
+ struct composite_context *result = state->result;
+ struct connect_multi_state *multi =
+ talloc_get_type_abort(result->private_data,
+ struct connect_multi_state);
+ NTSTATUS status;
+ multi->num_connects_recv++;
+
+ status = multi->ex->establish_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ 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_address * 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_ex_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_ex(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ struct composite_context *ctx =
+ socket_connect_multi_ex_send(mem_ctx, server_address,
+ num_server_ports, server_ports,
+ resolve_ctx,
+ event_ctx,
+ ex);
+ return socket_connect_multi_ex_recv(ctx, mem_ctx, result, result_port);
+}
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_name,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ return socket_connect_multi_ex_send(mem_ctx,
+ server_name,
+ num_server_ports,
+ server_ports,
+ resolve_ctx,
+ event_ctx,
+ NULL); /* ex */
+}
+
+/*
+ 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)
+{
+ return socket_connect_multi_ex_recv(ctx, mem_ctx, sock, 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 tevent_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ return socket_connect_multi_ex(mem_ctx,
+ server_address,
+ num_server_ports,
+ server_ports,
+ resolve_ctx,
+ event_ctx,
+ NULL, /* ex */
+ result,
+ result_port);
+}
diff --git a/source4/lib/socket/interface.c b/source4/lib/socket/interface.c
new file mode 100644
index 0000000..65543c6
--- /dev/null
+++ b/source4/lib/socket/interface.c
@@ -0,0 +1,525 @@
+/*
+ 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 "param/param.h"
+#include "lib/socket/netif.h"
+#include "../lib/util/util_net.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/util/smb_strtox.h"
+
+/* used for network interfaces */
+struct interface {
+ struct interface *next, *prev;
+ char *name;
+ int flags;
+ struct sockaddr_storage ip;
+ struct sockaddr_storage netmask;
+ struct sockaddr_storage bcast;
+ 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_list_find(struct interface *interfaces,
+ const struct sockaddr *ip,
+ bool check_mask)
+{
+ struct interface *i;
+
+ if (is_address_any(ip)) {
+ return interfaces;
+ }
+
+ for (i=interfaces;i;i=i->next) {
+ if (check_mask) {
+ if (same_net(ip, (struct sockaddr *)&i->ip, (struct sockaddr *)&i->netmask)) {
+ return i;
+ }
+ } else if (sockaddr_equal((struct sockaddr *)&i->ip, ip)) {
+ return i;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(TALLOC_CTX *mem_ctx, const struct iface_struct *ifs, struct interface **interfaces)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct interface *iface;
+
+ if (iface_list_find(*interfaces, (const struct sockaddr *)&ifs->ip, false)) {
+ DEBUG(3,("add_interface: not adding duplicate interface %s\n",
+ print_sockaddr(addr, sizeof(addr), &ifs->ip) ));
+ return;
+ }
+
+ if (ifs->ip.ss_family == AF_INET &&
+ !(ifs->flags & (IFF_BROADCAST|IFF_LOOPBACK))) {
+ DEBUG(3,("not adding non-broadcast interface %s\n",
+ ifs->name ));
+ return;
+ }
+
+ if (*interfaces != NULL) {
+ mem_ctx = *interfaces;
+ }
+
+ iface = talloc_zero(mem_ctx, struct interface);
+ if (iface == NULL) {
+ return;
+ }
+
+ iface->name = talloc_strdup(iface, ifs->name);
+ if (!iface->name) {
+ SAFE_FREE(iface);
+ return;
+ }
+ iface->flags = ifs->flags;
+ iface->ip = ifs->ip;
+ iface->netmask = ifs->netmask;
+ iface->bcast = ifs->bcast;
+
+ /* keep string versions too, to avoid people tripping over the implied
+ static in inet_ntoa() */
+ print_sockaddr(addr, sizeof(addr), &iface->ip);
+ DEBUG(4,("added interface %s ip=%s ",
+ iface->name, addr));
+ iface->ip_s = talloc_strdup(iface, addr);
+
+ print_sockaddr(addr, sizeof(addr),
+ &iface->bcast);
+ DEBUG(4,("bcast=%s ", addr));
+ iface->bcast_s = talloc_strdup(iface, addr);
+
+ print_sockaddr(addr, sizeof(addr),
+ &iface->netmask);
+ DEBUG(4,("netmask=%s\n", addr));
+ iface->nmask_s = talloc_strdup(iface, addr);
+
+ /*
+ this needs to be a ADD_END, as some tests (such as the
+ spoolss notify test) depend on the interfaces ordering
+ */
+ DLIST_ADD_END(*interfaces, iface);
+}
+
+/**
+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 sockaddr_storage ss;
+ struct sockaddr_storage ss_mask;
+ struct sockaddr_storage ss_net;
+ struct sockaddr_storage ss_bcast;
+ struct iface_struct ifs;
+ char *p;
+ int i;
+ bool added=false;
+ bool goodaddr = false;
+
+ /* 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],
+ local_interfaces);
+ added = true;
+ }
+ }
+ if (added) {
+ return;
+ }
+
+ p = strchr_m(token, ';');
+ if (p != NULL) {
+ /*
+ * skip smbd-specific extra data:
+ * link speed, capabilities, and interface index
+ */
+ *p = 0;
+ }
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (p == NULL) {
+ if (!interpret_string_addr(&ss, token, 0)) {
+ DEBUG(2, ("interpret_interface: Can't find address "
+ "for %s\n", token));
+ return;
+ }
+
+ for (i=0;i<total_probed;i++) {
+ if (sockaddr_equal((struct sockaddr *)&ss, (struct sockaddr *)&probed_ifaces[i].ip)) {
+ add_interface(mem_ctx, &probed_ifaces[i],
+ local_interfaces);
+ return;
+ }
+ }
+ DEBUG(2,("interpret_interface: "
+ "can't determine interface for %s\n",
+ token));
+ return;
+ }
+
+ /* parse it into an IP address/netmasklength pair */
+ *p = 0;
+ goodaddr = interpret_string_addr(&ss, token, 0);
+ *p++ = '/';
+
+ if (!goodaddr) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine interface for %s\n",
+ token));
+ return;
+ }
+
+ if (strlen(p) > 2) {
+ goodaddr = interpret_string_addr(&ss_mask, p, 0);
+ if (!goodaddr) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine netmask from %s\n",
+ p));
+ return;
+ }
+ } else {
+ int error = 0;
+
+ unsigned long val = smb_strtoul(p,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine netmask value from %s\n",
+ p));
+ return;
+ }
+ if (!make_netmask(&ss_mask, &ss, val)) {
+ DEBUG(2,("interpret_interface: "
+ "can't apply netmask value %lu from %s\n",
+ val,
+ p));
+ return;
+ }
+ }
+
+ make_bcast(&ss_bcast, &ss, &ss_mask);
+ make_net(&ss_net, &ss, &ss_mask);
+
+ /* Maybe the first component was a broadcast address. */
+ if (sockaddr_equal((struct sockaddr *)&ss_bcast, (struct sockaddr *)&ss) ||
+ sockaddr_equal((struct sockaddr *)&ss_net, (struct sockaddr *)&ss)) {
+ for (i=0;i<total_probed;i++) {
+ if (same_net((struct sockaddr *)&ss,
+ (struct sockaddr *)&probed_ifaces[i].ip,
+ (struct sockaddr *)&ss_mask)) {
+ /* Temporarily replace netmask on
+ * the detected interface - user knows
+ * best.... */
+ struct sockaddr_storage saved_mask =
+ probed_ifaces[i].netmask;
+ probed_ifaces[i].netmask = ss_mask;
+ DEBUG(2,("interpret_interface: "
+ "using netmask value %s from "
+ "config file on interface %s\n",
+ p,
+ probed_ifaces[i].name));
+ add_interface(mem_ctx, &probed_ifaces[i],
+ local_interfaces);
+ probed_ifaces[i].netmask = saved_mask;
+ return;
+ }
+ }
+ DEBUG(2,("interpret_interface: Can't determine ip for "
+ "broadcast address %s\n",
+ token));
+ return;
+ }
+
+ /* Just fake up the interface definition. User knows best. */
+
+ DEBUG(2,("interpret_interface: Adding interface %s\n",
+ token));
+
+ ZERO_STRUCT(ifs);
+ (void)strlcpy(ifs.name, token, sizeof(ifs.name));
+ ifs.flags = IFF_BROADCAST;
+ ifs.ip = ss;
+ ifs.netmask = ss_mask;
+ ifs.bcast = ss_bcast;
+ add_interface(mem_ctx, &ifs, local_interfaces);
+}
+
+
+/**
+load the list of network interfaces
+**/
+void load_interface_list(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct interface **local_interfaces)
+{
+ const char **ptr = lpcfg_interfaces(lp_ctx);
+ int i;
+ struct iface_struct *ifaces = NULL;
+ int total_probed;
+
+ *local_interfaces = NULL;
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(mem_ctx, &ifaces);
+
+ /* 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 (!is_loopback_addr((struct sockaddr *)&ifaces[i].ip)) {
+ add_interface(mem_ctx, &ifaces[i], 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"));
+ }
+ talloc_free(ifaces);
+}
+
+/**
+ how many interfaces do we have
+ **/
+int iface_list_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_list_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 the first IPv4 interface address we have registered
+ **/
+const char *iface_list_first_v4(struct interface *ifaces)
+{
+ struct interface *i;
+
+ for (i=ifaces; i; i=i->next) {
+ if (i->ip.ss_family == AF_INET) {
+ return i->ip_s;
+ }
+ }
+ return NULL;
+}
+
+/**
+ return the first IPv6 interface address we have registered
+ **/
+static const char *iface_list_first_v6(struct interface *ifaces)
+{
+ struct interface *i;
+
+#ifdef HAVE_IPV6
+ for (i=ifaces; i; i=i->next) {
+ if (i->ip.ss_family == AF_INET6) {
+ return i->ip_s;
+ }
+ }
+#endif
+ return NULL;
+}
+
+/**
+ check if an interface is IPv4
+ **/
+bool iface_list_n_is_v4(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->ip.ss_family == AF_INET;
+ }
+ return false;
+}
+
+/**
+ return bcast of the Nth interface
+ **/
+const char *iface_list_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_list_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_list_best_ip(struct interface *ifaces, const char *dest)
+{
+ struct interface *iface;
+ struct sockaddr_storage ss;
+
+ if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
+ return iface_list_n_ip(ifaces, 0);
+ }
+ iface = iface_list_find(ifaces, (const struct sockaddr *)&ss, true);
+ if (iface) {
+ return iface->ip_s;
+ }
+#ifdef HAVE_IPV6
+ if (ss.ss_family == AF_INET6) {
+ return iface_list_first_v6(ifaces);
+ }
+#endif
+ return iface_list_first_v4(ifaces);
+}
+
+/**
+ return true if an IP is one one of our local networks
+*/
+bool iface_list_is_local(struct interface *ifaces, const char *dest)
+{
+ struct sockaddr_storage ss;
+
+ if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (iface_list_find(ifaces, (const struct sockaddr *)&ss, true)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ return true if a IP matches a IP/netmask pair
+*/
+bool iface_list_same_net(const char *ip1, const char *ip2, const char *netmask)
+{
+ struct sockaddr_storage ip1_ss, ip2_ss, nm_ss;
+
+ if (!interpret_string_addr(&ip1_ss, ip1, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (!interpret_string_addr(&ip2_ss, ip2, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (!interpret_string_addr(&nm_ss, netmask, AI_NUMERICHOST)) {
+ return false;
+ }
+
+ return same_net((struct sockaddr *)&ip1_ss,
+ (struct sockaddr *)&ip2_ss,
+ (struct sockaddr *)&nm_ss);
+}
+
+/**
+ return the list of wildcard interfaces
+ this will include the IPv4 0.0.0.0, and may include IPv6 ::
+*/
+char **iface_list_wildcard(TALLOC_CTX *mem_ctx)
+{
+ char **ret;
+#ifdef HAVE_IPV6
+ ret = str_list_make(mem_ctx, "::,0.0.0.0", NULL);
+#else
+ ret = str_list_make(mem_ctx, "0.0.0.0", NULL);
+#endif
+ return ret;
+}
diff --git a/source4/lib/socket/netif.h b/source4/lib/socket/netif.h
new file mode 100644
index 0000000..1d90a4f
--- /dev/null
+++ b/source4/lib/socket/netif.h
@@ -0,0 +1,24 @@
+/*
+ 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"
+#include "lib/socket/interfaces.h"
+#include "lib/socket/netif_proto.h"
diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c
new file mode 100644
index 0000000..26f23f5
--- /dev/null
+++ b/source4/lib/socket/socket.c
@@ -0,0 +1,640 @@
+/*
+ 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"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/util/util_net.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_ void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data)
+{
+ /* this might be the socket_wrapper swrap_close() */
+ close(fd);
+}
+
+_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 (type == SOCKET_TYPE_STREAM &&
+ getenv("SOCKET_TESTNONBLOCK") != NULL) {
+ (*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 (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(TALLOC_CTX *mem_ctx,
+ 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(mem_ctx, 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_ struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a)
+{
+ struct tsocket_address *r;
+ int ret;
+
+ if (!a) {
+ return NULL;
+ }
+ if (a->sockaddr) {
+ ret = tsocket_address_bsd_from_sockaddr(mem_ctx,
+ a->sockaddr,
+ a->sockaddrlen,
+ &r);
+ } else {
+ ret = tsocket_address_inet_from_strings(mem_ctx,
+ a->family,
+ a->addr,
+ a->port,
+ &r);
+ }
+
+ if (ret != 0) {
+ return NULL;
+ }
+
+ return r;
+}
+
+_PUBLIC_ void socket_address_set_port(struct socket_address *a,
+ uint16_t port)
+{
+ if (a->sockaddr) {
+ set_sockaddr_port(a->sockaddr, port);
+ } else {
+ a->port = port;
+ }
+
+}
+
+_PUBLIC_ struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a)
+{
+ ssize_t ret;
+ struct sockaddr_storage ss;
+ size_t sslen = sizeof(ss);
+
+ ret = tsocket_address_bsd_sockaddr(a, (struct sockaddr *)(void *)&ss, sslen);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ return socket_address_from_sockaddr(mem_ctx, (struct sockaddr *)(void *)&ss, ret);
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_peer_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_my_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
+_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_common(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;
+ }
+
+ if (strcmp(family, "ip") == 0 && is_ipaddress_v6(host)) {
+ /* leaving as "ip" would force IPv4 */
+ family = "ipv6";
+ }
+
+ 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;
+ }
+ switch (sockaddr->sa_family) {
+ case AF_INET:
+ addr->family = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ addr->family = "ipv6";
+ break;
+#endif
+ case AF_UNIX:
+ addr->family = "unix";
+ break;
+ }
+ 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;
+}
+
+
+/*
+ Create a new socket_address from sockaddr_storage
+ */
+_PUBLIC_ struct socket_address *socket_address_from_sockaddr_storage(TALLOC_CTX *mem_ctx,
+ const struct sockaddr_storage *sockaddr,
+ uint16_t port)
+{
+ struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
+ char addr_str[INET6_ADDRSTRLEN+1];
+ const char *str;
+
+ if (!addr) {
+ return NULL;
+ }
+ addr->port = port;
+ switch (sockaddr->ss_family) {
+ case AF_INET:
+ addr->family = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ addr->family = "ipv6";
+ break;
+#endif
+ default:
+ talloc_free(addr);
+ return NULL;
+ }
+
+ str = print_sockaddr(addr_str, sizeof(addr_str), sockaddr);
+ if (str == NULL) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->addr = talloc_strdup(addr, str);
+ if (addr->addr == NULL) {
+ talloc_free(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+/* Copy a socket_address structure */
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr)
+{
+ struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ addr->family = oaddr->family;
+ if (oaddr->addr) {
+ addr->addr = talloc_strdup(addr, oaddr->addr);
+ if (!addr->addr) {
+ goto nomem;
+ }
+ }
+ addr->port = oaddr->port;
+ if (oaddr->sockaddr) {
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr,
+ oaddr->sockaddr,
+ oaddr->sockaddrlen);
+ if (!addr->sockaddr) {
+ goto nomem;
+ }
+ addr->sockaddrlen = oaddr->sockaddrlen;
+ }
+
+ return addr;
+
+nomem:
+ talloc_free(addr);
+ return NULL;
+}
+
+_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);
+ }
+
+#ifdef 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;
+}
+
+/*
+ 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 0000000..d0fb5e0
--- /dev/null
+++ b/source4/lib/socket/socket.h
@@ -0,0 +1,256 @@
+/*
+ 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
+
+struct tevent_context;
+struct tevent_fd;
+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_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;
+struct tsocket_address;
+
+/* 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(TALLOC_CTX *mem_ctx,
+ 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);
+struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a);
+struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a);
+struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct tsocket_address *socket_get_local_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);
+struct sockaddr_storage;
+struct socket_address *socket_address_from_sockaddr_storage(TALLOC_CTX *mem_ctx,
+ const struct sockaddr_storage *sockaddr,
+ uint16_t port);
+_PUBLIC_ void socket_address_set_port(struct socket_address *a,
+ uint16_t port);
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr);
+const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type);
+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 tevent_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 tevent_context *ev);
+
+struct socket_connect_multi_ex {
+ void *private_data;
+ struct tevent_req *(*establish_send)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct socket_context *sock,
+ struct socket_address *addr,
+ void *private_data);
+ NTSTATUS (*establish_recv)(struct tevent_req *req);
+};
+struct composite_context *socket_connect_multi_ex_send(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex);
+NTSTATUS socket_connect_multi_ex_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+NTSTATUS socket_connect_multi_ex(TALLOC_CTX *mem_ctx, const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex,
+ struct socket_context **result,
+ uint16_t *port);
+
+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 tevent_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 tevent_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 *sock, unsigned flags);
+
+void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data);
+
+extern bool testnonblock;
+
+#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 0000000..62dbf1d
--- /dev/null
+++ b/source4/lib/socket/socket_ip.c
@@ -0,0 +1,1033 @@
+/*
+ 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"
+#include "lib/util/util_net.h"
+
+#undef strcasecmp
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type);
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type);
+
+static NTSTATUS ipv4_init(struct socket_context *sock)
+{
+ 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_common(errno);
+ }
+
+ smb_set_close_on_exec(sock->fd);
+
+ sock->backend_name = "ipv4";
+ sock->family = AF_INET;
+
+ return NT_STATUS_OK;
+}
+
+static void ip_close(struct socket_context *sock)
+{
+ if (sock->fd != -1) {
+ close(sock->fd);
+ sock->fd = -1;
+ }
+}
+
+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_common(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix_common(error);
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(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_common(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_common(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(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_common(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_common(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(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, ret;
+
+ 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_common(errno);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(new_fd);
+
+
+ /* 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_common(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;
+ }
+ if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix_common(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_common(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_common(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_common(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;
+}
+
+#ifdef HAVE_IPV6
+
+static struct in6_addr interpret_addr6(const char *name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct in6_addr dest6;
+ const char *sp = name;
+ char *p;
+ int ret;
+
+ if (sp == NULL) return in6addr_any;
+
+ p = strchr_m(sp, '%');
+
+ if (strcasecmp(sp, "localhost") == 0) {
+ sp = "::1";
+ }
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+
+ if (p && (p > sp) && (if_nametoindex(p+1) != 0)) {
+ strlcpy(addr, sp,
+ MIN(PTR_DIFF(p,sp)+1,
+ sizeof(addr)));
+ sp = addr;
+ }
+
+ ret = inet_pton(AF_INET6, sp, &dest6);
+ if (ret > 0) {
+ return dest6;
+ }
+
+ return in6addr_any;
+}
+
+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_common(errno);
+ }
+
+ smb_set_close_on_exec(sock->fd);
+
+ 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_common(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_common(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_common(errno);
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+/*
+ fix the sin6_scope_id based on the address interface
+ */
+static void fix_scope_id(struct sockaddr_in6 *in6,
+ const char *address)
+{
+ const char *p = strchr(address, '%');
+ if (p != NULL) {
+ in6->sin6_scope_id = if_nametoindex(p+1);
+ }
+}
+
+
+static NTSTATUS ipv6_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ 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 {
+ int one = 1;
+ ip_addr = interpret_addr6(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = ip_addr;
+ my_addr.sin6_port = htons(my_address->port);
+ my_addr.sin6_family = PF_INET6;
+ fix_scope_id(&my_addr, my_address->addr);
+
+ /* when binding on ipv6 we always want to only bind on v6 */
+ ret = setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *)&one, sizeof(one));
+ if (ret != -1) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(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_in6 cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd, ret;
+
+ 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_common(errno);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+ smb_set_close_on_exec(new_fd);
+
+ /* 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_common(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 (memcmp(&addr.s6_addr, &in6addr_any,
+ sizeof(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_common(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(local, 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 0000000..d4946d8
--- /dev/null
+++ b/source4/lib/socket/socket_unix.c
@@ -0,0 +1,436 @@
+/*
+ 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"
+
+_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type);
+
+
+/*
+ approximate errno mapping
+*/
+static NTSTATUS unixdom_error(int ernum)
+{
+ return map_nt_error_from_unix_common(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_common(errno);
+ }
+ sock->private_data = NULL;
+
+ sock->backend_name = "unix";
+
+ smb_set_close_on_exec(sock->fd);
+
+ 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_common(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix_common(error);
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(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;
+ snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s", srv_address->addr);
+
+ 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;
+ snprintf(my_addr.sun_path, sizeof(my_addr.sun_path), "%s", my_address->addr);
+
+ 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);
+ }
+ }
+
+ 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, ret;
+
+ 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);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(new_fd);
+
+ (*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)
+{
+ struct sockaddr_un srv_addr;
+ const struct sockaddr *sa;
+ socklen_t sa_len;
+ ssize_t len;
+
+ *sendlen = 0;
+
+ if (dest->sockaddr) {
+ sa = dest->sockaddr;
+ sa_len = dest->sockaddrlen;
+ } else {
+ 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;
+ snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s",
+ dest->addr);
+ sa = (struct sockaddr *) &srv_addr;
+ sa_len = sizeof(srv_addr);
+ }
+
+ len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
+
+ /* retry once */
+ if (len == -1 && errno == EMSGSIZE) {
+ /* round up in 1K increments */
+ int bufsize = ((blob->length + 1023) & (~1023));
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, &bufsize,
+ sizeof(bufsize)) == -1)
+ {
+ return map_nt_error_from_unix_common(EMSGSIZE);
+ }
+ len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
+ }
+
+ if (len == -1) {
+ return map_nt_error_from_unix_common(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_un *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_un);
+ 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_un *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_un);
+ 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_common(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 0000000..1df96e3
--- /dev/null
+++ b/source4/lib/socket/testsuite.c
@@ -0,0 +1,194 @@
+/*
+ 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 "torture/local/proto.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_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_DGRAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_DGRAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_list_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_list_best_ip(ifaces, "127.0.0.1")) == 0,
+ talloc_asprintf(tctx,
+ "Expected server address of %s but got %s",
+ iface_list_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 tevent_context *ev = tctx->ev;
+ struct interface *ifaces;
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_STREAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_STREAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_list_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_list_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, 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;
+}
diff --git a/source4/lib/socket/wscript_build b/source4/lib/socket/wscript_build
new file mode 100644
index 0000000..e243824
--- /dev/null
+++ b/source4/lib/socket/wscript_build
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('netif',
+ source='interface.c',
+ deps='samba-util interfaces samba-hostconfig',
+ private_library=True,
+ autoproto='netif_proto.h'
+ )
+
+bld.SAMBA_MODULE('socket_ip',
+ source='socket_ip.c',
+ subsystem='samba_socket',
+ deps='samba-errors',
+ internal_module=True
+ )
+
+bld.SAMBA_MODULE('socket_unix',
+ source='socket_unix.c',
+ subsystem='samba_socket',
+ deps='talloc',
+ internal_module=True
+ )
+
+bld.SAMBA_SUBSYSTEM('samba_socket',
+ source='socket.c access.c connect_multi.c connect.c',
+ public_deps='talloc LIBTSOCKET',
+ deps='cli_composite LIBCLI_RESOLVE socket_ip socket_unix access'
+ )
+