summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2010-12-12 18:55:06 +0100
committerKarolin Seeger <kseeger@samba.org>2011-01-13 17:58:53 +0100
commit607c00d9aff86a88afad1e5929fbc70414472814 (patch)
tree1b172b5cf06e22db0b289f4ed3cc71aa16e6cb83 /source3
parent14e272c5022cb74b47d1e6021c8d919e3b354342 (diff)
downloadsamba-607c00d9aff86a88afad1e5929fbc70414472814.tar.gz
s3: Add an async smbsock_connect
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. (cherry picked from commit 35bbc2231760badaf0debc9f8f39ebdf00cfe8ad)
Diffstat (limited to 'source3')
-rw-r--r--source3/Makefile.in1
-rw-r--r--source3/include/proto.h11
-rw-r--r--source3/libsmb/smbsock_connect.c308
3 files changed, 320 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index b863e363e2a..82e108ac205 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -473,6 +473,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) \
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 35ac19b38b7..fbb55ae5e0a 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -7282,6 +7282,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
index 00000000000..33557957644
--- /dev/null
+++ b/source3/libsmb/smbsock_connect.c
@@ -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;
+}