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);
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;
if(data->bits.rangestringalloc) {
free(data->range);
data->range=NULL;
data->bits.rangestringalloc=0; /* free now */
/* 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 */
/* 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 = (size_t (*)(char *, size_t, size_t, FILE *))fwrite;
/* use fread as default function to read input */
data->fread = (size_t (*)(char *, size_t, size_t, FILE *))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 */
/* 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);
}
/* 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) {
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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;
break;
case CURLOPT_NOBODY:
/*
* 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.
*/
data->bits.http_put = va_arg(param, long)?TRUE:FALSE;
if(data->bits.http_put)
data->httpreq = HTTPREQ_PUT;
/*
* Stay absolutely quiet.
*/
data->bits.mute = va_arg(param, long)?TRUE:FALSE;
break;
case CURLOPT_TIMECONDITION:
/*
* 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);
/*
* Callback function for header data
*/
data->writeheader = (FILE *)va_arg(param, FILE *);
break;
/*
* 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 *);
data->bits.http_formpost = data->httppost?1:0;
if(data->bits.http_formpost)
data->httpreq = HTTPREQ_POST_FORM;
/*
* 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->bits.http_post = va_arg(param, long)?TRUE:FALSE;
if(data->bits.http_post)
data->httpreq = HTTPREQ_POST;
break;
/*
* A string with POST data. Makes curl HTTP POST.
*/
data->postfields = va_arg(param, char *);
data->bits.http_post = data->postfields?TRUE:FALSE;
if(data->bits.http_post)
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;
/*
* 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 *);
data->bits.user_passwd = data->userpwd?1:0;
/*
* 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 *);
data->bits.proxy_user_passwd = data->proxyuserpwd?1:0;
/*
* What range of the file you want to transfer
*/
data->range = va_arg(param, char *);
data->bits.set_range = data->range?1:0;
/*
* Resume transfer at the give file position
*/
data->resume_from = va_arg(param, long);
break;
/*
* Set to a FILE * that should receive all error writes. This
* defaults to stderr for normal operations.
*/
data->err = va_arg(param, FILE *);
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 */
#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_disconnect(struct connectdata *conn)
if(!conn)
return CURLE_OK; /* this is closed and fine already */
infof(conn->data, "Closing live connection (#%d)\n", conn->connectindex);
Daniel Stenberg
committed
if(-1 != conn->connectindex)
/* unlink ourselves! */
conn->data->connects[conn->connectindex] = NULL;
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);
if(conn->path) /* the URL path part */
free(conn->path);
#ifdef USE_SSLEAY
if (conn->ssl.use) {
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 */
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
/*
* 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->port == check->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 */
}
}
*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
* should take the previously set policy into account when deciding which
* of the connections to kill.
*/
static int
ConnectionKillOne(struct UrlData *data)
{
size_t i;
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
int highscore=-1;
int connindex=-1;
int score;
CURLcode result;
for(i=0; i< data->numconnects; i++) {
conn = data->connects[i];
if(!conn)
continue;
/*
* By using the set policy, we score each connection.
*/
switch(data->closepolicy) {
default:
score = 1; /* not implemented yet */
break;
}
if(score > highscore) {
highscore = score;
connindex = i;
}
}
if(connindex >= 0) {
/* the winner gets the honour of being disconnected */
result = Curl_disconnect(data->connects[connindex]);
/* clean the array entry */
data->connects[connindex] = NULL;
}
return connindex; /* return the available index or -1 */
}
/*
* The given input connection struct pointer is to be stored. If the "cache"
* is already full, we must clean out the most suitable using the previously
* set policy.
*
* The given connection should be unique. That must've been checked prior to
* this call.
*/
static unsigned int
ConnectionStore(struct UrlData *data,
struct connectdata *conn)
{
size_t i;
for(i=0; i< data->numconnects; i++) {
if(!data->connects[i])
break;
}
if(i == data->numconnects) {
/* there was no room available, kill one */
i = ConnectionKillOne(data);
infof(data, "Connection (#%d) was killed to make room\n", i);
}
data->connects[i] = conn; /* fill in this */
conn->connectindex = i; /* make the child know where the pointer to this
particular data is stored */
return i;
}
static CURLcode ConnectPlease(struct UrlData *data,
struct connectdata *conn)
{
#ifndef ENABLE_IPV6
conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0);