s3: Add an async smbsock_connect
authorVolker Lendecke <vl@samba.org>
Sun, 12 Dec 2010 17:55:06 +0000 (18:55 +0100)
committerVolker Lendecke <vl@samba.org>
Wed, 22 Dec 2010 16:04:14 +0000 (09:04 -0700)
This connects to 445 and after 5 milliseconds also to 139. It treats a netbios
session setup failure as equivalent as a TCP connect failure. So if 139 is
faster but fails the nb session setup, the 445 still has the chance to succeed.

source3/Makefile.in
source3/include/proto.h
source3/libsmb/smbsock_connect.c [new file with mode: 0644]

index 521636c8140012548d6b30cafb175f4977265198..720730297bf507f68ca5aad48087797815cf8a53 100644 (file)
@@ -478,6 +478,7 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
              libsmb/credentials.o \
             libsmb/clioplock.o libsmb/clirap2.o \
             libsmb/smb_seal.o libsmb/async_smb.o \
+            libsmb/smbsock_connect.o \
             $(LIBSAMBA_OBJ) \
             $(LIBNMB_OBJ) \
             $(LIBNBT_OBJ) \
index 034fe03c98a81656563f58039ef168243fbd5e7d..266bc633b16382778e273e461522ba185bf44b50 100644 (file)
@@ -7277,6 +7277,17 @@ void *avahi_start_register(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 
 /* Misc protos */
 
+struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       const struct sockaddr_storage *addr,
+                                       const char *called_name,
+                                       const char *calling_name);
+NTSTATUS smbsock_connect_recv(struct tevent_req *req, int *sock,
+                             uint16_t *port);
+NTSTATUS smbsock_connect(const struct sockaddr_storage *addr,
+                        const char *called_name, const char *calling_name,
+                        int *pfd, uint16_t *port);
+
 /* The following definitions come from rpc_server/srv_samr_nt.c */
 NTSTATUS access_check_object( SEC_DESC *psd, NT_USER_TOKEN *token,
                                SE_PRIV *rights, uint32 rights_mask,
diff --git a/source3/libsmb/smbsock_connect.c b/source3/libsmb/smbsock_connect.c
new file mode 100644 (file)
index 0000000..3355795
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+   Unix SMB/CIFS implementation.
+   Connect to 445 and 139/nbsesssetup
+   Copyright (C) Volker Lendecke 2010
+
+   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/async_req/async_sock.h"
+#include "async_smb.h"
+
+struct nb_connect_state {
+       struct tevent_context *ev;
+       int sock;
+       struct nmb_name called;
+       struct nmb_name calling;
+};
+
+static int nb_connect_state_destructor(struct nb_connect_state *state);
+static void nb_connect_connected(struct tevent_req *subreq);
+static void nb_connect_done(struct tevent_req *subreq);
+
+static struct tevent_req *nb_connect_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         const struct sockaddr_storage *addr,
+                                         const char *called_name,
+                                         int called_type,
+                                         const char *calling_name,
+                                         int calling_type)
+{
+       struct tevent_req *req, *subreq;
+       struct nb_connect_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct nb_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       make_nmb_name(&state->called, called_name, called_type);
+       make_nmb_name(&state->calling, calling_name, calling_type);
+       state->sock = -1;
+
+       talloc_set_destructor(state, nb_connect_state_destructor);
+
+       subreq = open_socket_out_send(state, ev, addr, 139, 5000);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, nb_connect_connected, req);
+       return req;
+}
+
+static int nb_connect_state_destructor(struct nb_connect_state *state)
+{
+       if (state->sock != -1) {
+               close(state->sock);
+       }
+       return 0;
+}
+
+static void nb_connect_connected(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct nb_connect_state *state = tevent_req_data(
+               req, struct nb_connect_state);
+       NTSTATUS status;
+
+       status = open_socket_out_recv(subreq, &state->sock);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       subreq = cli_session_request_send(state, state->ev, state->sock,
+                                         &state->called, &state->calling);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, nb_connect_done, req);
+}
+
+static void nb_connect_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       bool ret;
+       int err;
+       uint8_t resp;
+
+       ret = cli_session_request_recv(subreq, &err, &resp);
+       TALLOC_FREE(subreq);
+       if (!ret) {
+               tevent_req_nterror(req, map_nt_error_from_unix(err));
+               return;
+       }
+       if (resp != 0x82) {
+               tevent_req_nterror(req, NT_STATUS_RESOURCE_NAME_NOT_FOUND);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS nb_connect_recv(struct tevent_req *req, int *sock)
+{
+       struct nb_connect_state *state = tevent_req_data(
+               req, struct nb_connect_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *sock = state->sock;
+       state->sock = -1;
+       return NT_STATUS_OK;
+}
+
+struct smbsock_connect_state {
+       struct tevent_context *ev;
+       const struct sockaddr_storage *addr;
+       const char *called_name;
+       const char *calling_name;
+       struct tevent_req *req_139;
+       struct tevent_req *req_445;
+       int sock;
+       uint16_t port;
+};
+
+static int smbsock_connect_state_destructor(
+       struct smbsock_connect_state *state);
+static void smbsock_connect_connected(struct tevent_req *subreq);
+static void smbsock_connect_do_139(struct tevent_req *subreq);
+
+struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       const struct sockaddr_storage *addr,
+                                       const char *called_name,
+                                       const char *calling_name)
+{
+       struct tevent_req *req, *subreq;
+       struct smbsock_connect_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct smbsock_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->addr = addr;
+       state->sock = -1;
+       state->called_name =
+               (called_name != NULL) ? called_name : "*SMBSERVER";
+       state->calling_name =
+               (calling_name != NULL) ? calling_name : global_myname();
+
+       talloc_set_destructor(state, smbsock_connect_state_destructor);
+
+       state->req_445 = open_socket_out_send(state, ev, addr, 445, 5000);
+       if (tevent_req_nomem(state->req_445, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(state->req_445, smbsock_connect_connected,
+                               req);
+
+       /*
+        * After 5 msecs, fire the 139 request
+        */
+       subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 5000));
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(state->req_445);
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smbsock_connect_do_139, req);
+       return req;
+}
+
+static int smbsock_connect_state_destructor(
+       struct smbsock_connect_state *state)
+{
+       if (state->sock != -1) {
+               close(state->sock);
+       }
+       return 0;
+}
+
+static void smbsock_connect_do_139(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smbsock_connect_state *state = tevent_req_data(
+               req, struct smbsock_connect_state);
+       bool ret;
+
+       ret = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ret) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+       state->req_139 = nb_connect_send(state, state->ev, state->addr,
+                                        state->called_name, 0x20,
+                                        state->calling_name, 0x0);
+       if (tevent_req_nomem(state->req_139, req)) {
+               return;
+       }
+       tevent_req_set_callback(state->req_139, smbsock_connect_connected,
+                               req);
+}
+
+static void smbsock_connect_connected(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smbsock_connect_state *state = tevent_req_data(
+               req, struct smbsock_connect_state);
+       struct tevent_req *unfinished_req;
+       NTSTATUS status;
+
+       if (subreq == state->req_445) {
+
+               status = open_socket_out_recv(subreq, &state->sock);
+               TALLOC_FREE(state->req_445);
+               unfinished_req = state->req_139;
+               state->port = 445;
+
+       } else if (subreq == state->req_139) {
+
+               status = nb_connect_recv(subreq, &state->sock);
+               TALLOC_FREE(state->req_139);
+               unfinished_req = state->req_445;
+               state->port = 139;
+
+       } else {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(unfinished_req);
+               state->req_139 = NULL;
+               state->req_445 = NULL;
+               tevent_req_done(req);
+               return;
+       }
+       if (unfinished_req == NULL) {
+               /*
+                * Both requests failed
+                */
+               tevent_req_nterror(req, status);
+               return;
+       }
+       /*
+        * Do nothing, wait for the second request to come here.
+        */
+}
+
+NTSTATUS smbsock_connect_recv(struct tevent_req *req, int *sock,
+                         uint16_t *port)
+{
+       struct smbsock_connect_state *state = tevent_req_data(
+               req, struct smbsock_connect_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *sock = state->sock;
+       state->sock = -1;
+       *port = state->port;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS smbsock_connect(const struct sockaddr_storage *addr,
+                        const char *called_name, const char *calling_name,
+                        int *pfd, uint16_t *port)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = smbsock_connect_send(frame, ev, addr, called_name, calling_name);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = smbsock_connect_recv(req, pfd, port);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}