ncacn_http: Client implementation
authorSamuel Cabrero <samuelcabrero@kernevil.me>
Tue, 16 Sep 2014 14:41:27 +0000 (16:41 +0200)
committerStefan Metzmacher <metze@samba.org>
Mon, 22 Sep 2014 21:09:08 +0000 (23:09 +0200)
Signed-off-by: Samuel Cabrero <samuelcabrero@kernevil.me>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/librpc/rpc/dcerpc.h
source4/librpc/rpc/dcerpc_roh.c [new file with mode: 0644]
source4/librpc/rpc/dcerpc_roh.h [new file with mode: 0644]
source4/librpc/rpc/dcerpc_roh_channel_in.c [new file with mode: 0644]
source4/librpc/rpc/dcerpc_roh_channel_out.c [new file with mode: 0644]
source4/librpc/wscript_build

index 18be157967f6b35a4d06fa228d6fcc536f04234f..a7d16947707588ad0b2108a33f9cca9cf9c7a5b4 100644 (file)
@@ -139,6 +139,8 @@ struct smb2_tree;
 struct smbXcli_conn;
 struct smbXcli_session;
 struct smbXcli_tcon;
+struct roh_connection;
+struct tstream_tls_params;
 struct socket_address;
 
 NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx, 
diff --git a/source4/librpc/rpc/dcerpc_roh.c b/source4/librpc/rpc/dcerpc_roh.c
new file mode 100644 (file)
index 0000000..0907294
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   [MS-RPCH] - RPC over HTTP client
+
+   Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+
+   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/events/events.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/tls/tls.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "tsocket/tsocket.h"
+#include "tsocket/tsocket_internal.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+static ssize_t tstream_roh_pending_bytes(struct tstream_context *stream);
+static struct tevent_req * tstream_roh_readv_send(
+               TALLOC_CTX *mem_ctx,
+               struct tevent_context *ev,
+               struct tstream_context *stream,
+               struct iovec *vector,
+               size_t count);
+static int tstream_roh_readv_recv(struct tevent_req *req, int *perrno);
+static struct tevent_req * tstream_roh_writev_send(
+               TALLOC_CTX *mem_ctx,
+               struct tevent_context *ev,
+               struct tstream_context *stream,
+               const struct iovec *vector,
+               size_t count);
+static int tstream_roh_writev_recv(struct tevent_req *req, int *perrno);
+static struct tevent_req * tstream_roh_disconnect_send(
+               TALLOC_CTX *mem_ctx,
+               struct tevent_context *ev,
+               struct tstream_context *stream);
+static int tstream_roh_disconnect_recv(struct tevent_req *req, int *perrno);
+
+static const struct tstream_context_ops tstream_roh_ops = {
+       .name                   = "roh",
+       .pending_bytes          = tstream_roh_pending_bytes,
+       .readv_send             = tstream_roh_readv_send,
+       .readv_recv             = tstream_roh_readv_recv,
+       .writev_send            = tstream_roh_writev_send,
+       .writev_recv            = tstream_roh_writev_recv,
+       .disconnect_send        = tstream_roh_disconnect_send,
+       .disconnect_recv        = tstream_roh_disconnect_recv,
+};
+
+struct tstream_roh_context {
+       struct roh_connection *roh_conn;
+};
+
+struct roh_open_connection_state {
+       struct tevent_req               *req;
+       struct tevent_context           *event_ctx;
+       struct cli_credentials          *credentials;
+       struct resolve_context          *resolve_ctx;
+       const char                      **rpcproxy_addresses;
+       unsigned int                    rpcproxy_address_index;
+
+       struct dcecli_connection        *conn;
+       bool                            tls;
+
+       const char                      *rpc_proxy;
+       unsigned int                    rpc_proxy_port;
+       const char                      *rpc_server;
+       unsigned int                    rpc_server_port;
+       const char                      *target_hostname;
+
+       struct roh_connection           *roh;
+       struct tstream_tls_params       *tls_params;
+       struct loadparm_context         *lp_ctx;
+       bool                            use_ntlm;
+};
+
+NTSTATUS dcerpc_pipe_open_roh_recv(struct tevent_req *req,
+                                  TALLOC_CTX *mem_ctx,
+                                  struct tstream_context **stream,
+                                  struct tevent_queue **queue)
+{
+       struct roh_open_connection_state *state;
+       struct tstream_roh_context *roh_stream_ctx;
+       NTSTATUS status;
+
+       state = tevent_req_data(req, struct roh_open_connection_state);
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *stream = tstream_context_create(mem_ctx, &tstream_roh_ops,
+                                        &roh_stream_ctx,
+                                        struct tstream_roh_context,
+                                        __location__);
+       if (!stream) {
+               tevent_req_received(req);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ZERO_STRUCTP(roh_stream_ctx);
+
+       roh_stream_ctx->roh_conn = talloc_move(mem_ctx, &state->roh);
+       *queue = roh_stream_ctx->roh_conn->default_channel_in->send_queue;
+
+       tevent_req_received(req);
+
+       return NT_STATUS_OK;
+}
+
+static void roh_continue_resolve_name(struct composite_context *ctx);
+
+/**
+ * Send rpc pipe open request to given host:port using http transport
+ */
+struct tevent_req *dcerpc_pipe_open_roh_send(struct dcecli_connection *conn,
+                                            const char *localaddr,
+                                            const char *rpc_server,
+                                            uint32_t rpc_server_port,
+                                            const char *rpc_proxy,
+                                            uint32_t rpc_proxy_port,
+                                            const char *http_proxy,
+                                            uint32_t http_proxy_port,
+                                            bool use_tls,
+                                            bool use_proxy,
+                                            struct cli_credentials *credentials,
+                                            struct resolve_context *resolve_ctx,
+                                            struct loadparm_context *lp_ctx,
+                                            bool use_ntlm)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct composite_context                *ctx;
+       struct roh_open_connection_state        *state;
+       struct nbt_name                         name;
+
+       req = tevent_req_create(conn, &state, struct roh_open_connection_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       /* Set state fields */
+       state->req = req;
+       state->event_ctx = conn->event_ctx;
+       state->lp_ctx = lp_ctx,
+       state->credentials = credentials;
+       state->conn = conn;
+       state->tls = use_tls;
+
+       /* Initialize connection structure (3.2.1.3) */
+       /* TODO Initialize virtual connection cookie table */
+       state->rpc_server = talloc_strdup(state, rpc_server);
+       state->rpc_server_port = rpc_server_port;
+       state->rpc_proxy = talloc_strdup(state, rpc_proxy);
+       state->rpc_proxy_port = rpc_proxy_port;
+       state->use_ntlm = use_ntlm;
+
+       state->roh = talloc_zero(state, struct roh_connection);
+       state->roh->protocol_version = ROH_V2;
+       state->roh->connection_state = ROH_STATE_OPEN_START;
+       state->roh->connection_cookie = GUID_random();
+       state->roh->association_group_id_cookie = GUID_random();
+
+       /* Additional initialization steps (3.2.2.3) */
+       state->roh->proxy_use = use_proxy;
+       state->roh->current_keep_alive_time = 0;
+       state->roh->current_keep_alive_interval = 0;
+
+       /* Initialize TLS */
+       if (use_tls) {
+               status = tstream_tls_params_client(state->roh, NULL, NULL,
+                                                  &state->tls_params);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("%s: Failed tstream_tls_params_client - %s\n",
+                                __func__, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, conn->event_ctx);
+               }
+       }
+
+       /* Resolve RPC proxy server name */
+       make_nbt_name_server(&name, state->rpc_proxy);
+       ctx = resolve_name_send(resolve_ctx, state, &name, state->event_ctx);
+       if (tevent_req_nomem(ctx, req)) {
+               return tevent_req_post(req, state->event_ctx);
+       }
+       ctx->async.fn = roh_continue_resolve_name;
+       ctx->async.private_data = state;
+
+       return req;
+}
+
+static void roh_connect_channel_in_done(struct tevent_req *subreq);
+static void roh_continue_resolve_name(struct composite_context *ctx)
+{
+       NTSTATUS                                status;
+       struct roh_open_connection_state        *state;
+       struct tevent_req                       *subreq;
+
+       state = talloc_get_type_abort(ctx->async.private_data,
+                                     struct roh_open_connection_state);
+       status = resolve_name_multiple_recv(ctx, state,
+                                           &state->rpcproxy_addresses);
+       if (tevent_req_nterror(state->req, status)) {
+               DEBUG(2, ("%s: No server found: %s\n", __func__,
+                         nt_errstr(status)));
+               return;
+       }
+
+       state->rpcproxy_address_index = 0;
+       if (state->rpcproxy_addresses[state->rpcproxy_address_index] == NULL) {
+               DEBUG(2, ("%s: No server found\n", __func__));
+               tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       /*
+        * TODO Determine proxy use
+        * If state->roh->proxy_use == true, the client has requested to
+        * always use local proxy. Otherwise, run the proxy use discovery
+        */
+       state->roh->connection_state = ROH_STATE_OPEN_START;
+       subreq = roh_connect_channel_in_send(state,
+                                            state->event_ctx,
+                                            state->rpcproxy_addresses[state->rpcproxy_address_index],
+                                            state->rpc_proxy_port,
+                                            state->credentials,
+                                            state->roh, state->tls,
+                                            state->tls_params);
+       if (tevent_req_nomem(subreq, state->req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_connect_channel_in_done, state->req);
+}
+
+static void roh_connect_channel_out_done(struct tevent_req *);
+static void roh_connect_channel_in_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_connect_channel_in_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = roh_connect_channel_out_send(state,
+                                             state->event_ctx,
+                                             state->rpcproxy_addresses[state->rpcproxy_address_index],
+                                             state->rpc_proxy_port,
+                                             state->credentials,
+                                             state->roh,
+                                             state->tls,
+                                             state->tls_params);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_connect_channel_out_done, req);
+}
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *);
+static void roh_connect_channel_out_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_connect_channel_out_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = roh_send_RPC_DATA_IN_send(state, state->lp_ctx,
+                                          state->event_ctx,
+                                          state->credentials,
+                                          state->roh,
+                                          state->rpc_server,
+                                          state->rpc_server_port,
+                                          state->rpc_proxy,
+                                          state->use_ntlm);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_send_RPC_DATA_IN_done, req);
+}
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *);
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_send_RPC_DATA_IN_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = roh_send_RPC_DATA_OUT_send(state,
+                                           state->lp_ctx,
+                                           state->event_ctx,
+                                           state->credentials,
+                                           state->roh,
+                                           state->rpc_server,
+                                           state->rpc_server_port,
+                                           state->rpc_proxy,
+                                           state->use_ntlm);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_send_RPC_DATA_OUT_done, req);
+}
+
+static void roh_send_CONN_A1_done(struct tevent_req *);
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_send_RPC_DATA_OUT_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = roh_send_CONN_A1_send(state, state->event_ctx, state->roh);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_send_CONN_A1_done, req);
+}
+
+static void roh_send_CONN_B1_done(struct tevent_req *);
+static void roh_send_CONN_A1_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_send_CONN_A1_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = roh_send_CONN_B1_send(state, state->event_ctx, state->roh);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_send_CONN_B1_done, req);
+}
+
+static void roh_recv_out_channel_response_done(struct tevent_req *);
+static void roh_send_CONN_B1_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_send_CONN_B1_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       state->roh->connection_state = ROH_STATE_OUT_CHANNEL_WAIT;
+       subreq = roh_recv_out_channel_response_send(state, state->event_ctx,
+                                                   state->roh);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_recv_out_channel_response_done, req);
+}
+
+static void roh_recv_CONN_A3_done(struct tevent_req *);
+static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       char                                    *response;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_recv_out_channel_response_recv(subreq, state, &response);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       state->roh->connection_state = ROH_STATE_WAIT_A3W;
+       subreq = roh_recv_CONN_A3_send(state, state->event_ctx, state->roh);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_recv_CONN_A3_done, req);
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *);
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_recv_CONN_A3_recv(subreq, &state->roh->default_channel_out->connection_timeout);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       state->roh->connection_state = ROH_STATE_WAIT_C2;
+       subreq = roh_recv_CONN_C2_send(state, state->event_ctx, state->roh);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, roh_recv_CONN_C2_done, req);
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_open_connection_state        *state;
+       unsigned int                            version;
+       unsigned int                            recv;
+       unsigned int                            timeout;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_open_connection_state);
+
+       status = roh_recv_CONN_C2_recv(subreq, &version, &recv, &timeout);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       state->roh->connection_state = ROH_STATE_OPENED;
+
+       tevent_req_done(req);
+}
+
+static ssize_t tstream_roh_pending_bytes(struct tstream_context *stream)
+{
+       struct tstream_roh_context *ctx = NULL;
+
+       ctx = tstream_context_data(stream, struct tstream_roh_context);
+       if (!ctx->roh_conn) {
+               errno = ENOTCONN;
+               return -1;
+       }
+
+       return tstream_pending_bytes(ctx->roh_conn->default_channel_out->streams.active);
+}
+
+struct tstream_roh_readv_state {
+       struct roh_connection *roh_conn;
+       int ret;
+};
+
+static void tstream_roh_readv_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_readv_send(TALLOC_CTX *mem_ctx,
+                                                 struct tevent_context *ev,
+                                                 struct tstream_context *stream,
+                                                 struct iovec *vector,
+                                                 size_t count)
+{
+       struct tstream_roh_context *ctx = NULL;
+       struct tstream_roh_readv_state *state;
+       struct tevent_req *req, *subreq;
+
+       req = tevent_req_create(mem_ctx, &state, struct tstream_roh_readv_state);
+       if (!req) {
+               return NULL;
+       }
+
+       ctx = tstream_context_data(stream, struct tstream_roh_context);
+       if (!ctx->roh_conn) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_out) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_out->streams.active) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       state->roh_conn = ctx->roh_conn;
+
+       subreq = tstream_readv_send(state, ev,
+                                   ctx->roh_conn->default_channel_out->streams.active,
+                                   vector, count);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_roh_readv_handler, req);
+
+       return req;
+post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_roh_readv_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req;
+       struct tstream_roh_readv_state *state;
+       int ret;
+       int sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct tstream_roh_readv_state);
+       ret = tstream_readv_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+
+       state->ret = ret;
+
+       tevent_req_done(req);
+}
+
+static int tstream_roh_readv_recv(struct tevent_req *req, int *perrno)
+{
+       struct tstream_roh_readv_state *state;
+       int ret;
+
+       state = tevent_req_data(req, struct tstream_roh_readv_state);
+       ret = tsocket_simple_int_recv(req, perrno);
+       if (ret == 0) {
+               ret = state->ret;
+       }
+
+       tevent_req_received(req);
+       return ret;
+}
+
+struct tstream_roh_writev_state {
+       struct roh_connection *roh_conn;
+       int nwritten;
+};
+
+static void tstream_roh_writev_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_writev_send(TALLOC_CTX *mem_ctx,
+                                                  struct tevent_context *ev,
+                                                  struct tstream_context *stream,
+                                                  const struct iovec *vector,
+                                                  size_t count)
+{
+       struct tstream_roh_context *ctx = NULL;
+       struct tstream_roh_writev_state *state = NULL;
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+
+       req = tevent_req_create(mem_ctx, &state,
+                       struct tstream_roh_writev_state);
+       if (!req) {
+               return NULL;
+       }
+
+       ctx = tstream_context_data(stream, struct tstream_roh_context);
+       if (!ctx->roh_conn) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_in) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_in->streams.active) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       state->roh_conn = ctx->roh_conn;
+
+       subreq = tstream_writev_send(state, ev,
+                                    ctx->roh_conn->default_channel_in->streams.active,
+                                    vector, count);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_roh_writev_handler, req);
+
+       return req;
+post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_roh_writev_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req;
+       struct tstream_roh_writev_state *state;
+       int nwritten;
+       int sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct tstream_roh_writev_state);
+       nwritten = tstream_writev_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (nwritten == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+       state->nwritten = nwritten;
+       state->roh_conn->default_channel_in->sent_bytes += nwritten;
+
+       tevent_req_done(req);
+}
+
+static int tstream_roh_writev_recv(struct tevent_req *req, int *perrno)
+{
+       struct tstream_roh_writev_state *state;
+       int ret;
+
+       state = tevent_req_data(req, struct tstream_roh_writev_state);
+       ret = tsocket_simple_int_recv(req, perrno);
+       if (ret == 0) {
+               ret = state->nwritten;
+       }
+
+       return ret;
+}
+
+struct tstream_roh_disconnect_state {
+       struct tstream_context *stream;
+       struct tevent_context *ev;
+};
+
+static void tstream_roh_disconnect_channel_in_handler(struct tevent_req *subreq);
+static struct tevent_req * tstream_roh_disconnect_send(TALLOC_CTX *mem_ctx,
+                                                      struct tevent_context *ev,
+                                                      struct tstream_context *stream)
+{
+       struct tstream_roh_context *ctx = NULL;
+       struct tevent_req *req, *subreq;
+       struct tstream_roh_disconnect_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct tstream_roh_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->stream = stream;
+       state->ev = ev;
+
+       ctx = tstream_context_data(stream, struct tstream_roh_context);
+       if (!ctx->roh_conn) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_in) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+       if (!ctx->roh_conn->default_channel_in->streams.active) {
+               tevent_req_error(req, ENOTCONN);
+               goto post;
+       }
+
+       subreq = tstream_disconnect_send(state, ev, ctx->roh_conn->default_channel_in->streams.active);
+       if (tevent_req_nomem(subreq, req)) {
+               goto post;
+       }
+       tevent_req_set_callback(subreq, tstream_roh_disconnect_channel_in_handler, req);
+
+       return req;
+post:
+       tevent_req_post(req, ev);
+       return req;
+}
+
+static void tstream_roh_disconnect_channel_out_handler(struct tevent_req *subreq);
+
+static void tstream_roh_disconnect_channel_in_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req;
+       struct tstream_roh_disconnect_state *state;
+       struct tstream_context *stream;
+       struct tstream_roh_context *roh_stream;
+       int ret;
+       int sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct tstream_roh_disconnect_state);
+       stream = state->stream;
+       roh_stream = tstream_context_data(stream, struct tstream_roh_context);
+
+       ret = tstream_disconnect_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+       TALLOC_FREE(roh_stream->roh_conn->default_channel_in);
+
+       subreq = tstream_disconnect_send(state,
+                                        state->ev,
+                                        roh_stream->roh_conn->default_channel_out->streams.raw);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, tstream_roh_disconnect_channel_out_handler, req);
+
+       return;
+}
+
+static void tstream_roh_disconnect_channel_out_handler(struct tevent_req *subreq)
+{
+       struct tevent_req *req;
+       struct tstream_roh_disconnect_state *state;
+       struct tstream_context *stream;
+       struct tstream_roh_context *roh_stream;
+       int ret;
+       int sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct tstream_roh_disconnect_state);
+       stream =  state->stream;
+       roh_stream = tstream_context_data(stream, struct tstream_roh_context);
+
+       ret = tstream_disconnect_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, sys_errno);
+               return;
+       }
+       TALLOC_FREE(roh_stream->roh_conn->default_channel_out);
+
+       tevent_req_done(req);
+}
+
+static int tstream_roh_disconnect_recv(struct tevent_req *req, int *perrno)
+{
+       int ret;
+
+       ret = tsocket_simple_int_recv(req, perrno);
+       tevent_req_received(req);
+
+       return ret;
+}
diff --git a/source4/librpc/rpc/dcerpc_roh.h b/source4/librpc/rpc/dcerpc_roh.h
new file mode 100644 (file)
index 0000000..c4c577e
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   [MS-RPCH] - RPC over HTTP
+
+   Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+
+   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 DCERPC_ROH_H_
+#define DCERPC_ROH_H_
+
+#include "librpc/gen_ndr/misc.h"
+
+struct tevent_queue;
+struct tstream_context;
+struct tstream_tls_params;
+
+struct roh_channel {
+       /*
+        * The ConnectionTimeout command specifies the desired frequency for
+        * sending keep-alive PDUs (2.2.3.5.3)
+        */
+       unsigned int connection_timeout;
+
+       unsigned int sent_bytes;
+
+       struct GUID channel_cookie;
+
+       struct tevent_queue *send_queue;
+       struct {
+               struct tstream_context *raw;
+               struct tstream_context *tls;
+               struct tstream_context *active;
+       } streams;
+};
+
+enum roh_protocol_version {
+       ROH_V1,
+       ROH_V2,
+};
+
+enum roh_connection_state {
+       ROH_STATE_OPEN_START,
+       ROH_STATE_OUT_CHANNEL_WAIT,
+       ROH_STATE_WAIT_A3W,
+       ROH_STATE_WAIT_C2,
+       ROH_STATE_OPENED,
+};
+
+/*
+ * protocol_version:   A client node should be capable of using v1 and v2,
+ *                     try to use v2 in first place. If it fails, fallback
+ *                     to v1
+ * connection_state:   Tracks the protocol current state
+ * connection_cookie:  Identifies the virtual connection among a client, one
+ *                     or more inbound proxies, one or more outbound proxies,
+ *                     and a server
+ * association_group_id_cookie:        Used by higher layer protocols to link
+ *                     multiple virtual connections (2.2.3.1)
+ * default_channel_in:
+ * default_channel_out:
+ * non_default_channel_in:
+ * non_default_channel_out:
+ */
+struct roh_connection {
+       enum roh_protocol_version protocol_version;
+       enum roh_connection_state connection_state;
+
+       struct GUID connection_cookie;
+       struct GUID association_group_id_cookie;
+
+       struct roh_channel *default_channel_in;
+       struct roh_channel *non_default_channel_in;
+
+       struct roh_channel *default_channel_out;
+       struct roh_channel *non_default_channel_out;
+
+       /* Client role specific fields (3.2.2.1) */
+       bool proxy_use;
+       uint32_t current_keep_alive_time;
+       uint32_t current_keep_alive_interval;
+
+       /* TODO Add timers 3.2.2.2 */
+};
+
+/* Command type constants */
+#define ROH_CMD_TYPE_RECV_WINDOWS_SIZE 0x00000000      /* Section 2.2.3.5.1 */
+#define ROH_CMD_TYPE_FLOW_CONTROL_ACK  0x00000001      /* Section 2.2.3.5.2 */
+#define ROH_CMD_TYPE_CONNECTION_TIMEOUT        0x00000002      /* Section 2.2.3.5.3 */
+#define ROH_CMD_TYPE_COOKIE            0x00000003      /* Section 2.2.3.5.4 */
+#define ROH_CMD_TYPE_CHANNEL_LIFETIME  0x00000004      /* Section 2.2.3.5.5 */
+#define ROH_CMD_TYPE_CLIENT_KEEPALIVE  0x00000005      /* Section 2.2.3.5.6 */
+#define ROH_CMD_TYPE_VERSION           0x00000006      /* Section 2.2.3.5.7 */
+#define ROH_CMD_TYPE_EMPTY             0x00000007      /* Section 2.2.3.5.8 */
+#define ROH_CMD_TYPE_PADDING           0x00000008      /* Section 2.2.3.5.9 */
+#define ROH_CMD_TYPE_NEGATIVE_ANCE     0x00000009      /* Section 2.2.3.5.10 */
+#define ROH_CMD_TYPE_ANCE              0x0000000A      /* Section 2.2.3.5.11 */
+#define ROH_CMD_TYPE_CLIENT_ADDRESS    0x0000000B      /* Section 2.2.3.5.12 */
+#define ROH_CMD_TYPE_ASSOCIATION_GRP_ID        0x0000000C      /* Section 2.2.3.5.13 */
+#define ROH_CMD_TYPE_DESTINATION       0x0000000D      /* Section 2.2.3.5.14 */
+#define ROH_CMD_TYPE_PING              0x0000000E      /* Section 2.2.3.5.15 */
+
+#endif /* DCERPC_ROH_H_ */
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_in.c b/source4/librpc/rpc/dcerpc_roh_channel_in.c
new file mode 100644 (file)
index 0000000..887a57b
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   [MS-RPCH] - RPC over HTTP client
+
+   Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+   Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
+
+   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/tevent/tevent.h"
+#include "lib/talloc/talloc.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/util_net.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include <gen_ndr/dcerpc.h>
+#include <gen_ndr/ndr_dcerpc.h>
+
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/http/http.h"
+
+struct roh_connect_channel_state {
+       struct tevent_context           *ev;
+       struct tsocket_address          *local_address;
+       struct tsocket_address          *remote_address;
+       struct cli_credentials          *credentials;
+       struct roh_connection           *roh;
+       bool                            tls;
+       struct tstream_tls_params       *tls_params;
+};
+
+static void roh_connect_channel_in_done(struct tevent_req *);
+struct tevent_req *roh_connect_channel_in_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              const char *rpcproxy_ip_address,
+                                              unsigned int rpcproxy_port,
+                                              struct cli_credentials *credentials,
+                                              struct roh_connection *roh,
+                                              bool tls,
+                                              struct tstream_tls_params *tls_params)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct tevent_req                       *subreq;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+
+       DEBUG(8, ("%s: Connecting channel in socket, RPC proxy is %s:%d (TLS: %s)\n",
+                 __func__, rpcproxy_ip_address, rpcproxy_port,
+                 (tls ? "true" : "false")));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_connect_channel_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!is_ipaddress(rpcproxy_ip_address)) {
+               DEBUG(0, ("%s: Invalid host (%s), needs to be an IP address\n",
+                         __func__, rpcproxy_ip_address));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->credentials = credentials;
+       state->roh = roh;
+       state->tls = tls;
+       state->tls_params = tls_params;
+       ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+                                               &state->local_address);
+       if (ret != 0) {
+               DEBUG(0, ("%s: Cannot create local socket address, error: %s (%d)\n",
+                         __func__, strerror(errno), errno));
+               status = map_nt_error_from_unix_common(errno);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       ret = tsocket_address_inet_from_strings(state, "ip",
+                                               rpcproxy_ip_address,
+                                               rpcproxy_port,
+                                               &state->remote_address);
+       if (ret != 0) {
+               DEBUG(0, ("%s: Cannot create remote socket address, error: %s (%d)\n",
+                         __func__, strerror(errno), errno));
+               status = map_nt_error_from_unix_common(errno);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       /* Initialize channel structure */
+       state->roh->default_channel_in = talloc_zero(roh, struct roh_channel);
+       if (tevent_req_nomem(state->roh->default_channel_in, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->roh->default_channel_in->send_queue =
+                       tevent_queue_create(state->roh->default_channel_in,
+                                           "RoH IN virtual channel send queue");
+       if (tevent_req_nomem(state->roh->default_channel_in->send_queue, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->roh->default_channel_in->channel_cookie = GUID_random();
+       subreq = tstream_inet_tcp_connect_send(state, ev, state->local_address,
+                                              state->remote_address);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_connect_channel_in_done, req);
+
+       return req;
+}
+
+static void roh_connect_channel_in_tls_done(struct tevent_req *subreq);
+static void roh_connect_channel_in_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+       int                                     sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_connect_channel_state);
+       ret = tstream_inet_tcp_connect_recv(subreq, &sys_errno, state,
+                                           &state->roh->default_channel_in->streams.raw,
+                                           NULL);
+       talloc_steal(state->roh->default_channel_in,
+                    state->roh->default_channel_in->streams.raw);
+       state->roh->default_channel_in->streams.active = state->roh->default_channel_in->streams.raw;
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       DEBUG(8, ("%s: Socket connected\n", __func__));
+       if (state->tls) {
+               DEBUG(8, ("%s: Starting TLS handshake\n", __func__));
+               subreq = _tstream_tls_connect_send(state,
+                                                  state->ev,
+                                                  state->roh->default_channel_in->streams.raw,
+                                                  state->tls_params,
+                                                  __location__);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, roh_connect_channel_in_tls_done, req);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static void roh_connect_channel_in_tls_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+       int                                     sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_connect_channel_state);
+       ret = tstream_tls_connect_recv(subreq, &sys_errno, state,
+                                      &state->roh->default_channel_in->streams.tls);
+       talloc_steal(state->roh->default_channel_in,
+                    state->roh->default_channel_in->streams.tls);
+       state->roh->default_channel_in->streams.active = state->roh->default_channel_in->streams.tls;
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       DEBUG(8, ("%s: TLS handshake completed\n", __func__));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_connect_channel_in_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_request_state {
+       struct http_request     *request;
+       struct http_request     *response;
+};
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_RPC_DATA_IN_send(TALLOC_CTX *mem_ctx,
+                                            struct loadparm_context *lp_ctx,
+                                            struct tevent_context *ev,
+                                            struct cli_credentials *credentials,
+                                            struct roh_connection *roh,
+                                            const char *rpc_server,
+                                            uint32_t rpc_server_port,
+                                            const char *rpc_proxy,
+                                            bool use_ntlm)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_request_state        *state;
+       const char                      *path;
+       char                            *query;
+       char                            *uri;
+
+       DEBUG(8, ("%s: Sending RPC_IN_DATA request\n", __func__));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->request = talloc_zero(state, struct http_request);
+       if (tevent_req_nomem(state->request, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /* Build URI, as specified in section 2.2.2 */
+       query = talloc_asprintf(state, "%s:%d", rpc_server, rpc_server_port);
+       if (tevent_req_nomem(query, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
+        * certificates
+        */
+       path = "/rpc/rpcproxy.dll";
+       uri = talloc_asprintf(state, "%s?%s", path, query);
+       if (tevent_req_nomem(uri, req)) {
+               tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+               return tevent_req_post(req, ev);
+       }
+       TALLOC_FREE(query);
+
+       /*
+        * Create the HTTP channel IN request as specified in the
+        * section 2.1.2.1.1
+        */
+       state->request->type = HTTP_REQ_RPC_IN_DATA;
+       state->request->uri = uri;
+       state->request->body.length = 0;
+       state->request->body.data = NULL;
+       state->request->major = '1';
+       state->request->minor = '0';
+
+       http_add_header(state, &state->request->headers,
+                       "Accept", "application/rpc");
+       http_add_header(state, &state->request->headers,
+                       "User-Agent", "MSRPC");
+       http_add_header(state, &state->request->headers,
+                       "Host", rpc_proxy);
+       http_add_header(state, &state->request->headers,
+                       "Connection", "keep-alive");
+       http_add_header(state, &state->request->headers,
+                       "Content-Length", "1073741824");
+       http_add_header(state, &state->request->headers,
+                       "Cache-Control", "no-cache");
+       http_add_header(state, &state->request->headers,
+                       "Pragma", "no-cache");
+
+       subreq = http_send_auth_request_send(state,
+                                       ev,
+                                       roh->default_channel_in->streams.active,
+                                       roh->default_channel_in->send_queue,
+                                       state->request,
+                                       credentials,
+                                       lp_ctx,
+                                       use_ntlm ? HTTP_AUTH_NTLM :
+                                                  HTTP_AUTH_BASIC);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_send_RPC_DATA_IN_done, req);
+
+       return req;
+}
+
+static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq)
+{
+       NTSTATUS                status;
+       struct tevent_req       *req;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+
+       /* Receive the sent bytes to check if request has been properly sent */
+       status = http_send_auth_request_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       DEBUG(8, ("%s: RPC_IN_DATA sent\n", __func__));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_send_RPC_DATA_IN_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_send_pdu_state {
+       DATA_BLOB       buffer;
+       struct iovec    iov;
+       int             bytes_written;
+       int             sys_errno;
+};
+
+static void roh_send_CONN_B1_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_CONN_B1_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct roh_connection *roh)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_send_pdu_state       *state;
+       struct dcerpc_rts               rts;
+       struct ncacn_packet             pkt;
+       struct ndr_push                 *ndr;
+
+       DEBUG(8, ("%s: Sending CONN/B1 request\n", __func__));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_send_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       rts.Flags = RTS_FLAG_NONE;
+       rts.NumberOfCommands = 6;
+       rts.Commands = talloc_array(state, struct dcerpc_rts_cmd, 6);
+
+       /* CONN/B1: Version RTS command */
+       rts.Commands[0].CommandType = 0x00000006;
+       rts.Commands[0].Command.Version.Version = 0x00000001;
+
+       /* CONN/B1: VirtualConnectionCookie RTS command */
+       rts.Commands[1].CommandType = 0x00000003;
+       rts.Commands[1].Command.Cookie.Cookie.Cookie = roh->connection_cookie;
+
+       /* CONN/B1: InChannelCookie RTS command */
+       rts.Commands[2].CommandType = 0x00000003;
+       rts.Commands[2].Command.Cookie.Cookie.Cookie =
+                       roh->default_channel_in->channel_cookie;
+
+       /* CONN/B1: ChannelLifetime */
+       rts.Commands[3].CommandType = 0x00000004;
+       rts.Commands[3].Command.ReceiveWindowSize.ReceiveWindowSize =
+                       0x40000000;
+
+       /* CONN/B1: ClientKeepAlive */
+       rts.Commands[4].CommandType = 0x00000005;
+       rts.Commands[4].Command.ClientKeepalive.ClientKeepalive = 0x000493e0;
+
+       /* CONN/B1: AssociationGroupId */
+       rts.Commands[5].CommandType = 0x0000000C;
+       rts.Commands[5].Command.AssociationGroupId.AssociationGroupId.Cookie =
+                       roh->association_group_id_cookie;
+
+       pkt.rpc_vers = 5;
+       pkt.rpc_vers_minor = 0;
+       pkt.ptype = DCERPC_PKT_RTS;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_LAST | DCERPC_PFC_FLAG_FIRST;
+       pkt.drep[0] = DCERPC_DREP_LE;
+       pkt.drep[1] = 0;
+       pkt.drep[2] = 0;
+       pkt.drep[3] = 0;
+       pkt.frag_length = 104;
+       pkt.auth_length = 0;
+       pkt.call_id = 0;
+       pkt.u.rts = rts;
+
+       ndr = ndr_push_init_ctx(state);
+       ndr->offset = 0;
+       ndr_push_ncacn_packet(ndr, NDR_SCALARS, &pkt);
+
+       state->buffer = ndr_push_blob(ndr);
+       state->iov.iov_base = (char *) state->buffer.data;
+       state->iov.iov_len = state->buffer.length;
+
+       subreq = tstream_writev_queue_send(mem_ctx,
+                                          ev,
+                                          roh->default_channel_in->streams.active,
+                                          roh->default_channel_in->send_queue,
+                                          &state->iov,
+                                          1);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_send_CONN_B1_done, req);
+
+       return req;
+}
+
+static void roh_send_CONN_B1_done(struct tevent_req *subreq)
+{
+       NTSTATUS                        status;
+       struct tevent_req               *req;
+       struct roh_send_pdu_state       *state;
+       int                             sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_send_pdu_state);
+
+       state->bytes_written = tstream_writev_queue_recv(subreq, &sys_errno);
+       state->sys_errno = sys_errno;
+       TALLOC_FREE(subreq);
+       if (state->bytes_written <= 0 && state->sys_errno != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       DEBUG(8, ("%s: CONN/B1 sent (%d bytes written)\n",
+                 __func__, state->bytes_written));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_send_CONN_B1_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_out.c b/source4/librpc/rpc/dcerpc_roh_channel_out.c
new file mode 100644 (file)
index 0000000..b370e56
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   [MS-RPCH] - RPC over HTTP client
+
+   Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
+   Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
+
+   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/tevent/tevent.h"
+#include "lib/talloc/talloc.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/util_net.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include <gen_ndr/dcerpc.h>
+#include <gen_ndr/ndr_dcerpc.h>
+
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_roh.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/http/http.h"
+
+struct roh_connect_channel_state {
+       struct tevent_context           *ev;
+       struct tsocket_address          *local_address;
+       struct tsocket_address          *remote_address;
+       struct cli_credentials          *credentials;
+       struct roh_connection           *roh;
+       bool                            tls;
+       struct tstream_tls_params       *tls_params;
+};
+
+static void roh_connect_channel_out_done(struct tevent_req *subreq);
+struct tevent_req *roh_connect_channel_out_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               const char *rpcproxy_ip_address,
+                                               unsigned int rpcproxy_port,
+                                               struct cli_credentials *credentials,
+                                               struct roh_connection *roh,
+                                               bool tls,
+                                               struct tstream_tls_params *tls_params)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct tevent_req                       *subreq;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+
+       DEBUG(8, ("%s: Connecting channel out socket, RPC proxy is %s:%d (TLS: %s)\n",
+                 __func__, rpcproxy_ip_address, rpcproxy_port,
+                 (tls ? "true" : "false")));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_connect_channel_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!is_ipaddress(rpcproxy_ip_address)) {
+               DEBUG(0, ("%s: Invalid host (%s), needs to be an IP address\n",
+                         __func__, rpcproxy_ip_address));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->credentials = credentials;
+       state->roh = roh;
+       state->tls = tls;
+       state->tls_params = tls_params;
+       ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+                                               &state->local_address);
+       if (ret != 0) {
+               DEBUG(0, ("%s: Cannot create local socket address, error: %s (%d)\n",
+                         __func__, strerror(errno), errno));
+               status = map_nt_error_from_unix_common(errno);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       ret = tsocket_address_inet_from_strings(state, "ip",
+                                               rpcproxy_ip_address,
+                                               rpcproxy_port,
+                                               &state->remote_address);
+       if (ret != 0) {
+               DEBUG(0, ("%s: Cannot create remote socket address, error: %s (%d)\n",
+                         __func__, strerror(errno), errno));
+               status = map_nt_error_from_unix_common(errno);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       /* Initialize channel structure */
+       state->roh->default_channel_out = talloc_zero(roh, struct roh_channel);
+       if (tevent_req_nomem(state->roh->default_channel_out, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->roh->default_channel_out->send_queue =
+                       tevent_queue_create(state->roh->default_channel_out,
+                                           "RoH OUT virtual channel send queue");
+       if (tevent_req_nomem(state->roh->default_channel_out->send_queue, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->roh->default_channel_out->channel_cookie = GUID_random();
+       subreq = tstream_inet_tcp_connect_send(state, ev, state->local_address,
+                                              state->remote_address);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_connect_channel_out_done, req);
+
+       return req;
+}
+
+static void roh_connect_channel_out_tls_done(struct tevent_req *subreq);
+static void roh_connect_channel_out_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+       int                                     sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_connect_channel_state);
+       ret = tstream_inet_tcp_connect_recv(subreq, &sys_errno, state,
+                                           &state->roh->default_channel_out->streams.raw,
+                                           NULL);
+       talloc_steal(state->roh->default_channel_out,
+                    state->roh->default_channel_out->streams.raw);
+       state->roh->default_channel_out->streams.active = state->roh->default_channel_out->streams.raw;
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       DEBUG(8, ("%s: Socket connected\n", __func__));
+       if (state->tls) {
+               DEBUG(8, ("%s: Starting TLS handshake\n", __func__));
+               subreq = _tstream_tls_connect_send(state,
+                                                  state->ev,
+                                                  state->roh->default_channel_out->streams.raw,
+                                                  state->tls_params,
+                                                  __location__);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, roh_connect_channel_out_tls_done, req);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static void roh_connect_channel_out_tls_done(struct tevent_req *subreq)
+{
+       NTSTATUS                                status;
+       struct tevent_req                       *req;
+       struct roh_connect_channel_state        *state;
+       int                                     ret;
+       int                                     sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_connect_channel_state);
+       ret = tstream_tls_connect_recv(subreq, &sys_errno, state,
+                                      &state->roh->default_channel_out->streams.tls);
+       talloc_steal(state->roh->default_channel_out,
+                    state->roh->default_channel_out->streams.tls);
+       state->roh->default_channel_out->streams.active = state->roh->default_channel_out->streams.tls;
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       DEBUG(8, ("%s: TLS handshake completed\n", __func__));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_connect_channel_out_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_request_state {
+       struct http_request     *request;
+       struct http_request     *response;
+};
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_RPC_DATA_OUT_send(TALLOC_CTX *mem_ctx,
+                                             struct loadparm_context *lp_ctx,
+                                             struct tevent_context *ev,
+                                             struct cli_credentials *credentials,
+                                             struct roh_connection *roh,
+                                             const char *rpc_server,
+                                             uint32_t rpc_server_port,
+                                             const char *rpc_proxy,
+                                             bool use_ntlm)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_request_state        *state;
+       const char                      *path;
+       char                            *query;
+       char                            *uri;
+
+       DEBUG(8, ("%s: Sending RPC_OUT_DATA request\n", __func__));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->request = talloc_zero(state, struct http_request);
+       if (tevent_req_nomem(state->request, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /* Build URI, as specified in section 2.2.2 */
+       query = talloc_asprintf(state, "%s:%d", rpc_server, rpc_server_port);
+       if (tevent_req_nomem(query, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
+        * certificates
+        */
+       path = "/rpc/rpcproxy.dll";
+       uri = talloc_asprintf(state, "%s?%s", path, query);
+       if (tevent_req_nomem(uri, req)) {
+               tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+               return tevent_req_post(req, ev);
+       }
+       TALLOC_FREE(query);
+
+       /*
+        * Create the HTTP channel OUT request as specified in the
+        * section 2.1.2.1.2
+        */
+       state->request->type = HTTP_REQ_RPC_OUT_DATA;
+       state->request->uri = uri;
+       state->request->body.length = 0;
+       state->request->body.data = NULL;
+       state->request->major = '1';
+       state->request->minor = '0';
+
+       http_add_header(state, &state->request->headers,
+                       "Accept", "application/rpc");
+       http_add_header(state, &state->request->headers,
+                       "User-Agent", "MSRPC");
+       http_add_header(state, &state->request->headers,
+                       "Host", rpc_proxy);
+       http_add_header(state, &state->request->headers,
+                       "Connection", "keep-alive");
+       http_add_header(state, &state->request->headers,
+                       "Content-Length", "76");
+       http_add_header(state, &state->request->headers,
+                       "Cache-Control", "no-cache");
+       http_add_header(state, &state->request->headers,
+                       "Pragma", "no-cache");
+
+       subreq = http_send_auth_request_send(state,
+                                       ev,
+                                       roh->default_channel_out->streams.active,
+                                       roh->default_channel_out->send_queue,
+                                       state->request,
+                                       credentials,
+                                       lp_ctx,
+                                       use_ntlm ? HTTP_AUTH_NTLM :
+                                                  HTTP_AUTH_BASIC);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_send_RPC_DATA_OUT_done, req);
+
+       return req;
+}
+
+static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
+{
+       NTSTATUS                status;
+       struct tevent_req       *req;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+
+       /* Receive the sent bytes to check if request has been properly sent */
+       status = http_send_auth_request_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       DEBUG(8, ("%s: RPC_OUT_DATA sent", __func__));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_send_RPC_DATA_OUT_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_send_pdu_state {
+       DATA_BLOB       buffer;
+       struct iovec    iov;
+       int             bytes_written;
+       int             sys_errno;
+};
+
+static void roh_send_CONN_A1_done(struct tevent_req *subreq);
+struct tevent_req *roh_send_CONN_A1_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct roh_connection *roh)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_send_pdu_state       *state;
+       struct dcerpc_rts               rts;
+       struct ncacn_packet             pkt;
+       struct ndr_push                 *ndr;
+
+       DEBUG(8, ("%s: Sending CONN/A1 request\n", __func__));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_send_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       rts.Flags = RTS_FLAG_NONE;
+       rts.NumberOfCommands = 4;
+       rts.Commands = talloc_array(state, struct dcerpc_rts_cmd, 4);
+
+       /* CONN/A1: Version RTS command */
+       rts.Commands[0].CommandType = 0x00000006;
+       rts.Commands[0].Command.Version.Version = 0x00000001;
+
+       /* CONN/A1: VirtualConnectionCookie RTS command */
+       rts.Commands[1].CommandType = 0x00000003;
+       rts.Commands[1].Command.Cookie.Cookie.Cookie = roh->connection_cookie;
+
+       /* CONN/A1: OutChannelCookie RTS command */
+       rts.Commands[2].CommandType = 0x00000003;
+       rts.Commands[2].Command.Cookie.Cookie.Cookie =
+                       roh->default_channel_out->channel_cookie;
+
+       /* CONN/A1: ReceiveWindowSize */
+       rts.Commands[3].CommandType = 0x00000000;
+       rts.Commands[3].Command.ReceiveWindowSize.ReceiveWindowSize = 0x40000;
+
+       pkt.rpc_vers = 5;
+       pkt.rpc_vers_minor = 0;
+       pkt.ptype = DCERPC_PKT_RTS;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_LAST | DCERPC_PFC_FLAG_FIRST;
+       pkt.drep[0] = DCERPC_DREP_LE;
+       pkt.drep[1] = 0;
+       pkt.drep[2] = 0;
+       pkt.drep[3] = 0;
+       pkt.frag_length = 76;
+       pkt.auth_length = 0;
+       pkt.call_id = 0;
+       pkt.u.rts = rts;
+
+       ndr = ndr_push_init_ctx(state);
+       ndr->offset = 0;
+       ndr_push_ncacn_packet(ndr, NDR_SCALARS, &pkt);
+
+       state->buffer = ndr_push_blob(ndr);
+       state->iov.iov_base = (char *) state->buffer.data;
+       state->iov.iov_len = state->buffer.length;
+
+       subreq = tstream_writev_queue_send(mem_ctx,
+                                          ev,
+                                          roh->default_channel_out->streams.active,
+                                          roh->default_channel_out->send_queue,
+                                          &state->iov,
+                                          1);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_send_CONN_A1_done, req);
+
+       return req;
+}
+
+static void roh_send_CONN_A1_done(struct tevent_req *subreq)
+{
+       NTSTATUS                        status;
+       struct tevent_req               *req;
+       struct roh_send_pdu_state       *state;
+       int                             sys_errno;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_send_pdu_state);
+
+       state->bytes_written = tstream_writev_queue_recv(subreq, &sys_errno);
+       state->sys_errno = sys_errno;
+       TALLOC_FREE(subreq);
+       if (state->bytes_written <= 0 && sys_errno != 0) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       DEBUG(8, ("%s: CONN/A1 sent (%d bytes written)\n",
+                 __func__, state->bytes_written));
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_send_CONN_A1_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_recv_response_state
+{
+       struct http_request     *response;
+};
+
+static void roh_recv_out_channel_response_done(struct tevent_req *);
+struct tevent_req *roh_recv_out_channel_response_send(TALLOC_CTX *mem_ctx,
+                                                     struct tevent_context *ev,
+                                                     struct roh_connection *roh)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_recv_response_state  *state;
+
+       DEBUG(8, ("%s: Waiting for RPC_OUT_DATA response\n", __func__));
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_recv_response_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       subreq = http_read_response_send(state, ev,
+                                        roh->default_channel_out->streams.active);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_recv_out_channel_response_done, req);
+
+       return req;
+}
+
+static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
+{
+       NTSTATUS                        status;
+       struct tevent_req               *req;
+       struct roh_recv_response_state  *state;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_recv_response_state);
+       status = http_read_response_recv(subreq, state, &state->response);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       DEBUG(8, ("%s: RCP_OUT_DATA response received\n", __func__));
+
+       /* TODO Map response code to nt error */
+       switch (state->response->response_code) {
+       case 200:
+               break;
+       case 401:
+               DEBUG(0, ("%s: Server response: Access denied\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       case 503:
+               /* TODO Decode error info as specified in section 2.1.2.1.3 */
+               DEBUG(0, ("%s: Server response: RPC error\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
+               return;
+       default:
+               DEBUG(0, ("%s: Server response: Unknown error\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_out_channel_response_recv(struct tevent_req *req,
+               TALLOC_CTX *mem_ctx,
+               char **response_msg)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct roh_recv_pdu_state {
+       struct roh_connection   *roh;
+       uint32_t                connection_timeout;
+       uint32_t                version;
+       uint32_t                recv_window_size;
+};
+
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq);
+struct tevent_req *roh_recv_CONN_A3_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct roh_connection *roh)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_recv_pdu_state       *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       DEBUG(8, ("%s: Waiting for CONN/A3\n", __func__));
+       subreq = dcerpc_read_ncacn_packet_send(state, ev,
+                                              roh->default_channel_out->streams.active);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_recv_CONN_A3_done, req);
+
+       return req;
+}
+
+static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
+{
+       NTSTATUS                        status;
+       struct tevent_req               *req;
+       struct roh_recv_pdu_state       *state;
+       struct ncacn_packet             *pkt;
+       DATA_BLOB                       buffer;
+       struct dcerpc_rts               rts;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_recv_pdu_state);
+       status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
+       TALLOC_FREE(subreq);
+
+       if (tevent_req_nterror(req, status)) {
+               DEBUG(0, ("%s: Error receiving PDU\n", __func__));
+               return;
+       }
+
+       /*
+        * Check if it is a CONN/A3 (2.2.4.4) packet and get the connection
+        * timeout
+        */
+       rts = pkt->u.rts;
+       if (rts.NumberOfCommands != 1) {
+               DEBUG(0, ("%s: Invalid number of commands received\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       if (rts.Commands[0].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
+               DEBUG(0, ("%s: Invalid command type received\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       /* Extract connection timeout */
+       state->connection_timeout = rts.Commands[0].Command.ConnectionTimeout.ConnectionTimeout;
+
+       DEBUG(8, ("%s: CONN/A3 received, connection timeout is %u\n",
+                 __func__, state->connection_timeout));
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_CONN_A3_recv(struct tevent_req *req,
+                              unsigned int *connection_timeout)
+{
+       NTSTATUS                        status;
+       struct roh_recv_pdu_state       *state;
+
+       state = tevent_req_data(req, struct roh_recv_pdu_state);
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *connection_timeout = state->connection_timeout;
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq);
+struct tevent_req *roh_recv_CONN_C2_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct roh_connection *roh)
+{
+       struct tevent_req               *req;
+       struct tevent_req               *subreq;
+       struct roh_recv_pdu_state       *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       DEBUG(8, ("%s: Waiting for CONN/C2\n", __func__));
+       subreq = dcerpc_read_ncacn_packet_send(state, ev,
+                                              roh->default_channel_out->streams.active);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, roh_recv_CONN_C2_done, req);
+
+       return req;
+}
+
+static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
+{
+       NTSTATUS                        status;
+       struct tevent_req               *req;
+       struct roh_recv_pdu_state       *state;
+       struct ncacn_packet             *pkt;
+       DATA_BLOB                       buffer;
+       struct dcerpc_rts               rts;
+
+       req = tevent_req_callback_data(subreq, struct tevent_req);
+       state = tevent_req_data(req, struct roh_recv_pdu_state);
+
+       status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               DEBUG(0, ("%s: Error receiving PDU\n", __func__));
+               return;
+       }
+
+       /*
+        * Check if it is a CONN/C2 packet (2.2.4.9), and get the version, the
+        * receive windows size and the connection timeout for the IN channel
+        */
+       rts = pkt->u.rts;
+       if (rts.NumberOfCommands != 3) {
+               DEBUG(0, ("%s: Invalid number of commands received\n",
+                         __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+       if (rts.Commands[0].CommandType != ROH_CMD_TYPE_VERSION) {
+               DEBUG(0, ("%s: Invalid command type received\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+       if (rts.Commands[1].CommandType != ROH_CMD_TYPE_RECV_WINDOWS_SIZE) {
+               DEBUG(0, ("%s: Invalid command type received\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+       if (rts.Commands[2].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
+               DEBUG(0, ("%s: Invalid command type received\n", __func__));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       /* Extract data */
+       state->version = rts.Commands[0].Command.Version.Version;
+       state->recv_window_size = rts.Commands[1].Command.ReceiveWindowSize.ReceiveWindowSize;
+       state->connection_timeout = rts.Commands[2].Command.ConnectionTimeout.ConnectionTimeout;
+
+       DEBUG(8, ("%s: CONN/C2 received, version is %u, receive windows size is %u, connection timeout is %u\n",
+                 __func__, state->version, state->recv_window_size,
+                 state->connection_timeout));
+       tevent_req_done(req);
+}
+
+NTSTATUS roh_recv_CONN_C2_recv(struct tevent_req *req,
+                              unsigned int *version,
+                              unsigned int *recv_window_size,
+                              unsigned int *connection_timeout)
+{
+       NTSTATUS                        status;
+       struct roh_recv_pdu_state       *state;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       state =  tevent_req_data(req, struct roh_recv_pdu_state);
+       *version = state->version;
+       *recv_window_size = state->recv_window_size;
+       *connection_timeout = state->connection_timeout;
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
index 582092d5142252b990a0ab88ee5364684e38581e..5b53b6fdeb416a66df227599f703f32bfaceac10 100755 (executable)
@@ -121,9 +121,10 @@ bld.SAMBA_SUBSYSTEM('RPC_NDR_WINSIF',
 bld.SAMBA_LIBRARY('dcerpc',
        source='''rpc/dcerpc.c rpc/dcerpc_auth.c rpc/dcerpc_schannel.c
        rpc/dcerpc_util.c rpc/dcerpc_smb.c rpc/dcerpc_sock.c
+       rpc/dcerpc_roh_channel_in.c rpc/dcerpc_roh_channel_out.c rpc/dcerpc_roh.c
        rpc/dcerpc_connect.c rpc/dcerpc_secondary.c''',
        pc_files='dcerpc.pc',
-       deps='samba_socket LIBCLI_RESOLVE LIBCLI_SMB LIBCLI_SMB2 ndr NDR_DCERPC RPC_NDR_EPMAPPER NDR_SCHANNEL RPC_NDR_NETLOGON RPC_NDR_MGMT gensec LIBCLI_AUTH smbclient-raw LP_RESOLVE tevent-util dcerpc-binding param_options',
+       deps='samba_socket LIBCLI_RESOLVE LIBCLI_SMB LIBCLI_SMB2 ndr NDR_DCERPC RPC_NDR_EPMAPPER NDR_SCHANNEL RPC_NDR_NETLOGON RPC_NDR_MGMT gensec LIBCLI_AUTH smbclient-raw LP_RESOLVE tevent-util dcerpc-binding param_options http',
        allow_warnings=True,
        autoproto='rpc/dcerpc_proto.h',
        public_deps='samba-credentials tevent talloc',