summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2010-12-12 18:55:06 +0100
committerVolker Lendecke <vl@samba.org>2010-12-22 16:39:15 +0100
commit49cd92b2186addebba79efffc435083e6086aab1 (patch)
treeefa30cec98b918b0a35a5fbfe73fd1b3915f0600 /source3/libsmb
parent02a74fd36db962eaf6da14b920bccbee7d25f07e (diff)
downloadsamba-49cd92b2186addebba79efffc435083e6086aab1.tar.gz
samba-49cd92b2186addebba79efffc435083e6086aab1.tar.bz2
samba-49cd92b2186addebba79efffc435083e6086aab1.zip
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.
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/smbsock_connect.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/source3/libsmb/smbsock_connect.c b/source3/libsmb/smbsock_connect.c
new file mode 100644
index 0000000000..3355795764
--- /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;
+}