]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
getinfo: provide info which auth was used for HTTP and proxy
authorDaniel Stenberg <daniel@haxx.se>
Tue, 29 Oct 2024 15:53:32 +0000 (16:53 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 23 Dec 2024 22:03:54 +0000 (23:03 +0100)
CURLINFO_HTTPAUTH_USED and CURLINFO_PROXYAUTH_USED

Tested in 590 and 694

Ref: #12668
Idea-by: Ganesh Viswanathan
Closes #15450

15 files changed:
docs/libcurl/curl_easy_getinfo.md
docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md [new file with mode: 0644]
docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md [new file with mode: 0644]
docs/libcurl/opts/Makefile.inc
docs/libcurl/symbols-in-versions
include/curl/curl.h
lib/getinfo.c
lib/http.c
lib/http_ntlm.c
lib/urldata.h
tests/data/Makefile.am
tests/data/test694 [new file with mode: 0644]
tests/libtest/Makefile.inc
tests/libtest/lib590.c
tests/libtest/lib694.c [new file with mode: 0644]

index 31efc316560cbd3899a2daee1f24a2d07fd6313e..184b8d588a80b16c9fbf98c6390c14d605cbc5d4 100644 (file)
@@ -146,6 +146,10 @@ Number of bytes of all headers received. See CURLINFO_HEADER_SIZE(3)
 
 Available HTTP authentication methods. See CURLINFO_HTTPAUTH_AVAIL(3)
 
+## CURLINFO_HTTPAUTH_USED
+
+Used HTTP authentication method. See CURLINFO_HTTPAUTH_USED(3)
+
 ## CURLINFO_HTTP_CONNECTCODE
 
 Last proxy CONNECT response code. See CURLINFO_HTTP_CONNECTCODE(3)
@@ -225,6 +229,10 @@ CURLINFO_PROTOCOL(3)
 
 Available HTTP proxy authentication methods. See CURLINFO_PROXYAUTH_AVAIL(3)
 
+## CURLINFO_PROXYAUTH_USED
+
+Used HTTP proxy authentication methods. See CURLINFO_PROXYAUTH_USED(3)
+
 ## CURLINFO_PROXY_ERROR
 
 Detailed proxy error. See CURLINFO_PROXY_ERROR(3)
diff --git a/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md b/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md
new file mode 100644 (file)
index 0000000..29c5067
--- /dev/null
@@ -0,0 +1,76 @@
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLINFO_HTTPAUTH_USED
+Section: 3
+Source: libcurl
+See-also:
+  - CURLINFO_PROXYAUTH_USED (3)
+  - CURLINFO_HTTPAUTH_AVAIL (3)
+  - CURLOPT_HTTPAUTH (3)
+Protocol:
+  - HTTP
+Added-in: 8.12.0
+---
+
+# NAME
+
+CURLINFO_HTTPAUTH_USED - get used HTTP authentication method
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HTTPAUTH_USED, long *authp);
+~~~
+
+# DESCRIPTION
+
+Pass a pointer to a long to receive a bitmask indicating the authentication
+method that was used in the previous HTTP request. The meaning of the possible
+bits is explained in the CURLOPT_HTTPAUTH(3) option for curl_easy_setopt(3).
+
+The returned value has zero or one bit set.
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+int main(void)
+{
+  CURL *curl = curl_easy_init();
+  if(curl) {
+    CURLcode res;
+    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST);
+    curl_easy_setopt(curl, CURLOPT_USERNAME, "shrek");
+    curl_easy_setopt(curl, CURLOPT_PASSWORD, "swamp");
+
+    res = curl_easy_perform(curl);
+
+    if(!res) {
+      long auth;
+      res = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_USED, &auth);
+      if(!res) {
+        if(!auth)
+          printf("No auth used\n");
+        else {
+          if(auth == CURLAUTH_DIGEST)
+            printf("Used Digest authentication\n");
+          else
+            printf("Used Basic authentication\n");
+        }
+      }
+    }
+    curl_easy_cleanup(curl);
+  }
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
diff --git a/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md b/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md
new file mode 100644 (file)
index 0000000..45d488b
--- /dev/null
@@ -0,0 +1,79 @@
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLINFO_PROXYAUTH_USED
+Section: 3
+Source: libcurl
+See-also:
+  - CURLINFO_HTTPAUTH_USED (3)
+  - CURLINFO_PROXYAUTH_AVAIL (3)
+  - CURLOPT_HTTPAUTH (3)
+Protocol:
+  - HTTP
+Added-in: 8.12.0
+---
+
+# NAME
+
+CURLINFO_PROXYAUTH_USED - get used HTTP proxy authentication method
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PROXYAUTH_USED, long *authp);
+~~~
+
+# DESCRIPTION
+
+Pass a pointer to a long to receive a bitmask indicating the authentication
+method that was used in the previous request done over an HTTP proxy. The
+meaning of the possible bits is explained in the CURLOPT_HTTPAUTH(3) option
+for curl_easy_setopt(3).
+
+The returned value has zero or one bit set.
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+int main(void)
+{
+  CURL *curl = curl_easy_init();
+  if(curl) {
+    CURLcode res;
+    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+    curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example.com");
+    curl_easy_setopt(curl, CURLOPT_PROXYAUTH,
+                     CURLAUTH_BASIC | CURLAUTH_DIGEST);
+    curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, "shrek");
+    curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, "swamp");
+
+    res = curl_easy_perform(curl);
+
+    if(!res) {
+      long auth;
+      res = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_USED, &auth);
+      if(!res) {
+        if(!auth)
+          printf("No auth used\n");
+        else {
+          if(auth == CURLAUTH_DIGEST)
+            printf("Used Digest proxy authentication\n");
+          else
+            printf("Used Basic proxy authentication\n");
+        }
+      }
+    }
+    curl_easy_cleanup(curl);
+  }
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
index 8591f47b55da49e0bbdd8a20d3288e1a5cdc17b1..9d8606dd06842b53ea49b0a692ccd7fee5cdee29 100644 (file)
@@ -50,6 +50,7 @@ man_MANS =                                      \
   CURLINFO_HTTP_CONNECTCODE.3                   \
   CURLINFO_HTTP_VERSION.3                       \
   CURLINFO_HTTPAUTH_AVAIL.3                     \
