#include "setup.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#ifdef WIN32
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef VMS
#include <in.h>
#include <inet.h>
#endif
#ifdef HAVE_SETJMP_H
#include <setjmp.h>
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif
#ifdef USE_LIBIDN
#include <idna.h>
#include <tld.h>
#include <stringprep.h>
#ifdef HAVE_IDN_FREE_H
#include <idn-free.h>
#else
void idn_free (void *ptr);
#endif
#ifndef HAVE_IDN_FREE
#define idn_free(x) (free)(x)
#endif
#endif
#include "urldata.h"
#include "netrc.h"
#include "formdata.h"
#include "base64.h"
#include "sslgen.h"
#include "hostip.h"
#include "transfer.h"
#include "sendf.h"
#include "progress.h"
#include "cookie.h"
#include "strequal.h"
#include "strerror.h"
#include "escape.h"
#include "strtok.h"
#include "share.h"
#include "content_encoding.h"
#include "http_digest.h"
#include "http_negotiate.h"
#include "select.h"
#include "multiif.h"
#include "easyif.h"
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
#include "tftp.h"
#include "http.h"
#include "file.h"
#include "ldap.h"
#include "ssh.h"
#include "url.h"
#include "connect.h"
#include "inet_ntop.h"
#include "http_ntlm.h"
#include "socks.h"
#include <ca-bundle.h>
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#include "inet_ntoa_r.h"
#endif
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#ifdef HAVE_KRB4
#include "krb4.h"
#endif
#include "memory.h"
#include "memdebug.h"
static long ConnectionKillOne(struct SessionHandle *data);
static bool ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis);
static long ConnectionStore(struct SessionHandle *data,
struct connectdata *conn);
static bool IsPipeliningPossible(struct SessionHandle *handle);
static bool IsPipeliningEnabled(struct SessionHandle *handle);
static void conn_free(struct connectdata *conn);
static void signalPipeClose(struct curl_llist *pipe);
static struct SessionHandle* gethandleathead(struct curl_llist *pipe);
#define MAX_PIPELINE_LENGTH 5
#define ZERO_NULL 0
#ifndef USE_ARES
#ifndef WIN32
#ifdef HAVE_SIGSETJMP
extern sigjmp_buf curl_jmpenv;
#endif
#ifdef SIGALRM
static
RETSIGTYPE alarmfunc(int sig)
{
(void)sig;
#ifdef HAVE_SIGSETJMP
siglongjmp(curl_jmpenv, 1);
#endif
return;
}
#endif
#endif
#endif
#ifdef CURL_DISABLE_VERBOSE_STRINGS
#define verboseconnect(x) do { } while (0)
#endif
void Curl_safefree(void *ptr)
{
if(ptr)
free(ptr);
}
static void close_connections(struct SessionHandle *data)
{
while(-1 != ConnectionKillOne(data))
;
}
CURLcode Curl_close(struct SessionHandle *data)
{
struct Curl_multi *m = data->multi;
#ifdef CURLDEBUG
if(data->state.is_in_pipeline)
fprintf(stderr, "CLOSED when in pipeline!\n");
if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) {
struct conncache *c = data->state.connc;
int i;
struct curl_llist *pipe;
struct curl_llist_element *curr;
struct connectdata *connptr;
for(i=0; i< c->num; i++) {
connptr = c->connects[i];
if(!connptr)
continue;
pipe = connptr->send_pipe;
if(pipe) {
for (curr = pipe->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"MAJOR problem we %p are still in send pipe for %p done %d\n",
data, connptr, connptr->bits.done);
}
}
}
pipe = connptr->recv_pipe;
if(pipe) {
for (curr = pipe->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"MAJOR problem we %p are still in recv pipe for %p done %d\n",
data, connptr, connptr->bits.done);
}
}
}
}
}
#endif
if(m)
Curl_multi_rmeasy(data->multi, data);
data->magic = 0;
if(data->state.connc) {
if(data->state.connc->type == CONNCACHE_PRIVATE) {
close_connections(data);
Curl_rm_connc(data->state.connc);
}
}
if(data->state.shared_conn) {
data->state.closed = TRUE;
return CURLE_OK;
}
if ( ! (data->share && data->share->hostcache) ) {
if ( !Curl_global_host_cache_use(data)) {
Curl_hash_destroy(data->dns.hostcache);
}
}
if(data->reqdata.rangestringalloc)
free(data->reqdata.range);
Curl_safefree(data->reqdata.pathbuffer);
Curl_safefree(data->reqdata.proto.generic);
Curl_ssl_close_all(data);
Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch);
if(data->change.referer_alloc)
free(data->change.referer);
if(data->change.url_alloc)
free(data->change.url);
Curl_safefree(data->state.headerbuff);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
if(data->set.cookiejar) {
if(data->change.cookielist) {
Curl_cookie_loadfiles(data);
}
if(Curl_cookie_output(data->cookies, data->set.cookiejar))
infof(data, "WARNING: failed to save cookies in %s\n",
data->set.cookiejar);
}
else {
if(data->change.cookielist)
curl_slist_free_all(data->change.cookielist);
}
if( !data->share || (data->cookies != data->share->cookies) ) {
Curl_cookie_cleanup(data->cookies);
}
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
#endif
Curl_digest_cleanup(data);
Curl_safefree(data->info.contenttype);
ares_destroy(data->state.areschannel);
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
if (data->inbound_cd != (iconv_t)-1) {
iconv_close(data->inbound_cd);
}
if (data->outbound_cd != (iconv_t)-1) {
iconv_close(data->outbound_cd);
}
if (data->utf8_cd != (iconv_t)-1) {
iconv_close(data->utf8_cd);
}
#endif
if (data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
data->share->dirty--;
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
free(data);
return CURLE_OK;
}
struct conncache *Curl_mk_connc(int type,
long amount)
{
struct conncache *c;
long default_amount;
if (type == CONNCACHE_PRIVATE) {
default_amount = (amount < 0) ? 5 : amount;
}
else {
default_amount = (amount < 0) ? 10 : amount;
}
c= calloc(sizeof(struct conncache), 1);
if(!c)
return NULL;
if ((size_t)(default_amount) > ((size_t)-1) / sizeof(struct connectdata *))
default_amount = ((size_t)-1) / sizeof(struct connectdata *);
c->connects = calloc(sizeof(struct connectdata *), (size_t)default_amount);
if(!c->connects) {
free(c);
return NULL;
}
c->num = default_amount;
return c;
}
CURLcode Curl_ch_connc(struct SessionHandle *data,
struct conncache *c,
long newamount)
{
long i;
struct connectdata **newptr;
if(newamount < 1)
newamount = 1;
if(!c) {
data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, newamount);
if(!data->state.connc)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
if(newamount < c->num) {
for(i=newamount; i< c->num; i++)
Curl_disconnect(c->connects[i]);
if(data->state.lastconnect <= newamount)
data->state.lastconnect = -1;
}
if(newamount > 0) {
newptr= (struct connectdata **)
realloc(c->connects, sizeof(struct connectdata *) * newamount);
if(!newptr)
return CURLE_OUT_OF_MEMORY;
for(i=c->num; i<newamount; i++)
newptr[i] = NULL;
c->connects = newptr;
c->num = newamount;
}
return CURLE_OK;
}
void Curl_rm_connc(struct conncache *c)
{
if(c->connects) {
int i;
for(i = 0; i < c->num; ++i)
conn_free(c->connects[i]);
free(c->connects);
}
free(c);
}
CURLcode Curl_open(struct SessionHandle **curl)
{
CURLcode res = CURLE_OK;
struct SessionHandle *data;
#ifdef USE_ARES
int status;
#endif
data = (struct SessionHandle *)calloc(1, sizeof(struct SessionHandle));
if(!data) {
DEBUGF(fprintf(stderr, "Error: calloc of SessionHandle failed\n"));
return CURLE_OUT_OF_MEMORY;
}
data->magic = CURLEASY_MAGIC_NUMBER;
#ifdef USE_ARES
if ((status = ares_init(&data->state.areschannel)) != ARES_SUCCESS) {
DEBUGF(fprintf(stderr, "Error: ares_init failed\n"));
free(data);
if (status == ARES_ENOMEM)
return CURLE_OUT_OF_MEMORY;
else
return CURLE_FAILED_INIT;
}
#endif
data->state.headerbuff=(char*)malloc(HEADERSIZE);
if(!data->state.headerbuff) {
DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
res = CURLE_OUT_OF_MEMORY;
}
else {
data->state.headersize=HEADERSIZE;
data->set.out = stdout;
data->set.in = stdin;
data->set.err = stderr;
data->set.fwrite = (curl_write_callback)fwrite;
data->set.fread = (curl_read_callback)fread;
data->set.convfromnetwork = (curl_conv_callback)ZERO_NULL;
data->set.convtonetwork = (curl_conv_callback)ZERO_NULL;
data->set.convfromutf8 = (curl_conv_callback)ZERO_NULL;
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
data->outbound_cd = (iconv_t)-1;
data->inbound_cd = (iconv_t)-1;
data->utf8_cd = (iconv_t)-1;
#endif
data->set.infilesize = -1;
data->set.postfieldsize = -1;
data->set.maxredirs = -1;
data->state.current_speed = -1;
data->set.httpreq = HTTPREQ_GET;
data->set.ftp_use_epsv = TRUE;
data->set.ftp_use_eprt = TRUE;
data->set.ftp_filemethod = FTPFILE_MULTICWD;
data->set.dns_cache_timeout = 60;
data->set.hide_progress = TRUE;
data->progress.flags |= PGRS_HIDE;
data->set.ssl.numsessions = 5;
data->set.proxyport = 1080;
data->set.proxytype = CURLPROXY_HTTP;
data->set.httpauth = CURLAUTH_BASIC;
data->set.proxyauth = CURLAUTH_BASIC;
data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT;
data->state.lastconnect = -1;
Curl_easy_initHandleData(data);
data->set.ssl.verifypeer = TRUE;
data->set.ssl.verifyhost = 2;
data->set.ssl.sessionid = TRUE;
#ifdef CURL_CA_BUNDLE
data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE;
#endif
}
if(res) {
ares_destroy(data->state.areschannel);
if(data->state.headerbuff)
free(data->state.headerbuff);
free(data);
data = NULL;
}
else
*curl = data;
return res;
}
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
va_list param)
{
char *argptr;
CURLcode result = CURLE_OK;
switch(option) {
case CURLOPT_DNS_CACHE_TIMEOUT:
data->set.dns_cache_timeout = va_arg(param, int);
break;
case CURLOPT_DNS_USE_GLOBAL_CACHE:
{
int use_cache = va_arg(param, int);
if (use_cache) {
Curl_global_host_cache_init();
}
data->set.global_dns_cache = (bool)(0 != use_cache);
}
break;
case CURLOPT_SSL_CIPHER_LIST:
data->set.ssl.cipher_list = va_arg(param, char *);
break;
case CURLOPT_RANDOM_FILE:
data->set.ssl.random_file = va_arg(param, char *);
break;
case CURLOPT_EGDSOCKET:
data->set.ssl.egdsocket = va_arg(param, char *);
break;
case CURLOPT_MAXCONNECTS:
result = Curl_ch_connc(data, data->state.connc, va_arg(param, long));
break;
case CURLOPT_FORBID_REUSE:
data->set.reuse_forbid = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FRESH_CONNECT:
data->set.reuse_fresh = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_VERBOSE:
data->set.verbose = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_HEADER:
data->set.include_header = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_NOPROGRESS:
data->set.hide_progress = (bool)(0 != va_arg(param, long));
if(data->set.hide_progress)
data->progress.flags |= PGRS_HIDE;
else
data->progress.flags &= ~PGRS_HIDE;
break;
case CURLOPT_NOBODY:
data->set.opt_no_body = (bool)(0 != va_arg(param, long));
if(data->set.opt_no_body)
data->set.httpreq = HTTPREQ_HEAD;
break;
case CURLOPT_FAILONERROR:
data->set.http_fail_on_error = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_UPLOAD:
case CURLOPT_PUT:
data->set.upload = (bool)(0 != va_arg(param, long));
if(data->set.upload)
data->set.httpreq = HTTPREQ_PUT;
break;
case CURLOPT_FILETIME:
data->set.get_filetime = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_CREATE_MISSING_DIRS:
data->set.ftp_create_missing_dirs = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_RESPONSE_TIMEOUT:
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
break;
case CURLOPT_FTPLISTONLY:
data->set.ftp_list_only = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTPAPPEND:
data->set.ftp_append = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_FILEMETHOD:
data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
break;
case CURLOPT_NETRC:
data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
break;
case CURLOPT_NETRC_FILE:
data->set.netrc_file = va_arg(param, char *);
break;
case CURLOPT_TRANSFERTEXT:
data->set.prefer_ascii = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_TIMECONDITION:
data->set.timecondition = (curl_TimeCond)va_arg(param, long);
break;
case CURLOPT_TIMEVALUE:
data->set.timevalue = (time_t)va_arg(param, long);
break;
case CURLOPT_SSLVERSION:
data->set.ssl.version = va_arg(param, long);
break;
#ifndef CURL_DISABLE_HTTP
case CURLOPT_AUTOREFERER:
data->set.http_auto_referer = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_ENCODING:
data->set.encoding = va_arg(param, char *);
if(data->set.encoding && !*data->set.encoding)
data->set.encoding = (char*)ALL_CONTENT_ENCODINGS;
break;
case CURLOPT_FOLLOWLOCATION:
data->set.http_follow_location = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_UNRESTRICTED_AUTH:
data->set.http_disable_hostname_check_before_authentication =
(bool)(0 != va_arg(param, long));
break;
case CURLOPT_MAXREDIRS:
data->set.maxredirs = va_arg(param, long);
break;
case CURLOPT_POST:
if(va_arg(param, long)) {
data->set.httpreq = HTTPREQ_POST;
data->set.opt_no_body = FALSE;
}
else
data->set.httpreq = HTTPREQ_GET;
break;
case CURLOPT_POSTFIELDS:
data->set.postfields = va_arg(param, char *);
data->set.httpreq = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDSIZE:
data->set.postfieldsize = va_arg(param, long);
break;
case CURLOPT_POSTFIELDSIZE_LARGE:
data->set.postfieldsize = va_arg(param, curl_off_t);
break;
case CURLOPT_HTTPPOST:
data->set.httppost = va_arg(param, struct curl_httppost *);
data->set.httpreq = HTTPREQ_POST_FORM;
data->set.opt_no_body = FALSE;
break;
case CURLOPT_REFERER:
if(data->change.referer_alloc) {
free(data->change.referer);
data->change.referer_alloc = FALSE;
}
data->set.set_referer = va_arg(param, char *);
data->change.referer = data->set.set_referer;
break;
case CURLOPT_USERAGENT:
data->set.useragent = va_arg(param, char *);
break;
case CURLOPT_HTTPHEADER:
data->set.headers = va_arg(param, struct curl_slist *);
break;
case CURLOPT_HTTP200ALIASES:
data->set.http200aliases = va_arg(param, struct curl_slist *);
break;
#if !defined(CURL_DISABLE_COOKIES)
case CURLOPT_COOKIE:
data->set.cookie = va_arg(param, char *);
break;
case CURLOPT_COOKIEFILE:
argptr = (char *)va_arg(param, void *);
if(argptr) {
struct curl_slist *cl;
cl = curl_slist_append(data->change.cookielist, argptr);
if(!cl)
return CURLE_OUT_OF_MEMORY;
data->change.cookielist = cl;
}
break;
case CURLOPT_COOKIEJAR:
data->set.cookiejar = (char *)va_arg(param, void *);
data->cookies = Curl_cookie_init(data, NULL, data->cookies,
data->set.cookiesession);
break;
case CURLOPT_COOKIESESSION:
data->set.cookiesession = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_COOKIELIST:
argptr = va_arg(param, char *);
if(argptr == NULL)
break;
if(strequal(argptr, "ALL")) {
Curl_cookie_clearall(data->cookies);
break;
}
else if(strequal(argptr, "SESS")) {
Curl_cookie_clearsess(data->cookies);
break;
}
if(!data->cookies)
data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
argptr = strdup(argptr);
if(!argptr) {
result = CURLE_OUT_OF_MEMORY;
break;
}
if(checkprefix("Set-Cookie:", argptr))
Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);
else
Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
free(argptr);
break;
#endif
case CURLOPT_HTTPGET:
if(va_arg(param, long)) {
data->set.httpreq = HTTPREQ_GET;
data->set.upload = FALSE;
data->set.opt_no_body = FALSE;
}
break;
case CURLOPT_HTTP_VERSION:
data->set.httpversion = va_arg(param, long);
break;
case CURLOPT_HTTPPROXYTUNNEL:
data->set.tunnel_thru_httpproxy = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_CUSTOMREQUEST:
data->set.customrequest = va_arg(param, char *);
break;
case CURLOPT_PROXYPORT:
data->set.proxyport = va_arg(param, long);
break;
case CURLOPT_HTTPAUTH:
{
long auth = va_arg(param, long);
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM;
#endif
#ifndef HAVE_GSSAPI
auth &= ~CURLAUTH_GSSNEGOTIATE;
#endif
if(!auth)
return CURLE_FAILED_INIT;
data->set.httpauth = auth;
}
break;
case CURLOPT_PROXYAUTH:
{
long auth = va_arg(param, long);
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM;
#endif
#ifndef HAVE_GSSAPI
auth &= ~CURLAUTH_GSSNEGOTIATE;
#endif
if(!auth)
return CURLE_FAILED_INIT;
data->set.proxyauth = auth;
}
break;
#endif
case CURLOPT_PROXY:
data->set.proxy = va_arg(param, char *);
break;
case CURLOPT_WRITEHEADER:
data->set.writeheader = (void *)va_arg(param, void *);
break;
case CURLOPT_ERRORBUFFER:
data->set.errorbuffer = va_arg(param, char *);
break;
case CURLOPT_FILE:
data->set.out = va_arg(param, FILE *);
break;
case CURLOPT_FTPPORT:
data->set.ftpport = va_arg(param, char *);
data->set.ftp_use_port = (bool)(NULL != data->set.ftpport);
break;
case CURLOPT_FTP_USE_EPRT:
data->set.ftp_use_eprt = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_USE_EPSV:
data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_SSL_CCC:
data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long);
break;
case CURLOPT_FTP_SKIP_PASV_IP:
data->set.ftp_skip_ip = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_INFILE:
data->set.in = va_arg(param, FILE *);
break;
case CURLOPT_INFILESIZE:
data->set.infilesize = va_arg(param, long);
break;
case CURLOPT_INFILESIZE_LARGE:
data->set.infilesize = va_arg(param, curl_off_t);
break;
case CURLOPT_LOW_SPEED_LIMIT:
data->set.low_speed_limit=va_arg(param, long);
break;
case CURLOPT_MAX_SEND_SPEED_LARGE:
data->set.max_send_speed=va_arg(param, curl_off_t);
break;
case CURLOPT_MAX_RECV_SPEED_LARGE:
data->set.max_recv_speed=va_arg(param, curl_off_t);
break;
case CURLOPT_LOW_SPEED_TIME:
data->set.low_speed_time=va_arg(param, long);
break;
case CURLOPT_URL:
if(data->change.url_alloc) {
free(data->change.url);
data->change.url_alloc=FALSE;
}
data->set.set_url = va_arg(param, char *);
data->change.url = data->set.set_url;
data->change.url_changed = TRUE;
break;
case CURLOPT_PORT:
data->set.use_port = va_arg(param, long);
break;
case CURLOPT_TIMEOUT:
data->set.timeout = va_arg(param, long) * 1000L;
break;
case CURLOPT_TIMEOUT_MS:
data->set.timeout = va_arg(param, long);
break;
case CURLOPT_CONNECTTIMEOUT:
data->set.connecttimeout = va_arg(param, long) * 1000L;
break;
case CURLOPT_CONNECTTIMEOUT_MS:
data->set.connecttimeout = va_arg(param, long);
break;
case CURLOPT_USERPWD:
data->set.userpwd = va_arg(param, char *);
break;
case CURLOPT_POSTQUOTE:
data->set.postquote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_PREQUOTE:
data->set.prequote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_QUOTE:
data->set.quote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_PROGRESSFUNCTION:
data->set.fprogress = va_arg(param, curl_progress_callback);
if(data->set.fprogress)
data->progress.callback = TRUE;
else
data->progress.callback = FALSE;
break;
case CURLOPT_PROGRESSDATA:
data->set.progress_client = va_arg(param, void *);
break;
case CURLOPT_PROXYUSERPWD:
data->set.proxyuserpwd = va_arg(param, char *);
break;
case CURLOPT_RANGE:
data->set.set_range = va_arg(param, char *);
break;
case CURLOPT_RESUME_FROM:
data->set.set_resume_from = va_arg(param, long);
break;
case CURLOPT_RESUME_FROM_LARGE:
data->set.set_resume_from = va_arg(param, curl_off_t);
break;
case CURLOPT_DEBUGFUNCTION:
data->set.fdebug = va_arg(param, curl_debug_callback);
break;
case CURLOPT_DEBUGDATA:
data->set.debugdata = va_arg(param, void *);
break;
case CURLOPT_STDERR:
data->set.err = va_arg(param, FILE *);
if(!data->set.err)
data->set.err = stderr;
break;
case CURLOPT_HEADERFUNCTION:
data->set.fwrite_header = va_arg(param, curl_write_callback);
break;
case CURLOPT_WRITEFUNCTION:
data->set.fwrite = va_arg(param, curl_write_callback);
if(!data->set.fwrite)
data->set.fwrite = (curl_write_callback)fwrite;
break;
case CURLOPT_READFUNCTION:
data->set.fread = va_arg(param, curl_read_callback);
if(!data->set.fread)
data->set.fread = (curl_read_callback)fread;
break;
case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
data->set.convfromnetwork = va_arg(param, curl_conv_callback);
break;
case CURLOPT_CONV_TO_NETWORK_FUNCTION:
data->set.convtonetwork = va_arg(param, curl_conv_callback);
break;
case CURLOPT_CONV_FROM_UTF8_FUNCTION:
data->set.convfromutf8 = va_arg(param, curl_conv_callback);
break;
case CURLOPT_IOCTLFUNCTION:
data->set.ioctl = va_arg(param, curl_ioctl_callback);
break;
case CURLOPT_IOCTLDATA:
data->set.ioctl_client = va_arg(param, void *);
break;
case CURLOPT_SSLCERT:
data->set.cert = va_arg(param, char *);
break;
case CURLOPT_SSLCERTTYPE:
data->set.cert_type = va_arg(param, char *);
break;
case CURLOPT_SSLKEY:
data->set.key = va_arg(param, char *);
break;
case CURLOPT_SSLKEYTYPE:
data->set.key_type = va_arg(param, char *);
break;
case CURLOPT_SSLKEYPASSWD:
data->set.key_passwd = va_arg(param, char *);
break;
case CURLOPT_SSLENGINE:
argptr = va_arg(param, char *);
if (argptr && argptr[0])
result = Curl_ssl_set_engine(data, argptr);
break;
case CURLOPT_SSLENGINE_DEFAULT:
result = Curl_ssl_set_engine_default(data);
break;
case CURLOPT_CRLF:
data->set.crlf = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_INTERFACE:
data->set.device = va_arg(param, char *);
break;
case CURLOPT_LOCALPORT:
data->set.localport = (unsigned short) va_arg(param, long);
break;
case CURLOPT_LOCALPORTRANGE:
data->set.localportrange = (int) va_arg(param, long);
break;
case CURLOPT_KRB4LEVEL:
data->set.krb4_level = va_arg(param, char *);
data->set.krb4 = (bool)(NULL != data->set.krb4_level);
break;
case CURLOPT_SSL_VERIFYPEER:
data->set.ssl.verifypeer = va_arg(param, long);
break;
case CURLOPT_SSL_VERIFYHOST:
data->set.ssl.verifyhost = va_arg(param, long);
break;
case CURLOPT_SSL_CTX_FUNCTION:
data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
break;
case CURLOPT_SSL_CTX_DATA:
data->set.ssl.fsslctxp = va_arg(param, void *);
break;
case CURLOPT_CAINFO:
data->set.ssl.CAfile = va_arg(param, char *);
break;
case CURLOPT_CAPATH:
data->set.ssl.CApath = va_arg(param, char *);
break;
case CURLOPT_TELNETOPTIONS:
data->set.telnet_options = va_arg(param, struct curl_slist *);
break;
case CURLOPT_BUFFERSIZE:
data->set.buffer_size = va_arg(param, long);
if((data->set.buffer_size> (BUFSIZE -1 )) ||
(data->set.buffer_size < 1))
data->set.buffer_size = 0;
break;
case CURLOPT_NOSIGNAL:
data->set.no_signal = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_SHARE:
{
struct Curl_share *set;
set = va_arg(param, struct Curl_share *);
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
if(data->dns.hostcachetype == HCACHE_SHARED) {
data->dns.hostcache = NULL;
data->dns.hostcachetype = HCACHE_NONE;
}
if(data->share->cookies == data->cookies)
data->cookies = NULL;
data->share->dirty--;
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
data->share = NULL;
}
data->share = set;
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
data->share->dirty++;
if(data->share->hostcache) {
if(data->dns.hostcachetype == HCACHE_PRIVATE)
Curl_hash_destroy(data->dns.hostcache);
data->dns.hostcache = data->share->hostcache;
data->dns.hostcachetype = HCACHE_SHARED;
}
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->share->cookies) {
if (data->cookies)
Curl_cookie_cleanup(data->cookies);
data->cookies = data->share->cookies;
}
#endif
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(!data->cookies)
data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE );
#endif
}
break;
case CURLOPT_PROXYTYPE:
data->set.proxytype = (curl_proxytype)va_arg(param, long);
break;
case CURLOPT_PRIVATE:
data->set.private_data = va_arg(param, char *);
break;
case CURLOPT_MAXFILESIZE:
data->set.max_filesize = va_arg(param, long);
break;
case CURLOPT_FTP_SSL:
data->set.ftp_ssl = (curl_ftpssl)va_arg(param, long);
break;
case CURLOPT_FTPSSLAUTH:
data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
break;
case CURLOPT_IPRESOLVE:
data->set.ip_version = va_arg(param, long);
break;
case CURLOPT_MAXFILESIZE_LARGE:
data->set.max_filesize = va_arg(param, curl_off_t);
break;
case CURLOPT_TCP_NODELAY:
data->set.tcp_nodelay = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_ACCOUNT:
data->set.ftp_account = va_arg(param, char *);
break;
case CURLOPT_IGNORE_CONTENT_LENGTH:
data->set.ignorecl = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_CONNECT_ONLY:
data->set.connect_only = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
data->set.ftp_alternative_to_user = va_arg(param, char *);
break;
case CURLOPT_SOCKOPTFUNCTION:
data->set.fsockopt = va_arg(param, curl_sockopt_callback);
break;
case CURLOPT_SOCKOPTDATA:
data->set.sockopt_client = va_arg(param, void *);
break;
case CURLOPT_SSL_SESSIONID_CACHE:
data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
data->set.ssh_public_key = va_arg(param, char *);
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
data->set.ssh_private_key = va_arg(param, char *);
break;
case CURLOPT_HTTP_TRANSFER_DECODING:
data->set.http_te_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_HTTP_CONTENT_DECODING:
data->set.http_ce_skip = (bool)(0 == va_arg(param, long));
break;
default:
result = CURLE_FAILED_INIT;
break;
}
return result;
}
static void conn_free(struct connectdata *conn)
{
if (!conn)
return;
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
sclose(conn->sock[SECONDARYSOCKET]);
if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
sclose(conn->sock[FIRSTSOCKET]);
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
Curl_safefree(conn->proxyuser);
Curl_safefree(conn->proxypasswd);
Curl_safefree(conn->allocptr.proxyuserpwd);
Curl_safefree(conn->allocptr.uagent);
Curl_safefree(conn->allocptr.userpwd);
Curl_safefree(conn->allocptr.accept_encoding);
Curl_safefree(conn->allocptr.rangeline);
Curl_safefree(conn->allocptr.ref);
Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost);
Curl_safefree(conn->ip_addr_str);
Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc);
Curl_safefree(conn->proxy.rawalloc);
Curl_safefree(conn->master_buffer);
Curl_llist_destroy(conn->send_pipe, NULL);
Curl_llist_destroy(conn->recv_pipe, NULL);
#if defined(USE_ARES)
Curl_safefree(conn->async.hostname);
Curl_safefree(conn->async.os_specific);
#elif defined(CURLRES_THREADED)
Curl_destroy_thread_data(&conn->async);
#endif
Curl_ssl_close(conn);
Curl_free_ssl_config(&conn->ssl_config);
free(conn);
}
CURLcode Curl_disconnect(struct connectdata *conn)
{
struct SessionHandle *data;
if(!conn)
return CURLE_OK;
data = conn->data;
if(!data) {
DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
return CURLE_OK;
}
#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
Curl_hash_apply(data->hostcache,
NULL, Curl_scan_cache_used);
#endif
Curl_expire(data, 0);
Curl_hostcache_prune(data);
if((conn->ntlm.state != NTLMSTATE_NONE) ||
(conn->proxyntlm.state != NTLMSTATE_NONE)) {
data->state.authhost.done = FALSE;
data->state.authhost.picked =
data->state.authhost.want;
data->state.authproxy.done = FALSE;
data->state.authproxy.picked =
data->state.authproxy.want;
data->state.authproblem = FALSE;
Curl_ntlm_cleanup(conn);
}
if(conn->curl_disconnect)
conn->curl_disconnect(conn);
if(-1 != conn->connectindex) {
infof(data, "Closing connection #%ld\n", conn->connectindex);
if(data->state.connc)
data->state.connc->connects[conn->connectindex] = NULL;
}
#ifdef USE_LIBIDN
if(conn->host.encalloc)
idn_free(conn->host.encalloc);
if(conn->proxy.encalloc)
idn_free(conn->proxy.encalloc);
#endif
Curl_ssl_close(conn);
if (IsPipeliningEnabled(data)) {
signalPipeClose(conn->send_pipe);
signalPipeClose(conn->recv_pipe);
}
conn_free(conn);
return CURLE_OK;
}
static bool SocketIsDead(curl_socket_t sock)
{
int sval;
bool ret_val = TRUE;
sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
if(sval == 0)
ret_val = FALSE;
return ret_val;
}
static bool IsPipeliningPossible(struct SessionHandle *handle)
{
if (handle->multi && Curl_multi_canPipeline(handle->multi) &&
(handle->set.httpreq == HTTPREQ_GET ||
handle->set.httpreq == HTTPREQ_HEAD) &&
handle->set.httpversion != CURL_HTTP_VERSION_1_0)
return TRUE;
return FALSE;
}
static bool IsPipeliningEnabled(struct SessionHandle *handle)
{
if (handle->multi && Curl_multi_canPipeline(handle->multi))
return TRUE;
return FALSE;
}
CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
struct curl_llist *pipe)
{
#ifdef CURLDEBUG
if(!IsPipeliningPossible(data)) {
if(pipe->head)
infof(data, "PIPE when no PIPE supposed!\n");
}
#endif
if (!Curl_llist_insert_next(pipe, pipe->tail, data))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
struct curl_llist *pipe)
{
struct curl_llist_element *curr;
curr = pipe->head;
while (curr) {
if (curr->ptr == handle) {
Curl_llist_remove(pipe, curr, NULL);
return 1;
}
curr = curr->next;
}
return 0;
}
#if 0
static void Curl_printPipeline(struct curl_llist *pipe)
{
struct curl_llist_element *curr;
curr = pipe->head;
while (curr) {
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
infof(data, "Handle in pipeline: %s\n", data->reqdata.path);
curr = curr->next;
}
}
#endif
bool Curl_isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipe)
{
struct curl_llist_element *curr = pipe->head;
if (curr) {
return (bool)(curr->ptr == handle);
}
return FALSE;
}
static struct SessionHandle* gethandleathead(struct curl_llist *pipe)
{
struct curl_llist_element *curr = pipe->head;
if (curr) {
return (struct SessionHandle *) curr->ptr;
}
return NULL;
}
static void signalPipeClose(struct curl_llist *pipe)
{
struct curl_llist_element *curr;
if (!pipe)
return;
curr = pipe->head;
while (curr) {
struct curl_llist_element *next = curr->next;
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
#ifdef CURLDEBUG
if(data->magic != CURLEASY_MAGIC_NUMBER) {
infof(data, "signalPipeClose() found BAAD easy handle\n");
}
#endif
data->state.pipe_broke = TRUE;
Curl_multi_handlePipeBreak(data);
Curl_llist_remove(pipe, curr, NULL);
curr = next;
}
}
static bool
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis)
{
long i;
struct connectdata *check;
bool canPipeline = IsPipeliningPossible(data);
for(i=0; i< data->state.connc->num; i++) {
bool match = FALSE;
size_t pipeLen = 0;
check = data->state.connc->connects[i];
if(!check)
continue;
pipeLen = check->send_pipe->size + check->recv_pipe->size;
if (check->connectindex == -1) {
check->connectindex = i;
}
if(pipeLen > 0 && !canPipeline) {
continue;
}
#ifdef CURLRES_ASYNCH
if (!check->ip_addr_str) {
infof(data,
"Connection #%ld hasn't finished name resolve, can't reuse\n",
check->connectindex);
continue;
}
#endif
if ((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
infof(data, "Connection #%ld isn't open enough, can't reuse\n",
check->connectindex);
#ifdef CURLDEBUG
if (check->recv_pipe->size > 0) {
infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connectindex);
}
#endif
continue;
}
if (pipeLen >= MAX_PIPELINE_LENGTH) {
infof(data, "Connection #%ld has its pipeline full, can't reuse\n",
check->connectindex);
continue;
}
if (canPipeline) {
struct SessionHandle* sh = gethandleathead(check->send_pipe);
struct SessionHandle* rh = gethandleathead(check->recv_pipe);
if (sh) {
if(!IsPipeliningPossible(sh))
continue;
}
else if (rh) {
if(!IsPipeliningPossible(rh))
continue;
}
}
if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
continue;
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
continue;
if(strequal(needle->protostr, check->protostr) &&
strequal(needle->host.name, check->host.name) &&
(needle->remote_port == check->remote_port) ) {
if(needle->protocol & PROT_SSL) {
if(!Curl_ssl_config_matches(&needle->ssl_config,
&check->ssl_config)) {
infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connectindex );
continue;
}
}
if((needle->protocol & PROT_FTP) ||
((needle->protocol & PROT_HTTP) &&
(data->state.authhost.want==CURLAUTH_NTLM))) {
if(!strequal(needle->user, check->user) ||
!strequal(needle->passwd, check->passwd)) {
continue;
}
}
match = TRUE;
}
}
else {
if(check->bits.proxy &&
(needle->proxytype == check->proxytype) &&
strequal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
match = TRUE;
}
}
if(match) {
if (!IsPipeliningEnabled(data)) {
bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
if(dead) {
check->data = data;
infof(data, "Connection #%d seems to be dead!\n", i);
Curl_disconnect(check);
data->state.connc->connects[i]=NULL;
return FALSE;
}
}
check->inuse = TRUE;
if (canPipeline) {
check->is_in_pipeline = TRUE;
}
*usethis = check;
return TRUE;
}
}
return FALSE;
}
static long
ConnectionKillOne(struct SessionHandle *data)
{
long i;
struct connectdata *conn;
long highscore=-1;
long connindex=-1;
long score;
struct timeval now;
now = Curl_tvnow();
for(i=0; data->state.connc && (i< data->state.connc->num); i++) {
conn = data->state.connc->connects[i];
if(!conn || conn->inuse)
continue;
score = Curl_tvdiff(now, conn->now);
if(score > highscore) {
highscore = score;
connindex = i;
}
}
if(connindex >= 0) {
conn = data->state.connc->connects[connindex];
conn->data = data;
(void)Curl_disconnect(conn);
data->state.connc->connects[connindex] = NULL;
}
return connindex;
}
static void
ConnectionDone(struct connectdata *conn)
{
conn->inuse = FALSE;
if (!conn->send_pipe && !conn->recv_pipe)
conn->is_in_pipeline = FALSE;
}
static long
ConnectionStore(struct SessionHandle *data,
struct connectdata *conn)
{
long i;
for(i=0; i< data->state.connc->num; i++) {
if(!data->state.connc->connects[i])
break;
}
if(i == data->state.connc->num) {
i = ConnectionKillOne(data);
if(-1 != i)
infof(data, "Connection (#%d) was killed to make room (holds %d)\n",
i, data->state.connc->num);
else
infof(data, "This connection did not fit in the connection cache\n");
}
conn->connectindex = i;
conn->inuse = TRUE;
if(-1 != i) {
data->state.connc->connects[i] = conn;
conn->data = data;
}
return i;
}
static CURLcode ConnectPlease(struct SessionHandle *data,
struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *connected)
{
CURLcode result;
Curl_addrinfo *addr;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
char *hostname = conn->bits.proxy?conn->proxy.name:conn->host.name;
infof(data, "About to connect() to %s%s port %d (#%d)\n",
conn->bits.proxy?"proxy ":"",
hostname, conn->port, conn->connectindex);
#endif
result= Curl_connecthost(conn,
hostaddr,
&conn->sock[FIRSTSOCKET],
&addr,
connected);
if(CURLE_OK == result) {
conn->dns_entry = hostaddr;
conn->ip_addr = addr;
Curl_store_ip_addr(conn);
switch(data->set.proxytype) {
case CURLPROXY_SOCKS5:
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn);
break;
case CURLPROXY_HTTP:
break;
case CURLPROXY_SOCKS4:
result = Curl_SOCKS4(conn->proxyuser, conn->host.name, conn->remote_port,
FIRSTSOCKET, conn);
break;
default:
failf(data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
break;
}
}
if(result)
*connected = FALSE;
return result;
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void verboseconnect(struct connectdata *conn)
{
infof(conn->data, "Connected to %s (%s) port %d (#%d)\n",
conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
conn->ip_addr_str, conn->port, conn->connectindex);
}
#endif
int Curl_protocol_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
if(conn->curl_proto_getsock)
return conn->curl_proto_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
int Curl_doing_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
if(conn && conn->curl_doing_getsock)
return conn->curl_doing_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
CURLcode Curl_protocol_connecting(struct connectdata *conn,
bool *done)
{
CURLcode result=CURLE_OK;
if(conn && conn->curl_connecting) {
*done = FALSE;
result = conn->curl_connecting(conn, done);
}
else
*done = TRUE;
return result;
}
CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
{
CURLcode result=CURLE_OK;
if(conn && conn->curl_doing) {
*done = FALSE;
result = conn->curl_doing(conn, done);
}
else
*done = TRUE;
return result;
}
CURLcode Curl_protocol_connect(struct connectdata *conn,
bool *protocol_done)
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
*protocol_done = FALSE;
if(conn->bits.tcpconnect && conn->bits.protoconnstart) {
if(!conn->curl_connecting)
*protocol_done = TRUE;
return CURLE_OK;
}
if(!conn->bits.tcpconnect) {
Curl_pgrsTime(data, TIMER_CONNECT);
if(data->set.verbose)
verboseconnect(conn);
}
if(!conn->bits.protoconnstart) {
if(conn->curl_connect) {
conn->now = Curl_tvnow();
result = conn->curl_connect(conn, protocol_done);
}
else
*protocol_done = TRUE;
if (!result)
conn->bits.protoconnstart = TRUE;
}
return result;
}
#ifdef USE_LIBIDN
static bool is_ASCII_name(const char *hostname)
{
const unsigned char *ch = (const unsigned char*)hostname;
while (*ch) {
if (*ch++ & 0x80)
return FALSE;
}
return TRUE;
}
static bool tld_check_name(struct SessionHandle *data,
const char *ace_hostname)
{
size_t err_pos;
char *uc_name = NULL;
int rc;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
char *tld_errmsg = (char *)"<no msg>";
#else
(void)data;
#endif
rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
if (rc != IDNA_SUCCESS)
return (FALSE);
rc = tld_check_lz(uc_name, &err_pos, NULL);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
#ifdef HAVE_TLD_STRERROR
if (rc != TLD_SUCCESS)
tld_errmsg = (char *)tld_strerror((Tld_rc)rc);
#endif
if (rc == TLD_INVALID)
infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n",
tld_errmsg, err_pos, uc_name[err_pos],
uc_name[err_pos] & 255);
else if (rc != TLD_SUCCESS)
infof(data, "WARNING: TLD check for %s failed; %s\n",
uc_name, tld_errmsg);
#endif
if (uc_name)
idn_free(uc_name);
return (bool)(rc == TLD_SUCCESS);
}
#endif
static void fix_hostname(struct SessionHandle *data,
struct connectdata *conn, struct hostname *host)
{
#ifndef USE_LIBIDN
(void)data;
(void)conn;
#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
(void)conn;
#endif
host->dispname = host->name;
#ifdef USE_LIBIDN
if (!is_ASCII_name(host->name) &&
stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
char *ace_hostname = NULL;
int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
infof (data, "Input domain encoded as `%s'\n",
stringprep_locale_charset ());
if (rc != IDNA_SUCCESS)
infof(data, "Failed to convert %s to ACE; %s\n",
host->name, Curl_idn_strerror(conn,rc));
else {
(void)tld_check_name(data, ace_hostname);
host->encalloc = ace_hostname;
host->name = host->encalloc;
}
}
#endif
}
static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
struct connectdata *conn)
{
char *at;
char *tmp;
char *path = data->reqdata.path;
if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
conn->protostr,
path)) && strequal(conn->protostr, "file")) {
if(path[0] == '/' && path[1] == '/') {
memmove(path, path + 2, strlen(path + 2)+1);
}
if(path[0] != '/') {
char *ptr=strchr(path, '/');
if(ptr) {
if(ptr[1] && ('/' == ptr[1]))
ptr++;
memmove(path, ptr, strlen(ptr)+1);
}
}
strcpy(conn->protostr, "file");
}
else {
path[0]=0;
if (2 > sscanf(data->change.url,
"%15[^\n:]://%[^\n/]%[^\n]",
conn->protostr,
conn->host.name, path)) {
if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]",
conn->host.name, path)) ) {
failf(data, "<url> malformed");
return CURLE_URL_MALFORMAT;
}
if(checkprefix("FTP.", conn->host.name))
strcpy(conn->protostr, "ftp");
else if (checkprefix("DICT.", conn->host.name))
strcpy(conn->protostr, "DICT");
else if (checkprefix("LDAP.", conn->host.name))
strcpy(conn->protostr, "LDAP");
else {
strcpy(conn->protostr, "http");
}
conn->protocol |= PROT_MISSING;
}
}
at = strchr(conn->host.name, '@');
if(at)
tmp = strchr(at+1, '?');
else
tmp = strchr(conn->host.name, '?');
if(tmp) {
size_t hostlen = strlen(tmp);
size_t pathlen = strlen(path);
memmove(path+hostlen+1, path, pathlen+1);
memcpy(path+1, tmp, hostlen);
path[0]='/';
*tmp=0;
}
else if(!path[0]) {
strcpy(path, "/");
}
if(path[0] == '?') {
memmove(&path[1], path, strlen(path)+1);
path[0] = '/';
}
return CURLE_OK;
}
static void llist_dtor(void *user, void *element)
{
(void)user;
(void)element;
}
static CURLcode setup_range(struct SessionHandle *data)
{
struct HandleData *req = &data->reqdata;
req->resume_from = data->set.set_resume_from;
if (req->resume_from || data->set.set_range) {
if (req->rangestringalloc == TRUE)
free(req->range);
if(req->resume_from)
req->range = aprintf("%" FORMAT_OFF_T "-", req->resume_from);
else
req->range = strdup(data->set.set_range);
req->rangestringalloc = req->range?TRUE:FALSE;
if(!req->range)
return CURLE_OUT_OF_MEMORY;
req->use_range = TRUE;
}
else
req->use_range = FALSE;
return CURLE_OK;
}
static CURLcode CreateConnection(struct SessionHandle *data,
struct connectdata **in_connect,
struct Curl_dns_entry **addr,
bool *async)
{
char *tmp;
CURLcode result=CURLE_OK;
struct connectdata *conn;
struct connectdata *conn_temp = NULL;
size_t urllen;
struct Curl_dns_entry *hostaddr;
#if defined(HAVE_ALARM) && !defined(USE_ARES)
unsigned int prev_alarm=0;
#endif
char endbracket;
char user[MAX_CURL_USER_LENGTH];
char passwd[MAX_CURL_PASSWORD_LENGTH];
int rc;
bool reuse;
char *proxy = NULL;
#ifndef USE_ARES
#ifdef SIGALRM
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact;
bool keep_copysig=FALSE;
#else
#ifdef HAVE_SIGNAL
void (*keep_sigact)(int);
#endif
#endif
#endif
#endif
*addr = NULL;
*async = FALSE;
if(!data->change.url)
return CURLE_URL_MALFORMAT;
conn = (struct connectdata *)calloc(1, sizeof(struct connectdata));
if(!conn) {
*in_connect = NULL;
return CURLE_OUT_OF_MEMORY;
}
*in_connect = conn;
conn->data = data;
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
conn->connectindex = -1;
conn->proxytype = data->set.proxytype;
conn->bits.proxy = (bool)(data->set.proxy && *data->set.proxy);
conn->bits.httpproxy = (bool)(conn->bits.proxy
&& (conn->proxytype == CURLPROXY_HTTP));
conn->bits.close = TRUE;
conn->readchannel_inuse = FALSE;
conn->writechannel_inuse = FALSE;
conn->read_pos = 0;
conn->buf_len = 0;
conn->created = Curl_tvnow();
conn->bits.user_passwd = (bool)(NULL != data->set.userpwd);
conn->bits.proxy_user_passwd = (bool)(NULL != data->set.proxyuserpwd);
conn->bits.no_body = data->set.opt_no_body;
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
if (data->multi && Curl_multi_canPipeline(data->multi) &&
!conn->master_buffer) {
conn->master_buffer = calloc(BUFSIZE, sizeof (char));
if (!conn->master_buffer)
return CURLE_OUT_OF_MEMORY;
}
conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
if (!conn->send_pipe || !conn->recv_pipe)
return CURLE_OUT_OF_MEMORY;
#define LEAST_PATH_ALLOC 256
urllen=strlen(data->change.url);
if(urllen < LEAST_PATH_ALLOC)
urllen=LEAST_PATH_ALLOC;
if (!data->set.source_url
&& data->reqdata.pathbuffer) {
free(data->reqdata.pathbuffer);
}
data->reqdata.pathbuffer=(char *)malloc(urllen+2);
if(NULL == data->reqdata.pathbuffer)
return CURLE_OUT_OF_MEMORY;
data->reqdata.path = data->reqdata.pathbuffer;
conn->host.rawalloc=(char *)malloc(urllen+2);
if(NULL == conn->host.rawalloc)
return CURLE_OUT_OF_MEMORY;
conn->host.name = conn->host.rawalloc;
conn->host.name[0] = 0;
result = ParseURLAndFillConnection(data, conn);
if (result != CURLE_OK) {
return result;
}
if(conn->bits.proxy_user_passwd) {
char proxyuser[MAX_CURL_USER_LENGTH]="";
char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
sscanf(data->set.proxyuserpwd,
"%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
proxyuser, proxypasswd);
conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
if(!conn->proxyuser)
return CURLE_OUT_OF_MEMORY;
conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
if(!conn->proxypasswd)
return CURLE_OUT_OF_MEMORY;
}
if (data->set.proxy) {
proxy = strdup(data->set.proxy);
if(NULL == proxy) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
}
#ifndef CURL_DISABLE_HTTP
if(!proxy) {
char *no_proxy=NULL;
char *no_proxy_tok_buf;
char proxy_env[128];
no_proxy=curl_getenv("no_proxy");
if(!no_proxy)
no_proxy=curl_getenv("NO_PROXY");
if(!no_proxy || !strequal("*", no_proxy)) {
char *nope;
nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL;
while(nope) {
size_t namelen;
char *endptr = strchr(conn->host.name, ':');
if(endptr)
namelen=endptr-conn->host.name;
else
namelen=strlen(conn->host.name);
if(strlen(nope) <= namelen) {
char *checkn=
conn->host.name + namelen - strlen(nope);
if(checkprefix(nope, checkn)) {
break;
}
}
nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
}
if(!nope) {
char *protop = conn->protostr;
char *envp = proxy_env;
char *prox;
while(*protop)
*envp++ = (char)tolower((int)*protop++);
strcpy(envp, "_proxy");
prox=curl_getenv(proxy_env);
if(!prox && !strequal("http_proxy", proxy_env)) {
for(envp = proxy_env; *envp; envp++)
*envp = (char)toupper((int)*envp);
prox=curl_getenv(proxy_env);
}
if(prox && *prox) {
proxy = prox;
}
else {
proxy = curl_getenv("all_proxy");
if(!proxy)
proxy=curl_getenv("ALL_PROXY");
}
if(proxy && *proxy) {
long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING);
if(conn->proxytype == CURLPROXY_HTTP) {
conn->protocol = PROT_HTTP | bits;
conn->bits.httpproxy = TRUE;
}
}
}
}
if(no_proxy)
free(no_proxy);
}
#endif
if(conn->protocol&PROT_MISSING) {
char *reurl;
reurl = aprintf("%s://%s", conn->protostr, data->change.url);
if(!reurl) {
Curl_safefree(proxy);
return CURLE_OUT_OF_MEMORY;
}
data->change.url = reurl;
data->change.url_alloc = TRUE;
conn->protocol &= ~PROT_MISSING;
}
conn->socktype = SOCK_STREAM;
if (strequal(conn->protostr, "HTTP")) {
#ifndef CURL_DISABLE_HTTP
conn->port = PORT_HTTP;
conn->remote_port = PORT_HTTP;
conn->protocol |= PROT_HTTP;
conn->curl_do = Curl_http;
conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
#else
failf(data, LIBCURL_NAME
" was built with HTTP disabled, http: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "HTTPS")) {
#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
conn->port = PORT_HTTPS;
conn->remote_port = PORT_HTTPS;
conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
conn->curl_do = Curl_http;
conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
conn->curl_connecting = Curl_https_connecting;
conn->curl_proto_getsock = Curl_https_getsock;
#else
failf(data, LIBCURL_NAME
" was built with SSL disabled, https: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if(strequal(conn->protostr, "FTP") ||
strequal(conn->protostr, "FTPS")) {
#ifndef CURL_DISABLE_FTP
char *type;
int port = PORT_FTP;
if(strequal(conn->protostr, "FTPS")) {
#ifdef USE_SSL
conn->protocol |= PROT_FTPS|PROT_SSL;
conn->ssl[SECONDARYSOCKET].use = data->set.ftp_ssl != CURLFTPSSL_CONTROL;
port = PORT_FTPS;
#else
failf(data, LIBCURL_NAME
" was built with SSL disabled, ftps: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
conn->port = port;
conn->remote_port = (unsigned short)port;
conn->protocol |= PROT_FTP;
if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
#ifndef CURL_DISABLE_HTTP
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
conn->protocol = PROT_HTTP;
#else
failf(data, "FTP over http proxy requires HTTP support built-in!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else {
conn->curl_do = Curl_ftp;
conn->curl_do_more = Curl_ftp_nextconnect;
conn->curl_done = Curl_ftp_done;
conn->curl_connect = Curl_ftp_connect;
conn->curl_connecting = Curl_ftp_multi_statemach;
conn->curl_doing = Curl_ftp_doing;
conn->curl_proto_getsock = Curl_ftp_getsock;
conn->curl_doing_getsock = Curl_ftp_getsock;
conn->curl_disconnect = Curl_ftp_disconnect;
}
data->reqdata.path++;
type=strstr(data->reqdata.path, ";type=");
if(!type) {
type=strstr(conn->host.rawalloc, ";type=");
}
if(type) {
char command;
*type=0;
command = (char)toupper((int)type[6]);
switch(command) {
case 'A':
data->set.prefer_ascii = TRUE;
break;
case 'D':
data->set.ftp_list_only = TRUE;
break;
case 'I':
default:
data->set.prefer_ascii = FALSE;
break;
}
}
#else
failf(data, LIBCURL_NAME
" was built with FTP disabled, ftp/ftps: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if(strequal(conn->protostr, "TELNET")) {
#ifndef CURL_DISABLE_TELNET
conn->protocol |= PROT_TELNET;
conn->port = PORT_TELNET;
conn->remote_port = PORT_TELNET;
conn->curl_do = Curl_telnet;
conn->curl_done = Curl_telnet_done;
#else
failf(data, LIBCURL_NAME
" was built with TELNET disabled!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "DICT")) {
#ifndef CURL_DISABLE_DICT
conn->protocol |= PROT_DICT;
conn->port = PORT_DICT;
conn->remote_port = PORT_DICT;
conn->curl_do = Curl_dict;
conn->curl_done = (Curl_done_func)ZERO_NULL;
#else
failf(data, LIBCURL_NAME
" was built with DICT disabled!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "LDAP")) {
#ifndef CURL_DISABLE_LDAP
conn->protocol |= PROT_LDAP;
conn->port = PORT_LDAP;
conn->remote_port = PORT_LDAP;
conn->curl_do = Curl_ldap;
conn->curl_done = (Curl_done_func)ZERO_NULL;
#else
failf(data, LIBCURL_NAME
" was built with LDAP disabled!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "FILE")) {
#ifndef CURL_DISABLE_FILE
conn->protocol |= PROT_FILE;
conn->curl_do = Curl_file;
conn->curl_done = Curl_file_done;
#else
failf(data, LIBCURL_NAME
" was built with FILE disabled!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "TFTP")) {
#ifndef CURL_DISABLE_TFTP
char *type;
conn->socktype = SOCK_DGRAM;
conn->protocol |= PROT_TFTP;
conn->port = PORT_TFTP;
conn->remote_port = PORT_TFTP;
conn->curl_connect = Curl_tftp_connect;
conn->curl_do = Curl_tftp;
conn->curl_done = Curl_tftp_done;
type=strstr(data->reqdata.path, ";mode=");
if(!type) {
type=strstr(conn->host.rawalloc, ";mode=");
}
if(type) {
char command;
*type=0;
command = (char)toupper((int)type[6]);
switch(command) {
case 'A':
case 'N':
data->set.prefer_ascii = TRUE;
break;
case 'O':
case 'I':
default:
data->set.prefer_ascii = FALSE;
break;
}
}
#else
failf(data, LIBCURL_NAME
" was built with TFTP disabled!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "SCP")) {
#ifdef USE_LIBSSH2
conn->port = PORT_SSH;
conn->remote_port = PORT_SSH;
conn->protocol = PROT_SCP;
conn->curl_connect = Curl_ssh_connect;
conn->curl_do = Curl_scp_do;
conn->curl_done = Curl_scp_done;
conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
#else
failf(data, LIBCURL_NAME
" was built without LIBSSH2, scp: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else if (strequal(conn->protostr, "SFTP")) {
#ifdef USE_LIBSSH2
conn->port = PORT_SSH;
conn->remote_port = PORT_SSH;
conn->protocol = PROT_SFTP;
conn->curl_connect = Curl_ssh_connect;
conn->curl_do = Curl_sftp_do;
conn->curl_done = Curl_sftp_done;
conn->curl_do_more = (Curl_do_more_func)NULL;
#else
failf(data, LIBCURL_NAME
" was built without LIBSSH2, scp: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else {
failf(data, "Unsupported protocol: %s", conn->protostr);
return CURLE_UNSUPPORTED_PROTOCOL;
}
if(proxy && *proxy) {
char *prox_portno;
char *endofprot;
char *proxyptr=proxy;
char *portptr;
char *atsign;
endofprot=strstr(proxyptr, "://");
if(endofprot)
proxyptr = endofprot+3;
atsign = strchr(proxyptr, '@');
if(atsign) {
char proxyuser[MAX_CURL_USER_LENGTH];
char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
proxypasswd[0] = 0;
if(1 <= sscanf(proxyptr,
"%" MAX_CURL_USER_LENGTH_TXT"[^:]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
proxyuser, proxypasswd)) {
CURLcode res = CURLE_OK;
Curl_safefree(conn->proxyuser);
conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
if(!conn->proxyuser)
res = CURLE_OUT_OF_MEMORY;
else {
Curl_safefree(conn->proxypasswd);
conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
if(!conn->proxypasswd)
res = CURLE_OUT_OF_MEMORY;
}
if(CURLE_OK == res) {
conn->bits.proxy_user_passwd = TRUE;
atsign = strdup(atsign+1);
if(atsign) {
free(proxy);
proxy = proxyptr = atsign;
}
else
res = CURLE_OUT_OF_MEMORY;
}
if(res) {
free(proxy);
return res;
}
}
}
portptr = proxyptr;
if(*proxyptr == '[') {
char *ptr = ++proxyptr;
while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':')))
ptr++;
if(*ptr == ']') {
*ptr = 0;
portptr = ptr+1;
}
}
prox_portno = strchr(portptr, ':');
if (prox_portno) {
*prox_portno = 0x0;
prox_portno ++;
conn->port = atoi(prox_portno);
}
else if(data->set.proxyport) {
conn->port = data->set.proxyport;
}
conn->proxy.rawalloc = strdup(proxyptr);
conn->proxy.name = conn->proxy.rawalloc;
free(proxy);
proxy = NULL;
if(!conn->proxy.rawalloc)
return CURLE_OUT_OF_MEMORY;
}
#ifndef CURL_DISABLE_FILE
if (strequal(conn->protostr, "FILE")) {
result = Curl_file_connect(conn);
if(CURLE_OK == result) {
conn->data = data;
conn->bits.tcpconnect = TRUE;
ConnectionStore(data, conn);
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
-1, NULL);
}
return result;
}
#endif
if((conn->protocol&PROT_SSL) && conn->bits.httpproxy)
conn->bits.tunnel_proxy = TRUE;
user[0] =0;
passwd[0]=0;
if (conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP|PROT_SFTP)) {
char *ptr=strchr(conn->host.name, '@');
char *userpass = conn->host.name;
if(ptr != NULL) {
conn->host.name = ++ptr;
if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
conn->bits.user_passwd = 1;
if(*userpass != ':') {
sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
user, passwd);
}
else
sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
if(user[0]) {
char *newname=curl_easy_unescape(data, user, 0, NULL);
if(!newname)
return CURLE_OUT_OF_MEMORY;
if(strlen(newname) < sizeof(user))
strcpy(user, newname);
free(newname);
}
if (passwd[0]) {
char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
if(!newpasswd)
return CURLE_OUT_OF_MEMORY;
if(strlen(newpasswd) < sizeof(passwd))
strcpy(passwd, newpasswd);
free(newpasswd);
}
}
}
}
if((1 == sscanf(conn->host.name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
(']' == endbracket)) {
conn->bits.ipv6_ip = TRUE;
conn->host.name++;
tmp = strchr(conn->host.name, ']');
*tmp = 0;
tmp++;
if(':' != *tmp)
tmp = NULL;
}
else
tmp = strrchr(conn->host.name, ':');
if(data->set.use_port && data->state.allow_port) {
conn->remote_port = (unsigned short)data->set.use_port;
if(tmp)
*tmp = '\0';
if(conn->bits.httpproxy) {
char *url;
url = aprintf("%s://%s:%d%s", conn->protostr, conn->host.name,
conn->remote_port, data->reqdata.path);
if(!url)
return CURLE_OUT_OF_MEMORY;
if(data->change.url_alloc)
free(data->change.url);
data->change.url = url;
data->change.url_alloc = TRUE;
}
}
else if (tmp) {
char *rest;
unsigned long port;
port=strtoul(tmp+1, &rest, 10);
if (rest != (tmp+1) && *rest == '\0') {
if (port > 0xffff) {
failf(data, "Port number too large: %lu", port);
return CURLE_URL_MALFORMAT;
}
*tmp = '\0';
conn->remote_port = (unsigned short)port;
}
}
if (data->set.userpwd != NULL) {
sscanf(data->set.userpwd,
"%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
user, passwd);
}
conn->bits.netrc = FALSE;
if (data->set.use_netrc != CURL_NETRC_IGNORED) {
if(Curl_parsenetrc(conn->host.name,
user, passwd,
data->set.netrc_file)) {
infof(data, "Couldn't find host %s in the " DOT_CHAR
"netrc file, using defaults\n",
conn->host.name);
}
else {
conn->bits.netrc = TRUE;
conn->bits.user_passwd = 1;
}
}
if ( (conn->protocol & PROT_FTP) &&
!conn->bits.user_passwd) {
conn->user = strdup(CURL_DEFAULT_USER);
conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
}
else {
conn->user = strdup(user);
conn->passwd = strdup(passwd);
}
if(!conn->user || !conn->passwd)
return CURLE_OUT_OF_MEMORY;
if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
return CURLE_OUT_OF_MEMORY;
if(data->set.reuse_fresh && !data->state.this_is_a_follow)
reuse = FALSE;
else
reuse = ConnectionExists(data, conn, &conn_temp);
if(reuse) {
struct connectdata *old_conn = conn;
if(old_conn->proxy.rawalloc)
free(old_conn->proxy.rawalloc);
Curl_free_ssl_config(&conn->ssl_config);
conn = conn_temp;
conn->data = old_conn->data;
conn->bits.user_passwd = old_conn->bits.user_passwd;
if (conn->bits.user_passwd) {
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
conn->user = old_conn->user;
conn->passwd = old_conn->passwd;
old_conn->user = NULL;
old_conn->passwd = NULL;
}
conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
if (conn->bits.proxy_user_passwd) {
Curl_safefree(conn->proxyuser);
Curl_safefree(conn->proxypasswd);
conn->proxyuser = old_conn->proxyuser;
conn->proxypasswd = old_conn->proxypasswd;
old_conn->proxyuser = NULL;
old_conn->proxypasswd = NULL;
}
if (conn->bits.proxy) {
free(conn->host.rawalloc);
conn->host=old_conn->host;
}
else
free(old_conn->host.rawalloc);
conn->bits.no_body = old_conn->bits.no_body;
conn->bits.reuse = TRUE;
Curl_safefree(old_conn->user);
Curl_safefree(old_conn->passwd);
Curl_safefree(old_conn->proxyuser);
Curl_safefree(old_conn->proxypasswd);
Curl_llist_destroy(old_conn->send_pipe, NULL);
Curl_llist_destroy(old_conn->recv_pipe, NULL);
Curl_safefree(old_conn->master_buffer);
free(old_conn);
*in_connect = conn;
infof(data, "Re-using existing connection! (#%ld) with host %s\n",
conn->connectindex,
conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
}
else {
ConnectionStore(data, conn);
}
result = setup_range(data);
if(result)
return result;
conn->fread = data->set.fread;
conn->fread_in = data->set.in;
if ((conn->protocol&PROT_HTTP) &&
data->set.upload &&
(data->set.infilesize == -1) &&
(data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
conn->bits.upload_chunky = TRUE;
}
else {
conn->bits.upload_chunky = FALSE;
}
#ifndef USE_ARES
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
#ifdef SIGALRM
#ifdef HAVE_ALARM
long shortest;
#endif
#ifdef HAVE_SIGACTION
struct sigaction sigact;
sigaction(SIGALRM, NULL, &sigact);
keep_sigact = sigact;
keep_copysig = TRUE;
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
sigact.sa_flags &= ~SA_RESTART;
#endif
sigaction(SIGALRM, &sigact, NULL);
#else
#ifdef HAVE_SIGNAL
keep_sigact = signal(SIGALRM, alarmfunc);
#endif
#endif
#ifdef HAVE_ALARM
shortest = data->set.timeout;
if(shortest && data->set.connecttimeout &&
(data->set.connecttimeout < shortest))
shortest = data->set.connecttimeout;
else if(!shortest)
shortest = data->set.connecttimeout;
if(shortest < 1000)
return CURLE_OPERATION_TIMEDOUT;
prev_alarm = alarm((unsigned int) (shortest ? shortest/1000L : shortest));
#endif
#endif
}
#endif
if(conn->bits.reuse) {
hostaddr = NULL;
if (conn->bits.proxy)
fix_hostname(data, conn, &conn->host);
}
else {
fix_hostname(data, conn, &conn->host);
if(!conn->proxy.name || !*conn->proxy.name) {
conn->port = conn->remote_port;
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;
}
}
else {
fix_hostname(data, conn, &conn->proxy);
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;
}
}
}
*addr = hostaddr;
#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) {
sigaction(SIGALRM, &keep_sigact, NULL);
}
#else
#ifdef HAVE_SIGNAL
signal(SIGALRM, keep_sigact);
#endif
#endif
if(prev_alarm) {
unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
unsigned long alarm_set;
alarm_set = prev_alarm - elapsed_ms/1000;
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
alarm(1);
result = CURLE_OPERATION_TIMEOUTED;
failf(data, "Previous alarm fired off!");
}
else
alarm((unsigned int)alarm_set);
}
else
alarm(0);
}
#endif
return result;
}
static CURLcode SetupConnection(struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *protocol_done)
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE) {
*protocol_done = TRUE;
return result;
}
*protocol_done = FALSE;
if((conn->protocol&PROT_HTTP) && data->set.useragent) {
Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
aprintf("User-Agent: %s\r\n", data->set.useragent);
if(!conn->allocptr.uagent)
return CURLE_OUT_OF_MEMORY;
}
data->reqdata.keep.headerbytecount = 0;
#ifdef CURL_DO_LINEEND_CONV
data->state.crlf_conversions = 0;
#endif
for(;;) {
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
bool connected = FALSE;
result = ConnectPlease(data, conn, hostaddr, &connected);
if(connected) {
result = Curl_protocol_connect(conn, protocol_done);
if(CURLE_OK == result)
conn->bits.tcpconnect = TRUE;
}
else
conn->bits.tcpconnect = FALSE;
if(conn->bits.proxy_connect_closed) {
if (data->set.errorbuffer)
data->set.errorbuffer[0] = '\0';
data->state.errorbuf = FALSE;
continue;
}
if(CURLE_OK != result)
return result;
}
else {
Curl_pgrsTime(data, TIMER_CONNECT);
conn->bits.tcpconnect = TRUE;
*protocol_done = TRUE;
if(data->set.verbose)
verboseconnect(conn);
}
break;
}
conn->now = Curl_tvnow();
#ifdef __EMX__
if ((data->set.out)->_handle == NULL) {
_fsetmode(stdout, "b");
}
#endif
return CURLE_OK;
}
CURLcode Curl_connect(struct SessionHandle *data,
struct connectdata **in_connect,
bool *asyncp,
bool *protocol_done)
{
CURLcode code;
struct Curl_dns_entry *dns;
*asyncp = FALSE;
code = CreateConnection(data, in_connect, &dns, asyncp);
if(CURLE_OK == code) {
if(dns || !*asyncp)
code = SetupConnection(*in_connect, dns, protocol_done);
}
if(CURLE_OK != code) {
if(*in_connect) {
Curl_disconnect(*in_connect);
*in_connect = NULL;
}
}
else {
if ((*in_connect)->is_in_pipeline)
data->state.is_in_pipeline = TRUE;
}
return code;
}
CURLcode Curl_async_resolved(struct connectdata *conn,
bool *protocol_done)
{
#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
defined(USE_THREADING_GETADDRINFO)
CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done);
if(code)
Curl_disconnect(conn);
return code;
#else
(void)conn;
(void)protocol_done;
return CURLE_OK;
#endif
}
CURLcode Curl_done(struct connectdata **connp,
CURLcode status,
bool premature)
{
CURLcode result;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
Curl_expire(data, 0);
if(conn->bits.done)
return CURLE_OK;
conn->bits.done = TRUE;
if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
conn->readchannel_inuse)
conn->readchannel_inuse = FALSE;
if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
conn->writechannel_inuse)
conn->writechannel_inuse = FALSE;
if(data->reqdata.newurl) {
free(data->reqdata.newurl);
data->reqdata.newurl = NULL;
}
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry);
conn->dns_entry = NULL;
}
if(conn->curl_done)
result = conn->curl_done(conn, status, premature);
else
result = CURLE_OK;
Curl_pgrsDone(conn);
ares_cancel(data->state.areschannel);
if(data->set.reuse_forbid || conn->bits.close) {
CURLcode res2 = Curl_disconnect(conn);
if(!result && res2)
result = res2;
}
else {
ConnectionDone(conn);
data->state.lastconnect = conn->connectindex;
infof(data, "Connection #%ld to host %s left intact\n",
conn->connectindex,
conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
}
*connp = NULL;
return result;
}
CURLcode Curl_do(struct connectdata **connp, bool *done)
{
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
conn->bits.done = FALSE;
conn->bits.do_more = FALSE;
if(conn->curl_do) {
result = conn->curl_do(conn, done);
if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE;
result = Curl_done(&conn, result, FALSE);
if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
bool async;
bool protocol_done = TRUE;
result = Curl_connect(data, connp, &async, &protocol_done);
if(CURLE_OK == result) {
conn = *connp;
if(async) {
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
result = Curl_async_resolved(conn, &protocol_done);
if(result)
return result;
}
result = conn->curl_do(conn, done);
}
}
}
}
return result;
}
CURLcode Curl_do_more(struct connectdata *conn)
{
CURLcode result=CURLE_OK;
if(conn->curl_do_more)
result = conn->curl_do_more(conn);
return result;
}