Newer
Older
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
* $Id$
*****************************************************************************/
#include "setup.h"
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif
#include "urldata.h"
#include "netrc.h"
#include "formdata.h"
#include "base64.h"
#include "ssluse.h"
#include "hostip.h"
#include "if2ip.h"
Daniel Stenberg
committed
#include "transfer.h"
#include "sendf.h"
#include "getpass.h"
#include "progress.h"
#include "cookie.h"
#include "escape.h"
/* And now for the protocols */
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
#include "http.h"
#include "file.h"
#include "ldap.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
/* Local static prototypes */
static int ConnectionKillOne(struct UrlData *data);
static bool ConnectionExists(struct UrlData *data,
struct connectdata *needle,
struct connectdata **usethis);
static unsigned int ConnectionStore(struct UrlData *data,
struct connectdata *conn);
#if !defined(WIN32)||defined(__CYGWIN32__)
#ifndef RETSIGTYPE
#define RETSIGTYPE void
#endif
static
RETSIGTYPE alarmfunc(int signal)
{
/* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
(void)signal;
return;
}
#endif
CURLcode Curl_close(CURL *curl)
struct UrlData *data=(struct UrlData *)curl;
/* Loop through all open connections and kill them one by one */
while(-1 != ConnectionKillOne(data));
if(data->bits.proxystringalloc) {
data->bits.proxystringalloc=FALSE;;
free(data->proxy);
data->proxy=NULL;
/* Since we allocated the string the previous round, it means that we
"discovered" the proxy in the environment variables and thus we must
switch off that knowledge again... */
data->bits.httpproxy=FALSE;
/* check for allocated [URL] memory to free: */
if(data->freethis)
free(data->freethis);
if(data->headerbuff)
free(data->headerbuff);
if(data->free_referer)
free(data->referer);
if(data->bits.urlstringalloc)
/* the URL is allocated, free it! */
free(data->url);
Curl_cookie_cleanup(data->cookies);
/* free the connection cache */
free(data->connects);
static
int my_getpass(void *clientp, char *prompt, char* buffer, int buflen )
{
char *retbuf;
retbuf = getpass_r(prompt, buffer, buflen);
if(NULL == retbuf)
return 1;
else
return 0; /* success */
}
CURLcode Curl_open(CURL **curl, char *url)
{
/* We don't yet support specifying the URL at this point */
#ifdef HAVE_SIGACTION
struct sigaction sigact;
#endif
/* Very simple start-up: alloc the struct, init it with zeroes and return */
data = (struct UrlData *)malloc(sizeof(struct UrlData));
memset(data, 0, sizeof(struct UrlData));
/* We do some initial setup here, all those fields that can't be just 0 */
data-> headerbuff=(char*)malloc(HEADERSIZE);
if(!data->headerbuff) {
free(data); /* free the memory again */
return CURLE_OUT_OF_MEMORY;
}
Daniel Stenberg
committed
data->headersize=HEADERSIZE;
data->out = stdout; /* default output to stdout */
data->in = stdin; /* default input from stdin */
data->err = stderr; /* default stderr to stderr */
/* use fwrite as default function to store output */
data->fwrite = (curl_write_callback)fwrite;
/* use fread as default function to read input */
data->fread = (curl_read_callback)fread;
/* set the default passwd function */
data->fpasswd = my_getpass;
data->infilesize = -1; /* we don't know any size */
data->current_speed = -1; /* init to negative == impossible */
Daniel Stenberg
committed
data->httpreq = HTTPREQ_GET; /* Default HTTP request */
/* make libcurl quiet by default: */
data->bits.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
data->progress.flags |= PGRS_HIDE;
/* create an array with connection data struct pointers */
data->numconnects = 5; /* hard-coded right now */
data->connects = (struct connectdata **)
malloc(sizeof(struct connectdata *) * data->numconnects);
if(!data->connects) {
free(data);
return CURLE_OUT_OF_MEMORY;
}
memset(data->connects, 0, sizeof(struct connectdata *)*data->numconnects);
/*************************************************************
* Set signal handler
*************************************************************/
#ifdef HAVE_SIGACTION
sigaction(SIGALRM, NULL, &sigact);
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
/* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
sigact.sa_flags &= ~SA_RESTART;
#endif
sigaction(SIGALRM, &sigact, NULL);
#else
/* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
signal(SIGALRM, alarmfunc);
#endif
#endif
}
/* this is a very serious error */
CURLcode Curl_setopt(CURL *curl, CURLoption option, ...)
{
struct UrlData *data = curl;
va_list param;
char *cookiefile;
va_start(param, option);
switch(option) {
case CURLOPT_RANDOM_FILE:
/*
* This is the path name to a file that contains random data to seed
* the random SSL stuff with. The file is only used for reading.
*/
data->ssl.random_file = va_arg(param, char *);
break;
case CURLOPT_EGDSOCKET:
/*
* The Entropy Gathering Daemon socket pathname
*/
data->ssl.egdsocket = va_arg(param, char *);
break;
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
case CURLOPT_MAXCONNECTS:
/*
* Set the absolute number of maximum simultaneous alive connection that
* libcurl is allowed to have.
*/
{
long newconnects= va_arg(param, long);
struct connectdata **newptr;
if(newconnects < data->numconnects) {
/* Since this number is *decreased* from the existing number, we must
close the possibly open connections that live on the indexes that
are being removed! */
int i;
for(i=newconnects; i< data->numconnects; i++)
Curl_disconnect(data->connects[i]);
}
if(newconnects) {
newptr= (struct connectdata **)
realloc(data->connects,
sizeof(struct connectdata *) * newconnects);
if(!newptr)
/* we closed a few connections in vain, but so what? */
return CURLE_OUT_OF_MEMORY;
data->connects = newptr;
data->numconnects = newconnects;
}
else {
/* zero makes NO cache at all */
if(data->connects)
free(data->connects);
data->connects=NULL;
data->numconnects=0;
}
}
break;
case CURLOPT_FORBID_REUSE:
/*
* When this transfer is done, it must not be left to be reused by a
* subsequent transfer but shall be closed immediately.
*/
data->bits.reuse_forbid = va_arg(param, long)?TRUE:FALSE;
break;
case CURLOPT_FRESH_CONNECT:
/*
* This transfer shall not use a previously cached connection but
* should be made with a fresh new connect!
*/
data->bits.reuse_fresh = va_arg(param, long)?TRUE:FALSE;
break;
/*
* Verbose means infof() calls that give a lot of information about
* the connection and transfer procedures as well as internal choices.
*/
data->bits.verbose = va_arg(param, long)?TRUE:FALSE;
/*
* Set to include the header in the general data output stream.
*/
data->bits.http_include_header = va_arg(param, long)?TRUE:FALSE;
/*
* Shut off the internal supported progress meter
*/
data->bits.hide_progress = va_arg(param, long)?TRUE:FALSE;
if(data->bits.hide_progress)
data->progress.flags |= PGRS_HIDE;
else
data->progress.flags &= ~PGRS_HIDE;
/*
* Do not include the body part in the output data stream.
*/
data->bits.no_body = va_arg(param, long)?TRUE:FALSE;
break;
case CURLOPT_FAILONERROR:
/*
* Don't output the >=300 error code HTML-page, but instead only
* return error.
*/
data->bits.http_fail_on_error = va_arg(param, long)?TRUE:FALSE;
/*
* We want to sent data to the remote host
*/
data->bits.upload = va_arg(param, long)?TRUE:FALSE;
if(data->bits.upload)
/* If this is HTTP, PUT is what's needed to "upload" */
data->httpreq = HTTPREQ_PUT;
/*
* Try to get the file time of the remote document. The time will
* later (possibly) become available using curl_easy_getinfo().
*/
data->bits.get_filetime = va_arg(param, long)?TRUE:FALSE;
break;
/*
* An FTP option that changes the command to one that asks for a list
* only, no file info details.
*/
data->bits.ftp_list_only = va_arg(param, long)?TRUE:FALSE;
/*
* We want to upload and append to an existing (FTP) file.
*/
data->bits.ftp_append = va_arg(param, long)?TRUE:FALSE;
/*
* Parse the $HOME/.netrc file
*/
data->bits.use_netrc = va_arg(param, long)?TRUE:FALSE;
break;
case CURLOPT_FOLLOWLOCATION:
/*
* Follow Location: header hints on a HTTP-server.
*/
data->bits.http_follow_location = va_arg(param, long)?TRUE:FALSE;
/*
* Transfer FTP using ASCII instead of BINARY.
*/
data->bits.ftp_ascii = va_arg(param, long)?TRUE:FALSE;
* Use the HTTP PUT request to transfer data if this is TRUE. If this is
* FALSE, don't set the httpreq. We can't know what to revert it to!
#if 0
/* obsolete stuff, kept here a while for informational purposes */
/*
* Stay absolutely quiet.
*/
data->bits.mute = va_arg(param, long)?TRUE:FALSE;
/*
* Set HTTP time condition. This must be one of the defines in the
* curl/curl.h header file.
*/
data->timecondition = va_arg(param, long);
break;
/*
* This is the value to compare with the remote document with the
* method set with CURLOPT_TIMECONDITION
*/
data->timevalue = va_arg(param, long);
break;
/*
* Set explicit SSL version to try to connect with, as some SSL
* implementations are lame.
*/
Daniel Stenberg
committed
data->ssl.version = va_arg(param, long);
/*
* Set cookie file to read and parse.
*/
cookiefile = (char *)va_arg(param, void *);
if(cookiefile) {
data->cookies = Curl_cookie_init(cookiefile);
* Custom pointer to pass the header write callback function
data->writeheader = (void *)va_arg(param, void *);
/*
* Cookie string to send to the remote server in the request.
*/
data->cookie = va_arg(param, char *);
break;
/*
* Error buffer provided by the caller to get the human readable
* error string in.
*/
data->errorbuffer = va_arg(param, char *);
break;
/*
* FILE pointer to write to or include in the data write callback
*/
data->out = va_arg(param, FILE *);
break;
/*
* Use FTP PORT, this also specifies which IP address to use
*/
data->ftpport = va_arg(param, char *);
data->bits.ftp_use_port = data->ftpport?1:0;
/*
* Set a list with HTTP headers to use (or replace internals with)
*/
data->headers = va_arg(param, struct curl_slist *);
/*
* Set a custom string to use as request
*/
data->customrequest = va_arg(param, char *);
if(data->customrequest)
data->httpreq = HTTPREQ_CUSTOM;
/*
* Set to make us do HTTP POST
*/
data->httppost = va_arg(param, struct HttpPost *);
case CURLOPT_HTTPGET:
/*
* Set to force us do HTTP GET
*/
if(va_arg(param, long))
data->httpreq = HTTPREQ_GET;
break;
/*
* FILE pointer to read the file to be uploaded from. Or possibly
* used as argument to the read callback.
*/
data->in = va_arg(param, FILE *);
break;
/*
* If known, this should inform curl about the file size of the
* to-be-uploaded file.
*/
data->infilesize = va_arg(param, long);
break;
/*
* The low speed limit that if transfers are below this for
* CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
*/
data->low_speed_limit=va_arg(param, long);
break;
/*
* The low speed time that if transfers are below the set
* CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
*/
data->low_speed_time=va_arg(param, long);
break;
/*
* The URL to fetch.
*/
data->url = va_arg(param, char *);
break;
/*
* The port number to use when getting the URL
*/
data->use_port = va_arg(param, long);
case CURLOPT_POST:
/* Does this option serve a purpose anymore? */
data->httpreq = HTTPREQ_POST;
break;
/*
* A string with POST data. Makes curl HTTP POST.
*/
data->postfields = va_arg(param, char *);
data->httpreq = HTTPREQ_POST;
/*
* The size of the POSTFIELD data, if curl should now do a strlen
* to find out. Enables binary posts.
*/
data->postfieldsize = va_arg(param, long);
break;
/*
* String to set in the HTTP Referer: field.
*/
data->referer = va_arg(param, char *);
data->bits.http_set_referer = (data->referer && *data->referer)?1:0;
case CURLOPT_AUTOREFERER:
/*
* Switch on automatic referer that gets set if curl follows locations.
*/
data->bits.http_auto_referer = va_arg(param, long)?1:0;
break;
/*
* Set proxy server:port to use as HTTP proxy
*/
data->proxy = va_arg(param, char *);
data->bits.httpproxy = data->proxy?1:0;
case CURLOPT_HTTPPROXYTUNNEL:
/*
* Tunnel operations through the proxy instead of normal proxy use
*/
data->bits.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE;
break;
/*
* Explicitly set HTTP proxy port number.
*/
data->proxyport = va_arg(param, long);
/*
* The maximum time you allow curl to use for a single transfer
* operation.
*/
data->timeout = va_arg(param, long);
break;
case CURLOPT_CONNECTTIMEOUT:
/*
* The maximum time you allow curl to use to connect.
*/
data->connecttimeout = va_arg(param, long);
break;
/*
* The maximum amount of hops you allow curl to follow Location:
* headers. This should mostly be used to detect never-ending loops.
*/
data->maxredirs = va_arg(param, long);
break;
/*
* String to use in the HTTP User-Agent field
*/
data->useragent = va_arg(param, char *);
break;
/*
* user:password to use in the operation
*/
data->userpwd = va_arg(param, char *);
break;
/*
* List of RAW FTP commands to use after a transfer
*/
data->postquote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_QUOTE:
/*
* List of RAW FTP commands to use before a transfer
*/
data->quote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_PROGRESSFUNCTION:
/*
* Progress callback function
*/
data->fprogress = va_arg(param, curl_progress_callback);
data->progress.callback = TRUE; /* no longer internal */
break;
case CURLOPT_PROGRESSDATA:
/*
* Custom client data to pass to the progress callback
*/
data->progress_client = va_arg(param, void *);
break;
case CURLOPT_PASSWDFUNCTION:
/*
* Password prompt callback
*/
data->fpasswd = va_arg(param, curl_passwd_callback);
break;
case CURLOPT_PASSWDDATA:
/*
* Custom client data to pass to the password callback
*/
data->passwd_client = va_arg(param, void *);
break;
/*
* user:password needed to use the proxy
*/
data->proxyuserpwd = va_arg(param, char *);
break;
/*
* What range of the file you want to transfer
*/
data->set_range = va_arg(param, char *);
data->bits.set_range = data->set_range?1:0;
/*
* Resume transfer at the give file position
*/
data->set_resume_from = va_arg(param, long);
/*
* Set to a FILE * that should receive all error writes. This
* defaults to stderr for normal operations.
*/
data->err = va_arg(param, FILE *);
break;
case CURLOPT_HEADERFUNCTION:
/*
* Set header write callback
*/
data->fwrite_header = va_arg(param, curl_write_callback);
break;
/*
* Set data write callback
*/
data->fwrite = va_arg(param, curl_write_callback);
/*
* Read data callback
*/
data->fread = va_arg(param, curl_read_callback);
/*
* String that holds file name of the SSL certificate to use
*/
data->cert = va_arg(param, char *);
break;
/*
* String that holds the SSL certificate password.
*/
data->cert_passwd = va_arg(param, char *);
break;
/*
* Kludgy option to enable CRLF convertions. Subject for
* removal.
*/
data->crlf = va_arg(param, long);
break;
/*
* Set what interface to bind to when performing an operation and thus
* what from-IP your connection will use.
*/
data->device = va_arg(param, char *);
break;
/*
* A string that defines the krb4 security level.
*/
data->krb4_level = va_arg(param, char *);
data->bits.krb4=data->krb4_level?TRUE:FALSE;
break;
Daniel Stenberg
committed
case CURLOPT_SSL_VERIFYPEER:
/*
* Enable peer SSL verifying.
*/
Daniel Stenberg
committed
data->ssl.verifypeer = va_arg(param, long);
break;
case CURLOPT_CAINFO:
/*
* Set CA info for SSL connection. Specify file name of the CA certificate
*/
Daniel Stenberg
committed
data->ssl.CAfile = va_arg(param, char *);
data->ssl.CApath = NULL; /*This does not work on windows.*/
break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
*/
data->telnet_options = va_arg(param, struct curl_slist *);
break;
default:
/* unknown tag and its companion, just ignore: */
return CURLE_READ_ERROR; /* correct this */
CURLcode Curl_disconnect(struct connectdata *conn)
if(!conn)
return CURLE_OK; /* this is closed and fine already */
/*
* The range string is usually freed in curl_done(), but we might
* get here *instead* if we fail prematurely. Thus we need to be able
* to free this resource here as well.
*/
if(conn->bits.rangestringalloc) {
free(conn->range);
conn->bits.rangestringalloc = FALSE;
}
Daniel Stenberg
committed
/* unlink ourselves! */
infof(conn->data, "Closing live connection (#%d)\n", conn->connectindex);
Daniel Stenberg
committed
conn->data->connects[conn->connectindex] = NULL;
Daniel Stenberg
committed
if(conn->curl_disconnect)
/* This is set if protocol-specific cleanups should be made */
conn->curl_disconnect(conn);
if(conn->proto.generic)
free(conn->proto.generic);
if(conn->hp) /* host name info */
freeaddrinfo(conn->hp);
if(conn->hostent_buf) /* host name info */
free(conn->hostent_buf);
Daniel Stenberg
committed
if(conn->newurl)
free(conn->newurl);
if(conn->path) /* the URL path part */
free(conn->path);
#ifdef USE_SSLEAY
if (conn->ssl.use) {
/*
ERR_remove_state() frees the error queue associated with
thread pid. If pid == 0, the current thread will have its
error queue removed.
Since error queue data structures are allocated
automatically for new threads, they must be freed when
threads are terminated in oder to avoid memory leaks.
*/
ERR_remove_state(0);
if(conn->ssl.handle) {
(void)SSL_shutdown(conn->ssl.handle);
SSL_set_connect_state(conn->ssl.handle);
SSL_free (conn->ssl.handle);
conn->ssl.handle = NULL;
}
if(conn->ssl.ctx) {
SSL_CTX_free (conn->ssl.ctx);
conn->ssl.ctx = NULL;
}
conn->ssl.use = FALSE; /* get back to ordinary socket usage */
}
#endif /* USE_SSLEAY */
/* close possibly still open sockets */
if(-1 != conn->secondarysocket)
sclose(conn->secondarysocket);
if(-1 != conn->firstsocket)
sclose(conn->firstsocket);
if(conn->allocptr.proxyuserpwd)
free(conn->allocptr.proxyuserpwd);
if(conn->allocptr.uagent)
free(conn->allocptr.uagent);
if(conn->allocptr.userpwd)
free(conn->allocptr.userpwd);
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
if(conn->allocptr.ref)
free(conn->allocptr.ref);
if(conn->allocptr.cookie)
free(conn->allocptr.cookie);
if(conn->allocptr.host)
free(conn->allocptr.host);
if(conn->proxyhost)
free(conn->proxyhost);
free(conn); /* free all the connection oriented data */
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
/*
* This function should return TRUE if the socket is to be assumed to
* be dead. Most commonly this happens when the server has closed the
* connection due to inactivity.
*/
static bool SocketIsDead(int sock)
{
int sval;
bool ret_val = TRUE;
fd_set check_set;
struct timeval to;
FD_ZERO(&check_set);
FD_SET(sock,&check_set);
to.tv_sec = 0;
to.tv_usec = 1;
sval = select(sock + 1, &check_set, 0, 0, &to);
if(sval == 0)
/* timeout */
ret_val = FALSE;
return ret_val;
}
/*
* Given one filled in connection struct, this function should detect if there
* already is one that have all the significant details exactly the same and
* thus should be used instead.
*/
static bool
ConnectionExists(struct UrlData *data,
struct connectdata *needle,
struct connectdata **usethis)
size_t i;
struct connectdata *check;
for(i=0; i< data->numconnects; i++) {
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
check = data->connects[i];
if(!check)
/* NULL pointer means not filled-in entry */
continue;
if(!needle->bits.httpproxy) {
/* The requested connection does not use a HTTP proxy */
if(strequal(needle->protostr, check->protostr) &&
strequal(needle->name, check->name) &&
(needle->remote_port == check->remote_port) ) {
if(strequal(needle->protostr, "FTP")) {
/* This is FTP, verify that we're using the same name and
password as well */
if(!strequal(needle->data->user, check->proto.ftp->user) ||
!strequal(needle->data->passwd, check->proto.ftp->passwd)) {
/* one of them was different */
continue;
}
}
dead = SocketIsDead(check->firstsocket);
if(dead) {
infof(data, "Connection %d seems to be dead!\n", i);
Curl_disconnect(check); /* disconnect resources */
data->connects[i]=NULL; /* nothing here */
continue; /* try another one now */
}
Daniel Stenberg
committed
*usethis = check;
return TRUE; /* yes, we found one to use! */
else { /* The requested needle connection is using a proxy,
is the checked one using the same? */
if(check->bits.httpproxy &&
strequal(needle->proxyhost, check->proxyhost) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
*usethis = check;
return TRUE;
}
}
}
return FALSE; /* no matching connecting exists */
}
/*
* This function frees/closes a connection in the connection cache. This