Skip to content
Snippets Groups Projects
url.c 112 KiB
Newer Older
  • Learn to ignore specific revisions
  •   if (conn->protocol & (PROT_FTP|PROT_HTTP)) {
        /* This is a FTP or HTTP URL, we will now try to extract the possible
         * user+password pair in a string like:
         * ftp://user:password@ftp.my.site:8021/README */
    
        char *ptr=strchr(conn->host.name, '@');
        char *userpass = conn->host.name;
    
        if(ptr != NULL) {
          /* there's a user+password given here, to the left of the @ */
    
    
    
          /* So the hostname is sane.  Only bother interpreting the
           * results if we could care.  It could still be wasted
           * work because it might be overtaken by the programmatically
           * set user/passwd, but doing that first adds more cases here :-(
           */
    
          if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
            /* We could use the one in the URL */
    
            conn->bits.user_passwd = 1; /* enable user+password */
    
            if(*userpass != ':') {
              /* the name is given, get user+password */
              sscanf(userpass, "%127[^:@]:%127[^@]",
    
            }
            else
              /* no name given, get the password only */
    
              sscanf(userpass, ":%127[^@]", passwd);
    
            if(user[0]) {
              char *newname=curl_unescape(user, 0);
    
              if(!newname)
                return CURLE_OUT_OF_MEMORY;
              if(strlen(newname) < sizeof(user))
    
              /* if the new name is longer than accepted, then just use
                 the unconverted name, it'll be wrong but what the heck */
              free(newname);
            }
    
              /* we have a password found in the URL, decode it! */
    
              char *newpasswd=curl_unescape(passwd, 0);
    
              if(!newpasswd)
                return CURLE_OUT_OF_MEMORY;
              if(strlen(newpasswd) < sizeof(passwd))
    
              free(newpasswd);
            }
          }
        }
      }
    
      /* Programmatically set password:
       *   - always applies, if available
       *   - takes precedence over the values we just set above
       * so scribble it over the top.
       * User-supplied passwords are assumed not to need unescaping.
       *
       * user_password is set in "inherite initial knowledge' above,
       * so it doesn't have to be set in this block
       */
      if (data->set.userpwd != NULL) {
    
        /* the name is given, get user+password */
    
        sscanf(data->set.userpwd,
               "%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
               "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
    
      if (data->set.use_netrc != CURL_NETRC_IGNORED) {
    
                           user, passwd,
                           data->set.netrc_file)) {
    
          infof(data, "Couldn't find host %s in the .netrc file, using defaults\n",
    
          conn->bits.user_passwd = 1; /* enable user+password */
      }
    
      /* If our protocol needs a password and we have none, use the defaults */
    
           !conn->bits.user_passwd) {
    
        conn->user = strdup(CURL_DEFAULT_USER);
        conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
    
        /* This is the default password, so DON'T set conn->bits.user_passwd */
      }
    
        /* store user + password, zero-length if not set */
        conn->user = strdup(user);
        conn->passwd = strdup(passwd);
    
      if(!conn->user || !conn->passwd)
        return CURLE_OUT_OF_MEMORY;
    
      /*************************************************************
       * Check the current list of connections to see if we can
       * re-use an already existing one or if we have to create a
       * new one.
       *************************************************************/
    
    
      /* get a cloned copy of the SSL config situation stored in the
    
         connection struct */
      if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
        return CURLE_OUT_OF_MEMORY;
    
    
      /* reuse_fresh is TRUE if we are told to use a new connection by force, but
         we only acknowledge this option if this is not a re-used connection
         already (which happens due to follow-location or during a HTTP
         authentication phase). */
    
      if(data->set.reuse_fresh && !data->state.this_is_a_follow)
    
        reuse = ConnectionExists(data, conn, &conn_temp);
    
      if(reuse) {
    
        /*
         * We already have a connection for this, we got the former connection
         * in the conn_temp variable and thus we need to cleanup the one we
         * just allocated before we can move along and use the previously
         * existing one.
         */
    
        if(old_conn->proxy.rawalloc)
          free(old_conn->proxy.rawalloc);
    
        /* free the SSL config struct from this connection struct as this was
           allocated in vain and is targeted for destruction */
        Curl_free_ssl_config(&conn->ssl_config);
    
    
        conn = conn_temp;        /* use this connection from now on */
    
        /* get the user+password information from the old_conn struct since it may
         * be new for this request even when we re-use an existing connection */
        conn->bits.user_passwd = old_conn->bits.user_passwd;
    
        conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
    
        /* host can change, when doing keepalive with a proxy ! */
        if (conn->bits.httpproxy) {
          free(conn->host.rawalloc);
          conn->host=old_conn->host;
        }
    
    
        /* get the newly set value, not the old one */
        conn->bits.no_body = old_conn->bits.no_body;
    
    
        if (!conn->bits.httpproxy)
          free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
    
        free(conn->pathbuffer); /* free the newly allocated path pointer */
        conn->pathbuffer = old_conn->pathbuffer; /* use the old one */
        conn->path = old_conn->path;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        conn->bits.reuse = TRUE; /* yes, we're re-using here */
    
        conn->bits.chunk = FALSE; /* always assume not chunked unless told
                                     otherwise */
    
        conn->maxdownload = -1;  /* might have been used previously! */
    
        Curl_safefree(old_conn->user);
        Curl_safefree(old_conn->passwd);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        Curl_safefree(old_conn->proxyuser);
        Curl_safefree(old_conn->proxypasswd);
    
        if(old_conn->bits.rangestringalloc)
          free(old_conn->range);
    
    
        free(old_conn);          /* we don't need this anymore */
    
    
        /*
         * If we're doing a resumed transfer, we need to setup our stuff
         * properly.
         */
    
        conn->resume_from = data->set.set_resume_from;
    
          if (conn->bits.rangestringalloc == TRUE)
            free(conn->range);
    
          conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from);
          if(!conn->range)
            return CURLE_OUT_OF_MEMORY;
    
    
          /* tell ourselves to fetch this range */
          conn->bits.use_range = TRUE;        /* enable range download */
          conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
    
          /* There is a range, but is not a resume, useful for random ftp access */
    
          conn->range = strdup(data->set.set_range);
    
          conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
          conn->bits.use_range = TRUE;        /* enable range download */
        }
    
        else
          conn->bits.use_range = FALSE; /* disable range download */
    
        *in_connect = conn;      /* return this instead! */
    
    
        infof(data, "Re-using existing connection! (#%ld) with host %s\n",
    
              conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
    
      }
      else {
        /*
         * This is a brand new connection, so let's store it in the connection
         * cache of ours!
         */
        ConnectionStore(data, conn);
      }
    
    
      /* Continue connectdata initialization here.
    
       * Inherit the proper values from the urldata struct AFTER we have arranged
       * the persistant conncetion stuff */
      conn->fread = data->set.fread;
      conn->fread_in = data->set.in;
    
      conn->bits.upload_chunky =
        ((conn->protocol&PROT_HTTP) &&
         data->set.upload &&
         (data->set.infilesize == -1) &&
         (data->set.httpversion != CURL_HTTP_VERSION_1_0))?
        /* HTTP, upload, unknown file size and not HTTP 1.0 */
        TRUE:
      /* else, no chunky upload */
      FALSE;
    
    
      /*************************************************************
    
       * Set timeout if that is being used, and we're not using an asynchronous
       * name resolve.
    
       *************************************************************/
    
      if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
    
        /*************************************************************
         * Set signal handler to catch SIGALRM
         * Store the old value to be able to set it back later!
         *************************************************************/
    
    
    #ifdef HAVE_SIGACTION
        struct sigaction sigact;
        sigaction(SIGALRM, NULL, &sigact);
    
        keep_sigact = sigact;
    
        keep_copysig = TRUE; /* yes, we have a copy */
    
        sigact.sa_handler = alarmfunc;
    #ifdef SA_RESTART
        /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
        sigact.sa_flags &= ~SA_RESTART;
    #endif
        /* now set the new struct */
        sigaction(SIGALRM, &sigact, NULL);
    
    #else /* HAVE_SIGACTION */
    
        /* no sigaction(), revert to the much lamer signal() */
    #ifdef HAVE_SIGNAL
    
        keep_sigact = signal(SIGALRM, alarmfunc);
    
    #endif /* HAVE_SIGACTION */
    
        /* We set the timeout on the name resolving phase first, separately from
         * the download/upload part to allow a maximum time on everything. This is
         * a signal-based timeout, why it won't work and shouldn't be used in
         * multi-threaded environments. */
    
    
    #ifdef HAVE_ALARM
        /* alarm() makes a signal get sent when the timeout fires off, and that
    
        prev_alarm = alarm(data->set.connecttimeout?
                           data->set.connecttimeout:
                           data->set.timeout);
        /* We can expect the conn->created time to be "now", as that was just
           recently set in the beginning of this function and nothing slow
           has been done since then until now. */
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /*************************************************************
       * Resolve the name of the server or proxy
       *************************************************************/
    
      if(conn->bits.reuse) {
        /* re-used connection, no resolving is necessary */
        hostaddr = NULL;
    
        /* we'll need to clear conn->dns_entry later in Curl_disconnect() */
    
    
        if (conn->bits.httpproxy)
          fix_hostname(conn, &conn->host);
    
        /* set a pointer to the hostname we display */
        fix_hostname(conn, &conn->host);
    
        if(!data->change.proxy || !*data->change.proxy) {
          /* If not connecting via a proxy, extract the port from the URL, if it is
           * there, thus overriding any defaults that might have been set above. */
          conn->port =  conn->remote_port; /* it is the same port */
    
          /* Resolve target host right on */
    
          rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
    
          if(rc == CURLRESOLV_PENDING)
            *async = TRUE;
    
          else if(!hostaddr) {
            failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
            result =  CURLE_COULDNT_RESOLVE_HOST;
            /* don't return yet, we need to clean up the timeout first */
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
    
        else {
          /* This is a proxy that hasn't been resolved yet. */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          /* IDN-fix the proxy name */
    
          rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
    
          if(rc == CURLRESOLV_PENDING)
            *async = TRUE;
    
          else if(!hostaddr) {
            failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
            result = CURLE_COULDNT_RESOLVE_PROXY;
            /* don't return yet, we need to clean up the timeout first */
          }
    
    #if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
    
      if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
    
    #ifdef HAVE_SIGACTION
        if(keep_copysig) {
          /* we got a struct as it looked before, now put that one back nice
             and clean */
          sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
        }
    
    #else
    #ifdef HAVE_SIGNAL
        /* restore the previous SIGALRM handler */
        signal(SIGALRM, keep_sigact);
    #endif
    
        /* switch back the alarm() to either zero or to what it was before minus
           the time we spent until now! */
        if(prev_alarm) {
          /* there was an alarm() set before us, now put it back */
    
          unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
          unsigned long alarm_set;
    
    
          /* the alarm period is counted in even number of seconds */
          alarm_set = prev_alarm - elapsed_ms/1000;
    
          if(!alarm_set ||
             ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
            /* if the alarm time-left reached zero or turned "negative" (counted
               with unsigned values), we should fire off a SIGALRM here, but we
    
               won't, and zero would be to switch it off so we never set it to
               less than 1! */
            alarm(1);
            result = CURLE_OPERATION_TIMEOUTED;
            failf(data, "Previous alarm fired off!");
          }
          else
    
            alarm((unsigned int)alarm_set);
    
    
      return result;
    }
    
    /* SetupConnection() should be called after the name resolve initiated in
     * CreateConnection() is all done.
     */
    
    static CURLcode SetupConnection(struct connectdata *conn,
                                    struct Curl_dns_entry *hostaddr)
    {
      struct SessionHandle *data = conn->data;
      CURLcode result=CURLE_OK;
    
      Curl_pgrsTime(data, TIMER_NAMELOOKUP);
    
      if(conn->protocol & PROT_FILE)
        /* There's nothing in this function to setup if we're only doing
           a file:// transfer */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /*************************************************************
       * Send user-agent to HTTP proxies even if the target protocol
       * isn't HTTP.
       *************************************************************/
    
      if((conn->protocol&PROT_HTTP) ||
         (data->change.proxy && *data->change.proxy)) {
    
            aprintf("User-Agent: %s\015\012", data->set.useragent);
    
          if(!conn->allocptr.uagent)
            return CURLE_OUT_OF_MEMORY;
    
      conn->bytecount = 0;
      conn->headerbytecount = 0;
    
      if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* Connect only if not already connected! */
    
        result = ConnectPlease(conn, hostaddr, &connected);
    
    
          result = Curl_protocol_connect(conn);
    
          if(CURLE_OK == result)
            conn->bits.tcpconnect = TRUE;
        }
        else
          conn->bits.tcpconnect = FALSE;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        if(CURLE_OK != result)
          return result;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
    
      else {
        Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
    
    
      conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
                                   set this here perhaps a second time */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #ifdef __EMX__
      /* 20000330 mgs
       * the check is quite a hack...
       * we're calling _fsetmode to fix the problem with fwrite converting newline
       * characters (you get mangled text files, and corrupted binary files when
       * you download to stdout and redirect it to a file). */
    
    
      if ((data->set.out)->_handle == NULL) {
    
        _fsetmode(stdout, "b");
      }
    #endif
    
    
    CURLcode Curl_connect(struct SessionHandle *data,
    
                          struct connectdata **in_connect,
                          bool *asyncp)
    
    {
      CURLcode code;
    
      *asyncp = FALSE; /* assume synchronous resolves by default */
    
      /* call the stuff that needs to be called */
    
      code = CreateConnection(data, in_connect, &dns, asyncp);
    
      if(CURLE_OK == code) {
        /* no error */
        if(dns || !*asyncp)
          /* If an address is available it means that we already have the name
             resolved, OR it isn't async.
             If so => continue connecting from here */
          code = SetupConnection(*in_connect, dns);
        /* else
             response will be received and treated async wise */
      }
    
      if(CURLE_OK != code) {
        /* We're not allowed to return failure with memory left allocated
           in the connectdata struct, free those here */
    
        if(*in_connect) {
          Curl_disconnect(*in_connect); /* close the connection */
          *in_connect = NULL;           /* return a NULL */
    
    /* Call this function after Curl_connect() has returned async=TRUE and
       then a successful name resolve has been received */
    CURLcode Curl_async_resolved(struct connectdata *conn)
    {
    
    #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
        defined(USE_THREADING_GETADDRINFO)
    
      CURLcode code = SetupConnection(conn, conn->async.dns);
    
      if(code)
        /* We're not allowed to return failure with memory left allocated
           in the connectdata struct, free those here */
        Curl_disconnect(conn); /* close the connection */
    
      return code;
    #else
      (void)conn;
      return CURLE_OK;
    #endif
    }
    
    
    
    CURLcode Curl_done(struct connectdata **connp,
    
                       CURLcode status) /* an error if this is called after an
                                           error was detected */
    
    {
      CURLcode result;
    
      struct connectdata *conn = *connp;
      struct SessionHandle *data=conn->data;
    
      /* cleanups done even if the connection is re-used */
    
      if(conn->bits.rangestringalloc) {
        free(conn->range);
        conn->bits.rangestringalloc = FALSE;
      }
    
    
      /* Cleanup possible redirect junk */
      if(conn->newurl) {
        free(conn->newurl);
        conn->newurl = NULL;
      }
    
      /* this calls the protocol-specific function pointer previously set */
      if(conn->curl_done)
    
        result = conn->curl_done(conn, status);
    
      else
        result = CURLE_OK;
    
    
      Curl_pgrsDone(conn); /* done with the operation */
    
      /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
    
         forced us to close this no matter what we think.
    
         if conn->bits.close is TRUE, it means that the connection should be
         closed in spite of all our efforts to be nice, due to protocol
         restrictions in our or the server's end */
    
      if(data->set.reuse_forbid || conn->bits.close) {
        CURLcode res2;
        res2 = Curl_disconnect(conn); /* close the connection */
    
    
        *connp = NULL; /* to make the caller of this function better detect that
                          this was actually killed here */
    
    
        /* If we had an error already, make sure we return that one. But
           if we got a new error, return that. */
        if(!result && res2)
          result = res2;
      }
    
        infof(data, "Connection #%ld to host %s left intact\n",
    
              conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
    
    CURLcode Curl_do(struct connectdata **connp)
    
      CURLcode result=CURLE_OK;
    
      struct connectdata *conn = *connp;
      struct SessionHandle *data=conn->data;
    
      conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
    
        /* generic protocol-specific function pointer set in curl_connect() */
        result = conn->curl_do(conn);
    
    
        /* This was formerly done in transfer.c, but we better do it here */
    
        if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
    
          /* This was a re-use of a connection and we got a write error in the
           * DO-phase. Then we DISCONNECT this connection and have another attempt
           * to CONNECT and then DO again! The retry cannot possibly find another
           * connection to re-use, since we only keep one possible connection for
           * each.  */
    
          infof(data, "Re-used connection seems dead, get a new one\n");
    
    
          conn->bits.close = TRUE; /* enforce close of this connection */
    
          result = Curl_done(&conn, result); /* we are so done with this */
    
          /* conn may no longer be a good pointer */
    
          if(CURLE_OK == result) {
    
            /* Now, redo the connect and get a new connection */
    
            result = Curl_connect(data, connp, &async);
            if(CURLE_OK == result) {
              /* We have connected or sent away a name resolve query fine */
    
    
              conn = *connp; /* setup conn to again point to something nice */
    
              if(async) {
                /* Now, if async is TRUE here, we need to wait for the name
                   to resolve */
                result = Curl_wait_for_resolv(conn, NULL);
                if(result)
                  return result;
    
                /* Resolved, continue with the connection */
    
                result = Curl_async_resolved(conn);
    
              /* ... finally back to actually retry the DO phase */
    
    CURLcode Curl_do_more(struct connectdata *conn)
    {
      CURLcode result=CURLE_OK;
    
      if(conn->curl_do_more)
        result = conn->curl_do_more(conn);
    
      return result;
    }
    
    
    static bool safe_strequal(char* str1, char* str2)
    {
      if(str1 && str2)
        /* both pointers point to something then compare them */
        return strequal(str1, str2);
      else
        /* if both pointers are NULL then treat them as equal */
        return (!str1 && !str2);
    }
    
    
    bool
    Curl_ssl_config_matches(struct ssl_config_data* data,
                            struct ssl_config_data* needle)
    
    {
      if((data->version == needle->version) &&
         (data->verifypeer == needle->verifypeer) &&
         (data->verifyhost == needle->verifyhost) &&
         safe_strequal(data->CApath, needle->CApath) &&
         safe_strequal(data->CAfile, needle->CAfile) &&
         safe_strequal(data->random_file, needle->random_file) &&
         safe_strequal(data->egdsocket, needle->egdsocket) &&
         safe_strequal(data->cipher_list, needle->cipher_list))
    
        return TRUE;
    
      return FALSE;
    
    bool
    Curl_clone_ssl_config(struct ssl_config_data *source,
                          struct ssl_config_data *dest)
    
      dest->verifyhost = source->verifyhost;
      dest->verifypeer = source->verifypeer;
      dest->version = source->version;
    
      if(source->CAfile) {
        dest->CAfile = strdup(source->CAfile);
        if(!dest->CAfile)
    
          return FALSE;
    
      if(source->CApath) {
        dest->CApath = strdup(source->CApath);
        if(!dest->CApath)
    
          return FALSE;
    
      if(source->cipher_list) {
        dest->cipher_list = strdup(source->cipher_list);
        if(!dest->cipher_list)
    
          return FALSE;
    
      if(source->egdsocket) {
        dest->egdsocket = strdup(source->egdsocket);
        if(!dest->egdsocket)
    
          return FALSE;
    
      if(source->random_file) {
        dest->random_file = strdup(source->random_file);
        if(!dest->random_file)
    
          return FALSE;
    
    void Curl_free_ssl_config(struct ssl_config_data* sslc)
    
    {
      if(sslc->CAfile)
        free(sslc->CAfile);
    
      if(sslc->CApath)
        free(sslc->CApath);
    
      if(sslc->cipher_list)
        free(sslc->cipher_list);
    
      if(sslc->egdsocket)
        free(sslc->egdsocket);
    
      if(sslc->random_file)
        free(sslc->random_file);
    }