diff options
-rw-r--r-- | source4/libcli/cliconnect.c | 12 | ||||
-rw-r--r-- | source4/libcli/raw/clisocket.c | 280 | ||||
-rw-r--r-- | source4/libcli/smb_composite/connect.c | 40 |
3 files changed, 240 insertions, 92 deletions
diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c index d670324c88..e25a400985 100644 --- a/source4/libcli/cliconnect.c +++ b/source4/libcli/cliconnect.c @@ -58,7 +58,17 @@ bool smbcli_transport_establish(struct smbcli_state *cli, struct nbt_name *calling, struct nbt_name *called) { - return smbcli_transport_connect(cli->transport, calling, called); + uint32_t timeout_msec = cli->transport->options.request_timeout * 1000; + NTSTATUS status; + + status = smbcli_transport_connect(cli->transport->socket, + timeout_msec, + calling, called); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + return true; } /* wrapper around smb_raw_negotiate() */ diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c index 70a83a493f..d0867b2023 100644 --- a/source4/libcli/raw/clisocket.c +++ b/source4/libcli/raw/clisocket.c @@ -21,6 +21,9 @@ */ #include "includes.h" +#include "system/network.h" +#include "../lib/async_req/async_sock.h" +#include "../lib/util/tevent_ntstatus.h" #include "lib/events/events.h" #include "libcli/raw/libcliraw.h" #include "libcli/composite/composite.h" @@ -28,128 +31,251 @@ #include "libcli/resolve/resolve.h" #include "param/param.h" #include "libcli/raw/raw_proto.h" +#include "../libcli/smb/read_smb.h" + +struct smbcli_transport_connect_state { + struct tevent_context *ev; + struct smbcli_socket *sock; + uint8_t *request; + struct iovec iov; + uint8_t *response; +}; -/* - send a session request -*/ -struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport, - struct nbt_name *calling, - struct nbt_name *called) +static void smbcli_transport_connect_writev_done(struct tevent_req *subreq); +static void smbcli_transport_connect_read_smb_done(struct tevent_req *subreq); + +struct tevent_req *smbcli_transport_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbcli_socket *sock, + uint32_t timeout_msec, + struct nbt_name *calling, + struct nbt_name *called) { - uint8_t *p; - struct smbcli_request *req; + struct tevent_req *req; + struct smbcli_transport_connect_state *state; + struct tevent_req *subreq; DATA_BLOB calling_blob, called_blob; - TALLOC_CTX *tmp_ctx = talloc_new(transport); + uint8_t *p; NTSTATUS status; - status = nbt_name_dup(transport, called, &transport->called); - if (!NT_STATUS_IS_OK(status)) goto failed; - - status = nbt_name_to_blob(tmp_ctx, &calling_blob, calling); - if (!NT_STATUS_IS_OK(status)) goto failed; + req = tevent_req_create(mem_ctx, &state, + struct smbcli_transport_connect_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->sock = sock; + + if (sock->port != 139) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } - status = nbt_name_to_blob(tmp_ctx, &called_blob, called); - if (!NT_STATUS_IS_OK(status)) goto failed; + status = nbt_name_to_blob(state, &calling_blob, calling); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } - /* allocate output buffer */ - req = smbcli_request_setup_nonsmb(transport, - NBT_HDR_SIZE + - calling_blob.length + called_blob.length); - if (req == NULL) goto failed; + status = nbt_name_to_blob(state, &called_blob, called); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->request = talloc_array(state, uint8_t, + NBT_HDR_SIZE + + called_blob.length + + calling_blob.length); + if (tevent_req_nomem(state->request, req)) { + return tevent_req_post(req, ev); + } /* put in the destination name */ - p = req->out.buffer + NBT_HDR_SIZE; + p = state->request + NBT_HDR_SIZE; memcpy(p, called_blob.data, called_blob.length); p += called_blob.length; memcpy(p, calling_blob.data, calling_blob.length); p += calling_blob.length; - _smb_setlen_nbt(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE); - SCVAL(req->out.buffer,0,0x81); + _smb_setlen_nbt(state->request, + PTR_DIFF(p, state->request) - NBT_HDR_SIZE); + SCVAL(state->request, 0, NBSSrequest); + + state->iov.iov_len = talloc_array_length(state->request); + state->iov.iov_base = (void *)state->request; - if (!smbcli_request_send(req)) { - smbcli_request_destroy(req); - goto failed; + subreq = writev_send(state, ev, NULL, + sock->sock->fd, + true, /* err_on_readability */ + &state->iov, 1); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, + smbcli_transport_connect_writev_done, + req); - talloc_free(tmp_ctx); - return req; + if (timeout_msec > 0) { + struct timeval endtime; -failed: - talloc_free(tmp_ctx); - return NULL; + endtime = timeval_current_ofs_msec(timeout_msec); + if (!tevent_req_set_endtime(req, ev, endtime)) { + return tevent_req_post(req, ev); + } + } + + return req; } -/* - map a session request error to a NTSTATUS - */ -static NTSTATUS map_session_refused_error(uint8_t error) +static void smbcli_transport_connect_writev_done(struct tevent_req *subreq) { - switch (error) { - case 0x80: - case 0x81: - return NT_STATUS_REMOTE_NOT_LISTENING; - case 0x82: - return NT_STATUS_RESOURCE_NAME_NOT_FOUND; - case 0x83: - return NT_STATUS_REMOTE_RESOURCES; + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbcli_transport_connect_state *state = + tevent_req_data(req, + struct smbcli_transport_connect_state); + ssize_t ret; + int err; + + ret = writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + NTSTATUS status = map_nt_error_from_unix_common(err); + + close(state->sock->sock->fd); + state->sock->sock->fd = -1; + + tevent_req_nterror(req, status); + return; } - return NT_STATUS_UNEXPECTED_IO_ERROR; -} + subreq = read_smb_send(state, state->ev, + state->sock->sock->fd); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + smbcli_transport_connect_read_smb_done, + req); +} -/* - finish a smbcli_transport_connect() -*/ -NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req) +static void smbcli_transport_connect_read_smb_done(struct tevent_req *subreq) { + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbcli_transport_connect_state *state = + tevent_req_data(req, + struct smbcli_transport_connect_state); + ssize_t ret; + int err; NTSTATUS status; + uint8_t error; + + ret = read_smb_recv(subreq, state, + &state->response, &err); + if (ret == -1) { + status = map_nt_error_from_unix_common(err); + + close(state->sock->sock->fd); + state->sock->sock->fd = -1; - if (!smbcli_request_receive(req)) { - smbcli_request_destroy(req); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + tevent_req_nterror(req, status); + return; } - switch (CVAL(req->in.buffer,0)) { - case 0x82: - status = NT_STATUS_OK; - break; - case 0x83: - status = map_session_refused_error(CVAL(req->in.buffer,4)); + if (ret < 4) { + close(state->sock->sock->fd); + state->sock->sock->fd = -1; + + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + switch (CVAL(state->response, 0)) { + case NBSSpositive: + tevent_req_done(req); + return; + + case NBSSnegative: + if (ret < 5) { + close(state->sock->sock->fd); + state->sock->sock->fd = -1; + + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + error = CVAL(state->response, 4); + switch (error) { + case 0x80: + case 0x81: + status = NT_STATUS_REMOTE_NOT_LISTENING; + break; + case 0x82: + status = NT_STATUS_RESOURCE_NAME_NOT_FOUND; + break; + case 0x83: + status = NT_STATUS_REMOTE_RESOURCES; + break; + default: + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + break; + } break; - case 0x84: + + case NBSSretarget: DEBUG(1,("Warning: session retarget not supported\n")); status = NT_STATUS_NOT_SUPPORTED; break; + default: - status = NT_STATUS_UNEXPECTED_IO_ERROR; + status = NT_STATUS_INVALID_NETWORK_RESPONSE; break; } - smbcli_request_destroy(req); - return status; -} + close(state->sock->sock->fd); + state->sock->sock->fd = -1; + tevent_req_nterror(req, status); +} -/* - send a session request (if needed) -*/ -bool smbcli_transport_connect(struct smbcli_transport *transport, - struct nbt_name *calling, - struct nbt_name *called) +NTSTATUS smbcli_transport_connect_recv(struct tevent_req *req) { - struct smbcli_request *req; - NTSTATUS status; + return tevent_req_simple_recv_ntstatus(req); +} - if (transport->socket->port == 445) { - return true; +NTSTATUS smbcli_transport_connect(struct smbcli_socket *sock, + uint32_t timeout_msec, + struct nbt_name *calling, + struct nbt_name *called) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + bool ok; + + ev = tevent_context_init(frame); + if (ev == NULL) { + goto fail; } - - req = smbcli_transport_connect_send(transport, + req = smbcli_transport_connect_send(frame, ev, sock, + timeout_msec, calling, called); + if (req == NULL) { + goto fail; + } + ok = tevent_req_poll(req, ev); + if (!ok) { + status = map_nt_error_from_unix_common(errno); + goto fail; + } status = smbcli_transport_connect_recv(req); - return NT_STATUS_IS_OK(status); + fail: + TALLOC_FREE(frame); + return status; } struct sock_connect_state { diff --git a/source4/libcli/smb_composite/connect.c b/source4/libcli/smb_composite/connect.c index d36bf26914..306b96c167 100644 --- a/source4/libcli/smb_composite/connect.c +++ b/source4/libcli/smb_composite/connect.c @@ -52,11 +52,13 @@ struct connect_state { struct smb_composite_sesssetup *io_setup; struct smbcli_request *req; struct composite_context *creq; + struct tevent_req *subreq; }; static void request_handler(struct smbcli_request *); static void composite_handler(struct composite_context *); +static void subreq_handler(struct tevent_req *subreq); /* a tree connect request has completed @@ -301,7 +303,8 @@ static NTSTATUS connect_session_request(struct composite_context *c, struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); NTSTATUS status; - status = smbcli_transport_connect_recv(state->req); + status = smbcli_transport_connect_recv(state->subreq); + TALLOC_FREE(state->subreq); NT_STATUS_NOT_OK_RETURN(status); /* next step is a negprot */ @@ -317,6 +320,7 @@ static NTSTATUS connect_socket(struct composite_context *c, struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); NTSTATUS status; struct nbt_name calling, called; + uint32_t timeout_msec = io->in.options.request_timeout * 1000; status = smbcli_sock_connect_recv(state->creq, state, &state->sock); NT_STATUS_NOT_OK_RETURN(status); @@ -340,21 +344,18 @@ static NTSTATUS connect_socket(struct composite_context *c, nbt_choose_called_name(state, &called, io->in.called_name, NBT_NAME_SERVER); - /* we have a connected socket - next step is a session - request, if needed. Port 445 doesn't need it, so it goes - straight to the negprot */ - if (state->sock->port == 445) { - status = nbt_name_dup(state->transport, &called, - &state->transport->called); - NT_STATUS_NOT_OK_RETURN(status); - return connect_send_negprot(c, io); - } + status = nbt_name_dup(state->transport, &called, + &state->transport->called); + NT_STATUS_NOT_OK_RETURN(status); - state->req = smbcli_transport_connect_send(state->transport, &calling, &called); - NT_STATUS_HAVE_NO_MEMORY(state->req); + state->subreq = smbcli_transport_connect_send(state, + state->transport->ev, + state->transport->socket, + timeout_msec, + &calling, &called); + NT_STATUS_HAVE_NO_MEMORY(state->subreq); - state->req->async.fn = request_handler; - state->req->async.private_data = c; + tevent_req_set_callback(state->subreq, subreq_handler, c); state->stage = CONNECT_SESSION_REQUEST; return NT_STATUS_OK; @@ -421,6 +422,17 @@ static void composite_handler(struct composite_context *creq) } /* + handler for completion of a tevent_req sub-request +*/ +static void subreq_handler(struct tevent_req *subreq) +{ + struct composite_context *c = + tevent_req_callback_data(subreq, + struct composite_context); + state_handler(c); +} + +/* a function to establish a smbcli_tree from scratch */ struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io, |