add OSSL_STACK_OF_X509_free() for commonly used pattern
[openssl.git] / apps / cmp.c
index 1fbf10c4a4c4a5100739249c9da9c03dcb2051e0..0f810129b3924cc0e55b8fbbcfbbdfa85c13c823 100644 (file)
@@ -9,6 +9,8 @@
  * https://www.openssl.org/source/license.html
  */
 
+/* This app is disabled when OPENSSL_NO_CMP is defined. */
+
 #include <string.h>
 #include <ctype.h>
 
@@ -66,12 +68,14 @@ typedef enum {
 } cmp_cmd_t;
 
 /* message transfer */
+#ifndef OPENSSL_NO_SOCK
 static char *opt_server = NULL;
-static char server_port[32] = { '\0' };
-static char *opt_path = NULL;
 static char *opt_proxy = NULL;
 static char *opt_no_proxy = NULL;
+#endif
 static char *opt_recipient = NULL;
+static char *opt_path = NULL;
+static int opt_keep_alive = 1;
 static int opt_msg_timeout = -1;
 static int opt_total_timeout = -1;
 
@@ -131,11 +135,12 @@ static int opt_revreason = CRL_REASON_NONE;
 /* credentials format */
 static char *opt_certform_s = "PEM";
 static int opt_certform = FORMAT_PEM;
-static char *opt_keyform_s = "PEM";
-static int opt_keyform = FORMAT_PEM;
+static char *opt_keyform_s = NULL;
+static int opt_keyform = FORMAT_UNDEF;
 static char *opt_otherpass = NULL;
 static char *opt_engine = NULL;
 
+#ifndef OPENSSL_NO_SOCK
 /* TLS connection */
 static int opt_tls_used = 0;
 static char *opt_tls_cert = NULL;
@@ -144,6 +149,7 @@ static char *opt_tls_keypass = NULL;
 static char *opt_tls_extra = NULL;
 static char *opt_tls_trusted = NULL;
 static char *opt_tls_host = NULL;
+#endif
 
 /* client-side debugging */
 static int opt_batch = 0;
@@ -155,10 +161,11 @@ static char *opt_rspin = NULL;
 static char *opt_rspout = NULL;
 static int opt_use_mock_srv = 0;
 
-/* server-side debugging */
+/* mock server */
+#ifndef OPENSSL_NO_SOCK
 static char *opt_port = NULL;
 static int opt_max_msgs = 0;
-
+#endif
 static char *opt_srv_ref = NULL;
 static char *opt_srv_secret = NULL;
 static char *opt_srv_cert = NULL;