+  CURLINFO_HTTPAUTH_USED.3                      \
   CURLINFO_LASTSOCKET.3                         \
   CURLINFO_LOCAL_IP.3                           \
   CURLINFO_LOCAL_PORT.3                         \
@@ -67,6 +68,7 @@ man_MANS =                                      \
   CURLINFO_PROXY_ERROR.3                        \
   CURLINFO_PROXY_SSL_VERIFYRESULT.3             \
   CURLINFO_PROXYAUTH_AVAIL.3                    \
+  CURLINFO_PROXYAUTH_USED.3                     \
   CURLINFO_QUEUE_TIME_T.3                       \
   CURLINFO_REDIRECT_COUNT.3                     \
   CURLINFO_REDIRECT_TIME.3                      \
index ddda26d8321300595b13864965137f9e7584cb71..0fd02ff0926ec5986c9241785d0e6d04bb50c4b5 100644 (file)
@@ -449,6 +449,7 @@ CURLINFO_HTTP_CODE              7.4.1         7.10.8
 CURLINFO_HTTP_CONNECTCODE       7.10.7
 CURLINFO_HTTP_VERSION           7.50.0
 CURLINFO_HTTPAUTH_AVAIL         7.10.8
+CURLINFO_HTTPAUTH_USED          8.12.0
 CURLINFO_LASTONE                7.4.1
 CURLINFO_LASTSOCKET             7.15.2        7.45.0
 CURLINFO_LOCAL_IP               7.21.0
@@ -471,6 +472,7 @@ CURLINFO_PROTOCOL               7.52.0        7.85.0
 CURLINFO_PROXY_ERROR            7.73.0
 CURLINFO_PROXY_SSL_VERIFYRESULT 7.52.0
 CURLINFO_PROXYAUTH_AVAIL        7.10.8
