Report generated at: Mon Oct 13 11:12:53 CEST 2008
| Total number of functions | 755 |
| Number of low risk functions | 623 |
| Number of moderate risk functions | 70 |
| Number of high risk functions | 49 |
| Number of untestable functions | 13 |
Used ranges:
| Cyclomatic Complexity | Risk Evaluation | |
| 0 - 10 | Simple module, without much risk | |
| 11 - 20 | More complex module, moderate risk | |
| 21 - 50 | Complex module, high risk | |
| greater than 50 | Untestable module, very high risk |
| Function Name |
Cyclomatic
Complexity |
Number of
Statements |
Number of
Lines |
Source File | |
| ↓ | ssh_statemach_act | 254 | 880 | 1582 | lib/ssh.c |
static CURLcode ssh_statemach_act(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct SSHPROTO *sftp_scp = data->state.proto.ssh;
struct ssh_conn *sshc = &conn->proto.sshc;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
#ifdef CURL_LIBSSH2_DEBUG
const char *fingerprint;
#endif /* CURL_LIBSSH2_DEBUG */
const char *host_public_key_md5;
int rc,i;
int err;
switch(sshc->state) {
case SSH_S_STARTUP:
sshc->secondCreateDirs = 0;
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_OK;
rc = libssh2_session_startup(sshc->ssh_session, sock);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
failf(data, "Failure establishing ssh session");
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_FAILED_INIT;
break;
}
/* Set libssh2 to non-blocking, since cURL is all non-blocking */
libssh2_session_set_blocking(sshc->ssh_session, 0);
#ifdef CURL_LIBSSH2_DEBUG
/*
* Before we authenticate we should check the hostkey's fingerprint
* against our known hosts. How that is handled (reading from file,
* whatever) is up to us. As for know not much is implemented, besides
* showing how to get the fingerprint.
*/
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
/* The fingerprint points to static storage (!), don't free() it. */
infof(data, "Fingerprint: ");
for (rc = 0; rc < 16; rc++) {
infof(data, "%02X ", (unsigned char) fingerprint[rc]);
}
infof(data, "\n");
#endif /* CURL_LIBSSH2_DEBUG */
/* Before we authenticate we check the hostkey's MD5 fingerprint
* against a known fingerprint, if available. This implementation pulls
* it from the curl option.
*/
if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32) {
char buf[33];
host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
for (i = 0; i < 16; i++)
snprintf(&buf[i*2], 3, "%02x",
(unsigned char) host_public_key_md5[i]);
if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
failf(data,
"Denied establishing ssh session: mismatch md5 fingerprint. "
"Remote %s is not equal to %s",
buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
break;
}
}
state(conn, SSH_AUTHLIST);
break;
case SSH_AUTHLIST:
/* TBD - methods to check the host keys need to be done */
/*
* Figure out authentication methods
* NB: As soon as we have provided a username to an openssh server we
* must never change it later. Thus, always specify the correct username
* here, even though the libssh2 docs kind of indicate that it should be
* possible to get a 'generic' list (not user-specific) of authentication
* methods, presumably with a blank username. That won't work in my
* experience.
* So always specify it here.
*/
sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
conn->user,
strlen(conn->user));
if(!sshc->authlist) {
if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
state(conn, SSH_SESSION_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(err);
break;
}
}
infof(data, "SSH authentication methods available: %s\n", sshc->authlist);
state(conn, SSH_AUTH_PKEY_INIT);
break;
case SSH_AUTH_PKEY_INIT:
/*
* Check the supported auth types in the order I feel is most secure
* with the requested type of authentication
*/
sshc->authed = FALSE;
if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
(strstr(sshc->authlist, "publickey") != NULL)) {
char *home;
sshc->rsa_pub = sshc->rsa = NULL;
/* To ponder about: should really the lib be messing about with the
HOME environment variable etc? */
home = curl_getenv("HOME");
if(data->set.str[STRING_SSH_PUBLIC_KEY])
sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
else if(home)
sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
else
/* as a final resort, try current dir! */
sshc->rsa_pub = strdup("id_dsa.pub");
if(sshc->rsa_pub == NULL) {
Curl_safefree(home);
home = NULL;
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
if(data->set.str[STRING_SSH_PRIVATE_KEY])
sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
else if(home)
sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
else
/* as a final resort, try current dir! */
sshc->rsa = strdup("id_dsa");
if(sshc->rsa == NULL) {
Curl_safefree(home);
home = NULL;
Curl_safefree(sshc->rsa_pub);
sshc->rsa_pub = NULL;
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
sshc->passphrase = data->set.str[STRING_KEY_PASSWD];
if(!sshc->passphrase)
sshc->passphrase = "";
Curl_safefree(home);
home = NULL;
infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
infof(data, "Using ssh private key file %s\n", sshc->rsa);
state(conn, SSH_AUTH_PKEY);
}
else {
state(conn, SSH_AUTH_PASS_INIT);
}
break;
case SSH_AUTH_PKEY:
/* The function below checks if the files exists, no need to stat() here.
*/
rc = libssh2_userauth_publickey_fromfile(sshc->ssh_session,
conn->user, sshc->rsa_pub,
sshc->rsa, sshc->passphrase);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
Curl_safefree(sshc->rsa_pub);
sshc->rsa_pub = NULL;
Curl_safefree(sshc->rsa);
sshc->rsa = NULL;
if(rc == 0) {
sshc->authed = TRUE;
infof(data, "Initialized SSH public key authentication\n");
state(conn, SSH_AUTH_DONE);
}
else {
char *err_msg;
(void)libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0);
infof(data, "SSH public key authentication failed: %s\n", err_msg);
state(conn, SSH_AUTH_PASS_INIT);
}
break;
case SSH_AUTH_PASS_INIT:
if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
(strstr(sshc->authlist, "password") != NULL)) {
state(conn, SSH_AUTH_PASS);
}
else {
state(conn, SSH_AUTH_HOST_INIT);
}
break;
case SSH_AUTH_PASS:
rc = libssh2_userauth_password(sshc->ssh_session, conn->user,
conn->passwd);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc == 0) {
sshc->authed = TRUE;
infof(data, "Initialized password authentication\n");
state(conn, SSH_AUTH_DONE);
}
else {
state(conn, SSH_AUTH_HOST_INIT);
}
break;
case SSH_AUTH_HOST_INIT:
if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
(strstr(sshc->authlist, "hostbased") != NULL)) {
state(conn, SSH_AUTH_HOST);
}
else {
state(conn, SSH_AUTH_KEY_INIT);
}
break;
case SSH_AUTH_HOST:
state(conn, SSH_AUTH_KEY_INIT);
break;
case SSH_AUTH_KEY_INIT:
if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
&& (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
state(conn, SSH_AUTH_KEY);
}
else {
state(conn, SSH_AUTH_DONE);
}
break;
case SSH_AUTH_KEY:
/* Authentication failed. Continue with keyboard-interactive now. */
rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
conn->user,
strlen(conn->user),
&kbd_callback);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc == 0) {
sshc->authed = TRUE;
infof(data, "Initialized keyboard interactive authentication\n");
}
state(conn, SSH_AUTH_DONE);
break;
case SSH_AUTH_DONE:
if(!sshc->authed) {
failf(data, "Authentication failure");
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_LOGIN_DENIED;
break;
}
/*
* At this point we have an authenticated ssh session.
*/
infof(data, "Authentication complete\n");
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
if(conn->protocol == PROT_SFTP) {
state(conn, SSH_SFTP_INIT);
break;
}
infof(data, "SSH CONNECT phase done\n");
state(conn, SSH_STOP);
break;
case SSH_SFTP_INIT:
/*
* Start the libssh2 sftp session
*/
sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
if(!sshc->sftp_session) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
char *err_msg;
(void)libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0);
failf(data, "Failure initializing sftp session: %s", err_msg);
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_FAILED_INIT;
break;
}
}
state(conn, SSH_SFTP_REALPATH);
break;
case SSH_SFTP_REALPATH:
{
char tempHome[PATH_MAX];
/*
* Get the "home" directory
*/
rc = libssh2_sftp_realpath(sshc->sftp_session, ".",
tempHome, PATH_MAX-1);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc > 0) {
/* It seems that this string is not always NULL terminated */
tempHome[rc] = '\0';
sshc->homedir = strdup(tempHome);
if(!sshc->homedir) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
}
else {
/* Return the error type */
err = libssh2_sftp_last_error(sshc->sftp_session);
result = sftp_libssh2_error_to_CURLE(err);
DEBUGF(infof(data, "error = %d makes libcurl = %d\n", err, result));
state(conn, SSH_STOP);
break;
}
}
/* This is the last step in the SFTP connect phase. Do note that while
we get the homedir here, we get the "workingpath" in the DO action
since the homedir will remain the same between request but the
working path will not. */
DEBUGF(infof(data, "SSH CONNECT phase done\n"));
state(conn, SSH_STOP);
break;
case SSH_SFTP_QUOTE_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(conn, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands\n");
sshc->quote_item = data->set.quote;
state(conn, SSH_SFTP_QUOTE);
}
else {
state(conn, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_POSTQUOTE_INIT:
if(data->set.postquote) {
infof(data, "Sending quote commands\n");
sshc->quote_item = data->set.postquote;
state(conn, SSH_SFTP_QUOTE);
}
else {
state(conn, SSH_STOP);
}
break;
case SSH_SFTP_QUOTE:
/* Send any quote commands */
{
const char *cp;
/*
* Support some of the "FTP" commands
*/
if(curl_strnequal(sshc->quote_item->data, "PWD", 3)) {
/* output debug output if that is requested */
if(data->set.verbose) {
char tmp[PATH_MAX+1];
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n",
sftp_scp->path);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
}
else if(sshc->quote_item->data) {
/*
* the arguments following the command must be separated from the
* command with a space so we can check for it unconditionally
*/
cp = strchr(sshc->quote_item->data, ' ');
if(cp == NULL) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
/*
* also, every command takes at least one argument so we get that
* first argument right now
*/
result = get_pathname(&cp, &sshc->quote_path1);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error: Bad first parameter");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
break;
}
/*
* SFTP is a binary protocol, so we don't send text commands to
* the server. Instead, we scan for commands for commands used by
* OpenSSH's sftp program and call the appropriate libssh2
* functions.
*/
if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
/* get the destination */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in chgrp/chmod/chown: "
"Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
break;
}
memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
state(conn, SSH_SFTP_QUOTE_STAT);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data,
"Syntax error in ln/symlink: Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
break;
}
state(conn, SSH_SFTP_QUOTE_SYMLINK);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
/* create dir */
state(conn, SSH_SFTP_QUOTE_MKDIR);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in rename: Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
break;
}
state(conn, SSH_SFTP_QUOTE_RENAME);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
/* delete dir */
state(conn, SSH_SFTP_QUOTE_RMDIR);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
state(conn, SSH_SFTP_QUOTE_UNLINK);
break;
}
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
if(!sshc->quote_item) {
state(conn, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_NEXT_QUOTE:
if(sshc->quote_path1) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
}
if(sshc->quote_path2) {
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
}
sshc->quote_item = sshc->quote_item->next;
if(sshc->quote_item) {
state(conn, SSH_SFTP_QUOTE);
}
else {
if(sshc->nextstate != SSH_NO_STATE) {
state(conn, sshc->nextstate);
sshc->nextstate = SSH_NO_STATE;
}
else {
state(conn, SSH_SFTP_TRANS_INIT);
}
}
break;
case SSH_SFTP_QUOTE_STAT:
if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
/* Since chown and chgrp only set owner OR group but libssh2 wants to
* set them both at once, we need to obtain the current ownership first.
* This takes an extra protocol round trip.
*/
rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
&sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) { /* get those attributes */
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
/* Now set the new attributes... */
if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chgrp gid not a number");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
/* permissions are octal */
if(sshc->quote_attrs.permissions == 0 &&
!ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chmod permissions not a number");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chown uid not a number");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
/* Now send the completed structure... */
state(conn, SSH_SFTP_QUOTE_SETSTAT);
break;
case SSH_SFTP_QUOTE_SETSTAT:
rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
&sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Attempt to set SFTP stats failed: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_SYMLINK:
rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
sshc->quote_path2);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "symlink command failed: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_MKDIR:
rc = libssh2_sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 0755);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RENAME:
rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
sshc->quote_path2);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RMDIR:
rc = libssh2_sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_UNLINK:
rc = libssh2_sftp_unlink(sshc->sftp_session, sshc->quote_path1);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_TRANS_INIT:
if(data->set.upload)
state(conn, SSH_SFTP_UPLOAD_INIT);
else {
if(data->set.opt_no_body)
state(conn, SSH_STOP);
else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
state(conn, SSH_SFTP_READDIR_INIT);
else
state(conn, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
{
unsigned long flags;
/*
* NOTE!!! libssh2 requires that the destination path is a full path
* that includes the destination file and name OR ends in a "/"
* If this is not done the destination file will be named the
* same name as the last directory in the path.
*/
if(data->state.resume_from != 0) {
LIBSSH2_SFTP_ATTRIBUTES attrs;
if(data->state.resume_from< 0) {
rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
data->state.resume_from = 0;
}
else {
data->state.resume_from = attrs.filesize;
}
}
}
if(data->set.ftp_append)
/* Try to open for append, but create if nonexisting */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
else if (data->state.resume_from > 0)
/* If we have restart position then open for append */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
else
/* Clear file before writing (normal behaviour) */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
sshc->sftp_handle =
libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
flags, data->set.new_file_perms);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
if(sshc->secondCreateDirs) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
failf(data, "Creating the dir/file failed: %s",
sftp_libssh2_strerror(err));
break;
}
else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
(err == LIBSSH2_FX_FAILURE) ||
(err == LIBSSH2_FX_NO_SUCH_PATH)) &&
(data->set.ftp_create_missing_dirs &&
(strlen(sftp_scp->path) > 1))) {
/* try to create the path remotely */
sshc->secondCreateDirs = 1;
state(conn, SSH_SFTP_CREATE_DIRS_INIT);
break;
}
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
failf(data, "Upload failed: %s", sftp_libssh2_strerror(err));
break;
}
}
/* If we have restart point then we need to seek to the correct position. */
if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
curl_off_t readthisamountnow = data->state.resume_from;
if(conn->seek_func(conn->seek_client,
readthisamountnow, SEEK_SET) != 0) {
failf(data, "Could not seek stream");
return CURLE_FTP_COULDNT_USE_REST;
}
}
else {
curl_off_t passed=0;
curl_off_t readthisamountnow;
curl_off_t actuallyread;
do {
readthisamountnow = (data->state.resume_from - passed);
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread =
(curl_off_t) conn->fread_func(data->state.buffer, 1,
(size_t)readthisamountnow,
conn->fread_in);
passed += actuallyread;
if((actuallyread <= 0) || (actuallyread > readthisamountnow)) {
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
failf(data, "Failed to read data");
return CURLE_FTP_COULDNT_USE_REST;
}
} while(passed < data->state.resume_from);
}
/* now, decrease the size of the read */
if(data->set.infilesize>0) {
data->set.infilesize -= data->state.resume_from;
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
libssh2_sftp_seek(sshc->sftp_handle, data->state.resume_from);
}
if(data->set.infilesize>0) {
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
/* upload data */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
FIRSTSOCKET, NULL);
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
state(conn, SSH_STOP);
}
break;
}
case SSH_SFTP_CREATE_DIRS_INIT:
if(strlen(sftp_scp->path) > 1) {
sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
state(conn, SSH_SFTP_CREATE_DIRS);
}
else {
state(conn, SSH_SFTP_UPLOAD_INIT);
}
break;
case SSH_SFTP_CREATE_DIRS:
if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
*sshc->slash_pos = 0;
infof(data, "Creating directory '%s'\n", sftp_scp->path);
state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
break;
}
else {
state(conn, SSH_SFTP_UPLOAD_INIT);
}
break;
case SSH_SFTP_CREATE_DIRS_MKDIR:
/* 'mode' - parameter is preliminary - default to 0644 */
rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
data->set.new_directory_perms);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
*sshc->slash_pos = '/';
++sshc->slash_pos;
if(rc == -1) {
unsigned int sftp_err = 0;
/*
* abort if failure wasn't that the dir already exists or the
* permission was denied (creation might succeed further
* down the path) - retry on unspecific FAILURE also
*/
sftp_err = libssh2_sftp_last_error(sshc->sftp_session);
if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
(sftp_err != LIBSSH2_FX_FAILURE) &&
(sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
result = sftp_libssh2_error_to_CURLE(sftp_err);
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
break;
}
}
state(conn, SSH_SFTP_CREATE_DIRS);
break;
case SSH_SFTP_READDIR_INIT:
/*
* This is a directory that we are trying to get, so produce a
* directory listing
*/
sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
sftp_scp->path);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
failf(data, "Could not open directory for reading: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
break;
}
}
if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
state(conn, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
sshc->readdir_filename,
PATH_MAX,
sshc->readdir_longentry,
PATH_MAX,
&sshc->readdir_attrs);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
break;
}
if(sshc->readdir_len > 0) {
sshc->readdir_filename[sshc->readdir_len] = '\0';
if(data->set.ftp_list_only) {
char *tmpLine;
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(tmpLine == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
result = Curl_client_write(conn, CLIENTWRITE_BODY,
tmpLine, sshc->readdir_len+1);
Curl_safefree(tmpLine);
if(result) {
state(conn, SSH_STOP);
break;
}
/* since this counts what we send to the client, we include the newline
in this counter */
data->req.bytecount += sshc->readdir_len+1;
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
sshc->readdir_len, conn);
}
}
else {
sshc->readdir_currLen = strlen(sshc->readdir_longentry);
sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
if(!sshc->readdir_line) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
memcpy(sshc->readdir_line, sshc->readdir_longentry,
sshc->readdir_currLen);
if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
sshc->readdir_linkPath = malloc(PATH_MAX + 1);
if(sshc->readdir_linkPath == NULL) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
break;
}
state(conn, SSH_SFTP_READDIR_BOTTOM);
break;
}
}
else if(sshc->readdir_len == 0) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_READDIR_DONE);
break;
}
else if(sshc->readdir_len <= 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
failf(data, "Could not open remote file for reading: %s :: %d",
sftp_libssh2_strerror(err),
libssh2_session_last_errno(sshc->ssh_session));
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
break;
}
break;
case SSH_SFTP_READDIR_LINK:
sshc->readdir_len = libssh2_sftp_readlink(sshc->sftp_session,
sshc->readdir_linkPath,
sshc->readdir_filename,
PATH_MAX);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
break;
}
Curl_safefree(sshc->readdir_linkPath);
sshc->readdir_linkPath = NULL;
sshc->readdir_line = realloc(sshc->readdir_line,
sshc->readdir_totalLen + 4 +
sshc->readdir_len);
if(!sshc->readdir_line) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
sshc->readdir_currLen += snprintf(sshc->readdir_line +
sshc->readdir_currLen,
sshc->readdir_totalLen -
sshc->readdir_currLen,
" -> %s",
sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_BOTTOM);
break;
case SSH_SFTP_READDIR_BOTTOM:
sshc->readdir_currLen += snprintf(sshc->readdir_line +
sshc->readdir_currLen,
sshc->readdir_totalLen -
sshc->readdir_currLen, "\n");
result = Curl_client_write(conn, CLIENTWRITE_BODY,
sshc->readdir_line,
sshc->readdir_currLen);
if(result == CURLE_OK) {
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
sshc->readdir_currLen, conn);
}
data->req.bytecount += sshc->readdir_currLen;
}
Curl_safefree(sshc->readdir_line);
sshc->readdir_line = NULL;
if(result) {
state(conn, SSH_STOP);
}
else
state(conn, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR_DONE:
if(libssh2_sftp_closedir(sshc->sftp_handle) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
sshc->sftp_handle = NULL;
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
/* no data to transfer */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
state(conn, SSH_STOP);
break;
case SSH_SFTP_DOWNLOAD_INIT:
/*
* Work on getting the specified file
*/
sshc->sftp_handle =
libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
LIBSSH2_FXF_READ, data->set.new_file_perms);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
failf(data, "Could not open remote file for reading: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
break;
}
}
state(conn, SSH_SFTP_DOWNLOAD_STAT);
break;
case SSH_SFTP_DOWNLOAD_STAT:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
/*
* libssh2_sftp_open() didn't return an error, so maybe the server
* just doesn't support stat()
*/
data->req.size = -1;
data->req.maxdownload = -1;
}
else {
curl_off_t size;
size = attrs.filesize;
if(conn->data->state.use_range) {
curl_off_t from, to;
char *ptr;
char *ptr2;
from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
if ((ptr == ptr2) /* no "to" value given */
|| (to > size)) {
to = size;
}
if (from > to) {
from = to;
size = 0;
}
else {
size = to - from + 1;
}
libssh2_sftp_seek(conn->proto.sshc.sftp_handle, from);
}
data->req.size = size;
data->req.maxdownload = size;
Curl_pgrsSetDownloadSize(data, size);
}
/* We can resume if we can seek to the resume position */
if(data->state.resume_from) {
if(data->state.resume_from< 0) {
/* We're supposed to download the last abs(from) bytes */
if((curl_off_t)attrs.filesize < -data->state.resume_from) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* download from where? */
data->state.resume_from = attrs.filesize - data->state.resume_from;
}
else {
if((curl_off_t)attrs.filesize < data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
/* Does a completed file need to be seeked and started or closed ? */
/* Now store the number of bytes we are expected to download */
data->req.size = attrs.filesize - data->state.resume_from;
data->req.maxdownload = attrs.filesize - data->state.resume_from;
Curl_pgrsSetDownloadSize(data,
attrs.filesize - data->state.resume_from);
libssh2_sftp_seek(sshc->sftp_handle, data->state.resume_from);
}
}
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
infof(data, "File already completely downloaded\n");
state(conn, SSH_STOP);
break;
}
else {
result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
FALSE, NULL, -1, NULL);
}
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
state(conn, SSH_STOP);
}
break;
case SSH_SFTP_CLOSE:
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
}
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
DEBUGF(infof(data, "SFTP DONE done\n"));
#if 0 /* PREV */
state(conn, SSH_SFTP_SHUTDOWN);
#endif
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
case SSH_SFTP_SHUTDOWN:
/* during times we get here due to a broken transfer and then the
sftp_handle might not have been taken down so make sure that is done
before we proceed */
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
}
if(sshc->sftp_session) {
rc = libssh2_sftp_shutdown(sshc->sftp_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to stop libssh2 sftp subsystem\n");
}
sshc->sftp_session = NULL;
}
Curl_safefree(sshc->homedir);
sshc->homedir = NULL;
state(conn, SSH_SESSION_DISCONNECT);
break;
case SSH_SCP_TRANS_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(conn, SSH_STOP);
break;
}
if(data->set.upload) {
if(data->set.infilesize < 0) {
failf(data, "SCP requires a known file size for upload");
sshc->actualcode = CURLE_UPLOAD_FAILED;
state(conn, SSH_SCP_CHANNEL_FREE);
break;
}
state(conn, SSH_SCP_UPLOAD_INIT);
}
else {
state(conn, SSH_SCP_DOWNLOAD_INIT);
}
break;
case SSH_SCP_UPLOAD_INIT:
/*
* libssh2 requires that the destination path is a full path that
* includes the destination file and name OR ends in a "/" . If this is
* not done the destination file will be named the same name as the last
* directory in the path.
*/
sshc->ssh_channel =
libssh2_scp_send_ex(sshc->ssh_session, sftp_scp->path,
data->set.new_file_perms,
data->set.infilesize, 0, 0);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0);
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
/* upload data */
result = Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
FIRSTSOCKET, NULL);
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
}
else {
state(conn, SSH_STOP);
}
break;
case SSH_SCP_DOWNLOAD_INIT:
{
/*
* We must check the remote file; if it is a directory no values will
* be set in sb
*/
struct stat sb;
curl_off_t bytecount;
/* clear the struct scp recv will fill in */
memset(&sb, 0, sizeof(struct stat));
/* get a fresh new channel from the ssh layer */
sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
sftp_scp->path, &sb);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0);
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
/* download data */
bytecount = (curl_off_t)sb.st_size;
data->req.maxdownload = (curl_off_t)sb.st_size;
result = Curl_setup_transfer(conn, FIRSTSOCKET,
bytecount, FALSE, NULL, -1, NULL);
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
}
else
state(conn, SSH_STOP);
}
break;
case SSH_SCP_DONE:
if(data->set.upload)
state(conn, SSH_SCP_SEND_EOF);
else
state(conn, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Failed to send libssh2 channel EOF\n");
}
}
state(conn, SSH_SCP_WAIT_EOF);
break;
case SSH_SCP_WAIT_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_wait_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Failed to get channel EOF: %d\n", rc);
}
}
state(conn, SSH_SCP_WAIT_CLOSE);
break;
case SSH_SCP_WAIT_CLOSE:
if(sshc->ssh_channel) {
rc = libssh2_channel_wait_closed(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Channel failed to close: %d\n", rc);
}
}
state(conn, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
}
DEBUGF(infof(data, "SCP DONE phase complete\n"));
#if 0 /* PREV */
state(conn, SSH_SESSION_DISCONNECT);
#endif
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
case SSH_SESSION_DISCONNECT:
/* during weird times when we've been prematurely aborted, the channel
is still alive when we reach this state and we MUST kill the channel
properly first */
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
}
if(sshc->ssh_session) {
rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to disconnect libssh2 session\n");
}
}
Curl_safefree(sshc->homedir);
sshc->homedir = NULL;
state(conn, SSH_SESSION_FREE);
break;
case SSH_SESSION_FREE:
if(sshc->ssh_session) {
rc = libssh2_session_free(sshc->ssh_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 session\n");
}
sshc->ssh_session = NULL;
}
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
case SSH_QUIT:
/* fallthrough, just stop! */
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
break;
}
return result;
}
|
|||||
| ↓ | Curl_http | 204 | 395 | 886 | lib/http.c |
CURLcode Curl_http(struct connectdata *conn, bool *done)
{
struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is a short cut to the buffer */
CURLcode result=CURLE_OK;
struct HTTP *http;
char *ppath = data->state.path;
char ftp_typecode[sizeof(";type=?")] = "";
char *host = conn->host.name;
const char *te = ""; /* transfer-encoding */
char *ptr;
const char *request;
Curl_HttpReq httpreq = data->set.httpreq;
char *addcookies = NULL;
curl_off_t included_body = 0;
const char *httpstring;
send_buffer *req_buffer;
curl_off_t postsize; /* off_t type to be able to hold a large file size */
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with
the rest of the request in the PERFORM phase. */
*done = TRUE;
/* If there already is a protocol-specific struct allocated for this
sessionhandle, deal with it */
Curl_reset_reqproto(conn);
if(!data->state.proto.http) {
/* Only allocate this struct if we don't already have it! */
http = calloc(sizeof(struct HTTP), 1);
if(!http)
return CURLE_OUT_OF_MEMORY;
data->state.proto.http = http;
}
else
http = data->state.proto.http;
if( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
data->set.upload) {
httpreq = HTTPREQ_PUT;
}
/* Now set the 'request' pointer to the proper request string */
if(data->set.str[STRING_CUSTOMREQUEST])
request = data->set.str[STRING_CUSTOMREQUEST];
else {
if(data->set.opt_no_body)
request = "HEAD";
else {
DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
switch(httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
request = "POST";
break;
case HTTPREQ_PUT:
request = "PUT";
break;
default: /* this should never happen */
case HTTPREQ_GET:
request = "GET";
break;
case HTTPREQ_HEAD:
request = "HEAD";
break;
}
}
}
/* The User-Agent string might have been allocated in url.c already, because
it might have been used in the proxy connect, but if we have got a header
with the user-agent string specified, we erase the previously made string
here. */
if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
}
/* setup the authentication headers */
result = http_output_auth(conn, request, ppath, FALSE);
if(result)
return result;
if((data->state.authhost.multi || data->state.authproxy.multi) &&
(httpreq != HTTPREQ_GET) &&
(httpreq != HTTPREQ_HEAD)) {
/* Auth is required and we are not authenticated yet. Make a PUT or POST
with content-length zero as a "probe". */
conn->bits.authneg = TRUE;
}
else
conn->bits.authneg = FALSE;
Curl_safefree(conn->allocptr.ref);
if(data->change.referer && !checkheaders(data, "Referer:"))
conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
else
conn->allocptr.ref = NULL;
if(data->set.str[STRING_COOKIE] && !checkheaders(data, "Cookie:"))
addcookies = data->set.str[STRING_COOKIE];
if(!checkheaders(data, "Accept-Encoding:") &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding =
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
if(!conn->allocptr.accept_encoding)
return CURLE_OUT_OF_MEMORY;
}
ptr = checkheaders(data, "Transfer-Encoding:");
if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */
data->req.upload_chunky =
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
}
else {
if((conn->protocol&PROT_HTTP) &&
data->set.upload &&
(data->set.infilesize == -1)) {
if (use_http_1_1(data, conn)) {
/* HTTP, upload, unknown file size and not HTTP 1.0 */
data->req.upload_chunky = TRUE;
} else {
failf(data, "Chunky upload is not supported by HTTP 1.0");
return CURLE_UPLOAD_FAILED;
}
}
else {
/* else, no chunky upload */
data->req.upload_chunky = FALSE;
}
if(data->req.upload_chunky)
te = "Transfer-Encoding: chunked\r\n";
}
Curl_safefree(conn->allocptr.host);
ptr = checkheaders(data, "Host:");
if(ptr && (!data->state.this_is_a_follow ||
curl_strequal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
/* If we have a given custom Host: header, we extract the host name in
order to possibly use it for cookie reasons later on. We only allow the
custom Host: header if this is NOT a redirect, as setting Host: in the
redirected request is being out on thin ice. Except if the host name
is the same as the first one! */
char *cookiehost = Curl_copy_header_value(ptr);
if (!cookiehost)
return CURLE_OUT_OF_MEMORY;
if (!*cookiehost)
/* ignore empty data */
free(cookiehost);
else {
char *colon = strchr(cookiehost, ':');
if (colon)
*colon = 0; /* The host must not include an embedded port number */
Curl_safefree(conn->allocptr.cookiehost);
conn->allocptr.cookiehost = cookiehost;
}
#endif
conn->allocptr.host = NULL;
}
else {
/* When building Host: headers, we must put the host name within
[brackets] if the host name is a plain IPv6-address. RFC2732-style. */
if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
(!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
/* if(HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
the port number in the host string */
conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"");
else
conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"",
conn->remote_port);
if(!conn->allocptr.host)
/* without Host: we can't make a nice request */
return CURLE_OUT_OF_MEMORY;
}
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* Using a proxy but does not tunnel through it */
/* The path sent to the proxy is in fact the entire URL. But if the remote
host is a IDN-name, we must make sure that the request we produce only
uses the encoded host name! */
if(conn->host.dispname != conn->host.name) {
char *url = data->change.url;
ptr = strstr(url, conn->host.dispname);
if(ptr) {
/* This is where the display name starts in the URL, now replace this
part with the encoded name. TODO: This method of replacing the host
name is rather crude as I believe there's a slight risk that the
user has entered a user name or password that contain the host name
string. */
size_t currlen = strlen(conn->host.dispname);
size_t newlen = strlen(conn->host.name);
size_t urllen = strlen(url);
char *newurl;
newurl = malloc(urllen + newlen - currlen + 1);
if(newurl) {
/* copy the part before the host name */
memcpy(newurl, url, ptr - url);
/* append the new host name instead of the old */
memcpy(newurl + (ptr - url), conn->host.name, newlen);
/* append the piece after the host name */
memcpy(newurl + newlen + (ptr - url),
ptr + currlen, /* copy the trailing zero byte too */
urllen - (ptr-url) - currlen + 1);
if(data->change.url_alloc)
free(data->change.url);
data->change.url = newurl;
data->change.url_alloc = TRUE;
}
else
return CURLE_OUT_OF_MEMORY;
}
}
ppath = data->change.url;
if (data->set.proxy_transfer_mode) {
/* when doing ftp, append ;type= if not present */
if(checkprefix("ftp://", ppath) || checkprefix("ftps://", ppath)) {
char *p = strstr(ppath, ";type=");
if(p && p[6] && p[7] == 0) {
switch (toupper((int)((unsigned char)p[6]))) {
case 'A':
case 'D':
case 'I':
break;
default:
p = NULL;
}
}
if(!p)
snprintf(ftp_typecode, sizeof(ftp_typecode), ";type=%c",
data->set.prefer_ascii ? 'a' : 'i');
}
}
}
if(HTTPREQ_POST_FORM == httpreq) {
/* we must build the whole darned post sequence first, so that we have
a size of the whole shebang before we start to send it */
result = Curl_getFormData(&http->sendit, data->set.httppost,
checkheaders(data, "Content-Type:"),
&http->postsize);
if(CURLE_OK != result) {
/* Curl_getFormData() doesn't use failf() */
failf(data, "failed creating formpost data");
return result;
}
}
http->p_pragma =
(!checkheaders(data, "Pragma:") &&
(conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
"Pragma: no-cache\r\n":NULL;
http->p_accept = checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
if(( (HTTPREQ_POST == httpreq) ||
(HTTPREQ_POST_FORM == httpreq) ||
(HTTPREQ_PUT == httpreq) ) &&
data->state.resume_from) {
/**********************************************************************
* Resuming upload in HTTP means that we PUT or POST and that we have
* got a resume_from value set. The resume value has already created
* a Range: header that will be passed along. We need to "fast forward"
* the file the given number of bytes and decrease the assume upload
* file size before we continue this venture in the dark lands of HTTP.
*********************************************************************/
if(data->state.resume_from < 0 ) {
/*
* This is meant to get the size of the present remote-file by itself.
* We don't support this now. Bail out!
*/
data->state.resume_from = 0;
}
if(data->state.resume_from && !data->state.this_is_a_follow) {
/* do we still game? */
/* Now, let's read off the proper amount of bytes from the
input. */
if(conn->seek_func) {
curl_off_t readthisamountnow = data->state.resume_from;
if(conn->seek_func(conn->seek_client,
readthisamountnow, SEEK_SET) != 0) {
failf(data, "Could not seek stream");
return CURLE_READ_ERROR;
}
}
else {
curl_off_t passed=0;
do {
size_t readthisamountnow = (size_t)(data->state.resume_from - passed);
size_t actuallyread;
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread = data->set.fread_func(data->state.buffer, 1,
(size_t)readthisamountnow,
data->set.in);
passed += actuallyread;
if(actuallyread != readthisamountnow) {
failf(data, "Could only read %" FORMAT_OFF_T
" bytes from the input",
passed);
return CURLE_READ_ERROR;
}
} while(passed != data->state.resume_from); /* loop until done */
}
/* now, decrease the size of the read */
if(data->set.infilesize>0) {
data->set.infilesize -= data->state.resume_from;
if(data->set.infilesize <= 0) {
failf(data, "File already completely uploaded");
return CURLE_PARTIAL_FILE;
}
}
/* we've passed, proceed as normal */
}
}
if(data->state.use_range) {
/*
* A range is selected. We use different headers whether we're downloading
* or uploading and we always let customized headers override our internal
* ones if any such are specified.
*/
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
!checkheaders(data, "Range:")) {
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
data->state.range);
}
else if((httpreq != HTTPREQ_GET) &&
!checkheaders(data, "Content-Range:")) {
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
if(data->set.set_resume_from < 0) {
/* Upload resume was asked for, but we don't know the size of the
remote part so we tell the server (and act accordingly) that we
upload the whole file (again) */
conn->allocptr.rangeline =
aprintf("Content-Range: bytes 0-%" FORMAT_OFF_T
"/%" FORMAT_OFF_T "\r\n",
data->set.infilesize - 1, data->set.infilesize);
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
curl_off_t total_expected_size=
data->state.resume_from + data->set.infilesize;
conn->allocptr.rangeline =
aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
"/%" FORMAT_OFF_T "\r\n",
data->state.range, total_expected_size-1,
total_expected_size);
}
else {
/* Range was selected and then we just pass the incoming range and
append total size */
conn->allocptr.rangeline =
aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
data->state.range, data->set.infilesize);
}
if(!conn->allocptr.rangeline)
return CURLE_OUT_OF_MEMORY;
}
}
/* Use 1.1 unless the user specifically asked for 1.0 or the server only
supports 1.0 */
httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
/* initialize a dynamic send-buffer */
req_buffer = add_buffer_init();
if(!req_buffer)
return CURLE_OUT_OF_MEMORY;
/* add the main request stuff */
result =
add_bufferf(req_buffer,
"%s " /* GET/HEAD/POST/PUT */
"%s%s HTTP/%s\r\n" /* path + HTTP version */
"%s" /* proxyuserpwd */
"%s" /* userpwd */
"%s" /* range */
"%s" /* user agent */
"%s" /* host */
"%s" /* pragma */
"%s" /* accept */
"%s" /* accept-encoding */
"%s" /* referer */
"%s" /* Proxy-Connection */
"%s",/* transfer-encoding */
request,
ppath,
ftp_typecode,
httpstring,
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"",
(data->state.use_range && conn->allocptr.rangeline)?
conn->allocptr.rangeline:"",
(data->set.str[STRING_USERAGENT] &&
*data->set.str[STRING_USERAGENT] && conn->allocptr.uagent)?
conn->allocptr.uagent:"",
(conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
http->p_pragma?http->p_pragma:"",
http->p_accept?http->p_accept:"",
(data->set.str[STRING_ENCODING] &&
*data->set.str[STRING_ENCODING] &&
conn->allocptr.accept_encoding)?
conn->allocptr.accept_encoding:"",
(data->change.referer && conn->allocptr.ref)?
conn->allocptr.ref:"" /* Referer: */,
(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
!checkheaders(data, "Proxy-Connection:"))?
"Proxy-Connection: Keep-Alive\r\n":"",
te
);
/*
* Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
* with basic and digest, it will be freed anyway by the next request
*/
Curl_safefree (conn->allocptr.userpwd);
conn->allocptr.userpwd = NULL;
if(result)
return result;
#if !defined(CURL_DISABLE_COOKIES)
if(data->cookies || addcookies) {
struct Cookie *co=NULL; /* no cookies from start */
int count=0;
if(data->cookies) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
co = Curl_cookie_getlist(data->cookies,
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:host,
data->state.path,
(bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
if(co) {
struct Cookie *store=co;
/* now loop through all cookies that matched */
while(co) {
if(co->value) {
if(0 == count) {
result = add_bufferf(req_buffer, "Cookie: ");
if(result)
break;
}
result = add_bufferf(req_buffer,
"%s%s=%s", count?"; ":"",
co->name, co->value);
if(result)
break;
count++;
}
co = co->next; /* next cookie please */
}
Curl_cookie_freelist(store, FALSE); /* free the cookie list */
}
if(addcookies && (CURLE_OK == result)) {
if(!count)
result = add_bufferf(req_buffer, "Cookie: ");
if(CURLE_OK == result) {
result = add_bufferf(req_buffer, "%s%s",
count?"; ":"",
addcookies);
count++;
}
}
if(count && (CURLE_OK == result))
result = add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
}
#endif
if(data->set.timecondition) {
struct tm *tm;
/* The If-Modified-Since header family should have their times set in
* GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
* represented in Greenwich Mean Time (GMT), without exception. For the
* purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
* Time)." (see page 20 of RFC2616).
*/
#ifdef HAVE_GMTIME_R
/* thread-safe version */
struct tm keeptime;
tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
#else
tm = gmtime(&data->set.timevalue);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
snprintf(buf, BUFSIZE-1,
"%s, %02d %s %4d %02d:%02d:%02d GMT",
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
Curl_month[tm->tm_mon],
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
result = add_bufferf(req_buffer,
"If-Modified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_IFUNMODSINCE:
result = add_bufferf(req_buffer,
"If-Unmodified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_LASTMOD:
result = add_bufferf(req_buffer,
"Last-Modified: %s\r\n", buf);
break;
}
if(result)
return result;
}
result = add_custom_headers(conn, req_buffer);
if(result)
return result;
http->postdata = NULL; /* nothing to post at this point */
Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
/* If 'authdone' is FALSE, we must not set the write socket index to the
Curl_transfer() call below, as we're not ready to actually upload any
data yet. */
switch(httpreq) {
case HTTPREQ_POST_FORM:
if(!http->sendit || conn->bits.authneg) {
/* nothing to post! */
result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
if(result)
return result;
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
-1, NULL);
break;
}
if(Curl_FormInit(&http->form, http->sendit)) {
failf(data, "Internal HTTP POST error!");
return CURLE_HTTP_POST_ERROR;
}
/* Get the currently set callback function pointer and store that in the
form struct since we might want the actual user-provided callback later
on. The conn->fread_func pointer itself will be changed for the
multipart case to the function that returns a multipart formatted
stream. */
http->form.fread_func = conn->fread_func;
/* Set the read function to read from the generated form data */
conn->fread_func = (curl_read_callback)Curl_FormReader;
conn->fread_in = &http->form;
http->sending = HTTPSEND_BODY;
if(!data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */
result = add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T "\r\n",
http->postsize);
if(result)
return result;
}
result = expect100(data, conn, req_buffer);
if(result)
return result;
{
/* Get Content-Type: line from Curl_formpostheader.
*/
char *contentType;
size_t linelength=0;
contentType = Curl_formpostheader((void *)&http->form,
&linelength);
if(!contentType) {
failf(data, "Could not get Content-Type header line!");
return CURLE_HTTP_POST_ERROR;
}
result = add_buffer(req_buffer, contentType, linelength);
if(result)
return result;
}
/* make the request end in a true CRLF */
result = add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
/* set upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
/* fire away the whole request to the server */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
FIRSTSOCKET,
&http->writebytecount);
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
#ifdef CURL_DOES_CONVERSIONS
/* time to convert the form data... */
result = Curl_formconvert(data, http->sendit);
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
#endif /* CURL_DOES_CONVERSIONS */
break;
case HTTPREQ_PUT: /* Let's PUT the data to the server! */
if(conn->bits.authneg)
postsize = 0;
else
postsize = data->set.infilesize;
if((postsize != -1) && !data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */
result = add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T "\r\n",
postsize );
if(result)
return result;
}
result = expect100(data, conn, req_buffer);
if(result)
return result;
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
if(result)
return result;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize);
/* this sends the buffer and frees all the buffer resources */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending PUT request");
else
/* prepare for transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
postsize?FIRSTSOCKET:-1,
postsize?&http->writebytecount:NULL);
if(result)
return result;
break;
case HTTPREQ_POST:
/* this is the simple POST, using x-www-form-urlencoded style */
if(conn->bits.authneg)
postsize = 0;
else
/* figure out the size of the postfields */
postsize = (data->set.postfieldsize != -1)?
data->set.postfieldsize:
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
if(!data->req.upload_chunky) {
/* We only set Content-Length and allow a custom Content-Length if
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if(!checkheaders(data, "Content-Length:")) {
/* we allow replacing this header, although it isn't very wise to
actually set your own */
result = add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T"\r\n",
postsize);
if(result)
return result;
}
}
if(!checkheaders(data, "Content-Type:")) {
result = add_bufferf(req_buffer,
"Content-Type: application/x-www-form-urlencoded\r\n");
if(result)
return result;
}
/* For really small posts we don't use Expect: headers at all, and for
the somewhat bigger ones we allow the app to disable it. Just make
sure that the expect100header is always set to the preferred value
here. */
if(postsize > TINY_INITIAL_POST_SIZE) {
result = expect100(data, conn, req_buffer);
if(result)
return result;
}
else
data->state.expect100header = FALSE;
if(data->set.postfields) {
if(!data->state.expect100header &&
(postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
postsize is less than MAX_INITIAL_POST_SIZE
then append the post data to the HTTP request header. This limit
is no magic limit but only set to prevent really huge POSTs to
get the data duplicated with malloc() and family. */
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
if(!data->req.upload_chunky) {
/* We're not sending it 'chunked', append it to the request
already now to reduce the number if send() calls */
result = add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
included_body = postsize;
}
else {
/* Append the POST data chunky-style */
result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
if(CURLE_OK == result)
result = add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
if(CURLE_OK == result)
result = add_buffer(req_buffer,
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
/* CR LF 0 CR LF CR LF */
included_body = postsize + 7;
}
if(result)
return result;
}
else {
/* A huge POST coming up, do data separate from the request */
http->postsize = postsize;
http->postdata = data->set.postfields;
http->sending = HTTPSEND_BODY;
conn->fread_func = (curl_read_callback)readmoredata;
conn->fread_in = (void *)conn;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
}
}
else {
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
if(data->set.postfieldsize) {
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
/* set the pointer to mark that we will send the post body using the
read callback, but only if we're not in authenticate
negotiation */
if(!conn->bits.authneg) {
http->postdata = (char *)&http->postdata;
http->postsize = postsize;
}
}
}
/* issue the request */
result = add_buffer_send(req_buffer, conn, &data->info.request_size,
(size_t)included_body, FIRSTSOCKET);
if(result)
failf(data, "Failed sending HTTP POST request");
else
result =
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
http->postdata?FIRSTSOCKET:-1,
http->postdata?&http->writebytecount:NULL);
break;
default:
result = add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
/* issue the request */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending HTTP request");
else
/* HTTP GET/HEAD download: */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
http->postdata?FIRSTSOCKET:-1,
http->postdata?&http->writebytecount:NULL);
}
if(result)
return result;
if(http->writebytecount) {
/* if a request-body has been sent off, we make sure this progress is noted
properly */
Curl_pgrsSetUploadCounter(data, http->writebytecount);
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
}
return result;
}
|
|||||
| ↓ | readwrite_headers | 129 | 269 | 709 | lib/transfer.c |
static CURLcode readwrite_headers(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
ssize_t *nread,
bool *stop_reading)
{
CURLcode result;
/* header line within buffer loop */
do {
size_t hbufp_index;
size_t rest_length;
size_t full_length;
int writetype;
/* str_start is start of line within buf */
k->str_start = k->str;
/* data is in network encoding so use 0x0a instead of '\n' */
k->end_ptr = memchr(k->str_start, 0x0a, *nread);
if(!k->end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
if(k->hbuflen + *nread >= data->state.headersize) {
/* We enlarge the header buffer as it is too small */
char *newbuff;
size_t newsize=CURLMAX((k->hbuflen+*nread)*3/2,
data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = realloc(data->state.headerbuff, newsize);
if(!newbuff) {
failf (data, "Failed to alloc memory for big header!");
return CURLE_OUT_OF_MEMORY;
}
data->state.headersize=newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
memcpy(k->hbufp, k->str, *nread);
k->hbufp += *nread;
k->hbuflen += *nread;
if(!k->headerline && (k->hbuflen>5)) {
/* make a first check that this looks like a HTTP header */
if(!checkhttpprefix(data, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
break; /* read more and try again */
}
/* decrease the size of the remaining (supposed) header line */
rest_length = (k->end_ptr - k->str)+1;
*nread -= (ssize_t)rest_length;
k->str = k->end_ptr + 1; /* move past new line */
full_length = k->str - k->str_start;
/*
* We're about to copy a chunk of data to the end of the
* already received header. We make sure that the full string
* fit in the allocated header buffer, or else we enlarge
* it.
*/
if(k->hbuflen + full_length >=
data->state.headersize) {
char *newbuff;
size_t newsize=CURLMAX((k->hbuflen+full_length)*3/2,
data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = realloc(data->state.headerbuff, newsize);
if(!newbuff) {
failf (data, "Failed to alloc memory for big header!");
return CURLE_OUT_OF_MEMORY;
}
data->state.headersize= newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
/* copy to end of line */
memcpy(k->hbufp, k->str_start, full_length);
k->hbufp += full_length;
k->hbuflen += full_length;
*k->hbufp = 0;
k->end_ptr = k->hbufp;
k->p = data->state.headerbuff;
/****
* We now have a FULL header line that p points to
*****/
if(!k->headerline) {
/* the first read header */
if((k->hbuflen>5) &&
!checkhttpprefix(data, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */
k->header = FALSE;
if(*nread)
/* since there's more, this is a partial bad header */
k->badheader = HEADER_PARTHEADER;
else {
/* this was all we read so it's all a bad header */
k->badheader = HEADER_ALLBAD;
*nread = (ssize_t)rest_length;
}
break;
}
}
/* headers are in network encoding so
use 0x0a and 0x0d instead of '\n' and '\r' */
if((0x0a == *k->p) || (0x0d == *k->p)) {
size_t headerlen;
/* Zero-length header line means end of headers! */
#ifdef CURL_DOES_CONVERSIONS
if(0x0d == *k->p) {
*k->p = '\r'; /* replace with CR in host encoding */
k->p++; /* pass the CR byte */
}
if(0x0a == *k->p) {
*k->p = '\n'; /* replace with LF in host encoding */
k->p++; /* pass the LF byte */
}
#else
if('\r' == *k->p)
k->p++; /* pass the \r byte */
if('\n' == *k->p)
k->p++; /* pass the \n byte */
#endif /* CURL_DOES_CONVERSIONS */
#ifndef CURL_DISABLE_HTTP
if(100 <= k->httpcode && 199 >= k->httpcode) {
/*
* We have made a HTTP PUT or POST and this is 1.1-lingo
* that tells us that the server is OK with this and ready
* to receive the data.
* However, we'll get more headers now so we must get
* back into the header-parsing state!
*/
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
/* if we did wait for this do enable write now! */
if(k->exp100) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_WRITE;
}
}
else {
k->header = FALSE; /* no more header to parse! */
if((k->size == -1) && !k->chunk && !conn->bits.close &&
(conn->httpversion >= 11) ) {
/* On HTTP 1.1, when connection is not to get closed, but no
Content-Length nor Content-Encoding chunked have been
received, according to RFC2616 section 4.4 point 5, we
assume that the server will close the connection to
signal the end of the document. */
infof(data, "no chunk, no close, no size. Assume close to "
"signal end\n");
conn->bits.close = TRUE;
}
}
if(417 == k->httpcode) {
/*
* we got: "417 Expectation Failed" this means:
* we have made a HTTP call and our Expect Header
* seems to cause a problem => abort the write operations
* (or prevent them from starting).
*/
k->exp100 = EXP100_FAILED;
k->keepon &= ~KEEP_WRITE;
}
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
*/
if(Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
#endif /* CURL_DISABLE_HTTP */
/* now, only output this if the header AND body are requested:
*/
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
headerlen = k->p - data->state.headerbuff;
result = Curl_client_write(conn, writetype,
data->state.headerbuff,
headerlen);
if(result)
return result;
data->info.header_size += (long)headerlen;
data->req.headerbytecount += (long)headerlen;
data->req.deductheadercount =
(100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
if(data->state.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable" */
*stop_reading = TRUE;
}
#ifndef CURL_DISABLE_HTTP
if(!*stop_reading) {
/* Curl_http_auth_act() checks what authentication methods
* that are available and decides which one (if any) to
* use. It will set 'newurl' if an auth method was picked. */
result = Curl_http_auth_act(conn);
if(result)
return result;
if(conn->bits.rewindaftersend) {
/* We rewind after a complete send, so thus we continue
sending now */
infof(data, "Keep sending data to get tossed away!\n");
k->keepon |= KEEP_WRITE;
}
}
#endif /* CURL_DISABLE_HTTP */
if(!k->header) {
/*
* really end-of-headers.
*
* If we requested a "no body", this is a good time to get
* out and return home.
*/
if(data->set.opt_no_body)
*stop_reading = TRUE;
else {
/* If we know the expected size of this document, we set the
maximum download size to the size of the expected
document or else, we won't know when to stop reading!
Note that we set the download maximum even if we read a
"Connection: close" header, to make sure that
"Content-Length: 0" still prevents us from attempting to
read the (missing) response-body.
*/
/* According to RFC2616 section 4.4, we MUST ignore
Content-Length: headers if we are now receiving data
using chunked Transfer-Encoding.
*/
if(k->chunk)
k->size=-1;
}
if(-1 != k->size) {
/* We do this operation even if no_body is true, since this
data might be retrieved later with curl_easy_getinfo()
and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
Curl_pgrsSetDownloadSize(data, k->size);
k->maxdownload = k->size;
}
/* If max download size is *zero* (nothing) we already
have nothing and can safely return ok now! */
if(0 == k->maxdownload)
*stop_reading = TRUE;
if(*stop_reading) {
/* we make sure that this socket isn't read more now */
k->keepon &= ~KEEP_READ;
}
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->str_start, headerlen, conn);
break; /* exit header line loop */
}
/* We continue reading headers, so reset the line-based
header parsing variables hbufp && hbuflen */
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
continue;
}
#ifndef CURL_DISABLE_HTTP
/*
* Checks for special headers coming up.
*/
if(!k->headerline++) {
/* This is the first header, it MUST be the error code line
or else we consider this to be the body right away! */
int httpversion_major;
int nc;
#ifdef CURL_DOES_CONVERSIONS
#define HEADER1 scratch
#define SCRATCHSIZE 21
CURLcode res;
char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
/* We can't really convert this yet because we
don't know if it's the 1st header line or the body.
So we do a partial conversion into a scratch area,
leaving the data at k->p as-is.
*/
strncpy(&scratch[0], k->p, SCRATCHSIZE);
scratch[SCRATCHSIZE] = 0; /* null terminate */
res = Curl_convert_from_network(data,
&scratch[0],
SCRATCHSIZE);
if(CURLE_OK != res) {
/* Curl_convert_from_network calls failf if unsuccessful */
return res;
}
#else
#define HEADER1 k->p /* no conversion needed, just use k->p */
#endif /* CURL_DOES_CONVERSIONS */
nc = sscanf(HEADER1,
" HTTP/%d.%d %3d",
&httpversion_major,
&conn->httpversion,
&k->httpcode);
if(nc==3) {
conn->httpversion += 10 * httpversion_major;
}
else {
/* this is the real world, not a Nirvana
NCSA 1.5.x returns this crap when asked for HTTP/1.1
*/
nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
conn->httpversion = 10;
/* If user has set option HTTP200ALIASES,
compare header line against list of aliases
*/
if(!nc) {
if(checkhttpprefix(data, k->p)) {
nc = 1;
k->httpcode = 200;
conn->httpversion = 10;
}
}
}
if(nc) {
data->info.httpcode = k->httpcode;
data->info.httpversion = conn->httpversion;
if (!data->state.httpversion ||
data->state.httpversion > conn->httpversion)
/* store the lowest server version we encounter */
data->state.httpversion = conn->httpversion;
/*
* This code executes as part of processing the header. As a
* result, it's not totally clear how to interpret the
* response code yet as that depends on what other headers may
* be present. 401 and 407 may be errors, but may be OK
* depending on how authentication is working. Other codes
* are definitely errors, so give up here.
*/
if(data->set.http_fail_on_error && (k->httpcode >= 400) &&
((k->httpcode != 401) || !conn->bits.user_passwd) &&
((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) {
if(data->state.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable", just proceed and
pretend this is no error */
}
else {
/* serious error, go home! */
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
}
if(conn->httpversion == 10) {
/* Default action for HTTP/1.0 must be to close, unless
we get one of those fancy headers that tell us the
server keeps it open for us! */
infof(data, "HTTP 1.0, assume close after body\n");
conn->bits.close = TRUE;
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
/* If HTTP version is >= 1.1 and connection is persistent
server supports pipelining. */
DEBUGF(infof(data,
"HTTP 1.1 or later with persistent connection, "
"pipelining supported\n"));
conn->server_supports_pipelining = TRUE;
}
switch(k->httpcode) {
case 204:
/* (quote from RFC2616, section 10.2.5): The server has
* fulfilled the request but does not need to return an
* entity-body ... The 204 response MUST NOT include a
* message-body, and thus is always terminated by the first
* empty line after the header fields. */
/* FALLTHROUGH */
case 416: /* Requested Range Not Satisfiable, it has the
Content-Length: set as the "real" document but no
actual response is sent. */
case 304:
/* (quote from RFC2616, section 10.3.5): The 304 response
* MUST NOT contain a message-body, and thus is always
* terminated by the first empty line after the header
* fields. */
k->size=0;
k->maxdownload=0;
k->ignorecl = TRUE; /* ignore Content-Length headers */
break;
default:
/* nothing */
break;
}
}
else {
k->header = FALSE; /* this is not a header line */
break;
}
}
#endif /* CURL_DISABLE_HTTP */
#ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding */
result = Curl_convert_from_network(data, k->p, strlen(k->p));
if(CURLE_OK != result) {
return(result);
}
/* Curl_convert_from_network calls failf if unsuccessful */
#endif /* CURL_DOES_CONVERSIONS */
#ifndef CURL_DISABLE_HTTP
/* Check for Content-Length: header lines to get size. Ignore
the header completely if we get a 416 response as then we're
resuming a document that we don't get, and this header contains
info about the true size of the document we didn't get now. */
if(!k->ignorecl && !data->set.ignorecl &&
checkprefix("Content-Length:", k->p)) {
curl_off_t contentlength = curlx_strtoofft(k->p+15, NULL, 10);
if(data->set.max_filesize &&
contentlength > data->set.max_filesize) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
}
if(contentlength >= 0) {
k->size = contentlength;
k->maxdownload = k->size;
/* we set the progress download size already at this point
just to make it easier for apps/callbacks to extract this
info as soon as possible */
Curl_pgrsSetDownloadSize(data, k->size);
}
else {
/* Negative Content-Length is really odd, and we know it
happens for example when older Apache servers send large
files */
conn->bits.close = TRUE;
infof(data, "Negative content-length: %" FORMAT_OFF_T
", closing after transfer\n", contentlength);
}
}
/* check for Content-Type: header lines to get the MIME-type */
else if(checkprefix("Content-Type:", k->p)) {
char *contenttype = Curl_copy_header_value(k->p);
if (!contenttype)
return CURLE_OUT_OF_MEMORY;
if (!*contenttype)
/* ignore empty data */
free(contenttype);
else {
Curl_safefree(data->info.contenttype);
data->info.contenttype = contenttype;
}
}
else if((conn->httpversion == 10) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "keep-alive")) {
/*
* When a HTTP/1.0 reply comes when using a proxy, the
* 'Proxy-Connection: keep-alive' line tells us the
* connection will be kept alive for our pleasure.
* Default action for 1.0 is to close.
*/
conn->bits.close = FALSE; /* don't close when done */
infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
}
else if((conn->httpversion == 11) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "close")) {
/*
* We get a HTTP/1.1 response from a proxy and it says it'll
* close down after this transfer.
*/
conn->bits.close = TRUE; /* close when done */
infof(data, "HTTP/1.1 proxy connection set close!\n");
}
else if((conn->httpversion == 10) &&
Curl_compareheader(k->p, "Connection:", "keep-alive")) {
/*
* A HTTP/1.0 reply with the 'Connection: keep-alive' line
* tells us the connection will be kept alive for our
* pleasure. Default action for 1.0 is to close.
*
* [RFC2068, section 19.7.1] */
conn->bits.close = FALSE; /* don't close when done */
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if(Curl_compareheader(k->p, "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(Curl_compareheader(k->p,
"Transfer-Encoding:", "chunked")) {
/*
* [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
* means that the server will send a series of "chunks". Each
* chunk starts with line with info (including size of the
* coming block) (terminated with CRLF), then a block of data
* with the previously mentioned size. There can be any amount
* of chunks, and a chunk-data set to zero signals the
* end-of-chunks. */
k->chunk = TRUE; /* chunks coming our way */
/* init our chunky engine */
Curl_httpchunk_init(conn);
}
else if(checkprefix("Trailer:", k->p) ||
checkprefix("Trailers:", k->p)) {
/*
* This test helps Curl_httpchunk_read() to determine to look
* for well formed trailers after the zero chunksize record. In
* this case a CRLF is required after the zero chunksize record
* when no trailers are sent, or after the last trailer record.
*
* It seems both Trailer: and Trailers: occur in the wild.
*/
k->trailerhdrpresent = TRUE;
}
else if(checkprefix("Content-Encoding:", k->p) &&
data->set.str[STRING_ENCODING]) {
/*
* Process Content-Encoding. Look for the values: identity,
* gzip, deflate, compress, x-gzip and x-compress. x-gzip and
* x-compress are the same as gzip and compress. (Sec 3.5 RFC
* 2616). zlib cannot handle compress. However, errors are
* handled further down when the response body is processed
*/
char *start;
/* Find the first non-space letter */
for(start=k->p+17;
*start && ISSPACE(*start);
start++)
; /* empty loop */
/* Record the content-encoding for later use */
if(checkprefix("identity", start))
k->content_encoding = IDENTITY;
else if(checkprefix("deflate", start))
k->content_encoding = DEFLATE;
else if(checkprefix("gzip", start)
|| checkprefix("x-gzip", start))
k->content_encoding = GZIP;
else if(checkprefix("compress", start)
|| checkprefix("x-compress", start))
k->content_encoding = COMPRESS;
}
else if(checkprefix("Content-Range:", k->p)) {
/* Content-Range: bytes [num]-
Content-Range: bytes: [num]-
Content-Range: [num]-
The second format was added since Sun's webserver
JavaWebServer/1.1.1 obviously sends the header this way!
The third added since some servers use that!
*/
char *ptr = k->p + 14;
/* Move forward until first digit */
while(*ptr && !ISDIGIT(*ptr))
ptr++;
k->offset = curlx_strtoofft(ptr, NULL, 10);
if(data->state.resume_from == k->offset)
/* we asked for a resume and we got it */
k->content_range = TRUE;
}
#if !defined(CURL_DISABLE_COOKIES)
else if(data->cookies &&
checkprefix("Set-Cookie:", k->p)) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_add(data,
data->cookies, TRUE, k->p+11,
/* If there is a custom-set Host: name, use it
here, or else use real peer host name. */
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:conn->host.name,
data->state.path);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
#endif
else if(checkprefix("Last-Modified:", k->p) &&
(data->set.timecondition || data->set.get_filetime) ) {
time_t secs=time(NULL);
k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
&secs);
if(data->set.get_filetime)
data->info.filetime = (long)k->timeofdoc;
}
else if((checkprefix("WWW-Authenticate:", k->p) &&
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode, k->p);
if(result)
return result;
}
else if((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p)) {
/* this is the URL that the server advises us to use instead */
char *location = Curl_copy_header_value(k->p);
if (!location)
return CURLE_OUT_OF_MEMORY;
if (!*location)
/* ignore empty data */
free(location);
else {
DEBUGASSERT(!data->req.location);
data->req.location = location;
if(data->set.http_follow_location) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->req.location); /* clone */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
/* some cases of POST and PUT etc needs to rewind the data
stream at this point */
result = Curl_http_perhapsrewind(conn);
if(result)
return result;
}
}
}
#endif /* CURL_DISABLE_HTTP */
/*
* End of header-checks. Write them to the client.
*/
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->p, (size_t)k->hbuflen, conn);
result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
if(result)
return result;
data->info.header_size += (long)k->hbuflen;
data->req.headerbytecount += (long)k->hbuflen;
/* reset hbufp pointer && hbuflen */
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
}
while(!*stop_reading && *k->str); /* header line within buffer */
/* We might have reached the end of the header part here, but
there might be a non-header part left in the end of the read
buffer. */
return CURLE_OK;
}
|
|||||
| ↓ | Curl_cookie_add | 118 | 247 | 502 | lib/cookie.c |
struct Cookie *
Curl_cookie_add(struct SessionHandle *data,
/* The 'data' pointer here may be NULL at times, and thus
must only be used very carefully for things that can deal
with data being NULL. Such as infof() and similar */
struct CookieInfo *c,
bool httpheader, /* TRUE if HTTP header-style line */
char *lineptr, /* first character of the line */
const char *domain, /* default domain */
const char *path) /* full path used when this cookie is set,
used to get default path for the cookie
unless set */
{
struct Cookie *clist;
char name[MAX_NAME];
struct Cookie *co;
struct Cookie *lastc=NULL;
time_t now = time(NULL);
bool replace_old = FALSE;
bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
/* First, alloc and init a new struct for it */
co = calloc(sizeof(struct Cookie), 1);
if(!co)
return NULL; /* bail out if we're this low on memory */
if(httpheader) {
/* This line was read off a HTTP-header */
const char *ptr;
const char *sep;
const char *semiptr;
char *what;
what = malloc(MAX_COOKIE_LINE);
if(!what) {
free(co);
return NULL;
}
semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
ptr = lineptr;
do {
/* we have a
|
|||||
| ↓ | FormAdd | 105 | 210 | 421 | lib/formdata.c |
static
CURLFORMcode FormAdd(struct curl_httppost **httppost,
struct curl_httppost **last_post,
va_list params)
{
FormInfo *first_form, *current_form, *form = NULL;
CURLFORMcode return_value = CURL_FORMADD_OK;
const char *prevtype = NULL;
struct curl_httppost *post = NULL;
CURLformoption option;
struct curl_forms *forms = NULL;
char *array_value=NULL; /* value read from an array */
/* This is a state variable, that if TRUE means that we're parsing an
array that we got passed to us. If FALSE we're parsing the input
va_list arguments. */
bool array_state = FALSE;
/*
* We need to allocate the first struct to fill in.
*/
first_form = calloc(sizeof(struct FormInfo), 1);
if(!first_form)
return CURL_FORMADD_MEMORY;
current_form = first_form;
/*
* Loop through all the options set. Break if we have an error to report.
*/
while(return_value == CURL_FORMADD_OK) {
/* first see if we have more parts of the array param */
if( array_state ) {
/* get the upcoming option from the given array */
option = forms->option;
array_value = (char *)forms->value;
forms++; /* advance this to next entry */
if(CURLFORM_END == option) {
/* end of array state */
array_state = FALSE;
continue;
}
}
else {
/* This is not array-state, get next option */
option = va_arg(params, CURLformoption);
if(CURLFORM_END == option)
break;
}
switch (option) {
case CURLFORM_ARRAY:
if(array_state)
/* we don't support an array from within an array */
return_value = CURL_FORMADD_ILLEGAL_ARRAY;
else {
forms = va_arg(params, struct curl_forms *);
if(forms)
array_state = TRUE;
else
return_value = CURL_FORMADD_NULL;
}
break;
/*
* Set the Name property.
*/
case CURLFORM_PTRNAME:
#ifdef CURL_DOES_CONVERSIONS
/* treat CURLFORM_PTR like CURLFORM_COPYNAME so we'll
have safe memory for the eventual conversion */
#else
current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
#endif
case CURLFORM_COPYNAME:
if(current_form->name)
return_value = CURL_FORMADD_OPTION_TWICE;
else {
char *name = array_state?
array_value:va_arg(params, char *);
if(name)
current_form->name = name; /* store for the moment */
else
return_value = CURL_FORMADD_NULL;
}
break;
case CURLFORM_NAMELENGTH:
if(current_form->namelength)
return_value = CURL_FORMADD_OPTION_TWICE;
else
current_form->namelength =
array_state?(size_t)array_value:(size_t)va_arg(params, long);
break;
/*
* Set the contents property.
*/
case CURLFORM_PTRCONTENTS:
current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
case CURLFORM_COPYCONTENTS:
if(current_form->value)
return_value = CURL_FORMADD_OPTION_TWICE;
else {
char *value =
array_state?array_value:va_arg(params, char *);
if(value)
current_form->value = value; /* store for the moment */
else
return_value = CURL_FORMADD_NULL;
}
break;
case CURLFORM_CONTENTSLENGTH:
if(current_form->contentslength)
return_value = CURL_FORMADD_OPTION_TWICE;
else
current_form->contentslength =
array_state?(size_t)array_value:(size_t)va_arg(params, long);
break;
/* Get contents from a given file name */
case CURLFORM_FILECONTENT:
if(current_form->flags != 0)
return_value = CURL_FORMADD_OPTION_TWICE;
else {
const char *filename = array_state?
array_value:va_arg(params, char *);
if(filename) {
current_form->value = strdup(filename);
if(!current_form->value)
return_value = CURL_FORMADD_MEMORY;
else {
current_form->flags |= HTTPPOST_READFILE;
current_form->value_alloc = TRUE;
}
}
else
return_value = CURL_FORMADD_NULL;
}
break;
/* We upload a file */
case CURLFORM_FILE:
{
const char *filename = array_state?array_value:
va_arg(params, char *);
if(current_form->value) {
if(current_form->flags & HTTPPOST_FILENAME) {
if(filename) {
if((current_form = AddFormInfo(strdup(filename),
NULL, current_form)) == NULL)
return_value = CURL_FORMADD_MEMORY;
}
else
return_value = CURL_FORMADD_NULL;
}
else
return_value = CURL_FORMADD_OPTION_TWICE;
}
else {
if(filename) {
current_form->value = strdup(filename);
if(!current_form->value)
return_value = CURL_FORMADD_MEMORY;
else {
current_form->flags |= HTTPPOST_FILENAME;
current_form->value_alloc = TRUE;
}
}
else
return_value = CURL_FORMADD_NULL;
}
break;
}
case CURLFORM_BUFFER:
{
const char *filename = array_state?array_value:
va_arg(params, char *);
if(current_form->value) {
if(current_form->flags & HTTPPOST_BUFFER) {
if(filename) {
if((current_form = AddFormInfo(strdup(filename),
NULL, current_form)) == NULL)
return_value = CURL_FORMADD_MEMORY;
}
else
return_value = CURL_FORMADD_NULL;
}
else
return_value = CURL_FORMADD_OPTION_TWICE;
}
else {
if(filename) {
current_form->value = strdup(filename);
if(!current_form->value)
return_value = CURL_FORMADD_MEMORY;
}
else
return_value = CURL_FORMADD_NULL;
current_form->flags |= HTTPPOST_BUFFER;
}
break;
}
case CURLFORM_BUFFERPTR:
current_form->flags |= HTTPPOST_PTRBUFFER;
if(current_form->buffer)
return_value = CURL_FORMADD_OPTION_TWICE;
else {
char *buffer =
array_state?array_value:va_arg(params, char *);
if(buffer)
current_form->buffer = buffer; /* store for the moment */
else
return_value = CURL_FORMADD_NULL;
}
break;
case CURLFORM_BUFFERLENGTH:
if(current_form->bufferlength)
return_value = CURL_FORMADD_OPTION_TWICE;
else
current_form->bufferlength =
array_state?(size_t)array_value:(size_t)va_arg(params, long);
break;
case CURLFORM_STREAM:
current_form->flags |= HTTPPOST_CALLBACK;
if(current_form->userp)
return_value = CURL_FORMADD_OPTION_TWICE;
else {
char *userp =
array_state?array_value:va_arg(params, char *);
if(userp) {
current_form->userp = userp;
current_form->value = userp; /* this isn't strictly true but we
derive a value from this later on
and we need this non-NULL to be
accepted as a fine form part */
}
else
return_value = CURL_FORMADD_NULL;
}
break;
case CURLFORM_CONTENTTYPE:
{
const char *contenttype =
array_state?array_value:va_arg(params, char *);
if(current_form->contenttype) {
if(current_form->flags & HTTPPOST_FILENAME) {
if(contenttype) {
if((current_form = AddFormInfo(NULL,
strdup(contenttype),
current_form)) == NULL)
return_value = CURL_FORMADD_MEMORY;
}
else
return_value = CURL_FORMADD_NULL;
}
else
return_value = CURL_FORMADD_OPTION_TWICE;
}
else {
if(contenttype) {
current_form->contenttype = strdup(contenttype);
if(!current_form->contenttype)
return_value = CURL_FORMADD_MEMORY;
else
current_form->contenttype_alloc = TRUE;
}
else
return_value = CURL_FORMADD_NULL;
}
break;
}
case CURLFORM_CONTENTHEADER:
{
/* this "cast increases required alignment of target type" but
we consider it OK anyway */
struct curl_slist* list = array_state?
(struct curl_slist*)array_value:
va_arg(params, struct curl_slist*);
if( current_form->contentheader )
return_value = CURL_FORMADD_OPTION_TWICE;
else
current_form->contentheader = list;
break;
}
case CURLFORM_FILENAME:
{
const char *filename = array_state?array_value:
va_arg(params, char *);
if( current_form->showfilename )
return_value = CURL_FORMADD_OPTION_TWICE;
else {
current_form->showfilename = strdup(filename);
if(!current_form->showfilename)
return_value = CURL_FORMADD_MEMORY;
else
current_form->showfilename_alloc = TRUE;
}
break;
}
default:
return_value = CURL_FORMADD_UNKNOWN_OPTION;
}
}
if(CURL_FORMADD_OK == return_value) {
/* go through the list, check for completeness and if everything is
* alright add the HttpPost item otherwise set return_value accordingly */
post = NULL;
for(form = first_form;
form != NULL;
form = form->more) {
if( ((!form->name || !form->value) && !post) ||
( (form->contentslength) &&
(form->flags & HTTPPOST_FILENAME) ) ||
( (form->flags & HTTPPOST_FILENAME) &&
(form->flags & HTTPPOST_PTRCONTENTS) ) ||
( (!form->buffer) &&
(form->flags & HTTPPOST_BUFFER) &&
(form->flags & HTTPPOST_PTRBUFFER) ) ||
( (form->flags & HTTPPOST_READFILE) &&
(form->flags & HTTPPOST_PTRCONTENTS) )
) {
return_value = CURL_FORMADD_INCOMPLETE;
break;
}
else {
if( ((form->flags & HTTPPOST_FILENAME) ||
(form->flags & HTTPPOST_BUFFER)) &&
!form->contenttype ) {
/* our contenttype is missing */
form->contenttype
= strdup(ContentTypeForFilename(form->value, prevtype));
if(!form->contenttype) {
return_value = CURL_FORMADD_MEMORY;
break;
}
form->contenttype_alloc = TRUE;
}
if( !(form->flags & HTTPPOST_PTRNAME) &&
(form == first_form) ) {
/* copy name (without strdup; possibly contains null characters) */
form->name = memdup(form->name, form->namelength);
if(!form->name) {
return_value = CURL_FORMADD_MEMORY;
break;
}
form->name_alloc = TRUE;
}
if( !(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
HTTPPOST_CALLBACK)) ) {
/* copy value (without strdup; possibly contains null characters) */
form->value = memdup(form->value, form->contentslength);
if(!form->value) {
return_value = CURL_FORMADD_MEMORY;
break;
}
form->value_alloc = TRUE;
}
post = AddHttpPost(form->name, form->namelength,
form->value, form->contentslength,
form->buffer, form->bufferlength,
form->contenttype, form->flags,
form->contentheader, form->showfilename,
form->userp,
post, httppost,
last_post);
if(!post) {
return_value = CURL_FORMADD_MEMORY;
break;
}
if(form->contenttype)
prevtype = form->contenttype;
}
}
}
if(return_value) {
/* we return on error, free possibly allocated fields */
if(!form)
form = current_form;
if(form) {
if(form->name_alloc)
free(form->name);
if(form->value_alloc)
free(form->value);
if(form->contenttype_alloc)
free(form->contenttype);
if(form->showfilename_alloc)
free(form->showfilename);
}
}
/* always delete the allocated memory before returning */
form = first_form;
while(form != NULL) {
FormInfo *delete_form;
delete_form = form;
form = form->more;
free (delete_form);
}
return return_value;
}
|
|||||
| ↓ | dprintf_formatf | 92 | 233 | 395 | lib/mprintf.c |
static int dprintf_formatf(
void *data, /* untouched by format(), just sent to the stream() function in
the second argument */
/* function pointer called for each output character */
int (*stream)(int, FILE *),
const char *format, /* %-formatted string */
va_list ap_save) /* list of parameters */
{
/* Base-36 digits for numbers. */
const char *digits = lower_digits;
/* Pointer into the format string. */
char *f;
/* Number of characters written. */
int done = 0;
long param; /* current parameter to read */
long param_num=0; /* parameter counter */
va_stack_t vto[MAX_PARAMETERS];
char *endpos[MAX_PARAMETERS];
char **end;
char work[BUFFSIZE];
va_stack_t *p;
/* Do the actual %-code parsing */
dprintf_Pass1(format, vto, endpos, ap_save);
end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
created for us */
f = (char *)format;
while(*f != '\0') {
/* Format spec modifiers. */
int is_alt;
/* Width of a field. */
long width;
/* Precision of a field. */
long prec;
/* Decimal integer is negative. */
int is_neg;
/* Base of a number to be written. */
long base;
/* Integral values to be written. */
mp_uintmax_t num;
/* Used to convert negative in positive. */
mp_intmax_t signed_num;
if(*f != '%') {
/* This isn't a format spec, so write everything out until the next one
OR end of string is reached. */
do {
OUTCHAR(*f);
} while(*++f && ('%' != *f));
continue;
}
++f;
/* Check for "%%". Note that although the ANSI standard lists
'%' as a conversion specifier, it says "The complete format
specification shall be `%%'," so we can avoid all the width
and precision processing. */
if(*f == '%') {
++f;
OUTCHAR('%');
continue;
}
/* If this is a positional parameter, the position must follow imediately
after the %, thus create a %
|
|||||
| ↓ | multi_runsingle | 94 | 251 | 606 | lib/multi.c |
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct Curl_one_easy *easy)
{
struct Curl_message *msg = NULL;
bool connected;
bool async;
bool protocol_connect = FALSE;
bool dophase_done;
bool done = FALSE;
CURLMcode result = CURLM_OK;
struct SingleRequest *k;
if(!GOOD_EASY_HANDLE(easy->easy_handle))
return CURLM_BAD_EASY_HANDLE;
do {
/* this is a do-while loop just to allow a break to skip to the end
of it */
bool disconnect_conn = FALSE;
/* Handle the case when the pipe breaks, i.e., the connection
we're using gets cleaned up and we're left with nothing. */
if(easy->easy_handle->state.pipe_broke) {
infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n",
easy, easy->easy_handle->state.path);
if(easy->state != CURLM_STATE_COMPLETED) {
/* Head back to the CONNECT state */
multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
easy->result = CURLE_OK;
}
easy->easy_handle->state.pipe_broke = FALSE;
easy->easy_conn = NULL;
break;
}
if(easy->state > CURLM_STATE_CONNECT &&
easy->state < CURLM_STATE_COMPLETED)
/* Make sure we set the connection's current owner */
easy->easy_conn->data = easy->easy_handle;
switch(easy->state) {
case CURLM_STATE_INIT:
/* init this transfer. */
easy->result=Curl_pretransfer(easy->easy_handle);
if(CURLE_OK == easy->result) {
/* after init, go CONNECT */
multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
easy->easy_handle->state.used_interface = Curl_if_multi;
}
break;
case CURLM_STATE_CONNECT:
/* Connect. We get a connection identifier filled in. */
Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
&async, &protocol_connect);
if(CURLE_OK == easy->result) {
/* Add this handle to the send or pend pipeline */
easy->result = addHandleToSendOrPendPipeline(easy->easy_handle,
easy->easy_conn);
if(CURLE_OK == easy->result) {
if(async)
/* We're now waiting for an asynchronous name lookup */
multistate(easy, CURLM_STATE_WAITRESOLVE);
else {
/* after the connect has been sent off, go WAITCONNECT unless the
protocol connect is already done and we can go directly to
WAITDO or DO! */
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
multistate(easy, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
multistate(easy, CURLM_STATE_WAITCONNECT);
}
}
}
}
break;
case CURLM_STATE_WAITRESOLVE:
/* awaiting an asynch name resolve to complete */
{
struct Curl_dns_entry *dns = NULL;
/* check if we have the name resolved by now */
easy->result = Curl_is_resolved(easy->easy_conn, &dns);
if(dns) {
/* Update sockets here. Mainly because the socket(s) may have been
closed and the application thus needs to be told, even if it is
likely that the same socket(s) will again be used further down. */
singlesocket(multi, easy);
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
easy->result = Curl_async_resolved(easy->easy_conn,
&protocol_connect);
if(CURLE_OK != easy->result)
/* if Curl_async_resolved() returns failure, the connection struct
is already freed and gone */
easy->easy_conn = NULL; /* no more connection */
else {
/* call again please so that we get the next socket setup */
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
multistate(easy, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
multistate(easy, CURLM_STATE_WAITCONNECT);
}
}
}
if(CURLE_OK != easy->result) {
/* failure detected */
disconnect_conn = TRUE;
break;
}
}
break;
#ifndef CURL_DISABLE_HTTP
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
if(easy->easy_conn->bits.proxy_connect_closed) {
/* reset the error buffer */
if(easy->easy_handle->set.errorbuffer)
easy->easy_handle->set.errorbuffer[0] = '\0';
easy->easy_handle->state.errorbuf = FALSE;
easy->result = CURLE_OK;
result = CURLM_CALL_MULTI_PERFORM;
multistate(easy, CURLM_STATE_CONNECT);
}
else if (CURLE_OK == easy->result) {
if(!easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITCONNECT);
}
break;
#endif
case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */
easy->result = Curl_is_connected(easy->easy_conn,
FIRSTSOCKET,
&connected);
if(connected)
easy->result = Curl_protocol_connect(easy->easy_conn,
&protocol_connect);
if(CURLE_OK != easy->result) {
/* failure detected */
/* Just break, the cleaning up is handled all in one place */
disconnect_conn = TRUE;
break;
}
if(connected) {
if(!protocol_connect) {
/* We have a TCP connection, but 'protocol_connect' may be false
and then we continue to 'STATE_PROTOCONNECT'. If protocol
connect is TRUE, we move on to STATE_DO.
BUT if we are using a proxy we must change to WAITPROXYCONNECT
*/
#ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
multistate(easy, CURLM_STATE_PROTOCONNECT);
}
else {
/* after the connect has completed, go WAITDO or DO */
multistate(easy, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
}
break;
case CURLM_STATE_PROTOCONNECT:
/* protocol-specific connect phase */
easy->result = Curl_protocol_connecting(easy->easy_conn,
&protocol_connect);
if((easy->result == CURLE_OK) && protocol_connect) {
/* after the connect has completed, go WAITDO or DO */
multistate(easy, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
else if(easy->result) {
/* failure detected */
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result, FALSE);
disconnect_conn = TRUE;
}
break;
case CURLM_STATE_WAITDO:
/* Wait for our turn to DO when we're pipelining requests */
#ifdef CURLDEBUG
infof(easy->easy_handle, "Conn %d send pipe %d inuse %d athead %d\n",
easy->easy_conn->connectindex,
easy->easy_conn->send_pipe->size,
easy->easy_conn->writechannel_inuse,
isHandleAtHead(easy->easy_handle,
easy->easy_conn->send_pipe));
#endif
if(!easy->easy_conn->writechannel_inuse &&
isHandleAtHead(easy->easy_handle,
easy->easy_conn->send_pipe)) {
/* Grab the channel */
easy->easy_conn->writechannel_inuse = TRUE;
multistate(easy, CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
break;
case CURLM_STATE_DO:
if(easy->easy_handle->set.connect_only) {
/* keep connection open for application to use the socket */
easy->easy_conn->bits.close = FALSE;
multistate(easy, CURLM_STATE_DONE);
easy->result = CURLE_OK;
result = CURLM_OK;
}
else {
/* Perform the protocol's DO action */
easy->result = Curl_do(&easy->easy_conn,
&dophase_done);
if(CURLE_OK == easy->result) {
if(!dophase_done) {
/* DO was not completed in one function call, we must continue
DOING... */
multistate(easy, CURLM_STATE_DOING);
result = CURLM_OK;
}
/* after DO, go DO_DONE... or DO_MORE */
else if(easy->easy_conn->bits.do_more) {
/* we're supposed to do more, but we need to sit down, relax
and wait a little while first */
multistate(easy, CURLM_STATE_DO_MORE);
result = CURLM_OK;
}
else {
/* we're done with the DO, now DO_DONE */
multistate(easy, CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
}
else {
/* failure detected */
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result, FALSE);
disconnect_conn = TRUE;
}
}
break;
case CURLM_STATE_DOING:
/* we continue DOING until the DO phase is complete */
easy->result = Curl_protocol_doing(easy->easy_conn,
&dophase_done);
if(CURLE_OK == easy->result) {
if(dophase_done) {
/* after DO, go PERFORM... or DO_MORE */
if(easy->easy_conn->bits.do_more) {
/* we're supposed to do more, but we need to sit down, relax
and wait a little while first */
multistate(easy, CURLM_STATE_DO_MORE);
result = CURLM_OK;
}
else {
/* we're done with the DO, now DO_DONE */
multistate(easy, CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
} /* dophase_done */
}
else {
/* failure detected */
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result, FALSE);
disconnect_conn = TRUE;
}
break;
case CURLM_STATE_DO_MORE:
/* Ready to do more? */
easy->result = Curl_is_connected(easy->easy_conn,
SECONDARYSOCKET,
&connected);
if(connected) {
/*
* When we are connected, DO MORE and then go DO_DONE
*/
easy->result = Curl_do_more(easy->easy_conn);
/* No need to remove ourselves from the send pipeline here since that
is done for us in Curl_done() */
if(CURLE_OK == easy->result) {
multistate(easy, CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
else {
/* failure detected */
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result, FALSE);
disconnect_conn = TRUE;
}
}
break;
case CURLM_STATE_DO_DONE:
/* Move ourselves from the send to recv pipeline */
moveHandleFromSendToRecvPipeline(easy->easy_handle, easy->easy_conn);
/* Check if we can move pending requests to send pipe */
checkPendPipeline(easy->easy_conn);
multistate(easy, CURLM_STATE_WAITPERFORM);
result = CURLM_CALL_MULTI_PERFORM;
break;
case CURLM_STATE_WAITPERFORM:
#ifdef CURLDEBUG
infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n",
easy->easy_conn->connectindex,
easy->easy_conn->recv_pipe->size,
easy->easy_conn->readchannel_inuse,
isHandleAtHead(easy->easy_handle,
easy->easy_conn->recv_pipe));
#endif
/* Wait for our turn to PERFORM */
if(!easy->easy_conn->readchannel_inuse &&
isHandleAtHead(easy->easy_handle,
easy->easy_conn->recv_pipe)) {
/* Grab the channel */
easy->easy_conn->readchannel_inuse = TRUE;
multistate(easy, CURLM_STATE_PERFORM);
result = CURLM_CALL_MULTI_PERFORM;
}
break;
case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
/* if both rates are within spec, resume transfer */
Curl_pgrsUpdate(easy->easy_conn);
if( ( ( easy->easy_handle->set.max_send_speed == 0 ) ||
( easy->easy_handle->progress.ulspeed <
easy->easy_handle->set.max_send_speed ) ) &&
( ( easy->easy_handle->set.max_recv_speed == 0 ) ||
( easy->easy_handle->progress.dlspeed <
easy->easy_handle->set.max_recv_speed ) )
)
multistate(easy, CURLM_STATE_PERFORM);
break;
case CURLM_STATE_PERFORM:
/* check if over speed */
if( ( ( easy->easy_handle->set.max_send_speed > 0 ) &&
( easy->easy_handle->progress.ulspeed >
easy->easy_handle->set.max_send_speed ) ) ||
( ( easy->easy_handle->set.max_recv_speed > 0 ) &&
( easy->easy_handle->progress.dlspeed >
easy->easy_handle->set.max_recv_speed ) )
) {
/* Transfer is over the speed limit. Change state. TODO: Call
* Curl_expire() with the time left until we're targeted to be below
* the speed limit again. */
multistate(easy, CURLM_STATE_TOOFAST );
break;
}
/* read/write data if it is ready to do so */
easy->result = Curl_readwrite(easy->easy_conn, &done);
k = &easy->easy_handle->req;
if(!(k->keepon & KEEP_READ)) {
/* We're done reading */
easy->easy_conn->readchannel_inuse = FALSE;
}
if(!(k->keepon & KEEP_WRITE)) {
/* We're done writing */
easy->easy_conn->writechannel_inuse = FALSE;
}
if(easy->result) {
/* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is because we can't
* possibly know if the connection is in a good shape or not now. */
easy->easy_conn->bits.close = TRUE;
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
/* if we failed anywhere, we must clean up the secondary socket if
it was used */
sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
Curl_posttransfer(easy->easy_handle);
Curl_done(&easy->easy_conn, easy->result, FALSE);
}
else if(TRUE == done) {
char *newurl;
bool retry = Curl_retry_request(easy->easy_conn, &newurl);
followtype follow=FOLLOW_NONE;
/* call this even if the readwrite function returned error */
Curl_posttransfer(easy->easy_handle);
/* we're no longer receving */
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
/* expire the new receiving pipeline head */
if(easy->easy_conn->recv_pipe->head)
Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);
/* Check if we can move pending requests to send pipe */
checkPendPipeline(easy->easy_conn);
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
if(easy->easy_handle->req.newurl || retry) {
if(!retry) {
/* if the URL is a follow-location and not just a retried request
then figure out the URL here */
newurl = easy->easy_handle->req.newurl;
easy->easy_handle->req.newurl = NULL;
follow = FOLLOW_REDIR;
}
else
follow = FOLLOW_RETRY;
easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
if(easy->result == CURLE_OK)
easy->result = Curl_follow(easy->easy_handle, newurl, follow);
if(CURLE_OK == easy->result) {
multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
}
else
/* Since we "took it", we are in charge of freeing this on
failure */
free(newurl);
}
else {
/* after the transfer is done, go DONE */
/* but first check to see if we got a location info even though we're
not following redirects */
if (easy->easy_handle->req.location) {
newurl = easy->easy_handle->req.location;
easy->easy_handle->req.location = NULL;
easy->result = Curl_follow(easy->easy_handle, newurl, FOLLOW_FAKE);
if (easy->result)
free(newurl);
break;
}
multistate(easy, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
}
break;
case CURLM_STATE_DONE:
/* Remove ourselves from the receive pipeline */
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
checkPendPipeline(easy->easy_conn);
if(easy->easy_conn->bits.stream_was_rewound) {
/* This request read past its response boundary so we quickly let the
other requests consume those bytes since there is no guarantee that
the socket will become active again */
result = CURLM_CALL_MULTI_PERFORM;
}
/* post-transfer command */
easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
/* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the Curl_done() returned! */
multistate(easy, CURLM_STATE_COMPLETED);
break;
case CURLM_STATE_COMPLETED:
/* this is a completed transfer, it is likely to still be connected */
/* This node should be delinked from the list now and we should post
an information message that we are complete. */
/* Important: reset the conn pointer so that we don't point to memory
that could be freed anytime */
easy->easy_conn = NULL;
break;
default:
return CURLM_INTERNAL_ERROR;
}
if(CURLM_STATE_COMPLETED != easy->state) {
if(CURLE_OK != easy->result) {
/*
* If an error was returned, and we aren't in completed state now,
* then we go to completed and consider this transfer aborted.
*/
/* NOTE: no attempt to disconnect connections must be made
in the case blocks above - cleanup happens only here */
easy->easy_handle->state.pipe_broke = FALSE;
if(easy->easy_conn) {
/* if this has a connection, unsubscribe from the pipelines */
easy->easy_conn->writechannel_inuse = FALSE;
easy->easy_conn->readchannel_inuse = FALSE;
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->send_pipe);
Curl_removeHandleFromPipeline(easy->easy_handle,
easy->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
checkPendPipeline(easy->easy_conn);
}
if(disconnect_conn) {
Curl_disconnect(easy->easy_conn); /* disconnect properly */
/* This is where we make sure that the easy_conn pointer is reset.
We don't have to do this in every case block above where a
failure is detected */
easy->easy_conn = NULL;
}
multistate(easy, CURLM_STATE_COMPLETED);
}
}
} while(0);
if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
/* now add a node to the Curl_message linked list with this info */
msg = malloc(sizeof(struct Curl_message));
if(!msg)
return CURLM_OUT_OF_MEMORY;
msg->extmsg.msg = CURLMSG_DONE;
msg->extmsg.easy_handle = easy->easy_handle;
msg->extmsg.data.result = easy->result;
msg->next = NULL;
easy->msg = msg;
easy->msg_num = 1; /* there is one unread message here */
multi->num_msgs++; /* increase message counter */
}
if(CURLM_CALL_MULTI_PERFORM == result)
/* Set the timeout for this handle to expire really soon so that it will
be taken care of even when this handle is added in the midst of
operation when only the curl_multi_socket() API is used. During that
flow, only sockets that time-out or have actions will be dealt
with. Since this handle has no action yet, we make sure it times out to
get things to happen. Also, this makes it less important for callers of
the curl_multi_* functions to bother about the CURLM_CALL_MULTI_PERFORM
return code, as long as they deal with the timeouts properly. */
Curl_expire(easy->easy_handle, 1);
return result;
}
|
|||||
| ↓ | Curl_proxyCONNECT | 74 | 212 | 443 | lib/http.c |
CURLcode Curl_proxyCONNECT(struct connectdata *conn,
int sockindex,
const char *hostname,
unsigned short remote_port)
{
int subversion=0;
struct SessionHandle *data=conn->data;
struct SingleRequest *k = &data->req;
CURLcode result;
int res;
long timeout =
data->set.timeout?data->set.timeout:PROXY_TIMEOUT; /* in milliseconds */
curl_socket_t tunnelsocket = conn->sock[sockindex];
curl_off_t cl=0;
bool closeConnection = FALSE;
bool chunked_encoding = FALSE;
long check;
#define SELECT_OK 0
#define SELECT_ERROR 1
#define SELECT_TIMEOUT 2
int error = SELECT_OK;
conn->bits.proxy_connect_closed = FALSE;
do {
if(!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
char *host_port;
send_buffer *req_buffer;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port);
if(data->req.newurl) {
/* This only happens if we've looped here due to authentication
reasons, and we don't really use the newly cloned URL here
then. Just free() it. */
free(data->req.newurl);
data->req.newurl = NULL;
}
/* initialize a dynamic send-buffer */
req_buffer = add_buffer_init();
if(!req_buffer)
return CURLE_OUT_OF_MEMORY;
host_port = aprintf("%s:%d", hostname, remote_port);
if(!host_port) {
free(req_buffer);
return CURLE_OUT_OF_MEMORY;
}
/* Setup the proxy-authorization header, if any */
result = http_output_auth(conn, "CONNECT", host_port, TRUE);
if(CURLE_OK == result) {
char *host=(char *)"";
const char *proxyconn="";
const char *useragent="";
if(!checkheaders(data, "Host:")) {
host = aprintf("Host: %s\r\n", host_port);
if(!host) {
free(req_buffer);
free(host_port);
return CURLE_OUT_OF_MEMORY;
}
}
if(!checkheaders(data, "Proxy-Connection:"))
proxyconn = "Proxy-Connection: Keep-Alive\r\n";
if(!checkheaders(data, "User-Agent:") &&
data->set.str[STRING_USERAGENT])
useragent = conn->allocptr.uagent;
/* Send the connect request to the proxy */
/* BLOCKING */
result =
add_bufferf(req_buffer,
"CONNECT %s:%d HTTP/1.0\r\n"
"%s" /* Host: */
"%s" /* Proxy-Authorization */
"%s" /* User-Agent */
"%s", /* Proxy-Connection */
hostname, remote_port,
host,
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
useragent,
proxyconn);
if(host && *host)
free(host);
if(CURLE_OK == result)
result = add_custom_headers(conn, req_buffer);
if(CURLE_OK == result)
/* CRLF terminate the request */
result = add_bufferf(req_buffer, "\r\n");
if(CURLE_OK == result) {
/* Now send off the request */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, sockindex);
}
req_buffer = NULL;
if(result)
failf(data, "Failed sending CONNECT to proxy");
}
free(host_port);
Curl_safefree(req_buffer);
if(result)
return result;
conn->bits.tunnel_connecting = TRUE;
} /* END CONNECT PHASE */
/* now we've issued the CONNECT and we're waiting to hear back -
we try not to block here in multi-mode because that might be a LONG
wait if the proxy cannot connect-through to the remote host. */
/* if timeout is requested, find out how much remaining time we have */
check = timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
if(check <=0 ) {
failf(data, "Proxy CONNECT aborted due to timeout");
error = SELECT_TIMEOUT; /* already too little time */
break;
}
/* if we're in multi-mode and we would block, return instead for a retry */
if(Curl_if_multi == data->state.used_interface) {
if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
/* return so we'll be called again polling-style */
return CURLE_OK;
else {
DEBUGF(infof(data,
"Multi mode finished polling for response from "
"proxy CONNECT."));
}
}
else {
DEBUGF(infof(data, "Easy mode waiting for response from proxy CONNECT."));
}
/* at this point, either:
1) we're in easy-mode and so it's okay to block waiting for a CONNECT
response
2) we're in multi-mode and we didn't block - it's either an error or we
now have some data waiting.
In any case, the tunnel_connecting phase is over. */
conn->bits.tunnel_connecting = FALSE;
{ /* BEGIN NEGOTIATION PHASE */
size_t nread; /* total size read */
int perline; /* count bytes per line */
int keepon=TRUE;
ssize_t gotbytes;
char *ptr;
char *line_start;
ptr=data->state.buffer;
line_start = ptr;
nread=0;
perline=0;
keepon=TRUE;
while((nread
|
|||||
| ↓ | readwrite_data | 73 | 127 | 319 | lib/transfer.c |
static CURLcode readwrite_data(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat, bool *done)
{
CURLcode result = CURLE_OK;
ssize_t nread; /* number of bytes read */
bool is_empty_data = FALSE;
/* This is where we loop until we have read everything there is to
read or we get a EWOULDBLOCK */
do {
size_t buffersize = data->set.buffer_size?
data->set.buffer_size : BUFSIZE;
size_t bytestoread = buffersize;
int readrc;
if(k->size != -1 && !k->header) {
/* make sure we don't read "too much" if we can help it since we
might be pipelining and then someone else might want to read what
follows! */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
bytestoread = (size_t)totalleft;
}
if(bytestoread) {
/* receive data from the network! */
readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
/* subzero, this would've blocked */
if(0 > readrc)
break; /* get out of loop */
/* get the CURLcode from the int */
result = (CURLcode)readrc;
if(result>0)
return result;
}
else {
/* read nothing but since we wanted nothing we consider this an OK
situation to proceed from */
nread = 0;
}
if((k->bytecount == 0) && (k->writebytecount == 0)) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->exp100 > EXP100_SEND_DATA)
/* set time stamp to compare with when waiting for the 100 */
k->start100 = Curl_tvnow();
}
*didwhat |= KEEP_READ;
/* indicates data of zero size, i.e. empty file */
is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
/* NUL terminate, allowing string ops to be used */
if(0 < nread || is_empty_data) {
k->buf[nread] = 0;
}
else if(0 >= nread) {
/* if we receive 0 or less here, the server closed the connection
and we bail out from this! */
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
k->keepon &= ~KEEP_READ;
break;
}
/* Default buffer to use when we write the buffer, it may be changed
in the flow below before the actual storing is done. */
k->str = k->buf;
/* Since this is a two-state thing, we check if we are parsing
headers at the moment or not. */
if(k->header) {
/* we are in parse-the-header-mode */
bool stop_reading = FALSE;
result = readwrite_headers(data, conn, k, &nread, &stop_reading);
if(result)
return result;
if(stop_reading)
/* We've stopped dealing with input, get out of the do-while loop */
break;
}
/* This is not an 'else if' since it may be a rest from the header
parsing, where the beginning of the buffer is headers and the end
is non-headers. */
if(k->str && !k->header && (nread > 0 || is_empty_data)) {
if(0 == k->bodywrites && !is_empty_data) {
/* These checks are only made the first time we are about to
write a piece of the body */
if(conn->protocol&PROT_HTTP) {
/* HTTP-only checks */
if(data->req.newurl) {
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
and we're set to close anyway. */
k->keepon &= ~KEEP_READ;
*done = TRUE;
return CURLE_OK;
}
/* We have a new url to load, but since we want to be able
to re-use this connection properly, we read the full
response in "ignore more" */
k->ignorebody = TRUE;
infof(data, "Ignoring the response-body\n");
}
if(data->state.resume_from && !k->content_range &&
(data->set.httpreq==HTTPREQ_GET) &&
!k->ignorebody) {
/* we wanted to resume a download, although the server doesn't
* seem to support this and we did this with a GET (if it
* wasn't a GET we did a POST or PUT resume) */
failf(data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_RANGE_ERROR;
}
if(data->set.timecondition && !data->state.range) {
/* A time condition has been set AND no ranges have been
requested. This seems to be what chapter 13.3.4 of
RFC 2616 defines to be the correct action for a
HTTP/1.1 client */
if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
if(k->timeofdoc < data->set.timevalue) {
infof(data,
"The requested document is not new enough\n");
*done = TRUE;
return CURLE_OK;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(k->timeofdoc > data->set.timevalue) {
infof(data,
"The requested document is not old enough\n");
*done = TRUE;
return CURLE_OK;
}
break;
} /* switch */
} /* two valid time strings */
} /* we have a time condition */
} /* this is HTTP */
} /* this is the first time we write a body part */
k->bodywrites++;
/* pass data to the debug function before it gets "dechunked" */
if(data->set.verbose) {
if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
(size_t)k->hbuflen, conn);
if(k->badheader == HEADER_PARTHEADER)
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
else
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
#ifndef CURL_DISABLE_HTTP
if(k->chunk) {
/*
* Here comes a chunked transfer flying and we need to decode this
* properly. While the name says read, this function both reads
* and writes away the data. The returned 'nread' holds the number
* of actual data it wrote to the client.
*/
CHUNKcode res =
Curl_httpchunk_read(conn, k->str, nread, &nread);
if(CHUNKE_OK < res) {
if(CHUNKE_WRITE_ERROR == res) {
failf(data, "Failed writing data");
return CURLE_WRITE_ERROR;
}
failf(data, "Received problem %d in the chunky parser", res);
return CURLE_RECV_ERROR;
}
else if(CHUNKE_STOP == res) {
size_t dataleft;
/* we're done reading chunks! */
k->keepon &= ~KEEP_READ; /* read no more */
/* There are now possibly N number of bytes at the end of the
str buffer that weren't written to the client.
We DO care about this data if we are pipelining.
Push it back to be read on the next pass. */
dataleft = conn->chunk.dataleft;
if(dataleft != 0) {
infof(conn->data, "Leftovers after chunking. "
" Rewinding %d bytes\n",dataleft);
read_rewind(conn, dataleft);
}
}
/* If it returned OK, we just keep going */
}
#endif /* CURL_DISABLE_HTTP */
if((-1 != k->maxdownload) &&
(k->bytecount + nread >= k->maxdownload)) {
/* The 'excess' amount below can't be more than BUFSIZE which
always will fit in a size_t */
size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
infof(data,
"Rewinding stream by : %d"
" bytes on url %s (size = %" FORMAT_OFF_T
", maxdownload = %" FORMAT_OFF_T
", bytecount = %" FORMAT_OFF_T ", nread = %d)\n",
excess, data->state.path,
k->size, k->maxdownload, k->bytecount, nread);
read_rewind(conn, excess);
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
if(nread < 0 ) /* this should be unusual */
nread = 0;
k->keepon &= ~KEEP_READ; /* we're done reading */
}
k->bytecount += nread;
Curl_pgrsSetDownloadCounter(data, k->bytecount);
if(!k->chunk && (nread || k->badheader || is_empty_data)) {
/* If this is chunky transfer, it was already written */
if(k->badheader && !k->ignorebody) {
/* we parsed a piece of data wrongly assuming it was a header
and now we output it as body instead */
result = Curl_client_write(conn, CLIENTWRITE_BODY,
data->state.headerbuff,
k->hbuflen);
if(result)
return result;
}
if(k->badheader < HEADER_ALLBAD) {
/* This switch handles various content encodings. If there's an
error here, be sure to check over the almost identical code
in http_chunks.c.
Make sure that ALL_CONTENT_ENCODINGS contains all the
encodings handled here. */
#ifdef HAVE_LIBZ
switch (conn->data->set.http_ce_skip ?
IDENTITY : k->content_encoding) {
case IDENTITY:
#endif
/* This is the default when the server sends no
Content-Encoding header. See Curl_readwrite_init; the
memset() call initializes k->content_encoding to zero. */
if(!k->ignorebody)
result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
nread);
#ifdef HAVE_LIBZ
break;
case DEFLATE:
/* Assume CLIENTWRITE_BODY; headers are not encoded. */
if(!k->ignorebody)
result = Curl_unencode_deflate_write(conn, k, nread);
break;
case GZIP:
/* Assume CLIENTWRITE_BODY; headers are not encoded. */
if(!k->ignorebody)
result = Curl_unencode_gzip_write(conn, k, nread);
break;
case COMPRESS:
default:
failf (data, "Unrecognized content encoding type. "
"libcurl understands `identity', `deflate' and `gzip' "
"content encodings.");
result = CURLE_BAD_CONTENT_ENCODING;
break;
}
#endif
}
k->badheader = HEADER_NORMAL; /* taken care of now */
if(result)
return result;
}
} /* if(! header and data to read ) */
if(is_empty_data) {
/* if we received nothing, the server closed the connection and we
are done */
k->keepon &= ~KEEP_READ;
}
} while(data_pending(conn));
if(((k->keepon & (KEEP_READ|KEEP_WRITE)) == KEEP_WRITE) &&
conn->bits.close ) {
/* When we've read the entire thing and the close bit is set, the server
may now close the connection. If there's now any kind of sending going
on from our side, we need to stop that immediately. */
infof(data, "we are done reading and this is set to close, stop send\n");
k->keepon &= ~KEEP_WRITE; /* no writing anymore either */
}
return CURLE_OK;
}
|
|||||
| ↓ | ftp_state_use_port | 60 | 195 | 387 | lib/ftp.c |
static CURLcode ftp_state_use_port(struct connectdata *conn,
ftpport fcmd) /* start with this */
{
CURLcode result = CURLE_OK;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct SessionHandle *data=conn->data;
curl_socket_t portsock= CURL_SOCKET_BAD;
char myhost[256] = "";
#ifdef ENABLE_IPV6
/******************************************************************
* IPv6-specific section
*/
struct Curl_sockaddr_storage ss;
struct addrinfo *res, *ai;
socklen_t sslen;
char hbuf[NI_MAXHOST];
struct sockaddr *sa=(struct sockaddr *)&ss;
char tmp[1024];
static const char * const mode[] = { "EPRT", "PORT", NULL };
int rc;
int error;
char *host=NULL;
struct Curl_dns_entry *h=NULL;
unsigned short port = 0;
/* Step 1, figure out what address that is requested */
if(data->set.str[STRING_FTPPORT] &&
(strlen(data->set.str[STRING_FTPPORT]) > 1)) {
/* attempt to get the address of the given interface name */
if(!Curl_if2ip(data->set.str[STRING_FTPPORT], hbuf, sizeof(hbuf)))
/* not an interface, use the given string as host name instead */
host = data->set.str[STRING_FTPPORT];
else
host = hbuf; /* use the hbuf for host name */
} /* data->set.ftpport */
if(!host) {
/* not an interface and not a host name, get default by extracting
the IP from the control connection */
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
return CURLE_FTP_PORT_FAILED;
}
if(sslen > (socklen_t)sizeof(ss))
sslen = sizeof(ss);
rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
0, NIFLAGS);
if(rc) {
failf(data, "getnameinfo() returned %d", rc);
return CURLE_FTP_PORT_FAILED;
}
host = hbuf; /* use this host name */
}
rc = Curl_resolv(conn, host, 0, &h);
if(rc == CURLRESOLV_PENDING)
rc = Curl_wait_for_resolv(conn, &h);
if(h) {
res = h->addr;
/* when we return from this function, we can forget about this entry
to we can unlock it now already */
Curl_resolv_unlock(data, h);
} /* (h) */
else
res = NULL; /* failure! */
/* step 2, create a socket for the requested address */
portsock = CURL_SOCKET_BAD;
error = 0;
for (ai = res; ai; ai = ai->ai_next) {
/*
* Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
*/
if(ai->ai_socktype == 0)
ai->ai_socktype = conn->socktype;
portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(portsock == CURL_SOCKET_BAD) {
error = SOCKERRNO;
continue;
}
break;
}
if(!ai) {
failf(data, "socket failure: %s", Curl_strerror(conn, error));
return CURLE_FTP_PORT_FAILED;
}
/* step 3, bind to a suitable local address */
/* Try binding the given address. */
if(bind(portsock, ai->ai_addr, ai->ai_addrlen)) {
/* It failed. Bind the address used for the control connection instead */
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
}
/* set port number to zero to make bind() pick "any" */
if(((struct sockaddr *)sa)->sa_family == AF_INET)
((struct sockaddr_in *)sa)->sin_port=0;
else
((struct sockaddr_in6 *)sa)->sin6_port =0;
if(sslen > (socklen_t)sizeof(ss))
sslen = sizeof(ss);
if(bind(portsock, (struct sockaddr *)sa, sslen)) {
failf(data, "bind failed: %s", Curl_strerror(conn, SOCKERRNO));
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
}
}
/* get the name again after the bind() so that we can extract the
port number it uses now */
sslen = sizeof(ss);
if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
}
/* step 4, listen on the socket */
if(listen(portsock, 1)) {
failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
}
/* step 5, send the proper FTP command */
/* get a plain printable version of the numerical address to work with
below */
Curl_printable_address(ai, myhost, sizeof(myhost));
#ifdef PF_INET6
if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
/* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
request and enable EPRT again! */
conn->bits.ftp_use_eprt = TRUE;
#endif
for (; fcmd != DONE; fcmd++) {
if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
/* if disabled, goto next */
continue;
switch (sa->sa_family) {
case AF_INET:
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
break;
case AF_INET6:
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
break;
default:
break;
}
if(EPRT == fcmd) {
/*
* Two fine examples from RFC2428;
*
* EPRT |1|132.235.1.2|6275|
*
* EPRT |2|1080::8:800:200C:417A|5282|
*/
result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd],
ai->ai_family == AF_INET?1:2,
myhost, port);
if(result)
return result;
break;
}
else if(PORT == fcmd) {
char *source = myhost;
char *dest = tmp;
if((PORT == fcmd) && ai->ai_family != AF_INET)
continue;
/* translate x.x.x.x to x,x,x,x */
while(source && *source) {
if(*source == '.')
*dest=',';
else
*dest = *source;
dest++;
source++;
}
*dest = 0;
snprintf(dest, 20, ",%d,%d", port>>8, port&0xff);
result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp);
if(result)
return result;
break;
}
}
/* store which command was sent */
ftpc->count1 = fcmd;
/* 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 */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = portsock;
#else
/******************************************************************
* IPv4-specific section
*/
struct sockaddr_in sa;
unsigned short porttouse;
bool sa_filled_in = FALSE;
Curl_addrinfo *addr = NULL;
unsigned short ip[4];
bool freeaddr = TRUE;
socklen_t sslen = sizeof(sa);
const char *ftpportstr = data->set.str[STRING_FTPPORT];
(void)fcmd; /* not used in the IPv4 code */
if(ftpportstr) {
in_addr_t in;
/* First check if the given name is an IP address */
in=inet_addr(ftpportstr);
if(in != CURL_INADDR_NONE)
/* this is an IPv4 address */
addr = Curl_ip2addr(in, ftpportstr, 0);
else {
if(Curl_if2ip(ftpportstr, myhost, sizeof(myhost))) {
/* The interface to IP conversion provided a dotted address */
in=inet_addr(myhost);
addr = Curl_ip2addr(in, myhost, 0);
}
else if(strlen(ftpportstr)> 1) {
/* might be a host name! */
struct Curl_dns_entry *h=NULL;
int rc = Curl_resolv(conn, ftpportstr, 0, &h);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
rc = Curl_wait_for_resolv(conn, &h);
if(h) {
addr = h->addr;
/* when we return from this function, we can forget about this entry
so we can unlock it now already */
Curl_resolv_unlock(data, h);
freeaddr = FALSE; /* make sure we don't free 'addr' in this function
since it points to a DNS cache entry! */
} /* (h) */
else {
infof(data, "Failed to resolve host name %s\n", ftpportstr);
}
} /* strlen */
} /* CURL_INADDR_NONE */
} /* ftpportstr */
if(!addr) {
/* pick a suitable default here */
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)&sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
return CURLE_FTP_PORT_FAILED;
}
if(sslen > (socklen_t)sizeof(sa))
sslen = sizeof(sa);
sa_filled_in = TRUE; /* the sa struct is filled in */
}
if(addr || sa_filled_in) {
portsock = socket(AF_INET, SOCK_STREAM, 0);
if(CURL_SOCKET_BAD != portsock) {
/* 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 */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = portsock;
if(!sa_filled_in) {
memcpy(&sa, addr->ai_addr, sslen);
sa.sin_addr.s_addr = INADDR_ANY;
}
sa.sin_port = 0;
sslen = sizeof(sa);
if(bind(portsock, (struct sockaddr *)&sa, sslen) == 0) {
/* we succeeded to bind */
struct sockaddr_in add;
socklen_t socksize = sizeof(add);
if(getsockname(portsock, (struct sockaddr *) &add,
&socksize)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
return CURLE_FTP_PORT_FAILED;
}
porttouse = ntohs(add.sin_port);
if( listen(portsock, 1) < 0 ) {
failf(data, "listen(2) failed on socket");
return CURLE_FTP_PORT_FAILED;
}
}
else {
failf(data, "bind(2) failed on socket");
return CURLE_FTP_PORT_FAILED;
}
}
else {
failf(data, "socket(2) failed (%s)");
return CURLE_FTP_PORT_FAILED;
}
}
else {
failf(data, "couldn't find IP address to use");
return CURLE_FTP_PORT_FAILED;
}
if(sa_filled_in)
Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
myhost, sizeof(myhost));
else
Curl_printable_address(addr, myhost, sizeof(myhost));
if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3])) {
infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
ip[0], ip[1], ip[2], ip[3], porttouse);
result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
ip[0], ip[1], ip[2], ip[3],
porttouse >> 8, porttouse & 255);
if(result)
return result;
}
else
return CURLE_FTP_PORT_FAILED;
if(freeaddr)
Curl_freeaddrinfo(addr);
ftpc->count1 = PORT;
#endif /* end of ipv4-specific code */
/* this tcpconnect assignment below is a hackish work-around to make the
multi interface with active FTP work - as it will not wait for a
(passive) connect in Curl_is_connected().
The *proper* fix is to make sure that the active connection from the
server is done in a non-blocking way. Currently, it is still BLOCKING.
*/
conn->bits.tcpconnect = TRUE;
state(conn, FTP_PORT);
return result;
}
|
|||||
| ↓ | Curl_setopt | 206 | 448 | 1350 | lib/url.c |
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
va_list param)
{
char *argptr;
CURLcode result = CURLE_OK;
#ifndef CURL_DISABLE_HTTP
curl_off_t bigsize;
#endif
switch(option) {
case CURLOPT_DNS_CACHE_TIMEOUT:
data->set.dns_cache_timeout = va_arg(param, long);
break;
case CURLOPT_DNS_USE_GLOBAL_CACHE:
{
/* remember we want this enabled */
long use_cache = va_arg(param, long);
data->set.global_dns_cache = (bool)(0 != use_cache);
}
break;
case CURLOPT_SSL_CIPHER_LIST:
/* set a list of cipher we want to use in the SSL connection */
result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST],
va_arg(param, char *));
break;
case CURLOPT_RANDOM_FILE:
/*
* This is the path name to a file that contains random data to seed
* the random SSL stuff with. The file is only used for reading.
*/
result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE],
va_arg(param, char *));
break;
case CURLOPT_EGDSOCKET:
/*
* The Entropy Gathering Daemon socket pathname
*/
result = setstropt(&data->set.str[STRING_SSL_EGDSOCKET],
va_arg(param, char *));
break;
case CURLOPT_MAXCONNECTS:
/*
* Set the absolute number of maximum simultaneous alive connection that
* libcurl is allowed to have.
*/
result = Curl_ch_connc(data, data->state.connc, va_arg(param, long));
break;
case CURLOPT_FORBID_REUSE:
/*
* When this transfer is done, it must not be left to be reused by a
* subsequent transfer but shall be closed immediately.
*/
data->set.reuse_forbid = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FRESH_CONNECT:
/*
* This transfer shall not use a previously cached connection but
* should be made with a fresh new connect!
*/
data->set.reuse_fresh = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_VERBOSE:
/*
* Verbose means infof() calls that give a lot of information about
* the connection and transfer procedures as well as internal choices.
*/
data->set.verbose = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_HEADER:
/*
* Set to include the header in the general data output stream.
*/
data->set.include_header = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_NOPROGRESS:
/*
* Shut off the internal supported progress meter
*/
data->set.hide_progress = (bool)(0 != va_arg(param, long));
if(data->set.hide_progress)
data->progress.flags |= PGRS_HIDE;
else
data->progress.flags &= ~PGRS_HIDE;
break;
case CURLOPT_NOBODY:
/*
* Do not include the body part in the output data stream.
*/
data->set.opt_no_body = (bool)(0 != va_arg(param, long));
/* in HTTP lingo, no body means using the HEAD request and if unset there
really is no perfect method that is the "opposite" of HEAD but in
reality most people probably think GET then. The important thing is
that we can't let it remain HEAD if the opt_no_body is set FALSE since
then we'll behave wrong when getting HTTP. */
data->set.httpreq = data->set.opt_no_body?HTTPREQ_HEAD:HTTPREQ_GET;
break;
case CURLOPT_FAILONERROR:
/*
* Don't output the >=300 error code HTML-page, but instead only
* return error.
*/
data->set.http_fail_on_error = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_UPLOAD:
case CURLOPT_PUT:
/*
* We want to sent data to the remote host. If this is HTTP, that equals
* using the PUT request.
*/
data->set.upload = (bool)(0 != va_arg(param, long));
if(data->set.upload)
/* If this is HTTP, PUT is what's needed to "upload" */
data->set.httpreq = HTTPREQ_PUT;
else
/* In HTTP, the opposite of upload is either GET or a HEAD */
data->set.httpreq = data->set.opt_no_body?HTTPREQ_HEAD:HTTPREQ_GET;
break;
case CURLOPT_FILETIME:
/*
* Try to get the file time of the remote document. The time will
* later (possibly) become available using curl_easy_getinfo().
*/
data->set.get_filetime = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_CREATE_MISSING_DIRS:
/*
* An FTP option that modifies an upload to create missing directories on
* the server.
*/
data->set.ftp_create_missing_dirs = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_RESPONSE_TIMEOUT:
/*
* An FTP option that specifies how quickly an FTP response must be
* obtained before it is considered failure.
*/
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
break;
case CURLOPT_DIRLISTONLY:
/*
* An option that changes the command to one that asks for a list
* only, no file info details.
*/
data->set.ftp_list_only = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_APPEND:
/*
* We want to upload and append to an existing file.
*/
data->set.ftp_append = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_FILEMETHOD:
/*
* How do access files over FTP.
*/
data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
break;
case CURLOPT_NETRC:
/*
* Parse the $HOME/.netrc file
*/
data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
break;
case CURLOPT_NETRC_FILE:
/*
* Use this file instead of the $HOME/.netrc file
*/
result = setstropt(&data->set.str[STRING_NETRC_FILE],
va_arg(param, char *));
break;
case CURLOPT_TRANSFERTEXT:
/*
* This option was previously named 'FTPASCII'. Renamed to work with
* more protocols than merely FTP.
*
* Transfer using ASCII (instead of BINARY).
*/
data->set.prefer_ascii = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_TIMECONDITION:
/*
* Set HTTP time condition. This must be one of the defines in the
* curl/curl.h header file.
*/
data->set.timecondition = (curl_TimeCond)va_arg(param, long);
break;
case CURLOPT_TIMEVALUE:
/*
* This is the value to compare with the remote document with the
* method set with CURLOPT_TIMECONDITION
*/
data->set.timevalue = (time_t)va_arg(param, long);
break;
case CURLOPT_SSLVERSION:
/*
* Set explicit SSL version to try to connect with, as some SSL
* implementations are lame.
*/
data->set.ssl.version = va_arg(param, long);
break;
#ifndef CURL_DISABLE_HTTP
case CURLOPT_AUTOREFERER:
/*
* Switch on automatic referer that gets set if curl follows locations.
*/
data->set.http_auto_referer = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_ENCODING:
/*
* String to use at the value of Accept-Encoding header.
*
* If the encoding is set to "" we use an Accept-Encoding header that
* encompasses all the encodings we support.
* If the encoding is set to NULL we don't send an Accept-Encoding header
* and ignore an received Content-Encoding header.
*
*/
argptr = va_arg(param, char *);
result = setstropt(&data->set.str[STRING_ENCODING],
(argptr && !*argptr)?
(char *) ALL_CONTENT_ENCODINGS: argptr);
break;
case CURLOPT_FOLLOWLOCATION:
/*
* Follow Location: header hints on a HTTP-server.
*/
data->set.http_follow_location = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_UNRESTRICTED_AUTH:
/*
* Send authentication (user+password) when following locations, even when
* hostname changed.
*/
data->set.http_disable_hostname_check_before_authentication =
(bool)(0 != va_arg(param, long));
break;
case CURLOPT_MAXREDIRS:
/*
* The maximum amount of hops you allow curl to follow Location:
* headers. This should mostly be used to detect never-ending loops.
*/
data->set.maxredirs = va_arg(param, long);
break;
case CURLOPT_POSTREDIR:
{
/*
* Set the behaviour of POST when redirecting
* CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302
* CURL_REDIR_POST_301 - POST is kept as POST after 301
* CURL_REDIR_POST_302 - POST is kept as POST after 302
* CURL_REDIR_POST_ALL - POST is kept as POST after 301 and 302
* other - POST is kept as POST after 301 and 302
*/
long postRedir = va_arg(param, long);
data->set.post301 = (bool)((postRedir & CURL_REDIR_POST_301)?TRUE:FALSE);
data->set.post302 = (bool)((postRedir & CURL_REDIR_POST_302)?TRUE:FALSE);
}
break;
case CURLOPT_POST:
/* Does this option serve a purpose anymore? Yes it does, when
CURLOPT_POSTFIELDS isn't used and the POST data is read off the
callback! */
if(va_arg(param, long)) {
data->set.httpreq = HTTPREQ_POST;
data->set.opt_no_body = FALSE; /* this is implied */
}
else
data->set.httpreq = HTTPREQ_GET;
break;
case CURLOPT_COPYPOSTFIELDS:
/*
* A string with POST data. Makes curl HTTP POST. Even if it is NULL.
* If needed, CURLOPT_POSTFIELDSIZE must have been set prior to
* CURLOPT_COPYPOSTFIELDS and not altered later.
*/
argptr = va_arg(param, char *);
if(!argptr || data->set.postfieldsize == -1)
result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr);
else {
/*
* Check that requested length does not overflow the size_t type.
*/
if((data->set.postfieldsize < 0) ||
((sizeof(curl_off_t) != sizeof(size_t)) &&
(data->set.postfieldsize > (curl_off_t)((size_t)-1))))
result = CURLE_OUT_OF_MEMORY;
else {
char * p;
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
/* Allocate even when size == 0. This satisfies the need of possible
later address compare to detect the COPYPOSTFIELDS mode, and
to mark that postfields is used rather than read function or
form data.
*/
p = malloc((size_t)(data->set.postfieldsize?data->set.postfieldsize:1));
if(!p)
result = CURLE_OUT_OF_MEMORY;
else {
if(data->set.postfieldsize)
memcpy(p, argptr, (size_t)data->set.postfieldsize);
data->set.str[STRING_COPYPOSTFIELDS] = p;
}
}
}
data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
data->set.httpreq = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDS:
/*
* Like above, but use static data instead of copying it.
*/
data->set.postfields = va_arg(param, void *);
/* Release old copied data. */
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.httpreq = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDSIZE:
/*
* The size of the POSTFIELD data to prevent libcurl to do strlen() to
* figure it out. Enables binary posts.
*/
bigsize = va_arg(param, long);
if(data->set.postfieldsize < bigsize &&
data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
/* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.postfields = NULL;
}
data->set.postfieldsize = bigsize;
break;
case CURLOPT_POSTFIELDSIZE_LARGE:
/*
* The size of the POSTFIELD data to prevent libcurl to do strlen() to
* figure it out. Enables binary posts.
*/
bigsize = va_arg(param, curl_off_t);
if(data->set.postfieldsize < bigsize &&
data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
/* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
(void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
data->set.postfields = NULL;
}
data->set.postfieldsize = bigsize;
break;
case CURLOPT_HTTPPOST:
/*
* Set to make us do HTTP POST
*/
data->set.httppost = va_arg(param, struct curl_httppost *);
data->set.httpreq = HTTPREQ_POST_FORM;
data->set.opt_no_body = FALSE; /* this is implied */
break;
case CURLOPT_REFERER:
/*
* String to set in the HTTP Referer: field.
*/
if(data->change.referer_alloc) {
free(data->change.referer);
data->change.referer_alloc = FALSE;
}
result = setstropt(&data->set.str[STRING_SET_REFERER],
va_arg(param, char *));
data->change.referer = data->set.str[STRING_SET_REFERER];
break;
case CURLOPT_USERAGENT:
/*
* String to use in the HTTP User-Agent field
*/
result = setstropt(&data->set.str[STRING_USERAGENT],
va_arg(param, char *));
break;
case CURLOPT_HTTPHEADER:
/*
* Set a list with HTTP headers to use (or replace internals with)
*/
data->set.headers = va_arg(param, struct curl_slist *);
break;
case CURLOPT_HTTP200ALIASES:
/*
* Set a list of aliases for HTTP 200 in response header
*/
data->set.http200aliases = va_arg(param, struct curl_slist *);
break;
#if !defined(CURL_DISABLE_COOKIES)
case CURLOPT_COOKIE:
/*
* Cookie string to send to the remote server in the request.
*/
result = setstropt(&data->set.str[STRING_COOKIE],
va_arg(param, char *));
break;
case CURLOPT_COOKIEFILE:
/*
* Set cookie file to read and parse. Can be used multiple times.
*/
argptr = (char *)va_arg(param, void *);
if(argptr) {
struct curl_slist *cl;
/* append the cookie file name to the list of file names, and deal with
them later */
cl = curl_slist_append(data->change.cookielist, argptr);
if(!cl)
return CURLE_OUT_OF_MEMORY;
data->change.cookielist = cl; /* store the list for later use */
}
break;
case CURLOPT_COOKIEJAR:
/*
* Set cookie file name to dump all cookies to when we're done.
*/
result = setstropt(&data->set.str[STRING_COOKIEJAR],
va_arg(param, char *));
/*
* Activate the cookie parser. This may or may not already
* have been made.
*/
data->cookies = Curl_cookie_init(data, NULL, data->cookies,
data->set.cookiesession);
break;
case CURLOPT_COOKIESESSION:
/*
* Set this option to TRUE to start a new "cookie session". It will
* prevent the forthcoming read-cookies-from-file actions to accept
* cookies that are marked as being session cookies, as they belong to a
* previous session.
*
* In the original Netscape cookie spec, "session cookies" are cookies
* with no expire date set. RFC2109 describes the same action if no
* 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
* a 'Discard' action that can enforce the discard even for cookies that
* have a Max-Age.
*
* We run mostly with the original cookie spec, as hardly anyone implements
* anything else.
*/
data->set.cookiesession = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_COOKIELIST:
argptr = va_arg(param, char *);
if(argptr == NULL)
break;
if(strequal(argptr, "ALL")) {
/* clear all cookies */
Curl_cookie_clearall(data->cookies);
break;
}
else if(strequal(argptr, "SESS")) {
/* clear session cookies */
Curl_cookie_clearsess(data->cookies);
break;
}
else if(strequal(argptr, "FLUSH")) {
/* flush cookies to file */
flush_cookies(data, 0);
break;
}
if(!data->cookies)
/* if cookie engine was not running, activate it */
data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
argptr = strdup(argptr);
if(!argptr) {
result = CURLE_OUT_OF_MEMORY;
break;
}
if(checkprefix("Set-Cookie:", argptr))
/* HTTP Header format line */
Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);
else
/* Netscape format line */
Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
free(argptr);
break;
#endif /* CURL_DISABLE_COOKIES */
case CURLOPT_HTTPGET:
/*
* Set to force us do HTTP GET
*/
if(va_arg(param, long)) {
data->set.httpreq = HTTPREQ_GET;
data->set.upload = FALSE; /* switch off upload */
data->set.opt_no_body = FALSE; /* this is implied */
}
break;
case CURLOPT_HTTP_VERSION:
/*
* This sets a requested HTTP version to be used. The value is one of
* the listed enums in curl/curl.h.
*/
data->set.httpversion = va_arg(param, long);
break;
case CURLOPT_HTTPPROXYTUNNEL:
/*
* Tunnel operations through the proxy instead of normal proxy use
*/
data->set.tunnel_thru_httpproxy = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_CUSTOMREQUEST:
/*
* Set a custom string to use as request
*/
result = setstropt(&data->set.str[STRING_CUSTOMREQUEST],
va_arg(param, char *));
/* we don't set
data->set.httpreq = HTTPREQ_CUSTOM;
here, we continue as if we were using the already set type
and this just changes the actual request keyword */
break;
case CURLOPT_PROXYPORT:
/*
* Explicitly set HTTP proxy port number.
*/
data->set.proxyport = va_arg(param, long);
break;
case CURLOPT_HTTPAUTH:
/*
* Set HTTP Authentication type BITMASK.
*/
{
long auth = va_arg(param, long);
/* switch off bits we can't support */
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
#endif
#ifndef HAVE_GSSAPI
auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
#endif
if(!auth)
return CURLE_FAILED_INIT; /* no supported types left! */
data->set.httpauth = auth;
}
break;
case CURLOPT_PROXYAUTH:
/*
* Set HTTP Authentication type BITMASK.
*/
{
long auth = va_arg(param, long);
/* switch off bits we can't support */
#ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
#endif
#ifndef HAVE_GSSAPI
auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
#endif
if(!auth)
return CURLE_FAILED_INIT; /* no supported types left! */
data->set.proxyauth = auth;
}
break;
#endif /* CURL_DISABLE_HTTP */
case CURLOPT_PROXY:
/*
* Set proxy server:port to use as HTTP proxy.
*
* If the proxy is set to "" we explicitly say that we don't want to use a
* proxy (even though there might be environment variables saying so).
*
* Setting it to NULL, means no proxy but allows the environment variables
* to decide for us.
*/
result = setstropt(&data->set.str[STRING_PROXY],
va_arg(param, char *));
break;
case CURLOPT_WRITEHEADER:
/*
* Custom pointer to pass the header write callback function
*/
data->set.writeheader = (void *)va_arg(param, void *);
break;
case CURLOPT_ERRORBUFFER:
/*
* Error buffer provided by the caller to get the human readable
* error string in.
*/
data->set.errorbuffer = va_arg(param, char *);
break;
case CURLOPT_FILE:
/*
* FILE pointer to write to or include in the data write callback
*/
data->set.out = va_arg(param, FILE *);
break;
case CURLOPT_FTPPORT:
/*
* Use FTP PORT, this also specifies which IP address to use
*/
result = setstropt(&data->set.str[STRING_FTPPORT],
va_arg(param, char *));
data->set.ftp_use_port = (bool)(NULL != data->set.str[STRING_FTPPORT]);
break;
case CURLOPT_FTP_USE_EPRT:
data->set.ftp_use_eprt = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_USE_EPSV:
data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_SSL_CCC:
data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long);
break;
case CURLOPT_FTP_SKIP_PASV_IP:
/*
* Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
* bypass of the IP address in PASV responses.
*/
data->set.ftp_skip_ip = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_INFILE:
/*
* FILE pointer to read the file to be uploaded from. Or possibly
* used as argument to the read callback.
*/
data->set.in = va_arg(param, FILE *);
break;
case CURLOPT_INFILESIZE:
/*
* If known, this should inform curl about the file size of the
* to-be-uploaded file.
*/
data->set.infilesize = va_arg(param, long);
break;
case CURLOPT_INFILESIZE_LARGE:
/*
* If known, this should inform curl about the file size of the
* to-be-uploaded file.
*/
data->set.infilesize = va_arg(param, curl_off_t);
break;
case CURLOPT_LOW_SPEED_LIMIT:
/*
* The low speed limit that if transfers are below this for
* CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
*/
data->set.low_speed_limit=va_arg(param, long);
break;
case CURLOPT_MAX_SEND_SPEED_LARGE:
/*
* The max speed limit that sends transfer more than
* CURLOPT_MAX_SEND_PER_SECOND bytes per second the transfer is
* throttled..
*/
data->set.max_send_speed=va_arg(param, curl_off_t);
break;
case CURLOPT_MAX_RECV_SPEED_LARGE:
/*
* The max speed limit that sends transfer more than
* CURLOPT_MAX_RECV_PER_SECOND bytes per second the transfer is
* throttled..
*/
data->set.max_recv_speed=va_arg(param, curl_off_t);
break;
case CURLOPT_LOW_SPEED_TIME:
/*
* The low speed time that if transfers are below the set
* CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
*/
data->set.low_speed_time=va_arg(param, long);
break;
case CURLOPT_URL:
/*
* The URL to fetch.
*/
if(data->change.url_alloc) {
/* the already set URL is allocated, free it first! */
free(data->change.url);
data->change.url_alloc=FALSE;
}
result = setstropt(&data->set.str[STRING_SET_URL],
va_arg(param, char *));
data->change.url = data->set.str[STRING_SET_URL];
break;
case CURLOPT_PORT:
/*
* The port number to use when getting the URL
*/
data->set.use_port = va_arg(param, long);
break;
case CURLOPT_TIMEOUT:
/*
* The maximum time you allow curl to use for a single transfer
* operation.
*/
data->set.timeout = va_arg(param, long) * 1000L;
break;
case CURLOPT_TIMEOUT_MS:
data->set.timeout = va_arg(param, long);
break;
case CURLOPT_CONNECTTIMEOUT:
/*
* The maximum time you allow curl to use to connect.
*/
data->set.connecttimeout = va_arg(param, long) * 1000L;
break;
case CURLOPT_CONNECTTIMEOUT_MS:
data->set.connecttimeout = va_arg(param, long);
break;
case CURLOPT_USERPWD:
/*
* user:password to use in the operation
*/
result = setstropt(&data->set.str[STRING_USERPWD],
va_arg(param, char *));
break;
case CURLOPT_POSTQUOTE:
/*
* List of RAW FTP commands to use after a transfer
*/
data->set.postquote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_PREQUOTE:
/*
* List of RAW FTP commands to use prior to RETR (Wesley Laxton)
*/
data->set.prequote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_QUOTE:
/*
* List of RAW FTP commands to use before a transfer
*/
data->set.quote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_PROGRESSFUNCTION:
/*
* Progress callback function
*/
data->set.fprogress = va_arg(param, curl_progress_callback);
if(data->set.fprogress)
data->progress.callback = TRUE; /* no longer internal */
else
data->progress.callback = FALSE; /* NULL enforces internal */
break;
case CURLOPT_PROGRESSDATA:
/*
* Custom client data to pass to the progress callback
*/
data->set.progress_client = va_arg(param, void *);
break;
case CURLOPT_PROXYUSERPWD:
/*
* user:password needed to use the proxy
*/
result = setstropt(&data->set.str[STRING_PROXYUSERPWD],
va_arg(param, char *));
break;
case CURLOPT_RANGE:
/*
* What range of the file you want to transfer
*/
result = setstropt(&data->set.str[STRING_SET_RANGE],
va_arg(param, char *));
break;
case CURLOPT_RESUME_FROM:
/*
* Resume transfer at the give file position
*/
data->set.set_resume_from = va_arg(param, long);
break;
case CURLOPT_RESUME_FROM_LARGE:
/*
* Resume transfer at the give file position
*/
data->set.set_resume_from = va_arg(param, curl_off_t);
break;
case CURLOPT_DEBUGFUNCTION:
/*
* stderr write callback.
*/
data->set.fdebug = va_arg(param, curl_debug_callback);
/*
* if the callback provided is NULL, it'll use the default callback
*/
break;
case CURLOPT_DEBUGDATA:
/*
* Set to a void * that should receive all error writes. This
* defaults to CURLOPT_STDERR for normal operations.
*/
data->set.debugdata = va_arg(param, void *);
break;
case CURLOPT_STDERR:
/*
* Set to a FILE * that should receive all error writes. This
* defaults to stderr for normal operations.
*/
data->set.err = va_arg(param, FILE *);
if(!data->set.err)
data->set.err = stderr;
break;
case CURLOPT_HEADERFUNCTION:
/*
* Set header write callback
*/
data->set.fwrite_header = va_arg(param, curl_write_callback);
break;
case CURLOPT_WRITEFUNCTION:
/*
* Set data write callback
*/
data->set.fwrite_func = va_arg(param, curl_write_callback);
if(!data->set.fwrite_func)
/* When set to NULL, reset to our internal default function */
data->set.fwrite_func = (curl_write_callback)fwrite;
break;
case CURLOPT_READFUNCTION:
/*
* Read data callback
*/
data->set.fread_func = va_arg(param, curl_read_callback);
if(!data->set.fread_func)
/* When set to NULL, reset to our internal default function */
data->set.fread_func = (curl_read_callback)fread;
break;
case CURLOPT_SEEKFUNCTION:
/*
* Seek callback. Might be NULL.
*/
data->set.seek_func = va_arg(param, curl_seek_callback);
break;
case CURLOPT_SEEKDATA:
/*
* Seek control callback. Might be NULL.
*/
data->set.seek_client = va_arg(param, void *);
break;
case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
/*
* "Convert from network encoding" callback
*/
data->set.convfromnetwork = va_arg(param, curl_conv_callback);
break;
case CURLOPT_CONV_TO_NETWORK_FUNCTION:
/*
* "Convert to network encoding" callback
*/
data->set.convtonetwork = va_arg(param, curl_conv_callback);
break;
case CURLOPT_CONV_FROM_UTF8_FUNCTION:
/*
* "Convert from UTF-8 encoding" callback
*/
data->set.convfromutf8 = va_arg(param, curl_conv_callback);
break;
case CURLOPT_IOCTLFUNCTION:
/*
* I/O control callback. Might be NULL.
*/
data->set.ioctl_func = va_arg(param, curl_ioctl_callback);
break;
case CURLOPT_IOCTLDATA:
/*
* I/O control data pointer. Might be NULL.
*/
data->set.ioctl_client = va_arg(param, void *);
break;
case CURLOPT_SSLCERT:
/*
* String that holds file name of the SSL certificate to use
*/
result = setstropt(&data->set.str[STRING_CERT],
va_arg(param, char *));
break;
case CURLOPT_SSLCERTTYPE:
/*
* String that holds file type of the SSL certificate to use
*/
result = setstropt(&data->set.str[STRING_CERT_TYPE],
va_arg(param, char *));
break;
case CURLOPT_SSLKEY:
/*
* String that holds file name of the SSL certificate to use
*/
result = setstropt(&data->set.str[STRING_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSLKEYTYPE:
/*
* String that holds file type of the SSL certificate to use
*/
result = setstropt(&data->set.str[STRING_KEY_TYPE],
va_arg(param, char *));
break;
case CURLOPT_KEYPASSWD:
/*
* String that holds the SSL or SSH private key password.
*/
result = setstropt(&data->set.str[STRING_KEY_PASSWD],
va_arg(param, char *));
break;
case CURLOPT_SSLENGINE:
/*
* String that holds the SSL crypto engine.
*/
argptr = va_arg(param, char *);
if(argptr && argptr[0])
result = Curl_ssl_set_engine(data, argptr);
break;
case CURLOPT_SSLENGINE_DEFAULT:
/*
* flag to set engine as default.
*/
result = Curl_ssl_set_engine_default(data);
break;
case CURLOPT_CRLF:
/*
* Kludgy option to enable CRLF conversions. Subject for removal.
*/
data->set.crlf = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_INTERFACE:
/*
* Set what interface or address/hostname to bind the socket to when
* performing an operation and thus what from-IP your connection will use.
*/
result = setstropt(&data->set.str[STRING_DEVICE],
va_arg(param, char *));
break;
case CURLOPT_LOCALPORT:
/*
* Set what local port to bind the socket to when performing an operation.
*/
data->set.localport = (unsigned short) va_arg(param, long);
break;
case CURLOPT_LOCALPORTRANGE:
/*
* Set number of local ports to try, starting with CURLOPT_LOCALPORT.
*/
data->set.localportrange = (int) va_arg(param, long);
break;
case CURLOPT_KRBLEVEL:
/*
* A string that defines the kerberos security level.
*/
result = setstropt(&data->set.str[STRING_KRB_LEVEL],
va_arg(param, char *));
data->set.krb = (bool)(NULL != data->set.str[STRING_KRB_LEVEL]);
break;
case CURLOPT_SSL_VERIFYPEER:
/*
* Enable peer SSL verifying.
*/
data->set.ssl.verifypeer = va_arg(param, long);
break;
case CURLOPT_SSL_VERIFYHOST:
/*
* Enable verification of the CN contained in the peer certificate
*/
data->set.ssl.verifyhost = va_arg(param, long);
break;
#ifdef USE_SSLEAY
/* since these two options are only possible to use on an OpenSSL-
powered libcurl we #ifdef them on this condition so that libcurls
built against other SSL libs will return a proper error when trying
to set this option! */
case CURLOPT_SSL_CTX_FUNCTION:
/*
* Set a SSL_CTX callback
*/
data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
break;
case CURLOPT_SSL_CTX_DATA:
/*
* Set a SSL_CTX callback parameter pointer
*/
data->set.ssl.fsslctxp = va_arg(param, void *);
break;
case CURLOPT_CERTINFO:
data->set.ssl.certinfo = (bool)(0 != va_arg(param, long));
break;
#endif
case CURLOPT_CAINFO:
/*
* Set CA info for SSL connection. Specify file name of the CA certificate
*/
result = setstropt(&data->set.str[STRING_SSL_CAFILE],
va_arg(param, char *));
break;
case CURLOPT_CAPATH:
/*
* Set CA path info for SSL connection. Specify directory name of the CA
* certificates which have been prepared using openssl c_rehash utility.
*/
/* This does not work on windows. */
result = setstropt(&data->set.str[STRING_SSL_CAPATH],
va_arg(param, char *));
break;
case CURLOPT_CRLFILE:
/*
* Set CRL file info for SSL connection. Specify file name of the CRL
* to check certificates revocation
*/
result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
va_arg(param, char *));
break;
case CURLOPT_ISSUERCERT:
/*
* Set Issuer certificate file
* to check certificates issuer
*/
result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
va_arg(param, char *));
break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
*/
data->set.telnet_options = va_arg(param, struct curl_slist *);
break;
case CURLOPT_BUFFERSIZE:
/*
* The application kindly asks for a differently sized receive buffer.
* If it seems reasonable, we'll use it.
*/
data->set.buffer_size = va_arg(param, long);
if((data->set.buffer_size> (BUFSIZE -1 )) ||
(data->set.buffer_size < 1))
data->set.buffer_size = 0; /* huge internal default */
break;
case CURLOPT_NOSIGNAL:
/*
* The application asks not to set any signal() or alarm() handlers,
* even when using a timeout.
*/
data->set.no_signal = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_SHARE:
{
struct Curl_share *set;
set = va_arg(param, struct Curl_share *);
/* disconnect from old share, if any */
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
if(data->dns.hostcachetype == HCACHE_SHARED) {
data->dns.hostcache = NULL;
data->dns.hostcachetype = HCACHE_NONE;
}
if(data->share->cookies == data->cookies)
data->cookies = NULL;
data->share->dirty--;
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
data->share = NULL;
}
/* use new share if it set */
data->share = set;
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
data->share->dirty++;
if(data->share->hostcache) {
/* use shared host cache, first free the private one if any */
if(data->dns.hostcachetype == HCACHE_PRIVATE)
Curl_hash_destroy(data->dns.hostcache);
data->dns.hostcache = data->share->hostcache;
data->dns.hostcachetype = HCACHE_SHARED;
}
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->share->cookies) {
/* use shared cookie list, first free own one if any */
if(data->cookies)
Curl_cookie_cleanup(data->cookies);
/* enable cookies since we now use a share that uses cookies! */
data->cookies = data->share->cookies;
}
#endif /* CURL_DISABLE_HTTP */
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
/* check for host cache not needed,
* it will be done by curl_easy_perform */
}
break;
case CURLOPT_PROXYTYPE:
/*
* Set proxy type. HTTP/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME
*/
data->set.proxytype = (curl_proxytype)va_arg(param, long);
break;
case CURLOPT_PRIVATE:
/*
* Set private data pointer.
*/
data->set.private_data = va_arg(param, void *);
break;
case CURLOPT_MAXFILESIZE:
/*
* Set the maximum size of a file to download.
*/
data->set.max_filesize = va_arg(param, long);
break;
case CURLOPT_USE_SSL:
/*
* Make transfers attempt to use SSL/TLS.
*/
data->set.ftp_ssl = (curl_usessl)va_arg(param, long);
break;
case CURLOPT_FTPSSLAUTH:
/*
* Set a specific auth for FTP-SSL transfers.
*/
data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
break;
case CURLOPT_IPRESOLVE:
data->set.ip_version = va_arg(param, long);
break;
case CURLOPT_MAXFILESIZE_LARGE:
/*
* Set the maximum size of a file to download.
*/
data->set.max_filesize = va_arg(param, curl_off_t);
break;
case CURLOPT_TCP_NODELAY:
/*
* Enable or disable TCP_NODELAY, which will disable/enable the Nagle
* algorithm
*/
data->set.tcp_nodelay = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_ACCOUNT:
result = setstropt(&data->set.str[STRING_FTP_ACCOUNT],
va_arg(param, char *));
break;
case CURLOPT_IGNORE_CONTENT_LENGTH:
data->set.ignorecl = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_CONNECT_ONLY:
/*
* No data transfer, set up connection and let application use the socket
*/
data->set.connect_only = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
va_arg(param, char *));
break;
case CURLOPT_SOCKOPTFUNCTION:
/*
* socket callback function: called after socket() but before connect()
*/
data->set.fsockopt = va_arg(param, curl_sockopt_callback);
break;
case CURLOPT_SOCKOPTDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.sockopt_client = va_arg(param, void *);
break;
case CURLOPT_OPENSOCKETFUNCTION:
/*
* open/create socket callback function: called instead of socket(),
* before connect()
*/
data->set.fopensocket = va_arg(param, curl_opensocket_callback);
break;
case CURLOPT_OPENSOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.opensocket_client = va_arg(param, void *);
break;
case CURLOPT_SSL_SESSIONID_CACHE:
data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
break;
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa.pub file
*/
result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa file
*/
result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
va_arg(param, char *));
break;
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
/*
* Option to allow for the MD5 of the host public key to be checked
* for validation purposes.
*/
result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
va_arg(param, char *));
break;
case CURLOPT_HTTP_TRANSFER_DECODING:
/*
* disable libcurl transfer encoding is used
*/
data->set.http_te_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_HTTP_CONTENT_DECODING:
/*
* raw data passed to the application when content encoding is used
*/
data->set.http_ce_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_NEW_FILE_PERMS:
/*
* Uses these permissions instead of 0644
*/
data->set.new_file_perms = va_arg(param, long);
break;
case CURLOPT_NEW_DIRECTORY_PERMS:
/*
* Uses these permissions instead of 0755
*/
data->set.new_directory_perms = va_arg(param, long);
break;
case CURLOPT_PROXY_TRANSFER_MODE:
/*
* set transfer mode (;type=) when doing FTP via an HTTP proxy
*/
switch (va_arg(param, long)) {
case 0:
data->set.proxy_transfer_mode = FALSE;
break;
case 1:
data->set.proxy_transfer_mode = TRUE;
break;
default:
/* reserve other values for future use */
result = CURLE_FAILED_INIT;
break;
}
break;
case CURLOPT_ADDRESS_SCOPE:
/*
* We always get longs when passed plain numericals, but for this value we
* know that an unsigned int will always hold the value so we blindly
* typecast to this type
*/
data->set.scope = (unsigned int) va_arg(param, long);
break;
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_FAILED_INIT; /* correct this */
break;
}
return result;
}
|
|||||
| ↓ | Curl_nss_connect | 55 | 173 | 290 | lib/nss.c |
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
PRInt32 err;
PRFileDesc *model = NULL;
PRBool ssl2, ssl3, tlsv1;
struct SessionHandle *data = conn->data;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
SECStatus rv;
#ifdef HAVE_PK11_CREATEGENERICOBJECT
char *configstring = NULL;
#endif
char *certDir = NULL;
int curlerr;
curlerr = CURLE_SSL_CONNECT_ERROR;
if (connssl->state == ssl_connection_complete)
return CURLE_OK;
/* FIXME. NSS doesn't support multiple databases open at the same time. */
PR_Lock(nss_initlock);
if(!initialized && !NSS_IsInitialized()) {
initialized = 1;
certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */
if(!certDir) {
struct stat st;
if(stat(SSL_DIR, &st) == 0)
if(S_ISDIR(st.st_mode)) {
certDir = (char *)SSL_DIR;
}
}
if(!certDir) {
rv = NSS_NoDB_Init(NULL);
}
else {
rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db",
NSS_INIT_READONLY);
}
if(rv != SECSuccess) {
infof(conn->data, "Unable to initialize NSS database\n");
curlerr = CURLE_SSL_CACERT_BADFILE;
initialized = 0;
PR_Unlock(nss_initlock);
goto error;
}
NSS_SetDomesticPolicy();
#ifdef HAVE_PK11_CREATEGENERICOBJECT
configstring = malloc(PATH_MAX);
PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library);
mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
free(configstring);
if(!mod || !mod->loaded) {
if(mod) {
SECMOD_DestroyModule(mod);
mod = NULL;
}
infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
"PEM certificates will not work.\n", pem_library);
}
#endif
}
PR_Unlock(nss_initlock);
model = PR_NewTCPSocket();
if(!model)
goto error;
model = SSL_ImportFD(NULL, model);
if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
goto error;
if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
goto error;
if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
goto error;
ssl2 = ssl3 = tlsv1 = PR_FALSE;
switch (data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
ssl3 = tlsv1 = PR_TRUE;
break;
case CURL_SSLVERSION_TLSv1:
tlsv1 = PR_TRUE;
break;
case CURL_SSLVERSION_SSLv2:
ssl2 = PR_TRUE;
break;
case CURL_SSLVERSION_SSLv3:
ssl3 = PR_TRUE;
break;
}
if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
goto error;
if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
goto error;
if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
goto error;
if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
goto error;
if(data->set.ssl.cipher_list) {
if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
curlerr = CURLE_SSL_CIPHER;
goto error;
}
}
data->set.ssl.certverifyresult=0; /* not checked yet */
if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn)
!= SECSuccess) {
goto error;
}
if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback,
NULL) != SECSuccess)
goto error;
if(!data->set.ssl.verifypeer)
/* skip the verifying of the peer */
;
else if(data->set.ssl.CAfile) {
int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE);
if(!rc) {
curlerr = CURLE_SSL_CACERT_BADFILE;
goto error;
}
}
else if(data->set.ssl.CApath) {
struct stat st;
PRDir *dir;
PRDirEntry *entry;
if(stat(data->set.ssl.CApath, &st) == -1) {
curlerr = CURLE_SSL_CACERT_BADFILE;
goto error;
}
if(S_ISDIR(st.st_mode)) {
int rc;
dir = PR_OpenDir(data->set.ssl.CApath);
do {
entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN);
if(entry) {
char fullpath[PATH_MAX];
snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath,
entry->name);
rc = nss_load_cert(fullpath, PR_TRUE);
/* FIXME: check this return value! */
}
/* This is purposefully tolerant of errors so non-PEM files
* can be in the same directory */
} while(entry != NULL);
PR_CloseDir(dir);
}
}
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
data->set.ssl.CApath ? data->set.ssl.CApath : "none");
if (data->set.ssl.CRLfile) {
int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE);
if (!rc) {
curlerr = CURLE_SSL_CRL_BADFILE;
goto error;
}
infof(data,
" CRLfile: %s\n",
data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
}
if(data->set.str[STRING_CERT]) {
char *n;
char *nickname;
nickname = malloc(PATH_MAX);
if(is_file(data->set.str[STRING_CERT])) {
n = strrchr(data->set.str[STRING_CERT], '/');
if(n) {
n++; /* skip last slash */
snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n);
}
}
else {
strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX);
nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
}
if(nss_Init_Tokens(conn) != SECSuccess) {
free(nickname);
goto error;
}
if(!cert_stuff(conn, data->set.str[STRING_CERT],
data->set.str[STRING_KEY])) {
/* failf() is already done in cert_stuff() */
free(nickname);
return CURLE_SSL_CERTPROBLEM;
}
connssl->client_nickname = strdup(nickname);
if(SSL_GetClientAuthDataHook(model,
(SSLGetClientAuthData) SelectClientCert,
(void *)connssl) !=
SECSuccess) {
curlerr = CURLE_SSL_CERTPROBLEM;
goto error;
}
free(nickname);
PK11_SetPasswordFunc(nss_no_password);
}
else
connssl->client_nickname = NULL;
/* Import our model socket onto the existing file descriptor */
connssl->handle = PR_ImportTCPSocket(sockfd);
connssl->handle = SSL_ImportFD(model, connssl->handle);
if(!connssl->handle)
goto error;
PR_Close(model); /* We don't need this any more */
/* Force handshake on next I/O */
SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
SSL_SetURL(connssl->handle, conn->host.name);
/* Force the handshake now */
if(SSL_ForceHandshakeWithTimeout(connssl->handle,
PR_SecondsToInterval(HANDSHAKE_TIMEOUT))
!= SECSuccess) {
if(conn->data->set.ssl.certverifyresult!=0)
curlerr = CURLE_SSL_CACERT;
goto error;
}
connssl->state = ssl_connection_complete;
display_conn_info(conn, connssl->handle);
if (data->set.str[STRING_SSL_ISSUERCERT]) {
char *n;
char *nickname;
nickname = malloc(PATH_MAX);
if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) {
n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/');
if (n) {
n++; /* skip last slash */
snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n);
}
}
else {
strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX);
nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
}
if (check_issuer_cert(connssl->handle, nickname) == SECFailure) {
infof(data,"SSL certificate issuer check failed\n");
free(nickname);
curlerr = CURLE_SSL_ISSUER_ERROR;
goto error;
}
else {
infof(data, "SSL certificate issuer check ok\n");
}
}
return CURLE_OK;
error:
err = PR_GetError();
infof(data, "NSS error %d\n", err);
if(model)
PR_Close(model);
return curlerr;
}
|
|||||
| ↓ | Curl_getFormData | 52 | 141 | 293 | lib/formdata.c |
CURLcode Curl_getFormData(struct FormData **finalform,
struct curl_httppost *post,
const char *custom_content_type,
curl_off_t *sizep)
{
struct FormData *form = NULL;
struct FormData *firstform;
struct curl_httppost *file;
CURLcode result = CURLE_OK;
curl_off_t size=0; /* support potentially ENORMOUS formposts */
char *boundary;
char *fileboundary=NULL;
struct curl_slist* curList;
*finalform=NULL; /* default form is empty */
if(!post)
return result; /* no input => no output! */
boundary = Curl_FormBoundary();
if(!boundary)
return CURLE_OUT_OF_MEMORY;
/* Make the first line of the output */
result = AddFormDataf(&form, NULL,
"%s; boundary=%s\r\n",
custom_content_type?custom_content_type:
"Content-Type: multipart/form-data",
boundary);
if(result) {
free(boundary);
return result;
}
/* we DO NOT include that line in the total size of the POST, since it'll be
part of the header! */
firstform = form;
do {
if(size) {
result = AddFormDataf(&form, &size, "\r\n");
if(result)
break;
}
/* boundary */
result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
if(result)
break;
/* Maybe later this should be disabled when a custom_content_type is
passed, since Content-Disposition is not meaningful for all multipart
types.
*/
result = AddFormDataf(&form, &size,
"Content-Disposition: form-data; name=\"");
if(result)
break;
result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
&size);
if(result)
break;
result = AddFormDataf(&form, &size, "\"");
if(result)
break;
if(post->more) {
/* If used, this is a link to more file names, we must then do
the magic to include several files with the same field name */
fileboundary = Curl_FormBoundary();
result = AddFormDataf(&form, &size,
"\r\nContent-Type: multipart/mixed,"
" boundary=%s\r\n",
fileboundary);
if(result)
break;
}
file = post;
do {
/* If 'showfilename' is set, that is a faked name passed on to us
to use to in the formpost. If that is not set, the actually used
local file name should be added. */
if(post->more) {
/* if multiple-file */
char *filebasename=
(!file->showfilename)?strippath(file->contents):NULL;
result = AddFormDataf(&form, &size,
"\r\n--%s\r\nContent-Disposition: "
"attachment; filename=\"%s\"",
fileboundary,
(file->showfilename?file->showfilename:
filebasename));
if(filebasename)
free(filebasename);
if(result)
break;
}
else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
HTTPPOST_CALLBACK)) {
/* it should be noted that for the HTTPPOST_FILENAME and
HTTPPOST_CALLBACK cases the ->showfilename struct member is always
assigned at this point */
char *filebasename=
(!post->showfilename)?strippath(post->contents):NULL;
result = AddFormDataf(&form, &size,
"; filename=\"%s\"",
(post->showfilename?post->showfilename:
filebasename));
if(filebasename)
free(filebasename);
if(result)
break;
}
if(file->contenttype) {
/* we have a specified type */
result = AddFormDataf(&form, &size,
"\r\nContent-Type: %s",
file->contenttype);
if(result)
break;
}
curList = file->contentheader;
while( curList ) {
/* Process the additional headers specified for this form */
result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
if(result)
break;
curList = curList->next;
}
if(result) {
Curl_formclean(&firstform);
free(boundary);
return result;
}
#if 0
/* The header Content-Transfer-Encoding: seems to confuse some receivers
* (like the built-in PHP engine). While I can't see any reason why it
* should, I can just as well skip this to the benefit of the users who
* are using such confused receivers.
*/
if(file->contenttype &&
!checkprefix("text/", file->contenttype)) {
/* this is not a text content, mention our binary encoding */
result = AddFormDataf(&form, &size,
"\r\nContent-Transfer-Encoding: binary");
if(result)
break;
}
#endif
result = AddFormDataf(&form, &size, "\r\n\r\n");
if(result)
break;
if((post->flags & HTTPPOST_FILENAME) ||
(post->flags & HTTPPOST_READFILE)) {
/* we should include the contents from the specified file */
FILE *fileread;
fileread = strequal("-", file->contents)?
stdin:fopen(file->contents, "rb"); /* binary read for win32 */
/*
* VMS: This only allows for stream files on VMS. Stream files are
* OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
* every record needs to have a \n appended & 1 added to SIZE
*/
if(fileread) {
if(fileread != stdin) {
/* close the file again */
fclose(fileread);
/* add the file name only - for later reading from this */
result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
}
else {
/* When uploading from stdin, we can't know the size of the file,
* thus must read the full file as before. We *could* use chunked
* transfer-encoding, but that only works for HTTP 1.1 and we
* can't be sure we work with such a server.
*/
size_t nread;
char buffer[512];
while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
if(result)
break;
}
}
if(result) {
Curl_formclean(&firstform);
free(boundary);
return result;
}
}
else {
#ifdef _FORM_DEBUG
fprintf(stderr,
"\n==> Curl_getFormData couldn't open/read \"%s\"\n",
file->contents);
#endif
Curl_formclean(&firstform);
free(boundary);
*finalform = NULL;
return CURLE_READ_ERROR;
}
}
else if(post->flags & HTTPPOST_BUFFER) {
/* include contents of buffer */
result = AddFormData(&form, FORM_CONTENT, post->buffer,
post->bufferlength, &size);
if(result)
break;
}
else if(post->flags & HTTPPOST_CALLBACK) {
/* the contents should be read with the callback and the size
is set with the contentslength */
result = AddFormData(&form, FORM_CALLBACK, post->userp,
post->contentslength, &size);
if(result)
break;
}
else {
/* include the contents we got */
result = AddFormData(&form, FORM_CONTENT, post->contents,
post->contentslength, &size);
if(result)
break;
}
} while((file = file->more) != NULL); /* for each specified file for this field */
if(result) {
Curl_formclean(&firstform);
free(boundary);
return result;
}
if(post->more) {
/* this was a multiple-file inclusion, make a termination file
boundary: */
result = AddFormDataf(&form, &size,
"\r\n--%s--",
fileboundary);
free(fileboundary);
if(result)
break;
}
} while((post = post->next) != NULL); /* for each field */
if(result) {
Curl_formclean(&firstform);
free(boundary);
return result;
}
/* end-boundary for everything */
result = AddFormDataf(&form, &size,
"\r\n--%s--\r\n",
boundary);
if(result) {
Curl_formclean(&firstform);
free(boundary);
return result;
}
*sizep = size;
free(boundary);
*finalform=firstform;
return result;
}
|
|||||
| ↓ | parsedate | 48 | 94 | 176 | lib/parsedate.c |
static time_t parsedate(const char *date)
{
time_t t = 0;
int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */
int monnum=-1; /* month of the year number, 0-11 */
int mdaynum=-1; /* day of month, 1 - 31 */
int hournum=-1;
int minnum=-1;
int secnum=-1;
int yearnum=-1;
int tzoff=-1;
struct tm tm;
enum assume dignext = DATE_MDAY;
const char *indate = date; /* save the original pointer */
int part = 0; /* max 6 parts */
while(*date && (part < 6)) {
bool found=FALSE;
skip(&date);
if(ISALPHA(*date)) {
/* a name coming up */
char buf[32]="";
size_t len;
sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",
buf);
len = strlen(buf);
if(wdaynum == -1) {
wdaynum = checkday(buf, len);
if(wdaynum != -1)
found = TRUE;
}
if(!found && (monnum == -1)) {
monnum = checkmonth(buf);
if(monnum != -1)
found = TRUE;
}
if(!found && (tzoff == -1)) {
/* this just must be a time zone string */
tzoff = checktz(buf);
if(tzoff != -1)
found = TRUE;
}
if(!found)
return -1; /* bad string */
date += len;
}
else if(ISDIGIT(*date)) {
/* a digit */
int val;
char *end;
if((secnum == -1) &&
(3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
/* time stamp! */
date += 8;
found = TRUE;
}
else {
val = (int)strtol(date, &end, 10);
if((tzoff == -1) &&
((end - date) == 4) &&
(val <= 1400) &&
(indate< date) &&
((date[-1] == '+' || date[-1] == '-'))) {
/* four digits and a value less than or equal to 1400 (to take into
account all sorts of funny time zone diffs) and it is preceeded
with a plus or minus. This is a time zone indication. 1400 is
picked since +1300 is frequently used and +1400 is mentioned as
an edge number in the document "ISO C 200X Proposal: Timezone
Functions" at http://david.tribble.com/text/c0xtimezone.html If
anyone has a more authoritative source for the exact maximum time
zone offsets, please speak up! */
found = TRUE;
tzoff = (val/100 * 60 + val%100)*60;
/* the + and - prefix indicates the local time compared to GMT,
this we need ther reversed math to get what we want */
tzoff = date[-1]=='+'?-tzoff:tzoff;
}
if(((end - date) == 8) &&
(yearnum == -1) &&
(monnum == -1) &&
(mdaynum == -1)) {
/* 8 digits, no year, month or day yet. This is YYYYMMDD */
found = TRUE;
yearnum = val/10000;
monnum = (val%10000)/100-1; /* month is 0 - 11 */
mdaynum = val%100;
}
if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
if((val > 0) && (val<32)) {
mdaynum = val;
found = TRUE;
}
dignext = DATE_YEAR;
}
if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
yearnum = val;
found = TRUE;
if(yearnum < 1900) {
if(yearnum > 70)
yearnum += 1900;
else
yearnum += 2000;
}
if(mdaynum == -1)
dignext = DATE_MDAY;
}
if(!found)
return -1;
date = end;
}
}
part++;
}
if(-1 == secnum)
secnum = minnum = hournum = 0; /* no time, make it zero */
if((-1 == mdaynum) ||
(-1 == monnum) ||
(-1 == yearnum))
/* lacks vital info, fail */
return -1;
#if SIZEOF_TIME_T < 5
/* 32 bit time_t can only hold dates to the beginning of 2038 */
if(yearnum > 2037)
return 0x7fffffff;
#endif
tm.tm_sec = secnum;
tm.tm_min = minnum;
tm.tm_hour = hournum;
tm.tm_mday = mdaynum;
tm.tm_mon = monnum;
tm.tm_year = yearnum - 1900;
tm.tm_wday = 0;
tm.tm_yday = 0;
tm.tm_isdst = 0;
/* my_timegm() returns a time_t. time_t is often 32 bits, even on many
architectures that feature 64 bit 'long'.
Some systems have 64 bit time_t and deal with years beyond 2038. However,
even on some of the systems with 64 bit time_t mktime() returns -1 for
dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
*/
t = my_timegm(&tm);
/* time zone adjust (cast t to int to compare to negative one) */
if(-1 != (int)t) {
/* Add the time zone diff between local time zone and GMT. */
long delta = (long)(tzoff!=-1?tzoff:0);
if((delta>0) && (t + delta < t))
return -1; /* time_t overflow */
t += delta;
}
return t;
}
|
|||||
| ↓ | Curl_ldap | 47 | 175 | 286 | lib/ldap.c |
static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
{
CURLcode status = CURLE_OK;
int rc = 0;
LDAP *server = NULL;
LDAPURLDesc *ludp = NULL;
LDAPMessage *result = NULL;
LDAPMessage *entryIterator;
int num = 0;
struct SessionHandle *data=conn->data;
int ldap_proto = LDAP_VERSION3;
int ldap_ssl = 0;
char *val_b64;
size_t val_b64_sz;
#ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval ldap_timeout = {10,0}; /* 10 sec connection/search timeout */
#endif
*done = TRUE; /* unconditionally */
infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
infof(data, "LDAP local: %s\n", data->change.url);
#ifdef HAVE_LDAP_URL_PARSE
rc = ldap_url_parse(data->change.url, &ludp);
#else
rc = _ldap_url_parse(conn, &ludp);
#endif
if(rc != 0) {
failf(data, "LDAP local: %s", ldap_err2string(rc));
status = CURLE_LDAP_INVALID_URL;
goto quit;
}
/* Get the URL scheme ( either ldap or ldaps ) */
if(strequal(conn->protostr, "LDAPS"))
ldap_ssl = 1;
infof(data, "LDAP local: trying to establish %s connection\n",
ldap_ssl ? "encrypted" : "cleartext");
#ifdef LDAP_OPT_NETWORK_TIMEOUT
ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
#endif
ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
if(ldap_ssl) {
#ifdef HAVE_LDAP_SSL
#ifdef CURL_LDAP_WIN
/* Win32 LDAP SDK doesnt support insecure mode without CA! */
server = ldap_sslinit(conn->host.name, (int)conn->port, 1);
ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
#else
int ldap_option;
char* ldap_ca = data->set.str[STRING_SSL_CAFILE];
#if defined(CURL_HAS_NOVELL_LDAPSDK)
rc = ldapssl_client_init(NULL, NULL);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
if(data->set.ssl.verifypeer) {
/* Novell SDK supports DER or BASE64 files. */
int cert_type = LDAPSSL_CERT_FILETYPE_B64;
if((data->set.str[STRING_CERT_TYPE]) &&
(strequal(data->set.str[STRING_CERT_TYPE], "DER")))
cert_type = LDAPSSL_CERT_FILETYPE_DER;
if(!ldap_ca) {
failf(data, "LDAP local: ERROR %s CA cert not set!",
(cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
infof(data, "LDAP local: using %s CA cert '%s'\n",
(cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
ldap_ca);
rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR setting %s CA cert: %s",
(cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
ldap_option = LDAPSSL_VERIFY_SERVER;
} else {
ldap_option = LDAPSSL_VERIFY_NONE;
}
rc = ldapssl_set_verify_mode(ldap_option);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR setting cert verify mode: %s",
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
server = ldapssl_init(conn->host.name, (int)conn->port, 1);
if(server == NULL) {
failf(data, "LDAP local: Cannot connect to %s:%d",
conn->host.name, conn->port);
status = CURLE_COULDNT_CONNECT;
goto quit;
}
#elif defined(LDAP_OPT_X_TLS)
if(data->set.ssl.verifypeer) {
/* OpenLDAP SDK supports BASE64 files. */
if((data->set.str[STRING_CERT_TYPE]) &&
(!strequal(data->set.str[STRING_CERT_TYPE], "PEM"))) {
failf(data, "LDAP local: ERROR OpenLDAP does only support PEM cert-type!");
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
if(!ldap_ca) {
failf(data, "LDAP local: ERROR PEM CA cert not set!");
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
ldap_option = LDAP_OPT_X_TLS_DEMAND;
} else {
ldap_option = LDAP_OPT_X_TLS_NEVER;
}
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR setting cert verify mode: %s",
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
server = ldap_init(conn->host.name, (int)conn->port);
if(server == NULL) {
failf(data, "LDAP local: Cannot connect to %s:%d",
conn->host.name, conn->port);
status = CURLE_COULDNT_CONNECT;
goto quit;
}
ldap_option = LDAP_OPT_X_TLS_HARD;
rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
/*
rc = ldap_start_tls_s(server, NULL, NULL);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
ldap_err2string(rc));
status = CURLE_SSL_CERTPROBLEM;
goto quit;
}
*/
#else
/* we should probably never come up to here since configure
should check in first place if we can support LDAP SSL/TLS */
failf(data, "LDAP local: SSL/TLS not supported with this version "
"of the OpenLDAP toolkit\n");
status = CURLE_SSL_CERTPROBLEM;
goto quit;
#endif
#endif
#endif /* CURL_LDAP_USE_SSL */
} else {
server = ldap_init(conn->host.name, (int)conn->port);
if(server == NULL) {
failf(data, "LDAP local: Cannot connect to %s:%d",
conn->host.name, conn->port);
status = CURLE_COULDNT_CONNECT;
goto quit;
}
}
#ifdef CURL_LDAP_WIN
ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
#endif
rc = ldap_simple_bind_s(server,
conn->bits.user_passwd ? conn->user : NULL,
conn->bits.user_passwd ? conn->passwd : NULL);
if(!ldap_ssl && rc != 0) {
ldap_proto = LDAP_VERSION2;
ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
rc = ldap_simple_bind_s(server,
conn->bits.user_passwd ? conn->user : NULL,
conn->bits.user_passwd ? conn->passwd : NULL);
}
if(rc != 0) {
failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
status = CURLE_LDAP_CANNOT_BIND;
goto quit;
}
rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
ludp->lud_filter, ludp->lud_attrs, 0, &result);
if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
failf(data, "LDAP remote: %s", ldap_err2string(rc));
status = CURLE_LDAP_SEARCH_FAILED;
goto quit;
}
for(num = 0, entryIterator = ldap_first_entry(server, result);
entryIterator;
entryIterator = ldap_next_entry(server, entryIterator), num++)
{
BerElement *ber = NULL;
char *attribute; /*! suspicious that this isn't 'const' */
char *dn = ldap_get_dn(server, entryIterator);
int i;
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0);
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
for (attribute = ldap_first_attribute(server, entryIterator, &ber);
attribute;
attribute = ldap_next_attribute(server, entryIterator, ber))
{
BerValue **vals = ldap_get_values_len(server, entryIterator, attribute);
if(vals != NULL)
{
for (i = 0; (vals[i] != NULL); i++)
{
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0);
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
if((strlen(attribute) > 7) &&
(strcmp(";binary",
(char *)attribute +
(strlen((char *)attribute) - 7)) == 0)) {
/* Binary attribute, encode to base64. */
val_b64_sz = Curl_base64_encode(conn->data,
vals[i]->bv_val,
vals[i]->bv_len,
&val_b64);
if(val_b64_sz > 0) {
Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
free(val_b64);
}
} else
Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
vals[i]->bv_len);
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
}
/* Free memory used to store values */
ldap_value_free_len(vals);
}
Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
ldap_memfree(attribute);
}
ldap_memfree(dn);
if(ber)
ber_free(ber, 0);
}
quit:
if(result) {
ldap_msgfree(result);
LDAP_TRACE (("Received %d entries\n", num));
}
if(rc == LDAP_SIZELIMIT_EXCEEDED)
infof(data, "There are more than %d entries\n", num);
if(ludp)
ldap_free_urldesc(ludp);
if(server)
ldap_unbind_s(server);
#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
if(ldap_ssl)
ldapssl_client_deinit();
#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
conn->bits.close = TRUE;
return status;
}
|
|||||
| ↓ | Curl_gtls_connect | 47 | 170 | 368 | lib/gtls.c |
CURLcode
Curl_gtls_connect(struct connectdata *conn,
int sockindex)
{
static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
struct SessionHandle *data = conn->data;
gnutls_session session;
int rc;
unsigned int cert_list_size;
const gnutls_datum *chainp;
unsigned int verify_status;
gnutls_x509_crt x509_cert,x509_issuer;
gnutls_datum issuerp;
char certbuf[256]; /* big enough? */
size_t size;
unsigned int algo;
unsigned int bits;
time_t certclock;
const char *ptr;
void *ssl_sessionid;
size_t ssl_idsize;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
if(!gtls_inited)
_Curl_gtls_init();
/* GnuTLS only supports SSLv3 and TLSv1 */
if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
failf(data, "GnuTLS does not support SSLv2");
return CURLE_SSL_CONNECT_ERROR;
}
/* allocate a cred struct */
rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
if(rc < 0) {
failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
if(data->set.ssl.CAfile) {
/* set the trusted CA cert bundle file */
gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
data->set.ssl.CAfile,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
infof(data, "error reading ca cert file %s (%s)\n",
data->set.ssl.CAfile, gnutls_strerror(rc));
if(data->set.ssl.verifypeer)
return CURLE_SSL_CACERT_BADFILE;
}
else
infof(data, "found %d certificates in %s\n",
rc, data->set.ssl.CAfile);
}
if(data->set.ssl.CRLfile) {
/* set the CRL list file */
rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
data->set.ssl.CRLfile,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
failf(data, "error reading crl file %s (%s)\n",
data->set.ssl.CRLfile, gnutls_strerror(rc));
return CURLE_SSL_CRL_BADFILE;
}
else
infof(data, "found %d CRL in %s\n",
rc, data->set.ssl.CRLfile);
}
/* Initialize TLS session as a client */
rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
if(rc) {
failf(data, "gnutls_init() failed: %d", rc);
return CURLE_SSL_CONNECT_ERROR;
}
/* convenient assign */
session = conn->ssl[sockindex].session;
if ((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
#endif
(gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name,
strlen(conn->host.name)) < 0))
infof(data, "WARNING: failed to configure server name indication (SNI) "
"TLS extension\n");
/* Use default priorities */
rc = gnutls_set_default_priority(session);
if(rc < 0)
return CURLE_SSL_CONNECT_ERROR;
if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) {
int protocol_priority[] = { GNUTLS_SSL3, 0 };
gnutls_protocol_set_priority(session, protocol_priority);
if(rc < 0)
return CURLE_SSL_CONNECT_ERROR;
}
/* Sets the priority on the certificate types supported by gnutls. Priority
is higher for types specified before others. After specifying the types
you want, you must append a 0. */
rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
if(rc < 0)
return CURLE_SSL_CONNECT_ERROR;
if(data->set.str[STRING_CERT]) {
if( gnutls_certificate_set_x509_key_file(
conn->ssl[sockindex].cred,
data->set.str[STRING_CERT],
data->set.str[STRING_KEY] ?
data->set.str[STRING_KEY] : data->set.str[STRING_CERT],
do_file_type(data->set.str[STRING_CERT_TYPE]) ) ) {
failf(data, "error reading X.509 key or certificate file");
return CURLE_SSL_CONNECT_ERROR;
}
}
/* put the credentials to the current session */
rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
conn->ssl[sockindex].cred);
/* set the connection handle (file descriptor for the socket) */
gnutls_transport_set_ptr(session,
(gnutls_transport_ptr)conn->sock[sockindex]);
/* register callback functions to send and receive data. */
gnutls_transport_set_push_function(session, Curl_gtls_push);
gnutls_transport_set_pull_function(session, Curl_gtls_pull);
/* lowat must be set to zero when using custom push and pull functions. */
gnutls_transport_set_lowat(session, 0);
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
/* we got a session id, use it! */
gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
/* Informational message */
infof (data, "SSL re-using session ID\n");
}
rc = handshake(conn, session, sockindex, TRUE);
if(rc)
/* handshake() sets its own error message with failf() */
return rc;
/* This function will return the peer's raw certificate (chain) as sent by
the peer. These certificates are in raw format (DER encoded for
X.509). In case of a X.509 then a certificate list may be present. The
first certificate in the list is the peer's certificate, following the
issuer's certificate, then the issuer's issuer etc. */
chainp = gnutls_certificate_get_peers(session, &cert_list_size);
if(!chainp) {
if(data->set.ssl.verifypeer ||
data->set.ssl.verifyhost ||
data->set.ssl.issuercert) {
failf(data, "failed to get server cert");
return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "\t common name: WARNING couldn't obtain\n");
}
if(data->set.ssl.verifypeer) {
/* This function will try to verify the peer's certificate and return its
status (trusted, invalid etc.). The value of status should be one or
more of the gnutls_certificate_status_t enumerated elements bitwise
or'd. To avoid denial of service attacks some default upper limits
regarding the certificate key size and chain size are set. To override
them use gnutls_certificate_set_verify_limits(). */
rc = gnutls_certificate_verify_peers2(session, &verify_status);
if(rc < 0) {
failf(data, "server cert verify failed: %d", rc);
return CURLE_SSL_CONNECT_ERROR;
}
/* verify_status is a bitmask of gnutls_certificate_status bits */
if(verify_status & GNUTLS_CERT_INVALID) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate verification failed. CAfile: %s "
"CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
return CURLE_SSL_CACERT;
}
else
infof(data, "\t server certificate verification FAILED\n");
}
else
infof(data, "\t server certificate verification OK\n");
}
else
infof(data, "\t server certificate verification SKIPPED\n");
/* initialize an X.509 certificate structure. */
gnutls_x509_crt_init(&x509_cert);
/* convert the given DER or PEM encoded Certificate to the native
gnutls_x509_crt_t format */
gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
if (data->set.ssl.issuercert) {
gnutls_x509_crt_init(&x509_issuer);
issuerp = load_file(data->set.ssl.issuercert);
gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
unload_file(issuerp);
if (rc <= 0) {
failf(data, "server certificate issuer check failed (IssuerCert: %s)",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
return CURLE_SSL_ISSUER_ERROR;
}
infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
}
size=sizeof(certbuf);
rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
0, /* the first and only one */
FALSE,
certbuf,
&size);
if(rc) {
infof(data, "error fetching CN from cert:%s\n",
gnutls_strerror(rc));
}
/* This function will check if the given certificate's subject matches the
given hostname. This is a basic implementation of the matching described
in RFC2818 (HTTPS), which takes into account wildcards, and the subject
alternative name PKIX extension. Returns non zero on success, and zero on
failure. */
rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
if(!rc) {
if(data->set.ssl.verifyhost > 1) {
failf(data, "SSL: certificate subject name (%s) does not match "
"target host name '%s'", certbuf, conn->host.dispname);
gnutls_x509_crt_deinit(x509_cert);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t common name: %s (does not match '%s')\n",
certbuf, conn->host.dispname);
}
else
infof(data, "\t common name: %s (matched)\n", certbuf);
/* Check for time-based validity */
certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
if(certclock == (time_t)-1) {
failf(data, "server cert expiration date verify failed");
return CURLE_SSL_CONNECT_ERROR;
}
if(certclock < time(NULL)) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate expiration date has passed.");
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t server certificate expiration date FAILED\n");
}
else
infof(data, "\t server certificate expiration date OK\n");
certclock = gnutls_x509_crt_get_activation_time(x509_cert);
if(certclock == (time_t)-1) {
failf(data, "server cert activation date verify failed");
return CURLE_SSL_CONNECT_ERROR;
}
if(certclock > time(NULL)) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate not activated yet.");
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t server certificate activation date FAILED\n");
}
else
infof(data, "\t server certificate activation date OK\n");
/* Show:
- ciphers used
- subject
- start date
- expire date
- common name
- issuer
*/
/* public key algorithm's parameters */
algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
infof(data, "\t certificate public key: %s\n",
gnutls_pk_algorithm_get_name(algo));
/* version of the X.509 certificate. */
infof(data, "\t certificate version: #%d\n",
gnutls_x509_crt_get_version(x509_cert));
size = sizeof(certbuf);
gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
infof(data, "\t subject: %s\n", certbuf);
certclock = gnutls_x509_crt_get_activation_time(x509_cert);
showtime(data, "start date", certclock);
certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
showtime(data, "expire date", certclock);
size = sizeof(certbuf);
gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
infof(data, "\t issuer: %s\n", certbuf);
gnutls_x509_crt_deinit(x509_cert);
/* compression algorithm (if any) */
ptr = gnutls_compression_get_name(gnutls_compression_get(session));
/* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
infof(data, "\t compression: %s\n", ptr);
/* the name of the cipher used. ie 3DES. */
ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
infof(data, "\t cipher: %s\n", ptr);
/* the MAC algorithms name. ie SHA1 */
ptr = gnutls_mac_get_name(gnutls_mac_get(session));
infof(data, "\t MAC: %s\n", ptr);
conn->ssl[sockindex].state = ssl_connection_complete;
if(!ssl_sessionid) {
/* this session was not previously in the cache, add it now */
/* get the session ID data size */
gnutls_session_get_data(session, NULL, &ssl_idsize);
ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */
if(ssl_sessionid) {
/* extract session ID to the allocated buffer */
gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize);
/* store this session id */
return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize);
}
}
return CURLE_OK;
}
|
|||||
| ↓ | Curl_SOCKS5 | 47 | 160 | 331 | lib/socks.c |
CURLcode Curl_SOCKS5(const char *proxy_name,
const char *proxy_password,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn)
{
/*
According to the RFC1928, section "6. Replies". This is what a SOCK5
replies:
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
Where:
o VER protocol version: X'05'
o REP Reply field:
o X'00' succeeded
*/
unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
ssize_t actualread;
ssize_t written;
int result;
CURLcode code;
curl_socket_t sock = conn->sock[sockindex];
struct SessionHandle *data = conn->data;
long timeout;
bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5);
const size_t hostname_len = strlen(hostname);
ssize_t packetsize = 0;
/* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
if(!socks5_resolve_local && hostname_len > 255)
{
infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
"length > 255 [actual len=%d]\n", hostname_len);
socks5_resolve_local = TRUE;
}
/* get timeout */
timeout = Curl_timeleft(conn, NULL, TRUE);
if(timeout < 0) {
/* time-out, bail out, go home */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
Curl_nonblock(sock, TRUE);
/* wait until socket gets connected */
result = Curl_socket_ready(CURL_SOCKET_BAD, sock, (int)timeout);
if(-1 == result) {
failf(conn->data, "SOCKS5: no connection here");
return CURLE_COULDNT_CONNECT;
}
else if(0 == result) {
failf(conn->data, "SOCKS5: connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
if(result & CURL_CSELECT_ERR) {
failf(conn->data, "SOCKS5: error occured during connection");
return CURLE_COULDNT_CONNECT;
}
socksreq[0] = 5; /* version */
socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
socksreq[2] = 0; /* no authentication */
socksreq[3] = 2; /* username/password */
Curl_nonblock(sock, FALSE);
code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
&written);
if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
failf(data, "Unable to send initial SOCKS5 request.");
return CURLE_COULDNT_CONNECT;
}
Curl_nonblock(sock, TRUE);
result = Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout);
if(-1 == result) {
failf(conn->data, "SOCKS5 nothing to read");
return CURLE_COULDNT_CONNECT;
}
else if(0 == result) {
failf(conn->data, "SOCKS5 read timeout");
return CURLE_OPERATION_TIMEDOUT;
}
if(result & CURL_CSELECT_ERR) {
failf(conn->data, "SOCKS5 read error occured");
return CURLE_RECV_ERROR;
}
Curl_nonblock(sock, FALSE);
result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
if((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive initial SOCKS5 response.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[0] != 5) {
failf(data, "Received invalid version in initial SOCKS5 response.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[1] == 0) {
/* Nothing to do, no authentication needed */
;
}
else if(socksreq[1] == 2) {
/* Needs user name and password */
size_t userlen, pwlen;
int len;
if(proxy_name && proxy_password) {
userlen = strlen(proxy_name);
pwlen = strlen(proxy_password);
}
else {
userlen = 0;
pwlen = 0;
}
/* username/password request looks like
* +----+------+----------+------+----------+
* |VER | ULEN | UNAME | PLEN | PASSWD |
* +----+------+----------+------+----------+
* | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
* +----+------+----------+------+----------+
*/
len = 0;
socksreq[len++] = 1; /* username/pw subnegotiation version */
socksreq[len++] = (char) userlen;
memcpy(socksreq + len, proxy_name, (int) userlen);
len += userlen;
socksreq[len++] = (char) pwlen;
memcpy(socksreq + len, proxy_password, (int) pwlen);
len += pwlen;
code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
if((code != CURLE_OK) || (len != written)) {
failf(data, "Failed to send SOCKS5 sub-negotiation request.");
return CURLE_COULDNT_CONNECT;
}
result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
timeout);
if((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
return CURLE_COULDNT_CONNECT;
}
/* ignore the first (VER) byte */
if(socksreq[1] != 0) { /* status */
failf(data, "User was rejected by the SOCKS5 server (%d %d).",
socksreq[0], socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
/* Everything is good so far, user was authenticated! */
}
else {
/* error */
if(socksreq[1] == 1) {
failf(data,
"SOCKS5 GSSAPI per-message authentication is not supported.");
return CURLE_COULDNT_CONNECT;
}
else if(socksreq[1] == 255) {
if(!proxy_name || !*proxy_name) {
failf(data,
"No authentication method was acceptable. (It is quite likely"
" that the SOCKS5 server wanted a username/password, since none"
" was supplied to the server on this connection.)");
}
else {
failf(data, "No authentication method was acceptable.");
}
return CURLE_COULDNT_CONNECT;
}
else {
failf(data,
"Undocumented SOCKS5 mode attempted to be used by server.");
return CURLE_COULDNT_CONNECT;
}
}
/* Authentication is complete, now specify destination to the proxy */
socksreq[0] = 5; /* version (SOCKS5) */
socksreq[1] = 1; /* connect */
socksreq[2] = 0; /* must be zero */
if(!socks5_resolve_local) {
packetsize = (ssize_t)(5 + hostname_len + 2);
socksreq[3] = 3; /* ATYP: domain name = 3 */
socksreq[4] = (char) hostname_len; /* address length */
memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */
*((unsigned short*)&socksreq[hostname_len+5]) =
htons((unsigned short)remote_port);
}
else {
struct Curl_dns_entry *dns;
Curl_addrinfo *hp=NULL;
int rc = Curl_resolv(conn, hostname, remote_port, &dns);
packetsize = 10;
socksreq[3] = 1; /* IPv4 = 1 */
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_HOST;
if(rc == CURLRESOLV_PENDING)
/* this requires that we're in "wait for resolve" state */
rc = Curl_wait_for_resolv(conn, &dns);
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same.
*/
if(dns)
hp=dns->addr;
if(hp) {
char buf[64];
unsigned short ip[4];
Curl_printable_address(hp, buf, sizeof(buf));
if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3])) {
socksreq[4] = (unsigned char)ip[0];
socksreq[5] = (unsigned char)ip[1];
socksreq[6] = (unsigned char)ip[2];
socksreq[7] = (unsigned char)ip[3];
}
else
hp = NULL; /* fail! */
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
}
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
hostname);
return CURLE_COULDNT_RESOLVE_HOST;
}
*((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
}
code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written);
if((code != CURLE_OK) || (written != packetsize)) {
failf(data, "Failed to send SOCKS5 connect request.");
return CURLE_COULDNT_CONNECT;
}
packetsize = 10; /* minimum packet size is 10 */
result = blockread_all(conn, sock, (char *)socksreq, packetsize,
&actualread, timeout);
if((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[0] != 5) { /* version */
failf(data,
"SOCKS5 reply has wrong version, version should be 5.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[1] != 0) { /* Anything besides 0 is an error */
failf(data,
"Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
/* Fix: in general, returned BND.ADDR is variable length parameter by RFC
1928, so the reply packet should be read until the end to avoid errors at
subsequent protocol level.
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
ATYP:
o IP v4 address: X'01', BND.ADDR = 4 byte
o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
o IP v6 address: X'04', BND.ADDR = 16 byte
*/
/* Calculate real packet size */
if(socksreq[3] == 3) {
/* domain name */
int addrlen = (int) socksreq[4];
packetsize = 5 + addrlen + 2;
}
else if(socksreq[3] == 4) {
/* IPv6 */
packetsize = 4 + 16 + 2;
}
/* At this point we already read first 10 bytes */
if(packetsize > 10) {
packetsize -= 10;
result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize,
&actualread, timeout);
if((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
return CURLE_COULDNT_CONNECT;
}
}
Curl_nonblock(sock, TRUE);
return CURLE_OK; /* Proxy was successful! */
}
|
|||||
| ↓ | ftp_statemach_act | 78 | 169 | 368 | lib/ftp.c |
static CURLcode ftp_statemach_act(struct connectdata *conn)
{
CURLcode result;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
struct SessionHandle *data=conn->data;
int ftpcode;
struct ftp_conn *ftpc = &conn->proto.ftpc;
static const char * const ftpauth[] = {
"SSL", "TLS"
};
size_t nread = 0;
if(ftpc->sendleft) {
/* we have a piece of a command still left to send */
ssize_t written;
result = Curl_write(conn, sock, ftpc->sendthis + ftpc->sendsize -
ftpc->sendleft, ftpc->sendleft, &written);
if(result)
return result;
if(written != (ssize_t)ftpc->sendleft) {
/* only a fraction was sent */
ftpc->sendleft -= written;
}
else {
free(ftpc->sendthis);
ftpc->sendthis=NULL;
ftpc->sendleft = ftpc->sendsize = 0;
ftpc->response = Curl_tvnow();
}
return CURLE_OK;
}
/* we read a piece of response */
result = ftp_readresp(sock, conn, &ftpcode, &nread);
if(result)
return result;
if(ftpcode) {
/* we have now received a full FTP server response */
switch(ftpc->state) {
case FTP_WAIT220:
if(ftpcode != 220) {
failf(data, "Got a %03d ftp-server response when 220 was expected",
ftpcode);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
/* We have received a 220 response fine, now we proceed. */
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
if(data->set.krb) {
/* If not anonymous login, try a secure login. Note that this
procedure is still BLOCKING. */
Curl_sec_request_prot(conn, "private");
/* We set private first as default, in case the line below fails to
set a valid level */
Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
if(Curl_sec_login(conn) != 0)
infof(data, "Logging in with password in cleartext!\n");
else
infof(data, "Authentication successful\n");
}
#endif
if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
/* We don't have a SSL/TLS connection yet, but FTPS is
requested. Try a FTPS connection now */
ftpc->count3=0;
switch(data->set.ftpsslauth) {
case CURLFTPAUTH_DEFAULT:
case CURLFTPAUTH_SSL:
ftpc->count2 = 1; /* add one to get next */
ftpc->count1 = 0;
break;
case CURLFTPAUTH_TLS:
ftpc->count2 = -1; /* subtract one to get next */
ftpc->count1 = 1;
break;
default:
failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
data->set.ftpsslauth);
return CURLE_FAILED_INIT; /* we don't know what to do */
}
NBFTPSENDF(conn, "AUTH %s", ftpauth[ftpc->count1]);
state(conn, FTP_AUTH);
}
else {
result = ftp_state_user(conn);
if(result)
return result;
}
break;
case FTP_AUTH:
/* we have gotten the response to a previous AUTH command */
/* RFC2228 (page 5) says:
*
* If the server is willing to accept the named security mechanism,
* and does not require any security data, it must respond with
* reply code 234/334.
*/
if((ftpcode == 234) || (ftpcode == 334)) {
/* Curl_ssl_connect is BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(CURLE_OK == result) {
conn->protocol |= PROT_FTPS;
conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
result = ftp_state_user(conn);
}
}
else if(ftpc->count3 < 1) {
ftpc->count3++;
ftpc->count1 += ftpc->count2; /* get next attempt */
result = Curl_nbftpsendf(conn, "AUTH %s", ftpauth[ftpc->count1]);
/* remain in this same state */
}
else {
if(data->set.ftp_ssl > CURLUSESSL_TRY)
/* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
result = CURLE_USE_SSL_FAILED;
else
/* ignore the failure and continue */
result = ftp_state_user(conn);
}
if(result)
return result;
break;
case FTP_USER:
case FTP_PASS:
result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
break;
case FTP_ACCT:
result = ftp_state_acct_resp(conn, ftpcode);
break;
case FTP_PBSZ:
/* FIX: check response code */
/* For TLS, the data connection can have one of two security levels.
1) Clear (requested by 'PROT C')
2)Private (requested by 'PROT P')
*/
if(!conn->ssl[SECONDARYSOCKET].use) {
NBFTPSENDF(conn, "PROT %c",
data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
state(conn, FTP_PROT);
}
else {
result = ftp_state_pwd(conn);
if(result)
return result;
}
break;
case FTP_PROT:
if(ftpcode/100 == 2)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use =
(bool)(data->set.ftp_ssl != CURLUSESSL_CONTROL);
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
/* we failed and bails out */
return CURLE_USE_SSL_FAILED;
if(data->set.ftp_ccc) {
/* CCC - Clear Command Channel
*/
NBFTPSENDF(conn, "CCC", NULL);
state(conn, FTP_CCC);
}
else {
result = ftp_state_pwd(conn);
if(result)
return result;
}
break;
case FTP_CCC:
if(ftpcode < 500) {
/* First shut down the SSL layer (note: this call will block) */
result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
if(result) {
failf(conn->data, "Failed to clear the command channel (CCC)");
return result;
}
}
/* Then continue as normal */
result = ftp_state_pwd(conn);
if(result)
return result;
break;
case FTP_PWD:
if(ftpcode == 257) {
char *dir = malloc(nread+1);
char *store=dir;
char *ptr=&data->state.buffer[4]; /* start on the first letter */
if(!dir)
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
257
|
|||||
| ↓ | ConnectionExists | 44 | 67 | 173 | lib/url.c |
static bool
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis)
{
long i;
struct connectdata *check;
bool canPipeline = IsPipeliningPossible(data);
for(i=0; i< data->state.connc->num; i++) {
bool match = FALSE;
size_t pipeLen = 0;
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
check = data->state.connc->connects[i];
if(!check)
/* NULL pointer means not filled-in entry */
continue;
pipeLen = check->send_pipe->size + check->recv_pipe->size;
if(check->connectindex == -1) {
check->connectindex = i; /* Set this appropriately since it might have
been set to -1 when the easy was removed
from the multi */
}
if(canPipeline) {
/* Make sure the pipe has only GET requests */
struct SessionHandle* sh = gethandleathead(check->send_pipe);
struct SessionHandle* rh = gethandleathead(check->recv_pipe);
if(sh) {
if(!IsPipeliningPossible(sh))
continue;
}
else if(rh) {
if(!IsPipeliningPossible(rh))
continue;
}
#ifdef CURLDEBUG
if(pipeLen > MAX_PIPELINE_LENGTH) {
infof(data, "BAD! Connection #%ld has too big pipeline!\n",
check->connectindex);
}
#endif
}
else {
if(pipeLen > 0) {
/* can only happen within multi handles, and means that another easy
handle is using this connection */
continue;
}
#ifdef CURLRES_ASYNCH
/* ip_addr_str is NULL only if the resolving of the name hasn't completed
yet and until then we don't re-use this connection */
if(!check->ip_addr_str) {
infof(data,
"Connection #%ld hasn't finished name resolve, can't reuse\n",
check->connectindex);
continue;
}
#endif
if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
/* Don't pick a connection that hasn't connected yet or that is going to
get closed. */
infof(data, "Connection #%ld isn't open enough, can't reuse\n",
check->connectindex);
#ifdef CURLDEBUG
if(check->recv_pipe->size > 0) {
infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connectindex);
}
#endif
continue;
}
}
if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
/* don't do mixed SSL and non-SSL connections */
continue;
if(needle->bits.proxy != check->bits.proxy)
/* don't do mixed proxy and non-proxy connections */
continue;
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL ||
(needle->bits.httpproxy && check->bits.httpproxy &&
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
strequal(needle->proxy.name, check->proxy.name) &&
(needle->port == check->port))) {
/* The requested connection does not use a HTTP proxy or it uses SSL or
it is a non-SSL protocol tunneled over the same http proxy name and
port number */
if(strequal(needle->protostr, check->protostr) &&
strequal(needle->host.name, check->host.name) &&
(needle->remote_port == check->remote_port) ) {
if(needle->protocol & PROT_SSL) {
/* This is SSL, verify that we're using the same
ssl options as well */
if(!Curl_ssl_config_matches(&needle->ssl_config,
&check->ssl_config)) {
DEBUGF(infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connectindex));
continue;
}
else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
DEBUGF(infof(data,
"Connection #%ld has not started ssl connect, "
"can't reuse\n",
check->connectindex));
continue;
}
}
if((needle->protocol & PROT_FTP) ||
((needle->protocol & PROT_HTTP) &&
(data->state.authhost.want==CURLAUTH_NTLM))) {
/* This is FTP or HTTP+NTLM, verify that we're using the same name
and password as well */
if(!strequal(needle->user, check->user) ||
!strequal(needle->passwd, check->passwd)) {
/* one of them was different */
continue;
}
}
match = TRUE;
}
}
else { /* The requested needle connection is using a proxy,
is the checked one using the same host, port and type? */
if(check->bits.proxy &&
(needle->proxytype == check->proxytype) &&
strequal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
match = TRUE;
}
}
if(match) {
if(!pipeLen && !check->inuse) {
/* The check for a dead socket makes sense only if there are no
handles in pipeline and the connection isn't already marked in
use */
bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
if(dead) {
check->data = data;
infof(data, "Connection #%d seems to be dead!\n", i);
Curl_disconnect(check); /* disconnect resources */
data->state.connc->connects[i]=NULL; /* nothing here */
return FALSE;
}
}
check->inuse = TRUE; /* mark this as being in use so that no other
handle in a multi stack may nick it */
*usethis = check;
return TRUE; /* yes, we found one to use! */
}
}
return FALSE; /* no matching connecting exists */
}
|
|||||
| ↓ | http_output_auth | 42 | 80 | 174 | lib/http.c |
static CURLcode
http_output_auth(struct connectdata *conn,
const char *request,
const char *path,
bool proxytunnel) /* TRUE if this is the request setting
up the proxy tunnel */
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
const char *auth=NULL;
struct auth *authhost;
struct auth *authproxy;
DEBUGASSERT(data);
authhost = &data->state.authhost;
authproxy = &data->state.authproxy;
if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
conn->bits.user_passwd)
/* continue please */ ;
else {
authhost->done = TRUE;
authproxy->done = TRUE;
return CURLE_OK; /* no authentication with no user or password */
}
if(authhost->want && !authhost->picked)
/* The app has selected one or more methods, but none has been picked
so far by a server round-trip. Then we set the picked one to the
want one, and if this is one single bit it'll be used instantly. */
authhost->picked = authhost->want;
if(authproxy->want && !authproxy->picked)
/* The app has selected one or more methods, but none has been picked so
far by a proxy round-trip. Then we set the picked one to the want one,
and if this is one single bit it'll be used instantly. */
authproxy->picked = authproxy->want;
/* Send proxy authentication header if needed */
if(conn->bits.httpproxy &&
(conn->bits.tunnel_proxy == proxytunnel)) {
#ifdef HAVE_GSSAPI
if((authproxy->picked == CURLAUTH_GSSNEGOTIATE) &&
data->state.negotiate.context &&
!GSS_ERROR(data->state.negotiate.status)) {
auth="GSS-Negotiate";
result = Curl_output_negotiate(conn, TRUE);
if(result)
return result;
authproxy->done = TRUE;
}
else
#endif
#ifdef USE_NTLM
if(authproxy->picked == CURLAUTH_NTLM) {
auth="NTLM";
result = Curl_output_ntlm(conn, TRUE);
if(result)
return result;
}
else
#endif
if(authproxy->picked == CURLAUTH_BASIC) {
/* Basic */
if(conn->bits.proxy_user_passwd &&
!checkheaders(data, "Proxy-authorization:")) {
auth="Basic";
result = http_output_basic(conn, TRUE);
if(result)
return result;
}
/* NOTE: http_output_basic() should set 'done' TRUE, as the other auth
functions work that way */
authproxy->done = TRUE;
}
#ifndef CURL_DISABLE_CRYPTO_AUTH
else if(authproxy->picked == CURLAUTH_DIGEST) {
auth="Digest";
result = Curl_output_digest(conn,
TRUE, /* proxy */
(const unsigned char *)request,
(const unsigned char *)path);
if(result)
return result;
}
#else
(void)request;
(void)path;
#endif
if(auth) {
infof(data, "Proxy auth using %s with user '%s'\n",
auth, conn->proxyuser?conn->proxyuser:"");
authproxy->multi = (bool)(!authproxy->done);
}
else
authproxy->multi = FALSE;
}
else
/* we have no proxy so let's pretend we're done authenticating
with it */
authproxy->done = TRUE;
/* To prevent the user+password to get sent to other than the original
host due to a location-follow, we do some weirdo checks here */
if(!data->state.this_is_a_follow ||
conn->bits.netrc ||
!data->state.first_host ||
curl_strequal(data->state.first_host, conn->host.name) ||
data->set.http_disable_hostname_check_before_authentication) {
/* Send web authentication header if needed */
{
auth = NULL;
#ifdef HAVE_GSSAPI
if((authhost->picked == CURLAUTH_GSSNEGOTIATE) &&
data->state.negotiate.context &&
!GSS_ERROR(data->state.negotiate.status)) {
auth="GSS-Negotiate";
result = Curl_output_negotiate(conn, FALSE);
if(result)
return result;
authhost->done = TRUE;
}
else
#endif
#ifdef USE_NTLM
if(authhost->picked == CURLAUTH_NTLM) {
auth="NTLM";
result = Curl_output_ntlm(conn, FALSE);
if(result)
return result;
}
else
#endif
{
#ifndef CURL_DISABLE_CRYPTO_AUTH
if(authhost->picked == CURLAUTH_DIGEST) {
auth="Digest";
result = Curl_output_digest(conn,
FALSE, /* not a proxy */
(const unsigned char *)request,
(const unsigned char *)path);
if(result)
return result;
} else
#endif
if(authhost->picked == CURLAUTH_BASIC) {
if(conn->bits.user_passwd &&
!checkheaders(data, "Authorization:")) {
auth="Basic";
result = http_output_basic(conn, FALSE);
if(result)
return result;
}
/* basic is always ready */
authhost->done = TRUE;
}
}
if(auth) {
infof(data, "Server auth using %s with user '%s'\n",
auth, conn->user);
authhost->multi = (bool)(!authhost->done);
}
else
authhost->multi = FALSE;
}
}
else
authhost->done = TRUE;
return result;
}
|
|||||
| ↓ | create_conn | 42 | 138 | 336 | lib/url.c |
static CURLcode create_conn(struct SessionHandle *data,
struct connectdata **in_connect,
struct Curl_dns_entry **addr,
bool *async)
{
CURLcode result=CURLE_OK;
struct connectdata *conn;
struct connectdata *conn_temp = NULL;
size_t urllen;
char user[MAX_CURL_USER_LENGTH];
char passwd[MAX_CURL_PASSWORD_LENGTH];
bool reuse;
char *proxy = NULL;
*addr = NULL; /* nothing yet */
*async = FALSE;
/*************************************************************
* Check input data
*************************************************************/
if(!data->change.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 = allocate_conn();
/* We must set the return variable as soon as possible, so that our
parent can cleanup any possible allocs we may have done before
any failure */
*in_connect = conn;
if(!conn)
return CURLE_OUT_OF_MEMORY;
conn->data = data; /* Setup the association between this connection
and the SessionHandle */
conn->proxytype = data->set.proxytype; /* type */
conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] &&
*data->set.str[STRING_PROXY]);
conn->bits.httpproxy = (bool)(conn->bits.proxy
&& (conn->proxytype == CURLPROXY_HTTP));
conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERPWD]);
conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERPWD]);
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
if(data->multi && Curl_multi_canPipeline(data->multi) &&
!conn->master_buffer) {
/* Allocate master_buffer to be used for pipelining */
conn->master_buffer = calloc(BUFSIZE, sizeof (char));
if(!conn->master_buffer)
return CURLE_OUT_OF_MEMORY;
}
/* Initialize the pipeline lists */
conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe)
return CURLE_OUT_OF_MEMORY;
/* This initing continues below, see the comment "Continue connectdata
* initialization here" */
/***********************************************************
* We need to allocate memory to store the path in. We get the size of the
* full URL to be sure, and we need to make it at least 256 bytes since
* other parts of the code will rely on this fact
***********************************************************/
#define LEAST_PATH_ALLOC 256
urllen=strlen(data->change.url);
if(urllen < LEAST_PATH_ALLOC)
urllen=LEAST_PATH_ALLOC;
/*
* We malloc() the buffers below urllen+2 to make room for to possibilities:
* 1 - an extra terminating zero
* 2 - an extra slash (in case a syntax like "www.host.com?moo" is used)
*/
Curl_safefree(data->state.pathbuffer);
data->state.pathbuffer = malloc(urllen+2);
if(NULL == data->state.pathbuffer)
return CURLE_OUT_OF_MEMORY; /* really bad error */
data->state.path = data->state.pathbuffer;
conn->host.rawalloc = malloc(urllen+2);
if(NULL == conn->host.rawalloc)
return CURLE_OUT_OF_MEMORY;
conn->host.name = conn->host.rawalloc;
conn->host.name[0] = 0;
result = ParseURLAndFillConnection(data, conn);
if(result != CURLE_OK) {
return result;
}
#ifndef CURL_DISABLE_PROXY
/*************************************************************
* Extract the user and password from the authentication string
*************************************************************/
if(conn->bits.proxy_user_passwd) {
result = parse_proxy_auth(data, conn);
if(result != CURLE_OK)
return result;
}
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
if(data->set.str[STRING_PROXY]) {
proxy = strdup(data->set.str[STRING_PROXY]);
/* if global proxy is set, this is it */
if(NULL == proxy) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
}
if(!proxy)
proxy = detect_proxy(conn);
if(proxy && !*proxy) {
free(proxy); /* Don't bother with an empty proxy string */
proxy = NULL;
}
/* proxy must be freed later unless NULL */
#endif /* CURL_DISABLE_PROXY */
/*************************************************************
* No protocol part in URL was used, add it!
*************************************************************/
if(conn->protocol&PROT_MISSING) {
/* We're guessing prefixes here and if we're told to use a proxy or if
we're gonna follow a Location: later or... then we need the protocol
part added so that we have a valid URL. */
char *reurl;
reurl = aprintf("%s://%s", conn->protostr, data->change.url);
if(!reurl) {
Curl_safefree(proxy);
return CURLE_OUT_OF_MEMORY;
}
data->change.url = reurl;
data->change.url_alloc = TRUE; /* free this later */
conn->protocol &= ~PROT_MISSING; /* switch that one off again */
}
/*************************************************************
* Setup internals depending on protocol
*************************************************************/
result = setup_connection_internals(data, conn);
if(result != CURLE_OK) {
Curl_safefree(proxy);
return result;
}
#ifndef CURL_DISABLE_PROXY
/***********************************************************************
* If this is supposed to use a proxy, we need to figure out the proxy
* host name, so that we can re-use an existing connection
* that may exist registered to the same proxy host.
***********************************************************************/
if(proxy) {
result = parse_proxy(data, conn, proxy);
/* parse_proxy has freed the proxy string, so don't try to use it again */
proxy = NULL;
if(result != CURLE_OK)
return result;
}
#endif /* CURL_DISABLE_PROXY */
/***********************************************************************
* file: is a special case in that it doesn't need a network connection
***********************************************************************/
#ifndef CURL_DISABLE_FILE
if(conn->protocol & PROT_FILE) {
bool done;
/* this is supposed to be the connect function so we better at least check
that the file is present here! */
DEBUGASSERT(conn->handler->connect_it);
result = conn->handler->connect_it(conn, &done);
/* Setup a "faked" transfer that'll do nothing */
if(CURLE_OK == result) {
conn->data = data;
conn->bits.tcpconnect = TRUE; /* we are "connected */
ConnectionStore(data, conn);
/*
* Setup whatever necessary for a resumed transfer
*/
result = setup_range(data);
if(result) {
DEBUGASSERT(conn->handler->done);
/* we ignore the return code for the protocol-specific DONE */
(void)conn->handler->done(conn, result, FALSE);
return result;
}
result = Curl_setup_transfer(conn, -1, -1, FALSE,
NULL, /* no download */
-1, NULL); /* no upload */
}
return result;
}
#endif
/*************************************************************
* If the protocol is using SSL and HTTP proxy is used, we set
* the tunnel_proxy bit.
*************************************************************/
if((conn->protocol&PROT_SSL) && conn->bits.httpproxy)
conn->bits.tunnel_proxy = TRUE;
/*************************************************************
* Parse a user name and password in the URL and strip it out
* of the host name
*************************************************************/
result = parse_url_userpass(data, conn, user, passwd);
if(result != CURLE_OK)
return result;
/*************************************************************
* Figure out the remote port number and fix it in the URL
*************************************************************/
result = parse_remote_port(data, conn);
if(result != CURLE_OK)
return result;
/*************************************************************
* Check for an overridden user name and password, then set it
* for use
*************************************************************/
override_userpass(data, conn, user, passwd);
result = set_userpass(conn, user, passwd);
if(result != CURLE_OK)
return result;
/*************************************************************
* 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.
*************************************************************/
/* Get a cloned copy of the SSL config situation stored in the
connection struct. But to get this going nicely, we must first make
sure that the strings in the master copy are pointing to the correct
strings in the session handle strings array!
Keep in mind that the pointers in the master copy are pointing to strings
that will be freed as part of the SessionHandle struct, but all cloned
copies will be separately allocated.
*/
data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
return CURLE_OUT_OF_MEMORY;
/* reuse_fresh is TRUE if we are told to use a new connection by force, but
we only acknowledge this option if this is not a re-used connection
already (which happens due to follow-location or during a HTTP
authentication phase). */
if(data->set.reuse_fresh && !data->state.this_is_a_follow)
reuse = FALSE;
else
reuse = ConnectionExists(data, conn, &conn_temp);
if(reuse) {
/*
* 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.
*/
reuse_conn(conn, conn_temp);
free(conn); /* we don't need this anymore */
conn = conn_temp;
*in_connect = conn;
infof(data, "Re-using existing connection! (#%ld) with host %s\n",
conn->connectindex,
conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
}
else {
/*
* This is a brand new connection, so let's store it in the connection
* cache of ours!
*/
ConnectionStore(data, conn);
}
/*
* Setup whatever necessary for a resumed transfer
*/
result = setup_range(data);
if(result)
return result;
/* Continue connectdata initialization here. */
/*
* Inherit the proper values from the urldata struct AFTER we have arranged
* the persistent connection stuff
*/
conn->fread_func = data->set.fread_func;
conn->fread_in = data->set.in;
conn->seek_func = data->set.seek_func;
conn->seek_client = data->set.seek_client;
/*************************************************************
* Resolve the address of the server or proxy
*************************************************************/
result = resolve_server(data, conn, addr, async);
return result;
}
|
|||||
| ↓ | Curl_socket_ready | 42 | 115 | 162 | lib/select.c |
int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd,
int timeout_ms)
{
#ifdef HAVE_POLL_FINE
struct pollfd pfd[2];
int num;
#else
struct timeval pending_tv;
struct timeval *ptimeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
curl_socket_t maxfd;
#endif
struct timeval initial_tv = {0,0};
int pending_ms = 0;
int error;
int r;
int ret;
if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) {
r = wait_ms(timeout_ms);
return r;
}
/* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
time in this function does not need to be measured. This happens
when function is called with a zero timeout or a negative timeout
value indicating a blocking call should be performed. */
if(timeout_ms > 0) {
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
}
#ifdef HAVE_POLL_FINE
num = 0;
if(readfd != CURL_SOCKET_BAD) {
pfd[num].fd = readfd;
pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
pfd[num].revents = 0;
num++;
}
if(writefd != CURL_SOCKET_BAD) {
pfd[num].fd = writefd;
pfd[num].events = POLLWRNORM|POLLOUT;
pfd[num].revents = 0;
num++;
}
do {
if(timeout_ms < 0)
pending_ms = -1;
else if(!timeout_ms)
pending_ms = 0;
r = poll(pfd, num, pending_ms);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
if(r < 0)
return -1;
if(r == 0)
return 0;
ret = 0;
num = 0;
if(readfd != CURL_SOCKET_BAD) {
if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
ret |= CURL_CSELECT_IN;
if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
ret |= CURL_CSELECT_ERR;
num++;
}
if(writefd != CURL_SOCKET_BAD) {
if(pfd[num].revents & (POLLWRNORM|POLLOUT))
ret |= CURL_CSELECT_OUT;
if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
ret |= CURL_CSELECT_ERR;
}
return ret;
#else /* HAVE_POLL_FINE */
FD_ZERO(&fds_err);
maxfd = (curl_socket_t)-1;
FD_ZERO(&fds_read);
if(readfd != CURL_SOCKET_BAD) {
VERIFY_SOCK(readfd);
FD_SET(readfd, &fds_read);
FD_SET(readfd, &fds_err);
maxfd = readfd;
}
FD_ZERO(&fds_write);
if(writefd != CURL_SOCKET_BAD) {
VERIFY_SOCK(writefd);
FD_SET(writefd, &fds_write);
FD_SET(writefd, &fds_err);
if(writefd > maxfd)
maxfd = writefd;
}
ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
do {
if(timeout_ms > 0) {
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000;
}
else if(!timeout_ms) {
pending_tv.tv_sec = 0;
pending_tv.tv_usec = 0;
}
r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
if(r < 0)
return -1;
if(r == 0)
return 0;
ret = 0;
if(readfd != CURL_SOCKET_BAD) {
if(FD_ISSET(readfd, &fds_read))
ret |= CURL_CSELECT_IN;
if(FD_ISSET(readfd, &fds_err))
ret |= CURL_CSELECT_ERR;
}
if(writefd != CURL_SOCKET_BAD) {
if(FD_ISSET(writefd, &fds_write))
ret |= CURL_CSELECT_OUT;
if(FD_ISSET(writefd, &fds_err))
ret |= CURL_CSELECT_ERR;
}
return ret;
#endif /* HAVE_POLL_FINE */
}
|
|||||
| ↓ | ftp_state_pasv_resp | 45 | 110 | 264 | lib/ftp.c |
static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
int ftpcode)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result;
struct SessionHandle *data=conn->data;
Curl_addrinfo *conninfo;
struct Curl_dns_entry *addr=NULL;
int rc;
unsigned short connectport; /* the local port connect() should use! */
unsigned short newport=0; /* remote port */
bool connected;
/* newhost must be able to hold a full IP-style address in ASCII, which
in the IPv6 case means 5*8-1 = 39 letters */
#define NEWHOST_BUFSIZE 48
char newhost[NEWHOST_BUFSIZE];
char *str=&data->state.buffer[4]; /* start on the first letter */
if((ftpc->count1 == 0) &&
(ftpcode == 229)) {
/* positive EPSV response */
char *ptr = strchr(str, '(');
if(ptr) {
unsigned int num;
char separator[4];
ptr++;
if(5 == sscanf(ptr, "%c%c%c%u%c",
&separator[0],
&separator[1],
&separator[2],
&num,
&separator[3])) {
const char sep1 = separator[0];
int i;
/* The four separators should be identical, or else this is an oddly
formatted reply and we bail out immediately. */
for(i=1; i<4; i++) {
if(separator[i] != sep1) {
ptr=NULL; /* set to NULL to signal error */
break;
}
}
if(ptr) {
newport = (unsigned short)(num & 0xffff);
if(conn->bits.tunnel_proxy ||
data->set.proxytype == CURLPROXY_SOCKS5 ||
data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
data->set.proxytype == CURLPROXY_SOCKS4 ||
data->set.proxytype == CURLPROXY_SOCKS4A)
/* proxy tunnel -> use other host info because ip_addr_str is the
proxy address not the ftp host */
snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
else
/* use the same IP we are already connected to */
snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
}
}
else
ptr=NULL;
}
if(!ptr) {
failf(data, "Weirdly formatted EPSV reply");
return CURLE_FTP_WEIRD_PASV_REPLY;
}
}
else if((ftpc->count1 == 1) &&
(ftpcode == 227)) {
/* positive PASV response */
int ip[4];
int port[2];
/*
* Scan for a sequence of six comma-separated numbers and use them as
* IP+port indicators.
*
* Found reply-strings include:
* "227 Entering Passive Mode (127,0,0,1,4,51)"
* "227 Data transfer will passively listen to 127,0,0,1,4,51"
* "227 Entering passive mode. 127,0,0,1,4,51"
*/
while(*str) {
if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
&ip[0], &ip[1], &ip[2], &ip[3],
&port[0], &port[1]))
break;
str++;
}
if(!*str) {
failf(data, "Couldn't interpret the 227-response");
return CURLE_FTP_WEIRD_227_FORMAT;
}
/* we got OK from server */
if(data->set.ftp_skip_ip) {
/* told to ignore the remotely given IP but instead use the one we used
for the control connection */
infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
ip[0], ip[1], ip[2], ip[3],
conn->ip_addr_str);
if(conn->bits.tunnel_proxy ||
data->set.proxytype == CURLPROXY_SOCKS5 ||
data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
data->set.proxytype == CURLPROXY_SOCKS4 ||
data->set.proxytype == CURLPROXY_SOCKS4A)
/* proxy tunnel -> use other host info because ip_addr_str is the
proxy address not the ftp host */
snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
else
snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
}
else
snprintf(newhost, sizeof(newhost),
"%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
}
else if(ftpc->count1 == 0) {
/* EPSV failed, move on to PASV */
/* disable it for next transfer */
conn->bits.ftp_use_epsv = FALSE;
infof(data, "disabling EPSV usage\n");
NBFTPSENDF(conn, "PASV", NULL);
ftpc->count1++;
/* remain in the FTP_PASV state */
return result;
}
else {
failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
return CURLE_FTP_WEIRD_PASV_REPLY;
}
if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
/*
* This is a tunnel through a http proxy and we need to connect to the
* proxy again here.
*
* We don't want to rely on a former host lookup that might've expired
* now, instead we remake the lookup here and now!
*/
rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
rc = Curl_wait_for_resolv(conn, &addr);
connectport =
(unsigned short)conn->port; /* we connect to the proxy's port */
if(!addr) {
failf(data, "Can't resolve proxy host %s:%d",
conn->proxy.name, connectport);
return CURLE_FTP_CANT_GET_HOST;
}
}
else {
/* normal, direct, ftp connection */
rc = Curl_resolv(conn, newhost, newport, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
rc = Curl_wait_for_resolv(conn, &addr);
connectport = newport; /* we connect to the remote port */
if(!addr) {
failf(data, "Can't resolve new host %s:%d", newhost, connectport);
return CURLE_FTP_CANT_GET_HOST;
}
}
result = Curl_connecthost(conn,
addr,
&conn->sock[SECONDARYSOCKET],
&conninfo,
&connected);
Curl_resolv_unlock(data, addr); /* we're done using this address */
if(result && ftpc->count1 == 0 && ftpcode == 229) {
infof(data, "got positive EPSV response, but can't connect. "
"Disabling EPSV\n");
/* disable it for next transfer */
conn->bits.ftp_use_epsv = FALSE;
data->state.errorbuf = FALSE; /* allow error message to get rewritten */
NBFTPSENDF(conn, "PASV", NULL);
ftpc->count1++;
/* remain in the FTP_PASV state */
return result;
}
if(result)
return result;
conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
/*
* When this is used from the multi interface, this might've returned with
* the 'connected' set to FALSE and thus we are now awaiting a non-blocking
* connect to connect and we should not be "hanging" here waiting.
*/
if(data->set.verbose)
/* this just dumps information about this second connection */
ftp_pasv_verbose(conn, conninfo, newhost, connectport);
switch(data->set.proxytype) {
#ifndef CURL_DISABLE_PROXY
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
SECONDARYSOCKET, conn);
break;
case CURLPROXY_SOCKS4:
result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
SECONDARYSOCKET, conn, FALSE);
break;
case CURLPROXY_SOCKS4A:
result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
SECONDARYSOCKET, conn, TRUE);
break;
#endif /* CURL_DISABLE_PROXY */
case CURLPROXY_HTTP:
/* do nothing here. handled later. */
break;
default:
failf(data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
break;
}
#ifndef CURL_DISABLE_HTTP
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* FIX: this MUST wait for a proper connect first if 'connected' is
* FALSE */
/* BLOCKING */
/* We want "seamless" FTP operations through HTTP proxy tunnel */
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
* conn->proto.http; we want FTP through HTTP and we have to change the
* member temporarily for connecting to the HTTP proxy. After
* Curl_proxyCONNECT we have to set back the member to the original struct
* FTP pointer
*/
struct HTTP http_proxy;
struct FTP *ftp_save = data->state.proto.ftp;
memset(&http_proxy, 0, sizeof(http_proxy));
data->state.proto.http = &http_proxy;
result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
data->state.proto.ftp = ftp_save;
if(CURLE_OK != result)
return result;
}
#endif /* CURL_DISABLE_HTTP */
state(conn, FTP_STOP); /* this phase is completed */
return result;
}
|
|||||
| ↓ | Curl_httpchunk_read | 53 | 143 | 314 | lib/http_chunks.c |
CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
char *datap,
ssize_t datalen,
ssize_t *wrotep)
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
struct Curl_chunker *ch = &conn->chunk;
struct SingleRequest *k = &data->req;
size_t piece;
size_t length = (size_t)datalen;
size_t *wrote = (size_t *)wrotep;
*wrote = 0; /* nothing's written yet */
/* the original data is written to the client, but we go on with the
chunk read process, to properly calculate the content length*/
if(data->set.http_te_skip && !k->ignorebody) {
result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
if(result)
return CHUNKE_WRITE_ERROR;
}
while(length) {
switch(ch->state) {
case CHUNK_HEX:
/* Check for an ASCII hex digit.
We avoid the use of isxdigit to accommodate non-ASCII hosts. */
if((*datap >= 0x30 && *datap <= 0x39) /* 0-9 */
|| (*datap >= 0x41 && *datap <= 0x46) /* A-F */
|| (*datap >= 0x61 && *datap <= 0x66)) { /* a-f */
if(ch->hexindex < MAXNUM_SIZE) {
ch->hexbuffer[ch->hexindex] = *datap;
datap++;
length--;
ch->hexindex++;
}
else {
return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
}
}
else {
if(0 == ch->hexindex) {
/* This is illegal data, we received junk where we expected
a hexadecimal digit. */
return CHUNKE_ILLEGAL_HEX;
}
/* length and datap are unmodified */
ch->hexbuffer[ch->hexindex]=0;
#ifdef CURL_DOES_CONVERSIONS
/* convert to host encoding before calling strtoul */
result = Curl_convert_from_network(conn->data,
ch->hexbuffer,
ch->hexindex);
if(result != CURLE_OK) {
/* Curl_convert_from_network calls failf if unsuccessful */
/* Treat it as a bad hex character */
return(CHUNKE_ILLEGAL_HEX);
}
#endif /* CURL_DOES_CONVERSIONS */
ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
ch->state = CHUNK_POSTHEX;
}
break;
case CHUNK_POSTHEX:
/* In this state, we're waiting for CRLF to arrive. We support
this to allow so called chunk-extensions to show up here
before the CRLF comes. */
if(*datap == 0x0d)
ch->state = CHUNK_CR;
length--;
datap++;
break;
case CHUNK_CR:
/* waiting for the LF */
if(*datap == 0x0a) {
/* we're now expecting data to come, unless size was zero! */
if(0 == ch->datasize) {
if(k->trailerhdrpresent!=TRUE) {
/* No Trailer: header found - revert to original Curl processing */
ch->state = CHUNK_STOPCR;
/* We need to increment the datap here since we bypass the
increment below with the immediate break */
length--;
datap++;
/* This is the final byte, continue to read the final CRLF */
break;
}
else {
ch->state = CHUNK_TRAILER; /* attempt to read trailers */
conn->trlPos=0;
}
}
else {
ch->state = CHUNK_DATA;
}
}
else
/* previously we got a fake CR, go back to CR waiting! */
ch->state = CHUNK_CR;
datap++;
length--;
break;
case CHUNK_DATA:
/* we get pure and fine data
We expect another 'datasize' of data. We have 'length' right now,
it can be more or less than 'datasize'. Get the smallest piece.
*/
piece = (ch->datasize >= length)?length:ch->datasize;
/* Write the data portion available */
#ifdef HAVE_LIBZ
switch (conn->data->set.http_ce_skip?
IDENTITY : data->req.content_encoding) {
case IDENTITY:
#endif
if(!k->ignorebody) {
if( !data->set.http_te_skip )
result = Curl_client_write(conn, CLIENTWRITE_BODY, datap,
piece);
else
result = CURLE_OK;
}
#ifdef HAVE_LIBZ
break;
case DEFLATE:
/* update data->req.keep.str to point to the chunk data. */
data->req.str = datap;
result = Curl_unencode_deflate_write(conn, &data->req,
(ssize_t)piece);
break;
case GZIP:
/* update data->req.keep.str to point to the chunk data. */
data->req.str = datap;
result = Curl_unencode_gzip_write(conn, &data->req,
(ssize_t)piece);
break;
case COMPRESS:
default:
failf (conn->data,
"Unrecognized content encoding type. "
"libcurl understands `identity', `deflate' and `gzip' "
"content encodings.");
return CHUNKE_BAD_ENCODING;
}
#endif
if(result)
return CHUNKE_WRITE_ERROR;
*wrote += piece;
ch->datasize -= piece; /* decrease amount left to expect */
datap += piece; /* move read pointer forward */
length -= piece; /* decrease space left in this round */
if(0 == ch->datasize)
/* end of data this round, we now expect a trailing CRLF */
ch->state = CHUNK_POSTCR;
break;
case CHUNK_POSTCR:
if(*datap == 0x0d) {
ch->state = CHUNK_POSTLF;
datap++;
length--;
}
else {
return CHUNKE_BAD_CHUNK;
}
break;
case CHUNK_POSTLF:
if(*datap == 0x0a) {
/*
* The last one before we go back to hex state and start all
* over.
*/
Curl_httpchunk_init(conn);
datap++;
length--;
}
else {
return CHUNKE_BAD_CHUNK;
}
break;
case CHUNK_TRAILER:
/* conn->trailer is assumed to be freed in url.c on a
connection basis */
if(conn->trlPos >= conn->trlMax) {
char *ptr;
if(conn->trlMax) {
conn->trlMax *= 2;
ptr = realloc(conn->trailer,conn->trlMax);
}
else {
conn->trlMax=128;
ptr = malloc(conn->trlMax);
}
if(!ptr)
return CHUNKE_OUT_OF_MEMORY;
conn->trailer = ptr;
}
conn->trailer[conn->trlPos++]=*datap;
if(*datap == 0x0d)
ch->state = CHUNK_TRAILER_CR;
else {
datap++;
length--;
}
break;
case CHUNK_TRAILER_CR:
if(*datap == 0x0d) {
ch->state = CHUNK_TRAILER_POSTCR;
datap++;
length--;
}
else
return CHUNKE_BAD_CHUNK;
break;
case CHUNK_TRAILER_POSTCR:
if(*datap == 0x0a) {
conn->trailer[conn->trlPos++]=0x0a;
conn->trailer[conn->trlPos]=0;
if(conn->trlPos==2) {
ch->state = CHUNK_STOP;
datap++;
length--;
/*
* Note that this case skips over the final STOP states since we've
* already read the final CRLF and need to return
*/
ch->dataleft = length;
return CHUNKE_STOP; /* return stop */
}
else {
#ifdef CURL_DOES_CONVERSIONS
/* Convert to host encoding before calling Curl_client_write */
result = Curl_convert_from_network(conn->data,
conn->trailer,
conn->trlPos);
if(result != CURLE_OK) {
/* Curl_convert_from_network calls failf if unsuccessful */
/* Treat it as a bad chunk */
return(CHUNKE_BAD_CHUNK);
}
#endif /* CURL_DOES_CONVERSIONS */
if(!data->set.http_te_skip) {
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
conn->trailer, conn->trlPos);
if(result)
return CHUNKE_WRITE_ERROR;
}
}
ch->state = CHUNK_TRAILER;
conn->trlPos=0;
datap++;
length--;
}
else
return CHUNKE_BAD_CHUNK;
break;
case CHUNK_STOPCR:
/* Read the final CRLF that ends all chunk bodies */
if(*datap == 0x0d) {
ch->state = CHUNK_STOP;
datap++;
length--;
}
else {
return CHUNKE_BAD_CHUNK;
}
break;
case CHUNK_STOP:
if(*datap == 0x0a) {
datap++;
length--;
/* Record the length of any data left in the end of the buffer
even if there's no more chunks to read */
ch->dataleft = length;
return CHUNKE_STOP; /* return stop */
}
else {
return CHUNKE_BAD_CHUNK;
}
default:
return CHUNKE_STATE_ERROR;
}
}
return CHUNKE_OK;
}
|
|||||
| ↓ | ftp_done | 49 | 76 | 191 | lib/ftp.c |
static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
bool premature)
{
struct SessionHandle *data = conn->data;
struct FTP *ftp = data->state.proto.ftp;
struct ftp_conn *ftpc = &conn->proto.ftpc;
ssize_t nread;
int ftpcode;
CURLcode result=CURLE_OK;
bool was_ctl_valid = ftpc->ctl_valid;
char *path;
char *path_to_use = data->state.path;
if(!ftp)
/* When the easy handle is removed from the multi while libcurl is still
* trying to resolve the host name, it seems that the ftp struct is not
* yet initialized, but the removal action calls Curl_done() which calls
* this function. So we simply return success if no ftp pointer is set.
*/
return CURLE_OK;
switch(status) {
case CURLE_BAD_DOWNLOAD_RESUME:
case CURLE_FTP_WEIRD_PASV_REPLY:
case CURLE_FTP_PORT_FAILED:
case CURLE_FTP_COULDNT_SET_TYPE:
case CURLE_FTP_COULDNT_RETR_FILE:
case CURLE_UPLOAD_FAILED:
case CURLE_REMOTE_ACCESS_DENIED:
case CURLE_FILESIZE_EXCEEDED:
case CURLE_REMOTE_FILE_NOT_FOUND:
/* the connection stays alive fine even though this happened */
/* fall-through */
case CURLE_OK: /* doesn't affect the control connection's status */
if(!premature) {
ftpc->ctl_valid = was_ctl_valid;
break;
}
/* until we cope better with prematurely ended requests, let them
* fallback as if in complete failure */
default: /* by default, an error means the control connection is
wedged and should not be used anymore */
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
current path, as this connection is going */
conn->bits.close = TRUE; /* marked for closure */
break;
}
/* now store a copy of the directory we are in */
if(ftpc->prevpath)
free(ftpc->prevpath);
/* get the "raw" path */
path = curl_easy_unescape(data, path_to_use, 0, NULL);
if(!path) {
/* out of memory, but we can limp along anyway (and should try to
* since we're in the out of memory cleanup path) */
ftpc->prevpath = NULL; /* no path */
} else {
size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
size_t dlen = strlen(path)-flen;
if(!ftpc->cwdfail) {
if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
ftpc->prevpath = path;
if(flen)
/* if 'path' is not the whole string */
ftpc->prevpath[dlen]=0; /* terminate */
}
else {
/* we never changed dir */
ftpc->prevpath=strdup("");
free(path);
}
infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
}
else {
ftpc->prevpath = NULL; /* no path */
free(path);
}
}
/* free the dir tree and file parts */
freedirs(ftpc);
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
#endif
/* shut down the socket to inform the server we're done */
#ifdef _WIN32_WCE
shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
#endif
if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
if(conn->ssl[SECONDARYSOCKET].use) {
/* The secondary socket is using SSL so we must close down that part first
before we close the socket for real */
Curl_ssl_close(conn, SECONDARYSOCKET);
/* Note that we keep "use" set to TRUE since that (next) connection is
still requested to use SSL */
}
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
if((ftp->transfer == FTPTRANSFER_BODY) && !status && !premature) {
/*
* Let's see what the server says about the transfer we just performed,
* but lower the timeout as sometimes this connection has died while the
* data has been transfered. This happens when doing through NATs etc that
* abandon old silent connections.
*/
long old_time = ftpc->response_time;
ftpc->response_time = 60*1000; /* give it only a minute for now */
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
ftpc->response_time = old_time; /* set this back to previous value */
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
conn->bits.close = TRUE; /* mark for closure */
}
if(result)
return result;
if(!ftpc->dont_check) {
/* 226 Transfer complete, 250 Requested file action okay, completed. */
if((ftpcode != 226) && (ftpcode != 250)) {
failf(data, "server did not report OK, got %d", ftpcode);
result = CURLE_PARTIAL_FILE;
}
}
}
if(result || premature)
/* the response code from the transfer showed an error already so no
use checking further */
;
else if(data->set.upload) {
if((-1 != data->set.infilesize) &&
(data->set.infilesize != *ftp->bytecountp) &&
!data->set.crlf &&
(ftp->transfer == FTPTRANSFER_BODY)) {
failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
" out of %" FORMAT_OFF_T " bytes)",
*ftp->bytecountp, data->set.infilesize);
result = CURLE_PARTIAL_FILE;
}
}
else {
if((-1 != data->req.size) &&
(data->req.size != *ftp->bytecountp) &&
#ifdef CURL_DO_LINEEND_CONV
/* Most FTP servers don't adjust their file SIZE response for CRLFs, so
* we'll check to see if the discrepancy can be explained by the number
* of CRLFs we've changed to LFs.
*/
((data->req.size + data->state.crlf_conversions) !=
*ftp->bytecountp) &&
#endif /* CURL_DO_LINEEND_CONV */
(data->req.maxdownload != *ftp->bytecountp)) {
failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
*ftp->bytecountp);
result = CURLE_PARTIAL_FILE;
}
else if(!ftpc->dont_check &&
!*ftp->bytecountp &&
(data->req.size>0)) {
failf(data, "No data was received!");
result = CURLE_FTP_COULDNT_RETR_FILE;
}
}
/* clear these for next connection */
ftp->transfer = FTPTRANSFER_BODY;
ftpc->dont_check = FALSE;
/* Send any post-transfer QUOTE strings? */
if(!status && !result && !premature && data->set.postquote)
result = ftp_sendquote(conn, data->set.postquote);
return result;
}
|
|||||
| ↓ | Curl_input_digest | 40 | 88 | 155 | lib/http_digest.c |
CURLdigest Curl_input_digest(struct connectdata *conn,
bool proxy,
const char *header) /* rest of the *-authenticate:
header */
{
bool more = TRUE;
char *token = NULL;
char *tmp = NULL;
bool foundAuth = FALSE;
bool foundAuthInt = FALSE;
struct SessionHandle *data=conn->data;
bool before = FALSE; /* got a nonce before */
struct digestdata *d;
if(proxy) {
d = &data->state.proxydigest;
}
else {
d = &data->state.digest;
}
/* skip initial whitespaces */
while(*header && ISSPACE(*header))
header++;
if(checkprefix("Digest", header)) {
header += strlen("Digest");
/* If we already have received a nonce, keep that in mind */
if(d->nonce)
before = TRUE;
/* clear off any former leftovers and init to defaults */
Curl_digest_cleanup_one(d);
while(more) {
char value[256];
char content[1024];
size_t totlen=0;
while(*header && ISSPACE(*header))
header++;
/* how big can these strings be? */
if((2 == sscanf(header, "%255[^=]=\"%1023[^\"]\"",
value, content)) ||
/* try the same scan but without quotes around the content but don't
include the possibly trailing comma, newline or carriage return */
(2 == sscanf(header, "%255[^=]=%1023[^\r\n,]",
value, content)) ) {
if(!strcmp("\"\"", content)) {
/* for the name="" case where we get only the "" in the content variable,
* simply clear the content then
*/
content[0]=0;
}
if(strequal(value, "nonce")) {
d->nonce = strdup(content);
if(!d->nonce)
return CURLDIGEST_NOMEM;
}
else if(strequal(value, "stale")) {
if(strequal(content, "true")) {
d->stale = TRUE;
d->nc = 1; /* we make a new nonce now */
}
}
else if(strequal(value, "realm")) {
d->realm = strdup(content);
if(!d->realm)
return CURLDIGEST_NOMEM;
}
else if(strequal(value, "opaque")) {
d->opaque = strdup(content);
if(!d->opaque)
return CURLDIGEST_NOMEM;
}
else if(strequal(value, "qop")) {
char *tok_buf;
/* tokenize the list and choose auth if possible, use a temporary
clone of the buffer since strtok_r() ruins it */
tmp = strdup(content);
if(!tmp)
return CURLDIGEST_NOMEM;
token = strtok_r(tmp, ",", &tok_buf);
while(token != NULL) {
if(strequal(token, "auth")) {
foundAuth = TRUE;
}
else if(strequal(token, "auth-int")) {
foundAuthInt = TRUE;
}
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
/*select only auth o auth-int. Otherwise, ignore*/
if(foundAuth) {
d->qop = strdup("auth");
if(!d->qop)
return CURLDIGEST_NOMEM;
}
else if(foundAuthInt) {
d->qop = strdup("auth-int");
if(!d->qop)
return CURLDIGEST_NOMEM;
}
}
else if(strequal(value, "algorithm")) {
d->algorithm = strdup(content);
if(!d->algorithm)
return CURLDIGEST_NOMEM;
if(strequal(content, "MD5-sess"))
d->algo = CURLDIGESTALGO_MD5SESS;
else if(strequal(content, "MD5"))
d->algo = CURLDIGESTALGO_MD5;
else
return CURLDIGEST_BADALGO;
}
else {
/* unknown specifier, ignore it! */
}
totlen = strlen(value)+strlen(content)+1;
if(header[strlen(value)+1] == '\"')
/* the contents were within quotes, then add 2 for them to the
length */
totlen += 2;
}
else
break; /* we're done here */
header += totlen;
/* pass all additional spaces here */
while(*header && ISSPACE(*header))
header++;
if(',' == *header)
/* allow the list to be comma-separated */
header++;
}
/* We had a nonce since before, and we got another one now without
'stale=true'. This means we provided bad credentials in the previous
request */
if(before && !d->stale)
return CURLDIGEST_BAD;
/* We got this header without a nonce, that's a bad Digest line! */
if(!d->nonce)
return CURLDIGEST_BAD;
}
else
/* else not a digest, get out */
return CURLDIGEST_NONE;
return CURLDIGEST_FINE;
}
|
|||||
| ↓ | ossl_connect_step1 | 41 | 91 | 233 | lib/ssluse.c |
static CURLcode
ossl_connect_step1(struct connectdata *conn,
int sockindex)
{
CURLcode retcode = CURLE_OK;
struct SessionHandle *data = conn->data;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
void *ssl_sessionid=NULL;
X509_LOOKUP *lookup=NULL;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
#endif
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
/* Make funny stuff to get random input */
Curl_ossl_seed(data);
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
/* we try to figure out version */
req_method = SSLv23_client_method();
break;
case CURL_SSLVERSION_TLSv1:
req_method = TLSv1_client_method();
break;
case CURL_SSLVERSION_SSLv2:
req_method = SSLv2_client_method();
break;
case CURL_SSLVERSION_SSLv3:
req_method = SSLv3_client_method();
break;
}
if(connssl->ctx)
SSL_CTX_free(connssl->ctx);
connssl->ctx = SSL_CTX_new(req_method);
if(!connssl->ctx) {
failf(data, "SSL: couldn't create a context!");
return CURLE_OUT_OF_MEMORY;
}
#ifdef SSL_CTRL_SET_MSG_CALLBACK
if(data->set.fdebug && data->set.verbose) {
/* the SSL trace callback is only used for verbose logging so we only
inform about failures of setting it */
if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
(void (*)(void))ssl_tls_trace)) {
infof(data, "SSL: couldn't set callback!\n");
}
else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
conn)) {
infof(data, "SSL: couldn't set callback argument!\n");
}
}
#endif
/* OpenSSL contains code to work-around lots of bugs and flaws in various
SSL-implementations. SSL_CTX_set_options() is used to enabled those
work-arounds. The man page for this option states that SSL_OP_ALL enables
all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
enable the bug workaround options if compatibility with somewhat broken
implementations is desired."
*/
SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL);
/* disable SSLv2 in the default case (i.e. allow SSLv3 and TLSv1) */
if(data->set.ssl.version == CURL_SSLVERSION_DEFAULT)
SSL_CTX_set_options(connssl->ctx, SSL_OP_NO_SSLv2);
#if 0
/*
* Not sure it's needed to tell SSL_connect() that socket is
* non-blocking. It doesn't seem to care, but just return with
* SSL_ERROR_WANT_x.
*/
if(data->state.used_interface == Curl_if_multi)
SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
#endif
if(data->set.str[STRING_CERT]) {
if(!cert_stuff(conn,
connssl->ctx,
data->set.str[STRING_CERT],
data->set.str[STRING_CERT_TYPE],
data->set.str[STRING_KEY],
data->set.str[STRING_KEY_TYPE])) {
/* failf() is already done in cert_stuff() */
return CURLE_SSL_CERTPROBLEM;
}
}
if(data->set.str[STRING_SSL_CIPHER_LIST]) {
if(!SSL_CTX_set_cipher_list(connssl->ctx,
data->set.str[STRING_SSL_CIPHER_LIST])) {
failf(data, "failed setting cipher list");
return CURLE_SSL_CIPHER;
}
}
if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
/* tell SSL where to find CA certificates that are used to verify
the servers certificate. */
if(!SSL_CTX_load_verify_locations(connssl->ctx,
data->set.str[STRING_SSL_CAFILE],
data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
/* Fail if we insist on successfully verifying the server. */
failf(data,"error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s\n",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
data->set.str[STRING_SSL_CAPATH]?
data->set.str[STRING_SSL_CAPATH] : "none");
return CURLE_SSL_CACERT_BADFILE;
}
else {
/* Just continue with a warning if no strict certificate verification
is required. */
infof(data, "error setting certificate verify locations,"
" continuing anyway:\n");
}
}
else {
/* Everything is fine. */
infof(data, "successfully set certificate verify locations:\n");
}
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
"none",
data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
"none");
}
if (data->set.str[STRING_SSL_CRLFILE]) {
/* tell SSL where to find CRL file that is used to check certificate
* revocation */
lookup=X509_STORE_add_lookup(connssl->ctx->cert_store,X509_LOOKUP_file());
if ( !lookup ||
(X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
X509_FILETYPE_PEM)!=1) ) {
failf(data,"error loading CRL file :\n"
" CRLfile: %s\n",
data->set.str[STRING_SSL_CRLFILE]?
data->set.str[STRING_SSL_CRLFILE]: "none");
return CURLE_SSL_CRL_BADFILE;
}
else {
/* Everything is fine. */
infof(data, "successfully load CRL file:\n");
X509_STORE_set_flags(connssl->ctx->cert_store,
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
}
infof(data,
" CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
data->set.str[STRING_SSL_CRLFILE]: "none");
}
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
SSL_CTX_set_verify(connssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
cert_verify_callback);
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
data->set.ssl.fsslctxp);
if(retcode) {
failf(data,"error signaled by ssl ctx callback");
return retcode;
}
}
/* Lets make an SSL structure */
if(connssl->handle)
SSL_free(connssl->handle);
connssl->handle = SSL_new(connssl->ctx);
if(!connssl->handle) {
failf(data, "SSL: couldn't create a context (handle)!");
return CURLE_OUT_OF_MEMORY;
}
SSL_set_connect_state(connssl->handle);
connssl->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if ((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
#endif
!SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
infof(data, "WARNING: failed to configure server name indication (SNI) "
"TLS extension\n");
#endif
/* Check if there's a cached ID we can/should use here! */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
/* we got a session id, use it! */
if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
infof (data, "SSL re-using session ID\n");
}
/* pass the raw socket into the SSL layers */
if(!SSL_set_fd(connssl->handle, sockfd)) {
failf(data, "SSL: SSL_set_fd failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
|
|||||
| ↓ | telnet_do | 41 | 153 | 285 | lib/telnet.c |
static CURLcode telnet_do(struct connectdata *conn, bool *done)
{
CURLcode code;
struct SessionHandle *data = conn->data;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
#ifdef USE_WINSOCK
HMODULE wsock2;
WSOCK2_FUNC close_event_func;
WSOCK2_FUNC create_event_func;
WSOCK2_FUNC event_select_func;
WSOCK2_FUNC enum_netevents_func;
WSAEVENT event_handle;
WSANETWORKEVENTS events;
HANDLE stdin_handle;
HANDLE objs[2];
DWORD obj_count;
DWORD wait_timeout;
DWORD waitret;
DWORD readfile_read;
#else
int interval_ms;
struct pollfd pfd[2];
#endif
ssize_t nread;
bool keepon = TRUE;
char *buf = data->state.buffer;
struct TELNET *tn;
*done = TRUE; /* unconditionally */
code = init_telnet(conn);
if(code)
return code;
tn = (struct TELNET *)data->state.proto.telnet;
code = check_telnet_options(conn);
if(code)
return code;
#ifdef USE_WINSOCK
/*
** This functionality only works with WinSock >= 2.0. So,
** make sure have it.
*/
code = check_wsock2(data);
if(code)
return code;
/* OK, so we have WinSock 2.0. We need to dynamically */
/* load ws2_32.dll and get the function pointers we need. */
wsock2 = LoadLibrary("WS2_32.DLL");
if(wsock2 == NULL) {
failf(data,"failed to load WS2_32.DLL (%d)", ERRNO);
return CURLE_FAILED_INIT;
}
/* Grab a pointer to WSACreateEvent */
create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
if(create_event_func == NULL) {
failf(data,"failed to find WSACreateEvent function (%d)",
ERRNO);
FreeLibrary(wsock2);
return CURLE_FAILED_INIT;
}
/* And WSACloseEvent */
close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
if(close_event_func == NULL) {
failf(data,"failed to find WSACloseEvent function (%d)",
ERRNO);
FreeLibrary(wsock2);
return CURLE_FAILED_INIT;
}
/* And WSAEventSelect */
event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
if(event_select_func == NULL) {
failf(data,"failed to find WSAEventSelect function (%d)",
ERRNO);
FreeLibrary(wsock2);
return CURLE_FAILED_INIT;
}
/* And WSAEnumNetworkEvents */
enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
if(enum_netevents_func == NULL) {
failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
ERRNO);
FreeLibrary(wsock2);
return CURLE_FAILED_INIT;
}
/* We want to wait for both stdin and the socket. Since
** the select() function in winsock only works on sockets
** we have to use the WaitForMultipleObjects() call.
*/
/* First, create a sockets event object */
event_handle = (WSAEVENT)create_event_func();
if(event_handle == WSA_INVALID_EVENT) {
failf(data,"WSACreateEvent failed (%d)", SOCKERRNO);
FreeLibrary(wsock2);
return CURLE_FAILED_INIT;
}
/* The get the Windows file handle for stdin */
stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
/* Create the list of objects to wait for */
objs[0] = event_handle;
objs[1] = stdin_handle;
/* Tell winsock what events we want to listen to */
if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
close_event_func(event_handle);
FreeLibrary(wsock2);
return CURLE_OK;
}
/* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
else use the old WaitForMultipleObjects() way */
if(GetFileType(stdin_handle) == FILE_TYPE_PIPE) {
/* Don't wait for stdin_handle, just wait for event_handle */
obj_count = 1;
/* Check stdin_handle per 100 milliseconds */
wait_timeout = 100;
} else {
obj_count = 2;
wait_timeout = INFINITE;
}
/* Keep on listening and act on events */
while(keepon) {
waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
switch(waitret) {
case WAIT_TIMEOUT:
{
while(1) {
if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) {
keepon = FALSE;
code = CURLE_READ_ERROR;
break;
}
if(!readfile_read)
break;
if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
&readfile_read, NULL)) {
keepon = FALSE;
code = CURLE_READ_ERROR;
break;
}
code = send_telnet_data(conn, buf, readfile_read);
if(code) {
keepon = FALSE;
break;
}
}
}
break;
case WAIT_OBJECT_0 + 1:
{
if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
&readfile_read, NULL)) {
keepon = FALSE;
code = CURLE_READ_ERROR;
break;
}
code = send_telnet_data(conn, buf, readfile_read);
if(code) {
keepon = FALSE;
break;
}
}
break;
case WAIT_OBJECT_0:
if(enum_netevents_func(sockfd, event_handle, &events)
!= SOCKET_ERROR) {
if(events.lNetworkEvents & FD_READ) {
/* This reallu OUGHT to check its return code. */
(void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
telrcv(conn, (unsigned char *)buf, nread);
fflush(stdout);
/* 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(tn->please_negotiate && !tn->already_negotiated) {
negotiate(conn);
tn->already_negotiated = 1;
}
}
if(events.lNetworkEvents & FD_CLOSE) {
keepon = FALSE;
}
}
break;
}
}
/* We called WSACreateEvent, so call WSACloseEvent */
if(close_event_func(event_handle) == FALSE) {
infof(data,"WSACloseEvent failed (%d)", SOCKERRNO);
}
/* "Forget" pointers into the library we're about to free */
create_event_func = NULL;
close_event_func = NULL;
event_select_func = NULL;
enum_netevents_func = NULL;
/* We called LoadLibrary, so call FreeLibrary */
if(!FreeLibrary(wsock2))
infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO);
#else
pfd[0].fd = sockfd;
pfd[0].events = POLLIN;
pfd[1].fd = 0;
pfd[1].events = POLLIN;
interval_ms = 1 * 1000;
while(keepon) {
switch (Curl_poll(pfd, 2, interval_ms)) {
case -1: /* error, stop reading */
keepon = FALSE;
continue;
case 0: /* timeout */
break;
default: /* read! */
if(pfd[1].revents & POLLIN) { /* read from stdin */
nread = read(0, buf, 255);
code = send_telnet_data(conn, buf, nread);
if(code) {
keepon = FALSE;
break;
}
}
if(pfd[0].revents & POLLIN) {
/* This OUGHT to check the return code... */
(void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
/* if we receive 0 or less here, the server closed the connection and
we bail out from this! */
if(nread <= 0) {
keepon = FALSE;
break;
}
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(tn->please_negotiate && !tn->already_negotiated) {
negotiate(conn);
tn->already_negotiated = 1;
}
}
}
if(data->set.timeout) {
struct timeval now; /* current time */
now = Curl_tvnow();
if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
failf(data, "Time-out");
code = CURLE_OPERATION_TIMEDOUT;
keepon = FALSE;
}
}
}
#endif
/* mark this as "no further transfer wanted" */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
return code;
}
|
|||||
| ↓ | Curl_poll | 38 | 101 | 133 | lib/select.c |
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
{
#ifndef HAVE_POLL_FINE
struct timeval pending_tv;
struct timeval *ptimeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
curl_socket_t maxfd;
#endif
struct timeval initial_tv = {0,0};
bool fds_none = TRUE;
unsigned int i;
int pending_ms = 0;
int error;
int r;
if(ufds) {
for (i = 0; i < nfds; i++) {
if(ufds[i].fd != CURL_SOCKET_BAD) {
fds_none = FALSE;
break;
}
}
}
if(fds_none) {
r = wait_ms(timeout_ms);
return r;
}
/* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
time in this function does not need to be measured. This happens
when function is called with a zero timeout or a negative timeout
value indicating a blocking call should be performed. */
if(timeout_ms > 0) {
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
}
#ifdef HAVE_POLL_FINE
do {
if(timeout_ms < 0)
pending_ms = -1;
else if(!timeout_ms)
pending_ms = 0;
r = poll(ufds, nfds, pending_ms);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
#else /* HAVE_POLL_FINE */
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
FD_ZERO(&fds_err);
maxfd = (curl_socket_t)-1;
for (i = 0; i < nfds; i++) {
ufds[i].revents = 0;
if(ufds[i].fd == CURL_SOCKET_BAD)
continue;
VERIFY_SOCK(ufds[i].fd);
if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
if(ufds[i].fd > maxfd)
maxfd = ufds[i].fd;
if(ufds[i].events & (POLLRDNORM|POLLIN))
FD_SET(ufds[i].fd, &fds_read);
if(ufds[i].events & (POLLWRNORM|POLLOUT))
FD_SET(ufds[i].fd, &fds_write);
if(ufds[i].events & (POLLRDBAND|POLLPRI))
FD_SET(ufds[i].fd, &fds_err);
}
}
ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
do {
if(timeout_ms > 0) {
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000;
}
else if(!timeout_ms) {
pending_tv.tv_sec = 0;
pending_tv.tv_usec = 0;
}
r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
if(r != -1)
break;
error = SOCKERRNO;
if(error && error_not_EINTR)
break;
if(timeout_ms > 0) {
pending_ms = timeout_ms - elapsed_ms;
if(pending_ms <= 0)
break;
}
} while(r == -1);
if(r < 0)
return -1;
if(r == 0)
return 0;
r = 0;
for (i = 0; i < nfds; i++) {
ufds[i].revents = 0;
if(ufds[i].fd == CURL_SOCKET_BAD)
continue;
if(FD_ISSET(ufds[i].fd, &fds_read))
ufds[i].revents |= POLLIN;
if(FD_ISSET(ufds[i].fd, &fds_write))
ufds[i].revents |= POLLOUT;
if(FD_ISSET(ufds[i].fd, &fds_err))
ufds[i].revents |= POLLPRI;
if(ufds[i].revents != 0)
r++;
}
#endif /* HAVE_POLL_FINE */
return r;
}
|
|||||
| ↓ | dict_do | 36 | 84 | 158 | lib/dict.c |
static CURLcode dict_do(struct connectdata *conn, bool *done)
{
char *word;
char *eword;
char *ppath;
char *database = NULL;
char *strategy = NULL;
char *nthdef = NULL; /* This is not part of the protocol, but required
by RFC 2229 */
CURLcode result=CURLE_OK;
struct SessionHandle *data=conn->data;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
char *path = data->state.path;
curl_off_t *bytecount = &data->req.bytecount;
*done = TRUE; /* unconditionally */
if(conn->bits.user_passwd) {
/* AUTH is missing */
}
if(strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
word = strchr(path, ':');
if(word) {
word++;
database = strchr(word, ':');
if(database) {
*database++ = (char)0;
strategy = strchr(database, ':');
if(strategy) {
*strategy++ = (char)0;
nthdef = strchr(strategy, ':');
if(nthdef) {
*nthdef++ = (char)0;
}
}
}
}
if((word == NULL) || (*word == (char)0)) {
infof(data, "lookup word is missing");
word=(char *)"default";
}
if((database == NULL) || (*database == (char)0)) {
database = (char *)"!";
}
if((strategy == NULL) || (*strategy == (char)0)) {
strategy = (char *)".";
}
eword = unescape_word(data, word);
if(!eword)
return CURLE_OUT_OF_MEMORY;
result = Curl_sendf(sockfd, conn,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
"MATCH "
"%s " /* database */
"%s " /* strategy */
"%s\r\n" /* word */
"QUIT\r\n",
database,
strategy,
eword
);
free(eword);
if(result)
failf(data, "Failed sending DICT request");
else
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-1, NULL); /* no upload */
if(result)
return result;
}
else if(strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
word = strchr(path, ':');
if(word) {
word++;
database = strchr(word, ':');
if(database) {
*database++ = (char)0;
nthdef = strchr(database, ':');
if(nthdef) {
*nthdef++ = (char)0;
}
}
}
if((word == NULL) || (*word == (char)0)) {
infof(data, "lookup word is missing");
word=(char *)"default";
}
if((database == NULL) || (*database == (char)0)) {
database = (char *)"!";
}
eword = unescape_word(data, word);
if(!eword)
return CURLE_OUT_OF_MEMORY;
result = Curl_sendf(sockfd, conn,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
"DEFINE "
"%s " /* database */
"%s\r\n" /* word */
"QUIT\r\n",
database,
eword);
free(eword);
if(result)
failf(data, "Failed sending DICT request");
else
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-1, NULL); /* no upload */
if(result)
return result;
}
else {
ppath = strchr(path, '/');
if(ppath) {
int i;
ppath++;
for (i = 0; ppath[i]; i++) {
if(ppath[i] == ':')
ppath[i] = ' ';
}
result = Curl_sendf(sockfd, conn,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
"%s\r\n"
"QUIT\r\n", ppath);
if(result)
failf(data, "Failed sending DICT request");
else
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-1, NULL);
if(result)
return result;
}
}
return CURLE_OK;
}
|
|||||
| ↓ | Curl_output_ntlm | 37 | 229 | 594 | lib/http_ntlm.c |
CURLcode Curl_output_ntlm(struct connectdata *conn,
bool proxy)
{
const char *domain=""; /* empty */
char host [HOSTNAME_MAX+ 1] = ""; /* empty */
#ifndef USE_WINDOWS_SSPI
size_t domlen = strlen(domain);
size_t hostlen = strlen(host);
size_t hostoff; /* host name offset */
size_t domoff; /* domain name offset */
#endif
size_t size;
char *base64=NULL;
unsigned char ntlmbuf[1024]; /* enough, unless the user+host+domain is very
long */
/* point to the address of the pointer that holds the string to sent to the
server, which is for a plain host or for a HTTP proxy */
char **allocuserpwd;
/* point to the name and password for this */
const char *userp;
const char *passwdp;
/* point to the correct struct with this */
struct ntlmdata *ntlm;
struct auth *authp;
DEBUGASSERT(conn);
DEBUGASSERT(conn->data);
if(proxy) {
allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->proxyuser;
passwdp = conn->proxypasswd;
ntlm = &conn->proxyntlm;
authp = &conn->data->state.authproxy;
}
else {
allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user;
passwdp = conn->passwd;
ntlm = &conn->ntlm;
authp = &conn->data->state.authhost;
}
authp->done = FALSE;
/* not set means empty */
if(!userp)
userp="";
if(!passwdp)
passwdp="";
#ifdef USE_WINDOWS_SSPI
if (s_hSecDll == NULL) {
/* not thread safe and leaks - use curl_global_init() to avoid */
CURLcode err = Curl_ntlm_global_init();
if (s_hSecDll == NULL)
return err;
}
#endif
switch(ntlm->state) {
case NTLMSTATE_TYPE1:
default: /* for the weird cases we (re)start here */
#ifdef USE_WINDOWS_SSPI
{
SecBuffer buf;
SecBufferDesc desc;
SECURITY_STATUS status;
ULONG attrs;
const char *user;
int domlen;
TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
ntlm_sspi_cleanup(ntlm);
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if(user) {
domain = userp;
domlen = user - userp;
user++;
}
else {
user = userp;
domain = "";
domlen = 0;
}
if(user && *user) {
/* note: initialize all of this before doing the mallocs so that
* it can be cleaned up later without leaking memory.
*/
ntlm->p_identity = &ntlm->identity;
memset(ntlm->p_identity, 0, sizeof(*ntlm->p_identity));
if((ntlm->identity.User = (unsigned char *)strdup(user)) == NULL)
return CURLE_OUT_OF_MEMORY;
ntlm->identity.UserLength = strlen(user);
if((ntlm->identity.Password = (unsigned char *)strdup(passwdp)) == NULL)
return CURLE_OUT_OF_MEMORY;
ntlm->identity.PasswordLength = strlen(passwdp);
if((ntlm->identity.Domain = malloc(domlen+1)) == NULL)
return CURLE_OUT_OF_MEMORY;
strncpy((char *)ntlm->identity.Domain, domain, domlen);
ntlm->identity.Domain[domlen] = '\0';
ntlm->identity.DomainLength = domlen;
ntlm->identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
}
else {
ntlm->p_identity = NULL;
}
if(s_pSecFn->AcquireCredentialsHandle(
NULL, (char *)"NTLM", SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity,
NULL, NULL, &ntlm->handle, &tsDummy
) != SEC_E_OK) {
return CURLE_OUT_OF_MEMORY;
}
desc.ulVersion = SECBUFFER_VERSION;
desc.cBuffers = 1;
desc.pBuffers = &buf;
buf.cbBuffer = sizeof(ntlmbuf);
buf.BufferType = SECBUFFER_TOKEN;
buf.pvBuffer = ntlmbuf;
status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, NULL,
(char *) host,
ISC_REQ_CONFIDENTIALITY |
ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONNECTION,
0, SECURITY_NETWORK_DREP,
NULL, 0,
&ntlm->c_handle, &desc,
&attrs, &tsDummy);
if(status == SEC_I_COMPLETE_AND_CONTINUE ||
status == SEC_I_CONTINUE_NEEDED) {
s_pSecFn->CompleteAuthToken(&ntlm->c_handle, &desc);
}
else if(status != SEC_E_OK) {
s_pSecFn->FreeCredentialsHandle(&ntlm->handle);
return CURLE_RECV_ERROR;
}
ntlm->has_handles = 1;
size = buf.cbBuffer;
}
#else
hostoff = 0;
domoff = hostoff + hostlen; /* This is 0: remember that host and domain
are empty */
/* Create and send a type-1 message:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x01000000)
12 Flags long
16 Supplied Domain security buffer(*)
24 Supplied Workstation security buffer(*)
32 start of data block
*/
#if USE_NTLM2SESSION
#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY
#else
#define NTLM2FLAG 0
#endif
snprintf((char *)ntlmbuf, sizeof(ntlmbuf), NTLMSSP_SIGNATURE "%c"
"\x01%c%c%c" /* 32-bit type = 1 */
"%c%c%c%c" /* 32-bit NTLM flag field */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host name offset */
"%c%c" /* 2 zeroes */
"%s" /* host name */
"%s", /* domain string */
0, /* trailing zero */
0,0,0, /* part of type-1 long */
LONGQUARTET(
NTLMFLAG_NEGOTIATE_OEM|
NTLMFLAG_REQUEST_TARGET|
NTLMFLAG_NEGOTIATE_NTLM_KEY|
NTLM2FLAG|
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN
),
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0,0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0,0,
host /* this is empty */, domain /* this is empty */);
/* initial packet length */
size = 32 + hostlen + domlen;
#endif
DEBUG_OUT({
fprintf(stderr, "**** TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM|
NTLMFLAG_REQUEST_TARGET|
NTLMFLAG_NEGOTIATE_NTLM_KEY|
NTLM2FLAG|
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
NTLMFLAG_NEGOTIATE_OEM|
NTLMFLAG_REQUEST_TARGET|
NTLMFLAG_NEGOTIATE_NTLM_KEY|
NTLM2FLAG|
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
print_flags(stderr,
NTLMFLAG_NEGOTIATE_OEM|
NTLMFLAG_REQUEST_TARGET|
NTLMFLAG_NEGOTIATE_NTLM_KEY|
NTLM2FLAG|
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
fprintf(stderr, "\n****\n");
});
/* now size is the size of the base64 encoded package size */
size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
if(size >0 ) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
proxy?"Proxy-":"",
base64);
DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
free(base64);
}
else
return CURLE_OUT_OF_MEMORY; /* FIX TODO */
break;
case NTLMSTATE_TYPE2:
/* We received the type-2 message already, create a type-3 message:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x03000000)
12 LM/LMv2 Response security buffer(*)
20 NTLM/NTLMv2 Response security buffer(*)
28 Domain Name security buffer(*)
36 User Name security buffer(*)
44 Workstation Name security buffer(*)
(52) Session Key (optional) security buffer(*)
(60) Flags (optional) long
52 (64) start of data block
*/
{
#ifdef USE_WINDOWS_SSPI
SecBuffer type_2, type_3;
SecBufferDesc type_2_desc, type_3_desc;
SECURITY_STATUS status;
ULONG attrs;
TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION;
type_2_desc.cBuffers = type_3_desc.cBuffers = 1;
type_2_desc.pBuffers = &type_2;
type_3_desc.pBuffers = &type_3;
type_2.BufferType = SECBUFFER_TOKEN;
type_2.pvBuffer = ntlm->type_2;
type_2.cbBuffer = ntlm->n_type_2;
type_3.BufferType = SECBUFFER_TOKEN;
type_3.pvBuffer = ntlmbuf;
type_3.cbBuffer = sizeof(ntlmbuf);
status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, &ntlm->c_handle,
(char *) host,
ISC_REQ_CONFIDENTIALITY |
ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONNECTION,
0, SECURITY_NETWORK_DREP, &type_2_desc,
0, &ntlm->c_handle, &type_3_desc,
&attrs, &tsDummy);
if(status != SEC_E_OK)
return CURLE_RECV_ERROR;
size = type_3.cbBuffer;
ntlm_sspi_cleanup(ntlm);
#else
int lmrespoff;
unsigned char lmresp[24]; /* fixed-size */
#if USE_NTRESPONSES
int ntrespoff;
unsigned char ntresp[24]; /* fixed-size */
#endif
size_t useroff;
const char *user;
size_t userlen;
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if(user) {
domain = userp;
domlen = (user - domain);
user++;
}
else
user = userp;
userlen = strlen(user);
if(gethostname(host, HOSTNAME_MAX)) {
infof(conn->data, "gethostname() failed, continuing without!");
hostlen = 0;
}
else {
/* If the workstation if configured with a full DNS name (i.e.
* workstation.somewhere.net) gethostname() returns the fully qualified
* name, which NTLM doesn't like.
*/
char *dot = strchr(host, '.');
if(dot)
*dot = '\0';
hostlen = strlen(host);
}
#if USE_NTLM2SESSION
/* We don't support NTLM2 if we don't have USE_NTRESPONSES */
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
unsigned char tmp[0x18];
unsigned char md5sum[MD5_DIGEST_LENGTH];
MD5_CTX MD5pw;
unsigned char entropy[8];
/* Need to create 8 bytes random data */
Curl_ossl_seed(conn->data); /* Initiate the seed if not already done */
RAND_bytes(entropy,8);
/* 8 bytes random data as challenge in lmresp */
memcpy(lmresp,entropy,8);
/* Pad with zeros */
memset(lmresp+8,0,0x10);
/* Fill tmp with challenge(nonce?) + entropy */
memcpy(tmp,&ntlm->nonce[0],8);
memcpy(tmp+8,entropy,8);
MD5_Init(&MD5pw);
MD5_Update(&MD5pw, tmp, 16);
MD5_Final(md5sum, &MD5pw);
/* We shall only use the first 8 bytes of md5sum,
but the des code in lm_resp only encrypt the first 8 bytes */
if(mk_nt_hash(conn->data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY)
return CURLE_OUT_OF_MEMORY;
lm_resp(ntbuffer, md5sum, ntresp);
/* End of NTLM2 Session code */
}
else {
#endif
#if USE_NTRESPONSES
unsigned char ntbuffer[0x18];
#endif
unsigned char lmbuffer[0x18];
#if USE_NTRESPONSES
if(mk_nt_hash(conn->data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY)
return CURLE_OUT_OF_MEMORY;
lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
#endif
mk_lm_hash(conn->data, passwdp, lmbuffer);
lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
/* A safer but less compatible alternative is:
* lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
* See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
#if USE_NTLM2SESSION
}
#endif
lmrespoff = 64; /* size of the message header */
#if USE_NTRESPONSES
ntrespoff = lmrespoff + 0x18;
domoff = ntrespoff + 0x18;
#else
domoff = lmrespoff + 0x18;
#endif
useroff = domoff + domlen;
hostoff = useroff + userlen;
/*
* In the case the server sets the flag NTLMFLAG_NEGOTIATE_UNICODE, we
* need to filter it off because libcurl doesn't UNICODE encode the
* strings it packs into the NTLM authenticate packet.
*/
ntlm->flags &= ~NTLMFLAG_NEGOTIATE_UNICODE;
/* Create the big type-3 message binary blob */
size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
NTLMSSP_SIGNATURE "%c"
"\x03%c%c%c" /* type-3, 32 bits */
"%c%c" /* LanManager length */
"%c%c" /* LanManager allocated space */
"%c%c" /* LanManager offset */
"%c%c" /* 2 zeroes */
"%c%c" /* NT-response length */
"%c%c" /* NT-response allocated space */
"%c%c" /* NT-response offset */
"%c%c" /* 2 zeroes */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* user length */
"%c%c" /* user allocated space */
"%c%c" /* user offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host offset */
"%c%c" /* 2 zeroes */
"%c%c" /* session key length (unknown purpose) */
"%c%c" /* session key allocated space (unknown purpose) */
"%c%c" /* session key offset (unknown purpose) */
"%c%c" /* 2 zeroes */
"%c%c%c%c" /* flags */
/* domain string */
/* user string */
/* host string */
/* LanManager response */
/* NT response */
,
0, /* zero termination */
0,0,0, /* type-3 long, the 24 upper bits */
SHORTPAIR(0x18), /* LanManager response length, twice */
SHORTPAIR(0x18),
SHORTPAIR(lmrespoff),
0x0, 0x0,
#if USE_NTRESPONSES
SHORTPAIR(0x18), /* NT-response length, twice */
SHORTPAIR(0x18),
SHORTPAIR(ntrespoff),
0x0, 0x0,
#else
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
#endif
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0x0, 0x0,
SHORTPAIR(userlen),
SHORTPAIR(userlen),
SHORTPAIR(useroff),
0x0, 0x0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
LONGQUARTET(ntlm->flags));
DEBUGASSERT(size==64);
DEBUGASSERT(size == (size_t)lmrespoff);
/* We append the binary hashes */
if(size < (sizeof(ntlmbuf) - 0x18)) {
memcpy(&ntlmbuf[size], lmresp, 0x18);
size += 0x18;
}
DEBUG_OUT({
fprintf(stderr, "**** TYPE3 header lmresp=");
print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
});
#if USE_NTRESPONSES
if(size < (sizeof(ntlmbuf) - 0x18)) {
DEBUGASSERT(size == (size_t)ntrespoff);
memcpy(&ntlmbuf[size], ntresp, 0x18);
size += 0x18;
}
DEBUG_OUT({
fprintf(stderr, "\n ntresp=");
print_hex(stderr, (char *)&ntlmbuf[ntrespoff], 0x18);
});
#endif
DEBUG_OUT({
fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
LONGQUARTET(ntlm->flags), ntlm->flags);
print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n****\n");
});
/* Make sure that the domain, user and host strings fit in the target
buffer before we copy them there. */
if(size + userlen + domlen + hostlen >= sizeof(ntlmbuf)) {
failf(conn->data, "user + domain + host name too big");
return CURLE_OUT_OF_MEMORY;
}
DEBUGASSERT(size == domoff);
memcpy(&ntlmbuf[size], domain, domlen);
size += domlen;
DEBUGASSERT(size == useroff);
memcpy(&ntlmbuf[size], user, userlen);
size += userlen;
DEBUGASSERT(size == hostoff);
memcpy(&ntlmbuf[size], host, hostlen);
size += hostlen;
#ifdef CURL_DOES_CONVERSIONS
/* convert domain, user, and host to ASCII but leave the rest as-is */
if(CURLE_OK != Curl_convert_to_network(conn->data,
(char *)&ntlmbuf[domoff],
size-domoff)) {
return CURLE_CONV_FAILED;
}
#endif /* CURL_DOES_CONVERSIONS */
#endif
/* convert the binary blob into base64 */
size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
if(size >0 ) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
proxy?"Proxy-":"",
base64);
DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
free(base64);
}
else
return CURLE_OUT_OF_MEMORY; /* FIX TODO */
ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
authp->done = TRUE;
}
break;
case NTLMSTATE_TYPE3:
/* connection is already authenticated,
* don't send a header in future requests */
if(*allocuserpwd) {
free(*allocuserpwd);
*allocuserpwd=NULL;
}
authp->done = TRUE;
break;
}
return CURLE_OK;
}
|
|||||
| ↓ | ftp_parse_url_path | 36 | 94 | 177 | lib/ftp.c |
static
CURLcode ftp_parse_url_path(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
/* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = data->state.proto.ftp;
struct ftp_conn *ftpc = &conn->proto.ftpc;
size_t dlen;
char *slash_pos; /* position of the first '/' char in curpos */
char *path_to_use = data->state.path;
char *cur_pos;
cur_pos = path_to_use; /* current position in path. point at the begin
of next path component */
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = FALSE;
switch(data->set.ftp_filemethod) {
case FTPFILE_NOCWD:
/* fastest, but less standard-compliant */
/*
The best time to check whether the path is a file or directory is right
here. so:
the first condition in the if() right here, is there just in case
someone decides to set path to NULL one day
*/
if(data->state.path &&
data->state.path[0] &&
(data->state.path[strlen(data->state.path) - 1] != '/') )
ftpc->file = data->state.path; /* this is a full file path */
else
ftpc->file = NULL;
/*
ftpc->file is not used anywhere other than for operations on a file.
In other words, never for directory operations.
So we can safely set it to NULL here and use it as a
argument in dir/file decisions.
*/
break;
case FTPFILE_SINGLECWD:
/* get the last slash */
if(!path_to_use[0]) {
/* no dir, no file */
ftpc->dirdepth = 0;
ftpc->file = NULL;
break;
}
slash_pos=strrchr(cur_pos, '/');
if(slash_pos || !*cur_pos) {
ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
if(!ftpc->dirs)
return CURLE_OUT_OF_MEMORY;
ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
slash_pos?(int)(slash_pos-cur_pos):1,
NULL);
if(!ftpc->dirs[0]) {
freedirs(ftpc);
return CURLE_OUT_OF_MEMORY;
}
ftpc->dirdepth = 1; /* we consider it to be a single dir */
ftpc->file = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
}
else
ftpc->file = cur_pos; /* this is a file name only */
break;
default: /* allow pretty much anything */
case FTPFILE_MULTICWD:
ftpc->dirdepth = 0;
ftpc->diralloc = 5; /* default dir depth to allocate */
ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
if(!ftpc->dirs)
return CURLE_OUT_OF_MEMORY;
/* we have a special case for listing the root dir only */
if(strequal(path_to_use, "/")) {
cur_pos++; /* make it point to the zero byte */
ftpc->dirs[0] = strdup("/");
ftpc->dirdepth++;
}
else {
/* parse the URL path into separate path components */
while((slash_pos = strchr(cur_pos, '/')) != NULL) {
/* 1 or 0 to indicate absolute directory */
bool absolute_dir = (bool)((cur_pos - data->state.path > 0) &&
(ftpc->dirdepth == 0));
/* seek out the next path component */
if(slash_pos-cur_pos) {
/* we skip empty path components, like "x//y" since the FTP command
CWD requires a parameter and a non-existant parameter a) doesn't
work on many servers and b) has no effect on the others. */
int len = (int)(slash_pos - cur_pos + absolute_dir);
ftpc->dirs[ftpc->dirdepth] =
curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
failf(data, "no memory");
freedirs(ftpc);
return CURLE_OUT_OF_MEMORY;
}
if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
free(ftpc->dirs[ftpc->dirdepth]);
freedirs(ftpc);
return CURLE_URL_MALFORMAT;
}
}
else {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
continue;
}
cur_pos = slash_pos + 1; /* jump to the rest of the string */
if(++ftpc->dirdepth >= ftpc->diralloc) {
/* enlarge array */
char *bigger;
ftpc->diralloc *= 2; /* double the size each time */
bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
if(!bigger) {
freedirs(ftpc);
return CURLE_OUT_OF_MEMORY;
}
ftpc->dirs = (char **)bigger;
}
}
}
ftpc->file = cur_pos; /* the rest is the file name */
}
if(ftpc->file && *ftpc->file) {
ftpc->file = curl_easy_unescape(conn->data, ftpc->file, 0, NULL);
if(NULL == ftpc->file) {
freedirs(ftpc);
failf(data, "no memory");
return CURLE_OUT_OF_MEMORY;
}
if(isBadFtpString(ftpc->file)) {
freedirs(ftpc);
return CURLE_URL_MALFORMAT;
}
}
else
ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
pointer */
if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
/* We need a file name when uploading. Return error! */
failf(data, "Uploading to a URL without a file name!");
return CURLE_URL_MALFORMAT;
}
ftpc->cwddone = FALSE; /* default to not done */
if(ftpc->prevpath) {
/* prevpath is "raw" so we convert the input path before we compare the
strings */
char *path = curl_easy_unescape(conn->data, data->state.path, 0, NULL);
if(!path) {
freedirs(ftpc);
return CURLE_OUT_OF_MEMORY;
}
dlen = strlen(path) - (ftpc->file?strlen(ftpc->file):0);
if((dlen == strlen(ftpc->prevpath)) &&
curl_strnequal(path, ftpc->prevpath, dlen)) {
infof(data, "Request has same path as previous transfer\n");
ftpc->cwddone = TRUE;
}
free(path);
}
return CURLE_OK;
}
|
|||||
| ↓ | Curl_readwrite | 34 | 62 | 162 | lib/transfer.c |
CURLcode Curl_readwrite(struct connectdata *conn,
bool *done)
{
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
CURLcode result;
int didwhat=0;
curl_socket_t fd_read;
curl_socket_t fd_write;
int select_res = conn->cselect_bits;
conn->cselect_bits = 0;
/* only use the proper socket if the *_HOLD bit is not set simultaneously as
then we are in rate limiting state in that transfer direction */
if((k->keepon & KEEP_READBITS) == KEEP_READ) {
fd_read = conn->sockfd;
#if defined(USE_LIBSSH2)
if(conn->protocol & (PROT_SCP|PROT_SFTP))
select_res |= CURL_CSELECT_IN;
#endif /* USE_LIBSSH2 */
} else
fd_read = CURL_SOCKET_BAD;
if((k->keepon & KEEP_WRITEBITS) == KEEP_WRITE)
fd_write = conn->writesockfd;
else
fd_write = CURL_SOCKET_BAD;
if(!select_res) { /* Call for select()/poll() only, if read/write/error
status is not known. */
select_res = Curl_socket_ready(fd_read, fd_write, 0);
}
if(select_res == CURL_CSELECT_ERR) {
failf(data, "select/poll returned error");
return CURLE_SEND_ERROR;
}
/* We go ahead and do a read if we have a readable socket or if
the stream was rewound (in which case we have data in a
buffer) */
if((k->keepon & KEEP_READ) &&
((select_res & CURL_CSELECT_IN) || conn->bits.stream_was_rewound)) {
result = readwrite_data(data, conn, k, &didwhat, done);
if(result || *done)
return result;
}
/* If we still have writing to do, we check if we have a writable socket. */
if((k->keepon & KEEP_WRITE) && (select_res & CURL_CSELECT_OUT)) {
/* write */
result = readwrite_upload(data, conn, k, &didwhat);
if(result)
return result;
}
k->now = Curl_tvnow();
if(didwhat) {
/* Update read/write counters */
if(k->bytecountp)
*k->bytecountp = k->bytecount; /* read count */
if(k->writebytecountp)
*k->writebytecountp = k->writebytecount; /* write count */
}
else {
/* no read no write, this is a timeout? */
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
/* This should allow some time for the header to arrive, but only a
very short time as otherwise it'll be too much wasted time too
often. */
/* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
Therefore, when a client sends this header field to an origin server
(possibly via a proxy) from which it has never seen a 100 (Continue)
status, the client SHOULD NOT wait for an indefinite period before
sending the request body.
*/
long ms = Curl_tvdiff(k->now, k->start100);
if(ms > CURL_TIMEOUT_EXPECT_100) {
/* we've waited long enough, continue anyway */
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_WRITE;
infof(data, "Done waiting for 100-continue\n");
}
}
}
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(data, k->now);
if(result)
return result;
if(data->set.timeout &&
(Curl_tvdiff(k->now, k->start) >= data->set.timeout)) {
if(k->size != -1) {
failf(data, "Operation timed out after %ld milliseconds with %"
FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
data->set.timeout, k->bytecount, k->size);
} else {
failf(data, "Operation timed out after %ld milliseconds with %"
FORMAT_OFF_T " bytes received",
data->set.timeout, k->bytecount);
}
return CURLE_OPERATION_TIMEDOUT;
}
if(!k->keepon) {
/*
* The transfer has been performed. Just make some general checks before
* returning.
*/
if(!(data->set.opt_no_body) && (k->size != -1) &&
(k->bytecount != k->size) &&
#ifdef CURL_DO_LINEEND_CONV
/* Most FTP servers don't adjust their file SIZE response for CRLFs,
so we'll check to see if the discrepancy can be explained
by the number of CRLFs we've changed to LFs.
*/
(k->bytecount != (k->size + data->state.crlf_conversions)) &&
#endif /* CURL_DO_LINEEND_CONV */
!data->req.newurl) {
failf(data, "transfer closed with %" FORMAT_OFF_T
" bytes remaining to read",
k->size - k->bytecount);
return CURLE_PARTIAL_FILE;
}
else if(!(data->set.opt_no_body) &&
k->chunk &&
(conn->chunk.state != CHUNK_STOP)) {
/*
* In chunked mode, return an error if the connection is closed prior to
* the empty (terminiating) chunk is read.
*
* The condition above used to check for
* conn->proto.http->chunk.datasize != 0 which is true after reading
* *any* chunk, not just the empty chunk.
*
*/
failf(data, "transfer closed with outstanding read data remaining");
return CURLE_PARTIAL_FILE;
}
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
}
/* Now update the "done" boolean we return */
*done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE|
KEEP_READ_PAUSE|KEEP_WRITE_PAUSE)));
return CURLE_OK;
}
|
|||||
| ↓ | print_flags | 33 | 64 | 67 | lib/http_ntlm.c |
static void print_flags(FILE *handle, unsigned long flags)
{
if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
if(flags & NTLMFLAG_NEGOTIATE_OEM)
fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
if(flags & NTLMFLAG_REQUEST_TARGET)
fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
if(flags & (1<<3))
fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
if(flags & NTLMFLAG_NEGOTIATE_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
if(flags & NTLMFLAG_NEGOTIATE_SEAL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_NETWARE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
if(flags & (1<<10))
fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
if(flags & (1<<24))
fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
if(flags & (1<<25))
fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
if(flags & (1<<26))
fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
if(flags & (1<<27))
fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
if(flags & (1<<28))
fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
if(flags & NTLMFLAG_NEGOTIATE_128)
fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
if(flags & NTLMFLAG_NEGOTIATE_56)
fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
}
|
|||||
| cert_stuff | 9 | 17 | 28 | lib/nss.c | |
| ↓ | ftp_readresp | 34 | 108 | 219 | lib/ftp.c |
static CURLcode ftp_readresp(curl_socket_t sockfd,
struct connectdata *conn,
int *ftpcode, /* return the ftp-code if done */
size_t *size) /* size of the response */
{
ssize_t perline; /* count bytes per line */
bool keepon=TRUE;
ssize_t gotbytes;
char *ptr;
struct SessionHandle *data = conn->data;
char * const buf = data->state.buffer;
CURLcode result = CURLE_OK;
struct ftp_conn *ftpc = &conn->proto.ftpc;
int code = 0;
*ftpcode = 0; /* 0 for errors or not done */
*size = 0;
ptr=buf + ftpc->nread_resp;
/* number of bytes in the current line, so far */
perline = (ssize_t)(ptr-ftpc->linestart_resp);
keepon=TRUE;
while((ftpc->nread_resp
|
|||||
| ↓ | dprintf_Pass1 | 71 | 162 | 302 | lib/mprintf.c |
static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos,
va_list arglist)
{
char *fmt = (char *)format;
int param_num = 0;
long this_param;
long width;
long precision;
int flags;
long max_param=0;
long i;
while(*fmt) {
if(*fmt++ == '%') {
if(*fmt == '%') {
fmt++;
continue; /* while */
}
flags = FLAGS_NEW;
/* Handle the positional case (N$) */
param_num++;
this_param = dprintf_DollarString(fmt, &fmt);
if(0 == this_param)
/* we got no positional, get the next counter */
this_param = param_num;
if(this_param > max_param)
max_param = this_param;
/*
* The parameter with number 'i' should be used. Next, we need
* to get SIZE and TYPE of the parameter. Add the information
* to our array.
*/
width = 0;
precision = 0;
/* Handle the flags */
while(dprintf_IsQualifierNoDollar(*fmt)) {
switch (*fmt++) {
case ' ':
flags |= FLAGS_SPACE;
break;
case '+':
flags |= FLAGS_SHOWSIGN;
break;
case '-':
flags |= FLAGS_LEFT;
flags &= ~FLAGS_PAD_NIL;
break;
case '#':
flags |= FLAGS_ALT;
break;
case '.':
flags |= FLAGS_PREC;
if('*' == *fmt) {
/* The precision is picked from a specified parameter */
flags |= FLAGS_PRECPARAM;
fmt++;
param_num++;
i = dprintf_DollarString(fmt, &fmt);
if(i)
precision = i;
else
precision = param_num;
if(precision > max_param)
max_param = precision;
}
else {
flags |= FLAGS_PREC;
precision = strtol(fmt, &fmt, 10);
}
break;
case 'h':
flags |= FLAGS_SHORT;
break;
case 'l':
if(flags & FLAGS_LONG)
flags |= FLAGS_LONGLONG;
else
flags |= FLAGS_LONG;
break;
case 'L':
flags |= FLAGS_LONGDOUBLE;
break;
case 'q':
flags |= FLAGS_LONGLONG;
break;
case 'z':
/* the code below generates a warning if -Wunreachable-code is
used */
#if (SIZEOF_SIZE_T > CURL_SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
break;
case 'O':
#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
break;
case '0':
if(!(flags & FLAGS_LEFT))
flags |= FLAGS_PAD_NIL;
/* FALLTHROUGH */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
flags |= FLAGS_WIDTH;
width = strtol(fmt-1, &fmt, 10);
break;
case '*': /* Special case */
flags |= FLAGS_WIDTHPARAM;
param_num++;
i = dprintf_DollarString(fmt, &fmt);
if(i)
width = i;
else
width = param_num;
if(width > max_param)
max_param=width;
break;
default:
break;
}
} /* switch */
/* Handle the specifier */
i = this_param - 1;
switch (*fmt) {
case 'S':
flags |= FLAGS_ALT;
/* FALLTHROUGH */
case 's':
vto[i].type = FORMAT_STRING;
break;
case 'n':
vto[i].type = FORMAT_INTPTR;
break;
case 'p':
vto[i].type = FORMAT_PTR;
break;
case 'd': case 'i':
vto[i].type = FORMAT_INT;
break;
case 'u':
vto[i].type = FORMAT_INT;
flags |= FLAGS_UNSIGNED;
break;
case 'o':
vto[i].type = FORMAT_INT;
flags |= FLAGS_OCTAL;
break;
case 'x':
vto[i].type = FORMAT_INT;
flags |= FLAGS_HEX;
break;
case 'X':
vto[i].type = FORMAT_INT;
flags |= FLAGS_HEX|FLAGS_UPPER;
break;
case 'c':
vto[i].type = FORMAT_INT;
flags |= FLAGS_CHAR;
break;
case 'f':
vto[i].type = FORMAT_DOUBLE;
break;
case 'e':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE;
break;
case 'E':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE|FLAGS_UPPER;
break;
case 'g':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG;
break;
case 'G':
vto[i].type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG|FLAGS_UPPER;
break;
default:
vto[i].type = FORMAT_UNKNOWN;
break;
} /* switch */
vto[i].flags = flags;
vto[i].width = width;
vto[i].precision = precision;
if(flags & FLAGS_WIDTHPARAM) {
/* we have the width specified from a parameter, so we make that
parameter's info setup properly */
vto[i].width = width - 1;
i = width - 1;
vto[i].type = FORMAT_WIDTH;
vto[i].flags = FLAGS_NEW;
vto[i].precision = vto[i].width = 0; /* can't use width or precision
of width! */
}
if(flags & FLAGS_PRECPARAM) {
/* we have the precision specified from a parameter, so we make that
parameter's info setup properly */
vto[i].precision = precision - 1;
i = precision - 1;
vto[i].type = FORMAT_WIDTH;
vto[i].flags = FLAGS_NEW;
vto[i].precision = vto[i].width = 0; /* can't use width or precision
of width! */
}
*endpos++ = fmt + 1; /* end of this sequence */
}
}
#ifdef DPRINTF_DEBUG2
dprintf_Pass1Report(vto, max_param);
#endif
/* Read the arg list parameters into our data list */
for (i=0; i |
|||||
| ↓ | Curl_http_auth_act | 29 | 35 | 66 | lib/http.c |
CURLcode Curl_http_auth_act(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
bool pickhost = FALSE;
bool pickproxy = FALSE;
CURLcode code = CURLE_OK;
if(100 <= data->req.httpcode && 199 >= data->req.httpcode)
/* this is a transient response code, ignore */
return CURLE_OK;
if(data->state.authproblem)
return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
if(conn->bits.user_passwd &&
((data->req.httpcode == 401) ||
(conn->bits.authneg && data->req.httpcode < 300))) {
pickhost = pickoneauth(&data->state.authhost);
if(!pickhost)
data->state.authproblem = TRUE;
}
if(conn->bits.proxy_user_passwd &&
((data->req.httpcode == 407) ||
(conn->bits.authneg && data->req.httpcode < 300))) {
pickproxy = pickoneauth(&data->state.authproxy);
if(!pickproxy)
data->state.authproblem = TRUE;
}
if(pickhost || pickproxy) {
data->req.newurl = strdup(data->change.url); /* clone URL */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
if((data->set.httpreq != HTTPREQ_GET) &&
(data->set.httpreq != HTTPREQ_HEAD) &&
!conn->bits.rewindaftersend) {
code = Curl_http_perhapsrewind(conn);
if(code)
return code;
}
}
else if((data->req.httpcode < 300) &&
(!data->state.authhost.done) &&
conn->bits.authneg) {
/* no (known) authentication available,
authentication is not "done" yet and
no authentication seems to be required and
we didn't try HEAD or GET */
if((data->set.httpreq != HTTPREQ_GET) &&
(data->set.httpreq != HTTPREQ_HEAD)) {
data->req.newurl = strdup(data->change.url); /* clone URL */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
data->state.authhost.done = TRUE;
}
}
if(Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
data->req.httpcode);
code = CURLE_HTTP_RETURNED_ERROR;
}
return code;
}
|
|||||
| ↓ | Curl_parsenetrc | 30 | 79 | 153 | lib/netrc.c |
int Curl_parsenetrc(const char *host,
char *login,
char *password,
char *netrcfile)
{
FILE *file;
int retcode=1;
int specific_login = (login[0] != 0);
char *home = NULL;
bool home_alloc = FALSE;
bool netrc_alloc = FALSE;
int state=NOTHING;
char state_login=0; /* Found a login keyword */
char state_password=0; /* Found a password keyword */
int state_our_login=FALSE; /* With specific_login, found *our* login name */
#define NETRC DOT_CHAR "netrc"
#ifdef CURLDEBUG
{
/* This is a hack to allow testing.
* If compiled with --enable-debug and CURL_DEBUG_NETRC is defined,
* then it's the path to a substitute .netrc for testing purposes *only* */
char *override = curl_getenv("CURL_DEBUG_NETRC");
if(override) {
fprintf(stderr, "NETRC: overridden " NETRC " file: %s\n", override);
netrcfile = override;
netrc_alloc = TRUE;
}
}
#endif /* CURLDEBUG */
if(!netrcfile) {
home = curl_getenv("HOME"); /* portable environment reader */
if(home) {
home_alloc = TRUE;
#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
}
else {
struct passwd *pw;
pw= getpwuid(geteuid());
if(pw) {
#ifdef VMS
home = decc_translate_vms(pw->pw_dir);
#else
home = pw->pw_dir;
#endif
}
#endif
}
if(!home)
return -1;
netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
if(!netrcfile) {
if(home_alloc)
free(home);
return -1;
}
netrc_alloc = TRUE;
}
file = fopen(netrcfile, "r");
if(file) {
char *tok;
char *tok_buf;
bool done=FALSE;
char netrcbuffer[256];
while(!done && fgets(netrcbuffer, sizeof(netrcbuffer), file)) {
tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
while(!done && tok) {
if(login[0] && password[0]) {
done=TRUE;
break;
}
switch(state) {
case NOTHING:
if(strequal("machine", tok)) {
/* the next tok is the machine name, this is in itself the
delimiter that starts the stuff entered for this machine,
after this we need to search for 'login' and
'password'. */
state=HOSTFOUND;
}
break;
case HOSTFOUND:
if(strequal(host, tok)) {
/* and yes, this is our host! */
state=HOSTVALID;
#ifdef _NETRC_DEBUG
fprintf(stderr, "HOST: %s\n", tok);
#endif
retcode=0; /* we did find our host */
}
else
/* not our host */
state=NOTHING;
break;
case HOSTVALID:
/* we are now parsing sub-keywords concerning "our" host */
if(state_login) {
if(specific_login) {
state_our_login = strequal(login, tok);
}
else {
strncpy(login, tok, LOGINSIZE-1);
#ifdef _NETRC_DEBUG
fprintf(stderr, "LOGIN: %s\n", login);
#endif
}
state_login=0;
}
else if(state_password) {
if(state_our_login || !specific_login) {
strncpy(password, tok, PASSWORDSIZE-1);
#ifdef _NETRC_DEBUG
fprintf(stderr, "PASSWORD: %s\n", password);
#endif
}
state_password=0;
}
else if(strequal("login", tok))
state_login=1;
else if(strequal("password", tok))
state_password=1;
else if(strequal("machine", tok)) {
/* ok, there's machine here go => */
state = HOSTFOUND;
state_our_login = FALSE;
}
break;
} /* switch (state) */
tok = strtok_r(NULL, " \t\n", &tok_buf);
} /* while(tok) */
} /* while fgets() */
fclose(file);
}
if(home_alloc)
free(home);
if(netrc_alloc)
free(netrcfile);
return retcode;
}
|
|||||
| ↓ | Curl_pgrsUpdate | 28 | 83 | 208 | lib/progress.c |
int Curl_pgrsUpdate(struct connectdata *conn)
{
struct timeval now;
int result;
char max5[6][10];
int dlpercen=0;
int ulpercen=0;
int total_percen=0;
curl_off_t total_transfer;
curl_off_t total_expected_transfer;
long timespent;
struct SessionHandle *data = conn->data;
int nowindex = data->progress.speeder_c% CURR_TIME;
int checkindex;
int countindex; /* amount of seconds stored in the speeder array */
char time_left[10];
char time_total[10];
char time_spent[10];
long ulestimate=0;
long dlestimate=0;
long total_estimate;
bool shownow=FALSE;
now = Curl_tvnow(); /* what time is it */
/* The time spent so far (from the start) */
data->progress.timespent =
(double)(now.tv_sec - data->progress.start.tv_sec) +
(double)(now.tv_usec - data->progress.start.tv_usec)/1000000.0;
timespent = (long)data->progress.timespent;
/* The average download speed this far */
data->progress.dlspeed = (curl_off_t)
((double)data->progress.downloaded/
(data->progress.timespent>0?data->progress.timespent:1));
/* The average upload speed this far */
data->progress.ulspeed = (curl_off_t)
((double)data->progress.uploaded/
(data->progress.timespent>0?data->progress.timespent:1));
/* Calculations done at most once a second, unless end is reached */
if(data->progress.lastshow != (long)now.tv_sec) {
shownow = TRUE;
data->progress.lastshow = now.tv_sec;
/* Let's do the "current speed" thing, which should use the fastest
of the dl/ul speeds. Store the faster speed at entry 'nowindex'. */
data->progress.speeder[ nowindex ] =
data->progress.downloaded>data->progress.uploaded?
data->progress.downloaded:data->progress.uploaded;
/* remember the exact time for this moment */
data->progress.speeder_time [ nowindex ] = now;
/* advance our speeder_c counter, which is increased every time we get
here and we expect it to never wrap as 2^32 is a lot of seconds! */
data->progress.speeder_c++;
/* figure out how many index entries of data we have stored in our speeder
array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
transfer. Imagine, after one second we have filled in two entries,
after two seconds we've filled in three entries etc. */
countindex = ((data->progress.speeder_c>=CURR_TIME)?
CURR_TIME:data->progress.speeder_c) - 1;
/* first of all, we don't do this if there's no counted seconds yet */
if(countindex) {
long span_ms;
/* Get the index position to compare with the 'nowindex' position.
Get the oldest entry possible. While we have less than CURR_TIME
entries, the first entry will remain the oldest. */
checkindex = (data->progress.speeder_c>=CURR_TIME)?
data->progress.speeder_c%CURR_TIME:0;
/* Figure out the exact time for the time span */
span_ms = Curl_tvdiff(now,
data->progress.speeder_time[checkindex]);
if(0 == span_ms)
span_ms=1; /* at least one millisecond MUST have passed */
/* Calculate the average speed the last 'span_ms' milliseconds */
{
curl_off_t amount = data->progress.speeder[nowindex]-
data->progress.speeder[checkindex];
if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */)
/* the 'amount' value is bigger than would fit in 32 bits if
multiplied with 1000, so we use the double math for this */
data->progress.current_speed = (curl_off_t)
((double)amount/((double)span_ms/1000.0));
else
/* the 'amount' value is small enough to fit within 32 bits even
when multiplied with 1000 */
data->progress.current_speed = amount*CURL_OFF_T_C(1000)/span_ms;
}
}
else
/* the first second we use the main average */
data->progress.current_speed =
(data->progress.ulspeed>data->progress.dlspeed)?
data->progress.ulspeed:data->progress.dlspeed;
} /* Calculations end */
if(!(data->progress.flags & PGRS_HIDE)) {
/* progress meter has not been shut off */
if(data->set.fprogress) {
/* There's a callback set, so we call that instead of writing
anything ourselves. This really is the way to go. */
result= data->set.fprogress(data->set.progress_client,
(double)data->progress.size_dl,
(double)data->progress.downloaded,
(double)data->progress.size_ul,
(double)data->progress.uploaded);
if(result)
failf(data, "Callback aborted");
return result;
}
if(!shownow)
/* only show the internal progress meter once per second */
return 0;
/* If there's no external callback set, use internal code to show
progress */
if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
if(data->state.resume_from) {
fprintf(data->set.err,
"** Resuming transfer from byte position %" FORMAT_OFF_T "\n",
data->state.resume_from);
}
fprintf(data->set.err,
" %% Total %% Received %% Xferd Average Speed Time Time Time Current\n"
" Dload Upload Total Spent Left Speed\n");
data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
}
/* Figure out the estimated time of arrival for the upload */
if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
(data->progress.ulspeed>0) &&
(data->progress.size_ul > 100) ) {
ulestimate = (long)(data->progress.size_ul / data->progress.ulspeed);
ulpercen = (int)(100*(data->progress.uploaded/100) /
(data->progress.size_ul/100) );
}
/* ... and the download */
if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
(data->progress.dlspeed>0) &&
(data->progress.size_dl>100)) {
dlestimate = (long)(data->progress.size_dl / data->progress.dlspeed);
dlpercen = (int)(100*(data->progress.downloaded/100) /
(data->progress.size_dl/100));
}
/* Now figure out which of them is slower and use that one for the
total estimate! */
total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
/* create the three time strings */
time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
time2str(time_total, total_estimate);
time2str(time_spent, timespent);
/* Get the total amount of data expected to get transfered */
total_expected_transfer =
(data->progress.flags & PGRS_UL_SIZE_KNOWN?
data->progress.size_ul:data->progress.uploaded)+
(data->progress.flags & PGRS_DL_SIZE_KNOWN?
data->progress.size_dl:data->progress.downloaded);
/* We have transfered this much so far */
total_transfer = data->progress.downloaded + data->progress.uploaded;
/* Get the percentage of data transfered so far */
if(total_expected_transfer > 100)
total_percen=(int)(100*(total_transfer/100) /
(total_expected_transfer/100) );
fprintf(data->set.err,
"\r%3d %s %3d %s %3d %s %s %s %s %s %s %s",
total_percen, /* 3 letters */ /* total % */
max5data(total_expected_transfer, max5[2]), /* total size */
dlpercen, /* 3 letters */ /* rcvd % */
max5data(data->progress.downloaded, max5[0]), /* rcvd size */
ulpercen, /* 3 letters */ /* xfer % */
max5data(data->progress.uploaded, max5[1]), /* xfer size */
max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
time_total, /* 8 letters */ /* total time */
time_spent, /* 8 letters */ /* time spent */
time_left, /* 8 letters */ /* time left */
max5data(data->progress.current_speed, max5[5]) /* current speed */
);
/* we flush the output stream to make it appear as soon as possible */
fflush(data->set.err);
} /* !(data->progress.flags & PGRS_HIDE) */
return 0;
}
|
|||||
| ↓ | inet_ntop6 | 28 | 53 | 103 | lib/inet_ntop.c |
static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
char *tp;
struct {
long base;
long len;
} best, cur;
unsigned long words[IN6ADDRSZ / INT16SZ];
int i;
/* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof(words));
for (i = 0; i < IN6ADDRSZ; i++)
words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
cur.base = -1;
best.len = 0;
cur.len = 0;
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
{
if(words[i] == 0)
{
if(cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
}
else if(cur.base != -1)
{
if(best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
best = cur;
if(best.base != -1 && best.len < 2)
best.base = -1;
/* Format the result.
*/
tp = tmp;
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
{
/* Are we inside the best run of 0x00's?
*/
if(best.base != -1 && i >= best.base && i < (best.base + best.len))
{
if(i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex?
*/
if(i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4?
*/
if(i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
{
if(!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp)))
{
SET_ERRNO(ENOSPC);
return (NULL);
}
tp += strlen(tp);
break;
}
tp += snprintf(tp, 5, "%lx", words[i]);
}
/* Was it a trailing run of 0x00's?
*/
if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
*tp++ = ':';
*tp++ = '\0';
/* Check for overflow, copy, and we're done.
*/
if((size_t)(tp - tmp) > size)
{
SET_ERRNO(ENOSPC);
return (NULL);
}
strcpy(dst, tmp);
return dst;
}
|
|||||
| ↓ | Curl_output_digest | 28 | 110 | 255 | lib/http_digest.c |
CURLcode Curl_output_digest(struct connectdata *conn,
bool proxy,
const unsigned char *request,
const unsigned char *uripath)
{
/* We have a Digest setup for this, use it! Now, to get all the details for
this sorted out, I must urge you dear friend to read up on the RFC2617
section 3.2.2, */
unsigned char md5buf[16]; /* 16 bytes/128 bits */
unsigned char request_digest[33];
unsigned char *md5this;
unsigned char *ha1;
unsigned char ha2[33];/* 32 digits and 1 zero byte */
char cnoncebuf[7];
char *cnonce;
char *tmp = NULL;
struct timeval now;
char **allocuserpwd;
const char *userp;
const char *passwdp;
struct auth *authp;
struct SessionHandle *data = conn->data;
struct digestdata *d;
#ifdef CURL_DOES_CONVERSIONS
CURLcode rc;
/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
It converts digest text to ASCII so the MD5 will be correct for
what ultimately goes over the network.
*/
#define CURL_OUTPUT_DIGEST_CONV(a, b) \
rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
if(rc != CURLE_OK) { \
free(b); \
return rc; \
}
#else
#define CURL_OUTPUT_DIGEST_CONV(a, b)
#endif /* CURL_DOES_CONVERSIONS */
if(proxy) {
d = &data->state.proxydigest;
allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->proxyuser;
passwdp = conn->proxypasswd;
authp = &data->state.authproxy;
}
else {
d = &data->state.digest;
allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user;
passwdp = conn->passwd;
authp = &data->state.authhost;
}
if(*allocuserpwd) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = NULL;
}
/* not set means empty */
if(!userp)
userp="";
if(!passwdp)
passwdp="";
if(!d->nonce) {
authp->done = FALSE;
return CURLE_OK;
}
authp->done = TRUE;
if(!d->nc)
d->nc = 1;
if(!d->cnonce) {
/* Generate a cnonce */
now = Curl_tvnow();
snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", (long)now.tv_sec);
if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
d->cnonce = cnonce;
else
return CURLE_OUT_OF_MEMORY;
}
/*
if the algorithm is "MD5" or unspecified (which then defaults to MD5):
A1 = unq(username-value) ":" unq(realm-value) ":" passwd
if the algorithm is "MD5-sess" then:
A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
":" unq(nonce-value) ":" unq(cnonce-value)
*/
md5this = (unsigned char *)
aprintf("%s:%s:%s", userp, d->realm, passwdp);
if(!md5this)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */
ha1 = malloc(33); /* 32 digits and 1 zero byte */
if(!ha1)
return CURLE_OUT_OF_MEMORY;
md5_to_ascii(md5buf, ha1);
if(d->algo == CURLDIGESTALGO_MD5SESS) {
/* nonce and cnonce are OUTSIDE the hash */
tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
Curl_md5it(md5buf, (unsigned char *)tmp);
free(tmp); /* free this again */
md5_to_ascii(md5buf, ha1);
}
/*
If the "qop" directive's value is "auth" or is unspecified, then A2 is:
A2 = Method ":" digest-uri-value
If the "qop" value is "auth-int", then A2 is:
A2 = Method ":" digest-uri-value ":" H(entity-body)
(The "Method" value is the HTTP request method as specified in section
5.1.1 of RFC 2616)
*/
md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
if(!md5this) {
free(ha1);
return CURLE_OUT_OF_MEMORY;
}
if(d->qop && strequal(d->qop, "auth-int")) {
/* We don't support auth-int at the moment. I can't see a easy way to get
entity-body here */
/* TODO: Append H(entity-body)*/
}
CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */
md5_to_ascii(md5buf, ha2);
if(d->qop) {
md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
ha1,
d->nonce,
d->nc,
d->cnonce,
d->qop,
ha2);
}
else {
md5this = (unsigned char *)aprintf("%s:%s:%s",
ha1,
d->nonce,
ha2);
}
free(ha1);
if(!md5this)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */
md5_to_ascii(md5buf, request_digest);
/* for test case 64 (snooped from a Mozilla 1.3a request)
Authorization: Digest username="testuser", realm="testrealm", \
nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
*/
if(d->qop) {
*allocuserpwd =
aprintf( "%sAuthorization: Digest "
"username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"cnonce=\"%s\", "
"nc=%08x, "
"qop=\"%s\", "
"response=\"%s\"",
proxy?"Proxy-":"",
userp,
d->realm,
d->nonce,
uripath, /* this is the PATH part of the URL */
d->cnonce,
d->nc,
d->qop,
request_digest);
if(strequal(d->qop, "auth"))
d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
which tells to the server how many times you are using the
same nonce in the qop=auth mode. */
}
else {
*allocuserpwd =
aprintf( "%sAuthorization: Digest "
"username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"response=\"%s\"",
proxy?"Proxy-":"",
userp,
d->realm,
d->nonce,
uripath, /* this is the PATH part of the URL */
request_digest);
}
if(!*allocuserpwd)
return CURLE_OUT_OF_MEMORY;
/* Add optional fields */
if(d->opaque) {
/* append opaque */
tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
free(*allocuserpwd);
*allocuserpwd = tmp;
}
if(d->algorithm) {
/* append algorithm */
tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
free(*allocuserpwd);
*allocuserpwd = tmp;
}
/* append CRLF to the userpwd header */
tmp = realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
strcat(tmp, "\r\n");
*allocuserpwd = tmp;
return CURLE_OK;
}
|
|||||
| ↓ | file_do | 27 | 79 | 152 | lib/file.c |
static CURLcode file_do(struct connectdata *conn, bool *done)
{
/* This implementation ignores the host name in conformance with
RFC 1738. Only local files (reachable via the standard file system)
are supported. This means that files on remotely mounted directories
(via NFS, Samba, NT sharing) can be accessed through a file:// URL
*/
CURLcode res = CURLE_OK;
struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
Windows version to have a different struct without
having to redefine the simple word 'stat' */
curl_off_t expected_size=0;
bool fstated=FALSE;
ssize_t nread;
size_t bytestoread;
struct SessionHandle *data = conn->data;
char *buf = data->state.buffer;
curl_off_t bytecount = 0;
int fd;
struct timeval now = Curl_tvnow();
*done = TRUE; /* unconditionally */
Curl_initinfo(data);
Curl_pgrsStartNow(data);
if(data->set.upload)
return file_upload(conn);
/* get the fd from the connection phase */
fd = conn->data->state.proto.file->fd;
/* VMS: This only works reliable for STREAMLF files */
if( -1 != fstat(fd, &statbuf)) {
/* we could stat it, then read out the size */
expected_size = statbuf.st_size;
fstated = TRUE;
}
/* If we have selected NOBODY and HEADER, it means that we only want file
information. Which for FILE can't be much more than the file size and
date. */
if(data->set.opt_no_body && data->set.include_header && fstated) {
CURLcode result;
snprintf(buf, sizeof(data->state.buffer),
"Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
result = Curl_client_write(conn, CLIENTWRITE_BOTH,
(char *)"Accept-ranges: bytes\r\n", 0);
if(result)
return result;
if(fstated) {
const struct tm *tm;
time_t filetime = (time_t)statbuf.st_mtime;
#ifdef HAVE_GMTIME_R
struct tm buffer;
tm = (const struct tm *)gmtime_r(&filetime, &buffer);
#else
tm = gmtime(&filetime);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
snprintf(buf, BUFSIZE-1,
"Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
Curl_month[tm->tm_mon],
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
}
return result;
}
/* Check whether file range has been specified */
file_range(conn);
/* Adjust the start offset in case we want to get the N last bytes
* of the stream iff the filesize could be determined */
if(data->state.resume_from < 0) {
if(!fstated) {
failf(data, "Can't get the size of file.");
return CURLE_READ_ERROR;
}
else
data->state.resume_from += (curl_off_t)statbuf.st_size;
}
if(data->state.resume_from <= expected_size)
expected_size -= data->state.resume_from;
else {
failf(data, "failed to resume file:// transfer");
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* A high water mark has been specified so we obey... */
if (data->req.maxdownload > 0)
expected_size = data->req.maxdownload;
if(fstated && (expected_size == 0))
return CURLE_OK;
/* The following is a shortcut implementation of file reading
this is both more efficient than the former call to download() and
it avoids problems with select() and recv() on file descriptors
in Winsock */
if(fstated)
Curl_pgrsSetDownloadSize(data, expected_size);
if(data->state.resume_from) {
if(data->state.resume_from !=
lseek(fd, data->state.resume_from, SEEK_SET))
return CURLE_BAD_DOWNLOAD_RESUME;
}
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
while(res == CURLE_OK) {
/* Don't fill a whole buffer if we want less than all data */
bytestoread = (expected_size < BUFSIZE-1)?(size_t)expected_size:BUFSIZE-1;
nread = read(fd, buf, bytestoread);
if( nread > 0)
buf[nread] = 0;
if (nread <= 0 || expected_size == 0)
break;
bytecount += nread;
expected_size -= nread;
res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
if(res)
return res;
Curl_pgrsSetDownloadCounter(data, bytecount);
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else
res = Curl_speedcheck(data, now);
}
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
return res;
}
|
|||||
| ↓ | readwrite_upload | 27 | 69 | 165 | lib/transfer.c |
static CURLcode readwrite_upload(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat)
{
ssize_t i, si;
ssize_t bytes_written;
CURLcode result;
ssize_t nread; /* number of bytes read */
if((k->bytecount == 0) && (k->writebytecount == 0))
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
*didwhat |= KEEP_WRITE;
/*
* We loop here to do the READ and SEND loop until we run out of
* data to send or until we get EWOULDBLOCK back
*/
do {
/* only read more data if there's no upload data already
present in the upload buffer */
if(0 == data->req.upload_present) {
/* init the "upload from here" pointer */
data->req.upload_fromhere = k->uploadbuf;
if(!k->upload_done) {
/* HTTP pollution, this should be written nicer to become more
protocol agnostic. */
int fillcount;
if((k->exp100 == EXP100_SENDING_REQUEST) &&
(data->state.proto.http->sending == HTTPSEND_BODY)) {
/* If this call is to send body data, we must take some action:
We have sent off the full HTTP 1.1 request, and we shall now
go into the Expect: 100 state and await such a header */
k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
k->keepon &= ~KEEP_WRITE; /* disable writing */
k->start100 = Curl_tvnow(); /* timeout count starts now */
*didwhat &= ~KEEP_WRITE; /* we didn't write anything actually */
break;
}
result = Curl_fillreadbuffer(conn, BUFSIZE, &fillcount);
if(result)
return result;
nread = (ssize_t)fillcount;
}
else
nread = 0; /* we're done uploading/reading */
if(!nread && (k->keepon & KEEP_WRITE_PAUSE)) {
/* this is a paused transfer */
break;
}
else if(nread<=0) {
/* done */
k->keepon &= ~KEEP_WRITE; /* we're done writing */
if(conn->bits.rewindaftersend) {
result = Curl_readrewind(conn);
if(result)
return result;
}
break;
}
/* store number of bytes available for upload */
data->req.upload_present = nread;
/* convert LF to CRLF if so asked */
#ifdef CURL_DO_LINEEND_CONV
/* always convert if we're FTPing in ASCII mode */
if((data->set.crlf) || (data->set.prefer_ascii))
#else
if(data->set.crlf)
#endif /* CURL_DO_LINEEND_CONV */
{
if(data->state.scratch == NULL)
data->state.scratch = malloc(2*BUFSIZE);
if(data->state.scratch == NULL) {
failf (data, "Failed to alloc scratch buffer!");
return CURLE_OUT_OF_MEMORY;
}
/*
* ASCII/EBCDIC Note: This is presumably a text (not binary)
* transfer so the data should already be in ASCII.
* That means the hex values for ASCII CR (0x0d) & LF (0x0a)
* must be used instead of the escape sequences \r & \n.
*/
for(i = 0, si = 0; i < nread; i++, si++) {
if(data->req.upload_fromhere[i] == 0x0a) {
data->state.scratch[si++] = 0x0d;
data->state.scratch[si] = 0x0a;
if(!data->set.crlf) {
/* we're here only because FTP is in ASCII mode...
bump infilesize for the LF we just added */
data->set.infilesize++;
}
}
else
data->state.scratch[si] = data->req.upload_fromhere[i];
}
if(si != nread) {
/* only perform the special operation if we really did replace
anything */
nread = si;
/* upload from the new (replaced) buffer instead */
data->req.upload_fromhere = data->state.scratch;
/* set the new amount too */
data->req.upload_present = nread;
}
}
} /* if 0 == data->req.upload_present */
else {
/* We have a partial buffer left from a previous "round". Use
that instead of reading more data */
}
/* write to socket (send away data) */
result = Curl_write(conn,
conn->writesockfd, /* socket to send to */
data->req.upload_fromhere, /* buffer pointer */
data->req.upload_present, /* buffer size */
&bytes_written); /* actually send away */
if(result)
return result;
if(data->set.verbose)
/* show the data before we change the pointer upload_fromhere */
Curl_debug(data, CURLINFO_DATA_OUT, data->req.upload_fromhere,
(size_t)bytes_written, conn);
if(data->req.upload_present != bytes_written) {
/* we only wrote a part of the buffer (if anything), deal with it! */
/* store the amount of bytes left in the buffer to write */
data->req.upload_present -= bytes_written;
/* advance the pointer where to find the buffer when the next send
is to happen */
data->req.upload_fromhere += bytes_written;
}
else {
/* we've uploaded that buffer now */
data->req.upload_fromhere = k->uploadbuf;
data->req.upload_present = 0; /* no more bytes left */
if(k->upload_done) {
/* switch off writing, we're done! */
k->keepon &= ~KEEP_WRITE; /* we're done writing */
}
}
k->writebytecount += bytes_written;
Curl_pgrsSetUploadCounter(data, k->writebytecount);
} while(0); /* just to break out from! */
return CURLE_OK;
}
|
|||||
| ↓ | Curl_perform | 27 | 66 | 144 | lib/transfer.c |
CURLcode Curl_perform(struct SessionHandle *data)
{
CURLcode res;
CURLcode res2;
struct connectdata *conn=NULL;
char *newurl = NULL; /* possibly a new URL to follow to! */
followtype follow = FOLLOW_NONE;
data->state.used_interface = Curl_if_easy;
res = Curl_pretransfer(data);
if(res)
return res;
/*
* It is important that there is NO 'return' from this function at any other
* place than falling down to the end of the function! This is because we
* have cleanup stuff that must be done before we get back, and that is only
* performed after this do-while loop.
*/
do {
res = connect_host(data, &conn); /* primary connection */
if(res == CURLE_OK) {
bool do_done;
if(data->set.connect_only) {
/* keep connection open for application to use the socket */
conn->bits.close = FALSE;
res = Curl_done(&conn, CURLE_OK, FALSE);
break;
}
res = Curl_do(&conn, &do_done);
if(res == CURLE_OK) {
res = Transfer(conn); /* now fetch that URL please */
if(res == CURLE_OK) {
bool retry = Curl_retry_request(conn, &newurl);
if(retry) {
follow = FOLLOW_RETRY;
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
else {
/*
* We must duplicate the new URL here as the connection data may
* be free()ed in the Curl_done() function. We prefer the newurl
* one since that's used for redirects or just further requests
* for retries or multi-stage HTTP auth methods etc.
*/
if(data->req.newurl) {
follow = FOLLOW_REDIR;
newurl = strdup(data->req.newurl);
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
else if(data->req.location) {
follow = FOLLOW_FAKE;
newurl = strdup(data->req.location);
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
}
/* in the above cases where 'newurl' gets assigned, we have a fresh
* allocated memory pointed to */
}
if(res != CURLE_OK) {
/* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is because we can't
* possibly know if the connection is in a good shape or not now. */
conn->bits.close = TRUE;
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
/* if we failed anywhere, we must clean up the secondary socket if
it was used */
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
}
/* Always run Curl_done(), even if some of the previous calls
failed, but return the previous (original) error code */
res2 = Curl_done(&conn, res, FALSE);
if(CURLE_OK == res)
res = res2;
}
else if(conn)
/* Curl_do() failed, clean up left-overs in the done-call, but note
that at some cases the conn pointer is NULL when Curl_do() failed
and the connection cache is very small so only call Curl_done() if
conn is still "alive".
*/
res2 = Curl_done(&conn, res, FALSE);
/*
* Important: 'conn' cannot be used here, since it may have been closed
* in 'Curl_done' or other functions.
*/
if((res == CURLE_OK) && follow) {
res = Curl_follow(data, newurl, follow);
if(CURLE_OK == res) {
/* if things went fine, Curl_follow() freed or otherwise took
responsibility for the newurl pointer */
newurl = NULL;
if(follow >= FOLLOW_RETRY) {
follow = FOLLOW_NONE;
continue;
}
/* else we break out of the loop below */
}
}
}
break; /* it only reaches here when this shouldn't loop */
} while(1); /* loop if Location: */
if(newurl)
free(newurl);
if(res && !data->state.errorbuf) {
/*
* As an extra precaution: if no error string has been set and there was
* an error, use the strerror() string or if things are so bad that not
* even that is good, set a bad string that mentions the error code.
*/
const char *str = curl_easy_strerror(res);
if(!str)
failf(data, "unspecified error %d", (int)res);
else
failf(data, "%s", str);
}
/* run post-transfer unconditionally, but don't clobber the return code if
we already have an error code recorder */
res2 = Curl_posttransfer(data);
if(!res && res2)
res = res2;
return res;
}
|
|||||
| ↓ | verifyhost | 27 | 72 | 161 | lib/ssluse.c |
static CURLcode verifyhost(struct connectdata *conn,
X509 *server_cert)
{
bool matched = FALSE; /* no alternative match yet */
int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
int addrlen = 0;
struct SessionHandle *data = conn->data;
STACK_OF(GENERAL_NAME) *altnames;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
CURLcode res = CURLE_OK;
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
}
else
#endif
if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
}
/* get a "list" of alternative names */
altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
if(altnames) {
int numalts;
int i;
/* get amount of alternatives, RFC2459 claims there MUST be at least
one, but we don't depend on it... */
numalts = sk_GENERAL_NAME_num(altnames);
/* loop through all alternatives while none has matched */
for (i=0; (i
|
|||||
| ↓ | Curl_close | 26 | 77 | 142 | lib/url.c |
CURLcode Curl_close(struct SessionHandle *data)
{
struct Curl_multi *m = data->multi;
#ifdef CURLDEBUG
/* only for debugging, scan through all connections and see if there's a
pipe reference still identifying this handle */
if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) {
struct conncache *c = data->state.connc;
long i;
struct curl_llist *pipeline;
struct curl_llist_element *curr;
struct connectdata *connptr;
for(i=0; i< c->num; i++) {
connptr = c->connects[i];
if(!connptr)
continue;
pipeline = connptr->send_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"MAJOR problem we %p are still in send pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
pipeline = connptr->recv_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"MAJOR problem we %p are still in recv pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
pipeline = connptr->pend_pipe;
if(pipeline) {
for (curr = pipeline->head; curr; curr=curr->next) {
if(data == (struct SessionHandle *) curr->ptr) {
fprintf(stderr,
"MAJOR problem we %p are still in pend pipe for %p done %d\n",
data, connptr, (int)connptr->bits.done);
}
}
}
}
}
#endif
if(m)
/* This handle is still part of a multi handle, take care of this first
and detach this handle from there. */
Curl_multi_rmeasy(data->multi, data);
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
the multi handle, since that function uses the magic
field! */
if(data->state.connc) {
if(data->state.connc->type == CONNCACHE_PRIVATE) {
/* close all connections still alive that are in the private connection
cache, as we no longer have the pointer left to the shared one. */
close_connections(data);
/* free the connection cache if allocated privately */
Curl_rm_connc(data->state.connc);
}
}
if(data->state.shared_conn) {
/* marked to be used by a pending connection so we can't kill this handle
just yet */
data->state.closed = TRUE;
return CURLE_OK;
}
if(data->dns.hostcachetype == HCACHE_PRIVATE)
Curl_hash_destroy(data->dns.hostcache);
if(data->state.rangestringalloc)
free(data->state.range);
/* Free the pathbuffer */
Curl_safefree(data->state.pathbuffer);
Curl_safefree(data->state.proto.generic);
/* Close down all open SSL info and sessions */
Curl_ssl_close_all(data);
Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch);
Curl_ssl_free_certinfo(data);
if(data->change.referer_alloc)
free(data->change.referer);
if(data->change.url_alloc)
free(data->change.url);
Curl_safefree(data->state.headerbuff);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
flush_cookies(data, 1);
#endif
Curl_digest_cleanup(data);
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
/* this destroys the channel and we cannot use it anymore after this */
ares_destroy(data->state.areschannel);
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
/* close iconv conversion descriptors */
if(data->inbound_cd != (iconv_t)-1) {
iconv_close(data->inbound_cd);
}
if(data->outbound_cd != (iconv_t)-1) {
iconv_close(data->outbound_cd);
}
if(data->utf8_cd != (iconv_t)-1) {
iconv_close(data->utf8_cd);
}
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
/* No longer a dirty share, if it exists */
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
data->share->dirty--;
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
Curl_freeset(data);
free(data);
return CURLE_OK;
}
|
|||||
| ↓ | _ldap_url_parse2 | 25 | 61 | 95 | lib/ldap.c |
static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp)
{
char *p, *q;
int i;
if(!conn->data ||
!conn->data->state.path ||
conn->data->state.path[0] != '/' ||
!checkprefix(conn->protostr, conn->data->change.url))
return LDAP_INVALID_SYNTAX;
ludp->lud_scope = LDAP_SCOPE_BASE;
ludp->lud_port = conn->remote_port;
ludp->lud_host = conn->host.name;
/* parse DN (Distinguished Name).
*/
ludp->lud_dn = strdup(conn->data->state.path+1);
if(!ludp->lud_dn)
return LDAP_NO_MEMORY;
p = strchr(ludp->lud_dn, '?');
LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) :
strlen(ludp->lud_dn), ludp->lud_dn));
if(!p)
goto success;
*p++ = '\0';
/* parse attributes. skip "??".
*/
q = strchr(p, '?');
if(q)
*q++ = '\0';
if(*p && *p != '?') {
ludp->lud_attrs = split_str(p);
if(!ludp->lud_attrs)
return LDAP_NO_MEMORY;
for (i = 0; ludp->lud_attrs[i]; i++)
LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i]));
}
p = q;
if(!p)
goto success;
/* parse scope. skip "??"
*/
q = strchr(p, '?');
if(q)
*q++ = '\0';
if(*p && *p != '?') {
ludp->lud_scope = str2scope(p);
if(ludp->lud_scope == -1)
return LDAP_INVALID_SYNTAX;
LDAP_TRACE (("scope %d\n", ludp->lud_scope));
}
p = q;
if(!p)
goto success;
/* parse filter
*/
q = strchr(p, '?');
if(q)
*q++ = '\0';
if(!*p)
return LDAP_INVALID_SYNTAX;
ludp->lud_filter = p;
LDAP_TRACE (("filter '%s'\n", ludp->lud_filter));
p = q;
if(!p)
goto success;
/* parse extensions
*/
ludp->lud_exts = split_str(p);
if(!ludp->lud_exts)
return LDAP_NO_MEMORY;
for (i = 0; ludp->lud_exts[i]; i++)
LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i]));
success:
if(!unescape_elements(conn->data, ludp))
return LDAP_NO_MEMORY;
return LDAP_SUCCESS;
}
|
|||||
| ↓ | concat_url | 25 | 60 | 139 | lib/transfer.c |
static char *concat_url(const char *base, const char *relurl)
{
/***
TRY to append this new path to the old URL
to the right of the host part. Oh crap, this is doomed to cause
problems in the future...
*/
char *newest;
char *protsep;
char *pathsep;
size_t newlen;
const char *useurl = relurl;
size_t urllen;
/* we must make our own copy of the URL to play with, as it may
point to read-only data */
char *url_clone=strdup(base);
if(!url_clone)
return NULL; /* skip out of this NOW */
/* protsep points to the start of the host name */
protsep=strstr(url_clone, "//");
if(!protsep)
protsep=url_clone;
else
protsep+=2; /* pass the slashes */
if('/' != relurl[0]) {
int level=0;
/* First we need to find out if there's a ?-letter in the URL,
and cut it and the right-side of that off */
pathsep = strchr(protsep, '?');
if(pathsep)
*pathsep=0;
/* we have a relative path to append to the last slash if there's one
available, or if the new URL is just a query string (starts with a
'?') we append the new one at the end of the entire currently worked
out URL */
if(useurl[0] != '?') {
pathsep = strrchr(protsep, '/');
if(pathsep)
*pathsep=0;
}
/* Check if there's any slash after the host name, and if so, remember
that position instead */
pathsep = strchr(protsep, '/');
if(pathsep)
protsep = pathsep+1;
else
protsep = NULL;
/* now deal with one "./" or any amount of "../" in the newurl
and act accordingly */
if((useurl[0] == '.') && (useurl[1] == '/'))
useurl+=2; /* just skip the "./" */
while((useurl[0] == '.') &&
(useurl[1] == '.') &&
(useurl[2] == '/')) {
level++;
useurl+=3; /* pass the "../" */
}
if(protsep) {
while(level--) {
/* cut off one more level from the right of the original URL */
pathsep = strrchr(protsep, '/');
if(pathsep)
*pathsep=0;
else {
*protsep=0;
break;
}
}
}
}
else {
/* We got a new absolute path for this server, cut off from the
first slash */
pathsep = strchr(protsep, '/');
if(pathsep) {
/* When people use badly formatted URLs, such as
"http://www.url.com?dir=/home/daniel" we must not use the first
slash, if there's a ?-letter before it! */
char *sep = strchr(protsep, '?');
if(sep && (sep < pathsep))
pathsep = sep;
*pathsep=0;
}
else {
/* There was no slash. Now, since we might be operating on a badly
formatted URL, such as "http://www.url.com?id=2380" which doesn't
use a slash separator as it is supposed to, we need to check for a
?-letter as well! */
pathsep = strchr(protsep, '?');
if(pathsep)
*pathsep=0;
}
}
/* If the new part contains a space, this is a mighty stupid redirect
but we still make an effort to do "right". To the left of a '?'
letter we replace each space with %20 while it is replaced with '+'
on the right side of the '?' letter.
*/
newlen = strlen_url(useurl);
urllen = strlen(url_clone);
newest = malloc( urllen + 1 + /* possible slash */
newlen + 1 /* zero byte */);
if(!newest) {
free(url_clone); /* don't leak this */
return NULL;
}
/* copy over the root url part */
memcpy(newest, url_clone, urllen);
/* check if we need to append a slash */
if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
;
else
newest[urllen++]='/';
/* then append the new piece on the right side */
strcpy_url(&newest[urllen], useurl);
free(url_clone);
return newest;
}
|
|||||
| ↓ | ParseURLAndFillConnection | 25 | 58 | 198 | lib/url.c |
static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
struct connectdata *conn)
{
char *at;
char *tmp;
char *path = data->state.path;
int rc;
/*************************************************************
* Parse the URL.
*
* We need to parse the url even when using the proxy, because we will need
* the hostname and port in case we are trying to SSL connect through the
* proxy -- and we don't know if we will need to use SSL until we parse the
* url ...
************************************************************/
if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
conn->protostr,
path)) && strequal(conn->protostr, "file")) {
if(path[0] == '/' && path[1] == '/') {
/* Allow omitted hostname (e.g. file:/
|
|||||
| ↓ | dprintf_Pass1Report | 32 | 72 | 92 | lib/mprintf.c |
static void dprintf_Pass1Report(va_stack_t *vto, int max)
{
int i;
char buffer[256];
int bit;
int flags;
for(i=0; i |
|||||
| ↓ | krb5_auth | 24 | 91 | 146 | lib/krb5.c |
static int
krb5_auth(void *app_data, struct connectdata *conn)
{
int ret;
char *p;
const char *host = conn->dns_entry->addr->ai_canonname;
ssize_t nread;
socklen_t l = sizeof(conn->local_addr);
struct SessionHandle *data = conn->data;
CURLcode result;
const char *service = "ftp", *srv_host = "host";
gss_buffer_desc gssbuf, _gssresp, *gssresp;
OM_uint32 maj, min;
gss_name_t gssname;
gss_ctx_id_t *context = app_data;
struct gss_channel_bindings_struct chan;
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)LOCAL_ADDR, &l) < 0)
perror("getsockname()");
chan.initiator_addrtype = GSS_C_AF_INET;
chan.initiator_address.length = l - 4;
chan.initiator_address.value = &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr;
chan.acceptor_addrtype = GSS_C_AF_INET;
chan.acceptor_address.length = l - 4;
chan.acceptor_address.value = &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr;
chan.application_data.length = 0;
chan.application_data.value = NULL;
/* this loop will execute twice (once for service, once for host) */
while(1) {
/* this really shouldn't be repeated here, but can't help it */
if(service == srv_host) {
result = Curl_ftpsendf(conn, "AUTH GSSAPI");
if(result)
return -2;
if(Curl_GetFTPResponse(&nread, conn, NULL))
return -1;
if(data->state.buffer[0] != '3')
return -1;
}
gssbuf.value = data->state.buffer;
gssbuf.length = snprintf(gssbuf.value, BUFSIZE, "%s@%s", service, host);
maj = gss_import_name(&min, &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &gssname);
if(maj != GSS_S_COMPLETE) {
gss_release_name(&min, &gssname);
if(service == srv_host) {
Curl_failf(data, "Error importing service name %s", gssbuf.value);
return AUTH_ERROR;
}
service = srv_host;
continue;
}
{
gss_OID t;
gss_display_name(&min, gssname, &gssbuf, &t);
Curl_infof(data, "Trying against %s\n", gssbuf.value);
gss_release_buffer(&min, &gssbuf);
}
gssresp = GSS_C_NO_BUFFER;
*context = GSS_C_NO_CONTEXT;
do {
ret = AUTH_OK;
maj = gss_init_sec_context(&min,
GSS_C_NO_CREDENTIAL,
context,
gssname,
GSS_C_NO_OID,
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
0,
&chan,
gssresp,
NULL,
&gssbuf,
NULL,
NULL);
if(gssresp) {
free(_gssresp.value);
gssresp = NULL;
}
if(maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
Curl_infof(data, "Error creating security context");
ret = AUTH_ERROR;
break;
}
if(gssbuf.length != 0) {
if(Curl_base64_encode(data, (char *)gssbuf.value, gssbuf.length, &p) < 1) {
Curl_infof(data, "Out of memory base64-encoding");
ret = AUTH_CONTINUE;
break;
}
result = Curl_ftpsendf(conn, "ADAT %s", p);
free(p);
if(result) {
ret = -2;
break;
}
if(Curl_GetFTPResponse(&nread, conn, NULL)) {
ret = -1;
break;
}
if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3'){
Curl_infof(data, "Server didn't accept auth data\n");
ret = AUTH_ERROR;
break;
}
p = data->state.buffer + 4;
p = strstr(p, "ADAT=");
if(p) {
_gssresp.length = Curl_base64_decode(p + 5, (unsigned char **)&_gssresp.value);
if(_gssresp.length < 1) {
Curl_failf(data, "Out of memory base64-encoding");
ret = AUTH_CONTINUE;
break;
}
}
gssresp = &_gssresp;
}
} while(maj == GSS_S_CONTINUE_NEEDED);
gss_release_name(&min, &gssname);
if(gssresp)
free(_gssresp.value);
if(ret == AUTH_OK || service == srv_host)
return ret;
service = srv_host;
}
}
|
|||||
| ↓ | bindlocal | 24 | 81 | 181 | lib/connect.c |
static CURLcode bindlocal(struct connectdata *conn,
curl_socket_t sockfd)
{
#ifdef ENABLE_IPV6
char ipv6_addr[16];
#endif
struct SessionHandle *data = conn->data;
struct sockaddr_in me;
struct sockaddr *sock = NULL; /* bind to this address */
socklen_t socksize; /* size of the data sock points to */
unsigned short port = data->set.localport; /* use this port number, 0 for
"random" */
/* how many port numbers to try to bind to, increasing one at a time */
int portnum = data->set.localportrange;
const char *dev = data->set.str[STRING_DEVICE];
/*************************************************************
* Select device to bind socket to
*************************************************************/
if(dev && (strlen(dev)<255) ) {
struct Curl_dns_entry *h=NULL;
char myhost[256] = "";
in_addr_t in;
int rc;
bool was_iface = FALSE;
int in6 = -1;
/* First check if the given name is an IP address */
in=inet_addr((char *) dev);
if((in == CURL_INADDR_NONE) &&
Curl_if2ip(dev, myhost, sizeof(myhost))) {
/*
* We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
*/
rc = Curl_resolv(conn, myhost, 0, &h);
if(rc == CURLRESOLV_PENDING)
(void)Curl_wait_for_resolv(conn, &h);
if(h) {
was_iface = TRUE;
Curl_resolv_unlock(data, h);
}
}
if(!was_iface) {
/*
* This was not an interface, resolve the name as a host name
* or IP number
*/
rc = Curl_resolv(conn, dev, 0, &h);
if(rc == CURLRESOLV_PENDING)
(void)Curl_wait_for_resolv(conn, &h);
if(h) {
if(in == CURL_INADDR_NONE)
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
Curl_printable_address(h->addr, myhost, sizeof myhost);
else
/* we know data->set.device is shorter than the myhost array */
strcpy(myhost, dev);
Curl_resolv_unlock(data, h);
}
}
if(! *myhost) {
/* need to fix this
h=Curl_gethost(data,
getmyhost(*myhost,sizeof(myhost)),
hostent_buf,
sizeof(hostent_buf));
*/
failf(data, "Couldn't bind to '%s'", dev);
return CURLE_INTERFACE_FAILED;
}
infof(data, "Bind local address to %s\n", myhost);
#ifdef SO_BINDTODEVICE
/* I am not sure any other OSs than Linux that provide this feature, and
* at the least I cannot test. --Ben
*
* This feature allows one to tightly bind the local socket to a
* particular interface. This will force even requests to other local
* interfaces to go out the external interface.
*
*/
if(was_iface) {
/* Only bind to the interface when specified as interface, not just as a
* hostname or ip address.
*/
if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
dev, strlen(dev)+1) != 0) {
/* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
sockfd, dev, Curl_strerror(SOCKERRNO)); */
infof(data, "SO_BINDTODEVICE %s failed\n", dev);
/* This is typically "errno 1, error: Operation not permitted" if
you're not running as root or another suitable privileged user */
}
}
#endif
in=inet_addr(myhost);
#ifdef ENABLE_IPV6
in6 = Curl_inet_pton (AF_INET6, myhost, (void *)&ipv6_addr);
#endif
if(CURL_INADDR_NONE == in && -1 == in6) {
failf(data,"couldn't find my own IP address (%s)", myhost);
return CURLE_INTERFACE_FAILED;
} /* end of inet_addr */
if( h ) {
Curl_addrinfo *addr = h->addr;
sock = addr->ai_addr;
socksize = addr->ai_addrlen;
}
else
return CURLE_INTERFACE_FAILED;
}
else if(port) {
/* if a local port number is requested but no local IP, extract the
address from the socket */
memset(&me, 0, sizeof(struct sockaddr));
me.sin_family = AF_INET;
me.sin_addr.s_addr = INADDR_ANY;
sock = (struct sockaddr *)&me;
socksize = sizeof(struct sockaddr);
}
else
/* no local kind of binding was requested */
return CURLE_OK;
do {
/* Set port number to bind to, 0 makes the system pick one */
if(sock->sa_family == AF_INET)
((struct sockaddr_in *)sock)->sin_port = htons(port);
#ifdef ENABLE_IPV6
else
((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
#endif
if( bind(sockfd, sock, socksize) >= 0) {
/* we succeeded to bind */
struct Curl_sockaddr_storage add;
socklen_t size;
size = sizeof(add);
if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
failf(data, "getsockname() failed");
return CURLE_INTERFACE_FAILED;
}
/* We re-use/clobber the port variable here below */
if(((struct sockaddr *)&add)->sa_family == AF_INET)
port = ntohs(((struct sockaddr_in *)&add)->sin_port);
#ifdef ENABLE_IPV6
else
port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port);
#endif
infof(data, "Local port: %d\n", port);
conn->bits.bound = TRUE;
return CURLE_OK;
}
if(--portnum > 0) {
infof(data, "Bind to local port %d failed, trying next\n", port);
port++; /* try next port */
}
else
break;
} while(1);
data->state.os_errno = SOCKERRNO;
failf(data, "bind failure: %s",
Curl_strerror(conn, data->state.os_errno));
return CURLE_INTERFACE_FAILED;
}
|
|||||
| ↓ | Curl_input_negotiate | 24 | 75 | 139 | lib/http_negotiate.c |
int Curl_input_negotiate(struct connectdata *conn, bool proxy,
const char *header)
{
struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
&conn->data->state.negotiate;
OM_uint32 major_status, minor_status, minor_status2;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
int ret;
size_t len;
bool gss;
const char* protocol;
while(*header && ISSPACE(*header))
header++;
if(checkprefix("GSS-Negotiate", header)) {
protocol = "GSS-Negotiate";
gss = TRUE;
}
else if(checkprefix("Negotiate", header)) {
protocol = "Negotiate";
gss = FALSE;
}
else
return -1;
if(neg_ctx->context) {
if(neg_ctx->gss != gss) {
return -1;
}
}
else {
neg_ctx->protocol = protocol;
neg_ctx->gss = gss;
}
if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
/* We finished succesfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
Curl_cleanup_negotiate(conn->data);
return -1;
}
if(neg_ctx->server_name == NULL &&
(ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
return ret;
header += strlen(neg_ctx->protocol);
while(*header && ISSPACE(*header))
header++;
len = strlen(header);
if(len > 0) {
int rawlen = Curl_base64_decode(header,
(unsigned char **)&input_token.value);
if(rawlen < 0)
return -1;
input_token.length = rawlen;
#ifdef HAVE_SPNEGO /* Handle SPNEGO */
if(checkprefix("Negotiate", header)) {
ASN1_OBJECT * object = NULL;
int rc = 1;
unsigned char * spnegoToken = NULL;
size_t spnegoTokenLength = 0;
unsigned char * mechToken = NULL;
size_t mechTokenLength = 0;
if(input_token.value == NULL)
return CURLE_OUT_OF_MEMORY;
spnegoToken = malloc(input_token.length);
if(spnegoToken == NULL)
return CURLE_OUT_OF_MEMORY;
spnegoTokenLength = input_token.length;
object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
if(!parseSpnegoTargetToken(spnegoToken,
spnegoTokenLength,
NULL,
NULL,
&mechToken,
&mechTokenLength,
NULL,
NULL)) {
free(spnegoToken);
spnegoToken = NULL;
infof(conn->data, "Parse SPNEGO Target Token failed\n");
}
else {
free(input_token.value);
input_token.value = malloc(mechTokenLength);
if (input_token.value == NULL)
return CURLE_OUT_OF_MEMORY;
memcpy(input_token.value, mechToken,mechTokenLength);
input_token.length = mechTokenLength;
free(mechToken);
mechToken = NULL;
infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
}
}
#endif
}
major_status = gss_init_sec_context(&minor_status,
GSS_C_NO_CREDENTIAL,
&neg_ctx->context,
neg_ctx->server_name,
GSS_C_NO_OID,
GSS_C_DELEG_FLAG,
0,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
NULL,
&output_token,
NULL,
NULL);
if(input_token.length > 0)
gss_release_buffer(&minor_status2, &input_token);
neg_ctx->status = major_status;
if(GSS_ERROR(major_status)) {
/* Curl_cleanup_negotiate(conn->data) ??? */
log_gss_error(conn, minor_status,
"gss_init_sec_context() failed: ");
return -1;
}
if(output_token.length == 0) {
return -1;
}
neg_ctx->output_token = output_token;
/* conn->bits.close = FALSE; */
return 0;
}
|
|||||
| ↓ | Curl_follow | 27 | 61 | 194 | lib/transfer.c |
CURLcode Curl_follow(struct SessionHandle *data,
char *newurl, /* this 'newurl' is the Location: string,
and it must be malloc()ed before passed
here */
followtype type) /* see transfer.h */
{
/* Location: redirect */
bool disallowport = FALSE;
if(type == FOLLOW_REDIR) {
if((data->set.maxredirs != -1) &&
(data->set.followlocation >= data->set.maxredirs)) {
failf(data,"Maximum (%d) redirects followed", data->set.maxredirs);
return CURLE_TOO_MANY_REDIRECTS;
}
/* mark the next request as a followed location: */
data->state.this_is_a_follow = TRUE;
data->set.followlocation++; /* count location-followers */
if(data->set.http_auto_referer) {
/* We are asked to automatically set the previous URL as the referer
when we get the next URL. We pick the ->url field, which may or may
not be 100% correct */
if(data->change.referer_alloc)
/* If we already have an allocated referer, free this first */
free(data->change.referer);
data->change.referer = strdup(data->change.url);
if (!data->change.referer) {
data->change.referer_alloc = FALSE;
return CURLE_OUT_OF_MEMORY;
}
data->change.referer_alloc = TRUE; /* yes, free this later */
}
}
if(!is_absolute_url(newurl)) {
/***
*DANG* this is an RFC 2068 violation. The URL is supposed
to be absolute and this doesn't seem to be that!
*/
char *absolute = concat_url(data->change.url, newurl);
if (!absolute)
return CURLE_OUT_OF_MEMORY;
free(newurl);
newurl = absolute;
}
else {
/* This is an absolute URL, don't allow the custom port number */
disallowport = TRUE;
if(strchr(newurl, ' ')) {
/* This new URL contains at least one space, this is a mighty stupid
redirect but we still make an effort to do "right". */
char *newest;
size_t newlen = strlen_url(newurl);
newest = malloc(newlen+1); /* get memory for this */
if (!newest)
return CURLE_OUT_OF_MEMORY;
strcpy_url(newest, newurl); /* create a space-free URL */
free(newurl); /* that was no good */
newurl = newest; /* use this instead now */
}
}
if(type == FOLLOW_FAKE) {
/* we're only figuring out the new url if we would've followed locations
but now we're done so we can get out! */
data->info.wouldredirect = newurl;
return CURLE_OK;
}
if(disallowport)
data->state.allow_port = FALSE;
if(data->change.url_alloc)
free(data->change.url);
else
data->change.url_alloc = TRUE; /* the URL is allocated */
data->change.url = newurl;
newurl = NULL; /* don't free! */
infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
/*
* We get here when the HTTP code is 300-399 (and 401). We need to perform
* differently based on exactly what return code there was.
*
* News from 7.10.6: we can also get here on a 401 or 407, in case we act on
* a HTTP (proxy-) authentication scheme other than Basic.
*/
switch(data->info.httpcode) {
/* 401 - Act on a WWW-Authenticate, we keep on moving and do the
Authorization: XXXX header in the HTTP request code snippet */
/* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
Proxy-Authorization: XXXX header in the HTTP request code snippet */
/* 300 - Multiple Choices */
/* 306 - Not used */
/* 307 - Temporary Redirect */
default: /* for all above (and the unknown ones) */
/* Some codes are explicitly mentioned since I've checked RFC2616 and they
* seem to be OK to POST to.
*/
break;
case 301: /* Moved Permanently */
/* (quote from RFC2616, section 10.3.2):
*
* Note: When automatically redirecting a POST request after receiving a
* 301 status code, some existing HTTP/1.0 user agents will erroneously
* change it into a GET request.
*
* ----
*
* Warning: Because most of importants user agents do this obvious RFC2616
* violation, many webservers expect this misbehavior. So these servers
* often answers to a POST request with an error page. To be sure that
* libcurl gets the page that most user agents would get, libcurl has to
* force GET.
*
* This behaviour can be overridden with CURLOPT_POSTREDIR.
*/
if( (data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM)
&& !data->set.post301) {
infof(data,
"Violate RFC 2616/10.3.2 and switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 302: /* Found */
/* (From 10.3.3)
Note: RFC 1945 and RFC 2068 specify that the client is not allowed
to change the method on the redirected request. However, most
existing user agent implementations treat 302 as if it were a 303
response, performing a GET on the Location field-value regardless
of the original request method. The status codes 303 and 307 have
been added for servers that wish to make unambiguously clear which
kind of reaction is expected of the client.
(From 10.3.4)
Note: Many pre-HTTP/1.1 user agents do not understand the 303
status. When interoperability with such clients is a concern, the
302 status code may be used instead, since most user agents react
to a 302 response as described here for 303.
This behaviour can be overriden with CURLOPT_POSTREDIR
*/
if( (data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM)
&& !data->set.post302) {
infof(data,
"Violate RFC 2616/10.3.3 and switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 303: /* See Other */
/* Disable both types of POSTs, since doing a second POST when
* following isn't what anyone would want! */
if(data->set.httpreq != HTTPREQ_GET) {
data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
infof(data, "Disables POST, goes with %s\n",
data->set.opt_no_body?"HEAD":"GET");
}
break;
case 304: /* Not Modified */
/* 304 means we did a conditional request and it was "Not modified".
* We shouldn't get any Location: header in this response!
*/
break;
case 305: /* Use Proxy */
/* (quote from RFC2616, section 10.3.6):
* "The requested resource MUST be accessed through the proxy given
* by the Location field. The Location field gives the URI of the
* proxy. The recipient is expected to repeat this single request
* via the proxy. 305 responses MUST only be generated by origin
* servers."
*/
break;
}
Curl_pgrsTime(data, TIMER_REDIRECT);
Curl_pgrsResetTimes(data);
return CURLE_OK;
}
|
|||||
| ↓ | Curl_SOCKS4 | 26 | 87 | 229 | lib/socks.c |
CURLcode Curl_SOCKS4(const char *proxy_name,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn,
bool protocol4a)
{
#define SOCKS4REQLEN 262
unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
id */
int result;
CURLcode code;
curl_socket_t sock = conn->sock[sockindex];
long timeout;
struct SessionHandle *data = conn->data;
/* get timeout */
timeout = Curl_timeleft(conn, NULL, TRUE);
if(timeout < 0) {
/* time-out, bail out, go home */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
Curl_nonblock(sock, FALSE);
/*
* Compose socks4 request
*
* Request format
*
* +----+----+----+----+----+----+----+----+----+----+....+----+
* | VN | CD | DSTPORT | DSTIP | USERID |NULL|
* +----+----+----+----+----+----+----+----+----+----+....+----+
* # of bytes: 1 1 2 4 variable 1
*/
socksreq[0] = 4; /* version (SOCKS4) */
socksreq[1] = 1; /* connect */
*((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);
/* DNS resolve only for SOCKS4, not SOCKS4a */
if (!protocol4a) {
struct Curl_dns_entry *dns;
Curl_addrinfo *hp=NULL;
int rc;
rc = Curl_resolv(conn, hostname, remote_port, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_PROXY;
if(rc == CURLRESOLV_PENDING)
/* this requires that we're in "wait for resolve" state */
rc = Curl_wait_for_resolv(conn, &dns);
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same.
*/
if(dns)
hp=dns->addr;
if(hp) {
char buf[64];
unsigned short ip[4];
Curl_printable_address(hp, buf, sizeof(buf));
if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3])) {
/* Set DSTIP */
socksreq[4] = (unsigned char)ip[0];
socksreq[5] = (unsigned char)ip[1];
socksreq[6] = (unsigned char)ip[2];
socksreq[7] = (unsigned char)ip[3];
}
else
hp = NULL; /* fail! */
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
}
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
hostname);
return CURLE_COULDNT_RESOLVE_HOST;
}
}
/*
* This is currently not supporting "Identification Protocol (RFC1413)".
*/
socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
if(proxy_name)
strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
/*
* Make connection
*/
{
ssize_t actualread;
ssize_t written;
ssize_t hostnamelen = 0;
int packetsize = 9 +
(int)strlen((char*)socksreq + 8); /* size including NUL */
/* If SOCKS4a, set special invalid IP address 0.0.0.x */
if (protocol4a) {
socksreq[4] = 0;
socksreq[5] = 0;
socksreq[6] = 0;
socksreq[7] = 1;
/* If still enough room in buffer, also append hostname */
hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
if (packetsize + hostnamelen <= SOCKS4REQLEN)
strcpy((char*)socksreq + packetsize, hostname);
else
hostnamelen = 0; /* Flag: hostname did not fit in buffer */
}
/* Send request */
code = Curl_write_plain(conn, sock, (char *)socksreq,
packetsize + hostnamelen,
&written);
if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
failf(data, "Failed to send SOCKS4 connect request.");
return CURLE_COULDNT_CONNECT;
}
if (protocol4a && hostnamelen == 0) {
/* SOCKS4a with very long hostname - send that name separately */
hostnamelen = (ssize_t)strlen(hostname) + 1;
code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
&written);
if((code != CURLE_OK) || (written != hostnamelen)) {
failf(data, "Failed to send SOCKS4 connect request.");
return CURLE_COULDNT_CONNECT;
}
}
packetsize = 8; /* receive data size */
/* Receive response */
result = blockread_all(conn, sock, (char *)socksreq, packetsize,
&actualread, timeout);
if((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS4 connect request ack.");
return CURLE_COULDNT_CONNECT;
}
/*
* Response format
*
* +----+----+----+----+----+----+----+----+
* | VN | CD | DSTPORT | DSTIP |
* +----+----+----+----+----+----+----+----+
* # of bytes: 1 1 2 4
*
* VN is the version of the reply code and should be 0. CD is the result
* code with one of the following values:
*
* 90: request granted
* 91: request rejected or failed
* 92: request rejected because SOCKS server cannot connect to
* identd on the client
* 93: request rejected because the client program and identd
* report different user-ids
*/
/* wrong version ? */
if(socksreq[0] != 0) {
failf(data,
"SOCKS4 reply has wrong version, version should be 4.");
return CURLE_COULDNT_CONNECT;
}
/* Result */
switch(socksreq[1])
{
case 90:
if (protocol4a)
infof(data, "SOCKS4a request granted.\n");
else
infof(data, "SOCKS4 request granted.\n");
break;
case 91:
failf(data,
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected or failed.",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return CURLE_COULDNT_CONNECT;
case 92:
failf(data,
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected because SOCKS server cannot connect to "
"identd on the client.",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return CURLE_COULDNT_CONNECT;
case 93:
failf(data,
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected because the client program and identd "
"report different user-ids.",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return CURLE_COULDNT_CONNECT;
default:
failf(data,
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", Unknown.",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
}
Curl_nonblock(sock, TRUE);
return CURLE_OK; /* Proxy was successful! */
}
|
|||||
| ↓ | ftp_state_get_resp | 22 | 46 | 124 | lib/ftp.c |
static CURLcode ftp_state_get_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct FTP *ftp = data->state.proto.ftp;
char *buf = data->state.buffer;
if((ftpcode == 150) || (ftpcode == 125)) {
/*
A;
150 Opening BINARY mode data connection for /etc/passwd (2241
bytes). (ok, the file is being transfered)
B:
150 Opening ASCII mode data connection for /bin/ls
C:
150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
D:
150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
E:
125 Data connection already open; Transfer starting. */
curl_off_t size=-1; /* default unknown size */
/*
* It appears that there are FTP-servers that return size 0 for files when
* SIZE is used on the file while being in BINARY mode. To work around
* that (stupid) behavior, we attempt to parse the RETR response even if
* the SIZE returned size zero.
*
* Debugging help from Salvatore Sorrentino on February 26, 2003.
*/
if((instate != FTP_LIST) &&
!data->set.prefer_ascii &&
(ftp->downloadsize < 1)) {
/*
* It seems directory listings either don't show the size or very
* often uses size 0 anyway. ASCII transfers may very well turn out
* that the transfered amount of data is not the same as this line
* tells, why using this number in those cases only confuses us.
*
* Example D above makes this parsing a little tricky */
char *bytes;
bytes=strstr(buf, " bytes");
if(bytes--) {
long in=(long)(bytes-buf);
/* this is a hint there is size information in there! ;-) */
while(--in) {
/* scan for the left parenthesis and break there */
if('(' == *bytes)
break;
/* skip only digits */
if(!ISDIGIT(*bytes)) {
bytes=NULL;
break;
}
/* one more estep backwards */
bytes--;
}
/* if we have nothing but digits: */
if(bytes++) {
/* get the number! */
size = curlx_strtoofft(bytes, NULL, 0);
}
}
}
else if(ftp->downloadsize > -1)
size = ftp->downloadsize;
if(data->set.ftp_use_port) {
/* BLOCKING */
result = AllowServerConnect(conn);
if( result )
return result;
}
if(conn->ssl[SECONDARYSOCKET].use) {
/* since we only have a plaintext TCP connection here, we must now
do the TLS stuff */
infof(data, "Doing the SSL/TLS handshake on the data stream\n");
result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result)
return result;
}
if(size > data->req.maxdownload && data->req.maxdownload > 0)
size = data->req.size = data->req.maxdownload;
infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
if(instate != FTP_LIST)
infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
/* FTP download: */
result=Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
ftp->bytecountp,
-1, NULL); /* no upload here */
if(result)
return result;
state(conn, FTP_STOP);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
/* simply no matching files in the dir listing */
ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
state(conn, FTP_STOP); /* this phase is over */
}
else {
failf(data, "RETR response: %03d", ftpcode);
return CURLE_FTP_COULDNT_RETR_FILE;
}
}
return result;
}
|
|||||
| ↓ | printsub | 29 | 62 | 111 | lib/telnet.c |
static void printsub(struct SessionHandle *data,
int direction, /* '<' or '>' */
unsigned char *pointer, /* where suboption data is */
size_t length) /* length of suboption data */
{
unsigned int i = 0;
if(data->set.verbose)
{
if(direction)
{
infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
if(length >= 3)
{
int j;
i = pointer[length-2];
j = pointer[length-1];
if(i != CURL_IAC || j != CURL_SE)
{
infof(data, "(terminated by ");
if(CURL_TELOPT_OK(i))
infof(data, "%s ", CURL_TELOPT(i));
else if(CURL_TELCMD_OK(i))
infof(data, "%s ", CURL_TELCMD(i));
else
infof(data, "%d ", i);
if(CURL_TELOPT_OK(j))
infof(data, "%s", CURL_TELOPT(j));
else if(CURL_TELCMD_OK(j))
infof(data, "%s", CURL_TELCMD(j));
else
infof(data, "%d", j);
infof(data, ", not IAC SE!) ");
}
}
length -= 2;
}
if(length < 1)
{
infof(data, "(Empty suboption?)");
return;
}
if(CURL_TELOPT_OK(pointer[0])) {
switch(pointer[0]) {
case CURL_TELOPT_TTYPE:
case CURL_TELOPT_XDISPLOC:
case CURL_TELOPT_NEW_ENVIRON:
infof(data, "%s", CURL_TELOPT(pointer[0]));
break;
default:
infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
break;
}
}
else
infof(data, "%d (unknown)", pointer[i]);
switch(pointer[1]) {
case CURL_TELQUAL_IS:
infof(data, " IS");
break;
case CURL_TELQUAL_SEND:
infof(data, " SEND");
break;
case CURL_TELQUAL_INFO:
infof(data, " INFO/REPLY");
break;
case CURL_TELQUAL_NAME:
infof(data, " NAME");
break;
}
switch(pointer[0]) {
case CURL_TELOPT_TTYPE:
case CURL_TELOPT_XDISPLOC:
pointer[length] = 0;
infof(data, " \"%s\"", &pointer[2]);
break;
case CURL_TELOPT_NEW_ENVIRON:
if(pointer[1] == CURL_TELQUAL_IS) {
infof(data, " ");
for(i = 3;i < length;i++) {
switch(pointer[i]) {
case CURL_NEW_ENV_VAR:
infof(data, ", ");
break;
case CURL_NEW_ENV_VALUE:
infof(data, " = ");
break;
default:
infof(data, "%c", pointer[i]);
break;
}
}
}
break;
default:
for (i = 2; i < length; i++)
infof(data, " %.2x", pointer[i]);
break;
}
if(direction)
{
infof(data, "\n");
}
}
}
|
|||||
| ↓ | singleipconnect | 23 | 70 | 160 | lib/connect.c |
static curl_socket_t
singleipconnect(struct connectdata *conn,
const Curl_addrinfo *ai,
long timeout_ms,
bool *connected)
{
char addr_buf[128];
int rc;
int error;
bool isconnected;
struct SessionHandle *data = conn->data;
curl_socket_t sockfd;
CURLcode res;
/*
* Curl_sockaddr_storage, which is basically sockaddr_storage has a space
* for a largest possible struct sockaddr only. We should add some space for
* the other fields we are using. Hence the addr_storage size math.
*/
char addr_storage[sizeof(struct curl_sockaddr)-
sizeof(struct sockaddr)+
sizeof(struct Curl_sockaddr_storage)];
struct curl_sockaddr *addr=(struct curl_sockaddr*)&addr_storage;
const void *iptoprint;
addr->family=ai->ai_family;
addr->socktype=conn->socktype;
addr->protocol=ai->ai_protocol;
addr->addrlen =
(ai->ai_addrlen < (socklen_t)sizeof(struct Curl_sockaddr_storage)) ?
(unsigned int)ai->ai_addrlen : sizeof(struct Curl_sockaddr_storage);
memcpy(&addr->addr, ai->ai_addr, addr->addrlen);
/* If set, use opensocket callback to get the socket */
if(data->set.fopensocket)
sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN, addr);
else
sockfd = socket(addr->family, addr->socktype, addr->protocol);
if(sockfd == CURL_SOCKET_BAD)
return CURL_SOCKET_BAD;
*connected = FALSE; /* default is not connected */
#ifdef CURLRES_IPV6
if (conn->scope && (addr->family == AF_INET6)) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr->addr;
in6->sin6_scope_id = conn->scope;
}
#endif
/* FIXME: do we have Curl_printable_address-like with struct sockaddr* as
argument? */
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
if(addr->family==AF_UNIX) {
infof(data, " Trying %s... ",
((const struct sockaddr_un*)(&addr->addr))->sun_path);
snprintf(data->info.ip, MAX_IPADR_LEN, "%s",
((const struct sockaddr_un*)(&addr->addr))->sun_path);
}
else
#endif
{
#ifdef ENABLE_IPV6
if(addr->family==AF_INET6)
iptoprint= &((const struct sockaddr_in6*)(&addr->addr))->sin6_addr;
else
#endif
iptoprint = &((const struct sockaddr_in*)(&addr->addr))->sin_addr;
if(Curl_inet_ntop(addr->family, iptoprint, addr_buf,
sizeof(addr_buf)) != NULL) {
infof(data, " Trying %s... ", addr_buf);
snprintf(data->info.ip, MAX_IPADR_LEN, "%s", addr_buf);
}
}
if(data->set.tcp_nodelay)
tcpnodelay(conn, sockfd);
nosigpipe(conn, sockfd);
if(data->set.fsockopt) {
/* activate callback for setting socket options */
error = data->set.fsockopt(data->set.sockopt_client,
sockfd,
CURLSOCKTYPE_IPCXN);
if(error) {
sclose(sockfd); /* close the socket and bail out */
return CURL_SOCKET_BAD;
}
}
/* possibly bind the local end to an IP, interface or port */
res = bindlocal(conn, sockfd);
if(res) {
sclose(sockfd); /* close socket and bail out */
return CURL_SOCKET_BAD;
}
/* set socket non-blocking */
Curl_nonblock(sockfd, TRUE);
/* Connect TCP sockets, bind UDP */
if(conn->socktype == SOCK_STREAM)
rc = connect(sockfd, &addr->addr, addr->addrlen);
else
rc = 0;
if(-1 == rc) {
error = SOCKERRNO;
switch (error) {
case EINPROGRESS:
case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
/* On some platforms EAGAIN and EWOULDBLOCK are the
* same value, and on others they are different, hence
* the odd #if
*/
case EAGAIN:
#endif
rc = waitconnect(sockfd, timeout_ms);
break;
default:
/* unknown error, fallthrough and try another address! */
failf(data, "Failed to connect to %s: %s",
addr_buf, Curl_strerror(conn,error));
data->state.os_errno = error;
break;
}
}
/* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
connect(). We can be sure of this since connect() cannot return 1. */
if((WAITCONN_TIMEOUT == rc) &&
(data->state.used_interface == Curl_if_multi)) {
/* Timeout when running the multi interface */
return sockfd;
}
isconnected = verifyconnect(sockfd, &error);
if(!rc && isconnected) {
/* we are connected, awesome! */
*connected = TRUE; /* this is a true connect */
infof(data, "connected\n");
return sockfd;
}
else if(WAITCONN_TIMEOUT == rc)
infof(data, "Timeout\n");
else {
data->state.os_errno = error;
infof(data, "%s\n", Curl_strerror(conn, error));
}
/* connect failed or timed out */
sclose(sockfd);
return CURL_SOCKET_BAD;
}
|
|||||
| ↓ | curl_easy_duphandle | 21 | 57 | 112 | lib/easy.c |
CURL *curl_easy_duphandle(CURL *incurl)
{
bool fail = TRUE;
struct SessionHandle *data=(struct SessionHandle *)incurl;
struct SessionHandle *outcurl = calloc(sizeof(struct SessionHandle), 1);
if(NULL == outcurl)
return NULL; /* failure */
do {
/*
* We setup a few buffers we need. We should probably make them
* get setup on-demand in the code, as that would probably decrease
* the likeliness of us forgetting to init a buffer here in the future.
*/
outcurl->state.headerbuff = malloc(HEADERSIZE);
if(!outcurl->state.headerbuff) {
break;
}
outcurl->state.headersize=HEADERSIZE;
/* copy all userdefined values */
if(Curl_dupset(outcurl, data) != CURLE_OK)
break;
if(data->state.used_interface == Curl_if_multi)
outcurl->state.connc = data->state.connc;
else
outcurl->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1);
if(!outcurl->state.connc)
break;
outcurl->state.lastconnect = -1;
outcurl->progress.flags = data->progress.flags;
outcurl->progress.callback = data->progress.callback;
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->cookies) {
/* If cookies are enabled in the parent handle, we enable them
in the clone as well! */
outcurl->cookies = Curl_cookie_init(data,
data->cookies->filename,
outcurl->cookies,
data->set.cookiesession);
if(!outcurl->cookies) {
break;
}
}
#endif /* CURL_DISABLE_HTTP */
/* duplicate all values in 'change' */
if(data->change.url) {
outcurl->change.url = strdup(data->change.url);
if(!outcurl->change.url)
break;
outcurl->change.url_alloc = TRUE;
}
if(data->change.referer) {
outcurl->change.referer = strdup(data->change.referer);
if(!outcurl->change.referer)
break;
outcurl->change.referer_alloc = TRUE;
}
#ifdef USE_ARES
/* If we use ares, we setup a new ares channel for the new handle */
if(ARES_SUCCESS != ares_init(&outcurl->state.areschannel))
break;
#endif
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
CURL_ICONV_CODESET_OF_NETWORK);
outcurl->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
CURL_ICONV_CODESET_OF_HOST);
outcurl->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
CURL_ICONV_CODESET_FOR_UTF8);
#endif
Curl_easy_initHandleData(outcurl);
outcurl->magic = CURLEASY_MAGIC_NUMBER;
fail = FALSE; /* we reach this point and thus we are OK */
} while(0);
if(fail) {
if(outcurl) {
if(outcurl->state.connc &&
(outcurl->state.connc->type == CONNCACHE_PRIVATE))
Curl_rm_connc(outcurl->state.connc);
if(outcurl->state.headerbuff)
free(outcurl->state.headerbuff);
if(outcurl->change.url)
free(outcurl->change.url);
if(outcurl->change.referer)
free(outcurl->change.referer);
Curl_freeset(outcurl);
free(outcurl); /* free the memory again */
outcurl = NULL;
}
}
return outcurl;
}
|
|||||
| ↓ | detect_proxy | 21 | 50 | 118 | lib/url.c |
static char *detect_proxy(struct connectdata *conn)
{
char *proxy = NULL;
#ifndef CURL_DISABLE_HTTP
/* If proxy was not specified, we check for default proxy environment
* variables, to enable i.e Lynx compliance:
*
* http_proxy=http://some.server.dom:port/
* https_proxy=http://some.server.dom:port/
* ftp_proxy=http://some.server.dom:port/
* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
* all_proxy=http://some.server.dom:port/
* (seems to exist for the CERN www lib. Probably
* the first to check for.)
*
* For compatibility, the all-uppercase versions of these variables are
* checked if the lowercase versions don't exist.
*/
char *no_proxy=NULL;
char *no_proxy_tok_buf;
char proxy_env[128];
no_proxy=curl_getenv("no_proxy");
if(!no_proxy)
no_proxy=curl_getenv("NO_PROXY");
if(!no_proxy || !strequal("*", no_proxy)) {
/* NO_PROXY wasn't specified or it wasn't just an asterisk */
char *nope;
nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL;
while(nope) {
size_t namelen;
char *endptr = strchr(conn->host.name, ':');
if(endptr)
namelen=endptr-conn->host.name;
else
namelen=strlen(conn->host.name);
if(strlen(nope) <= namelen) {
char *checkn=
conn->host.name + namelen - strlen(nope);
if(checkprefix(nope, checkn)) {
/* no proxy for this host! */
break;
}
}
nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
}
if(!nope) {
/* It was not listed as without proxy */
char *protop = conn->protostr;
char *envp = proxy_env;
char *prox;
/* Now, build
|
|||||
| ↓ | ossl_connect_common | 21 | 50 | 100 | lib/ssluse.c |
static CURLcode
ossl_connect_common(struct connectdata *conn,
int sockindex,
bool nonblocking,
bool *done)
{
CURLcode retcode;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
long timeout_ms;
if(ssl_connect_1==connssl->connecting_state) {
/* Find out how much more time we're allowed */
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
retcode = ossl_connect_step1(conn, sockindex);
if(retcode)
return retcode;
}
timeout_ms = 0;
while(ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
ssl_connect_2_writing == connssl->connecting_state) {
/* check allowed time left */
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
/* if ssl is expecting something, check if it's available. */
if(connssl->connecting_state == ssl_connect_2_reading
|| connssl->connecting_state == ssl_connect_2_writing) {
int writefd = ssl_connect_2_writing==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
int readfd = ssl_connect_2_reading==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
while(1) {
int what = Curl_socket_ready(readfd, writefd,
nonblocking?0:(int)timeout_ms);
if(what > 0)
/* readable or writable, go loop in the outer loop */
break;
else if(0 == what) {
if(nonblocking) {
*done = FALSE;
return CURLE_OK;
}
else {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
}
else {
/* anything that gets here is fatally bad */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
return CURLE_SSL_CONNECT_ERROR;
}
} /* while()-loop for the select() */
}
/* get the timeout from step2 to avoid computing it twice. */
retcode = ossl_connect_step2(conn, sockindex);
if(retcode)
return retcode;
} /* repeat step2 until all transactions are done. */
if(ssl_connect_3==connssl->connecting_state) {
retcode = ossl_connect_step3(conn, sockindex);
if(retcode)
return retcode;
}
if(ssl_connect_done==connssl->connecting_state) {
connssl->state = ssl_connection_complete;
*done = TRUE;
}
else
*done = FALSE;
/* Reset our connect state machine */
connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
|
|||||
| ↓ | check_gzip_header | 21 | 44 | 82 | lib/content_encoding.c |
static enum {
GZIP_OK,
GZIP_BAD,
GZIP_UNDERFLOW
} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
{
int method, flags;
const ssize_t totallen = len;
/* The shortest header is 10 bytes */
if(len < 10)
return GZIP_UNDERFLOW;
if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
return GZIP_BAD;
method = data[2];
flags = data[3];
if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
/* Can't handle this compression method or unknown flag */
return GZIP_BAD;
}
/* Skip over time, xflags, OS code and all previous bytes */
len -= 10;
data += 10;
if(flags & EXTRA_FIELD) {
ssize_t extra_len;
if(len < 2)
return GZIP_UNDERFLOW;
extra_len = (data[1] << 8) | data[0];
if(len < (extra_len+2))
return GZIP_UNDERFLOW;
len -= (extra_len + 2);
data += (extra_len + 2);
}
if(flags & ORIG_NAME) {
/* Skip over NUL-terminated file name */
while(len && *data) {
--len;
++data;
}
if(!len || *data)
return GZIP_UNDERFLOW;
/* Skip over the NUL */
--len;
++data;
}
if(flags & COMMENT) {
/* Skip over NUL-terminated comment */
while(len && *data) {
--len;
++data;
}
if(!len || *data)
return GZIP_UNDERFLOW;
/* Skip over the NUL */
--len;
++data;
}
if(flags & HEAD_CRC) {
if(len < 2)
return GZIP_UNDERFLOW;
len -= 2;
data += 2;
}
*headerlen = totallen - len;
return GZIP_OK;
}
|
|||||
| ↓ | tftp_do | 33 | 86 | 174 | lib/tftp.c |
static CURLcode tftp_do(struct connectdata *conn, bool *done)
{
struct SessionHandle *data = conn->data;
tftp_state_data_t *state;
tftp_event_t event;
CURLcode code;
int rc;
struct Curl_sockaddr_storage fromaddr;
socklen_t fromlen;
int check_time = 0;
struct SingleRequest *k = &data->req;
*done = TRUE;
/*
Since connections can be re-used between SessionHandles, this might be a
connection already existing but on a fresh SessionHandle struct so we must
make sure we have a good 'struct TFTP' to play with. For new connections,
the struct TFTP is allocated and setup in the tftp_connect() function.
*/
Curl_reset_reqproto(conn);
if(!data->state.proto.tftp) {
code = tftp_connect(conn, done);
if(code)
return code;
}
state = (tftp_state_data_t *)data->state.proto.tftp;
/* Run the TFTP State Machine */
for(code=tftp_state_machine(state, TFTP_EVENT_INIT);
(state->state != TFTP_STATE_FIN) && (code == CURLE_OK);
code=tftp_state_machine(state, event) ) {
/* Wait until ready to read or timeout occurs */
rc=Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD,
state->retry_time * 1000);
if(rc == -1) {
/* bail out */
int error = SOCKERRNO;
failf(data, "%s", Curl_strerror(conn, error));
event = TFTP_EVENT_ERROR;
}
else if(rc==0) {
/* A timeout occured */
event = TFTP_EVENT_TIMEOUT;
/* Force a look at transfer timeouts */
check_time = 0;
}
else {
/* Receive the packet */
fromlen = sizeof(fromaddr);
state->rbytes = (ssize_t)recvfrom(state->sockfd,
(void *)&state->rpacket,
sizeof(state->rpacket),
0,
(struct sockaddr *)&fromaddr,
&fromlen);
if(state->remote_addrlen==0) {
memcpy(&state->remote_addr, &fromaddr, fromlen);
state->remote_addrlen = fromlen;
}
/* Sanity check packet length */
if(state->rbytes < 4) {
failf(data, "Received too short packet");
/* Not a timeout, but how best to handle it? */
event = TFTP_EVENT_TIMEOUT;
}
else {
/* The event is given by the TFTP packet time */
event = (tftp_event_t)getrpacketevent(&state->rpacket);
switch(event) {
case TFTP_EVENT_DATA:
/* Don't pass to the client empty or retransmitted packets */
if(state->rbytes > 4 &&
((state->block+1) == getrpacketblock(&state->rpacket))) {
code = Curl_client_write(conn, CLIENTWRITE_BODY,
(char *)&state->rpacket.data[4],
state->rbytes-4);
if(code)
return code;
k->bytecount += state->rbytes-4;
Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
}
break;
case TFTP_EVENT_ERROR:
state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
infof(data, "%s\n", (const char *)&state->rpacket.data[4]);
break;
case TFTP_EVENT_ACK:
break;
case TFTP_EVENT_RRQ:
case TFTP_EVENT_WRQ:
default:
failf(data, "%s", "Internal error: Unexpected packet");
break;
}
/* Update the progress meter */
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
}
}
/* Check for transfer timeout every 10 blocks, or after timeout */
if(check_time%10==0) {
time_t current;
time(¤t);
if(current>state->max_time) {
DEBUGF(infof(data, "timeout: %d > %d\n",
current, state->max_time));
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
}
}
}
if(code)
return code;
/* Tell curl we're done */
code = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
if(code)
return code;
/* If we have encountered an error */
if(state->error != TFTP_ERR_NONE) {
/* Translate internal error codes to curl error codes */
switch(state->error) {
case TFTP_ERR_NOTFOUND:
code = CURLE_TFTP_NOTFOUND;
break;
case TFTP_ERR_PERM:
code = CURLE_TFTP_PERM;
break;
case TFTP_ERR_DISKFULL:
code = CURLE_REMOTE_DISK_FULL;
break;
case TFTP_ERR_UNDEF:
case TFTP_ERR_ILLEGAL:
code = CURLE_TFTP_ILLEGAL;
break;
case TFTP_ERR_UNKNOWNID:
code = CURLE_TFTP_UNKNOWNID;
break;
case TFTP_ERR_EXISTS:
code = CURLE_REMOTE_FILE_EXISTS;
break;
case TFTP_ERR_NOSUCHUSER:
code = CURLE_TFTP_NOSUCHUSER;
break;
case TFTP_ERR_TIMEOUT:
code = CURLE_OPERATION_TIMEDOUT;
break;
case TFTP_ERR_NORESPONSE:
code = CURLE_COULDNT_CONNECT;
break;
default:
code= CURLE_ABORTED_BY_CALLBACK;
break;
}
}
else
code = CURLE_OK;
return code;
}
|
|||||
| ↓ | servercert | 20 | 80 | 129 | lib/ssluse.c |
static CURLcode servercert(struct connectdata *conn,
struct ssl_connect_data *connssl,
bool strict)
{
CURLcode retcode = CURLE_OK;
int rc;
long lerr;
ASN1_TIME *certdate;
struct SessionHandle *data = conn->data;
X509 *issuer;
FILE *fp;
char buffer[256];
if(data->set.ssl.certinfo)
/* we've been asked to gather certificate info! */
(void)get_cert_chain(conn, connssl);
data->set.ssl.certverifyresult = !X509_V_OK;
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
if(strict)
failf(data, "SSL: couldn't get peer certificate!");
return CURLE_PEER_FAILED_VERIFICATION;
}
infof (data, "Server certificate:\n");
rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
buffer, sizeof(buffer));
if(rc) {
if(strict)
failf(data, "SSL: couldn't get X509-subject!");
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_CONNECT_ERROR;
}
infof(data, "\t subject: %s\n", buffer);
certdate = X509_get_notBefore(connssl->server_cert);
asn1_output(certdate, buffer, sizeof(buffer));
infof(data, "\t start date: %s\n", buffer);
certdate = X509_get_notAfter(connssl->server_cert);
asn1_output(certdate, buffer, sizeof(buffer));
infof(data, "\t expire date: %s\n", buffer);
if(data->set.ssl.verifyhost) {
retcode = verifyhost(conn, connssl->server_cert);
if(retcode) {
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return retcode;
}
}
rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
buffer, sizeof(buffer));
if(rc) {
if(strict)
failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR;
}
else {
infof(data, "\t issuer: %s\n", buffer);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
/* e.g. match issuer name with provided issuer certificate */
if (data->set.str[STRING_SSL_ISSUERCERT]) {
if (! (fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"))) {
if (strict)
failf(data, "SSL: Unable to open issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
if (!issuer) {
if (strict)
failf(data, "SSL: Unable to read issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
}
fclose(fp);
if (X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
if (strict)
failf(data, "SSL: Certificate issuer check failed (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
infof(data, "\t SSL certificate issuer check ok (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(issuer);
}
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
and we return earlier if verifypeer is set? */
if(strict)
failf(data, "SSL certificate verify result: %s (%ld)",
X509_verify_cert_error_string(lerr), lerr);
retcode = CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t SSL certificate verify result: %s (%ld),"
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
}
else
infof(data, "\t SSL certificate verify ok.\n");
}
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
connssl->connecting_state = ssl_connect_done;
return retcode;
}
|
|||||
| ↓ | parse_proxy | 20 | 58 | 121 | lib/url.c |
static CURLcode parse_proxy(struct SessionHandle *data,
struct connectdata *conn, char *proxy)
{
char *prox_portno;
char *endofprot;
/* We use 'proxyptr' to point to the proxy name from now on... */
char *proxyptr=proxy;
char *portptr;
char *atsign;
/* We do the proxy host string parsing here. We want the host name and the
* port name. Accept a protocol:// prefix, even though it should just be
* ignored.
*/
/* Skip the protocol part if present */
endofprot=strstr(proxyptr, "://");
if(endofprot)
proxyptr = endofprot+3;
/* Is there a username and password given in this proxy url? */
atsign = strchr(proxyptr, '@');
if(atsign) {
char proxyuser[MAX_CURL_USER_LENGTH];
char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
proxypasswd[0] = 0;
if(1 <= sscanf(proxyptr,
"%" MAX_CURL_USER_LENGTH_TXT"[^:@]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
proxyuser, proxypasswd)) {
CURLcode res = CURLE_OK;
/* found user and password, rip them out. note that we are
unescaping them, as there is otherwise no way to have a
username or password with reserved characters like ':' in
them. */
Curl_safefree(conn->proxyuser);
conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
if(!conn->proxyuser)
res = CURLE_OUT_OF_MEMORY;
else {
Curl_safefree(conn->proxypasswd);
conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
if(!conn->proxypasswd)
res = CURLE_OUT_OF_MEMORY;
}
if(CURLE_OK == res) {
conn->bits.proxy_user_passwd = TRUE; /* enable it */
atsign = strdup(atsign+1); /* the right side of the @-letter */
if(atsign) {
free(proxy); /* free the former proxy string */
proxy = proxyptr = atsign; /* now use this instead */
}
else
res = CURLE_OUT_OF_MEMORY;
}
if(res) {
free(proxy); /* free the allocated proxy string */
return res;
}
}
}
/* start scanning for port number at this point */
portptr = proxyptr;
/* detect and extract RFC2732-style IPv6-addresses */
if(*proxyptr == '[') {
char *ptr = ++proxyptr; /* advance beyond the initial bracket */
while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') || (*ptr == '.')))
ptr++;
if(*ptr == ']') {
/* yeps, it ended nicely with a bracket as well */
*ptr++ = 0;
} else
infof(data, "Invalid IPv6 address format\n");
portptr = ptr;
/* Note that if this didn't end with a bracket, we still advanced the
* proxyptr first, but I can't see anything wrong with that as no host
* name nor a numeric can legally start with a bracket.
*/
}
/* Get port number off proxy.server.com:1080 */
prox_portno = strchr(portptr, ':');
if(prox_portno) {
*prox_portno = 0x0; /* cut off number from host name */
prox_portno ++;
/* now set the local port number */
conn->port = atoi(prox_portno);
}
else {
/* without a port number after the host name, some people seem to use
a slash so we strip everything from the first slash */
atsign = strchr(proxyptr, '/');
if(atsign)
*atsign = 0x0; /* cut off path part from host name */
if(data->set.proxyport)
/* None given in the proxy string, then get the default one if it is
given */
conn->port = data->set.proxyport;
}
/* now, clone the cleaned proxy host name */
conn->proxy.rawalloc = strdup(proxyptr);
conn->proxy.name = conn->proxy.rawalloc;
free(proxy);
if(!conn->proxy.rawalloc)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
|
|||||
| ↓ | curlx_strtoll | 20 | 48 | 89 | lib/strtoofft.c |
curl_off_t
curlx_strtoll(const char *nptr, char **endptr, int base)
{
char *end;
int is_negative = 0;
int overflow;
int i;
curl_off_t value = 0;
curl_off_t newval;
/* Skip leading whitespace. */
end = (char *)nptr;
while(ISSPACE(end[0])) {
end++;
}
/* Handle the sign, if any. */
if(end[0] == '-') {
is_negative = 1;
end++;
}
else if(end[0] == '+') {
end++;
}
else if(end[0] == '\0') {
/* We had nothing but perhaps some whitespace -- there was no number. */
if(endptr) {
*endptr = end;
}
return 0;
}
/* Handle special beginnings, if present and allowed. */
if(end[0] == '0' && end[1] == 'x') {
if(base == 16 || base == 0) {
end += 2;
base = 16;
}
}
else if(end[0] == '0') {
if(base == 8 || base == 0) {
end++;
base = 8;
}
}
/* Matching strtol, if the base is 0 and it doesn't look like
* the number is octal or hex, we assume it's base 10.
*/
if(base == 0) {
base = 10;
}
/* Loop handling digits. */
value = 0;
overflow = 0;
for (i = get_char(end[0], base);
i != -1;
end++, i = get_char(end[0], base)) {
newval = base * value + i;
if(newval < value) {
/* We've overflowed. */
overflow = 1;
break;
}
else
value = newval;
}
if(!overflow) {
if(is_negative) {
/* Fix the sign. */
value *= -1;
}
}
else {
if(is_negative)
value = CURL_LLONG_MIN;
else
value = CURL_LLONG_MAX;
SET_ERRNO(ERANGE);
}
if(endptr)
*endptr = end;
return value;
}
|
|||||
| ↓ | curl_multi_remove_handle | 20 | 45 | 146 | lib/multi.c |
CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
CURL *curl_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
/* Verify that we got a somewhat good easy handle too */
if(!GOOD_EASY_HANDLE(curl_handle))
return CURLM_BAD_EASY_HANDLE;
/* pick-up from the 'curl_handle' the kept position in the list */
easy = ((struct SessionHandle *)curl_handle)->multi_pos;
if(easy) {
bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED);
/* If the 'state' is not INIT or COMPLETED, we might need to do something
nice to put the easy_handle in a good known state when this returns. */
if(premature)
/* this handle is "alive" so we need to count down the total number of
alive connections when this is removed */
multi->num_alive--;
if(easy->easy_conn &&
(easy->easy_conn->send_pipe->size +
easy->easy_conn->recv_pipe->size > 1) &&
easy->state > CURLM_STATE_WAITDO &&
easy->state < CURLM_STATE_COMPLETED) {
/* If the handle is in a pipeline and has started sending off its
request but not received its reponse yet, we need to close
connection. */
easy->easy_conn->bits.close = TRUE;
/* Set connection owner so that Curl_done() closes it.
We can sefely do this here since connection is killed. */
easy->easy_conn->data = easy->easy_handle;
}
/* The timer must be shut down before easy->multi is set to NULL,
else the timenode will remain in the splay tree after
curl_easy_cleanup is called. */
Curl_expire(easy->easy_handle, 0);
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
/* we must call Curl_done() here (if we still "own it") so that we don't
leave a half-baked one around */
if(easy->easy_conn &&
(easy->easy_conn->data == easy->easy_handle)) {
/* Curl_done() clears the conn->data field to lose the association
between the easy handle and the connection
Note that this ignores the return code simply because there's nothing
really useful to do with it anyway! */
(void)Curl_done(&easy->easy_conn, easy->result, premature);
if(easy->easy_conn)
/* the connection is still alive, set back the association to enable
the check below to trigger TRUE */
easy->easy_conn->data = easy->easy_handle;
}
/* If this easy_handle was the last one in charge for one or more
connections a the shared connection cache, we might need to keep this
handle around until either A) the connection is closed and killed
properly, or B) another easy_handle uses the connection.
The reason why we need to have a easy_handle associated with a live
connection is simply that some connections will need a handle to get
closed down properly. Currently, the only connections that need to keep
a easy_handle handle around are using FTP(S). Such connections have
the PROT_CLOSEACTION bit set.
Thus, we need to check for all connections in the shared cache that
points to this handle and are using PROT_CLOSEACTION. If there's any,
we need to add this handle to the list of "easy handles kept around for
nice connection closures".
*/
if(multi_conn_using(multi, easy->easy_handle)) {
/* There's at least one connection using this handle so we must keep
this handle around. We also keep the connection cache pointer
pointing to the shared one since that will be used on close as
well. */
easy->easy_handle->state.shared_conn = multi;
/* this handle is still being used by a shared connection cache and
thus we leave it around for now */
add_closure(multi, easy->easy_handle);
}
if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) {
/* if this was using the shared connection cache we clear the pointer
to that since we're not part of that handle anymore */
easy->easy_handle->state.connc = NULL;
/* and modify the connectindex since this handle can't point to the
connection cache anymore */
if(easy->easy_conn &&
(easy->easy_conn->send_pipe->size +
easy->easy_conn->recv_pipe->size == 0))
easy->easy_conn->connectindex = -1;
}
/* change state without using multistate(), only to make singlesocket() do
what we want */
easy->state = CURLM_STATE_COMPLETED;
singlesocket(multi, easy); /* to let the application know what sockets
that vanish with this handle */
Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
to this multi handle */
/* make the previous node point to our next */
if(easy->prev)
easy->prev->next = easy->next;
/* make our next point to our previous node */
if(easy->next)
easy->next->prev = easy->prev;
easy->easy_handle->set.one_easy = NULL; /* detached */
/* Null the position in the controlling structure */
easy->easy_handle->multi_pos = NULL;
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
if(easy->msg)
free(easy->msg);
free(easy);
multi->num_easy--; /* one less to care about now */
update_timer(multi);
return CURLM_OK;
}
else
return CURLM_BAD_EASY_HANDLE; /* twasn't found */
}
|
|||||
| ↓ | Curl_done | 20 | 42 | 107 | lib/url.c |
CURLcode Curl_done(struct connectdata **connp,
CURLcode status, /* an error if this is called after an
error was detected */
bool premature)
{
CURLcode result;
struct connectdata *conn;
struct SessionHandle *data;
DEBUGASSERT(*connp);
conn = *connp;
data = conn->data;
Curl_expire(data, 0); /* stop timer */
if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
conn->readchannel_inuse)
conn->readchannel_inuse = FALSE;
if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
conn->writechannel_inuse)
conn->writechannel_inuse = FALSE;
Curl_removeHandleFromPipeline(data, conn->pend_pipe);
if(conn->bits.done ||
(conn->send_pipe->size + conn->recv_pipe->size != 0 &&
!data->set.reuse_forbid &&
!conn->bits.close))
/* Stop if Curl_done() has already been called or pipeline
is not empty and we do not have to close connection. */
return CURLE_OK;
conn->bits.done = TRUE; /* called just now! */
/* Cleanup possible redirect junk */
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
if(data->req.location) {
free(data->req.location);
data->req.location = NULL;
}
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
conn->dns_entry = NULL;
}
/* this calls the protocol-specific function pointer previously set */
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
else
result = CURLE_OK;
Curl_pgrsDone(conn); /* done with the operation */
/* if the transfer was completed in a paused state there can be buffered
data left to write and then kill */
if(data->state.tempwrite) {
free(data->state.tempwrite);
data->state.tempwrite = NULL;
}
/* for ares-using, make sure all possible outstanding requests are properly
cancelled before we proceed */
ares_cancel(data->state.areschannel);
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this no matter what we think.
if conn->bits.close is TRUE, it means that the connection should be
closed in spite of all our efforts to be nice, due to protocol
restrictions in our or the server's end
if premature is TRUE, it means this connection was said to be DONE before
the entire request operation is complete and thus we can't know in what
state it is for re-using, so we're forced to close it. In a perfect world
we can add code that keep track of if we really must close it here or not,
but currently we have no such detail knowledge.
*/
if(data->set.reuse_forbid || conn->bits.close || premature) {
CURLcode res2 = Curl_disconnect(conn); /* close the connection */
/* If we had an error already, make sure we return that one. But
if we got a new error, return that. */
if(!result && res2)
result = res2;
}
else {
ConnectionDone(conn); /* the connection is no longer in use */
/* remember the most recently used connection */
data->state.lastconnect = conn->connectindex;
infof(data, "Connection #%ld to host %s left intact\n",
conn->connectindex,
conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
}
*connp = NULL; /* to make the caller of this function better detect that
this was either closed or handed over to the connection
cache here, and therefore cannot be used from this point on
*/
return result;
}
|
|||||
| ↓ | Transfer | 20 | 36 | 85 | lib/transfer.c |
static CURLcode
Transfer(struct connectdata *conn)
{
CURLcode result;
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
bool done=FALSE;
if((conn->sockfd == CURL_SOCKET_BAD) &&
(conn->writesockfd == CURL_SOCKET_BAD))
/* nothing to read, nothing to write, we're already OK! */
return CURLE_OK;
/* we want header and/or body, if neither then don't do this! */
if(!k->getheader && data->set.opt_no_body)
return CURLE_OK;
while(!done) {
curl_socket_t fd_read;
curl_socket_t fd_write;
/* limit-rate logic: if speed exceeds threshold, then do not include fd in
select set. The current speed is recalculated in each Curl_readwrite()
call */
if((k->keepon & KEEP_WRITE) &&
(!data->set.max_send_speed ||
(data->progress.ulspeed < data->set.max_send_speed) )) {
fd_write = conn->writesockfd;
k->keepon &= ~KEEP_WRITE_HOLD;
}
else {
fd_write = CURL_SOCKET_BAD;
if(k->keepon & KEEP_WRITE)
k->keepon |= KEEP_WRITE_HOLD; /* hold it */
}
if((k->keepon & KEEP_READ) &&
(!data->set.max_recv_speed ||
(data->progress.dlspeed < data->set.max_recv_speed)) ) {
fd_read = conn->sockfd;
k->keepon &= ~KEEP_READ_HOLD;
}
else {
fd_read = CURL_SOCKET_BAD;
if(k->keepon & KEEP_READ)
k->keepon |= KEEP_READ_HOLD; /* hold it */
}
/* pause logic. Don't check descriptors for paused connections */
if(k->keepon & KEEP_READ_PAUSE)
fd_read = CURL_SOCKET_BAD;
if(k->keepon & KEEP_WRITE_PAUSE)
fd_write = CURL_SOCKET_BAD;
/* The *_HOLD and *_PAUSE logic is necessary since even though there might
be no traffic during the select interval, we still call
Curl_readwrite() for the timeout case and if we limit transfer speed we
must make sure that this function doesn't transfer anything while in
HOLD status. */
switch (Curl_socket_ready(fd_read, fd_write, 1000)) {
case -1: /* select() error, stop reading */
#ifdef EINTR
/* The EINTR is not serious, and it seems you might get this more
often when using the lib in a multi-threaded environment! */
if(SOCKERRNO == EINTR)
;
else
#endif
done = TRUE; /* no more read or write */
continue;
case 0: /* timeout */
default: /* readable descriptors */
result = Curl_readwrite(conn, &done);
break;
}
if(result)
return result;
/* "done" signals to us if the transfer(s) are ready */
}
return CURLE_OK;
}
|
|||||
| ↓ | krb4_auth | 19 | 69 | 105 | lib/krb4.c |
static int
krb4_auth(void *app_data, struct connectdata *conn)
{
int ret;
char *p;
unsigned char *ptr;
size_t len;
KTEXT_ST adat;
MSG_DAT msg_data;
int checksum;
u_int32_t cs;
struct krb4_data *d = app_data;
char *host = conn->host.name;
ssize_t nread;
int l = sizeof(conn->local_addr);
struct SessionHandle *data = conn->data;
CURLcode result;
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)LOCAL_ADDR, &l) < 0)
perror("getsockname()");
checksum = getpid();
ret = mk_auth(d, &adat, "ftp", host, checksum);
if(ret == KDC_PR_UNKNOWN)
ret = mk_auth(d, &adat, "rcmd", host, checksum);
if(ret) {
infof(data, "%s\n", krb_get_err_text(ret));
return AUTH_CONTINUE;
}
#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
if(krb_get_config_bool("nat_in_use")) {
struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR;
struct in_addr natAddr;
if(krb_get_our_ip_for_realm(krb_realmofhost(host),
&natAddr) != KSUCCESS
&& krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
infof(data, "Can't get address for realm %s\n",
krb_realmofhost(host));
else {
if(natAddr.s_addr != localaddr->sin_addr.s_addr) {
char addr_buf[128];
if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf)))
infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf);
localaddr->sin_addr = natAddr;
}
}
}
#endif
if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) {
Curl_failf(data, "Out of memory base64-encoding");
return AUTH_CONTINUE;
}
result = Curl_ftpsendf(conn, "ADAT %s", p);
free(p);
if(result)
return -2;
if(Curl_GetFTPResponse(&nread, conn, NULL))
return -1;
if(data->state.buffer[0] != '2'){
Curl_failf(data, "Server didn't accept auth data");
return AUTH_ERROR;
}
p = strstr(data->state.buffer, "ADAT=");
if(!p) {
Curl_failf(data, "Remote host didn't send adat reply");
return AUTH_ERROR;
}
p += 5;
len = Curl_base64_decode(p, &ptr);
if(len > sizeof(adat.dat)-1) {
free(ptr);
len=0;
}
if(!len || !ptr) {
Curl_failf(data, "Failed to decode base64 from server");
return AUTH_ERROR;
}
memcpy((char *)adat.dat, ptr, len);
free(ptr);
adat.length = len;
ret = krb_rd_safe(adat.dat, adat.length, &d->key,
(struct sockaddr_in *)hisctladdr,
(struct sockaddr_in *)myctladdr, &msg_data);
if(ret) {
Curl_failf(data, "Error reading reply from server: %s",
krb_get_err_text(ret));
return AUTH_ERROR;
}
krb_get_int(msg_data.app_data, &cs, 4, 0);
if(cs - checksum != 1) {
Curl_failf(data, "Bad checksum returned from server");
return AUTH_ERROR;
}
return AUTH_OK;
}
|
|||||
| ↓ | inet_pton6 | 19 | 62 | 83 | lib/inet_pton.c |
static int
inet_pton6(const char *src, unsigned char *dst)
{
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit;
unsigned int val;
memset((tp = tmp), 0, IN6ADDRSZ);
endp = tp + IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if(*src == ':')
if(*++src != ':')
return (0);
curtok = src;
saw_xdigit = 0;
val = 0;
while((ch = *src++) != '\0') {
const char *pch;
if((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if(pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if(val > 0xffff)
return (0);
saw_xdigit = 1;
continue;
}
if(ch == ':') {
curtok = src;
if(!saw_xdigit) {
if(colonp)
return (0);
colonp = tp;
continue;
}
if(tp + INT16SZ > endp)
return (0);
*tp++ = (unsigned char) (val >> 8) & 0xff;
*tp++ = (unsigned char) val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if(ch == '.' && ((tp + INADDRSZ) <= endp) &&
inet_pton4(curtok, tp) > 0) {
tp += INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if(saw_xdigit) {
if(tp + INT16SZ > endp)
return (0);
*tp++ = (unsigned char) (val >> 8) & 0xff;
*tp++ = (unsigned char) val & 0xff;
}
if(colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if(tp != endp)
return (0);
/* bcopy(tmp, dst, IN6ADDRSZ); */
memcpy(dst, tmp, IN6ADDRSZ);
return (1);
}
|
|||||
| ↓ | curl_multi_add_handle | 19 | 60 | 140 | lib/multi.c |
CURLMcode curl_multi_add_handle(CURLM *multi_handle,
CURL *easy_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_one_easy *easy;
struct closure *cl;
struct closure *prev=NULL;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
/* Verify that we got a somewhat good easy handle too */
if(!GOOD_EASY_HANDLE(easy_handle))
return CURLM_BAD_EASY_HANDLE;
/* Prevent users to add the same handle more than once! */
if(((struct SessionHandle *)easy_handle)->multi)
/* possibly we should create a new unique error code for this condition */
return CURLM_BAD_EASY_HANDLE;
/* Now, time to add an easy handle to the multi stack */
easy = calloc(sizeof(struct Curl_one_easy), 1);
if(!easy)
return CURLM_OUT_OF_MEMORY;
cl = multi->closure;
while(cl) {
struct closure *next = cl->next;
if(cl->easy_handle == (struct SessionHandle *)easy_handle) {
/* remove this handle from the closure list */
free(cl);
if(prev)
prev->next = next;
else
multi->closure = next;
break; /* no need to continue since this handle can only be present once
in the list */
}
prev = cl;
cl = next;
}
/* set the easy handle */
easy->easy_handle = easy_handle;
multistate(easy, CURLM_STATE_INIT);
/* set the back pointer to one_easy to assist in removal */
easy->easy_handle->multi_pos = easy;
/* for multi interface connections, we share DNS cache automatically if the
easy handle's one is currently private. */
if(easy->easy_handle->dns.hostcache &&
(easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) {
Curl_hash_destroy(easy->easy_handle->dns.hostcache);
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
if(!easy->easy_handle->dns.hostcache ||
(easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) {
easy->easy_handle->dns.hostcache = multi->hostcache;
easy->easy_handle->dns.hostcachetype = HCACHE_MULTI;
}
if(easy->easy_handle->state.connc) {
if(easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE) {
/* kill old private version */
Curl_rm_connc(easy->easy_handle->state.connc);
/* point out our shared one instead */
easy->easy_handle->state.connc = multi->connc;
}
/* else it is already using multi? */
}
else
/* point out our shared one */
easy->easy_handle->state.connc = multi->connc;
/* Make sure the type is setup correctly */
easy->easy_handle->state.connc->type = CONNCACHE_MULTI;
/* This adds the new entry at the back of the list
to try and maintain a FIFO queue so the pipelined
requests are in order. */
/* We add this new entry last in the list. We make our 'next' point to the
'first' struct and our 'prev' point to the previous 'prev' */
easy->next = &multi->easy;
easy->prev = multi->easy.prev;
/* make 'easy' the last node in the chain */
multi->easy.prev = easy;
/* if there was a prev node, make sure its 'next' pointer links to
the new node */
easy->prev->next = easy;
Curl_easy_addmulti(easy_handle, multi_handle);
/* make the SessionHandle struct refer back to this struct */
easy->easy_handle->set.one_easy = easy;
/* Set the timeout for this handle to expire really soon so that it will
be taken care of even when this handle is added in the midst of operation
when only the curl_multi_socket() API is used. During that flow, only
sockets that time-out or have actions will be dealt with. Since this
handle has no action yet, we make sure it times out to get things to
happen. */
Curl_expire(easy->easy_handle, 1);
/* increase the node-counter */
multi->num_easy++;
if((multi->num_easy * 4) > multi->connc->num) {
/* We want the connection cache to have plenty room. Before we supported
the shared cache every single easy handle had 5 entries in their cache
by default. */
long newmax = multi->num_easy * 4;
if(multi->maxconnects && (multi->maxconnects < newmax))
/* don't grow beyond the allowed size */
newmax = multi->maxconnects;
if(newmax > multi->connc->num) {
/* we only do this is we can in fact grow the cache */
CURLcode res = Curl_ch_connc(easy_handle, multi->connc, newmax);
if(res != CURLE_OK) {
/* FIXME: may need to do more cleanup here */
curl_multi_remove_handle(multi_handle, easy_handle);
return CURLM_OUT_OF_MEMORY;
}
}
}
/* increase the alive-counter */
multi->num_alive++;
update_timer(multi);
return CURLM_OK;
}
|
|||||
| ↓ | GetOrSetUpData | 19 | 53 | 121 | lib/nwlib.c |
int GetOrSetUpData(int id, libdata_t **appData,
libthreaddata_t **threadData )
{
int err;
libdata_t *app_data;
libthreaddata_t *thread_data;
NXKey_t key;
NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0);
err = 0;
thread_data = (libthreaddata_t *) NULL;
/*
** Attempt to get our data for the application calling us. This is where we
** store whatever application-specific information we need to carry in support
** of calling applications.
*/
app_data = (libdata_t *) get_app_data(id);
if(!app_data) {
/*
** This application hasn't called us before; set up application AND per-thread
** data. Of course, just in case a thread from this same application is calling
** us simultaneously, we better lock our application data-creation mutex. We
** also need to recheck for data after we acquire the lock because WE might be
** that other thread that was too late to create the data and the first thread
** in will have created it.
*/
NXLock(gLibLock);
if(!(app_data = (libdata_t *) get_app_data(id))) {
app_data = malloc(sizeof(libdata_t));
if(app_data) {
memset(app_data, 0, sizeof(libdata_t));
app_data->tenbytes = malloc(10);
app_data->lock = NXMutexAlloc(0, 0, &liblock);
if(!app_data->tenbytes || !app_data->lock) {
if(app_data->lock)
NXMutexFree(app_data->lock);
free(app_data);
app_data = (libdata_t *) NULL;
err = ENOMEM;
}
if(app_data) {
/*
** Here we burn in the application data that we were trying to get by calling
** get_app_data(). Next time we call the first function, we'll get this data
** we're just now setting. We also go on here to establish the per-thread data
** for the calling thread, something we'll have to do on each application
** thread the first time it calls us.
*/
err = set_app_data(gLibId, app_data);
if(err) {
free(app_data);
app_data = (libdata_t *) NULL;
err = ENOMEM;
}
else {
/* create key for thread-specific data... */
err = NXKeyCreate(DisposeThreadData, (void *) NULL, &key);
if(err) /* (no more keys left?) */
key = -1;
app_data->perthreadkey = key;
}
}
}
}
NXUnlock(gLibLock);
}
if(app_data) {
key = app_data->perthreadkey;
if(key != -1 /* couldn't create a key? no thread data */
&& !(err = NXKeyGetValue(key, (void **) &thread_data))
&& !thread_data) {
/*
** Allocate the per-thread data for the calling thread. Regardless of whether
** there was already application data or not, this may be the first call by a
** a new thread. The fact that we allocation 20 bytes on a pointer is not very
** important, this just helps to demonstrate that we can have arbitrarily
** complex per-thread data.
*/
thread_data = malloc(sizeof(libthreaddata_t));
if(thread_data) {
thread_data->_errno = 0;
thread_data->twentybytes = malloc(20);
if(!thread_data->twentybytes) {
free(thread_data);
thread_data = (libthreaddata_t *) NULL;
err = ENOMEM;
}
if((err = NXKeySetValue(key, thread_data))) {
free(thread_data->twentybytes);
free(thread_data);
thread_data = (libthreaddata_t *) NULL;
}
}
}
}
if(appData)
*appData = app_data;
if(threadData)
*threadData = thread_data;
return err;
}
|
|||||
| ↓ | file_upload | 18 | 70 | 119 | lib/file.c |
static CURLcode file_upload(struct connectdata *conn)
{
struct FILEPROTO *file = conn->data->state.proto.file;
const char *dir = strchr(file->path, DIRSEP);
FILE *fp;
CURLcode res=CURLE_OK;
struct SessionHandle *data = conn->data;
char *buf = data->state.buffer;
size_t nread;
size_t nwrite;
curl_off_t bytecount = 0;
struct timeval now = Curl_tvnow();
struct_stat file_stat;
const char* buf2;
/*
* Since FILE: doesn't do the full init, we need to provide some extra
* assignments here.
*/
conn->fread_func = data->set.fread_func;
conn->fread_in = data->set.in;
conn->data->req.upload_fromhere = buf;
if(!dir)
return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
if(!dir[1])
return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
if(data->state.resume_from)
fp = fopen( file->path, "ab" );
else {
int fd;
#ifdef DOS_FILESYSTEM
fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
conn->data->set.new_file_perms);
#else
fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC,
conn->data->set.new_file_perms);
#endif
if(fd < 0) {
failf(data, "Can't open %s for writing", file->path);
return CURLE_WRITE_ERROR;
}
close(fd);
fp = fopen(file->path, "wb");
}
if(!fp) {
failf(data, "Can't open %s for writing", file->path);
return CURLE_WRITE_ERROR;
}
if(-1 != data->set.infilesize)
/* known size of data to "upload" */
Curl_pgrsSetUploadSize(data, data->set.infilesize);
/* treat the negative resume offset value as the case of "-" */
if(data->state.resume_from < 0) {
if(fstat(fileno(fp), &file_stat)) {
fclose(fp);
failf(data, "Can't get the size of %s", file->path);
return CURLE_WRITE_ERROR;
}
else
data->state.resume_from = (curl_off_t)file_stat.st_size;
}
while(res == CURLE_OK) {
int readcount;
res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
if(res)
break;
if(readcount <= 0) /* fix questionable compare error. curlvms */
break;
nread = (size_t)readcount;
/*skip bytes before resume point*/
if(data->state.resume_from) {
if( (curl_off_t)nread <= data->state.resume_from ) {
data->state.resume_from -= nread;
nread = 0;
buf2 = buf;
}
else {
buf2 = buf + data->state.resume_from;
nread -= (size_t)data->state.resume_from;
data->state.resume_from = 0;
}
}
else
buf2 = buf;
/* write the data to the target */
nwrite = fwrite(buf2, 1, nread, fp);
if(nwrite != nread) {
res = CURLE_SEND_ERROR;
break;
}
bytecount += nread;
Curl_pgrsSetUploadCounter(data, bytecount);
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else
res = Curl_speedcheck(data, now);
}
if(!res && Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
fclose(fp);
return res;
}
|
|||||
| ↓ | Curl_client_write | 18 | 43 | 99 | lib/sendf.c |
CURLcode Curl_client_write(struct connectdata *conn,
int type,
char *ptr,
size_t len)
{
struct SessionHandle *data = conn->data;
size_t wrote;
/* If reading is actually paused, we're forced to append this chunk of data
to the already held data, but only if it is the same type as otherwise it
can't work and it'll return error instead. */
if(data->req.keepon & KEEP_READ_PAUSE) {
size_t newlen;
char *newptr;
if(type != data->state.tempwritetype)
/* major internal confusion */
return CURLE_RECV_ERROR;
DEBUGASSERT(data->state.tempwrite);
/* figure out the new size of the data to save */
newlen = len + data->state.tempwritesize;
/* allocate the new memory area */
newptr = realloc(data->state.tempwrite, newlen);
if(!newptr)
return CURLE_OUT_OF_MEMORY;
/* copy the new data to the end of the new area */
memcpy(newptr + data->state.tempwritesize, ptr, len);
/* update the pointer and the size */
data->state.tempwrite = newptr;
data->state.tempwritesize = newlen;
return CURLE_OK;
}
if(0 == len)
len = strlen(ptr);
if(type & CLIENTWRITE_BODY) {
if((conn->protocol&PROT_FTP) && conn->proto.ftpc.transfertype == 'A') {
#ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding */
size_t rc;
rc = Curl_convert_from_network(data, ptr, len);
/* Curl_convert_from_network calls failf if unsuccessful */
if(rc != CURLE_OK)
return rc;
#endif /* CURL_DOES_CONVERSIONS */
#ifdef CURL_DO_LINEEND_CONV
/* convert end-of-line markers */
len = convert_lineends(data, ptr, len);
#endif /* CURL_DO_LINEEND_CONV */
}
/* If the previous block of data ended with CR and this block of data is
just a NL, then the length might be zero */
if(len) {
wrote = data->set.fwrite_func(ptr, 1, len, data->set.out);
}
else {
wrote = len;
}
if(CURL_WRITEFUNC_PAUSE == wrote)
return pausewrite(data, type, ptr, len);
if(wrote != len) {
failf(data, "Failed writing body (%d != %d)", (int)wrote, (int)len);
return CURLE_WRITE_ERROR;
}
}
if((type & CLIENTWRITE_HEADER) &&
(data->set.fwrite_header || data->set.writeheader) ) {
/*
* Write headers to the same callback or to the especially setup
* header callback function (added after version 7.7.1).
*/
curl_write_callback writeit=
data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite_func;
/* Note: The header is in the host encoding
regardless of the ftp transfer mode (ASCII/Image) */
wrote = writeit(ptr, 1, len, data->set.writeheader);
if(CURL_WRITEFUNC_PAUSE == wrote)
/* here we pass in the HEADER bit only since if this was body as well
then it was passed already and clearly that didn't trigger the pause,
so this is saved for later with the HEADER bit only */
return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
if(wrote != len) {
failf (data, "Failed writing header");
return CURLE_WRITE_ERROR;
}
}
return CURLE_OK;
}
|
|||||
| ↓ | parse_remote_port | 18 | 38 | 75 | lib/url.c |
static CURLcode parse_remote_port(struct SessionHandle *data,
struct connectdata *conn)
{
char *portptr;
char endbracket;
if((1 == sscanf(conn->host.name, "[%*39[0123456789abcdefABCDEF:.%]%c", &endbracket)) &&
(']' == endbracket)) {
/* this is a RFC2732-style specified IP-address */
conn->bits.ipv6_ip = TRUE;
conn->host.name++; /* skip over the starting bracket */
portptr = strchr(conn->host.name, ']');
*portptr++ = 0; /* zero terminate, killing the bracket */
if(':' != *portptr)
portptr = NULL; /* no port number available */
}
else
portptr = strrchr(conn->host.name, ':');
if(data->set.use_port && data->state.allow_port) {
/* if set, we use this and ignore the port possibly given in the URL */
conn->remote_port = (unsigned short)data->set.use_port;
if(portptr)
*portptr = '\0'; /* cut off the name there anyway - if there was a port
number - since the port number is to be ignored! */
if(conn->bits.httpproxy) {
/* we need to create new URL with the new port number */
char *url;
bool isftp = (bool)(strequal("ftp", conn->protostr) ||
strequal("ftps", conn->protostr));
/*
* This synthesized URL isn't always right--suffixes like ;type=A
* are stripped off. It would be better to work directly from the
* original URL and simply replace the port part of it.
*/
url = aprintf("%s://%s%s%s:%d%s%s", conn->protostr,
conn->bits.ipv6_ip?"[":"", conn->host.name,
conn->bits.ipv6_ip?"]":"", conn->remote_port,
isftp?"/":"", data->state.path);
if(!url)
return CURLE_OUT_OF_MEMORY;
if(data->change.url_alloc)
free(data->change.url);
data->change.url = url;
data->change.url_alloc = TRUE;
}
}
else if(portptr) {
/* no CURLOPT_PORT given, extract the one from the URL */
char *rest;
unsigned long port;
port=strtoul(portptr+1, &rest, 10); /* Port number must be decimal */
if(rest != (portptr+1) && *rest == '\0') {
/* The colon really did have only digits after it,
* so it is either a port number or a mistake */
if(port > 0xffff) { /* Single unix standard says port numbers are
* 16 bits long */
failf(data, "Port number too large: %lu", port);
return CURLE_URL_MALFORMAT;
}
*portptr = '\0'; /* cut off the name there */
conn->remote_port = (unsigned short)port;
}
}
return CURLE_OK;
}
|
|||||
| ↓ | Curl_http_perhapsrewind | 20 | 35 | 86 | lib/http.c |
CURLcode Curl_http_perhapsrewind(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct HTTP *http = data->state.proto.http;
curl_off_t bytessent;
curl_off_t expectsend = -1; /* default is unknown */
if(!http || !(conn->protocol & PROT_HTTP))
/* If this is still NULL, we have not reach very far and we can
safely skip this rewinding stuff, or this is attempted to get used
when HTTP isn't activated */
return CURLE_OK;
switch(data->set.httpreq) {
case HTTPREQ_GET:
case HTTPREQ_HEAD:
return CURLE_OK;
default:
break;
}
bytessent = http->writebytecount;
if(conn->bits.authneg)
/* This is a state where we are known to be negotiating and we don't send
any data then. */
expectsend = 0;
else {
/* figure out how much data we are expected to send */
switch(data->set.httpreq) {
case HTTPREQ_POST:
if(data->set.postfieldsize != -1)
expectsend = data->set.postfieldsize;
break;
case HTTPREQ_PUT:
if(data->set.infilesize != -1)
expectsend = data->set.infilesize;
break;
case HTTPREQ_POST_FORM:
expectsend = http->postsize;
break;
default:
break;
}
}
conn->bits.rewindaftersend = FALSE; /* default */
if((expectsend == -1) || (expectsend > bytessent)) {
/* There is still data left to send */
if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
(data->state.authhost.picked == CURLAUTH_NTLM)) {
if(((expectsend - bytessent) < 2000) ||
(conn->ntlm.state != NTLMSTATE_NONE)) {
/* The NTLM-negotiation has started *OR* there is just a little (<2K)
data left to send, keep on sending. */
/* rewind data when completely done sending! */
if(!conn->bits.authneg)
conn->bits.rewindaftersend = TRUE;
return CURLE_OK;
}
if(conn->bits.close)
/* this is already marked to get closed */
return CURLE_OK;
infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
" bytes\n", (curl_off_t)(expectsend - bytessent));
}
/* This is not NTLM or NTLM with many bytes left to send: close
*/
conn->bits.close = TRUE;
data->req.size = 0; /* don't download any more than 0 bytes */
/* There still is data left to send, but this connection is marked for
closure so we can safely do the rewind right now */
}
if(bytessent)
/* we rewind now at once since if we already sent something */
return Curl_readrewind(conn);
return CURLE_OK;
}
|
|||||
| main | 1 | 20 | 38 | lib/mprintf.c | |
| ↓ | Curl_krb_kauth | 17 | 74 | 102 | lib/krb4.c |
CURLcode Curl_krb_kauth(struct connectdata *conn)
{
des_cblock key;
des_key_schedule schedule;
KTEXT_ST tkt, tktcopy;
char *name;
char *p;
char passwd[100];
size_t tmp;
ssize_t nread;
int save;
CURLcode result;
unsigned char *ptr;
save = Curl_set_command_prot(conn, prot_private);
result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user);
if(result)
return result;
result = Curl_GetFTPResponse(&nread, conn, NULL);
if(result)
return result;
if(conn->data->state.buffer[0] != '3'){
Curl_set_command_prot(conn, save);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
p = strstr(conn->data->state.buffer, "T=");
if(!p) {
Curl_failf(conn->data, "Bad reply from server");
Curl_set_command_prot(conn, save);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
p += 2;
tmp = Curl_base64_decode(p, &ptr);
if(tmp >= sizeof(tkt.dat)) {
free(ptr);
tmp=0;
}
if(!tmp || !ptr) {
Curl_failf(conn->data, "Failed to decode base64 in reply");
Curl_set_command_prot(conn, save);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
memcpy((char *)tkt.dat, ptr, tmp);
free(ptr);
tkt.length = tmp;
tktcopy.length = tkt.length;
p = strstr(conn->data->state.buffer, "P=");
if(!p) {
Curl_failf(conn->data, "Bad reply from server");
Curl_set_command_prot(conn, save);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
name = p + 2;
for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
*p = 0;
des_string_to_key (conn->passwd, &key);
des_key_sched(&key, schedule);
des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
tkt.length,
schedule, &key, DES_DECRYPT);
if(strcmp ((char*)tktcopy.dat + 8,
KRB_TICKET_GRANTING_TICKET) != 0) {
afs_string_to_key(passwd,
krb_realmofhost(conn->host.name),
&key);
des_key_sched(&key, schedule);
des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
tkt.length,
schedule, &key, DES_DECRYPT);
}
memset(key, 0, sizeof(key));
memset(schedule, 0, sizeof(schedule));
memset(passwd, 0, sizeof(passwd));
if(Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, &p)
< 1) {
failf(conn->data, "Out of memory base64-encoding.");
Curl_set_command_prot(conn, save);
return CURLE_OUT_OF_MEMORY;
}
memset (tktcopy.dat, 0, tktcopy.length);
result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p);
free(p);
if(result)
return result;
result = Curl_GetFTPResponse(&nread, conn, NULL);
if(result)
return result;
Curl_set_command_prot(conn, save);
return CURLE_OK;
}
|
|||||
| ↓ | Curl_read | 17 | 44 | 87 | lib/sendf.c |
int Curl_read(struct connectdata *conn, /* connection data */
curl_socket_t sockfd, /* read from this socket */
char *buf, /* store read data here */
size_t sizerequested, /* max amount to read */
ssize_t *n) /* amount bytes read */
{
ssize_t nread = 0;
size_t bytesfromsocket = 0;
char *buffertofill = NULL;
bool pipelining = (bool)(conn->data->multi &&
Curl_multi_canPipeline(conn->data->multi));
/* Set 'num' to 0 or 1, depending on which socket that has been sent here.
If it is the second socket, we set num to 1. Otherwise to 0. This lets
us use the correct ssl handle. */
int num = (sockfd == conn->sock[SECONDARYSOCKET]);
*n=0; /* reset amount to zero */
/* If session can pipeline, check connection buffer */
if(pipelining) {
size_t bytestocopy = MIN(conn->buf_len - conn->read_pos, sizerequested);
/* Copy from our master buffer first if we have some unread data there*/
if(bytestocopy > 0) {
memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
conn->read_pos += bytestocopy;
conn->bits.stream_was_rewound = FALSE;
*n = (ssize_t)bytestocopy;
return CURLE_OK;
}
/* If we come here, it means that there is no data to read from the buffer,
* so we read from the socket */
bytesfromsocket = MIN(sizerequested, BUFSIZE * sizeof (char));
buffertofill = conn->master_buffer;
}
else {
bytesfromsocket = MIN((long)sizerequested, conn->data->set.buffer_size ?
conn->data->set.buffer_size : BUFSIZE);
buffertofill = buf;
}
if(conn->ssl[num].state == ssl_connection_complete) {
nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket);
if(nread == -1) {
return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
}
}
else if(Curl_ssh_enabled(conn, (PROT_SCP|PROT_SFTP))) {
if(conn->protocol & PROT_SCP)
nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket);
else if(conn->protocol & PROT_SFTP)
nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket);
#ifdef LIBSSH2CHANNEL_EAGAIN
if((nread == LIBSSH2CHANNEL_EAGAIN) || (nread == 0))
/* EWOULDBLOCK */
return -1;
#endif
if(nread < 0)
/* since it is negative and not EGAIN, it was a protocol-layer error */
return CURLE_RECV_ERROR;
}
else {
if(conn->sec_complete)
nread = Curl_sec_read(conn, sockfd, buffertofill,
bytesfromsocket);
else {
int ret = Curl_read_plain(sockfd, buffertofill, bytesfromsocket,
&nread);
if(ret)
return ret;
}
}
if(nread >= 0) {
if(pipelining) {
memcpy(buf, conn->master_buffer, nread);
conn->buf_len = nread;
conn->read_pos = nread;
}
*n += nread;
}
return CURLE_OK;
}
|
|||||
| ↓ | resolve_server | 17 | 36 | 97 | lib/url.c |
static CURLcode resolve_server(struct SessionHandle *data,
struct connectdata *conn,
struct Curl_dns_entry **addr,
bool *async)
{
CURLcode result=CURLE_OK;
long shortest = 0; /* default to no timeout */
/*************************************************************
* Set timeout if that is being used
*************************************************************/
if(data->set.timeout || data->set.connecttimeout) {
/* We set the timeout on the name resolving phase first, separately from
* the download/upload part to allow a maximum time on everything. This is
* a signal-based timeout, why it won't work and shouldn't be used in
* multi-threaded environments. */
shortest = data->set.timeout; /* default to this timeout value */
if(shortest && data->set.connecttimeout &&
(data->set.connecttimeout < shortest))
/* if both are set, pick the shortest */
shortest = data->set.connecttimeout;
else if(!shortest)
/* if timeout is not set, use the connect timeout */
shortest = data->set.connecttimeout;
/* We can expect the conn->created time to be "now", as that was just
recently set in the beginning of this function and nothing slow
has been done since then until now. */
}
/*************************************************************
* Resolve the name of the server or proxy
*************************************************************/
if(conn->bits.reuse) {
/* re-used connection, no resolving is necessary */
*addr = NULL;
/* we'll need to clear conn->dns_entry later in Curl_disconnect() */
if(conn->bits.proxy)
fix_hostname(data, conn, &conn->host);
}
else {
/* this is a fresh connect */
int rc;
struct Curl_dns_entry *hostaddr;
/* set a pointer to the hostname we display */
fix_hostname(data, conn, &conn->host);
if(!conn->proxy.name || !*conn->proxy.name) {
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
conn->port = conn->remote_port; /* it is the same port */
/* Resolve target host right on */
rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
&hostaddr, shortest);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
else if(!hostaddr) {
failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
result = CURLE_COULDNT_RESOLVE_HOST;
/* don't return yet, we need to clean up the timeout first */
}
}
else {
/* This is a proxy that hasn't been resolved yet. */
/* IDN-fix the proxy name */
fix_hostname(data, conn, &conn->proxy);
/* resolve proxy */
rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port,
&hostaddr, shortest);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
result = CURLE_COULDNT_RESOLVE_PROXY;
/* don't return yet, we need to clean up the timeout first */
}
}
*addr = hostaddr;
}
return result;
}
|
|||||
| ↓ | Curl_cookie_getlist | 17 | 24 | 67 | lib/cookie.c |
struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
const char *host, const char *path,
bool secure)
{
struct Cookie *newco;
struct Cookie *co;
time_t now = time(NULL);
struct Cookie *mainco=NULL;
if(!c || !c->cookies)
return NULL; /* no cookie struct or no cookies in the struct */
co = c->cookies;
while(co) {
/* only process this cookie if it is not expired or had no expire
date AND that if the cookie requires we're secure we must only
continue if we are! */
if( (!co->expires || (co->expires > now)) &&
(co->secure?secure:TRUE) ) {
/* now check if the domain is correct */
if(!co->domain ||
(co->tailmatch && tailmatch(co->domain, host)) ||
(!co->tailmatch && strequal(host, co->domain)) ) {
/* the right part of the host matches the domain stuff in the
cookie data */
/* now check the left part of the path with the cookies path
requirement */
if(!co->path ||
/* not using checkprefix() because matching should be
case-sensitive */
!strncmp(co->path, path, strlen(co->path)) ) {
/* and now, we know this is a match and we should create an
entry for the return-linked-list */
newco = malloc(sizeof(struct Cookie));
if(newco) {
/* first, copy the whole source cookie: */
memcpy(newco, co, sizeof(struct Cookie));
/* then modify our next */
newco->next = mainco;
/* point the main to us */
mainco = newco;
}
else {
/* failure, clear up the allocated chain and return NULL */
while(mainco) {
co = mainco->next;
free(mainco);
mainco = co;
}
return NULL;
}
}
}
}
co = co->next;
}
return mainco; /* return the new list */
}
|
|||||
| ↓ | ftp_state_mdtm_resp | 18 | 45 | 101 | lib/ftp.c |
static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
int ftpcode)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
struct FTP *ftp = data->state.proto.ftp;
struct ftp_conn *ftpc = &conn->proto.ftpc;
switch(ftpcode) {
case 213:
{
/* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
last .sss part is optional and means fractions of a second */
int year, month, day, hour, minute, second;
char *buf = data->state.buffer;
if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
&year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
time_t secs=time(NULL);
/* using the good old yacc/bison yuck */
snprintf(buf, sizeof(conn->data->state.buffer),
"%04d%02d%02d %02d:%02d:%02d GMT",
year, month, day, hour, minute, second);
/* now, convert this into a time() value: */
data->info.filetime = (long)curl_getdate(buf, &secs);
}
#ifdef CURL_FTP_HTTPSTYLE_HEAD
/* If we asked for a time of the file and we actually got one as well,
we "emulate" a HTTP-style header in our output. */
if(data->set.opt_no_body &&
ftpc->file &&
data->set.get_filetime &&
(data->info.filetime>=0) ) {
struct tm *tm;
time_t filetime = (time_t)data->info.filetime;
#ifdef HAVE_GMTIME_R
struct tm buffer;
tm = (struct tm *)gmtime_r(&filetime, &buffer);
#else
tm = gmtime(&filetime);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26" */
snprintf(buf, BUFSIZE-1,
"Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
Curl_month[tm->tm_mon],
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
} /* end of a ridiculous amount of conditionals */
#endif
}
break;
default:
infof(data, "unsupported MDTM reply format\n");
break;
case 550: /* "No such file or directory" */
failf(data, "Given file does not exist");
result = CURLE_FTP_COULDNT_RETR_FILE;
break;
}
if(data->set.timecondition) {
if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
if(data->info.filetime <= data->set.timevalue) {
infof(data, "The requested document is not new enough\n");
ftp->transfer = FTPTRANSFER_NONE; /* mark this to not transfer data */
state(conn, FTP_STOP);
return CURLE_OK;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(data->info.filetime > data->set.timevalue) {
infof(data, "The requested document is not old enough\n");
ftp->transfer = FTPTRANSFER_NONE; /* mark this to not transfer data */
state(conn, FTP_STOP);
return CURLE_OK;
}
break;
} /* switch */
}
else {
infof(data, "Skipping time comparison\n");
}
}
if(!result)
result = ftp_state_post_mdtm(conn);
return result;
}
|
|||||
| ↓ | nss_load_crl | 16 | 57 | 73 | lib/nss.c |
static int nss_load_crl(const char* crlfilename, PRBool ascii)
{
PRFileDesc *infile;
PRStatus prstat;
PRFileInfo info;
PRInt32 nb;
int rv;
SECItem crlDER;
CERTSignedCrl *crl=NULL;
PK11SlotInfo *slot=NULL;
infile = PR_Open(crlfilename,PR_RDONLY,0);
if (!infile) {
return 0;
}
crlDER.data = NULL;
prstat = PR_GetOpenFileInfo(infile,&info);
if (prstat!=PR_SUCCESS)
return 0;
if (ascii) {
SECItem filedata;
char *asc,*body;
filedata.data = NULL;
if (!SECITEM_AllocItem(NULL,&filedata,info.size))
return 0;
nb = PR_Read(infile,filedata.data,info.size);
if (nb!=info.size)
return 0;
asc = (char*)filedata.data;
if (!asc)
return 0;
body=strstr(asc,"-----BEGIN");
if (body != NULL) {
char *trailer=NULL;
asc = body;
body = PORT_Strchr(asc,'\n');
if (!body)
body = PORT_Strchr(asc,'\r');
if (body)
trailer = strstr(++body,"-----END");
if (trailer!=NULL)
*trailer='\0';
else
return 0;
}
else {
body = asc;
}
rv = ATOB_ConvertAsciiToItem(&crlDER,body);
PORT_Free(filedata.data);
if (rv)
return 0;
}
else {
if (!SECITEM_AllocItem(NULL,&crlDER,info.size))
return 0;
nb = PR_Read(infile,crlDER.data,info.size);
if (nb!=info.size)
return 0;
}
slot = PK11_GetInternalKeySlot();
crl = PK11_ImportCRL(slot,&crlDER,
NULL,SEC_CRL_TYPE,
NULL,CRL_IMPORT_DEFAULT_OPTIONS,
NULL,(CRL_DECODE_DEFAULT_OPTIONS|
CRL_DECODE_DONT_COPY_DER));
if (slot) PK11_FreeSlot(slot);
if (!crl) return 0;
SEC_DestroyCrl(crl);
return 1;
}
|
|||||
| ↓ | Curl_http_input_auth | 16 | 51 | 116 | lib/http.c |
CURLcode Curl_http_input_auth(struct connectdata *conn,
int httpcode,
const char *header) /* the first non-space */
{
/*
* This resource requires authentication
*/
struct SessionHandle *data = conn->data;
long *availp;
const char *start;
struct auth *authp;
if(httpcode == 407) {
start = header+strlen("Proxy-authenticate:");
availp = &data->info.proxyauthavail;
authp = &data->state.authproxy;
}
else {
start = header+strlen("WWW-Authenticate:");
availp = &data->info.httpauthavail;
authp = &data->state.authhost;
}
/* pass all white spaces */
while(*start && ISSPACE(*start))
start++;
/*
* Here we check if we want the specific single authentication (using ==) and
* if we do, we initiate usage of it.
*
* If the provided authentication is wanted as one out of several accepted
* types (using &), we OR this authentication type to the authavail
* variable.
*/
#ifdef HAVE_GSSAPI
if(checkprefix("GSS-Negotiate", start) ||
checkprefix("Negotiate", start)) {
*availp |= CURLAUTH_GSSNEGOTIATE;
authp->avail |= CURLAUTH_GSSNEGOTIATE;
if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
/* if exactly this is wanted, go */
int neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
if(neg == 0) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->change.url);
data->state.authproblem = (data->req.newurl == NULL);
}
else {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
}
else
#endif
#ifdef USE_NTLM
/* NTLM support requires the SSL crypto libs */
if(checkprefix("NTLM", start)) {
*availp |= CURLAUTH_NTLM;
authp->avail |= CURLAUTH_NTLM;
if(authp->picked == CURLAUTH_NTLM) {
/* NTLM authentication is picked and activated */
CURLntlm ntlm =
Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
if(CURLNTLM_BAD != ntlm)
data->state.authproblem = FALSE;
else {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
}
else
#endif
#ifndef CURL_DISABLE_CRYPTO_AUTH
if(checkprefix("Digest", start)) {
if((authp->avail & CURLAUTH_DIGEST) != 0) {
infof(data, "Ignoring duplicate digest auth header.\n");
}
else {
CURLdigest dig;
*availp |= CURLAUTH_DIGEST;
authp->avail |= CURLAUTH_DIGEST;
/* We call this function on input Digest headers even if Digest
* authentication isn't activated yet, as we need to store the
* incoming data from this header in case we are gonna use Digest. */
dig = Curl_input_digest(conn, (bool)(httpcode == 407), start);
if(CURLDIGEST_FINE != dig) {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
}
else
#endif
if(checkprefix("Basic", start)) {
*availp |= CURLAUTH_BASIC;
authp->avail |= CURLAUTH_BASIC;
if(authp->picked == CURLAUTH_BASIC) {
/* We asked for Basic authentication but got a 40X back
anyway, which basically means our name+password isn't
valid. */
authp->avail = CURLAUTH_NONE;
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
return CURLE_OK;
}
|
|||||
| ↓ | singlesocket | 16 | 51 | 97 | lib/multi.c |
static void singlesocket(struct Curl_multi *multi,
struct Curl_one_easy *easy)
{
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
int i;
struct Curl_sh_entry *entry;
curl_socket_t s;
int num;
unsigned int curraction;
memset(&socks, 0, sizeof(socks));
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
socks[i] = CURL_SOCKET_BAD;
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE);
/* We have 0 .. N sockets already and we get to know about the 0 .. M
sockets we should have from now on. Detect the differences, remove no
longer supervised ones and add new ones */
/* walk over the sockets we got right now */
for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
(curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
i++) {
int action = CURL_POLL_NONE;
s = socks[i];
/* get it from the hash */
entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
if(curraction & GETSOCK_READSOCK(i))
action |= CURL_POLL_IN;
if(curraction & GETSOCK_WRITESOCK(i))
action |= CURL_POLL_OUT;
if(entry) {
/* yeps, already present so check if it has the same action set */
if(entry->action == action)
/* same, continue */
continue;
}
else {
/* this is a socket we didn't have before, add it! */
entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
if(!entry)
/* fatal */
return;
}
multi->socket_cb(easy->easy_handle,
s,
action,
multi->socket_userp,
entry ? entry->socketp : NULL);
entry->action = action; /* store the current action state */
}
num = i; /* number of sockets */
/* when we've walked over all the sockets we should have right now, we must
make sure to detect sockets that are removed */
for(i=0; i< easy->numsocks; i++) {
int j;
s = easy->sockets[i];
for(j=0; j
|
|||||
| ↓ | Curl_cookie_init | 16 | 38 | 66 | lib/cookie.c |
struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
const char *file,
struct CookieInfo *inc,
bool newsession)
{
struct CookieInfo *c;
FILE *fp;
bool fromfile=TRUE;
if(NULL == inc) {
/* we didn't get a struct, create one */
c = calloc(1, sizeof(struct CookieInfo));
if(!c)
return NULL; /* failed to get memory */
c->filename = strdup(file?file:"none"); /* copy the name just in case */
}
else {
/* we got an already existing one, use that */
c = inc;
}
c->running = FALSE; /* this is not running, this is init */
if(file && strequal(file, "-")) {
fp = stdin;
fromfile=FALSE;
}
else if(file && !*file) {
/* points to a "" string */
fp = NULL;
}
else
fp = file?fopen(file, "r"):NULL;
c->newsession = newsession; /* new session? */
if(fp) {
char *lineptr;
bool headerline;
char *line = malloc(MAX_COOKIE_LINE);
if(line) {
while(fgets(line, MAX_COOKIE_LINE, fp)) {
if(checkprefix("Set-Cookie:", line)) {
/* This is a cookie line, get it! */
lineptr=&line[11];
headerline=TRUE;
}
else {
lineptr=line;
headerline=FALSE;
}
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
}
free(line); /* free the line buffer */
}
if(fromfile)
fclose(fp);
}
c->running = TRUE; /* now, we're running */
return c;
}
|
|||||
| ↓ | Curl_GetFTPResponse | 16 | 41 | 116 | lib/ftp.c |
CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
struct connectdata *conn,
int *ftpcode) /* return the ftp-code */
{
/*
* We cannot read just one byte per read() and then go back to select() as
* the OpenSSL read() doesn't grok that properly.
*
* Alas, read as much as possible, split up into lines, use the ending
* line in a response or continue reading. */
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
long timeout; /* timeout in milliseconds */
long interval_ms;
struct SessionHandle *data = conn->data;
CURLcode result = CURLE_OK;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct timeval now = Curl_tvnow();
size_t nread;
int cache_skip=0;
int value_to_be_ignored=0;
if(ftpcode)
*ftpcode = 0; /* 0 for errors */
else
/* make the pointer point to something for the rest of this function */
ftpcode = &value_to_be_ignored;
*nreadp=0;
while(!*ftpcode && !result) {
/* check and reset timeout value every lap */
if(data->set.ftp_response_timeout )
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
remaining time. Also, use "now" as opposed to "conn->now"
because ftp_response_timeout is only supposed to govern
the response for any given ftp response, not for the time
from connect to the given ftp response. */
timeout = data->set.ftp_response_timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
else if(data->set.timeout)
/* if timeout is requested, find out how much remaining time we have */
timeout = data->set.timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
else
/* Even without a requested timeout, we only wait response_time
seconds for the full response to arrive before we bail out */
timeout = ftpc->response_time -
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
if(timeout <=0 ) {
failf(data, "FTP response timeout");
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
}
interval_ms = 1 * 1000; /* use 1 second timeout intervals */
if(timeout < interval_ms)
interval_ms = timeout;
/*
* Since this function is blocking, we need to wait here for input on the
* connection and only then we call the response reading function. We do
* timeout at least every second to make the timeout check run.
*
* A caution here is that the ftp_readresp() function has a cache that may
* contain pieces of a response from the previous invoke and we need to
* make sure we don't just wait for input while there is unhandled data in
* that cache. But also, if the cache is there, we call ftp_readresp() and
* the cache wasn't good enough to continue we must not just busy-loop
* around this function.
*
*/
if(ftpc->cache && (cache_skip < 2)) {
/*
* There's a cache left since before. We then skipping the wait for
* socket action, unless this is the same cache like the previous round
* as then the cache was deemed not enough to act on and we then need to
* wait for more data anyway.
*/
}
else {
switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, (int)interval_ms)) {
case -1: /* select() error, stop reading */
failf(data, "FTP response aborted due to select/poll error: %d",
SOCKERRNO);
return CURLE_RECV_ERROR;
case 0: /* timeout */
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
continue; /* just continue in our loop for the timeout duration */
default: /* for clarity */
break;
}
}
result = ftp_readresp(sockfd, conn, ftpcode, &nread);
if(result)
break;
if(!nread && ftpc->cache)
/* bump cache skip counter as on repeated skips we must wait for more
data */
cache_skip++;
else
/* when we got data or there is no cache left, we reset the cache skip
counter */
cache_skip=0;
*nreadp += nread;
} /* while there's buffer left and loop is requested */
return result;
}
|
|||||
| ↓ | Curl_resolv | 15 | 46 | 95 | lib/hostip.c |
int Curl_resolv(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
size_t entry_len;
struct SessionHandle *data = conn->data;
CURLcode result;
int rc = CURLRESOLV_ERROR; /* default to failure */
*entry = NULL;
/* Create an entry id, based upon the hostname and port */
entry_id = create_hostcache_id(hostname, port);
/* If we can't create the entry id, fail */
if(!entry_id)
return rc;
entry_len = strlen(entry_id);
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* See if its already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
/* See whether the returned entry is stale. Done before we release lock */
if( remove_entry_if_stale(data, dns) )
dns = NULL; /* the memory deallocation is being handled by the hash */
if(dns) {
dns->inuse++; /* we use it! */
rc = CURLRESOLV_RESOLVED;
}
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
/* free the allocated entry_id again */
free(entry_id);
if(!dns) {
/* The entry was not in the cache. Resolve it to IP address */
Curl_addrinfo *addr;
int respwait;
/* Check what IP specifics the app has requested and if we can provide it.
* If not, bail out. */
if(!Curl_ipvalid(data))
return CURLRESOLV_ERROR;
/* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
non-zero value indicating that we need to wait for the response to the
resolve call */
addr = Curl_getaddrinfo(conn, hostname, port, &respwait);
if(!addr) {
if(respwait) {
/* the response to our resolve call will come asynchronously at
a later time, good or bad */
/* First, check that we haven't received the info by now */
result = Curl_is_resolved(conn, &dns);
if(result) /* error detected */
return CURLRESOLV_ERROR;
if(dns)
rc = CURLRESOLV_RESOLVED; /* pointer provided */
else
rc = CURLRESOLV_PENDING; /* no info yet */
}
}
else {
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
dns = Curl_cache_addr(data, addr, hostname, port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
if(!dns)
/* returned failure, bail out nicely */
Curl_freeaddrinfo(addr);
else
rc = CURLRESOLV_RESOLVED;
}
}
*entry = dns;
return rc;
}
|
|||||
| ↓ | ftp_state_ul_setup | 15 | 39 | 95 | lib/ftp.c |
static CURLcode ftp_state_ul_setup(struct connectdata *conn,
bool sizechecked)
{
CURLcode result = CURLE_OK;
struct FTP *ftp = conn->data->state.proto.ftp;
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
if((data->state.resume_from && !sizechecked) ||
((data->state.resume_from > 0) && sizechecked)) {
/* we're about to continue the uploading of a file */
/* 1. get already existing file's size. We use the SIZE command for this
which may not exist in the server! The SIZE command is not in
RFC959. */
/* 2. This used to set REST. But since we can do append, we
don't another ftp command. We just skip the source file
offset and then we APPEND the rest on the file instead */
/* 3. pass file-size number of bytes in the source file */
/* 4. lower the infilesize counter */
/* => transfer as usual */
if(data->state.resume_from < 0 ) {
/* Got no given size to start from, figure it out */
NBFTPSENDF(conn, "SIZE %s", ftpc->file);
state(conn, FTP_STOR_SIZE);
return result;
}
/* enable append */
data->set.ftp_append = TRUE;
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
curl_off_t readthisamountnow = data->state.resume_from;
if(conn->seek_func(conn->seek_client,
readthisamountnow, SEEK_SET) != 0) {
failf(data, "Could not seek stream");
return CURLE_FTP_COULDNT_USE_REST;
}
}
else {
curl_off_t passed=0;
do {
curl_off_t readthisamountnow = (data->state.resume_from - passed);
curl_off_t actuallyread;
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread = (curl_off_t)
conn->fread_func(data->state.buffer, 1, (size_t)readthisamountnow,
conn->fread_in);
passed += actuallyread;
if((actuallyread <= 0) || (actuallyread > readthisamountnow)) {
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
failf(data, "Failed to read data");
return CURLE_FTP_COULDNT_USE_REST;
}
} while(passed < data->state.resume_from);
}
/* now, decrease the size of the read */
if(data->set.infilesize>0) {
data->set.infilesize -= data->state.resume_from;
if(data->set.infilesize <= 0) {
infof(data, "File already completely uploaded\n");
/* no data to transfer */
result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* Set ->transfer so that we won't get any error in
* ftp_done() because we didn't transfer anything! */
ftp->transfer = FTPTRANSFER_NONE;
state(conn, FTP_STOP);
return CURLE_OK;
}
}
/* we've passed, proceed as normal */
} /* resume_from */
NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
ftpc->file);
state(conn, FTP_STOR);
return result;
}
|
|||||
| ↓ | get_cert_chain | 17 | 102 | 152 | lib/ssluse.c |
static CURLcode get_cert_chain(struct connectdata *conn,
struct ssl_connect_data *connssl)
{
STACK_OF(X509) *sk;
int i;
char buf[512];
struct SessionHandle *data = conn->data;
int numcerts;
sk = SSL_get_peer_cert_chain(connssl->handle);
if(!sk)
return CURLE_OUT_OF_MEMORY;
numcerts = sk_X509_num(sk);
if(init_certinfo(data, numcerts))
return CURLE_OUT_OF_MEMORY;
infof(data, "--- Certificate chain\n");
for (i=0; i
|
|||||
| ↓ | add_buffer_send | 14 | 61 | 146 | lib/http.c |
static
CURLcode add_buffer_send(send_buffer *in,
struct connectdata *conn,
long *bytes_written, /* add the number of sent bytes
to this counter */
size_t included_body_bytes, /* how much of the buffer
contains body data */
int socketindex)
{
ssize_t amount;
CURLcode res;
char *ptr;
size_t size;
struct HTTP *http = conn->data->state.proto.http;
size_t sendsize;
curl_socket_t sockfd;
size_t headersize;
DEBUGASSERT(socketindex <= SECONDARYSOCKET);
sockfd = conn->sock[socketindex];
/* The looping below is required since we use non-blocking sockets, but due
to the circumstances we will just loop and try again and again etc */
ptr = in->buffer;
size = in->size_used;
headersize = size - included_body_bytes; /* the initial part that isn't body
is header */
DEBUGASSERT(size > included_body_bytes);
#ifdef CURL_DOES_CONVERSIONS
res = Curl_convert_to_network(conn->data, ptr, headersize);
/* Curl_convert_to_network calls failf if unsuccessful */
if(res != CURLE_OK) {
/* conversion failed, free memory and return to the caller */
if(in->buffer)
free(in->buffer);
free(in);
return res;
}
#endif /* CURL_DOES_CONVERSIONS */
if(conn->protocol & PROT_HTTPS) {
/* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
when we speak HTTPS, as if only a fraction of it is sent now, this data
needs to fit into the normal read-callback buffer later on and that
buffer is using this size.
*/
sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
/* OpenSSL is very picky and we must send the SAME buffer pointer to the
library when we attempt to re-send this buffer. Sending the same data
is not enough, we must use the exact same address. For this reason, we
must copy the data to the uploadbuffer first, since that is the buffer
we will be using if this send is retried later.
*/
memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
ptr = conn->data->state.uploadbuffer;
}
else
sendsize = size;
res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
if(CURLE_OK == res) {
/*
* Note that we may not send the entire chunk at once, and we have a set
* number of data bytes at the end of the big buffer (out of which we may
* only send away a part).
*/
/* how much of the header that was sent */
size_t headlen = (size_t)amount>headersize?headersize:(size_t)amount;
size_t bodylen = amount - headlen;
if(conn->data->set.verbose) {
/* this data _may_ contain binary stuff */
Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, headlen, conn);
if((size_t)amount > headlen) {
/* there was body data sent beyond the initial header part, pass that
on to the debug callback too */
Curl_debug(conn->data, CURLINFO_DATA_OUT,
ptr+headlen, bodylen, conn);
}
}
if(bodylen)
/* since we sent a piece of the body here, up the byte counter for it
accordingly */
http->writebytecount += bodylen;
*bytes_written += amount;
if(http) {
if((size_t)amount != size) {
/* The whole request could not be sent in one system call. We must
queue it up and send it later when we get the chance. We must not
loop here and wait until it might work again. */
size -= amount;
ptr = in->buffer + amount;
/* backup the currently set pointers */
http->backup.fread_func = conn->fread_func;
http->backup.fread_in = conn->fread_in;
http->backup.postdata = http->postdata;
http->backup.postsize = http->postsize;
/* set the new pointers for the request-sending */
conn->fread_func = (curl_read_callback)readmoredata;
conn->fread_in = (void *)conn;
http->postdata = ptr;
http->postsize = (curl_off_t)size;
http->send_buffer = in;
http->sending = HTTPSEND_REQUEST;
return CURLE_OK;
}
http->sending = HTTPSEND_BODY;
/* the full buffer was sent, clean up and return */
}
else {
if((size_t)amount != size)
/* We have no continue-send mechanism now, fail. This can only happen
when this function is used from the CONNECT sending function. We
currently (stupidly) assume that the whole request is always sent
away in the first single chunk.
This needs FIXing.
*/
return CURLE_SEND_ERROR;
else
conn->writechannel_inuse = FALSE;
}
}
if(in->buffer)
free(in->buffer);
free(in);
return res;
}
|
|||||
| ↓ | Curl_resolv_timeout | 14 | 45 | 132 | lib/hostip.c |
int Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
long timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
bool keep_copysig=FALSE; /* did copy it? */
struct sigaction sigact;
#else
#ifdef HAVE_SIGNAL
void (*keep_sigact)(int); /* store the old handler here */
#endif /* HAVE_SIGNAL */
#endif /* HAVE_SIGACTION */
volatile long timeout;
unsigned int prev_alarm=0;
struct SessionHandle *data = conn->data;
#endif /* USE_ALARM_TIMEOUT */
int rc;
*entry = NULL;
#ifdef USE_ALARM_TIMEOUT
if (data->set.no_signal)
/* Ignore the timeout when signals are disabled */
timeout = 0;
else
timeout = timeoutms;
if(timeout && timeout < 1000)
/* The alarm() function only provides integer second resolution, so if
we want to wait less than one second we must bail out already now. */
return CURLRESOLV_TIMEDOUT;
if (timeout > 0) {
/* This allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here.
This technique has problems (see alarmfunc). */
if(sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() after an alarm signal */
failf(data, "name lookup timed out");
return CURLRESOLV_ERROR;
}
/*************************************************************
* Set signal handler to catch SIGALRM
* Store the old value to be able to set it back later!
*************************************************************/
#ifdef HAVE_SIGACTION
sigaction(SIGALRM, NULL, &sigact);
keep_sigact = sigact;
keep_copysig = TRUE; /* yes, we have a copy */
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
/* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
sigact.sa_flags &= ~SA_RESTART;
#endif
/* now set the new struct */
sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
/* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
keep_sigact = signal(SIGALRM, alarmfunc);
#endif
#endif /* HAVE_SIGACTION */
/* alarm() makes a signal get sent when the timeout fires off, and that
will abort system calls */
prev_alarm = alarm((unsigned int) (timeout ? timeout/1000L : timeout));
}
#else
#ifndef CURLRES_ASYNCH
if(timeoutms)
infof(conn->data, "timeout on name lookup is not supported\n");
#else
(void)timeoutms; /* timeoutms not used with an async resolver */
#endif
#endif /* USE_ALARM_TIMEOUT */
/* Perform the actual name resolution. This might be interrupted by an
* alarm if it takes too long.
*/
rc = Curl_resolv(conn, hostname, port, entry);
#ifdef USE_ALARM_TIMEOUT
if (timeout > 0) {
#ifdef HAVE_SIGACTION
if(keep_copysig) {
/* we got a struct as it looked before, now put that one back nice
and clean */
sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
}
#else
#ifdef HAVE_SIGNAL
/* restore the previous SIGALRM handler */
signal(SIGALRM, keep_sigact);
#endif
#endif /* HAVE_SIGACTION */
/* switch back the alarm() to either zero or to what it was before minus
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
/* the alarm period is counted in even number of seconds */
unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
/* if the alarm time-left reached zero or turned "negative" (counted
with unsigned values), we should fire off a SIGALRM here, but we
won't, and zero would be to switch it off so we never set it to
less than 1! */
alarm(1);
rc = CURLRESOLV_TIMEDOUT;
failf(data, "Previous alarm fired off!");
}
else
alarm((unsigned int)alarm_set);
}
else
alarm(0); /* just shut it off */
}
#endif /* USE_ALARM_TIMEOUT */
return rc;
}
|
|||||
| Curl_wait_for_resolv | 1 | 3 | 7 | lib/hostsyn.c | |
| ↓ | Curl_disconnect | 14 | 42 | 92 | lib/url.c |
CURLcode Curl_disconnect(struct connectdata *conn)
{
struct SessionHandle *data;
if(!conn)
return CURLE_OK; /* this is closed and fine already */
data = conn->data;
if(!data) {
DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
return CURLE_OK;
}
#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
/* scan for DNS cache entries still marked as in use */
Curl_hash_apply(data->hostcache,
NULL, Curl_scan_cache_used);
#endif
Curl_expire(data, 0); /* shut off timers */
Curl_hostcache_prune(data); /* kill old DNS cache entries */
{
int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);
/* Authentication data is a mix of connection-related and sessionhandle-
related stuff. NTLM is connection-related so when we close the shop
we shall forget. */
if (has_host_ntlm) {
data->state.authhost.done = FALSE;
data->state.authhost.picked =
data->state.authhost.want;
}
if (has_proxy_ntlm) {
data->state.authproxy.done = FALSE;
data->state.authproxy.picked =
data->state.authproxy.want;
}
if (has_host_ntlm || has_proxy_ntlm) {
data->state.authproblem = FALSE;
Curl_ntlm_cleanup(conn);
}
}
/* Cleanup possible redirect junk */
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
if(conn->handler->disconnect)
/* This is set if protocol-specific cleanups should be made */
conn->handler->disconnect(conn);
if(-1 != conn->connectindex) {
/* unlink ourselves! */
infof(data, "Closing connection #%ld\n", conn->connectindex);
if(data->state.connc)
/* only clear the table entry if we still know in which cache we
used to be in */
data->state.connc->connects[conn->connectindex] = NULL;
}
#ifdef USE_LIBIDN
if(conn->host.encalloc)
idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
with idn_free() since this was allocated
by libidn */
if(conn->proxy.encalloc)
idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
freed with idn_free() since this was
allocated by libidn */
#endif
Curl_ssl_close(conn, FIRSTSOCKET);
/* Indicate to all handles on the pipe that we're dead */
if(Curl_isPipeliningEnabled(data)) {
signalPipeClose(conn->send_pipe);
signalPipeClose(conn->recv_pipe);
signalPipeClose(conn->pend_pipe);
}
conn_free(conn);
data->state.current_conn = NULL;
return CURLE_OK;
}
|
|||||
| ↓ | inflate_stream | 14 | 41 | 74 | lib/content_encoding.c |
static CURLcode
inflate_stream(struct connectdata *conn,
struct SingleRequest *k)
{
int allow_restart = 1;
z_stream *z = &k->z; /* zlib state structure */
uInt nread = z->avail_in;
Bytef *orig_in = z->next_in;
int status; /* zlib status */
CURLcode result = CURLE_OK; /* Curl_client_write status */
char *decomp; /* Put the decompressed data here. */
/* Dynamically allocate a buffer for decompression because it's uncommonly
large to hold on the stack */
decomp = malloc(DSIZ);
if(decomp == NULL) {
return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
}
/* because the buffer size is fixed, iteratively decompress and transfer to
the client via client_write. */
for (;;) {
/* (re)set buffer for decompressed output for every iteration */
z->next_out = (Bytef *)decomp;
z->avail_out = DSIZ;
status = inflate(z, Z_SYNC_FLUSH);
if(status == Z_OK || status == Z_STREAM_END) {
allow_restart = 0;
if(DSIZ - z->avail_out) {
result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp,
DSIZ - z->avail_out);
/* if !CURLE_OK, clean up, return */
if(result) {
free(decomp);
return exit_zlib(z, &k->zlib_init, result);
}
}
/* Done? clean up, return */
if(status == Z_STREAM_END) {
free(decomp);
if(inflateEnd(z) == Z_OK)
return exit_zlib(z, &k->zlib_init, result);
else
return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
}
/* Done with these bytes, exit */
if(status == Z_OK && z->avail_in == 0) {
free(decomp);
return result;
}
}
else if(allow_restart && status == Z_DATA_ERROR) {
/* some servers seem to not generate zlib headers, so this is an attempt
to fix and continue anyway */
(void) inflateEnd(z); /* don't care about the return code */
if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
return process_zlib_error(conn, z);
}
z->next_in = orig_in;
z->avail_in = nread;
allow_restart = 0;
continue;
}
else { /* Error; exit loop, handle below */
free(decomp);
return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
}
}
/* Will never get here */
}
|
|||||
| ↓ | get_pathname | 14 | 41 | 69 | lib/ssh.c |
static CURLcode
get_pathname(const char **cpp, char **path)
{
const char *cp = *cpp, *end;
char quot;
unsigned int i, j;
static const char * const WHITESPACE = " \t\r\n";
cp += strspn(cp, WHITESPACE);
if(!*cp) {
*cpp = cp;
*path = NULL;
return CURLE_QUOTE_ERROR;
}
*path = malloc(strlen(cp) + 1);
if(*path == NULL)
return CURLE_OUT_OF_MEMORY;
/* Check for quoted filenames */
if(*cp == '\"' || *cp == '\'') {
quot = *cp++;
/* Search for terminating quote, unescape some chars */
for (i = j = 0; i <= strlen(cp); i++) {
if(cp[i] == quot) { /* Found quote */
i++;
(*path)[j] = '\0';
break;
}
if(cp[i] == '\0') { /* End of string */
/*error("Unterminated quote");*/
goto fail;
}
if(cp[i] == '\\') { /* Escaped characters */
i++;
if(cp[i] != '\'' && cp[i] != '\"' &&
cp[i] != '\\') {
/*error("Bad escaped character '\\%c'",
cp[i]);*/
goto fail;
}
}
(*path)[j++] = cp[i];
}
if(j == 0) {
/*error("Empty quotes");*/
goto fail;
}
*cpp = cp + i + strspn(cp + i, WHITESPACE);
}
else {
/* Read to end of filename */
end = strpbrk(cp, WHITESPACE);
if(end == NULL)
end = strchr(cp, '\0');
*cpp = end + strspn(end, WHITESPACE);
memcpy(*path, cp, end - cp);
(*path)[end - cp] = '\0';
}
return CURLE_OK;
fail:
Curl_safefree(*path);
*path = NULL;
return CURLE_QUOTE_ERROR;
}
|
|||||
| ↓ | Curl_input_ntlm | 14 | 41 | 88 | lib/http_ntlm.c |
CURLntlm Curl_input_ntlm(struct connectdata *conn,
bool proxy, /* if proxy or not */
const char *header) /* rest of the www-authenticate:
header */
{
/* point to the correct struct with this */
struct ntlmdata *ntlm;
#ifndef USE_WINDOWS_SSPI
static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
#endif
ntlm = proxy?&conn->proxyntlm:&conn->ntlm;
/* skip initial whitespaces */
while(*header && ISSPACE(*header))
header++;
if(checkprefix("NTLM", header)) {
header += strlen("NTLM");
while(*header && ISSPACE(*header))
header++;
if(*header) {
/* We got a type-2 message here:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x02000000)
12 Target Name security buffer(*)
20 Flags long
24 Challenge 8 bytes
(32) Context (optional) 8 bytes (two consecutive longs)
(40) Target Information (optional) security buffer(*)
32 (48) start of data block
*/
size_t size;
unsigned char *buffer;
size = Curl_base64_decode(header, &buffer);
if(!buffer)
return CURLNTLM_BAD;
ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
#ifdef USE_WINDOWS_SSPI
ntlm->type_2 = malloc(size+1);
if(ntlm->type_2 == NULL) {
free(buffer);
return CURLE_OUT_OF_MEMORY;
}
ntlm->n_type_2 = size;
memcpy(ntlm->type_2, buffer, size);
#else
ntlm->flags = 0;
if((size < 32) ||
(memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) ||
(memcmp(buffer+8, type2_marker, sizeof(type2_marker)) != 0)) {
/* This was not a good enough type-2 message */
free(buffer);
return CURLNTLM_BAD;
}
ntlm->flags = readint_le(&buffer[20]);
memcpy(ntlm->nonce, &buffer[24], 8);
DEBUG_OUT({
fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n nonce=");
print_hex(stderr, (char *)ntlm->nonce, 8);
fprintf(stderr, "\n****\n");
fprintf(stderr, "**** Header %s\n ", header);
});
free(buffer);
#endif
}
else {
if(ntlm->state >= NTLMSTATE_TYPE1)
return CURLNTLM_BAD;
ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
}
}
return CURLNTLM_FINE;
}
|
|||||
| ↓ | set_ciphers | 14 | 40 | 63 | lib/nss.c |
static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model,
char *cipher_list)
{
unsigned int i;
PRBool cipher_state[NUM_OF_CIPHERS];
PRBool found;
char *cipher;
SECStatus rv;
/* First disable all ciphers. This uses a different max value in case
* NSS adds more ciphers later we don't want them available by
* accident
*/
for(i=0; i |
|||||
| ↓ | Curl_strerror | 14 | 36 | 87 | lib/strerror.c |
const char *Curl_strerror(struct connectdata *conn, int err)
{
char *buf, *p;
size_t max;
int old_errno = ERRNO;
DEBUGASSERT(conn);
DEBUGASSERT(err >= 0);
buf = conn->syserr_buf;
max = sizeof(conn->syserr_buf)-1;
*buf = '\0';
#ifdef USE_WINSOCK
#ifdef _WIN32_WCE
{
wchar_t wbuf[256];
wbuf[0] = L'\0';
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
wcstombs(buf,wbuf,max);
}
#else
/* 'sys_nerr' is the maximum errno number, it is not widely portable */
if(err >= 0 && err < sys_nerr)
strncpy(buf, strerror(err), max);
else {
if(!get_winsock_error(err, buf, max) &&
!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
LANG_NEUTRAL, buf, (DWORD)max, NULL))
snprintf(buf, max, "Unknown error %d (%#x)", err, err);
}
#endif
#else /* not USE_WINSOCK coming up */
#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
/*
* The POSIX-style strerror_r() may set errno to ERANGE if insufficient
* storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
* message string, or EINVAL if 'errnum' is not a valid error number.
*/
if(0 != strerror_r(err, buf, max)) {
if('\0' == buf[0])
snprintf(buf, max, "Unknown error %d", err);
}
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
/*
* The glibc-style strerror_r() only *might* use the buffer we pass to
* the function, but it always returns the error message as a pointer,
* so we must copy that string unconditionally (if non-NULL).
*/
{
char buffer[256];
char *msg = strerror_r(err, buffer, sizeof(buffer));
if(msg)
strncpy(buf, msg, max);
else
snprintf(buf, max, "Unknown error %d", err);
}
#else
{
char *msg = strerror(err);
if(msg)
strncpy(buf, msg, max);
else
snprintf(buf, max, "Unknown error %d", err);
}
#endif
#endif /* end of ! USE_WINSOCK */
buf[max] = '\0'; /* make sure the string is zero terminated */
/* strip trailing '\r\n' or '\n'. */
if((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2)
*p = '\0';
if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
*p = '\0';
if(old_errno != ERRNO)
SET_ERRNO(old_errno);
return buf;
}
|
|||||
| ↓ | asn1_output | 14 | 30 | 46 | lib/ssluse.c |
static int asn1_output(const ASN1_UTCTIME *tm,
char *buf,
size_t sizeofbuf)
{
const char *asn1_string;
int gmt=FALSE;
int i;
int year=0,month=0,day=0,hour=0,minute=0,second=0;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)prefix;
#endif
i=tm->length;
asn1_string=(const char *)tm->data;
if(i < 10)
return 1;
if(asn1_string[i-1] == 'Z')
gmt=TRUE;
for (i=0; i<10; i++)
if((asn1_string[i] > '9') || (asn1_string[i] < '0'))
return 2;
year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0');
if(year < 50)
year+=100;
month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0');
if((month > 12) || (month < 1))
return 3;
day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0');
hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0');
minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0');
if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') &&
(asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
snprintf(buf, sizeofbuf,
"%04d-%02d-%02d %02d:%02d:%02d %s",
year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
return 0;
}
|
|||||
| ↓ | Curl_http_should_fail | 14 | 28 | 80 | lib/http.c |
int Curl_http_should_fail(struct connectdata *conn)
{
struct SessionHandle *data;
int httpcode;
DEBUGASSERT(conn);
data = conn->data;
DEBUGASSERT(data);
httpcode = data->req.httpcode;
/*
** If we haven't been asked to fail on error,
** don't fail.
*/
if(!data->set.http_fail_on_error)
return 0;
/*
** Any code < 400 is never terminal.
*/
if(httpcode < 400)
return 0;
if(data->state.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(httpcode == 416)) {
/* "Requested Range Not Satisfiable", just proceed and
pretend this is no error */
return 0;
}
/*
** Any code >= 400 that's not 401 or 407 is always
** a terminal error
*/
if((httpcode != 401) &&
(httpcode != 407))
return 1;
/*
** All we have left to deal with is 401 and 407
*/
DEBUGASSERT((httpcode == 401) || (httpcode == 407));
/*
** Examine the current authentication state to see if this
** is an error. The idea is for this function to get
** called after processing all the headers in a response
** message. So, if we've been to asked to authenticate a
** particular stage, and we've done it, we're OK. But, if
** we're already completely authenticated, it's not OK to
** get another 401 or 407.
**
** It is possible for authentication to go stale such that
** the client needs to reauthenticate. Once that info is
** available, use it here.
*/
#if 0 /* set to 1 when debugging this functionality */
infof(data,"%s: authstage = %d\n",__FUNCTION__,data->state.authstage);
infof(data,"%s: authwant = 0x%08x\n",__FUNCTION__,data->state.authwant);
infof(data,"%s: authavail = 0x%08x\n",__FUNCTION__,data->state.authavail);
infof(data,"%s: httpcode = %d\n",__FUNCTION__,k->httpcode);
infof(data,"%s: authdone = %d\n",__FUNCTION__,data->state.authdone);
infof(data,"%s: newurl = %s\n",__FUNCTION__,data->req.newurl ?
data->req.newurl : "(null)");
infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem);
#endif
/*
** Either we're not authenticating, or we're supposed to
** be authenticating something else. This is an error.
*/
if((httpcode == 401) && !conn->bits.user_passwd)
return TRUE;
if((httpcode == 407) && !conn->bits.proxy_user_passwd)
return TRUE;
return data->state.authproblem;
}
|
|||||
| ↓ | ftp_state_post_listtype | 14 | 26 | 64 | lib/ftp.c |
static CURLcode ftp_state_post_listtype(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
/* If this output is to be machine-parsed, the NLST command might be better
to use, since the LIST command output is not specified or standard in any
way. It has turned out that the NLST list output is not the same on all
servers either... */
/*
if FTPFILE_NOCWD was specified, we are currently in
the user's home directory, so we should add the path
as argument for the LIST / NLST / or custom command.
Whether the server will support this, is uncertain.
The other ftp_filemethods will CWD into dir/dir/ first and
then just do LIST (in that case: nothing to do here)
*/
char *cmd,*lstArg,*slashPos;
lstArg = NULL;
if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
data->state.path &&
data->state.path[0] &&
strchr(data->state.path,'/')) {
lstArg = strdup(data->state.path);
if(!lstArg)
return CURLE_OUT_OF_MEMORY;
/* Check if path does not end with /, as then we cut off the file part */
if(lstArg[strlen(lstArg) - 1] != '/') {
/* chop off the file part if format is dir/dir/file */
slashPos = strrchr(lstArg,'/');
*(slashPos+1) = '\0';
}
}
cmd = aprintf( "%s%s%s",
data->set.str[STRING_CUSTOMREQUEST]?
data->set.str[STRING_CUSTOMREQUEST]:
(data->set.ftp_list_only?"NLST":"LIST"),
lstArg? " ": "",
lstArg? lstArg: "" );
if(!cmd) {
if(lstArg)
free(lstArg);
return CURLE_OUT_OF_MEMORY;
}
NBFTPSENDF(conn, "%s",cmd);
if(lstArg)
free(lstArg);
free(cmd);
state(conn, FTP_LIST);
return result;
}
|
|||||
| ↓ | Curl_sec_login | 14 | 43 | 70 | lib/security.c |
int
Curl_sec_login(struct connectdata *conn)
{
int ret;
const struct Curl_sec_client_mech * const *m;
ssize_t nread;
struct SessionHandle *data=conn->data;
int ftpcode;
for(m = mechs; *m && (*m)->name; m++) {
void *tmp;
tmp = realloc(conn->app_data, (*m)->size);
if(tmp == NULL) {
failf (data, "realloc %u failed", (*m)->size);
return -1;
}
conn->app_data = tmp;
if((*m)->init && (*(*m)->init)(conn->app_data) != 0) {
infof(data, "Skipping %s...\n", (*m)->name);
continue;
}
infof(data, "Trying %s...\n", (*m)->name);
if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name))
return -1;
if(Curl_GetFTPResponse(&nread, conn, &ftpcode))
return -1;
if(conn->data->state.buffer[0] != '3'){
switch(ftpcode) {
case 504:
infof(data,
"%s is not supported by the server.\n", (*m)->name);
break;
case 534:
infof(data, "%s rejected as security mechanism.\n", (*m)->name);
break;
default:
if(conn->data->state.buffer[0] == '5') {
infof(data, "The server doesn't support the FTP "
"security extensions.\n");
return -1;
}
break;
}
continue;
}
ret = (*(*m)->auth)(conn->app_data, conn);
if(ret == AUTH_CONTINUE)
continue;
else if(ret != AUTH_OK){
/* mechanism is supposed to output error string */
return -1;
}
conn->mech = *m;
conn->sec_complete = 1;
conn->command_prot = prot_safe;
/* Set the requested protection level */
/* BLOCKING */
Curl_sec_set_protection_level(conn);
break;
}
return *m == NULL;
}
|
|||||
| ↓ | setup_conn | 13 | 44 | 103 | lib/url.c |
static CURLcode setup_conn(struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *protocol_done)
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE) {
/* There's nothing in this function to setup if we're only doing
a file:// transfer */
*protocol_done = TRUE;
return result;
}
*protocol_done = FALSE; /* default to not done */
/* set proxy_connect_closed to false unconditionally already here since it
is used strictly to provide extra information to a parent function in the
case of proxy CONNECT failures and we must make sure we don't have it
lingering set from a previous invoke */
conn->bits.proxy_connect_closed = FALSE;
/*
* Set user-agent. Used for HTTP, but since we can attempt to tunnel
* basically anything through a http proxy we can't limit this based on
* protocol.
*/
if(data->set.str[STRING_USERAGENT]) {
Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
if(!conn->allocptr.uagent)
return CURLE_OUT_OF_MEMORY;
}
data->req.headerbytecount = 0;
#ifdef CURL_DO_LINEEND_CONV
data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
#endif /* CURL_DO_LINEEND_CONV */
for(;;) {
/* loop for CURL_SERVER_CLOSED_CONNECTION */
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
bool connected = FALSE;
/* Connect only if not already connected! */
result = ConnectPlease(data, conn, hostaddr, &connected);
if(connected) {
result = Curl_protocol_connect(conn, protocol_done);
if(CURLE_OK == result)
conn->bits.tcpconnect = TRUE;
}
else
conn->bits.tcpconnect = FALSE;
/* if the connection was closed by the server while exchanging
authentication informations, retry with the new set
authentication information */
if(conn->bits.proxy_connect_closed) {
/* reset the error buffer */
if(data->set.errorbuffer)
data->set.errorbuffer[0] = '\0';
data->state.errorbuf = FALSE;
continue;
}
if(CURLE_OK != result)
return result;
}
else {
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
conn->bits.tcpconnect = TRUE;
*protocol_done = TRUE;
if(data->set.verbose)
verboseconnect(conn);
}
/* Stop the loop now */
break;
}
conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
set this here perhaps a second time */
#ifdef __EMX__
/*
* This check is quite a hack. We're calling _fsetmode to fix the problem
* with fwrite converting newline characters (you get mangled text files,
* and corrupted binary files when you download to stdout and redirect it to
* a file).
*/
if((data->set.out)->_handle == NULL) {
_fsetmode(stdout, "b");
}
#endif
return result;
}
|
|||||
| ↓ | file_connect | 13 | 43 | 84 | lib/file.c |
static CURLcode file_connect(struct connectdata *conn, bool *done)
{
struct SessionHandle *data = conn->data;
char *real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
struct FILEPROTO *file;
int fd;
#ifdef DOS_FILESYSTEM
int i;
char *actual_path;
#endif
if(!real_path)
return CURLE_OUT_OF_MEMORY;
/* If there already is a protocol-specific struct allocated for this
sessionhandle, deal with it */
Curl_reset_reqproto(conn);
if(!data->state.proto.file) {
file = calloc(sizeof(struct FILEPROTO), 1);
if(!file) {
free(real_path);
return CURLE_OUT_OF_MEMORY;
}
data->state.proto.file = file;
}
else {
/* file is not a protocol that can deal with "persistancy" */
file = data->state.proto.file;
Curl_safefree(file->freepath);
if(file->fd != -1)
close(file->fd);
file->path = NULL;
file->freepath = NULL;
file->fd = -1;
}
#ifdef DOS_FILESYSTEM
/* If the first character is a slash, and there's
something that looks like a drive at the beginning of
the path, skip the slash. If we remove the initial
slash in all cases, paths without drive letters end up
relative to the current directory which isn't how
browsers work.
Some browsers accept | instead of : as the drive letter
separator, so we do too.
On other platforms, we need the slash to indicate an
absolute pathname. On Windows, absolute paths start
with a drive letter.
*/
actual_path = real_path;
if((actual_path[0] == '/') &&
actual_path[1] &&
(actual_path[2] == ':' || actual_path[2] == '|'))
{
actual_path[2] = ':';
actual_path++;
}
/* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
for (i=0; actual_path[i] != '\0'; ++i)
if(actual_path[i] == '/')
actual_path[i] = '\\';
fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
file->path = actual_path;
#else
fd = open(real_path, O_RDONLY);
file->path = real_path;
#endif
file->freepath = real_path; /* free this when done */
file->fd = fd;
if(!data->set.upload && (fd == -1)) {
failf(data, "Couldn't open file %s", data->state.path);
file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
return CURLE_FILE_COULDNT_READ_FILE;
}
*done = TRUE;
return CURLE_OK;
}
|
|||||
| ↓ | multi_socket | 13 | 42 | 109 | lib/multi.c |
static CURLMcode multi_socket(struct Curl_multi *multi,
bool checkall,
curl_socket_t s,
int ev_bitmask,
int *running_handles)
{
CURLMcode result = CURLM_OK;
struct SessionHandle *data = NULL;
struct Curl_tree *t;
if(checkall) {
struct Curl_one_easy *easyp;
/* *perform() deals with running_handles on its own */
result = curl_multi_perform(multi, running_handles);
/* walk through each easy handle and do the socket state change magic
and callbacks */
easyp=multi->easy.next;
while(easyp != &multi->easy) {
singlesocket(multi, easyp);
easyp = easyp->next;
}
/* or should we fall-through and do the timer-based stuff? */
return result;
}
else if(s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry =
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
if(!entry)
/* Unmatched socket, we can't act on it but we ignore this fact. In
real-world tests it has been proved that libevent can in fact give
the application actions even though the socket was just previously
asked to get removed, so thus we better survive stray socket actions
and just move on. */
;
else {
data = entry->easy;
if(data->magic != CURLEASY_MAGIC_NUMBER)
/* bad bad bad bad bad bad bad */
return CURLM_INTERNAL_ERROR;
if(data->set.one_easy->easy_conn) /* set socket event bitmask */
data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
result = multi_runsingle(multi, data->set.one_easy);
if(data->set.one_easy->easy_conn)
data->set.one_easy->easy_conn->cselect_bits = 0;
if(CURLM_OK >= result)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
/* Now we fall-through and do the timer-based stuff, since we don't want
to force the user to have to deal with timeouts as long as at least
one connection in fact has traffic. */
data = NULL; /* set data to NULL again to avoid calling
multi_runsingle() in case there's no need to */
}
}
/*
* The loop following here will go on as long as there are expire-times left
* to process in the splay and 'data' will be re-assigned for every expired
* handle we deal with.
*/
do {
struct timeval now;
/* the first loop lap 'data' can be NULL */
if(data) {
result = multi_runsingle(multi, data->set.one_easy);
if(CURLM_OK >= result)
/* get the socket(s) and check if the state has been changed since
last */
singlesocket(multi, data->set.one_easy);
}
/* Check if there's one (more) expired timer to deal with! This function
extracts a matching node if there is one */
now = Curl_tvnow();
now.tv_usec += 1000; /* to compensate for the truncating of 999us to 0ms,
we always add time here to make the comparison
below better */
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
if(t) {
/* assign 'data' to be the easy handle we just removed from the splay
tree */
data = t->payload;
/* clear the expire time within the handle we removed from the
splay tree */
data->state.expiretime.tv_sec = 0;
data->state.expiretime.tv_usec = 0;
}
} while(t);
*running_handles = multi->num_alive;
return result;
}
|
|||||
| Curl_wait_for_resolv | 1 | 3 | 7 | lib/hostsyn.c | |
| ↓ | Curl_setup_transfer | 13 | 29 | 79 | lib/transfer.c |
CURLcode
Curl_setup_transfer(
struct connectdata *conn, /* connection data */
int sockindex, /* socket index to read from or -1 */
curl_off_t size, /* -1 if unknown at this point */
bool getheader, /* TRUE if header parsing is wanted */
curl_off_t *bytecountp, /* return number of bytes read or NULL */
int writesockindex, /* socket index to write to, it may very well be
the same we read from. -1 disables */
curl_off_t *writecountp /* return number of bytes written or NULL */
)
{
struct SessionHandle *data;
struct SingleRequest *k;
DEBUGASSERT(conn != NULL);
data = conn->data;
k = &data->req;
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
/* now copy all input parameters */
conn->sockfd = sockindex == -1 ?
CURL_SOCKET_BAD : conn->sock[sockindex];
conn->writesockfd = writesockindex == -1 ?
CURL_SOCKET_BAD:conn->sock[writesockindex];
k->getheader = getheader;
k->size = size;
k->bytecountp = bytecountp;
k->writebytecountp = writecountp;
/* The code sequence below is placed in this function just because all
necessary input is not always known in do_complete() as this function may
be called after that */
if(!k->getheader) {
k->header = FALSE;
if(size > 0)
Curl_pgrsSetDownloadSize(data, size);
}
/* we want header and/or body, if neither then don't do this! */
if(k->getheader || !data->set.opt_no_body) {
if(conn->sockfd != CURL_SOCKET_BAD) {
k->keepon |= KEEP_READ;
}
if(conn->writesockfd != CURL_SOCKET_BAD) {
/* HTTP 1.1 magic:
Even if we require a 100-return code before uploading data, we might
need to write data before that since the REQUEST may not have been
finished sent off just yet.
Thus, we must check if the request has been sent before we set the
state info where we wait for the 100-return code
*/
if((data->state.expect100header) &&
(data->state.proto.http->sending == HTTPSEND_BODY)) {
/* wait with write until we either got 100-continue or a timeout */
k->exp100 = EXP100_AWAITING_CONTINUE;
k->start100 = k->start;
}
else {
if(data->state.expect100header)
/* when we've sent off the rest of the headers, we must await a
100-continue but first finish sending the request */
k->exp100 = EXP100_SENDING_REQUEST;
/* enable the write bit when we're not waiting for continue */
k->keepon |= KEEP_WRITE;
}
} /* if(conn->writesockfd != CURL_SOCKET_BAD) */
} /* if(k->getheader || !data->set.opt_no_body) */
return CURLE_OK;
}
|
|||||
| ↓ | Curl_destroy_thread_data | 13 | 27 | 45 | lib/hostthre.c |
void Curl_destroy_thread_data (struct Curl_async *async)
{
if(async->hostname)
free(async->hostname);
if(async->os_specific) {
struct thread_data *td = (struct thread_data*) async->os_specific;
curl_socket_t sock = td->dummy_sock;
if(td->mutex_terminate && td->event_terminate) {
/* Signaling resolver thread to terminate */
if(WaitForSingleObject(td->mutex_terminate, INFINITE) == WAIT_OBJECT_0) {
SetEvent(td->event_terminate);
ReleaseMutex(td->mutex_terminate);
}
else {
/* Something went wrong - just ignoring it */
}
}
if(td->mutex_terminate)
CloseHandle(td->mutex_terminate);
if(td->event_terminate)
CloseHandle(td->event_terminate);
if(td->event_thread_started)
CloseHandle(td->event_thread_started);
if(sock != CURL_SOCKET_BAD)
sclose(sock);
/* destroy the synchronization objects */
if(td->mutex_waiting)
CloseHandle(td->mutex_waiting);
td->mutex_waiting = NULL;
if(td->event_resolved)
CloseHandle(td->event_resolved);
if(td->thread_hnd)
CloseHandle(td->thread_hnd);
free(async->os_specific);
}
async->hostname = NULL;
async->os_specific = NULL;
}
|
|||||
| ↓ | Curl_unencode_gzip_write | 18 | 56 | 150 | lib/content_encoding.c |
CURLcode
Curl_unencode_gzip_write(struct connectdata *conn,
struct SingleRequest *k,
ssize_t nread)
{
z_stream *z = &k->z; /* zlib state structure */
/* Initialize zlib? */
if(k->zlib_init == ZLIB_UNINIT) {
z->zalloc = (alloc_func)Z_NULL;
z->zfree = (free_func)Z_NULL;
z->opaque = 0;
z->next_in = NULL;
z->avail_in = 0;
if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
/* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
if(inflateInit2(z, MAX_WBITS+32) != Z_OK) {
return process_zlib_error(conn, z);
}
k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
} else {
/* we must parse the gzip header ourselves */
if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
return process_zlib_error(conn, z);
}
k->zlib_init = ZLIB_INIT; /* Initial call state */
}
}
if(k->zlib_init == ZLIB_INIT_GZIP) {
/* Let zlib handle the gzip decompression entirely */
z->next_in = (Bytef *)k->str;
z->avail_in = (uInt)nread;
/* Now uncompress the data */
return inflate_stream(conn, k);
}
#ifndef OLD_ZLIB_SUPPORT
/* Support for old zlib versions is compiled away and we are running with
an old version, so return an error. */
return exit_zlib(z, &k->zlib_init, CURLE_FUNCTION_NOT_FOUND);
#else
/* This next mess is to get around the potential case where there isn't
* enough data passed in to skip over the gzip header. If that happens, we
* malloc a block and copy what we have then wait for the next call. If
* there still isn't enough (this is definitely a worst-case scenario), we
* make the block bigger, copy the next part in and keep waiting.
*
* This is only required with zlib versions < 1.2.0.4 as newer versions
* can handle the gzip header themselves.
*/
switch (k->zlib_init) {
/* Skip over gzip header? */
case ZLIB_INIT:
{
/* Initial call state */
ssize_t hlen;
switch (check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
case GZIP_OK:
z->next_in = (Bytef *)k->str + hlen;
z->avail_in = (uInt)(nread - hlen);
k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
break;
case GZIP_UNDERFLOW:
/* We need more data so we can find the end of the gzip header. It's
* possible that the memory block we malloc here will never be freed if
* the transfer abruptly aborts after this point. Since it's unlikely
* that circumstances will be right for this code path to be followed in
* the first place, and it's even more unlikely for a transfer to fail
* immediately afterwards, it should seldom be a problem.
*/
z->avail_in = (uInt)nread;
z->next_in = malloc(z->avail_in);
if(z->next_in == NULL) {
return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
}
memcpy(z->next_in, k->str, z->avail_in);
k->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */
/* We don't have any data to inflate yet */
return CURLE_OK;
case GZIP_BAD:
default:
return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
}
}
break;
case ZLIB_GZIP_HEADER:
{
/* Need more gzip header data state */
ssize_t hlen;
unsigned char *oldblock = z->next_in;
z->avail_in += nread;
z->next_in = realloc(z->next_in, z->avail_in);
if(z->next_in == NULL) {
free(oldblock);
return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
}
/* Append the new block of data to the previous one */
memcpy(z->next_in + z->avail_in - nread, k->str, nread);
switch (check_gzip_header(z->next_in, z->avail_in, &hlen)) {
case GZIP_OK:
/* This is the zlib stream data */
free(z->next_in);
/* Don't point into the malloced block since we just freed it */
z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
z->avail_in = (uInt)(z->avail_in - hlen);
k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
break;
case GZIP_UNDERFLOW:
/* We still don't have any data to inflate! */
return CURLE_OK;
case GZIP_BAD:
default:
free(z->next_in);
return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
}
}
break;
case ZLIB_GZIP_INFLATING:
default:
/* Inflating stream state */
z->next_in = (Bytef *)k->str;
z->avail_in = (uInt)nread;
break;
}
if(z->avail_in == 0) {
/* We don't have any data to inflate; wait until next time */
return CURLE_OK;
}
/* We've parsed the header, now uncompress the data */
return inflate_stream(conn, k);
#endif
}
|
|||||
| ↓ | curl_easy_pause | 12 | 38 | 91 | lib/easy.c |
CURLcode curl_easy_pause(CURL *curl, int action)
{
struct SessionHandle *data = (struct SessionHandle *)curl;
struct SingleRequest *k = &data->req;
CURLcode result = CURLE_OK;
/* first switch off both pause bits */
int newstate = k->keepon &~ (KEEP_READ_PAUSE| KEEP_WRITE_PAUSE);
/* set the new desired pause bits */
newstate |= ((action & CURLPAUSE_RECV)?KEEP_READ_PAUSE:0) |
((action & CURLPAUSE_SEND)?KEEP_WRITE_PAUSE:0);
/* put it back in the keepon */
k->keepon = newstate;
if(!(newstate & KEEP_READ_PAUSE) && data->state.tempwrite) {
/* we have a buffer for writing that we now seem to be able to deliver since
the receive pausing is lifted! */
/* get the pointer, type and length in local copies since the function may
return PAUSE again and then we'll get a new copy allocted and stored in
the tempwrite variables */
char *tempwrite = data->state.tempwrite;
char *freewrite = tempwrite; /* store this pointer to free it later */
size_t tempsize = data->state.tempwritesize;
int temptype = data->state.tempwritetype;
size_t chunklen;
/* clear tempwrite here just to make sure it gets cleared if there's no
further use of it, and make sure we don't clear it after the function
invoke as it may have been set to a new value by then */
data->state.tempwrite = NULL;
/* since the write callback API is define to never exceed
CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact
have more data than that in our buffer here, we must loop sending the
data in multiple calls until there's no data left or we get another
pause returned.
A tricky part is that the function we call will "buffer" the data
itself when it pauses on a particular buffer, so we may need to do some
extra trickery if we get a pause return here.
*/
do {
chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
result = Curl_client_write(data->state.current_conn,
temptype, tempwrite, chunklen);
if(result)
/* failures abort the loop at once */
break;
if(data->state.tempwrite && (tempsize - chunklen)) {
/* Ouch, the reading is again paused and the block we send is now
"cached". If this is the final chunk we can leave it like this, but
if we have more chunks that are cached after this, we need to free
the newly cached one and put back a version that is truly the entire
contents that is saved for later
*/
char *newptr;
/* note that tempsize is still the size as before the callback was
used, and thus the whole piece of data to keep */
newptr = realloc(data->state.tempwrite, tempsize);
if(!newptr) {
free(data->state.tempwrite); /* free old area */
data->state.tempwrite = NULL;
result = CURLE_OUT_OF_MEMORY;
/* tempwrite will be freed further down */
break;
}
data->state.tempwrite = newptr; /* store new pointer */
memcpy(newptr, tempwrite, tempsize);
data->state.tempwritesize = tempsize; /* store new size */
/* tempwrite will be freed further down */
break; /* go back to pausing until further notice */
}
else {
tempsize -= chunklen; /* left after the call above */
tempwrite += chunklen; /* advance the pointer */
}
} while((result == CURLE_OK) && tempsize);
free(freewrite); /* this is unconditionally no longer used */
}
return result;
}
|
|||||
| ↓ | ssh_getworkingpath | 12 | 35 | 63 | lib/ssh.c |
static CURLcode ssh_getworkingpath(struct connectdata *conn,
char *homedir, /* when SFTP is used */
char **path) /* returns the allocated
real path to work with */
{
struct SessionHandle *data = conn->data;
char *real_path = NULL;
char *working_path;
int working_path_len;
working_path = curl_easy_unescape(data, data->state.path, 0,
&working_path_len);
if(!working_path)
return CURLE_OUT_OF_MEMORY;
/* Check for /~/ , indicating relative to the user's home directory */
if(conn->protocol & PROT_SCP) {
real_path = malloc(working_path_len+1);
if(real_path == NULL) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
if((working_path_len > 1) && (working_path[1] == '~'))
/* It is referenced to the home directory, so strip the leading '/' */
memcpy(real_path, working_path+1, 1 + working_path_len-1);
else
memcpy(real_path, working_path, 1 + working_path_len);
}
else if(conn->protocol & PROT_SFTP) {
if((working_path_len > 1) && (working_path[1] == '~')) {
size_t homelen = strlen(homedir);
real_path = malloc(homelen + working_path_len + 1);
if(real_path == NULL) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
/* It is referenced to the home directory, so strip the
leading '/' */
memcpy(real_path, homedir, homelen);
real_path[homelen] = '/';
real_path[homelen+1] = '\0';
if(working_path_len > 3) {
memcpy(real_path+homelen+1, working_path + 3,
1 + working_path_len -3);
}
}
else {
real_path = malloc(working_path_len+1);
if(real_path == NULL) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
memcpy(real_path, working_path, 1+working_path_len);
}
}
free(working_path);
/* store the pointer for the caller to receive */
*path = real_path;
return CURLE_OK;
}
|
|||||
| ↓ | ossl_seed | 12 | 34 | 89 | lib/ssluse.c |
static int ossl_seed(struct SessionHandle *data)
{
char *buf = data->state.buffer; /* point to the big buffer */
int nread=0;
/* Q: should we add support for a random file name as a libcurl option?
A: Yes, it is here */
#ifndef RANDOM_FILE
/* if RANDOM_FILE isn't defined, we only perform this if an option tells
us to! */
if(data->set.ssl.random_file)
#define RANDOM_FILE "" /* doesn't matter won't be used */
#endif
{
/* let the option override the define */
nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
data->set.str[STRING_SSL_RANDOM_FILE]:
RANDOM_FILE),
RAND_LOAD_LENGTH);
if(seed_enough(nread))
return nread;
}
#if defined(HAVE_RAND_EGD)
/* only available in OpenSSL 0.9.5 and later */
/* EGD_SOCKET is set at configure time or not at all */
#ifndef EGD_SOCKET
/* If we don't have the define set, we only do this if the egd-option
is set */
if(data->set.str[STRING_SSL_EGDSOCKET])
#define EGD_SOCKET "" /* doesn't matter won't be used */
#endif
{
/* If there's an option and a define, the option overrides the
define */
int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
if(-1 != ret) {
nread += ret;
if(seed_enough(nread))
return nread;
}
}
#endif
/* If we get here, it means we need to seed the PRNG using a "silly"
approach! */
#ifdef HAVE_RAND_SCREEN
/* This one gets a random value by reading the currently shown screen */
RAND_screen();
nread = 100; /* just a value */
#else
{
int len;
char *area;
/* Changed call to RAND_seed to use the underlying RAND_add implementation
* directly. Do this in a loop, with the amount of additional entropy
* being dependent upon the algorithm used by Curl_FormBoundary(): N bytes
* of a 7-bit ascii set. -- Richard Gorton, March 11 2003.
*/
do {
area = Curl_FormBoundary();
if(!area)
return 3; /* out of memory */
len = (int)strlen(area);
RAND_add(area, len, (len >> 1));
free(area); /* now remove the random junk */
} while(!RAND_status());
}
#endif
/* generates a default path for the random seed file */
buf[0]=0; /* blank it first */
RAND_file_name(buf, BUFSIZE);
if(buf[0]) {
/* we got a file name to try */
nread += RAND_load_file(buf, RAND_LOAD_LENGTH);
if(seed_enough(nread))
return nread;
}
infof(data, "libcurl is now using a weak random seed!\n");
return nread;
}
|
|||||
| ↓ | sec_prot_internal | 12 | 32 | 49 | lib/security.c |
static int
sec_prot_internal(struct connectdata *conn, int level)
{
char *p;
unsigned int s = 1048576;
ssize_t nread;
if(!conn->sec_complete){
infof(conn->data, "No security data exchange has taken place.\n");
return -1;
}
if(level){
int code;
if(Curl_ftpsendf(conn, "PBSZ %u", s))
return -1;
if(Curl_GetFTPResponse(&nread, conn, &code))
return -1;
if(code/100 != 2){
failf(conn->data, "Failed to set protection buffer size.");
return -1;
}
conn->buffer_size = s;
p = strstr(conn->data->state.buffer, "PBSZ=");
if(p)
sscanf(p, "PBSZ=%u", &s);
if(s < conn->buffer_size)
conn->buffer_size = s;
}
if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"]))
return -1;
if(Curl_GetFTPResponse(&nread, conn, NULL))
return -1;
if(conn->data->state.buffer[0] != '2'){
failf(conn->data, "Failed to set protection level.");
return -1;
}
conn->data_prot = (enum protection_level)level;
if(level == prot_private)
conn->command_prot = (enum protection_level)level;
return 0;
}
|
|||||
| ↓ | convert_lineends | 12 | 32 | 74 | lib/sendf.c |
static size_t convert_lineends(struct SessionHandle *data,
char *startPtr, size_t size)
{
char *inPtr, *outPtr;
/* sanity check */
if((startPtr == NULL) || (size < 1)) {
return(size);
}
if(data->state.prev_block_had_trailing_cr == TRUE) {
/* The previous block of incoming data
had a trailing CR, which was turned into a LF. */
if(*startPtr == '\n') {
/* This block of incoming data starts with the
previous block's LF so get rid of it */
memmove(startPtr, startPtr+1, size-1);
size--;
/* and it wasn't a bare CR but a CRLF conversion instead */
data->state.crlf_conversions++;
}
data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
}
/* find 1st CR, if any */
inPtr = outPtr = memchr(startPtr, '\r', size);
if(inPtr) {
/* at least one CR, now look for CRLF */
while(inPtr < (startPtr+size-1)) {
/* note that it's size-1, so we'll never look past the last byte */
if(memcmp(inPtr, "\r\n", 2) == 0) {
/* CRLF found, bump past the CR and copy the NL */
inPtr++;
*outPtr = *inPtr;
/* keep track of how many CRLFs we converted */
data->state.crlf_conversions++;
}
else {
if(*inPtr == '\r') {
/* lone CR, move LF instead */
*outPtr = '\n';
}
else {
/* not a CRLF nor a CR, just copy whatever it is */
*outPtr = *inPtr;
}
}
outPtr++;
inPtr++;
} /* end of while loop */
if(inPtr < startPtr+size) {
/* handle last byte */
if(*inPtr == '\r') {
/* deal with a CR at the end of the buffer */
*outPtr = '\n'; /* copy a NL instead */
/* note that a CRLF might be split across two blocks */
data->state.prev_block_had_trailing_cr = TRUE;
}
else {
/* copy last byte */
*outPtr = *inPtr;
}
outPtr++;
inPtr++;
}
if(outPtr < startPtr+size) {
/* tidy up by null terminating the now shorter data */
*outPtr = '\0';
}
return(outPtr - startPtr);
}
return(size);
}
|
|||||
| ↓ | Curl_do | 12 | 27 | 70 | lib/url.c |
CURLcode Curl_do(struct connectdata **connp, bool *done)
{
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
/* setup and init stuff before DO starts, in preparing for the transfer */
do_init(conn);
if(conn->handler->do_it) {
/* generic protocol-specific function pointer set in curl_connect() */
result = conn->handler->do_it(conn, done);
/* This was formerly done in transfer.c, but we better do it here */
if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
/* This was a re-use of a connection and we got a write error in the
* DO-phase. Then we DISCONNECT this connection and have another attempt
* to CONNECT and then DO again! The retry cannot possibly find another
* connection to re-use, since we only keep one possible connection for
* each. */
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE; /* enforce close of this connection */
result = Curl_done(&conn, result, FALSE); /* we are so done with this */
/* conn may no longer be a good pointer */
/*
* According to bug report #1330310. We need to check for
* CURLE_SEND_ERROR here as well. I figure this could happen when the
* request failed on a FTP connection and thus Curl_done() itself tried
* to use the connection (again). Slight Lack of feedback in the report,
* but I don't think this extra check can do much harm.
*/
if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
bool async;
bool protocol_done = TRUE;
/* Now, redo the connect and get a new connection */
result = Curl_connect(data, connp, &async, &protocol_done);
if(CURLE_OK == result) {
/* We have connected or sent away a name resolve query fine */
conn = *connp; /* setup conn to again point to something nice */
if(async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
/* Resolved, continue with the connection */
result = Curl_async_resolved(conn, &protocol_done);
if(result)
return result;
}
/* ... finally back to actually retry the DO phase */
result = conn->handler->do_it(conn, done);
}
}
}
if((result == CURLE_OK) && *done)
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);
}
return result;
}
|
|||||
| ↓ | ftp_nextconnect | 12 | 27 | 59 | lib/ftp.c |
static CURLcode ftp_nextconnect(struct connectdata *conn)
{
struct SessionHandle *data=conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result = CURLE_OK;
/* the ftp struct is inited in ftp_connect() */
struct FTP *ftp = data->state.proto.ftp;
DEBUGF(infof(data, "DO-MORE phase starts\n"));
if(ftp->transfer <= FTPTRANSFER_INFO) {
/* a transfer is about to take place, or if not a file name was given
so we'll do a SIZE on it later and then we need the right TYPE first */
if(data->set.upload) {
result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
if(result)
return result;
}
else {
/* download */
ftp->downloadsize = -1; /* unknown as of yet */
result = ftp_range(conn);
if(result)
;
else if(data->set.ftp_list_only || !ftpc->file) {
/* The specified path ends with a slash, and therefore we think this
is a directory that is requested, use LIST. But before that we
need to set ASCII transfer mode. */
/* But only if a body transfer was requested. */
if(ftp->transfer == FTPTRANSFER_BODY) {
result = ftp_nb_type(conn, 1, FTP_LIST_TYPE);
if(result)
return result;
}
/* otherwise just fall through */
}
else {
result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
if(result)
return result;
}
}
result = ftp_easy_statemach(conn);
}
if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
/* no data to transfer. FIX: it feels like a kludge to have this here
too! */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* end of transfer */
DEBUGF(infof(data, "DO-MORE phase ends with %d\n", result));
return result;
}
|
|||||
| ↓ | Curl_http_connect | 12 | 27 | 64 | lib/http.c |
CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
{
struct SessionHandle *data;
CURLcode result;
data=conn->data;
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
/* If we are not using a proxy and we want a secure connection, perform SSL
* initialization & connection now. If using a proxy with https, then we
* must tell the proxy to CONNECT to the host we want to talk to. Only
* after the connect has occurred, can we start talking SSL
*/
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* either SSL over proxy, or explicitly asked for */
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name,
conn->remote_port);
if(CURLE_OK != result)
return result;
}
if(conn->bits.tunnel_connecting) {
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
}
if(!data->state.this_is_a_follow) {
/* this is not a followed location, get the original host name */
if(data->state.first_host)
/* Free to avoid leaking memory on multiple requests*/
free(data->state.first_host);
data->state.first_host = strdup(conn->host.name);
if(!data->state.first_host)
return CURLE_OUT_OF_MEMORY;
}
if(conn->protocol & PROT_HTTPS) {
/* perform SSL initialization */
if(data->state.used_interface == Curl_if_multi) {
result = https_connecting(conn, done);
if(result)
return result;
}
else {
/* BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
*done = TRUE;
}
}
else {
*done = TRUE;
}
return CURLE_OK;
}
|
|||||
| curl_easy_perform | 10 | 43 | 74 | lib/easy.c | |
| ↓ | printoption | 12 | 22 | 38 | lib/telnet.c |
static void printoption(struct SessionHandle *data,
const char *direction, int cmd, int option)
{
const char *fmt;
const char *opt;
if(data->set.verbose)
{
if(cmd == CURL_IAC)
{
if(CURL_TELCMD_OK(option))
infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
else
infof(data, "%s IAC %d\n", direction, option);
}
else
{
fmt = (cmd == CURL_WILL) ? "WILL" : (cmd == CURL_WONT) ? "WONT" :
(cmd == CURL_DO) ? "DO" : (cmd == CURL_DONT) ? "DONT" : 0;
if(fmt)
{
if(CURL_TELOPT_OK(option))
opt = CURL_TELOPT(option);
else if(option == CURL_TELOPT_EXOPL)
opt = "EXOPL";
else
opt = NULL;
if(opt)
infof(data, "%s %s %s\n", direction, fmt, opt);
else
infof(data, "%s %s %d\n", direction, fmt, option);
}
else
infof(data, "%s %d %d\n", direction, cmd, option);
}
}
}
|
|||||
| ↓ | curl_share_setopt | 21 | 48 | 103 | lib/share.c |
CURLSHcode
curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
{
struct Curl_share *share = (struct Curl_share *)sh;
va_list param;
int type;
curl_lock_function lockfunc;
curl_unlock_function unlockfunc;
void *ptr;
if(share->dirty)
/* don't allow setting options while one or more handles are already
using this share */
return CURLSHE_IN_USE;
va_start(param, option);
switch(option) {
case CURLSHOPT_SHARE:
/* this is a type this share will share */
type = va_arg(param, int);
share->specifier |= (1<
|
|||||
| ↓ | tftp_tx | 13 | 50 | 103 | lib/tftp.c |
static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
{
struct SessionHandle *data = state->conn->data;
int sbytes;
int rblock;
CURLcode res = CURLE_OK;
struct SingleRequest *k = &data->req;
switch(event) {
case TFTP_EVENT_ACK:
/* Ack the packet */
rblock = getrpacketblock(&state->rpacket);
if(rblock != state->block) {
/* This isn't the expected block. Log it and up the retry counter */
infof(data, "Received ACK for block %d, expecting %d\n",
rblock, state->block);
state->retries++;
/* Bail out if over the maximum */
if(state->retries>state->retry_max) {
failf(data, "tftp_tx: giving up waiting for block %d ack",
state->block);
res = CURLE_SEND_ERROR;
}
else {
/* Re-send the data packet */
sbytes = sendto(state->sockfd, (void *)&state->spacket,
4+state->sbytes, SEND_4TH_ARG,
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
res = CURLE_SEND_ERROR;
}
}
return res;
}
/* This is the expected packet. Reset the counters and send the next
block */
state->block++;
state->retries = 0;
setpacketevent(&state->spacket, TFTP_EVENT_DATA);
setpacketblock(&state->spacket, state->block);
if(state->block > 1 && state->sbytes < TFTP_BLOCKSIZE) {
state->state = TFTP_STATE_FIN;
return CURLE_OK;
}
res = Curl_fillreadbuffer(state->conn, TFTP_BLOCKSIZE, &state->sbytes);
if(res)
return res;
sbytes = sendto(state->sockfd, (void *)state->spacket.data,
4+state->sbytes, SEND_4TH_ARG,
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
return CURLE_SEND_ERROR;
}
/* Update the progress meter */
k->writebytecount += state->sbytes;
Curl_pgrsSetUploadCounter(data, k->writebytecount);
break;
case TFTP_EVENT_TIMEOUT:
/* Increment the retry counter and log the timeout */
state->retries++;
infof(data, "Timeout waiting for block %d ACK. "
" Retries = %d\n", state->retries);
/* Decide if we've had enough */
if(state->retries > state->retry_max) {
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
}
else {
/* Re-send the data packet */
sbytes = sendto(state->sockfd, (void *)&state->spacket,
4+state->sbytes, SEND_4TH_ARG,
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
return CURLE_SEND_ERROR;
}
/* since this was a re-send, we remain at the still byte position */
Curl_pgrsSetUploadCounter(data, k->writebytecount);
}
break;
case TFTP_EVENT_ERROR:
state->state = TFTP_STATE_FIN;
break;
default:
failf(data, "%s", "tftp_tx: internal error");
break;
}
return res;
}
|
|||||
| ↓ | Curl_base64_encode | 12 | 52 | 98 | lib/base64.c |
size_t Curl_base64_encode(struct SessionHandle *data,
const char *inp, size_t insize, char **outptr)
{
unsigned char ibuf[3];
unsigned char obuf[4];
int i;
int inputparts;
char *output;
char *base64data;
#ifdef CURL_DOES_CONVERSIONS
char *convbuf = NULL;
#endif
const char *indata = inp;
*outptr = NULL; /* set to NULL in case of failure before we reach the end */
if(0 == insize)
insize = strlen(indata);
base64data = output = malloc(insize*4/3+4);
if(NULL == output)
return 0;
#ifdef CURL_DOES_CONVERSIONS
/*
* The base64 data needs to be created using the network encoding
* not the host encoding. And we can't change the actual input
* so we copy it to a buffer, translate it, and use that instead.
*/
if(data) {
convbuf = malloc(insize);
if(!convbuf) {
free(output);
return 0;
}
memcpy(convbuf, indata, insize);
if(CURLE_OK != Curl_convert_to_network(data, convbuf, insize)) {
free(convbuf);
free(output);
return 0;
}
indata = convbuf; /* switch to the converted buffer */
}
#else
(void)data;
#endif
while(insize > 0) {
for (i = inputparts = 0; i < 3; i++) {
if(insize > 0) {
inputparts++;
ibuf[i] = *indata;
indata++;
insize--;
}
else
ibuf[i] = 0;
}
obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);
obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
((ibuf[1] & 0xF0) >> 4));
obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
((ibuf[2] & 0xC0) >> 6));
obuf[3] = (unsigned char) (ibuf[2] & 0x3F);
switch(inputparts) {
case 1: /* only one byte read */
snprintf(output, 5, "%c%c==",
table64[obuf[0]],
table64[obuf[1]]);
break;
case 2: /* two bytes read */
snprintf(output, 5, "%c%c%c=",
table64[obuf[0]],
table64[obuf[1]],
table64[obuf[2]]);
break;
default:
snprintf(output, 5, "%c%c%c%c",
table64[obuf[0]],
table64[obuf[1]],
table64[obuf[2]],
table64[obuf[3]] );
break;
}
output += 4;
}
*output=0;
*outptr = base64data; /* make it return the actual data memory */
#ifdef CURL_DOES_CONVERSIONS
if(data)
free(convbuf);
#endif
return strlen(base64data); /* return the length of the new data */
}
|
|||||
| ↓ | nss_load_cert | 11 | 61 | 116 | lib/nss.c |
static int
nss_load_cert(const char *filename, PRBool cacert)
{
#ifdef HAVE_PK11_CREATEGENERICOBJECT
CK_SLOT_ID slotID;
PK11SlotInfo * slot = NULL;
PK11GenericObject *rv;
CK_ATTRIBUTE *attrs;
CK_ATTRIBUTE theTemplate[20];
CK_BBOOL cktrue = CK_TRUE;
CK_BBOOL ckfalse = CK_FALSE;
CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
char *slotname = NULL;
#endif
CERTCertificate *cert;
char *nickname = NULL;
char *n = NULL;
/* If there is no slash in the filename it is assumed to be a regular
* NSS nickname.
*/
if(is_file(filename)) {
n = strrchr(filename, '/');
if(n)
n++;
if(!mod)
return 1;
}
else {
/* A nickname from the NSS internal database */
if(cacert)
return 0; /* You can't specify an NSS CA nickname this way */
nickname = strdup(filename);
goto done;
}
#ifdef HAVE_PK11_CREATEGENERICOBJECT
attrs = theTemplate;
/* All CA and trust objects go into slot 0. Other slots are used
* for storing certificates. With each new user certificate we increment
* the slot count. We only support 1 user certificate right now.
*/
if(cacert)
slotID = 0;
else
slotID = 1;
slotname = malloc(SLOTSIZE);
nickname = malloc(PATH_MAX);
snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID);
snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", slotID, n);
slot = PK11_FindSlotByName(slotname);
if(!slot) {
free(slotname);
free(nickname);
return 0;
}
PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) );
attrs++;
PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) );
attrs++;
PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename,
strlen(filename)+1);
attrs++;
if(cacert) {
PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) );
}
else {
PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) );
}
attrs++;
/* This load the certificate in our PEM module into the appropriate
* slot.
*/
rv = PK11_CreateGenericObject(slot, theTemplate, 4, PR_FALSE /* isPerm */);
PK11_FreeSlot(slot);
free(slotname);
if(rv == NULL) {
free(nickname);
return 0;
}
#else
/* We don't have PK11_CreateGenericObject but a file-based cert was passed
* in. We need to fail.
*/
return 0;
#endif
done:
/* Double-check that the certificate or nickname requested exists in
* either the token or the NSS certificate database.
*/
if(!cacert) {
cert = PK11_FindCertFromNickname((char *)nickname, NULL);
/* An invalid nickname was passed in */
if(cert == NULL) {
free(nickname);
PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0);
return 0;
}
CERT_DestroyCertificate(cert);
}
free(nickname);
return 1;
}
|
|||||
| ↓ | Curl_is_connected | 11 | 43 | 84 | lib/connect.c |
CURLcode Curl_is_connected(struct connectdata *conn,
int sockindex,
bool *connected)
{
int rc;
struct SessionHandle *data = conn->data;
CURLcode code = CURLE_OK;
curl_socket_t sockfd = conn->sock[sockindex];
long allow = DEFAULT_CONNECT_TIMEOUT;
DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
*connected = FALSE; /* a very negative world view is best */
if(conn->bits.tcpconnect) {
/* we are connected already! */
long allow_total = 0;
/* subtract the most strict timeout of the ones */
if(data->set.timeout)
allow_total = data->set.timeout;
Curl_expire(data, allow_total);
*connected = TRUE;
return CURLE_OK;
}
/* figure out how long time we have left to connect */
allow = Curl_timeleft(conn, NULL, TRUE);
if(allow < 0) {
/* time-out, bail out, go home */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
Curl_expire(data, allow);
/* check for connect without timeout as we want to return immediately */
rc = waitconnect(sockfd, 0);
if(WAITCONN_CONNECTED == rc) {
int error;
if(verifyconnect(sockfd, &error)) {
/* we are connected, awesome! */
*connected = TRUE;
return CURLE_OK;
}
/* nope, not connected for real */
data->state.os_errno = error;
infof(data, "Connection failed\n");
if(trynextip(conn, sockindex, connected)) {
failf(data, "Failed connect to %s:%d; %s",
conn->host.name, conn->port, Curl_strerror(conn, error));
code = CURLE_COULDNT_CONNECT;
}
}
else if(WAITCONN_TIMEOUT != rc) {
int error = 0;
/* nope, not connected */
if(WAITCONN_FDSET_ERROR == rc) {
(void)verifyconnect(sockfd, &error);
data->state.os_errno = error;
infof(data, "%s\n",Curl_strerror(conn,error));
}
else
infof(data, "Connection failed\n");
if(trynextip(conn, sockindex, connected)) {
error = SOCKERRNO;
data->state.os_errno = error;
failf(data, "Failed connect to %s:%d; %s",
conn->host.name, conn->port, Curl_strerror(conn, error));
code = CURLE_COULDNT_CONNECT;
}
}
/*
* If the connection failed here, we should attempt to connect to the "next
* address" for the given host.
*/
return code;
}
|
|||||
| ↓ | Curl_splay | 11 | 42 | 54 | lib/splay.c |
struct Curl_tree *Curl_splay(struct timeval i,
struct Curl_tree *t)
{
struct Curl_tree N, *l, *r, *y;
long comp;
if(t == NULL)
return t;
N.smaller = N.larger = NULL;
l = r = &N;
for (;;) {
comp = compare(i, t->key);
if(comp < 0) {
if(t->smaller == NULL)
break;
if(compare(i, t->smaller->key) < 0) {
y = t->smaller; /* rotate smaller */
t->smaller = y->larger;
y->larger = t;
t = y;
if(t->smaller == NULL)
break;
}
r->smaller = t; /* link smaller */
r = t;
t = t->smaller;
}
else if(comp > 0) {
if(t->larger == NULL)
break;
if(compare(i, t->larger->key) > 0) {
y = t->larger; /* rotate larger */
t->larger = y->smaller;
y->smaller = t;
t = y;
if(t->larger == NULL)
break;
}
l->larger = t; /* link larger */
l = t;
t = t->larger;
}
else
break;
}
l->larger = t->smaller; /* assemble */
r->smaller = t->larger;
t->smaller = N.larger;
t->larger = N.smaller;
return t;
}
|
|||||
| ↓ | Curl_expire | 11 | 34 | 73 | lib/multi.c |
void Curl_expire(struct SessionHandle *data, long milli)
{
struct Curl_multi *multi = data->multi;
struct timeval *nowp = &data->state.expiretime;
int rc;
/* this is only interesting for multi-interface using libcurl, and only
while there is still a multi interface struct remaining! */
if(!multi)
return;
if(!milli) {
/* No timeout, clear the time data. */
if(nowp->tv_sec || nowp->tv_usec) {
/* Since this is an cleared time, we must remove the previous entry from
the splay tree */
rc = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode,
&multi->timetree);
if(rc)
infof(data, "Internal error clearing splay node = %d\n", rc);
infof(data, "Expire cleared\n");
nowp->tv_sec = 0;
nowp->tv_usec = 0;
}
}
else {
struct timeval set;
int rest;
set = Curl_tvnow();
set.tv_sec += milli/1000;
set.tv_usec += (milli%1000)*1000;
rest = (int)(set.tv_usec - 1000000);
if(rest > 0) {
/* bigger than a full microsec */
set.tv_sec++;
set.tv_usec -= 1000000;
}
if(nowp->tv_sec || nowp->tv_usec) {
/* This means that the struct is added as a node in the splay tree.
Compare if the new time is earlier, and only remove-old/add-new if it
is. */
long diff = curlx_tvdiff(set, *nowp);
if(diff > 0)
/* the new expire time was later so we don't change this */
return;
/* Since this is an updated time, we must remove the previous entry from
the splay tree first and then re-add the new value */
rc = Curl_splayremovebyaddr(multi->timetree,
&data->state.timenode,
&multi->timetree);
if(rc)
infof(data, "Internal error removing splay node = %d\n", rc);
}
*nowp = set;
#if 0
infof(data, "Expire at %ld / %ld (%ldms) %p\n",
(long)nowp->tv_sec, (long)nowp->tv_usec, milli, data);
#endif
data->state.timenode.payload = data;
multi->timetree = Curl_splayinsert(*nowp,
multi->timetree,
&data->state.timenode);
}
#if 0
Curl_splayprint(multi->timetree, 0, TRUE);
#endif
}
|
|||||
| ↓ | ftp_range | 11 | 29 | 50 | lib/ftp.c |
static CURLcode ftp_range(struct connectdata *conn)
{
curl_off_t from, to;
curl_off_t totalsize=-1;
char *ptr;
char *ptr2;
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
if(data->state.use_range && data->state.range) {
from=curlx_strtoofft(data->state.range, &ptr, 0);
while(ptr && *ptr && (ISSPACE(*ptr) || (*ptr=='-')))
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
if(ptr == ptr2) {
/* we didn't get any digit */
to=-1;
}
if((-1 == to) && (from>=0)) {
/* X - */
data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
from));
}
else if(from < 0) {
/* -Y */
totalsize = -from;
data->req.maxdownload = -from;
data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
totalsize));
}
else {
/* X-Y */
totalsize = to-from;
data->req.maxdownload = totalsize+1; /* include last byte */
data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
" getting %" FORMAT_OFF_T " bytes\n",
from, data->req.maxdownload));
}
DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
" to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
from, to, data->req.maxdownload));
ftpc->dont_check = TRUE; /* dont check for successful transfer */
}
else
data->req.maxdownload = -1;
return CURLE_OK;
}
|
|||||
| ↓ | Curl_copy_header_value | 11 | 29 | 47 | lib/http.c |
char *Curl_copy_header_value(const char *h)
{
const char *start;
const char *end;
char *value;
size_t len;
DEBUGASSERT(h);
/* Find the end of the header name */
while (*h && (*h != ':'))
++h;
if (*h)
/* Skip over colon */
++h;
/* Find the first non-space letter */
for(start=h;
*start && ISSPACE(*start);
start++)
; /* empty loop */
/* data is in the host encoding so
use '\r' and '\n' instead of 0x0d and 0x0a */
end = strchr(start, '\r');
if(!end)
end = strchr(start, '\n');
if(!end)
end = strchr(start, '\0');
/* skip all trailing space letters */
for(; ISSPACE(*end) && (end > start); end--)
; /* empty loop */
/* get length of the type */
len = end-start+1;
value = malloc(len + 1);
if(!value)
return NULL;
memcpy(value, start, len);
value[len] = 0; /* zero terminate */
return value;
}
|
|||||
| ↓ | parse_url_userpass | 11 | 27 | 72 | lib/url.c |
static CURLcode parse_url_userpass(struct SessionHandle *data,
struct connectdata *conn,
char *user, char *passwd)
{
/* At this point, we're hoping all the other special cases have
* been taken care of, so conn->host.name is at most
* [user[:password]]@]hostname
*
* We need somewhere to put the embedded details, so do that first.
*/
user[0] =0; /* to make everything well-defined */
passwd[0]=0;
if(conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP|PROT_SFTP)) {
/* This is a FTP, HTTP, SCP or SFTP URL, we will now try to extract the
* possible user+password pair in a string like:
* ftp://user:password@ftp.my.site:8021/README */
char *ptr=strchr(conn->host.name, '@');
char *userpass = conn->host.name;
if(ptr != NULL) {
/* there's a user+password given here, to the left of the @ */
conn->host.name = ++ptr;
/* So the hostname is sane. Only bother interpreting the
* results if we could care. It could still be wasted
* work because it might be overtaken by the programmatically
* set user/passwd, but doing that first adds more cases here :-(
*/
if(data->set.use_netrc != CURL_NETRC_REQUIRED) {
/* We could use the one in the URL */
conn->bits.user_passwd = 1; /* enable user+password */
if(*userpass != ':') {
/* the name is given, get user+password */
sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
user, passwd);
}
else
/* no name given, get the password only */
sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
if(user[0]) {
char *newname=curl_easy_unescape(data, user, 0, NULL);
if(!newname)
return CURLE_OUT_OF_MEMORY;
if(strlen(newname) < MAX_CURL_USER_LENGTH)
strcpy(user, newname);
/* if the new name is longer than accepted, then just use
the unconverted name, it'll be wrong but what the heck */
free(newname);
}
if(passwd[0]) {
/* we have a password found in the URL, decode it! */
char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
if(!newpasswd)
return CURLE_OUT_OF_MEMORY;
if(strlen(newpasswd) < MAX_CURL_PASSWORD_LENGTH)
strcpy(passwd, newpasswd);
free(newpasswd);
}
}
}
}
return CURLE_OK;
}
|
|||||
| ↓ | file_range | 11 | 27 | 48 | lib/file.c |
static CURLcode file_range(struct connectdata *conn)
{
curl_off_t from, to;
curl_off_t totalsize=-1;
char *ptr;
char *ptr2;
struct SessionHandle *data = conn->data;
if(data->state.use_range && data->state.range) {
from=curlx_strtoofft(data->state.range, &ptr, 0);
while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
if(ptr == ptr2) {
/* we didn't get any digit */
to=-1;
}
if((-1 == to) && (from>=0)) {
/* X - */
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
from));
}
else if(from < 0) {
/* -Y */
totalsize = -from;
data->req.maxdownload = -from;
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
totalsize));
}
else {
/* X-Y */
totalsize = to-from;
data->req.maxdownload = totalsize+1; /* include last byte */
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
" getting %" FORMAT_OFF_T " bytes\n",
from, data->req.maxdownload));
}
DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
" to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
from, to, data->req.maxdownload));
}
else
data->req.maxdownload = -1;
return CURLE_OK;
}
|
|||||
| ↓ | Curl_clone_ssl_config | 11 | 25 | 41 | lib/sslgen.c |
bool
Curl_clone_ssl_config(struct ssl_config_data *source,
struct ssl_config_data *dest)
{
dest->sessionid = source->sessionid;
dest->verifyhost = source->verifyhost;
dest->verifypeer = source->verifypeer;
dest->version = source->version;
if(source->CAfile) {
dest->CAfile = strdup(source->CAfile);
if(!dest->CAfile)
return FALSE;
}
if(source->CApath) {
dest->CApath = strdup(source->CApath);
if(!dest->CApath)
return FALSE;
}
if(source->cipher_list) {
dest->cipher_list = strdup(source->cipher_list);
if(!dest->cipher_list)
return FALSE;
}
if(source->egdsocket) {
dest->egdsocket = strdup(source->egdsocket);
if(!dest->egdsocket)
return FALSE;
}
if(source->random_file) {
dest->random_file = strdup(source->random_file);
if(!dest->random_file)
return FALSE;
}
return TRUE;
}
|
|||||
| ↓ | unescape_elements | 11 | 25 | 33 | lib/ldap.c |
static bool unescape_elements (void *data, LDAPURLDesc *ludp)
{
int i;
if(ludp->lud_filter) {
ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL);
if(!ludp->lud_filter)
return (FALSE);
}
for (i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) {
ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL);
if(!ludp->lud_attrs[i])
return (FALSE);
}
for (i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) {
ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL);
if(!ludp->lud_exts[i])
return (FALSE);
}
if(ludp->lud_dn) {
char *dn = ludp->lud_dn;
char *new_dn = curl_easy_unescape(data, dn, 0, NULL);
free(dn);
ludp->lud_dn = new_dn;
if(!new_dn)
return (FALSE);
}
return (TRUE);
}
|
|||||
| ↓ | ssl_tls_trace | 11 | 21 | 38 | lib/ssluse.c |
static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, const SSL *ssl,
struct connectdata *conn)
{
struct SessionHandle *data;
const char *msg_name, *tls_rt_name;
char ssl_buf[1024];
int ver, msg_type, txt_len;
if(!conn || !conn->data || !conn->data->set.fdebug ||
(direction != 0 && direction != 1))
return;
data = conn->data;
ssl_ver >>= 8;
ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
/* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
* always pass-up content-type as 0. But the interesting message-type
* is at 'buf[0]'.
*/
if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
tls_rt_name = tls_rt_type(content_type);
else
tls_rt_name = "";
msg_type = *(char*)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
ver, tls_rt_name, msg_name, msg_type);
Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
(void) ssl;
}
|
|||||
| ↓ | add_custom_headers | 11 | 18 | 39 | lib/http.c |
static CURLcode add_custom_headers(struct connectdata *conn,
send_buffer *req_buffer)
{
char *ptr;
struct curl_slist *headers=conn->data->set.headers;
while(headers) {
ptr = strchr(headers->data, ':');
if(ptr) {
/* we require a colon for this to be a true header */
ptr++; /* pass the colon */
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
/* only send this if the contents was non-blank */
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
curl_strnequal("Host:", headers->data, 5))
;
else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
curl_strnequal("Content-Type:", headers->data,
strlen("Content-Type:")))
;
else {
CURLcode result = add_bufferf(req_buffer, "%s\r\n", headers->data);
if(result)
return result;
}
}
}
headers = headers->next;
}
return CURLE_OK;
}
|
|||||
| telrcv | 26 | 85 | 169 | lib/telnet.c | |
| ftp_state_quote | 16 | 36 | 70 | lib/ftp.c | |
| tftp_send_first | 14 | 45 | 83 | lib/tftp.c | |
| ossl_connect_step2 | 11 | 34 | 93 | lib/ssluse.c | |
| get_netscape_format | 10 | 8 | 23 | lib/cookie.c | |
| init_resolve_thread | 10 | 56 | 114 | lib/hostthre.c | |
| curl_easy_perform | 10 | 43 | 74 | lib/easy.c | |
| curl_multi_cleanup | 10 | 40 | 66 | lib/multi.c | |
| X509V3_ext | 10 | 37 | 55 | lib/ssluse.c | |
| Curl_getaddrinfo | 4 | 16 | 34 | lib/hostthre.c | |
| Curl_fillreadbuffer | 10 | 36 | 73 | lib/transfer.c | |
| ares_waitperform | 10 | 34 | 49 | lib/hostares.c | |
| curl_easy_unescape | 10 | 30 | 54 | lib/escape.c | |
| AddFormData | 10 | 28 | 57 | lib/formdata.c | |
| Curl_http_done | 10 | 28 | 55 | lib/http.c | |
| inet_pton4 | 10 | 28 | 41 | lib/inet_pton.c | |
| Curl_ch_connc | 10 | 27 | 54 | lib/url.c | |
| get_char | 10 | 18 | 34 | lib/strtoofft.c | |
| curl_formfree | 1 | 1 | 5 | lib/formdata.c | |
| Curl_ssl_config_matches | 9 | 3 | 16 | lib/sslgen.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| Curl_base64_decode | 9 | 35 | 53 | lib/base64.c | |
| curl_easy_escape | 9 | 33 | 59 | lib/escape.c | |
| ftp_connect | 9 | 33 | 77 | lib/ftp.c | |
| ftp_state_post_retr_size | 9 | 33 | 81 | lib/ftp.c | |
| Curl_qsossl_shutdown | 9 | 32 | 55 | lib/qssl.c | |
| tftp_set_timeouts | 9 | 32 | 70 | lib/tftp.c | |
| wait_ms | 9 | 31 | 47 | lib/select.c | |
| Curl_cookie_output | 9 | 27 | 49 | lib/cookie.c | |
| curl_formget | 1 | 4 | 8 | lib/formdata.c | |
| Curl_splayremovebyaddr | 9 | 27 | 66 | lib/splay.c | |
| ftp_state_user_resp | 9 | 25 | 54 | lib/ftp.c | |
| handshake | 9 | 25 | 48 | lib/gtls.c | |
| Curl_readrewind | 9 | 24 | 58 | lib/transfer.c | |
| ftp_state_size_resp | 9 | 23 | 40 | lib/ftp.c | |
| add_buffer | 9 | 22 | 51 | lib/http.c | |
| Curl_hash_init | 9 | 21 | 35 | lib/hash.c | |
| unescape_word | 9 | 20 | 32 | lib/dict.c | |
| Curl_protocol_connect | 9 | 19 | 51 | lib/url.c | |
| cert_stuff | 9 | 17 | 28 | lib/nss.c | |
| hostmatch | 9 | 16 | 26 | lib/ssluse.c | |
| countcheck | 9 | 12 | 27 | lib/memdebug.c | |
| flush_cookies | 9 | 12 | 30 | lib/url.c | |
| Curl_getinfo | 45 | 98 | 158 | lib/getinfo.c | |
| Curl_ossl_shutdown | 14 | 44 | 93 | lib/ssluse.c | |
| BadCertHandler | 12 | 37 | 54 | lib/nss.c | |
| showit | 11 | 29 | 68 | lib/sendf.c | |
| Curl_timeleft | 11 | 25 | 49 | lib/connect.c | |
| Curl_getaddrinfo | 4 | 16 | 34 | lib/hostthre.c | |
| Curl_he2ai | 9 | 40 | 79 | lib/hostip.c | |
| Curl_connecthost | 8 | 42 | 89 | lib/connect.c | |
| Curl_output_negotiate | 8 | 38 | 60 | lib/http_negotiate.c | |
| check_telnet_options | 8 | 36 | 59 | lib/telnet.c | |
| SelectClientCert | 8 | 31 | 51 | lib/nss.c | |
| curl_multi_fdset | 8 | 29 | 49 | lib/multi.c | |
| add_closure | 8 | 29 | 52 | lib/multi.c | |
| Curl_convert_from_utf8 | 8 | 28 | 60 | lib/easy.c | |
| Curl_ssl_addsessionid | 8 | 27 | 50 | lib/sslgen.c | |
| sec_send | 8 | 26 | 40 | lib/security.c | |
| Curl_llist_move | 8 | 25 | 48 | lib/llist.c | |
| Curl_splayprint | 8 | 23 | 33 | lib/splay.c | |
| ftp_easy_statemach | 8 | 23 | 37 | lib/ftp.c | |
| Curl_multi_dump | 8 | 23 | 32 | lib/multi.c | |
| Curl_cmalloc | 8 | 23 | 70 | lib/easy.c | |
| Curl_sec_read | 8 | 23 | 34 | lib/security.c | |
| Curl_compareheader | 8 | 23 | 48 | lib/http.c | |
| verifyconnect | 8 | 22 | 63 | lib/connect.c | |
| Curl_FormReader | 8 | 22 | 51 | lib/formdata.c | |
| _ldap_free_urldesc | 8 | 20 | 26 | lib/ldap.c | |
| Curl_ssl_getsessionid | 8 | 20 | 32 | lib/sslgen.c | |
| checkPendPipeline | 8 | 20 | 35 | lib/multi.c | |
| max5data | 8 | 17 | 56 | lib/progress.c | |
| curl_share_cleanup | 8 | 17 | 32 | lib/share.c | |
| freecookie | 8 | 15 | 19 | lib/cookie.c | |
| Curl_strtok_r | 8 | 14 | 35 | lib/strtok.c | |
| curl_global_init_mem | 8 | 13 | 26 | lib/easy.c | |
| set_local_option | 18 | 26 | 81 | lib/telnet.c | |
| set_remote_option | 18 | 26 | 81 | lib/telnet.c | |
| tftp_rx | 10 | 37 | 80 | lib/tftp.c | |
| suboption | 9 | 46 | 69 | lib/telnet.c | |
| Curl_gtls_shutdown | 9 | 31 | 63 | lib/gtls.c | |
| ftp_setup_connection | 9 | 25 | 59 | lib/ftp.c | |
| send_telnet_data | 8 | 22 | 37 | lib/telnet.c | |
| Curl_hash_alloc | 7 | 9 | 23 | lib/hash.c | |
| load_file | 7 | 9 | 20 | lib/gtls.c | |
| cert_hostcheck | 7 | 7 | 14 | lib/ssluse.c | |
| Curl_open | 7 | 67 | 133 | lib/url.c | |
| blockread_all | 7 | 31 | 50 | lib/socks.c | |
| nss_Init_Tokens | 7 | 29 | 48 | lib/nss.c | |
| ssh_connect | 7 | 28 | 60 | lib/ssh.c | |
| Curl_splaygetbest | 7 | 26 | 55 | lib/splay.c | |
| curl_multi_perform | 7 | 26 | 48 | lib/multi.c | |
| Curl_convert_from_network | 7 | 25 | 56 | lib/easy.c | |
| Curl_convert_to_network | 7 | 25 | 55 | lib/easy.c | |
| ConnectionKillOne | 7 | 23 | 40 | lib/url.c | |
| Curl_cookie_list | 7 | 22 | 35 | lib/cookie.c | |
| Curl_digest_cleanup_one | 7 | 21 | 30 | lib/http_digest.c | |
| checkhttpprefix | 7 | 20 | 39 | lib/transfer.c | |
| Curl_gtls_recv | 7 | 19 | 39 | lib/gtls.c | |
| get_gss_name | 7 | 19 | 38 | lib/http_negotiate.c | |
| Curl_mk_connc | 7 | 18 | 33 | lib/url.c | |
| Curl_single_getsock | 7 | 17 | 42 | lib/transfer.c | |
| ftp_state_type_resp | 7 | 16 | 29 | lib/ftp.c | |
| ntlm_sspi_cleanup | 7 | 15 | 19 | lib/http_ntlm.c | |
| setup_range | 7 | 15 | 26 | lib/url.c | |
| curl_docalloc | 7 | 15 | 25 | lib/memdebug.c | |
| readfromfile | 7 | 15 | 28 | lib/formdata.c | |
| Curl_ntlm_global_init | 7 | 15 | 31 | lib/http_ntlm.c | |
| curl_domalloc | 7 | 14 | 23 | lib/memdebug.c | |
| Curl_getconnectinfo | 7 | 13 | 36 | lib/connect.c | |
| Curl_connect | 7 | 13 | 39 | lib/url.c | |
| GetEnv | 7 | 13 | 23 | lib/getenv.c | |
| block_read | 7 | 13 | 18 | lib/security.c | |
| do_file_type | 5 | 7 | 10 | lib/gtls.c | |
| Curl_speedcheck | 7 | 10 | 36 | lib/speedcheck.c | |
| ConnectPlease | 11 | 25 | 63 | lib/url.c | |
| Curl_debug | 10 | 19 | 35 | lib/sendf.c | |
| Curl_getaddrinfo | 4 | 16 | 34 | lib/hostthre.c | |
| Curl_read_plain | 6 | 9 | 23 | lib/sendf.c | |
| Curl_nbftpsendf | 6 | 37 | 71 | lib/ftp.c | |
| IsPipeliningPossible | 6 | 3 | 10 | lib/url.c | |
| Curl_if2ip | 6 | 29 | 53 | lib/if2ip.c | |
| Curl_ftpsendf | 6 | 29 | 57 | lib/ftp.c | |
| Curl_sendf | 6 | 27 | 44 | lib/sendf.c | |
| Curl_splayinsert | 6 | 25 | 51 | lib/splay.c | |
| AddHttpPost | 6 | 24 | 48 | lib/formdata.c | |
| http_output_basic | 6 | 23 | 36 | lib/http.c | |
| Curl_sec_read_msg | 6 | 22 | 35 | lib/security.c | |
| Curl_hash_print | 6 | 21 | 29 | lib/hash.c | |
| addrinfo_callback | 6 | 20 | 52 | lib/hostasyn.c | |
| ftp_multi_statemach | 6 | 20 | 33 | lib/ftp.c | |
| ftp_state_stor_resp | 6 | 20 | 44 | lib/ftp.c | |
| Curl_cookie_clearsess | 6 | 18 | 29 | lib/cookie.c | |
| tld_check_name | 6 | 18 | 35 | lib/url.c | |
| curl_multi_info_read | 6 | 18 | 31 | lib/multi.c | |
| sec_get_data | 6 | 18 | 24 | lib/security.c | |
| ConnectionStore | 6 | 17 | 39 | lib/url.c | |
| Curl_strlcat | 6 | 17 | 26 | lib/strequal.c | |
| Curl_write | 6 | 16 | 26 | lib/sendf.c | |
| Curl_llist_remove | 6 | 15 | 28 | lib/llist.c | |
| ftp_dophase_done | 6 | 15 | 28 | lib/ftp.c | |
| ContentTypeForFilename | 6 | 15 | 42 | lib/formdata.c | |
| Curl_formconvert | 6 | 14 | 22 | lib/formdata.c | |
| curl_dorealloc | 6 | 14 | 25 | lib/memdebug.c | |
| curl_strnequal | 6 | 12 | 23 | lib/strequal.c | |
| basename | 6 | 12 | 20 | lib/formdata.c | |
| str2scope | 6 | 11 | 14 | lib/ldap.c | |
| ftp_state_loggedin | 6 | 11 | 40 | lib/ftp.c | |
| Curl_retry_request | 6 | 11 | 33 | lib/transfer.c | |
| block_write | 6 | 11 | 16 | lib/security.c | |
| multi_getsock | 22 | 11 | 63 | lib/multi.c | |
| AllowServerConnect | 6 | 26 | 54 | lib/ftp.c | |
| Curl_getaddrinfo | 4 | 16 | 34 | lib/hostthre.c | |
| ftp_state_rest_resp | 6 | 16 | 35 | lib/ftp.c | |
| Curl_qsossl_check_cxn | 5 | 9 | 21 | lib/qssl.c | |
| remove_entry_if_stale | 5 | 9 | 22 | lib/hostip.c | |
| Curl_hostcache_prune | 5 | 9 | 22 | lib/hostip.c | |
| destroy_thread_sync_data | 5 | 9 | 13 | lib/hostthre.c | |
| set_userpass | 5 | 8 | 21 | lib/url.c | |
| send_plain | 5 | 8 | 30 | lib/sendf.c | |
| addHandleToSendOrPendPipeline | 5 | 8 | 19 | lib/multi.c | |
| sftp_done | 5 | 7 | 16 | lib/ssh.c | |
| do_file_type | 5 | 7 | 10 | lib/gtls.c | |
| multi_conn_using | 5 | 7 | 15 | lib/multi.c | |
| tls_rt_type | 5 | 5 | 9 | lib/ssluse.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| reuse_conn | 5 | 33 | 56 | lib/url.c | |
| display_conn_info | 5 | 30 | 45 | lib/nss.c | |
| tftp_connect | 5 | 26 | 60 | lib/tftp.c | |
| mk_lm_hash | 5 | 23 | 47 | lib/http_ntlm.c | |
| curl_multi_init | 5 | 22 | 40 | lib/multi.c | |
| Curl_pretransfer | 5 | 22 | 48 | lib/transfer.c | |
| setup_connection_internals | 5 | 20 | 38 | lib/url.c | |
| alloc_addbyter | 5 | 20 | 33 | lib/mprintf.c | |
| ossl_connect_step3 | 5 | 19 | 59 | lib/ssluse.c | |
| Curl_splayremove | 5 | 19 | 42 | lib/splay.c | |
| Curl_nss_recv | 5 | 19 | 34 | lib/nss.c | |
| Curl_hash_add | 5 | 18 | 33 | lib/hash.c | |
| curl_maprintf | 5 | 18 | 26 | lib/mprintf.c | |
| ftp_perform | 5 | 17 | 38 | lib/ftp.c | |
| ftp_disconnect | 5 | 16 | 35 | lib/ftp.c | |
| ftp_sendquote | 5 | 16 | 28 | lib/ftp.c | |
| memdup | 5 | 16 | 28 | lib/formdata.c | |
| init_thread_sync_data | 5 | 16 | 40 | lib/hostthre.c | |
| curl_easy_send | 5 | 16 | 27 | lib/easy.c | |
| ftp_state_cwd | 5 | 15 | 35 | lib/ftp.c | |
| netware_init | 1 | 1 | 4 | lib/nwos.c | |
| Curl_sec_write | 5 | 15 | 22 | lib/security.c | |
| pickoneauth | 5 | 15 | 25 | lib/http.c | |
| AddFormInfo | 5 | 15 | 29 | lib/formdata.c | |
| checkday | 5 | 15 | 18 | lib/parsedate.c | |
| ftp_state_use_pasv | 5 | 15 | 40 | lib/ftp.c | |
| curl_mvaprintf | 5 | 15 | 24 | lib/mprintf.c | |
| easy_connection | 5 | 15 | 30 | lib/easy.c | |
| freedirs | 5 | 14 | 19 | lib/ftp.c | |
| Curl_failf | 5 | 14 | 23 | lib/sendf.c | |
| win32_init | 5 | 14 | 47 | lib/easy.c | |
| ftp_init | 5 | 13 | 26 | lib/ftp.c | |
| use_http_1_1 | 5 | 1 | 9 | lib/http.c | |
| curl_global_cleanup | 5 | 11 | 22 | lib/easy.c | |
| update_timer | 5 | 11 | 21 | lib/multi.c | |
| https_getsock | 1 | 4 | 9 | lib/http.c | |
| ftp_state_post_mdtm | 5 | 11 | 30 | lib/ftp.c | |
| multistate | 5 | 10 | 27 | lib/multi.c | |
| ssl_msg_type | 22 | 22 | 50 | lib/ssluse.c | |
| Curl_qsossl_handshake | 17 | 31 | 80 | lib/qssl.c | |
| Curl_qsossl_init_session | 11 | 31 | 73 | lib/qssl.c | |
| rec_do | 10 | 20 | 52 | lib/telnet.c | |
| rec_will | 10 | 20 | 52 | lib/telnet.c | |
| rec_dont | 9 | 18 | 44 | lib/telnet.c | |
| rec_wont | 9 | 18 | 44 | lib/telnet.c | |
| tftp_setup_connection | 7 | 15 | 36 | lib/tftp.c | |
| Curl_qsossl_send | 6 | 10 | 38 | lib/qssl.c | |
| strcpy_url | 5 | 16 | 30 | lib/transfer.c | |
| Curl_qsossl_recv | 5 | 15 | 39 | lib/qssl.c | |
| strlen_url | 5 | 14 | 24 | lib/transfer.c | |
| curl_strequal | 4 | 9 | 19 | lib/strequal.c | |
| negotiate | 4 | 9 | 14 | lib/telnet.c | |
| setstropt | 4 | 9 | 21 | lib/url.c | |
| dprintf_DollarString | 4 | 9 | 14 | lib/mprintf.c | |
| Curl_cookie_freelist | 4 | 8 | 15 | lib/cookie.c | |
| override_userpass | 4 | 8 | 31 | lib/url.c | |
| cleanup | 4 | 8 | 14 | lib/http_negotiate.c | |
| Curl_sec_putc | 4 | 8 | 15 | lib/security.c | |
| ftp_state_post_cwd | 4 | 8 | 20 | lib/ftp.c | |
| is_file | 4 | 7 | 13 | lib/nss.c | |
| Curl_share_unlock | 4 | 7 | 15 | lib/share.c | |
| expect100 | 4 | 7 | 19 | lib/http.c | |
| Curl_share_lock | 4 | 7 | 17 | lib/share.c | |
| Curl_global_host_cache_init | 4 | 7 | 11 | lib/hostip.c | |
| curl_dogetnameinfo | 4 | 7 | 24 | lib/hostip6.c | |
| curl_dogetaddrinfo | 4 | 7 | 19 | lib/hostip6.c | |
| Curl_resolv_unlock | 4 | 6 | 12 | lib/hostip.c | |
| Curl_resolv_getsock | 1 | 4 | 10 | lib/hostsyn.c | |
| Curl_addHandleToPipeline | 4 | 6 | 14 | lib/url.c | |
| Curl_posttransfer | 4 | 6 | 17 | lib/transfer.c | |
| Curl_sec_getc | 4 | 6 | 12 | lib/security.c | |
| Curl_str_key_compare | 4 | 5 | 13 | lib/hash.c | |
| Curl_ossl_seed | 4 | 5 | 13 | lib/ssluse.c | |
| acquire_thread_sync | 4 | 5 | 25 | lib/hostthre.c | |
| nss_load_key | 4 | 40 | 64 | lib/nss.c | |
| safe_strequal | 4 | 3 | 9 | lib/sslgen.c | |
| conn_free | 4 | 33 | 48 | lib/url.c | |
| curl_version | 4 | 32 | 58 | lib/version.c | |
| Curl_cache_addr | 4 | 26 | 51 | lib/hostip.c | |
| readmoredata | 4 | 22 | 43 | lib/http.c | |
| getaddrinfo_thread | 4 | 22 | 52 | lib/hostthre.c | |
| ftp_regular_transfer | 4 | 19 | 36 | lib/ftp.c | |
| Curl_ossl_version | 4 | 19 | 61 | lib/ssluse.c | |
| gethostbyname_thread | 4 | 19 | 47 | lib/hostthre.c | |
| Curl_ossl_set_engine | 4 | 18 | 31 | lib/ssluse.c | |
| Curl_llist_insert_next | 4 | 17 | 31 | lib/llist.c | |
| curl_easy_recv | 4 | 17 | 26 | lib/easy.c | |
| curl_slist_append | 4 | 16 | 30 | lib/sendf.c | |
| check_issuer_cert | 4 | 16 | 31 | lib/nss.c | |
| Curl_getaddrinfo | 4 | 16 | 34 | lib/hostthre.c | |
| trynextip | 4 | 16 | 31 | lib/connect.c | |
| Curl_hash_clean_with_criterium | 4 | 16 | 24 | lib/hash.c | |
| ftp_state_port_resp | 4 | 16 | 32 | lib/ftp.c | |
| MD5_Update | 4 | 16 | 33 | lib/md5.c | |
| Curl_nss_send | 4 | 16 | 30 | lib/nss.c | |
| gLibHandle | 4 | 15 | 74 | lib/nwlib.c | |
| decodeQuantum | 4 | 15 | 19 | lib/base64.c | |
| Curl_qsossl_connect | 4 | 15 | 26 | lib/qssl.c | |
| my_timegm | 4 | 15 | 30 | lib/parsedate.c | |
| ssh_done | 4 | 15 | 30 | lib/ssh.c | |
| log_gss_error | 4 | 15 | 28 | lib/http_negotiate.c | |
| Curl_ossl_engines_list | 4 | 14 | 21 | lib/ssluse.c | |
| split_str | 4 | 14 | 17 | lib/ldap.c | |
| check_wsock2 | 4 | 14 | 37 | lib/telnet.c | |
| fix_hostname | 4 | 13 | 38 | lib/url.c | |
| sh_addentry | 4 | 13 | 27 | lib/multi.c | |
| signalPipeClose | 4 | 13 | 25 | lib/url.c | |
| checktz | 4 | 13 | 16 | lib/parsedate.c | |
| checkmonth | 4 | 13 | 16 | lib/parsedate.c | |
| time2str | 4 | 12 | 22 | lib/progress.c | |
| curl_easy_init | 4 | 12 | 24 | lib/easy.c | |
| curlx_strdup | 4 | 12 | 22 | lib/strdup.c | |
| Curl_qsossl_close_one | 4 | 12 | 25 | lib/qssl.c | |
| buffer_write | 4 | 12 | 18 | lib/security.c | |
| curl_dostrdup | 4 | 12 | 22 | lib/memdebug.c | |
| curl_slist_free_all | 4 | 12 | 19 | lib/sendf.c | |
| Curl_cookie_cleanup | 4 | 11 | 17 | lib/cookie.c | |
| Curl_formclean | 4 | 11 | 18 | lib/formdata.c | |
| connect_host | 4 | 11 | 26 | lib/transfer.c | |
| moveHandleFromSendToRecvPipeline | 4 | 11 | 29 | lib/multi.c | |
| strlcpy | 4 | 10 | 16 | lib/krb4.c | |
| amiga_init | 4 | 10 | 26 | lib/amigaos.c | |
| _ldap_trace | 4 | 10 | 16 | lib/ldap.c | |
| waitconnect | 4 | 10 | 29 | lib/connect.c | |
| dump_addrinfo | 4 | 10 | 14 | lib/hostip6.c | |
| https_connecting | 4 | 10 | 22 | lib/http.c | |
| Curl_idn_strerror | 15 | 43 | 71 | lib/strerror.c | |
| curl_easy_strerror | 87 | 76 | 257 | lib/strerror.c | |
| curl_multi_setopt | 8 | 22 | 38 | lib/multi.c | |
| curl_share_strerror | 8 | 10 | 32 | lib/strerror.c | |
| Curl_ossl_send | 6 | 15 | 41 | lib/ssluse.c | |
| Curl_ossl_recv | 6 | 13 | 36 | lib/ssluse.c | |
| get_winsock_error | 53 | 108 | 184 | lib/strerror.c | |
| Curl_ossl_close | 3 | 9 | 16 | lib/ssluse.c | |
| Curl_is_resolved | 1 | 3 | 8 | lib/hostsyn.c | |
| Curl_store_ip_addr | 3 | 9 | 18 | lib/connect.c | |
| Curl_sec_end | 3 | 9 | 14 | lib/security.c | |
| Curl_ssl_free_certinfo | 3 | 9 | 12 | lib/sslgen.c | |
| Curl_ssl_close_all | 3 | 9 | 16 | lib/sslgen.c | |
| ftp_state_timeout | 3 | 9 | 25 | lib/ftp.c | |
| Curl_infof | 3 | 9 | 13 | lib/sendf.c | |
| multi_timeout | 3 | 9 | 24 | lib/multi.c | |
| Curl_is_resolved | 1 | 3 | 8 | lib/hostsyn.c | |
| Curl_cookie_loadfiles | 3 | 9 | 17 | lib/cookie.c | |
| _Curl_gtls_init | 3 | 8 | 13 | lib/gtls.c | |
| Curl_nss_check_cxn | 3 | 8 | 17 | lib/nss.c | |
| Curl_strcasestr | 3 | 8 | 16 | lib/strequal.c | |
| Curl_ossl_check_cxn | 3 | 8 | 14 | lib/ssluse.c | |
| Curl_rm_connc | 3 | 8 | 12 | lib/url.c | |
| nss_get_password | 3 | 8 | 13 | lib/nss.c | |
| Curl_ssl_recv | 3 | 8 | 18 | lib/sslgen.c | |
| Curl_removeHandleFromPipeline | 3 | 8 | 16 | lib/url.c | |
| curl_multi_assign | 3 | 8 | 16 | lib/multi.c | |
| checkheaders | 3 | 8 | 11 | lib/http.c | |
| ftp_state_post_type | 3 | 8 | 19 | lib/ftp.c | |
| ftp_state_post_size | 3 | 8 | 20 | lib/ftp.c | |
| Curl_nss_close | 3 | 7 | 13 | lib/nss.c | |
| name_to_level | 3 | 7 | 9 | lib/security.c | |
| ftp_getsock | 3 | 7 | 19 | lib/ftp.c | |
| Curl_freeaddrinfo | 1 | 1 | 4 | lib/hostip6.c | |
| Curl_ossl_set_engine_default | 3 | 7 | 17 | lib/ssluse.c | |
| slist_get_last | 3 | 7 | 15 | lib/sendf.c | |
| close_one | 3 | 7 | 13 | lib/gtls.c | |
| krb4_encode | 3 | 7 | 20 | lib/krb4.c | |
| waitconnect_getsock | 3 | 6 | 16 | lib/multi.c | |
| Curl_gtls_send | 3 | 6 | 15 | lib/gtls.c | |
| Curl_do_more | 3 | 6 | 13 | lib/url.c | |
| Curl_sec_fflush_fd | 3 | 6 | 13 | lib/security.c | |
| DisposeLibraryData | 3 | 6 | 13 | lib/nwlib.c | |
| Curl_protocol_doing | 3 | 6 | 13 | lib/url.c | |
| Curl_protocol_connecting | 3 | 6 | 14 | lib/url.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| Curl_ipvalid | 2 | 3 | 8 | lib/hostip4.c | |
| passwd_callback | 3 | 6 | 19 | lib/ssluse.c | |
| curl_memdebug | 3 | 5 | 13 | lib/memdebug.c | |
| Curl_reset_reqproto | 3 | 5 | 9 | lib/url.c | |
| DisposeThreadData | 3 | 5 | 11 | lib/nwlib.c | |
| is_ASCII_name | 3 | 5 | 10 | lib/url.c | |
| hostcache_timestamp_remove | 3 | 5 | 16 | lib/hostip.c | |
| ssh_easy_statemach | 3 | 5 | 10 | lib/ssh.c | |
| idna_init | 3 | 5 | 14 | lib/easy.c | |
| curl_socket | 3 | 4 | 9 | lib/memdebug.c | |
| Curl_llist_destroy | 3 | 4 | 10 | lib/llist.c | |
| Curl_doing_getsock | 3 | 3 | 8 | lib/url.c | |
| Curl_isPipeliningEnabled | 3 | 3 | 7 | lib/url.c | |
| Curl_sec_set_protection_level | 3 | 2 | 6 | lib/security.c | |
| skip | 3 | 2 | 6 | lib/parsedate.c | |
| push_certinfo_len | 3 | 19 | 41 | lib/ssluse.c | |
| krb5_encode | 3 | 18 | 35 | lib/krb5.c | |
| ssh_do | 3 | 17 | 34 | lib/ssh.c | |
| krb5_decode | 3 | 16 | 27 | lib/krb5.c | |
| Curl_nonblock | 3 | 16 | 63 | lib/connect.c | |
| mk_nt_hash | 3 | 15 | 36 | lib/http_ntlm.c | |
| Curl_FormBoundary | 3 | 15 | 27 | lib/formdata.c | |
| Curl_unencode_deflate_write | 3 | 13 | 26 | lib/content_encoding.c | |
| add_bufferf | 3 | 13 | 20 | lib/http.c | |
| sftp_perform | 3 | 12 | 30 | lib/ssh.c | |
| scp_perform | 3 | 12 | 30 | lib/ssh.c | |
| ssh_init | 3 | 12 | 20 | lib/ssh.c | |
| krb4_decode | 3 | 12 | 24 | lib/krb4.c | |
| tcpnodelay | 3 | 11 | 32 | lib/connect.c | |
| _ldap_url_parse | 3 | 11 | 18 | lib/ldap.c | |
| inet_ntop4 | 3 | 11 | 23 | lib/inet_ntop.c | |
| ftp_do | 3 | 11 | 25 | lib/ftp.c | |
| ftp_nb_type | 3 | 11 | 19 | lib/ftp.c | |
| Curl_dupset | 3 | 11 | 22 | lib/url.c | |
| Curl_hash_delete | 3 | 11 | 15 | lib/hash.c | |
| mk_hash_element | 3 | 11 | 23 | lib/hash.c | |
| curl_mvsnprintf | 3 | 11 | 21 | lib/mprintf.c | |
| Curl_ssl_initsessions | 3 | 10 | 18 | lib/sslgen.c | |
| parse_proxy_auth | 3 | 10 | 21 | lib/url.c | |
| Curl_hash_apply | 3 | 10 | 16 | lib/hash.c | |
| Curl_hash_pick | 3 | 10 | 16 | lib/hash.c | |
| ftp_state_post_rest | 3 | 10 | 23 | lib/ftp.c | |
| curl_multi_strerror | 11 | 13 | 41 | lib/strerror.c | |
| Curl_isalnum | 63 | 3 | 21 | lib/escape.c | |
| tftp_state_machine | 5 | 18 | 29 | lib/tftp.c | |
| display_error | 3 | 6 | 15 | lib/nss.c | |
| Curl_inet_pton | 3 | 4 | 19 | lib/inet_pton.c | |
| Curl_inet_ntop | 3 | 4 | 14 | lib/inet_ntop.c | |
| curlx_tvnow | 1 | 3 | 11 | lib/timeval.c | |
| curl_easy_setopt | 2 | 9 | 16 | lib/easy.c | |
| kill_session | 2 | 9 | 21 | lib/sslgen.c | |
| sftp_disconnect | 2 | 9 | 20 | lib/ssh.c | |
| init_certinfo | 2 | 9 | 16 | lib/ssluse.c | |
| X509_signature | 2 | 9 | 13 | lib/ssluse.c | |
| Curl_write_plain | 2 | 8 | 17 | lib/sendf.c | |
| Encode | 2 | 8 | 13 | lib/md5.c | |
| Curl_formpostheader | 2 | 8 | 15 | lib/formdata.c | |
| Curl_qsossl_create | 2 | 8 | 16 | lib/qssl.c | |
| strippath | 2 | 8 | 14 | lib/formdata.c | |
| addbyter | 2 | 8 | 14 | lib/mprintf.c | |
| Curl_ossl_close_all | 2 | 7 | 24 | lib/ssluse.c | |
| Curl_nss_cleanup | 2 | 7 | 15 | lib/nss.c | |
| ftp_state_user | 2 | 7 | 12 | lib/ftp.c | |
| Curl_async_resolved | 2 | 7 | 19 | lib/url.c | |
| Curl_hash_str | 2 | 7 | 13 | lib/hash.c | |
| file_done | 2 | 7 | 13 | lib/file.c | |
| ftp_state_acct_resp | 2 | 7 | 14 | lib/ftp.c | |
| Curl_ossl_connect | 2 | 7 | 15 | lib/ssluse.c | |
| curl_dofree | 2 | 7 | 17 | lib/memdebug.c | |
| scp_disconnect | 2 | 7 | 17 | lib/ssh.c | |
| Curl_ssl_connect | 2 | 7 | 15 | lib/sslgen.c | |
| FormAddTest | 2 | 7 | 14 | lib/formdata.c | |
| showtime | 2 | 7 | 24 | lib/gtls.c | |
| add_buffer_init | 2 | 6 | 11 | lib/http.c | |
| Curl_llist_alloc | 2 | 6 | 13 | lib/llist.c | |
| ascii_to_unicode_le | 2 | 6 | 9 | lib/http_ntlm.c | |
| ftp_doing | 2 | 6 | 13 | lib/ftp.c | |
| ftp_quit | 2 | 6 | 13 | lib/ftp.c | |
| hash_element_dtor | 2 | 6 | 13 | lib/hash.c | |
| curl_fclose | 2 | 6 | 12 | lib/memdebug.c | |
| curl_accept | 2 | 6 | 11 | lib/memdebug.c | |
| Curl_sftp_send | 2 | 6 | 14 | lib/ssh.c | |
| Curl_printPipeline | 2 | 6 | 11 | lib/url.c | |
| Curl_hash_clean | 2 | 6 | 11 | lib/hash.c | |
| Curl_scp_send | 2 | 6 | 14 | lib/ssh.c | |
| SocketIsDead | 2 | 6 | 12 | lib/url.c | |
| Curl_printable_address | 2 | 6 | 13 | lib/hostip.c | |
| Curl_num_addresses | 2 | 6 | 7 | lib/hostip.c | |
| Curl_FormInit | 2 | 6 | 11 | lib/formdata.c | |
| state | 2 | 5 | 68 | lib/ssh.c | |
| Curl_ossl_init | 2 | 5 | 15 | lib/ssluse.c | |
| process_zlib_error | 2 | 5 | 13 | lib/content_encoding.c | |
| Curl_sec_request_prot | 2 | 5 | 9 | lib/security.c | |
| curl_share_init | 2 | 5 | 11 | lib/share.c | |
| Curl_ssl_shutdown | 2 | 5 | 10 | lib/sslgen.c | |
| Decode | 2 | 5 | 10 | lib/md5.c | |
| state | 2 | 5 | 68 | lib/ssh.c | |
| Curl_freeset | 2 | 5 | 7 | lib/url.c | |
| sftp_doing | 2 | 5 | 11 | lib/ssh.c | |
| scp_doing | 2 | 5 | 11 | lib/ssh.c | |
| md5_to_ascii | 2 | 5 | 7 | lib/http_digest.c | |
| buffer_read | 2 | 5 | 9 | lib/security.c | |
| tailmatch | 2 | 5 | 10 | lib/cookie.c | |
| Curl_nss_init | 2 | 4 | 12 | lib/nss.c | |
| Curl_cookie_clearall | 2 | 4 | 8 | lib/cookie.c | |
| krb5_check_prot | 2 | 4 | 8 | lib/krb5.c | |
| domore_getsock | 2 | 4 | 16 | lib/multi.c | |
| nosigpipe | 2 | 4 | 10 | lib/connect.c | |
| curl_easy_cleanup | 2 | 4 | 9 | lib/easy.c | |
| do_complete | 2 | 4 | 8 | lib/url.c | |
| Curl_qsossl_close | 2 | 4 | 9 | lib/qssl.c | |
| curl_fdopen | 2 | 4 | 9 | lib/memdebug.c | |
| curl_fopen | 2 | 4 | 9 | lib/memdebug.c | |
| curl_sclose | 2 | 4 | 8 | lib/memdebug.c | |
| Curl_hash_destroy | 2 | 4 | 9 | lib/hash.c | |
| gethandleathead | 2 | 4 | 9 | lib/url.c | |
| scp_done | 2 | 4 | 11 | lib/ssh.c | |
| Curl_pgrsSetUploadSize | 2 | 4 | 8 | lib/progress.c | |
| print_hex | 2 | 4 | 7 | lib/http_ntlm.c | |
| Curl_pgrsSetDownloadSize | 2 | 4 | 8 | lib/progress.c | |
| isHandleAtHead | 2 | 4 | 9 | lib/multi.c | |
| curl_multi_timeout | 2 | 4 | 11 | lib/multi.c | |
| curl_multi_socket_all | 2 | 4 | 9 | lib/multi.c | |
| curl_multi_socket_action | 2 | 4 | 9 | lib/multi.c | |
| curl_multi_socket | 2 | 4 | 9 | lib/multi.c | |
| asn1_object_dump | 2 | 4 | 7 | lib/ssluse.c | |
| Curl_ssl_init | 2 | 4 | 9 | lib/sslgen.c | |
| Curl_ntlm_global_cleanup | 2 | 4 | 8 | lib/http_ntlm.c | |
| Curl_gtls_cleanup | 2 | 4 | 8 | lib/gtls.c | |
| krb4_check_prot | 2 | 4 | 8 | lib/krb4.c | |
| curl_memlimit | 2 | 3 | 7 | lib/memdebug.c | |
| Curl_ipvalid | 2 | 3 | 8 | lib/hostip4.c | |
| Curl_multi_handlePipeBreak | 2 | 3 | 7 | lib/multi.c | |
| Curl_easy_addmulti | 2 | 3 | 9 | lib/easy.c | |
| amiga_cleanup | 2 | 3 | 7 | lib/amigaos.c | |
| sh_delentry | 2 | 3 | 12 | lib/multi.c | |
| Curl_protocol_getsock | 2 | 3 | 8 | lib/url.c | |
| libssh2_session_error_to_CURLE | 2 | 3 | 11 | lib/ssh.c | |
| Curl_ossl_data_pending | 2 | 3 | 9 | lib/ssluse.c | |
| dprintf_IsQualifierNoDollar | 23 | 2 | 13 | lib/mprintf.c | |
| Curl_ssl_cleanup | 2 | 3 | 8 | lib/sslgen.c | |
| curl_dofreeaddrinfo | 2 | 3 | 8 | lib/hostip6.c | |
| Curl_global_host_cache_dtor | 2 | 3 | 7 | lib/hostip.c | |
| Curl_ssl_close | 2 | 2 | 5 | lib/sslgen.c | |
| ftp_need_type | 2 | 2 | 5 | lib/ftp.c | |
| verboseconnect | 2 | 2 | 6 | lib/url.c | |
| close_connections | 2 | 2 | 6 | lib/url.c | |
| Curl_safefree | 2 | 2 | 5 | lib/url.c | |
| curl_free | 2 | 2 | 5 | lib/escape.c | |
| Curl_ip2addr6 | 2 | 19 | 50 | lib/hostares.c | |
| Curl_initinfo | 2 | 18 | 25 | lib/getinfo.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| curl_version_info | 2 | 17 | 49 | lib/version.c | |
| mk_auth | 2 | 17 | 23 | lib/krb4.c | |
| kbd_callback | 2 | 15 | 28 | lib/ssh.c | |
| pubkey_show | 2 | 14 | 23 | lib/ssluse.c | |
| x509_name_oneline | 2 | 13 | 25 | lib/ssluse.c | |
| suck | 2 | 13 | 18 | lib/base64.c | |
| data_pending | 2 | 1 | 7 | lib/transfer.c | |
| isBadFtpString | 2 | 1 | 4 | lib/ftp.c | |
| send_negotiation | 2 | 12 | 19 | lib/telnet.c | |
| init_telnet | 2 | 12 | 24 | lib/telnet.c | |
| sftp_libssh2_strerror | 21 | 21 | 65 | lib/ssh.c | |
| pausewrite | 2 | 11 | 28 | lib/sendf.c | |
| allocate_conn | 2 | 11 | 28 | lib/url.c | |
| read_rewind | 2 | 10 | 26 | lib/transfer.c | |
| MD5_Final | 2 | 10 | 23 | lib/md5.c | |
| sftp_libssh2_error_to_CURLE | 12 | 9 | 34 | lib/ssh.c | |
| Curl_pgrsTime | 10 | 16 | 41 | lib/progress.c | |
| Curl_resolv_getsock | 1 | 4 | 10 | lib/hostsyn.c | |
| lm_resp | 1 | 7 | 18 | lib/http_ntlm.c | |
| dumpcert | 1 | 7 | 18 | lib/ssluse.c | |
| MD5Transform | 1 | 71 | 87 | lib/md5.c | |
| telnet_done | 1 | 7 | 14 | lib/telnet.c | |
| curl_msprintf | 1 | 7 | 10 | lib/mprintf.c | |
| AddFormDataf | 1 | 6 | 12 | lib/formdata.c | |
| curl_formadd | 1 | 3 | 8 | lib/formdata.c | |
| curl_easy_getinfo | 1 | 6 | 11 | lib/easy.c | |
| curl_mfprintf | 1 | 6 | 9 | lib/mprintf.c | |
| curl_mprintf | 1 | 6 | 10 | lib/mprintf.c | |
| curl_msnprintf | 1 | 6 | 9 | lib/mprintf.c | |
| cert_verify_callback | 1 | 5 | 10 | lib/ssluse.c | |
| curlx_tvnow | 1 | 3 | 11 | lib/timeval.c | |
| ssh_multi_statemach | 1 | 5 | 10 | lib/ssh.c | |
| Curl_ssl_connect_nonblocking | 1 | 5 | 14 | lib/sslgen.c | |
| MD5_Init | 1 | 5 | 9 | lib/md5.c | |
| Curl_free_ssl_config | 1 | 5 | 8 | lib/sslgen.c | |
| storebuffer | 1 | 5 | 8 | lib/mprintf.c | |
| curlx_tvnow | 1 | 3 | 11 | lib/timeval.c | |
| Curl_httpchunk_init | 1 | 4 | 7 | lib/http_chunks.c | |
| ftp_state_pwd | 1 | 4 | 10 | lib/ftp.c | |
| tftp_done | 1 | 4 | 10 | lib/tftp.c | |
| Curl_ossl_cleanup | 1 | 4 | 19 | lib/ssluse.c | |
| SSL_strerror | 1 | 4 | 13 | lib/ssluse.c | |
| nss_no_password | 1 | 4 | 7 | lib/nss.c | |
| tpf_select_libcurl | 1 | 4 | 9 | lib/select.c | |
| Curl_llist_init | 1 | 4 | 8 | lib/llist.c | |
| Curl_md5it | 1 | 4 | 8 | lib/md5.c | |
| Curl_sftp_recv | 1 | 4 | 10 | lib/ssh.c | |
| hostcache_prune | 1 | 4 | 12 | lib/hostip.c | |
| Curl_scp_recv | 1 | 4 | 11 | lib/ssh.c | |
| https_getsock | 1 | 4 | 9 | lib/http.c | |
| https_getsock | 1 | 4 | 9 | lib/http.c | |
| https_getsock | 1 | 4 | 9 | lib/http.c | |
| curl_formget | 1 | 4 | 8 | lib/formdata.c | |
| Curl_pgrsResetTimes | 1 | 4 | 7 | lib/progress.c | |
| krb4_overhead | 1 | 4 | 9 | lib/krb4.c | |
| Curl_pgrsDone | 1 | 4 | 8 | lib/progress.c | |
| curl_mvsprintf | 1 | 4 | 7 | lib/mprintf.c | |
| Curl_resolv_getsock | 1 | 4 | 10 | lib/hostsyn.c | |
| krb5_overhead | 1 | 4 | 9 | lib/krb5.c | |
| Curl_wait_for_resolv | 1 | 3 | 7 | lib/hostsyn.c | |
| curlx_tvnow | 1 | 3 | 11 | lib/timeval.c | |
| exit_zlib | 1 | 3 | 7 | lib/content_encoding.c | |
| HandshakeCallback | 1 | 3 | 6 | lib/nss.c | |
| freednsentry | 1 | 3 | 8 | lib/hostip.c | |
| curl_easy_reset | 1 | 36 | 73 | lib/easy.c | |
| alarmfunc | 1 | 3 | 8 | lib/hostip.c | |
| ftps_setup_connection | 1 | 3 | 7 | lib/ftp.c | |
| Curl_set_command_prot | 1 | 3 | 7 | lib/security.c | |
| ftp_respinit | 1 | 3 | 6 | lib/ftp.c | |
| ftp_pasv_verbose | 1 | 3 | 10 | lib/ftp.c | |
| hash_fd | 1 | 3 | 7 | lib/multi.c | |
| fd_key_compare | 1 | 3 | 6 | lib/multi.c | |
| is_absolute_url | 1 | 3 | 7 | lib/transfer.c | |
| http_getsock_do | 1 | 3 | 9 | lib/http.c | |
| curl_formadd | 1 | 3 | 8 | lib/formdata.c | |
| ftp_state_post_stortype | 1 | 3 | 10 | lib/ftp.c | |
| ftp_state_post_retrtype | 1 | 3 | 10 | lib/ftp.c | |
| Curl_ntlm_cleanup | 1 | 3 | 10 | lib/http_ntlm.c | |
| Curl_is_resolved | 1 | 3 | 8 | lib/hostsyn.c | |
| Curl_addrinfo_copy | 1 | 2 | 6 | lib/hostip.c | |
| Curl_nss_close_all | 1 | 2 | 5 | lib/nss.c | |
| Curl_addrinfo_copy | 1 | 2 | 6 | lib/hostip.c | |
| Curl_easy_initHandleData | 1 | 2 | 6 | lib/easy.c | |
| debug_print_sock_hash | 1 | 2 | 7 | lib/multi.c | |
| Curl_digest_cleanup | 1 | 2 | 5 | lib/http_digest.c | |
| Curl_qsossl_version | 1 | 2 | 6 | lib/qssl.c | |
| curl_getdate | 1 | 2 | 5 | lib/parsedate.c | |
| Curl_cleanup_negotiate | 1 | 2 | 5 | lib/http_negotiate.c | |
| Curl_sec_send | 1 | 2 | 6 | lib/security.c | |
| llist_dtor | 1 | 2 | 6 | lib/url.c | |
| sh_freeentry | 1 | 2 | 6 | lib/multi.c | |
| Curl_qsossl_close_all | 1 | 2 | 7 | lib/qssl.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| LIBSSH2_FREE_FUNC | 1 | 2 | 5 | lib/ssh.c | |
| setpacketblock | 1 | 2 | 5 | lib/tftp.c | |
| LIBSSH2_REALLOC_FUNC | 1 | 2 | 5 | lib/ssh.c | |
| setpacketevent | 1 | 2 | 5 | lib/tftp.c | |
| LIBSSH2_ALLOC_FUNC | 1 | 2 | 5 | lib/ssh.c | |
| Curl_pgrsStartNow | 1 | 2 | 5 | lib/progress.c | |
| push_certinfo | 1 | 2 | 9 | lib/ssluse.c | |
| Curl_addrinfo6_callback | 1 | 2 | 15 | lib/hostasyn.c | |
| Curl_addrinfo4_callback | 1 | 2 | 12 | lib/hostasyn.c | |
| _NonAppStop | 1 | 2 | 5 | lib/nwlib.c | |
| LDAP_TRACE | 1 | 2 | 4 | lib/ldap.c | |
| win32_cleanup | 1 | 2 | 9 | lib/easy.c | |
| main | 1 | 20 | 38 | lib/mprintf.c | |
| Curl_ip2addr | 1 | 19 | 47 | lib/hostip.c | |
| do_init | 1 | 19 | 31 | lib/url.c | |
| Curl_ossl_session_free | 1 | 1 | 5 | lib/ssluse.c | |
| Curl_gtls_init | 1 | 1 | 10 | lib/gtls.c | |
| Curl_freeaddrinfo | 1 | 1 | 4 | lib/hostip6.c | |
| __deinit_environment | 1 | 1 | 4 | lib/nwos.c | |
| Curl_gtls_pull | 1 | 1 | 4 | lib/gtls.c | |
| __init_environment | 1 | 1 | 4 | lib/nwos.c | |
| Curl_gtls_version | 1 | 1 | 4 | lib/gtls.c | |
| Curl_gtls_push | 1 | 1 | 4 | lib/gtls.c | |
| curl_unescape | 1 | 1 | 4 | lib/escape.c | |
| Curl_gtls_session_free | 1 | 1 | 4 | lib/gtls.c | |
| curl_escape | 1 | 1 | 4 | lib/escape.c | |
| Curl_multi_canPipeline | 1 | 1 | 4 | lib/multi.c | |
| Curl_mk_dnscache | 1 | 1 | 4 | lib/hostip.c | |
| Curl_gtls_close | 1 | 1 | 4 | lib/gtls.c | |
| curl_getenv | 1 | 1 | 4 | lib/getenv.c | |
| Curl_gtls_close_all | 1 | 1 | 5 | lib/gtls.c | |
| tls_log_func | 1 | 1 | 4 | lib/gtls.c | |
| Curl_ssl_data_pending | 1 | 1 | 5 | lib/sslgen.c | |
| Curl_ssl_check_cxn | 1 | 1 | 4 | lib/sslgen.c | |
| Curl_ssl_version | 1 | 1 | 4 | lib/sslgen.c | |
| Curl_qsossl_init | 1 | 1 | 9 | lib/qssl.c | |
| Curl_ssl_send | 1 | 1 | 7 | lib/sslgen.c | |
| Curl_ssl_engines_list | 1 | 1 | 4 | lib/sslgen.c | |
| Curl_ssl_set_engine_default | 1 | 1 | 4 | lib/sslgen.c | |
| Curl_ssl_set_engine | 1 | 1 | 4 | lib/sslgen.c | |
| Curl_speedinit | 1 | 1 | 4 | lib/speedcheck.c | |
| sh_init | 1 | 1 | 5 | lib/multi.c | |
| netware_init | 1 | 1 | 4 | lib/nwos.c | |
| getrpacketblock | 1 | 1 | 4 | lib/tftp.c | |
| getrpacketevent | 1 | 1 | 4 | lib/tftp.c | |
| ConnectionDone | 1 | 1 | 5 | lib/url.c | |
| Curl_ossl_connect_nonblocking | 1 | 1 | 7 | lib/ssluse.c | |
| release_thread_sync | 1 | 1 | 5 | lib/hostthre.c | |
| Curl_pgrsSetUploadCounter | 1 | 1 | 4 | lib/progress.c | |
| Curl_pgrsSetDownloadCounter | 1 | 1 | 4 | lib/progress.c | |
| create_hostcache_id | 1 | 1 | 6 | lib/hostip.c | |
| curl_formfree | 1 | 1 | 5 | lib/formdata.c | |
| unload_file | 1 | 1 | 3 | lib/gtls.c | |
| rand_enough | 1 | 1 | 4 | lib/ssluse.c | |
| rand_enough | 1 | 1 | 4 | lib/ssluse.c | |
| Curl_qsossl_trap_cert | 1 | 1 | 5 | lib/qssl.c | |
| _NonAppCheckUnload | 1 | 1 | 4 | lib/nwlib.c | |
| Curl_multi_rmeasy | 1 | 1 | 4 | lib/multi.c | |
| Curl_llist_count | 1 | 1 | 5 | lib/llist.c | |
| setup_des_key | 1 | 11 | 17 | lib/http_ntlm.c | |
| readint_le | 1 | 1 | 6 | lib/http_ntlm.c | |
| Curl_tvlong | 1 | 1 | 4 | lib/timeval.c | |
| Curl_nss_version | 1 | 1 | 4 | lib/nss.c | |
| curlx_tvdiff_secs | 1 | 1 | 5 | lib/timeval.c | |
| curl_mvfprintf | 1 | 1 | 4 | lib/mprintf.c | |
| curl_mvprintf | 1 | 1 | 4 | lib/mprintf.c | |
| curlx_tvdiff | 1 | 1 | 5 | lib/timeval.c | |
| Curl_qsossl_cleanup | 1 | 0 | 5 | lib/qssl.c | |