Add simple http_client for use in black box tests (in following commits)
authorNoel Power <noel.power@suse.com>
Mon, 25 Mar 2024 19:21:54 +0000 (19:21 +0000)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 27 Mar 2024 01:14:31 +0000 (01:14 +0000)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15611
Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/client/http_test.c [new file with mode: 0644]
source4/wscript_build

diff --git a/source4/client/http_test.c b/source4/client/http_test.c
new file mode 100644 (file)
index 0000000..ca542ba
--- /dev/null
@@ -0,0 +1,401 @@
+#include "includes.h"
+#include "version.h"
+#include "libcli/libcli.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/http/http.h"
+#include "credentials.h"
+#include "util/tevent_ntstatus.h"
+#include "lib/tls/tls.h"
+#include "lib/cmdline/cmdline.h"
+
+
+struct http_client_info {
+       struct http_conn *http_conn;
+       uint16_t server_port;
+       const char *server_addr;
+       struct tstream_tls_params *tls_params;
+       struct cli_credentials *creds;
+       struct loadparm_context *lp_ctx;
+       const char *uri;
+};
+
+static bool send_http_request(TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev_ctx,
+       struct http_client_info* es,
+       size_t response_size,
+       NTSTATUS *pstatus)
+{
+       struct http_request *http_req = NULL;
+       struct tevent_req *req = NULL;
+       char *uri = NULL;
+       struct http_request *http_response = NULL;
+       NTSTATUS status;
+
+       http_req = talloc_zero(mem_ctx, struct http_request);
+       if (!http_req) {
+               DBG_ERR("no memory\n");
+               return false;
+       }
+
+       uri = talloc_strdup(mem_ctx, es->uri);
+
+       http_req->type = HTTP_REQ_POST;
+       http_req->uri = uri;
+       http_req->body = data_blob_null;
+       http_req->major = '1';
+       http_req->minor = '1';
+
+       http_add_header(mem_ctx, &http_req->headers,
+                       "User-Agent", "Samba/http_test");
+       http_add_header(mem_ctx, &http_req->headers,
+                       "Accept", "*/*");
+
+       req = http_send_auth_request_send(mem_ctx,
+                               ev_ctx,
+                               es->http_conn,
+                               http_req,
+                               es->creds,
+                               es->lp_ctx,
+                               HTTP_AUTH_BASIC);
+       if (!tevent_req_set_endtime(req, ev_ctx, timeval_current_ofs(10, 0))) {
+               DBG_ERR("Failed to set timeout\n");
+               return false;
+       }
+
+       if (!tevent_req_poll_ntstatus(req, ev_ctx, pstatus)) {
+               DBG_ERR("Failed to connect: %s\n", nt_errstr(*pstatus));
+               return false;
+       }
+
+       status = http_send_auth_request_recv(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Auth request failed: %s\n", nt_errstr(status));
+               return false;
+       }
+
+       req = http_read_response_send(mem_ctx,
+                               ev_ctx,
+                               es->http_conn,
+                               response_size);
+       if (!req) {
+               DBG_ERR("no memory\n");
+               return -1;
+       }
+
+       if (!tevent_req_set_endtime(req, ev_ctx, timeval_current_ofs(10, 0))) {
+               DBG_ERR("Failed to set timeout\n");
+               return false;
+       }
+
+       if (!tevent_req_poll_ntstatus(req, ev_ctx, pstatus)) {
+               DBG_ERR("Failed to read_resonse: %s\n", nt_errstr(*pstatus));
+               return false;
+       }
+
+       *pstatus = http_read_response_recv(req, mem_ctx, &http_response);
+
+       if (!NT_STATUS_IS_OK(*pstatus)) {
+               DBG_ERR("Failed to receive response: %s\n", nt_errstr(*pstatus));
+               return false;
+       }
+       /* following are not 'hard' errors */
+       if (http_response->response_code != 200) {
+               fprintf(stdout, "HTTP server response: %u\n",
+                       http_response->response_code);
+               fflush(stdout);
+               return false;
+
+       }
+       if (http_response->body.length == 0) {
+               fprintf(stdout, "unexpected 0 len response\n");
+               fflush(stdout);
+               return false;
+       }
+       DBG_ERR("response: len (%d)\n%s\n",
+                 (int)http_response->body.length,
+                 talloc_strndup(mem_ctx,
+                                (char *)http_response->body.data,
+                                http_response->body.length));
+       fprintf(stdout,"%s", talloc_strndup(mem_ctx,
+                                           (char *)http_response->body.data,
+                                           http_response->body.length));
+       fflush(stdout);
+       return true;
+}
+
+int main(int argc, const char *argv[])
+
+{
+       TALLOC_CTX *mem_ctx;
+       struct tevent_context *ev_ctx;
+       int retries = 4;
+       int count = 0;
+       struct http_client_info *http_info = NULL;
+       bool use_tls = false;
+       int res;
+       NTSTATUS status;
+       struct tevent_req *req = NULL;
+       bool connected = false;
+       poptContext pc;
+       const char **const_argv = discard_const_p(const char *, argv);
+       int opt;
+       bool ok;
+       const char *ca_file = NULL;
+       int port = 0;
+       size_t response_size = 8192000;
+       struct cli_credentials *cli_creds;
+
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+
+               {
+                       .longName   = "usetls",
+                       .shortName  = 't',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 't',
+                       .descrip    = "Use tls",
+                       .argDescrip = "enable tls",
+               },
+               {
+                       .longName   = "ip-address",
+                       .shortName  = 'I',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = NULL,
+                       .val        = 'I',
+                       .descrip    = "Use this IP to connect to",
+                       .argDescrip = "IP",
+               },
+               {
+                       .longName   = "port",
+                       .shortName  = 'p',
+                       .argInfo    = POPT_ARG_INT,
+                       .arg        = &port,
+                       .val        = 'p',
+                       .descrip    = "port to connect to",
+                       .argDescrip = "port",
+               },
+               {
+                       .longName   = "cacart",
+                       .shortName  = 'c',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = NULL,
+                       .val        = 'c',
+                       .descrip    = "CA certificate to verify peer against",
+                       .argDescrip = "ca cert",
+               },
+               {
+                       .longName   = "uri",
+                       .shortName  = 'u',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = NULL,
+                       .val        = 'u',
+                       .descrip    = "uri to send as part of http request",
+                       .argDescrip = "uri",
+               },
+               {
+                       .longName   = "rsize",
+                       .argInfo    = POPT_ARG_LONG,
+                       .arg        = &response_size,
+                       .descrip    = "response size",
+               },
+               POPT_COMMON_SAMBA
+               POPT_COMMON_CREDENTIALS
+               POPT_TABLEEND
+       };
+
+       mem_ctx = talloc_init("http_test");
+
+       if (!mem_ctx) {
+               DBG_ERR("Not enough memory\n");
+               res = -1;
+               goto done;
+       }
+
+       http_info = talloc_zero(mem_ctx, struct http_client_info);
+
+       if (http_info == NULL) {
+               DBG_ERR("Not enough memory\n");
+               res = -1;
+               goto done;
+       }
+
+       ok = samba_cmdline_init(mem_ctx,
+                               SAMBA_CMDLINE_CONFIG_CLIENT,
+                               false /* require_smbconf */);
+       if (!ok) {
+               DBG_ERR("Failed to init cmdline parser!\n");
+               res = -1;
+               goto done;
+       }
+
+       pc = samba_popt_get_context(getprogname(),
+                                   argc,
+                                   const_argv,
+                                   long_options,
+                                   0);
+       if (pc == NULL) {
+               DBG_ERR("Failed to setup popt context!\n");
+               res = -1;
+               goto done;
+       }
+
+       /* some defaults */
+
+       http_info->server_addr = "localhost";
+       http_info->uri = "/_search?pretty";
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+                       case 't':
+                               use_tls = true;
+                               break;
+                       case  'c': {
+                               ca_file = talloc_strdup(mem_ctx,
+                                                       poptGetOptArg(pc));
+                               if (ca_file == NULL) {
+                                       DBG_ERR("Not enough memory\n");
+                                       res = -1;
+                                       goto done;
+                               }
+                               break;
+                       }
+                       case 'I': {
+                               http_info->server_addr = talloc_strdup(mem_ctx,
+                                                       poptGetOptArg(pc));
+                               if (http_info->server_addr == NULL) {
+                                       DBG_ERR("Not enough memory\n");
+                                       res = -1;
+                                       goto done;
+                               }
+                               break;
+                       }
+                       case 'u': {
+                               http_info->uri = talloc_strdup(mem_ctx,
+                                                       poptGetOptArg(pc));
+                               if (http_info->uri == NULL) {
+                                       DBG_ERR("Not enough memory\n");
+                                       res = -1;
+                                       goto done;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       if (use_tls && ca_file == NULL) {
+               DBG_ERR("No cacert\n");
+               res = -1;
+               poptPrintUsage(pc, stderr, 0);
+               goto done;
+       }
+
+       if (!port) {
+               port = 8080;
+       }
+       http_info->server_port = port;
+
+       ev_ctx = s4_event_context_init(mem_ctx);
+       if (!ev_ctx) {
+               DBG_ERR("Not enough memory\n");
+               res = -1;
+               goto done;
+       }
+
+
+       cli_creds = samba_cmdline_get_creds();
+       if (!cli_credentials_is_anonymous(cli_creds)) {
+               http_info->creds = cli_credentials_init(mem_ctx);
+               cli_credentials_set_username(
+                       http_info->creds,
+                       cli_credentials_get_username(cli_creds),
+                       CRED_SPECIFIED);
+               cli_credentials_set_password(http_info->creds,
+                       cli_credentials_get_password(cli_creds),
+                       CRED_SPECIFIED);
+       } else {
+               DBG_DEBUG("Anonymous creds!!!\n");
+               http_info->creds = cli_creds;
+       }
+       if (http_info->creds == NULL) {
+               DBG_ERR("Failed to create creds\n");
+               res = -1;
+               goto done;
+       }
+       http_info->lp_ctx = samba_cmdline_get_lp_ctx();
+
+       DBG_ERR("retries = %d/%d, Using server %s, port %d, using tls %s\n",
+               count, retries,
+               http_info->server_addr,
+               http_info->server_port,
+               use_tls ? "true" : "false");
+
+       while (count < retries) {
+               int error;
+               DBG_ERR("Connecting to HTTP [%s] port [%"PRIu16"]%s\n",
+                       http_info->server_addr, http_info->server_port,
+                       use_tls ? " with tls" : " without tls");
+               if (use_tls) {
+                       const char *crl_file = NULL;
+                       const char *tls_priority = "NORMAL:-VERS-SSL3.0";
+                       enum tls_verify_peer_state verify_peer =
+                               TLS_VERIFY_PEER_CA_ONLY;
+
+                       status = tstream_tls_params_client(mem_ctx,
+                                                  ca_file,
+                                                  crl_file,
+                                                  tls_priority,
+                                                  verify_peer,
+                                                  http_info->server_addr,
+                                                  &http_info->tls_params);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DBG_ERR("Failed tstream_tls_params_client - %s\n",
+                                       nt_errstr(status));
+                               res = -1;
+                               goto done;
+                       }
+               }
+
+               req = http_connect_send(mem_ctx,
+                                       ev_ctx,
+                                       http_info->server_addr,
+                                       http_info->server_port,
+                                       http_info->creds,
+                                       http_info->tls_params);
+               if (!tevent_req_poll_ntstatus(req, ev_ctx, &status)) {
+                       res = -1;
+                       goto done;
+               }
+
+               error = http_connect_recv(req,
+                                       mem_ctx,
+                                       &http_info->http_conn);
+               if (error != 0) {
+                       count++;
+                       DBG_ERR("HTTP connection failed retry %d/%d: %s\n", count, retries, strerror(error));
+               } else {
+                       DBG_ERR("HTTP connection succeeded\n");
+                       connected = true;
+                       break;
+               }
+       }
+
+       if (!connected) {
+               DBG_ERR("Leaving early\n");
+               res = -1;
+               goto done;
+       }
+
+       if (!send_http_request(mem_ctx, ev_ctx, http_info, response_size, &status)) {
+               DBG_ERR("Failure\n");
+               res = -1;
+               goto done;
+       }
+       res = 0;
+done:
+       TALLOC_FREE(mem_ctx);
+       return res;
+}
index d20444135ebc20fd8d980e10b22c97e215b30997..db2ad3eda5b02380fb9b6b489b23f7bfd43b7540 100644 (file)
@@ -6,6 +6,11 @@ bld.SAMBA_BINARY('client/smbclient'  + bld.env.suffix4,
        install=False
        )
 
+bld.SAMBA_BINARY('client/http_test',
+    source='client/http_test.c',
+    deps='samba-hostconfig SMBREADLINE samba-util LIBCLI_SMB RPC_NDR_SRVSVC LIBCLI_LSA popt CMDLINE_S4',
+    for_selftest=True,
+    )
 
 bld.SAMBA_BINARY('client/cifsdd',
        source='client/cifsdd.c client/cifsddio.c',