@@ -188,7 +195,7 @@ static int opt_accept_raverified = 0;
 static X509_VERIFY_PARAM *vpm = NULL;
 
 typedef enum OPTION_choice {
-    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_COMMON,
     OPT_CONFIG, OPT_SECTION, OPT_VERBOSITY,
 
     OPT_CMD, OPT_INFOTYPE, OPT_GENINFO,
@@ -203,9 +210,11 @@ typedef enum OPTION_choice {
 
     OPT_OLDCERT, OPT_REVREASON,
 
-    OPT_SERVER, OPT_PATH, OPT_PROXY, OPT_NO_PROXY,
-    OPT_RECIPIENT,
-    OPT_MSG_TIMEOUT, OPT_TOTAL_TIMEOUT,
+#ifndef OPENSSL_NO_SOCK
+    OPT_SERVER, OPT_PROXY, OPT_NO_PROXY,
+#endif
+    OPT_RECIPIENT, OPT_PATH,
+    OPT_KEEP_ALIVE, OPT_MSG_TIMEOUT, OPT_TOTAL_TIMEOUT,
 
     OPT_TRUSTED, OPT_UNTRUSTED, OPT_SRVCERT,
     OPT_EXPECT_SENDER,
@@ -224,15 +233,19 @@ typedef enum OPTION_choice {
     OPT_PROV_ENUM,
     OPT_R_ENUM,
 
+#ifndef OPENSSL_NO_SOCK
     OPT_TLS_USED, OPT_TLS_CERT, OPT_TLS_KEY,
     OPT_TLS_KEYPASS,
     OPT_TLS_EXTRA, OPT_TLS_TRUSTED, OPT_TLS_HOST,
+#endif
 
     OPT_BATCH, OPT_REPEAT,
     OPT_REQIN, OPT_REQIN_NEW_TID, OPT_REQOUT, OPT_RSPIN, OPT_RSPOUT,
     OPT_USE_MOCK_SRV,
 
+#ifndef OPENSSL_NO_SOCK
     OPT_PORT, OPT_MAX_MSGS,
+#endif
     OPT_SRV_REF, OPT_SRV_SECRET,
     OPT_SRV_CERT, OPT_SRV_KEY, OPT_SRV_KEYPASS,
     OPT_SRV_TRUSTED, OPT_SRV_UNTRUSTED,
@@ -255,7 +268,7 @@ const OPTIONS cmp_options[] = {
      "Configuration file to use. \"\" = none. Default from env variable OPENSSL_CONF"},
     {"section", OPT_SECTION, 's',
      "Section(s) in config file to get options from. \"\" = 'default'. Default 'cmp'"},
-    {"verbosity", OPT_VERBOSITY, 'n',
+    {"verbosity", OPT_VERBOSITY, 'N',
      "Log level; 3=ERR, 4=WARN, 6=INFO, 7=DEBUG, 8=TRACE. Default 6 = INFO"},
 
     OPT_SECTION("Generic message"),
@@ -274,14 +287,14 @@ const OPTIONS cmp_options[] = {
     {"subject", OPT_SUBJECT, 's',
      "Distinguished Name (DN) of subject to use in the requested cert template"},
     {OPT_MORE_STR, 0, 0,
-     "For kur, default is subject of -csr arg or else of reference cert (see -oldcert)"},
+     "For kur, default is subject of -csr arg or reference cert (see -oldcert)"},
     {OPT_MORE_STR, 0, 0,
      "this default is used for ir and cr only if no Subject Alt Names are set"},
     {"issuer", OPT_ISSUER, 's',
      "DN of the issuer to place in the requested certificate template"},
     {OPT_MORE_STR, 0, 0,
      "also used as recipient if neither -recipient nor -srvcert are given"},
-    {"days", OPT_DAYS, 'n',
+    {"days", OPT_DAYS, 'N',
      "Requested validity time of the new certificate in number of days"},
     {"reqexts", OPT_REQEXTS, 's',
      "Name of config file section defining certificate request extensions."},
@@ -323,30 +336,37 @@ const OPTIONS cmp_options[] = {
     {OPT_MORE_STR, 0, 0,
      "also used as reference (defaulting to -cert) for subject DN and SANs."},
     {OPT_MORE_STR, 0, 0,
-     "Its issuer is used as recipient unless -recipient, -srvcert, or -issuer given"},
+     "Issuer is used as recipient unless -recipient, -srvcert, or -issuer given"},
     {"revreason", OPT_REVREASON, 'n',
      "Reason code to include in revocation request (rr); possible values:"},
     {OPT_MORE_STR, 0, 0,
      "0..6, 8..10 (see RFC5280, 5.3.1) or -1. Default -1 = none included"},
 
     OPT_SECTION("Message transfer"),
+#ifdef OPENSSL_NO_SOCK
+    {OPT_MORE_STR, 0, 0,
+     "NOTE: -server, -proxy, and -no_proxy not supported due to no-sock build"},
+#else
     {"server", OPT_SERVER, 's',
      "[http[s]://]address[:port][/path] of CMP server. Default port 80 or 443."},
     {OPT_MORE_STR, 0, 0,
      "address may be a DNS name or an IP address; path can be overridden by -path"},
-    {"path", OPT_PATH, 's',
-     "HTTP path (aka CMP alias) at the CMP server. Default from -server, else \"/\""},
     {"proxy", OPT_PROXY, 's',
      "[http[s]://]address[:port][/path] of HTTP(S) proxy to use; path is ignored"},
     {"no_proxy", OPT_NO_PROXY, 's',
      "List of addresses of servers not to use HTTP(S) proxy for"},
     {OPT_MORE_STR, 0, 0,
      "Default from environment variable 'no_proxy', else 'NO_PROXY', else none"},
+#endif
     {"recipient", OPT_RECIPIENT, 's',
      "DN of CA. Default: subject of -srvcert, -issuer, issuer of -oldcert or -cert"},
-    {"msg_timeout", OPT_MSG_TIMEOUT, 'n',
-     "Timeout per CMP message round trip (or 0 for none). Default 120 seconds"},
-    {"total_timeout", OPT_TOTAL_TIMEOUT, 'n',
+    {"path", OPT_PATH, 's',
+     "HTTP path (aka CMP alias) at the CMP server. Default from -server, else \"/\""},
+    {"keep_alive", OPT_KEEP_ALIVE, 'N',
+     "Persistent HTTP connections. 0: no, 1 (the default): request, 2: require"},
+    {"msg_timeout", OPT_MSG_TIMEOUT, 'N',
+     "Number of seconds allowed per CMP message round trip, or 0 for infinite"},
+    {"total_timeout", OPT_TOTAL_TIMEOUT, 'N',
      "Overall time an enrollment incl. polling may take. Default 0 = infinite"},
 
     OPT_SECTION("Server authentication"),
@@ -416,6 +436,10 @@ const OPTIONS cmp_options[] = {
     OPT_R_OPTIONS,
 
     OPT_SECTION("TLS connection"),
+#ifdef OPENSSL_NO_SOCK
+    {OPT_MORE_STR, 0, 0,
+     "NOTE: -tls_used and all other TLS options not supported due to no-sock build"},
+#else
     {"tls_used", OPT_TLS_USED, '-',
      "Enable using TLS (also when other TLS options are not set)"},
     {"tls_cert", OPT_TLS_CERT, 's',
@@ -431,12 +455,13 @@ const OPTIONS cmp_options[] = {
     {OPT_MORE_STR, 0, 0, "this implies host name validation"},
     {"tls_host", OPT_TLS_HOST, 's',
      "Address to be checked (rather than -server) during TLS host name validation"},
+#endif
 
     OPT_SECTION("Client-side debugging"),
     {"batch", OPT_BATCH, '-',
      "Do not interactively prompt for input when a password is required etc."},
-    {"repeat", OPT_REPEAT, 'n',
-     "Invoke the transaction the given number of times. Default 1"},
+    {"repeat", OPT_REPEAT, 'p',
+     "Invoke the transaction the given positive number of times. Default 1"},
     {"reqin", OPT_REQIN, 's', "Take sequence of CMP requests from file(s)"},
     {"reqin_new_tid", OPT_REQIN_NEW_TID, '-',
      "Use fresh transactionID for CMP requests read from -reqin"},
@@ -445,12 +470,19 @@ const OPTIONS cmp_options[] = {
      "Process sequence of CMP responses provided in file(s), skipping server"},
     {"rspout", OPT_RSPOUT, 's', "Save sequence of CMP responses to file(s)"},
 
-    {"use_mock_srv", OPT_USE_MOCK_SRV, '-', "Use mock server at API level, bypassing HTTP"},
+    {"use_mock_srv", OPT_USE_MOCK_SRV, '-',
+     "Use internal mock server at API level, bypassing socket-based HTTP"},
 
     OPT_SECTION("Mock server"),
-    {"port", OPT_PORT, 's', "Act as HTTP mock server listening on given port"},
-    {"max_msgs", OPT_MAX_MSGS, 'n',
+#ifdef OPENSSL_NO_SOCK
+    {OPT_MORE_STR, 0, 0,
+     "NOTE: -port and -max_msgs not supported due to no-sock build"},
+#else
+    {"port", OPT_PORT, 's',
+     "Act as HTTP-based mock server listening on given port"},
+    {"max_msgs", OPT_MAX_MSGS, 'N',
      "max number of messages handled by HTTP mock server. Default: 0 = unlimited"},
+#endif
 
     {"srv_ref", OPT_SRV_REF, 's',
      "Reference value to use as senderKID of server in case no -srv_cert is given"},
@@ -472,18 +504,18 @@ const OPTIONS cmp_options[] = {
      "Extra certificates to be included in mock certification responses"},
     {"rsp_capubs", OPT_RSP_CAPUBS, 's',
      "CA certificates to be included in mock ip response"},
-    {"poll_count", OPT_POLL_COUNT, 'n',
+    {"poll_count", OPT_POLL_COUNT, 'N',
      "Number of times the client must poll before receiving a certificate"},
-    {"check_after", OPT_CHECK_AFTER, 'n',
+    {"check_after", OPT_CHECK_AFTER, 'N',
      "The check_after value (time to wait) to include in poll response"},
     {"grant_implicitconf", OPT_GRANT_IMPLICITCONF, '-',
      "Grant implicit confirmation of newly enrolled certificate"},
 
-    {"pkistatus", OPT_PKISTATUS, 'n',
+    {"pkistatus", OPT_PKISTATUS, 'N',
      "PKIStatus to be included in server response. Possible values: 0..6"},
-    {"failure", OPT_FAILURE, 'n',
+    {"failure", OPT_FAILURE, 'N',
      "A single failure info bit number to include in server response, 0..26"},
-    {"failurebits", OPT_FAILUREBITS, 'n',
+    {"failurebits", OPT_FAILUREBITS, 'N',
      "Number representing failure bits to include in server response, 0..2^27 - 1"},
     {"statusstring", OPT_STATUSSTRING, 's',
      "Status string to be included in server response"},
@@ -529,8 +561,10 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
 
     {&opt_oldcert}, {(char **)&opt_revreason},
 
-    {&opt_server}, {&opt_path}, {&opt_proxy}, {&opt_no_proxy},
-    {&opt_recipient},
+#ifndef OPENSSL_NO_SOCK
+    {&opt_server}, {&opt_proxy}, {&opt_no_proxy},
+#endif
+    {&opt_recipient}, {&opt_path}, {(char **)&opt_keep_alive},
     {(char **)&opt_msg_timeout}, {(char **)&opt_total_timeout},
 
     {&opt_trusted}, {&opt_untrusted}, {&opt_srvcert},
@@ -549,15 +583,20 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {&opt_engine},
 #endif
 
+#ifndef OPENSSL_NO_SOCK
     {(char **)&opt_tls_used}, {&opt_tls_cert}, {&opt_tls_key},
     {&opt_tls_keypass},
     {&opt_tls_extra}, {&opt_tls_trusted}, {&opt_tls_host},
+#endif
 
     {(char **)&opt_batch}, {(char **)&opt_repeat},
     {&opt_reqin}, {(char **)&opt_reqin_new_tid},
     {&opt_reqout}, {&opt_rspin}, {&opt_rspout},
 
-    {(char **)&opt_use_mock_srv}, {&opt_port}, {(char **)&opt_max_msgs},
+    {(char **)&opt_use_mock_srv},
+#ifndef OPENSSL_NO_SOCK
+    {&opt_port}, {(char **)&opt_max_msgs},
+#endif
     {&opt_srv_ref}, {&opt_srv_secret},
     {&opt_srv_cert}, {&opt_srv_key}, {&opt_srv_keypass},
     {&opt_srv_trusted}, {&opt_srv_untrusted},
@@ -610,6 +649,12 @@ static int print_to_bio_out(const char *func, const char *file, int line,
     return OSSL_CMP_print_to_bio(bio_out, func, file, line, level, msg);
 }
 
+static int print_to_bio_err(const char *func, const char *file, int line,
+                            OSSL_CMP_severity level, const char *msg)
+{
+    return OSSL_CMP_print_to_bio(bio_err, func, file, line, level, msg);
+}
+
 static int set_verbosity(int level)
 {
     if (level < OSSL_CMP_LOG_EMERG || level > OSSL_CMP_LOG_MAX) {
@@ -635,15 +680,11 @@ static X509 *load_cert_pwd(const char *uri, const char *pass, const char *desc)
     X509 *cert;
     char *pass_string = get_passwd(pass, desc);
 
-    cert = load_cert_pass(uri, 0, pass_string, desc);
+    cert = load_cert_pass(uri, FORMAT_UNDEF, 0, pass_string, desc);
     clear_free(pass_string);
     return cert;
 }
 
-/*
- * TODO potentially move this and related functions to apps/lib/
- * or even better extend OSSL_STORE with type OSSL_STORE_INFO_CRL
- */
 static X509_REQ *load_csr_autofmt(const char *infile, const char *desc)
 {
     X509_REQ *csr;
@@ -730,7 +771,7 @@ static OSSL_CMP_MSG *read_PKIMESSAGE(char **filenames)
     file = *filenames;
     *filenames = next_item(file);
 
-    ret = OSSL_CMP_MSG_read(file);
+    ret = OSSL_CMP_MSG_read(file, app_get0_libctx(), app_get0_propq());
     if (ret == NULL)
         CMP_err1("cannot read PKIMessage from file '%s'", file);
     return ret;
@@ -748,9 +789,9 @@ static OSSL_CMP_MSG *read_write_req_resp(OSSL_CMP_CTX *ctx,
     OSSL_CMP_MSG *req_new = NULL;
     OSSL_CMP_MSG *res = NULL;
     OSSL_CMP_PKIHEADER *hdr;
+    const char *prev_opt_rspin = opt_rspin;
 
-    if (req != NULL && opt_reqout != NULL
-            && !write_PKIMESSAGE(req, &opt_reqout))
+    if (opt_reqout != NULL && !write_PKIMESSAGE(req, &opt_reqout))
         goto err;
     if (opt_reqin != NULL && opt_rspin == NULL) {
         if ((req_new = read_PKIMESSAGE(&opt_reqin)) == NULL)
@@ -777,7 +818,7 @@ static OSSL_CMP_MSG *read_write_req_resp(OSSL_CMP_CTX *ctx,
     if (res == NULL)
         goto err;
 
-    if (opt_reqin != NULL || opt_rspin != NULL) {
+    if (opt_reqin != NULL || prev_opt_rspin != NULL) {
         /* need to satisfy nonce and transactionID checks */
         ASN1_OCTET_STRING *nonce;
         ASN1_OCTET_STRING *tid;
@@ -837,11 +878,12 @@ static int set_gennames(OSSL_CMP_CTX *ctx, char *names, const char *desc)
             continue;
         }
 
-        /* try IP address first, then URI or domain name */
+        /* try IP address first, then email/URI/domain name */
         (void)ERR_set_mark();
         n = a2i_GENERAL_NAME(NULL, NULL, NULL, GEN_IPADD, names, 0);
         if (n == NULL)
             n = a2i_GENERAL_NAME(NULL, NULL, NULL,
+                                 strchr(names, '@') != NULL ? GEN_EMAIL :
                                  strchr(names, ':') != NULL ? GEN_URI : GEN_DNS,
                                  names, 0);
         (void)ERR_pop_to_mark();
@@ -872,7 +914,7 @@ static X509_STORE *load_trusted(char *input, int for_new_cert, const char *desc)
     if (X509_STORE_set1_param(ts, vpm /* may be NULL */)
             && (for_new_cert || truststore_set_host_etc(ts, NULL)))
         return ts;
-    BIO_printf(bio_err, "error setting verification parameters\n");
+    BIO_printf(bio_err, "error setting verification parameters for %s\n", desc);
     OSSL_CMP_CTX_print_errors(cmp_ctx);
     X509_STORE_free(ts);
     return NULL;
@@ -891,7 +933,7 @@ static int setup_certs(char *files, const char *desc, void *ctx,
     if ((certs = load_certs_multifile(files, opt_otherpass, desc, vpm)) == NULL)
         return 0;
     ok = (*set1_fn)(ctx, certs);
-    sk_X509_pop_free(certs, X509_free);
+    OSSL_STACK_OF_X509_free(certs);
     return ok;
 }
 
@@ -960,7 +1002,7 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
     if (opt_srv_ref == NULL) {
         if (opt_srv_cert == NULL) {
             /* opt_srv_cert should determine the sender */
-            CMP_err("must give -srv_ref for server if no -srv_cert given");
+            CMP_err("must give -srv_ref for mock server if no -srv_cert given");
             goto err;
         }
     } else {
@@ -971,7 +1013,7 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
 
     if (opt_srv_secret != NULL) {
         int res;
-        char *pass_str = get_passwd(opt_srv_secret, "PBMAC secret of server");
+        char *pass_str = get_passwd(opt_srv_secret, "PBMAC secret of mock server");
 
         if (pass_str != NULL) {
             cleanse(opt_srv_secret);
@@ -982,10 +1024,10 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
                 goto err;
         }
     } else if (opt_srv_cert == NULL) {
-        CMP_err("server credentials must be given if -use_mock_srv or -port is used");
+        CMP_err("mock server credentials must be given if -use_mock_srv or -port is used");
         goto err;
     } else {
-        CMP_warn("server will not be able to handle PBM-protected requests since -srv_secret is not given");
+        CMP_warn("mock server will not be able to handle PBM-protected requests since -srv_secret is not given");
     }
 
     if (opt_srv_secret == NULL
@@ -995,7 +1037,7 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
     }
     if (opt_srv_cert != NULL) {
         X509 *srv_cert = load_cert_pwd(opt_srv_cert, opt_srv_keypass,
-                                       "certificate of the server");
+                                       "certificate of the mock server");
 
         if (srv_cert == NULL || !OSSL_CMP_CTX_set1_cert(ctx, srv_cert)) {
             X509_free(srv_cert);
@@ -1006,7 +1048,7 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
     if (opt_srv_key != NULL) {
         EVP_PKEY *pkey = load_key_pwd(opt_srv_key, opt_keyform,
                                       opt_srv_keypass,
-                                      engine, "private key for server cert");
+                                      engine, "private key for mock server cert");
 
         if (pkey == NULL || !OSSL_CMP_CTX_set1_pkey(ctx, pkey)) {
             EVP_PKEY_free(pkey);
@@ -1018,14 +1060,14 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
 
     if (opt_srv_trusted != NULL) {
         X509_STORE *ts =
-            load_trusted(opt_srv_trusted, 0, "certs trusted by server");
+            load_trusted(opt_srv_trusted, 0, "certs trusted by mock server");
 
         if (ts == NULL || !OSSL_CMP_CTX_set0_trustedStore(ctx, ts)) {
             X509_STORE_free(ts);
             goto err;
         }
     } else {
-        CMP_warn("server will not be able to handle signature-protected requests since -srv_trusted is not given");
+        CMP_warn("mock server will not be able to handle signature-protected requests since -srv_trusted is not given");
     }
     if (!setup_certs(opt_srv_untrusted,
                      "untrusted certificates for mock server", ctx,
@@ -1033,8 +1075,7 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
         goto err;
 
     if (opt_rsp_cert == NULL) {
-        CMP_err("must give -rsp_cert for mock server");
-        goto err;
+        CMP_warn("no -rsp_cert given for mock server");
     } else {
         X509 *cert = load_cert_pwd(opt_rsp_cert, opt_keypass,
                                    "cert to be returned by the mock server");
@@ -1048,7 +1089,6 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
         }
         X509_free(cert);
     }
-    /* TODO find a cleaner solution not requiring type casts */
     if (!setup_certs(opt_rsp_extracerts,
                      "CMP extra certificates for mock server", srv_ctx,
                      (add_X509_stack_fn_t)ossl_cmp_mock_srv_set1_chainOut))
@@ -1195,13 +1235,10 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, const char *host,
         return NULL;
 
     if (opt_tls_trusted != NULL) {
-        trust_store = load_certstore(opt_tls_trusted, opt_otherpass,
-                                     "trusted TLS certificates", vpm);
+        trust_store = load_trusted(opt_tls_trusted, 0, "trusted TLS certs");
         if (trust_store == NULL)
             goto err;
         SSL_CTX_set_cert_store(ssl_ctx, trust_store);
-        /* for improved diagnostics on SSL_CTX_build_cert_chain() errors: */
-        X509_STORE_set_verify_cb(trust_store, X509_STORE_CTX_print_verify_cb);
     }
 
     if (opt_tls_cert != NULL && opt_tls_key != NULL) {
@@ -1225,7 +1262,7 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, const char *host,
         if (!ok || !SSL_CTX_set0_chain(ssl_ctx, certs)) {
             CMP_err1("unable to use client TLS certificate file '%s'",
                      opt_tls_cert);
-            sk_X509_pop_free(certs, X509_free);
+            OSSL_STACK_OF_X509_free(certs);
             goto err;
         }
         for (i = 0; i < sk_X509_num(untrusted); i++) {
@@ -1315,7 +1352,6 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, const char *host,
         /* enable and parameterize server hostname/IP address check */
         if (!truststore_set_host_etc(trust_store,
                                      opt_tls_host != NULL ? opt_tls_host : host))
-            /* TODO: is the server host name correct for TLS via proxy? */
             goto err;
         SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
     }
@@ -1324,7 +1360,7 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, const char *host,
     SSL_CTX_free(ssl_ctx);
     return NULL;
 }
-#endif
+#endif /* OPENSSL_NO_SOCK */
 
 /*
  * set up protection aspects of OSSL_CMP_CTX based on options from config
@@ -1405,7 +1441,7 @@ static int setup_protection_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             ok = ok && OSSL_CMP_CTX_build_cert_chain(ctx, own_trusted, certs);
         }
         X509_STORE_free(own_trusted);
-        sk_X509_pop_free(certs, X509_free);
+        OSSL_STACK_OF_X509_free(certs);
         if (!ok)
             return 0;
     } else if (opt_own_trusted != NULL) {
@@ -1709,11 +1745,10 @@ static int handle_opt_geninfo(OSSL_CMP_CTX *ctx)
     valptr[0] = '\0';
     valptr++;
 
-    if (strncasecmp(valptr, "int:", 4) != 0) {
+    if (!CHECK_AND_SKIP_CASE_PREFIX(valptr, "int:")) {
         CMP_err("missing 'int:' in -geninfo option");
         return 0;
     }
-    valptr += 4;
 
     value = strtol(valptr, &endstr, 10);
     if (endstr == valptr || *endstr != '\0') {
@@ -1764,16 +1799,37 @@ static int handle_opt_geninfo(OSSL_CMP_CTX *ctx)
 static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
 {
     int ret = 0;
-    char *host = NULL, *port = NULL, *path = NULL, *used_path;
+    char *host = NULL, *port = NULL, *path = NULL, *used_path = opt_path;
+#ifndef OPENSSL_NO_SOCK
     int portnum, ssl;
-    char server_buf[200] = { '\0' };
-    char proxy_buf[200] = { '\0' };
-    char *proxy_host = NULL;
-    char *proxy_port_str = NULL;
+    static char server_port[32] = { '\0' };
+    const char *proxy_host = NULL;
+#endif
+    char server_buf[200] = "mock server";
+    char proxy_buf[200] = "";
 
-    if (opt_server == NULL) {
-        CMP_err("missing -server option");
+    if (!opt_use_mock_srv && opt_rspin == NULL) { /* note: -port is not given */
+#ifndef OPENSSL_NO_SOCK
+        if (opt_server == NULL) {
+            CMP_err("missing -server or -use_mock_srv or -rspin option");
+            goto err;
+        }
+#else
+        CMP_err("missing -use_mock_srv or -rspin option; -server option is not supported due to no-sock build");
         goto err;
+#endif
+    }
+#ifndef OPENSSL_NO_SOCK
+    if (opt_server == NULL) {
+        if (opt_proxy != NULL)
+            CMP_warn("ignoring -proxy option since -server is not given");
+        if (opt_no_proxy != NULL)
+            CMP_warn("ignoring -no_proxy option since -server is not given");
+        if (opt_tls_used) {
+            CMP_warn("ignoring -tls_used option since -server is not given");
+            opt_tls_used = 0;
+        }
+        goto set_path;
     }
     if (!OSSL_HTTP_parse_url(opt_server, &ssl, NULL /* user */, &host, &port,
                              &portnum, &path, NULL /* q */, NULL /* frag */)) {
@@ -1784,11 +1840,12 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         CMP_err("missing -tls_used option since -server URL indicates https");
         goto err;
     }
+
     BIO_snprintf(server_port, sizeof(server_port), "%s", port);
-    used_path = opt_path != NULL ? opt_path : path;
+    if (opt_path == NULL)
+        used_path = path;
     if (!OSSL_CMP_CTX_set1_server(ctx, host)
-            || !OSSL_CMP_CTX_set_serverPort(ctx, portnum)
-            || !OSSL_CMP_CTX_set1_serverPath(ctx, used_path))
+            || !OSSL_CMP_CTX_set_serverPort(ctx, portnum))
         goto oom;
     if (opt_proxy != NULL && !OSSL_CMP_CTX_set1_proxy(ctx, opt_proxy))
         goto oom;
@@ -1798,9 +1855,15 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
                        opt_tls_used ? "s" : "", host, port,
                        *used_path == '/' ? used_path + 1 : used_path);
 
-    if (opt_proxy != NULL)
-        (void)BIO_snprintf(proxy_buf, sizeof(proxy_buf), " via %s", opt_proxy);
+    proxy_host = OSSL_HTTP_adapt_proxy(opt_proxy, opt_no_proxy, host, ssl);
+    if (proxy_host != NULL)
+        (void)BIO_snprintf(proxy_buf, sizeof(proxy_buf), " via %s", proxy_host);
+
+ set_path:
+#endif
 
+    if (!OSSL_CMP_CTX_set1_serverPath(ctx, used_path))
+        goto oom;
     if (!transform_opts())
         goto err;
 
@@ -1817,6 +1880,15 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
     if (!setup_verification_ctx(ctx))
         goto err;
 
+    if (opt_keep_alive != 1)
+        (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_KEEP_ALIVE,
+                                      opt_keep_alive);
+    if (opt_total_timeout > 0 && opt_msg_timeout > 0
+            && opt_total_timeout < opt_msg_timeout) {
+        CMP_err2("-total_timeout argument = %d must not be < %d (-msg_timeout)",
+                 opt_total_timeout, opt_msg_timeout);
+        goto err;
+    }
     if (opt_msg_timeout >= 0) /* must do this before setup_ssl_ctx() */
         (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_MSG_TIMEOUT,
                                       opt_msg_timeout);
@@ -1832,16 +1904,8 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             || opt_rspin != NULL || opt_rspout != NULL || opt_use_mock_srv)
         (void)OSSL_CMP_CTX_set_transfer_cb(ctx, read_write_req_resp);
 
-    if ((opt_tls_cert != NULL || opt_tls_key != NULL
-         || opt_tls_keypass != NULL || opt_tls_extra != NULL
-         || opt_tls_trusted != NULL || opt_tls_host != NULL)
-            && !opt_tls_used)
-        CMP_warn("TLS options(s) given but not -tls_used");
+#ifndef OPENSSL_NO_SOCK
     if (opt_tls_used) {
-#ifdef OPENSSL_NO_SOCK
-        BIO_printf(bio_err, "Cannot use TLS - sockets not supported\n");
-        goto err;
-#else
         APP_HTTP_TLS_INFO *info;
 
         if (opt_tls_cert != NULL
@@ -1854,10 +1918,6 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
                 goto err;
             }
         }
-        if (opt_use_mock_srv) {
-            CMP_err("cannot use TLS options together with -use_mock_srv");
-            goto err;
-        }
         if ((info = OPENSSL_zalloc(sizeof(*info))) == NULL)
             goto err;
         (void)OSSL_CMP_CTX_set_http_cb_arg(ctx, info);
@@ -1870,8 +1930,8 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         if (info->ssl_ctx == NULL)
             goto err;
         (void)OSSL_CMP_CTX_set_http_cb(ctx, app_http_tls_cb);
-#endif
     }
+#endif
 
     if (!setup_protection_ctx(ctx, engine))
         goto err;
@@ -1888,7 +1948,10 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         goto err;
 
     /* not printing earlier, to minimize confusion in case setup fails before */
-    CMP_info2("will contact %s%s", server_buf, proxy_buf);
+    if (opt_rspin != NULL)
+        CMP_info("will not contact any server since -rspin is given");
+    else
+        CMP_info2("will contact %s%s", server_buf, proxy_buf);
 
     ret = 1;
 
@@ -1896,8 +1959,6 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
     OPENSSL_free(host);
     OPENSSL_free(port);
     OPENSSL_free(path);
-    OPENSSL_free(proxy_host);
-    OPENSSL_free(proxy_port_str);
     return ret;
  oom:
     CMP_err("out of memory");
@@ -1959,7 +2020,7 @@ static int save_free_certs(OSSL_CMP_CTX *ctx,
 
  end:
     BIO_free(bio);
-    sk_X509_pop_free(certs, X509_free);
+    OSSL_STACK_OF_X509_free(certs);
     return n;
 }
 
@@ -1988,7 +2049,7 @@ static void print_itavs(STACK_OF(OSSL_CMP_ITAV) *itavs)
 }
 
 static char opt_item[SECTION_NAME_MAX + 1];
-/* get previous name from a comma-separated list of names */
+/* get previous name from a comma or space-separated list of names */
 static const char *prev_item(const char *opt, const char *end)
 {
     const char *beg;
@@ -1997,19 +2058,28 @@ static const char *prev_item(const char *opt, const char *end)
     if (end == opt)
         return NULL;
     beg = end;
-    while (beg != opt && beg[-1] != ',' && !isspace(beg[-1]))
-        beg--;
+    while (beg > opt) {
+        --beg;
+        if (beg[0] == ',' || isspace(beg[0])) {
+            ++beg;
+            break;
+        }
+    }
     len = end - beg;
-    if (len > SECTION_NAME_MAX)
+    if (len > SECTION_NAME_MAX) {
+        CMP_warn3("using only first %d characters of section name starting with \"%.*s\"",
+                  SECTION_NAME_MAX, SECTION_NAME_MAX, beg);
         len = SECTION_NAME_MAX;
-    strncpy(opt_item, beg, len);
-    opt_item[SECTION_NAME_MAX] = '\0'; /* avoid gcc v8 O3 stringop-truncation */
+    }
+    memcpy(opt_item, beg, len);
     opt_item[len] = '\0';
-    if (len > SECTION_NAME_MAX)
-        CMP_warn2("using only first %d characters of section name starting with \"%s\"",
-                  SECTION_NAME_MAX, opt_item);
-    while (beg != opt && (beg[-1] == ',' || isspace(beg[-1])))
-        beg--;
+    while (beg > opt) {
+        --beg;
+        if (beg[0] != ',' && !isspace(beg[0])) {
+            ++beg;
+            break;
+        }
+    }
     return beg;
 }
 
@@ -2057,16 +2127,16 @@ static int read_config(void)
     long num = 0;
     char *txt = NULL;
     const OPTIONS *opt;
-    int start = OPT_VERBOSITY;
+    int start_opt = OPT_VERBOSITY - OPT_HELP;
+    int start_idx = OPT_VERBOSITY - 2;
     /*
      * starting with offset OPT_VERBOSITY because OPT_CONFIG and OPT_SECTION
      * would not make sense within the config file.
-     * Moreover, these two options and OPT_VERBOSITY have already been handled.
      */
     int n_options = OSSL_NELEM(cmp_options) - 1;
 
-    for (i = start - OPT_HELP, opt = &cmp_options[start];
-         opt->name; i++, opt++)
+    for (opt = &cmp_options[start_opt], i = start_idx;
+         opt->name != NULL; i++, opt++)
         if (!strcmp(opt->name, OPT_SECTION_STR)
                 || !strcmp(opt->name, OPT_MORE_STR))
             n_options--;
@@ -2074,8 +2144,8 @@ static int read_config(void)
                  + OPT_PROV__FIRST + 1 - OPT_PROV__LAST
                  + OPT_R__FIRST + 1 - OPT_R__LAST
                  + OPT_V__FIRST + 1 - OPT_V__LAST);
-    for (i = start - OPT_HELP, opt = &cmp_options[start];
-         opt->name; i++, opt++) {
+    for (opt = &cmp_options[start_opt], i = start_idx;
+         opt->name != NULL; i++, opt++) {
         int provider_option = (OPT_PROV__FIRST <= opt->retval
                                && opt->retval < OPT_PROV__LAST);
         int rand_state_option = (OPT_R__FIRST <= opt->retval
@@ -2092,12 +2162,24 @@ static int read_config(void)
             i--;
         switch (opt->valtype) {
         case '-':
+        case 'p':
         case 'n':
+        case 'N':
         case 'l':
             if (!conf_get_number_e(conf, opt_section, opt->name, &num)) {
                 ERR_clear_error();
                 continue; /* option not provided */
             }
+            if (opt->valtype == 'p' && num <= 0) {
+                opt_printf_stderr("Non-positive number \"%ld\" for config option -%s\n",
+                                  num, opt->name);
+                return -1;
+            }
+            if (opt->valtype == 'N' && num < 0) {
+                opt_printf_stderr("Negative number \"%ld\" for config option -%s\n",
+                                  num, opt->name);
+                return -1;
+            }
             break;
         case 's':
         case '>':
@@ -2144,7 +2226,9 @@ static int read_config(void)
         } else {
             switch (opt->valtype) {
             case '-':
+            case 'p':
             case 'n':
+            case 'N':
                 if (num < INT_MIN || INT_MAX < num) {
                     BIO_printf(bio_err,
                                "integer value out of range for option '%s'\n",
@@ -2168,30 +2252,20 @@ static int read_config(void)
     return 1;
 }
 
-static char *opt_str(char *opt)
+static char *opt_str(void)
 {
     char *arg = opt_arg();
 
     if (arg[0] == '\0') {
-        CMP_warn1("argument of -%s option is empty string, resetting option",
-                  opt);
+        CMP_warn1("%s option argument is empty string, resetting option",
+                  opt_flag());
         arg = NULL;
     } else if (arg[0] == '-') {
-        CMP_warn1("argument of -%s option starts with hyphen", opt);
+        CMP_warn1("%s option argument starts with hyphen", opt_flag());
     }
     return arg;
 }
 
-static int opt_nat(void)
-{
-    int result = -1;
-
-    if (opt_int(opt_arg(), &result) && result < 0)
-        BIO_printf(bio_err, "error: argument '%s' must not be negative\n",
-                   opt_arg());
-    return result;
-}
-
 /* returns 1 on success, 0 on error, -1 on -help (i.e., stop with success) */
 static int get_opts(int argc, char **argv)
 {
@@ -2211,94 +2285,107 @@ static int get_opts(int argc, char **argv)
             return -1;
         case OPT_CONFIG: /* has already been handled */
         case OPT_SECTION: /* has already been handled */
-        case OPT_VERBOSITY: /* has already been handled */
             break;
+        case OPT_VERBOSITY:
+            if (!set_verbosity(opt_int_arg()))
+                goto opthelp;
+            break;
+#ifndef OPENSSL_NO_SOCK
         case OPT_SERVER:
-            opt_server = opt_str("server");
+            opt_server = opt_str();
             break;
         case OPT_PROXY:
-            opt_proxy = opt_str("proxy");
+            opt_proxy = opt_str();
             break;
         case OPT_NO_PROXY:
-            opt_no_proxy = opt_str("no_proxy");
+            opt_no_proxy = opt_str();
+            break;
+#endif
+        case OPT_RECIPIENT:
+            opt_recipient = opt_str();
             break;
         case OPT_PATH:
-            opt_path = opt_str("path");
+            opt_path = opt_str();
             break;
-        case OPT_RECIPIENT:
-            opt_recipient = opt_str("recipient");
+        case OPT_KEEP_ALIVE:
+            opt_keep_alive = opt_int_arg();
+            if (opt_keep_alive > 2) {
+                CMP_err("-keep_alive argument must be 0, 1, or 2");
+                goto opthelp;
+            }
             break;
         case OPT_MSG_TIMEOUT:
-            if ((opt_msg_timeout = opt_nat()) < 0)
-                goto opthelp;
+            opt_msg_timeout = opt_int_arg();
             break;
         case OPT_TOTAL_TIMEOUT:
-            if ((opt_total_timeout = opt_nat()) < 0)
-                goto opthelp;
+            opt_total_timeout = opt_int_arg();
             break;
+#ifndef OPENSSL_NO_SOCK
         case OPT_TLS_USED:
             opt_tls_used = 1;
             break;
         case OPT_TLS_CERT:
-            opt_tls_cert = opt_str("tls_cert");
+            opt_tls_cert = opt_str();
             break;
         case OPT_TLS_KEY:
-            opt_tls_key = opt_str("tls_key");
+            opt_tls_key = opt_str();
             break;
         case OPT_TLS_KEYPASS:
-            opt_tls_keypass = opt_str("tls_keypass");
+            opt_tls_keypass = opt_str();
             break;
         case OPT_TLS_EXTRA:
-            opt_tls_extra = opt_str("tls_extra");
+            opt_tls_extra = opt_str();
             break;
         case OPT_TLS_TRUSTED:
-            opt_tls_trusted = opt_str("tls_trusted");
+            opt_tls_trusted = opt_str();
             break;
         case OPT_TLS_HOST:
-            opt_tls_host = opt_str("tls_host");
+            opt_tls_host = opt_str();
             break;
+#endif
+
         case OPT_REF:
-            opt_ref = opt_str("ref");
+            opt_ref = opt_str();
             break;
         case OPT_SECRET:
-            opt_secret = opt_str("secret");
+            opt_secret = opt_str();
             break;
         case OPT_CERT:
-            opt_cert = opt_str("cert");
+            opt_cert = opt_str();
             break;
         case OPT_OWN_TRUSTED:
-            opt_own_trusted = opt_str("own_trusted");
+            opt_own_trusted = opt_str();
             break;
         case OPT_KEY:
-            opt_key = opt_str("key");
+            opt_key = opt_str();
             break;
         case OPT_KEYPASS:
-            opt_keypass = opt_str("keypass");
+            opt_keypass = opt_str();
             break;
         case OPT_DIGEST:
-            opt_digest = opt_str("digest");
+            opt_digest = opt_str();
             break;
         case OPT_MAC:
-            opt_mac = opt_str("mac");
+            opt_mac = opt_str();
             break;
         case OPT_EXTRACERTS:
-            opt_extracerts = opt_str("extracerts");
+            opt_extracerts = opt_str();
             break;
         case OPT_UNPROTECTED_REQUESTS:
             opt_unprotected_requests = 1;
             break;
 
         case OPT_TRUSTED:
-            opt_trusted = opt_str("trusted");
+            opt_trusted = opt_str();
             break;
         case OPT_UNTRUSTED:
-            opt_untrusted = opt_str("untrusted");
+            opt_untrusted = opt_str();
             break;
         case OPT_SRVCERT:
-            opt_srvcert = opt_str("srvcert");
+            opt_srvcert = opt_str();
             break;
         case OPT_EXPECT_SENDER:
-            opt_expect_sender = opt_str("expect_sender");
+            opt_expect_sender = opt_str();
             break;
         case OPT_IGNORE_KEYUSAGE:
             opt_ignore_keyusage = 1;
@@ -2307,10 +2394,10 @@ static int get_opts(int argc, char **argv)
             opt_unprotected_errors = 1;
             break;
         case OPT_EXTRACERTSOUT:
-            opt_extracertsout = opt_str("extracertsout");
+            opt_extracertsout = opt_str();
             break;
         case OPT_CACERTSOUT:
-            opt_cacertsout = opt_str("cacertsout");
+            opt_cacertsout = opt_str();
             break;
 
         case OPT_V_CASES:
@@ -2318,52 +2405,51 @@ static int get_opts(int argc, char **argv)
                 goto opthelp;
             break;
         case OPT_CMD:
-            opt_cmd_s = opt_str("cmd");
+            opt_cmd_s = opt_str();
             break;
         case OPT_INFOTYPE:
-            opt_infotype_s = opt_str("infotype");
+            opt_infotype_s = opt_str();
             break;
         case OPT_GENINFO:
-            opt_geninfo = opt_str("geninfo");
+            opt_geninfo = opt_str();
             break;
 
         case OPT_NEWKEY:
-            opt_newkey = opt_str("newkey");
+            opt_newkey = opt_str();
             break;
         case OPT_NEWKEYPASS:
-            opt_newkeypass = opt_str("newkeypass");
+            opt_newkeypass = opt_str();
             break;
         case OPT_SUBJECT:
-            opt_subject = opt_str("subject");
+            opt_subject = opt_str();
             break;
         case OPT_ISSUER:
-            opt_issuer = opt_str("issuer");
+            opt_issuer = opt_str();
             break;
         case OPT_DAYS:
-            if ((opt_days = opt_nat()) < 0)
-                goto opthelp;
+            opt_days = opt_int_arg();
             break;
         case OPT_REQEXTS:
-            opt_reqexts = opt_str("reqexts");
+            opt_reqexts = opt_str();
             break;
         case OPT_SANS:
-            opt_sans = opt_str("sans");
+            opt_sans = opt_str();
             break;
         case OPT_SAN_NODEFAULT:
             opt_san_nodefault = 1;
             break;
         case OPT_POLICIES:
-            opt_policies = opt_str("policies");
+            opt_policies = opt_str();
             break;
         case OPT_POLICY_OIDS:
-            opt_policy_oids = opt_str("policy_oids");
+            opt_policy_oids = opt_str();
             break;
         case OPT_POLICY_OIDS_CRITICAL:
             opt_policy_oids_critical = 1;
             break;
         case OPT_POPO:
-            if (!opt_int(opt_arg(), &opt_popo)
-                    || opt_popo < OSSL_CRMF_POPO_NONE
+            opt_popo = opt_int_arg();
+            if (opt_popo < OSSL_CRMF_POPO_NONE
                     || opt_popo > OSSL_CRMF_POPO_KEYENC) {
                 CMP_err("invalid popo spec. Valid values are -1 .. 2");
                 goto opthelp;
@@ -2373,7 +2459,7 @@ static int get_opts(int argc, char **argv)
             opt_csr = opt_arg();
             break;
         case OPT_OUT_TRUSTED:
-            opt_out_trusted = opt_str("out_trusted");
+            opt_out_trusted = opt_str();
             break;
         case OPT_IMPLICIT_CONFIRM:
             opt_implicit_confirm = 1;
@@ -2382,17 +2468,17 @@ static int get_opts(int argc, char **argv)
             opt_disable_confirm = 1;
             break;
         case OPT_CERTOUT:
-            opt_certout = opt_str("certout");
+            opt_certout = opt_str();
             break;
         case OPT_CHAINOUT:
-            opt_chainout = opt_str("chainout");
+            opt_chainout = opt_str();
             break;
         case OPT_OLDCERT:
-            opt_oldcert = opt_str("oldcert");
+            opt_oldcert = opt_str();
             break;
         case OPT_REVREASON:
-            if (!opt_int(opt_arg(), &opt_revreason)
-                    || opt_revreason < CRL_REASON_NONE
+            opt_revreason = opt_int_arg();
+                if (opt_revreason < CRL_REASON_NONE
                     || opt_revreason > CRL_REASON_AA_COMPROMISE
                     || opt_revreason == 7) {
                 CMP_err("invalid revreason. Valid values are -1 .. 6, 8 .. 10");
@@ -2400,17 +2486,17 @@ static int get_opts(int argc, char **argv)
             }
             break;
         case OPT_CERTFORM:
-            opt_certform_s = opt_str("certform");
+            opt_certform_s = opt_str();
             break;
         case OPT_KEYFORM:
-            opt_keyform_s = opt_str("keyform");
+            opt_keyform_s = opt_str();
             break;
         case OPT_OTHERPASS:
-            opt_otherpass = opt_str("otherpass");
+            opt_otherpass = opt_str();
             break;
 #ifndef OPENSSL_NO_ENGINE
         case OPT_ENGINE:
-            opt_engine = opt_str("engine");
+            opt_engine = opt_str();
             break;
 #endif
         case OPT_PROV_CASES:
@@ -2426,83 +2512,85 @@ static int get_opts(int argc, char **argv)
             opt_batch = 1;
             break;
         case OPT_REPEAT:
-            opt_repeat = opt_nat();
+            opt_repeat = opt_int_arg();
             break;
         case OPT_REQIN:
-            opt_reqin = opt_str("reqin");
+            opt_reqin = opt_str();
             break;
         case OPT_REQIN_NEW_TID:
             opt_reqin_new_tid = 1;
             break;
         case OPT_REQOUT:
-            opt_reqout = opt_str("reqout");
+            opt_reqout = opt_str();
             break;
         case OPT_RSPIN:
-            opt_rspin = opt_str("rspin");
+            opt_rspin = opt_str();
             break;
         case OPT_RSPOUT:
-            opt_rspout = opt_str("rspout");
+            opt_rspout = opt_str();
             break;
         case OPT_USE_MOCK_SRV:
             opt_use_mock_srv = 1;
             break;
+
+#ifndef OPENSSL_NO_SOCK
         case OPT_PORT:
-            opt_port = opt_str("port");
+            opt_port = opt_str();
             break;
         case OPT_MAX_MSGS:
-            if ((opt_max_msgs = opt_nat()) < 0)
-                goto opthelp;
+            opt_max_msgs = opt_int_arg();
             break;
+#endif
         case OPT_SRV_REF:
-            opt_srv_ref = opt_str("srv_ref");
+            opt_srv_ref = opt_str();
             break;
         case OPT_SRV_SECRET:
-            opt_srv_secret = opt_str("srv_secret");
+            opt_srv_secret = opt_str();
             break;
         case OPT_SRV_CERT:
-            opt_srv_cert = opt_str("srv_cert");
+            opt_srv_cert = opt_str();
             break;
         case OPT_SRV_KEY:
-            opt_srv_key = opt_str("srv_key");
+            opt_srv_key = opt_str();
             break;
         case OPT_SRV_KEYPASS:
-            opt_srv_keypass = opt_str("srv_keypass");
+            opt_srv_keypass = opt_str();
             break;
         case OPT_SRV_TRUSTED:
-            opt_srv_trusted = opt_str("srv_trusted");
+            opt_srv_trusted = opt_str();
             break;
         case OPT_SRV_UNTRUSTED:
-            opt_srv_untrusted = opt_str("srv_untrusted");
+            opt_srv_untrusted = opt_str();
             break;
         case OPT_RSP_CERT:
-            opt_rsp_cert = opt_str("rsp_cert");
+            opt_rsp_cert = opt_str();
             break;
         case OPT_RSP_EXTRACERTS:
-            opt_rsp_extracerts = opt_str("rsp_extracerts");
+            opt_rsp_extracerts = opt_str();
             break;
         case OPT_RSP_CAPUBS:
-            opt_rsp_capubs = opt_str("rsp_capubs");
+            opt_rsp_capubs = opt_str();
             break;
         case OPT_POLL_COUNT:
-            opt_poll_count = opt_nat();
+            opt_poll_count = opt_int_arg();
             break;
         case OPT_CHECK_AFTER:
-            opt_check_after = opt_nat();
+            opt_check_after = opt_int_arg();
             break;
         case OPT_GRANT_IMPLICITCONF:
             opt_grant_implicitconf = 1;
             break;
         case OPT_PKISTATUS:
-            opt_pkistatus = opt_nat();
+            opt_pkistatus = opt_int_arg();
             break;
         case OPT_FAILURE:
-            opt_failure = opt_nat();
+            opt_failure = opt_int_arg();
             break;
         case OPT_FAILUREBITS:
-            opt_failurebits = opt_nat();
+            opt_failurebits = opt_int_arg();
             break;
         case OPT_STATUSSTRING:
-            opt_statusstring = opt_str("statusstring");
+            opt_statusstring = opt_str();
             break;
         case OPT_SEND_ERROR:
             opt_send_error = 1;
@@ -2526,20 +2614,96 @@ static int get_opts(int argc, char **argv)
     }
 
     /* No extra args. */
-    argc = opt_num_rest();
-    argv = opt_rest();
-    if (argc != 0)
+    if (!opt_check_rest_arg(NULL))
         goto opthelp;
     return 1;
 }
 
+#ifndef OPENSSL_NO_SOCK
+static int cmp_server(OSSL_CMP_CTX *srv_cmp_ctx) {
+    BIO *acbio;
+    BIO *cbio = NULL;
+    int keep_alive = 0;
+    int msgs = 0;
+    int retry = 1;
+    int ret = 1;
+
+    if ((acbio = http_server_init(prog, opt_port, opt_verbosity)) == NULL)
+        return 0;
+    while (opt_max_msgs <= 0 || msgs < opt_max_msgs) {
+        char *path = NULL;
+        OSSL_CMP_MSG *req = NULL;
+        OSSL_CMP_MSG *resp = NULL;
+
+        ret = http_server_get_asn1_req(ASN1_ITEM_rptr(OSSL_CMP_MSG),
+                                       (ASN1_VALUE **)&req, &path,
+                                       &cbio, acbio, &keep_alive,
+                                       prog, 0, 0);
+        if (ret == 0) { /* no request yet */
+            if (retry) {
+                ossl_sleep(1000);
+                retry = 0;
+                continue;
+            }
+            ret = 0;
+            goto next;
+        }
+        if (ret++ == -1) /* fatal error */
+            break;
+
+        ret = 0;
+        msgs++;
+        if (req != NULL) {
+            if (strcmp(path, "") != 0 && strcmp(path, "pkix/") != 0) {
+                (void)http_server_send_status(cbio, 404, "Not Found");
+                CMP_err1("expecting empty path or 'pkix/' but got '%s'",
+                         path);
+                OPENSSL_free(path);
+                OSSL_CMP_MSG_free(req);
+                goto next;
+            }
+            OPENSSL_free(path);
+            resp = OSSL_CMP_CTX_server_perform(cmp_ctx, req);
+            OSSL_CMP_MSG_free(req);
+            if (resp == NULL) {
+                (void)http_server_send_status(cbio,
+                                              500, "Internal Server Error");
+                break; /* treated as fatal error */
+            }
+            ret = http_server_send_asn1_resp(cbio, keep_alive,
+                                             "application/pkixcmp",
+                                             ASN1_ITEM_rptr(OSSL_CMP_MSG),
+                                             (const ASN1_VALUE *)resp);
+            OSSL_CMP_MSG_free(resp);
+            if (!ret)
+                break; /* treated as fatal error */
+        }
+    next:
+        if (!ret) { /* on transmission error, cancel CMP transaction */
+            (void)OSSL_CMP_CTX_set1_transactionID(srv_cmp_ctx, NULL);
+            (void)OSSL_CMP_CTX_set1_senderNonce(srv_cmp_ctx, NULL);
+        }
+        if (!ret || !keep_alive
+            || OSSL_CMP_CTX_get_status(srv_cmp_ctx) == -1
+             /* transaction closed by OSSL_CMP_CTX_server_perform() */) {
+            BIO_free_all(cbio);
+            cbio = NULL;
+        }
+    }
+
+    BIO_free_all(cbio);
+    BIO_free_all(acbio);
+    return ret;
+}
+#endif
+
 int cmp_main(int argc, char **argv)
 {
     char *configfile = NULL;
     int i;
     X509 *newcert = NULL;
     ENGINE *engine = NULL;
-    char mock_server[] = "mock server:1";
+    OSSL_CMP_CTX *srv_cmp_ctx = NULL;
     int ret = 0; /* default: failure */
 
     prog = opt_appname(argv[0]);
@@ -2599,8 +2763,14 @@ int cmp_main(int argc, char **argv)
                     }
                 }
             }
-            if (!read_config())
+            ret = read_config();
+            if (!set_verbosity(opt_verbosity)) /* just for checking range */
+                ret = -1;
+            if (ret <= 0) {
+                if (ret == -1)
+                    BIO_printf(bio_err, "Use -help for summary.\n");
                 goto err;
+            }
         }
     }
     (void)BIO_flush(bio_err); /* prevent interference with opt_help() */
@@ -2623,113 +2793,79 @@ int cmp_main(int argc, char **argv)
         }
     }
 
+    cmp_ctx = OSSL_CMP_CTX_new(app_get0_libctx(), app_get0_propq());
+    if (cmp_ctx == NULL)
+        goto err;
+    OSSL_CMP_CTX_set_log_verbosity(cmp_ctx, opt_verbosity);
+    if (!OSSL_CMP_CTX_set_log_cb(cmp_ctx, print_to_bio_out)) {
+        CMP_err1("cannot set up error reporting and logging for %s", prog);
+        goto err;
+    }
+
+#ifndef OPENSSL_NO_SOCK
+    if ((opt_tls_cert != NULL || opt_tls_key != NULL
+         || opt_tls_keypass != NULL || opt_tls_extra != NULL
+         || opt_tls_trusted != NULL || opt_tls_host != NULL)
+            && !opt_tls_used)
+        CMP_warn("Ingnoring TLS options(s) since -tls_used is not given");
     if (opt_port != NULL) {
-        if (opt_use_mock_srv) {
-            CMP_err("cannot use both -port and -use_mock_srv options");
+        if (opt_tls_used) {
+            CMP_err("-tls_used option not supported with -port option");
             goto err;
         }
-        if (opt_server != NULL) {
-            CMP_err("cannot use both -port and -server options");
+        if (opt_use_mock_srv || opt_server != NULL || opt_rspin != NULL) {
+            CMP_err("cannot use -port with -use_mock_srv, -server, or -rspin options");
             goto err;
         }
     }
-
-    cmp_ctx = OSSL_CMP_CTX_new(app_get0_libctx(), app_get0_propq());
-    if (cmp_ctx == NULL)
+    if (opt_server != NULL && opt_use_mock_srv) {
+        CMP_err("cannot use both -server and -use_mock_srv options");
         goto err;
-    OSSL_CMP_CTX_set_log_verbosity(cmp_ctx, opt_verbosity);
-    if (!OSSL_CMP_CTX_set_log_cb(cmp_ctx, print_to_bio_out)) {
-        CMP_err1("cannot set up error reporting and logging for %s", prog);
+    }
+#endif
+    if (opt_rspin != NULL && opt_use_mock_srv) {
+        CMP_err("cannot use both -rspin and -use_mock_srv options");
         goto err;
     }
-    if ((opt_use_mock_srv || opt_port != NULL)) {
+
+    if (opt_use_mock_srv
+#ifndef OPENSSL_NO_SOCK
+        || opt_port != NULL
+#endif
+        ) {
         OSSL_CMP_SRV_CTX *srv_ctx;
 
         if ((srv_ctx = setup_srv_ctx(engine)) == NULL)
             goto err;
+        srv_cmp_ctx = OSSL_CMP_SRV_CTX_get0_cmp_ctx(srv_ctx);
         OSSL_CMP_CTX_set_transfer_cb_arg(cmp_ctx, srv_ctx);
-        if (!OSSL_CMP_CTX_set_log_cb(OSSL_CMP_SRV_CTX_get0_cmp_ctx(srv_ctx),
-                                     print_to_bio_out)) {
+        if (!OSSL_CMP_CTX_set_log_cb(srv_cmp_ctx, print_to_bio_err)) {
             CMP_err1("cannot set up error reporting and logging for %s", prog);
             goto err;
         }
+        OSSL_CMP_CTX_set_log_verbosity(srv_cmp_ctx, opt_verbosity);
     }
 
+#ifndef OPENSSL_NO_SOCK
+    if (opt_tls_used && (opt_use_mock_srv || opt_rspin != NULL)) {
+        CMP_warn("ignoring -tls_used option since -use_mock_srv or -rspin is given");
+        opt_tls_used = 0;
+    }
 
     if (opt_port != NULL) { /* act as very basic CMP HTTP server */
-#ifdef OPENSSL_NO_SOCK
-        BIO_printf(bio_err, "Cannot act as server - sockets not supported\n");
-#else
-        BIO *acbio;
-        BIO *cbio = NULL;
-        int msgs = 0;
-
-        if ((acbio = http_server_init_bio(prog, opt_port)) == NULL)
-            goto err;
-        while (opt_max_msgs <= 0 || msgs < opt_max_msgs) {
-            char *path = NULL;
-            OSSL_CMP_MSG *req = NULL;
-            OSSL_CMP_MSG *resp = NULL;
-
-            ret = http_server_get_asn1_req(ASN1_ITEM_rptr(OSSL_CMP_MSG),
-                                           (ASN1_VALUE **)&req, &path,
-                                           &cbio, acbio, prog, 0, 0);
-            if (ret == 0)
-                continue;
-            if (ret++ == -1)
-                break; /* fatal error */
-
-            ret = 0;
-            msgs++;
-            if (req != NULL) {
-                if (strcmp(path, "") != 0 && strcmp(path, "pkix/") != 0) {
-                    (void)http_server_send_status(cbio, 404, "Not Found");
-                    CMP_err1("expecting empty path or 'pkix/' but got '%s'",
-                             path);
-                    OPENSSL_free(path);
-                    OSSL_CMP_MSG_free(req);
-                    goto cont;
-                }
-                OPENSSL_free(path);
-                resp = OSSL_CMP_CTX_server_perform(cmp_ctx, req);
-                OSSL_CMP_MSG_free(req);
-                if (resp == NULL) {
-                    (void)http_server_send_status(cbio,
-                                                  500, "Internal Server Error");
-                    break; /* treated as fatal error */
-                }
-                ret = http_server_send_asn1_resp(cbio, "application/pkixcmp",
-                                                 ASN1_ITEM_rptr(OSSL_CMP_MSG),
-                                                 (const ASN1_VALUE *)resp);
-                OSSL_CMP_MSG_free(resp);
-                if (!ret)
-                    break; /* treated as fatal error */
-            } else {
-                (void)http_server_send_status(cbio, 400, "Bad Request");
-            }
-        cont:
-            BIO_free_all(cbio);
-            cbio = NULL;
-        }
-        BIO_free_all(cbio);
-        BIO_free_all(acbio);
-#endif
+        ret = cmp_server(srv_cmp_ctx);
         goto err;
     }
-    /* else act as CMP client */
 
-    if (opt_use_mock_srv) {
-        if (opt_server != NULL) {
-            CMP_err("cannot use both -use_mock_srv and -server options");
-            goto err;
-        }
-        if (opt_proxy != NULL) {
-            CMP_err("cannot use both -use_mock_srv and -proxy options");
-            goto err;
+    /* act as CMP client, possibly using internal mock server */
+
+    if (opt_server != NULL) {
+        if (opt_rspin != NULL) {
+            CMP_warn("ignoring -server option since -rspin is given");
+            opt_server = NULL;
         }
-        opt_server = mock_server;
-        opt_proxy = "API";
     }
+#endif
 
     if (!setup_client_ctx(cmp_ctx, engine)) {
         CMP_err("cannot set up CMP context");
@@ -2793,7 +2929,14 @@ int cmp_main(int argc, char **argv)
             const char *string =
                 OSSL_CMP_CTX_snprint_PKIStatus(cmp_ctx, buf,
                                                OSSL_CMP_PKISI_BUFLEN);
+            const char *from = "", *server = "";
 
+#ifndef OPENSSL_NO_SOCK
+            if (opt_server != NULL) {
+                from = " from ";
+                server = opt_server;
+            }
+#endif
             CMP_print(bio_err,
                       status == OSSL_CMP_PKISTATUS_accepted
                       ? OSSL_CMP_LOG_INFO :
@@ -2804,8 +2947,8 @@ int cmp_main(int argc, char **argv)
                       status == OSSL_CMP_PKISTATUS_rejection ? "server error" :
                       status == OSSL_CMP_PKISTATUS_waiting ? "internal error"
                                                            : "warning",
-                      "received from %s %s %s", opt_server,
-                      string != NULL ? string : "<unknown PKIStatus>", "");
+                      "received%s%s %s", from, server,
+                      string != NULL ? string : "<unknown PKIStatus>");
             OPENSSL_free(buf);
         }
 
@@ -2842,7 +2985,9 @@ int cmp_main(int argc, char **argv)
     cleanse(opt_keypass);
     cleanse(opt_newkeypass);
     cleanse(opt_otherpass);
+#ifndef OPENSSL_NO_SOCK
     cleanse(opt_tls_keypass);
+#endif
     cleanse(opt_secret);
     cleanse(opt_srv_keypass);
     cleanse(opt_srv_secret);
@@ -2851,7 +2996,9 @@ int cmp_main(int argc, char **argv)
         OSSL_CMP_CTX_print_errors(cmp_ctx);
 
     ossl_cmp_mock_srv_free(OSSL_CMP_CTX_get_transfer_cb_arg(cmp_ctx));
+#ifndef OPENSSL_NO_SOCK
     APP_HTTP_TLS_INFO_free(OSSL_CMP_CTX_get_http_cb_arg(cmp_ctx));
+#endif
     X509_STORE_free(OSSL_CMP_CTX_get_certConf_cb_arg(cmp_ctx));
     OSSL_CMP_CTX_free(cmp_ctx);
     X509_VERIFY_PARAM_free(vpm);