1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/libcli/http/http_conn.c
Ralph Boehme fc0371b932 s4:lib/http: move to the toplevel
This is going to be used from the s3 RPC server soon...

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Samuel Cabrero <scabrero@suse.de>
2019-08-07 12:54:40 +00:00

349 lines
8.5 KiB
C

/*
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);
}