Esempio n. 1
0
/* Resolve a name and return a pointer in the 'entry' argument if one
   is available.

   Return codes:

   -1 = error, no pointer
   0 = OK, pointer provided
   1 = waiting for response, no pointer
*/
int Curl_resolv(struct connectdata *conn,
                char *hostname,
                int port,
                struct Curl_dns_entry **entry)
{
  char *entry_id = NULL;
  struct Curl_dns_entry *dns = NULL;
  size_t entry_len;
  int wait;
  struct SessionHandle *data = conn->data;
  CURLcode result;

  /* default to failure */
  int rc = -1;
  *entry = NULL;

#ifdef HAVE_SIGSETJMP
  /* this allows us to time-out from the name resolver, as the timeout
     will generate a signal and we will siglongjmp() from that here */
  if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
    /* this is coming from a siglongjmp() */
    failf(data, "name lookup timed out");
    return -1;
  }
#endif

  /* Create an entry id, based upon the hostname and port */
  entry_id = create_hostcache_id(hostname, port, &entry_len);
  /* If we can't create the entry id, fail */
  if (!entry_id)
    return -1;

  if(data->share)
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

  /* See if its already in our dns cache */
  dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
  
  if(data->share)
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

  /* free the allocated entry_id again */
  free(entry_id);

  if (!dns) {
    /* The entry was not in the cache. Resolve it to IP address */
      
    /* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
       value indicating that we need to wait for the response to the resolve
       call */
    Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
    
    if (!addr) {
      if(wait) {
        /* the response to our resolve call will come asynchronously at 
           a later time, good or bad */
        /* First, check that we haven't received the info by now */
        result = Curl_is_resolved(conn, &dns);
        if(result) /* error detected */
          return -1;
        if(dns)
          rc = 0; /* pointer provided */
        else
          rc = 1; /* no info yet */
      }
    }
    else {
      if(data->share)
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

      /* we got a response, store it in the cache */
      dns = cache_resolv_response(data, addr, hostname, port);
      
      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

      if(!dns)
        /* returned failure, bail out nicely */
        Curl_freeaddrinfo(addr);
      else
        rc = 0;
    }
  }
  else {
    dns->inuse++; /* we use it! */
    rc = 0;
  }

  *entry = dns;

  return rc;
}
Esempio n. 2
0
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  bool done;
  CURLMcode result=CURLM_OK;
  struct Curl_message *msg;
  bool connected;
  bool async;

  *running_handles = 0; /* bump this once for every living handle */

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  easy=multi->easy.next;
  while(easy) {
#if 0
    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);

        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
        if(CURLE_OK == easy->result) {
          gotourl = strdup(easy->easy_handle->change.url);
          if(gotourl) {
            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);
          }
          else {
            easy->result = CURLE_OUT_OF_MEMORY;
            easy->state = CURLM_STATE_COMPLETED;
            break;
          }
        }
      }

      easy->easy_handle->change.url_changed = FALSE;

      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;
        }
        break;

      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 = NULL;

        /* check if we have the name resolved by now */
        easy->result = Curl_is_resolved(easy->easy_conn, &dns);

        if(dns) {
          /* Perform the next step in the connection phase, and then move on
             to the WAITCONNECT state */
          easy->result = Curl_async_resolved(easy->easy_conn);

          if(CURLE_OK != easy->result)
            /* if Curl_async_resolved() returns failure, the connection struct
               is already freed and gone */
            easy->easy_conn = NULL;           /* no more connection */

          easy->state = CURLM_STATE_WAITCONNECT;
        }

        if(CURLE_OK != easy->result) {
          /* failure detected */
          Curl_disconnect(easy->easy_conn); /* disconnect properly */
          easy->easy_conn = NULL;           /* no more connection */
          break;
        }
      }
      break;

      case CURLM_STATE_WAITCONNECT:
        /* awaiting a completion of an asynch connect */
        easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
                                         &connected);
        if(connected)
          easy->result = Curl_protocol_connect(easy->easy_conn);

        if(CURLE_OK != easy->result) {
          /* failure detected */
          Curl_disconnect(easy->easy_conn); /* close the connection */
          easy->easy_conn = NULL;           /* no more connection */
          break;
        }

        if(connected) {
          /* after the connect has completed, go DO */
          easy->state = CURLM_STATE_DO;
          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;

      case CURLM_STATE_DO_MORE:
        /*
         * First, check if we really are ready to do more.
         */
        easy->result = Curl_is_connected(easy->easy_conn, 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;
          }
        }
        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(CURL_SOCKET_BAD != 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, easy->result);
        }

        /* 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_done(&easy->easy_conn, CURLE_OK);
            if(easy->result == CURLE_OK)
              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;
          }
        }
        break;
      case CURLM_STATE_DONE:
        /* post-transfer command */
        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);

        /* 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 */

        /* 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;
        }
        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 */
      easy->easy_handle->hostcache = NULL;

      /* now add a node to the Curl_message linked list with this info */
      msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));

      if(!msg)
        return CURLM_OUT_OF_MEMORY;

      msg->extmsg.msg = CURLMSG_DONE;
      msg->extmsg.easy_handle = easy->easy_handle;
      msg->extmsg.data.result = easy->result;
      msg->next=NULL;

      easy->msg = msg;
      easy->msg_num = 1; /* there is one unread message here */

      multi->num_msgs++; /* increase message counter */
    }

    easy = easy->next; /* operate on next handle */
  }

  return result;
}
Esempio n. 3
0
int Curl_resolv(struct connectdata *conn,
                const char *hostname,
                int port,
                struct Curl_dns_entry **entry)
{
  char *entry_id = NULL;
  struct Curl_dns_entry *dns = NULL;
  size_t entry_len;
  struct SessionHandle *data = conn->data;
  CURLcode result;
  int rc = CURLRESOLV_ERROR; /* default to failure */

