summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2019-03-25 14:39:59 +0100
committerRalph Boehme <slow@samba.org>2019-08-07 12:54:40 +0000
commit314043828b3e3d70600f0f365caad37dbf171d1a (patch)
tree34b8d86d60f853c0e73a405a750a8754c8b35221 /source4
parent2a90202052558c945e02675d1331e65aeb15f9fa (diff)
downloadsamba-314043828b3e3d70600f0f365caad37dbf171d1a.tar.gz
s4:lib/http: add http_[dis]connect_send and recv
Not used for now, that comes later. Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Samuel Cabrero <scabrero@suse.de>
Diffstat (limited to 'source4')
-rw-r--r--source4/lib/http/http.h23
-rw-r--r--source4/lib/http/http_conn.c348
-rw-r--r--source4/lib/http/http_internal.h9
-rw-r--r--source4/lib/http/wscript_build4
4 files changed, 382 insertions, 2 deletions
diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h
index 0fa65ca89a0..25055c91007 100644
--- a/source4/lib/http/http.h
+++ b/source4/lib/http/http.h
@@ -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
index 00000000000..de1be3f2d8e
--- /dev/null
+++ b/source4/lib/http/http_conn.c
@@ -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);
+}
diff --git a/source4/lib/http/http_internal.h b/source4/lib/http/http_internal.h
index 040466dcb29..13b748f5716 100644
--- a/source4/lib/http/http_internal.h
+++ b/source4/lib/http/http_internal.h
@@ -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;
diff --git a/source4/lib/http/wscript_build b/source4/lib/http/wscript_build
index e77a699a351..4ef1574bc67 100644
--- a/source4/lib/http/wscript_build
+++ b/source4/lib/http/wscript_build
@@ -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,
)