#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
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#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 "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 "speedcheck.h"
#include "rawstr.h"
#include "warnless.h"
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
#include "tftp.h"
#include "http.h"
#include "file.h"
#include "curl_ldap.h"
#include "ssh.h"
#include "imap.h"
#include "url.h"
#include "connect.h"
#include "inet_ntop.h"
#include "http_ntlm.h"
#include "socks.h"
#include "rtsp.h"
#include "curl_rtmp.h"
#include "gopher.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "curl_memory.h"
#include "memdebug.h"
static long ConnectionKillOne(struct SessionHandle *data);
static void conn_free(struct connectdata *conn);
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
static const struct Curl_handler * const protocols[] = {
#ifndef CURL_DISABLE_HTTP
&Curl_handler_http,
#endif
#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
&Curl_handler_https,
#endif
#ifndef CURL_DISABLE_FTP
&Curl_handler_ftp,
#endif
#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
&Curl_handler_ftps,
#endif
#ifndef CURL_DISABLE_TELNET
&Curl_handler_telnet,
#endif
#ifndef CURL_DISABLE_DICT
&Curl_handler_dict,
#endif
#ifndef CURL_DISABLE_LDAP
&Curl_handler_ldap,
#if (defined(USE_OPENLDAP) && defined(USE_SSL)) || \
(!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))
&Curl_handler_ldaps,
#endif
#endif
#ifndef CURL_DISABLE_FILE
&Curl_handler_file,
#endif
#ifndef CURL_DISABLE_TFTP
&Curl_handler_tftp,
#endif
#ifdef USE_LIBSSH2
&Curl_handler_scp,
&Curl_handler_sftp,
#endif
#ifndef CURL_DISABLE_IMAP
&Curl_handler_imap,
#ifdef USE_SSL
&Curl_handler_imaps,
#endif
#endif
#ifndef CURL_DISABLE_POP3
&Curl_handler_pop3,
#ifdef USE_SSL
&Curl_handler_pop3s,
#endif
#endif
#ifndef CURL_DISABLE_SMTP
&Curl_handler_smtp,
#ifdef USE_SSL
&Curl_handler_smtps,
#endif
#endif
#ifndef CURL_DISABLE_RTSP
&Curl_handler_rtsp,
#endif
#ifndef CURL_DISABLE_GOPHER
&Curl_handler_gopher,
#endif
#ifdef USE_LIBRTMP
&Curl_handler_rtmp,
&Curl_handler_rtmpt,
&Curl_handler_rtmpe,
&Curl_handler_rtmpte,
&Curl_handler_rtmps,
&Curl_handler_rtmpts,
#endif
(struct Curl_handler *) NULL
};
static const struct Curl_handler Curl_handler_dummy = {
"<no protocol>",
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
0,
0
};
void Curl_safefree(void *ptr)
{
if(ptr)
free(ptr);
}
static void close_connections(struct SessionHandle *data)
{
long i;
do {
i = ConnectionKillOne(data);
} while(i != -1L);
}
void Curl_freeset(struct SessionHandle * data)
{
enum dupstring i;
for(i=(enum dupstring)0; i < STRING_LAST; i++)
Curl_safefree(data->set.str[i]);
}
static CURLcode setstropt(char **charp, char * s)
{
if(*charp) {
free(*charp);
*charp = (char *) NULL;
}
if(s) {
s = strdup(s);
if(!s)
return CURLE_OUT_OF_MEMORY;
*charp = s;
}
return CURLE_OK;
}
static CURLcode setstropt_userpwd(char *option, char **user_storage,
char **pwd_storage)
{
char* separator;
CURLcode result = CURLE_OK;
if(!option) {
Curl_safefree(*user_storage);
*user_storage = (char *) NULL;
Curl_safefree(*pwd_storage);
*pwd_storage = (char *) NULL;
return CURLE_OK;
}
separator = strchr(option, ':');
if (separator != NULL) {
char * p;
size_t username_len = (size_t)(separator-option);
p = malloc(username_len+1);
if(!p)
result = CURLE_OUT_OF_MEMORY;
else {
memcpy(p, option, username_len);
p[username_len] = '\0';
Curl_safefree(*user_storage);
*user_storage = p;
}
if (result == CURLE_OK) {
result = setstropt(pwd_storage, separator+1);
}
}
else {
result = setstropt(user_storage, option);
}
return result;
}
CURLcode Curl_dupset(struct SessionHandle * dst, struct SessionHandle * src)
{
CURLcode r = CURLE_OK;
enum dupstring i;
dst->set = src->set;
memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
for(i=(enum dupstring)0; i< STRING_LAST; i++) {
r = setstropt(&dst->set.str[i], src->set.str[i]);
if(r != CURLE_OK)
break;
}
return r;
}
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
static void flush_cookies(struct SessionHandle *data, int cleanup)
{
if(data->set.str[STRING_COOKIEJAR]) {
if(data->change.cookielist) {
Curl_cookie_loadfiles(data);
}
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
if(Curl_cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
infof(data, "WARNING: failed to save cookies in %s\n",
data->set.str[STRING_COOKIEJAR]);
}
else {
if(cleanup && data->change.cookielist)
curl_slist_free_all(data->change.cookielist);
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
}
if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
Curl_cookie_cleanup(data->cookies);
}
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
#endif
CURLcode Curl_close(struct SessionHandle *data)
{
struct Curl_multi *m = data->multi;
#ifdef DEBUGBUILD
if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) {
struct conncache *c = data->state.connc;
long i;
struct curl_llist *pipeline;
struct curl_llist_element *curr;
struct connectdata *connptr;
for(i=0; i< c->num; i++) {
connptr = c->connects[i];
if(!connptr)
continue;
pipeline = connptr->send_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"problem we %p are still in send pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
pipeline = connptr->recv_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"problem we %p are still in recv pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
pipeline = connptr->done_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"problem we %p are still in done pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
pipeline = connptr->pend_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"problem we %p are still in pend pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
}
}
#endif
Curl_expire(data, 0);
if(m)
curl_multi_remove_handle(data->multi, data);
if(data->state.timeoutlist) {
Curl_llist_destroy(data->state.timeoutlist, NULL);
data->state.timeoutlist = NULL;
}
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->dns.hostcachetype == HCACHE_PRIVATE) {
Curl_hash_destroy(data->dns.hostcache);
data->dns.hostcachetype = HCACHE_NONE;
data->dns.hostcache = NULL;
}
if(data->state.rangestringalloc)
free(data->state.range);
Curl_safefree(data->state.pathbuffer);
Curl_safefree(data->state.proto.generic);
Curl_ssl_close_all(data);
Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch);
Curl_ssl_free_certinfo(data);
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)
flush_cookies(data, 1);
#endif
Curl_digest_cleanup(data);
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
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);
}
Curl_freeset(data);
free(data);
return CURLE_OK;
}
struct conncache *Curl_mk_connc(int type,
long amount)
{
struct conncache *c;
long default_amount;
long max_amount = (long)(((size_t)INT_MAX) / sizeof(struct connectdata *));
if(type == CONNCACHE_PRIVATE) {
default_amount = (amount < 1L) ? 5L : amount;
}
else {
default_amount = (amount < 1L) ? 10L : amount;
}
if(default_amount > max_amount)
default_amount = max_amount;
c = calloc(1, sizeof(struct conncache));
if(!c)
return NULL;
c->connects = calloc((size_t)default_amount, sizeof(struct connectdata *));
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;
long max_amount = (long)(((size_t)INT_MAX) / sizeof(struct connectdata *));
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], FALSE);
if(data->state.lastconnect <= newamount)
data->state.lastconnect = -1;
}
if(newamount > 0) {
if(newamount > max_amount)
newamount = max_amount;
newptr = 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) {
long i;
for(i = 0; i < c->num; ++i)
conn_free(c->connects[i]);
free(c->connects);
}
free(c);
}
CURLcode Curl_init_userdefined(struct UserDefined *set)
{
CURLcode res = CURLE_OK;
set->out = stdout;
set->in = stdin;
set->err = stderr;
set->fwrite_func = (curl_write_callback)fwrite;
set->fread_func = (curl_read_callback)fread;
set->is_fread_set = 0;
set->is_fwrite_set = 0;
set->seek_func = ZERO_NULL;
set->seek_client = ZERO_NULL;
set->convfromnetwork = ZERO_NULL;
set->convtonetwork = ZERO_NULL;
set->convfromutf8 = ZERO_NULL;
set->infilesize = -1;
set->postfieldsize = -1;
set->maxredirs = -1;
set->httpreq = HTTPREQ_GET;
set->rtspreq = RTSPREQ_OPTIONS;
set->ftp_use_epsv = TRUE;
set->ftp_use_eprt = TRUE;
set->ftp_use_pret = FALSE;
set->ftp_filemethod = FTPFILE_MULTICWD;
set->dns_cache_timeout = 60;
set->ssl.numsessions = 5;
set->proxyport = CURL_DEFAULT_PROXY_PORT;
set->proxytype = CURLPROXY_HTTP;
set->httpauth = CURLAUTH_BASIC;
set->proxyauth = CURLAUTH_BASIC;
set->hide_progress = TRUE;
set->ssl.verifypeer = TRUE;
set->ssl.verifyhost = 2;
#ifdef USE_TLS_SRP
set->ssl.authtype = CURL_TLSAUTH_NONE;
#endif
set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
set->ssl.sessionid = TRUE;
set->new_file_perms = 0644;
set->new_directory_perms = 0755;
set->allowed_protocols = PROT_EXTMASK;
set->redir_protocols =
PROT_EXTMASK & ~(CURLPROTO_FILE|CURLPROTO_SCP);
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
set->socks5_gssapi_nec = FALSE;
res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE],
(char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE);
if (res != CURLE_OK)
return res;
#endif
#if defined(CURL_CA_BUNDLE)
res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE);
#elif defined(CURL_CA_PATH)
res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
#endif
set->wildcardmatch = FALSE;
set->chunk_bgn = ZERO_NULL;
set->chunk_end = ZERO_NULL;
return res;
}
CURLcode Curl_open(struct SessionHandle **curl)
{
CURLcode res = CURLE_OK;
struct SessionHandle *data;
#ifdef USE_ARES
int status;
#endif
data = 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 = malloc(HEADERSIZE);
if(!data->state.headerbuff) {
DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
res = CURLE_OUT_OF_MEMORY;
}
else {
Curl_easy_initHandleData(data);
res = Curl_init_userdefined(&data->set);
data->state.headersize=HEADERSIZE;
#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->state.lastconnect = -1;
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1;
data->wildcard.state = CURLWC_INIT;
data->wildcard.filelist = NULL;
data->set.fnmatch = ZERO_NULL;
}
if(res) {
ares_destroy(data->state.areschannel);
if(data->state.headerbuff)
free(data->state.headerbuff);
Curl_freeset(data);
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;
#ifndef CURL_DISABLE_HTTP
curl_off_t bigsize;
#endif
switch(option) {
case CURLOPT_DNS_CACHE_TIMEOUT:
data->set.dns_cache_timeout = va_arg(param, long);
break;
case CURLOPT_DNS_USE_GLOBAL_CACHE:
{
long use_cache = va_arg(param, long);
data->set.global_dns_cache = (bool)(0 != use_cache);
}
break;
case CURLOPT_SSL_CIPHER_LIST:
result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST],
va_arg(param, char *));
break;
case CURLOPT_RANDOM_FILE:
result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE],
va_arg(param, char *));
break;
case CURLOPT_EGDSOCKET:
result = setstropt(&data->set.str[STRING_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));
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;
data->set.opt_no_body = FALSE;
}
else
data->set.httpreq = HTTPREQ_GET;
break;
case CURLOPT_FILETIME:
data->set.get_filetime = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_CREATE_MISSING_DIRS:
switch(va_arg(param, long)) {
case 0:
data->set.ftp_create_missing_dirs = 0;
break;
case 1:
data->set.ftp_create_missing_dirs = 1;
break;
case 2:
data->set.ftp_create_missing_dirs = 2;
break;
default:
result = CURLE_FAILED_INIT;
break;
}
break;
case CURLOPT_SERVER_RESPONSE_TIMEOUT:
data->set.server_response_timeout = va_arg( param , long ) * 1000;
break;
case CURLOPT_TFTP_BLKSIZE:
data->set.tftp_blksize = va_arg(param, long);
break;
case CURLOPT_DIRLISTONLY:
data->set.ftp_list_only = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_APPEND:
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:
result = setstropt(&data->set.str[STRING_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:
argptr = va_arg(param, char *);
result = setstropt(&data->set.str[STRING_ENCODING],
(argptr && !*argptr)?
(char *) ALL_CONTENT_ENCODINGS: argptr);
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_POSTREDIR:
{
long postRedir = va_arg(param, long);
data->set.post301 = (bool)((postRedir & CURL_REDIR_POST_301)?TRUE:FALSE);
data->set.post302 = (bool)((postRedir & CURL_REDIR_POST_302)?TRUE:FALSE);
}
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_COPYPOSTFIELDS:
argptr = va_arg(param, char *);
if(!argptr || data->set.postfieldsize == -1)
result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr);
else {
if((data->set.postfieldsize < 0) ||
((sizeof(curl_off_t) != sizeof(size_t)) &&
(data->set.postfieldsize > (curl_off_t)((size_t)-1))))
result = CURLE_OUT_OF_MEMORY;
else {
char * p;
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
p = malloc((size_t)(data->set.postfieldsize?
data->set.postfieldsize:1));
if(!p)
result = CURLE_OUT_OF_MEMORY;
else {
if(data->set.postfieldsize)
memcpy(p, argptr, (size_t)data->set.postfieldsize);
data->set.str[STRING_COPYPOSTFIELDS] = p;
}
}
}
data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
data->set.httpreq = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDS:
data->set.postfields = va_arg(param, void *);
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.httpreq = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDSIZE:
bigsize = va_arg(param, long);
if(data->set.postfieldsize < bigsize &&
data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.postfields = NULL;
}
data->set.postfieldsize = bigsize;
break;
case CURLOPT_POSTFIELDSIZE_LARGE:
bigsize = va_arg(param, curl_off_t);
if(data->set.postfieldsize < bigsize &&
data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.postfields = NULL;
}
data->set.postfieldsize = bigsize;
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;
}
result = setstropt(&data->set.str[STRING_SET_REFERER],
va_arg(param, char *));
data->change.referer = data->set.str[STRING_SET_REFERER];
break;
case CURLOPT_USERAGENT:
result = setstropt(&data->set.str[STRING_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:
result = setstropt(&data->set.str[STRING_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:
result = setstropt(&data->set.str[STRING_COOKIEJAR],
va_arg(param, char *));
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(Curl_raw_equal(argptr, "ALL")) {
Curl_cookie_clearall(data->cookies);
break;
}
else if(Curl_raw_equal(argptr, "SESS")) {
Curl_cookie_clearsess(data->cookies);
break;
}
else if(Curl_raw_equal(argptr, "FLUSH")) {
flush_cookies(data, 0);
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_HTTPAUTH:
{
long auth = va_arg(param, long);
data->state.authhost.iestyle = (bool)((auth & CURLAUTH_DIGEST_IE)?
TRUE:FALSE);
if(auth & CURLAUTH_DIGEST_IE) {
auth |= CURLAUTH_DIGEST;
auth &= ~CURLAUTH_DIGEST_IE;
}
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM;
#endif
#ifndef USE_HTTP_NEGOTIATE
auth &= ~CURLAUTH_GSSNEGOTIATE;
#endif
if(!auth)
return CURLE_FAILED_INIT;
data->set.httpauth = auth;
}
break;
#endif
case CURLOPT_CUSTOMREQUEST:
result = setstropt(&data->set.str[STRING_CUSTOMREQUEST],
va_arg(param, char *));
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_HTTPPROXYTUNNEL:
data->set.tunnel_thru_httpproxy = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_PROXYPORT:
data->set.proxyport = va_arg(param, long);
break;
case CURLOPT_PROXYAUTH:
{
long auth = va_arg(param, long);
data->state.authproxy.iestyle = (bool)((auth & CURLAUTH_DIGEST_IE)?
TRUE:FALSE);
if(auth & CURLAUTH_DIGEST_IE) {
auth |= CURLAUTH_DIGEST;
auth &= ~CURLAUTH_DIGEST_IE;
}
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM;
#endif
#ifndef USE_HTTP_NEGOTIATE
auth &= ~CURLAUTH_GSSNEGOTIATE;
#endif
if(!auth)
return CURLE_FAILED_INIT;
data->set.proxyauth = auth;
}
break;
case CURLOPT_PROXY:
result = setstropt(&data->set.str[STRING_PROXY],
va_arg(param, char *));
break;
case CURLOPT_PROXYTYPE:
data->set.proxytype = (curl_proxytype)va_arg(param, long);
break;
case CURLOPT_PROXY_TRANSFER_MODE:
switch (va_arg(param, long)) {
case 0:
data->set.proxy_transfer_mode = FALSE;
break;
case 1:
data->set.proxy_transfer_mode = TRUE;
break;
default:
result = CURLE_FAILED_INIT;
break;
}
break;
#endif
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
result = setstropt(&data->set.str[STRING_SOCKS5_GSSAPI_SERVICE],
va_arg(param, char *));
break;
case CURLOPT_SOCKS5_GSSAPI_NEC:
data->set.socks5_gssapi_nec = (bool)(0 != va_arg(param, long));
break;
#endif
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:
result = setstropt(&data->set.str[STRING_FTPPORT],
va_arg(param, char *));
data->set.ftp_use_port = (bool)(NULL != data->set.str[STRING_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_USE_PRET:
data->set.ftp_use_pret = (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;
}
result = setstropt(&data->set.str[STRING_SET_URL],
va_arg(param, char *));
data->change.url = data->set.str[STRING_SET_URL];
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:
result = setstropt_userpwd(va_arg(param, char *),
&data->set.str[STRING_USERNAME],
&data->set.str[STRING_PASSWORD]);
break;
case CURLOPT_USERNAME:
result = setstropt(&data->set.str[STRING_USERNAME],
va_arg(param, char *));
break;
case CURLOPT_PASSWORD:
result = setstropt(&data->set.str[STRING_PASSWORD],
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_RESOLVE:
data->set.resolve = va_arg(param, struct curl_slist *);
data->change.resolve = data->set.resolve;
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;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXYUSERPWD:
result = setstropt_userpwd(va_arg(param, char *),
&data->set.str[STRING_PROXYUSERNAME],
&data->set.str[STRING_PROXYPASSWORD]);
break;
case CURLOPT_PROXYUSERNAME:
result = setstropt(&data->set.str[STRING_PROXYUSERNAME],
va_arg(param, char *));
break;
case CURLOPT_PROXYPASSWORD:
result = setstropt(&data->set.str[STRING_PROXYPASSWORD],
va_arg(param, char *));
break;
case CURLOPT_NOPROXY:
result = setstropt(&data->set.str[STRING_NOPROXY],
va_arg(param, char *));
break;
#endif
case CURLOPT_RANGE:
result = setstropt(&data->set.str[STRING_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_func = va_arg(param, curl_write_callback);
if(!data->set.fwrite_func) {
data->set.is_fwrite_set = 0;
data->set.fwrite_func = (curl_write_callback)fwrite;
}
else
data->set.is_fwrite_set = 1;
break;
case CURLOPT_READFUNCTION:
data->set.fread_func = va_arg(param, curl_read_callback);
if(!data->set.fread_func) {
data->set.is_fread_set = 0;
data->set.fread_func = (curl_read_callback)fread;
}
else
data->set.is_fread_set = 1;
break;
case CURLOPT_SEEKFUNCTION:
data->set.seek_func = va_arg(param, curl_seek_callback);
break;
case CURLOPT_SEEKDATA:
data->set.seek_client = va_arg(param, void *);
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_func = va_arg(param, curl_ioctl_callback);
break;
case CURLOPT_IOCTLDATA:
data->set.ioctl_client = va_arg(param, void *);
break;
case CURLOPT_SSLCERT:
result = setstropt(&data->set.str[STRING_CERT],
va_arg(param, char *));
break;
case CURLOPT_SSLCERTTYPE:
result = setstropt(&data->set.str[STRING_CERT_TYPE],
va_arg(param, char *));
break;
case CURLOPT_SSLKEY:
result = setstropt(&data->set.str[STRING_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSLKEYTYPE:
result = setstropt(&data->set.str[STRING_KEY_TYPE],
va_arg(param, char *));
break;
case CURLOPT_KEYPASSWD:
result = setstropt(&data->set.str[STRING_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:
result = setstropt(&data->set.str[STRING_DEVICE],
va_arg(param, char *));
break;
case CURLOPT_LOCALPORT:
data->set.localport = curlx_sltous(va_arg(param, long));
break;
case CURLOPT_LOCALPORTRANGE:
data->set.localportrange = curlx_sltosi(va_arg(param, long));
break;
case CURLOPT_KRBLEVEL:
result = setstropt(&data->set.str[STRING_KRB_LEVEL],
va_arg(param, char *));
data->set.krb = (bool)(NULL != data->set.str[STRING_KRB_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;
#ifdef USE_SSLEAY
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_CERTINFO:
data->set.ssl.certinfo = (bool)(0 != va_arg(param, long));
break;
#endif
case CURLOPT_CAINFO:
result = setstropt(&data->set.str[STRING_SSL_CAFILE],
va_arg(param, char *));
break;
case CURLOPT_CAPATH:
result = setstropt(&data->set.str[STRING_SSL_CAPATH],
va_arg(param, char *));
break;
case CURLOPT_CRLFILE:
result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
va_arg(param, char *));
break;
case CURLOPT_ISSUERCERT:
result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
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);
}
}
break;
case CURLOPT_PRIVATE:
data->set.private_data = va_arg(param, void *);
break;
case CURLOPT_MAXFILESIZE:
data->set.max_filesize = va_arg(param, long);
break;
#ifdef USE_SSL
case CURLOPT_USE_SSL:
data->set.ftp_ssl = (curl_usessl)va_arg(param, long);
break;
#endif
case CURLOPT_FTPSSLAUTH:
data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
break;
case CURLOPT_IPRESOLVE:
data->set.ipver = 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:
result = setstropt(&data->set.str[STRING_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:
result = setstropt(&data->set.str[STRING_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_OPENSOCKETFUNCTION:
data->set.fopensocket = va_arg(param, curl_opensocket_callback);
break;
case CURLOPT_OPENSOCKETDATA:
data->set.opensocket_client = va_arg(param, void *);
break;
case CURLOPT_SSL_SESSIONID_CACHE:
data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
break;
#ifdef USE_LIBSSH2
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
va_arg(param, char *));
break;
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
case CURLOPT_SSH_KNOWNHOSTS:
result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
va_arg(param, char *));
break;
case CURLOPT_SSH_KEYFUNCTION:
data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
break;
case CURLOPT_SSH_KEYDATA:
data->set.ssh_keyfunc_userp = va_arg(param, void *);
break;
#endif
#endif
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;
case CURLOPT_NEW_FILE_PERMS:
data->set.new_file_perms = va_arg(param, long);
break;
case CURLOPT_NEW_DIRECTORY_PERMS:
data->set.new_directory_perms = va_arg(param, long);
break;
case CURLOPT_ADDRESS_SCOPE:
data->set.scope = curlx_sltoui(va_arg(param, long));
break;
case CURLOPT_PROTOCOLS:
data->set.allowed_protocols = va_arg(param, long) & PROT_EXTMASK;
break;
case CURLOPT_REDIR_PROTOCOLS:
data->set.redir_protocols = va_arg(param, long) & PROT_EXTMASK;
break;
case CURLOPT_MAIL_FROM:
result = setstropt(&data->set.str[STRING_MAIL_FROM],
va_arg(param, char *));
break;
case CURLOPT_MAIL_RCPT:
data->set.mail_rcpt = va_arg(param, struct curl_slist *);
break;
case CURLOPT_RTSP_REQUEST:
{
long curl_rtspreq = va_arg(param, long);
Curl_RtspReq rtspreq = RTSPREQ_NONE;
switch(curl_rtspreq) {
case CURL_RTSPREQ_OPTIONS:
rtspreq = RTSPREQ_OPTIONS;
break;
case CURL_RTSPREQ_DESCRIBE:
rtspreq = RTSPREQ_DESCRIBE;
break;
case CURL_RTSPREQ_ANNOUNCE:
rtspreq = RTSPREQ_ANNOUNCE;
break;
case CURL_RTSPREQ_SETUP:
rtspreq = RTSPREQ_SETUP;
break;
case CURL_RTSPREQ_PLAY:
rtspreq = RTSPREQ_PLAY;
break;
case CURL_RTSPREQ_PAUSE:
rtspreq = RTSPREQ_PAUSE;
break;
case CURL_RTSPREQ_TEARDOWN:
rtspreq = RTSPREQ_TEARDOWN;
break;
case CURL_RTSPREQ_GET_PARAMETER:
rtspreq = RTSPREQ_GET_PARAMETER;
break;
case CURL_RTSPREQ_SET_PARAMETER:
rtspreq = RTSPREQ_SET_PARAMETER;
break;
case CURL_RTSPREQ_RECORD:
rtspreq = RTSPREQ_RECORD;
break;
case CURL_RTSPREQ_RECEIVE:
rtspreq = RTSPREQ_RECEIVE;
break;
default:
rtspreq = RTSPREQ_NONE;
}
data->set.rtspreq = rtspreq;
break;
}
case CURLOPT_RTSP_SESSION_ID:
result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
va_arg(param, char *));
break;
case CURLOPT_RTSP_STREAM_URI:
result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
va_arg(param, char *));
break;
case CURLOPT_RTSP_TRANSPORT:
result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
va_arg(param, char *));
break;
case CURLOPT_RTSP_CLIENT_CSEQ:
data->state.rtsp_next_client_CSeq = va_arg(param, long);
break;
case CURLOPT_RTSP_SERVER_CSEQ:
data->state.rtsp_next_client_CSeq = va_arg(param, long);
break;
case CURLOPT_INTERLEAVEDATA:
data->set.rtp_out = va_arg(param, void *);
break;
case CURLOPT_INTERLEAVEFUNCTION:
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
break;
case CURLOPT_WILDCARDMATCH:
data->set.wildcardmatch = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_CHUNK_BGN_FUNCTION:
data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
break;
case CURLOPT_CHUNK_END_FUNCTION:
data->set.chunk_end = va_arg(param, curl_chunk_end_callback);
break;
case CURLOPT_FNMATCH_FUNCTION:
data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
break;
case CURLOPT_CHUNK_DATA:
data->wildcard.customptr = va_arg(param, void *);
break;
case CURLOPT_FNMATCH_DATA:
data->set.fnmatch_data = va_arg(param, void *);
break;
#ifdef USE_TLS_SRP
case CURLOPT_TLSAUTH_USERNAME:
result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
va_arg(param, char *));
if (data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
data->set.ssl.authtype = CURL_TLSAUTH_SRP;
break;
case CURLOPT_TLSAUTH_PASSWORD:
result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
va_arg(param, char *));
if (data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
data->set.ssl.authtype = CURL_TLSAUTH_SRP;
break;
case CURLOPT_TLSAUTH_TYPE:
if (strncmp((char *)va_arg(param, char *), "SRP", strlen("SRP")) == 0)
data->set.ssl.authtype = CURL_TLSAUTH_SRP;
else
data->set.ssl.authtype = CURL_TLSAUTH_NONE;
break;
#endif
default:
result = CURLE_FAILED_INIT;
break;
}
return result;
}
static void conn_free(struct connectdata *conn)
{
if(!conn)
return;
Curl_ssl_close(conn, FIRSTSOCKET);
Curl_ssl_close(conn, SECONDARYSOCKET);
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->allocptr.rtsp_transport);
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);
Curl_llist_destroy(conn->pend_pipe, NULL);
Curl_llist_destroy(conn->done_pipe, NULL);
#if defined(CURLRES_THREADED)
Curl_destroy_thread_data(&conn->async);
#elif defined(CURLRES_ASYNCH)
Curl_safefree(conn->async.hostname);
Curl_safefree(conn->async.os_specific);
#endif
Curl_free_ssl_config(&conn->ssl_config);
free(conn);
}
CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
{
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(conn->dns_entry != NULL) {
Curl_resolv_unlock(data, conn->dns_entry);
conn->dns_entry = NULL;
}
#if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST)
Curl_hash_apply(data->hostcache,
NULL, Curl_scan_cache_used);
#endif
Curl_hostcache_prune(data);
{
int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);
if (has_host_ntlm) {
data->state.authhost.done = FALSE;
data->state.authhost.picked =
data->state.authhost.want;
}
if (has_proxy_ntlm) {
data->state.authproxy.done = FALSE;
data->state.authproxy.picked =
data->state.authproxy.want;
}
if (has_host_ntlm || has_proxy_ntlm) {
data->state.authproblem = FALSE;
Curl_ntlm_cleanup(conn);
}
}
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
if(conn->handler->disconnect)
conn->handler->disconnect(conn, dead_connection);
if(-1 != conn->connectindex) {
infof(data, "Closing connection #%ld\n", conn->connectindex);
if(data->state.connc)
data->state.connc->connects[conn->connectindex] = NULL;
}
#if defined(USE_LIBIDN)
if(conn->host.encalloc)
idn_free(conn->host.encalloc);
if(conn->proxy.encalloc)
idn_free(conn->proxy.encalloc);
#elif defined(USE_WIN32_IDN)
free(conn->host.encalloc);
if(conn->proxy.encalloc)
free(conn->proxy.encalloc);
#endif
Curl_ssl_close(conn, FIRSTSOCKET);
if(Curl_isPipeliningEnabled(data)) {
signalPipeClose(conn->send_pipe, TRUE);
signalPipeClose(conn->recv_pipe, TRUE);
signalPipeClose(conn->pend_pipe, TRUE);
signalPipeClose(conn->done_pipe, FALSE);
}
conn_free(conn);
data->state.current_conn = NULL;
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;
}
#ifndef CURL_DISABLE_RTSP
static bool RTSPConnIsDead(struct connectdata *check)
{
int sval;
bool ret_val = TRUE;
sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0);
if(sval == 0) {
ret_val = FALSE;
}
else if (sval & CURL_CSELECT_ERR) {
ret_val = TRUE;
}
else if (sval & CURL_CSELECT_IN) {
curl_socket_t connectinfo =
Curl_getconnectinfo(check->data, &check);
if(connectinfo != CURL_SOCKET_BAD)
ret_val = FALSE;
}
return ret_val;
}
#endif
static bool IsPipeliningPossible(const struct SessionHandle *handle,
const struct connectdata *conn)
{
if((conn->handler->protocol & PROT_HTTP) &&
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;
}
bool Curl_isPipeliningEnabled(const struct SessionHandle *handle)
{
if(handle->multi && Curl_multi_canPipeline(handle->multi))
return TRUE;
return FALSE;
}
CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
struct curl_llist *pipeline)
{
if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
while(curr) {
if(curr->ptr == handle) {
Curl_llist_remove(pipeline, curr, NULL);
return 1;
}
curr = curr->next;
}
return 0;
}
#if 0
static void Curl_printPipeline(struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
while(curr) {
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
infof(data, "Handle in pipeline: %s\n", data->state.path);
curr = curr->next;
}
}
#endif
static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
{
struct curl_llist_element *curr = pipeline->head;
if(curr) {
return (struct SessionHandle *) curr->ptr;
}
return NULL;
}
void Curl_getoff_all_pipelines(struct SessionHandle *data,
struct connectdata *conn)
{
bool recv_head = (bool)(conn->readchannel_inuse &&
(gethandleathead(conn->recv_pipe) == data));
bool send_head = (bool)(conn->writechannel_inuse &&
(gethandleathead(conn->send_pipe) == data));
if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
conn->readchannel_inuse = FALSE;
if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
conn->writechannel_inuse = FALSE;
Curl_removeHandleFromPipeline(data, conn->pend_pipe);
Curl_removeHandleFromPipeline(data, conn->done_pipe);
}
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
{
struct curl_llist_element *curr;
if(!pipeline)
return;
curr = pipeline->head;
while(curr) {
struct curl_llist_element *next = curr->next;
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
#ifdef DEBUGBUILD
if(data->magic != CURLEASY_MAGIC_NUMBER) {
infof(data, "signalPipeClose() found BAAD easy handle\n");
}
#endif
if (pipe_broke)
data->state.pipe_broke = TRUE;
Curl_multi_handlePipeBreak(data);
Curl_llist_remove(pipeline, 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, needle);
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 && !check->inuse) {
bool dead;
#ifndef CURL_DISABLE_RTSP
if(check->protocol & PROT_RTSP)
dead = RTSPConnIsDead(check);
else
#endif
dead = SocketIsDead(check->sock[FIRSTSOCKET]);
if(dead) {
check->data = data;
infof(data, "Connection #%ld seems to be dead!\n", i);
Curl_disconnect(check, TRUE);
data->state.connc->connects[i]=NULL;
continue;
}
}
if(canPipeline) {
struct SessionHandle* sh = gethandleathead(check->send_pipe);
struct SessionHandle* rh = gethandleathead(check->recv_pipe);
if(sh) {
if(!IsPipeliningPossible(sh, check))
continue;
}
else if(rh) {
if(!IsPipeliningPossible(rh, check))
continue;
}
#ifdef DEBUGBUILD
if(pipeLen > MAX_PIPELINE_LENGTH) {
infof(data, "BAD! Connection #%ld has too big pipeline!\n",
check->connectindex);
}
#endif
}
else {
if(pipeLen > 0) {
continue;
}
#ifdef CURLRES_ASYNCH
if(!check->ip_addr_str[0]) {
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 DEBUGBUILD
if(check->recv_pipe->size > 0) {
infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connectindex);
}
#endif
continue;
}
}
if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
continue;
if(needle->protocol&PROT_SSL) {
if((data->set.ssl.verifypeer != check->verifypeer) ||
(data->set.ssl.verifyhost != check->verifyhost))
continue;
}
if(needle->bits.proxy != check->bits.proxy)
continue;
if(!canPipeline && check->inuse)
continue;
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL ||
(needle->bits.httpproxy && check->bits.httpproxy &&
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
(needle->port == check->port))) {
if(Curl_raw_equal(needle->handler->scheme, check->handler->scheme) &&
Curl_raw_equal(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)) {
DEBUGF(infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connectindex));
continue;
}
else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
DEBUGF(infof(data,
"Connection #%ld has not started ssl connect, "
"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) &&
(needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
match = TRUE;
}
}
if(match) {
check->inuse = 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, FALSE);
data->state.connc->connects[connindex] = NULL;
}
return connindex;
}
static void
ConnectionDone(struct connectdata *conn)
{
conn->inuse = 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 (#%ld) was killed to make room (holds %ld)\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;
}
CURLcode Curl_connected_proxy(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
switch(data->set.proxytype) {
#ifndef CURL_DISABLE_PROXY
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
conn->host.name, conn->remote_port,
FIRSTSOCKET, conn);
break;
case CURLPROXY_SOCKS4:
result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, FALSE);
break;
case CURLPROXY_SOCKS4A:
result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, TRUE);
break;
#endif
case CURLPROXY_HTTP:
case CURLPROXY_HTTP_1_0:
break;
default:
break;
}
return result;
}
static CURLcode ConnectPlease(struct SessionHandle *data,
struct connectdata *conn,
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 %ld (#%ld)\n",
conn->bits.proxy?"proxy ":"",
hostname, conn->port, conn->connectindex);
#else
(void)data;
#endif
result= Curl_connecthost(conn,
conn->dns_entry,
&conn->sock[FIRSTSOCKET],
&addr,
connected);
if(CURLE_OK == result) {
conn->ip_addr = addr;
if(*connected)
result = Curl_connected_proxy(conn);
}
if(result)
*connected = FALSE;
return result;
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
void Curl_verboseconnect(struct connectdata *conn)
{
if(conn->data->set.verbose)
infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\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->handler->proto_getsock)
return conn->handler->proto_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
int Curl_doing_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
if(conn && conn->handler->doing_getsock)
return conn->handler->doing_getsock(conn, socks, numsocks);
return GETSOCK_BLANK;
}
CURLcode Curl_protocol_connecting(struct connectdata *conn,
bool *done)
{
CURLcode result=CURLE_OK;
if(conn && conn->handler->connecting) {
*done = FALSE;
result = conn->handler->connecting(conn, done);
}
else
*done = TRUE;
return result;
}
CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
{
CURLcode result=CURLE_OK;
if(conn && conn->handler->doing) {
*done = FALSE;
result = conn->handler->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->handler->connecting)
*protocol_done = TRUE;
return CURLE_OK;
}
if(!conn->bits.tcpconnect) {
Curl_pgrsTime(data, TIMER_CONNECT);
Curl_verboseconnect(conn);
}
if(!conn->bits.protoconnstart) {
if(conn->handler->connect_it) {
conn->now = Curl_tvnow();
result = conn->handler->connect_it(conn, protocol_done);
}
else
*protocol_done = TRUE;
if(!result)
conn->bits.protoconnstart = TRUE;
}
return result;
}
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;
}
#ifdef USE_LIBIDN
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
const char *tld_errmsg = "<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 = 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);
if(rc != TLD_SUCCESS)
return FALSE;
return TRUE;
}
#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;
if(!is_ASCII_name(host->name)) {
#ifdef USE_LIBIDN
if(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;
}
}
#elif defined(USE_WIN32_IDN)
char *ace_hostname = NULL;
int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname, 0);
if(rc == 0)
infof(data, "Failed to convert %s to ACE;\n",
host->name);
else {
host->encalloc = ace_hostname;
host->name = host->encalloc;
}
#else
infof (data, "IDN support not present, can't parse Unicode (UTF-8) domains");
#endif
}
}
static void llist_dtor(void *user, void *element)
{
(void)user;
(void)element;
}
static struct connectdata *allocate_conn(struct SessionHandle *data)
{
struct connectdata *conn = calloc(1, sizeof(struct connectdata));
if(!conn)
return NULL;
conn->handler = &Curl_handler_dummy;
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
conn->connectindex = -1;
conn->port = -1;
conn->bits.close = TRUE;
conn->created = Curl_tvnow();
conn->data = data;
conn->proxytype = data->set.proxytype;
#ifdef CURL_DISABLE_PROXY
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.proxy_user_passwd = FALSE;
conn->bits.tunnel_proxy = FALSE;
#else
conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] &&
*data->set.str[STRING_PROXY]);
conn->bits.httpproxy = (bool)(conn->bits.proxy &&
(conn->proxytype == CURLPROXY_HTTP ||
conn->proxytype == CURLPROXY_HTTP_1_0));
conn->bits.proxy_user_passwd =
(bool)(NULL != data->set.str[STRING_PROXYUSERNAME]);
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
#endif
conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERNAME]);
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
conn->verifypeer = data->set.ssl.verifypeer;
conn->verifyhost = data->set.ssl.verifyhost;
conn->ip_version = data->set.ipver;
if(data->multi && Curl_multi_canPipeline(data->multi) &&
!conn->master_buffer) {
conn->master_buffer = calloc(BUFSIZE, sizeof (char));
if(!conn->master_buffer)
goto error;
}
conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->done_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe ||
!conn->done_pipe)
goto error;
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
conn->data_prot = PROT_CLEAR;
#endif
return conn;
error:
Curl_llist_destroy(conn->send_pipe, NULL);
Curl_llist_destroy(conn->recv_pipe, NULL);
Curl_llist_destroy(conn->pend_pipe, NULL);
Curl_llist_destroy(conn->done_pipe, NULL);
Curl_safefree(conn->master_buffer);
Curl_safefree(conn);
return NULL;
}
static CURLcode findprotocol(struct SessionHandle *data,
struct connectdata *conn,
const char *protostr)
{
const struct Curl_handler * const *pp;
const struct Curl_handler *p;
for (pp = protocols; (p = *pp) != NULL; pp++) {
if(Curl_raw_equal(p->scheme, protostr)) {
if(!(data->set.allowed_protocols & p->protocol))
break;
if(data->state.this_is_a_follow &&
!(data->set.redir_protocols & p->protocol))
break;
conn->handler = p;
conn->protocol |= p->protocol;
return CURLE_OK;
}
}
failf(data, "Protocol %s not supported or disabled in " LIBCURL_NAME,
protostr);
return CURLE_UNSUPPORTED_PROTOCOL;
}
static CURLcode parseurlandfillconn(struct SessionHandle *data,
struct connectdata *conn,
bool *prot_missing)
{
char *at;
char *fragment;
char *path = data->state.path;
char *query;
int rc;
char protobuf[16];
const char *protop;
*prot_missing = FALSE;
if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
protobuf, path)) &&
Curl_raw_equal(protobuf, "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);
}
}
protop = "file";
}
else {
path[0]=0;
if(2 > sscanf(data->change.url,
"%15[^\n:]://%[^\n/?]%[^\n]",
protobuf,
conn->host.name, path)) {
rc = sscanf(data->change.url, "%[^\n/?]%[^\n]", conn->host.name, path);
if(1 > rc) {
#if defined(__DJGPP__) && (DJGPP_MINOR == 4)
if (!(rc == -1 && *conn->host.name))
#endif
{
failf(data, "<url> malformed");
return CURLE_URL_MALFORMAT;
}
}
if(checkprefix("FTP.", conn->host.name))
protop = "ftp";
else if(checkprefix("DICT.", conn->host.name))
protop = "DICT";
else if(checkprefix("LDAP.", conn->host.name))
protop = "LDAP";
else if(checkprefix("IMAP.", conn->host.name))
protop = "IMAP";
else {
protop = "http";
}
*prot_missing = TRUE;
}
else
protop = protobuf;
}
at = strchr(conn->host.name, '@');
if(at)
query = strchr(at+1, '?');
else
query = strchr(conn->host.name, '?');
if(query) {
size_t hostlen = strlen(query);
size_t pathlen = strlen(path);
memmove(path+hostlen+1, path, pathlen+1);
memcpy(path+1, query, hostlen);
path[0]='/';
*query=0;
}
else if(!path[0]) {
strcpy(path, "/");
}
if(path[0] == '?') {
memmove(&path[1], path, strlen(path)+1);
path[0] = '/';
}
if (conn->host.name[0] == '[') {
char *percent = strstr (conn->host.name, "%25");
if (percent) {
char *endp;
unsigned long scope = strtoul (percent + 3, &endp, 10);
if (*endp == ']') {
memmove(percent, endp, strlen(endp)+1);
if (!data->state.this_is_a_follow)
conn->scope = (unsigned int)scope;
} else
infof(data, "Invalid IPv6 address format\n");
}
}
if(data->set.scope)
conn->scope = data->set.scope;
fragment = strchr(path, '#');
if(fragment)
*fragment = 0;
return findprotocol(data, conn, protop);
}
static CURLcode setup_range(struct SessionHandle *data)
{
struct UrlState *s = &data->state;
s->resume_from = data->set.set_resume_from;
if(s->resume_from || data->set.str[STRING_SET_RANGE]) {
if(s->rangestringalloc)
free(s->range);
if(s->resume_from)
s->range = aprintf("%" FORMAT_OFF_TU "-", s->resume_from);
else
s->range = strdup(data->set.str[STRING_SET_RANGE]);
s->rangestringalloc = (bool)(s->range?TRUE:FALSE);
if(!s->range)
return CURLE_OUT_OF_MEMORY;
s->use_range = TRUE;
}
else
s->use_range = FALSE;
return CURLE_OK;
}
static CURLcode setup_connection_internals(struct connectdata *conn)
{
const struct Curl_handler * p;
CURLcode result;
conn->socktype = SOCK_STREAM;
p = conn->handler;
if(p->setup_connection) {
result = (*p->setup_connection)(conn);
if(result != CURLE_OK)
return result;
p = conn->handler;
}
if(conn->port < 0)
conn->port = p->defport;
conn->remote_port = (unsigned short)p->defport;
conn->protocol |= p->protocol;
return CURLE_OK;
}
#ifndef CURL_DISABLE_PROXY
static bool check_noproxy(const char* name, const char* no_proxy)
{
size_t tok_start;
size_t tok_end;
const char* separator = ", ";
size_t no_proxy_len;
size_t namelen;
char *endptr;
if(no_proxy && no_proxy[0]) {
if(Curl_raw_equal("*", no_proxy)) {
return TRUE;
}
no_proxy_len = strlen(no_proxy);
endptr = strchr(name, ':');
if(endptr)
namelen = endptr - name;
else
namelen = strlen(name);
for (tok_start = 0; tok_start < no_proxy_len; tok_start = tok_end + 1) {
while (tok_start < no_proxy_len &&
strchr(separator, no_proxy[tok_start]) != NULL) {
++tok_start;
}
if(tok_start == no_proxy_len)
break;
for (tok_end = tok_start; tok_end < no_proxy_len &&
strchr(separator, no_proxy[tok_end]) == NULL; ++tok_end) {
}
if(no_proxy[tok_start] == '.')
++tok_start;
if((tok_end - tok_start) <= namelen) {
const char *checkn = name + namelen - (tok_end - tok_start);
if(Curl_raw_nequal(no_proxy + tok_start, checkn,
tok_end - tok_start)) {
if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') {
return TRUE;
}
}
}
}
}
return FALSE;
}
static char *detect_proxy(struct connectdata *conn)
{
char *proxy = NULL;
#ifndef CURL_DISABLE_HTTP
char *no_proxy=NULL;
char proxy_env[128];
no_proxy=curl_getenv("no_proxy");
if(!no_proxy)
no_proxy=curl_getenv("NO_PROXY");
if(!check_noproxy(conn->host.name, no_proxy)) {
const char *protop = conn->handler->scheme;
char *envp = proxy_env;
char *prox;
while(*protop)
*envp++ = (char)tolower((int)*protop++);
strcpy(envp, "_proxy");
prox=curl_getenv(proxy_env);
if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) {
Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
prox=curl_getenv(proxy_env);
}
if(prox && *prox) {
proxy = prox;
}
else {
proxy = curl_getenv("all_proxy");
if(!proxy)
proxy=curl_getenv("ALL_PROXY");
}
}
if(no_proxy)
free(no_proxy);
#else
(void)conn;
#endif
return proxy;
}
static CURLcode parse_proxy(struct SessionHandle *data,
struct connectdata *conn, char *proxy)
{
char *prox_portno;
char *endofprot;
char *proxyptr;
char *portptr;
char *atsign;
endofprot = strstr(proxy, "://");
if(endofprot)
proxyptr = endofprot+3;
else
proxyptr = proxy;
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 == '%') ||
(*ptr == '.')))
ptr++;
if(*ptr == ']') {
*ptr++ = 0;
} else
infof(data, "Invalid IPv6 address format\n");
portptr = ptr;
}
prox_portno = strchr(portptr, ':');
if(prox_portno) {
*prox_portno = 0x0;
prox_portno ++;
conn->port = strtol(prox_portno, NULL, 10);
}
else {
atsign = strchr(proxyptr, '/');
if(atsign)
*atsign = 0x0;
if(data->set.proxyport)
conn->port = data->set.proxyport;
}
conn->proxy.rawalloc = strdup(proxyptr);
conn->proxy.name = conn->proxy.rawalloc;
free(proxy);
if(!conn->proxy.rawalloc)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
static CURLcode parse_proxy_auth(struct SessionHandle *data,
struct connectdata *conn)
{
char proxyuser[MAX_CURL_USER_LENGTH]="";
char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
if(data->set.str[STRING_PROXYUSERNAME] != NULL) {
strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME],
MAX_CURL_USER_LENGTH);
proxyuser[MAX_CURL_USER_LENGTH-1] = '\0';
}
if(data->set.str[STRING_PROXYPASSWORD] != NULL) {
strncpy(proxypasswd, data->set.str[STRING_PROXYPASSWORD],
MAX_CURL_PASSWORD_LENGTH);
proxypasswd[MAX_CURL_PASSWORD_LENGTH-1] = '\0';
}
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;
return CURLE_OK;
}
#endif
static CURLcode parse_url_userpass(struct SessionHandle *data,
struct connectdata *conn,
char *user, char *passwd)
{
char *ptr=strchr(conn->host.name, '@');
char *userpass = conn->host.name;
user[0] =0;
passwd[0]=0;
if(ptr != NULL) {
conn->host.name = ++ptr;
conn->bits.userpwd_in_url = TRUE;
if(data->set.use_netrc != CURL_NETRC_REQUIRED) {
conn->bits.user_passwd = TRUE;
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) < MAX_CURL_USER_LENGTH)
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) < MAX_CURL_PASSWORD_LENGTH)
strcpy(passwd, newpasswd);
free(newpasswd);
}
}
}
return CURLE_OK;
}
static CURLcode parse_remote_port(struct SessionHandle *data,
struct connectdata *conn)
{
char *portptr;
char endbracket;
if((1 == sscanf(conn->host.name, "[%*45[0123456789abcdefABCDEF:.]%c",
&endbracket)) &&
(']' == endbracket)) {
conn->bits.ipv6_ip = TRUE;
conn->host.name++;
portptr = strchr(conn->host.name, ']');
if(portptr) {
*portptr++ = '\0';
if(':' != *portptr)
portptr = NULL;
}
}
else
portptr = strrchr(conn->host.name, ':');
if(data->set.use_port && data->state.allow_port) {
conn->remote_port = (unsigned short)data->set.use_port;
if(portptr)
*portptr = '\0';
if(conn->bits.httpproxy) {
char *url;
char type[12]="";
if(conn->bits.type_set)
snprintf(type, sizeof(type), ";type=%c",
data->set.prefer_ascii?'A':
(data->set.ftp_list_only?'D':'I'));
url = aprintf("%s://%s%s%s:%hu%s%s%s", conn->handler->scheme,
conn->bits.ipv6_ip?"[":"", conn->host.name,
conn->bits.ipv6_ip?"]":"", conn->remote_port,
data->state.slash_removed?"/":"", data->state.path,
type);
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(portptr) {
char *rest;
unsigned long port;
port=strtoul(portptr+1, &rest, 10);
if(rest != (portptr+1) && *rest == '\0') {
if(port > 0xffff) {
failf(data, "Port number too large: %lu", port);
return CURLE_URL_MALFORMAT;
}
*portptr = '\0';
conn->remote_port = curlx_ultous(port);
}
else if(!port)
*portptr = '\0';
}
return CURLE_OK;
}
static void override_userpass(struct SessionHandle *data,
struct connectdata *conn,
char *user, char *passwd)
{
if(data->set.str[STRING_USERNAME] != NULL) {
strncpy(user, data->set.str[STRING_USERNAME], MAX_CURL_USER_LENGTH);
user[MAX_CURL_USER_LENGTH-1] = '\0';
}
if(data->set.str[STRING_PASSWORD] != NULL) {
strncpy(passwd, data->set.str[STRING_PASSWORD], MAX_CURL_PASSWORD_LENGTH);
passwd[MAX_CURL_PASSWORD_LENGTH-1] = '\0';
}
conn->bits.netrc = FALSE;
if(data->set.use_netrc != CURL_NETRC_IGNORED) {
if(Curl_parsenetrc(conn->host.name,
user, passwd,
data->set.str[STRING_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 = TRUE;
}
}
}
static CURLcode set_userpass(struct connectdata *conn,
const char *user, const char *passwd)
{
if( (conn->protocol & (PROT_FTP|PROT_IMAP)) &&
!conn->bits.user_passwd) {
conn->user = strdup(CURL_DEFAULT_USER);
if(conn->user)
conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
else
conn->passwd = NULL;
}
else {
conn->user = strdup(user);
if(conn->user)
conn->passwd = strdup(passwd);
else
conn->passwd = NULL;
}
if(!conn->user || !conn->passwd)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
static CURLcode resolve_server(struct SessionHandle *data,
struct connectdata *conn,
bool *async)
{
CURLcode result=CURLE_OK;
long timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(conn->bits.reuse) {
*async = FALSE;
if(conn->bits.proxy)
fix_hostname(data, conn, &conn->host);
}
else {
int rc;
struct Curl_dns_entry *hostaddr;
fix_hostname(data, conn, &conn->host);
if(!conn->proxy.name || !*conn->proxy.name) {
conn->port = conn->remote_port;
rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
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_timeout(conn, conn->proxy.name, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
result = CURLE_COULDNT_RESOLVE_PROXY;
}
}
DEBUGASSERT(conn->dns_entry == NULL);
conn->dns_entry = hostaddr;
}
return result;
}
static void reuse_conn(struct connectdata *old_conn,
struct connectdata *conn)
{
if(old_conn->proxy.rawalloc)
free(old_conn->proxy.rawalloc);
Curl_free_ssl_config(&old_conn->ssl_config);
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);
Curl_persistconninfo(conn);
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_llist_destroy(old_conn->pend_pipe, NULL);
Curl_llist_destroy(old_conn->done_pipe, NULL);
Curl_safefree(old_conn->master_buffer);
}
static CURLcode create_conn(struct SessionHandle *data,
struct connectdata **in_connect,
bool *async)
{
CURLcode result=CURLE_OK;
struct connectdata *conn;
struct connectdata *conn_temp = NULL;
size_t urllen;
char user[MAX_CURL_USER_LENGTH];
char passwd[MAX_CURL_PASSWORD_LENGTH];
bool reuse;
char *proxy = NULL;
bool prot_missing = FALSE;
*async = FALSE;
if(!data->change.url)
return CURLE_URL_MALFORMAT;
conn = allocate_conn(data);
if(!conn)
return CURLE_OUT_OF_MEMORY;
*in_connect = conn;
#define LEAST_PATH_ALLOC 256
urllen=strlen(data->change.url);
if(urllen < LEAST_PATH_ALLOC)
urllen=LEAST_PATH_ALLOC;
Curl_safefree(data->state.pathbuffer);
data->state.pathbuffer = malloc(urllen+2);
if(NULL == data->state.pathbuffer)
return CURLE_OUT_OF_MEMORY;
data->state.path = data->state.pathbuffer;
conn->host.rawalloc = 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 = parseurlandfillconn(data, conn, &prot_missing);
if(result != CURLE_OK) {
return result;
}
if(prot_missing) {
char *reurl;
reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url);
if(!reurl) {
Curl_safefree(proxy);
return CURLE_OUT_OF_MEMORY;
}
data->change.url = reurl;
data->change.url_alloc = TRUE;
}
result = parse_url_userpass(data, conn, user, passwd);
if(result != CURLE_OK)
return result;
#ifndef CURL_DISABLE_PROXY
if(conn->bits.proxy_user_passwd) {
result = parse_proxy_auth(data, conn);
if(result != CURLE_OK)
return result;
}
if(data->set.str[STRING_PROXY]) {
proxy = strdup(data->set.str[STRING_PROXY]);
if(NULL == proxy) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
}
if(data->set.str[STRING_NOPROXY] &&
check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
if(proxy) {
free(proxy);
proxy = NULL;
}
}
else if(!proxy)
proxy = detect_proxy(conn);
if(proxy && !*proxy) {
free(proxy);
proxy = NULL;
}
if(proxy) {
long bits = conn->protocol & (PROT_HTTPS|PROT_SSL);
if((conn->proxytype == CURLPROXY_HTTP) ||
(conn->proxytype == CURLPROXY_HTTP_1_0)) {
conn->protocol = PROT_HTTP | bits;
conn->bits.httpproxy = TRUE;
}
conn->bits.proxy = TRUE;
}
else {
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.proxy_user_passwd = FALSE;
conn->bits.tunnel_proxy = FALSE;
}
if(proxy) {
result = parse_proxy(data, conn, proxy);
proxy = NULL;
if(result != CURLE_OK)
return result;
}
#endif
result = setup_connection_internals(conn);
if(result != CURLE_OK) {
Curl_safefree(proxy);
return result;
}
conn->recv[FIRSTSOCKET] = Curl_recv_plain;
conn->send[FIRSTSOCKET] = Curl_send_plain;
conn->recv[SECONDARYSOCKET] = Curl_recv_plain;
conn->send[SECONDARYSOCKET] = Curl_send_plain;
#ifndef CURL_DISABLE_FILE
if(conn->protocol & PROT_FILE) {
bool done;
DEBUGASSERT(conn->handler->connect_it);
result = conn->handler->connect_it(conn, &done);
if(CURLE_OK == result) {
conn->data = data;
conn->bits.tcpconnect = TRUE;
ConnectionStore(data, conn);
result = setup_range(data);
if(result) {
DEBUGASSERT(conn->handler->done);
(void)conn->handler->done(conn, result, FALSE);
return 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;
result = parse_remote_port(data, conn);
if(result != CURLE_OK)
return result;
override_userpass(data, conn, user, passwd);
result = set_userpass(conn, user, passwd);
if(result != CURLE_OK)
return result;
data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
#ifdef USE_TLS_SRP
data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME];
data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD];
#endif
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) {
reuse_conn(conn, conn_temp);
free(conn);
conn = conn_temp;
*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_func = data->set.fread_func;
conn->fread_in = data->set.in;
conn->seek_func = data->set.seek_func;
conn->seek_client = data->set.seek_client;
result = resolve_server(data, conn, async);
return result;
}
static CURLcode setup_conn(struct connectdata *conn,
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;
conn->bits.proxy_connect_closed = FALSE;
if(data->set.str[STRING_USERAGENT]) {
Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
if(!conn->allocptr.uagent)
return CURLE_OUT_OF_MEMORY;
}
data->req.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, &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);
Curl_pgrsTime(data, TIMER_APPCONNECT);
conn->bits.tcpconnect = TRUE;
*protocol_done = TRUE;
Curl_verboseconnect(conn);
Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
}
break;
}
conn->now = Curl_tvnow();
#ifdef __EMX__
if((data->set.out)->_handle == NULL) {
_fsetmode(stdout, "b");
}
#endif
return result;
}
CURLcode Curl_connect(struct SessionHandle *data,
struct connectdata **in_connect,
bool *asyncp,
bool *protocol_done)
{
CURLcode code;
*asyncp = FALSE;
code = create_conn(data, in_connect, asyncp);
if(CURLE_OK == code) {
if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size)
*protocol_done = TRUE;
else if (!*asyncp) {
code = setup_conn(*in_connect, protocol_done);
}
}
if(code && *in_connect) {
Curl_disconnect(*in_connect, FALSE);
*in_connect = NULL;
}
return code;
}
CURLcode Curl_async_resolved(struct connectdata *conn,
bool *protocol_done)
{
#ifdef CURLRES_ASYNCH
CURLcode code;
if(conn->async.dns) {
conn->dns_entry = conn->async.dns;
conn->async.dns = NULL;
}
code = setup_conn(conn, protocol_done);
if(code)
Curl_disconnect(conn, FALSE);
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;
struct SessionHandle *data;
DEBUGASSERT(*connp);
conn = *connp;
data = conn->data;
if(conn->bits.done)
return CURLE_OK;
Curl_getoff_all_pipelines(data, conn);
if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
!data->set.reuse_forbid &&
!conn->bits.close))
return CURLE_OK;
conn->bits.done = TRUE;
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
if(data->req.location) {
free(data->req.location);
data->req.location = NULL;
}
Curl_async_cancel(conn);
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry);
conn->dns_entry = NULL;
}
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
else
result = CURLE_OK;
Curl_pgrsDone(conn);
if(data->state.tempwrite) {
free(data->state.tempwrite);
data->state.tempwrite = NULL;
}
if(data->set.reuse_forbid || conn->bits.close || premature ||
(-1 == conn->connectindex)) {
CURLcode res2 = Curl_disconnect(conn, FALSE);
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;
}
static CURLcode do_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
conn->bits.done = FALSE;
conn->bits.do_more = FALSE;
data->state.expect100header = FALSE;
if(data->set.opt_no_body)
data->set.httpreq = HTTPREQ_HEAD;
else if(HTTPREQ_HEAD == data->set.httpreq)
data->set.httpreq = HTTPREQ_GET;
Curl_easy_initHandleData(data);
k->start = Curl_tvnow();
k->now = k->start;
k->header = TRUE;
k->bytecount = 0;
k->buf = data->state.buffer;
k->uploadbuf = data->state.uploadbuffer;
k->hbufp = data->state.headerbuff;
k->ignorebody=FALSE;
Curl_pgrsTime(data, TIMER_PRETRANSFER);
Curl_speedinit(data);
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
return CURLE_OK;
}
static void do_complete(struct connectdata *conn)
{
conn->data->req.chunk=FALSE;
conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
conn->sockfd:conn->writesockfd)+1;
}
CURLcode Curl_do(struct connectdata **connp, bool *done)
{
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
do_init(conn);
if(conn->handler->do_it) {
result = conn->handler->do_it(conn, done);
if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
if(!data->multi) {
result = Curl_reconnect_request(connp);
if(result == CURLE_OK) {
conn = *connp;
result = conn->handler->do_it(conn, done);
}
}
else
return result;
}
if((result == CURLE_OK) && *done)
do_complete(conn);
}
return result;
}
CURLcode Curl_do_more(struct connectdata *conn)
{
CURLcode result=CURLE_OK;
if(conn->handler->do_more)
result = conn->handler->do_more(conn);
if(result == CURLE_OK)
do_complete(conn);
return result;
}
void Curl_reset_reqproto(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
if(data->state.proto.generic && data->state.current_conn != conn) {
free(data->state.proto.generic);
data->state.proto.generic = NULL;
}
data->state.current_conn = conn;
}