  *entry = NULL;

  /* Create an entry id, based upon the hostname and port */
  entry_id = create_hostcache_id(hostname, port);
  /* If we can't create the entry id, fail */
  if(!entry_id)
    return rc;

  entry_len = strlen(entry_id);

  if(data->share)
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

  /* See if its already in our dns cache */
  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);

  /* free the allocated entry_id again */
  free(entry_id);

  /* See whether the returned entry is stale. Done before we release lock */
  if( remove_entry_if_stale(data, dns) )
    dns = NULL; /* the memory deallocation is being handled by the hash */

  if(dns) {
    dns->inuse++; /* we use it! */
    rc = CURLRESOLV_RESOLVED;
  }

  if(data->share)
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

  if(!dns) {
    /* The entry was not in the cache. Resolve it to IP address */

    Curl_addrinfo *addr;
    int respwait;

    /* Check what IP specifics the app has requested and if we can provide it.
     * If not, bail out. */
    if(!Curl_ipvalid(conn))
      return CURLRESOLV_ERROR;

    /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
       non-zero value indicating that we need to wait for the response to the
       resolve call */
    addr = Curl_getaddrinfo(conn,
#ifdef DEBUGBUILD
                            (data->set.str[STRING_DEVICE]
                             && !strcmp(data->set.str[STRING_DEVICE],
                                        "LocalHost"))?"localhost":
#endif
                            hostname, port, &respwait);

    if(!addr) {
      if(respwait) {
        /* the response to our resolve call will come asynchronously at
           a later time, good or bad */
        /* First, check that we haven't received the info by now */
        result = Curl_is_resolved(conn, &dns);
        if(result) /* error detected */
          return CURLRESOLV_ERROR;
        if(dns)
          rc = CURLRESOLV_RESOLVED; /* pointer provided */
        else
          rc = CURLRESOLV_PENDING; /* no info yet */
      }
    }
    else {
      if(data->share)
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

      /* we got a response, store it in the cache */
      dns = Curl_cache_addr(data, addr, hostname, port);

      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

      if(!dns)
        /* returned failure, bail out nicely */
        Curl_freeaddrinfo(addr);
      else
        rc = CURLRESOLV_RESOLVED;
    }
  }

  *entry = dns;

  return rc;
}
Esempio n. 4
0
int Curl_resolv(struct connectdata *conn,
                const char *hostname,
                int port,
                struct Curl_dns_entry **entry)
{
  char *entry_id = NULL;
  struct Curl_dns_entry *dns = NULL;
  size_t entry_len;
  int wait;
  struct SessionHandle *data = conn->data;
  CURLcode result;
  int rc;
  *entry = NULL;

#ifdef HAVE_SIGSETJMP
  /* this allows us to time-out from the name resolver, as the timeout
     will generate a signal and we will siglongjmp() from that here */
  if(!data->set.no_signal) {
    if (sigsetjmp(curl_jmpenv, 1)) {
      /* this is coming from a siglongjmp() */
      failf(data, "name lookup timed out");
      return CURLRESOLV_ERROR;
    }
  }
#endif

  /* Create an entry id, based upon the hostname and port */
  entry_id = create_hostcache_id(hostname, port);
  /* If we can't create the entry id, fail */
  if (!entry_id)
    return CURLRESOLV_ERROR;

  entry_len = strlen(entry_id);

  if(data->share)
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

  /* See if its already in our dns cache */
  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);

  if(data->share)
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

  /* free the allocated entry_id again */
  free(entry_id);

  /* See whether the returned entry is stale. Deliberately done after the
     locked block */
  if ( remove_entry_if_stale(data,dns) )
    dns = NULL; /* the memory deallocation is being handled by the hash */

  rc = CURLRESOLV_ERROR; /* default to failure */

  if (!dns) {
    /* The entry was not in the cache. Resolve it to IP address */

    Curl_addrinfo *addr;

    /* Check what IP specifics the app has requested and if we can provide it.
     * If not, bail out. */
    if(!Curl_ipvalid(data))
      return CURLRESOLV_ERROR;

    /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
       value indicating that we need to wait for the response to the resolve
       call */
    addr = Curl_getaddrinfo(conn, hostname, port, &wait);

    if (!addr) {
      if(wait) {
        /* the response to our resolve call will come asynchronously at
           a later time, good or bad */
        /* First, check that we haven't received the info by now */
        result = Curl_is_resolved(conn, &dns);
        if(result) /* error detected */
          return CURLRESOLV_ERROR;
        if(dns)
          rc = CURLRESOLV_RESOLVED; /* pointer provided */
        else
          rc = CURLRESOLV_PENDING; /* no info yet */
      }
    }
    else {
      if(data->share)
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

      /* we got a response, store it in the cache */
      dns = Curl_cache_addr(data, addr, hostname, port);

      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

      if(!dns)
        /* returned failure, bail out nicely */
        Curl_freeaddrinfo(addr);
      else
        rc = CURLRESOLV_RESOLVED;
    }
  }
  else {
    if(data->share)
      Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    dns->inuse++; /* we use it! */
    if(data->share)
      Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    rc = CURLRESOLV_RESOLVED;
  }

  *entry = dns;

  return rc;
}