Peter Sylvester brought code that now allows a callback to modified the URL
authorDaniel Stenberg <daniel@haxx.se>
Mon, 12 Jan 2004 15:26:32 +0000 (15:26 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 12 Jan 2004 15:26:32 +0000 (15:26 +0000)
even when the multi interface is used, and then libcurl will simulate a
"follow location" to that new URL. Test 509 was added to test this feature.

CHANGES
lib/multi.c
tests/data/Makefile.am
tests/data/test509 [new file with mode: 0644]
tests/libtest/Makefile.am
tests/libtest/lib509.c [new file with mode: 0644]
tests/stunnel.pem

diff --git a/CHANGES b/CHANGES
index 8ad1f32..3e8e112 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,10 @@
                                   Changelog
 
 Daniel (12 January 2004)
+- Peter Sylvester brought code that now allows a callback to modified the URL
+  even when the multi interface is used, and then libcurl will simulate a
+  "follow location" to that new URL. Test 509 was added to test this feature.
+
 - Extended the time we retry servers in the test script, and I also made it
   retry the https and ftps servers before they are considered bad. I believe
   the previous approach could turn problematic on really slow hosts.
index e7a6a1d..a16359c 100644 (file)
@@ -320,45 +320,62 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
 
   easy=multi->easy.next;
   while(easy) {
-
 #ifdef CURLDEBUG
     fprintf(stderr, "HANDLE %p: State: %x\n",
             (char *)easy, easy->state);
 #endif
+    do {
+      if (CURLM_STATE_WAITCONNECT <= easy->state &&
+          easy->state <= CURLM_STATE_DO &&
+          easy->easy_handle->change.url_changed) {
+        char *gotourl;
+        Curl_posttransfer(easy->easy_handle);
 
-    switch(easy->state) {
-    case CURLM_STATE_INIT:
-      /* init this transfer. */
-      easy->result=Curl_pretransfer(easy->easy_handle);
-
-      if(CURLE_OK == easy->result) {
-        /* after init, go CONNECT */
-        easy->state = CURLM_STATE_CONNECT;
-        result = CURLM_CALL_MULTI_PERFORM; 
-        
-        easy->easy_handle->state.used_interface = Curl_if_multi;
+        gotourl = strdup(easy->easy_handle->change.url);
+        easy->easy_handle->change.url_changed = FALSE;
+        easy->result = Curl_follow(easy->easy_handle, gotourl);
+        if(CURLE_OK == easy->result)
+          easy->state = CURLM_STATE_CONNECT;
+        else
+          free(gotourl);
       }
-      break;
+    
+      easy->easy_handle->change.url_changed = FALSE;
 
-    case CURLM_STATE_CONNECT:
-      /* Connect. We get a connection identifier filled in. */
-      Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
-      easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async);
+      switch(easy->state) {
+      case CURLM_STATE_INIT:
+        /* init this transfer. */
+        easy->result=Curl_pretransfer(easy->easy_handle);
 
-      if(CURLE_OK == easy->result) {
-        if(async)
-          /* We're now waiting for an asynchronous name lookup */
-          easy->state = CURLM_STATE_WAITRESOLVE;
-        else {
-          /* after the connect has been sent off, go WAITCONNECT */
-          easy->state = CURLM_STATE_WAITCONNECT;
-          result = CURLM_CALL_MULTI_PERFORM;
+        if(CURLE_OK == easy->result) {
+          /* after init, go CONNECT */
+          easy->state = CURLM_STATE_CONNECT;
+          result = CURLM_CALL_MULTI_PERFORM; 
+        
+          easy->easy_handle->state.used_interface = Curl_if_multi;
         }
-      }
-      break;
+        break;
 
-    case CURLM_STATE_WAITRESOLVE:
-      /* awaiting an asynch name resolve to complete */
+      case CURLM_STATE_CONNECT:
+        /* Connect. We get a connection identifier filled in. */
+        Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
+        easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
+                                    &async);
+
+        if(CURLE_OK == easy->result) {
+          if(async)
+            /* We're now waiting for an asynchronous name lookup */
+            easy->state = CURLM_STATE_WAITRESOLVE;
+          else {
+            /* after the connect has been sent off, go WAITCONNECT */
+            easy->state = CURLM_STATE_WAITCONNECT;
+            result = CURLM_CALL_MULTI_PERFORM;
+          }
+        }
+        break;
+
+      case CURLM_STATE_WAITRESOLVE:
+        /* awaiting an asynch name resolve to complete */
       {
         struct Curl_dns_entry *dns;
 
@@ -387,146 +404,149 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
       }
       break;
 
-    case CURLM_STATE_WAITCONNECT:
-      /* awaiting a completion of an asynch connect */
-      easy->result = Curl_is_connected(easy->easy_conn,
-                                       easy->easy_conn->sock[FIRSTSOCKET],
-                                       &connected);
-      if(connected)
-        easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
-
-      if(CURLE_OK != easy->result) {
-        /* failure detected */
-        Curl_disconnect(easy->easy_conn); /* close the connection */
-        easy->easy_conn = NULL;           /* no more connection */
-        break;
-      }
+      case CURLM_STATE_WAITCONNECT:
+        /* awaiting a completion of an asynch connect */
+        easy->result = Curl_is_connected(easy->easy_conn,
+                                         easy->easy_conn->sock[FIRSTSOCKET],
+                                         &connected);
+        if(connected)
+          easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
 
-      if(connected) {
-        /* after the connect has completed, go DO */
-        easy->state = CURLM_STATE_DO;
-        result = CURLM_CALL_MULTI_PERFORM; 
-      }
-      break;
+        if(CURLE_OK != easy->result) {
+          /* failure detected */
+          Curl_disconnect(easy->easy_conn); /* close the connection */
+          easy->easy_conn = NULL;           /* no more connection */
+          break;
+        }
 
-    case CURLM_STATE_DO:
-      /* Do the fetch or put request */
-      easy->result = Curl_do(&easy->easy_conn);
-      if(CURLE_OK == easy->result) {
-
-        /* after do, go PERFORM... or DO_MORE */
-        if(easy->easy_conn->bits.do_more) {
-          /* we're supposed to do more, but we need to sit down, relax
-             and wait a little while first */
-          easy->state = CURLM_STATE_DO_MORE;
-          result = CURLM_OK;
+        if(connected) {
+          /* after the connect has completed, go DO */
+          easy->state = CURLM_STATE_DO;
+          result = CURLM_CALL_MULTI_PERFORM; 
         }
-        else {
-          /* we're done with the DO, now PERFORM */
-          easy->result = Curl_readwrite_init(easy->easy_conn);
-          if(CURLE_OK == easy->result) {
-            easy->state = CURLM_STATE_PERFORM;
-            result = CURLM_CALL_MULTI_PERFORM; 
+        break;
+
+      case CURLM_STATE_DO:
+        /* Do the fetch or put request */
+        easy->result = Curl_do(&easy->easy_conn);
+        if(CURLE_OK == easy->result) {
+
+          /* after do, go PERFORM... or DO_MORE */
+          if(easy->easy_conn->bits.do_more) {
+            /* we're supposed to do more, but we need to sit down, relax
+               and wait a little while first */
+            easy->state = CURLM_STATE_DO_MORE;
+            result = CURLM_OK;
+          }
+          else {
+            /* we're done with the DO, now PERFORM */
+            easy->result = Curl_readwrite_init(easy->easy_conn);
+            if(CURLE_OK == easy->result) {
+              easy->state = CURLM_STATE_PERFORM;
+              result = CURLM_CALL_MULTI_PERFORM; 
+            }
           }
         }
-      }
-      break;
+        break;
 
-    case CURLM_STATE_DO_MORE:
-      /*
-       * First, check if we really are ready to do more.
-       */
-      easy->result = Curl_is_connected(easy->easy_conn,
-                                       easy->easy_conn->sock[SECONDARYSOCKET],
-                                       &connected);
-      if(connected) {
+      case CURLM_STATE_DO_MORE:
         /*
-         * When we are connected, DO MORE and then go PERFORM
+         * First, check if we really are ready to do more.
          */
-        easy->result = Curl_do_more(easy->easy_conn);
-
-        if(CURLE_OK == easy->result)
-          easy->result = Curl_readwrite_init(easy->easy_conn);
+        easy->result =
+          Curl_is_connected(easy->easy_conn,
+                            easy->easy_conn->sock[SECONDARYSOCKET],
+                            &connected);
+        if(connected) {
+          /*
+           * When we are connected, DO MORE and then go PERFORM
+           */
+          easy->result = Curl_do_more(easy->easy_conn);
+
+          if(CURLE_OK == easy->result)
+            easy->result = Curl_readwrite_init(easy->easy_conn);
 
-        if(CURLE_OK == easy->result) {
-          easy->state = CURLM_STATE_PERFORM;
-          result = CURLM_CALL_MULTI_PERFORM; 
+          if(CURLE_OK == easy->result) {
+            easy->state = CURLM_STATE_PERFORM;
+            result = CURLM_CALL_MULTI_PERFORM; 
+          }
         }
-      }
-      break;
+        break;
 
-    case CURLM_STATE_PERFORM:
-      /* read/write data if it is ready to do so */
-      easy->result = Curl_readwrite(easy->easy_conn, &done);
-
-      if(easy->result)  {
-        /* The transfer phase returned error, we mark the connection to get
-         * closed to prevent being re-used. This is becasue we can't
-         * possibly know if the connection is in a good shape or not now. */
-        easy->easy_conn->bits.close = TRUE;
-
-        if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) {
-          /* if we failed anywhere, we must clean up the secondary socket if
-             it was used */
-          sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
-          easy->easy_conn->sock[SECONDARYSOCKET]=-1;
+      case CURLM_STATE_PERFORM:
+        /* read/write data if it is ready to do so */
+        easy->result = Curl_readwrite(easy->easy_conn, &done);
+
+        if(easy->result)  {
+          /* The transfer phase returned error, we mark the connection to get
+           * closed to prevent being re-used. This is becasue we can't
+           * possibly know if the connection is in a good shape or not now. */
+          easy->easy_conn->bits.close = TRUE;
+
+          if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) {
+            /* if we failed anywhere, we must clean up the secondary socket if
+               it was used */
+            sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
+            easy->easy_conn->sock[SECONDARYSOCKET]=-1;
+          }
+          Curl_posttransfer(easy->easy_handle);
+          Curl_done(easy->easy_conn);
         }
-        Curl_posttransfer(easy->easy_handle);
-        Curl_done(easy->easy_conn);
-      }
 
-      /* after the transfer is done, go DONE */
-      else if(TRUE == done) {
-
-        /* call this even if the readwrite function returned error */
-        Curl_posttransfer(easy->easy_handle);
-
-        /* When we follow redirects, must to go back to the CONNECT state */
-        if(easy->easy_conn->newurl) {
-          char *newurl = easy->easy_conn->newurl;
-          easy->easy_conn->newurl = NULL;
-          easy->result = Curl_follow(easy->easy_handle, newurl);
-          if(CURLE_OK == easy->result) {
-            easy->state = CURLM_STATE_CONNECT;
-            result = CURLM_CALL_MULTI_PERFORM;
+        /* after the transfer is done, go DONE */
+        else if(TRUE == done) {
+
+          /* call this even if the readwrite function returned error */
+          Curl_posttransfer(easy->easy_handle);
+
+          /* When we follow redirects, must to go back to the CONNECT state */
+          if(easy->easy_conn->newurl) {
+            char *newurl = easy->easy_conn->newurl;
+            easy->easy_conn->newurl = NULL;
+            easy->result = Curl_follow(easy->easy_handle, newurl);
+            if(CURLE_OK == easy->result) {
+              easy->state = CURLM_STATE_CONNECT;
+              result = CURLM_CALL_MULTI_PERFORM;
+            }
+          }
+          else {
+            easy->state = CURLM_STATE_DONE;
+            result = CURLM_CALL_MULTI_PERFORM; 
           }
         }
-        else {
-          easy->state = CURLM_STATE_DONE;
-          result = CURLM_CALL_MULTI_PERFORM; 
-        }
-      }
-      break;
-    case CURLM_STATE_DONE:
-      /* post-transfer command */
-      easy->result = Curl_done(easy->easy_conn);
+        break;
+      case CURLM_STATE_DONE:
+        /* post-transfer command */
+        easy->result = Curl_done(easy->easy_conn);
 
-      /* after we have DONE what we're supposed to do, go COMPLETED, and
-         it doesn't matter what the Curl_done() returned! */
-      easy->state = CURLM_STATE_COMPLETED;
-      break;
+        /* after we have DONE what we're supposed to do, go COMPLETED, and
+           it doesn't matter what the Curl_done() returned! */
+        easy->state = CURLM_STATE_COMPLETED;
+        break;
 
-    case CURLM_STATE_COMPLETED:
-      /* this is a completed transfer, it is likely to still be connected */
+      case CURLM_STATE_COMPLETED:
+        /* this is a completed transfer, it is likely to still be connected */
 
-      /* This node should be delinked from the list now and we should post
-         an information message that we are complete. */
-      break;
-    default:
-      return CURLM_INTERNAL_ERROR;
-    }
+        /* This node should be delinked from the list now and we should post
+           an information message that we are complete. */
+        break;
+      default:
+        return CURLM_INTERNAL_ERROR;
+      }
 
-    if(CURLM_STATE_COMPLETED != easy->state) {
-      if(CURLE_OK != easy->result) {
-        /*
-         * If an error was returned, and we aren't in completed state now,
-         * then we go to completed and consider this transfer aborted.  */
-        easy->state = CURLM_STATE_COMPLETED;
+      if(CURLM_STATE_COMPLETED != easy->state) {
+        if(CURLE_OK != easy->result) {
+          /*
+           * If an error was returned, and we aren't in completed state now,
+           * then we go to completed and consider this transfer aborted.  */
+          easy->state = CURLM_STATE_COMPLETED;
+        }
+        else
+          /* this one still lives! */
+          (*running_handles)++;
       }
-      else
-        /* this one still lives! */
-        (*running_handles)++;
-    }
+
+    } while (easy->easy_handle->change.url_changed);
 
     if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
       /* clear out the usage of the shared DNS cache */
index d0d3209..ec90fed 100644 (file)
@@ -24,4 +24,4 @@ test62 test63 test64 test65 test66 test144 test145 test67 test68 test41 \
 test40 test42 test69 test70 test71 test72 test73 test146 test505 \
 test74 test75 test76 test77 test78 test147 test148 test506 test79 test80 \
 test81 test82 test83 test84 test85 test86 test87 test507 test149 test88 \
-test89 test90 test508 test91 test92 test203 test93 test94 test95
+test89 test90 test508 test91 test92 test203 test93 test94 test95 test509
diff --git a/tests/data/test509 b/tests/data/test509
new file mode 100644 (file)
index 0000000..27bda4c
--- /dev/null
@@ -0,0 +1,51 @@
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: localhost:8433
+Content-length:6
+
+Hello
+</data>
+<datacheck>
+Hello
+</datacheck>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+https
+</server>
+<features>
+SSL
+</features>
+<tool>
+lib509
+</tool>
+
+ <name>
+simple HTTPS GET and URL redirect in certificate
+ </name>
+ <command>
+https://localhost:%HTTPSPORT/dvcs
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /509 HTTP/1.1
+Host: localhost:8433
+Pragma: no-cache
+Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
+
+</protocol>
+</verify>
index 52f9e6e..9a358f0 100644 (file)
@@ -12,7 +12,7 @@ SUPPORTFILES = first.c test.h
 
 # here are all tools used for running libcurl tests
 noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \
-  lib508
+  lib508 lib509
 
 lib500_SOURCES = lib500.c $(SUPPORTFILES)
 lib500_LDADD = $(LIBDIR)/libcurl.la
@@ -49,3 +49,7 @@ lib507_DEPENDENCIES = $(LIBDIR)/libcurl.la
 lib508_SOURCES = lib508.c $(SUPPORTFILES)
 lib508_LDADD = $(LIBDIR)/libcurl.la
 lib508_DEPENDENCIES = $(LIBDIR)/libcurl.la
+
+lib509_SOURCES = lib509.c $(SUPPORTFILES)
+lib509_LDADD = $(LIBDIR)/libcurl.la
+lib509_DEPENDENCIES = $(LIBDIR)/libcurl.la
diff --git a/tests/libtest/lib509.c b/tests/libtest/lib509.c
new file mode 100644 (file)
index 0000000..d9774d1
--- /dev/null
@@ -0,0 +1,260 @@
+#include "test.h"
+
+#ifdef USE_SSLEAY
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <openssl/opensslv.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/crypto.h>
+#include <openssl/lhash.h>
+#include <openssl/objects.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs12.h>
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+
+
+typedef struct sslctxparm_st {
+   CURL* curl;
+   int accesstype ;
+   unsigned char * accessinfoURL ;
+
+} sslctxparm;
+
+
+static unsigned char *i2s_ASN1_IA5STRING( ASN1_IA5STRING *ia5)
+{
+       unsigned char *tmp;
+       if(!ia5 || !ia5->length) return NULL;
+       tmp = OPENSSL_malloc(ia5->length + 1);
+       memcpy(tmp, ia5->data, ia5->length);
+       tmp[ia5->length] = 0;
+       return tmp;
+}
+
+/* A conveniance routine to get an access URI. */
+
+static unsigned char *my_get_ext(X509 * cert, const int type,
+                                 int extensiontype)
+{
+  int i;
+  STACK_OF(ACCESS_DESCRIPTION) * accessinfo ;
+  accessinfo =  X509_get_ext_d2i(cert, extensiontype, NULL, NULL) ;
+  
+  if (!sk_ACCESS_DESCRIPTION_num(accessinfo))
+    return NULL;
+  
+  for (i = 0; i < sk_ACCESS_DESCRIPTION_num(accessinfo); i++) {
+    ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(accessinfo, i);
+    if (OBJ_obj2nid(ad->method) == type) {
+      if (ad->location->type == GEN_URI) {
+        return i2s_ASN1_IA5STRING(ad->location->d.ia5);
+      }
+      return NULL;
+    }
+  }    
+  return NULL;
+}
+
+void * globalparm = NULL;
+
+static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
+{
+  sslctxparm * p = (sslctxparm *) arg;
+  int ok, err;
+
+  fprintf(stderr,"ssl_app_verify_callback sslctxparm=%p ctx=%p\n",
+          (void *)p, (void*)ctx);
+
+#if OPENSSL_VERSION_NUMBER<0x00907000L
+/* not necessary in openssl 0.9.7 or later */
+        
+  fprintf(stderr,"This version %s of openssl does not support a parm (%p)"
+          ", getting a global static %p \n",
+          OPENSSL_VERSION_TEXT, (void *)p, (void *)globalparm);
+  
+  p = globalparm;
+#endif
+
+/* The following error should not occur. We test this to avoid segfault. */
+  if (!p || !ctx) { 
+    fprintf(stderr,"Internal error in ssl_app_verify_callback "
+            "sslctxparm=%p ctx=%p\n",(void *)p,(void*)ctx);
+    return 0;
+  }
+
+  ok= X509_verify_cert(ctx);
+  err=X509_STORE_CTX_get_error(ctx);
+
+/* The following seems to be a problem in 0.9.7/8 openssl versions */
+
+#if 1
+  if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
+      err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
+    fprintf(stderr,"X509_verify_cert: repairing self signed\n") ; 
+    X509_STORE_CTX_set_error(ctx,X509_V_OK);
+    ok = 1;
+  }
+#endif
+
+  if (ok && ctx->cert) {
+    unsigned char * accessinfoURL ;
+               
+    accessinfoURL = my_get_ext(ctx->cert,p->accesstype ,NID_info_access);
+    if (accessinfoURL) {
+
+      if (strcmp((char *)p->accessinfoURL, (char *)accessinfoURL)) {   
+        fprintf(stderr, "Setting URL <%s>, was <%s>\n",
+                accessinfoURL,p->accessinfoURL);
+        OPENSSL_free(p->accessinfoURL);
+        p->accessinfoURL = accessinfoURL;
+        curl_easy_setopt(p->curl, CURLOPT_URL,p->accessinfoURL);
+      }
+      else 
+        OPENSSL_free(accessinfoURL);
+    }           
+  }
+  return(ok);
+}
+
+
+static CURLcode sslctxfun(CURL * curl, void * sslctx, void * parm)
+{
+  sslctxparm * p = (sslctxparm *) parm;
+       
+  SSL_CTX * ctx = (SSL_CTX *) sslctx ;
+  fprintf(stderr,"sslctxfun start curl=%p ctx=%p parm=%p\n",
+          (void *)curl,(void *)ctx,(void *)p);
+               
+  SSL_CTX_set_quiet_shutdown(ctx,1);
+  SSL_CTX_set_cipher_list(ctx,"RC4-MD5");
+  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
+/* one might assume that the cert validaton would not fail when setting this,
+   but it still does, see the error handling in the call back */
+
+  SSL_CTX_set_verify_depth(ctx,0);
+  SSL_CTX_set_verify(ctx,SSL_VERIFY_NONE,NULL); 
+
+#if OPENSSL_VERSION_NUMBER<0x00907000L
+/* in newer openssl versions we can set a parameter for the call back. */
+  fprintf(stderr,"This version %s of openssl does not support a parm,"
+          " setting global one\n", OPENSSL_VERSION_TEXT);
+  /* this is only done to support 0.9.6 version */
+  globalparm = parm;
+
+/* in 0.9.6 the parm is not taken */ 
+#endif
+  SSL_CTX_set_cert_verify_callback(ctx, ssl_app_verify_callback, parm); 
+  fprintf(stderr,"sslctxfun end\n");
+
+  return CURLE_OK ;
+}
+
+
+CURLcode test(char *URL)
+{
+  CURLM* multi;
+  sslctxparm p;
+
+  int i;
+  CURLMsg *msg;
+
+  curl_global_init(CURL_GLOBAL_ALL);
+
+  p.curl = curl_easy_init(); 
+
+  p.accessinfoURL = (unsigned char *) strdup(URL);
+  p.accesstype = OBJ_obj2nid(OBJ_txt2obj("AD_DVCS",0)) ;
+  curl_easy_setopt(p.curl, CURLOPT_URL, p.accessinfoURL);
+
+  curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun)  ; 
+  curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_DATA, &p);  
+
+  curl_easy_setopt(p.curl, CURLOPT_SSL_VERIFYPEER, FALSE); 
+  curl_easy_setopt(p.curl, CURLOPT_SSL_VERIFYHOST, 1);
+
+  fprintf(stderr,"Going to perform %s\n",p.accessinfoURL);
+
+  {
+    CURLMcode res;
+    int running;
+    char done=FALSE;
+
+    multi = curl_multi_init();
+
+    res = curl_multi_add_handle(multi, p.curl);
+
+    while(!done) {
+      fd_set rd, wr, exc;
+      int max_fd;
+      struct timeval interval;
+
+      interval.tv_sec = 1;
+      interval.tv_usec = 0;
+
+      while (res == CURLM_CALL_MULTI_PERFORM) {
+        res = curl_multi_perform(multi, &running);
+        fprintf(stderr, "running=%d res=%d\n",running,res);
+        if (running <= 0) {
+          done = TRUE;
+          break;
+        }
+      }
+      if(done)
+        break;
+      
+      if (res != CURLM_OK) {
+        fprintf(stderr, "not okay???\n");
+        return 80;
+      }
+
+      FD_ZERO(&rd);
+      FD_ZERO(&wr);
+      FD_ZERO(&exc);
+      max_fd = 0;
+
+      if (curl_multi_fdset(multi, &rd, &wr, &exc, &max_fd) != CURLM_OK) {
+        fprintf(stderr, "unexpected failured of fdset.\n");
+        return 89;
+      }
+
+      if (select(max_fd+1, &rd, &wr, &exc, &interval) == -1) {
+        fprintf(stderr, "bad select??\n");
+        return 95;
+      }
+
+      res = CURLM_CALL_MULTI_PERFORM;
+    }
+    msg = curl_multi_info_read(multi, &running);
+    /* this should now contain a result code from the easy handle, get it */
+    i = msg->data.result;
+  }
+
+  fprintf(stderr, "all done\n");
+
+  curl_multi_remove_handle(multi, p.curl);
+  curl_easy_cleanup(p.curl);
+  curl_multi_cleanup(multi);
+
+  curl_global_cleanup(); 
+  free(p.accessinfoURL);
+
+  return i; 
+}
+#else /* USE_SSLEAY */
+CURLcode test(char *URL)
+{
+  (void)URL;
+  return CURLE_FAILED_INIT;
+}
+#endif /* USE_SSLEAY */
index 2668a4b..1e09cd2 100644 (file)
+#
+#  This file contains a private key and a certificate used for stunnel.
+#  The certificate contains a number of extensions essentially being
+#  used in the 509 test. The certificate has been generated using
+#  openssl with the parameters listed below up to the line
+#  contain [something], after that you find the result.
+# 
+#
+extensions = x509v3
+[ x509v3 ]
+subjectAltName   = DNS:localhost
+nsCertType       = server
+nsComment       = "CURL stunnel server test certificate"
+keyUsage       = digitalSignature,keyEncipherment
+extendedKeyUsage = serverAuth
+basicConstraints = CA:false
+subjectKeyIdentifier = hash
+subjectInfoAccess = AD_DVCS;URI:"https://localhost:8433/509"
+authorityInfoAccess = AD_DVCS;URI:"https://localhost:8433/509"
+[ req ]
+default_bits                    = 1234
+distinguished_name              = req_DN
+default_md                     = sha1
+string_mask                    = pkix
+[ req_DN ]
+countryName                     = "1. Country Name             (2 letter code)"
+countryName_value             = SE
+stateOrProvinceName             = "2. State or Province Name   (full name)    "
+stateOrProvinceName_value     = Solna
+localityName                    = "3. Locality Name            (eg, city)     "
+localityName_value            = Mooo
+0.organizationName              = "4. Organization Name        (eg, company)  "
+0.organizationName_value      = Haxx
+organizationalUnitName          = "5. Organizational Unit Name (eg, section)  "
+organizationalUnitName_value  = Coolx
+commonName                      = "6. Common Name              (eg, FQDN)     "
+commonName_value              = "storbror"
+1.commonName                      = "6. Common Name              (eg, FQDN)     "
+1.commonName_value              = "localhost"
+[something]
 -----BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDQPvaAU+Pdd5B0XlR71lDw0LyMridewzGaN6H3T+vRKYkvxyZw
-nrxhYSLWn4g9wcu/EWcTN23A07Oa4HSZ/wHQ/yO+Yv01OmfGHylWZNN0eb/qemv8
-wp/bFRaubeuIGz9hl4r1HM5Mn3cNjhDjIjDqzGorObUItigCHY4ZDNHOOQIDAQAB
-AoGAfK4V3ANeHEznuiR4JKTOVUBEg14JyFzJp/HDEVVSdSqPLoHn6+m+E/eaajGd
-2ovbNwxuUD0Rgy+9Cu90IHdjV+8i7xo/ttnzg8UDUUhT0BMuOsbEPGGABqPxXEoM
-+c58smG9r30K+5ToctRlsYuH4xQvHL3fc2DHRDEwPDD+KdkCQQD3kO9D6sV9/So3
-wWLamW0AFK/spK0i1MqSdPi/gnor37WNqOxBz2MVYDmTWeDWtq9HuG2QRKCNiqGB
-tAUyKSVjAkEA11ccOxGleOM+scugobGkktnCniyMJSxQijMpIDmO/S0DBH5SiTee
-BywwtcadjUWn4uQGJ0CQCJo2mjsmY13OswJBAKJgPJ7WWKyXJV6mh9kLMrQP3Yeg
-RzMGUMuYzyKFSO6H74O3nSZZCQsXLwxXsiICdpra+3nZmVjc6aux0Iqi9DMCQQCh
-DXaAux7t/c9q/CeEJy814YWL9TdPqggGhGLyxfmqYprKJowmMiGPrb40hXpaKUl6
-CR6NBt1ygZvq1+hLEuK/AkBYJ0ikl3YHkWqSFgiw/yDsVa3CDpChLGxjlheX5gop
-LKrKE8RO3rtT3CdWyI/IBWPyc0h+qCTEqUdqKW1vRYeS
+MIIC1AIBAAKBmwNZN+oG6vJ8DAze+FvOKSS49X4xGMxALhKRLhQQb7qvM+7BcMgR
+v+RKxkX7SNgcxKPLcIHf7QQ6DBIlLXuAuVHQtWW9b06q64kBElkEwh6gP5Ia9JrR
+ysGbu2U6NRP+xBU33dVwZjF07ocN9Pp392W4VxEc+g3+FkRzUEaahDGOabmjgKuq
+DdlKdZLzgJj7+9sEKpb7+FdG56rZAgMBAAECgZsCkK1Z1XTUz5x3m7PMuHEiVaKS
+yk/B4ISq6pbO/gxpieARzhR038wNug6L+8VA8UDebXHBvGYYr9Mhb2OZUfIlr+nW
+h7kmHZ+T88M3eH/hQc3jtnvnu1dGmMlIXjTLQOrKgrAn6fYaw2HAGPdGKjpatAy/
+3vRjguv/22pNJLRQmMHdozJdc8mEYY+AhqrQxXCWQT/1peZzlq/IAQJOAfhE2YWf
+qB9iYNmuhxJ1PolPW4I63atXuoavqadbaRoaLm/pqLVB1QjMeyak8O/0TmO6CXk6
+878ps85fLFgARRjSYX+rYwoYNzqxK3cBAk4Bsy4oofReVT8xB+7rFZFMV4McyL7e
+sOABFqecLuNIGT6CdeEU1z7TUfq8sKM1MQ25e0J1PMmoWTqDwzhnxK+ckeFsZ8Te
+dgqVW+Oyy9kCTgHqyc/P/uEZkp1ioDu0WkpAR+1vZa2jeyH+vm9nhE9Z6Uty/r6F
+k4otIx9lMDmTwXqeE03vINJlJshqvjShfbnCe9gK8xrUk1cFl7QPAQJOATD3LQRq
+At2MniioFtiTbUN6n2ZS1C5xnHGq3fnBzxnZw4UmSfuZjG/L3gWPKkyJCK3HYe9K
+ho6ZQhNB6P5d7sQQjG6f+SIRwp+VjwvpAk4AnM4do54FETeLHhY4zy47dM/zdy3u
+iDjiFwoMTR+PfF03evsWe5pW3EaXolGi3FRAZ/idFA+L3Gi2y4xR44z71HkbF32L
+WKaLdOuBQvI=
 -----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            09:c2:f9:ca:9a:d8
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=SE, ST=Solna, L=Mooo, O=Haxx, OU=Coolx, CN=storbror, CN=localhost
+        Validity
+            Not Before: Jan  5 11:25:13 2004 GMT
+            Not After : Feb 13 11:25:13 2008 GMT
+        Subject: C=SE, ST=Solna, L=Mooo, O=Haxx, OU=Coolx, CN=storbror, CN=localhost
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (1234 bit)
+                Modulus (1234 bit):
+                    03:59:37:ea:06:ea:f2:7c:0c:0c:de:f8:5b:ce:29:
+                    24:b8:f5:7e:31:18:cc:40:2e:12:91:2e:14:10:6f:
+                    ba:af:33:ee:c1:70:c8:11:bf:e4:4a:c6:45:fb:48:
+                    d8:1c:c4:a3:cb:70:81:df:ed:04:3a:0c:12:25:2d:
+                    7b:80:b9:51:d0:b5:65:bd:6f:4e:aa:eb:89:01:12:
+                    59:04:c2:1e:a0:3f:92:1a:f4:9a:d1:ca:c1:9b:bb:
+                    65:3a:35:13:fe:c4:15:37:dd:d5:70:66:31:74:ee:
+                    87:0d:f4:fa:77:f7:65:b8:57:11:1c:fa:0d:fe:16:
+                    44:73:50:46:9a:84:31:8e:69:b9:a3:80:ab:aa:0d:
+                    d9:4a:75:92:f3:80:98:fb:fb:db:04:2a:96:fb:f8:
+                    57:46:e7:aa:d9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                DNS:localhost
+            Netscape Cert Type: 
+                SSL Server
+            Netscape Comment: 
+                CURL stunnel server test certificate
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                35:77:35:3B:9B:98:3C:B6:C7:9A:E7:A8:04:B9:7C:70:AD:FA:37:A9
+            Subject Information Access: 
+                ad dvcs - URI:https://localhost:8433/509
+
+            Authority Information Access: 
+                ad dvcs - URI:https://localhost:8433/509
+
+    Signature Algorithm: md5WithRSAEncryption
+        02:47:57:18:5f:54:3f:1d:29:0a:05:7a:d1:0f:e6:b9:2c:8b:
+        29:38:50:bf:c1:a1:7f:73:4a:9d:f1:1b:bf:2e:ea:87:91:dd:
+        ff:75:73:57:78:cf:75:52:57:0b:5b:bb:d4:77:b2:dd:e8:0c:
+        06:ce:1b:c0:3d:9f:68:c0:0b:66:b8:f9:46:db:04:a9:2f:a3:
+        5e:9c:c6:70:ff:1d:af:1f:17:9c:38:3d:12:aa:af:7b:72:44:
+        a0:44:41:6b:9a:9b:60:8c:50:94:f1:d7:a5:77:6b:d3:0e:66:
+        88:a1:52:63:23:f6:66:a6:ea:f9:d0:df:4d:8a:14:f0:73:c6:
+        b6:b6:b6:68:4f:3d:9a:b6:31:ba:19:f3:66:9f:16:6d:87:64:
+        32:53:d2:90:e5:8c:1e:f5:18:61:1a
 -----BEGIN CERTIFICATE-----
-MIICcTCCAdqgAwIBAgIBADANBgkqhkiG9w0BAQQFADBzMQswCQYDVQQGEwJTRTEO
-MAwGA1UECBMFU29sbmExDTALBgNVBAcTBE1vb28xDTALBgNVBAoTBEhheHgxDjAM
-BgNVBAsTBUNvb2x4MRIwEAYDVQQDEwlzdG9yZWJyb3IxEjAQBgNVBAMTCWxvY2Fs
-aG9zdDAeFw0wMTA0MjAxODI2MjhaFw0wMjA0MjAxODI2MjhaMHMxCzAJBgNVBAYT
+MIIDujCCAwigAwIBAgIGCcMA3QkhMA0GCSqGSIb3DQEBBAUAMHIxCzAJBgNVBAYT
 AlNFMQ4wDAYDVQQIEwVTb2xuYTENMAsGA1UEBxMETW9vbzENMAsGA1UEChMESGF4
-eDEOMAwGA1UECxMFQ29vbHgxEjAQBgNVBAMTCXN0b3JlYnJvcjESMBAGA1UEAxMJ
-bG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQPvaAU+Pdd5B0
-XlR71lDw0LyMridewzGaN6H3T+vRKYkvxyZwnrxhYSLWn4g9wcu/EWcTN23A07Oa
-4HSZ/wHQ/yO+Yv01OmfGHylWZNN0eb/qemv8wp/bFRaubeuIGz9hl4r1HM5Mn3cN
-jhDjIjDqzGorObUItigCHY4ZDNHOOQIDAQABoxUwEzARBglghkgBhvhCAQEEBAMC
-BkAwDQYJKoZIhvcNAQEEBQADgYEASTzH6Af6xmkpPGAdL/lecdkeuChfgb7tVkOE
-rTzgxE5fVcW9NK79HXZ3OND2uHyu9WMieIr7QKyzfK1uskxQcY6mPApn/Z+yD1vn
-TYBuyP8I+khaLKA69QzVv+5yVBR0xlAaXGwTKxNzBTXznSmshgvYt5nsmcMw2Xfg
-CMkSsOU=
+eDEOMAwGA1UECxMFQ29vbHgxETAPBgNVBAMTCHN0b3Jicm9yMRIwEAYDVQQDEwls
+b2NhbGhvc3QwHhcNMDQwMTA1MTQ0MjU2WhcNMDgwMjEzMTQ0MjU2WjByMQswCQYD
+VQQGEwJTRTEOMAwGA1UECBMFU29sbmExDTALBgNVBAcTBE1vb28xDTALBgNVBAoT
+BEhheHgxDjAMBgNVBAsTBUNvb2x4MREwDwYDVQQDEwhzdG9yYnJvcjESMBAGA1UE
+AxMJbG9jYWxob3N0MIG5MA0GCSqGSIb3DQEBAQUAA4GnADCBowKBmwNZN+oG6vJ8
+DAze+FvOKSS49X4xGMxALhKRLhQQb7qvM+7BcMgRv+RKxkX7SNgcxKPLcIHf7QQ6
+DBIlLXuAuVHQtWW9b06q64kBElkEwh6gP5Ia9JrRysGbu2U6NRP+xBU33dVwZjF0
+7ocN9Pp392W4VxEc+g3+FkRzUEaahDGOabmjgKuqDdlKdZLzgJj7+9sEKpb7+FdG
+56rZAgMBAAGjggEkMIIBIDARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgEN
+BCYWJENVUkwgc3R1bm5lbCBzZXJ2ZXIgdGVzdCBjZXJ0aWZpY2F0ZTALBgNVHQ8E
+BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCQYDVR0TBAIwADAdBgNVHQ4EFgQU
+NXc1O5uYPLbHmueoBLl8cK36N6kwNgYIKwYBBQUHAQsEKjAoMCYGCCsGAQUFBzAE
+hhpodHRwczovL2xvY2FsaG9zdDo4NDMzLzUwOTA2BggrBgEFBQcBAQQqMCgwJgYI
+KwYBBQUHMASGGmh0dHBzOi8vbG9jYWxob3N0Ojg0MzMvNTA5MBoGA1UdEQQTMBGH
+BH8AAAGCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQQFAAOBnAAAIHkvI0V6padfc8Lv
+onuNqBwCMIg4SugCslkN597Yb8ZDEAUe3ArkOvzAHUngsD5D0gfbKblKP/P0bN6Y
+Ft896NmH4QFsDAetZcCFf24AM4DbUQo5jtG+dkanI/7IxxNYJ1PQ64/yscdQFvHW
+xhIX3Q6FqABjcN5nc80Rog+b6eS8QRX1BRnQqbGtocuptUgW5mWsSb+DR6pZbA==
 -----END CERTIFICATE-----