+CURLINFO_PROXYAUTH_USED         8.12.0
 CURLINFO_PTR                    7.54.1
 CURLINFO_QUEUE_TIME_T           8.6.0
 CURLINFO_REDIRECT_COUNT         7.9.7
index 18835586a1e3353b222af257b5b2d8e0a8689ede..fae168966d236ffe741b4a6efd8ce6528318060b 100644 (file)
@@ -2959,7 +2959,9 @@ typedef enum {
   CURLINFO_USED_PROXY       = CURLINFO_LONG + 66,
   CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67,
   CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68,
-  CURLINFO_LASTONE          = 68
+  CURLINFO_HTTPAUTH_USED    = CURLINFO_LONG + 69,
+  CURLINFO_PROXYAUTH_USED   = CURLINFO_LONG + 70,
+  CURLINFO_LASTONE          = 70
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
index d40bb3a3a8c9355bf6893dc0958d2e64db4fd174..ae6b3b8aa0f0e7597c54003ca73779fc9d0ed314 100644 (file)
@@ -69,6 +69,8 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
   info->request_size = 0;
   info->proxyauthavail = 0;
   info->httpauthavail = 0;
+  info->proxyauthpicked = 0;
+  info->httpauthpicked = 0;
   info->numconnects = 0;
 
   free(info->contenttype);
@@ -272,6 +274,14 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
     lptr.to_long = param_longp;
     *lptr.to_ulong = data->info.proxyauthavail;
     break;
+  case CURLINFO_HTTPAUTH_USED:
+    lptr.to_long = param_longp;
+    *lptr.to_ulong = data->info.httpauthpicked;
+    break;
+  case CURLINFO_PROXYAUTH_USED:
+    lptr.to_long = param_longp;
+    *lptr.to_ulong = data->info.proxyauthpicked;
+    break;
   case CURLINFO_OS_ERRNO:
     *param_longp = data->state.os_errno;
     break;
index 83efb64679555b4467f84a77f8b3abeefa8a7b21..2bfe8fc8f7afc0334bec2835b78f8a4df324c495 100644 (file)
@@ -530,6 +530,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
     pickhost = pickoneauth(&data->state.authhost, authmask);
     if(!pickhost)
       data->state.authproblem = TRUE;
+    else
+      data->info.httpauthpicked = data->state.authhost.picked;
     if(data->state.authhost.picked == CURLAUTH_NTLM &&
        conn->httpversion > 11) {
       infof(data, "Forcing HTTP/1.1 for NTLM");
@@ -545,6 +547,9 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
                             authmask & ~CURLAUTH_BEARER);
     if(!pickproxy)
       data->state.authproblem = TRUE;
+    else
+      data->info.proxyauthpicked = data->state.authproxy.picked;
+
   }
 #endif
 
@@ -851,12 +856,12 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
   struct connectdata *conn = data->conn;
 #ifdef USE_SPNEGO
   curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
-                                    &conn->http_negotiate_state;
+    &conn->http_negotiate_state;
 #endif
-#if defined(USE_SPNEGO) || \
-  defined(USE_NTLM) || \
-  !defined(CURL_DISABLE_DIGEST_AUTH) || \
-  !defined(CURL_DISABLE_BASIC_AUTH) || \
+#if defined(USE_SPNEGO) ||                      \
+  defined(USE_NTLM) ||                          \
+  !defined(CURL_DISABLE_DIGEST_AUTH) ||         \
+  !defined(CURL_DISABLE_BASIC_AUTH) ||          \
   !defined(CURL_DISABLE_BEARER_AUTH)
 
   unsigned long *availp;
