s4:lib/http: add http_[dis]connect_send and recv
authorRalph Boehme <slow@samba.org>
Mon, 25 Mar 2019 13:39:59 +0000 (14:39 +0100)
committerRalph Boehme <slow@samba.org>
Wed, 7 Aug 2019 12:54:40 +0000 (12:54 +0000)
Not used for now, that comes later.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Samuel Cabrero <scabrero@suse.de>
source4/lib/http/http.h
source4/lib/http/http_conn.c [new file with mode: 0644]
source4/lib/http/http_internal.h
source4/lib/http/wscript_build

index 0fa65ca89a0b3c20948333d686345befcdc3d689..25055c91007550cce3b2d4b776daa33ea1b1da69 100644 (file)
@@ -92,6 +92,29 @@ int http_remove_header(struct http_header **, const char *);
 int http_add_header(TALLOC_CTX *, struct http_header **, const char *, const char *);
 int http_replace_header(TALLOC_CTX *, struct http_header **, const char *, const char *);
 
+/* HTTP(s) connect */
+
+struct http_conn;
+struct tstream_tls_params;
+
+struct tevent_req *http_connect_send(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    const char *http_server,
+                                    uint16_t http_port,
+                                    struct cli_credentials *credentials,
+                                    struct tstream_tls_params *tls_params);
+int http_connect_recv(struct tevent_req *req,
+                     TALLOC_CTX *mem_ctx,
+                     struct http_conn **http_conn);
+
+struct tevent_req *http_disconnect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct http_conn *http_conn);
+int http_disconnect_recv(struct tevent_req *req);
+
+struct tevent_queue *http_conn_send_queue(struct http_conn *http_conn);
+struct tstream_context *http_conn_tstream(struct http_conn *http_conn);
+
 /* HTTP request */
 struct tevent_req *http_send_request_send(TALLOC_CTX *,
                                          struct tevent_context *,
diff --git a/source4/lib/http/http_conn.c b/source4/lib/http/http_conn.c
new file mode 100644 (file)
index 0000000..de1be3f
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   HTTP library
+
+   Copyright (C) 2019 Ralph Boehme <slow@samba.org>
+
+   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/util/tevent_ntstatus.h"
+#include "libcli/dns/dns_lookup.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/util_net.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_unix.h"
+#include "http.h"
+#include "http_internal.h"
+
+struct http_connect_state {
+       struct tevent_context *ev;
+       const char *http_server;
+       const char *http_server_ip;
+       uint16_t http_port;
+       struct tsocket_address *local_address;
+       struct tsocket_address *remote_address;
+       struct cli_credentials *credentials;
+       struct tstream_tls_params *tls_params;
+
+       struct http_conn *http_conn;
+};
+
+static void http_connect_dns_done(struct tevent_req *subreq);
+static void http_connect_tcp_connect(struct tevent_req *req);
+static void http_connect_tcp_done(struct tevent_req *subreq);
+static void http_connect_tls_done(struct tevent_req *subreq);
+
+struct tevent_req *http_connect_send(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    const char *http_server,
+                                    uint16_t http_port,
+                                    struct cli_credentials *credentials,
+                                    struct tstream_tls_params *tls_params)
+{
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct http_connect_state *state = NULL;
+       int ret;
+
+       DBG_DEBUG("Connecting to [%s] over HTTP%s\n",
+                 http_server, tls_params != NULL ? "S" : "");
+
+       req = tevent_req_create(mem_ctx, &state, struct http_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       *state = (struct http_connect_state) {
+               .ev = ev,
+               .http_port = http_port,
+               .credentials = credentials,
+               .tls_params = tls_params,
+       };
+
+       state->http_server = talloc_strdup(state, http_server);
+       if (tevent_req_nomem(state->http_server, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->http_conn = talloc_zero(state, struct http_conn);
+       if (tevent_req_nomem(state->http_conn, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->http_conn->send_queue = tevent_queue_create(state->http_conn,
+                                                          "HTTP send queue");
+       if (tevent_req_nomem(state->http_conn->send_queue, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       ret = tsocket_address_inet_from_strings(state,
+                                               "ip",
+                                               NULL,
+                                               0,
+                                               &state->local_address);
+       if (ret != 0) {
+               tevent_req_error(req, errno);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!is_ipaddress(http_server)) {
+               subreq = dns_lookup_send(state,
+                                        ev,
+                                        NULL,
+                                        http_server,
+                                        DNS_QCLASS_IN,
+                                        DNS_QTYPE_A);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, http_connect_dns_done, req);
+               return req;
+       }
+       state->http_server_ip = state->http_server;
+
+       http_connect_tcp_connect(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       return req;
+}
+
+static void http_connect_dns_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct http_connect_state *state = tevent_req_data(
+               req, struct http_connect_state);
+       struct dns_name_packet *dns_reply = NULL;
+       struct dns_res_rec *an = NULL;
+       uint16_t i;
+       int ret;
+
+       ret = dns_lookup_recv(subreq, state, &dns_reply);
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       for (i = 0; i < dns_reply->ancount; i++) {
+               an = &dns_reply->answers[i];
+               if (an->rr_type == DNS_QTYPE_A) {
+                       break;
+               }
+       }
+       if (i >= dns_reply->ancount) {
+               tevent_req_error(req, ENOENT);
+               return;
+       }
+
+       state->http_server_ip = talloc_strdup(state, an->rdata.ipv4_record);
+       if (tevent_req_nomem(state->http_server_ip, req)) {
+               return;
+       }
+
+       return http_connect_tcp_connect(req);
+}
+
+static void http_connect_tcp_connect(struct tevent_req *req)
+{
+       struct http_connect_state *state = tevent_req_data(
+               req, struct http_connect_state);
+       struct tevent_req *subreq = NULL;
+       int ret;
+
+       ret = tsocket_address_inet_from_strings(state,
+                                               "ip",
+                                               state->http_server_ip,
+                                               state->http_port,
+                                               &state->remote_address);
+       if (ret != 0) {
+               int saved_errno = errno;
+
+               DBG_ERR("Cannot create remote socket address, error: %s (%d)\n",
+                       strerror(errno), errno);
+               tevent_req_error(req, saved_errno);
+               return;
+       }
+
+       subreq = tstream_inet_tcp_connect_send(state,
+                                              state->ev,
+                                              state->local_address,
+                                              state->remote_address);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, http_connect_tcp_done, req);
+}
+
+static void http_connect_tcp_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct http_connect_state *state = tevent_req_data(
+               req, struct http_connect_state);
+       int error;
+       int ret;
+
+       ret = tstream_inet_tcp_connect_recv(subreq,
+                                           &error,
+                                           state->http_conn,
+                                           &state->http_conn->tstreams.raw,
+                                           NULL);
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               tevent_req_error(req, error);
+               return;
+       }
+
+       state->http_conn->tstreams.active = state->http_conn->tstreams.raw;
+       DBG_DEBUG("Socket connected\n");
+
+       if (state->tls_params == NULL) {
+               tevent_req_done(req);
+               return;
+       }
+
+       DBG_DEBUG("Starting TLS\n");
+
+       subreq = tstream_tls_connect_send(state,
+                                         state->ev,
+                                         state->http_conn->tstreams.active,
+                                         state->tls_params);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, http_connect_tls_done, req);
+}
+
+static void http_connect_tls_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct http_connect_state *state = tevent_req_data(
+               req, struct http_connect_state);
+       int error;
+       int ret;
+
+       ret = tstream_tls_connect_recv(subreq,
+                                      &error,
+                                      state->http_conn,
+                                      &state->http_conn->tstreams.tls);
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               tevent_req_error(req, error);
+               return;
+       }
+
+       state->http_conn->tstreams.active = state->http_conn->tstreams.tls;
+
+       DBG_DEBUG("TLS handshake completed\n");
+       tevent_req_done(req);
+}
+
+int http_connect_recv(struct tevent_req *req,
+                     TALLOC_CTX *mem_ctx,
+                     struct http_conn **http_conn)
+{
+       struct http_connect_state *state = tevent_req_data(
+               req, struct http_connect_state);
+       int error;
+
+       if (tevent_req_is_unix_error(req, &error)) {
+               tevent_req_received(req);
+               return error;
+       }
+
+       *http_conn = talloc_move(mem_ctx, &state->http_conn);
+       tevent_req_received(req);
+
+       return 0;
+}
+
+struct tevent_queue *http_conn_send_queue(struct http_conn *http_conn)
+{
+       return http_conn->send_queue;
+}
+
+struct tstream_context *http_conn_tstream(struct http_conn *http_conn)
+{
+       return http_conn->tstreams.active;
+}
+
+struct http_conn_disconnect_state {
+       struct tevent_context *ev;
+       struct http_conn *http_conn;
+};
+
+static void http_conn_disconnect_done(struct tevent_req *subreq);
+
+struct tevent_req *http_disconnect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct http_conn *http_conn)
+{
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct http_conn_disconnect_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct http_conn_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       *state = (struct http_conn_disconnect_state) {
+               .ev = ev,
+               .http_conn = http_conn,
+       };
+
+       if (http_conn->tstreams.active == NULL) {
+               tevent_req_error(req, ENOTCONN);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = tstream_disconnect_send(state, ev, http_conn->tstreams.active);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, http_conn_disconnect_done, req);
+
+       return req;
+}
+
+static void http_conn_disconnect_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       int ret;
+       int error;
+
+       ret = tstream_disconnect_recv(subreq, &error);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_error(req, error);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+int http_disconnect_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_unix(req);
+}
index 040466dcb293885eb8912ad8757a02015bcb9999..13b748f5716ef917756b3982705efa6755527e56 100644 (file)
@@ -38,6 +38,15 @@ enum http_read_status {
        HTTP_DATA_TOO_LONG,
 };
 
+struct http_conn {
+       struct tevent_queue *send_queue;
+       struct {
+               struct tstream_context *raw;
+               struct tstream_context *tls;
+               struct tstream_context *active;
+       } tstreams;
+};
+
 struct http_send_request_state {
        struct tevent_context   *ev;
        struct tstream_context  *stream;
index e77a699a351c5cfcc3874cc23cf288beaee01aed..4ef1574bc674c350a45bb3a981247e43a94f0cbb 100644 (file)
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 
 bld.SAMBA_LIBRARY('http',
-       source='http.c http_auth.c',
-       deps='talloc tevent samba3core',
+       source='http.c http_auth.c http_conn.c',
+       deps='talloc tevent samba3core dns_lookup',
        private_library=True,
 )