s4:lib/http: add support for http POST
[garming/samba-autobuild/.git] / libcli / http / http_conn.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    HTTP library
5
6    Copyright (C) 2019 Ralph Boehme <slow@samba.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/util/tevent_ntstatus.h"
24 #include "libcli/dns/dns_lookup.h"
25 #include "lib/tsocket/tsocket.h"
26 #include "lib/util/util_net.h"
27 #include "lib/tls/tls.h"
28 #include "lib/util/tevent_unix.h"
29 #include "http.h"
30 #include "http_internal.h"
31
32 struct http_connect_state {
33         struct tevent_context *ev;
34         const char *http_server;
35         const char *http_server_ip;
36         uint16_t http_port;
37         struct tsocket_address *local_address;
38         struct tsocket_address *remote_address;
39         struct cli_credentials *credentials;
40         struct tstream_tls_params *tls_params;
41
42         struct http_conn *http_conn;
43 };
44
45 static void http_connect_dns_done(struct tevent_req *subreq);
46 static void http_connect_tcp_connect(struct tevent_req *req);
47 static void http_connect_tcp_done(struct tevent_req *subreq);
48 static void http_connect_tls_done(struct tevent_req *subreq);
49
50 struct tevent_req *http_connect_send(TALLOC_CTX *mem_ctx,
51                                      struct tevent_context *ev,
52                                      const char *http_server,
53                                      uint16_t http_port,
54                                      struct cli_credentials *credentials,
55                                      struct tstream_tls_params *tls_params)
56 {
57         struct tevent_req *req = NULL;
58         struct tevent_req *subreq = NULL;
59         struct http_connect_state *state = NULL;
60         int ret;
61
62         DBG_DEBUG("Connecting to [%s] over HTTP%s\n",
63                   http_server, tls_params != NULL ? "S" : "");
64
65         req = tevent_req_create(mem_ctx, &state, struct http_connect_state);
66         if (req == NULL) {
67                 return NULL;
68         }
69
70         *state = (struct http_connect_state) {
71                 .ev = ev,
72                 .http_port = http_port,
73                 .credentials = credentials,
74                 .tls_params = tls_params,
75         };
76
77         state->http_server = talloc_strdup(state, http_server);
78         if (tevent_req_nomem(state->http_server, req)) {
79                 return tevent_req_post(req, ev);
80         }
81
82         state->http_conn = talloc_zero(state, struct http_conn);
83         if (tevent_req_nomem(state->http_conn, req)) {
84                 return tevent_req_post(req, ev);
85         }
86
87         state->http_conn->send_queue = tevent_queue_create(state->http_conn,
88                                                            "HTTP send queue");
89         if (tevent_req_nomem(state->http_conn->send_queue, req)) {
90                 return tevent_req_post(req, ev);
91         }
92
93         ret = tsocket_address_inet_from_strings(state,
94                                                 "ip",
95                                                 NULL,
96                                                 0,
97                                                 &state->local_address);
98         if (ret != 0) {
99                 tevent_req_error(req, errno);
100                 return tevent_req_post(req, ev);
101         }
102
103         if (!is_ipaddress(http_server)) {
104                 subreq = dns_lookup_send(state,
105                                          ev,
106                                          NULL,
107                                          http_server,
108                                          DNS_QCLASS_IN,
109                                          DNS_QTYPE_A);
110                 if (tevent_req_nomem(subreq, req)) {
111                         return tevent_req_post(req, ev);
112                 }
113                 tevent_req_set_callback(subreq, http_connect_dns_done, req);
114                 return req;
115         }
116         state->http_server_ip = state->http_server;
117
118         http_connect_tcp_connect(req);
119         if (!tevent_req_is_in_progress(req)) {
120                 return tevent_req_post(req, ev);
121         }
122
123         return req;
124 }
125
126 static void http_connect_dns_done(struct tevent_req *subreq)
127 {
128         struct tevent_req *req = tevent_req_callback_data(
129                 subreq, struct tevent_req);
130         struct http_connect_state *state = tevent_req_data(
131                 req, struct http_connect_state);
132         struct dns_name_packet *dns_reply = NULL;
133         struct dns_res_rec *an = NULL;
134         uint16_t i;
135         int ret;
136
137         ret = dns_lookup_recv(subreq, state, &dns_reply);
138         TALLOC_FREE(subreq);
139         if (ret != 0) {
140                 tevent_req_error(req, ret);
141                 return;
142         }
143
144         for (i = 0; i < dns_reply->ancount; i++) {
145                 an = &dns_reply->answers[i];
146                 if (an->rr_type == DNS_QTYPE_A) {
147                         break;
148                 }
149         }
150         if (i >= dns_reply->ancount) {
151                 tevent_req_error(req, ENOENT);
152                 return;
153         }
154
155         state->http_server_ip = talloc_strdup(state, an->rdata.ipv4_record);
156         if (tevent_req_nomem(state->http_server_ip, req)) {
157                 return;
158         }
159
160         return http_connect_tcp_connect(req);
161 }
162
163 static void http_connect_tcp_connect(struct tevent_req *req)
164 {
165         struct http_connect_state *state = tevent_req_data(
166                 req, struct http_connect_state);
167         struct tevent_req *subreq = NULL;
168         int ret;
169
170         ret = tsocket_address_inet_from_strings(state,
171                                                 "ip",
172                                                 state->http_server_ip,
173                                                 state->http_port,
174                                                 &state->remote_address);
175         if (ret != 0) {
176                 int saved_errno = errno;
177
178                 DBG_ERR("Cannot create remote socket address, error: %s (%d)\n",
179                         strerror(errno), errno);
180                 tevent_req_error(req, saved_errno);
181                 return;
182         }
183
184         subreq = tstream_inet_tcp_connect_send(state,
185                                                state->ev,
186                                                state->local_address,
187                                                state->remote_address);
188         if (tevent_req_nomem(subreq, req)) {
189                 return;
190         }
191         tevent_req_set_callback(subreq, http_connect_tcp_done, req);
192 }
193
194 static void http_connect_tcp_done(struct tevent_req *subreq)
195 {
196         struct tevent_req *req = tevent_req_callback_data(
197                 subreq, struct tevent_req);
198         struct http_connect_state *state = tevent_req_data(
199                 req, struct http_connect_state);
200         int error;
201         int ret;
202
203         ret = tstream_inet_tcp_connect_recv(subreq,
204                                             &error,
205                                             state->http_conn,
206                                             &state->http_conn->tstreams.raw,
207                                             NULL);
208         TALLOC_FREE(subreq);
209         if (ret != 0) {
210                 tevent_req_error(req, error);
211                 return;
212         }
213
214         state->http_conn->tstreams.active = state->http_conn->tstreams.raw;
215         DBG_DEBUG("Socket connected\n");
216
217         if (state->tls_params == NULL) {
218                 tevent_req_done(req);
219                 return;
220         }
221
222         DBG_DEBUG("Starting TLS\n");
223
224         subreq = tstream_tls_connect_send(state,
225                                           state->ev,
226                                           state->http_conn->tstreams.active,
227                                           state->tls_params);
228         if (tevent_req_nomem(subreq, req)) {
229                 return;
230         }
231         tevent_req_set_callback(subreq, http_connect_tls_done, req);
232 }
233
234 static void http_connect_tls_done(struct tevent_req *subreq)
235 {
236         struct tevent_req *req = tevent_req_callback_data(
237                 subreq, struct tevent_req);
238         struct http_connect_state *state = tevent_req_data(
239                 req, struct http_connect_state);
240         int error;
241         int ret;
242
243         ret = tstream_tls_connect_recv(subreq,
244                                        &error,
245                                        state->http_conn,
246                                        &state->http_conn->tstreams.tls);
247         TALLOC_FREE(subreq);
248         if (ret != 0) {
249                 tevent_req_error(req, error);
250                 return;
251         }
252
253         state->http_conn->tstreams.active = state->http_conn->tstreams.tls;
254
255         DBG_DEBUG("TLS handshake completed\n");
256         tevent_req_done(req);
257 }
258
259 int http_connect_recv(struct tevent_req *req,
260                       TALLOC_CTX *mem_ctx,
261                       struct http_conn **http_conn)
262 {
263         struct http_connect_state *state = tevent_req_data(
264                 req, struct http_connect_state);
265         int error;
266
267         if (tevent_req_is_unix_error(req, &error)) {
268                 tevent_req_received(req);
269                 return error;
270         }
271
272         *http_conn = talloc_move(mem_ctx, &state->http_conn);
273         tevent_req_received(req);
274
275         return 0;
276 }
277
278 struct tevent_queue *http_conn_send_queue(struct http_conn *http_conn)
279 {
280         return http_conn->send_queue;
281 }
282
283 struct tstream_context *http_conn_tstream(struct http_conn *http_conn)
284 {
285         return http_conn->tstreams.active;
286 }
287
288 struct http_conn_disconnect_state {
289         struct tevent_context *ev;
290         struct http_conn *http_conn;
291 };
292
293 static void http_conn_disconnect_done(struct tevent_req *subreq);
294
295 struct tevent_req *http_disconnect_send(TALLOC_CTX *mem_ctx,
296                                         struct tevent_context *ev,
297                                         struct http_conn *http_conn)
298 {
299         struct tevent_req *req = NULL;
300         struct tevent_req *subreq = NULL;
301         struct http_conn_disconnect_state *state = NULL;
302
303         req = tevent_req_create(mem_ctx, &state,
304                                 struct http_conn_disconnect_state);
305         if (req == NULL) {
306                 return NULL;
307         }
308
309         *state = (struct http_conn_disconnect_state) {
310                 .ev = ev,
311                 .http_conn = http_conn,
312         };
313
314         if (http_conn->tstreams.active == NULL) {
315                 tevent_req_error(req, ENOTCONN);
316                 return tevent_req_post(req, ev);
317         }
318
319         subreq = tstream_disconnect_send(state, ev, http_conn->tstreams.active);
320         if (tevent_req_nomem(subreq, req)) {
321                 return tevent_req_post(req, ev);
322         }
323         tevent_req_set_callback(subreq, http_conn_disconnect_done, req);
324
325         return req;
326 }
327
328 static void http_conn_disconnect_done(struct tevent_req *subreq)
329 {
330         struct tevent_req *req = tevent_req_callback_data(
331                 subreq, struct tevent_req);
332         int ret;
333         int error;
334
335         ret = tstream_disconnect_recv(subreq, &error);
336         TALLOC_FREE(subreq);
337         if (ret == -1) {
338                 tevent_req_error(req, error);
339                 return;
340         }
341
342         tevent_req_done(req);
343 }
344
345 int http_disconnect_recv(struct tevent_req *req)
346 {
347         return tevent_req_simple_recv_unix(req);
348 }