@@ -987,7 +992,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
               authp->avail |= CURLAUTH_BEARER;
               if(authp->picked == CURLAUTH_BEARER) {
                 /* We asked for Bearer authentication but got a 40X back
-                  anyway, which basically means our token is not valid. */
+                   anyway, which basically means our token is not valid. */
                 authp->avail = CURLAUTH_NONE;
                 infof(data, "Authentication problem. Ignoring this.");
                 data->state.authproblem = TRUE;
index 49230bc1bdf0423ab4a2060aaed3172e7bc75546..ab6f1dd9212eb1a4471a25cf051464c29983a9e4 100644 (file)
@@ -252,6 +252,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     break;
 
   case NTLMSTATE_LAST:
+    /* since this is a little artificial in that this is used without any
+       outgoing auth headers being set, we need to set the bit by force */
+    if(proxy)
+      data->info.proxyauthpicked = CURLAUTH_NTLM;
+    else
+      data->info.httpauthpicked = CURLAUTH_NTLM;
     Curl_safefree(*allocuserpwd);
     authp->done = TRUE;
     break;
index ca6fc97714115034b55f206f749abf8b7ef4fbf8..fc09efad61035f659cf6992a5432ef0d28e038cc 100644 (file)
@@ -983,6 +983,8 @@ struct PureInfo {
   curl_off_t request_size; /* the amount of bytes sent in the request(s) */
   unsigned long proxyauthavail; /* what proxy auth types were announced */
   unsigned long httpauthavail;  /* what host auth types were announced */
+  unsigned long proxyauthpicked; /* selected proxy auth type */
+  unsigned long httpauthpicked;  /* selected host auth type */
   long numconnects; /* how many new connection did libcurl created */
   char *contenttype; /* the content type of the object */
   char *wouldredirect; /* URL this would have been redirected to if asked to */
index 5bef667fa16e5ef35c5231539f2524fa7555dc28..ec30fdf31c86252310798f3510b7c492f866e5eb 100644 (file)
@@ -101,7 +101,7 @@ test652 test653 test654 test655 test656 test658 test659 test660 test661 \
 test662 test663 test664 test665 test666 test667 test668 test669 test670 \
 test671 test672 test673 test674 test675 test676 test677 test678 test679 \
 test680 test681 test682 test683 test684 test685 test686 test687 test688 \
-test689 test690 test691 test692 test693 \
+test689 test690 test691 test692 test693 test694 \
 \
 test700 test701 test702 test703 test704 test705 test706 test707 test708 \
 test709 test710 test711 test712 test713 test714 test715 test716 test717 \
diff --git a/tests/data/test694 b/tests/data/test694
new file mode 100644 (file)
index 0000000..bdb3e04
--- /dev/null
@@ -0,0 +1,127 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP POST
+POST callback
+HTTP proxy
+HTTP proxy NTLM auth
+NTLM
+</keywords>
+</info>
+# Server-side
+<reply>
+
+<data>
+HTTP/1.1 401 Authorization Required\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+WWW-Authenticate: Negotiate\r
+WWW-Authenticate: NTLM\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 26\r
+\r
+This is not the real page
+</data>
+
+# this is returned first since we get no proxy-auth
+<data1001>
+HTTP/1.1 401 Authorization Required\r
+WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAACGggEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==\r
+Content-Length: 34\r
+\r
+Hey you, authenticate or go away!
+</data1001>
+
+# This is supposed to be returned when the server gets the second
+# Authorization: NTLM line passed-in from the client
+<data1002>
+HTTP/1.1 200 Things are fine\r
+Server: Microsoft-IIS/5.0\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 42\r
+\r
+Contents of that page you requested, sir.
+</data1002>
+
+# This is supposed to be returned when the server gets the second
+# request.
+<data10>
+HTTP/1.1 200 Things are fine\r
+Content-Type: yeah/maybe\r
+Content-Length: 42\r
+\r
+Contents of that second request. Differn.
+</data10>
+
+<datacheck>
+HTTP/1.1 401 Authorization Required\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+WWW-Authenticate: Negotiate\r
+WWW-Authenticate: NTLM\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 26\r
+\r
+HTTP/1.1 401 Authorization Required\r
+WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAACGggEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==\r
+Content-Length: 34\r
+\r
+HTTP/1.1 200 Things are fine\r
+Server: Microsoft-IIS/5.0\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 42\r
+\r
+Contents of that page you requested, sir.
+HTTP/1.1 200 Things are fine\r
+Content-Type: yeah/maybe\r
+Content-Length: 42\r
+\r
+Contents of that second request. Differn.
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# tool to use
+<tool>
+lib%TESTNUMBER
+</tool>
+<features>
+NTLM
+!SSPI
+</features>
+<name>
+HTTP with NTLM twice, verify CURLINFO_HTTPAUTH_USED
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/path/mine http://%HOSTIP:%HTTPPORT/path/%TESTNUMBER0010
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+
+<protocol crlf="yes">
+GET /path/mine HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /path/mine HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Authorization: NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA=
+Accept: */*
+
+GET /path/mine HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEAAAAAYABgAWAAAAAAAAABwAAAAAgACAHAAAAALAAsAcgAAAAAAAAAAAAAAhoIBAAQt1KW5CgG4YdWWcfXyfXBz1ZMCzYp37xYjBiAizmw58O6eQS7yR66eqYGWeSwl9W1lV09SS1NUQVRJT04=
+Accept: */*
+
+GET /path/6940010 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
index 7d41954df3df2f8fee22b93683bbbb4af4277427..a84886f591998ece9d5b242674fa14c369a700d0 100644 (file)
@@ -48,7 +48,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq                     \
  lib599 \
  lib643        lib645 lib650 lib651 lib652 lib653 lib654 lib655 lib658   \
  lib659 lib661 lib666 lib667 lib668 \
- lib670 lib671 lib672 lib673 lib674 lib676 lib677 lib678 \
+ lib670 lib671 lib672 lib673 lib674 lib676 lib677 lib678 lib694 \
  lib1156 \
  lib1301 \
  lib1485 \
@@ -338,6 +338,8 @@ lib677_LDADD = $(TESTUTIL_LIBS)
 lib678_SOURCES = lib678.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) $(MULTIBYTE)
 lib678_LDADD = $(TESTUTIL_LIBS)
 
+lib694_SOURCES = lib694.c $(SUPPORTFILES)
+
 lib1301_SOURCES = lib1301.c $(SUPPORTFILES) $(TESTUTIL)
 lib1301_LDADD = $(TESTUTIL_LIBS)
 
index cda3b029e34701681cda1030f337a7995f5dba15..3d0390c4572fddb8a31063cd93bfb55275187450 100644 (file)
@@ -42,6 +42,7 @@ CURLcode test(char *URL)
 {
   CURLcode res;
   CURL *curl;
+  long usedauth = 0;
 
   if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
     fprintf(stderr, "curl_global_init() failed\n");
@@ -64,6 +65,11 @@ CURLcode test(char *URL)
 
   res = curl_easy_perform(curl);
 
+  res = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_USED, &usedauth);
+  if(CURLAUTH_NTLM != usedauth) {
+    printf("CURLINFO_PROXYAUTH_USED did not say NTLM\n");
+  }
+
 test_cleanup:
 
   curl_easy_cleanup(curl);
diff --git a/tests/libtest/lib694.c b/tests/libtest/lib694.c
new file mode 100644 (file)
index 0000000..b1791e4
--- /dev/null
@@ -0,0 +1,73 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+CURLcode test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  long usedauth = 0;
+  int count = 0;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_URL, URL);
+  test_setopt(curl, CURLOPT_HEADER, 1L);
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_HTTPAUTH,
+              (long) (CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM));
+  test_setopt(curl, CURLOPT_USERPWD, "me:password");
+
+  do {
+
+    res = curl_easy_perform(curl);
+
+    res = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_USED, &usedauth);
+    if(CURLAUTH_NTLM != usedauth) {
+      printf("CURLINFO_HTTPAUTH_USED did not say NTLM\n");
+    }
+
+    /* set a new URL for the second, so that we don't restart NTLM */
+    test_setopt(curl, CURLOPT_URL, libtest_arg2);
+  } while(!res && ++count < 2);
+
+test_cleanup:
+
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}