From a1d6ad26100bc493c7b04f1301b1634b7f5aa8b4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 20 Feb 2001 17:35:51 +0000 Subject: multiple connection support initial commit --- CHANGES | 48 ++ docs/FEATURES | 3 +- docs/curl.1 | 15 +- include/curl/curl.h | 24 +- lib/dict.c | 12 +- lib/ftp.c | 141 +++--- lib/http.c | 30 +- lib/krb4.c | 14 +- lib/ldap.c | 4 +- lib/security.c | 12 +- lib/sendf.c | 14 +- lib/ssluse.c | 218 +++++----- lib/ssluse.h | 3 +- lib/telnet.c | 1137 ++++++++++++++++++++++++++++-------------------- lib/transfer.c | 16 + lib/url.c | 877 ++++++++++++++++++++++--------------- lib/urldata.h | 182 ++++---- src/main.c | 25 +- src/version.h | 2 +- tests/data/reply11.txt | 1 + 20 files changed, 1623 insertions(+), 1155 deletions(-) diff --git a/CHANGES b/CHANGES index 40469d798..88a9eed98 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,54 @@ History of Changes +** curl 7.7 DOES NOT currently WORK. ** + +Daniel (20 February 2001) +- Added the docs/examples/win32sockets.c file for our windows friends. + +- Linus Nielsen Feltzing provided brand new TELNET functionality and + improvements: + + * Negotiation is now passive. Curl does not negotiate until the peer does. + * Possibility to set negotiation options on the command line, currently only + XDISPLOC, TTYPE and NEW_ENVIRON (called NEW_ENV). + * Now sends the USER environment variable if the -u switch is used. + * Use -t to set telnet options (Linus even updated the man page, awesome!) + +- Haven't done this big changes to curl for a while. Moved around a lot of + struct fields and stuff to make multiple connections get connection specific + data in separate structs so that they can co-exist in a nice way. See the + mailing lists for discussions around how this is gonna be implemented. Docs + and more will follow. + + Studied the HTTP RFC to find out better how persistant connections should + work. Seems cool enough. + +Daniel (19 February 2001) +- Bob Schader brought me two files that help set up a MS VC++ libcurl project + easier. He also provided me with an up-to-date libcurl.def file. + +- I moved a bunch of prototypes from the public file to the + library private urldata.h. This is because of the upcoming changes. The + low level interface is no longer being planned to become reality. + +Daniel (15 February 2001) +- CURLOPT_POST is not required anymore. Just setting the POST string with + CURLOPT_POSTFIELDS will switch on the HTTP POST. Most other things in + libcurl already works this way, i.e they require only the parameter to + switch on a feature so I think this works well with the rest. Setting a NULL + string switches off the POST again. + +- Excellent suggestions from Rich Gray, Rick Jones, Johan Nilsson and Bjorn + Reese helped me define a way how to incorporate persistant connections into + libcurl in a very smooth way. If done right, no change may have to be made + to older programs and they will just start using persistant connections when + applicable! + +Daniel (13 February 2001) +- Changed the word 'timeouted' to 'timed out' in two different error messages. + Suggested by Larry Fahnoe. + Version 7.6.1 Daniel (9 February 2001) diff --git a/docs/FEATURES b/docs/FEATURES index a77542c24..e64ccbb71 100644 --- a/docs/FEATURES +++ b/docs/FEATURES @@ -20,6 +20,7 @@ Misc - compiles on win32 - redirectable stderr - use selected network interface for outgoing traffic + - IPv6 support HTTP - GET @@ -28,7 +29,7 @@ HTTP - POST - multipart POST - authentication - - resume + - resume (both GET and PUT) - follow redirects - maximum amount of redirects to follow - custom HTTP request diff --git a/docs/curl.1 b/docs/curl.1 index f57e93fd7..88176cad0 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -425,11 +425,14 @@ If this option is used twice, the second will again disable mute. When used with -s it makes curl show error message if it fails. If this option is used twice, the second will again disable show error. -.IP "-t/--upload" -.B Deprecated. Use '-T -' instead. -Transfer the stdin data to the specified file. Curl will read -everything from stdin until EOF and store with the supplied name. If -this is used on a http(s) server, the PUT command will be used. +.IP "-t/--telnet-option " +Pass options to the telnet protocol. Supported options are: + +TTYPE= Sets the terminal type. + +XDISPLOC= Sets the X display location. + +NEW_ENV= Sets an environment variable. .IP "-T/--upload-file " Like -t, but this transfers the specified local file. If there is no file part in the specified URL, Curl will append the local file @@ -758,7 +761,7 @@ If you do find bugs, mail them to curl-bug@haxx.se. - Lars J. Aas - Jörn Hartroth - Matthew Clarke - - Linus Nielsen + - Linus Nielsen Feltzing - Felix von Leitner - Dan Zitter - Jongki Suwandi diff --git a/include/curl/curl.h b/include/curl/curl.h index f47eae2ce..3c94af55f 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -158,6 +158,9 @@ typedef enum { CURLE_BAD_PASSWORD_ENTERED, /* when the my_getpass() returns fail */ CURLE_TOO_MANY_REDIRECTS , /* catch endless re-direct loops */ + CURLE_UNKNOWN_TELNET_OPTION , /* User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* Malformed telnet option */ + CURL_LAST } CURLcode; @@ -406,6 +409,9 @@ typedef enum { document! Pass a NULL to shut it off. */ CINIT(FILETIME, OBJECTPOINT, 69), + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + CURLOPT_LASTENTRY /* the last unusued */ } CURLoption; @@ -452,8 +458,8 @@ char *curl_getenv(char *variable); char *curl_version(void); /* This is the version number */ -#define LIBCURL_VERSION "7.6.1" -#define LIBCURL_VERSION_NUM 0x070601 +#define LIBCURL_VERSION "7.7-alpha1" +#define LIBCURL_VERSION_NUM 0x070000 /* linked-list structure for the CURLOPT_QUOTE option (and other) */ struct curl_slist { @@ -520,6 +526,20 @@ typedef enum { */ CURLcode curl_getinfo(CURL *curl, CURLINFO info, ...); + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + + #ifdef __cplusplus } #endif diff --git a/lib/dict.c b/lib/dict.c index 0dd9dbb9c..893ef256e 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -141,7 +141,7 @@ CURLcode Curl_dict(struct connectdata *conn) nth = atoi(nthdef); } - Curl_sendf(data->firstsocket, conn, + Curl_sendf(conn->firstsocket, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" "MATCH " "%s " /* database */ @@ -154,7 +154,7 @@ CURLcode Curl_dict(struct connectdata *conn) word ); - result = Curl_Transfer(conn, data->firstsocket, -1, FALSE, bytecount, + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, -1, NULL); /* no upload */ if(result) @@ -191,7 +191,7 @@ CURLcode Curl_dict(struct connectdata *conn) nth = atoi(nthdef); } - Curl_sendf(data->firstsocket, conn, + Curl_sendf(conn->firstsocket, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" "DEFINE " "%s " /* database */ @@ -202,7 +202,7 @@ CURLcode Curl_dict(struct connectdata *conn) word ); - result = Curl_Transfer(conn, data->firstsocket, -1, FALSE, bytecount, + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, -1, NULL); /* no upload */ if(result) @@ -220,13 +220,13 @@ CURLcode Curl_dict(struct connectdata *conn) if (ppath[i] == ':') ppath[i] = ' '; } - Curl_sendf(data->firstsocket, conn, + Curl_sendf(conn->firstsocket, conn, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n" "%s\n" "QUIT\n", ppath); - result = Curl_Transfer(conn, data->firstsocket, -1, FALSE, bytecount, + result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount, -1, NULL); if(result) diff --git a/lib/ftp.c b/lib/ftp.c index 061736437..8d97719fb 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -158,6 +158,7 @@ void curl_slist_free_all(struct curl_slist *list) static CURLcode AllowServerConnect(struct UrlData *data, + struct connectdata *conn, int sock) { fd_set rdset; @@ -199,7 +200,7 @@ static CURLcode AllowServerConnect(struct UrlData *data, } infof(data, "Connection accepted from server\n"); - data->secondarysocket = s; + conn->secondarysocket = s; } break; } @@ -362,7 +363,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) return CURLE_OUT_OF_MEMORY; memset(ftp, 0, sizeof(struct FTP)); - data->proto.ftp = ftp; + conn->proto.ftp = ftp; /* get some initial data into the ftp struct */ ftp->bytecountp = &conn->bytecount; @@ -371,14 +372,14 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) if (data->bits.tunnel_thru_httpproxy) { /* We want "seamless" FTP operations through HTTP proxy tunnel */ - result = Curl_ConnectHTTPProxyTunnel(conn, data->firstsocket, - data->hostname, data->remote_port); + result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, + conn->hostname, conn->remote_port); if(CURLE_OK != result) return result; } /* The first thing we do is wait for the "220*" line: */ - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -398,7 +399,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) set a valid level */ sec_request_prot(conn, data->krb4_level); - data->cmdchannel = fdopen(data->firstsocket, "w"); + data->cmdchannel = fdopen(conn->firstsocket, "w"); if(sec_login(conn) != 0) infof(data, "Logging in with password in cleartext!\n"); @@ -408,10 +409,10 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) #endif /* send USER */ - ftpsendf(data->firstsocket, conn, "USER %s", ftp->user); + ftpsendf(conn->firstsocket, conn, "USER %s", ftp->user); /* wait for feedback */ - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -424,8 +425,8 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) else if(ftpcode == 331) { /* 331 Password required for ... (the server requires to send the user's password too) */ - ftpsendf(data->firstsocket, conn, "PASS %s", ftp->passwd); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + ftpsendf(conn->firstsocket, conn, "PASS %s", ftp->passwd); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -477,7 +478,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn) CURLcode Curl_ftp_done(struct connectdata *conn) { struct UrlData *data = conn->data; - struct FTP *ftp = data->proto.ftp; + struct FTP *ftp = conn->proto.ftp; size_t nread; char *buf = data->buffer; /* this is our buffer */ struct curl_slist *qitem; /* QUOTE item */ @@ -502,16 +503,16 @@ CURLcode Curl_ftp_done(struct connectdata *conn) } } #ifdef KRB4 - sec_fflush_fd(conn, data->secondarysocket); + sec_fflush_fd(conn, conn->secondarysocket); #endif /* shut down the socket to inform the server we're done */ - sclose(data->secondarysocket); - data->secondarysocket = -1; + sclose(conn->secondarysocket); + conn->secondarysocket = -1; if(!data->bits.no_body) { /* now let's see what the server says about the transfer we just performed: */ - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -529,9 +530,9 @@ CURLcode Curl_ftp_done(struct connectdata *conn) while (qitem) { /* Send string */ if (qitem->data) { - ftpsendf(data->firstsocket, conn, "%s", qitem->data); + ftpsendf(conn->firstsocket, conn, "%s", qitem->data); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -571,7 +572,7 @@ CURLcode _ftp(struct connectdata *conn) struct curl_slist *qitem; /* QUOTE item */ /* the ftp struct is already inited in ftp_connect() */ - struct FTP *ftp = data->proto.ftp; + struct FTP *ftp = conn->proto.ftp; long *bytecountp = ftp->bytecountp; int ftpcode; /* for ftp status */ @@ -583,9 +584,9 @@ CURLcode _ftp(struct connectdata *conn) while (qitem) { /* Send string */ if (qitem->data) { - ftpsendf(data->firstsocket, conn, "%s", qitem->data); + ftpsendf(conn->firstsocket, conn, "%s", qitem->data); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -601,8 +602,8 @@ CURLcode _ftp(struct connectdata *conn) /* change directory first! */ if(ftp->dir && ftp->dir[0]) { - ftpsendf(data->firstsocket, conn, "CWD %s", ftp->dir); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + ftpsendf(conn->firstsocket, conn, "CWD %s", ftp->dir); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -615,9 +616,9 @@ CURLcode _ftp(struct connectdata *conn) if(data->bits.get_filetime && ftp->file) { /* we have requested to get the modified-time of the file, this is yet again a grey area as the MDTM is not kosher RFC959 */ - ftpsendf(data->firstsocket, conn, "MDTM %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "MDTM %s", ftp->file); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -651,10 +652,10 @@ CURLcode _ftp(struct connectdata *conn) /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ - ftpsendf(data->firstsocket, conn, "TYPE %s", + ftpsendf(conn->firstsocket, conn, "TYPE %s", (data->bits.ftp_ascii)?"A":"I"); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -665,9 +666,9 @@ CURLcode _ftp(struct connectdata *conn) CURLE_FTP_COULDNT_SET_BINARY; } - ftpsendf(data->firstsocket, conn, "SIZE %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "SIZE %s", ftp->file); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -731,7 +732,7 @@ CURLcode _ftp(struct connectdata *conn) * I believe we should use the same address as the control connection. */ sslen = sizeof(ss); - if (getsockname(data->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) + if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) return CURLE_FTP_PORT_FAILED; if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, @@ -819,7 +820,7 @@ CURLcode _ftp(struct connectdata *conn) if (q) *q = '\0'; } - ftpsendf(data->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf, + ftpsendf(conn->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf, portmsgbuf, tmp); } else if (strcmp(*modep, "LPRT") == 0 || strcmp(*modep, "PORT") == 0) { int i; @@ -856,10 +857,10 @@ CURLcode _ftp(struct connectdata *conn) goto again; } } - ftpsendf(data->firstsocket, conn, "%s %s", *modep, portmsgbuf); + ftpsendf(conn->firstsocket, conn, "%s %s", *modep, portmsgbuf); } - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if (nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -909,7 +910,7 @@ again:; /* we set the secondary socket variable to this for now, it is only so that the cleanup function will close it in case we fail before the true secondary stuff is made */ - data->secondarysocket = portsock; + conn->secondarysocket = portsock; memset((char *)&sa, 0, sizeof(sa)); memcpy((char *)&sa.sin_addr, @@ -971,13 +972,13 @@ again:; sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]); #endif - ftpsendf(data->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d", + ftpsendf(conn->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d", ip[0], ip[1], ip[2], ip[3], porttouse >> 8, porttouse & 255); } - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -998,8 +999,8 @@ again:; int modeoff; for (modeoff = 0; mode[modeoff]; modeoff++) { - ftpsendf(data->firstsocket, conn, mode[modeoff]); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + ftpsendf(conn->firstsocket, conn, mode[modeoff]); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1081,15 +1082,15 @@ again:; } #ifdef ENABLE_IPV6 - data->secondarysocket = -1; + conn->secondarysocket = -1; for (ai = res; ai; ai = ai->ai_next) { /* XXX for now, we can do IPv4 only */ if (ai->ai_family != AF_INET) continue; - data->secondarysocket = socket(ai->ai_family, ai->ai_socktype, + conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (data->secondarysocket < 0) + if (conn->secondarysocket < 0) continue; if(data->bits.verbose) { @@ -1114,21 +1115,21 @@ again:; } } - if (connect(data->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { - close(data->secondarysocket); - data->secondarysocket = -1; + if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { + close(conn->secondarysocket); + conn->secondarysocket = -1; continue; } break; } - if (data->secondarysocket < 0) { + if (conn->secondarysocket < 0) { failf(data, strerror(errno)); return CURLE_FTP_CANT_RECONNECT; } #else - data->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); + conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memcpy((char *)&(serv_addr.sin_addr), he->h_addr, he->h_length); @@ -1202,7 +1203,7 @@ again:; if(hostdataptr) free(hostdataptr); - if (connect(data->secondarysocket, (struct sockaddr *) &serv_addr, + if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { switch(errno) { #ifdef ECONNREFUSED @@ -1226,7 +1227,7 @@ again:; if (data->bits.tunnel_thru_httpproxy) { /* We want "seamless" FTP operations through HTTP proxy tunnel */ - result = Curl_ConnectHTTPProxyTunnel(conn, data->secondarysocket, + result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket, newhost, newport); if(CURLE_OK != result) return result; @@ -1241,10 +1242,10 @@ again:; if(data->bits.upload) { /* Set type to binary (unless specified ASCII) */ - ftpsendf(data->firstsocket, conn, "TYPE %s", + ftpsendf(conn->firstsocket, conn, "TYPE %s", (data->bits.ftp_ascii)?"A":"I"); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1273,9 +1274,9 @@ again:; /* we could've got a specified offset from the command line, but now we know we didn't */ - ftpsendf(data->firstsocket, conn, "SIZE %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "SIZE %s", ftp->file); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1332,11 +1333,11 @@ again:; /* Send everything on data->in to the socket */ if(data->bits.ftp_append) /* we append onto the file instead of rewriting it */ - ftpsendf(data->firstsocket, conn, "APPE %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "APPE %s", ftp->file); else - ftpsendf(data->firstsocket, conn, "STOR %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "STOR %s", ftp->file); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1347,7 +1348,7 @@ again:; } if(data->bits.ftp_use_port) { - result = AllowServerConnect(data, portsock); + result = AllowServerConnect(data, conn, portsock); if( result ) return result; } @@ -1360,7 +1361,7 @@ again:; Curl_pgrsSetUploadSize(data, data->infilesize); result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ - data->secondarysocket, bytecountp); + conn->secondarysocket, bytecountp); if(result) return result; @@ -1414,9 +1415,9 @@ again:; dirlist = TRUE; /* Set type to ASCII */ - ftpsendf(data->firstsocket, conn, "TYPE A"); + ftpsendf(conn->firstsocket, conn, "TYPE A"); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1429,16 +1430,16 @@ again:; better used since the LIST command output is not specified or standard in any way */ - ftpsendf(data->firstsocket, conn, "%s", + ftpsendf(conn->firstsocket, conn, "%s", data->customrequest?data->customrequest: (data->bits.ftp_list_only?"NLST":"LIST")); } else { /* Set type to binary (unless specified ASCII) */ - ftpsendf(data->firstsocket, conn, "TYPE %s", + ftpsendf(conn->firstsocket, conn, "TYPE %s", (data->bits.ftp_ascii)?"A":"I"); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1457,9 +1458,9 @@ again:; * of the file we're gonna get. If we can get the size, this is by far * the best way to know if we're trying to resume beyond the EOF. */ - ftpsendf(data->firstsocket, conn, "SIZE %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "SIZE %s", ftp->file); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1501,9 +1502,9 @@ again:; infof(data, "Instructs server to resume from offset %d\n", data->resume_from); - ftpsendf(data->firstsocket, conn, "REST %d", data->resume_from); + ftpsendf(conn->firstsocket, conn, "REST %d", data->resume_from); - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1513,10 +1514,10 @@ again:; } } - ftpsendf(data->firstsocket, conn, "RETR %s", ftp->file); + ftpsendf(conn->firstsocket, conn, "RETR %s", ftp->file); } - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); if(nread < 0) return CURLE_OPERATION_TIMEOUTED; @@ -1580,7 +1581,7 @@ again:; size = downloadsize; if(data->bits.ftp_use_port) { - result = AllowServerConnect(data, portsock); + result = AllowServerConnect(data, conn, portsock); if( result ) return result; } @@ -1588,7 +1589,7 @@ again:; infof(data, "Getting file with size: %d\n", size); /* FTP download: */ - result=Curl_Transfer(conn, data->secondarysocket, size, FALSE, + result=Curl_Transfer(conn, conn->secondarysocket, size, FALSE, bytecountp, -1, NULL); /* no upload here */ if(result) @@ -1617,7 +1618,7 @@ CURLcode Curl_ftp(struct connectdata *conn) int dirlength=0; /* 0 forces strlen() */ /* the ftp struct is already inited in ftp_connect() */ - ftp = data->proto.ftp; + ftp = conn->proto.ftp; /* We split the path into dir and file parts *before* we URLdecode it */ diff --git a/lib/http.c b/lib/http.c index 93ae049b1..836a16622 100644 --- a/lib/http.c +++ b/lib/http.c @@ -325,21 +325,21 @@ CURLcode Curl_http_connect(struct connectdata *conn) if (conn->protocol & PROT_HTTPS) { if (data->bits.httpproxy) { /* HTTPS through a proxy can only be done with a tunnel */ - result = Curl_ConnectHTTPProxyTunnel(conn, data->firstsocket, - data->hostname, data->remote_port); + result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, + conn->hostname, conn->remote_port); if(CURLE_OK != result) return result; } /* now, perform the SSL initialization for this socket */ - if(Curl_SSLConnect(data)) + if(Curl_SSLConnect(conn)) return CURLE_SSL_CONNECT_ERROR; } if(data->bits.user_passwd && !data->bits.this_is_a_follow) { /* Authorization: is requested, this is not a followed location, get the original host name */ - data->auth_host = strdup(data->hostname); + data->auth_host = strdup(conn->hostname); } return CURLE_OK; @@ -361,7 +361,7 @@ CURLcode Curl_http_done(struct connectdata *conn) struct HTTP *http; data=conn->data; - http=data->proto.http; + http=conn->proto.http; if(data->bits.http_formpost) { *bytecount = http->readbytecount + http->writebytecount; @@ -394,7 +394,7 @@ CURLcode Curl_http(struct connectdata *conn) if(!http) return CURLE_OUT_OF_MEMORY; memset(http, 0, sizeof(struct HTTP)); - data->proto.http = http; + conn->proto.http = http; if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && data->bits.upload) { @@ -417,7 +417,7 @@ CURLcode Curl_http(struct connectdata *conn) host due to a location-follow, we do some weirdo checks here */ if(!data->bits.this_is_a_follow || !data->auth_host || - strequal(data->auth_host, data->hostname)) { + strequal(data->auth_host, conn->hostname)) { sprintf(data->buffer, "%s:%s", data->user, data->passwd); if(Curl_base64_encode(data->buffer, strlen(data->buffer), &authorization) >= 0) { @@ -690,10 +690,10 @@ CURLcode Curl_http(struct connectdata *conn) Curl_pgrsSetUploadSize(data, http->postsize); data->request_size = - add_buffer_send(data->firstsocket, conn, req_buffer); - result = Curl_Transfer(conn, data->firstsocket, -1, TRUE, + add_buffer_send(conn->firstsocket, conn, req_buffer); + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, &http->readbytecount, - data->firstsocket, + conn->firstsocket, &http->writebytecount); if(result) { Curl_FormFree(http->sendit); /* free that whole lot */ @@ -716,12 +716,12 @@ CURLcode Curl_http(struct connectdata *conn) /* this sends the buffer and frees all the buffer resources */ data->request_size = - add_buffer_send(data->firstsocket, conn, req_buffer); + add_buffer_send(conn->firstsocket, conn, req_buffer); /* prepare for transfer */ - result = Curl_Transfer(conn, data->firstsocket, -1, TRUE, + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, &http->readbytecount, - data->firstsocket, + conn->firstsocket, &http->writebytecount); if(result) return result; @@ -762,10 +762,10 @@ CURLcode Curl_http(struct connectdata *conn) /* issue the request */ data->request_size = - add_buffer_send(data->firstsocket, conn, req_buffer); + add_buffer_send(conn->firstsocket, conn, req_buffer); /* HTTP GET/HEAD download: */ - result = Curl_Transfer(conn, data->firstsocket, -1, TRUE, bytecount, + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, bytecount, -1, NULL); /* nothing to upload */ } if(result) diff --git a/lib/krb4.c b/lib/krb4.c index 5ca108762..3d7e530b1 100644 --- a/lib/krb4.c +++ b/lib/krb4.c @@ -290,7 +290,7 @@ krb4_auth(void *app_data, struct connectdata *conn) size_t nread; int l = sizeof(local_addr); - if(getsockname(conn->data->firstsocket, + if(getsockname(conn->firstsocket, (struct sockaddr *)LOCAL_ADDR, &l) < 0) perror("getsockname()"); @@ -339,9 +339,9 @@ krb4_auth(void *app_data, struct connectdata *conn) return AUTH_CONTINUE; } /*ret = command("ADAT %s", p)*/ - Curl_ftpsendf(conn->data->firstsocket, conn, "ADAT %s", p); + Curl_ftpsendf(conn->firstsocket, conn, "ADAT %s", p); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/-1; @@ -409,10 +409,10 @@ void krb_kauth(struct connectdata *conn) save = set_command_prot(conn, prot_private); /*ret = command("SITE KAUTH %s", name);***/ - Curl_ftpsendf(conn->data->firstsocket, conn, + Curl_ftpsendf(conn->firstsocket, conn, "SITE KAUTH %s", conn->data->user); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, conn->data->buffer, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/; @@ -486,10 +486,10 @@ void krb_kauth(struct connectdata *conn) } memset (tktcopy.dat, 0, tktcopy.length); /*ret = command("SITE KAUTH %s %s", name, p);***/ - Curl_ftpsendf(conn->data->firstsocket, conn, + Curl_ftpsendf(conn->firstsocket, conn, "SITE KAUTH %s %s", name, p); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, conn->data->buffer, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/; diff --git a/lib/ldap.c b/lib/ldap.c index 28bdc88de..e8aec2818 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -171,10 +171,10 @@ CURLcode Curl_ldap(struct connectdata *conn) DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long), ldap_entry2text); DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long, char *, char *), ldap_entry2html); - server = ldap_open(data->hostname, data->port); + server = ldap_open(conn->hostname, conn->port); if (server == NULL) { failf(data, "LDAP: Cannot connect to %s:%d", - data->hostname, data->port); + conn->hostname, conn->port); status = CURLE_COULDNT_CONNECT; } else { rc = ldap_simple_bind_s(server, data->user, data->passwd); diff --git a/lib/security.c b/lib/security.c index ced5e7d38..98553a6ba 100644 --- a/lib/security.c +++ b/lib/security.c @@ -482,10 +482,10 @@ sec_prot_internal(struct connectdata *conn, int level) } if(level){ - Curl_ftpsendf(conn->data->firstsocket, conn, + Curl_ftpsendf(conn->firstsocket, conn, "PBSZ %u", s); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/-1; @@ -501,10 +501,10 @@ sec_prot_internal(struct connectdata *conn, int level) conn->buffer_size = s; } - Curl_ftpsendf(conn->data->firstsocket, conn, + Curl_ftpsendf(conn->firstsocket, conn, "PROT %c", level["CSEP"]); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/-1; @@ -610,10 +610,10 @@ sec_login(struct connectdata *conn) } infof(data, "Trying %s...\n", (*m)->name); /*ret = command("AUTH %s", (*m)->name);***/ - Curl_ftpsendf(conn->data->firstsocket, conn, + Curl_ftpsendf(conn->firstsocket, conn, "AUTH %s", (*m)->name); /* wait for feedback */ - nread = Curl_GetFTPResponse(conn->data->firstsocket, + nread = Curl_GetFTPResponse(conn->firstsocket, conn->data->buffer, conn, NULL); if(nread < 0) return /*CURLE_OPERATION_TIMEOUTED*/-1; diff --git a/lib/sendf.c b/lib/sendf.c index d077d8b15..c296af2fc 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -111,15 +111,14 @@ CURLcode Curl_write(struct connectdata *conn, int sockfd, size_t *written) { size_t bytes_written; - struct UrlData *data=conn->data; /* conn knows data, not vice versa */ #ifdef USE_SSLEAY - if (data->ssl.use) { + if (conn->ssl.use) { int loop=100; /* just a precaution to never loop endlessly */ while(loop--) { - bytes_written = SSL_write(data->ssl.handle, mem, len); + bytes_written = SSL_write(conn->ssl.handle, mem, len); if((-1 != bytes_written) || - (SSL_ERROR_WANT_WRITE != SSL_get_error(data->ssl.handle, + (SSL_ERROR_WANT_WRITE != SSL_get_error(conn->ssl.handle, bytes_written) )) break; } @@ -200,16 +199,15 @@ CURLcode Curl_read(struct connectdata *conn, int sockfd, char *buf, size_t buffersize, ssize_t *n) { - struct UrlData *data = conn->data; ssize_t nread; #ifdef USE_SSLEAY - if (data->ssl.use) { + if (conn->ssl.use) { int loop=100; /* just a precaution to never loop endlessly */ while(loop--) { - nread = SSL_read(data->ssl.handle, buf, buffersize); + nread = SSL_read(conn->ssl.handle, buf, buffersize); if((-1 != nread) || - (SSL_ERROR_WANT_READ != SSL_get_error(data->ssl.handle, nread) )) + (SSL_ERROR_WANT_READ != SSL_get_error(conn->ssl.handle, nread) )) break; } } diff --git a/lib/ssluse.c b/lib/ssluse.c index 9def3e805..789175735 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -64,6 +64,7 @@ static int passwd_callback(char *buf, int num, int verify static int cert_stuff(struct UrlData *data, + struct connectdata *conn, char *cert_file, char *key_file) { @@ -78,10 +79,10 @@ int cert_stuff(struct UrlData *data, */ strcpy(global_passwd, data->cert_passwd); /* Set passwd callback: */ - SSL_CTX_set_default_passwd_cb(data->ssl.ctx, passwd_callback); + SSL_CTX_set_default_passwd_cb(conn->ssl.ctx, passwd_callback); } - if (SSL_CTX_use_certificate_file(data->ssl.ctx, + if (SSL_CTX_use_certificate_file(conn->ssl.ctx, cert_file, SSL_FILETYPE_PEM) <= 0) { failf(data, "unable to set certificate file (wrong password?)\n"); @@ -90,14 +91,14 @@ int cert_stuff(struct UrlData *data, if (key_file == NULL) key_file=cert_file; - if (SSL_CTX_use_PrivateKey_file(data->ssl.ctx, + if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx, key_file, SSL_FILETYPE_PEM) <= 0) { failf(data, "unable to set public key file\n"); return(0); } - ssl=SSL_new(data->ssl.ctx); + ssl=SSL_new(conn->ssl.ctx); x509=SSL_get_certificate(ssl); if (x509 != NULL) @@ -111,7 +112,7 @@ int cert_stuff(struct UrlData *data, /* Now we know that a key and cert have been set against * the SSL context */ - if (!SSL_CTX_check_private_key(data->ssl.ctx)) { + if (!SSL_CTX_check_private_key(conn->ssl.ctx)) { failf(data, "Private key does not match the certificate public key\n"); return(0); } @@ -141,22 +142,23 @@ int cert_verify_callback(int ok, X509_STORE_CTX *ctx) /* ====================================================== */ int -Curl_SSLConnect (struct UrlData *data) +Curl_SSLConnect(struct connectdata *conn) { #ifdef USE_SSLEAY - int err; - char * str; - SSL_METHOD *req_method; + struct UrlData *data = conn->data; + int err; + char * str; + SSL_METHOD *req_method; - /* mark this is being ssl enabled from here on out. */ - data->ssl.use = TRUE; + /* mark this is being ssl enabled from here on out. */ + conn->ssl.use = TRUE; - /* Lets get nice error messages */ - SSL_load_error_strings(); + /* Lets get nice error messages */ + SSL_load_error_strings(); #ifdef HAVE_RAND_STATUS - /* RAND_status() was introduced in OpenSSL 0.9.5 */ - if(0 == RAND_status()) + /* RAND_status() was introduced in OpenSSL 0.9.5 */ + if(0 == RAND_status()) #endif { /* We need to seed the PRNG properly! */ @@ -177,116 +179,116 @@ Curl_SSLConnect (struct UrlData *data) #endif } - /* Setup all the global SSL stuff */ - SSLeay_add_ssl_algorithms(); - - switch(data->ssl.version) { - default: - req_method = SSLv23_client_method(); - break; - case 2: - req_method = SSLv2_client_method(); - break; - case 3: - req_method = SSLv3_client_method(); - break; - } + /* Setup all the global SSL stuff */ + SSLeay_add_ssl_algorithms(); + + switch(data->ssl.version) { + default: + req_method = SSLv23_client_method(); + break; + case 2: + req_method = SSLv2_client_method(); + break; + case 3: + req_method = SSLv3_client_method(); + break; + } - data->ssl.ctx = SSL_CTX_new(req_method); + conn->ssl.ctx = SSL_CTX_new(req_method); - if(!data->ssl.ctx) { - failf(data, "SSL: couldn't create a context!"); - return 1; - } + if(!conn->ssl.ctx) { + failf(data, "SSL: couldn't create a context!"); + return 1; + } - if(data->cert) { - if (!cert_stuff(data, data->cert, data->cert)) { - failf(data, "couldn't use certificate!\n"); - return 2; - } + if(data->cert) { + if (!cert_stuff(data, conn, data->cert, data->cert)) { + failf(data, "couldn't use certificate!\n"); + return 2; } + } - if(data->ssl.verifypeer){ - SSL_CTX_set_verify(data->ssl.ctx, - SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT| - SSL_VERIFY_CLIENT_ONCE, - cert_verify_callback); - if (!SSL_CTX_load_verify_locations(data->ssl.ctx, - data->ssl.CAfile, - data->ssl.CApath)) { - failf(data,"error setting cerficate verify locations\n"); - return 2; - } + if(data->ssl.verifypeer){ + SSL_CTX_set_verify(conn->ssl.ctx, + SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT| + SSL_VERIFY_CLIENT_ONCE, + cert_verify_callback); + if (!SSL_CTX_load_verify_locations(conn->ssl.ctx, + data->ssl.CAfile, + data->ssl.CApath)) { + failf(data,"error setting cerficate verify locations\n"); + return 2; } - else - SSL_CTX_set_verify(data->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback); + } + else + SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback); - /* Lets make an SSL structure */ - data->ssl.handle = SSL_new (data->ssl.ctx); - SSL_set_connect_state (data->ssl.handle); + /* Lets make an SSL structure */ + conn->ssl.handle = SSL_new (conn->ssl.ctx); + SSL_set_connect_state (conn->ssl.handle); - data->ssl.server_cert = 0x0; + conn->ssl.server_cert = 0x0; - /* pass the raw socket into the SSL layers */ - SSL_set_fd (data->ssl.handle, data->firstsocket); - err = SSL_connect (data->ssl.handle); + /* pass the raw socket into the SSL layers */ + SSL_set_fd (conn->ssl.handle, conn->firstsocket); + err = SSL_connect (conn->ssl.handle); - if (-1 == err) { - err = ERR_get_error(); - failf(data, "SSL: %s", ERR_error_string(err, NULL)); - return 10; - } + if (-1 == err) { + err = ERR_get_error(); + failf(data, "SSL: %s", ERR_error_string(err, NULL)); + return 10; + } - /* Informational message */ - infof (data, "SSL connection using %s\n", - SSL_get_cipher(data->ssl.handle)); + /* Informational message */ + infof (data, "SSL connection using %s\n", + SSL_get_cipher(conn->ssl.handle)); - /* Get server's certificate (note: beware of dynamic allocation) - opt */ - /* major serious hack alert -- we should check certificates - * to authenticate the server; otherwise we risk man-in-the-middle - * attack - */ - - data->ssl.server_cert = SSL_get_peer_certificate (data->ssl.handle); - if(!data->ssl.server_cert) { - failf(data, "SSL: couldn't get peer certificate!"); - return 3; - } - infof (data, "Server certificate:\n"); + /* Get server's certificate (note: beware of dynamic allocation) - opt */ + /* major serious hack alert -- we should check certificates + * to authenticate the server; otherwise we risk man-in-the-middle + * attack + */ + + conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle); + if(!conn->ssl.server_cert) { + failf(data, "SSL: couldn't get peer certificate!"); + return 3; + } + infof (data, "Server certificate:\n"); - str = X509_NAME_oneline (X509_get_subject_name (data->ssl.server_cert), - NULL, 0); - if(!str) { - failf(data, "SSL: couldn't get X509-subject!"); - return 4; - } - infof(data, "\t subject: %s\n", str); - CRYPTO_free(str); - - str = X509_NAME_oneline (X509_get_issuer_name (data->ssl.server_cert), - NULL, 0); - if(!str) { - failf(data, "SSL: couldn't get X509-issuer name!"); - return 5; - } - infof(data, "\t issuer: %s\n", str); - CRYPTO_free(str); + str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert), + NULL, 0); + if(!str) { + failf(data, "SSL: couldn't get X509-subject!"); + return 4; + } + infof(data, "\t subject: %s\n", str); + CRYPTO_free(str); + + str = X509_NAME_oneline (X509_get_issuer_name (conn->ssl.server_cert), + NULL, 0); + if(!str) { + failf(data, "SSL: couldn't get X509-issuer name!"); + return 5; + } + infof(data, "\t issuer: %s\n", str); + CRYPTO_free(str); - /* We could do all sorts of certificate verification stuff here before - deallocating the certificate. */ + /* We could do all sorts of certificate verification stuff here before + deallocating the certificate. */ - if(data->ssl.verifypeer) { - data->ssl.certverifyresult=SSL_get_verify_result(data->ssl.handle); - infof(data, "Verify result: %d\n", data->ssl.certverifyresult); - } - else - data->ssl.certverifyresult=0; + if(data->ssl.verifypeer) { + data->ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle); + infof(data, "Verify result: %d\n", data->ssl.certverifyresult); + } + else + data->ssl.certverifyresult=0; - X509_free(data->ssl.server_cert); + X509_free(conn->ssl.server_cert); #else /* USE_SSLEAY */ - /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ - (void) data; + /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ + (void) data; #endif - return 0; + return 0; } diff --git a/lib/ssluse.h b/lib/ssluse.h index 6279c6257..bb36f0e96 100644 --- a/lib/ssluse.h +++ b/lib/ssluse.h @@ -22,5 +22,6 @@ * * $Id$ *****************************************************************************/ -int Curl_SSLConnect (struct UrlData *data); +#include "urldata.h" +int Curl_SSLConnect(struct connectdata *conn); #endif diff --git a/lib/telnet.c b/lib/telnet.c index cda76d446..23a55b62f 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -96,7 +96,7 @@ #define SB_LEN() (subend - subpointer) static -void telrcv(struct UrlData *data, +void telrcv(struct connectdata *, unsigned char *inbuf, /* Data received from socket */ int count); /* Number of bytes received */ @@ -104,14 +104,14 @@ static void printoption(struct UrlData *data, const char *direction, int cmd, int option); -static void negotiate(struct UrlData *data); -static void send_negotiation(struct UrlData *data, int cmd, int option); -static void set_local_option(struct UrlData *data, int cmd, int option); -static void set_remote_option(struct UrlData *data, int cmd, int option); +static void negotiate(struct connectdata *); +static void send_negotiation(struct connectdata *, int cmd, int option); +static void set_local_option(struct connectdata *, int cmd, int option); +static void set_remote_option(struct connectdata *, int cmd, int option); static void printsub(struct UrlData *data, int direction, unsigned char *pointer, int length); -static void suboption(struct UrlData *data); +static void suboption(struct connectdata *); /* suboptions */ static char subbuffer[SUBBUFSIZE]; @@ -142,6 +142,8 @@ static enum #define EMPTY 0 #define OPPOSITE 1 +static int please_negotiate = 0; +static int already_negotiated = 0; static int us[256]; static int usq[256]; static int us_preferred[256]; @@ -149,86 +151,87 @@ static int him[256]; static int himq[256]; static int him_preferred[256]; +static char *subopt_ttype = NULL; /* Set with suboption TTYPE */ +static char *subopt_xdisploc = NULL; /* Set with suboption XDISPLOC */ +static struct curl_slist *telnet_vars = NULL; /* Environment variables */ + static -void init_telnet(struct UrlData *data) +void init_telnet(struct connectdata *conn) { - telrcv_state = TS_DATA; - - /* Init suboptions */ - SB_CLEAR(); - - /* Set all options to NO */ - memset(us, NO, 256); - memset(usq, NO, 256); - memset(us_preferred, NO, 256); - memset(him, NO, 256); - memset(himq, NO, 256); - memset(him_preferred, NO, 256); - - /* Set the options we want */ - us_preferred[TELOPT_BINARY] = YES; - us_preferred[TELOPT_SGA] = YES; - him_preferred[TELOPT_BINARY] = YES; - him_preferred[TELOPT_SGA] = YES; - - /* Start negotiating */ - negotiate(data); + telrcv_state = TS_DATA; + + /* Init suboptions */ + SB_CLEAR(); + + /* Set all options to NO */ + memset(us, NO, 256); + memset(usq, NO, 256); + memset(us_preferred, NO, 256); + memset(him, NO, 256); + memset(himq, NO, 256); + memset(him_preferred, NO, 256); + + /* Set the options we want by default */ + us_preferred[TELOPT_BINARY] = YES; + us_preferred[TELOPT_SGA] = YES; + him_preferred[TELOPT_BINARY] = YES; + him_preferred[TELOPT_SGA] = YES; } -static void negotiate(struct UrlData *data) +static void negotiate(struct connectdata *conn) { - int i; + int i; - for(i = 0;i < NTELOPTS;i++) - { - if(us_preferred[i] == YES) - set_local_option(data, i, YES); + for(i = 0;i < NTELOPTS;i++) + { + if(us_preferred[i] == YES) + set_local_option(conn, i, YES); - if(him_preferred[i] == YES) - set_remote_option(data, i, YES); - } + if(him_preferred[i] == YES) + set_remote_option(conn, i, YES); + } } static void printoption(struct UrlData *data, const char *direction, int cmd, int option) { - char *fmt; - char *opt; + char *fmt; + char *opt; - if (data->bits.verbose) - { - if (cmd == IAC) - { - if (TELCMD_OK(option)) - printf("%s IAC %s\n", direction, TELCMD(option)); - else - printf("%s IAC %d\n", direction, option); - } + if (data->bits.verbose) + { + if (cmd == IAC) + { + if (TELCMD_OK(option)) + printf("%s IAC %s\n", direction, TELCMD(option)); else + printf("%s IAC %d\n", direction, option); + } + else + { + fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : + (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; + if (fmt) { - fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : - (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; - if (fmt) - { - if (TELOPT_OK(option)) - opt = TELOPT(option); - else if (option == TELOPT_EXOPL) - opt = "EXOPL"; - else - opt = NULL; - - if(opt) - printf("%s %s %s\n", direction, fmt, opt); - else - printf("%s %s %d\n", direction, fmt, option); - } - else - printf("%s %d %d\n", direction, cmd, option); + if (TELOPT_OK(option)) + opt = TELOPT(option); + else if (option == TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; + + if(opt) + printf("%s %s %s\n", direction, fmt, opt); + else + printf("%s %s %d\n", direction, fmt, option); } - } + else + printf("%s %d %d\n", direction, cmd, option); + } + } } -static void send_negotiation(struct UrlData *data, int cmd, int option) +static void send_negotiation(struct connectdata *conn, int cmd, int option) { unsigned char buf[3]; @@ -236,362 +239,362 @@ static void send_negotiation(struct UrlData *data, int cmd, int option) buf[1] = cmd; buf[2] = option; - swrite(data->firstsocket, buf, 3); + swrite(conn->firstsocket, buf, 3); - printoption(data, "SENT", cmd, option); + printoption(conn->data, "SENT", cmd, option); } static -void set_remote_option(struct UrlData *data, int option, int newstate) +void set_remote_option(struct connectdata *conn, int option, int newstate) { - if(newstate == YES) - { - switch(him[option]) - { + if(newstate == YES) + { + switch(him[option]) + { case NO: - him[option] = WANTYES; - send_negotiation(data, DO, option); - break; + him[option] = WANTYES; + send_negotiation(conn, DO, option); + break; case YES: - /* Already enabled */ - break; + /* Already enabled */ + break; case WANTNO: - switch(himq[option]) - { - case EMPTY: - /* Already negotiating for YES, queue the request */ - himq[option] = OPPOSITE; - break; - case OPPOSITE: - /* Error: already queued an enable request */ - break; - } - break; + switch(himq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + himq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; case WANTYES: - switch(himq[option]) - { - case EMPTY: - /* Error: already negotiating for enable */ - break; - case OPPOSITE: - himq[option] = EMPTY; - break; - } - break; - } - } - else /* NO */ - { - switch(him[option]) - { + switch(himq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + himq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(him[option]) + { case NO: - /* Already disabled */ - break; + /* Already disabled */ + break; case YES: - him[option] = WANTNO; - send_negotiation(data, DONT, option); - break; + him[option] = WANTNO; + send_negotiation(conn, DONT, option); + break; case WANTNO: - switch(himq[option]) - { - case EMPTY: - /* Already negotiating for NO */ - break; - case OPPOSITE: - himq[option] = EMPTY; - break; - } - break; + switch(himq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + himq[option] = EMPTY; + break; + } + break; case WANTYES: - switch(himq[option]) - { - case EMPTY: - himq[option] = OPPOSITE; - break; - case OPPOSITE: - break; - } - break; - } - } + switch(himq[option]) + { + case EMPTY: + himq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } } static -void rec_will(struct UrlData *data, int option) +void rec_will(struct connectdata *conn, int option) { - switch(him[option]) - { - case NO: + switch(him[option]) + { + case NO: if(him_preferred[option] == YES) { - him[option] = YES; - send_negotiation(data, DO, option); + him[option] = YES; + send_negotiation(conn, DO, option); } else { - send_negotiation(data, DONT, option); + send_negotiation(conn, DONT, option); } break; - case YES: + case YES: /* Already enabled */ break; - case WANTNO: + case WANTNO: switch(himq[option]) { - case EMPTY: - /* Error: DONT answered by WILL */ - him[option] = NO; - break; - case OPPOSITE: - /* Error: DONT answered by WILL */ - him[option] = YES; - himq[option] = EMPTY; - break; + case EMPTY: + /* Error: DONT answered by WILL */ + him[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + him[option] = YES; + himq[option] = EMPTY; + break; } break; - case WANTYES: + case WANTYES: switch(himq[option]) { - case EMPTY: - him[option] = YES; - break; - case OPPOSITE: - him[option] = WANTNO; - himq[option] = EMPTY; - send_negotiation(data, DONT, option); - break; + case EMPTY: + him[option] = YES; + break; + case OPPOSITE: + him[option] = WANTNO; + himq[option] = EMPTY; + send_negotiation(conn, DONT, option); + break; } break; - } + } } static -void rec_wont(struct UrlData *data, int option) +void rec_wont(struct connectdata *conn, int option) { - switch(him[option]) - { - case NO: + switch(him[option]) + { + case NO: /* Already disabled */ break; - case YES: + case YES: him[option] = NO; - send_negotiation(data, DONT, option); + send_negotiation(conn, DONT, option); break; - case WANTNO: + case WANTNO: switch(himq[option]) { - case EMPTY: - him[option] = NO; - break; + case EMPTY: + him[option] = NO; + break; - case OPPOSITE: - him[option] = WANTYES; - himq[option] = EMPTY; - send_negotiation(data, DO, option); - break; + case OPPOSITE: + him[option] = WANTYES; + himq[option] = EMPTY; + send_negotiation(conn, DO, option); + break; } break; - case WANTYES: + case WANTYES: switch(himq[option]) { - case EMPTY: - him[option] = NO; - break; - case OPPOSITE: - him[option] = NO; - himq[option] = EMPTY; - break; + case EMPTY: + him[option] = NO; + break; + case OPPOSITE: + him[option] = NO; + himq[option] = EMPTY; + break; } break; - } + } } -void set_local_option(struct UrlData *data, int option, int newstate) +void set_local_option(struct connectdata *conn, int option, int newstate) { - if(newstate == YES) - { - switch(us[option]) - { + if(newstate == YES) + { + switch(us[option]) + { case NO: - us[option] = WANTYES; - send_negotiation(data, WILL, option); - break; + us[option] = WANTYES; + send_negotiation(conn, WILL, option); + break; case YES: - /* Already enabled */ - break; + /* Already enabled */ + break; case WANTNO: - switch(usq[option]) - { - case EMPTY: - /* Already negotiating for YES, queue the request */ - usq[option] = OPPOSITE; - break; - case OPPOSITE: - /* Error: already queued an enable request */ - break; - } - break; + switch(usq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + usq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; case WANTYES: - switch(usq[option]) - { - case EMPTY: - /* Error: already negotiating for enable */ - break; - case OPPOSITE: - usq[option] = EMPTY; - break; - } - break; - } - } - else /* NO */ - { - switch(us[option]) - { + switch(usq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + usq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(us[option]) + { case NO: - /* Already disabled */ - break; + /* Already disabled */ + break; case YES: - us[option] = WANTNO; - send_negotiation(data, WONT, option); - break; + us[option] = WANTNO; + send_negotiation(conn, WONT, option); + break; case WANTNO: - switch(usq[option]) - { - case EMPTY: - /* Already negotiating for NO */ - break; - case OPPOSITE: - usq[option] = EMPTY; - break; - } - break; + switch(usq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + usq[option] = EMPTY; + break; + } + break; case WANTYES: - switch(usq[option]) - { - case EMPTY: - usq[option] = OPPOSITE; - break; - case OPPOSITE: - break; - } - break; - } - } + switch(usq[option]) + { + case EMPTY: + usq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } } static -void rec_do(struct UrlData *data, int option) +void rec_do(struct connectdata *conn, int option) { - switch(us[option]) - { - case NO: + switch(us[option]) + { + case NO: if(us_preferred[option] == YES) { - us[option] = YES; - send_negotiation(data, WILL, option); + us[option] = YES; + send_negotiation(conn, WILL, option); } else { - send_negotiation(data, WONT, option); + send_negotiation(conn, WONT, option); } break; - case YES: + case YES: /* Already enabled */ break; - case WANTNO: + case WANTNO: switch(usq[option]) { - case EMPTY: - /* Error: DONT answered by WILL */ - us[option] = NO; - break; - case OPPOSITE: - /* Error: DONT answered by WILL */ - us[option] = YES; - usq[option] = EMPTY; - break; + case EMPTY: + /* Error: DONT answered by WILL */ + us[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + us[option] = YES; + usq[option] = EMPTY; + break; } break; - case WANTYES: + case WANTYES: switch(usq[option]) { - case EMPTY: - us[option] = YES; - break; - case OPPOSITE: - us[option] = WANTNO; - himq[option] = EMPTY; - send_negotiation(data, WONT, option); - break; + case EMPTY: + us[option] = YES; + break; + case OPPOSITE: + us[option] = WANTNO; + himq[option] = EMPTY; + send_negotiation(conn, WONT, option); + break; } break; - } + } } static -void rec_dont(struct UrlData *data, int option) +void rec_dont(struct connectdata *conn, int option) { - switch(us[option]) - { - case NO: + switch(us[option]) + { + case NO: /* Already disabled */ break; - case YES: + case YES: us[option] = NO; - send_negotiation(data, WONT, option); + send_negotiation(conn, WONT, option); break; - case WANTNO: + case WANTNO: switch(usq[option]) { - case EMPTY: - us[option] = NO; - break; + case EMPTY: + us[option] = NO; + break; - case OPPOSITE: - us[option] = WANTYES; - usq[option] = EMPTY; - send_negotiation(data, WILL, option); - break; + case OPPOSITE: + us[option] = WANTYES; + usq[option] = EMPTY; + send_negotiation(conn, WILL, option); + break; } break; - case WANTYES: + case WANTYES: switch(usq[option]) { - case EMPTY: - us[option] = NO; - break; - case OPPOSITE: - us[option] = NO; - usq[option] = EMPTY; - break; + case EMPTY: + us[option] = NO; + break; + case OPPOSITE: + us[option] = NO; + usq[option] = EMPTY; + break; } break; - } + } } @@ -599,60 +602,170 @@ static void printsub(struct UrlData *data, int direction, /* '<' or '>' */ unsigned char *pointer, /* where suboption data is */ int length) /* length of suboption data */ - { - int i = 0; - - if (data->bits.verbose) - { - if (direction) + int i = 0; + + if (data->bits.verbose) + { + if (direction) + { + printf("%s IAC SB ", (direction == '<')? "RCVD":"SENT"); + if (length >= 3) { - printf("%s IAC SB ", (direction == '<')? "RCVD":"SENT"); - if (length >= 3) - { - int j; - - i = pointer[length-2]; - j = pointer[length-1]; - - if (i != IAC || j != SE) - { - printf("(terminated by "); - if (TELOPT_OK(i)) - printf("%s ", TELOPT(i)); - else if (TELCMD_OK(i)) - printf("%s ", TELCMD(i)); - else - printf("%d ", i); - if (TELOPT_OK(j)) - printf("%s", TELOPT(j)); - else if (TELCMD_OK(j)) - printf("%s", TELCMD(j)); - else - printf("%d", j); - printf(", not IAC SE!) "); + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) + { + printf("(terminated by "); + if (TELOPT_OK(i)) + printf("%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + printf("%s ", TELCMD(i)); + else + printf("%d ", i); + if (TELOPT_OK(j)) + printf("%s", TELOPT(j)); + else if (TELCMD_OK(j)) + printf("%s", TELCMD(j)); + else + printf("%d", j); + printf(", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) + { + printf("(Empty suboption?)"); + return; + } + + if (TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + printf("%s", TELOPT(pointer[0])); + break; + default: + printf("%s (unsupported)", TELOPT(pointer[0])); + break; + } + } + else + printf("%d (unknown)", pointer[i]); + + switch(pointer[1]) { + case TELQUAL_IS: + printf(" IS"); + break; + case TELQUAL_SEND: + printf(" SEND"); + break; + case TELQUAL_INFO: + printf(" INFO/REPLY"); + break; + case TELQUAL_NAME: + printf(" NAME"); + break; + } + + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + pointer[length] = 0; + printf(" \"%s\"", &pointer[2]); + break; + case TELOPT_NEW_ENVIRON: + if(pointer[1] == TELQUAL_IS) { + printf(" "); + for(i = 3;i < length;i++) { + switch(pointer[i]) { + case NEW_ENV_VAR: + printf(", "); + break; + case NEW_ENV_VALUE: + printf(" = "); + break; + default: + printf("%c", pointer[i]); + break; } - } - length -= 2; + } + } + break; + default: + for (i = 2; i < length; i++) + printf(" %.2x", pointer[i]); + break; + } + + if (direction) + { + printf("\n"); + } + } +} + +static int check_telnet_options(struct connectdata *conn) +{ + struct curl_slist *head; + char option_keyword[128]; + char option_arg[256]; + char *buf; + struct UrlData *data = conn->data; + + /* Add the user name as an environment variable if it + was given on the command line */ + if(data->bits.user_passwd) + { + char *buf = malloc(256); + sprintf(buf, "USER,%s", data->user); + telnet_vars = curl_slist_append(telnet_vars, buf); + + us_preferred[TELOPT_NEW_ENVIRON] = YES; + } + + for(head = data->telnet_options; head; head=head->next) { + if(sscanf(head->data, "%127[^= ]%*[ =]%255s", + option_keyword, option_arg) == 2) { + + /* Terminal type */ + if(strequal(option_keyword, "TTYPE")) { + subopt_ttype = option_arg; + us_preferred[TELOPT_TTYPE] = YES; + continue; } - if (length < 1) - { - printf("(Empty suboption?)"); - return; + + /* Display variable */ + if(strequal(option_keyword, "XDISPLOC")) { + subopt_xdisploc = option_arg; + us_preferred[TELOPT_XDISPLOC] = YES; + continue; } - if (TELOPT_OK(pointer[0])) - printf("%s (unknown)", TELOPT(pointer[0])); - else - printf("%d (unknown)", pointer[i]); - for (i = 1; i < length; i++) - printf(" %d", pointer[i]); - - if (direction) - { - printf("\n"); + /* Environment variable */ + if(strequal(option_keyword, "NEW_ENV")) { + buf = strdup(option_arg); + if(!buf) + return CURLE_OUT_OF_MEMORY; + telnet_vars = curl_slist_append(telnet_vars, buf); + us_preferred[TELOPT_NEW_ENVIRON] = YES; + continue; } - } + + failf(data, "Unknown telnet option %s", head->data); + return CURLE_UNKNOWN_TELNET_OPTION; + } else { + failf(data, "Syntax error in telnet option: %s", head->data); + return CURLE_TELNET_OPTION_SYNTAX; + } + } + + return CURLE_OK; } /* @@ -663,171 +776,227 @@ static void printsub(struct UrlData *data, * No suboptions are supported yet. */ -static void suboption(struct UrlData *data) +static void suboption(struct connectdata *conn) { - printsub(data, '<', (unsigned char *)subbuffer, SB_LEN()+2); - return; + struct curl_slist *v; + unsigned char subchar; + unsigned char temp[2048]; + int len; + int tmplen; + char varname[128]; + char varval[128]; + struct UrlData *data = conn->data; + + printsub(data, '<', (unsigned char *)subbuffer, SB_LEN()+2); + switch (subchar = SB_GET()) { + case TELOPT_TTYPE: + len = strlen(subopt_ttype) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, + TELQUAL_IS, subopt_ttype, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_XDISPLOC: + len = strlen(subopt_xdisploc) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC, + TELQUAL_IS, subopt_xdisploc, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_NEW_ENVIRON: + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS); + len = 4; + + for(v = telnet_vars;v;v = v->next) { + tmplen = (strlen(v->data) + 1); + /* Add the variable only if it fits */ + if(len + tmplen < sizeof(temp)-6) { + sscanf(v->data, "%127[^,],%s", varname, varval); + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%s%c%s", NEW_ENV_VAR, varname, + NEW_ENV_VALUE, varval); + len += tmplen; + } + } + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%c", IAC, SE); + len += 2; + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + } + return; } static -void telrcv(struct UrlData *data, - unsigned char *inbuf, /* Data received from socket */ - int count) /* Number of bytes received */ +void telrcv(struct connectdata *conn, + unsigned char *inbuf, /* Data received from socket */ + int count) /* Number of bytes received */ { - unsigned char c; - int index = 0; + unsigned char c; + int index = 0; + struct UrlData *data = conn->data; - while(count--) - { - c = inbuf[index++]; + while(count--) + { + c = inbuf[index++]; - switch (telrcv_state) - { + switch (telrcv_state) + { case TS_CR: - telrcv_state = TS_DATA; - if (c == '\0') - { - break; /* Ignore \0 after CR */ - } + telrcv_state = TS_DATA; + if (c == '\0') + { + break; /* Ignore \0 after CR */ + } - Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); - continue; + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; case TS_DATA: - if (c == IAC) - { - telrcv_state = TS_IAC; - break; - } - else if(c == '\r') - { - telrcv_state = TS_CR; - } - - Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); - continue; + if (c == IAC) + { + telrcv_state = TS_IAC; + break; + } + else if(c == '\r') + { + telrcv_state = TS_CR; + } + + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; case TS_IAC: - process_iac: - switch (c) - { - case WILL: - telrcv_state = TS_WILL; - continue; - case WONT: - telrcv_state = TS_WONT; - continue; - case DO: - telrcv_state = TS_DO; - continue; - case DONT: - telrcv_state = TS_DONT; - continue; - case SB: - SB_CLEAR(); - telrcv_state = TS_SB; - continue; - case IAC: + process_iac: + switch (c) + { + case WILL: + telrcv_state = TS_WILL; + continue; + case WONT: + telrcv_state = TS_WONT; + continue; + case DO: + telrcv_state = TS_DO; + continue; + case DONT: + telrcv_state = TS_DONT; + continue; + case SB: + SB_CLEAR(); + telrcv_state = TS_SB; + continue; + case IAC: Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); break; - case DM: - case NOP: - case GA: - default: - printoption(data, "RCVD", IAC, c); - break; - } - telrcv_state = TS_DATA; - continue; + case DM: + case NOP: + case GA: + default: + printoption(data, "RCVD", IAC, c); + break; + } + telrcv_state = TS_DATA; + continue; case TS_WILL: - printoption(data, "RCVD", WILL, c); - rec_will(data, c); - telrcv_state = TS_DATA; - continue; + printoption(data, "RCVD", WILL, c); + please_negotiate = 1; + rec_will(conn, c); + telrcv_state = TS_DATA; + continue; case TS_WONT: - printoption(data, "RCVD", WONT, c); - rec_wont(data, c); - telrcv_state = TS_DATA; - continue; + printoption(data, "RCVD", WONT, c); + please_negotiate = 1; + rec_wont(conn, c); + telrcv_state = TS_DATA; + continue; case TS_DO: - printoption(data, "RCVD", DO, c); - rec_do(data, c); - telrcv_state = TS_DATA; - continue; + printoption(data, "RCVD", DO, c); + please_negotiate = 1; + rec_do(conn, c); + telrcv_state = TS_DATA; + continue; case TS_DONT: - printoption(data, "RCVD", DONT, c); - rec_dont(data, c); - telrcv_state = TS_DATA; - continue; + printoption(data, "RCVD", DONT, c); + please_negotiate = 1; + rec_dont(conn, c); + telrcv_state = TS_DATA; + continue; case TS_SB: - if (c == IAC) - { - telrcv_state = TS_SE; - } - else - { - SB_ACCUM(c); - } - continue; + if (c == IAC) + { + telrcv_state = TS_SE; + } + else + { + SB_ACCUM(c); + } + continue; case TS_SE: - if (c != SE) - { - if (c != IAC) - { - /* - * This is an error. We only expect to get - * "IAC IAC" or "IAC SE". Several things may - * have happend. An IAC was not doubled, the - * IAC SE was left off, or another option got - * inserted into the suboption are all possibilities. - * If we assume that the IAC was not doubled, - * and really the IAC SE was left off, we could - * get into an infinate loop here. So, instead, - * we terminate the suboption, and process the - * partial suboption if we can. - */ - SB_ACCUM((unsigned char)IAC); - SB_ACCUM(c); - subpointer -= 2; - SB_TERM(); + if (c != SE) + { + if (c != IAC) + { + /* + * This is an error. We only expect to get + * "IAC IAC" or "IAC SE". Several things may + * have happend. An IAC was not doubled, the + * IAC SE was left off, or another option got + * inserted into the suboption are all possibilities. + * If we assume that the IAC was not doubled, + * and really the IAC SE was left off, we could + * get into an infinate loop here. So, instead, + * we terminate the suboption, and process the + * partial suboption if we can. + */ + SB_ACCUM((unsigned char)IAC); + SB_ACCUM(c); + subpointer -= 2; + SB_TERM(); - printoption(data, "In SUBOPTION processing, RCVD", IAC, c); - suboption(data); /* handle sub-option */ - telrcv_state = TS_IAC; - goto process_iac; - } - SB_ACCUM(c); - telrcv_state = TS_SB; - } - else - { - SB_ACCUM((unsigned char)IAC); - SB_ACCUM((unsigned char)SE); - subpointer -= 2; - SB_TERM(); - suboption(data); /* handle sub-option */ - telrcv_state = TS_DATA; - } - break; - } - } + printoption(data, "In SUBOPTION processing, RCVD", IAC, c); + suboption(conn); /* handle sub-option */ + telrcv_state = TS_IAC; + goto process_iac; + } + SB_ACCUM(c); + telrcv_state = TS_SB; + } + else + { + SB_ACCUM((unsigned char)IAC); + SB_ACCUM((unsigned char)SE); + subpointer -= 2; + SB_TERM(); + suboption(conn); /* handle sub-option */ + telrcv_state = TS_DATA; + } + break; + } + } } CURLcode Curl_telnet_done(struct connectdata *conn) { + curl_slist_free_all(telnet_vars); return CURLE_OK; } CURLcode Curl_telnet(struct connectdata *conn) { + CURLcode code; struct UrlData *data = conn->data; - int sockfd = data->firstsocket; + int sockfd = conn->firstsocket; fd_set readfd; fd_set keepfd; @@ -835,8 +1004,12 @@ CURLcode Curl_telnet(struct connectdata *conn) char *buf = data->buffer; ssize_t nread; - init_telnet(data); + init_telnet(conn); + code = check_telnet_options(conn); + if(code) + return code; + FD_ZERO (&readfd); /* clear it */ FD_SET (sockfd, &readfd); FD_SET (1, &readfd); @@ -867,7 +1040,7 @@ CURLcode Curl_telnet(struct connectdata *conn) if(outbuf[0] == IAC) outbuf[out_count++] = IAC; - Curl_write(conn, data->firstsocket, outbuf, + Curl_write(conn, conn->firstsocket, outbuf, out_count, &bytes_written); } } @@ -882,12 +1055,18 @@ CURLcode Curl_telnet(struct connectdata *conn) break; } - telrcv(data, (unsigned char *)buf, nread); + telrcv(conn, (unsigned char *)buf, nread); + + /* Negotiate if the peer has started negotiating, + otherwise don't. We don't want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(please_negotiate && !already_negotiated) { + negotiate(conn); + already_negotiated = 1; + } } } } /* mark this as "no further transfer wanted" */ return Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); } - - diff --git a/lib/transfer.c b/lib/transfer.c index 81bcfca2b..5eaeddb6b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -366,6 +366,16 @@ _Transfer(struct connectdata *c_conn) if (strnequal("Content-Length", p, 14) && sscanf (p+14, ": %ld", &contentlength)) conn->size = contentlength; + else if (strnequal("Connection: close", p, + strlen("Connection: close"))) { + /* + * [RFC 2616, section 8.1.2.1] + * "Connection: close" is HTTP/1.1 language and means that + * the connection will close when this request has been + * served. + */ + conn->bits.close = TRUE; /* close when done */ + } else if (strnequal("Content-Range", p, 13)) { if (sscanf (p+13, ": bytes %d-", &offset) || sscanf (p+13, ": bytes: %d-", &offset)) { @@ -635,7 +645,9 @@ CURLcode curl_transfer(CURL *curl) if (data->maxredirs && (data->followlocation >= data->maxredirs)) { failf(data,"Maximum (%d) redirects followed", data->maxredirs); +#ifdef USE_OLD_DISCONNECT curl_disconnect(c_connect); +#endif res=CURLE_TOO_MANY_REDIRECTS; break; } @@ -772,11 +784,15 @@ CURLcode curl_transfer(CURL *curl) */ break; } +#ifdef USE_OLD_DISCONNECT curl_disconnect(c_connect); +#endif continue; } +#ifdef USE_OLD_DISCONNECT curl_disconnect(c_connect); +#endif } break; /* it only reaches here when this shouldn't loop */ diff --git a/lib/url.c b/lib/url.c index d4b33e828..0dbf4ed16 100644 --- a/lib/url.c +++ b/lib/url.c @@ -113,11 +113,15 @@ #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 _urlget(struct UrlData *data); - /* does nothing, returns OK */ CURLcode curl_init(void) { @@ -129,34 +133,12 @@ void curl_free(void) { } -void static urlfree(struct UrlData *data, bool totally) +CURLcode curl_close(CURL *curl) { -#ifdef USE_SSLEAY - if (data->ssl.use) { - if(data->ssl.handle) { - (void)SSL_shutdown(data->ssl.handle); - SSL_set_connect_state(data->ssl.handle); - - SSL_free (data->ssl.handle); - data->ssl.handle = NULL; - } - if(data->ssl.ctx) { - SSL_CTX_free (data->ssl.ctx); - data->ssl.ctx = NULL; - } - data->ssl.use = FALSE; /* get back to ordinary socket usage */ - } -#endif /* USE_SSLEAY */ - - /* close possibly still open sockets */ - if(-1 != data->secondarysocket) { - sclose(data->secondarysocket); - data->secondarysocket = -1; - } - if(-1 != data->firstsocket) { - sclose(data->firstsocket); - data->firstsocket=-1; - } + 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;; @@ -168,7 +150,8 @@ void static urlfree(struct UrlData *data, bool totally) switch off that knowledge again... */ data->bits.httpproxy=FALSE; } - + + if(data->bits.rangestringalloc) { free(data->range); data->range=NULL; @@ -204,44 +187,29 @@ void static urlfree(struct UrlData *data, bool totally) data->ptr_host=NULL; } - if(totally) { - /* we let the switch decide whether we're doing a part or total - cleanup */ - - /* check for allocated [URL] memory to free: */ - if(data->freethis) - free(data->freethis); - - if(data->headerbuff) - free(data->headerbuff); + /* check for allocated [URL] memory to free: */ + if(data->freethis) + free(data->freethis); - if(data->free_referer) - free(data->referer); + if(data->headerbuff) + free(data->headerbuff); - if(data->bits.urlstringalloc) - /* the URL is allocated, free it! */ - free(data->url); + if(data->free_referer) + free(data->referer); - Curl_cookie_cleanup(data->cookies); + if(data->bits.urlstringalloc) + /* the URL is allocated, free it! */ + free(data->url); - free(data); + Curl_cookie_cleanup(data->cookies); - /* global cleanup */ - curl_free(); - } -} + /* free the connection cache */ + free(data->connects); -CURLcode curl_close(CURL *curl) -{ - struct UrlData *data=(struct UrlData *)curl; - - void *protocol = data->proto.generic; + free(data); - /* total session cleanup (frees 'data' as well!)*/ - urlfree(data, TRUE); - - if(protocol) - free(protocol); + /* global cleanup */ + curl_free(); return CURLE_OK; } @@ -284,9 +252,6 @@ CURLcode curl_open(CURL **curl, char *url) data->in = stdin; /* default input from stdin */ data->err = stderr; /* default stderr to stderr */ - data->firstsocket = -1; /* no file descriptor */ - data->secondarysocket = -1; /* no file descriptor */ - /* use fwrite as default function to store output */ data->fwrite = (size_t (*)(char *, size_t, size_t, FILE *))fwrite; @@ -302,6 +267,18 @@ CURLcode curl_open(CURL **curl, char *url) 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); + *curl = data; return CURLE_OK; } @@ -539,6 +516,9 @@ CURLcode curl_setopt(CURL *curl, CURLoption option, ...) data->ssl.CAfile = va_arg(param, char *); data->ssl.CApath = NULL; /*This does not work on windows.*/ break; + case CURLOPT_TELNETOPTIONS: + data->telnet_options = va_arg(param, struct curl_slist *); + break; default: /* unknown tag and its companion, just ignore: */ return CURLE_READ_ERROR; /* correct this */ @@ -563,11 +543,8 @@ CURLcode curl_disconnect(CURLconnect *c_connect) { struct connectdata *conn = c_connect; - struct UrlData *data = conn->data; - - if(data->proto.generic) - free(data->proto.generic); - data->proto.generic=NULL; /* it is gone */ + if(conn->proto.generic) + free(conn->proto.generic); #ifdef ENABLE_IPV6 if(conn->res) /* host name info */ @@ -580,80 +557,407 @@ CURLcode curl_disconnect(CURLconnect *c_connect) if(conn->path) /* the URL path part */ free(conn->path); - free(conn); /* free the connection oriented data */ +#ifdef USE_SSLEAY + if (conn->ssl.use) { + if(conn->ssl.handle) { + (void)SSL_shutdown(conn->ssl.handle); + SSL_set_connect_state(conn->ssl.handle); - /* clean up the sockets and SSL stuff from the previous "round" */ - urlfree(data, FALSE); + 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); + conn->secondarysocket = -1; + } + if(-1 != conn->firstsocket) { + sclose(conn->firstsocket); + conn->firstsocket=-1; + } + + free(conn); /* free all the connection oriented data */ return CURLE_OK; } -static CURLcode _connect(CURL *curl, CURLconnect **in_connect) +/* + * 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) { - char *tmp; - char *buf; - CURLcode result; - char resumerange[40]=""; - struct UrlData *data = curl; + 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(strequal(needle->protostr, check->protostr) && + strequal(needle->name, check->name) && + (needle->port == check->port) ) { + *usethis = check; + return TRUE; /* yes, we found one to use! */ + } + } + 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; struct connectdata *conn; - char endbracket; -#ifdef HAVE_SIGACTION - struct sigaction sigact; + 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); + + 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); + + memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr)); + memcpy((char *)&(conn->serv_addr.sin_addr), + conn->hp->h_addr, conn->hp->h_length); + conn->serv_addr.sin_family = conn->hp->h_addrtype; + conn->serv_addr.sin_port = htons(data->port); #endif - int urllen; -#ifdef ENABLE_IPV6 - struct addrinfo *ai; + +#if !defined(WIN32)||defined(__CYGWIN32__) + /* We don't generally like checking for OS-versions, we should make this + HAVE_XXXX based, although at the moment I don't have a decent test for + this! */ + +#ifdef HAVE_INET_NTOA + +#ifndef INADDR_NONE +#define INADDR_NONE (unsigned long) ~0 #endif +#ifndef ENABLE_IPV6 /************************************************************* - * Check input data + * Select device to bind socket to *************************************************************/ + if (data->device && (strlen(data->device)<255)) { + struct sockaddr_in sa; + struct hostent *h=NULL; + char *hostdataptr=NULL; + size_t size; + char myhost[256] = ""; + unsigned long in; - if(!data || (data->handle != STRUCT_OPEN)) - return CURLE_BAD_FUNCTION_ARGUMENT; /* TBD: make error codes */ + if(Curl_if2ip(data->device, myhost, sizeof(myhost))) { + h = Curl_gethost(data, myhost, &hostdataptr); + } + else { + if(strlen(data->device)>1) { + h = Curl_gethost(data, data->device, &hostdataptr); + } + if(h) { + /* we know data->device is shorter than the myhost array */ + strcpy(myhost, data->device); + } + } - if(!data->url) - return CURLE_URL_MALFORMAT; + if(! *myhost) { + /* need to fix this + h=Curl_gethost(data, + getmyhost(*myhost,sizeof(myhost)), + hostent_buf, + sizeof(hostent_buf)); + */ + printf("in here\n"); + } - /************************************************************* - * Allocate and initiate a connection struct - *************************************************************/ - conn = (struct connectdata *)malloc(sizeof(struct connectdata)); - if(!conn) { - *in_connect = NULL; /* clear the pointer */ - return CURLE_OUT_OF_MEMORY; - } - *in_connect = conn; + infof(data, "We connect from %s\n", myhost); - memset(conn, 0, sizeof(struct connectdata)); - conn->handle = STRUCT_CONNECT; + if ( (in=inet_addr(myhost)) != INADDR_NONE ) { + + if ( h ) { + memset((char *)&sa, 0, sizeof(sa)); + memcpy((char *)&sa.sin_addr, + h->h_addr, + h->h_length); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = in; + sa.sin_port = 0; /* get any port */ + + if( bind(conn->firstsocket, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { + /* we succeeded to bind */ + struct sockaddr_in add; + + size = sizeof(add); + if(getsockname(conn->firstsocket, (struct sockaddr *) &add, + (int *)&size)<0) { + failf(data, "getsockname() failed"); + return CURLE_HTTP_PORT_FAILED; + } + } + else { + switch(errno) { + case EBADF: + failf(data, "Invalid descriptor: %d", errno); + break; + case EINVAL: + failf(data, "Invalid request: %d", errno); + break; + case EACCES: + failf(data, "Address is protected, user not superuser: %d", errno); + break; + case ENOTSOCK: + failf(data, + "Argument is a descriptor for a file, not a socket: %d", + errno); + break; + case EFAULT: + failf(data, "Inaccessable memory error: %d", errno); + break; + case ENAMETOOLONG: + failf(data, "Address too long: %d", errno); + break; + case ENOMEM: + failf(data, "Insufficient kernel memory was available: %d", errno); + break; + default: + failf(data,"errno %d\n"); + } /* end of switch */ + + return CURLE_HTTP_PORT_FAILED; + } /* end of else */ + + } /* end of if h */ + else { + failf(data,"could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + } /* end of inet_addr */ - conn->data = data; /* remember our daddy */ - conn->state = CONN_INIT; + else { + failf(data, "could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } - conn->upload_bufsize = UPLOAD_BUFSIZE; /* the smallest upload buffer size - we use */ + if(hostdataptr) + free(hostdataptr); /* allocated by Curl_gethost() */ - buf = data->buffer; /* this is our buffer */ + } /* end of device selection support */ +#endif /* end of HAVE_INET_NTOA */ +#endif /* end of not WIN32 */ +#endif /*ENABLE_IPV6*/ /************************************************************* - * Set signal handler + * Connect to server/proxy *************************************************************/ +#ifdef ENABLE_IPV6 + data->firstsocket = -1; + for (ai = conn->res; ai; ai = ai->ai_next) { + data->firstsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (data->firstsocket < 0) + continue; + + if (connect(data->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) { + close(data->firstsocket); + data->firstsocket = -1; + continue; + } + + break; + } + if (data->firstsocket < 0) { + failf(data, strerror(errno)); + return CURLE_COULDNT_CONNECT; + } +#else + if (connect(conn->firstsocket, + (struct sockaddr *) &(conn->serv_addr), + sizeof(conn->serv_addr) + ) < 0) { + switch(errno) { +#ifdef ECONNREFUSED + /* this should be made nicer */ + case ECONNREFUSED: + failf(data, "Connection refused"); + break; + case EFAULT: + failf(data, "Invalid socket address: %d",errno); + break; + case EISCONN: + failf(data, "Socket already connected: %d",errno); + break; + case ETIMEDOUT: + failf(data, "Timeout while accepting connection, server busy: %d",errno); + break; + case ENETUNREACH: + failf(data, "Network is unreachable: %d",errno); + break; + case EADDRINUSE: + failf(data, "Local address already in use: %d",errno); + break; + case EINPROGRESS: + failf(data, "Socket is nonblocking and connection can not be completed immediately: %d",errno); + break; + case EALREADY: + failf(data, "Socket is nonblocking and a previous connection attempt not completed: %d",errno); + break; + case EAGAIN: + failf(data, "No more free local ports: %d",errno); + break; + case EACCES: + case EPERM: + failf(data, "Attempt to connect to broadcast address without socket broadcast flag or local firewall rule violated: %d",errno); + break; +#endif + case EINTR: + failf(data, "Connection timed out"); + break; + default: + failf(data, "Can't connect to server: %d", errno); + break; + } + return CURLE_COULDNT_CONNECT; + } +#endif + + return CURLE_OK; +} + + +static CURLcode _connect(CURL *curl, CURLconnect **in_connect) +{ + char *tmp; + char *buf; + CURLcode result; + char resumerange[40]=""; + struct UrlData *data = curl; + struct connectdata *conn; + struct connectdata *conn_temp; + char endbracket; #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; + struct sigaction sigact; #endif - sigaction(SIGALRM, &sigact, NULL); -#else - /* no sigaction(), revert to the much lamer signal() */ -#ifdef HAVE_SIGNAL - signal(SIGALRM, alarmfunc); + int urllen; +#ifdef ENABLE_IPV6 + struct addrinfo *ai; #endif -#endif + /************************************************************* + * Check input data + *************************************************************/ + + if(!data || (data->handle != STRUCT_OPEN)) + return CURLE_BAD_FUNCTION_ARGUMENT; /* TBD: make error codes */ + + if(!data->url) + return CURLE_URL_MALFORMAT; + + /* First, split up the current URL in parts so that we can use the + parts for checking against the already present connections. In order + to not have to modify everything at once, we allocate a temporary + connection data struct and fill in for comparison purposes. */ + + conn = (struct connectdata *)malloc(sizeof(struct connectdata)); + if(!conn) { + *in_connect = NULL; /* clear the pointer */ + return CURLE_OUT_OF_MEMORY; + } + /* we have to init the struct */ + memset(conn, 0, sizeof(struct connectdata)); + + /* and we setup a few fields in case we end up actually using this struct */ + conn->handle = STRUCT_CONNECT; /* this is a connection handle */ + conn->data = data; /* remember our daddy */ + conn->state = CONN_INIT; /* init state */ + conn->upload_bufsize = UPLOAD_BUFSIZE; /* default upload buffer size */ + conn->firstsocket = -1; /* no file descriptor */ + conn->secondarysocket = -1; /* no file descriptor */ + conn->connectindex = -1; /* no index */ /*********************************************************** * We need to allocate memory to store the path in. We get the size of the @@ -678,8 +982,8 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) * url ... ************************************************************/ if((2 == sscanf(data->url, "%64[^:]://%[^\n]", - conn->proto, - conn->path)) && strequal(conn->proto, "file")) { + conn->protostr, + conn->path)) && strequal(conn->protostr, "file")) { /* * we deal with file:/// differently since it supports no * hostname other than "localhost" and "127.0.0.1", which is unique among @@ -692,7 +996,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) * quietly ommitted */ strcpy(conn->path, &conn->path[10]); - strcpy(conn->proto, "file"); /* store protocol string lowercase */ + strcpy(conn->protostr, "file"); /* store protocol string lowercase */ } else { /* Set default host and default path */ @@ -701,7 +1005,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) if (2 > sscanf(data->url, "%64[^\n:]://%256[^\n/]%[^\n]", - conn->proto, conn->gname, conn->path)) { + conn->protostr, conn->gname, conn->path)) { /* * The URL was badly formatted, let's try the browser-style _without_ @@ -722,28 +1026,49 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) */ if(strnequal(conn->gname, "FTP", 3)) { - strcpy(conn->proto, "ftp"); + strcpy(conn->protostr, "ftp"); } else if(strnequal(conn->gname, "GOPHER", 6)) - strcpy(conn->proto, "gopher"); + strcpy(conn->protostr, "gopher"); #ifdef USE_SSLEAY else if(strnequal(conn->gname, "HTTPS", 5)) - strcpy(conn->proto, "https"); + strcpy(conn->protostr, "https"); #endif /* USE_SSLEAY */ else if(strnequal(conn->gname, "TELNET", 6)) - strcpy(conn->proto, "telnet"); + strcpy(conn->protostr, "telnet"); else if (strnequal(conn->gname, "DICT", sizeof("DICT")-1)) - strcpy(conn->proto, "DICT"); + strcpy(conn->protostr, "DICT"); else if (strnequal(conn->gname, "LDAP", sizeof("LDAP")-1)) - strcpy(conn->proto, "LDAP"); + strcpy(conn->protostr, "LDAP"); else { - strcpy(conn->proto, "http"); + strcpy(conn->protostr, "http"); } conn->protocol |= PROT_MISSING; /* not given in URL */ } } + buf = data->buffer; /* this is our buffer */ + + /************************************************************* + * 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 + /************************************************************* * Take care of user and password authentication stuff *************************************************************/ @@ -803,7 +1128,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) *************************************************************/ conn->name = conn->gname; conn->ppath = conn->path; - data->hostname = conn->name; + conn->hostname = conn->name; /************************************************************* @@ -854,7 +1179,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } if(!nope) { /* It was not listed as without proxy */ - char *protop = conn->proto; + char *protop = conn->protostr; char *envp = proxy_env; char *prox; @@ -905,7 +1230,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) */ char *reurl; - reurl = aprintf("%s://%s", conn->proto, data->url); + reurl = aprintf("%s://%s", conn->protostr, data->url); if(!reurl) return CURLE_OUT_OF_MEMORY; @@ -951,7 +1276,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) * Setup internals depending on protocol *************************************************************/ - if (strequal(conn->proto, "HTTP")) { + if (strequal(conn->protostr, "HTTP")) { if(!data->port) data->port = PORT_HTTP; data->remote_port = PORT_HTTP; @@ -960,7 +1285,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->curl_done = Curl_http_done; conn->curl_close = Curl_http_close; } - else if (strequal(conn->proto, "HTTPS")) { + else if (strequal(conn->protostr, "HTTPS")) { #ifdef USE_SSLEAY if(!data->port) data->port = PORT_HTTPS; @@ -978,7 +1303,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) return CURLE_UNSUPPORTED_PROTOCOL; #endif /* !USE_SSLEAY */ } - else if (strequal(conn->proto, "GOPHER")) { + else if (strequal(conn->protostr, "GOPHER")) { if(!data->port) data->port = PORT_GOPHER; data->remote_port = PORT_GOPHER; @@ -993,7 +1318,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->curl_done = Curl_http_done; conn->curl_close = Curl_http_close; } - else if(strequal(conn->proto, "FTP")) { + else if(strequal(conn->protostr, "FTP")) { char *type; if(!data->port) data->port = PORT_FTP; @@ -1041,7 +1366,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } } } - else if(strequal(conn->proto, "TELNET")) { + else if(strequal(conn->protostr, "TELNET")) { /* telnet testing factory */ conn->protocol |= PROT_TELNET; if(!data->port) @@ -1052,7 +1377,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->curl_done = Curl_telnet_done; } - else if (strequal(conn->proto, "DICT")) { + else if (strequal(conn->protostr, "DICT")) { conn->protocol |= PROT_DICT; if(!data->port) data->port = PORT_DICT; @@ -1060,7 +1385,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->curl_do = Curl_dict; conn->curl_done = Curl_dict_done; } - else if (strequal(conn->proto, "LDAP")) { + else if (strequal(conn->protostr, "LDAP")) { conn->protocol |= PROT_LDAP; if(!data->port) data->port = PORT_LDAP; @@ -1068,7 +1393,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->curl_do = Curl_ldap; conn->curl_done = Curl_ldap_done; } - else if (strequal(conn->proto, "FILE")) { + else if (strequal(conn->protostr, "FILE")) { conn->protocol |= PROT_FILE; conn->curl_do = file; @@ -1082,7 +1407,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) else { /* We fell through all checks and thus we don't support the specified protocol */ - failf(data, "Unsupported protocol: %s", conn->proto); + failf(data, "Unsupported protocol: %s", conn->protostr); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -1090,9 +1415,9 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) * .netrc scanning coming up *************************************************************/ if(data->bits.use_netrc) { - if(Curl_parsenetrc(data->hostname, data->user, data->passwd)) { + if(Curl_parsenetrc(conn->hostname, data->user, data->passwd)) { infof(data, "Couldn't find host %s in the .netrc file, using defaults", - data->hostname); + conn->hostname); } else data->bits.user_passwd = 1; /* enable user+password */ @@ -1195,6 +1520,39 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) data->remote_port = atoi(tmp); } + /* copy the port-specifics to the connection struct */ + conn->port = data->port; + conn->remote_port = data->remote_port; + + /************************************************************* + * Check the current list of connections to see if we can + * re-use an already existing one or if we have to create a + * new one. + *************************************************************/ + + if(ConnectionExists(data, conn, &conn_temp)) { + /* + * We already have a connection for this, we got the former connection + * in the conn_temp variable and thus we need to cleanup the one we + * just allocated before we can move along and use the previously + * existing one. + */ + char *path = conn->path; /* setup the current path pointer properly */ + free(conn); /* we don't need this new one */ + conn = conn_temp; /* use this connection from now on */ + free(conn->path); /* free the previous path pointer */ + conn->path = path; /* use this one */ + conn->ppath = path; /* set this too */ + } + else { + /* + * This is a brand new connection, so let's store it in the connection + * cache of ours! + */ + ConnectionStore(data, conn); + } + *in_connect = conn; + /************************************************************* * Resolve the name of the server or proxy *************************************************************/ @@ -1203,12 +1561,16 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) * there, thus overriding any defaults that might have been set above. */ data->port = data->remote_port; /* it is the same port */ - /* Connect to target host right on */ + /* Resolve target host right on */ #ifdef ENABLE_IPV6 - conn->res = Curl_getaddrinfo(data, conn->name, data->port); + if(!conn->res) + /* it might already be set if reusing a connection */ + conn->res = Curl_getaddrinfo(data, conn->name, data->port); if(!conn->res) #else - conn->hp = Curl_gethost(data, conn->name, &conn->hostent_buf); + if(!conn->hp) + /* it might already be set if reusing a connection */ + conn->hp = Curl_gethost(data, conn->name, &conn->hostent_buf); if(!conn->hp) #endif { @@ -1216,7 +1578,9 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) return CURLE_COULDNT_RESOLVE_HOST; } } - else { + else if(!conn->hp) { + /* This is a proxy that hasn't been resolved yet. It may be resolved + if we're reusing an existing connection. */ #ifdef ENABLE_IPV6 failf(data, "proxy yet to be supported"); return CURLE_OUT_OF_MEMORY; @@ -1261,7 +1625,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) data->port = data->proxyport; } - /* connect to proxy */ + /* resolve proxy */ conn->hp = Curl_gethost(data, proxyptr, &conn->hostent_buf); if(!conn->hp) { failf(data, "Couldn't resolve proxy '%s'", proxyptr); @@ -1273,210 +1637,12 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } Curl_pgrsTime(data, TIMER_NAMELOOKUP); -#ifndef ENABLE_IPV6 - data->firstsocket = socket(AF_INET, SOCK_STREAM, 0); - - memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr)); - memcpy((char *)&(conn->serv_addr.sin_addr), - conn->hp->h_addr, conn->hp->h_length); - conn->serv_addr.sin_family = conn->hp->h_addrtype; - conn->serv_addr.sin_port = htons(data->port); -#endif - -#if !defined(WIN32)||defined(__CYGWIN32__) - /* We don't generally like checking for OS-versions, we should make this - HAVE_XXXX based, although at the moment I don't have a decent test for - this! */ - -#ifdef HAVE_INET_NTOA - -#ifndef INADDR_NONE -#define INADDR_NONE (unsigned long) ~0 -#endif - -#ifndef ENABLE_IPV6 - /************************************************************* - * Select device to bind socket to - *************************************************************/ - if (data->device && (strlen(data->device)<255)) { - struct sockaddr_in sa; - struct hostent *h=NULL; - char *hostdataptr=NULL; - size_t size; - char myhost[256] = ""; - unsigned long in; - - if(Curl_if2ip(data->device, myhost, sizeof(myhost))) { - h = Curl_gethost(data, myhost, &hostdataptr); - } - else { - if(strlen(data->device)>1) { - h = Curl_gethost(data, data->device, &hostdataptr); - } - if(h) { - /* we know data->device is shorter than the myhost array */ - strcpy(myhost, data->device); - } - } - - if(! *myhost) { - /* need to fix this - h=Curl_gethost(data, - getmyhost(*myhost,sizeof(myhost)), - hostent_buf, - sizeof(hostent_buf)); - */ - printf("in here\n"); - } - - infof(data, "We connect from %s\n", myhost); - - if ( (in=inet_addr(myhost)) != INADDR_NONE ) { - - if ( h ) { - memset((char *)&sa, 0, sizeof(sa)); - memcpy((char *)&sa.sin_addr, - h->h_addr, - h->h_length); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = in; - sa.sin_port = 0; /* get any port */ - - if( bind(data->firstsocket, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { - /* we succeeded to bind */ - struct sockaddr_in add; - - size = sizeof(add); - if(getsockname(data->firstsocket, (struct sockaddr *) &add, - (int *)&size)<0) { - failf(data, "getsockname() failed"); - return CURLE_HTTP_PORT_FAILED; - } - } - else { - switch(errno) { - case EBADF: - failf(data, "Invalid descriptor: %d", errno); - break; - case EINVAL: - failf(data, "Invalid request: %d", errno); - break; - case EACCES: - failf(data, "Address is protected, user not superuser: %d", errno); - break; - case ENOTSOCK: - failf(data, - "Argument is a descriptor for a file, not a socket: %d", - errno); - break; - case EFAULT: - failf(data, "Inaccessable memory error: %d", errno); - break; - case ENAMETOOLONG: - failf(data, "Address too long: %d", errno); - break; - case ENOMEM: - failf(data, "Insufficient kernel memory was available: %d", errno); - break; - default: - failf(data,"errno %d\n"); - } /* end of switch */ - - return CURLE_HTTP_PORT_FAILED; - } /* end of else */ - - } /* end of if h */ - else { - failf(data,"could't find my own IP address (%s)", myhost); - return CURLE_HTTP_PORT_FAILED; - } - } /* end of inet_addr */ - - else { - failf(data, "could't find my own IP address (%s)", myhost); - return CURLE_HTTP_PORT_FAILED; - } - - if(hostdataptr) - free(hostdataptr); /* allocated by Curl_gethost() */ - - } /* end of device selection support */ -#endif /* end of HAVE_INET_NTOA */ -#endif /* end of not WIN32 */ -#endif /*ENABLE_IPV6*/ - - /************************************************************* - * Connect to server/proxy - *************************************************************/ -#ifdef ENABLE_IPV6 - data->firstsocket = -1; - for (ai = conn->res; ai; ai = ai->ai_next) { - data->firstsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (data->firstsocket < 0) - continue; - - if (connect(data->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) { - close(data->firstsocket); - data->firstsocket = -1; - continue; - } - - break; - } - if (data->firstsocket < 0) { - failf(data, strerror(errno)); - return CURLE_COULDNT_CONNECT; - } -#else - if (connect(data->firstsocket, - (struct sockaddr *) &(conn->serv_addr), - sizeof(conn->serv_addr) - ) < 0) { - switch(errno) { -#ifdef ECONNREFUSED - /* this should be made nicer */ - case ECONNREFUSED: - failf(data, "Connection refused"); - break; - case EFAULT: - failf(data, "Invalid socket address: %d",errno); - break; - case EISCONN: - failf(data, "Socket already connected: %d",errno); - break; - case ETIMEDOUT: - failf(data, "Timeout while accepting connection, server busy: %d",errno); - break; - case ENETUNREACH: - failf(data, "Network is unreachable: %d",errno); - break; - case EADDRINUSE: - failf(data, "Local address already in use: %d",errno); - break; - case EINPROGRESS: - failf(data, "Socket is nonblocking and connection can not be completed immediately: %d",errno); - break; - case EALREADY: - failf(data, "Socket is nonblocking and a previous connection attempt not completed: %d",errno); - break; - case EAGAIN: - failf(data, "No more free local ports: %d",errno); - break; - case EACCES: - case EPERM: - failf(data, "Attempt to connect to broadcast address without socket broadcast flag or local firewall rule violated: %d",errno); - break; -#endif - case EINTR: - failf(data, "Connection timed out"); - break; - default: - failf(data, "Can't connect to server: %d", errno); - break; - } - return CURLE_COULDNT_CONNECT; + if(-1 == conn->firstsocket) { + /* Connect only if not already connected! */ + result = ConnectPlease(data, conn); + if(CURLE_OK != result) + return result; } -#endif /************************************************************* * Proxy authentication @@ -1621,6 +1787,7 @@ CURLcode curl_done(CURLconnect *c_connect) struct connectdata *conn = c_connect; struct UrlData *data; CURLcode result; + int index; if(!conn || (conn->handle!= STRUCT_CONNECT)) { return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1641,6 +1808,14 @@ CURLcode curl_done(CURLconnect *c_connect) conn->state = CONN_DONE; + /* if bits.close is TRUE, it means that the connection should be closed + in spite of all our efforts to be nice */ + if((CURLE_OK == result) && conn->bits.close) { + index = conn->connectindex; /* get the index */ + result = curl_disconnect(conn); /* close the connection */ + data->connects[index]=NULL; /* clear the pointer */ + } + return result; } diff --git a/lib/urldata.h b/lib/urldata.h index b660009ac..fe098c3f6 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -133,6 +133,61 @@ enum protection_level { }; #endif +/* struct for data related to SSL and SSL connections */ +struct ssl_connect_data { + bool use; /* use ssl encrypted communications TRUE/FALSE */ +#ifdef USE_SSLEAY + /* these ones requires specific SSL-types */ + SSL_CTX* ctx; + SSL* handle; + X509* server_cert; +#endif /* USE_SSLEAY */ +}; + +struct ssl_config_data { + long version; /* what version the client wants to use */ + long certverifyresult; /* result from the certificate verification */ + long verifypeer; /* set TRUE if this is desired */ + char *CApath; /* DOES NOT WORK ON WINDOWS */ + char *CAfile; /* cerficate to verify peer against */ +}; + +/**************************************************************************** + * HTTP unique setup + ***************************************************************************/ +struct HTTP { + struct FormData *sendit; + int postsize; + char *p_pragma; /* Pragma: string */ + char *p_accept; /* Accept: string */ + long readbytecount; + long writebytecount; + + /* For FORM posting */ + struct Form form; + size_t (*storefread)(char *, size_t , size_t , FILE *); + FILE *in; +}; + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +struct FTP { + long *bytecountp; + char *user; /* user name string */ + char *passwd; /* password string */ + char *urlpath; /* the originally given path part of the URL */ + char *dir; /* decoded directory */ + char *file; /* decoded file */ +}; + +/* + * Boolean values that concerns this connection. + */ +struct ConnectBits { + bool close; /* if set, we close the connection after this request */ +}; + /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. @@ -145,6 +200,9 @@ struct connectdata { Handle handle; /* struct identifier */ struct UrlData *data; /* link to the root CURL struct */ + int connectindex; /* what index in the connects index this particular + struct has */ + /**** curl_connect() phase fields */ ConnState state; /* for state dependent actions */ @@ -166,17 +224,27 @@ struct connectdata { struct hostent *hp; struct sockaddr_in serv_addr; #endif - char proto[64]; /* store the protocol string in this buffer */ + char protostr[64]; /* store the protocol string in this buffer */ char gname[257]; /* store the hostname in this buffer */ char *name; /* host name pointer to fool around with */ char *path; /* allocated buffer to store the URL's path part in */ + char *hostname; /* hostname to connect, as parsed from url */ + long port; /* which port to use locally */ + unsigned short remote_port; /* what remote port to connect to, + not the proxy port! */ char *ppath; long bytecount; struct timeval now; /* current time */ + int firstsocket; /* the main socket to use */ + int secondarysocket; /* for i.e ftp transfers */ long upload_bufsize; /* adjust as you see fit, never bigger than BUFSIZE never smaller than UPLOAD_BUFSIZE */ + struct ssl_connect_data ssl; /* this is for ssl-stuff */ + + struct ConnectBits bits; /* various state-flags for this connection */ + /* These two functions MUST be set by the curl_connect() function to be be protocol dependent */ CURLcode (*curl_do)(struct connectdata *connect); @@ -205,6 +273,7 @@ struct connectdata { the same we read from. -1 disables */ long *writebytecountp; /* return number of bytes written or NULL */ + #ifdef KRB4 enum protection_level command_prot; @@ -218,6 +287,24 @@ struct connectdata { void *app_data; #endif + + /*************** Request - specific items ************/ + /* previously this was in the urldata struct */ + union { + struct HTTP *http; + struct HTTP *gopher; /* alias, just for the sake of being more readable */ + struct HTTP *https; /* alias, just for the sake of being more readable */ + struct FTP *ftp; +#if 0 /* no need for special ones for these: */ + struct TELNET *telnet; + struct FILE *file; + struct LDAP *ldap; + struct DICT *dict; +#endif + void *generic; + } proto; + + }; struct Progress { @@ -253,35 +340,6 @@ struct Progress { int speeder_c; }; -/**************************************************************************** - * HTTP unique setup - ***************************************************************************/ -struct HTTP { - struct FormData *sendit; - int postsize; - char *p_pragma; /* Pragma: string */ - char *p_accept; /* Accept: string */ - long readbytecount; - long writebytecount; - - /* For FORM posting */ - struct Form form; - size_t (*storefread)(char *, size_t , size_t , FILE *); - FILE *in; -}; - -/**************************************************************************** - * FTP unique setup - ***************************************************************************/ -struct FTP { - long *bytecountp; - char *user; /* user name string */ - char *passwd; /* password string */ - char *urlpath; /* the originally given path part of the URL */ - char *dir; /* decoded directory */ - char *file; /* decoded file */ -}; - typedef enum { HTTPREQ_NONE, /* first in list */ HTTPREQ_GET, @@ -338,22 +396,6 @@ typedef enum { CURLI_LAST } CurlInterface; -/* struct for data related to SSL and SSL connections */ -struct ssldata { - bool use; /* use ssl encrypted communications TRUE/FALSE */ - long version; /* what version the client wants to use */ - long certverifyresult; /* result from the certificate verification */ - long verifypeer; /* set TRUE if this is desired */ - char *CApath; /* DOES NOT WORK ON WINDOWS */ - char *CAfile; /* cerficate to verify peer against */ -#ifdef USE_SSLEAY - /* these ones requires specific SSL-types */ - SSL_CTX* ctx; - SSL* handle; - X509* server_cert; -#endif /* USE_SSLEAY */ -}; - /* * As of April 11, 2000 we're now trying to split up the urldata struct in * three different parts: @@ -370,6 +412,9 @@ struct ssldata { * 3 - Request-specific. Variables that are of interest for this particular * transfer being made right now. * + * In Febrary 2001, this is being done stricter. The 'connectdata' struct + * MUST have all the connection oriented stuff as we may now have several + * simultaneous connections and connection structs in memory. */ struct UrlData { @@ -381,7 +426,7 @@ struct UrlData { char *errorbuffer; /* store failure messages in here */ /*************** Session - specific items ************/ - char *proxy; /* if proxy, set it here, set CONF_PROXY to use this */ + char *proxy; /* if proxy, set it here */ char *proxyuserpwd; /* Proxy , if used */ long proxyport; /* If non-zero, use this port number by default. If the proxy string features a ":[port]" that one will override @@ -391,33 +436,16 @@ struct UrlData { long header_size; /* size of read header(s) in bytes */ long request_size; /* the amount of bytes sent in the request(s) */ - /*************** Request - specific items ************/ - - union { - struct HTTP *http; - struct HTTP *gopher; /* alias, just for the sake of being more readable */ - struct HTTP *https; /* alias, just for the sake of being more readable */ - struct FTP *ftp; -#if 0 /* no need for special ones for these: */ - struct TELNET *telnet; - struct FILE *file; - struct LDAP *ldap; - struct DICT *dict; -#endif - void *generic; - } proto; - FILE *out; /* the fetched file goes here */ FILE *in; /* the uploaded file is read from here */ FILE *writeheader; /* write the header to this is non-NULL */ char *url; /* what to get */ char *freethis; /* if non-NULL, an allocated string for the URL */ - char *hostname; /* hostname to connect, as parsed from url */ - long port; /* which port to use (if non-protocol bind) set - CONF_PORT to use this */ + long port; /* which port to use (if non-protocol bind) */ unsigned short remote_port; /* what remote port to connect to, not the proxy port! */ struct Configbits bits; /* new-style (v7) flag data */ + struct ssl_config_data ssl; /* this is for ssl-stuff */ char *userpwd; /* , if used */ char *range; /* range, if used. See README for detailed specification on @@ -462,10 +490,6 @@ struct UrlData { long maxdownload; /* in bytes, the maximum amount of data to fetch, 0 means unlimited */ - /* fields only set and used within _urlget() */ - int firstsocket; /* the main socket to use */ - int secondarysocket; /* for i.e ftp transfers */ - char buffer[BUFSIZE+1]; /* buffer with size BUFSIZE */ double current_speed; /* the ProgressShow() funcion sets this */ @@ -488,12 +512,13 @@ struct UrlData { struct CookieInfo *cookies; - struct ssldata ssl; /* this is for ssl-stuff */ - long crlf; struct curl_slist *quote; /* before the transfer */ struct curl_slist *postquote; /* after the transfer */ + /* Telnet negotiation options */ + struct curl_slist *telnet_options; /* linked list of telnet options */ + TimeCond timecondition; /* kind of comparison */ time_t timevalue; /* what time to compare with */ @@ -504,12 +529,6 @@ struct UrlData { char *headerbuff; /* allocated buffer to store headers in */ int headersize; /* size of the allocation */ -#if 0 - /* this was removed in libcurl 7.4 */ - char *writeinfo; /* if non-NULL describes what to output on a successful - completion */ -#endif - struct Progress progress; /* for all the progress meter data */ #define MAX_CURL_USER_LENGTH 128 @@ -541,6 +560,13 @@ struct UrlData { #endif struct timeval keeps_speed; /* this should be request-specific */ + + /* 'connects' will be an allocated array with pointers. If the pointer is + set, it holds an allocated connection. */ + struct connectdata **connects; + size_t numconnects; /* size of the 'connects' array */ + curl_closepolicy closepolicy; + }; #define LIBCURL_NAME "libcurl" diff --git a/src/main.c b/src/main.c index 465ac5e4b..6647df872 100644 --- a/src/main.c +++ b/src/main.c @@ -102,16 +102,13 @@ typedef enum { #define CONF_NOBODY (1<<11) /* use HEAD to get http document */ #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */ #define CONF_UPLOAD (1<<14) /* this is an upload */ -#define CONF_POST (1<<15) /* HTTP POST method */ #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */ #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */ #define CONF_NETRC (1<<22) /* read user+password from .netrc */ #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */ #define CONF_GETTEXT (1<<24) /* use ASCII/text for transfer */ #define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */ -#if 0 -#define CONF_PUT (1<<27) /* PUT the input file */ -#endif + #define CONF_MUTE (1<<28) /* force NOPROGRESS */ #ifndef HAVE_STRDUP @@ -279,6 +276,7 @@ static void help(void) " -r/--range Retrieve a byte range from a HTTP/1.1 or FTP server\n" " -s/--silent Silent mode. Don't output anything\n" " -S/--show-error Show error. With -s, make curl show errors when they occur\n" + " -t/--telnet-option Set telnet option\n" " -T/--upload-file Transfer/upload to remote site\n" " --url Another way to specify URL to work with\n" " -u/--user Specify user and password to use\n" @@ -366,6 +364,8 @@ struct Configurable { struct HttpPost *httppost; struct HttpPost *last_post; + struct curl_slist *telnet_options; + HttpReq httpreq; }; @@ -565,7 +565,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"r", "range", TRUE}, {"s", "silent", FALSE}, {"S", "show-error", FALSE}, - {"t", "upload", FALSE}, + {"t", "telnet-options", TRUE}, {"T", "upload-file", TRUE}, {"u", "user", TRUE}, {"U", "proxy-user", TRUE}, @@ -785,8 +785,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ else config->postfields=postdata; } - if(config->postfields) - config->conf |= CONF_POST; + if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) return PARAM_BAD_USE; break; @@ -959,9 +958,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ config->showerror ^= TRUE; /* toggle on if used with -s */ break; case 't': - /* we are uploading */ - config->conf ^= CONF_UPLOAD; - fprintf(stderr, "-t is a deprecated switch, use '-T -' instead!\n"); + /* Telnet options */ + config->telnet_options = curl_slist_append(config->telnet_options, nextarg); break; case 'T': /* we are uploading */ @@ -1743,7 +1741,6 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_FAILONERROR, config->conf&CONF_FAILONERROR); curl_easy_setopt(curl, CURLOPT_UPLOAD, config->conf&CONF_UPLOAD); - curl_easy_setopt(curl, CURLOPT_POST, config->conf&CONF_POST); curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, config->conf&CONF_FTPLISTONLY); curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND); @@ -1751,9 +1748,6 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, config->conf&CONF_FOLLOWLOCATION); curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT); -#if 0 - curl_easy_setopt(curl, CURLOPT_PUT, config->conf&CONF_PUT); -#endif curl_easy_setopt(curl, CURLOPT_MUTE, config->conf&CONF_MUTE); curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd); curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); @@ -1824,6 +1818,9 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar); } + /* new in libcurl 7.6.2: */ + curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); + res = curl_easy_perform(curl); if(config->writeout) { diff --git a/src/version.h b/src/version.h index 0a11cfd4d..a5860e6ea 100644 --- a/src/version.h +++ b/src/version.h @@ -1,3 +1,3 @@ #define CURL_NAME "curl" -#define CURL_VERSION "7.6.1" +#define CURL_VERSION "7.7-alpha1" #define CURL_ID CURL_NAME " " CURL_VERSION " (" OS ") " diff --git a/tests/data/reply11.txt b/tests/data/reply11.txt index c7027df03..5973e7320 100644 --- a/tests/data/reply11.txt +++ b/tests/data/reply11.txt @@ -2,5 +2,6 @@ HTTP/1.1 301 This is a weirdo text message Date: Thu, 09 Nov 2010 14:49:00 GMT Server: test-server/fake Location: data/110002.txt?coolsite=yes +Connection: close This server reply is for testing a simple Location: following -- cgit v1.2.3