diff options
130 files changed, 21531 insertions, 9841 deletions
diff --git a/Utilities/cmcurl/CMake/CheckCSourceCompiles.cmake b/Utilities/cmcurl/CMake/CheckCSourceCompiles.cmake new file mode 100644 index 0000000..41762b4 --- /dev/null +++ b/Utilities/cmcurl/CMake/CheckCSourceCompiles.cmake @@ -0,0 +1,75 @@ +# - Check if the source code provided in the SOURCE argument compiles. +# CHECK_C_SOURCE_COMPILES(SOURCE VAR) +# - macro which checks if the source code compiles +# SOURCE - source code to try to compile +# VAR - variable to store whether the source code compiled +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +MACRO(CHECK_C_SOURCE_COMPILES SOURCE VAR) + IF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") + SET(message "${VAR}") + # If the number of arguments is greater than 2 (SOURCE VAR) + IF(${ARGC} GREATER 2) + # then add the third argument as a message + SET(message "${ARGV2} (${VAR})") + ENDIF(${ARGC} GREATER 2) + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES) + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + SET(src "") + FOREACH(def ${EXTRA_DEFINES}) + SET(src "${src}#define ${def} 1\n") + ENDFOREACH(def) + FOREACH(inc ${HEADER_INCLUDES}) + SET(src "${src}#include <${inc}>\n") + ENDFOREACH(inc) + + SET(src "${src}\nint main() { ${SOURCE} ; return 0; }") + SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${src}") + CONFIGURE_FILE(${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c" + IMMEDIATE) + MESSAGE(STATUS "Performing Test ${message}") + TRY_COMPILE(${VAR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}" + "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + IF(${VAR}) + SET(${VAR} 1 CACHE INTERNAL "Test ${message}") + MESSAGE(STATUS "Performing Test ${message} - Success") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing C SOURCE FILE Test ${message} succeded with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${src}\n") + ELSE(${VAR}) + MESSAGE(STATUS "Performing Test ${message} - Failed") + SET(${VAR} "" CACHE INTERNAL "Test ${message}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing C SOURCE FILE Test ${message} failed with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${src}\n") + ENDIF(${VAR}) + ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") +ENDMACRO(CHECK_C_SOURCE_COMPILES) diff --git a/Utilities/cmcurl/CMake/CheckCSourceRuns.cmake b/Utilities/cmcurl/CMake/CheckCSourceRuns.cmake new file mode 100644 index 0000000..35d2cef --- /dev/null +++ b/Utilities/cmcurl/CMake/CheckCSourceRuns.cmake @@ -0,0 +1,78 @@ +# - Check if the source code provided in the SOURCE argument compiles and runs. +# CHECK_C_SOURCE_RUNS(SOURCE VAR) +# - macro which checks if the source code runs +# SOURCE - source code to try to compile +# VAR - variable to store size if the type exists. +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +MACRO(CHECK_C_SOURCE_RUNS SOURCE VAR) + IF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES) + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + SET(src "") + FOREACH(def ${EXTRA_DEFINES}) + SET(src "${src}#define ${def} 1\n") + ENDFOREACH(def) + FOREACH(inc ${HEADER_INCLUDES}) + SET(src "${src}#include <${inc}>\n") + ENDFOREACH(inc) + + SET(src "${src}\nint main() { ${SOURCE} ; return 0; }") + FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src2.c" + "${src}\n") + EXEC_PROGRAM("${CMAKE_COMMAND}" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp" + ARGS -E copy src2.c src.c) + MESSAGE(STATUS "Performing Test ${VAR}") + TRY_RUN(${VAR} ${VAR}_COMPILED + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}" + "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + # if it did not compile make the return value fail code of 1 + IF(NOT ${VAR}_COMPILED) + SET(${VAR} 1) + ENDIF(NOT ${VAR}_COMPILED) + # if the return value was 0 then it worked + SET(result_var ${${VAR}}) + IF("${result_var}" EQUAL 0) + SET(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + MESSAGE(STATUS "Performing Test ${VAR} - Success") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing C SOURCE FILE Test ${VAR} succeded with the following output:\n" + "${OUTPUT}\n" + "Return value: ${${VAR}}\n" + "Source file was:\n${src}\n") + ELSE("${result_var}" EQUAL 0) + MESSAGE(STATUS "Performing Test ${VAR} - Failed") + SET(${VAR} "" CACHE INTERNAL "Test ${VAR}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing C SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "Return value: ${result_var}\n" + "Source file was:\n${src}\n") + ENDIF("${result_var}" EQUAL 0) + ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") +ENDMACRO(CHECK_C_SOURCE_RUNS) diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake new file mode 100644 index 0000000..30d3600 --- /dev/null +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -0,0 +1,245 @@ +INCLUDE(CheckCSourceCompiles) +SET(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2") +SET(HEADER_INCLUDES) +SET(headers_hack) +MACRO(add_header_include check header) + IF(${check}) + SET(headers_hack + "${headers_hack}\n#include <${header}>") + #SET(HEADER_INCLUDES + # ${HEADER_INCLUDES} + # "${header}") + ENDIF(${check}) +ENDMACRO(add_header_include) +SET(signature_call_conv) +IF(HAVE_WINDOWS_H) + add_header_include(HAVE_WINDOWS_H "windows.h") + add_header_include(HAVE_WINSOCK2_H "winsock2.h") + add_header_include(HAVE_WINSOCK_H "winsock.h") + SET(EXTRA_DEFINES ${EXTRA_DEFINES} + "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3") + SET(signature_call_conv "PASCAL") +ELSE(HAVE_WINDOWS_H) + add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") +ENDIF(HAVE_WINDOWS_H) + +SET(EXTRA_DEFINES_BACKUP "${EXTRA_DEFINES}") +SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") +CHECK_C_SOURCE_COMPILES("recv(0, 0, 0, 0)" curl_cv_recv) +IF(curl_cv_recv) + # AC_CACHE_CHECK([types of arguments and return type for recv], + #[curl_cv_func_recv_args], [ + #SET(curl_cv_func_recv_args "unknown") + #for recv_retv in 'int' 'ssize_t'; do + IF(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + FOREACH(recv_retv "ssize_t" "int") + FOREACH(recv_arg1 "int" "ssize_t" "SOCKET") + FOREACH(recv_arg2 "void *" "char *") + FOREACH(recv_arg3 "size_t" "int" "socklen_t" "unsigned int") + FOREACH(recv_arg4 "int" "unsigned int") + IF(NOT curl_cv_func_recv_done) + SET(curl_cv_func_recv_test "UNKNOWN") + SET(extern_line "extern ${recv_retv} ${signature_call_conv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})\;") + SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") + CHECK_C_SOURCE_COMPILES(" + ${recv_arg1} s=0; + ${recv_arg2} buf=0; + ${recv_arg3} len=0; + ${recv_arg4} flags=0; + ${recv_retv} res = recv(s, buf, len, flags)" + curl_cv_func_recv_test + "${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})") + IF(curl_cv_func_recv_test) + SET(curl_cv_func_recv_args + "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}") + SET(RECV_TYPE_ARG1 "${recv_arg1}") + SET(RECV_TYPE_ARG2 "${recv_arg2}") + SET(RECV_TYPE_ARG3 "${recv_arg3}") + SET(RECV_TYPE_ARG4 "${recv_arg4}") + SET(RECV_TYPE_RETV "${recv_retv}") + SET(HAVE_RECV 1) + SET(curl_cv_func_recv_done 1) + ENDIF(curl_cv_func_recv_test) + ENDIF(NOT curl_cv_func_recv_done) + ENDFOREACH(recv_arg4) + ENDFOREACH(recv_arg3) + ENDFOREACH(recv_arg2) + ENDFOREACH(recv_arg1) + ENDFOREACH(recv_retv) + ELSE(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + STRING(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG1 "${curl_cv_func_recv_args}") + STRING(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG2 "${curl_cv_func_recv_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG3 "${curl_cv_func_recv_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" RECV_TYPE_ARG4 "${curl_cv_func_recv_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}") + #MESSAGE("RECV_TYPE_ARG1 ${RECV_TYPE_ARG1}") + #MESSAGE("RECV_TYPE_ARG2 ${RECV_TYPE_ARG2}") + #MESSAGE("RECV_TYPE_ARG3 ${RECV_TYPE_ARG3}") + #MESSAGE("RECV_TYPE_ARG4 ${RECV_TYPE_ARG4}") + #MESSAGE("RECV_TYPE_RETV ${RECV_TYPE_RETV}") + ENDIF(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + + IF("${curl_cv_func_recv_args}" STREQUAL "unknown") + MESSAGE(FATAL_ERROR "Cannot find proper types to use for recv args") + ENDIF("${curl_cv_func_recv_args}" STREQUAL "unknown") +ELSE(curl_cv_recv) + MESSAGE(FATAL_ERROR "Unable to link function recv") +ENDIF(curl_cv_recv) +SET(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv") +SET(HAVE_RECV 1) + +CHECK_C_SOURCE_COMPILES("send(0, 0, 0, 0)" curl_cv_send) +IF(curl_cv_send) + # AC_CACHE_CHECK([types of arguments and return type for send], + #[curl_cv_func_send_args], [ + #SET(curl_cv_func_send_args "unknown") + #for send_retv in 'int' 'ssize_t'; do + IF(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") + FOREACH(send_retv "ssize_t" "int") + FOREACH(send_arg1 "int" "ssize_t" "SOCKET") + FOREACH(send_arg2 "const void *" "void *" "char *" "const char *") + FOREACH(send_arg3 "size_t" "int" "socklen_t" "unsigned int") + FOREACH(send_arg4 "int" "unsigned int") + IF(NOT curl_cv_func_send_done) + SET(curl_cv_func_send_test "UNKNOWN") + SET(extern_line "extern ${send_retv} ${signature_call_conv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})\;") + SET(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") + CHECK_C_SOURCE_COMPILES(" + ${send_arg1} s=0; + ${send_arg2} buf=0; + ${send_arg3} len=0; + ${send_arg4} flags=0; + ${send_retv} res = send(s, buf, len, flags)" + curl_cv_func_send_test + "${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})") + IF(curl_cv_func_send_test) + #MESSAGE("Found arguments: ${curl_cv_func_send_test}") + STRING(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}") + STRING(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}") + SET(curl_cv_func_send_args + "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}") + SET(SEND_TYPE_ARG1 "${send_arg1}") + SET(SEND_TYPE_ARG2 "${send_arg2}") + SET(SEND_TYPE_ARG3 "${send_arg3}") + SET(SEND_TYPE_ARG4 "${send_arg4}") + SET(SEND_TYPE_RETV "${send_retv}") + SET(HAVE_SEND 1) + SET(curl_cv_func_send_done 1) + ENDIF(curl_cv_func_send_test) + ENDIF(NOT curl_cv_func_send_done) + ENDFOREACH(send_arg4) + ENDFOREACH(send_arg3) + ENDFOREACH(send_arg2) + ENDFOREACH(send_arg1) + ENDFOREACH(send_retv) + ELSE(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") + STRING(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG1 "${curl_cv_func_send_args}") + STRING(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG2 "${curl_cv_func_send_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG3 "${curl_cv_func_send_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG4 "${curl_cv_func_send_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" SEND_TYPE_RETV "${curl_cv_func_send_args}") + STRING(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" SEND_QUAL_ARG2 "${curl_cv_func_send_args}") + #MESSAGE("SEND_TYPE_ARG1 ${SEND_TYPE_ARG1}") + #MESSAGE("SEND_TYPE_ARG2 ${SEND_TYPE_ARG2}") + #MESSAGE("SEND_TYPE_ARG3 ${SEND_TYPE_ARG3}") + #MESSAGE("SEND_TYPE_ARG4 ${SEND_TYPE_ARG4}") + #MESSAGE("SEND_TYPE_RETV ${SEND_TYPE_RETV}") + #MESSAGE("SEND_QUAL_ARG2 ${SEND_QUAL_ARG2}") + ENDIF(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") + + IF("${curl_cv_func_send_args}" STREQUAL "unknown") + MESSAGE(FATAL_ERROR "Cannot find proper types to use for send args") + ENDIF("${curl_cv_func_send_args}" STREQUAL "unknown") + SET(SEND_QUAL_ARG2 "const") +ELSE(curl_cv_send) + MESSAGE(FATAL_ERROR "Unable to link function send") +ENDIF(curl_cv_send) +SET(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send") +SET(HAVE_SEND 1) + +SET(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5") +CHECK_C_SOURCE_COMPILES("int flag = MSG_NOSIGNAL" HAVE_MSG_NOSIGNAL) + +SET(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2") +SET(HEADER_INCLUDES) +SET(headers_hack) +MACRO(add_header_include check header) + IF(${check}) + SET(headers_hack + "${headers_hack}\n#include <${header}>") + #SET(HEADER_INCLUDES + # ${HEADER_INCLUDES} + # "${header}") + ENDIF(${check}) +ENDMACRO(add_header_include header) +IF(HAVE_WINDOWS_H) + SET(EXTRA_DEFINES ${EXTRA_DEFINES} + "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3") + add_header_include(HAVE_WINDOWS_H "windows.h") + add_header_include(HAVE_WINSOCK2_H "winsock2.h") + add_header_include(HAVE_WINSOCK_H "winsock.h") +ELSE(HAVE_WINDOWS_H) + add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + add_header_include(HAVE_SYS_TIME_H "sys/time.h") + add_header_include(TIME_WITH_SYS_TIME "time.h") + add_header_include(HAVE_TIME_H "time.h") +ENDIF(HAVE_WINDOWS_H) +SET(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5") +CHECK_C_SOURCE_COMPILES("struct timeval ts;\nts.tv_sec = 0;\nts.tv_usec = 0" HAVE_STRUCT_TIMEVAL) + + +INCLUDE(CheckCSourceRuns) +SET(EXTRA_DEFINES) +SET(HEADER_INCLUDES) +IF(HAVE_SYS_POLL_H) + SET(HEADER_INCLUDES "sys/poll.h") +ENDIF(HAVE_SYS_POLL_H) +CHECK_C_SOURCE_RUNS("return poll((void *)0, 0, 10 /*ms*/)" HAVE_POLL_FINE) + +SET(HAVE_SIG_ATOMIC_T 1) +SET(EXTRA_DEFINES) +SET(HEADER_INCLUDES) +IF(HAVE_SIGNAL_H) + SET(HEADER_INCLUDES "signal.h") + SET(CMAKE_EXTRA_INCLUDE_FILES "signal.h") +ENDIF(HAVE_SIGNAL_H) +CHECK_TYPE_SIZE("sig_atomic_t" SIZEOF_SIG_ATOMIC_T) +IF(HAVE_SIZEOF_SIG_ATOMIC_T) + CHECK_C_SOURCE_COMPILES("static volatile sig_atomic_t dummy = 0" HAVE_SIG_ATOMIC_T_NOT_VOLATILE) + IF(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE) + SET(HAVE_SIG_ATOMIC_T_VOLATILE 1) + ENDIF(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE) +ENDIF(HAVE_SIZEOF_SIG_ATOMIC_T) + +SET(CHECK_TYPE_SIZE_PREINCLUDE + "#undef inline") + +IF(HAVE_WINDOWS_H) + SET(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE} + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include <windows.h>") + IF(HAVE_WINSOCK2_H) + SET(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE}\n#include <winsock2.h>") + ENDIF(HAVE_WINSOCK2_H) +ELSE(HAVE_WINDOWS_H) + IF(HAVE_SYS_SOCKET_H) + SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} + "sys/socket.h") + ENDIF(HAVE_SYS_SOCKET_H) + IF(HAVE_NETINET_IN_H) + SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} + "netinet/in.h") + ENDIF(HAVE_NETINET_IN_H) + IF(HAVE_ARPA_INET_H) + SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} + "arpa/inet.h") + ENDIF(HAVE_ARPA_INET_H) +ENDIF(HAVE_WINDOWS_H) +CHECK_TYPE_SIZE("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) +IF(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE) + SET(HAVE_STRUCT_SOCKADDR_STORAGE 1) +ENDIF(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE) + diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 3fcf926..c794737 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -5,7 +5,7 @@ INCLUDE_REGULAR_EXPRESSION("^.*$") # Setup package meta-data SET(PACKAGE "curl") -SET(VERSION "7.12.1") +SET(VERSION "7.16.1") SET(PACKAGE_TARNAME "curl") SET(PACKAGE_BUGREPORT " ") SET(PACKAGE_NAME "curl") @@ -38,6 +38,7 @@ INCLUDE (CheckSymbolExists) INCLUDE (CheckTypeSize) SET(libCurl_SRCS + # amigaos.c - does not build on AmigaOS base64.c connect.c content_encoding.c @@ -48,15 +49,15 @@ SET(libCurl_SRCS file.c formdata.c ftp.c - getdate.c getenv.c getinfo.c + gtls.c hash.c hostares.c hostasyn.c - hostip.c hostip4.c hostip6.c + hostip.c hostsyn.c hostthre.c http.c @@ -68,20 +69,33 @@ SET(libCurl_SRCS inet_ntop.c inet_pton.c krb4.c + ldap.c llist.c md5.c memdebug.c mprintf.c multi.c netrc.c + # nwlib.c - Not used + parsedate.c progress.c + security.c + select.c sendf.c share.c + socks.c speedcheck.c + splay.c + ssh.c + sslgen.c ssluse.c + strdup.c strequal.c strerror.c + # strtok.c - specify later + strtoofft.c telnet.c + tftp.c timeval.c transfer.c url.c @@ -152,13 +166,16 @@ IF(NOT CURL_SPECIAL_LIBZ) CHECK_LIBRARY_EXISTS_CONCAT("z" inflateEnd HAVE_LIBZ) ENDIF(NOT CURL_SPECIAL_LIBZ) -#OPTION(CMAKE_USE_OPENSSL "Use OpenSSL code. Experimental" OFF) +OPTION(CMAKE_USE_OPENSSL "Use OpenSSL code. Experimental" ON) MARK_AS_ADVANCED(CMAKE_USE_OPENSSL) IF(CMAKE_USE_OPENSSL) CHECK_LIBRARY_EXISTS_CONCAT("crypto" CRYPTO_lock HAVE_LIBCRYPTO) CHECK_LIBRARY_EXISTS_CONCAT("ssl" SSL_connect HAVE_LIBSSL) ENDIF(CMAKE_USE_OPENSSL) +# Check for idn +CHECK_LIBRARY_EXISTS_CONCAT("idn" idna_to_ascii_lz HAVE_LIBIDN) + # Check for symbol dlopen (same as HAVE_LIBDL) CHECK_LIBRARY_EXISTS("${CURL_LIBS}" dlopen "" HAVE_DLOPEN) @@ -202,7 +219,11 @@ MACRO(CHECK_INCLUDE_FILE_CONCAT FILE VARIABLE) ENDMACRO(CHECK_INCLUDE_FILE_CONCAT) # Check for header files +CHECK_INCLUDE_FILE_CONCAT("ws2tcpip.h" HAVE_WS2TCPIP_H) +CHECK_INCLUDE_FILE_CONCAT("winsock2.h" HAVE_WINSOCK2_H) CHECK_INCLUDE_FILE_CONCAT("stdio.h" HAVE_STDIO_H) +CHECK_INCLUDE_FILE_CONCAT("windows.h" HAVE_WINDOWS_H) +CHECK_INCLUDE_FILE_CONCAT("winsock.h" HAVE_WINSOCK_H) CHECK_INCLUDE_FILE_CONCAT("stddef.h" HAVE_STDDEF_H) CHECK_INCLUDE_FILE_CONCAT("sys/types.h" HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILE_CONCAT("inttypes.h" HAVE_INTTYPES_H) @@ -226,6 +247,7 @@ IF(CMAKE_USE_OPENSSL) CHECK_INCLUDE_FILE_CONCAT("openssl/ssl.h" HAVE_OPENSSL_SSL_H) CHECK_INCLUDE_FILE_CONCAT("openssl/err.h" HAVE_OPENSSL_ERR_H) CHECK_INCLUDE_FILE_CONCAT("openssl/rand.h" HAVE_OPENSSL_RAND_H) + CHECK_INCLUDE_FILE_CONCAT("openssl/pkcs12.h" HAVE_OPENSSL_PKCS12_H) ENDIF(CMAKE_USE_OPENSSL) IF(NOT CURL_SPECIAL_LIBZ) @@ -250,26 +272,35 @@ CHECK_INCLUDE_FILE_CONCAT("strings.h" HAVE_STRINGS_H) CHECK_INCLUDE_FILE_CONCAT("sys/param.h" HAVE_SYS_PARAM_H) CHECK_INCLUDE_FILE_CONCAT("sys/stat.h" HAVE_SYS_STAT_H) CHECK_INCLUDE_FILE_CONCAT("sys/time.h" HAVE_SYS_TIME_H) +CHECK_INCLUDE_FILE_CONCAT("sys/resource.h" HAVE_SYS_RESOURCE_H) CHECK_INCLUDE_FILE_CONCAT("termios.h" HAVE_TERMIOS_H) CHECK_INCLUDE_FILE_CONCAT("termio.h" HAVE_TERMIO_H) CHECK_INCLUDE_FILE_CONCAT("io.h" HAVE_IO_H) CHECK_INCLUDE_FILE_CONCAT("time.h" HAVE_TIME_H) CHECK_INCLUDE_FILE_CONCAT("unistd.h" HAVE_UNISTD_H) CHECK_INCLUDE_FILE_CONCAT("sys/utime.h" HAVE_SYS_UTIME_H) -CHECK_INCLUDE_FILE_CONCAT("winsock.h" HAVE_WINSOCK_H) CHECK_INCLUDE_FILE_CONCAT("sockio.h" HAVE_SOCKIO_H) CHECK_INCLUDE_FILE_CONCAT("sys/sockio.h" HAVE_SYS_SOCKIO_H) CHECK_INCLUDE_FILE_CONCAT("x509.h" HAVE_X509_H) +CHECK_INCLUDE_FILE_CONCAT("locale.h" HAVE_LOCALE_H) CHECK_INCLUDE_FILE_CONCAT("setjmp.h" HAVE_SETJMP_H) CHECK_INCLUDE_FILE_CONCAT("signal.h" HAVE_SIGNAL_H) CHECK_INCLUDE_FILE_CONCAT("sys/ioctl.h" HAVE_SYS_IOCTL_H) CHECK_INCLUDE_FILE_CONCAT("sys/utsname.h" HAVE_SYS_UTSNAME_H) +CHECK_INCLUDE_FILE_CONCAT("idn-free.h" HAVE_IDN_FREE_H) +CHECK_INCLUDE_FILE_CONCAT("idna.h" HAVE_IDNA_H) +CHECK_INCLUDE_FILE_CONCAT("tld.h" HAVE_TLD_H) +CHECK_INCLUDE_FILE_CONCAT("arpa/tftp.h" HAVE_ARPA_TFTP_H) +CHECK_INCLUDE_FILE_CONCAT("errno.h" HAVE_ERRNO_H) +CHECK_INCLUDE_FILE_CONCAT("libgen.h" HAVE_LIBGEN_H) +CHECK_INCLUDE_FILE_CONCAT("sys/filio.h" HAVE_SYS_FILIO_H) CHECK_TYPE_SIZE(size_t SIZEOF_SIZE_T) CHECK_TYPE_SIZE(ssize_t SIZEOF_SSIZE_T) CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) CHECK_TYPE_SIZE("long" SIZEOF_LONG) CHECK_TYPE_SIZE("__int64" SIZEOF___INT64) CHECK_TYPE_SIZE("long double" SIZEOF_LONG_DOUBLE) +CHECK_TYPE_SIZE("time_t" SIZEOF_TIME_T) IF(NOT HAVE_SIZEOF_SSIZE_T) IF(SIZEOF_LONG EQUAL SIZEOF_SIZE_T) SET(ssize_t long) @@ -281,11 +312,49 @@ ENDIF(NOT HAVE_SIZEOF_SSIZE_T) IF(HAVE_SIZEOF_LONG_LONG) SET(HAVE_LONGLONG 1) + SET(HAVE_LL 1) ENDIF(HAVE_SIZEOF_LONG_LONG) FIND_FILE(RANDOM_FILE urandom /dev) MARK_AS_ADVANCED(RANDOM_FILE) +#strtoll \ +#socket \ +#select \ +#strdup \ +#strstr \ +#strtok_r \ +#uname \ +#strcasecmp \ +#stricmp \ +#strcmpi \ +#gethostbyaddr \ +#gettimeofday \ +#inet_addr \ +#inet_ntoa \ +#inet_pton \ +#perror \ +#closesocket \ +#siginterrupt \ +#sigaction \ +#signal \ +#getpass_r \ +#strlcat \ +#getpwuid \ +#geteuid \ +#dlopen \ +#utime \ +#sigsetjmp \ +#basename \ +#setlocale \ +#ftruncate \ +#pipe \ +#poll \ +#getprotobyname \ +#getrlimit \ +#setrlimit \ +#fork + # Check for some functions that are used CHECK_SYMBOL_EXISTS(socket "${CURL_INCLUDES}" HAVE_SOCKET) CHECK_SYMBOL_EXISTS(poll "${CURL_INCLUDES}" HAVE_POLL) @@ -338,12 +407,22 @@ CHECK_SYMBOL_EXISTS(SIGALRM "${CURL_INCLUDES}" HAVE_SIGNAL_MACRO) IF(HAVE_SIGNAL_FUNC AND HAVE_SIGNAL_MACRO) SET(HAVE_SIGNAL 1) ENDIF(HAVE_SIGNAL_FUNC AND HAVE_SIGNAL_MACRO) -CHECK_SYMBOL_EXISTS(uname "${CURL_INCLUDES}" HAVE_UNAME) -CHECK_SYMBOL_EXISTS(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL) -CHECK_SYMBOL_EXISTS(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64) -CHECK_SYMBOL_EXISTS(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R) -CHECK_SYMBOL_EXISTS(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT) -CHECK_SYMBOL_EXISTS(perror "${CURL_INCLUDES}" HAVE_PERROR) +CHECK_SYMBOL_EXISTS(uname "${CURL_INCLUDES}" HAVE_UNAME) +CHECK_SYMBOL_EXISTS(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL) +CHECK_SYMBOL_EXISTS(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64) +CHECK_SYMBOL_EXISTS(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R) +CHECK_SYMBOL_EXISTS(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT) +CHECK_SYMBOL_EXISTS(perror "${CURL_INCLUDES}" HAVE_PERROR) +CHECK_SYMBOL_EXISTS(fork "${CURL_INCLUDES}" HAVE_FORK) +CHECK_SYMBOL_EXISTS(pipe "${CURL_INCLUDES}" HAVE_PIPE) +CHECK_SYMBOL_EXISTS(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE) +CHECK_SYMBOL_EXISTS(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME) +CHECK_SYMBOL_EXISTS(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT) +CHECK_SYMBOL_EXISTS(idn_free "${CURL_INCLUDES}" HAVE_IDN_FREE) +CHECK_SYMBOL_EXISTS(idna_strerror "${CURL_INCLUDES}" HAVE_IDNA_STRERROR) +CHECK_SYMBOL_EXISTS(tld_strerror "${CURL_INCLUDES}" HAVE_TLD_STRERROR) +CHECK_SYMBOL_EXISTS(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE) +CHECK_SYMBOL_EXISTS(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT) # only build compat strtok if we need to IF (NOT HAVE_STRTOK_R) @@ -588,11 +667,14 @@ IF(CMAKE_COMPILER_IS_GNUCC AND APPLE) COMPILE_FLAGS ${MPRINTF_COMPILE_FLAGS}) ENDIF(CMAKE_COMPILER_IS_GNUCC AND APPLE) +INCLUDE(CMake/OtherTests.cmake) + # The rest of the build INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR}) INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR}) -ADD_DEFINITIONS(-DHAVE_CONFIG_H) +ADD_DEFINITIONS(-DHAVE_CONFIG_H + -DCURL_STATICLIB) CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in ${LIBCURL_BINARY_DIR}/config.h) diff --git a/Utilities/cmcurl/Platforms/WindowsCache.cmake b/Utilities/cmcurl/Platforms/WindowsCache.cmake index d660c0a..a0ae5a6 100644 --- a/Utilities/cmcurl/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/Platforms/WindowsCache.cmake @@ -46,7 +46,6 @@ IF(NOT UNIX) SET(HAVE_TIME_H 1) SET(HAVE_UNISTD_H 0) SET(HAVE_UTIME_H 0) - SET(HAVE_WINSOCK_H 1) SET(HAVE_X509_H 0) SET(HAVE_ZLIB_H 0) diff --git a/Utilities/cmcurl/amigaos.c b/Utilities/cmcurl/amigaos.c index 16a7d5e..7106f8d 100644 --- a/Utilities/cmcurl/amigaos.c +++ b/Utilities/cmcurl/amigaos.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,28 +22,53 @@ ***************************************************************************/ #include "amigaos.h" -#include <stdio.h> /* for stderr */ +#include <amitcp/socketbasetags.h> struct Library *SocketBase = NULL; +extern int errno, h_errno; + +#ifdef __libnix__ +#include <stabs.h> +void __request(const char *msg); +#else +# define __request( msg ) Printf( msg "\n\a") +#endif void amiga_cleanup() { - if(SocketBase) + if(SocketBase) { CloseLibrary(SocketBase); - - SocketBase = NULL; + SocketBase = NULL; + } } BOOL amiga_init() { if(!SocketBase) SocketBase = OpenLibrary("bsdsocket.library", 4); - + if(!SocketBase) { - fprintf(stderr, "No TCP/IP Stack running!\n\a"); + __request("No TCP/IP Stack running!"); return FALSE; } - + + if(SocketBaseTags( + SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno, +// SBTM_SETVAL(SBTC_HERRNOLONGPTR), (ULONG) &h_errno, + SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "cURL", + TAG_DONE)) { + + __request("SocketBaseTags ERROR"); + return FALSE; + } + +#ifndef __libnix__ atexit(amiga_cleanup); +#endif + return TRUE; } + +#ifdef __libnix__ +ADD2EXIT(amiga_cleanup,-50); +#endif diff --git a/Utilities/cmcurl/amigaos.h b/Utilities/cmcurl/amigaos.h index 0196eec..e5786d4 100644 --- a/Utilities/cmcurl/amigaos.h +++ b/Utilities/cmcurl/amigaos.h @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,13 +32,19 @@ #include <proto/exec.h> #include <proto/dos.h> -#include <bsdsocket.h> +#include <sys/socket.h> #include "config-amigaos.h" -#define select(args...) WaitSelect( args, NULL) -#define inet_ntoa(x) Inet_NtoA( x ## .s_addr) -#define ioctl(a,b,c,d) IoctlSocket( (LONG)a, (ULONG)b, (char*)c) +#ifndef select +# define select(args...) WaitSelect( args, NULL) +#endif +#ifndef inet_ntoa +# define inet_ntoa(x) Inet_NtoA( x ## .s_addr) +#endif +#ifndef ioctl +# define ioctl(a,b,c,d) IoctlSocket( (LONG)a, (ULONG)b, (char*)c) +#endif #define _AMIGASF 1 extern void amiga_cleanup(); diff --git a/Utilities/cmcurl/arpa_telnet.h b/Utilities/cmcurl/arpa_telnet.h index 5359ff1..e6a04dc 100644 --- a/Utilities/cmcurl/arpa_telnet.h +++ b/Utilities/cmcurl/arpa_telnet.h @@ -39,7 +39,7 @@ /* * The telnet options represented as strings */ -static const char *telnetoptions[]= +static const char * const telnetoptions[]= { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", @@ -78,7 +78,7 @@ static const char *telnetoptions[]= /* * Then those numbers represented as strings: */ -static const char *telnetcmds[]= +static const char * const telnetcmds[]= { "EOF", "SUSP", "ABORT", "EOR", "SE", "NOP", "DMARK", "BRK", "IP", "AO", diff --git a/Utilities/cmcurl/base64.c b/Utilities/cmcurl/base64.c index 7f8ae86..aa03f83 100644 --- a/Utilities/cmcurl/base64.c +++ b/Utilities/cmcurl/base64.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,28 +40,27 @@ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> +#include "urldata.h" /* for the SessionHandle definition */ +#include "easyif.h" /* for Curl_convert_... prototypes */ #include "base64.h" -#include "curl_memory.h" +#include "memory.h" /* include memdebug.h last */ #include "memdebug.h" +/* ---- Base64 Encoding/Decoding Table --- */ +static const char table64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static void decodeQuantum(unsigned char *dest, const char *src) { unsigned int x = 0; int i; + char *found; + for(i = 0; i < 4; i++) { - if(src[i] >= 'A' && src[i] <= 'Z') - x = (x << 6) + (unsigned int)(src[i] - 'A' + 0); - else if(src[i] >= 'a' && src[i] <= 'z') - x = (x << 6) + (unsigned int)(src[i] - 'a' + 26); - else if(src[i] >= '0' && src[i] <= '9') - x = (x << 6) + (unsigned int)(src[i] - '0' + 52); - else if(src[i] == '+') - x = (x << 6) + 62; - else if(src[i] == '/') - x = (x << 6) + 63; + if((found = strchr(table64, src[i]))) + x = (x << 6) + (unsigned int)(found - table64); else if(src[i] == '=') x = (x << 6); } @@ -76,43 +75,63 @@ static void decodeQuantum(unsigned char *dest, const char *src) /* * Curl_base64_decode() * - * Given a base64 string at src, decode it into the memory pointed to by - * dest. Returns the length of the decoded data. + * Given a base64 string at src, decode it and return an allocated memory in + * the *outptr. Returns the length of the decoded data. */ -size_t Curl_base64_decode(const char *src, char *dest) +size_t Curl_base64_decode(const char *src, unsigned char **outptr) { int length = 0; int equalsTerm = 0; int i; int numQuantums; unsigned char lastQuantum[3]; - size_t rawlen; + size_t rawlen=0; + unsigned char *newstr; + + *outptr = NULL; while((src[length] != '=') && src[length]) length++; - while(src[length+equalsTerm] == '=') + /* A maximum of two = padding characters is allowed */ + if(src[length] == '=') { equalsTerm++; - + if(src[length+equalsTerm] == '=') + equalsTerm++; + } numQuantums = (length + equalsTerm) / 4; + /* Don't allocate a buffer if the decoded length is 0 */ + if (numQuantums <= 0) + return 0; + rawlen = (numQuantums * 3) - equalsTerm; + /* The buffer must be large enough to make room for the last quantum + (which may be partially thrown out) and the zero terminator. */ + newstr = malloc(rawlen+4); + if(!newstr) + return 0; + + *outptr = newstr; + + /* Decode all but the last quantum (which may not decode to a + multiple of 3 bytes) */ for(i = 0; i < numQuantums - 1; i++) { - decodeQuantum((unsigned char *)dest, src); - dest += 3; src += 4; + decodeQuantum((unsigned char *)newstr, src); + newstr += 3; src += 4; } + /* This final decode may actually read slightly past the end of the buffer + if the input string is missing pad bytes. This will almost always be + harmless. */ decodeQuantum(lastQuantum, src); for(i = 0; i < 3 - equalsTerm; i++) - dest[i] = lastQuantum[i]; + newstr[i] = lastQuantum[i]; + newstr[i] = 0; /* zero terminate */ return rawlen; } -/* ---- Base64 Encoding --- */ -static char table64[]= - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - /* * Curl_base64_encode() * @@ -121,7 +140,8 @@ static char table64[]= * went wrong, -1 is returned. * */ -size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) +size_t Curl_base64_encode(struct SessionHandle *data, + const char *inp, size_t insize, char **outptr) { unsigned char ibuf[3]; unsigned char obuf[4]; @@ -129,6 +149,9 @@ size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) int inputparts; char *output; char *base64data; +#ifdef CURL_DOES_CONVERSIONS + char *convbuf; +#endif char *indata = (char *)inp; @@ -141,6 +164,28 @@ size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) 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 = (char*)malloc(insize); + if(!convbuf) { + return 0; + } + memcpy(convbuf, indata, insize); + if(CURLE_OK != Curl_convert_to_network(data, convbuf, insize)) { + free(convbuf); + 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) { @@ -153,10 +198,12 @@ size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) ibuf[i] = 0; } - obuf [0] = (ibuf [0] & 0xFC) >> 2; - obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4); - obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6); - obuf [3] = ibuf [2] & 0x3F; + 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 */ @@ -183,6 +230,10 @@ size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr) *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 */ } /* ---- End of Base64 Encoding ---- */ @@ -205,14 +256,26 @@ int main(int argc, char **argv, char **envp) size_t base64Len; unsigned char *data; int dataLen; + struct SessionHandle *handle = NULL; +#ifdef CURL_DOES_CONVERSIONS + /* get a Curl handle so Curl_base64_encode can translate properly */ + handle = curl_easy_init(); + if(handle == NULL) { + fprintf(stderr, "Error: curl_easy_init failed\n"); + return 0; + } +#endif data = (unsigned char *)suck(&dataLen); - base64Len = Curl_base64_encode(data, dataLen, &base64); + base64Len = Curl_base64_encode(handle, data, dataLen, &base64); fprintf(stderr, "%d\n", base64Len); - fprintf(stdout, "%s", base64); + fprintf(stdout, "%s\n", base64); free(base64); free(data); +#ifdef CURL_DOES_CONVERSIONS + curl_easy_cleanup(handle); +#endif return 0; } #endif @@ -235,10 +298,17 @@ int main(int argc, char **argv, char **envp) unsigned char *data; int dataLen; int i, j; +#ifdef CURL_DOES_CONVERSIONS + /* get a Curl handle so main can translate properly */ + struct SessionHandle *handle = curl_easy_init(); + if(handle == NULL) { + fprintf(stderr, "Error: curl_easy_init failed\n"); + return 0; + } +#endif base64 = (char *)suck(&base64Len); - data = (unsigned char *)malloc(base64Len * 3/4 + 8); - dataLen = Curl_base64_decode(base64, data); + dataLen = Curl_base64_decode(base64, &data); fprintf(stderr, "%d\n", dataLen); @@ -253,13 +323,21 @@ int main(int argc, char **argv, char **envp) printf(" | "); for(j=0; j < 0x10; j++) - if((j+i) < dataLen) - printf("%c", isgraph(data[i+j])?data[i+j]:'.'); - else + if((j+i) < dataLen) { +#ifdef CURL_DOES_CONVERSIONS + if(CURLE_OK != + Curl_convert_from_network(handle, &data[i+j], (size_t)1)) + data[i+j] = '.'; +#endif /* CURL_DOES_CONVERSIONS */ + printf("%c", ISGRAPH(data[i+j])?data[i+j]:'.'); + } else break; puts(""); } +#ifdef CURL_DOES_CONVERSIONS + curl_easy_cleanup(handle); +#endif free(base64); free(data); return 0; } diff --git a/Utilities/cmcurl/base64.h b/Utilities/cmcurl/base64.h index 307148e..59742bc 100644 --- a/Utilities/cmcurl/base64.h +++ b/Utilities/cmcurl/base64.h @@ -1,18 +1,18 @@ #ifndef __BASE64_H #define __BASE64_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -22,6 +22,7 @@ * * $Id$ ***************************************************************************/ -size_t Curl_base64_encode(const char *input, size_t size, char **str); -size_t Curl_base64_decode(const char *source, char *dest); +size_t Curl_base64_encode(struct SessionHandle *data, + const char *input, size_t size, char **str); +size_t Curl_base64_decode(const char *source, unsigned char **outptr); #endif diff --git a/Utilities/cmcurl/config.h.in b/Utilities/cmcurl/config.h.in index 1086027..c8ae15e 100644 --- a/Utilities/cmcurl/config.h.in +++ b/Utilities/cmcurl/config.h.in @@ -1,4 +1,13 @@ -/* lib/config.h.in. Generated from configure.in by autoheader. */ +/* lib/config.h.in. Generated from configure.ac by autoheader. */ + +/* when building libcurl itself */ +#cmakedefine BUILDING_LIBCURL ${BUILDING_LIBCURL} + +/* to disable cookies support */ +#cmakedefine CURL_DISABLE_COOKIES ${CURL_DISABLE_COOKIES} + +/* to disable cryptographic authentication */ +#cmakedefine CURL_DISABLE_CRYPTO_AUTH ${CURL_DISABLE_CRYPTO_AUTH} /* to disable DICT */ #cmakedefine CURL_DISABLE_DICT ${CURL_DISABLE_DICT} @@ -9,9 +18,6 @@ /* to disable FTP */ #cmakedefine CURL_DISABLE_FTP ${CURL_DISABLE_FTP} -/* to disable GOPHER */ -#cmakedefine CURL_DISABLE_GOPHER ${CURL_DISABLE_GOPHER} - /* to disable HTTP */ #cmakedefine CURL_DISABLE_HTTP ${CURL_DISABLE_HTTP} @@ -21,24 +27,66 @@ /* to disable TELNET */ #cmakedefine CURL_DISABLE_TELNET ${CURL_DISABLE_TELNET} +/* to disable TFTP */ +#cmakedefine CURL_DISABLE_TFTP ${CURL_DISABLE_TFTP} + +/* to disable verbose strings */ +#cmakedefine CURL_DISABLE_VERBOSE_STRINGS ${CURL_DISABLE_VERBOSE_STRINGS} + +/* to make a symbol visible */ +#cmakedefine CURL_EXTERN_SYMBOL ${CURL_EXTERN_SYMBOL} + +/* to enable hidden symbols */ +#cmakedefine CURL_HIDDEN_SYMBOLS ${CURL_HIDDEN_SYMBOLS} + +/* when not building a shared library */ +#cmakedefine CURL_STATICLIB ${CURL_STATICLIB} + /* Set to explicitly specify we don't want to use thread-safe functions */ #cmakedefine DISABLED_THREADSAFE ${DISABLED_THREADSAFE} +/* lber dynamic library file */ +#cmakedefine DL_LBER_FILE ${DL_LBER_FILE} + +/* ldap dynamic library file */ +#cmakedefine DL_LDAP_FILE ${DL_LDAP_FILE} + /* your Entropy Gathering Daemon socket pathname */ #cmakedefine EGD_SOCKET ${EGD_SOCKET} /* Define if you want to enable IPv6 support */ #cmakedefine ENABLE_IPV6 ${ENABLE_IPV6} +/* Define to the type qualifier of arg 1 for getnameinfo. */ +#cmakedefine GETNAMEINFO_QUAL_ARG1 ${GETNAMEINFO_QUAL_ARG1} + +/* Define to the type of arg 1 for getnameinfo. */ +#cmakedefine GETNAMEINFO_TYPE_ARG1 ${GETNAMEINFO_TYPE_ARG1} + +/* Define to the type of arg 2 for getnameinfo. */ +#cmakedefine GETNAMEINFO_TYPE_ARG2 ${GETNAMEINFO_TYPE_ARG2} + +/* Define to the type of args 4 and 6 for getnameinfo. */ +#cmakedefine GETNAMEINFO_TYPE_ARG46 ${GETNAMEINFO_TYPE_ARG46} + +/* Define to the type of arg 7 for getnameinfo. */ +#cmakedefine GETNAMEINFO_TYPE_ARG7 ${GETNAMEINFO_TYPE_ARG7} + /* Define to 1 if you have the <alloca.h> header file. */ #cmakedefine HAVE_ALLOCA_H ${HAVE_ALLOCA_H} /* Define to 1 if you have the <arpa/inet.h> header file. */ #cmakedefine HAVE_ARPA_INET_H ${HAVE_ARPA_INET_H} +/* Define to 1 if you have the <arpa/tftp.h> header file. */ +#cmakedefine HAVE_ARPA_TFTP_H ${HAVE_ARPA_TFTP_H} + /* Define to 1 if you have the <assert.h> header file. */ #cmakedefine HAVE_ASSERT_H ${HAVE_ASSERT_H} +/* Define to 1 if you have the `basename' function. */ +#cmakedefine HAVE_BASENAME ${HAVE_BASENAME} + /* Define to 1 if you have the `closesocket' function. */ #cmakedefine HAVE_CLOSESOCKET ${HAVE_CLOSESOCKET} @@ -60,6 +108,12 @@ /* Define to 1 if you have the `dlopen' function. */ #cmakedefine HAVE_DLOPEN ${HAVE_DLOPEN} +/* Define to 1 if you have the `ENGINE_load_builtin_engines' function. */ +#cmakedefine HAVE_ENGINE_LOAD_BUILTIN_ENGINES ${HAVE_ENGINE_LOAD_BUILTIN_ENGINES} + +/* Define to 1 if you have the <errno.h> header file. */ +#cmakedefine HAVE_ERRNO_H ${HAVE_ERRNO_H} + /* Define to 1 if you have the <err.h> header file. */ #cmakedefine HAVE_ERR_H ${HAVE_ERR_H} @@ -69,6 +123,12 @@ /* use FIONBIO for non-blocking sockets */ #cmakedefine HAVE_FIONBIO ${HAVE_FIONBIO} +/* Define to 1 if you have the `fork' function. */ +#cmakedefine HAVE_FORK ${HAVE_FORK} + +/* Define to 1 if you have the `ftruncate' function. */ +#cmakedefine HAVE_FTRUNCATE ${HAVE_FTRUNCATE} + /* Define if getaddrinfo exists and works */ #cmakedefine HAVE_GETADDRINFO ${HAVE_GETADDRINFO} @@ -93,12 +153,21 @@ /* gethostbyname_r() takes 6 args */ #cmakedefine HAVE_GETHOSTBYNAME_R_6 ${HAVE_GETHOSTBYNAME_R_6} +/* Define to 1 if you have the getnameinfo function. */ +#cmakedefine HAVE_GETNAMEINFO ${HAVE_GETNAMEINFO} + /* Define to 1 if you have the `getpass_r' function. */ #cmakedefine HAVE_GETPASS_R ${HAVE_GETPASS_R} +/* Define to 1 if you have the `getprotobyname' function. */ +#cmakedefine HAVE_GETPROTOBYNAME ${HAVE_GETPROTOBYNAME} + /* Define to 1 if you have the `getpwuid' function. */ #cmakedefine HAVE_GETPWUID ${HAVE_GETPWUID} +/* Define to 1 if you have the `getrlimit' function. */ +#cmakedefine HAVE_GETRLIMIT ${HAVE_GETRLIMIT} + /* Define to 1 if you have the `gettimeofday' function. */ #cmakedefine HAVE_GETTIMEOFDAY ${HAVE_GETTIMEOFDAY} @@ -111,12 +180,18 @@ /* if you have the gssapi libraries */ #cmakedefine HAVE_GSSAPI ${HAVE_GSSAPI} +/* if you have the GNU gssapi libraries */ +#cmakedefine HAVE_GSSGNU ${HAVE_GSSGNU} + /* if you have the Heimdal gssapi libraries */ #cmakedefine HAVE_GSSHEIMDAL ${HAVE_GSSHEIMDAL} /* if you have the MIT gssapi libraries */ #cmakedefine HAVE_GSSMIT ${HAVE_GSSMIT} +/* Define to 1 if you have the `idna_strerror' function. */ +#cmakedefine HAVE_IDNA_STRERROR ${HAVE_IDNA_STRERROR} + /* Define to 1 if you have the `idn_free' function. */ #cmakedefine HAVE_IDN_FREE ${HAVE_IDN_FREE} @@ -159,12 +234,12 @@ /* Define to 1 if you have the <krb.h> header file. */ #cmakedefine HAVE_KRB_H ${HAVE_KRB_H} -/* Define to 1 if you have the `crypto' library (-lcrypto). */ -#cmakedefine HAVE_LIBCRYPTO ${HAVE_LIBCRYPTO} - /* Define to 1 if you have the `dl' library (-ldl). */ #cmakedefine HAVE_LIBDL ${HAVE_LIBDL} +/* Define to 1 if you have the <libgen.h> header file. */ +#cmakedefine HAVE_LIBGEN_H ${HAVE_LIBGEN_H} + /* Define to 1 if you have the `idn' library (-lidn). */ #cmakedefine HAVE_LIBIDN ${HAVE_LIBIDN} @@ -177,6 +252,12 @@ /* Define to 1 if you have the `socket' library (-lsocket). */ #cmakedefine HAVE_LIBSOCKET ${HAVE_LIBSOCKET} +/* Define to 1 if you have the `ssh2' library (-lssh2). */ +#cmakedefine HAVE_LIBSSH2 ${HAVE_LIBSSH2} + +/* Define to 1 if you have the <libssh2.h> header file. */ +#cmakedefine HAVE_LIBSSH2_H ${HAVE_LIBSSH2_H} + /* Define to 1 if you have the `ssl' library (-lssl). */ #cmakedefine HAVE_LIBSSL ${HAVE_LIBSSL} @@ -186,18 +267,27 @@ /* Define to 1 if you have the <limits.h> header file. */ #cmakedefine HAVE_LIMITS_H ${HAVE_LIMITS_H} +/* if your compiler supports LL */ +#cmakedefine HAVE_LL ${HAVE_LL} + +/* Define to 1 if you have the <locale.h> header file. */ +#cmakedefine HAVE_LOCALE_H ${HAVE_LOCALE_H} + /* Define to 1 if you have the `localtime_r' function. */ #cmakedefine HAVE_LOCALTIME_R ${HAVE_LOCALTIME_R} -/* if your compiler supports 'long long' */ +/* if your compiler supports long long */ #cmakedefine HAVE_LONGLONG ${HAVE_LONGLONG} -/* Define to 1 if you have the <malloc.h> header file. */ +/* Define to 1 if you have the malloc.h header file. */ #cmakedefine HAVE_MALLOC_H ${HAVE_MALLOC_H} /* Define to 1 if you have the <memory.h> header file. */ #cmakedefine HAVE_MEMORY_H ${HAVE_MEMORY_H} +/* Define to 1 if you have the MSG_NOSIGNAL flag. */ +#cmakedefine HAVE_MSG_NOSIGNAL ${HAVE_MSG_NOSIGNAL} + /* Define to 1 if you have the <netdb.h> header file. */ #cmakedefine HAVE_NETDB_H ${HAVE_NETDB_H} @@ -210,9 +300,12 @@ /* Define to 1 if you have the <net/if.h> header file. */ #cmakedefine HAVE_NET_IF_H ${HAVE_NET_IF_H} -/* Define if NI_WITHSCOPEID exists and works */ +/* Define to 1 if NI_WITHSCOPEID exists and works. */ #cmakedefine HAVE_NI_WITHSCOPEID ${HAVE_NI_WITHSCOPEID} +/* Defined if no inet_pton() prototype available */ +#cmakedefine HAVE_NO_INET_PTON_PROTO ${HAVE_NO_INET_PTON_PROTO} + /* we have no strerror_r() proto */ #cmakedefine HAVE_NO_STRERROR_R_DECL ${HAVE_NO_STRERROR_R_DECL} @@ -228,6 +321,9 @@ /* Define to 1 if you have the <openssl/pem.h> header file. */ #cmakedefine HAVE_OPENSSL_PEM_H ${HAVE_OPENSSL_PEM_H} +/* Define to 1 if you have the <openssl/pkcs12.h> header file. */ +#cmakedefine HAVE_OPENSSL_PKCS12_H ${HAVE_OPENSSL_PKCS12_H} + /* Define to 1 if you have the <openssl/rsa.h> header file. */ #cmakedefine HAVE_OPENSSL_RSA_H ${HAVE_OPENSSL_RSA_H} @@ -246,6 +342,9 @@ /* Define to 1 if you have the `perror' function. */ #cmakedefine HAVE_PERROR ${HAVE_PERROR} +/* Define to 1 if you have the `pipe' function. */ +#cmakedefine HAVE_PIPE ${HAVE_PIPE} + /* Define to 1 if you have the `poll' function. */ #cmakedefine HAVE_POLL ${HAVE_POLL} @@ -267,15 +366,27 @@ /* Define to 1 if you have the `RAND_status' function. */ #cmakedefine HAVE_RAND_STATUS ${HAVE_RAND_STATUS} +/* Define to 1 if you have the recv function. */ +#cmakedefine HAVE_RECV ${HAVE_RECV} + /* Define to 1 if you have the <rsa.h> header file. */ #cmakedefine HAVE_RSA_H ${HAVE_RSA_H} -/* Define to 1 if you have the `select' function. */ +/* Define to 1 if you have the select function. */ #cmakedefine HAVE_SELECT ${HAVE_SELECT} +/* Define to 1 if you have the send function. */ +#cmakedefine HAVE_SEND ${HAVE_SEND} + /* Define to 1 if you have the <setjmp.h> header file. */ #cmakedefine HAVE_SETJMP_H ${HAVE_SETJMP_H} +/* Define to 1 if you have the `setlocale' function. */ +#cmakedefine HAVE_SETLOCALE ${HAVE_SETLOCALE} + +/* Define to 1 if you have the `setrlimit' function. */ +#cmakedefine HAVE_SETRLIMIT ${HAVE_SETRLIMIT} + /* Define to 1 if you have the <sgtty.h> header file. */ #cmakedefine HAVE_SGTTY_H ${HAVE_SGTTY_H} @@ -288,9 +399,18 @@ /* Define to 1 if you have the `signal' function. */ #cmakedefine HAVE_SIGNAL ${HAVE_SIGNAL} +/* Define to 1 if you have the <signal.h> header file. */ +#cmakedefine HAVE_SIGNAL_H ${HAVE_SIGNAL_H} + /* If you have sigsetjmp */ #cmakedefine HAVE_SIGSETJMP ${HAVE_SIGSETJMP} +/* Define to 1 if sig_atomic_t is an available typedef. */ +#cmakedefine HAVE_SIG_ATOMIC_T ${HAVE_SIG_ATOMIC_T} + +/* Define to 1 if sig_atomic_t is already defined as volatile. */ +#cmakedefine HAVE_SIG_ATOMIC_T_VOLATILE ${HAVE_SIG_ATOMIC_T_VOLATILE} + /* Define to 1 if you have the `socket' function. */ #cmakedefine HAVE_SOCKET ${HAVE_SOCKET} @@ -306,6 +426,9 @@ /* Define to 1 if you have the <stdint.h> header file. */ #cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H} +/* Define to 1 if you have the <stdio.h> header file. */ +#cmakedefine HAVE_STDIO_H ${HAVE_STDIO_H} + /* Define to 1 if you have the <stdlib.h> header file. */ #cmakedefine HAVE_STDLIB_H ${HAVE_STDLIB_H} @@ -321,9 +444,6 @@ /* Define to 1 if you have the `strerror_r' function. */ #cmakedefine HAVE_STRERROR_R ${HAVE_STRERROR_R} -/* Define to 1 if you have the `strftime' function. */ -#cmakedefine HAVE_STRFTIME ${HAVE_STRFTIME} - /* Define to 1 if you have the `stricmp' function. */ #cmakedefine HAVE_STRICMP ${HAVE_STRICMP} @@ -348,6 +468,15 @@ /* Define to 1 if you have the `strtoll' function. */ #cmakedefine HAVE_STRTOLL ${HAVE_STRTOLL} +/* if struct sockaddr_storage is defined */ +#cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_STRUCT_SOCKADDR_STORAGE} + +/* Define to 1 if you have the timeval struct. */ +#cmakedefine HAVE_STRUCT_TIMEVAL ${HAVE_STRUCT_TIMEVAL} + +/* Define to 1 if you have the <sys/filio.h> header file. */ +#cmakedefine HAVE_SYS_FILIO_H ${HAVE_SYS_FILIO_H} + /* Define to 1 if you have the <sys/ioctl.h> header file. */ #cmakedefine HAVE_SYS_IOCTL_H ${HAVE_SYS_IOCTL_H} @@ -357,6 +486,9 @@ /* Define to 1 if you have the <sys/poll.h> header file. */ #cmakedefine HAVE_SYS_POLL_H ${HAVE_SYS_POLL_H} +/* Define to 1 if you have the <sys/resource.h> header file. */ +#cmakedefine HAVE_SYS_RESOURCE_H ${HAVE_SYS_RESOURCE_H} + /* Define to 1 if you have the <sys/select.h> header file. */ #cmakedefine HAVE_SYS_SELECT_H ${HAVE_SYS_SELECT_H} @@ -378,12 +510,6 @@ /* Define to 1 if you have the <sys/utime.h> header file. */ #cmakedefine HAVE_SYS_UTIME_H ${HAVE_SYS_UTIME_H} -/* Define to 1 if you have the `tcgetattr' function. */ -#cmakedefine HAVE_TCGETATTR ${HAVE_TCGETATTR} - -/* Define to 1 if you have the `tcsetattr' function. */ -#cmakedefine HAVE_TCSETATTR ${HAVE_TCSETATTR} - /* Define to 1 if you have the <termios.h> header file. */ #cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H} @@ -393,6 +519,12 @@ /* Define to 1 if you have the <time.h> header file. */ #cmakedefine HAVE_TIME_H ${HAVE_TIME_H} +/* Define to 1 if you have the <tld.h> header file. */ +#cmakedefine HAVE_TLD_H ${HAVE_TLD_H} + +/* Define to 1 if you have the `tld_strerror' function. */ +#cmakedefine HAVE_TLD_STRERROR ${HAVE_TLD_STRERROR} + /* Define to 1 if you have the `uname' function. */ #cmakedefine HAVE_UNAME ${HAVE_UNAME} @@ -405,44 +537,74 @@ /* Define to 1 if you have the <utime.h> header file. */ #cmakedefine HAVE_UTIME_H ${HAVE_UTIME_H} -/* Define to 1 if you have the <winsock.h> header file. */ +/* Define to 1 if you have the windows.h header file. */ +#cmakedefine HAVE_WINDOWS_H ${HAVE_WINDOWS_H} + +/* Define to 1 if you have the winsock2.h header file. */ +#cmakedefine HAVE_WINSOCK2_H ${HAVE_WINSOCK2_H} + +/* Define to 1 if you have the winsock.h header file. */ #cmakedefine HAVE_WINSOCK_H ${HAVE_WINSOCK_H} /* Define this symbol if your OS supports changing the contents of argv */ #cmakedefine HAVE_WRITABLE_ARGV ${HAVE_WRITABLE_ARGV} +/* Define to 1 if you have the ws2tcpip.h header file. */ +#cmakedefine HAVE_WS2TCPIP_H ${HAVE_WS2TCPIP_H} + /* Define to 1 if you have the <x509.h> header file. */ #cmakedefine HAVE_X509_H ${HAVE_X509_H} /* if you have the zlib.h header file */ #cmakedefine HAVE_ZLIB_H ${HAVE_ZLIB_H} +/* If you lack a fine basename() prototype */ +#cmakedefine NEED_BASENAME_PROTO ${NEED_BASENAME_PROTO} + +/* Define to 1 if you need the malloc.h header file even with stdlib.h */ +#cmakedefine NEED_MALLOC_H ${NEED_MALLOC_H} + /* need REENTRANT defined */ #cmakedefine NEED_REENTRANT ${NEED_REENTRANT} /* cpu-machine-OS */ -#define OS "${OPERATING_SYSTEM}" +#define OS "${OPERATING_SYSTEM}" /* Name of package */ -#cmakedefine PACKAGE "${PACKAGE}" +#cmakedefine PACKAGE "${PACKAGE}" /* Define to the address where bug reports for this package should be sent. */ -#cmakedefine PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" +#cmakedefine PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" /* Define to the full name of this package. */ -#cmakedefine PACKAGE_NAME "${PACKAGE_NAME}" +#cmakedefine PACKAGE_NAME "${PACKAGE_NAME}" /* Define to the full name and version of this package. */ -#cmakedefine PACKAGE_STRING "${PACKAGE_STRING}" +#cmakedefine PACKAGE_STRING "${PACKAGE_STRING}" /* Define to the one symbol short name of this package. */ -#cmakedefine PACKAGE_TARNAME "${PACKAGE_TARNAME}" +#cmakedefine PACKAGE_TARNAME "${PACKAGE_TARNAME}" /* Define to the version of this package. */ -#cmakedefine PACKAGE_VERSION "${PACKAGE_VERSION}" +#cmakedefine PACKAGE_VERSION "${PACKAGE_VERSION}" /* a suitable file to read random data from */ -#cmakedefine RANDOM_FILE "${RANDOM_FILE}" +#cmakedefine RANDOM_FILE "${RANDOM_FILE}" + +/* Define to the type of arg 1 for recv. */ +#cmakedefine RECV_TYPE_ARG1 ${RECV_TYPE_ARG1} + +/* Define to the type of arg 2 for recv. */ +#cmakedefine RECV_TYPE_ARG2 ${RECV_TYPE_ARG2} + +/* Define to the type of arg 3 for recv. */ +#cmakedefine RECV_TYPE_ARG3 ${RECV_TYPE_ARG3} + +/* Define to the type of arg 4 for recv. */ +#cmakedefine RECV_TYPE_ARG4 ${RECV_TYPE_ARG4} + +/* Define to the function return type for recv. */ +#cmakedefine RECV_TYPE_RETV ${RECV_TYPE_RETV} /* Define as the return type of signal handlers (`int' or `void'). */ #cmakedefine RETSIGTYPE ${RETSIGTYPE} @@ -456,12 +618,36 @@ /* Define to the type of arg 5 for `select'. */ #cmakedefine SELECT_TYPE_ARG5 ${SELECT_TYPE_ARG5} -/* The size of a `curl_off_t', as computed by sizeof. */ +/* Define to the type qualifier of arg 2 for send. */ +#cmakedefine SEND_QUAL_ARG2 ${SEND_QUAL_ARG2} + +/* Define to the type of arg 1 for send. */ +#cmakedefine SEND_TYPE_ARG1 ${SEND_TYPE_ARG1} + +/* Define to the type of arg 2 for send. */ +#cmakedefine SEND_TYPE_ARG2 ${SEND_TYPE_ARG2} + +/* Define to the type of arg 3 for send. */ +#cmakedefine SEND_TYPE_ARG3 ${SEND_TYPE_ARG3} + +/* Define to the type of arg 4 for send. */ +#cmakedefine SEND_TYPE_ARG4 ${SEND_TYPE_ARG4} + +/* Define to the function return type for send. */ +#cmakedefine SEND_TYPE_RETV ${SEND_TYPE_RETV} + +/* The size of `curl_off_t', as computed by sizeof. */ #cmakedefine SIZEOF_CURL_OFF_T ${SIZEOF_CURL_OFF_T} -/* The size of a `size_t', as computed by sizeof. */ +/* The size of `long', as computed by sizeof. */ +#cmakedefine SIZEOF_LONG ${SIZEOF_LONG} + +/* The size of `size_t', as computed by sizeof. */ #cmakedefine SIZEOF_SIZE_T ${SIZEOF_SIZE_T} +/* The size of `time_t', as computed by sizeof. */ +#cmakedefine SIZEOF_TIME_T ${SIZEOF_TIME_T} + /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS ${STDC_HEADERS} @@ -471,11 +657,29 @@ /* Define if you want to enable ares support */ #cmakedefine USE_ARES ${USE_ARES} +/* if GnuTLS is enabled */ +#cmakedefine USE_GNUTLS ${USE_GNUTLS} + +/* if libSSH2 is in use */ +#cmakedefine USE_LIBSSH2 ${USE_LIBSSH2} + /* If you want to build curl with the built-in manual */ #cmakedefine USE_MANUAL ${USE_MANUAL} +/* if OpenSSL is in use */ +#cmakedefine USE_OPENSSL ${USE_OPENSSL} + +/* if SSL is enabled */ +#cmakedefine USE_SSLEAY ${USE_SSLEAY} + +/* to enable SSPI support */ +#cmakedefine USE_WINDOWS_SSPI ${USE_WINDOWS_SSPI} + /* Version number of package */ -#cmakedefine VERSION "${VERSION}" +#cmakedefine VERSION "${VERSION}" + +/* Define to avoid automatic inclusion of winsock.h */ +#cmakedefine WIN32_LEAN_AND_MEAN ${WIN32_LEAN_AND_MEAN} /* Define to 1 if on AIX 3. System headers sometimes define this. @@ -490,13 +694,16 @@ /* Define for large files, on AIX-style hosts. */ #cmakedefine _LARGE_FILES ${_LARGE_FILES} +/* define this if you need it to compile thread-safe code */ +#cmakedefine _THREAD_SAFE ${_THREAD_SAFE} + /* Define to empty if `const' does not conform to ANSI C. */ #cmakedefine const ${const} /* type to use in place of in_addr_t if not defined */ #cmakedefine in_addr_t ${in_addr_t} -/* Define to `unsigned' if <sys/types.h> does not define. */ +/* Define to `unsigned int' if <sys/types.h> does not define. */ #cmakedefine size_t ${size_t} /* type to use in place of socklen_t if not defined */ @@ -505,8 +712,5 @@ /* the signed version of size_t */ #cmakedefine ssize_t ${ssize_t} -/* define if the compiler supports number 0x3627676LL */ -#cmakedefine HAVE_LONG_LONG_CONSTANT ${HAVE_LONG_LONG_CONSTANT} - /* Special handling of zlib library */ #cmakedefine CURL_SPECIAL_ZLIB_H "${CURL_SPECIAL_ZLIB_H}" diff --git a/Utilities/cmcurl/connect.c b/Utilities/cmcurl/connect.c index c107354..2b38972 100644 --- a/Utilities/cmcurl/connect.c +++ b/Utilities/cmcurl/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,7 +25,9 @@ #ifndef WIN32 /* headers for non-win32 */ +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -67,7 +69,7 @@ #undef in_addr_t #define in_addr_t unsigned long #endif -#ifdef VMS +#ifdef VMS #include <in.h> #include <inet.h> #endif @@ -82,8 +84,7 @@ #define FALSE 0 #endif -#ifdef WIN32 -#include <windows.h> +#ifdef USE_WINSOCK #define EINPROGRESS WSAEINPROGRESS #define EWOULDBLOCK WSAEWOULDBLOCK #define EISCONN WSAEISCONN @@ -96,7 +97,12 @@ #include "if2ip.h" #include "strerror.h" #include "connect.h" -#include "curl_memory.h" +#include "memory.h" +#include "select.h" +#include "url.h" /* for Curl_safefree() */ +#include "multiif.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "inet_ntop.h" /* The last #include file should be: */ #include "memdebug.h" @@ -105,18 +111,18 @@ static bool verifyconnect(curl_socket_t sockfd, int *error); static curl_socket_t singleipconnect(struct connectdata *conn, - Curl_addrinfo *ai, /* start connecting to this */ + const Curl_addrinfo *ai, /* start connecting to this */ long timeout_ms, bool *connected); /* - * Curl_ourerrno() returns the errno (or equivalent) on this platform to - * hide platform specific for the function that calls this. + * Curl_sockerrno() returns the *socket-related* errno (or equivalent) on this + * platform to hide platform specific for the function that calls this. */ -int Curl_ourerrno(void) +int Curl_sockerrno(void) { -#ifdef WIN32 - return (int)GetLastError(); +#ifdef USE_WINSOCK + return (int)WSAGetLastError(); #else return errno; #endif @@ -131,72 +137,62 @@ int Curl_nonblock(curl_socket_t sockfd, /* operate on this */ int nonblock /* TRUE or FALSE */) { #undef SETBLOCK +#define SETBLOCK 0 #ifdef HAVE_O_NONBLOCK - { - /* most recent unix versions */ - int flags; + /* most recent unix versions */ + int flags; - flags = fcntl(sockfd, F_GETFL, 0); - if (TRUE == nonblock) - return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - else - return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); - } + flags = fcntl(sockfd, F_GETFL, 0); + if (TRUE == nonblock) + return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); +#undef SETBLOCK #define SETBLOCK 1 #endif -#ifdef HAVE_FIONBIO - { - /* older unix versions */ - int flags; +#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) + /* older unix versions */ + int flags; - flags = nonblock; - return ioctl(sockfd, FIONBIO, &flags); - } -#ifdef SETBLOCK -# undef SETBLOCK -#endif + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); +#undef SETBLOCK #define SETBLOCK 2 #endif -#ifdef HAVE_IOCTLSOCKET +#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) /* Windows? */ unsigned long flags; flags = nonblock; + return ioctlsocket(sockfd, FIONBIO, &flags); +#undef SETBLOCK #define SETBLOCK 3 #endif -#ifdef HAVE_IOCTLSOCKET_CASE +#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) /* presumably for Amiga */ return IoctlSocket(sockfd, FIONBIO, (long)nonblock); -#ifdef SETBLOCK -# undef SETBLOCK -#endif +#undef SETBLOCK #define SETBLOCK 4 #endif -#ifdef HAVE_SO_NONBLOCK +#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) /* BeOS */ long b = nonblock ? 1 : 0; return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); -#ifdef SETBLOCK -# undef SETBLOCK -#endif +#undef SETBLOCK #define SETBLOCK 5 #endif #ifdef HAVE_DISABLED_NONBLOCKING - (void)nonblock; - (void)sockfd; return 0; /* returns success */ -#ifdef SETBLOCK -# undef SETBLOCK -#endif +#undef SETBLOCK #define SETBLOCK 6 #endif -#ifndef SETBLOCK +#if (SETBLOCK == 0) #error "no non-blocking method was found/used/set" #endif } @@ -219,30 +215,16 @@ static int waitconnect(curl_socket_t sockfd, /* socket */ long timeout_msec) { - fd_set fd; - fd_set errfd; - struct timeval interval; int rc; #ifdef mpeix /* Call this function once now, and ignore the results. We do this to "clear" the error state on the socket so that we can later read it reliably. This is reported necessary on the MPE/iX operating system. */ - verifyconnect(sockfd, NULL); + (void)verifyconnect(sockfd, NULL); #endif /* now select() until we get connect or timeout */ - FD_ZERO(&fd); - FD_SET(sockfd, &fd); - - FD_ZERO(&errfd); - FD_SET(sockfd, &errfd); - - interval.tv_sec = (int)(timeout_msec/1000); - timeout_msec -= interval.tv_sec*1000; - - interval.tv_usec = timeout_msec*1000; - - rc = select(sockfd+1, NULL, &fd, &errfd, &interval); + rc = Curl_select(CURL_SOCKET_BAD, sockfd, (int)timeout_msec); if(-1 == rc) /* error, no connect here, try next */ return WAITCONN_SELECT_ERROR; @@ -251,7 +233,7 @@ int waitconnect(curl_socket_t sockfd, /* socket */ /* timeout, no connect today */ return WAITCONN_TIMEOUT; - if(FD_ISSET(sockfd, &errfd)) + if(rc & CSELECT_ERR) /* error condition caught */ return WAITCONN_FDSET_ERROR; @@ -262,14 +244,19 @@ int waitconnect(curl_socket_t sockfd, /* socket */ static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { -#ifdef HAVE_INET_NTOA - bool bindworked = FALSE; 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; /************************************************************* * Select device to bind socket to *************************************************************/ - if (strlen(data->set.device)<255) { + if (data->set.device && (strlen(data->set.device)<255) ) { struct Curl_dns_entry *h=NULL; char myhost[256] = ""; in_addr_t in; @@ -288,8 +275,10 @@ static CURLcode bindlocal(struct connectdata *conn, if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); - if(h) + if(h) { was_iface = TRUE; + Curl_resolv_unlock(data, h); + } } if(!was_iface) { @@ -301,9 +290,17 @@ static CURLcode bindlocal(struct connectdata *conn, if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); - if(h) - /* we know data->set.device is shorter than the myhost array */ - strcpy(myhost, data->set.device); + if(h) { + if(in == CURL_INADDR_NONE) + /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ + Curl_inet_ntop(h->addr->ai_addr->sa_family, + &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr, + myhost, sizeof myhost); + else + /* we know data->set.device is shorter than the myhost array */ + strcpy(myhost, data->set.device); + Curl_resolv_unlock(data, h); + } } if(! *myhost) { @@ -317,7 +314,7 @@ static CURLcode bindlocal(struct connectdata *conn, return CURLE_HTTP_PORT_FAILED; } - infof(data, "We bind local end to %s\n", myhost); + 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 @@ -335,7 +332,7 @@ static CURLcode bindlocal(struct connectdata *conn, if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, data->set.device, strlen(data->set.device)+1) != 0) { /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n", - sockfd, data->set.device, Curl_strerror(Curl_ourerrno())); */ + sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */ infof(data, "SO_BINDTODEVICE %s failed\n", data->set.device); /* This is typically "errno 1, error: Operation not permitted" if @@ -345,59 +342,78 @@ static CURLcode bindlocal(struct connectdata *conn, #endif in=inet_addr(myhost); - if (CURL_INADDR_NONE != in) { + if (CURL_INADDR_NONE == in) { + failf(data,"couldn't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } /* end of inet_addr */ - if ( h ) { - Curl_addrinfo *addr = h->addr; + if ( h ) { + Curl_addrinfo *addr = h->addr; + sock = addr->ai_addr; + socksize = addr->ai_addrlen; + } + else + return CURLE_HTTP_PORT_FAILED; - Curl_resolv_unlock(data, h); - /* we don't need it anymore after this function has returned */ + } + 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; - if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) { - /* we succeeded to bind */ -#ifdef ENABLE_IPV6 - struct sockaddr_in6 add; -#else - struct sockaddr_in add; -#endif + sock = (struct sockaddr *)&me; + socksize = sizeof(struct sockaddr); -#ifdef __hpux - int gsize = sizeof(add); -#else - socklen_t gsize = sizeof(add); -#endif - bindworked = TRUE; + } + else + /* no local kind of binding was requested */ + return CURLE_OK; - if(getsockname(sockfd, (struct sockaddr *) &add, - &gsize)<0) { - failf(data, "getsockname() failed"); - return CURLE_HTTP_PORT_FAILED; - } - } + do { - if(!bindworked) { - failf(data, "%s", Curl_strerror(conn, Curl_ourerrno())); - return CURLE_HTTP_PORT_FAILED; - } + /* 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; - } /* end of if h */ - else { - failf(data,"could't find my own IP address (%s)", myhost); + size = sizeof(add); + if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { + failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } - } /* end of inet_addr */ - - else { - failf(data, "could't find my own IP address (%s)", myhost); - return CURLE_HTTP_PORT_FAILED; + /* 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); + 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); - return CURLE_OK; - - } /* end of device selection support */ -#endif /* end of HAVE_INET_NTOA */ - + data->state.os_errno = Curl_sockerrno(); + failf(data, "bind failure: %s", + Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; + } /* @@ -405,15 +421,10 @@ static CURLcode bindlocal(struct connectdata *conn, */ static bool verifyconnect(curl_socket_t sockfd, int *error) { - bool rc; + bool rc = TRUE; #ifdef SO_ERROR int err = 0; -#ifdef __hpux - int errSize = sizeof(err); -#else socklen_t errSize = sizeof(err); -#endif - #ifdef WIN32 /* @@ -431,12 +442,24 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * * Someone got to verify this on Win-NT 4.0, 2000." */ + +#ifdef _WIN32_WCE + Sleep(0); +#else SleepEx(0, FALSE); #endif +#endif + if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = Curl_ourerrno(); + err = Curl_sockerrno(); + +#ifdef _WIN32_WCE + /* Always returns this error, bug in CE? */ + if(WSAENOPROTOOPT==err) + err=0; +#endif if ((0 == err) || (EISCONN == err)) /* we are connected, awesome! */ @@ -449,11 +472,30 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) #else (void)sockfd; if (error) - *error = Curl_ourerrno(); + *error = Curl_sockerrno(); #endif return rc; } +CURLcode Curl_store_ip_addr(struct connectdata *conn) +{ + char addrbuf[256]; + Curl_printable_address(conn->ip_addr, addrbuf, sizeof(addrbuf)); + + /* save the string */ + Curl_safefree(conn->ip_addr_str); + conn->ip_addr_str = strdup(addrbuf); + if(!conn->ip_addr_str) + return CURLE_OUT_OF_MEMORY; /* FAIL */ + +#ifdef PF_INET6 + if(conn->ip_addr->ai_family == PF_INET6) + conn->bits.ipv6 = TRUE; +#endif + + return CURLE_OK; +} + /* Used within the multi interface. Try next IP address, return TRUE if no more address exists */ static bool trynextip(struct connectdata *conn, @@ -463,6 +505,11 @@ static bool trynextip(struct connectdata *conn, curl_socket_t sockfd; Curl_addrinfo *ai; + /* first close the failed socket */ + sclose(conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + *connected = FALSE; + if(sockindex != FIRSTSOCKET) return TRUE; /* no next */ @@ -475,6 +522,8 @@ static bool trynextip(struct connectdata *conn, /* store the new socket descriptor */ conn->sock[sockindex] = sockfd; conn->ip_addr = ai; + + Curl_store_ip_addr(conn); return FALSE; } ai = ai->ai_next; @@ -496,6 +545,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; + long allow_total = 0; long has_passed; curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); @@ -503,17 +553,17 @@ CURLcode Curl_is_connected(struct connectdata *conn, *connected = FALSE; /* a very negative world view is best */ /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) - allow = data->set.timeout*1000; + allow_total = allow = data->set.timeout*1000; else allow = data->set.connecttimeout*1000; } else if(data->set.timeout) { - allow = data->set.timeout*1000; + allow_total = allow = data->set.timeout*1000; } else if(data->set.connecttimeout) { allow = data->set.connecttimeout*1000; @@ -526,30 +576,45 @@ CURLcode Curl_is_connected(struct connectdata *conn, } if(conn->bits.tcpconnect) { /* we are connected already! */ + Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } + Curl_expire(data, allow); + /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); if(WAITCONN_CONNECTED == rc) { - if (verifyconnect(sockfd, NULL)) { + 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)) { code = CURLE_COULDNT_CONNECT; } } else if(WAITCONN_TIMEOUT != rc) { + int error = 0; + /* nope, not connected */ - infof(data, "Connection failed\n"); + 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)) { - int error = Curl_ourerrno(); + error = Curl_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; @@ -569,10 +634,18 @@ static void tcpnodelay(struct connectdata *conn, #ifdef TCP_NODELAY struct SessionHandle *data= conn->data; socklen_t onoff = (socklen_t) data->set.tcp_nodelay; - if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&onoff, + int proto = IPPROTO_TCP; + +#ifdef HAVE_GETPROTOBYNAME + struct protoent *pe = getprotobyname("tcp"); + if (pe) + proto = pe->p_proto; +#endif + + if(setsockopt(sockfd, proto, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", - Curl_strerror(conn, Curl_ourerrno())); + Curl_strerror(conn, Curl_sockerrno())); else infof(data,"TCP_NODELAY set\n"); #else @@ -581,21 +654,42 @@ static void tcpnodelay(struct connectdata *conn, #endif } +#ifdef SO_NOSIGPIPE +/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when + sending data to a dead peer (instead of relying on the 4th argument to send + being MSG_NOSIGNAL). Possibly also existing and in use on other BSD + systems? */ +static void nosigpipe(struct connectdata *conn, + curl_socket_t sockfd) +{ + struct SessionHandle *data= conn->data; + int onoff = 1; + if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, + sizeof(onoff)) < 0) + infof(data, "Could not set SO_NOSIGPIPE: %s\n", + Curl_strerror(conn, Curl_sockerrno())); +} +#else +#define nosigpipe(x,y) +#endif + /* singleipconnect() connects to the given IP only, and it may return without having connected if used from the multi interface. */ static curl_socket_t singleipconnect(struct connectdata *conn, - Curl_addrinfo *ai, + const Curl_addrinfo *ai, long timeout_ms, bool *connected) { char addr_buf[128]; int rc; int error; - bool conected; + bool isconnected; struct SessionHandle *data = conn->data; - curl_socket_t sockfd = socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol); + curl_socket_t sockfd; + CURLcode res; + + sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol); if (sockfd == CURL_SOCKET_BAD) return CURL_SOCKET_BAD; @@ -607,21 +701,37 @@ singleipconnect(struct connectdata *conn, if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); - if(conn->data->set.device) { - /* user selected to bind the outgoing socket to a specified "device" - before doing connect */ - CURLcode res = bindlocal(conn, sockfd); - if(res) - return res; + 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); - rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); + /* Connect TCP sockets, bind UDP */ + if(conn->socktype == SOCK_STREAM) + rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); + else + rc = 0; if(-1 == rc) { - error = Curl_ourerrno(); + error = Curl_sockerrno(); switch (error) { case EINPROGRESS: @@ -639,6 +749,7 @@ singleipconnect(struct connectdata *conn, /* 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; } } @@ -651,9 +762,9 @@ singleipconnect(struct connectdata *conn, return sockfd; } - conected = verifyconnect(sockfd, &error); + isconnected = verifyconnect(sockfd, &error); - if(!rc && conected) { + if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); @@ -661,8 +772,10 @@ singleipconnect(struct connectdata *conn, } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); - else + else { + data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); + } /* connect failed or timed out */ sclose(sockfd); @@ -677,7 +790,7 @@ singleipconnect(struct connectdata *conn, */ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ - struct Curl_dns_entry *remotehost, /* use this one */ + const struct Curl_dns_entry *remotehost, /* use this one */ curl_socket_t *sockconn, /* the connected socket */ Curl_addrinfo **addr, /* the one we used */ bool *connected) /* really connected? */ @@ -704,7 +817,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ long has_passed; /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -731,6 +844,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ return CURLE_OPERATION_TIMEOUTED; } } + Curl_expire(data, timeout_ms); /* Max time for each address */ num_addr = Curl_num_addresses(remotehost->addr); @@ -744,7 +858,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ - timeout_per_addr = timeout_ms = 0; + timeout_per_addr = 0; /* * Connecting with a Curl_addrinfo chain @@ -771,6 +885,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ if (sockfd == CURL_SOCKET_BAD) { /* no good connect was made */ *sockconn = CURL_SOCKET_BAD; + failf(data, "couldn't connect to host"); return CURLE_COULDNT_CONNECT; } @@ -784,5 +899,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ if(sockconn) *sockconn = sockfd; /* the socket descriptor we've connected */ + data->info.numconnects++; /* to track the number of connections made */ + return CURLE_OK; } diff --git a/Utilities/cmcurl/connect.h b/Utilities/cmcurl/connect.h index d39495c..599572b 100644 --- a/Utilities/cmcurl/connect.h +++ b/Utilities/cmcurl/connect.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,13 +31,15 @@ CURLcode Curl_is_connected(struct connectdata *conn, bool *connected); CURLcode Curl_connecthost(struct connectdata *conn, - struct Curl_dns_entry *host, /* connect to this */ + const struct Curl_dns_entry *host, /* connect to this */ curl_socket_t *sockconn, /* not set if error */ Curl_addrinfo **addr, /* the one we used */ bool *connected /* truly connected? */ ); -int Curl_ourerrno(void); +int Curl_sockerrno(void); + +CURLcode Curl_store_ip_addr(struct connectdata *conn); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ diff --git a/Utilities/cmcurl/content_encoding.c b/Utilities/cmcurl/content_encoding.c index b8c68bd..97f8341 100644 --- a/Utilities/cmcurl/content_encoding.c +++ b/Utilities/cmcurl/content_encoding.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,10 +32,14 @@ #include <curl/curl.h> #include "sendf.h" #include "content_encoding.h" -#include "curl_memory.h" +#include "memory.h" #include "memdebug.h" +/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 + (doing so will reduce code size slightly). */ +#define OLD_ZLIB_SUPPORT 1 + #define DSIZ 0x10000 /* buffer size for decompressed data */ #define GZIP_MAGIC_0 0x1f @@ -49,14 +53,23 @@ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ +enum zlibState { + ZLIB_UNINIT, /* uninitialized */ + ZLIB_INIT, /* initialized */ + ZLIB_GZIP_HEADER, /* reading gzip header */ + ZLIB_GZIP_INFLATING, /* inflating gzip stream */ + ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ +}; + static CURLcode -process_zlib_error(struct SessionHandle *data, z_stream *z) +process_zlib_error(struct connectdata *conn, z_stream *z) { + struct SessionHandle *data = conn->data; if (z->msg) - failf (data, "Error while processing content unencoding.\n%s", + failf (data, "Error while processing content unencoding: %s", z->msg); else - failf (data, "Error while processing content unencoding.\n" + failf (data, "Error while processing content unencoding: " "Unknown failure within decompression software."); return CURLE_BAD_CONTENT_ENCODING; @@ -66,71 +79,113 @@ static CURLcode exit_zlib(z_stream *z, bool *zlib_init, CURLcode result) { inflateEnd(z); - *zlib_init = 0; + *zlib_init = ZLIB_UNINIT; return result; } -CURLcode -Curl_unencode_deflate_write(struct SessionHandle *data, - struct Curl_transfer_keeper *k, - ssize_t nread) +static CURLcode +inflate_stream(struct connectdata *conn, + struct Curl_transfer_keeper *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[DSIZ]; /* Put the decompressed data here. */ - z_stream *z = &k->z; /* zlib state structure */ + char *decomp; /* Put the decompressed data here. */ - /* Initialize zlib? */ - if (!k->zlib_init) { - z->zalloc = (alloc_func)Z_NULL; - z->zfree = (free_func)Z_NULL; - z->opaque = 0; - z->next_in = NULL; - z->avail_in = 0; - if (inflateInit(z) != Z_OK) - return process_zlib_error(data, z); - k->zlib_init = 1; + /* Dynamically allocate a buffer for decompression because it's uncommonly + large to hold on the stack */ + decomp = (char*)malloc(DSIZ); + if (decomp == NULL) { + return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY); } - /* Set the compressed input when this function is called */ - z->next_in = (Bytef *)k->str; - z->avail_in = (uInt)nread; - - /* because the buffer size is fixed, iteratively decompress - and transfer to the client via client_write. */ + /* 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[0]; + z->next_out = (Bytef *)decomp; z->avail_out = DSIZ; status = inflate(z, Z_SYNC_FLUSH); if (status == Z_OK || status == Z_STREAM_END) { - if (DSIZ - z->avail_out) { - result = Curl_client_write(data, CLIENTWRITE_BODY, decomp, + 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) + if (result) { + free(decomp); return exit_zlib(z, &k->zlib_init, result); + } } - /* Done?; clean up, return */ + /* 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(data, z)); + 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 && z->avail_out > 0) + 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 */ + + inflateReset(z); + 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 */ - return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + free(decomp); + return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); } } + /* Will never get here */ +} + +CURLcode +Curl_unencode_deflate_write(struct connectdata *conn, + struct Curl_transfer_keeper *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 (inflateInit(z) != Z_OK) + return process_zlib_error(conn, z); + k->zlib_init = ZLIB_INIT; + } + + /* Set the compressed input when this function is called */ + z->next_in = (Bytef *)k->str; + z->avail_in = (uInt)nread; + + /* Now uncompress the data */ + return inflate_stream(conn, k); } +#ifdef OLD_ZLIB_SUPPORT /* Skip over the gzip header */ static enum { GZIP_OK, @@ -172,6 +227,7 @@ static enum { return GZIP_UNDERFLOW; len -= (extra_len + 2); + data += (extra_len + 2); } if (flags & ORIG_NAME) { @@ -213,38 +269,67 @@ static enum { *headerlen = totallen - len; return GZIP_OK; } +#endif CURLcode -Curl_unencode_gzip_write(struct SessionHandle *data, +Curl_unencode_gzip_write(struct connectdata *conn, struct Curl_transfer_keeper *k, ssize_t nread) { - int status; /* zlib status */ - CURLcode result = CURLE_OK; /* Curl_client_write status */ - char decomp[DSIZ]; /* Put the decompressed data here. */ z_stream *z = &k->z; /* zlib state structure */ /* Initialize zlib? */ - if (!k->zlib_init) { + 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 (inflateInit2(z, -MAX_WBITS) != Z_OK) - return process_zlib_error(data, z); - k->zlib_init = 1; /* Initial call state */ + + 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? */ - if (k->zlib_init == 1) { + case ZLIB_INIT: + { /* Initial call state */ ssize_t hlen; @@ -252,7 +337,7 @@ Curl_unencode_gzip_write(struct SessionHandle *data, case GZIP_OK: z->next_in = (Bytef *)k->str + hlen; z->avail_in = (uInt)(nread - hlen); - k->zlib_init = 3; /* Inflating stream state */ + k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: @@ -269,17 +354,20 @@ Curl_unencode_gzip_write(struct SessionHandle *data, return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY); } memcpy(z->next_in, k->str, z->avail_in); - k->zlib_init = 2; /* Need more gzip header data state */ + 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(data, z)); + return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); } } - else if (k->zlib_init == 2) { + break; + + case ZLIB_GZIP_HEADER: + { /* Need more gzip header data state */ ssize_t hlen; unsigned char *oldblock = z->next_in; @@ -300,7 +388,7 @@ Curl_unencode_gzip_write(struct SessionHandle *data, /* 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 = 3; /* Inflating stream state */ + k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: @@ -310,14 +398,18 @@ Curl_unencode_gzip_write(struct SessionHandle *data, case GZIP_BAD: default: free(z->next_in); - return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); + return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); } } - else { + 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) { @@ -325,39 +417,8 @@ Curl_unencode_gzip_write(struct SessionHandle *data, return CURLE_OK; } - /* 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[0]; - z->avail_out = DSIZ; - - status = inflate(z, Z_SYNC_FLUSH); - if (status == Z_OK || status == Z_STREAM_END) { - if(DSIZ - z->avail_out) { - result = Curl_client_write(data, CLIENTWRITE_BODY, decomp, - DSIZ - z->avail_out); - /* if !CURLE_OK, clean up, return */ - if (result) - return exit_zlib(z, &k->zlib_init, result); - } - - /* Done?; clean up, return */ - /* We should really check the gzip CRC here */ - if (status == Z_STREAM_END) { - if (inflateEnd(z) == Z_OK) - return exit_zlib(z, &k->zlib_init, result); - else - return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); - } - - /* Done with these bytes, exit */ - if (status == Z_OK && z->avail_in == 0 && z->avail_out > 0) - return result; - } - else { /* Error; exit loop, handle below */ - return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z)); - } - } + /* We've parsed the header, now uncompress the data */ + return inflate_stream(conn, k); +#endif } #endif /* HAVE_LIBZ */ diff --git a/Utilities/cmcurl/content_encoding.h b/Utilities/cmcurl/content_encoding.h index a65dbd2..b31669b 100644 --- a/Utilities/cmcurl/content_encoding.h +++ b/Utilities/cmcurl/content_encoding.h @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -31,11 +31,11 @@ #define ALL_CONTENT_ENCODINGS "identity" #endif -CURLcode Curl_unencode_deflate_write(struct SessionHandle *data, - struct Curl_transfer_keeper *k, +CURLcode Curl_unencode_deflate_write(struct connectdata *conn, + struct Curl_transfer_keeper *k, ssize_t nread); CURLcode -Curl_unencode_gzip_write(struct SessionHandle *data, +Curl_unencode_gzip_write(struct connectdata *conn, struct Curl_transfer_keeper *k, ssize_t nread); diff --git a/Utilities/cmcurl/cookie.c b/Utilities/cmcurl/cookie.c index 3ba5627..2856ad8 100644 --- a/Utilities/cmcurl/cookie.c +++ b/Utilities/cmcurl/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -80,25 +80,30 @@ Example set of cookies: #include "setup.h" -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) #include <stdlib.h> #include <string.h> -#include <ctype.h> + +#define _MPRINTF_REPLACE /* without this on windows OS we get undefined reference to snprintf */ +#include <curl/mprintf.h> #include "urldata.h" #include "cookie.h" -#include "getdate.h" #include "strequal.h" #include "strtok.h" #include "sendf.h" -#include "curl_memory.h" +#include "memory.h" +#include "share.h" +#include "strtoofft.h" /* The last #include file should be: */ #ifdef CURLDEBUG #include "memdebug.h" #endif +#define my_isspace(x) ((x == ' ') || (x == '\t')) + static void freecookie(struct Cookie *co) { if(co->expirestr) @@ -111,6 +116,10 @@ static void freecookie(struct Cookie *co) free(co->name); if(co->value) free(co->value); + if(co->maxage) + free(co->maxage); + if(co->version) + free(co->version); free(co); } @@ -126,6 +135,27 @@ static bool tailmatch(const char *little, const char *bigone) return (bool)strequal(little, bigone+biglen-littlelen); } +/* + * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). + */ +void Curl_cookie_loadfiles(struct SessionHandle *data) +{ + struct curl_slist *list = data->change.cookielist; + if(list) { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + while(list) { + data->cookies = Curl_cookie_init(data, + list->data, + data->cookies, + data->set.cookiesession); + list = list->next; + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + curl_slist_free_all(data->change.cookielist); /* clean up list */ + data->change.cookielist = NULL; /* don't do this again! */ + } +} + /**************************************************************************** * * Curl_cookie_add() @@ -156,7 +186,7 @@ Curl_cookie_add(struct SessionHandle *data, struct Cookie *co; struct Cookie *lastc=NULL; time_t now = time(NULL); - bool replace_old; + bool replace_old = FALSE; bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ /* First, alloc and init a new struct for it */ @@ -176,7 +206,7 @@ Curl_cookie_add(struct SessionHandle *data, semiptr=strchr(lineptr, ';'); /* first, find a semicolon */ - while(*lineptr && isspace((int)*lineptr)) + while(*lineptr && my_isspace(*lineptr)) lineptr++; ptr = lineptr; @@ -199,14 +229,14 @@ Curl_cookie_add(struct SessionHandle *data, /* Strip off trailing whitespace from the 'what' */ size_t len=strlen(what); - while(len && isspace((int)what[len-1])) { + while(len && my_isspace(what[len-1])) { what[len-1]=0; len--; } /* Skip leading whitespace from the 'what' */ whatptr=what; - while(isspace((int)*whatptr)) { + while(my_isspace(*whatptr)) { whatptr++; } @@ -305,7 +335,7 @@ Curl_cookie_add(struct SessionHandle *data, break; } co->expires = - atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + now; + atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + (long)now; } else if(strequal("expires", name)) { co->expirestr=strdup(whatptr); @@ -348,7 +378,7 @@ Curl_cookie_add(struct SessionHandle *data, } ptr=semiptr+1; - while(ptr && *ptr && isspace((int)*ptr)) + while(ptr && *ptr && my_isspace(*ptr)) ptr++; semiptr=strchr(ptr, ';'); /* now, find the next semicolon */ @@ -466,7 +496,7 @@ Curl_cookie_add(struct SessionHandle *data, co->secure = (bool)strequal(ptr, "TRUE"); break; case 4: - co->expires = atoi(ptr); + co->expires = curlx_strtoofft(ptr, NULL, 10); break; case 5: co->name = strdup(ptr); @@ -647,6 +677,10 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, fp = stdin; fromfile=FALSE; } + else if(file && !*file) { + /* points to a "" string */ + fp = NULL; + } else fp = file?fopen(file, "r"):NULL; @@ -668,7 +702,7 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, lineptr=line; headerline=FALSE; } - while(*lineptr && isspace((int)*lineptr)) + while(*lineptr && my_isspace(*lineptr)) lineptr++; Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL); @@ -699,68 +733,85 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, char *host, 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<=0 || (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 || - checkprefix(co->path, path) ) { - - /* and now, we know this is a match and we should create an - entry for the return-linked-list */ - - newco = (struct Cookie *)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 */ + 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<=0 || (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 = (struct Cookie *)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 */ } +/***************************************************************************** + * + * Curl_cookie_clearall() + * + * Clear all existing cookies and reset the counter. + * + ****************************************************************************/ +void Curl_cookie_clearall(struct CookieInfo *cookies) +{ + if(cookies) { + Curl_cookie_freelist(cookies->cookies); + cookies->cookies = NULL; + cookies->numcookies = 0; + } +} /***************************************************************************** * @@ -772,17 +823,56 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, void Curl_cookie_freelist(struct Cookie *co) { - struct Cookie *next; - if(co) { - while(co) { - next = co->next; - free(co); /* we only free the struct since the "members" are all + struct Cookie *next; + if(co) { + while(co) { + next = co->next; + free(co); /* we only free the struct since the "members" are all just copied! */ - co = next; - } - } + co = next; + } + } +} + + +/***************************************************************************** + * + * Curl_cookie_clearsess() + * + * Free all session cookies in the cookies list. + * + ****************************************************************************/ +void Curl_cookie_clearsess(struct CookieInfo *cookies) +{ + struct Cookie *first, *curr, *next, *prev = NULL; + + if(!cookies->cookies) + return; + + first = curr = prev = cookies->cookies; + + for(; curr; curr = next) { + next = curr->next; + if(!curr->expires) { + if(first == curr) + first = next; + + if(prev == curr) + prev = next; + else + prev->next = next; + + free(curr); + cookies->numcookies--; + } + else + prev = curr; + } + + cookies->cookies = first; } + /***************************************************************************** * * Curl_cookie_cleanup() @@ -792,20 +882,48 @@ void Curl_cookie_freelist(struct Cookie *co) ****************************************************************************/ void Curl_cookie_cleanup(struct CookieInfo *c) { - struct Cookie *co; - struct Cookie *next; - if(c) { - if(c->filename) - free(c->filename); - co = c->cookies; - - while(co) { - next = co->next; - freecookie(co); - co = next; - } - free(c); /* free the base struct as well */ - } + struct Cookie *co; + struct Cookie *next; + if(c) { + if(c->filename) + free(c->filename); + co = c->cookies; + + while(co) { + next = co->next; + freecookie(co); + co = next; + } + free(c); /* free the base struct as well */ + } +} + +/* get_netscape_format() + * + * Formats a string for Netscape output file, w/o a newline at the end. + * + * Function returns a char * to a formatted line. Has to be free()d +*/ +static char *get_netscape_format(const struct Cookie *co) +{ + return aprintf( + "%s%s\t" /* domain */ + "%s\t" /* tailmatch */ + "%s\t" /* path */ + "%s\t" /* secure */ + "%" FORMAT_OFF_T "\t" /* expires */ + "%s\t" /* name */ + "%s", /* value */ + /* Make sure all domains are prefixed with a dot if they allow + tailmatching. This is Mozilla-style. */ + (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", + co->domain?co->domain:"unknown", + co->tailmatch?"TRUE":"FALSE", + co->path?co->path:"/", + co->secure?"TRUE":"FALSE", + co->expires, + co->name, + co->value?co->value:""); } /* @@ -839,33 +957,22 @@ int Curl_cookie_output(struct CookieInfo *c, char *dumphere) } if(c) { + char *format_ptr; + fputs("# Netscape HTTP Cookie File\n" - "# http://www.netscape.com/newsref/std/cookie_spec.html\n" + "# http://curlm.haxx.se/rfc/cookie_spec.html\n" "# This file was generated by libcurl! Edit at your own risk.\n\n", out); co = c->cookies; while(co) { - fprintf(out, - "%s%s\t" /* domain */ - "%s\t" /* tailmatch */ - "%s\t" /* path */ - "%s\t" /* secure */ - "%u\t" /* expires */ - "%s\t" /* name */ - "%s\n", /* value */ - - /* Make sure all domains are prefixed with a dot if they allow - tailmatching. This is Mozilla-style. */ - (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", - co->domain?co->domain:"unknown", - co->tailmatch?"TRUE":"FALSE", - co->path?co->path:"/", - co->secure?"TRUE":"FALSE", - (unsigned int)co->expires, - co->name, - co->value?co->value:""); - + format_ptr = get_netscape_format(co); + if (format_ptr == NULL) { + fprintf(out, "#\n# Fatal libcurl error\n"); + return 1; + } + fprintf(out, "%s\n", format_ptr); + free(format_ptr); co=co->next; } } @@ -876,4 +983,35 @@ int Curl_cookie_output(struct CookieInfo *c, char *dumphere) return 0; } -#endif /* CURL_DISABLE_HTTP */ +struct curl_slist *Curl_cookie_list(struct SessionHandle *data) +{ + struct curl_slist *list = NULL; + struct curl_slist *beg; + struct Cookie *c; + char *line; + + if ((data->cookies == NULL) || + (data->cookies->numcookies == 0)) + return NULL; + + c = data->cookies->cookies; + + beg = list; + while (c) { + /* fill the list with _all_ the cookies we know */ + line = get_netscape_format(c); + if (line == NULL) { + /* get_netscape_format returns null only if we run out of memory */ + + curl_slist_free_all(beg); /* free some memory */ + return NULL; + } + list = curl_slist_append(list, line); + free(line); + c = c->next; + } + + return list; +} + +#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ diff --git a/Utilities/cmcurl/cookie.h b/Utilities/cmcurl/cookie.h index 6f8e8e5..57c3acb 100644 --- a/Utilities/cmcurl/cookie.h +++ b/Utilities/cmcurl/cookie.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,11 +24,13 @@ ***************************************************************************/ #include <stdio.h> -#ifdef WIN32 +#if defined(WIN32) #include <time.h> #else +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif +#endif #include <curl/curl.h> @@ -38,7 +40,7 @@ struct Cookie { char *value; /* name = <this> */ char *path; /* path = <this> */ char *domain; /* domain = <this> */ - long expires; /* expires = <this> */ + curl_off_t expires; /* expires = <this> */ char *expirestr; /* the plain text version */ bool tailmatch; /* weather we do tail-matchning of the domain name */ @@ -89,7 +91,17 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, char *, struct CookieInfo *, bool); struct Cookie *Curl_cookie_getlist(struct CookieInfo *, char *, char *, bool); void Curl_cookie_freelist(struct Cookie *); +void Curl_cookie_clearall(struct CookieInfo *cookies); +void Curl_cookie_clearsess(struct CookieInfo *cookies); void Curl_cookie_cleanup(struct CookieInfo *); int Curl_cookie_output(struct CookieInfo *, char *); +#if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) +#define Curl_cookie_list(x) NULL +#define Curl_cookie_loadfiles(x) do { } while (0) +#else +struct curl_slist *Curl_cookie_list(struct SessionHandle *data); +void Curl_cookie_loadfiles(struct SessionHandle *data); +#endif + #endif diff --git a/Utilities/cmcurl/curl.copyright b/Utilities/cmcurl/curl.copyright index 16ea773..768438e 100644 --- a/Utilities/cmcurl/curl.copyright +++ b/Utilities/cmcurl/curl.copyright @@ -4,7 +4,7 @@ It was downloaded from http://curl.haxx.se COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2004, Daniel Stenberg, <daniel@haxx.se>. +Copyright (c) 1996 - 2007, Daniel Stenberg, <daniel@haxx.se>. All rights reserved. diff --git a/Utilities/cmcurl/curl/curl.h b/Utilities/cmcurl/curl/curl.h index 96c8fcc..f410676 100644 --- a/Utilities/cmcurl/curl/curl.h +++ b/Utilities/cmcurl/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,15 +48,48 @@ extern "C" { #endif /* + * Decorate exportable functions for Win32 DLL linking. + * This avoids using a .def file for building libcurl.dll. + */ +#if (defined(WIN32) || defined(_WIN32)) && !defined(CURL_STATICLIB) +#if defined(BUILDING_LIBCURL) +#define CURL_EXTERN __declspec(dllexport) +#else +#define CURL_EXTERN __declspec(dllimport) +#endif +#else + +#ifdef CURL_HIDDEN_SYMBOLS +/* + * This definition is used to make external definitions visibile in the + * shared library when symbols are hidden by default. It makes no + * difference when compiling applications whether this is set or not, + * only when compiling the library. + */ +#define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +#define CURL_EXTERN +#endif +#endif + +/* * We want the typedef curl_off_t setup for large file support on all * platforms. We also provide a CURL_FORMAT_OFF_T define to use in *printf * format strings when outputting a variable of type curl_off_t. + * + * Note: "pocc -Ze" is MSVC compatibily mode and this sets _MSC_VER! */ -#if defined(_MSC_VER) || defined(__LCC__) + +#if (defined(_MSC_VER) && !defined(__POCC__)) || (defined(__LCC__) && defined(WIN32)) /* MSVC */ +#ifdef _WIN32_WCE + typedef long curl_off_t; +#define CURL_FORMAT_OFF_T "%ld" +#else typedef signed __int64 curl_off_t; #define CURL_FORMAT_OFF_T "%I64d" -#else /* _MSC_VER || __LCC__ */ +#endif +#else /* (_MSC_VER && !__POCC__) || (__LCC__ && WIN32) */ #if (defined(__GNUC__) && defined(WIN32)) || defined(__WATCOMC__) /* gcc on windows or Watcom */ typedef long long curl_off_t; @@ -88,7 +121,7 @@ extern "C" { #define CURL_FORMAT_OFF_T "%ld" #endif #endif /* GCC or Watcom on Windows */ -#endif /* _MSC_VER || __LCC__ */ +#endif /* (_MSC_VER && !__POCC__) || (__LCC__ && WIN32) */ #ifdef UNDEF_FILE_OFFSET_BITS /* this was defined above for our checks, undefine it again */ @@ -100,6 +133,49 @@ extern "C" { #undef FILESIZEBITS #endif +#if defined(_WIN32) && !defined(WIN32) +/* Chris Lewis mentioned that he doesn't get WIN32 defined, only _WIN32 so we + make this adjustment to catch this. */ +#define WIN32 1 +#endif + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) && \ + !defined(__CYGWIN__) || defined(__MINGW32__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include <winsock2.h> +#endif +#else + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on system that are known to + require it! */ +#if defined(_AIX) || defined(NETWARE) || defined(__NetBSD__) || defined(__minix) +#include <sys/select.h> +#endif + +#ifndef _WIN32_WCE +#include <sys/socket.h> +#endif +#ifndef __WATCOMC__ +#include <sys/time.h> +#endif +#include <sys/types.h> +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#ifdef WIN32 +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + struct curl_httppost { struct curl_httppost *next; /* next entry in the list */ char *name; /* pointer to allocated name */ @@ -143,19 +219,47 @@ typedef size_t (*curl_write_callback)(char *buffer, size_t nitems, void *outstream); -/* This is a brand new return code for the read callback that will signal - the caller to immediately abort the current transfer. */ +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ #define CURL_READFUNC_ABORT 0x10000000 typedef size_t (*curl_read_callback)(char *buffer, - size_t size, - size_t nitems, - void *instream); + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); +#ifndef CURL_NO_OLDIES /* not used since 7.10.8, will be removed in a future release */ typedef int (*curl_passwd_callback)(void *clientp, const char *prompt, char *buffer, int buflen); +#endif + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); /* * The following typedef's are signatures of malloc, free, realloc, strdup and @@ -200,13 +304,15 @@ typedef enum { CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ CURLE_FAILED_INIT, /* 2 */ CURLE_URL_MALFORMAT, /* 3 */ - CURLE_URL_MALFORMAT_USER, /* 4 (NOT USED) */ + CURLE_URL_MALFORMAT_USER, /* 4 - NOT USED */ CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ CURLE_COULDNT_RESOLVE_HOST, /* 6 */ CURLE_COULDNT_CONNECT, /* 7 */ CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ - CURLE_FTP_ACCESS_DENIED, /* 9 */ - CURLE_FTP_USER_PASSWORD_INCORRECT, /* 10 */ + CURLE_FTP_ACCESS_DENIED, /* 9 a service was denied by the FTP server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_USER_PASSWORD_INCORRECT, /* 10 - NOT USED */ CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ CURLE_FTP_WEIRD_USER_REPLY, /* 12 */ CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ @@ -224,6 +330,10 @@ typedef enum { CURLE_FTP_COULDNT_STOR_FILE, /* 25 - failed FTP upload */ CURLE_READ_ERROR, /* 26 - could open/read from file */ CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ CURLE_OPERATION_TIMEOUTED, /* 28 - the timeout time was reached */ CURLE_FTP_COULDNT_SET_ASCII, /* 29 - TYPE A failed */ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ @@ -262,10 +372,39 @@ typedef enum { CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ CURLE_FTP_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ - + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_TFTP_DISKFULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_TFTP_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ CURL_LAST /* never use! */ } CURLcode; +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ void *ssl_ctx, /* actually an OpenSSL SSL_CTX */ @@ -274,9 +413,12 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ /* Make a spelling correction for the operation timed-out define */ #define CURLE_OPERATION_TIMEDOUT CURLE_OPERATION_TIMEOUTED +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ /* backwards compatibility with older names */ #define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR #define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#endif typedef enum { CURLPROXY_HTTP = 0, @@ -292,17 +434,29 @@ typedef enum { #define CURLAUTH_ANY ~0 /* all types set */ #define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC) +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ /* this was the error code 50 in 7.7.3 and a few earlier versions, this is no longer used by libcurl but is instead #defined here only to not make programs break */ #define CURLE_ALREADY_COMPLETE 99999 -/* This is just to make older programs not break: */ +/* These are just to make older programs not break: */ #define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE #define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME +#endif #define CURL_ERROR_SIZE 256 +/* parameter for the CURLOPT_FTP_SSL option */ typedef enum { CURLFTPSSL_NONE, /* do not attempt to use SSL */ CURLFTPSSL_TRY, /* try using SSL, proceed anyway otherwise */ @@ -311,6 +465,23 @@ typedef enum { CURLFTPSSL_LAST /* not an option, never use */ } curl_ftpssl; +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ #define CURLOPTTYPE_LONG 0 @@ -332,7 +503,8 @@ typedef enum { * platforms. */ #if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ - defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) /* This compiler is believed to have an ISO compatible preprocessor */ #define CURL_ISOCPP #else @@ -655,7 +827,7 @@ typedef enum { CINIT(SSLENGINE_DEFAULT, LONG, 90), /* Non-zero value means to use the global dns cache */ - CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To become OBSOLETE soon */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To becomeO BSOLETE soon */ /* DNS cache timeout */ CINIT(DNS_CACHE_TIMEOUT, LONG, 92), @@ -785,33 +957,102 @@ typedef enum { /* Enable/disable the TCP Nagle algorithm */ CINIT(TCP_NODELAY, LONG, 121), - /* When doing 3rd party transfer, set the source host name with this */ - CINIT(SOURCE_HOST, OBJECTPOINT, 122), + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_FTP_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), - /* When doing 3rd party transfer, set the source user and password with - this */ - CINIT(SOURCE_USERPWD, OBJECTPOINT, 123), + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), - /* When doing 3rd party transfer, set the source file path with this */ - CINIT(SOURCE_PATH, OBJECTPOINT, 124), + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), - /* When doing 3rd party transfer, set the source server's port number - with this */ - CINIT(SOURCE_PORT, LONG, 125), + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), - /* When doing 3rd party transfer, decide which server that should get the - PASV command (and the other gets the PORT). - 0 (default) - The target host issues PASV. - 1 - The source host issues PASV */ - CINIT(PASV_HOST, LONG, 126), + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), - /* When doing 3rd party transfer, set the source pre-quote linked list - of commands with this */ - CINIT(SOURCE_PREQUOTE, OBJECTPOINT, 127), + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), - /* When doing 3rd party transfer, set the source post-quote linked list - of commands with this */ - CINIT(SOURCE_POSTQUOTE, OBJECTPOINT, 128), + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -831,13 +1072,6 @@ typedef enum { #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ -#define CURLOPT_HTTPREQUEST -1 -#define CURLOPT_FTPASCII CURLOPT_TRANSFERTEXT -#define CURLOPT_MUTE -2 -#define CURLOPT_PASSWDFUNCTION -3 -#define CURLOPT_PASSWDDATA -4 -#define CURLOPT_CLOSEFUNCTION -5 - #else /* This is set if CURL_NO_OLDIES is defined at compile-time */ #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ @@ -894,8 +1128,8 @@ typedef enum { /* curl_strequal() and curl_strnequal() are subject for removal in a future libcurl, see lib/README.curlx for details */ -extern int (curl_strequal)(const char *s1, const char *s2); -extern int (curl_strnequal)(const char *s1, const char *s2, size_t n); +CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); +CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); /* name is uppercase CURLFORM_<name> */ #ifdef CFINIT @@ -941,8 +1175,8 @@ typedef enum { /* structure to be used as parameter for CURLFORM_ARRAY */ struct curl_forms { - CURLformoption option; - const char *value; + CURLformoption option; + const char *value; }; /* use this for multipart formpost building */ @@ -984,18 +1218,38 @@ typedef enum { * adds one part that together construct a full post. Then use * CURLOPT_HTTPPOST to send it off to libcurl. */ -CURLFORMcode curl_formadd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...); +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); /* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* * NAME curl_formfree() * * DESCRIPTION * * Free a multipart formpost previously built with curl_formadd(). */ -void curl_formfree(struct curl_httppost *form); +CURL_EXTERN void curl_formfree(struct curl_httppost *form); /* * NAME curl_getenv() @@ -1005,7 +1259,7 @@ void curl_formfree(struct curl_httppost *form); * Returns a malloc()'ed string that MUST be curl_free()ed after usage is * complete. DEPRECATED - see lib/README.curlx */ -char *curl_getenv(const char *variable); +CURL_EXTERN char *curl_getenv(const char *variable); /* * NAME curl_version() @@ -1014,10 +1268,10 @@ char *curl_getenv(const char *variable); * * Returns a static ascii string of the libcurl version. */ -char *curl_version(void); +CURL_EXTERN char *curl_version(void); /* - * NAME curl_escape() + * NAME curl_easy_escape() * * DESCRIPTION * @@ -1025,18 +1279,34 @@ char *curl_version(void); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -char *curl_escape(const char *string, int length); +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + /* - * NAME curl_unescape() + * NAME curl_easy_unescape() * * DESCRIPTION * * Unescapes URL encoding in strings (converts all %XX codes to their 8bit * versions). This function returns a new allocated string or NULL if an error * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. */ -char *curl_unescape(const char *string, int length); +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); /* * NAME curl_free() @@ -1046,7 +1316,7 @@ char *curl_unescape(const char *string, int length); * Provided for de-allocation in the same translation unit that did the * allocation. Added in libcurl 7.10 */ -void curl_free(void *p); +CURL_EXTERN void curl_free(void *p); /* * NAME curl_global_init() @@ -1056,7 +1326,7 @@ void curl_free(void *p); * curl_global_init() should be invoked exactly once for each application that * uses libcurl */ -CURLcode curl_global_init(long flags); +CURL_EXTERN CURLcode curl_global_init(long flags); /* * NAME curl_global_init_mem() @@ -1071,12 +1341,12 @@ CURLcode curl_global_init(long flags); * callback routines with be invoked by this library instead of the system * memory management routines like malloc, free etc. */ -CURLcode curl_global_init_mem(long flags, - curl_malloc_callback m, - curl_free_callback f, - curl_realloc_callback r, - curl_strdup_callback s, - curl_calloc_callback c); +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); /* * NAME curl_global_cleanup() @@ -1086,7 +1356,7 @@ CURLcode curl_global_init_mem(long flags, * curl_global_cleanup() should be invoked exactly once for each application * that uses libcurl */ -void curl_global_cleanup(void); +CURL_EXTERN void curl_global_cleanup(void); /* linked-list structure for the CURLOPT_QUOTE option (and other) */ struct curl_slist { @@ -1102,7 +1372,8 @@ struct curl_slist { * Appends a string to a linked list. If no list exists, it will be created * first. Returns the new list, after appending. */ -struct curl_slist *curl_slist_append(struct curl_slist *, const char *); +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); /* * NAME curl_slist_free_all() @@ -1111,7 +1382,7 @@ struct curl_slist *curl_slist_append(struct curl_slist *, const char *); * * free a previously built curl_slist. */ -void curl_slist_free_all(struct curl_slist *); +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); /* * NAME curl_getdate() @@ -1119,15 +1390,15 @@ void curl_slist_free_all(struct curl_slist *); * DESCRIPTION * * Returns the time, in seconds since 1 Jan 1970 of the time string given in - * the first argument. The time argument in the second parameter is for cases - * where the specified time is relative now, like 'two weeks' or 'tomorrow' - * etc. + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. */ -time_t curl_getdate(const char *p, const time_t *now); +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); #define CURLINFO_STRING 0x100000 #define CURLINFO_LONG 0x200000 #define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 #define CURLINFO_MASK 0x0fffff #define CURLINFO_TYPEMASK 0xf00000 @@ -1157,9 +1428,15 @@ typedef enum { CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 23 + CURLINFO_LASTONE = 30 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -1242,9 +1519,9 @@ typedef enum { CURLSHOPT_LAST /* never use */ } CURLSHoption; -CURLSH *curl_share_init(void); -CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); -CURLSHcode curl_share_cleanup(CURLSH *); +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -1254,15 +1531,16 @@ typedef enum { CURLVERSION_FIRST, CURLVERSION_SECOND, CURLVERSION_THIRD, + CURLVERSION_FOURTH, CURLVERSION_LAST /* never actually use this */ } CURLversion; /* The 'CURLVERSION_NOW' is the symbolic name meant to be used by basicly all programs ever, that want to get version information. It is meant to be a built-in version number for what kind of struct the caller - expects. If the struct ever changes, we redfine the NOW to another enum + expects. If the struct ever changes, we redefine the NOW to another enum from above. */ -#define CURLVERSION_NOW CURLVERSION_THIRD +#define CURLVERSION_NOW CURLVERSION_FOURTH typedef struct { CURLversion age; /* age of the returned struct */ @@ -1270,18 +1548,26 @@ typedef struct { unsigned int version_num; /* LIBCURL_VERSION_NUM */ const char *host; /* OS/host/cpu/machine when configured */ int features; /* bitmask, see defines below */ - char *ssl_version; /* human readable string */ - long ssl_version_num; /* number */ - const char *libz_version; /* human readable string */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ /* protocols is terminated by an entry with a NULL protoname */ - const char **protocols; + const char * const *protocols; /* The fields below this were added in CURLVERSION_SECOND */ const char *ares; int ares_num; - /* This field was aded in CURLVERSION_THIRD */ + /* This field was added in CURLVERSION_THIRD */ const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + } curl_version_info_data; #define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ @@ -1295,6 +1581,9 @@ typedef struct { #define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ #define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ #define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ +#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ +#define CURL_VERSION_CONV (1<<12) /* character conversions are + supported */ /* * NAME curl_version_info() @@ -1304,7 +1593,7 @@ typedef struct { * This function returns a pointer to a static copy of the version info * struct. See above. */ -curl_version_info_data *curl_version_info(CURLversion); +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); /* * NAME curl_easy_strerror() @@ -1315,7 +1604,7 @@ curl_version_info_data *curl_version_info(CURLversion); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -const char *curl_easy_strerror(CURLcode); +CURL_EXTERN const char *curl_easy_strerror(CURLcode); /* * NAME curl_share_strerror() @@ -1326,7 +1615,7 @@ const char *curl_easy_strerror(CURLcode); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -const char *curl_share_strerror(CURLSHcode); +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); #ifdef __cplusplus } diff --git a/Utilities/cmcurl/curl/curlver.h b/Utilities/cmcurl/curl/curlver.h index ef0a4c5..9380022 100644 --- a/Utilities/cmcurl/curl/curlver.h +++ b/Utilities/cmcurl/curl/curlver.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,7 +28,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.12.1" +#define LIBCURL_VERSION "7.16.1" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 16 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparions by programs. The LIBCURL_VERSION_NUM define will @@ -37,19 +43,14 @@ 0xXXYYZZ Where XX, YY and ZZ are the main version, release and patch numbers in - hexadecimal. All three numbers are always represented using two digits. 1.2 - would appear as "0x010200" while version 9.11.7 appears as "0x090b07". + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". - This 6-digit hexadecimal number does not show pre-release number, and it is - always a greater number in a more recent release. It makes comparisons with - greater than and less than work. + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. */ -#define LIBCURL_VERSION_NUM 0x70C01 - -/* The numeric version number is also available "in parts" by using these - defines: */ -#define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 12 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_NUM 0x071001 #endif /* __CURL_CURLVER_H */ diff --git a/Utilities/cmcurl/curl/easy.h b/Utilities/cmcurl/curl/easy.h index 336e542..17de210 100644 --- a/Utilities/cmcurl/curl/easy.h +++ b/Utilities/cmcurl/curl/easy.h @@ -26,10 +26,10 @@ extern "C" { #endif -CURL *curl_easy_init(void); -CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); -CURLcode curl_easy_perform(CURL *curl); -void curl_easy_cleanup(CURL *curl); +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); /* * NAME curl_easy_getinfo() @@ -44,7 +44,7 @@ void curl_easy_cleanup(CURL *curl); * performed transfer, all results from this function are undefined until the * transfer is completed. */ -CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); /* @@ -59,7 +59,7 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); * curl_easy_duphandle() for each new thread to avoid a series of identical * curl_easy_setopt() invokes in every thread. */ -CURL* curl_easy_duphandle(CURL *curl); +CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); /* * NAME curl_easy_reset() @@ -72,7 +72,7 @@ CURL* curl_easy_duphandle(CURL *curl); * It does keep: live connections, the Session ID cache, the DNS cache and the * cookies. */ -void curl_easy_reset(CURL *curl); +CURL_EXTERN void curl_easy_reset(CURL *curl); #ifdef __cplusplus } diff --git a/Utilities/cmcurl/curl/mprintf.h b/Utilities/cmcurl/curl/mprintf.h index 65dc114..b087d9a 100644 --- a/Utilities/cmcurl/curl/mprintf.h +++ b/Utilities/cmcurl/curl/mprintf.h @@ -1,16 +1,18 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -21,34 +23,48 @@ * $Id$ ***************************************************************************/ -#ifndef H_MPRINTF -#define H_MPRINTF - #include <stdarg.h> #include <stdio.h> /* needed for FILE */ -int curl_mprintf(const char *format, ...); -int curl_mfprintf(FILE *fd, const char *format, ...); -int curl_msprintf(char *buffer, const char *format, ...); -int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...); -int curl_mvprintf(const char *format, va_list args); -int curl_mvfprintf(FILE *fd, const char *format, va_list args); -int curl_mvsprintf(char *buffer, const char *format, va_list args); -int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list args); -char *curl_maprintf(const char *format, ...); -char *curl_mvaprintf(const char *format, va_list args); +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); #ifdef _MPRINTF_REPLACE # define printf curl_mprintf # define fprintf curl_mfprintf +#ifdef CURLDEBUG +/* When built with CURLDEBUG we define away the sprintf() functions since we + don't want internal code to be using them */ +# define sprintf sprintf_was_used +# define vsprintf vsprintf_was_used +#else # define sprintf curl_msprintf +# define vsprintf curl_mvsprintf +#endif # define snprintf curl_msnprintf # define vprintf curl_mvprintf # define vfprintf curl_mvfprintf -# define vsprintf curl_mvsprintf # define vsnprintf curl_mvsnprintf # define aprintf curl_maprintf # define vaprintf curl_mvaprintf #endif -#endif /* H_MPRINTF */ +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/Utilities/cmcurl/curl/multi.h b/Utilities/cmcurl/curl/multi.h index 3a867ab..d253372 100644 --- a/Utilities/cmcurl/curl/multi.h +++ b/Utilities/cmcurl/curl/multi.h @@ -1,18 +1,18 @@ #ifndef __CURL_MULTI_H #define __CURL_MULTI_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -23,16 +23,8 @@ * $Id$ ***************************************************************************/ /* - This is meant to be the "external" header file. Don't give away any - internals here! - - This document presents a mixture of ideas from at least: - - Daniel Stenberg - - Steve Dekorte - - Sterling Hughes - - Ben Greear + This is an "external" header file. Don't give away any internals here! - ------------------------------------------- GOALS o Enable a "pull" interface. The application that uses libcurl decides where @@ -43,30 +35,18 @@ o Enable the application to select() on its own file descriptors and curl's file descriptors simultaneous easily. - -*/ -#if defined(_WIN32) && !defined(WIN32) -/* Chris Lewis mentioned that he doesn't get WIN32 defined, only _WIN32 so we - make this adjustment to catch this. */ -#define WIN32 1 -#endif - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) -#include <winsock2.h> -#else - -/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish - libc5-based Linux systems. Only include it on system that are known to - require it! */ -#if defined(_AIX) || defined(NETWARE) -#include <sys/select.h> -#endif -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/types.h> -#endif +*/ +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * <curl/curl.h> without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ #include "curl.h" #ifdef __cplusplus @@ -76,15 +56,23 @@ extern "C" { typedef void CURLM; typedef enum { - CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */ + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ CURLM_OK, CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ CURLM_LAST } CURLMcode; +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + typedef enum { CURLMSG_NONE, /* first, not used */ CURLMSG_DONE, /* This easy handle has completed. 'result' contains @@ -106,27 +94,30 @@ typedef struct CURLMsg CURLMsg; * Name: curl_multi_init() * * Desc: inititalize multi-style curl usage + * * Returns: a new CURLM handle to use in all 'curl_multi' functions. */ -CURLM *curl_multi_init(void); +CURL_EXTERN CURLM *curl_multi_init(void); /* * Name: curl_multi_add_handle() * * Desc: add a standard curl handle to the multi stack + * * Returns: CURLMcode type, general multi error code. */ -CURLMcode curl_multi_add_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); /* * Name: curl_multi_remove_handle() * * Desc: removes a curl handle from the multi stack again + * * Returns: CURLMcode type, general multi error code. */ -CURLMcode curl_multi_remove_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); /* * Name: curl_multi_fdset() @@ -134,13 +125,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, * Desc: Ask curl for its fd_set sets. The app can use these to select() or * poll() on. We want curl_multi_perform() called as soon as one of * them are ready. + * * Returns: CURLMcode type, general multi error code. */ -CURLMcode curl_multi_fdset(CURLM *multi_handle, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *exc_fd_set, - int *max_fd); +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); /* * Name: curl_multi_perform() @@ -158,8 +150,8 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, * still have occurred problems on invidual transfers even when this * returns OK. */ -CURLMcode curl_multi_perform(CURLM *multi_handle, - int *running_handles); +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); /* * Name: curl_multi_cleanup() @@ -168,9 +160,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, * touch any individual easy handles in any way. We need to define * in what state those handles will be if this function is called * in the middle of a transfer. + * * Returns: CURLMcode type, general multi error code. */ -CURLMcode curl_multi_cleanup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); /* * Name: curl_multi_info_read() @@ -200,22 +193,135 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle); * queue (after this read) in the integer the second argument points * to. */ -CURLMsg *curl_multi_info_read(CURLM *multi_handle, - int *msgs_in_queue); +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,number) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + number +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + /* - * NAME curl_multi_strerror() + * Name: curl_multi_assign() * - * DESCRIPTION + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. * - * The curl_multi_strerror function may be used to turn a CURLMcode value - * into the equivalent human readable error string. This is useful - * for printing meaningful error messages. + * Returns: CURLM error code. */ -const char *curl_multi_strerror(CURLMcode); +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); #ifdef __cplusplus } /* end of extern "C" */ #endif - + #endif diff --git a/Utilities/cmcurl/curlx.h b/Utilities/cmcurl/curlx.h index 0dd9a09..26948d3 100644 --- a/Utilities/cmcurl/curlx.h +++ b/Utilities/cmcurl/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -79,7 +79,19 @@ #ifdef ENABLE_CURLX_PRINTF /* If this define is set, we define all "standard" printf() functions to use the curlx_* version instead. It makes the source code transparant and - easier to understand/patch. */ + easier to understand/patch. Undefine them first in case _MPRINTF_REPLACE + is set. */ +# undef printf +# undef fprintf +# undef sprintf +# undef snprintf +# undef vprintf +# undef vfprintf +# undef vsprintf +# undef vsnprintf +# undef aprintf +# undef vaprintf + # define printf curlx_mprintf # define fprintf curlx_mfprintf # define sprintf curlx_msprintf diff --git a/Utilities/cmcurl/dict.c b/Utilities/cmcurl/dict.c index 06cb6b6..d6443f4 100644 --- a/Utilities/cmcurl/dict.c +++ b/Utilities/cmcurl/dict.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -23,18 +23,22 @@ #include "setup.h" +#ifndef CURL_DISABLE_DICT + /* -- WIN32 approved -- */ #include <stdio.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif -#include <errno.h> - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -42,7 +46,9 @@ #include <sys/socket.h> #endif #include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -79,20 +85,59 @@ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> -CURLcode Curl_dict(struct connectdata *conn) +/* The last #include file should be: */ +#include "memdebug.h" + +static char *unescape_word(struct SessionHandle *data, char *inp) +{ + char *newp; + char *dictp; + char *ptr; + int len; + unsigned char byte; + int olen=0; + + newp = curl_easy_unescape(data, inp, 0, &len); + if(!newp) + return NULL; + + dictp = malloc(len*2 + 1); /* add one for terminating zero */ + if(dictp) { + /* According to RFC2229 section 2.2, these letters need to be escaped with + \[letter] */ + for(ptr = newp; + (byte = (unsigned char)*ptr) != 0; + ptr++) { + if ((byte <= 32) || (byte == 127) || + (byte == '\'') || (byte == '\"') || (byte == '\\')) { + dictp[olen++] = '\\'; + } + dictp[olen++] = byte; + } + dictp[olen]=0; + + free(newp); + } + return dictp; +} + +CURLcode Curl_dict(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; + CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - char *path = conn->path; - curl_off_t *bytecount = &conn->bytecount; + char *path = data->reqdata.path; + curl_off_t *bytecount = &data->reqdata.keep.bytecount; + + *done = TRUE; /* unconditionally */ if(conn->bits.user_passwd) { /* AUTH is missing */ @@ -101,7 +146,7 @@ CURLcode Curl_dict(struct connectdata *conn) 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++; @@ -118,7 +163,7 @@ CURLcode Curl_dict(struct connectdata *conn) } } } - + if ((word == NULL) || (*word == (char)0)) { failf(data, "lookup word is missing"); } @@ -128,31 +173,38 @@ CURLcode Curl_dict(struct connectdata *conn) 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 "\n" + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "MATCH " "%s " /* database */ "%s " /* strategy */ - "%s\n" /* word */ - "QUIT\n", - + "%s\r\n" /* word */ + "QUIT\r\n", + database, strategy, - word + eword ); + + free(eword); + if(result) failf(data, "Failed sending DICT request"); else - result = Curl_Transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, - -1, NULL); /* no upload */ + 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++; @@ -165,57 +217,64 @@ CURLcode Curl_dict(struct connectdata *conn) } } } - + if ((word == NULL) || (*word == (char)0)) { failf(data, "lookup word is missing"); } 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 "\n" + "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "DEFINE " "%s " /* database */ - "%s\n" /* word */ - "QUIT\n", + "%s\r\n" /* word */ + "QUIT\r\n", database, - word); + eword); + + free(eword); + if(result) failf(data, "Failed sending DICT request"); else - result = Curl_Transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, - -1, NULL); /* no upload */ - + 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 "\n" - "%s\n" - "QUIT\n", ppath); + "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_Transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, - -1, NULL); + result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, + -1, NULL); if(result) return result; } } - (void)nthdef; return CURLE_OK; } +#endif /*CURL_DISABLE_DICT*/ diff --git a/Utilities/cmcurl/dict.h b/Utilities/cmcurl/dict.h index 4301f01..d3da193 100644 --- a/Utilities/cmcurl/dict.h +++ b/Utilities/cmcurl/dict.h @@ -2,18 +2,18 @@ #define __DICT_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,7 +24,7 @@ * $Id$ ***************************************************************************/ #ifndef CURL_DISABLE_DICT -CURLcode Curl_dict(struct connectdata *conn); +CURLcode Curl_dict(struct connectdata *conn, bool *done); CURLcode Curl_dict_done(struct connectdata *conn); #endif #endif diff --git a/Utilities/cmcurl/easy.c b/Utilities/cmcurl/easy.c index fb116fd..f3c2273 100644 --- a/Utilities/cmcurl/easy.c +++ b/Utilities/cmcurl/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,14 +29,18 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif #include <errno.h> #include "strequal.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -44,7 +48,9 @@ #include <sys/socket.h> #endif #include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -71,20 +77,37 @@ #include "urldata.h" #include <curl/curl.h> #include "transfer.h" -#include "ssluse.h" +#include "sslgen.h" #include "url.h" #include "getinfo.h" #include "hostip.h" #include "share.h" -#include "curl_memory.h" +#include "strdup.h" +#include "memory.h" +#include "progress.h" +#include "easyif.h" +#include "sendf.h" /* for failf function prototype */ +#include <ca-bundle.h> #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) +#include <iconv.h> +/* set default codesets for iconv */ +#ifndef CURL_ICONV_CODESET_OF_NETWORK +#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1" +#endif +#ifndef CURL_ICONV_CODESET_FOR_UTF8 +#define CURL_ICONV_CODESET_FOR_UTF8 "UTF-8" +#endif +#define ICONV_ERROR (size_t)-1 +#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ + /* The last #include file should be: */ #include "memdebug.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef USE_WINSOCK /* win32_cleanup() is for win32 socket cleanup functionality, the opposite of win32_init() */ static void win32_cleanup(void) @@ -100,12 +123,12 @@ static CURLcode win32_init(void) WSADATA wsaData; int err; -#ifdef ENABLE_IPV6 - wVersionRequested = MAKEWORD(2, 0); -#else - wVersionRequested = MAKEWORD(1, 1); +#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2) + Error IPV6_requires_winsock2 #endif + wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); + err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) @@ -160,17 +183,31 @@ static void idna_init (void) #endif /* USE_LIBIDN */ /* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized = 0; -static long init_flags = 0; +static unsigned int initialized; +static long init_flags; + +/* + * strdup (and other memory functions) is redefined in complicated + * ways, but at this point it must be defined as the system-supplied strdup + * so the callback pointer is initialized correctly. + */ +#if defined(_WIN32_WCE) +#define system_strdup _strdup +#elif !defined(HAVE_STRDUP) +#define system_strdup curlx_strdup +#else +#define system_strdup strdup +#endif /* * If a memory-using function (like curl_getenv) is used before * curl_global_init() is called, we need to have these pointers set already. */ + curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; -curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; /** @@ -179,18 +216,19 @@ curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; */ CURLcode curl_global_init(long flags) { - if (initialized) + if (initialized++) return CURLE_OK; /* Setup the default memory functions here (again) */ Curl_cmalloc = (curl_malloc_callback)malloc; Curl_cfree = (curl_free_callback)free; Curl_crealloc = (curl_realloc_callback)realloc; - Curl_cstrdup = (curl_strdup_callback)strdup; + Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; if (flags & CURL_GLOBAL_SSL) - Curl_SSL_init(); + if (!Curl_ssl_init()) + return CURLE_FAILED_INIT; if (flags & CURL_GLOBAL_WIN32) if (win32_init() != CURLE_OK) @@ -205,7 +243,6 @@ CURLcode curl_global_init(long flags) idna_init(); #endif - initialized = 1; init_flags = flags; return CURLE_OK; @@ -219,7 +256,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, curl_free_callback f, curl_realloc_callback r, curl_strdup_callback s, curl_calloc_callback c) { - CURLcode code; + CURLcode code = CURLE_OK; /* Invalid input, return immediately */ if (!m || !f || !r || !s || !c) @@ -251,10 +288,13 @@ void curl_global_cleanup(void) if (!initialized) return; + if (--initialized) + return; + Curl_global_host_cache_dtor(); if (init_flags & CURL_GLOBAL_SSL) - Curl_SSL_cleanup(); + Curl_ssl_cleanup(); if (init_flags & CURL_GLOBAL_WIN32) win32_cleanup(); @@ -263,7 +303,6 @@ void curl_global_cleanup(void) amiga_cleanup(); #endif - initialized = 0; init_flags = 0; } @@ -296,14 +335,10 @@ CURL *curl_easy_init(void) * curl_easy_setopt() is the external interface for setting options on an * easy handle. */ -typedef int (*func_T)(void); + CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) { va_list arg; - func_T param_func; - long param_long; - void *param_obj; - curl_off_t param_offset; struct SessionHandle *data = curl; CURLcode ret; @@ -312,38 +347,95 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) va_start(arg, tag); - /* PORTING NOTE: - Object pointers can't necessarily be casted to function pointers and - therefore we need to know what type it is and read the correct type - at once. This should also correct problems with different sizes of - the types. - */ - - if(tag < CURLOPTTYPE_OBJECTPOINT) { - /* This is a LONG type */ - param_long = va_arg(arg, long); - ret = Curl_setopt(data, tag, param_long); - } - else if(tag < CURLOPTTYPE_FUNCTIONPOINT) { - /* This is a object pointer type */ - param_obj = va_arg(arg, void *); - ret = Curl_setopt(data, tag, param_obj); - } - else if(tag < CURLOPTTYPE_OFF_T) { - /* This is a function pointer type */ - param_func = va_arg(arg, func_T ); - ret = Curl_setopt(data, tag, param_func); - } - else { - /* This is a curl_off_t type */ - param_offset = va_arg(arg, curl_off_t); - ret = Curl_setopt(data, tag, param_offset); - } + ret = Curl_setopt(data, tag, arg); va_end(arg); return ret; } +#ifdef CURL_MULTIEASY +/*************************************************************************** + * This function is still only for testing purposes. It makes a great way + * to run the full test suite on the multi interface instead of the easy one. + *************************************************************************** + * + * The *new* curl_easy_perform() is the external interface that performs a + * transfer previously setup. + * + * Wrapper-function that: creates a multi handle, adds the easy handle to it, + * runs curl_multi_perform() until the transfer is done, then detaches the + * easy handle, destroys the multi handle and returns the easy handle's return + * code. This will make everything internally use and assume multi interface. + */ +CURLcode curl_easy_perform(CURL *easy) +{ + CURLM *multi; + CURLMcode mcode; + CURLcode code = CURLE_OK; + int still_running; + struct timeval timeout; + int rc; + CURLMsg *msg; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd; + + if(!easy) + return CURLE_BAD_FUNCTION_ARGUMENT; + + multi = curl_multi_init(); + if(!multi) + return CURLE_OUT_OF_MEMORY; + + mcode = curl_multi_add_handle(multi, easy); + if(mcode) { + curl_multi_cleanup(multi); + return CURLE_FAILED_INIT; + } + + /* we start some action by calling perform right away */ + + do { + while(CURLM_CALL_MULTI_PERFORM == + curl_multi_perform(multi, &still_running)); + + if(!still_running) + break; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* timeout once per second */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + /* get file descriptors from the transfers */ + curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); + + rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); + + if(rc == -1) + /* select error */ + break; + + /* timeout or data to send/receive => loop! */ + } while(still_running); + + msg = curl_multi_info_read(multi, &rc); + if(msg) + code = msg->data.result; + + mcode = curl_multi_remove_handle(multi, easy); + /* what to do if it fails? */ + + mcode = curl_multi_cleanup(multi); + /* what to do if it fails? */ + + return code; +} +#else /* * curl_easy_perform() is the external interface that performs a transfer * previously setup. @@ -358,16 +450,18 @@ CURLcode curl_easy_perform(CURL *curl) if ( ! (data->share && data->share->hostcache) ) { if (Curl_global_host_cache_use(data) && - data->hostcache != Curl_global_host_cache_get()) { - if (data->hostcache) - Curl_hash_destroy(data->hostcache); - data->hostcache = Curl_global_host_cache_get(); + (data->dns.hostcachetype != HCACHE_GLOBAL)) { + if (data->dns.hostcachetype == HCACHE_PRIVATE) + Curl_hash_destroy(data->dns.hostcache); + data->dns.hostcache = Curl_global_host_cache_get(); + data->dns.hostcachetype = HCACHE_GLOBAL; } - if (!data->hostcache) { - data->hostcache = Curl_mk_dnscache(); + if (!data->dns.hostcache) { + data->dns.hostcachetype = HCACHE_PRIVATE; + data->dns.hostcache = Curl_mk_dnscache(); - if(!data->hostcache) + if(!data->dns.hostcache) /* While we possibly could survive and do good without a host cache, the fact that creating it failed indicates that things are truly screwed up and we should bail out! */ @@ -376,8 +470,16 @@ CURLcode curl_easy_perform(CURL *curl) } + if(!data->state.connc) { + /* oops, no connection cache, make one up */ + data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, -1); + if(!data->state.connc) + return CURLE_OUT_OF_MEMORY; + } + return Curl_perform(data); } +#endif /* * curl_easy_cleanup() is the external interface to cleaning/freeing the given @@ -390,15 +492,26 @@ void curl_easy_cleanup(CURL *curl) if(!data) return; - if ( ! (data->share && data->share->hostcache) ) { - if ( !Curl_global_host_cache_use(data)) { - Curl_hash_destroy(data->hostcache); - } - } Curl_close(data); } /* + * Store a pointed to the multi handle within the easy handle's data struct. + */ +void Curl_easy_addmulti(struct SessionHandle *data, + void *multi) +{ + data->multi = multi; +} + +void Curl_easy_initHandleData(struct SessionHandle *data) +{ + memset(&data->reqdata, 0, sizeof(struct HandleData)); + + data->reqdata.maxdownload = -1; +} + +/* * curl_easy_getinfo() is an external interface that allows an app to retrieve * information from a performed transfer and similar. */ @@ -445,21 +558,21 @@ CURL *curl_easy_duphandle(CURL *incurl) /* copy all userdefined values */ outcurl->set = data->set; - outcurl->state.numconnects = data->state.numconnects; - outcurl->state.connects = (struct connectdata **) - malloc(sizeof(struct connectdata *) * outcurl->state.numconnects); - if(!outcurl->state.connects) { + 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; - } - memset(outcurl->state.connects, 0, - sizeof(struct connectdata *)*outcurl->state.numconnects); + outcurl->state.lastconnect = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; -#ifndef CURL_DISABLE_HTTP +#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! */ @@ -474,18 +587,14 @@ CURL *curl_easy_duphandle(CURL *incurl) #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.proxy) { - outcurl->change.proxy = strdup(data->change.proxy); - if(!outcurl->change.proxy) - break; - outcurl->change.proxy_alloc = TRUE; - } + if(data->change.referer) { outcurl->change.referer = strdup(data->change.referer); if(!outcurl->change.referer) @@ -499,18 +608,29 @@ CURL *curl_easy_duphandle(CURL *incurl) 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.connects) - free(outcurl->state.connects); + if(outcurl->state.connc->type == CONNCACHE_PRIVATE) + Curl_rm_connc(outcurl->state.connc); if(outcurl->state.headerbuff) free(outcurl->state.headerbuff); - if(outcurl->change.proxy) - free(outcurl->change.proxy); if(outcurl->change.url) free(outcurl->change.url); if(outcurl->change.referer) @@ -531,12 +651,21 @@ void curl_easy_reset(CURL *curl) { struct SessionHandle *data = (struct SessionHandle *)curl; + Curl_safefree(data->reqdata.pathbuffer); + data->reqdata.pathbuffer=NULL; + + Curl_safefree(data->reqdata.proto.generic); + data->reqdata.proto.generic=NULL; + /* zero out UserDefined data: */ memset(&data->set, 0, sizeof(struct UserDefined)); /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); + /* init Handle data */ + Curl_easy_initHandleData(data); + /* The remainder of these calls have been taken from Curl_open() */ data->set.out = stdout; /* default output to stdout */ @@ -550,6 +679,7 @@ void curl_easy_reset(CURL *curl) data->set.fread = (curl_read_callback)fread; data->set.infilesize = -1; /* we don't know any size */ + data->set.postfieldsize = -1; data->state.current_speed = -1; /* init to negative == impossible */ @@ -561,6 +691,7 @@ void curl_easy_reset(CURL *curl) /* make libcurl quiet by default: */ data->set.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ + data->progress.flags |= PGRS_HIDE; /* Set the default size of the SSL session ID cache */ data->set.ssl.numsessions = 5; @@ -580,4 +711,185 @@ void curl_easy_reset(CURL *curl) /* This is our prefered CA cert bundle since install time */ data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; #endif + + data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth + type */ } + +#ifdef CURL_DOES_CONVERSIONS +/* + * Curl_convert_to_network() is an internal function + * for performing ASCII conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_to_network(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convtonetwork) { + /* use translation callback */ + rc = data->set.convtonetwork(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->outbound_cd == (iconv_t)-1) { + data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, + CURL_ICONV_CODESET_OF_HOST); + if(data->outbound_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_NETWORK, + CURL_ICONV_CODESET_OF_HOST, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_to_network iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } +#else + failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +/* + * Curl_convert_from_network() is an internal function + * for performing ASCII conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_from_network(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convfromnetwork) { + /* use translation callback */ + rc = data->set.convfromnetwork(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->inbound_cd == (iconv_t)-1) { + data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_OF_NETWORK); + if(data->inbound_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_OF_NETWORK, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_from_network iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } +#else + failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +/* + * Curl_convert_from_utf8() is an internal function + * for performing UTF-8 conversions on non-ASCII platforms. + */ +CURLcode Curl_convert_from_utf8(struct SessionHandle *data, + char *buffer, size_t length) +{ + CURLcode rc; + + if(data->set.convfromutf8) { + /* use translation callback */ + rc = data->set.convfromutf8(buffer, length); + if(rc != CURLE_OK) { + failf(data, + "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %i: %s", + rc, curl_easy_strerror(rc)); + } + return(rc); + } else { +#ifdef HAVE_ICONV + /* do the translation ourselves */ + char *input_ptr, *output_ptr; + size_t in_bytes, out_bytes, rc; + + /* open an iconv conversion descriptor if necessary */ + if(data->utf8_cd == (iconv_t)-1) { + data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_FOR_UTF8); + if(data->utf8_cd == (iconv_t)-1) { + failf(data, + "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", + CURL_ICONV_CODESET_OF_HOST, + CURL_ICONV_CODESET_FOR_UTF8, + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + } + /* call iconv */ + input_ptr = output_ptr = buffer; + in_bytes = out_bytes = length; + rc = iconv(data->utf8_cd, (const char**)&input_ptr, &in_bytes, + &output_ptr, &out_bytes); + if ((rc == ICONV_ERROR) || (in_bytes != 0)) { + failf(data, + "The Curl_convert_from_utf8 iconv call failed with errno %i: %s", + errno, strerror(errno)); + return CURLE_CONV_FAILED; + } + if (output_ptr < input_ptr) { + /* null terminate the now shorter output string */ + *output_ptr = 0x00; + } +#else + failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required"); + return CURLE_CONV_REQD; +#endif /* HAVE_ICONV */ + } + + return CURLE_OK; +} + +#endif /* CURL_DOES_CONVERSIONS */ diff --git a/Utilities/cmcurl/easyif.h b/Utilities/cmcurl/easyif.h new file mode 100644 index 0000000..4c0f7e7 --- /dev/null +++ b/Utilities/cmcurl/easyif.h @@ -0,0 +1,40 @@ +#ifndef __EASYIF_H +#define __EASYIF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* + * Prototypes for library-wide functions provided by easy.c + */ +void Curl_easy_addmulti(struct SessionHandle *data, void *multi); + +void Curl_easy_initHandleData(struct SessionHandle *data); + +CURLcode Curl_convert_to_network(struct SessionHandle *data, + char *buffer, size_t length); +CURLcode Curl_convert_from_network(struct SessionHandle *data, + char *buffer, size_t length); +CURLcode Curl_convert_from_utf8(struct SessionHandle *data, + char *buffer, size_t length); + +#endif /* __EASYIF_H */ diff --git a/Utilities/cmcurl/escape.c b/Utilities/cmcurl/escape.c index 2b9a883..9552b0f 100644 --- a/Utilities/cmcurl/escape.c +++ b/Utilities/cmcurl/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,7 +31,10 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "curl_memory.h" +#include "memory.h" +/* urldata.h and easyif.h are included for Curl_convert_... prototypes */ +#include "urldata.h" +#include "easyif.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -39,16 +42,32 @@ /* The last #include file should be: */ #include "memdebug.h" +/* for ABI-compatibility with previous versions */ char *curl_escape(const char *string, int inlength) { + return curl_easy_escape(NULL, string, inlength); +} + +/* for ABI-compatibility with previous versions */ +char *curl_unescape(const char *string, int length) +{ + return curl_easy_unescape(NULL, string, length, NULL); +} + +char *curl_easy_escape(CURL *handle, const char *string, int inlength) +{ size_t alloc = (inlength?(size_t)inlength:strlen(string))+1; char *ns; - char *testing_ptr; + char *testing_ptr = NULL; unsigned char in; size_t newlen = alloc; int strindex=0; size_t length; +#ifndef CURL_DOES_CONVERSIONS + /* avoid compiler warnings */ + (void)handle; +#endif ns = malloc(alloc); if(!ns) return NULL; @@ -72,6 +91,17 @@ char *curl_escape(const char *string, int inlength) ns = testing_ptr; } } + +#ifdef CURL_DOES_CONVERSIONS +/* escape sequences are always in ASCII so convert them on non-ASCII hosts */ + if (!handle || + (Curl_convert_to_network(handle, &in, 1) != CURLE_OK)) { + /* Curl_convert_to_network calls failf if unsuccessful */ + free(ns); + return NULL; + } +#endif /* CURL_DOES_CONVERSIONS */ + snprintf(&ns[strindex], 4, "%%%02X", in); strindex+=3; @@ -86,11 +116,8 @@ char *curl_escape(const char *string, int inlength) return ns; } -#define ishex(in) ((in >= 'a' && in <= 'f') || \ - (in >= 'A' && in <= 'F') || \ - (in >= '0' && in <= '9')) - -char *curl_unescape(const char *string, int length) +char *curl_easy_unescape(CURL *handle, const char *string, int length, + int *olen) { int alloc = (length?length:(int)strlen(string))+1; char *ns = malloc(alloc); @@ -98,12 +125,16 @@ char *curl_unescape(const char *string, int length) int strindex=0; long hex; +#ifndef CURL_DOES_CONVERSIONS + /* avoid compiler warnings */ + (void)handle; +#endif if( !ns ) return NULL; while(--alloc > 0) { in = *string; - if(('%' == in) && ishex(string[1]) && ishex(string[2])) { + if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ char hexstr[3]; char *ptr; @@ -114,6 +145,17 @@ char *curl_unescape(const char *string, int length) hex = strtol(hexstr, &ptr, 16); in = (unsigned char)hex; /* this long is never bigger than 255 anyway */ + +#ifdef CURL_DOES_CONVERSIONS +/* escape sequences are always in ASCII so convert them on non-ASCII hosts */ + if (!handle || + (Curl_convert_from_network(handle, &in, 1) != CURLE_OK)) { + /* Curl_convert_from_network calls failf if unsuccessful */ + free(ns); + return NULL; + } +#endif /* CURL_DOES_CONVERSIONS */ + string+=2; alloc-=2; } @@ -122,6 +164,10 @@ char *curl_unescape(const char *string, int length) string++; } ns[strindex]=0; /* terminate it */ + + if(olen) + /* store output size */ + *olen = strindex; return ns; } diff --git a/Utilities/cmcurl/escape.h b/Utilities/cmcurl/escape.h index 4d29236..a0a0209 100644 --- a/Utilities/cmcurl/escape.h +++ b/Utilities/cmcurl/escape.h @@ -2,18 +2,18 @@ #define __ESCAPE_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -26,7 +26,5 @@ /* Escape and unescape URL encoding in strings. The functions return a new * allocated string or NULL if an error occurred. */ -char *curl_escape(const char *string, int length); -char *curl_unescape(const char *string, int length); #endif diff --git a/Utilities/cmcurl/file.c b/Utilities/cmcurl/file.c index cd4366c..b247a77 100644 --- a/Utilities/cmcurl/file.c +++ b/Utilities/cmcurl/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,12 +30,14 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif -#include <errno.h> - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #include <fcntl.h> @@ -46,7 +48,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -66,9 +70,6 @@ #include <sys/param.h> #endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif @@ -85,7 +86,8 @@ #include "getinfo.h" #include "transfer.h" #include "url.h" -#include "curl_memory.h" +#include "memory.h" +#include "parsedate.h" /* for the week day and month names */ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -100,10 +102,10 @@ */ CURLcode Curl_file_connect(struct connectdata *conn) { - char *real_path = curl_unescape(conn->path, 0); + char *real_path = curl_easy_unescape(conn->data, conn->data->reqdata.path, 0, NULL); struct FILEPROTO *file; int fd; -#if defined(WIN32) || defined(__EMX__) +#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) int i; char *actual_path; #endif @@ -117,9 +119,13 @@ CURLcode Curl_file_connect(struct connectdata *conn) return CURLE_OUT_OF_MEMORY; } - conn->proto.file = file; + if (conn->data->reqdata.proto.file) { + free(conn->data->reqdata.proto.file); + } + + conn->data->reqdata.proto.file = file; -#if defined(WIN32) || defined(__EMX__) +#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) /* 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 @@ -143,7 +149,7 @@ CURLcode Curl_file_connect(struct connectdata *conn) actual_path++; } - /* change path separators from '/' to '\\' for Windows and OS/2 */ + /* 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] = '\\'; @@ -156,31 +162,31 @@ CURLcode Curl_file_connect(struct connectdata *conn) #endif file->freepath = real_path; /* free this when done */ + file->fd = fd; if(!conn->data->set.upload && (fd == -1)) { - failf(conn->data, "Couldn't open file %s", conn->path); - Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE); + failf(conn->data, "Couldn't open file %s", conn->data->reqdata.path); + Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE); return CURLE_FILE_COULDNT_READ_FILE; } - file->fd = fd; return CURLE_OK; } -#if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4) -#define lseek(x,y,z) _lseeki64(x, y, z) -#endif - CURLcode Curl_file_done(struct connectdata *conn, - CURLcode status) + CURLcode status, bool premature) { - struct FILEPROTO *file = conn->proto.file; + struct FILEPROTO *file = conn->data->reqdata.proto.file; (void)status; /* not used */ + (void)premature; /* not used */ Curl_safefree(file->freepath); + if(file->fd != -1) + close(file->fd); + return CURLE_OK; } -#if defined(WIN32) || defined(__EMX__) +#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) #define DIRSEP '\\' #else #define DIRSEP '/' @@ -188,7 +194,7 @@ CURLcode Curl_file_done(struct connectdata *conn, static CURLcode file_upload(struct connectdata *conn) { - struct FILEPROTO *file = conn->proto.file; + struct FILEPROTO *file = conn->data->reqdata.proto.file; char *dir = strchr(file->path, DIRSEP); FILE *fp; CURLcode res=CURLE_OK; @@ -205,7 +211,7 @@ static CURLcode file_upload(struct connectdata *conn) */ conn->fread = data->set.fread; conn->fread_in = data->set.in; - conn->upload_fromhere = buf; + conn->data->reqdata.upload_fromhere = buf; if(!dir) return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ @@ -227,13 +233,13 @@ static CURLcode file_upload(struct connectdata *conn) int readcount; res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount); if(res) - return res; - - nread = (size_t)readcount; + break; - if (nread <= 0) + if (readcount <= 0) /* fix questionable compare error. curlvms */ break; + nread = (size_t)readcount; + /* write the data to the target */ nwrite = fwrite(buf, 1, nread, fp); if(nwrite != nread) { @@ -266,7 +272,7 @@ static CURLcode file_upload(struct connectdata *conn) * opposed to sockets) we instead perform the whole do-operation in this * function. */ -CURLcode Curl_file(struct connectdata *conn) +CURLcode Curl_file(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) @@ -274,7 +280,9 @@ CURLcode Curl_file(struct connectdata *conn) (via NFS, Samba, NT sharing) can be accessed through a file:// URL */ CURLcode res = CURLE_OK; - struct stat statbuf; + 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; @@ -284,6 +292,8 @@ CURLcode Curl_file(struct connectdata *conn) int fd; struct timeval now = Curl_tvnow(); + *done = TRUE; /* unconditionally */ + Curl_readwrite_init(conn); Curl_initinfo(data); Curl_pgrsStartNow(data); @@ -292,7 +302,7 @@ CURLcode Curl_file(struct connectdata *conn) return file_upload(conn); /* get the fd from the connection phase */ - fd = conn->proto.file->fd; + fd = conn->data->reqdata.proto.file->fd; /* VMS: This only works reliable for STREAMLF files */ if( -1 != fstat(fd, &statbuf)) { @@ -308,40 +318,45 @@ CURLcode Curl_file(struct connectdata *conn) CURLcode result; snprintf(buf, sizeof(data->state.buffer), "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0); if(result) return result; - result = Curl_client_write(data, CLIENTWRITE_BOTH, + result = Curl_client_write(conn, CLIENTWRITE_BOTH, (char *)"Accept-ranges: bytes\r\n", 0); if(result) return result; -#ifdef HAVE_STRFTIME if(fstated) { struct tm *tm; - time_t cuClock = (time_t)statbuf.st_mtime; + time_t clock = (time_t)statbuf.st_mtime; #ifdef HAVE_GMTIME_R struct tm buffer; - tm = (struct tm *)gmtime_r(&cuClock, &buffer); + tm = (struct tm *)gmtime_r(&clock, &buffer); #else - tm = gmtime(&cuClock); + tm = gmtime(&clock); #endif /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n", - tm); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + 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); } -#endif return result; } - /* Added by Dolbneff A.V & Spiridonoff A.V */ - if (conn->resume_from <= expected_size) - expected_size -= conn->resume_from; - else - /* Is this error code suitable in such situation? */ - return CURLE_FTP_BAD_DOWNLOAD_RESUME; + if (data->reqdata.resume_from <= expected_size) + expected_size -= data->reqdata.resume_from; + else { + failf(data, "failed to resume file:// transfer"); + return CURLE_BAD_DOWNLOAD_RESUME; + } if (fstated && (expected_size == 0)) return CURLE_OK; @@ -353,8 +368,11 @@ CURLcode Curl_file(struct connectdata *conn) if(fstated) Curl_pgrsSetDownloadSize(data, expected_size); - if(conn->resume_from) - lseek(fd, conn->resume_from, SEEK_SET); + if(data->reqdata.resume_from) { + if(data->reqdata.resume_from != + lseek(fd, data->reqdata.resume_from, SEEK_SET)) + return CURLE_BAD_DOWNLOAD_RESUME; + } Curl_pgrsTime(data, TIMER_STARTTRANSFER); @@ -369,7 +387,7 @@ CURLcode Curl_file(struct connectdata *conn) bytecount += nread; - res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread); + res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread); if(res) return res; @@ -383,8 +401,7 @@ CURLcode Curl_file(struct connectdata *conn) if(Curl_pgrsUpdate(conn)) res = CURLE_ABORTED_BY_CALLBACK; - close(fd); - return res; } + #endif diff --git a/Utilities/cmcurl/file.h b/Utilities/cmcurl/file.h index 689b8ae..20a1c4c 100644 --- a/Utilities/cmcurl/file.h +++ b/Utilities/cmcurl/file.h @@ -2,18 +2,18 @@ #define __FILE_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,8 +24,8 @@ * $Id$ ***************************************************************************/ #ifndef CURL_DISABLE_FILE -CURLcode Curl_file(struct connectdata *); -CURLcode Curl_file_done(struct connectdata *, CURLcode); +CURLcode Curl_file(struct connectdata *, bool *done); +CURLcode Curl_file_done(struct connectdata *, CURLcode, bool premature); CURLcode Curl_file_connect(struct connectdata *); #endif #endif diff --git a/Utilities/cmcurl/formdata.c b/Utilities/cmcurl/formdata.c index 0e66e60..f10c6c7 100644 --- a/Utilities/cmcurl/formdata.c +++ b/Utilities/cmcurl/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,7 @@ /* Debug the form generator stand-alone by compiling this source file with: - gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c + gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -DCURLDEBUG -o formdata -I../include formdata.c strequal.c memdebug.c mprintf.c strerror.c run the 'formdata' executable the output should end with: All Tests seem to have worked ... @@ -63,7 +63,7 @@ Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz Content-Disposition: attachment; filename="inet_ntoa_r.h" Content-Type: text/plain ... -Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Disposition: attachment; filename="Makefile.b32" Content-Type: text/plain ... @@ -73,7 +73,7 @@ Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 Content-Disposition: attachment; filename="inet_ntoa_r.h" Content-Type: text/plain ... -Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Disposition: attachment; filename="Makefile.b32" Content-Type: text/plain ... Content-Disposition: attachment; filename="inet_ntoa_r.h" @@ -87,7 +87,7 @@ Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 Content-Disposition: attachment; filename="inet_ntoa_r.h" Content-Type: text/plain ... -Content-Disposition: attachment; filename="Makefile.b32.resp" +Content-Disposition: attachment; filename="Makefile.b32" Content-Type: text/plain ... Content-Disposition: attachment; filename="inet_ntoa_r.h" @@ -105,17 +105,24 @@ Content-Disposition: form-data; name="FILECONTENT" /* Length of the random boundary string. */ #define BOUNDARY_LENGTH 40 -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <time.h> +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif +#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) +#include <libgen.h> +#endif +#include "urldata.h" /* for struct SessionHandle */ +#include "easyif.h" /* for Curl_convert_... prototypes */ #include "formdata.h" #include "strequal.h" -#include "curl_memory.h" +#include "memory.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -123,6 +130,17 @@ Content-Disposition: form-data; name="FILECONTENT" /* The last #include file should be: */ #include "memdebug.h" +#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ + +#ifndef CURL_DISABLE_HTTP + +#if defined(HAVE_BASENAME) && defined(NEED_BASENAME_PROTO) +/* This system has a basename() but no prototype for it! */ +char *basename(char *path); +#endif + +static size_t readfromfile(struct Form *form, char *buffer, size_t size); + /* What kind of Content-Type to use on un-specified files with unrecognized extensions. */ #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream" @@ -241,7 +259,7 @@ static FormInfo * AddFormInfo(char *value, static const char * ContentTypeForFilename (const char *filename, const char *prevtype) { - const char *contenttype; + const char *contenttype = NULL; unsigned int i; /* * No type was specified, we scan through a few well-known @@ -251,7 +269,7 @@ static const char * ContentTypeForFilename (const char *filename, const char *extension; const char *type; }; - static struct ContentType ctts[]={ + static const struct ContentType ctts[]={ {".gif", "image/gif"}, {".jpg", "image/jpeg"}, {".jpeg", "image/jpeg"}, @@ -374,7 +392,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, FormInfo *first_form, *current_form, *form = NULL; CURLFORMcode return_value = CURL_FORMADD_OK; const char *prevtype = NULL; - struct curl_httppost *post; + struct curl_httppost *post = NULL; CURLformoption option; struct curl_forms *forms = NULL; char *array_value=NULL; /* value read from an array */ @@ -436,7 +454,12 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, * 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; @@ -454,7 +477,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, return_value = CURL_FORMADD_OPTION_TWICE; else current_form->namelength = - array_state?(long)array_value:va_arg(params, long); + array_state?(long)array_value:(long)va_arg(params, long); break; /* @@ -512,8 +535,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if (current_form->value) { if (current_form->flags & HTTPPOST_FILENAME) { if (filename) { - if (!(current_form = AddFormInfo(strdup(filename), - NULL, current_form))) + if ((current_form = AddFormInfo(strdup(filename), + NULL, current_form)) == NULL) return_value = CURL_FORMADD_MEMORY; } else @@ -546,8 +569,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if (current_form->value) { if (current_form->flags & HTTPPOST_BUFFER) { if (filename) { - if (!(current_form = AddFormInfo(strdup(filename), - NULL, current_form))) + if ((current_form = AddFormInfo(strdup(filename), + NULL, current_form)) == NULL) return_value = CURL_FORMADD_MEMORY; } else @@ -598,9 +621,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if (current_form->contenttype) { if (current_form->flags & HTTPPOST_FILENAME) { if (contenttype) { - if (!(current_form = AddFormInfo(NULL, - strdup(contenttype), - current_form))) + if ((current_form = AddFormInfo(NULL, + strdup(contenttype), + current_form)) == NULL) return_value = CURL_FORMADD_MEMORY; } else @@ -626,15 +649,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, { /* this "cast increases required alignment of target type" but we consider it OK anyway */ - struct curl_slist* list = 0; - if ( array_state ) - { - memcpy(&list, &array_value, sizeof(struct curl_slist*)); - } - else - { - list = va_arg(params, struct curl_slist*); - } + 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; @@ -825,13 +842,13 @@ static CURLcode AddFormData(struct FormData **formp, *formp = newform; if (size) { - if(type == FORM_DATA) + if((type == FORM_DATA) || (type == FORM_CONTENT)) *size += length; else { /* Since this is a file to be uploaded here, add the size of the actual file */ if(!strequal("-", newform->line)) { - struct stat file; + struct_stat file; if(!stat(newform->line, &file)) { *size += file.st_size; } @@ -862,10 +879,11 @@ static CURLcode AddFormDataf(struct FormData **formp, * Curl_formclean() is used from http.c, this cleans a built FormData linked * list */ -void Curl_formclean(struct FormData *form) +void Curl_formclean(struct FormData **form_ptr) { - struct FormData *next; + struct FormData *next, *form; + form = *form_ptr; if(!form) return; @@ -874,7 +892,84 @@ void Curl_formclean(struct FormData *form) free(form->line); /* free the line */ free(form); /* free the struct */ - } while((form=next)); /* continue */ + } while ((form = next) != NULL); /* continue */ + + *form_ptr = NULL; +} + +#ifdef CURL_DOES_CONVERSIONS +/* + * Curl_formcovert() is used from http.c, this converts any + form items that need to be sent in the network encoding. + Returns CURLE_OK on success. + */ +CURLcode Curl_formconvert(struct SessionHandle *data, struct FormData *form) +{ + struct FormData *next; + CURLcode rc; + + if(!form) + return CURLE_OK; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + do { + next=form->next; /* the following form line */ + if (form->type == FORM_DATA) { + rc = Curl_convert_to_network(data, form->line, form->length); + /* Curl_convert_to_network calls failf if unsuccessful */ + if (rc != CURLE_OK) + return rc; + } + } while ((form = next) != NULL); /* continue */ + return CURLE_OK; +} +#endif /* CURL_DOES_CONVERSIONS */ + +/* + * curl_formget() + * Serialize a curl_httppost struct. + * Returns 0 on success. + */ +int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append) +{ + CURLcode rc; + curl_off_t size; + struct FormData *data, *ptr; + + rc = Curl_getFormData(&data, form, NULL, &size); + if (rc != CURLE_OK) + return (int)rc; + + for (ptr = data; ptr; ptr = ptr->next) { + if (ptr->type == FORM_FILE) { + char buffer[8192]; + size_t read; + struct Form temp; + + Curl_FormInit(&temp, ptr); + + do { + read = readfromfile(&temp, buffer, sizeof(buffer)); + if ((read == (size_t) -1) || (read != append(arg, buffer, read))) { + if (temp.fp) { + fclose(temp.fp); + } + Curl_formclean(&data); + return -1; + } + } while (read == sizeof(buffer)); + } else { + if (ptr->length != append(arg, ptr->line, ptr->length)) { + Curl_formclean(&data); + return -1; + } + } + } + Curl_formclean(&data); + return 0; } /* @@ -906,7 +1001,69 @@ void curl_formfree(struct curl_httppost *form) free(form->showfilename); /* free the faked file name */ free(form); /* free the struct */ - } while((form=next)); /* continue */ + } while ((form = next) != NULL); /* continue */ +} + +#ifndef HAVE_BASENAME +/* + (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + Edition) + + The basename() function shall take the pathname pointed to by path and + return a pointer to the final component of the pathname, deleting any + trailing '/' characters. + + If the string pointed to by path consists entirely of the '/' character, + basename() shall return a pointer to the string "/". If the string pointed + to by path is exactly "//", it is implementation-defined whether '/' or "//" + is returned. + + If path is a null pointer or points to an empty string, basename() shall + return a pointer to the string ".". + + The basename() function may modify the string pointed to by path, and may + return a pointer to static storage that may then be overwritten by a + subsequent call to basename(). + + The basename() function need not be reentrant. A function that is not + required to be reentrant is not required to be thread-safe. + +*/ +static char *basename(char *path) +{ + /* Ignore all the details above for now and make a quick and simple + implementaion here */ + char *s1; + char *s2; + + s1=strrchr(path, '/'); + s2=strrchr(path, '\\'); + + if(s1 && s2) { + path = (s1 > s2? s1 : s2)+1; + } + else if(s1) + path = s1 + 1; + else if(s2) + path = s2 + 1; + + return path; +} +#endif + +static char *strippath(char *fullfile) +{ + char *filename; + char *base; + filename = strdup(fullfile); /* duplicate since basename() may ruin the + buffer it works on */ + if(!filename) + return NULL; + base = strdup(basename(filename)); + + free(filename); /* free temporary buffer */ + + return base; /* returns an allocated string! */ } /* @@ -914,10 +1071,13 @@ void curl_formfree(struct curl_httppost *form) * (possibly huge) multipart formdata. The input list is in 'post', while the * output resulting linked lists gets stored in '*finalform'. *sizep will get * the total size of the whole POST. + * A multipart/form_data content-type is built, unless a custom content-type + * is passed in 'custom_content_type'. */ CURLcode Curl_getFormData(struct FormData **finalform, struct curl_httppost *post, + const char *custom_content_type, curl_off_t *sizep) { struct FormData *form = NULL; @@ -941,9 +1101,11 @@ CURLcode Curl_getFormData(struct FormData **finalform, /* Make the first line of the output */ result = AddFormDataf(&form, NULL, - "Content-Type: multipart/form-data;" - " boundary=%s\r\n", + "%s; boundary=%s\r\n", + custom_content_type?custom_content_type: + "Content-Type: multipart/form-data", boundary); + if (result) { free(boundary); return result; @@ -966,6 +1128,10 @@ CURLcode Curl_getFormData(struct FormData **finalform, 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) @@ -1004,22 +1170,33 @@ CURLcode Curl_getFormData(struct FormData **finalform, 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: - file->contents)); + filebasename)); + if (filebasename) + free(filebasename); if (result) break; } else if((post->flags & HTTPPOST_FILENAME) || (post->flags & HTTPPOST_BUFFER)) { + char *filebasename= + (!post->showfilename)?strippath(post->contents):NULL; + result = AddFormDataf(&form, &size, "; filename=\"%s\"", (post->showfilename?post->showfilename: - post->contents)); + filebasename)); + if (filebasename) + free(filebasename); + if (result) break; } @@ -1042,7 +1219,7 @@ CURLcode Curl_getFormData(struct FormData **finalform, curList = curList->next; } if (result) { - Curl_formclean(firstform); + Curl_formclean(&firstform); free(boundary); return result; } @@ -1057,7 +1234,10 @@ CURLcode Curl_getFormData(struct FormData **finalform, if(file->contenttype && !checkprefix("text/", file->contenttype)) { /* this is not a text content, mention our binary encoding */ - size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0); + result = AddFormDataf(&form, &size, + "\r\nContent-Transfer-Encoding: binary"); + if (result) + break; } #endif @@ -1094,22 +1274,27 @@ CURLcode Curl_getFormData(struct FormData **finalform, */ size_t nread; char buffer[512]; - while((nread = fread(buffer, 1, sizeof(buffer), fileread))) { - result = AddFormData(&form, FORM_DATA, buffer, nread, &size); + 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); + Curl_formclean(&firstform); free(boundary); return result; } } else { - Curl_formclean(firstform); +#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; @@ -1118,7 +1303,7 @@ CURLcode Curl_getFormData(struct FormData **finalform, } else if (post->flags & HTTPPOST_BUFFER) { /* include contents of buffer */ - result = AddFormData(&form, FORM_DATA, post->buffer, + result = AddFormData(&form, FORM_CONTENT, post->buffer, post->bufferlength, &size); if (result) break; @@ -1126,14 +1311,14 @@ CURLcode Curl_getFormData(struct FormData **finalform, else { /* include the contents we got */ - result = AddFormData(&form, FORM_DATA, post->contents, + result = AddFormData(&form, FORM_CONTENT, post->contents, post->contentslength, &size); if (result) break; } - } while((file = file->more)); /* for each specified file for this field */ + } while ((file = file->more) != NULL); /* for each specified file for this field */ if (result) { - Curl_formclean(firstform); + Curl_formclean(&firstform); free(boundary); return result; } @@ -1149,9 +1334,9 @@ CURLcode Curl_getFormData(struct FormData **finalform, break; } - } while((post=post->next)); /* for each field */ + } while ((post = post->next) != NULL); /* for each field */ if (result) { - Curl_formclean(firstform); + Curl_formclean(&firstform); free(boundary); return result; } @@ -1161,7 +1346,7 @@ CURLcode Curl_getFormData(struct FormData **finalform, "\r\n--%s--\r\n", boundary); if (result) { - Curl_formclean(firstform); + Curl_formclean(&firstform); free(boundary); return result; } @@ -1198,12 +1383,12 @@ static size_t readfromfile(struct Form *form, char *buffer, size_t size) /* this file hasn't yet been opened */ form->fp = fopen(form->data->line, "rb"); /* b is for binary */ if(!form->fp) - return -1; /* failure */ + return (size_t)-1; /* failure */ } nread = fread(buffer, 1, size, form->fp); if(nread != size) { - /* this is the last chunk form the file, move on */ + /* this is the last chunk from the file, move on */ fclose(form->fp); form->fp = NULL; form->data = form->data->next; @@ -1232,9 +1417,13 @@ size_t Curl_FormReader(char *buffer, if(!form->data) return 0; /* nothing, error, empty */ - if(form->data->type == FORM_FILE) - return readfromfile(form, buffer, wantedsize); + if(form->data->type == FORM_FILE) { + gotsize = readfromfile(form, buffer, wantedsize); + if(gotsize) + /* If positive or -1, return. If zero, continue! */ + return gotsize; + } do { if( (form->data->length - form->sent ) > wantedsize - gotsize) { @@ -1256,7 +1445,7 @@ size_t Curl_FormReader(char *buffer, form->data = form->data->next; /* advance */ - } while(form->data && (form->data->type == FORM_DATA)); + } while(form->data && (form->data->type != FORM_FILE)); /* If we got an empty line and we have more data, we proceed to the next line immediately to avoid returning zero before we've reached the end. This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ @@ -1323,7 +1512,7 @@ int main() char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH"; char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE"; char value7[] = "inet_ntoa_r.h"; - char value8[] = "Makefile.b32.resp"; + char value8[] = "Makefile.b32"; char type2[] = "image/gif"; char type6[] = "text/plain"; char type7[] = "text/html"; @@ -1332,7 +1521,8 @@ int main() int value5length = strlen(value4); int value6length = strlen(value5); int errors = 0; - int size; + CURLcode rc; + size_t size; size_t nread; char buffer[4096]; struct curl_httppost *httppost=NULL; @@ -1408,7 +1598,14 @@ int main() CURLFORM_END)) ++errors; - form=Curl_getFormData(httppost, &size); + rc = Curl_getFormData(&form, httppost, NULL, &size); + if(rc != CURLE_OK) { + if(rc != CURLE_READ_ERROR) { + const char *errortext = curl_easy_strerror(rc); + fprintf(stdout, "\n==> Curl_getFormData error: %s\n", errortext); + } + return 0; + } Curl_FormInit(&formread, form); @@ -1416,7 +1613,7 @@ int main() nread = Curl_FormReader(buffer, 1, sizeof(buffer), (FILE *)&formread); - if(-1 == nread) + if(nread < 1) break; fwrite(buffer, nread, 1, stdout); } while(1); @@ -1430,7 +1627,7 @@ int main() return 0; } -#endif +#endif /* _FORM_DEBUG */ #else /* CURL_DISABLE_HTTP */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, @@ -1442,6 +1639,15 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost, return CURL_FORMADD_DISABLED; } +int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append) +{ + (void) form; + (void) arg; + (void) append; + return CURL_FORMADD_DISABLED; +} + void curl_formfree(struct curl_httppost *form) { (void)form; @@ -1450,6 +1656,8 @@ void curl_formfree(struct curl_httppost *form) #endif /* CURL_DISABLE_HTTP */ +#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) + /* * Curl_FormBoundary() creates a suitable boundary string and returns an * allocated one. This is also used by SSL-code so it must be present even @@ -1458,18 +1666,18 @@ void curl_formfree(struct curl_httppost *form) char *Curl_FormBoundary(void) { char *retstring; - static int randomizer=0; /* this is just so that two boundaries within + static int randomizer; /* this is just so that two boundaries within the same form won't be identical */ size_t i; - static char table16[]="abcdef0123456789"; + static const char table16[]="abcdef0123456789"; retstring = (char *)malloc(BOUNDARY_LENGTH+1); if(!retstring) return NULL; /* failed */ - srand((unsigned int)((time(NULL)+randomizer++))); /* seed */ + srand((unsigned int)time(NULL)+randomizer++); /* seed */ strcpy(retstring, "----------------------------"); @@ -1482,3 +1690,5 @@ char *Curl_FormBoundary(void) return retstring; } + +#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ diff --git a/Utilities/cmcurl/formdata.h b/Utilities/cmcurl/formdata.h index c6a78cd..4ca0f3c 100644 --- a/Utilities/cmcurl/formdata.h +++ b/Utilities/cmcurl/formdata.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,8 +25,10 @@ ***************************************************************************/ enum formtype { - FORM_DATA, /* regular data */ - FORM_FILE /* 'line' points to a file name we should read from */ + FORM_DATA, /* form metadata (convert to network encoding if necessary) */ + FORM_CONTENT, /* form content (never convert) */ + FORM_FILE /* 'line' points to a file name we should read from + to create the form data (never convert) */ }; /* plain and simple linked list with lines to send */ @@ -69,6 +71,7 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata ); CURLcode Curl_getFormData(struct FormData **, struct curl_httppost *post, + const char *custom_contenttype, curl_off_t *size); /* fread() emulation */ @@ -86,7 +89,9 @@ char *Curl_formpostheader(void *formp, size_t *len); char *Curl_FormBoundary(void); -void Curl_formclean(struct FormData *); +void Curl_formclean(struct FormData **); + +CURLcode Curl_formconvert(struct SessionHandle *, struct FormData *); #endif diff --git a/Utilities/cmcurl/ftp.c b/Utilities/cmcurl/ftp.c index 2501412..378cd7f 100644 --- a/Utilities/cmcurl/ftp.c +++ b/Utilities/cmcurl/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,18 +29,14 @@ #include <stdlib.h> #include <stdarg.h> #include <ctype.h> -#include <errno.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 -#else /* some kind of unix */ +#else /* probably some kind of unix */ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif @@ -51,7 +47,9 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif +#ifdef HAVE_UTSNAME_H #include <sys/utsname.h> +#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -61,10 +59,6 @@ #endif #endif -#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__) -#include <errno.h> -#endif - #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) #undef in_addr_t #define in_addr_t unsigned long @@ -73,6 +67,7 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" +#include "easyif.h" /* for Curl_convert_... prototypes */ #include "if2ip.h" #include "hostip.h" @@ -83,17 +78,20 @@ #include "ftp.h" #ifdef HAVE_KRB4 -#include "security.h" #include "krb4.h" #endif #include "strtoofft.h" #include "strequal.h" -#include "ssluse.h" +#include "sslgen.h" #include "connect.h" #include "strerror.h" -#include "curl_memory.h" +#include "memory.h" #include "inet_ntop.h" +#include "select.h" +#include "parsedate.h" /* for the week day and month names */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "multiif.h" #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) #include "inet_ntoa_r.h" @@ -116,30 +114,43 @@ /* Local API functions */ static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote); -static CURLcode ftp_cwd(struct connectdata *conn, char *path); -static CURLcode ftp_mkd(struct connectdata *conn, char *path); -static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path); static CURLcode ftp_quit(struct connectdata *conn); -static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn); -static CURLcode ftp_3rdparty_transfer(struct connectdata *conn); -static CURLcode ftp_regular_transfer(struct connectdata *conn); -static CURLcode ftp_3rdparty(struct connectdata *conn); +static CURLcode ftp_parse_url_path(struct connectdata *conn); +static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done); +static void ftp_pasv_verbose(struct connectdata *conn, + Curl_addrinfo *ai, + char *newhost, /* ascii version */ + int port); +static CURLcode ftp_state_post_rest(struct connectdata *conn); +static CURLcode ftp_state_post_cwd(struct connectdata *conn); +static CURLcode ftp_state_quote(struct connectdata *conn, + bool init, ftpstate instate); +static CURLcode ftp_nb_type(struct connectdata *conn, + bool ascii, ftpstate state); +static int ftp_need_type(struct connectdata *conn, + bool ascii); /* easy-to-use macro: */ -#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result +#define FTPSENDF(x,y,z) if ((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \ + return result +#define NBFTPSENDF(x,y,z) if ((result = Curl_nbftpsendf(x,y,z)) != CURLE_OK) \ + return result -static void freedirs(struct FTP *ftp) +static void freedirs(struct connectdata *conn) { + struct ftp_conn *ftpc = &conn->proto.ftpc; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + int i; - if(ftp->dirs) { - for (i=0; i < ftp->dirdepth; i++){ - if(ftp->dirs[i]) { - free(ftp->dirs[i]); - ftp->dirs[i]=NULL; + if(ftpc->dirs) { + for (i=0; i < ftpc->dirdepth; i++){ + if(ftpc->dirs[i]) { + free(ftpc->dirs[i]); + ftpc->dirs[i]=NULL; } } - free(ftp->dirs); - ftp->dirs = NULL; + free(ftpc->dirs); + ftpc->dirs = NULL; } if(ftp->file) { free(ftp->file); @@ -147,6 +158,17 @@ static void freedirs(struct FTP *ftp) } } +/* Returns non-zero if the given string contains CR (\r) or LF (\n), + which are not allowed within RFC 959 <string>. + Note: The input string is in the client's encoding which might + not be ASCII, so escape sequences \r & \n must be used instead + of hex values 0x0d & 0x0a. +*/ +static bool isBadFtpString(const char *string) +{ + return (bool)((NULL != strchr(string, '\r')) || (NULL != strchr(string, '\n'))); +} + /*********************************************************************** * * AllowServerConnect() @@ -158,8 +180,7 @@ static void freedirs(struct FTP *ftp) */ static CURLcode AllowServerConnect(struct connectdata *conn) { - fd_set rdset; - struct timeval dt; + int timeout_ms; struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[SECONDARYSOCKET]; struct timeval now = Curl_tvnow(); @@ -167,10 +188,6 @@ static CURLcode AllowServerConnect(struct connectdata *conn) long timeout = data->set.connecttimeout?data->set.connecttimeout: (data->set.timeout?data->set.timeout: 0); - FD_ZERO(&rdset); - - FD_SET(sock, &rdset); - if(timeout) { timeout -= timespent; if(timeout<=0) { @@ -179,11 +196,11 @@ static CURLcode AllowServerConnect(struct connectdata *conn) } } - /* we give the server 60 seconds to connect to us, or a custom timeout */ - dt.tv_sec = (int)(timeout?timeout:60); - dt.tv_usec = 0; + /* We allow the server 60 seconds to connect to us, or a custom timeout. + Note the typecast here. */ + timeout_ms = (timeout?(int)timeout:60) * 1000; - switch (select(sock+1, &rdset, NULL, NULL, &dt)) { + switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) { case -1: /* error */ /* let's die here */ failf(data, "Error while waiting for server connect"); @@ -195,17 +212,19 @@ static CURLcode AllowServerConnect(struct connectdata *conn) default: /* we have received data here */ { - curl_socket_t s; -#ifdef __hpux - int size = sizeof(struct sockaddr_in); + curl_socket_t s = CURL_SOCKET_BAD; +#ifdef ENABLE_IPV6 + struct Curl_sockaddr_storage add; #else - socklen_t size = sizeof(struct sockaddr_in); -#endif struct sockaddr_in add; +#endif + socklen_t size = (socklen_t) sizeof(add); - getsockname(sock, (struct sockaddr *) &add, &size); - s=accept(sock, (struct sockaddr *) &add, &size); + if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { + size = sizeof(add); + s=accept(sock, (struct sockaddr *) &add, &size); + } sclose(sock); /* close the first socket */ if (CURL_SOCKET_BAD == s) { @@ -224,6 +243,182 @@ static CURLcode AllowServerConnect(struct connectdata *conn) return CURLE_OK; } +/* initialize stuff to prepare for reading a fresh new response */ +static void ftp_respinit(struct connectdata *conn) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + ftpc->nread_resp = 0; + ftpc->linestart_resp = conn->data->state.buffer; +} + +/* macro to check for the last line in an FTP server response */ +#define lastline(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \ + ISDIGIT(line[2]) && (' ' == line[3])) + +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 */ +{ + int perline; /* count bytes per line */ + bool keepon=TRUE; + ssize_t gotbytes; + char *ptr; + struct SessionHandle *data = conn->data; + char *buf = data->state.buffer; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; + int code = 0; + + if (ftpcode) + *ftpcode = 0; /* 0 for errors or not done */ + + ptr=buf + ftpc->nread_resp; + + perline= (int)(ptr-ftpc->linestart_resp); /* number of bytes in the current + line, so far */ + keepon=TRUE; + + while((ftpc->nread_resp<BUFSIZE) && (keepon && !result)) { + + if(ftpc->cache) { + /* we had data in the "cache", copy that instead of doing an actual + * read + * + * ftp->cache_size is cast to int here. This should be safe, + * because it would have been populated with something of size + * int to begin with, even though its datatype may be larger + * than an int. + */ + memcpy(ptr, ftpc->cache, (int)ftpc->cache_size); + gotbytes = (int)ftpc->cache_size; + free(ftpc->cache); /* free the cache */ + ftpc->cache = NULL; /* clear the pointer */ + ftpc->cache_size = 0; /* zero the size just in case */ + } + else { + int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp, + &gotbytes); + if(res < 0) + /* EWOULDBLOCK */ + return CURLE_OK; /* return */ + +#ifdef CURL_DOES_CONVERSIONS + if((res == CURLE_OK) && (gotbytes > 0)) { + /* convert from the network encoding */ + result = res = Curl_convert_from_network(data, ptr, gotbytes); + /* Curl_convert_from_network calls failf if unsuccessful */ + } +#endif /* CURL_DOES_CONVERSIONS */ + + if(CURLE_OK != res) + keepon = FALSE; + } + + if(!keepon) + ; + else if(gotbytes <= 0) { + keepon = FALSE; + result = CURLE_RECV_ERROR; + failf(data, "FTP response reading failed"); + } + else { + /* we got a whole chunk of data, which can be anything from one + * byte to a set of lines and possible just a piece of the last + * line */ + int i; + + conn->headerbytecount += gotbytes; + + ftpc->nread_resp += gotbytes; + for(i = 0; i < gotbytes; ptr++, i++) { + perline++; + if(*ptr=='\n') { + /* a newline is CRLF in ftp-talk, so the CR is ignored as + the line isn't really terminated until the LF comes */ + + /* output debug output if that is requested */ + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, + ftpc->linestart_resp, (size_t)perline, conn); + + /* + * We pass all response-lines to the callback function registered + * for "headers". The response lines can be seen as a kind of + * headers. + */ + result = Curl_client_write(conn, CLIENTWRITE_HEADER, + ftpc->linestart_resp, perline); + if(result) + return result; + + if(perline>3 && lastline(ftpc->linestart_resp)) { + /* This is the end of the last line, copy the last line to the + start of the buffer and zero terminate, for old times sake (and + krb4)! */ + char *meow; + int n; + for(meow=ftpc->linestart_resp, n=0; meow<ptr; meow++, n++) + buf[n] = *meow; + *meow=0; /* zero terminate */ + keepon=FALSE; + ftpc->linestart_resp = ptr+1; /* advance pointer */ + i++; /* skip this before getting out */ + + *size = ftpc->nread_resp; /* size of the response */ + ftpc->nread_resp = 0; /* restart */ + break; + } + perline=0; /* line starts over here */ + ftpc->linestart_resp = ptr+1; + } + } + if(!keepon && (i != gotbytes)) { + /* We found the end of the response lines, but we didn't parse the + full chunk of data we have read from the server. We therefore need + to store the rest of the data to be checked on the next invoke as + it may actually contain another end of response already! */ + ftpc->cache_size = gotbytes - i; + ftpc->cache = (char *)malloc((int)ftpc->cache_size); + if(ftpc->cache) + memcpy(ftpc->cache, ftpc->linestart_resp, (int)ftpc->cache_size); + else + return CURLE_OUT_OF_MEMORY; /**BANG**/ + } + } /* there was data */ + + } /* while there's buffer left and loop is requested */ + + if(!result) + code = atoi(buf); + +#ifdef HAVE_KRB4 + /* handle the security-oriented responses 6xx ***/ + /* FIXME: some errorchecking perhaps... ***/ + switch(code) { + case 631: + Curl_sec_read_msg(conn, buf, prot_safe); + break; + case 632: + Curl_sec_read_msg(conn, buf, prot_private); + break; + case 633: + Curl_sec_read_msg(conn, buf, prot_confidential); + break; + default: + /* normal ftp stuff we pass through! */ + break; + } +#endif + + *ftpcode=code; /* return the initial number like this */ + + + /* store the latest code for later retrieval */ + conn->data->info.httpcode=code; + + return result; +} /* --- parse FTP server responses --- */ @@ -237,9 +432,9 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ struct connectdata *conn, int *ftpcode) /* return the ftp-code */ { - /* Brand new implementation. - * We cannot read just one byte per read() and then go back to select() - * as it seems that the OpenSSL read() stuff doesn't grok that properly. + /* + * 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. */ @@ -250,32 +445,24 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ ssize_t gotbytes; char *ptr; long timeout; /* timeout in seconds */ - struct timeval interval; - fd_set rkeepfd; - fd_set readfd; + int interval_ms; struct SessionHandle *data = conn->data; char *line_start; int code=0; /* default ftp "error code" to return */ char *buf = data->state.buffer; CURLcode result = CURLE_OK; - struct FTP *ftp = conn->proto.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; struct timeval now = Curl_tvnow(); if (ftpcode) *ftpcode = 0; /* 0 for errors */ - FD_ZERO (&readfd); /* clear it */ - FD_SET (sockfd, &readfd); /* read socket */ - - /* get this in a backup variable to be able to restore it on each lap in the - select() loop */ - rkeepfd = readfd; - ptr=buf; line_start = buf; *nreadp=0; perline=0; + keepon=TRUE; while((*nreadp<BUFSIZE) && (keepon && !result)) { /* check and reset timeout value every lap */ @@ -294,7 +481,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ else /* Even without a requested timeout, we only wait response_time seconds for the full response to arrive before we bail out */ - timeout = ftp->response_time - + timeout = ftpc->response_time - Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */ if(timeout <=0 ) { @@ -302,15 +489,14 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ return CURLE_OPERATION_TIMEDOUT; /* already too little time */ } - if(!ftp->cache) { - readfd = rkeepfd; /* set every lap */ - interval.tv_sec = 1; /* use 1 second timeout intervals */ - interval.tv_usec = 0; + if(!ftpc->cache) { + interval_ms = 1 * 1000; /* use 1 second timeout intervals */ - switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) { + switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) { case -1: /* select() error, stop reading */ result = CURLE_RECV_ERROR; - failf(data, "FTP response aborted due to select() error: %d", errno); + failf(data, "FTP response aborted due to select() error: %d", + Curl_sockerrno()); break; case 0: /* timeout */ if(Curl_pgrsUpdate(conn)) @@ -327,7 +513,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ * to read, but when we use Curl_read() it may do so. Do confirm * that this is still ok and then remove this comment! */ - if(ftp->cache) { + if(ftpc->cache) { /* we had data in the "cache", copy that instead of doing an actual * read * @@ -337,11 +523,11 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ * int to begin with, even though its datatype may be larger * than an int. */ - memcpy(ptr, ftp->cache, (int)ftp->cache_size); - gotbytes = (int)ftp->cache_size; - free(ftp->cache); /* free the cache */ - ftp->cache = NULL; /* clear the pointer */ - ftp->cache_size = 0; /* zero the size just in case */ + memcpy(ptr, ftpc->cache, (int)ftpc->cache_size); + gotbytes = (int)ftpc->cache_size; + free(ftpc->cache); /* free the cache */ + ftpc->cache = NULL; /* clear the pointer */ + ftpc->cache_size = 0; /* zero the size just in case */ } else { int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes); @@ -349,6 +535,14 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ /* EWOULDBLOCK */ continue; /* go looping again */ +#ifdef CURL_DOES_CONVERSIONS + if((res == CURLE_OK) && (gotbytes > 0)) { + /* convert from the network encoding */ + result = res = Curl_convert_from_network(data, ptr, gotbytes); + /* Curl_convert_from_network calls failf if unsuccessful */ + } +#endif /* CURL_DOES_CONVERSIONS */ + if(CURLE_OK != res) keepon = FALSE; } @@ -377,21 +571,19 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ /* output debug output if that is requested */ if(data->set.verbose) - Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn->host.dispname); + Curl_debug(data, CURLINFO_HEADER_IN, + line_start, (size_t)perline, conn); /* * We pass all response-lines to the callback function registered * for "headers". The response lines can be seen as a kind of * headers. */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, + result = Curl_client_write(conn, CLIENTWRITE_HEADER, line_start, perline); if(result) return result; -#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \ - isdigit((int)line[2]) && (' ' == line[3])) - if(perline>3 && lastline(line_start)) { /* This is the end of the last line, copy the last * line to the start of the buffer and zero terminate, @@ -417,10 +609,10 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ invoke as it may actually contain another end of response already! Cleverly figured out by Eric Lavigne in December 2001. */ - ftp->cache_size = gotbytes - i; - ftp->cache = (char *)malloc((int)ftp->cache_size); - if(ftp->cache) - memcpy(ftp->cache, line_start, (int)ftp->cache_size); + ftpc->cache_size = gotbytes - i; + ftpc->cache = (char *)malloc((int)ftpc->cache_size); + if(ftpc->cache) + memcpy(ftpc->cache, line_start, (int)ftpc->cache_size); else return CURLE_OUT_OF_MEMORY; /**BANG**/ } @@ -459,655 +651,224 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ return result; } -static const char *ftpauth[]= { - "SSL", "TLS", NULL -}; - -/* - * Curl_ftp_connect() should do everything that is to be considered a part of - * the connection phase. - */ -CURLcode Curl_ftp_connect(struct connectdata *conn) +/* This is the ONLY way to change FTP state! */ +static void state(struct connectdata *conn, + ftpstate state) { - /* this is FTP and no proxy */ - ssize_t nread; - struct SessionHandle *data=conn->data; - char *buf = data->state.buffer; /* this is our buffer */ - struct FTP *ftp; - CURLcode result; - int ftpcode, try; - - ftp = (struct FTP *)malloc(sizeof(struct FTP)); - if(!ftp) - return CURLE_OUT_OF_MEMORY; - - memset(ftp, 0, sizeof(struct FTP)); - conn->proto.ftp = ftp; - - /* We always support persistant connections on ftp */ - conn->bits.close = FALSE; - - /* get some initial data into the ftp struct */ - ftp->bytecountp = &conn->bytecount; - - /* no need to duplicate them, this connectdata struct won't change */ - ftp->user = conn->user; - ftp->passwd = conn->passwd; - ftp->response_time = 3600; /* set default response time-out */ - -#ifndef CURL_DISABLE_HTTP - if (conn->bits.tunnel_proxy) { - /* We want "seamless" FTP operations through HTTP proxy tunnel */ - result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET, - conn->host.name, conn->remote_port); - if(CURLE_OK != result) - return result; - } -#endif /* CURL_DISABLE_HTTP */ - - if(conn->protocol & PROT_FTPS) { - /* FTPS is simply ftp with SSL for the control channel */ - /* now, perform the SSL initialization for this socket */ - result = Curl_SSLConnect(conn, FIRSTSOCKET); - if(result) - return result; - } - - /* The first thing we do is wait for the "220*" line: */ - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if(ftpcode != 220) { - failf(data, "This doesn't seem like a nice ftp-server response"); - return CURLE_FTP_WEIRD_SERVER_REPLY; - } - -#ifdef HAVE_KRB4 - /* if not anonymous login, try a secure login */ - if(data->set.krb4) { - - /* request data protection level (default is 'clear') */ - 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.krb4_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, try a FTPS connection now */ - - for (try = 0; ftpauth[try]; try++) { - - FTPSENDF(conn, "AUTH %s", ftpauth[try]); - - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - - if(result) - return result; - - /* 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)) { - result = Curl_SSLConnect(conn, FIRSTSOCKET); - if(result) - return result; - conn->protocol |= PROT_FTPS; - conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */ - break; - } - } - } - - /* send USER */ - FTPSENDF(conn, "USER %s", ftp->user?ftp->user:""); - - /* wait for feedback */ - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if(ftpcode == 530) { - /* 530 User ... access denied - (the server denies to log the specified user) */ - failf(data, "Access denied: %s", &buf[4]); - return CURLE_FTP_ACCESS_DENIED; - } - else if(ftpcode == 331) { - /* 331 Password required for ... - (the server requires to send the user's password too) */ - FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:""); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if(ftpcode == 530) { - /* 530 Login incorrect. - (the username and/or the password are incorrect) */ - failf(data, "the username and/or the password are incorrect"); - return CURLE_FTP_USER_PASSWORD_INCORRECT; - } - else if(ftpcode == 230) { - /* 230 User ... logged in. - (user successfully logged in) */ - - infof(data, "We have successfully logged in\n"); - } - else { - failf(data, "Odd return code after PASS"); - return CURLE_FTP_WEIRD_PASS_REPLY; - } - } - else if(buf[0] == '2') { - /* 230 User ... logged in. - (the user logged in without password) */ - infof(data, "We have successfully logged in\n"); - if (conn->ssl[FIRSTSOCKET].use) { -#ifdef HAVE_KRB4 - /* We are logged in with Kerberos, now set the requested protection - * level - */ - if(conn->sec_complete) - Curl_sec_set_protection_level(conn); - - /* We may need to issue a KAUTH here to have access to the files - * do it if user supplied a password - */ - if(conn->passwd && *conn->passwd) { - result = Curl_krb_kauth(conn); - if(result) - return result; - } +#ifdef CURLDEBUG + /* for debug purposes */ + const char *names[]={ + "STOP", + "WAIT220", + "AUTH", + "USER", + "PASS", + "ACCT", + "PBSZ", + "PROT", + "CCC", + "PWD", + "QUOTE", + "RETR_PREQUOTE", + "STOR_PREQUOTE", + "POSTQUOTE", + "CWD", + "MKD", + "MDTM", + "TYPE", + "LIST_TYPE", + "RETR_TYPE", + "STOR_TYPE", + "SIZE", + "RETR_SIZE", + "STOR_SIZE", + "REST", + "RETR_REST", + "PORT", + "PASV", + "LIST", + "RETR", + "STOR", + "QUIT" + }; #endif - } - } - else { - failf(data, "Odd return code after USER"); - return CURLE_FTP_WEIRD_USER_REPLY; - } - - if(conn->ssl[FIRSTSOCKET].use) { - /* PBSZ = PROTECTION BUFFER SIZE. - - The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: - - Specifically, the PROT command MUST be preceded by a PBSZ command - and a PBSZ command MUST be preceded by a successful security data - exchange (the TLS negotiation in this case) - - ... (and on page 8): - - Thus the PBSZ command must still be issued, but must have a parameter - of '0' to indicate that no buffering is taking place and the data - connection should not be encapsulated. - */ - FTPSENDF(conn, "PBSZ %d", 0); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - /* 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) { - FTPSENDF(conn, "PROT %c", 'P'); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if(ftpcode == 200) - /* We have enabled SSL for the data connection! */ - conn->ssl[SECONDARYSOCKET].use = TRUE; - - /* FTP servers typically responds with 500 if they decide to reject - our 'P' request */ - } - } - - /* send PWD to discover our entry point */ - FTPSENDF(conn, "PWD", NULL); - - /* wait for feedback */ - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if(ftpcode == 257) { - char *dir = (char *)malloc(nread+1); - char *store=dir; - char *ptr=&buf[4]; /* start on the first letter */ - - if(!dir) - return CURLE_OUT_OF_MEMORY; - - /* Reply format is like - 257<space>"<directory-name>"<space><commentary> and the RFC959 says - - The directory name can contain any character; embedded double-quotes - should be escaped by double-quotes (the "quote-doubling" convention). - */ - if('\"' == *ptr) { - /* it started good */ - ptr++; - while(ptr && *ptr) { - if('\"' == *ptr) { - if('\"' == ptr[1]) { - /* "quote-doubling" */ - *store = ptr[1]; - ptr++; - } - else { - /* end of path */ - *store = '\0'; /* zero terminate */ - break; /* get out of this loop */ - } - } - else - *store = *ptr; - store++; - ptr++; - } - ftp->entrypath =dir; /* remember this */ - infof(data, "Entry path is '%s'\n", ftp->entrypath); - } - else { - /* couldn't get the path */ - free(dir); - infof(data, "Failed to figure out path\n"); - } - - } - else { - /* We couldn't read the PWD response! */ - } - - return CURLE_OK; -} - -/*********************************************************************** - * - * Curl_ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status) -{ - struct SessionHandle *data = conn->data; - struct FTP *ftp = conn->proto.ftp; - ssize_t nread; - int ftpcode; - CURLcode result=CURLE_OK; - - bool was_ctl_valid = ftp->ctl_valid; - - /* free the dir tree and file parts */ - freedirs(ftp); - - ftp->ctl_valid = FALSE; - - if(data->set.upload) { - if((-1 != data->set.infilesize) && - (data->set.infilesize != *ftp->bytecountp) && - !data->set.crlf) { - failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T - " out of %" FORMAT_OFF_T " bytes)", - *ftp->bytecountp, data->set.infilesize); - conn->bits.close = TRUE; /* close this connection since we don't - know what state this error leaves us in */ - return CURLE_PARTIAL_FILE; - } - } - else { - if((-1 != conn->size) && (conn->size != *ftp->bytecountp) && - (conn->maxdownload != *ftp->bytecountp)) { - failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes", - *ftp->bytecountp); - conn->bits.close = TRUE; /* close this connection since we don't - know what state this error leaves us in */ - return CURLE_PARTIAL_FILE; - } - else if(!ftp->dont_check && - !*ftp->bytecountp && - (conn->size>0)) { - /* We consider this an error, but there's no true FTP error received - why we need to continue to "read out" the server response too. - We don't want to leave a "waiting" server reply if we'll get told - to make a second request on this same connection! */ - failf(data, "No data was received!"); - result = CURLE_FTP_COULDNT_RETR_FILE; - } - } - - switch(status) { - case CURLE_BAD_DOWNLOAD_RESUME: - case CURLE_FTP_WEIRD_PASV_REPLY: - case CURLE_FTP_PORT_FAILED: - case CURLE_FTP_COULDNT_SET_BINARY: - case CURLE_FTP_COULDNT_RETR_FILE: - case CURLE_FTP_ACCESS_DENIED: - /* the connection stays alive fine even though this happened */ - /* fall-through */ - case CURLE_OK: /* doesn't affect the control connection's status */ - ftp->ctl_valid = was_ctl_valid; - break; - default: /* by default, an error means the control connection is - wedged and should not be used anymore */ - ftp->ctl_valid = FALSE; - break; - } - -#ifdef HAVE_KRB4 - Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]); + struct ftp_conn *ftpc = &conn->proto.ftpc; +#ifdef CURLDEBUG + if(ftpc->state != state) + infof(conn->data, "FTP %p state change from %s to %s\n", + ftpc, names[ftpc->state], names[state]); #endif - /* shut down the socket to inform the server we're done */ - sclose(conn->sock[SECONDARYSOCKET]); - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; - - if(!ftp->no_transfer && !status) { - /* 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. - */ - ftp->response_time = 60; /* give it only a minute for now */ - - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - - ftp->response_time = 3600; /* set this back to one hour waits */ - - if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { - failf(data, "control connection looks dead"); - return result; - } - - if(result) - return result; - - if(!ftp->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); - return CURLE_FTP_WRITE_ERROR; - } - } - } - - /* clear these for next connection */ - ftp->no_transfer = FALSE; - ftp->dont_check = FALSE; - - if (!result && conn->sec_conn) { /* 3rd party transfer */ - /* "done" with the secondary connection */ - result = Curl_ftp_done(conn->sec_conn, status); - } - - /* Send any post-transfer QUOTE strings? */ - if(!status && !result && data->set.postquote) - result = ftp_sendquote(conn, data->set.postquote); - - return result; + ftpc->state = state; } -/*********************************************************************** - * - * ftp_sendquote() - * - * Where a 'quote' means a list of custom commands to send to the server. - * The quote list is passed as an argument. - */ - -static -CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) +static CURLcode ftp_state_user(struct connectdata *conn) { - struct curl_slist *item; - ssize_t nread; - int ftpcode; CURLcode result; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + /* send USER */ + NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:""); - item = quote; - while (item) { - if (item->data) { - FTPSENDF(conn, "%s", item->data); - - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if (result) - return result; - - if (ftpcode >= 400) { - failf(conn->data, "QUOT string not accepted: %s", item->data); - return CURLE_FTP_QUOTE_ERROR; - } - } - - item = item->next; - } + state(conn, FTP_USER); + conn->data->state.ftp_trying_alternative = FALSE; return CURLE_OK; } -/*********************************************************************** - * - * ftp_getfiletime() - * - * Get the timestamp of the given file. - */ -static -CURLcode ftp_getfiletime(struct connectdata *conn, char *file) +static CURLcode ftp_state_pwd(struct connectdata *conn) { CURLcode result; - int ftpcode; /* for ftp status */ - ssize_t nread; - char *buf = conn->data->state.buffer; - - /* we have requested to get the modified-time of the file, this is yet - again a grey area as the MDTM is not kosher RFC959 */ - FTPSENDF(conn, "MDTM %s", file); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; + /* send PWD to discover our entry point */ + NBFTPSENDF(conn, "PWD", NULL); + state(conn, FTP_PWD); - 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; - 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); - 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: */ - conn->data->info.filetime = curl_getdate(buf, &secs); - } - } - break; - default: - infof(conn->data, "unsupported MDTM reply format\n"); - break; - case 550: /* "No such file or directory" */ - failf(conn->data, "Given file does not exist"); - result = CURLE_FTP_COULDNT_RETR_FILE; - break; - } - return result; + return CURLE_OK; } -/*********************************************************************** - * - * ftp_transfertype() - * - * Set transfer type. We only deal with ASCII or BINARY so this function - * sets one of them. - */ -static CURLcode ftp_transfertype(struct connectdata *conn, - bool ascii) +/* For the FTP "protocol connect" and "doing" phases only */ +int Curl_ftp_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) { - struct SessionHandle *data = conn->data; - int ftpcode; - ssize_t nread; - CURLcode result; + struct ftp_conn *ftpc = &conn->proto.ftpc; - FTPSENDF(conn, "TYPE %s", ascii?"A":"I"); + if(!numsocks) + return GETSOCK_BLANK; - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; + socks[0] = conn->sock[FIRSTSOCKET]; - if(ftpcode != 200) { - failf(data, "Couldn't set %s mode", - ascii?"ASCII":"binary"); - return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY; + if(ftpc->sendleft) { + /* write mode */ + return GETSOCK_WRITESOCK(0); } - return CURLE_OK; + /* read mode */ + return GETSOCK_READSOCK(0); } -/*********************************************************************** - * - * ftp_getsize() - * - * Returns the file size (in bytes) of the given remote file. - */ +/* This is called after the FTP_QUOTE state is passed. -static -CURLcode ftp_getsize(struct connectdata *conn, char *file, - curl_off_t *size) + ftp_state_cwd() sends the range of PWD commands to the server to change to + the correct directory. It may also need to send MKD commands to create + missing ones, if that option is enabled. +*/ +static CURLcode ftp_state_cwd(struct connectdata *conn) { - struct SessionHandle *data = conn->data; - int ftpcode; - ssize_t nread; - char *buf=data->state.buffer; - CURLcode result; - - FTPSENDF(conn, "SIZE %s", file); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; - if(ftpcode == 213) { - /* get the size from the ascii string: */ - *size = curlx_strtoofft(buf+4, NULL, 0); + if(ftpc->cwddone) + /* already done and fine */ + result = ftp_state_post_cwd(conn); + else { + ftpc->count2 = 0; + if (conn->bits.reuse && ftpc->entrypath) { + /* This is a re-used connection. Since we change directory to where the + transfer is taking place, we must first get back to the original dir + where we ended up after login: */ + ftpc->count1 = 0; /* we count this as the first path, then we add one + for all upcoming ones in the ftp->dirs[] array */ + NBFTPSENDF(conn, "CWD %s", ftpc->entrypath); + state(conn, FTP_CWD); + } + else { + if(ftpc->dirdepth) { + ftpc->count1 = 1; + /* issue the first CWD, the rest is sent when the CWD responses are + received... */ + NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 -1]); + state(conn, FTP_CWD); + } + else { + /* No CWD necessary */ + result = ftp_state_post_cwd(conn); + } + } } - else - return CURLE_FTP_COULDNT_GET_SIZE; - - return CURLE_OK; + return result; } -/*************************************************************************** - * - * ftp_pasv_verbose() - * - * This function only outputs some informationals about this second connection - * when we've issued a PASV command before and thus we have connected to a - * possibly new IP address. - * - */ -static void -ftp_pasv_verbose(struct connectdata *conn, - Curl_addrinfo *ai, - char *newhost, /* ascii version */ - int port) -{ - char buf[256]; - Curl_printable_address(ai, buf, sizeof(buf)); - infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port); -} +typedef enum { + EPRT, + PORT, + DONE +} ftpport; -/*********************************************************************** - * - * ftp_use_port() - * - * Send the proper PORT command. PORT is the ftp client's way of telling the - * server that *WE* open a port that we listen on an awaits the server to - * connect to. This is the opposite of PASV. - */ +static CURLcode ftp_state_use_port(struct connectdata *conn, + ftpport fcmd) /* start with this */ -static -CURLcode ftp_use_port(struct connectdata *conn) { + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; struct SessionHandle *data=conn->data; - curl_socket_t portsock; - ssize_t nread; - int ftpcode; /* receive FTP response codes in this */ - CURLcode result; + curl_socket_t portsock= CURL_SOCKET_BAD; + char myhost[256] = ""; #ifdef ENABLE_IPV6 /****************************************************************** - * - * Here's a piece of IPv6-specific code coming up - * + * IPv6-specific section */ - - struct addrinfo hints, *res, *ai; - struct sockaddr_storage ss; + struct Curl_sockaddr_storage ss; + struct addrinfo *res, *ai; socklen_t sslen; char hbuf[NI_MAXHOST]; - struct sockaddr *sa=(struct sockaddr *)&ss; - unsigned char *ap; - unsigned char *pp; - char portmsgbuf[1024], tmp[1024]; - - const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; - char **modep; + char tmp[1024]; + const char *mode[] = { "EPRT", "PORT", NULL }; int rc; int error; + char *host=NULL; + struct Curl_dns_entry *h=NULL; + unsigned short port = 0; - /* - * we should use Curl_if2ip? given pickiness of recent ftpd, - * I believe we should use the same address as the control connection. - */ - sslen = sizeof(ss); - rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen); - if(rc < 0) { - failf(data, "getsockname() returned %d\n", rc); - return CURLE_FTP_PORT_FAILED; - } + /* Step 1, figure out what address that is requested */ - rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, - NIFLAGS); - if(rc) { - failf(data, "getnameinfo() returned %d\n", rc); - return CURLE_FTP_PORT_FAILED; - } + if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) { + /* attempt to get the address of the given interface name */ + if(!Curl_if2ip(data->set.ftpport, hbuf, sizeof(hbuf))) + /* not an interface, use the given string as host name instead */ + host = data->set.ftpport; + else + host = hbuf; /* use the hbuf for host name */ + } /* data->set.ftpport */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = sa->sa_family; - /*hints.ai_family = ss.ss_family; - this way can be used if sockaddr_storage is properly defined, as glibc - 2.1.X doesn't do*/ - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; + if(!host) { + /* not an interface and not a host name, get default by extracting + the IP from the control connection */ - rc = getaddrinfo(hbuf, NULL, &hints, &res); - if(rc) { - failf(data, "getaddrinfo() returned %d\n", rc); - return CURLE_FTP_PORT_FAILED; + sslen = sizeof(ss); + if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(conn, Curl_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 \n", 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) { @@ -1115,175 +876,164 @@ CURLcode ftp_use_port(struct connectdata *conn) * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype): */ if (ai->ai_socktype == 0) - ai->ai_socktype = hints.ai_socktype; + ai->ai_socktype = conn->socktype; portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (portsock == CURL_SOCKET_BAD) { - error = Curl_ourerrno(); + error = Curl_sockerrno(); continue; } + break; + } + if(!ai) { + failf(data, "socket failure: %s", Curl_strerror(conn, error)); + return CURLE_FTP_PORT_FAILED; + } - if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { - error = Curl_ourerrno(); + /* 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, Curl_sockerrno()) ); sclose(portsock); - portsock = CURL_SOCKET_BAD; - continue; + return CURLE_FTP_PORT_FAILED; } - if (listen(portsock, 1) < 0) { - error = Curl_ourerrno(); + /* 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, Curl_sockerrno())); sclose(portsock); - portsock = CURL_SOCKET_BAD; - continue; + return CURLE_FTP_PORT_FAILED; } - - break; } - freeaddrinfo(res); - if (portsock == CURL_SOCKET_BAD) { - failf(data, "%s", Curl_strerror(conn,error)); + + /* 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, Curl_sockerrno()) ); + sclose(portsock); return CURLE_FTP_PORT_FAILED; } - sslen = sizeof(ss); - if (getsockname(portsock, sa, &sslen) < 0) { - failf(data, "%s", Curl_strerror(conn,Curl_ourerrno())); + /* step 4, listen on the socket */ + + if (listen(portsock, 1)) { + failf(data, "socket failure: %s", Curl_strerror(conn, Curl_sockerrno())); + sclose(portsock); return CURLE_FTP_PORT_FAILED; } - for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]); - modep && *modep; modep++) { - int lprtaf, eprtaf; - int alen=0, plen=0; + /* 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: - ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; - alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); - pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; - plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); - lprtaf = 4; - eprtaf = 1; + port = ntohs(((struct sockaddr_in *)sa)->sin_port); break; case AF_INET6: - ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; - alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); - pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; - plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); - lprtaf = 6; - eprtaf = 2; + port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); break; default: - ap = pp = NULL; - lprtaf = eprtaf = -1; break; } - if (strcmp(*modep, "EPRT") == 0) { - if (eprtaf < 0) - continue; - if (getnameinfo((struct sockaddr *)&ss, sslen, - portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), - NIFLAGS)) - continue; - - /* do not transmit IPv6 scope identifier to the wire */ - if (sa->sa_family == AF_INET6) { - char *q = strchr(portmsgbuf, '%'); - if (q) - *q = '\0'; - } + 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_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf, - portmsgbuf, tmp); + 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 (strcmp(*modep, "LPRT") == 0 || - strcmp(*modep, "PORT") == 0) { - int i; + else if (PORT == fcmd) { + char *source = myhost; + char *dest = tmp; - if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) - continue; - if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET) + if ((PORT == fcmd) && ai->ai_family != AF_INET) continue; - portmsgbuf[0] = '\0'; - if (strcmp(*modep, "LPRT") == 0) { - snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } - } - - for (i = 0; i < alen; i++) { - if (portmsgbuf[0]) - snprintf(tmp, sizeof(tmp), ",%u", ap[i]); + /* translate x.x.x.x to x,x,x,x */ + while(source && *source) { + if(*source == '.') + *dest=','; else - snprintf(tmp, sizeof(tmp), "%u", ap[i]); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } - } - - if (strcmp(*modep, "LPRT") == 0) { - snprintf(tmp, sizeof(tmp), ",%d", plen); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) - continue; + *dest = *source; + dest++; + source++; } + *dest = 0; + snprintf(dest, 20, ",%d,%d", port>>8, port&0xff); - for (i = 0; i < plen; i++) { - snprintf(tmp, sizeof(tmp), ",%u", pp[i]); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } - } - - result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf); + result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp); if(result) return result; - } - - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; - - if (ftpcode != 200) { - continue; - } - else break; + } } - if (!*modep) { - sclose(portsock); - failf(data, "PORT command attempts failed"); - return CURLE_FTP_PORT_FAILED; - } - /* 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 */ + /* 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 /****************************************************************** - * - * Here's a piece of IPv4-specific code coming up - * + * IPv4-specific section */ struct sockaddr_in sa; unsigned short porttouse; - char myhost[256] = ""; bool sa_filled_in = FALSE; Curl_addrinfo *addr = NULL; unsigned short ip[4]; + bool freeaddr = TRUE; + socklen_t sslen = sizeof(sa); + (void)fcmd; /* not used in the IPv4 code */ if(data->set.ftpport) { in_addr_t in; @@ -1302,16 +1052,22 @@ CURLcode ftp_use_port(struct connectdata *conn) else if(strlen(data->set.ftpport)> 1) { /* might be a host name! */ struct Curl_dns_entry *h=NULL; - int rc = Curl_resolv(conn, myhost, 0, &h); + int rc = Curl_resolv(conn, data->set.ftpport, 0, &h); if(rc == CURLRESOLV_PENDING) + /* BLOCKING */ rc = Curl_wait_for_resolv(conn, &h); - (void)rc; if(h) { addr = h->addr; /* when we return from this function, we can forget about this entry - to we can unlock it now already */ + 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", data->set.ftpport); + } } /* strlen */ } /* CURL_INADDR_NONE */ } /* data->set.ftpport */ @@ -1319,18 +1075,14 @@ CURLcode ftp_use_port(struct connectdata *conn) if(!addr) { /* pick a suitable default here */ -#ifdef __hpux - int sslen; -#else - socklen_t sslen; -#endif - - sslen = sizeof(sa); if (getsockname(conn->sock[FIRSTSOCKET], - (struct sockaddr *)&sa, &sslen) < 0) { - failf(data, "getsockname() failed"); + (struct sockaddr *)&sa, &sslen)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(conn, Curl_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 */ } @@ -1338,33 +1090,31 @@ CURLcode ftp_use_port(struct connectdata *conn) if (addr || sa_filled_in) { portsock = socket(AF_INET, SOCK_STREAM, 0); if(CURL_SOCKET_BAD != portsock) { - socklen_t size; /* 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, sizeof(sa)); + memcpy(&sa, addr->ai_addr, sslen); sa.sin_addr.s_addr = INADDR_ANY; } sa.sin_port = 0; - size = sizeof(sa); + sslen = sizeof(sa); - if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) { + if(bind(portsock, (struct sockaddr *)&sa, sslen) == 0) { /* we succeeded to bind */ struct sockaddr_in add; -#ifdef __hpux - int socksize = sizeof(add); -#else socklen_t socksize = sizeof(add); -#endif if(getsockname(portsock, (struct sockaddr *) &add, - &socksize)<0) { - failf(data, "getsockname() failed"); + &socksize)) { + failf(data, "getsockname() failed: %s", + Curl_strerror(conn, Curl_sockerrno()) ); return CURLE_FTP_PORT_FAILED; } porttouse = ntohs(add.sin_port); @@ -1385,7 +1135,7 @@ CURLcode ftp_use_port(struct connectdata *conn) } } else { - failf(data, "could't find IP address to use"); + failf(data, "couldn't find IP address to use"); return CURLE_FTP_PORT_FAILED; } @@ -1401,54 +1151,39 @@ CURLcode ftp_use_port(struct connectdata *conn) infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], porttouse); - result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d", - ip[0], ip[1], ip[2], ip[3], - porttouse >> 8, - porttouse & 255); + 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; - Curl_freeaddrinfo(addr); + if(freeaddr) + Curl_freeaddrinfo(addr); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; + ftpc->count1 = PORT; - if(ftpcode != 200) { - failf(data, "Server does not grok PORT, try without it!"); - return CURLE_FTP_PORT_FAILED; - } #endif /* end of ipv4-specific code */ - return CURLE_OK; -} + /* 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(). -/*********************************************************************** - * - * ftp_use_pasv() - * - * Send the PASV command. PASV is the ftp client's way of asking the server to - * open a second port that we can connect to (for the data transfer). This is - * the opposite of PORT. - */ + 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; -static -CURLcode ftp_use_pasv(struct connectdata *conn, - bool *connected) -{ - struct SessionHandle *data = conn->data; - ssize_t nread; - char *buf = data->state.buffer; /* this is our buffer */ - int ftpcode; /* receive FTP response codes in this */ - CURLcode result; - struct Curl_dns_entry *addr=NULL; - Curl_addrinfo *conninfo; - int rc; + state(conn, FTP_PORT); + return result; +} +static CURLcode ftp_state_use_pasv(struct connectdata *conn) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = CURLE_OK; /* Here's the excecutive summary on what to do: @@ -1464,68 +1199,371 @@ CURLcode ftp_use_pasv(struct connectdata *conn, */ const char *mode[] = { "EPSV", "PASV", NULL }; - int results[] = { 229, 227, 0 }; int modeoff; - unsigned short connectport; /* the local port connect() should use! */ - unsigned short newport=0; /* remote port, not necessary the local one */ - /* newhost must be able to hold a full IP-style address in ASCII, which - in the IPv6 case means 5*8-1 = 39 letters */ - char newhost[48]; - char *newhostp=NULL; +#ifdef PF_INET6 + if(!conn->bits.ftp_use_epsv && conn->bits.ipv6) + /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the + request and enable EPSV again! */ + conn->bits.ftp_use_epsv = TRUE; +#endif - for (modeoff = (data->set.ftp_use_epsv?0:1); - mode[modeoff]; modeoff++) { - result = Curl_ftpsendf(conn, "%s", mode[modeoff]); - if(result) - return result; - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) + modeoff = conn->bits.ftp_use_epsv?0:1; + + result = Curl_nbftpsendf(conn, "%s", mode[modeoff]); + if(result) + return result; + + ftpc->count1 = modeoff; + state(conn, FTP_PASV); + infof(conn->data, "Connect data stream passively\n"); + + return result; +} + +/* REST is the last command in the chain of commands when a "head"-like + request is made. Thus, if an actual transfer is to be made this is where + we take off for real. */ +static CURLcode ftp_state_post_rest(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + struct SessionHandle *data = conn->data; + + if(ftp->no_transfer || conn->bits.no_body) { + /* doesn't transfer any data */ + ftp->no_transfer = TRUE; + + /* still possibly do PRE QUOTE jobs */ + state(conn, FTP_RETR_PREQUOTE); + result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); + } + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT (or similar) command */ + result = ftp_state_use_port(conn, EPRT); + } + else { + /* We have chosen (this is default) to use the PASV (or similar) command */ + result = ftp_state_use_pasv(conn); + } + return result; +} + +static CURLcode ftp_state_post_size(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + + if(ftp->no_transfer) { + /* if a "head"-like request is being made */ + + /* Determine if server can respond to REST command and therefore + whether it supports range */ + NBFTPSENDF(conn, "REST %d", 0); + + state(conn, FTP_REST); + } + else + result = ftp_state_post_rest(conn); + + return result; +} + +static CURLcode ftp_state_post_type(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + + if(ftp->no_transfer) { + /* if a "head"-like request is being made */ + + /* we know ftp->file is a valid pointer to a file name */ + NBFTPSENDF(conn, "SIZE %s", ftp->file); + + state(conn, FTP_SIZE); + } + else + result = ftp_state_post_size(conn); + + return result; +} + +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... */ + + NBFTPSENDF(conn, "%s", + data->set.customrequest?data->set.customrequest: + (data->set.ftp_list_only?"NLST":"LIST")); + + state(conn, FTP_LIST); + + return result; +} + +static CURLcode ftp_state_post_retrtype(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* We've sent the TYPE, now we must send the list of prequote strings */ + + result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); + + return result; +} + +static CURLcode ftp_state_post_stortype(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* We've sent the TYPE, now we must send the list of prequote strings */ + + result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE); + + return result; +} + +static CURLcode ftp_state_post_mdtm(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + struct SessionHandle *data = conn->data; + + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP can't be much more than the file size and + date. */ + if(conn->bits.no_body && data->set.include_header && ftp->file && + ftp_need_type(conn, data->set.prefer_ascii)) { + /* The SIZE command is _not_ RFC 959 specified, and therefor many servers + may not support it! It is however the only way we have to get a file's + size! */ + + ftp->no_transfer = TRUE; /* this means no actual transfer will be made */ + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE); + if (result) return result; - if (ftpcode == results[modeoff]) - break; } + else + result = ftp_state_post_type(conn); - if (!mode[modeoff]) { - failf(data, "Odd return code after PASV"); - return CURLE_FTP_WEIRD_PASV_REPLY; + return result; +} + +/* This is called after the CWD commands have been done in the beginning of + the DO phase */ +static CURLcode ftp_state_post_cwd(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + struct SessionHandle *data = conn->data; + + /* Requested time of file or time-depended transfer? */ + if((data->set.get_filetime || data->set.timecondition) && ftp->file) { + + /* we have requested to get the modified-time of the file, this is a white + spot as the MDTM is not mentioned in RFC959 */ + NBFTPSENDF(conn, "MDTM %s", ftp->file); + + state(conn, FTP_MDTM); } - else if (227 == results[modeoff]) { - int ip[4]; - int port[2]; - char *str=buf; + else + result = ftp_state_post_mdtm(conn); - /* - * New 227-parser June 3rd 1999. - * It now scans for a sequence of six comma-separated numbers and - * will take 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" - */ + return result; +} - 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++; + +/* This is called after the TYPE and possible quote commands have been sent */ +static CURLcode ftp_state_ul_setup(struct connectdata *conn, + bool sizechecked) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + struct SessionHandle *data = conn->data; + curl_off_t passed=0; + + if((data->reqdata.resume_from && !sizechecked) || + ((data->reqdata.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->reqdata.resume_from < 0 ) { + /* Got no given size to start from, figure it out */ + NBFTPSENDF(conn, "SIZE %s", ftp->file); + state(conn, FTP_STOR_SIZE); + return result; } - if(!*str) { - failf(data, "Couldn't interpret this 227-reply: %s", buf); - return CURLE_FTP_WEIRD_227_FORMAT; + /* enable append */ + data->set.ftp_append = TRUE; + + /* Let's read off the proper amount of bytes from the input. If we knew it + was a proper file we could've just fseek()ed but we only have a stream + here */ + + /* TODO: allow the ioctlfunction to provide a fast forward function that + can be used here and use this method only as a fallback! */ + do { + curl_off_t readthisamountnow = (data->reqdata.resume_from - passed); + curl_off_t actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = (curl_off_t) + conn->fread(data->state.buffer, 1, (size_t)readthisamountnow, + conn->fread_in); + + passed += actuallyread; + if(actuallyread != readthisamountnow) { + failf(data, "Could only read %" FORMAT_OFF_T + " bytes from the input", passed); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed != data->reqdata.resume_from); + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= data->reqdata.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 no_transfer so that we won't get any error in + * Curl_ftp_done() because we didn't transfer anything! */ + ftp->no_transfer = TRUE; + + state(conn, FTP_STOP); + return CURLE_OK; + } } + /* we've passed, proceed as normal */ + } /* resume_from */ - snprintf(newhost, sizeof(newhost), - "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); - newhostp = newhost; - newport = (port[0]<<8) + port[1]; + NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s", + ftp->file); + + state(conn, FTP_STOR); + + return result; +} + +static CURLcode ftp_state_quote(struct connectdata *conn, + bool init, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct FTP *ftp = data->reqdata.proto.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + bool quote=FALSE; + struct curl_slist *item; + + switch(instate) { + case FTP_QUOTE: + default: + item = data->set.quote; + break; + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + item = data->set.prequote; + break; + case FTP_POSTQUOTE: + item = data->set.postquote; + break; } - else if (229 == results[modeoff]) { - char *ptr = strchr(buf, '('); + + if(init) + ftpc->count1 = 0; + else + ftpc->count1++; + + if(item) { + int i = 0; + + /* Skip count1 items in the linked list */ + while((i< ftpc->count1) && item) { + item = item->next; + i++; + } + if(item) { + NBFTPSENDF(conn, "%s", item->data); + state(conn, instate); + quote = TRUE; + } + } + + if(!quote) { + /* No more quote to send, continue to ... */ + switch(instate) { + case FTP_QUOTE: + default: + result = ftp_state_cwd(conn); + break; + case FTP_RETR_PREQUOTE: + if (ftp->no_transfer) + state(conn, FTP_STOP); + else { + NBFTPSENDF(conn, "SIZE %s", ftp->file); + state(conn, FTP_RETR_SIZE); + } + break; + case FTP_STOR_PREQUOTE: + result = ftp_state_ul_setup(conn, FALSE); + break; + case FTP_POSTQUOTE: + break; + } + } + + return result; +} + +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]; @@ -1536,7 +1574,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn, &separator[2], &num, &separator[3])) { - char sep1 = separator[0]; + const char sep1 = separator[0]; int i; /* The four separators should be identical, or else this is an oddly @@ -1550,8 +1588,13 @@ CURLcode ftp_use_pasv(struct connectdata *conn, if(ptr) { newport = num; - /* we should use the same host we already are connected to */ - newhostp = conn->host.name; + if (conn->bits.tunnel_proxy) + /* 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 @@ -1562,10 +1605,71 @@ CURLcode ftp_use_pasv(struct connectdata *conn, return CURLE_FTP_WEIRD_PASV_REPLY; } } - else - return CURLE_FTP_CANT_RECONNECT; + else if((ftpc->count1 == 1) && + (ftpcode == 227)) { + /* positive PASV response */ + int ip[4]; + int port[2]; - if(data->change.proxy && *data->change.proxy) { + /* + * 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) + /* 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 = (port[0]<<8) + port[1]; + } + 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.proxy && *data->set.proxy) { /* * This is a tunnel through a http proxy and we need to connect to the * proxy again here. @@ -1575,6 +1679,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn, */ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr); if(rc == CURLRESOLV_PENDING) + /* BLOCKING */ rc = Curl_wait_for_resolv(conn, &addr); connectport = @@ -1583,12 +1688,13 @@ CURLcode ftp_use_pasv(struct connectdata *conn, } else { /* normal, direct, ftp connection */ - rc = Curl_resolv(conn, newhostp, newport, &addr); + rc = Curl_resolv(conn, newhost, newport, &addr); if(rc == CURLRESOLV_PENDING) + /* BLOCKING */ rc = Curl_wait_for_resolv(conn, &addr); if(!addr) { - failf(data, "Can't resolve new host %s:%d", newhostp, newport); + failf(data, "Can't resolve new host %s:%d", newhost, newport); return CURLE_FTP_CANT_GET_HOST; } connectport = newport; /* we connect to the remote port */ @@ -1598,13 +1704,27 @@ CURLcode ftp_use_pasv(struct connectdata *conn, addr, &conn->sock[SECONDARYSOCKET], &conninfo, - connected); + &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 @@ -1613,152 +1733,477 @@ CURLcode ftp_use_pasv(struct connectdata *conn, if(data->set.verbose) /* this just dumps information about this second connection */ - ftp_pasv_verbose(conn, conninfo, newhostp, connectport); + ftp_pasv_verbose(conn, conninfo, newhost, connectport); #ifndef CURL_DISABLE_HTTP - if(conn->bits.tunnel_proxy) { + 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 */ - result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET, - newhostp, newport); + + /* 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->reqdata.proto.ftp; + memset(&http_proxy, 0, sizeof(http_proxy)); + data->reqdata.proto.http = &http_proxy; + + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); + + data->reqdata.proto.ftp = ftp_save; + if(CURLE_OK != result) return result; } #endif /* CURL_DISABLE_HTTP */ - (void)rc; - return CURLE_OK; + state(conn, FTP_STOP); /* this phase is completed */ + + return result; } -/* - * Curl_ftp_nextconnect() - * - * This function shall be called when the second FTP connection has been - * established and is confirmed connected. - */ +static CURLcode ftp_state_port_resp(struct connectdata *conn, + int ftpcode) +{ + struct SessionHandle *data = conn->data; + struct ftp_conn *ftpc = &conn->proto.ftpc; + ftpport fcmd = (ftpport)ftpc->count1; + CURLcode result = CURLE_OK; -CURLcode Curl_ftp_nextconnect(struct connectdata *conn) + if(ftpcode != 200) { + /* the command failed */ + + if (EPRT == fcmd) { + infof(data, "disabling EPRT usage\n"); + conn->bits.ftp_use_eprt = FALSE; + } + fcmd++; + + if(fcmd == DONE) { + failf(data, "Failed to do PORT"); + result = CURLE_FTP_PORT_FAILED; + } + else + /* try next */ + result = ftp_state_use_port(conn, fcmd); + } + else { + infof(data, "Connect data stream actively\n"); + state(conn, FTP_STOP); /* end of DO phase */ + } + + return result; +} + +static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, + int ftpcode) { + CURLcode result = CURLE_OK; struct SessionHandle *data=conn->data; - char *buf = data->state.buffer; /* this is our buffer */ - CURLcode result; - ssize_t nread; - int ftpcode; /* for ftp status */ + struct FTP *ftp = data->reqdata.proto.ftp; - /* the ftp struct is already inited in Curl_ftp_connect() */ - struct FTP *ftp = conn->proto.ftp; - curl_off_t *bytecountp = ftp->bytecountp; + 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); + } - if(data->set.upload) { + /* 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. */ - /* Set type to binary (unless specified ASCII) */ - result = ftp_transfertype(conn, data->set.ftp_ascii); - if(result) - return result; + if(conn->bits.no_body && + data->set.include_header && + ftp->file && + data->set.get_filetime && + (data->info.filetime>=0) ) { + struct tm *tm; + time_t clock = (time_t)data->info.filetime; +#ifdef HAVE_GMTIME_R + struct tm buffer; + tm = (struct tm *)gmtime_r(&clock, &buffer); +#else + tm = gmtime(&clock); +#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 */ + } + 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; + } - /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/ - if(data->set.prequote) { - if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) - return result; + 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->no_transfer = TRUE; /* 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->no_transfer = TRUE; /* 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; +} + +static CURLcode ftp_state_type_resp(struct connectdata *conn, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data=conn->data; - if(conn->resume_from) { - /* 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. */ + if(ftpcode/100 != 2) { + /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a + successful 'TYPE I'. While that is not as RFC959 says, it is still a + positive response code and we allow that. */ + failf(data, "Couldn't set desired mode"); + return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */ + } + if(ftpcode != 200) + infof(data, "Got a %03d response code instead of the assumed 200\n", + ftpcode); + + if(instate == FTP_TYPE) + result = ftp_state_post_type(conn); + else if(instate == FTP_LIST_TYPE) + result = ftp_state_post_listtype(conn); + else if(instate == FTP_RETR_TYPE) + result = ftp_state_post_retrtype(conn); + else if(instate == FTP_STOR_TYPE) + result = ftp_state_post_stortype(conn); - /* 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 */ + return result; +} - /* 3. pass file-size number of bytes in the source file */ - /* 4. lower the infilesize counter */ - /* => transfer as usual */ +static CURLcode ftp_state_post_retr_size(struct connectdata *conn, + curl_off_t filesize) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data=conn->data; + struct FTP *ftp = data->reqdata.proto.ftp; - if(conn->resume_from < 0 ) { - /* we could've got a specified offset from the command line, - but now we know we didn't */ - curl_off_t gottensize; + if (data->set.max_filesize && (filesize > data->set.max_filesize)) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + ftp->downloadsize = filesize; - if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) { - failf(data, "Couldn't get remote file size"); - return CURLE_FTP_COULDNT_GET_SIZE; + if(data->reqdata.resume_from) { + /* We always (attempt to) get the size of downloads, so it is done before + this even when not doing resumes. */ + if(filesize == -1) { + infof(data, "ftp server doesn't support SIZE\n"); + /* We couldn't get the size and therefore we can't know if there really + is a part of the file left to get, although the server will just + close the connection when we start the connection so it won't cause + us any harm, just not make us exit as nicely. */ + } + else { + /* We got a file size report, so we check that there actually is a + part of the file left to get, or else we go home. */ + if(data->reqdata.resume_from< 0) { + /* We're supposed to download the last abs(from) bytes */ + if(filesize < -data->reqdata.resume_from) { + failf(data, "Offset (%" FORMAT_OFF_T + ") was beyond file size (%" FORMAT_OFF_T ")", + data->reqdata.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; } - conn->resume_from = gottensize; + /* convert to size to download */ + ftp->downloadsize = -data->reqdata.resume_from; + /* download from where? */ + data->reqdata.resume_from = filesize - ftp->downloadsize; } - - if(conn->resume_from) { - /* do we still game? */ - curl_off_t passed=0; - /* enable append instead */ - data->set.ftp_append = 1; - - /* Now, let's read off the proper amount of bytes from the - input. If we knew it was a proper file we could've just - fseek()ed but we only have a stream here */ - do { - curl_off_t readthisamountnow = (conn->resume_from - passed); - curl_off_t actuallyread; - - if(readthisamountnow > BUFSIZE) - readthisamountnow = BUFSIZE; - - actuallyread = (curl_off_t) - conn->fread(data->state.buffer, 1, (size_t)readthisamountnow, - conn->fread_in); - - passed += actuallyread; - if(actuallyread != readthisamountnow) { - failf(data, "Could only read %" FORMAT_OFF_T - " bytes from the input", passed); - return CURLE_FTP_COULDNT_USE_REST; - } + else { + if(filesize < data->reqdata.resume_from) { + failf(data, "Offset (%" FORMAT_OFF_T + ") was beyond file size (%" FORMAT_OFF_T ")", + data->reqdata.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; } - while(passed != conn->resume_from); + /* Now store the number of bytes we are expected to download */ + ftp->downloadsize = filesize-data->reqdata.resume_from; + } + } - /* now, decrease the size of the read */ - if(data->set.infilesize>0) { - data->set.infilesize -= conn->resume_from; + if(ftp->downloadsize == 0) { + /* no data to transfer */ + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + infof(data, "File already completely downloaded\n"); - if(data->set.infilesize <= 0) { - infof(data, "File already completely uploaded\n"); + /* Set no_transfer so that we won't get any error in Curl_ftp_done() + * because we didn't transfer the any file */ + ftp->no_transfer = TRUE; + state(conn, FTP_STOP); + return CURLE_OK; + } - /* no data to transfer */ - result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - (void)result; + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T + "\n", data->reqdata.resume_from); - /* Set no_transfer so that we won't get any error in - * Curl_ftp_done() because we didn't transfer anything! */ - ftp->no_transfer = TRUE; + NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, data->reqdata.resume_from); - return CURLE_OK; - } - } - /* we've passed, proceed as normal */ - } + state(conn, FTP_RETR_REST); + + } + else { + /* no resume */ + NBFTPSENDF(conn, "RETR %s", ftp->file); + state(conn, FTP_RETR); + } + + return result; +} + +static CURLcode ftp_state_size_resp(struct connectdata *conn, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data=conn->data; + curl_off_t filesize; + char *buf = data->state.buffer; + + /* get the size from the ascii string: */ + filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1; + + if(instate == FTP_SIZE) { + if(-1 != filesize) { + snprintf(buf, sizeof(data->state.buffer), + "Content-Length: %" FORMAT_OFF_T "\r\n", filesize); + result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } + result = ftp_state_post_size(conn); + } + else if(instate == FTP_RETR_SIZE) + result = ftp_state_post_retr_size(conn, filesize); + else if(instate == FTP_STOR_SIZE) { + data->reqdata.resume_from = filesize; + result = ftp_state_ul_setup(conn, TRUE); + } + + return result; +} + +static CURLcode ftp_state_rest_resp(struct connectdata *conn, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + + switch(instate) { + case FTP_REST: + default: + if (ftpcode == 350) { + result = Curl_client_write(conn, CLIENTWRITE_BOTH, + (char *)"Accept-ranges: bytes\r\n", 0); + if(result) + return result; } - /* Send everything on data->state.in to the socket */ - if(data->set.ftp_append) { - /* we append onto the file instead of rewriting it */ - FTPSENDF(conn, "APPE %s", ftp->file); + result = ftp_state_post_rest(conn); + break; + + case FTP_RETR_REST: + if (ftpcode != 350) { + failf(conn->data, "Couldn't use REST"); + result = CURLE_FTP_COULDNT_USE_REST; } else { - FTPSENDF(conn, "STOR %s", ftp->file); + NBFTPSENDF(conn, "RETR %s", ftp->file); + state(conn, FTP_RETR); } + break; + } - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + return result; +} + +static CURLcode ftp_state_stor_resp(struct connectdata *conn, + int ftpcode) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct FTP *ftp = data->reqdata.proto.ftp; + + if(ftpcode>=400) { + failf(data, "Failed FTP upload: %0d", ftpcode); + /* oops, we never close the sockets! */ + return CURLE_FTP_COULDNT_STOR_FILE; + } + + if(data->set.ftp_use_port) { + /* BLOCKING */ + /* PORT means we are now awaiting the server to connect to us. */ + 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"); + /* BLOCKING */ + result = Curl_ssl_connect(conn, SECONDARYSOCKET); if(result) return result; + } + + *(ftp->bytecountp)=0; + + /* When we know we're uploading a specified file, we can get the file + size prior to the actual upload. */ + + Curl_pgrsSetUploadSize(data, data->set.infilesize); - if(ftpcode>=400) { - failf(data, "Failed FTP upload:%s", buf+3); - /* oops, we never close the sockets! */ - return CURLE_FTP_COULDNT_STOR_FILE; + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ + SECONDARYSOCKET, ftp->bytecountp); + state(conn, FTP_STOP); + + return result; +} + +/* for LIST and RETR responses */ +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->reqdata.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) { - /* PORT means we are now awaiting the server to connect to us. */ + /* BLOCKING */ result = AllowServerConnect(conn); if( result ) return result; @@ -1768,479 +2213,1179 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn) /* 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_SSLConnect(conn, SECONDARYSOCKET); + result = Curl_ssl_connect(conn, SECONDARYSOCKET); if(result) return result; } - *bytecountp=0; + if(size > data->reqdata.maxdownload && data->reqdata.maxdownload > 0) + size = data->reqdata.size = data->reqdata.maxdownload; - /* When we know we're uploading a specified file, we can get the file - size prior to the actual upload. */ + infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->reqdata.maxdownload); - Curl_pgrsSetUploadSize(data, data->set.infilesize); + if(instate != FTP_LIST) + infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size); - result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ - SECONDARYSOCKET, bytecountp); + /* 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(!conn->bits.no_body) { - /* Retrieve file or directory */ - bool dirlist=FALSE; - curl_off_t downloadsize=-1; - - if(conn->bits.use_range && conn->range) { - curl_off_t from, to; - curl_off_t totalsize; - char *ptr; - char *ptr2; - - from=curlx_strtoofft(conn->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 - */ - conn->resume_from = from; - infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from); - } - else if(from < 0) { - /* -Y */ - totalsize = -from; - conn->maxdownload = -from; - conn->resume_from = from; - infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize); - } - else { - /* X-Y */ - totalsize = to-from; - conn->maxdownload = totalsize+1; /* include the last mentioned byte */ - conn->resume_from = from; - infof(data, "FTP RANGE from %" FORMAT_OFF_T - " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload); - } - infof(data, "range-download from %" FORMAT_OFF_T - " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n", - from, to, conn->maxdownload); - ftp->dont_check = TRUE; /* dont check for successful transfer */ + else { + if((instate == FTP_LIST) && (ftpcode == 450)) { + /* simply no matching files in the dir listing */ + ftp->no_transfer = TRUE; /* 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; + } + } - if((data->set.ftp_list_only) || !ftp->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. */ - dirlist = TRUE; - - /* Set type to ASCII */ - result = ftp_transfertype(conn, TRUE /* ASCII enforced */); - if(result) - return result; + return result; +} - /* if this output is to be machine-parsed, the NLST command will be - better used since the LIST command output is not specified or - standard in any way */ +/* after USER, PASS and ACCT */ +static CURLcode ftp_state_loggedin(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - FTPSENDF(conn, "%s", - data->set.customrequest?data->set.customrequest: - (data->set.ftp_list_only?"NLST":"LIST")); - } - else { - curl_off_t foundsize; +#ifdef HAVE_KRB4 + if(conn->data->set.krb4) { + /* We are logged in, asked to use Kerberos. Set the requested + * protection level + */ + if(conn->sec_complete) + /* BLOCKING */ + Curl_sec_set_protection_level(conn); - /* Set type to binary (unless specified ASCII) */ - result = ftp_transfertype(conn, data->set.ftp_ascii); + /* We may need to issue a KAUTH here to have access to the files + * do it if user supplied a password + */ + if(conn->passwd && *conn->passwd) { + /* BLOCKING */ + result = Curl_krb_kauth(conn); if(result) return result; + } + } +#endif + if(conn->ssl[FIRSTSOCKET].use) { + /* PBSZ = PROTECTION BUFFER SIZE. - /* Send any PREQUOTE strings after transfer type is set? */ - if(data->set.prequote) { - if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK) - return result; - } + The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: - /* Attempt to get the size, it'll be useful in some cases: for resumed - downloads and when talking to servers that don't give away the size - in the RETR response line. */ - result = ftp_getsize(conn, ftp->file, &foundsize); - if(CURLE_OK == result) { - if (data->set.max_filesize && foundsize > data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - downloadsize = foundsize; - } + Specifically, the PROT command MUST be preceded by a PBSZ + command and a PBSZ command MUST be preceded by a successful + security data exchange (the TLS negotiation in this case) - if(conn->resume_from) { + ... (and on page 8): - /* Daniel: (August 4, 1999) - * - * We start with trying to use the SIZE command to figure out the size - * of the file we're gonna get. If we can get the size, this is by far - * the best way to know if we're trying to resume beyond the EOF. - * - * Daniel, November 28, 2001. We *always* get the size on downloads - * now, so it is done before this even when not doing resumes. I saved - * the comment above for nostalgical reasons! ;-) - */ - if(CURLE_OK != result) { - infof(data, "ftp server doesn't support SIZE\n"); - /* We couldn't get the size and therefore we can't know if there - really is a part of the file left to get, although the server - will just close the connection when we start the connection so it - won't cause us any harm, just not make us exit as nicely. */ - } - else { - /* We got a file size report, so we check that there actually is a - part of the file left to get, or else we go home. */ - if(conn->resume_from< 0) { - /* We're supposed to download the last abs(from) bytes */ - if(foundsize < -conn->resume_from) { - failf(data, "Offset (%" FORMAT_OFF_T - ") was beyond file size (%" FORMAT_OFF_T ")", - conn->resume_from, foundsize); - return CURLE_FTP_BAD_DOWNLOAD_RESUME; - } - /* convert to size to download */ - downloadsize = -conn->resume_from; - /* download from where? */ - conn->resume_from = foundsize - downloadsize; - } - else { - if(foundsize < conn->resume_from) { - failf(data, "Offset (%" FORMAT_OFF_T - ") was beyond file size (%" FORMAT_OFF_T ")", - conn->resume_from, foundsize); - return CURLE_FTP_BAD_DOWNLOAD_RESUME; - } - /* Now store the number of bytes we are expected to download */ - downloadsize = foundsize-conn->resume_from; - } - } + Thus the PBSZ command must still be issued, but must have a + parameter of '0' to indicate that no buffering is taking place + and the data connection should not be encapsulated. + */ + NBFTPSENDF(conn, "PBSZ %d", 0); + state(conn, FTP_PBSZ); + } + else { + result = ftp_state_pwd(conn); + } + return result; +} - if (downloadsize == 0) { - /* no data to transfer */ - result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - (void)result; - infof(data, "File already completely downloaded\n"); +/* for USER and PASS responses */ +static CURLcode ftp_state_user_resp(struct connectdata *conn, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct FTP *ftp = data->reqdata.proto.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; + (void)instate; /* no use for this yet */ - /* Set no_transfer so that we won't get any error in Curl_ftp_done() - * because we didn't transfer the any file */ - ftp->no_transfer = TRUE; - return CURLE_OK; - } + if((ftpcode == 331) && (ftpc->state == FTP_USER)) { + /* 331 Password required for ... + (the server requires to send the user's password too) */ + NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:""); + state(conn, FTP_PASS); + } + else if(ftpcode/100 == 2) { + /* 230 User ... logged in. + (the user logged in with or without password) */ + result = ftp_state_loggedin(conn); + } + else if(ftpcode == 332) { + if(data->set.ftp_account) { + NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account); + state(conn, FTP_ACCT); + } + else { + failf(data, "ACCT requested but none available"); + result = CURLE_LOGIN_DENIED; + } + } + else { + /* All other response codes, like: - /* Set resume file transfer offset */ - infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T - "\n", - conn->resume_from); + 530 User ... access denied + (the server denies to log the specified user) */ - FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from); + if (conn->data->set.ftp_alternative_to_user && + !conn->data->state.ftp_trying_alternative) { + /* Ok, USER failed. Let's try the supplied command. */ + NBFTPSENDF(conn, "%s", conn->data->set.ftp_alternative_to_user); + conn->data->state.ftp_trying_alternative = TRUE; + state(conn, FTP_USER); + result = CURLE_OK; + } + else { + failf(data, "Access denied: %03d", ftpcode); + result = CURLE_LOGIN_DENIED; + } + } + return result; +} - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; +/* for ACCT response */ +static CURLcode ftp_state_acct_resp(struct connectdata *conn, + int ftpcode) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + if(ftpcode != 230) { + failf(data, "ACCT rejected by server: %03d", ftpcode); + result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */ + } + else + result = ftp_state_loggedin(conn); - if(ftpcode != 350) { - failf(data, "Couldn't use REST: %s", buf+4); - return CURLE_FTP_COULDNT_USE_REST; - } - } + return result; +} - FTPSENDF(conn, "RETR %s", ftp->file); - } - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); +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((ftpcode == 150) || (ftpcode == 125)) { + 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; + } - /* - A; - 150 Opening BINARY mode data connection for /etc/passwd (2241 - bytes). (ok, the file is being transfered) + /* we read a piece of response */ + result = ftp_readresp(sock, conn, &ftpcode, &nread); + if(result) + return result; - B: - 150 Opening ASCII mode data connection for /bin/ls + if(ftpcode) { + /* we have now received a full FTP server response */ + switch(ftpc->state) { + case FTP_WAIT220: + if(ftpcode != 220) { + failf(data, "This doesn't seem like a nice ftp-server response"); + return CURLE_FTP_WEIRD_SERVER_REPLY; + } - C: - 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). + /* We have received a 220 response fine, now we proceed. */ +#ifdef HAVE_KRB4 + if(data->set.krb4) { + /* If not anonymous login, try a secure login. Note that this + procedure is still BLOCKING. */ - D: - 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes). + 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.krb4_level); - E: - 125 Data connection already open; Transfer starting. */ + if(Curl_sec_login(conn) != 0) + infof(data, "Logging in with password in cleartext!\n"); + else + infof(data, "Authentication successful\n"); + } +#endif - curl_off_t size=-1; /* default unknown size */ + 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\n", + 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; - /* - * 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. + case FTP_AUTH: + /* we have gotten the response to a previous AUTH command */ + + /* RFC2228 (page 5) says: * - * Debugging help from Salvatore Sorrentino on February 26, 2003. + * 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(!dirlist && - !data->set.ftp_ascii && - (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=bytes-buf; - /* this is a hint there is size information in there! ;-) */ - while(--in) { - /* scan for the parenthesis and break there */ - if('(' == *bytes) - break; - /* if only skip digits, or else we're in deep trouble */ - if(!isdigit((int)*bytes)) { - bytes=NULL; - break; - } - /* one more estep backwards */ - bytes--; - } - /* only if we have nothing but digits: */ - if(bytes++) { - /* get the number! */ - size = curlx_strtoofft(bytes, NULL, 0); - } - + 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(downloadsize > -1) - size = downloadsize; + 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 > CURLFTPSSL_TRY) + /* we failed and CURLFTPSSL_CONTROL or CURLFTPSSL_ALL is set */ + result = CURLE_FTP_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') - if(data->set.ftp_use_port) { - result = AllowServerConnect(conn); - if( result ) + 2)Private (requested by 'PROT P') + */ + if(!conn->ssl[SECONDARYSOCKET].use) { + NBFTPSENDF(conn, "PROT %c", + data->set.ftp_ssl == CURLFTPSSL_CONTROL ? 'C' : 'P'); + state(conn, FTP_PROT); + } + else { + result = ftp_state_pwd(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_SSLConnect(conn, SECONDARYSOCKET); + 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 != CURLFTPSSL_CONTROL); + /* FTP servers typically responds with 500 if they decide to reject + our 'P' request */ + else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL) + /* we failed and bails out */ + return CURLE_FTP_SSL_FAILED; + + if(data->set.ftp_use_ccc) { + /* CCC - Clear Command Channel + */ + NBFTPSENDF(conn, "CCC", NULL); + state(conn, FTP_CCC); + } + else { + result = ftp_state_pwd(conn); if(result) return result; } + break; - if(size > conn->maxdownload && conn->maxdownload > 0) - size = conn->size = conn->maxdownload; + case FTP_CCC: + if (ftpcode < 500) { + /* First shut down the SSL layer (note: this call will block) */ + result = Curl_ssl_shutdown(conn, FIRSTSOCKET); - infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size); + if(result) { + failf(conn->data, "Failed to clear the command channel (CCC)"); + return result; + } + } - /* FTP download: */ - result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE, - bytecountp, - -1, NULL); /* no upload here */ + /* Then continue as normal */ + result = ftp_state_pwd(conn); if(result) return result; - } - else { - if(dirlist && (ftpcode == 450)) { - /* simply no matching files */ - ftp->no_transfer = TRUE; /* don't think we should download anything */ + break; + + case FTP_PWD: + if(ftpcode == 257) { + char *dir = (char *)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<space>"<directory-name>"<space><commentary> and the RFC959 + says + + The directory name can contain any character; embedded + double-quotes should be escaped by double-quotes (the + "quote-doubling" convention). + */ + if('\"' == *ptr) { + /* it started good */ + ptr++; + while(ptr && *ptr) { + if('\"' == *ptr) { + if('\"' == ptr[1]) { + /* "quote-doubling" */ + *store = ptr[1]; + ptr++; + } + else { + /* end of path */ + *store = '\0'; /* zero terminate */ + break; /* get out of this loop */ + } + } + else + *store = *ptr; + store++; + ptr++; + } + ftpc->entrypath =dir; /* remember this */ + infof(data, "Entry path is '%s'\n", ftpc->entrypath); + /* also save it where getinfo can access it: */ + data->state.most_recent_ftp_entrypath = ftpc->entrypath; + } + else { + /* couldn't get the path */ + free(dir); + infof(data, "Failed to figure out path\n"); + } + } + state(conn, FTP_STOP); /* we are done with the CONNECT phase! */ + DEBUGF(infof(data, "protocol connect phase DONE\n")); + break; + + case FTP_QUOTE: + case FTP_POSTQUOTE: + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + if(ftpcode >= 400) { + failf(conn->data, "QUOT command failed with %03d", ftpcode); + return CURLE_FTP_QUOTE_ERROR; + } + result = ftp_state_quote(conn, FALSE, ftpc->state); + if(result) + return result; + + break; + + case FTP_CWD: + if(ftpcode/100 != 2) { + /* failure to CWD there */ + if(conn->data->set.ftp_create_missing_dirs && + ftpc->count1 && !ftpc->count2) { + /* try making it */ + ftpc->count2++; /* counter to prevent CWD-MKD loops */ + NBFTPSENDF(conn, "MKD %s", ftpc->dirs[ftpc->count1 - 1]); + state(conn, FTP_MKD); + } + else { + /* return failure */ + failf(data, "Server denied you to change to the given directory"); + ftpc->cwdfail = TRUE; /* don't remember this path as we failed + to enter it */ + return CURLE_FTP_ACCESS_DENIED; + } } else { - failf(data, "%s", buf+4); - return CURLE_FTP_COULDNT_RETR_FILE; + /* success */ + ftpc->count2=0; + if(++ftpc->count1 <= ftpc->dirdepth) { + /* send next CWD */ + NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]); + } + else { + result = ftp_state_post_cwd(conn); + if(result) + return result; + } } + break; + + case FTP_MKD: + if(ftpcode/100 != 2) { + /* failure to MKD the dir */ + failf(data, "Failed to MKD dir: %03d", ftpcode); + return CURLE_FTP_ACCESS_DENIED; + } + state(conn, FTP_CWD); + /* send CWD */ + NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]); + break; + + case FTP_MDTM: + result = ftp_state_mdtm_resp(conn, ftpcode); + break; + + case FTP_TYPE: + case FTP_LIST_TYPE: + case FTP_RETR_TYPE: + case FTP_STOR_TYPE: + result = ftp_state_type_resp(conn, ftpcode, ftpc->state); + break; + + case FTP_SIZE: + case FTP_RETR_SIZE: + case FTP_STOR_SIZE: + result = ftp_state_size_resp(conn, ftpcode, ftpc->state); + break; + + case FTP_REST: + case FTP_RETR_REST: + result = ftp_state_rest_resp(conn, ftpcode, ftpc->state); + break; + + case FTP_PASV: + result = ftp_state_pasv_resp(conn, ftpcode); + break; + + case FTP_PORT: + result = ftp_state_port_resp(conn, ftpcode); + break; + + case FTP_LIST: + case FTP_RETR: + result = ftp_state_get_resp(conn, ftpcode, ftpc->state); + break; + + case FTP_STOR: + result = ftp_state_stor_resp(conn, ftpcode); + break; + + case FTP_QUIT: + /* fallthrough, just stop! */ + default: + /* internal error */ + state(conn, FTP_STOP); + break; } + } /* if(ftpcode) */ + + return result; +} + +/* Returns timeout in ms. 0 or negative number means the timeout has already + triggered */ +static long ftp_state_timeout(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + struct ftp_conn *ftpc = &conn->proto.ftpc; + long timeout_ms=360000; /* in milliseconds */ + + if(data->set.ftp_response_timeout ) + /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining + time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed + to govern the response for any given ftp response, not for the time + from connect to the given ftp response. */ + timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */ + else if(data->set.timeout) + /* if timeout is requested, find out how much remaining time we have */ + timeout_ms = data->set.timeout*1000 - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ + else + /* Without a requested timeout, we only wait 'response_time' seconds for + the full response to arrive before we bail out */ + timeout_ms = ftpc->response_time*1000 - + Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */ + + return timeout_ms; +} + +/* called repeatedly until done from multi.c */ +CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, + bool *done) +{ + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + struct SessionHandle *data=conn->data; + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = CURLE_OK; + long timeout_ms = ftp_state_timeout(conn); + + *done = FALSE; /* default to not done yet */ + + if(timeout_ms <= 0) { + failf(data, "FTP response timeout"); + return CURLE_OPERATION_TIMEDOUT; } - /* end of transfer */ + + rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */ + ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */ + 0); + + if(rc == -1) { + failf(data, "select error"); + return CURLE_OUT_OF_MEMORY; + } + else if(rc != 0) { + result = ftp_statemach_act(conn); + *done = (bool)(ftpc->state == FTP_STOP); + } + /* if rc == 0, then select() timed out */ + + return result; +} + +static CURLcode ftp_easy_statemach(struct connectdata *conn) +{ + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + struct SessionHandle *data=conn->data; + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result = CURLE_OK; + + while(ftpc->state != FTP_STOP) { + long timeout_ms = ftp_state_timeout(conn); + + if(timeout_ms <=0 ) { + failf(data, "FTP response timeout"); + return CURLE_OPERATION_TIMEDOUT; /* already too little time */ + } + + rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */ + ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */ + (int)timeout_ms); + + if(rc == -1) { + failf(data, "select error"); + return CURLE_OUT_OF_MEMORY; + } + else if(rc == 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + else { + result = ftp_statemach_act(conn); + if(result) + break; + } + } + + return result; +} + +/* + * Allocate and initialize the struct FTP for the current SessionHandle. If + * need be. + */ +static CURLcode ftp_init(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct FTP *ftp; + if(data->reqdata.proto.ftp) + return CURLE_OK; + + ftp = (struct FTP *)calloc(sizeof(struct FTP), 1); + if(!ftp) + return CURLE_OUT_OF_MEMORY; + + data->reqdata.proto.ftp = ftp; + + /* get some initial data into the ftp struct */ + ftp->bytecountp = &data->reqdata.keep.bytecount; + + /* no need to duplicate them, this connectdata struct won't change */ + ftp->user = conn->user; + ftp->passwd = conn->passwd; + if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd)) + return CURLE_URL_MALFORMAT; return CURLE_OK; } -/*********************************************************************** - * - * ftp_perform() +/* + * Curl_ftp_connect() should do everything that is to be considered a part of + * the connection phase. * - * This is the actual DO function for FTP. Get a file/directory according to - * the options previously setup. + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE is not. When called as + * a part of the easy interface, it will always be TRUE. */ - -static -CURLcode ftp_perform(struct connectdata *conn, - bool *connected) /* for the TCP connect status after - PASV / PORT */ +CURLcode Curl_ftp_connect(struct connectdata *conn, + bool *done) /* see description above */ { - /* this is FTP and no proxy */ - CURLcode result=CURLE_OK; + CURLcode result; +#ifndef CURL_DISABLE_HTTP + /* for FTP over HTTP proxy */ + struct HTTP http_proxy; + struct FTP *ftp_save; +#endif /* CURL_DISABLE_HTTP */ + struct ftp_conn *ftpc = &conn->proto.ftpc; struct SessionHandle *data=conn->data; - char *buf = data->state.buffer; /* this is our buffer */ - /* the ftp struct is already inited in Curl_ftp_connect() */ - struct FTP *ftp = conn->proto.ftp; + *done = FALSE; /* default to not done yet */ + + if (data->reqdata.proto.ftp) { + Curl_ftp_disconnect(conn); + free(data->reqdata.proto.ftp); + data->reqdata.proto.ftp = NULL; + } + + result = ftp_init(conn); + if(result) + return result; + + /* We always support persistant connections on ftp */ + conn->bits.close = FALSE; + + ftpc->response_time = 3600; /* set default response time-out */ + +#ifndef CURL_DISABLE_HTTP + if (conn->bits.tunnel_proxy && conn->bits.httpproxy) { + /* 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 + */ + ftp_save = data->reqdata.proto.ftp; + memset(&http_proxy, 0, sizeof(http_proxy)); + data->reqdata.proto.http = &http_proxy; + + result = Curl_proxyCONNECT(conn, FIRSTSOCKET, + conn->host.name, conn->remote_port); - /* Send any QUOTE strings? */ - if(data->set.quote) { - if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) + data->reqdata.proto.ftp = ftp_save; + + if(CURLE_OK != result) return result; } +#endif /* CURL_DISABLE_HTTP */ - /* This is a re-used connection. Since we change directory to where the - transfer is taking place, we must now get back to the original dir - where we ended up after login: */ - if (conn->bits.reuse && ftp->entrypath) { - if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK) + if(conn->protocol & PROT_FTPS) { + /* BLOCKING */ + /* FTPS is simply ftp with SSL for the control channel */ + /* now, perform the SSL initialization for this socket */ + result = Curl_ssl_connect(conn, FIRSTSOCKET); + if(result) return result; } - { - int i; /* counter for loop */ - for (i=0; i < ftp->dirdepth; i++) { - /* RFC 1738 says empty components should be respected too, but - that is plain stupid since CWD can't be used with an empty argument */ - if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK) - return result; + /* When we connect, we start in the state where we await the 220 + response */ + ftp_respinit(conn); /* init the response reader stuff */ + state(conn, FTP_WAIT220); + ftpc->response = Curl_tvnow(); /* start response time-out now! */ + + if(data->state.used_interface == Curl_if_multi) + result = Curl_ftp_multi_statemach(conn, done); + else { + result = ftp_easy_statemach(conn); + if(!result) + *done = TRUE; + } + + return result; +} + +/*********************************************************************** + * + * Curl_ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status, bool premature) +{ + struct SessionHandle *data = conn->data; + struct FTP *ftp = data->reqdata.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; + size_t flen; + size_t dlen; + char *path; + char *path_to_use = data->reqdata.path; + struct Curl_transfer_keeper *k = &data->reqdata.keep; + + 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_BINARY: + case CURLE_FTP_COULDNT_RETR_FILE: + case CURLE_FTP_COULDNT_STOR_FILE: + case CURLE_FTP_ACCESS_DENIED: + /* 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; } - /* Requested time of file or time-depended transfer? */ - if((data->set.get_filetime || data->set.timecondition) && - ftp->file) { - result = ftp_getfiletime(conn, ftp->file); - switch( result ) - { - case CURLE_FTP_COULDNT_RETR_FILE: - case CURLE_OK: - 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->no_transfer = TRUE; /* mark this to not transfer data */ - 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->no_transfer = TRUE; /* mark this to not transfer data */ - return CURLE_OK; - } - break; - } /* switch */ - } - else { - infof(data, "Skipping time comparison\n"); - } - } - break; - default: - return result; - } /* switch */ + /* 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) + return CURLE_OUT_OF_MEMORY; + + flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */ + dlen = strlen(path)-flen; + if(dlen && !ftpc->cwdfail) { + ftpc->prevpath = path; + if(flen) + /* if 'path' is not the whole string */ + ftpc->prevpath[dlen]=0; /* terminate */ + 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(conn); - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP can't be much more than the file size and - date. */ - if(conn->bits.no_body && data->set.include_header && ftp->file) { - /* The SIZE command is _not_ RFC 959 specified, and therefor many servers - may not support it! It is however the only way we have to get a file's - size! */ - curl_off_t filesize; - ssize_t nread; - int ftpcode; +#ifdef HAVE_KRB4 + Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]); +#endif - ftp->no_transfer = TRUE; /* this means no actual transfer is made */ + /* shut down the socket to inform the server we're done */ + +#ifdef _WIN32_WCE + shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */ +#endif + + sclose(conn->sock[SECONDARYSOCKET]); + + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; + + if(!ftp->no_transfer && !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; /* 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 */ + return result; + } - /* Some servers return different sizes for different modes, and thus we - must set the proper type before we check the size */ - result = ftp_transfertype(conn, data->set.ftp_ascii); if(result) return result; - /* failing to get size is not a serious error */ - result = ftp_getsize(conn, ftp->file, &filesize); + 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(CURLE_OK == result) { - snprintf(buf, sizeof(data->state.buffer), - "Content-Length: %" FORMAT_OFF_T "\r\n", filesize); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) - return result; + 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->no_transfer) { + 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 != k->size) && (k->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. + */ + ((k->size + data->state.crlf_conversions) != *ftp->bytecountp) && +#endif /* CURL_DO_LINEEND_CONV */ + (k->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 && + (k->size>0)) { + failf(data, "No data was received!"); + result = CURLE_FTP_COULDNT_RETR_FILE; + } + } - /* Determine if server can respond to REST command and therefore - whether it can do a range */ - FTPSENDF(conn, "REST 0", NULL); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + /* clear these for next connection */ + ftp->no_transfer = FALSE; + ftpc->dont_check = FALSE; - if ((CURLE_OK == result) && (ftpcode == 350)) { - result = Curl_client_write(data, CLIENTWRITE_BOTH, - (char *)"Accept-ranges: bytes\r\n", 0); - if(result) - return result; - } + /* Send any post-transfer QUOTE strings? */ + if(!status && !result && !premature && data->set.postquote) + result = ftp_sendquote(conn, data->set.postquote); - /* 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. */ + return result; +} -#ifdef HAVE_STRFTIME - if(data->set.get_filetime && (data->info.filetime>=0) ) { - struct tm *tm; - time_t cuClock = (time_t)data->info.filetime; -#ifdef HAVE_GMTIME_R - struct tm buffer; - tm = (struct tm *)gmtime_r(&cuClock, &buffer); -#else - tm = gmtime(&cuClock); -#endif - /* format: "Tue, 15 Nov 1994 12:45:26" */ - strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n", - tm); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) +/*********************************************************************** + * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + */ + +static +CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) +{ + struct curl_slist *item; + ssize_t nread; + int ftpcode; + CURLcode result; + + item = quote; + while (item) { + if (item->data) { + FTPSENDF(conn, "%s", item->data); + + result = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if (result) return result; + + if (ftpcode >= 400) { + failf(conn->data, "QUOT string not accepted: %s", item->data); + return CURLE_FTP_QUOTE_ERROR; + } } -#endif - return CURLE_OK; + item = item->next; } - if(conn->bits.no_body) - /* doesn't really transfer any data */ - ftp->no_transfer = TRUE; - /* Get us a second connection up and connected */ - else if(data->set.ftp_use_port) { - /* We have chosen to use the PORT command */ - result = ftp_use_port(conn); - if(CURLE_OK == result) { - /* we have the data connection ready */ - infof(data, "Ordered connect of the data stream with PORT!\n"); - *connected = TRUE; /* mark us "still connected" */ + return CURLE_OK; +} + +/*********************************************************************** + * + * ftp_need_type() + * + * Returns TRUE if we in the current situation should send TYPE + */ +static int ftp_need_type(struct connectdata *conn, + bool ascii_wanted) +{ + return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I'); +} + +/*********************************************************************** + * + * ftp_nb_type() + * + * Set TYPE. We only deal with ASCII or BINARY so this function + * sets one of them. + * If the transfer type is not sent, simulate on OK response in newstate + */ +static CURLcode ftp_nb_type(struct connectdata *conn, + bool ascii, ftpstate newstate) +{ + struct ftp_conn *ftpc = &conn->proto.ftpc; + CURLcode result; + int want = ascii?'A':'I'; + + if (ftpc->transfertype == want) { + state(conn, newstate); + return ftp_state_type_resp(conn, 200, newstate); + } + + NBFTPSENDF(conn, "TYPE %c", want); + state(conn, newstate); + + /* keep track of our current transfer type */ + ftpc->transfertype = want; + return CURLE_OK; +} + +/*************************************************************************** + * + * ftp_pasv_verbose() + * + * This function only outputs some informationals about this second connection + * when we've issued a PASV command before and thus we have connected to a + * possibly new IP address. + * + */ +static void +ftp_pasv_verbose(struct connectdata *conn, + Curl_addrinfo *ai, + char *newhost, /* ascii version */ + int port) +{ + char buf[256]; + Curl_printable_address(ai, buf, sizeof(buf)); + infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port); +} + +/* + Check if this is a range download, and if so, set the internal variables + properly. + */ + +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->reqdata.use_range && data->reqdata.range) { + from=curlx_strtoofft(data->reqdata.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->reqdata.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->reqdata.maxdownload = -from; + data->reqdata.resume_from = from; + DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", + totalsize)); + } + else { + /* X-Y */ + totalsize = to-from; + data->reqdata.maxdownload = totalsize+1; /* include last byte */ + data->reqdata.resume_from = from; + DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T + " getting %" FORMAT_OFF_T " bytes\n", + from, data->reqdata.maxdownload)); + } + DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T + " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n", + from, to, data->reqdata.maxdownload)); + ftpc->dont_check = TRUE; /* dont check for successful transfer */ + } + return CURLE_OK; +} + + +/* + * Curl_ftp_nextconnect() + * + * This function shall be called when the second FTP (data) connection is + * connected. + */ + +CURLcode Curl_ftp_nextconnect(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + CURLcode result = CURLE_OK; + + /* the ftp struct is inited in Curl_ftp_connect() */ + struct FTP *ftp = data->reqdata.proto.ftp; + + DEBUGF(infof(data, "DO-MORE phase starts\n")); + + if(!ftp->no_transfer && !conn->bits.no_body) { + /* a transfer is about to take place */ + + 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) || !ftp->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. */ + result = ftp_nb_type(conn, 1, FTP_LIST_TYPE); + if (result) + return result; + } + else { + result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE); + if (result) + return result; + } + } + result = ftp_easy_statemach(conn); } + + if(ftp->no_transfer) + /* 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; +} + + + +/*********************************************************************** + * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct connectdata *conn, + bool *connected, /* connect status after PASV / PORT */ + bool *dophase_done) +{ + /* this is FTP and no proxy */ + CURLcode result=CURLE_OK; + + DEBUGF(infof(conn->data, "DO phase starts\n")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + result = ftp_state_quote(conn, TRUE, FTP_QUOTE); + if(result) + return result; + + /* run the state-machine */ + if(conn->data->state.used_interface == Curl_if_multi) + result = Curl_ftp_multi_statemach(conn, dophase_done); else { - /* We have chosen (this is default) to use the PASV command */ - result = ftp_use_pasv(conn, connected); - if(CURLE_OK == result && *connected) - infof(data, "Connected the data stream with PASV!\n"); + result = ftp_easy_statemach(conn); + *dophase_done = TRUE; /* with the easy interface we are done here */ } + *connected = conn->bits.tcpconnect; + + if(*dophase_done) + DEBUGF(infof(conn->data, "DO phase is complete\n")); return result; } @@ -2254,27 +3399,103 @@ CURLcode ftp_perform(struct connectdata *conn, * * The input argument is already checked for validity. */ -CURLcode Curl_ftp(struct connectdata *conn) +CURLcode Curl_ftp(struct connectdata *conn, bool *done) { - CURLcode retcode; + CURLcode retcode = CURLE_OK; - if (conn->sec_conn) /* 3rd party transfer */ - retcode = ftp_3rdparty(conn); - else - retcode = ftp_regular_transfer(conn); + *done = FALSE; /* default to false */ + + /* + 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 FTP' to play with. For new connections, + the struct FTP is allocated and setup in the Curl_ftp_connect() function. + */ + retcode = ftp_init(conn); + if(retcode) + return retcode; + + retcode = ftp_parse_url_path(conn); + if (retcode) + return retcode; + + retcode = ftp_regular_transfer(conn, done); return retcode; } /*********************************************************************** * - * Curl_ftpsendf() + * Curl_(nb)ftpsendf() * * Sends the formated string as a ftp command to a ftp server * * NOTE: we build the command in a fixed-length buffer, which sets length * restrictions on the command! + * + * The "nb" version is made to Never Block. */ +CURLcode Curl_nbftpsendf(struct connectdata *conn, + const char *fmt, ...) +{ + ssize_t bytes_written; + char s[256]; + size_t write_len; + char *sptr=s; + CURLcode res = CURLE_OK; + struct SessionHandle *data = conn->data; + struct ftp_conn *ftpc = &conn->proto.ftpc; + + va_list ap; + va_start(ap, fmt); + vsnprintf(s, 250, fmt, ap); + va_end(ap); + + strcat(s, "\r\n"); /* append a trailing CRLF */ + + bytes_written=0; + write_len = strlen(s); + + ftp_respinit(conn); + +#ifdef CURL_DOES_CONVERSIONS + res = Curl_convert_to_network(data, s, write_len); + /* Curl_convert_to_network calls failf if unsuccessful */ + if(res != CURLE_OK) { + return res; + } +#endif /* CURL_DOES_CONVERSIONS */ + + res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, + &bytes_written); + + if(CURLE_OK != res) + return res; + + if(conn->data->set.verbose) + Curl_debug(conn->data, CURLINFO_HEADER_OUT, + sptr, (size_t)bytes_written, conn); + + if(bytes_written != (ssize_t)write_len) { + /* the whole chunk was not sent, store the rest of the data */ + write_len -= bytes_written; + sptr += bytes_written; + ftpc->sendthis = malloc(write_len); + if(ftpc->sendthis) { + memcpy(ftpc->sendthis, sptr, write_len); + ftpc->sendsize = ftpc->sendleft = write_len; + } + else { + failf(data, "out of memory"); + res = CURLE_OUT_OF_MEMORY; + } + } + else + ftpc->response = Curl_tvnow(); + + return res; +} + CURLcode Curl_ftpsendf(struct connectdata *conn, const char *fmt, ...) { @@ -2282,7 +3503,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, char s[256]; size_t write_len; char *sptr=s; - CURLcode res; + CURLcode res = CURLE_OK; va_list ap; va_start(ap, fmt); @@ -2294,6 +3515,14 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, bytes_written=0; write_len = strlen(s); +#ifdef CURL_DOES_CONVERSIONS + res = Curl_convert_to_network(conn->data, s, write_len); + /* Curl_convert_to_network calls failf if unsuccessful */ + if(res != CURLE_OK) { + return(res); + } +#endif /* CURL_DOES_CONVERSIONS */ + while(1) { res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, &bytes_written); @@ -2302,7 +3531,8 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, break; if(conn->data->set.verbose) - Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname); + Curl_debug(conn->data, CURLINFO_HEADER_OUT, + sptr, (size_t)bytes_written, conn); if(bytes_written != (ssize_t)write_len) { write_len -= bytes_written; @@ -2327,17 +3557,16 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, */ static CURLcode ftp_quit(struct connectdata *conn) { - ssize_t nread; - int ftpcode; - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; - if(conn->proto.ftp->ctl_valid) { - ret = Curl_ftpsendf(conn, "%s", "QUIT"); - if(CURLE_OK == ret) - ret = Curl_GetFTPResponse(&nread, conn, &ftpcode); + if(conn->proto.ftpc.ctl_valid) { + NBFTPSENDF(conn, "QUIT", NULL); + state(conn, FTP_QUIT); + + result = ftp_easy_statemach(conn); } - return ret; + return result; } /*********************************************************************** @@ -2345,11 +3574,11 @@ static CURLcode ftp_quit(struct connectdata *conn) * Curl_ftp_disconnect() * * Disconnect from an FTP server. Cleanup protocol-specific per-connection - * resources + * resources. BLOCKING. */ CURLcode Curl_ftp_disconnect(struct connectdata *conn) { - struct FTP *ftp= conn->proto.ftp; + struct ftp_conn *ftpc= &conn->proto.ftpc; /* We cannot send quit unconditionally. If this connection is stale or bad in any way, sending quit and waiting around here will make the @@ -2360,415 +3589,276 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn) */ /* The FTP session may or may not have been allocated/setup at this point! */ - if(ftp) { + if(conn->data->reqdata.proto.ftp) { (void)ftp_quit(conn); /* ignore errors on the QUIT */ - if(ftp->entrypath) - free(ftp->entrypath); - if(ftp->cache) { - free(ftp->cache); - ftp->cache = NULL; + if(ftpc->entrypath) { + struct SessionHandle *data = conn->data; + data->state.most_recent_ftp_entrypath = NULL; + free(ftpc->entrypath); + ftpc->entrypath = NULL; + } + if(ftpc->cache) { + free(ftpc->cache); + ftpc->cache = NULL; + } + freedirs(conn); + if(ftpc->prevpath) { + free(ftpc->prevpath); + ftpc->prevpath = NULL; } - freedirs(ftp); } return CURLE_OK; } /*********************************************************************** * - * ftp_mkd() + * ftp_parse_url_path() * - * Makes a directory on the FTP server. + * Parse the URL path into separate path components. * - * Calls failf() */ -static CURLcode ftp_mkd(struct connectdata *conn, char *path) +static +CURLcode ftp_parse_url_path(struct connectdata *conn) { - CURLcode result; - int ftpcode; /* for ftp status */ - ssize_t nread; + CURLcode retcode = CURLE_OK; + struct SessionHandle *data = conn->data; + /* the ftp struct is already inited in ftp_connect() */ + struct FTP *ftp = data->reqdata.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->reqdata.path; + char *cur_pos; - /* Create a directory on the remote server */ - FTPSENDF(conn, "MKD %s", path); + cur_pos = path_to_use; /* current position in path. point at the begin + of next path component */ - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if(result) - return result; + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = FALSE; - switch(ftpcode) { - case 257: - /* success! */ - infof( conn->data , "Created remote directory %s\n" , path ); - break; - case 550: - failf(conn->data, "Permission denied to make directory %s", path); - result = CURLE_FTP_ACCESS_DENIED; - break; - default: - failf(conn->data, "unrecognized MKD response: %d", ftpcode ); - result = CURLE_FTP_ACCESS_DENIED; + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: + /* fastest, but less standard-compliant */ + ftp->file = data->reqdata.path; /* this is a full file path */ break; - } - return result; -} -/*********************************************************************** - * - * ftp_cwd() - * - * Send 'CWD' to the remote server to Change Working Directory. It is the ftp - * version of the unix 'cd' command. This function is only called from the - * ftp_cwd_and_mkd() function these days. - * - * This function does NOT call failf(). - */ -static -CURLcode ftp_cwd(struct connectdata *conn, char *path) -{ - ssize_t nread; - int ftpcode; - CURLcode result; + case FTPFILE_SINGLECWD: + /* get the last slash */ + slash_pos=strrchr(cur_pos, '/'); + if(slash_pos || !cur_pos || !*cur_pos) { + ftpc->dirdepth = 1; /* we consider it to be a single dir */ + ftpc->dirs = (char **)calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; - FTPSENDF(conn, "CWD %s", path); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if (!result) { - /* According to RFC959, CWD is supposed to return 250 on success, but - there seem to be non-compliant FTP servers out there that return 200, - so we accept any '2xy' code here. */ - if (ftpcode/100 != 2) - result = CURLE_FTP_ACCESS_DENIED; - } + 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]) { + free(ftpc->dirs); + return CURLE_OUT_OF_MEMORY; + } + ftp->file = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */ + } + else + ftp->file = cur_pos; /* this is a file name only */ + break; - return result; -} + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: + ftpc->dirdepth = 0; + ftpc->diralloc = 5; /* default dir depth to allocate */ + ftpc->dirs = (char **)calloc(ftpc->diralloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; -/*********************************************************************** - * - * ftp_cwd_and_mkd() - * - * Change to the given directory. If the directory is not present, and we - * have been told to allow it, then create the directory and cd to it. - * - */ -static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path) -{ - CURLcode result; + /* 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->reqdata.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(conn); + return CURLE_OUT_OF_MEMORY; + } + if (isBadFtpString(ftpc->dirs[ftpc->dirdepth])) { + freedirs(conn); + return CURLE_URL_MALFORMAT; + } + } + else { + cur_pos = slash_pos + 1; /* jump to the rest of the string */ + continue; + } - result = ftp_cwd(conn, path); - if (result) { - if(conn->data->set.ftp_create_missing_dirs) { - result = ftp_mkd(conn, path); - if (result) - /* ftp_mkd() calls failf() itself */ - return result; - result = ftp_cwd(conn, path); + if(!retcode) { + 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) { + ftpc->dirdepth--; + freedirs(conn); + return CURLE_OUT_OF_MEMORY; + } + ftpc->dirs = (char **)bigger; + } + } } - if(result) - failf(conn->data, "Couldn't cd to %s", path); + + ftp->file = cur_pos; /* the rest is the file name */ } - return result; -} + if(*ftp->file) { + ftp->file = curl_easy_unescape(conn->data, ftp->file, 0, NULL); + if(NULL == ftp->file) { + freedirs(conn); + failf(data, "no memory"); + return CURLE_OUT_OF_MEMORY; + } + if (isBadFtpString(ftp->file)) { + freedirs(conn); + return CURLE_URL_MALFORMAT; + } + } + else + ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL + pointer */ + if(data->set.upload && !ftp->file && + (!ftp->no_transfer || conn->bits.no_body)) { + /* We need a file name when uploading. Return error! */ + failf(data, "Uploading to a URL without a file name!"); + return CURLE_URL_MALFORMAT; + } -/*********************************************************************** - * - * ftp_3rdparty_pretransfer() - * - * Preparation for 3rd party transfer. - * - */ -static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn) -{ - CURLcode result; - struct SessionHandle *data = conn->data; - struct connectdata *sec_conn = conn->sec_conn; + ftpc->cwddone = FALSE; /* default to not done */ - /* sets transfer type */ - result = ftp_transfertype(conn, data->set.ftp_ascii); - if (result) - return result; - - result = ftp_transfertype(sec_conn, data->set.ftp_ascii); - if (result) - return result; + 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->reqdata.path, 0, NULL); + if(!path) + return CURLE_OUT_OF_MEMORY; - /* Send any PREQUOTE strings after transfer type is set? */ - if (data->set.source_prequote) { - /* sends command(s) to source server before file transfer */ - result = ftp_sendquote(sec_conn, data->set.source_prequote); + dlen = strlen(path) - (ftp->file?strlen(ftp->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); } - if (!result && data->set.prequote) - result = ftp_sendquote(conn, data->set.prequote); - return result; + return retcode; } - - -/*********************************************************************** - * - * ftp_3rdparty_transfer() - * - * Performs 3rd party transfer. - * - */ -static CURLcode ftp_3rdparty_transfer(struct connectdata *conn) +/* call this when the DO phase has completed */ +static CURLcode ftp_dophase_done(struct connectdata *conn, + bool connected) { - CURLcode result; - ssize_t nread; - int ftpcode, ip[4], port[2]; - struct SessionHandle *data = conn->data; - struct connectdata *sec_conn = conn->sec_conn; - char *buf = data->state.buffer; /* this is our buffer */ - char *str = buf; - char pasv_port[50]; - const char *stor_cmd; - struct connectdata *pasv_conn; - struct connectdata *port_conn; - - if (data->set.pasvHost == CURL_TARGET_PASV) { - pasv_conn = conn; - port_conn = sec_conn; - } - else { - pasv_conn = sec_conn; - port_conn = conn; - } - - /* sets the passive mode */ - FTPSENDF(pasv_conn, "%s", "PASV"); - result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode); - if (result) return result; - if (ftpcode != 227) { - failf(data, "Odd return code after PASV:%s", buf + 3); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - - 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(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf); - return CURLE_FTP_WEIRD_227_FORMAT; - } + CURLcode result = CURLE_OK; + struct FTP *ftp = conn->data->reqdata.proto.ftp; + struct ftp_conn *ftpc = &conn->proto.ftpc; - snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1], - ip[2], ip[3], port[0], port[1]); + if(connected) + result = Curl_ftp_nextconnect(conn); - /* sets data connection between remote hosts */ - FTPSENDF(port_conn, "PORT %s", pasv_port); - result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode); - if (result) + if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) { + /* Failure detected, close the second socket if it was created already */ + sclose(conn->sock[SECONDARYSOCKET]); + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; return result; - - if (ftpcode != 200) { - failf(data, "PORT command attempts failed:%s", buf + 3); - return CURLE_FTP_PORT_FAILED; } - /* we might append onto the file instead of overwriting it */ - stor_cmd = data->set.ftp_append?"APPE":"STOR"; - - /* transfers file between remote hosts */ - FTPSENDF(sec_conn, "RETR %s", data->set.source_path); - - if(data->set.pasvHost == CURL_TARGET_PASV) { - - result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode); - if (result) - return result; - - if (ftpcode != 150) { - failf(data, "Failed RETR: %s", buf + 4); - return CURLE_FTP_COULDNT_RETR_FILE; - } - - result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path); - if(CURLE_OK == result) - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if (result) - return result; - - if (ftpcode != 150) { - failf(data, "Failed FTP upload: %s", buf + 4); - return CURLE_FTP_COULDNT_STOR_FILE; - } + if(ftp->no_transfer) + /* no data to transfer */ + result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + else if(!connected) + /* since we didn't connect now, we want do_more to get called */ + conn->bits.do_more = TRUE; - } - else { + ftpc->ctl_valid = TRUE; /* seems good */ - result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path); - if(CURLE_OK == result) - result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode); - if (result) - return result; + return result; +} - if (ftpcode != 150) { - failf(data, "Failed FTP upload: %s", buf + 4); - return CURLE_FTP_COULDNT_STOR_FILE; - } +/* called from multi.c while DOing */ +CURLcode Curl_ftp_doing(struct connectdata *conn, + bool *dophase_done) +{ + CURLcode result; + result = Curl_ftp_multi_statemach(conn, dophase_done); - result = Curl_GetFTPResponse(&nread, conn, &ftpcode); - if (result) - return result; + if(*dophase_done) { + result = ftp_dophase_done(conn, FALSE /* not connected */); - if (ftpcode != 150) { - failf(data, "Failed FTP upload: %s", buf + 4); - return CURLE_FTP_COULDNT_STOR_FILE; - } + DEBUGF(infof(conn->data, "DO phase is complete\n")); } - - return CURLE_OK; + return result; } - - /*********************************************************************** * * ftp_regular_transfer() * * The input argument is already checked for validity. - * Performs a regular transfer between local and remote hosts. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. * * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the * Curl_ftp_done() function without finding any major problem. */ static -CURLcode ftp_regular_transfer(struct connectdata *conn) +CURLcode ftp_regular_transfer(struct connectdata *conn, + bool *dophase_done) { - CURLcode retcode=CURLE_OK; + CURLcode result=CURLE_OK; bool connected=0; struct SessionHandle *data = conn->data; - struct FTP *ftp; - - char *slash_pos; /* position of the first '/' char in curpos */ - char *cur_pos=conn->path; /* current position in ppath. point at the begin - of next path component */ - - /* the ftp struct is already inited in ftp_connect() */ - ftp = conn->proto.ftp; - ftp->ctl_valid = FALSE; - conn->size = -1; /* make sure this is unknown at this point */ + struct ftp_conn *ftpc = &conn->proto.ftpc; + data->reqdata.size = -1; /* make sure this is unknown at this point */ Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); Curl_pgrsSetDownloadSize(data, 0); - ftp->dirdepth = 0; - ftp->diralloc = 5; /* default dir depth to allocate */ - ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0])); - if(!ftp->dirs) - return CURLE_OUT_OF_MEMORY; - ftp->dirs[0] = NULL; /* to start with */ - - /* parse the URL path into separate path components */ - while((slash_pos=strchr(cur_pos, '/'))) { - /* 1 or 0 to indicate absolute directory */ - bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->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); - ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len); - - if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */ - failf(data, "no memory"); - freedirs(ftp); - return CURLE_OUT_OF_MEMORY; - } - } - else { - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - continue; - } + ftpc->ctl_valid = TRUE; /* starts good */ - if(!retcode) { - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - if(++ftp->dirdepth >= ftp->diralloc) { - /* enlarge array */ - char **bigger; - ftp->diralloc *= 2; /* double the size each time */ - bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0])); - if(!bigger) { - freedirs(ftp); - return CURLE_OUT_OF_MEMORY; - } - ftp->dirs = (char **)bigger; - } - } - } + result = ftp_perform(conn, + &connected, /* have we connected after PASV/PORT */ + dophase_done); /* all commands in the DO-phase done? */ - ftp->file = cur_pos; /* the rest is the file name */ + if(CURLE_OK == result) { - if(*ftp->file) { - ftp->file = curl_unescape(ftp->file, 0); - if(NULL == ftp->file) { - freedirs(ftp); - failf(data, "no memory"); - return CURLE_OUT_OF_MEMORY; - } - } - else - ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL - pointer */ - - retcode = ftp_perform(conn, &connected); - - if(CURLE_OK == retcode) { - if(connected) - retcode = Curl_ftp_nextconnect(conn); - - if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) { - /* Failure detected, close the second socket if it was created already */ - sclose(conn->sock[SECONDARYSOCKET]); - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; - } + if(!*dophase_done) + /* the DO phase has not completed yet */ + return CURLE_OK; - if(ftp->no_transfer) - /* no data to transfer */ - retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - else if(!connected) - /* since we didn't connect now, we want do_more to get called */ - conn->bits.do_more = TRUE; + result = ftp_dophase_done(conn, connected); + if(result) + return result; } else - freedirs(ftp); - - ftp->ctl_valid = TRUE; /* seems good */ - - return retcode; -} - - - -/*********************************************************************** - * - * ftp_3rdparty() - * - * The input argument is already checked for validity. - * Performs a 3rd party transfer between two remote hosts. - */ -static CURLcode ftp_3rdparty(struct connectdata *conn) -{ - CURLcode retcode; - - conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE; - conn->size = conn->sec_conn->size = -1; + freedirs(conn); - retcode = ftp_3rdparty_pretransfer(conn); - if (!retcode) - retcode = ftp_3rdparty_transfer(conn); - - return retcode; + return result; } #endif /* CURL_DISABLE_FTP */ diff --git a/Utilities/cmcurl/ftp.h b/Utilities/cmcurl/ftp.h index dc7cf79..b64e705 100644 --- a/Utilities/cmcurl/ftp.h +++ b/Utilities/cmcurl/ftp.h @@ -1,18 +1,18 @@ #ifndef __FTP_H #define __FTP_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,14 +24,20 @@ ***************************************************************************/ #ifndef CURL_DISABLE_FTP -CURLcode Curl_ftp(struct connectdata *conn); -CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode); -CURLcode Curl_ftp_connect(struct connectdata *conn); +CURLcode Curl_ftp(struct connectdata *conn, bool *done); +CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode, bool premature); +CURLcode Curl_ftp_connect(struct connectdata *conn, bool *done); CURLcode Curl_ftp_disconnect(struct connectdata *conn); CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); +CURLcode Curl_nbftpsendf(struct connectdata *, const char *fmt, ...); CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn, int *ftpcode); CURLcode Curl_ftp_nextconnect(struct connectdata *conn); -#endif - -#endif +CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done); +int Curl_ftp_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); +CURLcode Curl_ftp_doing(struct connectdata *conn, + bool *dophase_done); +#endif /* CURL_DISABLE_FTP */ +#endif /* __FTP_H */ diff --git a/Utilities/cmcurl/getdate.c b/Utilities/cmcurl/getdate.c deleted file mode 100644 index 9d946d3..0000000 --- a/Utilities/cmcurl/getdate.c +++ /dev/null @@ -1,2471 +0,0 @@ -/* A Bison parser, made by GNU Bison 1.875a. */ - -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ - -/* Written by Richard Stallman by simplifying the original so called - ``semantic'' parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 1 - -/* Using locations. */ -#define YYLSP_NEEDED 0 - - - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - tAGO = 258, - tDAY = 259, - tDAY_UNIT = 260, - tDAYZONE = 261, - tDST = 262, - tHOUR_UNIT = 263, - tID = 264, - tMERIDIAN = 265, - tMINUTE_UNIT = 266, - tMONTH = 267, - tMONTH_UNIT = 268, - tSEC_UNIT = 269, - tSNUMBER = 270, - tUNUMBER = 271, - tYEAR_UNIT = 272, - tZONE = 273 - }; -#endif -#define tAGO 258 -#define tDAY 259 -#define tDAY_UNIT 260 -#define tDAYZONE 261 -#define tDST 262 -#define tHOUR_UNIT 263 -#define tID 264 -#define tMERIDIAN 265 -#define tMINUTE_UNIT 266 -#define tMONTH 267 -#define tMONTH_UNIT 268 -#define tSEC_UNIT 269 -#define tSNUMBER 270 -#define tUNUMBER 271 -#define tYEAR_UNIT 272 -#define tZONE 273 - - - - -/* Copy the first part of user declarations. */ -#line 1 "getdate.y" - -/* -** Originally written by Steven M. Bellovin <smb@research.att.com> while -** at the University of North Carolina at Chapel Hill. Later tweaked by -** a couple of people on Usenet. Completely overhauled by Rich $alz -** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. -** -** This code has been modified since it was included in curl, to make it -** thread-safe and to make compilers complain less about it. -** -** This code is in the public domain and has no copyright. -*/ - -#include "setup.h" - -# ifdef HAVE_ALLOCA_H -# include <alloca.h> -# endif - -# ifdef HAVE_TIME_H -# include <time.h> -# endif - -#ifndef YYDEBUG - /* to satisfy gcc -Wundef, we set this to 0 */ -#define YYDEBUG 0 -#endif - -#ifndef YYSTACK_USE_ALLOCA - /* to satisfy gcc -Wundef, we set this to 0 */ -#define YYSTACK_USE_ALLOCA 0 -#endif - -/* Since the code of getdate.y is not included in the Emacs executable - itself, there is no need to #define static in this file. Even if - the code were included in the Emacs executable, it probably - wouldn't do any harm to #undef it here; this will only cause - problems if we try to write to a static variable, which I don't - think this code needs to do. */ -#ifdef emacs -# undef static -#endif - -#ifdef __APPLE__ -#include <sys/types.h> -#include <sys/malloc.h> -#else - -#endif -#include <string.h> -#include <stdio.h> -#include <ctype.h> - -#if HAVE_STDLIB_H -# include <stdlib.h> /* for `free'; used by Bison 1.27 */ -#else - -#ifdef HAVE_MALLOC_H -#include <malloc.h> -#endif - -#endif - -#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) -# define IN_CTYPE_DOMAIN(c) 1 -#else -# define IN_CTYPE_DOMAIN(c) isascii(c) -#endif - -#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) -#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) -#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) -#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) - -/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: - - Its arg may be any int or unsigned int; it need not be an unsigned char. - - It's guaranteed to evaluate its argument exactly once. - - It's typically faster. - Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that - only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless - it's important to use the locale's definition of `digit' even when the - host does not conform to Posix. */ -#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) - -#if defined (STDC_HEADERS) || defined (USG) -# include <string.h> -#endif - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#ifndef YYMAXDEPTH -#define YYMAXDEPTH 0 -#endif - -#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __attribute__(x) -#endif - -#ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - -/* Some old versions of bison generate parsers that use bcopy. - That loses on systems that don't provide the function, so we have - to redefine it here. */ -#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) -# define bcopy(from, to, len) memcpy ((to), (from), (len)) -#endif - -/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), - as well as gratuitiously global symbol names, so we can have multiple - yacc generated parsers in the same program. Note that these are only - the variables produced by yacc. If other parser generators (bison, - byacc, etc) produce additional global names that conflict at link time, - then those parser generators need to be fixed instead of adding those - names to this list. */ - -#define yymaxdepth Curl_gd_maxdepth -#define yyparse Curl_gd_parse -#define yylex Curl_gd_lex -#define yyerror Curl_gd_error -#define yylval Curl_gd_lval -#define yychar Curl_gd_char -#define yydebug Curl_gd_debug -#define yypact Curl_gd_pact -#define yyr1 Curl_gd_r1 -#define yyr2 Curl_gd_r2 -#define yydef Curl_gd_def -#define yychk Curl_gd_chk -#define yypgo Curl_gd_pgo -#define yyact Curl_gd_act -#define yyexca Curl_gd_exca -#define yyerrflag Curl_gd_errflag -#define yynerrs Curl_gd_nerrs -#define yyps Curl_gd_ps -#define yypv Curl_gd_pv -#define yys Curl_gd_s -#define yy_yys Curl_gd_yys -#define yystate Curl_gd_state -#define yytmp Curl_gd_tmp -#define yyv Curl_gd_v -#define yy_yyv Curl_gd_yyv -#define yyval Curl_gd_val -#define yylloc Curl_gd_lloc -#define yyreds Curl_gd_reds /* With YYDEBUG defined */ -#define yytoks Curl_gd_toks /* With YYDEBUG defined */ -#define yylhs Curl_gd_yylhs -#define yylen Curl_gd_yylen -#define yydefred Curl_gd_yydefred -#define yydgoto Curl_gd_yydgoto -#define yysindex Curl_gd_yysindex -#define yyrindex Curl_gd_yyrindex -#define yygindex Curl_gd_yygindex -#define yytable Curl_gd_yytable -#define yycheck Curl_gd_yycheck - -#define EPOCH 1970 -#define HOUR(x) ((x) * 60) - -#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ - -/* -** An entry in the lexical lookup table. -*/ -typedef struct _TABLE { - const char *name; - int type; - int value; -} TABLE; - - -/* -** Meridian: am, pm, or 24-hour style. -*/ -typedef enum _MERIDIAN { - MERam, MERpm, MER24 -} MERIDIAN; - -/* parse results and input string */ -typedef struct _CURL_CONTEXT { - const char *yyInput; - int yyDayOrdinal; - int yyDayNumber; - int yyHaveDate; - int yyHaveDay; - int yyHaveRel; - int yyHaveTime; - int yyHaveZone; - int yyTimezone; - int yyDay; - int yyHour; - int yyMinutes; - int yyMonth; - int yySeconds; - int yyYear; - MERIDIAN yyMeridian; - int yyRelDay; - int yyRelHour; - int yyRelMinutes; - int yyRelMonth; - int yyRelSeconds; - int yyRelYear; -} CURL_CONTEXT; - -/* enable use of extra argument to yyparse and yylex which can be used to pass -** in a user defined value (CURL_CONTEXT struct in our case) -*/ -#define YYPARSE_PARAM cookie -#define YYLEX_PARAM cookie -#define context ((CURL_CONTEXT *) cookie) - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) -#line 223 "getdate.y" -typedef union YYSTYPE { - int Number; - enum _MERIDIAN Meridian; -} YYSTYPE; -/* Line 191 of yacc.c. */ -#line 331 "y.tab.c" -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - - - -/* Copy the second part of user declarations. */ -#line 228 "getdate.y" - -static int yylex (YYSTYPE *yylval, void *cookie); -static int yyerror (const char *s); - - -/* Line 214 of yacc.c. */ -#line 347 "y.tab.c" - -#if ! defined (yyoverflow) || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# if YYSTACK_USE_ALLOCA -# define YYSTACK_ALLOC alloca -# else -# ifndef YYSTACK_USE_ALLOCA -# if defined (alloca) || defined (_ALLOCA_H) -# define YYSTACK_ALLOC alloca -# else -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# else -# if defined (__STDC__) || defined (__cplusplus) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -# define YYSTACK_ALLOC malloc -# define YYSTACK_FREE free -# endif -#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ - - -#if (! defined (yyoverflow) \ - && (! defined (__cplusplus) \ - || (YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - short yyss; - YYSTYPE yyvs; - }; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - register YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (0) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - (void)yyptr; \ - } \ - while (0) - -#endif - -#if defined (__STDC__) || defined (__cplusplus) - typedef signed char yysigned_char; -#else - typedef short yysigned_char; -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 2 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 50 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 22 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 11 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 51 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 61 - -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 273 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const unsigned char yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 20, 2, 2, 21, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18 -}; - -#if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const unsigned char yyprhs[] = -{ - 0, 0, 3, 4, 7, 9, 11, 13, 15, 17, - 19, 22, 27, 32, 39, 46, 48, 50, 53, 55, - 58, 61, 65, 71, 75, 79, 82, 87, 90, 94, - 97, 99, 102, 105, 107, 110, 113, 115, 118, 121, - 123, 126, 129, 131, 134, 137, 139, 142, 145, 147, - 149, 150 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yysigned_char yyrhs[] = -{ - 23, 0, -1, -1, 23, 24, -1, 25, -1, 26, - -1, 28, -1, 27, -1, 29, -1, 31, -1, 16, - 10, -1, 16, 19, 16, 32, -1, 16, 19, 16, - 15, -1, 16, 19, 16, 19, 16, 32, -1, 16, - 19, 16, 19, 16, 15, -1, 18, -1, 6, -1, - 18, 7, -1, 4, -1, 4, 20, -1, 16, 4, - -1, 16, 21, 16, -1, 16, 21, 16, 21, 16, - -1, 16, 15, 15, -1, 16, 12, 15, -1, 12, - 16, -1, 12, 16, 20, 16, -1, 16, 12, -1, - 16, 12, 16, -1, 30, 3, -1, 30, -1, 16, - 17, -1, 15, 17, -1, 17, -1, 16, 13, -1, - 15, 13, -1, 13, -1, 16, 5, -1, 15, 5, - -1, 5, -1, 16, 8, -1, 15, 8, -1, 8, - -1, 16, 11, -1, 15, 11, -1, 11, -1, 16, - 14, -1, 15, 14, -1, 14, -1, 16, -1, -1, - 10, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const unsigned short yyrline[] = -{ - 0, 244, 244, 245, 248, 251, 254, 257, 260, 263, - 266, 272, 278, 287, 293, 305, 308, 312, 317, 321, - 325, 331, 335, 353, 359, 365, 369, 374, 378, 385, - 393, 396, 399, 402, 405, 408, 411, 414, 417, 420, - 423, 426, 429, 432, 435, 438, 441, 444, 447, 452, - 487, 490 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE -/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE", - "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", - "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT", - "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone", - "day", "date", "rel", "relunit", "number", "o_merid", 0 -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ -static const unsigned short yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, - 44, 47 -}; -# endif - -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const unsigned char yyr1[] = -{ - 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, - 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, - 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, - 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, - 32, 32 -}; - -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const unsigned char yyr2[] = -{ - 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, - 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, - 2, 3, 5, 3, 3, 2, 4, 2, 3, 2, - 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, - 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, - 0, 1 -}; - -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const unsigned char yydefact[] = -{ - 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, - 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, - 8, 30, 9, 19, 25, 38, 41, 44, 35, 47, - 32, 20, 37, 40, 10, 43, 27, 34, 46, 0, - 31, 0, 0, 17, 29, 0, 24, 28, 23, 50, - 21, 26, 51, 12, 0, 11, 0, 50, 22, 14, - 13 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yysigned_char yydefgoto[] = -{ - -1, 1, 15, 16, 17, 18, 19, 20, 21, 22, - 55 -}; - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -20 -static const yysigned_char yypact[] = -{ - -20, 0, -20, -19, -20, -20, -20, -20, -13, -20, - -20, 30, 15, -20, 14, -20, -20, -20, -20, -20, - -20, 19, -20, -20, 4, -20, -20, -20, -20, -20, - -20, -20, -20, -20, -20, -20, -6, -20, -20, 16, - -20, 17, 23, -20, -20, 24, -20, -20, -20, 27, - 28, -20, -20, -20, 29, -20, 32, -8, -20, -20, - -20 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yysigned_char yypgoto[] = -{ - -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, - -7 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -1 -static const unsigned char yytable[] = -{ - 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, - 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, - 32, 43, 44, 33, 45, 34, 35, 36, 37, 38, - 39, 48, 40, 49, 41, 25, 42, 52, 26, 50, - 51, 27, 53, 28, 29, 57, 54, 30, 58, 56, - 60 -}; - -static const unsigned char yycheck[] = -{ - 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, - 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, - 5, 7, 3, 8, 20, 10, 11, 12, 13, 14, - 15, 15, 17, 16, 19, 5, 21, 10, 8, 16, - 16, 11, 15, 13, 14, 16, 19, 17, 16, 21, - 57 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const unsigned char yystos[] = -{ - 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, - 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, - 29, 30, 31, 20, 16, 5, 8, 11, 13, 14, - 17, 4, 5, 8, 10, 11, 12, 13, 14, 15, - 17, 19, 21, 7, 3, 20, 15, 16, 15, 16, - 16, 16, 10, 15, 19, 32, 21, 16, 16, 15, - 32 -}; - -#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) -# define YYSIZE_T __SIZE_TYPE__ -#endif -#if ! defined (YYSIZE_T) && defined (size_t) -# define YYSIZE_T size_t -#endif -#if ! defined (YYSIZE_T) -# if defined (__STDC__) || defined (__cplusplus) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -#endif -#if ! defined (YYSIZE_T) -# define YYSIZE_T unsigned int -#endif - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrlab1 - - -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ - -#define YYFAIL goto yyerrlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror ("syntax error: cannot back up");\ - YYERROR; \ - } \ -while (0) - -#define YYTERROR 1 -#define YYERRCODE 256 - -/* YYLLOC_DEFAULT -- Compute the default location (before the actions - are run). */ - -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - Current.first_line = Rhs[1].first_line; \ - Current.first_column = Rhs[1].first_column; \ - Current.last_line = Rhs[N].last_line; \ - Current.last_column = Rhs[N].last_column; -#endif - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX yylex (&yylval, YYLEX_PARAM) -#else -# define YYLEX yylex (&yylval) -#endif - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - -# define YYDSYMPRINT(Args) \ -do { \ - if (yydebug) \ - yysymprint Args; \ -} while (0) - -# define YYDSYMPRINTF(Title, Token, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yysymprint (stderr, \ - Token, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (cinluded). | -`------------------------------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yy_stack_print (short *bottom, short *top) -#else -static void -yy_stack_print (bottom, top) - short *bottom; - short *top; -#endif -{ - YYFPRINTF (stderr, "Stack now"); - for (/* Nothing. */; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yy_reduce_print (int yyrule) -#else -static void -yy_reduce_print (yyrule) - int yyrule; -#endif -{ - int yyi; - unsigned int yylineno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", - yyrule - 1, yylineno); - /* Print the symbols being reduced, and their result. */ - for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) - YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); - YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (Rule); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YYDSYMPRINT(Args) -# define YYDSYMPRINTF(Title, Token, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#if YYMAXDEPTH == 0 -# undef YYMAXDEPTH -#endif - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - - -#if YYERROR_VERBOSE - -# ifndef yystrlen -# if defined (__GLIBC__) && defined (_STRING_H) -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -static YYSIZE_T -# if defined (__STDC__) || defined (__cplusplus) -yystrlen (const char *yystr) -# else -yystrlen (yystr) - const char *yystr; -# endif -{ - register const char *yys = yystr; - - while (*yys++ != '\0') - continue; - - return yys - yystr - 1; -} -# endif -# endif - -# ifndef yystpcpy -# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -# if defined (__STDC__) || defined (__cplusplus) -yystpcpy (char *yydest, const char *yysrc) -# else -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -# endif -{ - register char *yyd = yydest; - register const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif - -#endif /* !YYERROR_VERBOSE */ - - - -#if YYDEBUG -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) -#else -static void -yysymprint (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; - - if (yytype < YYNTOKENS) - { - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); -# ifdef YYPRINT - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# endif - } - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); - - switch (yytype) - { - default: - break; - } - YYFPRINTF (yyoutput, ")"); -} - -#endif /* ! YYDEBUG */ -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yydestruct (int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yytype, yyvaluep) - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; - - switch (yytype) - { - - default: - break; - } -} - - -/* Prevent warnings from -Wmissing-prototypes. */ - -#ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -int yyparse (void *YYPARSE_PARAM); -# else -int yyparse (); -# endif -#else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ - - - - - - -/*----------. -| yyparse. | -`----------*/ - -#ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -int yyparse (void *YYPARSE_PARAM) -# else -int yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -# endif -#else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) -int -yyparse (void) -#else -int -yyparse () - -#endif -#endif -{ - /* The lookahead symbol. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; - -/* Number of syntax errors so far. */ -int yynerrs; - - register int yystate; - register int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Lookahead token as an internal (translated) token number. */ - int yytoken = 0; - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - short yyssa[YYINITDEPTH]; - short *yyss = yyssa; - register short *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - register YYSTYPE *yyvsp; - - - -#define YYPOPSTACK (yyvsp--, yyssp--) - - YYSIZE_T yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - - /* When reducing, the number of symbols on the RHS of the reduced - rule. */ - int yylen; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - yyssp = yyss; - yyvsp = yyvs; - - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. so pushing a state here evens the stacks. - */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = (YYSIZE_T)(yyssp - yyss + 1); - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - short *yyss1 = yyss; - - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow ("parser stack overflow", - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyoverflowlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyoverflowlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - short *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyoverflowlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - -/* Do appropriate processing given the current state. */ -/* Read a lookahead token if we need one and don't already have one. */ -/* yyresume: */ - - /* First try to decide what to do without reference to lookahead token. */ - - yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - if (yyn == YYFINAL) - YYACCEPT; - - /* Shift the lookahead token. */ - YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); - - /* Discard the token being shifted unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; - - *++yyvsp = yylval; - - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - yystate = yyn; - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 4: -#line 248 "getdate.y" - { - context->yyHaveTime++; - } - break; - - case 5: -#line 251 "getdate.y" - { - context->yyHaveZone++; - } - break; - - case 6: -#line 254 "getdate.y" - { - context->yyHaveDate++; - } - break; - - case 7: -#line 257 "getdate.y" - { - context->yyHaveDay++; - } - break; - - case 8: -#line 260 "getdate.y" - { - context->yyHaveRel++; - } - break; - - case 10: -#line 266 "getdate.y" - { - context->yyHour = yyvsp[-1].Number; - context->yyMinutes = 0; - context->yySeconds = 0; - context->yyMeridian = yyvsp[0].Meridian; - } - break; - - case 11: -#line 272 "getdate.y" - { - context->yyHour = yyvsp[-3].Number; - context->yyMinutes = yyvsp[-1].Number; - context->yySeconds = 0; - context->yyMeridian = yyvsp[0].Meridian; - } - break; - - case 12: -#line 278 "getdate.y" - { - context->yyHour = yyvsp[-3].Number; - context->yyMinutes = yyvsp[-1].Number; - context->yyMeridian = MER24; - context->yyHaveZone++; - context->yyTimezone = (yyvsp[0].Number < 0 - ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 - : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); - } - break; - - case 13: -#line 287 "getdate.y" - { - context->yyHour = yyvsp[-5].Number; - context->yyMinutes = yyvsp[-3].Number; - context->yySeconds = yyvsp[-1].Number; - context->yyMeridian = yyvsp[0].Meridian; - } - break; - - case 14: -#line 293 "getdate.y" - { - context->yyHour = yyvsp[-5].Number; - context->yyMinutes = yyvsp[-3].Number; - context->yySeconds = yyvsp[-1].Number; - context->yyMeridian = MER24; - context->yyHaveZone++; - context->yyTimezone = (yyvsp[0].Number < 0 - ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60 - : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60)); - } - break; - - case 15: -#line 305 "getdate.y" - { - context->yyTimezone = yyvsp[0].Number; - } - break; - - case 16: -#line 308 "getdate.y" - { - context->yyTimezone = yyvsp[0].Number - 60; - } - break; - - case 17: -#line 312 "getdate.y" - { - context->yyTimezone = yyvsp[-1].Number - 60; - } - break; - - case 18: -#line 317 "getdate.y" - { - context->yyDayOrdinal = 1; - context->yyDayNumber = yyvsp[0].Number; - } - break; - - case 19: -#line 321 "getdate.y" - { - context->yyDayOrdinal = 1; - context->yyDayNumber = yyvsp[-1].Number; - } - break; - - case 20: -#line 325 "getdate.y" - { - context->yyDayOrdinal = yyvsp[-1].Number; - context->yyDayNumber = yyvsp[0].Number; - } - break; - - case 21: -#line 331 "getdate.y" - { - context->yyMonth = yyvsp[-2].Number; - context->yyDay = yyvsp[0].Number; - } - break; - - case 22: -#line 335 "getdate.y" - { - /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. - The goal in recognizing YYYY/MM/DD is solely to support legacy - machine-generated dates like those in an RCS log listing. If - you want portability, use the ISO 8601 format. */ - if (yyvsp[-4].Number >= 1000) - { - context->yyYear = yyvsp[-4].Number; - context->yyMonth = yyvsp[-2].Number; - context->yyDay = yyvsp[0].Number; - } - else - { - context->yyMonth = yyvsp[-4].Number; - context->yyDay = yyvsp[-2].Number; - context->yyYear = yyvsp[0].Number; - } - } - break; - - case 23: -#line 353 "getdate.y" - { - /* ISO 8601 format. yyyy-mm-dd. */ - context->yyYear = yyvsp[-2].Number; - context->yyMonth = -yyvsp[-1].Number; - context->yyDay = -yyvsp[0].Number; - } - break; - - case 24: -#line 359 "getdate.y" - { - /* e.g. 17-JUN-1992. */ - context->yyDay = yyvsp[-2].Number; - context->yyMonth = yyvsp[-1].Number; - context->yyYear = -yyvsp[0].Number; - } - break; - - case 25: -#line 365 "getdate.y" - { - context->yyMonth = yyvsp[-1].Number; - context->yyDay = yyvsp[0].Number; - } - break; - - case 26: -#line 369 "getdate.y" - { - context->yyMonth = yyvsp[-3].Number; - context->yyDay = yyvsp[-2].Number; - context->yyYear = yyvsp[0].Number; - } - break; - - case 27: -#line 374 "getdate.y" - { - context->yyMonth = yyvsp[0].Number; - context->yyDay = yyvsp[-1].Number; - } - break; - - case 28: -#line 378 "getdate.y" - { - context->yyMonth = yyvsp[-1].Number; - context->yyDay = yyvsp[-2].Number; - context->yyYear = yyvsp[0].Number; - } - break; - - case 29: -#line 385 "getdate.y" - { - context->yyRelSeconds = -context->yyRelSeconds; - context->yyRelMinutes = -context->yyRelMinutes; - context->yyRelHour = -context->yyRelHour; - context->yyRelDay = -context->yyRelDay; - context->yyRelMonth = -context->yyRelMonth; - context->yyRelYear = -context->yyRelYear; - } - break; - - case 31: -#line 396 "getdate.y" - { - context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 32: -#line 399 "getdate.y" - { - context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 33: -#line 402 "getdate.y" - { - context->yyRelYear += yyvsp[0].Number; - } - break; - - case 34: -#line 405 "getdate.y" - { - context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 35: -#line 408 "getdate.y" - { - context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 36: -#line 411 "getdate.y" - { - context->yyRelMonth += yyvsp[0].Number; - } - break; - - case 37: -#line 414 "getdate.y" - { - context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 38: -#line 417 "getdate.y" - { - context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 39: -#line 420 "getdate.y" - { - context->yyRelDay += yyvsp[0].Number; - } - break; - - case 40: -#line 423 "getdate.y" - { - context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 41: -#line 426 "getdate.y" - { - context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 42: -#line 429 "getdate.y" - { - context->yyRelHour += yyvsp[0].Number; - } - break; - - case 43: -#line 432 "getdate.y" - { - context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 44: -#line 435 "getdate.y" - { - context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 45: -#line 438 "getdate.y" - { - context->yyRelMinutes += yyvsp[0].Number; - } - break; - - case 46: -#line 441 "getdate.y" - { - context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 47: -#line 444 "getdate.y" - { - context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number; - } - break; - - case 48: -#line 447 "getdate.y" - { - context->yyRelSeconds += yyvsp[0].Number; - } - break; - - case 49: -#line 453 "getdate.y" - { - if (context->yyHaveTime && context->yyHaveDate && - !context->yyHaveRel) - context->yyYear = yyvsp[0].Number; - else - { - if (yyvsp[0].Number>10000) - { - context->yyHaveDate++; - context->yyDay= (yyvsp[0].Number)%100; - context->yyMonth= (yyvsp[0].Number/100)%100; - context->yyYear = yyvsp[0].Number/10000; - } - else - { - context->yyHaveTime++; - if (yyvsp[0].Number < 100) - { - context->yyHour = yyvsp[0].Number; - context->yyMinutes = 0; - } - else - { - context->yyHour = yyvsp[0].Number / 100; - context->yyMinutes = yyvsp[0].Number % 100; - } - context->yySeconds = 0; - context->yyMeridian = MER24; - } - } - } - break; - - case 50: -#line 487 "getdate.y" - { - yyval.Meridian = MER24; - } - break; - - case 51: -#line 491 "getdate.y" - { - yyval.Meridian = yyvsp[0].Meridian; - } - break; - - - } - -/* Line 999 of yacc.c. */ -#line 1688 "y.tab.c" - - yyvsp -= yylen; - yyssp -= yylen; - - - YY_STACK_PRINT (yyss, yyssp); - - *++yyvsp = yyval; - - - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; - - goto yynewstate; - - -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ -yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if YYERROR_VERBOSE - yyn = yypact[yystate]; - - if (YYPACT_NINF < yyn && yyn < YYLAST) - { - YYSIZE_T yysize = 0; - int yytype = YYTRANSLATE (yychar); - char *yymsg; - int yyx, yycount; - - yycount = 0; - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - for (yyx = yyn < 0 ? -yyn : 0; - yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - yysize += yystrlen (yytname[yyx]) + 15, yycount++; - yysize += yystrlen ("syntax error, unexpected ") + 1; - yysize += yystrlen (yytname[yytype]); - yymsg = (char *) YYSTACK_ALLOC (yysize); - if (yymsg != 0) - { - char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); - yyp = yystpcpy (yyp, yytname[yytype]); - - if (yycount < 5) - { - yycount = 0; - for (yyx = yyn < 0 ? -yyn : 0; - yyx < (int) (sizeof (yytname) / sizeof (char *)); - yyx++) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - const char *yyq = ! yycount ? ", expecting " : " or "; - yyp = yystpcpy (yyp, yyq); - yyp = yystpcpy (yyp, yytname[yyx]); - yycount++; - } - } - yyerror (yymsg); - YYSTACK_FREE (yymsg); - } - else - yyerror ("syntax error; also virtual memory exhausted"); - } - else -#endif /* YYERROR_VERBOSE */ - yyerror ("syntax error"); - } - (void)yynerrs; - - - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - /* Return failure if at end of input. */ - if (yychar == YYEOF) - { - /* Pop the error token. */ - YYPOPSTACK; - /* Pop the rest of the stack. */ - while (yyss < yyssp) - { - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[*yyssp], yyvsp); - YYPOPSTACK; - } - YYABORT; - } - - YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); - yydestruct (yytoken, &yylval); - yychar = YYEMPTY; - - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*----------------------------------------------------. -| yyerrlab1 -- error raised explicitly by an action. | -`----------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[yystate], yyvsp); - yyvsp--; - yystate = *--yyssp; - - YY_STACK_PRINT (yyss, yyssp); - } - - if (yyn == YYFINAL) - YYACCEPT; - - YYDPRINTF ((stderr, "Shifting error token, ")); - - *++yyvsp = yylval; - - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -#ifndef yyoverflow -/*----------------------------------------------. -| yyoverflowlab -- parser overflow comes here. | -`----------------------------------------------*/ -yyoverflowlab: - yyerror ("parser stack overflow"); - yyresult = 2; - /* Fall through. */ -#endif - -yyreturn: -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif - return yyresult; -} - - -#line 496 "getdate.y" - - -/* Include this file down here because bison inserts code above which - may define-away `const'. We want the prototype for get_date to have - the same signature as the function definition does. */ -#include "getdate.h" - -#ifndef WIN32 /* the windows dudes don't need these, does anyone really? */ -extern struct tm *gmtime (const time_t *); -extern struct tm *localtime (const time_t *); -extern time_t mktime (struct tm *); -#endif - -/* Month and day table. */ -static TABLE const MonthDayTable[] = { - { "january", tMONTH, 1 }, - { "february", tMONTH, 2 }, - { "march", tMONTH, 3 }, - { "april", tMONTH, 4 }, - { "may", tMONTH, 5 }, - { "june", tMONTH, 6 }, - { "july", tMONTH, 7 }, - { "august", tMONTH, 8 }, - { "september", tMONTH, 9 }, - { "sept", tMONTH, 9 }, - { "october", tMONTH, 10 }, - { "november", tMONTH, 11 }, - { "december", tMONTH, 12 }, - { "sunday", tDAY, 0 }, - { "monday", tDAY, 1 }, - { "tuesday", tDAY, 2 }, - { "tues", tDAY, 2 }, - { "wednesday", tDAY, 3 }, - { "wednes", tDAY, 3 }, - { "thursday", tDAY, 4 }, - { "thur", tDAY, 4 }, - { "thurs", tDAY, 4 }, - { "friday", tDAY, 5 }, - { "saturday", tDAY, 6 }, - { NULL, 0, 0 } -}; - -/* Time units table. */ -static TABLE const UnitsTable[] = { - { "year", tYEAR_UNIT, 1 }, - { "month", tMONTH_UNIT, 1 }, - { "fortnight", tDAY_UNIT, 14 }, - { "week", tDAY_UNIT, 7 }, - { "day", tDAY_UNIT, 1 }, - { "hour", tHOUR_UNIT, 1 }, - { "minute", tMINUTE_UNIT, 1 }, - { "min", tMINUTE_UNIT, 1 }, - { "second", tSEC_UNIT, 1 }, - { "sec", tSEC_UNIT, 1 }, - { NULL, 0, 0 } -}; - -/* Assorted relative-time words. */ -static TABLE const OtherTable[] = { - { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, - { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, - { "today", tMINUTE_UNIT, 0 }, - { "now", tMINUTE_UNIT, 0 }, - { "last", tUNUMBER, -1 }, - { "this", tMINUTE_UNIT, 0 }, - { "next", tUNUMBER, 1 }, - { "first", tUNUMBER, 1 }, -/* { "second", tUNUMBER, 2 }, */ - { "third", tUNUMBER, 3 }, - { "fourth", tUNUMBER, 4 }, - { "fifth", tUNUMBER, 5 }, - { "sixth", tUNUMBER, 6 }, - { "seventh", tUNUMBER, 7 }, - { "eighth", tUNUMBER, 8 }, - { "ninth", tUNUMBER, 9 }, - { "tenth", tUNUMBER, 10 }, - { "eleventh", tUNUMBER, 11 }, - { "twelfth", tUNUMBER, 12 }, - { "ago", tAGO, 1 }, - { NULL, 0, 0 } -}; - -/* The timezone table. */ -static TABLE const TimezoneTable[] = { - { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ - { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ - { "utc", tZONE, HOUR ( 0) }, - { "wet", tZONE, HOUR ( 0) }, /* Western European */ - { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ - { "wat", tZONE, HOUR ( 1) }, /* West Africa */ - { "at", tZONE, HOUR ( 2) }, /* Azores */ -#if 0 - /* For completeness. BST is also British Summer, and GST is - * also Guam Standard. */ - { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ - { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ -#endif -#if 0 - { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ - { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ - { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ -#endif - { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ - { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ - { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ - { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ - { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ - { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ - { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ - { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ - { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ - { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ - { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ - { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ - { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ - { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ - { "cat", tZONE, HOUR (10) }, /* Central Alaska */ - { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ - { "nt", tZONE, HOUR (11) }, /* Nome */ - { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ - { "cet", tZONE, -HOUR (1) }, /* Central European */ - { "met", tZONE, -HOUR (1) }, /* Middle European */ - { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ - { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ - { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ - { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ - { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ - { "fwt", tZONE, -HOUR (1) }, /* French Winter */ - { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ - { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ - { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ -#if 0 - { "it", tZONE, -HOUR (3.5) },/* Iran */ -#endif - { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ - { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ -#if 0 - { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ -#endif - { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ -#if 0 - /* For completeness. NST is also Newfoundland Standard, and SST is - * also Swedish Summer. */ - { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ - { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ -#endif /* 0 */ - { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ - { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ -#if 0 - { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ -#endif - { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ - { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ -#if 0 - { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ - { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ -#endif - { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ - { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ - { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ - { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ - { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ - { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ - { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ - { NULL, 0, 0 } -}; - -/* Military timezone table. */ -static TABLE const MilitaryTable[] = { - { "a", tZONE, HOUR ( 1) }, - { "b", tZONE, HOUR ( 2) }, - { "c", tZONE, HOUR ( 3) }, - { "d", tZONE, HOUR ( 4) }, - { "e", tZONE, HOUR ( 5) }, - { "f", tZONE, HOUR ( 6) }, - { "g", tZONE, HOUR ( 7) }, - { "h", tZONE, HOUR ( 8) }, - { "i", tZONE, HOUR ( 9) }, - { "k", tZONE, HOUR ( 10) }, - { "l", tZONE, HOUR ( 11) }, - { "m", tZONE, HOUR ( 12) }, - { "n", tZONE, HOUR (- 1) }, - { "o", tZONE, HOUR (- 2) }, - { "p", tZONE, HOUR (- 3) }, - { "q", tZONE, HOUR (- 4) }, - { "r", tZONE, HOUR (- 5) }, - { "s", tZONE, HOUR (- 6) }, - { "t", tZONE, HOUR (- 7) }, - { "u", tZONE, HOUR (- 8) }, - { "v", tZONE, HOUR (- 9) }, - { "w", tZONE, HOUR (-10) }, - { "x", tZONE, HOUR (-11) }, - { "y", tZONE, HOUR (-12) }, - { "z", tZONE, HOUR ( 0) }, - { NULL, 0, 0 } -}; - - - - -/* ARGSUSED */ -static int -yyerror (const char *s ATTRIBUTE_UNUSED) -{ - return 0; -} - -static int -ToHour (int Hours, MERIDIAN Meridian) -{ - switch (Meridian) - { - case MER24: - if (Hours < 0 || Hours > 23) - return -1; - return Hours; - case MERam: - if (Hours < 1 || Hours > 12) - return -1; - if (Hours == 12) - Hours = 0; - return Hours; - case MERpm: - if (Hours < 1 || Hours > 12) - return -1; - if (Hours == 12) - Hours = 0; - return Hours + 12; - default: - break; /* used to do abort() here */ - } - /* NOTREACHED - but make gcc happy! */ - return -1; -} - -static int -ToYear (int Year) -{ - if (Year < 0) - Year = -Year; - - /* XPG4 suggests that years 00-68 map to 2000-2068, and - years 69-99 map to 1969-1999. */ - if (Year < 69) - Year += 2000; - else if (Year < 100) - Year += 1900; - - return Year; -} - -static int -LookupWord (YYSTYPE *yylval, char *buff) -{ - char *p; - char *q; - const TABLE *tp; - size_t i; - int abbrev; - - /* Make it lowercase. */ - for (p = buff; *p; p++) - if (ISUPPER ((unsigned char) *p)) - *p = tolower ((int)*p); - - if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) - { - yylval->Meridian = MERam; - return tMERIDIAN; - } - if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) - { - yylval->Meridian = MERpm; - return tMERIDIAN; - } - - /* See if we have an abbreviation for a month. */ - if (strlen (buff) == 3) - abbrev = 1; - else if (strlen (buff) == 4 && buff[3] == '.') - { - abbrev = 1; - buff[3] = '\0'; - } - else - abbrev = 0; - - for (tp = MonthDayTable; tp->name; tp++) - { - if (abbrev) - { - if (strncmp (buff, tp->name, 3) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - } - else if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - } - - for (tp = TimezoneTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - - if (strcmp (buff, "dst") == 0) - return tDST; - - for (tp = UnitsTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - - /* Strip off any plural and try the units table again. */ - i = strlen (buff) - 1; - if (buff[i] == 's') - { - buff[i] = '\0'; - for (tp = UnitsTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - buff[i] = 's'; /* Put back for "this" in OtherTable. */ - } - - for (tp = OtherTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - - /* Military timezones. */ - if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) - { - for (tp = MilitaryTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - } - - /* Drop out any periods and try the timezone table again. */ - for (i = 0, p = q = buff; *q; q++) - if (*q != '.') - *p++ = *q; - else - i++; - *p = '\0'; - if (i) - for (tp = TimezoneTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval->Number = tp->value; - return tp->type; - } - - return tID; -} - -static int -yylex (YYSTYPE *yylval, void *cookie) -{ - register unsigned char c; - register char *p; - char buff[20]; - int Count; - int sign; - - for (;;) - { - while (ISSPACE ((unsigned char) *context->yyInput)) - context->yyInput++; - - if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+') - { - if (c == '-' || c == '+') - { - sign = c == '-' ? -1 : 1; - if (!ISDIGIT (*++context->yyInput)) - /* skip the '-' sign */ - continue; - } - else - sign = 0; - for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);) - yylval->Number = 10 * yylval->Number + c - '0'; - context->yyInput--; - if (sign < 0) - yylval->Number = -yylval->Number; - return sign ? tSNUMBER : tUNUMBER; - } - if (ISALPHA (c)) - { - for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';) - if (p < &buff[sizeof buff - 1]) - *p++ = c; - *p = '\0'; - context->yyInput--; - return LookupWord (yylval, buff); - } - if (c != '(') - return *context->yyInput++; - Count = 0; - do - { - c = *context->yyInput++; - if (c == '\0') - return c; - if (c == '(') - Count++; - else if (c == ')') - Count--; - } - while (Count > 0); - } -} - -#define TM_YEAR_ORIGIN 1900 - -/* Yield A - B, measured in seconds. */ -static long -difftm (struct tm *a, struct tm *b) -{ - int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); - int by = b->tm_year + (TM_YEAR_ORIGIN - 1); - long days = ( - /* difference in day of year */ - a->tm_yday - b->tm_yday - /* + intervening leap days */ - + ((ay >> 2) - (by >> 2)) - - (ay / 100 - by / 100) - + ((ay / 100 >> 2) - (by / 100 >> 2)) - /* + difference in years * 365 */ - + (long) (ay - by) * 365 - ); - return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) - + (a->tm_min - b->tm_min)) - + (a->tm_sec - b->tm_sec)); -} - -time_t -curl_getdate (const char *p, const time_t *now) -{ - struct tm tm, tm0, *tmp; - time_t Start; - CURL_CONTEXT cookie; -#ifdef HAVE_LOCALTIME_R - struct tm keeptime; -#endif - cookie.yyInput = p; - Start = now ? *now : time ((time_t *) NULL); -#ifdef HAVE_LOCALTIME_R - tmp = (struct tm *)localtime_r(&Start, &keeptime); -#else - tmp = localtime (&Start); -#endif - if (!tmp) - return -1; - cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN; - cookie.yyMonth = tmp->tm_mon + 1; - cookie.yyDay = tmp->tm_mday; - cookie.yyHour = tmp->tm_hour; - cookie.yyMinutes = tmp->tm_min; - cookie.yySeconds = tmp->tm_sec; - tm.tm_isdst = tmp->tm_isdst; - cookie.yyMeridian = MER24; - cookie.yyRelSeconds = 0; - cookie.yyRelMinutes = 0; - cookie.yyRelHour = 0; - cookie.yyRelDay = 0; - cookie.yyRelMonth = 0; - cookie.yyRelYear = 0; - cookie.yyHaveDate = 0; - cookie.yyHaveDay = 0; - cookie.yyHaveRel = 0; - cookie.yyHaveTime = 0; - cookie.yyHaveZone = 0; - - if (yyparse ((void*)&cookie) - || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 || - cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1) - return -1; - - tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear; - tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth; - tm.tm_mday = cookie.yyDay + cookie.yyRelDay; - if (cookie.yyHaveTime || - (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay)) - { - tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian); - if (tm.tm_hour < 0) - return -1; - tm.tm_min = cookie.yyMinutes; - tm.tm_sec = cookie.yySeconds; - } - else - { - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - } - tm.tm_hour += cookie.yyRelHour; - tm.tm_min += cookie.yyRelMinutes; - tm.tm_sec += cookie.yyRelSeconds; - - /* Let mktime deduce tm_isdst if we have an absolute timestamp, - or if the relative timestamp mentions days, months, or years. */ - if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime | - cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear) - tm.tm_isdst = -1; - - tm0 = tm; - - Start = mktime (&tm); - - if (Start == (time_t) -1) - { - - /* Guard against falsely reporting errors near the time_t boundaries - when parsing times in other time zones. For example, if the min - time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead - of UTC, then the min localtime value is 1970-01-01 08:00:00; if - we apply mktime to 1970-01-01 00:00:00 we will get an error, so - we apply mktime to 1970-01-02 08:00:00 instead and adjust the time - zone by 24 hours to compensate. This algorithm assumes that - there is no DST transition within a day of the time_t boundaries. */ - if (cookie.yyHaveZone) - { - tm = tm0; - if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) - { - tm.tm_mday++; - cookie.yyTimezone -= 24 * 60; - } - else - { - tm.tm_mday--; - cookie.yyTimezone += 24 * 60; - } - Start = mktime (&tm); - } - - if (Start == (time_t) -1) - return Start; - } - - if (cookie.yyHaveDay && !cookie.yyHaveDate) - { - tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7 - + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal))); - Start = mktime (&tm); - if (Start == (time_t) -1) - return Start; - } - - if (cookie.yyHaveZone) - { - long delta; - struct tm *gmt; -#ifdef HAVE_GMTIME_R - /* thread-safe version */ - struct tm keeptime2; - gmt = (struct tm *)gmtime_r(&Start, &keeptime2); -#else - gmt = gmtime(&Start); -#endif - if (!gmt) - return -1; - delta = cookie.yyTimezone * 60L + difftm (&tm, gmt); - if ((Start + delta < Start) != (delta < 0)) - return -1; /* time_t overflow */ - Start += delta; - } - - return Start; -} - - diff --git a/Utilities/cmcurl/getdate.h b/Utilities/cmcurl/getdate.h deleted file mode 100644 index 85650e3..0000000 --- a/Utilities/cmcurl/getdate.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -** Originally written by Steven M. Bellovin <smb@research.att.com> while -** at the University of North Carolina at Chapel Hill. Later tweaked by -** a couple of people on Usenet. Completely overhauled by Rich $alz -** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. -** -** This code is in the public domain and has no copyright. -*/ - -# include "setup.h" - -#ifndef PARAMS -# if defined PROTOTYPES || (defined __STDC__ && __STDC__) -# define PARAMS(Args) Args -# else -# define PARAMS(Args) () -# endif -#endif - -#ifdef vms -# include <types.h> -# include <time.h> -#else -# include <sys/types.h> -# if TIME_WITH_SYS_TIME -# include <sys/time.h> -# include <time.h> -# else -# if HAVE_SYS_TIME_H -# include <sys/time.h> -# else -# include <time.h> -# endif -# endif -#endif /* defined (vms) */ - -time_t curl_getdate PARAMS ((const char *p, const time_t *now)); diff --git a/Utilities/cmcurl/getenv.c b/Utilities/cmcurl/getenv.c index 302db2e..4f955f8 100644 --- a/Utilities/cmcurl/getenv.c +++ b/Utilities/cmcurl/getenv.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -27,24 +27,22 @@ #include <stdlib.h> #include <string.h> -#ifdef WIN32 -#include <windows.h> -#endif - #ifdef VMS #include <unixlib.h> #endif #include <curl/curl.h> -#include "curl_memory.h" +#include "memory.h" #include "memdebug.h" static char *GetEnv(const char *variable) { +#ifdef _WIN32_WCE + return NULL; +#else #ifdef WIN32 - /* This shit requires windows.h (HUGE) to be included */ char env[MAX_PATH]; /* MAX_PATH is from windef.h */ char *temp = getenv(variable); env[0] = '\0'; @@ -62,6 +60,7 @@ char *GetEnv(const char *variable) #endif #endif return (env && env[0])?strdup(env):NULL; +#endif } char *curl_getenv(const char *v) diff --git a/Utilities/cmcurl/getinfo.c b/Utilities/cmcurl/getinfo.c index 7316d3a..5cf3bca 100644 --- a/Utilities/cmcurl/getinfo.c +++ b/Utilities/cmcurl/getinfo.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -32,13 +32,14 @@ #include <string.h> #include <stdarg.h> #include <stdlib.h> -#include "curl_memory.h" +#include "memory.h" +#include "sslgen.h" /* Make this the last #include */ #include "memdebug.h" /* - * This is supposed to be called in the beginning of a permform() session + * This is supposed to be called in the beginning of a perform() session * and should reset all session-info variables */ CURLcode Curl_initinfo(struct SessionHandle *data) @@ -56,13 +57,14 @@ CURLcode Curl_initinfo(struct SessionHandle *data) info->httpcode = 0; info->httpversion=0; info->filetime=-1; /* -1 is an illegal time and thus means unknown */ - + if (info->contenttype) free(info->contenttype); info->contenttype = NULL; info->header_size = 0; info->request_size = 0; + info->numconnects = 0; return CURLE_OK; } @@ -72,13 +74,19 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) long *param_longp=NULL; double *param_doublep=NULL; char **param_charp=NULL; + struct curl_slist **param_slistp=NULL; + char buf; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + va_start(arg, info); switch(info&CURLINFO_TYPEMASK) { default: return CURLE_BAD_FUNCTION_ARGUMENT; case CURLINFO_STRING: - param_charp = va_arg(arg, char **); + param_charp = va_arg(arg, char **); if(NULL == param_charp) return CURLE_BAD_FUNCTION_ARGUMENT; break; @@ -92,8 +100,13 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) if(NULL == param_doublep) return CURLE_BAD_FUNCTION_ARGUMENT; break; + case CURLINFO_SLIST: + param_slistp = va_arg(arg, struct curl_slist **); + if(NULL == param_slistp) + return CURLE_BAD_FUNCTION_ARGUMENT; + break; } - + switch(info) { case CURLINFO_EFFECTIVE_URL: *param_charp = data->change.url?data->change.url:(char *)""; @@ -159,7 +172,7 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) *param_charp = data->info.contenttype; break; case CURLINFO_PRIVATE: - *param_charp = data->set.private; + *param_charp = data->set.private_data; break; case CURLINFO_HTTPAUTH_AVAIL: *param_longp = data->info.httpauthavail; @@ -167,6 +180,53 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) case CURLINFO_PROXYAUTH_AVAIL: *param_longp = data->info.proxyauthavail; break; + case CURLINFO_OS_ERRNO: + *param_longp = data->state.os_errno; + break; + case CURLINFO_NUM_CONNECTS: + *param_longp = data->info.numconnects; + break; + case CURLINFO_SSL_ENGINES: + *param_slistp = Curl_ssl_engines_list(data); + break; + case CURLINFO_COOKIELIST: + *param_slistp = Curl_cookie_list(data); + break; + case CURLINFO_FTP_ENTRY_PATH: + /* Return the entrypath string from the most recent connection. + This pointer was copied from the connectdata structure by FTP. + The actual string may be free()ed by subsequent libcurl calls so + it must be copied to a safer area before the next libcurl call. + Callers must never free it themselves. */ + *param_charp = data->state.most_recent_ftp_entrypath; + break; + case CURLINFO_LASTSOCKET: + if((data->state.lastconnect != -1) && + (data->state.connc->connects[data->state.lastconnect] != NULL)) { + struct connectdata *c = data->state.connc->connects + [data->state.lastconnect]; + *param_longp = c->sock[FIRSTSOCKET]; + /* we have a socket connected, let's determine if the server shut down */ + /* determine if ssl */ + if(c->ssl[FIRSTSOCKET].use) { + /* use the SSL context */ + if (!Curl_ssl_check_cxn(c)) + *param_longp = -1; /* FIN received */ + } +/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ +#ifdef MSG_PEEK + else { + /* use the socket */ + if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, + (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { + *param_longp = -1; /* FIN received */ + } + } +#endif + } + else + *param_longp = -1; + break; default: return CURLE_BAD_FUNCTION_ARGUMENT; } diff --git a/Utilities/cmcurl/gtls.c b/Utilities/cmcurl/gtls.c new file mode 100644 index 0000000..250ecad --- /dev/null +++ b/Utilities/cmcurl/gtls.c @@ -0,0 +1,640 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* + * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + * + * Note: don't use the GnuTLS' *_t variable type names in this source code, + * since they were not present in 1.0.X. + */ + +#include "setup.h" +#ifdef USE_GNUTLS +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "urldata.h" +#include "sendf.h" +#include "gtls.h" +#include "sslgen.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> +#include "memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* Enable GnuTLS debugging by defining GTLSDEBUG */ +/*#define GTLSDEBUG */ + +#ifdef GTLSDEBUG +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "|<%d>| %s", level, str); +} +#endif + +/* + * Custom push and pull callback functions used by GNU TLS to read and write + * to the socket. These functions are simple wrappers to send() and recv() + * (although here using the sread/swrite macros as defined by setup_once.h). + * We use custom functions rather than the GNU TLS defaults because it allows + * us to get specific about the fourth "flags" argument, and to use arbitrary + * private data with gnutls_transport_set_ptr if we wish. + */ +static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len) +{ + return swrite(s, buf, len); +} + +static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len) +{ + return sread(s, buf, len); +} + +/* Global GnuTLS init, called from Curl_ssl_init() */ +int Curl_gtls_init(void) +{ + gnutls_global_init(); +#ifdef GTLSDEBUG + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(2); +#endif + return 1; +} + +int Curl_gtls_cleanup(void) +{ + gnutls_global_deinit(); + return 1; +} + +static void showtime(struct SessionHandle *data, + const char *text, + time_t stamp) +{ + struct tm *tm; +#ifdef HAVE_GMTIME_R + struct tm buffer; + tm = (struct tm *)gmtime_r(&stamp, &buffer); +#else + tm = gmtime(&stamp); +#endif + snprintf(data->state.buffer, + BUFSIZE, + "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n", + text, + 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); + infof(data, "%s", data->state.buffer); +} + +/* this function does a BLOCKING SSL/TLS (re-)handshake */ +static CURLcode handshake(struct connectdata *conn, + gnutls_session session, + int sockindex, + bool duringconnect) +{ + struct SessionHandle *data = conn->data; + int rc; + + do { + rc = gnutls_handshake(session); + + if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { + long timeout_ms = DEFAULT_CONNECT_TIMEOUT; + long has_passed; + + if(duringconnect && data->set.connecttimeout) + timeout_ms = data->set.connecttimeout*1000; + + if(data->set.timeout) { + /* get the strictest timeout of the ones converted to milliseconds */ + if((data->set.timeout*1000) < timeout_ms) + timeout_ms = data->set.timeout*1000; + } + + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + + /* subtract the passed time */ + timeout_ms -= has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + + rc = Curl_select(conn->sock[sockindex], + conn->sock[sockindex], (int)timeout_ms); + if(rc > 0) + /* reabable or writable, go loop*/ + continue; + else if(0 == rc) { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + else { + /* anything that gets here is fatally bad */ + failf(data, "select on SSL socket, errno: %d", Curl_sockerrno()); + return CURLE_SSL_CONNECT_ERROR; + } + } + else + break; + } while(1); + + if (rc < 0) { + failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + + return CURLE_OK; +} + +static gnutls_x509_crt_fmt do_file_type(const char *type) +{ + if(!type || !type[0]) + return GNUTLS_X509_FMT_PEM; + if(curl_strequal(type, "PEM")) + return GNUTLS_X509_FMT_PEM; + if(curl_strequal(type, "DER")) + return GNUTLS_X509_FMT_DER; + return -1; +} + + +/* + * This function is called after the TCP connect has completed. Setup the TLS + * layer and do all necessary magic. + */ +CURLcode +Curl_gtls_connect(struct connectdata *conn, + int sockindex) + +{ + 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; + char certbuf[256]; /* big enough? */ + size_t size; + unsigned int algo; + unsigned int bits; + time_t clock; + const char *ptr; + void *ssl_sessionid; + size_t ssl_idsize; + + /* GnuTLS only supports TLSv1 (and SSLv3?) */ + 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); + } + + /* 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; + + /* Use default priorities */ + rc = gnutls_set_default_priority(session); + 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.cert) { + if( gnutls_certificate_set_x509_key_file( + conn->ssl[sockindex].cred, data->set.cert, + data->set.key != 0 ? data->set.key : data->set.cert, + do_file_type(data->set.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.verifyhost) { + failf(data, "failed to get server cert"); + return CURLE_SSL_PEER_CERTIFICATE; + } + infof(data, "\t common name: WARNING couldn't obtain\n"); + } + + /* 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", + data->set.ssl.CAfile?data->set.ssl.CAfile:"none"); + return CURLE_SSL_CACERT; + } + else + infof(data, "\t server certificate verification FAILED\n"); + } + else + infof(data, "\t server certificate verification OK\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); + + 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_SSL_PEER_CERTIFICATE; + } + 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); + + /* 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); + + clock = gnutls_x509_crt_get_activation_time(x509_cert); + showtime(data, "start date", clock); + + clock = gnutls_x509_crt_get_expiration_time(x509_cert); + showtime(data, "expire date", clock); + + 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); + + 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; +} + + +/* return number of sent (non-SSL) bytes */ +ssize_t Curl_gtls_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ + ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len); + + if(rc < 0 ) { + if(rc == GNUTLS_E_AGAIN) + return 0; /* EWOULDBLOCK equivalent */ + rc = -1; /* generic error code for send failure */ + } + + return rc; +} + +void Curl_gtls_close_all(struct SessionHandle *data) +{ + /* FIX: make the OpenSSL code more generic and use parts of it here */ + (void)data; +} + +static void close_one(struct connectdata *conn, + int index) +{ + if(conn->ssl[index].session) { + gnutls_bye(conn->ssl[index].session, GNUTLS_SHUT_RDWR); + gnutls_deinit(conn->ssl[index].session); + } + gnutls_certificate_free_credentials(conn->ssl[index].cred); +} + +void Curl_gtls_close(struct connectdata *conn) +{ + if(conn->ssl[0].use) + close_one(conn, 0); + if(conn->ssl[1].use) + close_one(conn, 1); +} + +/* + * This function is called to shut down the SSL layer but keep the + * socket open (CCC - Clear Command Channel) + */ +int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) +{ + int result; + int retval = 0; + struct SessionHandle *data = conn->data; + int done = 0; + ssize_t nread; + char buf[120]; + + /* This has only been tested on the proftpd server, and the mod_tls code + sends a close notify alert without waiting for a close notify alert in + response. Thus we wait for a close notify alert from the server, but + we do not send one. Let's hope other servers do the same... */ + + if(conn->ssl[sockindex].session) { + while(!done) { + int what = Curl_select(conn->sock[sockindex], + CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); + if(what > 0) { + /* Something to read, let's do it and hope that it is the close + notify alert from the server */ + result = gnutls_record_recv(conn->ssl[sockindex].session, + buf, sizeof(buf)); + switch(result) { + case 0: + /* This is the expected response. There was no data but only + the close notify alert */ + done = 1; + break; + case GNUTLS_E_AGAIN: + case GNUTLS_E_INTERRUPTED: + infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n"); + break; + default: + retval = -1; + done = 1; + break; + } + } + else if(0 == what) { + /* timeout */ + failf(data, "SSL shutdown timeout"); + done = 1; + break; + } + else { + /* anything that gets here is fatally bad */ + failf(data, "select on SSL socket, errno: %d", Curl_sockerrno()); + retval = -1; + done = 1; + } + } + gnutls_deinit(conn->ssl[sockindex].session); + } + gnutls_certificate_free_credentials(conn->ssl[sockindex].cred); + + conn->ssl[sockindex].session = NULL; + conn->ssl[sockindex].use = FALSE; + + return retval; +} + +/* + * If the read would block we return -1 and set 'wouldblock' to TRUE. + * Otherwise we return the amount of data read. Other errors should return -1 + * and set 'wouldblock' to FALSE. + */ +ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock) +{ + ssize_t ret; + + ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize); + if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { + *wouldblock = TRUE; + return -1; + } + + if(ret == GNUTLS_E_REHANDSHAKE) { + /* BLOCKING call, this is bad but a work-around for now. Fixing this "the + proper way" takes a whole lot of work. */ + CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE); + if(rc) + /* handshake() writes error message on its own */ + return rc; + *wouldblock = TRUE; /* then return as if this was a wouldblock */ + return -1; + } + + *wouldblock = FALSE; + if (!ret) { + failf(conn->data, "Peer closed the TLS connection"); + return -1; + } + + if (ret < 0) { + failf(conn->data, "GnuTLS recv error (%d): %s", + (int)ret, gnutls_strerror(ret)); + return -1; + } + + return ret; +} + +void Curl_gtls_session_free(void *ptr) +{ + free(ptr); +} + +size_t Curl_gtls_version(char *buffer, size_t size) +{ + return snprintf(buffer, size, " GnuTLS/%s", gnutls_check_version(NULL)); +} + +#endif /* USE_GNUTLS */ diff --git a/Utilities/cmcurl/gtls.h b/Utilities/cmcurl/gtls.h new file mode 100644 index 0000000..bff3f86 --- /dev/null +++ b/Utilities/cmcurl/gtls.h @@ -0,0 +1,46 @@ +#ifndef __GTLS_H +#define __GTLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ +int Curl_gtls_init(void); +int Curl_gtls_cleanup(void); +CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex); + +/* tell GnuTLS to close down all open information regarding connections (and + thus session ID caching etc) */ +void Curl_gtls_close_all(struct SessionHandle *data); +void Curl_gtls_close(struct connectdata *conn); /* close a SSL connection */ + +/* return number of sent (non-SSL) bytes */ +ssize_t Curl_gtls_send(struct connectdata *conn, int sockindex, + void *mem, size_t len); +ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock); +void Curl_gtls_session_free(void *ptr); +size_t Curl_gtls_version(char *buffer, size_t size); +int Curl_gtls_shutdown(struct connectdata *conn, int sockindex); + +#endif diff --git a/Utilities/cmcurl/hash.c b/Utilities/cmcurl/hash.c index 614d692..e004627 100644 --- a/Utilities/cmcurl/hash.c +++ b/Utilities/cmcurl/hash.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,7 +28,7 @@ #include "hash.h" #include "llist.h" -#include "curl_memory.h" +#include "memory.h" /* this must be the last include file */ #include "memdebug.h" @@ -50,12 +50,11 @@ hash_str(const char *key, size_t key_length) static void hash_element_dtor(void *user, void *element) { - curl_hash *h = (curl_hash *) user; - curl_hash_element *e = (curl_hash_element *) element; + struct curl_hash *h = (struct curl_hash *) user; + struct curl_hash_element *e = (struct curl_hash_element *) element; - if (e->key) { + if (e->key) free(e->key); - } h->dtor(e->ptr); @@ -64,7 +63,7 @@ hash_element_dtor(void *user, void *element) /* return 1 on error, 0 is fine */ int -Curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor) +Curl_hash_init(struct curl_hash *h, int slots, curl_hash_dtor dtor) { int i; @@ -72,7 +71,7 @@ Curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor) h->size = 0; h->slots = slots; - h->table = (curl_llist **) malloc(slots * sizeof(curl_llist *)); + h->table = (struct curl_llist **) malloc(slots * sizeof(struct curl_llist *)); if(h->table) { for (i = 0; i < slots; ++i) { h->table[i] = Curl_llist_alloc((curl_llist_dtor) hash_element_dtor); @@ -89,12 +88,12 @@ Curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor) return 1; /* failure */ } -curl_hash * +struct curl_hash * Curl_hash_alloc(int slots, curl_hash_dtor dtor) { - curl_hash *h; + struct curl_hash *h; - h = (curl_hash *) malloc(sizeof(curl_hash)); + h = (struct curl_hash *) malloc(sizeof(struct curl_hash)); if (h) { if(Curl_hash_init(h, slots, dtor)) { /* failure */ @@ -118,15 +117,18 @@ hash_key_compare(char *key1, size_t key1_len, char *key2, size_t key2_len) return 0; } -static curl_hash_element * +static struct curl_hash_element * mk_hash_element(char *key, size_t key_len, const void *p) { - curl_hash_element *he = - (curl_hash_element *) malloc(sizeof(curl_hash_element)); + struct curl_hash_element *he = + (struct curl_hash_element *) malloc(sizeof(struct curl_hash_element)); if(he) { - char *dup = strdup(key); + char *dup = malloc(key_len); if(dup) { + /* copy the key */ + memcpy(dup, key, key_len); + he->key = dup; he->key_len = key_len; he->ptr = (void *) p; @@ -147,14 +149,14 @@ mk_hash_element(char *key, size_t key_len, const void *p) /* Return the data in the hash. If there already was a match in the hash, that data is returned. */ void * -Curl_hash_add(curl_hash *h, char *key, size_t key_len, void *p) +Curl_hash_add(struct curl_hash *h, char *key, size_t key_len, void *p) { - curl_hash_element *he; - curl_llist_element *le; - curl_llist *l = FETCH_LIST(h, key, key_len); + struct curl_hash_element *he; + struct curl_llist_element *le; + struct curl_llist *l = FETCH_LIST(h, key, key_len); for (le = l->head; le; le = le->next) { - he = (curl_hash_element *) le->ptr; + he = (struct curl_hash_element *) le->ptr; if (hash_key_compare(he->key, he->key_len, key, key_len)) { h->dtor(p); /* remove the NEW entry */ return he->ptr; /* return the EXISTING entry */ @@ -180,16 +182,31 @@ Curl_hash_add(curl_hash *h, char *key, size_t key_len, void *p) return NULL; /* failure */ } +/* remove the identified hash entry, returns non-zero on failure */ +int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len) +{ + struct curl_llist_element *le; + struct curl_hash_element *he; + struct curl_llist *l = FETCH_LIST(h, key, key_len); + + for (le = l->head; le; le = le->next) { + he = le->ptr; + if (hash_key_compare(he->key, he->key_len, key, key_len)) { + Curl_llist_remove(l, le, (void *) h); + return 0; + } + } + return 1; +} + void * -Curl_hash_pick(curl_hash *h, char *key, size_t key_len) +Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len) { - curl_llist_element *le; - curl_hash_element *he; - curl_llist *l = FETCH_LIST(h, key, key_len); + struct curl_llist_element *le; + struct curl_hash_element *he; + struct curl_llist *l = FETCH_LIST(h, key, key_len); - for (le = l->head; - le; - le = le->next) { + for (le = l->head; le; le = le->next) { he = le->ptr; if (hash_key_compare(he->key, he->key_len, key, key_len)) { return he->ptr; @@ -204,7 +221,7 @@ void Curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *user, void *ptr)) { - curl_llist_element *le; + struct curl_llist_element *le; int i; for (i = 0; i < h->slots; ++i) { @@ -219,7 +236,7 @@ Curl_hash_apply(curl_hash *h, void *user, #endif void -Curl_hash_clean(curl_hash *h) +Curl_hash_clean(struct curl_hash *h) { int i; @@ -231,19 +248,19 @@ Curl_hash_clean(curl_hash *h) } void -Curl_hash_clean_with_criterium(curl_hash *h, void *user, +Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, int (*comp)(void *, void *)) { - curl_llist_element *le; - curl_llist_element *lnext; - curl_llist *list; + struct curl_llist_element *le; + struct curl_llist_element *lnext; + struct curl_llist *list; int i; for (i = 0; i < h->slots; ++i) { list = h->table[i]; le = list->head; /* get first list entry */ while(le) { - curl_hash_element *he = le->ptr; + struct curl_hash_element *he = le->ptr; lnext = le->next; /* ask the callback function if we shall remove this entry or not */ if (comp(user, he->ptr)) { @@ -256,7 +273,7 @@ Curl_hash_clean_with_criterium(curl_hash *h, void *user, } void -Curl_hash_destroy(curl_hash *h) +Curl_hash_destroy(struct curl_hash *h) { if (!h) return; @@ -265,3 +282,34 @@ Curl_hash_destroy(curl_hash *h) free(h); } +#if 0 /* useful function for debugging hashes and their contents */ +void Curl_hash_print(struct curl_hash *h, + void (*func)(void *)) +{ + int i; + struct curl_llist_element *le; + struct curl_llist *list; + struct curl_hash_element *he; + if (!h) + return; + + fprintf(stderr, "=Hash dump=\n"); + + for (i = 0; i < h->slots; i++) { + list = h->table[i]; + le = list->head; /* get first list entry */ + if(le) { + fprintf(stderr, "index %d:", i); + while(le) { + he = le->ptr; + if(func) + func(he->ptr); + else + fprintf(stderr, " [%p]", he->ptr); + le = le->next; + } + fprintf(stderr, "\n"); + } + } +} +#endif diff --git a/Utilities/cmcurl/hash.h b/Utilities/cmcurl/hash.h index 7814674..ceebb52 100644 --- a/Utilities/cmcurl/hash.h +++ b/Utilities/cmcurl/hash.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,30 +31,31 @@ typedef void (*curl_hash_dtor)(void *); -typedef struct _curl_hash { - curl_llist **table; +struct curl_hash { + struct curl_llist **table; curl_hash_dtor dtor; - int slots; - size_t size; -} curl_hash; + int slots; + size_t size; +}; -typedef struct _curl_hash_element { +struct curl_hash_element { void *ptr; char *key; size_t key_len; -} curl_hash_element; +}; -int Curl_hash_init(curl_hash *, int, curl_hash_dtor); -curl_hash *Curl_hash_alloc(int, curl_hash_dtor); -void *Curl_hash_add(curl_hash *, char *, size_t, void *); -int Curl_hash_delete(curl_hash *h, char *key, size_t key_len); -void *Curl_hash_pick(curl_hash *, char *, size_t); -void Curl_hash_apply(curl_hash *h, void *user, +int Curl_hash_init(struct curl_hash *, int, curl_hash_dtor); +struct curl_hash *Curl_hash_alloc(int, curl_hash_dtor); +void *Curl_hash_add(struct curl_hash *, char *, size_t, void *); +int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len); +void *Curl_hash_pick(struct curl_hash *, char *, size_t); +void Curl_hash_apply(struct curl_hash *h, void *user, void (*cb)(void *user, void *ptr)); -int Curl_hash_count(curl_hash *h); -void Curl_hash_clean(curl_hash *h); -void Curl_hash_clean_with_criterium(curl_hash *h, void *user, int (*comp)(void *, void *)); -void Curl_hash_destroy(curl_hash *h); +int Curl_hash_count(struct curl_hash *h); +void Curl_hash_clean(struct curl_hash *h); +void Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, + int (*comp)(void *, void *)); +void Curl_hash_destroy(struct curl_hash *h); #endif diff --git a/Utilities/cmcurl/hostares.c b/Utilities/cmcurl/hostares.c index 197f540..1db0f43 100644 --- a/Utilities/cmcurl/hostares.c +++ b/Utilities/cmcurl/hostares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,13 +54,12 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif @@ -79,6 +75,8 @@ #include "share.h" #include "strerror.h" #include "url.h" +#include "multiif.h" +#include "connect.h" /* for the Curl_sockerrno() proto */ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -87,7 +85,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -99,7 +97,7 @@ #ifdef CURLRES_ARES /* - * Curl_fdset() is called when someone from the outside world (using + * Curl_resolv_fdset() is called when someone from the outside world (using * curl_multi_fdset()) wants to get our fd_set setup and we're talking with * ares. The caller must make sure that this function is only called when we * have a working ares channel. @@ -107,17 +105,26 @@ * Returns: CURLE_OK always! */ -CURLcode Curl_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - int *max_fdp) +int Curl_resolv_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) { - int max = ares_fds(conn->data->state.areschannel, - read_fd_set, write_fd_set); - *max_fdp = max; + struct timeval maxtime; + struct timeval timeout; + int max = ares_getsock(conn->data->state.areschannel, + (int *)socks, numsocks); - return CURLE_OK; + + maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; + maxtime.tv_usec = 0; + + ares_timeout(conn->data->state.areschannel, &maxtime, &timeout); + + Curl_expire(conn->data, + (timeout.tv_sec * 1000) + (timeout.tv_usec/1000) ); + + return max; } /* @@ -213,7 +220,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, break; tvp = ares_timeout(data->state.areschannel, &store, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); - if (count < 0 && errno != EINVAL) + if (count < 0 && Curl_sockerrno() != EINVAL) break; ares_process(data->state.areschannel, &read_fds, &write_fds); @@ -249,7 +256,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, /* close the connection, since we can't return failure here without cleaning up this connection properly */ - Curl_disconnect(conn); + conn->bits.close = TRUE; } return rc; @@ -264,7 +271,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, * Curl_freeaddrinfo(), nothing else. */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp) { @@ -291,11 +298,10 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, /* areschannel is already setup in the Curl_open() function */ ares_gethostbyname(data->state.areschannel, hostname, PF_INET, - Curl_addrinfo4_callback, conn); + (ares_host_callback)Curl_addrinfo4_callback, conn); *waitp = TRUE; /* please wait for the response */ } return NULL; /* no struct yet */ } - #endif /* CURLRES_ARES */ diff --git a/Utilities/cmcurl/hostasyn.c b/Utilities/cmcurl/hostasyn.c index b3c9dfa..3df1479 100644 --- a/Utilities/cmcurl/hostasyn.c +++ b/Utilities/cmcurl/hostasyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +54,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -87,7 +78,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -108,21 +99,21 @@ * * The storage operation locks and unlocks the DNS cache. */ -static void addrinfo_callback(void *arg, /* "struct connectdata *" */ - int status, - void *addr) +static CURLcode addrinfo_callback(void *arg, /* "struct connectdata *" */ + int status, + void *addr) { struct connectdata *conn = (struct connectdata *)arg; struct Curl_dns_entry *dns = NULL; + CURLcode rc = CURLE_OK; - conn->async.done = TRUE; conn->async.status = status; if(CURL_ASYNC_SUCCESS == status) { /* - * IPv4: Curl_addrinfo_copy() copies the address and returns an allocated - * version. + * IPv4/ares: Curl_addrinfo_copy() copies the address and returns an + * allocated version. * * IPv6: Curl_addrinfo_copy() returns the input pointer! */ @@ -136,34 +127,47 @@ static void addrinfo_callback(void *arg, /* "struct connectdata *" */ dns = Curl_cache_addr(data, ai, conn->async.hostname, conn->async.port); - if(!dns) + if(!dns) { /* failed to store, cleanup and return error */ Curl_freeaddrinfo(ai); + rc = CURLE_OUT_OF_MEMORY; + } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } + else + rc = CURLE_OUT_OF_MEMORY; } conn->async.dns = dns; + /* Set async.done TRUE last in this function since it may be used multi- + threaded and once this is TRUE the other thread may read fields from the + async struct */ + conn->async.done = TRUE; + /* ipv4: The input hostent struct will be freed by ares when we return from this function */ + return rc; } -void Curl_addrinfo4_callback(void *arg, /* "struct connectdata *" */ - int status, - struct hostent *hostent) +CURLcode Curl_addrinfo4_callback(void *arg, /* "struct connectdata *" */ + int status, + struct hostent *hostent) { - addrinfo_callback(arg, status, hostent); + return addrinfo_callback(arg, status, hostent); } #ifdef CURLRES_IPV6 -void Curl_addrinfo6_callback(void *arg, /* "struct connectdata *" */ - int status, - struct addrinfo *ai) +CURLcode Curl_addrinfo6_callback(void *arg, /* "struct connectdata *" */ + int status, + struct addrinfo *ai) { - addrinfo_callback(arg, status, ai); + /* NOTE: for CURLRES_ARES, the 'ai' argument is really a + * 'struct hostent' pointer. + */ + return addrinfo_callback(arg, status, ai); } #endif diff --git a/Utilities/cmcurl/hostip.c b/Utilities/cmcurl/hostip.c index 43ade26..fd555ef 100644 --- a/Utilities/cmcurl/hostip.c +++ b/Utilities/cmcurl/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +54,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -88,7 +79,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -105,8 +96,7 @@ * defined. * * CURLRES_ARES - is defined if libcurl is built to use c-ares for - * asynchronous name resolves. It cannot have ENABLE_IPV6 defined at the same - * time, as c-ares has no ipv6 support. This can be Windows or *nix. + * asynchronous name resolves. This can be Windows or *nix. * * CURLRES_THREADED - is defined if libcurl is built to run under (native) * Windows, and then the name resolve will be done in a new thread, and the @@ -131,7 +121,7 @@ */ /* These two symbols are for the global DNS cache */ -static curl_hash hostname_cache; +static struct curl_hash hostname_cache; static int host_cache_initialized; static void freednsentry(void *freethis); @@ -152,7 +142,7 @@ void Curl_global_host_cache_init(void) /* * Return a pointer to the global cache */ -curl_hash *Curl_global_host_cache_get(void) +struct curl_hash *Curl_global_host_cache_get(void) { return &hostname_cache; } @@ -174,20 +164,11 @@ void Curl_global_host_cache_dtor(void) int Curl_num_addresses(const Curl_addrinfo *addr) { int i; - for (i = 0; addr; addr = addr->ai_next, i++); + for (i = 0; addr; addr = addr->ai_next, i++) + ; /* empty loop */ return i; } -#define GET_SIN_ADDR_FROM_CURL_ADDRINFO(ai_addr, si, sin, sinaddr, ip) \ - { \ - union { \ - struct si* vsi; \ - struct sin* vsin;\ - } vi; \ - vi.vsi = ai_addr; \ - ip = &(vi.vsin->sinaddr); \ - } - /* * Curl_printable_address() returns a printable version of the 1st address * given in the 'ip' argument. The result will be stored in the buf that is @@ -198,17 +179,13 @@ int Curl_num_addresses(const Curl_addrinfo *addr) const char *Curl_printable_address(const Curl_addrinfo *ip, char *buf, size_t bufsize) { + const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr; int af = ip->ai_family; - const void *ip4; #ifdef CURLRES_IPV6 - const void *ip6; - GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in6, - sin6_addr, ip6); + const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr; #else const void *ip6 = NULL; #endif - GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in, - sin_addr, ip4); return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize); } @@ -218,7 +195,7 @@ const char *Curl_printable_address(const Curl_addrinfo *ip, * the DNS caching. */ static char * -create_hostcache_id(char *server, int port) +create_hostcache_id(const char *server, int port) { /* create and return the new allocated entry */ return aprintf("%s:%d", server, port); @@ -257,7 +234,7 @@ hostcache_timestamp_remove(void *datap, void *hc) * Prune the DNS cache. This assumes that a lock has already been taken. */ static void -hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now) +hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now) { struct hostcache_prune_data user; @@ -277,8 +254,9 @@ void Curl_hostcache_prune(struct SessionHandle *data) { time_t now; - if(data->set.dns_cache_timeout == -1) - /* cache forever means never prune! */ + if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache) + /* cache forever means never prune, and NULL hostcache means + we can't do it */ return; if(data->share) @@ -287,7 +265,7 @@ void Curl_hostcache_prune(struct SessionHandle *data) time(&now); /* Remove outdated and unused entries from the hostcache */ - hostcache_prune(data->hostcache, + hostcache_prune(data->dns.hostcache, data->set.dns_cache_timeout, now); @@ -295,6 +273,39 @@ void Curl_hostcache_prune(struct SessionHandle *data) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } +static int +remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns) +{ + struct hostcache_prune_data user; + + if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache) + /* cache forever means never prune, and NULL hostcache means + we can't do it */ + return 0; + + time(&user.now); + user.cache_timeout = data->set.dns_cache_timeout; + + if ( !hostcache_timestamp_remove(&user,dns) ) + return 0; + + /* ok, we do need to clear the cache. although we need to remove just a + single entry we clean the entire hash, as no explicit delete function + is provided */ + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + Curl_hash_clean_with_criterium(data->dns.hostcache, + (void *) &user, + hostcache_timestamp_remove); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + return 1; +} + + #ifdef HAVE_SIGSETJMP /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This @@ -315,7 +326,7 @@ sigjmp_buf curl_jmpenv; struct Curl_dns_entry * Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr, - char *hostname, + const char *hostname, int port) { char *entry_id; @@ -332,7 +343,7 @@ Curl_cache_addr(struct SessionHandle *data, entry_len = strlen(entry_id); /* Create a new cache entry */ - dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); + dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1); if (!dns) { free(entry_id); return NULL; @@ -344,7 +355,8 @@ Curl_cache_addr(struct SessionHandle *data, /* Store the resolved data in our DNS cache. This function may return a pointer to an existing struct already present in the hash, and it may return the same argument we pass in. Make no assumptions. */ - dns2 = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns); + dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1, + (void *)dns); if(!dns2) { /* Major badness, run away. */ free(dns); @@ -381,31 +393,30 @@ Curl_cache_addr(struct SessionHandle *data, */ int Curl_resolv(struct connectdata *conn, - char *hostname, + const char *hostname, int port, struct Curl_dns_entry **entry) { - char *entry_id; + char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; int wait; struct SessionHandle *data = conn->data; CURLcode result; - - /* default to failure */ int rc; *entry = NULL; #ifdef HAVE_SIGSETJMP /* this allows us to time-out from the name resolver, as the timeout will generate a signal and we will siglongjmp() from that here */ - if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) { - /* this is coming from a siglongjmp() */ - failf(data, "name lookup timed out"); - return CURLRESOLV_ERROR; + if(!data->set.no_signal) { + if (sigsetjmp(curl_jmpenv, 1)) { + /* this is coming from a siglongjmp() */ + failf(data, "name lookup timed out"); + return CURLRESOLV_ERROR; + } } #endif - rc = CURLRESOLV_ERROR; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); @@ -419,7 +430,7 @@ int Curl_resolv(struct connectdata *conn, 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->hostcache, entry_id, entry_len+1); + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -427,6 +438,13 @@ int Curl_resolv(struct connectdata *conn, /* free the allocated entry_id again */ free(entry_id); + /* See whether the returned entry is stale. Deliberately done after the + locked block */ + if ( remove_entry_if_stale(data,dns) ) + dns = NULL; /* the memory deallocation is being handled by the hash */ + + rc = CURLRESOLV_ERROR; /* default to failure */ + if (!dns) { /* The entry was not in the cache. Resolve it to IP address */ @@ -474,7 +492,11 @@ int Curl_resolv(struct connectdata *conn, } } else { + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns->inuse++; /* we use it! */ + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); rc = CURLRESOLV_RESOLVED; } @@ -516,7 +538,7 @@ static void freednsentry(void *freethis) /* * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it. */ -curl_hash *Curl_mk_dnscache(void) +struct curl_hash *Curl_mk_dnscache(void) { return Curl_hash_alloc(7, freednsentry); } @@ -531,10 +553,84 @@ curl_hash *Curl_mk_dnscache(void) * returns a pointer to the malloc()ed copy. You need to call free() on the * returned buffer when you're done with it. */ -Curl_addrinfo *Curl_addrinfo_copy(void *org, int port) +Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port) { - struct hostent *orig = org; + const struct hostent *orig = org; return Curl_he2ai(orig, port); } #endif /* CURLRES_ADDRINFO_COPY */ + +/*********************************************************************** + * Only for plain-ipv4 and c-ares builds + **********************************************************************/ + +#if defined(CURLRES_IPV4) || defined(CURLRES_ARES) +/* + * This is a function for freeing name information in a protocol independent + * way. + */ +void Curl_freeaddrinfo(Curl_addrinfo *ai) +{ + Curl_addrinfo *next; + + /* walk over the list and free all entries */ + while(ai) { + next = ai->ai_next; + free(ai); + ai = next; + } +} + +struct namebuf { + struct hostent hostentry; + char *h_addr_list[2]; + struct in_addr addrentry; + char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */ +}; + +/* + * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter + * together with a pointer to the string version of the address, and it + * returns a Curl_addrinfo chain filled in correctly with information for this + * address/host. + * + * The input parameters ARE NOT checked for validity but they are expected + * to have been checked already when this is called. + */ +Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port) +{ + Curl_addrinfo *ai; + struct hostent *h; + struct in_addr *addrentry; + struct namebuf buffer; + struct namebuf *buf = &buffer; + + h = &buf->hostentry; + h->h_addr_list = &buf->h_addr_list[0]; + addrentry = &buf->addrentry; +#ifdef _CRAYC + /* On UNICOS, s_addr is a bit field and for some reason assigning to it + * doesn't work. There must be a better fix than this ugly hack. + */ + memcpy(addrentry, &num, SIZEOF_in_addr); +#else + addrentry->s_addr = num; +#endif + h->h_addr_list[0] = (char*)addrentry; + h->h_addr_list[1] = NULL; + h->h_addrtype = AF_INET; + h->h_length = sizeof(*addrentry); + h->h_name = &buf->h_name[0]; + h->h_aliases = NULL; + + /* Now store the dotted version of the address */ + snprintf((char *)h->h_name, 16, "%s", hostname); + + ai = Curl_he2ai(h, port); + + return ai; +} +#endif + + diff --git a/Utilities/cmcurl/hostip.h b/Utilities/cmcurl/hostip.h index 545fec0..e6d63ca 100644 --- a/Utilities/cmcurl/hostip.h +++ b/Utilities/cmcurl/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,8 +26,9 @@ #include "setup.h" #include "hash.h" -#ifdef HAVE_NETDB_H -#include <netdb.h> +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t uint32_t #endif /* @@ -55,14 +56,14 @@ #define CURLRES_IPV4 #endif -#ifdef CURLRES_IPV4 +#if defined(CURLRES_IPV4) || defined(CURLRES_ARES) #if !defined(HAVE_GETHOSTBYNAME_R) || defined(CURLRES_ASYNCH) /* If built for ipv4 and missing gethostbyname_r(), or if using async name resolve, we need the Curl_addrinfo_copy() function (which itself needs the - Curl_hostent_relocate() function)) */ + Curl_he2ai() function)) */ #define CURLRES_ADDRINFO_COPY #endif -#endif /* IPv4-only */ +#endif /* IPv4/ares-only */ #ifndef CURLRES_ASYNCH #define CURLRES_SYNCH @@ -86,6 +87,8 @@ #define CURL_ASYNC_SUCCESS ARES_SUCCESS #else #define CURL_ASYNC_SUCCESS CURLE_OK +#define ares_cancel(x) do {} while(0) +#define ares_destroy(x) do {} while(0) #endif /* @@ -97,25 +100,26 @@ typedef struct addrinfo Curl_addrinfo; /* OK, so some ipv4-only include tree probably have the addrinfo struct, but to work even on those that don't, we provide our own look-alike! */ struct Curl_addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - struct sockaddr *ai_addr; - char *ai_canonname; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; /* Follow rfc3493 struct addrinfo */ + char *ai_canonname; + struct sockaddr *ai_addr; struct Curl_addrinfo *ai_next; }; typedef struct Curl_addrinfo Curl_addrinfo; #endif struct addrinfo; +struct hostent; struct SessionHandle; struct connectdata; void Curl_global_host_cache_init(void); void Curl_global_host_cache_dtor(void); -curl_hash *Curl_global_host_cache_get(void); +struct curl_hash *Curl_global_host_cache_get(void); #define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache) @@ -137,7 +141,7 @@ struct Curl_dns_entry { #define CURLRESOLV_ERROR -1 #define CURLRESOLV_RESOLVED 0 #define CURLRESOLV_PENDING 1 -int Curl_resolv(struct connectdata *conn, char *hostname, +int Curl_resolv(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **dnsentry); /* @@ -153,7 +157,7 @@ bool Curl_ipvalid(struct SessionHandle *data); * of arguments */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp); @@ -162,15 +166,19 @@ CURLcode Curl_is_resolved(struct connectdata *conn, CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **dnsentry); -/* Curl_fdset() is a generic function that exists in multiple versions - depending on what name resolve technology we've built to use. The function - is called from the curl_multi_fdset() function */ -CURLcode Curl_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - int *max_fdp); +/* Curl_resolv_getsock() is a generic function that exists in multiple + versions depending on what name resolve technology we've built to use. The + function is called from the multi_getsock() function. 'sock' is a pointer + to an array to hold the file descriptors, with 'numsock' being the size of + that array (in number of entries). This function is supposed to return + bitmask indicating what file descriptors (referring to array indexes in the + 'sock' array) to wait for, read/write. */ +int Curl_resolv_getsock(struct connectdata *conn, curl_socket_t *sock, + int numsocks); + /* unlock a previously resolved dns entry */ -void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns); +void Curl_resolv_unlock(struct SessionHandle *data, + struct Curl_dns_entry *dns); /* for debugging purposes only: */ void Curl_scan_cache_used(void *user, void *ptr); @@ -179,7 +187,7 @@ void Curl_scan_cache_used(void *user, void *ptr); void Curl_freeaddrinfo(Curl_addrinfo *freeaddr); /* make a new dns cache and return the handle */ -curl_hash *Curl_mk_dnscache(void); +struct curl_hash *Curl_mk_dnscache(void); /* prune old entries from the DNS cache */ void Curl_hostcache_prune(struct SessionHandle *data); @@ -190,41 +198,42 @@ int Curl_num_addresses (const Curl_addrinfo *addr); #ifdef CURLDEBUG void curl_dofreeaddrinfo(struct addrinfo *freethis, int line, const char *source); -int curl_dogetaddrinfo(char *hostname, char *service, +int curl_dogetaddrinfo(const char *hostname, const char *service, struct addrinfo *hints, struct addrinfo **result, int line, const char *source); -int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, size_t hostlen, - char *serv, size_t servlen, int flags, +#ifdef HAVE_GETNAMEINFO +int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, + GETNAMEINFO_TYPE_ARG2 salen, + char *host, GETNAMEINFO_TYPE_ARG46 hostlen, + char *serv, GETNAMEINFO_TYPE_ARG46 servlen, + GETNAMEINFO_TYPE_ARG7 flags, int line, const char *source); #endif +#endif /* This is the callback function that is used when we build with asynch resolve, ipv4 */ -void Curl_addrinfo4_callback(void *arg, - int status, - struct hostent *hostent); +CURLcode Curl_addrinfo4_callback(void *arg, + int status, + struct hostent *hostent); /* This is the callback function that is used when we build with asynch resolve, ipv6 */ -void Curl_addrinfo6_callback(void *arg, - int status, - struct addrinfo *ai); +CURLcode Curl_addrinfo6_callback(void *arg, + int status, + struct addrinfo *ai); -/* [ipv4 only] Creates a Curl_addrinfo struct from a numerical-only IP +/* [ipv4/ares only] Creates a Curl_addrinfo struct from a numerical-only IP address */ -Curl_addrinfo *Curl_ip2addr(in_addr_t num, char *hostname, int port); +Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port); -/* [ipv4 only] Curl_he2ai() converts a struct hostent to a Curl_addrinfo chain +/* [ipv4/ares only] Curl_he2ai() converts a struct hostent to a Curl_addrinfo chain and returns it */ -Curl_addrinfo *Curl_he2ai(struct hostent *, int port); - -/* relocate a hostent struct */ -void Curl_hostent_relocate(struct hostent *h, long offset); +Curl_addrinfo *Curl_he2ai(const struct hostent *, int port); /* Clone a Curl_addrinfo struct, works protocol independently */ -Curl_addrinfo *Curl_addrinfo_copy(void *orig, int port); +Curl_addrinfo *Curl_addrinfo_copy(const void *orig, int port); /* * Curl_printable_address() returns a printable version of the 1st address @@ -241,7 +250,14 @@ const char *Curl_printable_address(const Curl_addrinfo *ip, */ struct Curl_dns_entry * Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr, - char *hostname, int port); + const char *hostname, int port); + +/* + * Curl_destroy_thread_data() cleans up async resolver data. + * Complementary of ares_destroy. + */ +struct Curl_async; /* forward-declaration */ +void Curl_destroy_thread_data(struct Curl_async *async); #ifndef INADDR_NONE #define CURL_INADDR_NONE (in_addr_t) ~0 diff --git a/Utilities/cmcurl/hostip4.c b/Utilities/cmcurl/hostip4.c index 1b4c3c1..877baa2 100644 --- a/Utilities/cmcurl/hostip4.c +++ b/Utilities/cmcurl/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,11 +26,9 @@ #include <string.h> #include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +55,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -79,6 +71,7 @@ #include "share.h" #include "strerror.h" #include "url.h" +#include "inet_pton.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -87,7 +80,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -95,23 +88,6 @@ * Only for plain-ipv4 builds **********************************************************************/ #ifdef CURLRES_IPV4 /* plain ipv4 code coming up */ - -/* - * This is a function for freeing name information in a protocol independent - * way. - */ -void Curl_freeaddrinfo(Curl_addrinfo *ai) -{ - Curl_addrinfo *next; - - /* walk over the list and free all entries */ - while(ai) { - next = ai->ai_next; - free(ai); - ai = next; - } -} - /* * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've * been set and returns TRUE if they are OK. @@ -125,57 +101,13 @@ bool Curl_ipvalid(struct SessionHandle *data) return TRUE; /* OK, proceed */ } -struct namebuf { - struct hostent hostentry; - char *h_addr_list[2]; - struct in_addr addrentry; - char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */ -}; - -/* - * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter - * together with a pointer to the string version of the address, and it - * returns a Curl_addrinfo chain filled in correctly with information for this - * address/host. - * - * The input parameters ARE NOT checked for validity but they are expected - * to have been checked already when this is called. - */ -Curl_addrinfo *Curl_ip2addr(in_addr_t num, char *hostname, int port) -{ - Curl_addrinfo *ai; - struct hostent *h; - struct in_addr *addrentry; - struct namebuf buffer; - struct namebuf *buf = &buffer; - - h = &buf->hostentry; - h->h_addr_list = &buf->h_addr_list[0]; - addrentry = &buf->addrentry; - addrentry->s_addr = num; - h->h_addr_list[0] = (char*)addrentry; - h->h_addr_list[1] = NULL; - h->h_addrtype = AF_INET; - h->h_length = sizeof(*addrentry); - h->h_name = &buf->h_name[0]; - h->h_aliases = NULL; - - /* Now store the dotted version of the address */ - snprintf((char*)(h->h_name), 16, "%s", hostname); - - ai = Curl_he2ai(h, port); - - return ai; -} - #ifdef CURLRES_SYNCH /* the functions below are for synchronous resolves */ /* * Curl_getaddrinfo() - the ipv4 synchronous version. * - * The original code to this function was once stolen from the Dancer source - * code, written by Bjorn Reese, it has since been patched and modified - * considerably. + * The original code to this function was from the Dancer source code, written + * by Bjorn Reese, it has since been patched and modified considerably. * * gethostbyname_r() is the thread-safe version of the gethostbyname() * function. When we build for plain IPv4, we attempt to use this @@ -188,7 +120,7 @@ Curl_addrinfo *Curl_ip2addr(in_addr_t num, char *hostname, int port) * */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp) { @@ -202,11 +134,9 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, *waitp = 0; /* don't wait, we act synchronously */ - in=inet_addr(hostname); - if (in != CURL_INADDR_NONE) { + if(1 == Curl_inet_pton(AF_INET, hostname, &in)) /* This is a dotted IP address 123.123.123.123-style */ return Curl_ip2addr(in, hostname, port); - } #if defined(HAVE_GETHOSTBYNAME_R) /* @@ -370,55 +300,55 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } #endif /* CURLRES_SYNCH */ +#endif /* CURLRES_IPV4 */ /* * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct. * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6 * stacks, but for all hosts and environments. + * + * Curl_addrinfo defined in "lib/hostip.h" + * + * struct Curl_addrinfo { + * int ai_flags; + * int ai_family; + * int ai_socktype; + * int ai_protocol; + * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo * + * char *ai_canonname; + * struct sockaddr *ai_addr; + * struct Curl_addrinfo *ai_next; + * }; + * + * hostent defined in <netdb.h> + * + * struct hostent { + * char *h_name; + * char **h_aliases; + * int h_addrtype; + * int h_length; + * char **h_addr_list; + * }; + * + * for backward compatibility: + * + * #define h_addr h_addr_list[0] + */ -struct Curl_addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - struct sockaddr *ai_addr; - char *ai_canonname; - struct addrinfo *ai_next; -}; - -struct hostent { - char *h_name; * official name of host * - char **h_aliases; * alias list * - int h_addrtype; * host address type * - int h_length; * length of address * - char **h_addr_list; * list of addresses * -} -#define h_addr h_addr_list[0] * for backward compatibility * - -*/ - -Curl_addrinfo *Curl_he2ai(struct hostent *he, int port) +Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) { Curl_addrinfo *ai; Curl_addrinfo *prevai = NULL; Curl_addrinfo *firstai = NULL; + struct sockaddr_in *addr; int i; - - union { - struct in_addr *addr; - char* list; - } curr; - union { - struct sockaddr_in* addr_in; - struct sockaddr* addr; - } address; + struct in_addr *curr; if(!he) /* no input == no output! */ return NULL; - for(i=0; (curr.list = he->h_addr_list[i]); i++) { + for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) { ai = calloc(1, sizeof(Curl_addrinfo) + sizeof(struct sockaddr_in)); @@ -434,23 +364,26 @@ Curl_addrinfo *Curl_he2ai(struct hostent *he, int port) prevai->ai_next = ai; ai->ai_family = AF_INET; /* we only support this */ - ai->ai_socktype = SOCK_STREAM; /* we only support this */ + + /* we return all names as STREAM, so when using this address for TFTP + the type must be ignored and conn->socktype be used instead! */ + ai->ai_socktype = SOCK_STREAM; + ai->ai_addrlen = sizeof(struct sockaddr_in); /* make the ai_addr point to the address immediately following this struct and use that area to store the address */ - ai->ai_addr = (struct sockaddr *) (ai + 1); + ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo)); /* leave the rest of the struct filled with zero */ - address.addr = ai->ai_addr; /* storage area for this info */ + addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */ - memcpy((char *)&(address.addr_in->sin_addr), curr.addr, sizeof(struct in_addr)); - address.addr_in->sin_family = he->h_addrtype; - address.addr_in->sin_port = htons((unsigned short)port); + memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr)); + addr->sin_family = he->h_addrtype; + addr->sin_port = htons((unsigned short)port); prevai = ai; } return firstai; } -#endif /* CURLRES_IPV4 */ diff --git a/Utilities/cmcurl/hostip6.c b/Utilities/cmcurl/hostip6.c index 6717c00..c8bbdb5 100644 --- a/Utilities/cmcurl/hostip6.c +++ b/Utilities/cmcurl/hostip6.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> - -#define _REENTRANT -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +54,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -79,6 +70,8 @@ #include "share.h" #include "strerror.h" #include "url.h" +#include "inet_pton.h" +#include "connect.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -87,7 +80,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -95,6 +88,7 @@ * Only for ipv6-enabled builds **********************************************************************/ #ifdef CURLRES_IPV6 +#ifndef CURLRES_ARES /* * This is a wrapper function for freeing name information in a protocol * independent way. This takes care of using the appropriate underlaying @@ -111,19 +105,20 @@ void Curl_freeaddrinfo(Curl_addrinfo *p) * address. But this is an ipv6 build and then we don't copy the address, we * just return the same pointer! */ -Curl_addrinfo *Curl_addrinfo_copy(void *source, int port) +Curl_addrinfo *Curl_addrinfo_copy(const void *orig, int port) { (void) port; - return source; + return (Curl_addrinfo*)orig; } -#endif +#endif /* CURLRES_ASYNCH */ +#endif /* CURLRES_ARES */ #ifdef CURLDEBUG /* These are strictly for memory tracing and are using the same style as the * family otherwise present in memdebug.c. I put these ones here since they * require a bunch of structs I didn't wanna include in memdebug.c */ -int curl_dogetaddrinfo(char *hostname, char *service, +int curl_dogetaddrinfo(const char *hostname, const char *service, struct addrinfo *hints, struct addrinfo **result, int line, const char *source) @@ -143,12 +138,22 @@ int curl_dogetaddrinfo(char *hostname, char *service, return res; } -int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, size_t hostlen, - char *serv, size_t servlen, int flags, +/* + * For CURLRES_ARS, this should be written using ares_gethostbyaddr() + * (ignoring the fact c-ares doesn't return 'serv'). + */ +#ifdef HAVE_GETNAMEINFO +int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, + GETNAMEINFO_TYPE_ARG2 salen, + char *host, GETNAMEINFO_TYPE_ARG46 hostlen, + char *serv, GETNAMEINFO_TYPE_ARG46 servlen, + GETNAMEINFO_TYPE_ARG7 flags, int line, const char *source) { - int res=(getnameinfo)(sa, salen, host, hostlen, serv, servlen, flags); + int res = (getnameinfo)(sa, salen, + host, hostlen, + serv, servlen, + flags); if(0 == res) { /* success */ if(logfile) @@ -162,6 +167,7 @@ int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen, } return res; } +#endif void curl_dofreeaddrinfo(struct addrinfo *freethis, int line, const char *source) @@ -171,8 +177,7 @@ void curl_dofreeaddrinfo(struct addrinfo *freethis, fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n", source, line, (void *)freethis); } - -#endif +#endif /* CURLDEBUG */ /* * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've @@ -191,9 +196,30 @@ bool Curl_ipvalid(struct SessionHandle *data) return TRUE; } -#ifndef USE_THREADING_GETADDRINFO +#if !defined(USE_THREADING_GETADDRINFO) && !defined(CURLRES_ARES) + +#ifdef DEBUG_ADDRINFO +static void dump_addrinfo(struct connectdata *conn, const struct addrinfo *ai) +{ + printf("dump_addrinfo:\n"); + for ( ; ai; ai = ai->ai_next) { + char buf[INET6_ADDRSTRLEN]; + + printf(" fam %2d, CNAME %s, ", + ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>"); + if (Curl_printable_address(ai, buf, sizeof(buf))) + printf("%s\n", buf); + else + printf("failed; %s\n", Curl_strerror(conn, Curl_sockerrno())); + } +} +#else +#define dump_addrinfo(x,y) +#endif + /* - * Curl_getaddrinfo() when built ipv6-enabled (non-threading version). + * Curl_getaddrinfo() when built ipv6-enabled (non-threading and + * non-ares version). * * Returns name information about the given hostname and port number. If * successful, the 'addrinfo' is returned and the forth argument will point to @@ -201,13 +227,15 @@ bool Curl_ipvalid(struct SessionHandle *data) * Curl_freeaddrinfo(), nothing else. */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp) { struct addrinfo hints, *res; int error; char sbuf[NI_MAXSERV]; + char *sbufptr = NULL; + char addrbuf[128]; curl_socket_t s; int pf; struct SessionHandle *data = conn->data; @@ -216,7 +244,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, /* see if we have an IPv6 stack */ s = socket(PF_INET6, SOCK_DGRAM, 0); - if (s < 0) { + if (s == CURL_SOCKET_BAD) { /* Some non-IPv6 stacks have been found to make very slow name resolves * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if * the stack seems to be a non-ipv6 one. */ @@ -244,20 +272,35 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, break; } } - + memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - snprintf(sbuf, sizeof(sbuf), "%d", port); - error = getaddrinfo(hostname, sbuf, &hints, &res); + hints.ai_socktype = conn->socktype; + + if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) || + (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) { + /* the given address is numerical only, prevent a reverse lookup */ + hints.ai_flags = AI_NUMERICHOST; + } +#if 0 /* removed nov 8 2005 before 7.15.1 */ + else + hints.ai_flags = AI_CANONNAME; +#endif + + if(port) { + snprintf(sbuf, sizeof(sbuf), "%d", port); + sbufptr=sbuf; + } + error = getaddrinfo(hostname, sbufptr, &hints, &res); if (error) { - infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); + infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); return NULL; } + dump_addrinfo(conn, res); + return res; } -#endif /* USE_THREADING_GETADDRINFO */ +#endif /* !USE_THREADING_GETADDRINFO && !CURLRES_ARES */ #endif /* ipv6 */ diff --git a/Utilities/cmcurl/hostsyn.c b/Utilities/cmcurl/hostsyn.c index 786f9d9..2f816b8 100644 --- a/Utilities/cmcurl/hostsyn.c +++ b/Utilities/cmcurl/hostsyn.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> - -#define _REENTRANT -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +54,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -87,7 +78,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -133,17 +124,15 @@ CURLcode Curl_is_resolved(struct connectdata *conn, * It is present here to keep #ifdefs out from multi.c */ -CURLcode Curl_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - int *max_fdp) +int Curl_resolv_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) { (void)conn; - (void)read_fd_set; - (void)write_fd_set; - (void)max_fdp; + (void)sock; + (void)numsocks; - return CURLE_OK; + return 0; /* no bits since we don't use any socks */ } #endif /* truly sync */ diff --git a/Utilities/cmcurl/hostthre.c b/Utilities/cmcurl/hostthre.c index 4f56ccb..12e31ea 100644 --- a/Utilities/cmcurl/hostthre.c +++ b/Utilities/cmcurl/hostthre.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,11 +26,9 @@ #include <string.h> #include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,13 +55,12 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif @@ -79,16 +76,21 @@ #include "share.h" #include "strerror.h" #include "url.h" +#include "multiif.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> #include "inet_ntop.h" -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" +#if defined(_MSC_VER) && defined(CURL_NO__BEGINTHREADEX) +#pragma message ("No _beginthreadex() available in this RTL") +#endif + /*********************************************************************** * Only for Windows threaded name resolves builds **********************************************************************/ @@ -154,13 +156,126 @@ struct thread_data { HANDLE thread_hnd; unsigned thread_id; DWORD thread_status; - curl_socket_t dummy_sock; /* dummy for Curl_fdset() */ - FILE *stderr_file; + curl_socket_t dummy_sock; /* dummy for Curl_resolv_fdset() */ + HANDLE mutex_waiting; /* marks that we are still waiting for a resolve */ + HANDLE event_resolved; /* marks that the thread obtained the information */ + HANDLE event_thread_started; /* marks that the thread has initialized and + started */ + HANDLE mutex_terminate; /* serializes access to flag_terminate */ + HANDLE event_terminate; /* flag for thread to terminate instead of calling + callbacks */ #ifdef CURLRES_IPV6 struct addrinfo hints; #endif }; +/* Data for synchronization between resolver thread and its parent */ +struct thread_sync_data { + HANDLE mutex_waiting; /* thread_data.mutex_waiting duplicate */ + HANDLE mutex_terminate; /* thread_data.mutex_terminate duplicate */ + HANDLE event_terminate; /* thread_data.event_terminate duplicate */ + char * hostname; /* hostname to resolve, Curl_async.hostname + duplicate */ +}; + +/* Destroy resolver thread synchronization data */ +static +void destroy_thread_sync_data(struct thread_sync_data * tsd) +{ + if (tsd->hostname) { + free(tsd->hostname); + tsd->hostname = NULL; + } + if (tsd->event_terminate) { + CloseHandle(tsd->event_terminate); + tsd->event_terminate = NULL; + } + if (tsd->mutex_terminate) { + CloseHandle(tsd->mutex_terminate); + tsd->mutex_terminate = NULL; + } + if (tsd->mutex_waiting) { + CloseHandle(tsd->mutex_waiting); + tsd->mutex_waiting = NULL; + } +} + +/* Initialize resolver thread synchronization data */ +static +BOOL init_thread_sync_data(struct thread_data * td, + char * hostname, + struct thread_sync_data * tsd) +{ + HANDLE curr_proc = GetCurrentProcess(); + + memset(tsd, 0, sizeof(*tsd)); + if (!DuplicateHandle(curr_proc, td->mutex_waiting, + curr_proc, &tsd->mutex_waiting, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + /* failed to duplicate the mutex, no point in continuing */ + destroy_thread_sync_data(tsd); + return FALSE; + } + if (!DuplicateHandle(curr_proc, td->mutex_terminate, + curr_proc, &tsd->mutex_terminate, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + /* failed to duplicate the mutex, no point in continuing */ + destroy_thread_sync_data(tsd); + return FALSE; + } + if (!DuplicateHandle(curr_proc, td->event_terminate, + curr_proc, &tsd->event_terminate, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + /* failed to duplicate the event, no point in continuing */ + destroy_thread_sync_data(tsd); + return FALSE; + } + /* Copying hostname string because original can be destroyed by parent + * thread during gethostbyname execution. + */ + tsd->hostname = strdup(hostname); + if (!tsd->hostname) { + /* Memory allocation failed */ + destroy_thread_sync_data(tsd); + return FALSE; + } + return TRUE; +} + +/* acquire resolver thread synchronization */ +static +BOOL acquire_thread_sync(struct thread_sync_data * tsd) +{ + /* is the thread initiator still waiting for us ? */ + if (WaitForSingleObject(tsd->mutex_waiting, 0) == WAIT_TIMEOUT) { + /* yes, it is */ + + /* Waiting access to event_terminate */ + if (WaitForSingleObject(tsd->mutex_terminate, INFINITE) != WAIT_OBJECT_0) { + /* Something went wrong - now just ignoring */ + } + else { + if (WaitForSingleObject(tsd->event_terminate, 0) != WAIT_TIMEOUT) { + /* Parent thread signaled us to terminate. + * This means that all data in conn->async is now destroyed + * and we cannot use it. + */ + } + else { + return TRUE; + } + } + } + return FALSE; +} + +/* release resolver thread synchronization */ +static +void release_thread_sync(struct thread_sync_data * tsd) +{ + ReleaseMutex(tsd->mutex_terminate); +} + #if defined(CURLRES_IPV4) /* * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback @@ -174,26 +289,45 @@ static unsigned __stdcall gethostbyname_thread (void *arg) struct connectdata *conn = (struct connectdata*) arg; struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct hostent *he; - int rc; + int rc = 0; - /* Sharing the same _iob[] element with our parent thread should - * hopefully make printouts synchronised. I'm not sure it works - * with a static runtime lib (MSVC's libc.lib). + /* Duplicate the passed mutex and event handles. + * This allows us to use it even after the container gets destroyed + * due to a resolver timeout. */ - *stderr = *td->stderr_file; + struct thread_sync_data tsd = { 0,0,0,NULL }; + if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { + /* thread synchronization data initialization failed */ + return (unsigned)-1; + } WSASetLastError (conn->async.status = NO_DATA); /* pending status */ - he = gethostbyname (conn->async.hostname); - if (he) { - Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he); - rc = 1; - } - else { - Curl_addrinfo4_callback(conn, (int)WSAGetLastError(), NULL); - rc = 0; + + /* Signaling that we have initialized all copies of data and handles we + need */ + SetEvent(td->event_thread_started); + + he = gethostbyname (tsd.hostname); + + /* is parent thread waiting for us and are we able to access conn members? */ + if (acquire_thread_sync(&tsd)) { + /* Mark that we have obtained the information, and that we are calling + * back with it. */ + SetEvent(td->event_resolved); + if (he) { + rc = Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he); + } + else { + rc = Curl_addrinfo4_callback(conn, (int)WSAGetLastError(), NULL); + } + TRACE(("Winsock-error %d, addr %s\n", conn->async.status, + he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown")); + release_thread_sync(&tsd); } - TRACE(("Winsock-error %d, addr %s\n", conn->async.status, - he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown")); + + /* clean up */ + destroy_thread_sync_data(&tsd); + return (rc); /* An implicit _endthreadex() here */ } @@ -214,44 +348,99 @@ static unsigned __stdcall getaddrinfo_thread (void *arg) struct addrinfo *res; char service [NI_MAXSERV]; int rc; + struct addrinfo hints = td->hints; - *stderr = *td->stderr_file; + /* Duplicate the passed mutex handle. + * This allows us to use it even after the container gets destroyed + * due to a resolver timeout. + */ + struct thread_sync_data tsd = { 0,0,0,NULL }; + if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { + /* thread synchronization data initialization failed */ + return -1; + } itoa(conn->async.port, service, 10); WSASetLastError(conn->async.status = NO_DATA); /* pending status */ - rc = getaddrinfo(conn->async.hostname, service, &td->hints, &res); + /* Signaling that we have initialized all copies of data and handles we + need */ + SetEvent(td->event_thread_started); - if (rc == 0) { + rc = getaddrinfo(tsd.hostname, service, &hints, &res); + + /* is parent thread waiting for us and are we able to access conn members? */ + if (acquire_thread_sync(&tsd)) { + /* Mark that we have obtained the information, and that we are calling + back with it. */ + SetEvent(td->event_resolved); + + if (rc == 0) { #ifdef DEBUG_THREADING_GETADDRINFO - dump_addrinfo (conn, res); + dump_addrinfo (conn, res); #endif - Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res); - } - else { - Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL); - TRACE(("Winsock-error %d, no address\n", conn->async.status)); + rc = Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res); + } + else { + rc = Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL); + TRACE(("Winsock-error %d, no address\n", conn->async.status)); + } + release_thread_sync(&tsd); } + + /* clean up */ + destroy_thread_sync_data(&tsd); + return (rc); /* An implicit _endthreadex() here */ } #endif /* - * destroy_thread_data() cleans up async resolver data. + * Curl_destroy_thread_data() cleans up async resolver data and thread handle. * Complementary of ares_destroy. */ -static void destroy_thread_data (struct Curl_async *async) +void Curl_destroy_thread_data (struct Curl_async *async) { if (async->hostname) free(async->hostname); if (async->os_specific) { - curl_socket_t sock = ((const struct thread_data*)async->os_specific)->dummy_sock; + 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; @@ -269,6 +458,7 @@ static bool init_resolve_thread (struct connectdata *conn, const Curl_addrinfo *hints) { struct thread_data *td = calloc(sizeof(*td), 1); + HANDLE thread_and_event[2] = {0}; if (!td) { SetLastError(ENOMEM); @@ -288,11 +478,62 @@ static bool init_resolve_thread (struct connectdata *conn, conn->async.status = 0; conn->async.dns = NULL; conn->async.os_specific = (void*) td; - td->dummy_sock = CURL_SOCKET_BAD; - td->stderr_file = stderr; + + /* Create the mutex used to inform the resolver thread that we're + * still waiting, and take initial ownership. + */ + td->mutex_waiting = CreateMutex(NULL, TRUE, NULL); + if (td->mutex_waiting == NULL) { + Curl_destroy_thread_data(&conn->async); + SetLastError(EAGAIN); + return FALSE; + } + + /* Create the event that the thread uses to inform us that it's + * done resolving. Do not signal it. + */ + td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL); + if (td->event_resolved == NULL) { + Curl_destroy_thread_data(&conn->async); + SetLastError(EAGAIN); + return FALSE; + } + /* Create the mutex used to serialize access to event_terminated + * between us and resolver thread. + */ + td->mutex_terminate = CreateMutex(NULL, FALSE, NULL); + if (td->mutex_terminate == NULL) { + Curl_destroy_thread_data(&conn->async); + SetLastError(EAGAIN); + return FALSE; + } + /* Create the event used to signal thread that it should terminate. + */ + td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL); + if (td->event_terminate == NULL) { + Curl_destroy_thread_data(&conn->async); + SetLastError(EAGAIN); + return FALSE; + } + /* Create the event used by thread to inform it has initialized its own data. + */ + td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL); + if (td->event_thread_started == NULL) { + Curl_destroy_thread_data(&conn->async); + SetLastError(EAGAIN); + return FALSE; + } + +#ifdef _WIN32_WCE + td->thread_hnd = (HANDLE) CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) THREAD_FUNC, + conn, 0, &td->thread_id); +#else td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC, conn, 0, &td->thread_id); +#endif + #ifdef CURLRES_IPV6 curlassert(hints); td->hints = *hints; @@ -301,14 +542,30 @@ static bool init_resolve_thread (struct connectdata *conn, #endif if (!td->thread_hnd) { +#ifdef _WIN32_WCE + TRACE(("CreateThread() failed; %s\n", Curl_strerror(conn,GetLastError()))); +#else SetLastError(errno); TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno))); - destroy_thread_data(&conn->async); +#endif + Curl_destroy_thread_data(&conn->async); return FALSE; } - /* This socket is only to keep Curl_fdset() and select() happy; should never - * become signalled for read/write since it's unbound but Windows needs - * atleast 1 socket in select(). + /* Waiting until the thread will initialize its data or it will exit due errors. + */ + thread_and_event[0] = td->thread_hnd; + thread_and_event[1] = td->event_thread_started; + if (WaitForMultipleObjects(sizeof(thread_and_event) / + sizeof(thread_and_event[0]), + (const HANDLE*)thread_and_event, FALSE, + INFINITE) == WAIT_FAILED) { + /* The resolver thread has been created, + * most probably it works now - ignoring this "minor" error + */ + } + /* This socket is only to keep Curl_resolv_fdset() and select() happy; + * should never become signalled for read/write since it's unbound but + * Windows needs atleast 1 socket in select(). */ td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0); return TRUE; @@ -341,28 +598,49 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, conn->data->set.timeout ? conn->data->set.timeout : CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ ticks = GetTickCount(); - (void)ticks; - - status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout); - if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) { - /* Thread finished before timeout; propagate Winsock error to this thread. - * 'conn->async.done = TRUE' is set in Curl_addrinfo4/6_callback(). - */ - WSASetLastError(conn->async.status); - GetExitCodeThread(td->thread_hnd, &td->thread_status); - TRACE(("%s() status %lu, thread retval %lu, ", - THREAD_NAME, status, td->thread_status)); + + /* wait for the thread to resolve the name */ + status = WaitForSingleObject(td->event_resolved, 1000UL*timeout); + + /* mark that we are now done waiting */ + ReleaseMutex(td->mutex_waiting); + + /* close our handle to the mutex, no point in hanging on to it */ + CloseHandle(td->mutex_waiting); + td->mutex_waiting = NULL; + + /* close the event handle, it's useless now */ + CloseHandle(td->event_resolved); + td->event_resolved = NULL; + + /* has the resolver thread succeeded in resolving our query ? */ + if (status == WAIT_OBJECT_0) { + /* wait for the thread to exit, it's in the callback sequence */ + if (WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) { + TerminateThread(td->thread_hnd, 0); + conn->async.done = TRUE; + td->thread_status = (DWORD)-1; + TRACE(("%s() thread stuck?!, ", THREAD_NAME)); + } + else { + /* Thread finished before timeout; propagate Winsock error to this + * thread. 'conn->async.done = TRUE' is set in + * Curl_addrinfo4/6_callback(). + */ + WSASetLastError(conn->async.status); + GetExitCodeThread(td->thread_hnd, &td->thread_status); + TRACE(("%s() status %lu, thread retval %lu, ", + THREAD_NAME, status, td->thread_status)); + } } else { - conn->async.done = TRUE; - td->thread_status = (DWORD)-1; - TRACE(("%s() timeout, ", THREAD_NAME)); + conn->async.done = TRUE; + td->thread_status = (DWORD)-1; + TRACE(("%s() timeout, ", THREAD_NAME)); } TRACE(("elapsed %lu ms\n", GetTickCount()-ticks)); - CloseHandle(td->thread_hnd); - if(entry) *entry = conn->async.dns; @@ -370,25 +648,34 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, if (!conn->async.dns) { /* a name was not resolved */ - if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { - failf(data, "Resolving host timed out: %s", conn->host.name); - rc = CURLE_OPERATION_TIMEDOUT; + if (td->thread_status == CURLE_OUT_OF_MEMORY) { + rc = CURLE_OUT_OF_MEMORY; + failf(data, "Could not resolve host: %s", curl_easy_strerror(rc)); } else if(conn->async.done) { - failf(data, "Could not resolve host: %s; %s", - conn->host.name, Curl_strerror(conn,conn->async.status)); - rc = CURLE_COULDNT_RESOLVE_HOST; + if(conn->bits.httpproxy) { + failf(data, "Could not resolve proxy: %s; %s", + conn->proxy.dispname, Curl_strerror(conn, conn->async.status)); + rc = CURLE_COULDNT_RESOLVE_PROXY; + } + else { + failf(data, "Could not resolve host: %s; %s", + conn->host.name, Curl_strerror(conn, conn->async.status)); + rc = CURLE_COULDNT_RESOLVE_HOST; + } + } + else if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { + failf(data, "Resolving host timed out: %s", conn->host.name); + rc = CURLE_OPERATION_TIMEDOUT; } else rc = CURLE_OPERATION_TIMEDOUT; } - destroy_thread_data(&conn->async); + Curl_destroy_thread_data(&conn->async); - if(CURLE_OK != rc) - /* close the connection, since we must not return failure from here - without cleaning up this connection properly */ - Curl_disconnect(conn); + if(!conn->async.dns) + conn->bits.close = TRUE; return (rc); } @@ -405,7 +692,7 @@ CURLcode Curl_is_resolved(struct connectdata *conn, if (conn->async.done) { /* we're done */ - destroy_thread_data(&conn->async); + Curl_destroy_thread_data(&conn->async); if (!conn->async.dns) { TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n")); return CURLE_COULDNT_RESOLVE_HOST; @@ -413,25 +700,25 @@ CURLcode Curl_is_resolved(struct connectdata *conn, *entry = conn->async.dns; TRACE(("resolved okay, dns %p\n", *entry)); } - else - TRACE(("not yet\n")); return CURLE_OK; } -CURLcode Curl_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - int *max_fdp) +int Curl_resolv_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) { const struct thread_data *td = (const struct thread_data *) conn->async.os_specific; if (td && td->dummy_sock != CURL_SOCKET_BAD) { - FD_SET(td->dummy_sock,write_fd_set); - *max_fdp = td->dummy_sock; + if(numsocks) { + /* return one socket waiting for writable, even though this is just + a dummy */ + socks[0] = td->dummy_sock; + return GETSOCK_WRITESOCK(0); + } } - (void) read_fd_set; - return CURLE_OK; + return 0; } #ifdef CURLRES_IPV4 @@ -439,11 +726,11 @@ CURLcode Curl_fdset(struct connectdata *conn, * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6. */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp) { - struct hostent *h; + struct hostent *h = NULL; struct SessionHandle *data = conn->data; in_addr_t in; @@ -461,8 +748,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } /* fall-back to blocking version */ - infof(data, "init_resolve_thread() failed for %s; code %lu\n", - hostname, GetLastError()); + infof(data, "init_resolve_thread() failed for %s; %s\n", + hostname, Curl_strerror(conn,GetLastError())); h = gethostbyname(hostname); if (!h) { @@ -479,7 +766,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, * Curl_getaddrinfo() - for Windows threading IPv6 enabled */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - char *hostname, + const char *hostname, int port, int *waitp) { @@ -525,8 +812,10 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = conn->socktype; +#if 0 /* removed nov 8 2005 before 7.15.1 */ hints.ai_flags = AI_CANONNAME; +#endif itoa(port, sbuf, 10); /* fire up a new resolver thread! */ @@ -536,8 +825,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } /* fall-back to blocking version */ - infof(data, "init_resolve_thread() failed for %s; code %lu\n", - hostname, GetLastError()); + infof(data, "init_resolve_thread() failed for %s; %s\n", + hostname, Curl_strerror(conn,GetLastError())); error = getaddrinfo(hostname, sbuf, &hints, &res); if (error) { diff --git a/Utilities/cmcurl/http.c b/Utilities/cmcurl/http.c index 7cf543b..c07053b 100644 --- a/Utilities/cmcurl/http.c +++ b/Utilities/cmcurl/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,12 +30,14 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif -#include <errno.h> - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -45,7 +47,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_TIME_H #ifdef TIME_WITH_SYS_TIME @@ -70,22 +74,19 @@ #include <sys/param.h> #endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - #endif #include "urldata.h" #include <curl/curl.h> #include "transfer.h" #include "sendf.h" +#include "easyif.h" /* for Curl_convert_... prototypes */ #include "formdata.h" #include "progress.h" #include "base64.h" #include "cookie.h" #include "strequal.h" -#include "ssluse.h" +#include "sslgen.h" #include "http_digest.h" #include "http_ntlm.h" #include "http_negotiate.h" @@ -93,7 +94,11 @@ #include "share.h" #include "hostip.h" #include "http.h" -#include "curl_memory.h" +#include "memory.h" +#include "select.h" +#include "parsedate.h" /* for the week day and month names */ +#include "strtoofft.h" +#include "multiif.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -145,12 +150,12 @@ static CURLcode Curl_output_basic(struct connectdata *conn, bool proxy) } snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd); - if(Curl_base64_encode(data->state.buffer, + if(Curl_base64_encode(data, data->state.buffer, strlen(data->state.buffer), &authorization) > 0) { if(*userp) free(*userp); - *userp = aprintf( "%sAuthorization: Basic %s\015\012", + *userp = aprintf( "%sAuthorization: Basic %s\r\n", proxy?"Proxy-":"", authorization); free(authorization); @@ -192,6 +197,104 @@ static bool pickoneauth(struct auth *pick) } /* + * perhapsrewind() + * + * If we are doing POST or PUT { + * If we have more data to send { + * If we are doing NTLM { + * Keep sending since we must not disconnect + * } + * else { + * If there is more than just a little data left to send, close + * the current connection by force. + * } + * } + * If we have sent any data { + * If we don't have track of all the data { + * call app to tell it to rewind + * } + * else { + * rewind internally so that the operation can restart fine + * } + * } + * } + */ +static CURLcode perhapsrewind(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct HTTP *http = data->reqdata.proto.http; + struct Curl_transfer_keeper *k = &data->reqdata.keep; + curl_off_t bytessent; + curl_off_t expectsend = -1; /* default is unknown */ + + if(!http) + /* If this is still NULL, we have not reach very far and we can + safely skip this rewinding stuff */ + return CURLE_OK; + + 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; + k->size = 0; /* don't download any more than 0 bytes */ + } + + if(bytessent) + return Curl_readrewind(conn); + + return CURLE_OK; +} + +/* * Curl_http_auth_act() gets called when a all HTTP headers have been received * and it checks what authentication methods that are available and decides * which one (if any) to use. It will set 'newurl' if an auth metod was @@ -205,43 +308,56 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) bool pickproxy = FALSE; CURLcode code = CURLE_OK; + if(100 == data->reqdata.keep.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 && - ((conn->keep.httpcode == 401) || - (conn->bits.authprobe && conn->keep.httpcode < 300))) { + ((data->reqdata.keep.httpcode == 401) || + (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost); if(!pickhost) data->state.authproblem = TRUE; } if(conn->bits.proxy_user_passwd && - ((conn->keep.httpcode == 407) || - (conn->bits.authprobe && conn->keep.httpcode < 300))) { + ((data->reqdata.keep.httpcode == 407) || + (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy); if(!pickproxy) data->state.authproblem = TRUE; } - if(pickhost || pickproxy) - conn->newurl = strdup(data->change.url); /* clone URL */ + if(pickhost || pickproxy) { + data->reqdata.newurl = strdup(data->change.url); /* clone URL */ + + if((data->set.httpreq != HTTPREQ_GET) && + (data->set.httpreq != HTTPREQ_HEAD) && + !conn->bits.rewindaftersend) { + code = perhapsrewind(conn); + if(code) + return code; + } + } - else if((conn->keep.httpcode < 300) && + else if((data->reqdata.keep.httpcode < 300) && (!data->state.authhost.done) && - conn->bits.authprobe) { + 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)) { - conn->newurl = strdup(data->change.url); /* clone URL */ + data->reqdata.newurl = strdup(data->change.url); /* clone URL */ data->state.authhost.done = TRUE; } } if (Curl_http_should_fail(conn)) { failf (data, "The requested URL returned error: %d", - conn->keep.httpcode); + data->reqdata.keep.httpcode); code = CURLE_HTTP_RETURNED_ERROR; } @@ -272,50 +388,48 @@ Curl_http_output_auth(struct connectdata *conn, CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char *auth=NULL; + struct auth *authhost; + struct auth *authproxy; curlassert(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 { - data->state.authhost.done = TRUE; - data->state.authproxy.done = TRUE; + authhost->done = TRUE; + authproxy->done = TRUE; return CURLE_OK; /* no authentication with no user or password */ } - if(data->state.authhost.want && !data->state.authhost.picked) + 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. */ - data->state.authhost.picked = data->state.authhost.want; + authhost->picked = authhost->want; - if(data->state.authproxy.want && !data->state.authproxy.picked) + 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. */ - data->state.authproxy.picked = data->state.authproxy.want; - - /* 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 || - !data->state.auth_host || - curl_strequal(data->state.auth_host, conn->host.name) || - data->set.http_disable_hostname_check_before_authentication) { - - /* Send proxy authentication header if needed */ - if (conn->bits.httpproxy && - (conn->bits.tunnel_proxy == proxytunnel)) { -#ifdef USE_SSLEAY - if(data->state.authproxy.want == CURLAUTH_NTLM) { - auth=(char *)"NTLM"; - result = Curl_output_ntlm(conn, TRUE); - if(result) - return result; - } - else + authproxy->picked = authproxy->want; + + /* Send proxy authentication header if needed */ + if (conn->bits.httpproxy && + (conn->bits.tunnel_proxy == proxytunnel)) { +#ifdef USE_NTLM + if(authproxy->picked == CURLAUTH_NTLM) { + auth=(char *)"NTLM"; + result = Curl_output_ntlm(conn, TRUE); + if(result) + return result; + } + else #endif - if(data->state.authproxy.want == CURLAUTH_BASIC) { + if(authproxy->picked == CURLAUTH_BASIC) { /* Basic */ if(conn->bits.proxy_user_passwd && !checkheaders(data, "Proxy-authorization:")) { @@ -324,9 +438,12 @@ Curl_http_output_auth(struct connectdata *conn, if(result) return result; } - data->state.authproxy.done = TRUE; + /* NOTE: Curl_output_basic() should set 'done' TRUE, as the other auth + functions work that way */ + authproxy->done = TRUE; } - else if(data->state.authproxy.want == CURLAUTH_DIGEST) { +#ifndef CURL_DISABLE_CRYPTO_AUTH + else if(authproxy->picked == CURLAUTH_DIGEST) { auth=(char *)"Digest"; result = Curl_output_digest(conn, TRUE, /* proxy */ @@ -335,32 +452,45 @@ Curl_http_output_auth(struct connectdata *conn, if(result) return result; } - - infof(data, "Proxy auth using %s with user '%s'\n", - auth, conn->proxyuser?conn->proxyuser:""); +#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 */ - data->state.authproxy.done = TRUE; + 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((data->state.authhost.want == CURLAUTH_GSSNEGOTIATE) && + if((authhost->picked == CURLAUTH_GSSNEGOTIATE) && data->state.negotiate.context && !GSS_ERROR(data->state.negotiate.status)) { auth=(char *)"GSS-Negotiate"; result = Curl_output_negotiate(conn); if (result) return result; - data->state.authhost.done = TRUE; + authhost->done = TRUE; } else #endif -#ifdef USE_SSLEAY - if(data->state.authhost.picked == CURLAUTH_NTLM) { +#ifdef USE_NTLM + if(authhost->picked == CURLAUTH_NTLM) { auth=(char *)"NTLM"; result = Curl_output_ntlm(conn, FALSE); if(result) @@ -369,7 +499,8 @@ Curl_http_output_auth(struct connectdata *conn, else #endif { - if(data->state.authhost.picked == CURLAUTH_DIGEST) { +#ifndef CURL_DISABLE_CRYPTO_AUTH + if(authhost->picked == CURLAUTH_DIGEST) { auth=(char *)"Digest"; result = Curl_output_digest(conn, FALSE, /* not a proxy */ @@ -377,8 +508,9 @@ Curl_http_output_auth(struct connectdata *conn, (unsigned char *)path); if(result) return result; - } - else if(data->state.authhost.picked == CURLAUTH_BASIC) { + } else +#endif + if(authhost->picked == CURLAUTH_BASIC) { if(conn->bits.user_passwd && !checkheaders(data, "Authorization:")) { auth=(char *)"Basic"; @@ -387,16 +519,21 @@ Curl_http_output_auth(struct connectdata *conn, return result; } /* basic is always ready */ - data->state.authhost.done = TRUE; + authhost->done = TRUE; } } - if(auth) + 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 - data->state.authhost.done = TRUE; + authhost->done = TRUE; return result; } @@ -433,15 +570,15 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, } /* pass all white spaces */ - while(*start && isspace((int)*start)) + while(*start && ISSPACE(*start)) start++; /* - * Here we check if we want the specific single authentiction (using ==) and + * 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 authenticaion type to the authavail + * types (using &), we OR this authentication type to the authavail * variable. */ @@ -454,8 +591,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, /* if exactly this is wanted, go */ int neg = Curl_input_negotiate(conn, start); if (neg == 0) { - conn->newurl = strdup(data->change.url); - data->state.authproblem = (conn->newurl == NULL); + data->reqdata.newurl = strdup(data->change.url); + data->state.authproblem = (data->reqdata.newurl == NULL); } else { infof(data, "Authentication problem. Ignoring this.\n"); @@ -465,7 +602,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, } else #endif -#ifdef USE_SSLEAY +#ifdef USE_NTLM /* NTLM support requires the SSL crypto libs */ if(checkprefix("NTLM", start)) { *availp |= CURLAUTH_NTLM; @@ -485,22 +622,30 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, } else #endif +#ifndef CURL_DISABLE_CRYPTO_AUTH if(checkprefix("Digest", start)) { - 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; + 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 if(checkprefix("Basic", start)) { + else +#endif + if(checkprefix("Basic", start)) { *availp |= CURLAUTH_BASIC; authp->avail |= CURLAUTH_BASIC; if(authp->picked == CURLAUTH_BASIC) { @@ -538,7 +683,7 @@ int Curl_http_should_fail(struct connectdata *conn) /* ** For readability */ - k = &conn->keep; + k = &data->reqdata.keep; /* ** If we haven't been asked to fail on error, @@ -553,6 +698,14 @@ int Curl_http_should_fail(struct connectdata *conn) if (k->httpcode < 400) return 0; + if (data->reqdata.resume_from && + (data->set.httpreq==HTTPREQ_GET) && + (k->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 @@ -585,7 +738,7 @@ int Curl_http_should_fail(struct connectdata *conn) 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__,conn->newurl ? conn->newurl : "(null)"); + infof(data,"%s: newurl = %s\n",__FUNCTION__,data->reqdata.newurl ? data->reqdata.newurl : "(null)"); infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem); #endif @@ -615,7 +768,7 @@ static size_t readmoredata(char *buffer, void *userp) { struct connectdata *conn = (struct connectdata *)userp; - struct HTTP *http = conn->proto.http; + struct HTTP *http = conn->data->reqdata.proto.http; size_t fullsize = size * nitems; if(0 == http->postsize) @@ -623,7 +776,7 @@ static size_t readmoredata(char *buffer, return 0; /* make sure that a HTTP request is never sent away chunked! */ - conn->bits.forbidchunk= (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; + conn->bits.forbidchunk = (bool)(http->sending == HTTPSEND_REQUEST); if(http->postsize <= (curl_off_t)fullsize) { memcpy(buffer, http->postdata, (size_t)http->postsize); @@ -667,6 +820,8 @@ struct send_buffer { }; typedef struct send_buffer send_buffer; +static CURLcode add_custom_headers(struct connectdata *conn, + send_buffer *req_buffer); static CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size); @@ -686,23 +841,32 @@ send_buffer *add_buffer_init(void) } /* - * add_buffer_send() sends a buffer and frees all associated memory. + * add_buffer_send() sends a header buffer and frees all associated memory. + * Body data may be appended to the header data if desired. * * Returns CURLcode */ static CURLcode add_buffer_send(send_buffer *in, struct connectdata *conn, - long *bytes_written) /* add the number of sent + 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 (for log tracing) */ + int socketindex) + { ssize_t amount; CURLcode res; char *ptr; size_t size; - struct HTTP *http = conn->proto.http; + struct HTTP *http = conn->data->reqdata.proto.http; size_t sendsize; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + curl_socket_t sockfd; + + curlassert(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 */ @@ -710,6 +874,20 @@ CURLcode add_buffer_send(send_buffer *in, ptr = in->buffer; size = in->size_used; +#ifdef CURL_DOES_CONVERSIONS + if(size - included_body_bytes > 0) { + res = Curl_convert_to_network(conn->data, ptr, size - included_body_bytes); + /* 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 @@ -735,41 +913,61 @@ CURLcode add_buffer_send(send_buffer *in, if(CURLE_OK == res) { - if(conn->data->set.verbose) + if(conn->data->set.verbose) { /* this data _may_ contain binary stuff */ - Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount, - conn->host.dispname); + Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, + (size_t)(amount-included_body_bytes), conn); + if (included_body_bytes) + Curl_debug(conn->data, CURLINFO_DATA_OUT, + ptr+amount-included_body_bytes, + (size_t)included_body_bytes, conn); + } *bytes_written += amount; - 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. */ + 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; + size -= amount; - ptr = in->buffer + amount; + ptr = in->buffer + amount; - /* backup the currently set pointers */ - http->backup.fread = conn->fread; - http->backup.fread_in = conn->fread_in; - http->backup.postdata = http->postdata; - http->backup.postsize = http->postsize; + /* backup the currently set pointers */ + http->backup.fread = conn->fread; + 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 = (curl_read_callback)readmoredata; - conn->fread_in = (void *)conn; - http->postdata = ptr; - http->postsize = (curl_off_t)size; + /* set the new pointers for the request-sending */ + conn->fread = (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; + http->send_buffer = in; + http->sending = HTTPSEND_REQUEST; - return CURLE_OK; + 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; } - http->sending = HTTPSEND_BODY; - /* the full buffer was sent, clean up and return */ } if(in->buffer) free(in->buffer); @@ -868,7 +1066,7 @@ Curl_compareheader(char *headerline, /* line to check */ start = &headerline[hlen]; /* pass all white spaces */ - while(*start && isspace((int)*start)) + while(*start && ISSPACE(*start)) start++; /* find the end of the header line */ @@ -895,34 +1093,41 @@ Curl_compareheader(char *headerline, /* line to check */ } /* - * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This + * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This * function will issue the necessary commands to get a seamless tunnel through * this proxy. After that, the socket can be used just as a normal socket. + * + * This badly needs to be rewritten. CONNECT should be sent and dealt with + * like any ordinary HTTP request, and not specially crafted like this. This + * function only remains here like this for now since the rewrite is a bit too + * much work to do at the moment. + * + * This function is BLOCKING which is nasty for all multi interface using apps. */ -CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, - int sockindex, - char *hostname, - int remote_port) +CURLcode Curl_proxyCONNECT(struct connectdata *conn, + int sockindex, + char *hostname, + int remote_port) { int subversion=0; struct SessionHandle *data=conn->data; - struct Curl_transfer_keeper *k = &conn->keep; + struct Curl_transfer_keeper *k = &data->reqdata.keep; CURLcode result; int res; - size_t nread; /* total size read */ int perline; /* count bytes per line */ - bool keepon=TRUE; + int keepon=TRUE; ssize_t gotbytes; char *ptr; - long timeout; /* default timeout in seconds */ - struct timeval interval; - fd_set rkeepfd; - fd_set readfd; + long timeout = + data->set.timeout?data->set.timeout:3600; /* in seconds */ char *line_start; char *host_port; curl_socket_t tunnelsocket = conn->sock[sockindex]; + send_buffer *req_buffer; + curl_off_t cl=0; + bool closeConnection = FALSE; #define SELECT_OK 0 #define SELECT_ERROR 1 @@ -930,36 +1135,78 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, int error = SELECT_OK; infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); + conn->bits.proxy_connect_closed = FALSE; do { - if(conn->newurl) { + if(data->reqdata.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(conn->newurl); - conn->newurl = NULL; + free(data->reqdata.newurl); + data->reqdata.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) return CURLE_OUT_OF_MEMORY; /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(conn, (char *)"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) + result = CURLE_OUT_OF_MEMORY; + } + if(!checkheaders(data, "Proxy-Connection:")) + proxyconn = "Proxy-Connection: Keep-Alive\r\n"; - /* OK, now send the connect request to the proxy */ - result = - Curl_sendf(tunnelsocket, conn, - "CONNECT %s:%d HTTP/1.0\015\012" - "%s" - "%s" - "\r\n", - hostname, remote_port, - conn->bits.proxy_user_passwd? - conn->allocptr.proxyuserpwd:"", - data->set.useragent?conn->allocptr.uagent:"" - ); + if(!checkheaders(data, "User-Agent:") && data->set.useragent) + useragent = conn->allocptr.uagent; + + if(CURLE_OK == result) { + /* 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(CURLE_OK == result) + result = add_custom_headers(conn, req_buffer); + + if(host && *host) + free(host); + + 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); + } if(result) failf(data, "Failed sending CONNECT to proxy"); } @@ -967,36 +1214,26 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, if(result) return result; - FD_ZERO (&readfd); /* clear it */ - FD_SET (tunnelsocket, &readfd); /* read socket */ - - /* get this in a backup variable to be able to restore it on each lap in - the select() loop */ - rkeepfd = readfd; - ptr=data->state.buffer; line_start = ptr; nread=0; perline=0; + keepon=TRUE; while((nread<BUFSIZE) && (keepon && !error)) { - readfd = rkeepfd; /* set every lap */ - interval.tv_sec = 1; /* timeout each second and check the timeout */ - interval.tv_usec = 0; - - 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)/1000; /* spent time */ - if(timeout <=0 ) { - failf(data, "Proxy connection aborted due to timeout"); - error = SELECT_TIMEOUT; /* already too little time */ - break; - } + + /* if timeout is requested, find out how much remaining time we have */ + long check = timeout - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + if(check <=0 ) { + failf(data, "Proxy CONNECT aborted due to timeout"); + error = SELECT_TIMEOUT; /* already too little time */ + break; } - switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) { + /* timeout each second and check the timeout */ + switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, 1000)) { case -1: /* select() error, stop reading */ error = SELECT_ERROR; failf(data, "Proxy CONNECT aborted due to select() error"); @@ -1004,12 +1241,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, case 0: /* timeout */ break; default: - /* - * This code previously didn't use the kerberos sec_read() code - * to read, but when we use Curl_read() it may do so. Do confirm - * that this is still ok and then remove this comment! - */ - res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes); + res = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes); if(res< 0) /* EWOULDBLOCK */ continue; /* go loop yourself */ @@ -1024,32 +1256,39 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, /* * We got a whole chunk of data, which can be anything from one byte * to a set of lines and possibly just a piece of the last line. - * - * TODO: To make this code work less error-prone, we need to make - * sure that we read and create full lines before we compare them, - * as there is really nothing that stops the proxy from delivering - * the response lines in multiple parts, each part consisting of - * only a little piece of the line(s). */ + */ int i; nread += gotbytes; + + if(keepon > TRUE) { + /* This means we are currently ignoring a response-body, so we + simply count down our counter and make sure to break out of the + loop when we're done! */ + cl -= gotbytes; + if(cl<=0) { + keepon = FALSE; + break; + } + } + else for(i = 0; i < gotbytes; ptr++, i++) { perline++; /* amount of bytes in this line so far */ if(*ptr=='\n') { char letter; int writetype; - /* output debug output if that is requested */ + /* output debug if that is requested */ if(data->set.verbose) - Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, - conn->host.dispname); + Curl_debug(data, CURLINFO_HEADER_IN, + line_start, (size_t)perline, conn); /* send the header to the callback */ writetype = CLIENTWRITE_HEADER; if(data->set.include_header) writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(data, writetype, line_start, perline); + result = Curl_client_write(conn, writetype, line_start, perline); if(result) return result; @@ -1060,7 +1299,21 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, if(('\r' == line_start[0]) || ('\n' == line_start[0])) { /* end of response-headers from the proxy */ - keepon=FALSE; + if(cl && (407 == k->httpcode) && !data->state.authproblem) { + /* If we get a 407 response code with content length when we + * have no auth problem, we must ignore the whole + * response-body */ + keepon = 2; + infof(data, "Ignore %" FORMAT_OFF_T + " bytes of response-body\n", cl); + cl -= (gotbytes - i);/* remove the remaining chunk of what + we already read */ + if(cl<=0) + /* if the whole thing was already read, we are done! */ + keepon=FALSE; + } + else + keepon = FALSE; break; /* breaks out of for-loop, not switch() */ } @@ -1075,6 +1328,13 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, if(result) return result; } + else if(checkprefix("Content-Length:", line_start)) { + cl = curlx_strtoofft(line_start + strlen("Content-Length:"), + NULL, 10); + } + else if(Curl_compareheader(line_start, + "Connection:", "close")) + closeConnection = TRUE; else if(2 == sscanf(line_start, "HTTP/1.%d %d", &subversion, &k->httpcode)) { @@ -1101,11 +1361,21 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, headers. 'newurl' is set to a new URL if we must loop. */ Curl_http_auth_act(conn); - } while(conn->newurl); + if (closeConnection && data->reqdata.newurl) { + /* Connection closed by server. Don't use it anymore */ + sclose(conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + break; + } + } while(data->reqdata.newurl); if(200 != k->httpcode) { failf(data, "Received HTTP code %d from proxy after CONNECT", k->httpcode); + + if (closeConnection && data->reqdata.newurl) + conn->bits.proxy_connect_closed = TRUE; + return CURLE_RECV_ERROR; } @@ -1125,7 +1395,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, * Curl_http_connect() performs HTTP stuff to do at connect-time, called from * the generic Curl_connect(). */ -CURLcode Curl_http_connect(struct connectdata *conn) +CURLcode Curl_http_connect(struct connectdata *conn, bool *done) { struct SessionHandle *data; CURLcode result; @@ -1135,53 +1405,117 @@ CURLcode Curl_http_connect(struct connectdata *conn) /* 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 occured, can we start talking SSL + * after the connect has occurred, can we start talking SSL */ - if(conn->bits.tunnel_proxy) { + if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { /* either SSL over proxy, or explicitly asked for */ - result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET, - conn->host.name, - conn->remote_port); + result = Curl_proxyCONNECT(conn, FIRSTSOCKET, + conn->host.name, + conn->remote_port); if(CURLE_OK != result) return result; } + 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) { - /* now, perform the SSL initialization for this socket */ - result = Curl_SSLConnect(conn, FIRSTSOCKET); - if(result) - return result; + /* perform SSL initialization */ + if(data->state.used_interface == Curl_if_multi) { + result = Curl_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; } - if(conn->bits.user_passwd && !data->state.this_is_a_follow) { - /* Authorization: is requested, this is not a followed location, get the - original host name */ - if (data->state.auth_host) - /* Free to avoid leaking memory on multiple requests*/ - free(data->state.auth_host); + return CURLE_OK; +} - data->state.auth_host = strdup(conn->host.name); - } +CURLcode Curl_https_connecting(struct connectdata *conn, bool *done) +{ + CURLcode result; + curlassert(conn->protocol & PROT_HTTPS); + + /* perform SSL initialization for this socket */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done); + if(result) + return result; return CURLE_OK; } +#ifdef USE_SSLEAY +/* This function is OpenSSL-specific. It should be made to query the generic + SSL layer instead. */ +int Curl_https_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) +{ + if (conn->protocol & PROT_HTTPS) { + struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; + + if(!numsocks) + return GETSOCK_BLANK; + + if (connssl->connecting_state == ssl_connect_2_writing) { + /* write mode */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_WRITESOCK(0); + } + else if (connssl->connecting_state == ssl_connect_2_reading) { + /* read mode */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0); + } + } + return CURLE_OK; +} +#else +#ifdef USE_GNUTLS +int Curl_https_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) +{ + (void)conn; + (void)socks; + (void)numsocks; + return GETSOCK_BLANK; +} +#endif +#endif + /* * Curl_http_done() gets called from Curl_done() after a single HTTP request * has been performed. */ CURLcode Curl_http_done(struct connectdata *conn, - CURLcode status) + CURLcode status, bool premature) { - struct SessionHandle *data; - struct HTTP *http; - (void)status; /* no use for us */ - - data=conn->data; - http=conn->proto.http; + struct SessionHandle *data = conn->data; + struct HTTP *http =data->reqdata.proto.http; + struct Curl_transfer_keeper *k = &data->reqdata.keep; + (void)premature; /* not used */ /* set the proper values (possibly modified on POST) */ conn->fread = data->set.fread; /* restore */ @@ -1195,16 +1529,24 @@ CURLcode Curl_http_done(struct connectdata *conn, free(buff->buffer); free(buff); - http->send_buffer = NULL; /* cleaer the pointer */ + http->send_buffer = NULL; /* clear the pointer */ } if(HTTPREQ_POST_FORM == data->set.httpreq) { - conn->bytecount = http->readbytecount + http->writebytecount; + k->bytecount = http->readbytecount + http->writebytecount; - Curl_formclean(http->sendit); /* Now free that whole lot */ + Curl_formclean(&http->sendit); /* Now free that whole lot */ + if(http->form.fp) { + /* a file being uploaded was left opened, close it! */ + fclose(http->form.fp); + http->form.fp = NULL; + } } else if(HTTPREQ_PUT == data->set.httpreq) - conn->bytecount = http->readbytecount + http->writebytecount; + k->bytecount = http->readbytecount + http->writebytecount; + + if (status != CURLE_OK) + return (status); if(!conn->bits.retry && ((http->readbytecount + @@ -1220,38 +1562,106 @@ CURLcode Curl_http_done(struct connectdata *conn, return CURLE_OK; } +/* check and possibly add an Expect: header */ +static CURLcode expect100(struct SessionHandle *data, + send_buffer *req_buffer) +{ + CURLcode result = CURLE_OK; + data->state.expect100header = FALSE; /* default to false unless it is set + to TRUE below */ + if((data->set.httpversion != CURL_HTTP_VERSION_1_0) && + !checkheaders(data, "Expect:")) { + /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect: + 100-continue to the headers which actually speeds up post + operations (as there is one packet coming back from the web + server) */ + result = add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + if(result == CURLE_OK) + data->state.expect100header = TRUE; + } + return result; +} + +static CURLcode add_custom_headers(struct connectdata *conn, + send_buffer *req_buffer) +{ + CURLcode result = CURLE_OK; + 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 { + result = add_bufferf(req_buffer, "%s\r\n", headers->data); + if(result) + return result; + } + } + } + headers = headers->next; + } + return result; +} + /* * Curl_http() gets called from the generic Curl_do() function when a HTTP * request is to be performed. This creates and sends a properly constructed * HTTP request. */ -CURLcode Curl_http(struct connectdata *conn) +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; + CURLcode result=CURLE_OK; struct HTTP *http; - char *ppath = conn->path; + char *ppath = data->reqdata.path; char *host = conn->host.name; - const char *te = ""; /* tranfer-encoding */ + const char *te = ""; /* transfer-encoding */ char *ptr; char *request; Curl_HttpReq httpreq = data->set.httpreq; char *addcookies = NULL; + curl_off_t included_body = 0; - if(!conn->proto.http) { + /* 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(!data->reqdata.proto.http) { /* Only allocate this struct if we don't already have it! */ http = (struct HTTP *)malloc(sizeof(struct HTTP)); if(!http) return CURLE_OUT_OF_MEMORY; memset(http, 0, sizeof(struct HTTP)); - conn->proto.http = http; + data->reqdata.proto.http = http; } else - http = conn->proto.http; + http = data->reqdata.proto.http; - /* We default to persistant connections */ + /* We default to persistent connections */ conn->bits.close = FALSE; if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && @@ -1300,81 +1710,77 @@ CURLcode Curl_http(struct connectdata *conn) if(result) return result; - if((!data->state.authhost.done || !data->state.authproxy.done ) && - (httpreq != HTTPREQ_GET)) { - /* Until we are authenticated, we switch over to HEAD. Unless its a GET - we want to do. The explanation for this is rather long and boring, but - the point is that it can't be done otherwise without risking having to - send the POST or PUT data multiple times. */ - httpreq = HTTPREQ_HEAD; - request = (char *)"HEAD"; - conn->bits.no_body = TRUE; - conn->bits.authprobe = TRUE; /* this is a request done to probe for - authentication methods */ + 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.authprobe = FALSE; + conn->bits.authneg = FALSE; Curl_safefree(conn->allocptr.ref); if(data->change.referer && !checkheaders(data, "Referer:")) - conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer); + conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); else conn->allocptr.ref = NULL; if(data->set.cookie && !checkheaders(data, "Cookie:")) addcookies = data->set.cookie; - if(!conn->bits.upload_chunky && (httpreq != HTTPREQ_GET)) { - /* not a chunky transfer yet, but data is to be sent */ - ptr = checkheaders(data, "Transfer-Encoding:"); - if(ptr) { - /* Some kind of TE is requested, check if 'chunked' is chosen */ - conn->bits.upload_chunky = - Curl_compareheader(ptr, "Transfer-Encoding:", "chunked"); - te = ""; - } + if(!checkheaders(data, "Accept-Encoding:") && + data->set.encoding) { + Curl_safefree(conn->allocptr.accept_encoding); + conn->allocptr.accept_encoding = + aprintf("Accept-Encoding: %s\r\n", data->set.encoding); + if(!conn->allocptr.accept_encoding) + return CURLE_OUT_OF_MEMORY; } - else if(conn->bits.upload_chunky) { - /* RFC2616 section 4.4: - Messages MUST NOT include both a Content-Length header field and a - non-identity transfer-coding. If the message does include a non- - identity transfer-coding, the Content-Length MUST be ignored. */ - if(!checkheaders(data, "Transfer-Encoding:")) { + ptr = checkheaders(data, "Transfer-Encoding:"); + if(ptr) { + /* Some kind of TE is requested, check if 'chunked' is chosen */ + conn->bits.upload_chunky = + Curl_compareheader(ptr, "Transfer-Encoding:", "chunked"); + } + else { + if (httpreq == HTTPREQ_GET) + conn->bits.upload_chunky = FALSE; + if(conn->bits.upload_chunky) te = "Transfer-Encoding: chunked\r\n"; - } - else { - te = ""; - conn->bits.upload_chunky = FALSE; /* transfer-encoding was disabled, - so don't chunkify this! */ - } } Curl_safefree(conn->allocptr.host); ptr = checkheaders(data, "Host:"); - if(ptr && !data->state.this_is_a_follow) { + 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. */ + redirected request is being out on thin ice. Except if the host name + is the same as the first one! */ char *start = ptr+strlen("Host:"); - while(*start && isspace((int)*start )) + while(*start && ISSPACE(*start )) start++; ptr = start; /* start host-scanning here */ /* scan through the string to find the end (space or colon) */ - while(*ptr && !isspace((int)*ptr) && !(':'==*ptr)) + while(*ptr && !ISSPACE(*ptr) && !(':'==*ptr)) ptr++; if(ptr != start) { size_t len=ptr-start; + Curl_safefree(conn->allocptr.cookiehost); conn->allocptr.cookiehost = malloc(len+1); if(!conn->allocptr.cookiehost) return CURLE_OUT_OF_MEMORY; memcpy(conn->allocptr.cookiehost, start, len); conn->allocptr.cookiehost[len]=0; } +#endif conn->allocptr.host = NULL; } @@ -1410,8 +1816,8 @@ CURLcode Curl_http(struct connectdata *conn) uses the encoded host name! */ if(conn->host.dispname != conn->host.name) { char *url = data->change.url; - char *iPtr = strstr(url, conn->host.dispname); - if(iPtr) { + 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 @@ -1426,13 +1832,13 @@ CURLcode Curl_http(struct connectdata *conn) newurl = malloc(urllen + newlen - currlen + 1); if(newurl) { /* copy the part before the host name */ - memcpy(newurl, url, iPtr - url); + memcpy(newurl, url, ptr - url); /* append the new host name instead of the old */ - memcpy(newurl + (iPtr - url), conn->host.name, newlen); + memcpy(newurl + (ptr - url), conn->host.name, newlen); /* append the piece after the host name */ - memcpy(newurl + newlen + (iPtr - url), - iPtr + currlen, /* copy the trailing zero byte too */ - urllen - (iPtr-url) - currlen + 1); + 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; @@ -1448,6 +1854,7 @@ CURLcode Curl_http(struct connectdata *conn) /* 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() */ @@ -1457,8 +1864,10 @@ CURLcode Curl_http(struct connectdata *conn) } - if(!checkheaders(data, "Pragma:")) - http->p_pragma = "Pragma: no-cache\r\n"; + http->p_pragma = + (!checkheaders(data, "Pragma:") && + (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )? + "Pragma: no-cache\r\n":NULL; if(!checkheaders(data, "Accept:")) http->p_accept = "Accept: */*\r\n"; @@ -1466,7 +1875,7 @@ CURLcode Curl_http(struct connectdata *conn) if(( (HTTPREQ_POST == httpreq) || (HTTPREQ_POST_FORM == httpreq) || (HTTPREQ_PUT == httpreq) ) && - conn->resume_from) { + data->reqdata.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 @@ -1475,15 +1884,15 @@ CURLcode Curl_http(struct connectdata *conn) * file size before we continue this venture in the dark lands of HTTP. *********************************************************************/ - if(conn->resume_from < 0 ) { + if(data->reqdata.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! */ - conn->resume_from = 0; + data->reqdata.resume_from = 0; } - if(conn->resume_from) { + if(data->reqdata.resume_from) { /* do we still game? */ curl_off_t passed=0; @@ -1491,7 +1900,7 @@ CURLcode Curl_http(struct connectdata *conn) input. If we knew it was a proper file we could've just fseek()ed but we only have a stream here */ do { - size_t readthisamountnow = (size_t)(conn->resume_from - passed); + size_t readthisamountnow = (size_t)(data->reqdata.resume_from - passed); size_t actuallyread; if(readthisamountnow > BUFSIZE) @@ -1508,11 +1917,11 @@ CURLcode Curl_http(struct connectdata *conn) passed); return CURLE_READ_ERROR; } - } while(passed != conn->resume_from); /* loop until done */ + } while(passed != data->reqdata.resume_from); /* loop until done */ /* now, decrease the size of the read */ if(data->set.infilesize>0) { - data->set.infilesize -= conn->resume_from; + data->set.infilesize -= data->reqdata.resume_from; if(data->set.infilesize <= 0) { failf(data, "File already completely uploaded"); @@ -1522,7 +1931,7 @@ CURLcode Curl_http(struct connectdata *conn) /* we've passed, proceed as normal */ } } - if(conn->bits.use_range) { + if(data->reqdata.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 @@ -1533,19 +1942,19 @@ CURLcode Curl_http(struct connectdata *conn) /* 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", conn->range); + conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", data->reqdata.range); } else if((httpreq != HTTPREQ_GET) && !checkheaders(data, "Content-Range:")) { - if(conn->resume_from) { + if(data->reqdata.resume_from) { /* This is because "resume" was selected */ curl_off_t total_expected_size= - conn->resume_from + data->set.infilesize; + data->reqdata.resume_from + data->set.infilesize; conn->allocptr.rangeline = aprintf("Content-Range: bytes %s%" FORMAT_OFF_T "/%" FORMAT_OFF_T "\r\n", - conn->range, total_expected_size-1, + data->reqdata.range, total_expected_size-1, total_expected_size); } else { @@ -1553,7 +1962,7 @@ CURLcode Curl_http(struct connectdata *conn) append total size */ conn->allocptr.rangeline = aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n", - conn->range, data->set.infilesize); + data->reqdata.range, data->set.infilesize); } } } @@ -1564,7 +1973,6 @@ CURLcode Curl_http(struct connectdata *conn) data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1"; send_buffer *req_buffer; - struct curl_slist *headers=data->set.headers; curl_off_t postsize; /* off_t type to be able to hold a large file size */ /* initialize a dynamic send-buffer */ @@ -1587,6 +1995,7 @@ CURLcode Curl_http(struct connectdata *conn) "%s" /* accept */ "%s" /* accept-encoding */ "%s" /* referer */ + "%s" /* Proxy-Connection */ "%s",/* transfer-encoding */ request, @@ -1595,7 +2004,7 @@ CURLcode Curl_http(struct connectdata *conn) conn->allocptr.proxyuserpwd? conn->allocptr.proxyuserpwd:"", conn->allocptr.userpwd?conn->allocptr.userpwd:"", - (conn->bits.use_range && conn->allocptr.rangeline)? + (data->reqdata.use_range && conn->allocptr.rangeline)? conn->allocptr.rangeline:"", (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)? conn->allocptr.uagent:"", @@ -1605,12 +2014,17 @@ CURLcode Curl_http(struct connectdata *conn) (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)? conn->allocptr.accept_encoding:"", (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> */, + (conn->bits.httpproxy && + !conn->bits.tunnel_proxy && + !checkheaders(data, "Proxy-Connection:"))? + "Proxy-Connection: Keep-Alive\r\n":"", te ); if(result) return result; +#if !defined(CURL_DISABLE_COOKIES) if(data->cookies || addcookies) { struct Cookie *co=NULL; /* no cookies from start */ int count=0; @@ -1619,7 +2033,7 @@ CURLcode Curl_http(struct connectdata *conn) 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, ppath, + conn->allocptr.cookiehost:host, data->reqdata.path, (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE)); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } @@ -1660,9 +2074,10 @@ CURLcode Curl_http(struct connectdata *conn) if(result) return result; } +#endif if(data->set.timecondition) { - struct tm *thistime; + struct tm *tm; /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since * header family should have their times set in GMT as RFC2616 defines: @@ -1674,18 +2089,22 @@ CURLcode Curl_http(struct connectdata *conn) #ifdef HAVE_GMTIME_R /* thread-safe version */ struct tm keeptime; - thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime); + tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime); #else - thistime = gmtime(&data->set.timevalue); + tm = gmtime(&data->set.timevalue); #endif -#ifdef HAVE_STRFTIME /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime); -#else - /* TODO: Right, we *could* write a replacement here */ - strcpy(buf, "no strftime() support"); -#endif + 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: @@ -1705,25 +2124,9 @@ CURLcode Curl_http(struct connectdata *conn) return result; } - 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((int)*ptr)) - ptr++; - - if(*ptr) { - /* only send this if the contents was non-blank */ - - result = add_bufferf(req_buffer, "%s\r\n", headers->data); - if(result) - return result; - } - } - headers = headers->next; - } + 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 */ @@ -1735,6 +2138,24 @@ CURLcode Curl_http(struct connectdata *conn) 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; @@ -1755,23 +2176,13 @@ CURLcode Curl_http(struct connectdata *conn) return result; } - if(!checkheaders(data, "Expect:")) { - /* if not disabled explicitly we add a Expect: 100-continue - to the headers which actually speeds up post operations (as - there is one packet coming back from the web server) */ - result = add_bufferf(req_buffer, - "Expect: 100-continue\r\n"); - if(result) - return result; - data->set.expect100header = TRUE; - } + result = expect100(data, req_buffer); + if(result) + return result; - if(!checkheaders(data, "Content-Type:")) { - /* Get Content-Type: line from Curl_formpostheader. + { - The Content-Type header line also contains the MIME boundary - string etc why disabling this header is likely to not make things - work, but we support disabling it anyway. + /* Get Content-Type: line from Curl_formpostheader. */ char *contentType; size_t linelength=0; @@ -1781,6 +2192,7 @@ CURLcode Curl_http(struct connectdata *conn) 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; @@ -1796,61 +2208,68 @@ CURLcode Curl_http(struct connectdata *conn) /* fire away the whole request to the server */ result = add_buffer_send(req_buffer, conn, - &data->info.request_size); + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); else /* setup variables for the upcoming transfer */ - result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, - FIRSTSOCKET, - &http->writebytecount); + result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, + &http->readbytecount, + FIRSTSOCKET, + &http->writebytecount); + if(result) { - Curl_formclean(http->sendit); /* free that whole lot */ + 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((data->set.infilesize>0) && !conn->bits.upload_chunky) { + if(conn->bits.authneg) + postsize = 0; + else + postsize = data->set.infilesize; + + if((postsize != -1) && !conn->bits.upload_chunky) { /* only add Content-Length if not uploading chunked */ result = add_bufferf(req_buffer, - "Content-Length: %" FORMAT_OFF_T "\r\n", /* size */ - data->set.infilesize ); + "Content-Length: %" FORMAT_OFF_T "\r\n", + postsize ); if(result) return result; } - if(!checkheaders(data, "Expect:")) { - /* if not disabled explicitly we add a Expect: 100-continue - to the headers which actually speeds up post operations (as - there is one packet coming back from the web server) */ - result = add_bufferf(req_buffer, - "Expect: 100-continue\r\n"); - if(result) - return result; - data->set.expect100header = TRUE; - } + result = expect100(data, 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, data->set.infilesize); + 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); + &data->info.request_size, 0, FIRSTSOCKET); if(result) - failf(data, "Failed sending POST request"); + failf(data, "Failed sending PUT request"); else /* prepare for transfer */ - result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, - FIRSTSOCKET, - &http->writebytecount); + result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, + &http->readbytecount, + postsize?FIRSTSOCKET:-1, + postsize?&http->writebytecount:NULL); if(result) return result; break; @@ -1858,10 +2277,13 @@ CURLcode Curl_http(struct connectdata *conn) case HTTPREQ_POST: /* this is the simple POST, using x-www-form-urlencoded style */ - /* store the size of the postfields */ - postsize = data->set.postfieldsize? - data->set.postfieldsize: - (data->set.postfields?(curl_off_t)strlen(data->set.postfields):0); + 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(!conn->bits.upload_chunky) { /* We only set Content-Length and allow a custom Content-Length if @@ -1888,15 +2310,24 @@ CURLcode Curl_http(struct connectdata *conn) if(data->set.postfields) { - if((data->state.authhost.done || data->state.authproxy.done ) - && (postsize < (100*1024))) { - /* If we're not done with the authentication phase, we don't expect - to actually send off any data yet. Hence, we delay the sending of - the body until we receive that friendly 100-continue response */ + /* 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 */ + if(postsize > TINY_INITIAL_POST_SIZE) { + result = expect100(data, req_buffer); + if(result) + return result; + } + else + data->state.expect100header = FALSE; - /* The post data is less than 100K, then append it to the header. - This limit is no magic limit but only set to prevent really huge - POSTs to get the data duplicated with malloc() and family. */ + 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) @@ -1907,6 +2338,7 @@ CURLcode Curl_http(struct connectdata *conn) 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 */ @@ -1916,8 +2348,9 @@ CURLcode Curl_http(struct connectdata *conn) (size_t)postsize); if(CURLE_OK == result) result = add_buffer(req_buffer, - "\r\n0\r\n\r\n", 7); /* end of a chunked - transfer stream */ + "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7); + /* CR LF 0 CR LF CR LF */ + included_body = postsize + 7; } if(result) return result; @@ -1935,37 +2368,30 @@ CURLcode Curl_http(struct connectdata *conn) /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, http->postsize); - if(!checkheaders(data, "Expect:")) { - /* if not disabled explicitly we add a Expect: 100-continue to the - headers which actually speeds up post operations (as there is - one packet coming back from the web server) */ - add_bufferf(req_buffer, - "Expect: 100-continue\r\n"); - data->set.expect100header = TRUE; - } - add_buffer(req_buffer, "\r\n", 2); /* end of headers! */ } } else { add_buffer(req_buffer, "\r\n", 2); /* end of headers! */ - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, data->set.infilesize); + 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 */ - http->postdata = (char *)&http->postdata; + /* set the pointer to mark that we will send the post body using + the read callback */ + http->postdata = (char *)&http->postdata; + } } /* issue the request */ - result = add_buffer_send(req_buffer, conn, - &data->info.request_size); + 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_Transfer(conn, FIRSTSOCKET, -1, TRUE, + Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, http->postdata?FIRSTSOCKET:-1, http->postdata?&http->writebytecount:NULL); @@ -1976,13 +2402,13 @@ CURLcode Curl_http(struct connectdata *conn) /* issue the request */ result = add_buffer_send(req_buffer, conn, - &data->info.request_size); + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP request"); else /* HTTP GET/HEAD download: */ - result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, + result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, http->postdata?FIRSTSOCKET:-1, http->postdata?&http->writebytecount:NULL); diff --git a/Utilities/cmcurl/http.h b/Utilities/cmcurl/http.h index 80c1807..0f4b58f 100644 --- a/Utilities/cmcurl/http.h +++ b/Utilities/cmcurl/http.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,14 +29,18 @@ bool Curl_compareheader(char *headerline, /* line to check */ const char *content); /* content string to find */ /* ftp can use this as well */ -CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, - int tunnelsocket, - char *hostname, int remote_port); +CURLcode Curl_proxyCONNECT(struct connectdata *conn, + int tunnelsocket, + char *hostname, int remote_port); /* protocol-specific functions set up to be called by the main engine */ -CURLcode Curl_http(struct connectdata *conn); -CURLcode Curl_http_done(struct connectdata *, CURLcode); -CURLcode Curl_http_connect(struct connectdata *conn); +CURLcode Curl_http(struct connectdata *conn, bool *done); +CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature); +CURLcode Curl_http_connect(struct connectdata *conn, bool *done); +CURLcode Curl_https_connecting(struct connectdata *conn, bool *done); +int Curl_https_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); /* The following functions are defined in http_chunks.c */ void Curl_httpchunk_init(struct connectdata *conn); @@ -57,5 +61,25 @@ int Curl_http_should_fail(struct connectdata *conn); public curl/curl.h header. */ #define CURLAUTH_PICKNONE (1<<30) /* don't use auth */ +/* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST + data get included in the initial data chunk sent to the server. If the + data is larger than this, it will automatically get split up in multiple + system calls. + + This value used to be fairly big (100K), but we must take into account that + if the server rejects the POST due for authentication reasons, this data + will always be uncondtionally sent and thus it may not be larger than can + always be afforded to send twice. + + It must not be greater than 64K to work on VMS. +*/ +#ifndef MAX_INITIAL_POST_SIZE +#define MAX_INITIAL_POST_SIZE (64*1024) +#endif + +#ifndef TINY_INITIAL_POST_SIZE +#define TINY_INITIAL_POST_SIZE 1024 +#endif + #endif #endif diff --git a/Utilities/cmcurl/http_chunks.c b/Utilities/cmcurl/http_chunks.c index 02fdfc5..1b03a55 100644 --- a/Utilities/cmcurl/http_chunks.c +++ b/Utilities/cmcurl/http_chunks.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -35,7 +35,8 @@ #include "content_encoding.h" #include "http.h" -#include "curl_memory.h" +#include "memory.h" +#include "easyif.h" /* for Curl_convert_to_network prototype */ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -43,7 +44,7 @@ /* The last #include file should be: */ #include "memdebug.h" -/* +/* * Chunk format (simplified): * * <HEX SIZE>[ chunk extension ] CRLF @@ -83,7 +84,7 @@ void Curl_httpchunk_init(struct connectdata *conn) { - struct Curl_chunker *chunk = &conn->proto.http->chunk; + struct Curl_chunker *chunk = &conn->data->reqdata.proto.http->chunk; chunk->hexindex=0; /* start at 0 */ chunk->dataleft=0; /* no data left yet! */ chunk->state = CHUNK_HEX; /* we get hex first! */ @@ -96,6 +97,9 @@ void Curl_httpchunk_init(struct connectdata *conn) * client (for byte-counting and whatever). * * The states and the state-machine is further explained in the header file. + * + * This function always uses ASCII hex values to accommodate non-ASCII hosts. + * For example, 0x0d and 0x0a are used instead of '\r' and '\n'. */ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, @@ -103,8 +107,9 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, ssize_t *wrotep) { CURLcode result=CURLE_OK; - struct Curl_chunker *ch = &conn->proto.http->chunk; - struct Curl_transfer_keeper *k = &conn->keep; + struct SessionHandle *data = conn->data; + struct Curl_chunker *ch = &data->reqdata.proto.http->chunk; + struct Curl_transfer_keeper *k = &data->reqdata.keep; size_t piece; size_t length = (size_t)datalen; size_t *wrote = (size_t *)wrotep; @@ -114,7 +119,11 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, while(length) { switch(ch->state) { case CHUNK_HEX: - if(isxdigit((int)*datap)) { + /* 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++; @@ -133,6 +142,17 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, } /* 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; } @@ -142,7 +162,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, /* 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 == '\r') + if(*datap == 0x0d) ch->state = CHUNK_CR; length--; datap++; @@ -150,13 +170,20 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, case CHUNK_CR: /* waiting for the LF */ - if(*datap == '\n') { + if(*datap == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { - ch->state = CHUNK_STOP; /* stop reading! */ - if(1 == length) { - /* This was the final byte, return right now */ - return CHUNKE_STOP; + if (conn->bits.trailerHdrPresent!=TRUE) { + /* No Trailer: header found - revert to original Curl processing */ + ch->state = CHUNK_STOP; + if (1 == length) { + /* This is the final byte, return right now */ + return CHUNKE_STOP; + } + } + else { + ch->state = CHUNK_TRAILER; /* attempt to read trailers */ + conn->trlPos=0; } } else @@ -179,26 +206,26 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, /* Write the data portion available */ #ifdef HAVE_LIBZ - switch (conn->keep.content_encoding) { + switch (data->reqdata.keep.content_encoding) { case IDENTITY: #endif if(!k->ignorebody) - result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap, + result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece); #ifdef HAVE_LIBZ break; - case DEFLATE: - /* update conn->keep.str to point to the chunk data. */ - conn->keep.str = datap; - result = Curl_unencode_deflate_write(conn->data, &conn->keep, + case DEFLATE: + /* update data->reqdata.keep.str to point to the chunk data. */ + data->reqdata.keep.str = datap; + result = Curl_unencode_deflate_write(conn, &data->reqdata.keep, (ssize_t)piece); break; case GZIP: - /* update conn->keep.str to point to the chunk data. */ - conn->keep.str = datap; - result = Curl_unencode_gzip_write(conn->data, &conn->keep, + /* update data->reqdata.keep.str to point to the chunk data. */ + data->reqdata.keep.str = datap; + result = Curl_unencode_gzip_write(conn, &data->reqdata.keep, (ssize_t)piece); break; @@ -227,7 +254,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, break; case CHUNK_POSTCR: - if(*datap == '\r') { + if(*datap == 0x0d) { ch->state = CHUNK_POSTLF; datap++; length--; @@ -237,7 +264,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, break; case CHUNK_POSTLF: - if(*datap == '\n') { + if(*datap == 0x0a) { /* * The last one before we go back to hex state and start all * over. @@ -250,6 +277,75 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, 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 = (char*)realloc(conn->trailer,conn->trlMax); + } + else { + conn->trlMax=128; + ptr = (char*)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; + return CHUNKE_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 */ + Curl_client_write(conn, CLIENTWRITE_HEADER, + conn->trailer, conn->trlPos); + } + ch->state = CHUNK_TRAILER; + conn->trlPos=0; + datap++; + length--; + } + else + return CHUNKE_BAD_CHUNK; + break; + case CHUNK_STOP: /* If we arrive here, there is data left in the end of the buffer even if there's no more chunks to read */ diff --git a/Utilities/cmcurl/http_chunks.h b/Utilities/cmcurl/http_chunks.h index 26b79de..211818a 100644 --- a/Utilities/cmcurl/http_chunks.h +++ b/Utilities/cmcurl/http_chunks.h @@ -1,18 +1,18 @@ #ifndef __HTTP_CHUNKS_H #define __HTTP_CHUNKS_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -52,8 +52,8 @@ typedef enum { /* POSTCR should get a CR and nothing else, then move to POSTLF */ CHUNK_POSTCR, - /* POSTLF should get a LF and nothing else, then move back to HEX as - the CRLF combination marks the end of a chunk */ + /* POSTLF should get a LF and nothing else, then move back to HEX as the + CRLF combination marks the end of a chunk */ CHUNK_POSTLF, /* This is mainly used to really mark that we're out of the game. @@ -62,7 +62,22 @@ typedef enum { buffer! */ CHUNK_STOP, + /* At this point optional trailer headers can be found, unless the next line + is CRLF */ + CHUNK_TRAILER, + + /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR. + Next char must be a LF */ + CHUNK_TRAILER_CR, + + /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be + signalled If this is an empty trailer CHUNKE_STOP will be signalled. + Otherwise the trailer will be broadcasted via Curl_client_write() and the + next state will be CHUNK_TRAILER */ + CHUNK_TRAILER_POSTCR, + CHUNK_LAST /* never use */ + } ChunkyState; typedef enum { @@ -74,6 +89,7 @@ typedef enum { CHUNKE_WRITE_ERROR, CHUNKE_STATE_ERROR, CHUNKE_BAD_ENCODING, + CHUNKE_OUT_OF_MEMORY, CHUNKE_LAST } CHUNKcode; diff --git a/Utilities/cmcurl/http_digest.c b/Utilities/cmcurl/http_digest.c index b997ccb..c223784 100644 --- a/Utilities/cmcurl/http_digest.c +++ b/Utilities/cmcurl/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ ***************************************************************************/ #include "setup.h" -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) /* -- WIN32 approved -- */ #include <stdio.h> #include <string.h> @@ -38,7 +38,8 @@ #include "http_digest.h" #include "strtok.h" #include "url.h" /* for Curl_safefree() */ -#include "curl_memory.h" +#include "memory.h" +#include "easyif.h" /* included for Curl_convert_... prototypes */ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -59,8 +60,8 @@ CURLdigest Curl_input_digest(struct connectdata *conn, header */ { bool more = TRUE; - char *token; - char *tmp; + char *token = NULL; + char *tmp = NULL; bool foundAuth = FALSE; bool foundAuthInt = FALSE; struct SessionHandle *data=conn->data; @@ -75,7 +76,7 @@ CURLdigest Curl_input_digest(struct connectdata *conn, } /* skip initial whitespaces */ - while(*header && isspace((int)*header)) + while(*header && ISSPACE(*header)) header++; if(checkprefix("Digest", header)) { @@ -91,9 +92,9 @@ CURLdigest Curl_input_digest(struct connectdata *conn, while(more) { char value[32]; char content[128]; - size_t totlen; + size_t totlen=0; - while(*header && isspace((int)*header)) + while(*header && ISSPACE(*header)) header++; /* how big can these strings be? */ @@ -224,7 +225,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[7]; char *cnonce; - char *tmp; + char *tmp = NULL; struct timeval now; char **allocuserpwd; @@ -234,6 +235,21 @@ CURLcode Curl_output_digest(struct connectdata *conn, 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; @@ -270,7 +286,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, /* Generate a cnonce */ now = Curl_tvnow(); snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec); - if(Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), &cnonce)) + if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce)) d->cnonce = cnonce; else return CURLE_OUT_OF_MEMORY; @@ -291,6 +307,8 @@ CURLcode Curl_output_digest(struct connectdata *conn, 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 */ @@ -303,10 +321,12 @@ CURLcode Curl_output_digest(struct connectdata *conn, if(d->algo == CURLDIGESTALGO_MD5SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce); - free(ha1); if(!tmp) return CURLE_OUT_OF_MEMORY; - ha1 = (unsigned char *)tmp; + 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); } /* @@ -333,6 +353,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, 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); @@ -356,6 +377,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, 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); diff --git a/Utilities/cmcurl/http_digest.h b/Utilities/cmcurl/http_digest.h index b4fca06..6cf0259 100644 --- a/Utilities/cmcurl/http_digest.h +++ b/Utilities/cmcurl/http_digest.h @@ -1,18 +1,18 @@ #ifndef __HTTP_DIGEST_H #define __HTTP_DIGEST_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -47,7 +47,12 @@ CURLcode Curl_output_digest(struct connectdata *conn, bool proxy, unsigned char *request, unsigned char *uripath); -void Curl_digest_cleanup(struct SessionHandle *data); void Curl_digest_cleanup_one(struct digestdata *dig); +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +void Curl_digest_cleanup(struct SessionHandle *data); +#else +#define Curl_digest_cleanup(x) do {} while(0) +#endif + #endif diff --git a/Utilities/cmcurl/http_negotiate.c b/Utilities/cmcurl/http_negotiate.c index 62a23f1..bdfeefa 100644 --- a/Utilities/cmcurl/http_negotiate.c +++ b/Utilities/cmcurl/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,14 +34,13 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> -#include <errno.h> #include "urldata.h" #include "sendf.h" #include "strequal.h" #include "base64.h" #include "http_negotiate.h" -#include "curl_memory.h" +#include "memory.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -111,7 +110,7 @@ log_gss_error(struct connectdata *conn, OM_uint32 error_status, char *prefix) gss_release_buffer(&min_stat, &status_string); } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); - infof(conn->data, buf); + infof(conn->data, "%s", buf); } int Curl_input_negotiate(struct connectdata *conn, char *header) @@ -125,7 +124,7 @@ int Curl_input_negotiate(struct connectdata *conn, char *header) bool gss; const char* protocol; - while(*header && isspace((int)*header)) + while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; @@ -161,17 +160,12 @@ int Curl_input_negotiate(struct connectdata *conn, char *header) return ret; header += strlen(neg_ctx->protocol); - while(*header && isspace((int)*header)) + while(*header && ISSPACE(*header)) header++; len = strlen(header); if (len > 0) { - int rawlen; - input_token.length = (len+3)/4 * 3; - input_token.value = malloc(input_token.length); - if (input_token.value == NULL) - return ENOMEM; - rawlen = Curl_base64_decode(header, input_token.value); + int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value); if (rawlen < 0) return -1; input_token.length = rawlen; @@ -211,7 +205,7 @@ int Curl_input_negotiate(struct connectdata *conn, char *header) input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; - infof(conn->data, "Parse SPNEGO Target Token succeded\n"); + infof(conn->data, "Parse SPNEGO Target Token succeeded\n"); } } #endif @@ -292,11 +286,12 @@ CURLcode Curl_output_negotiate(struct connectdata *conn) neg_ctx->output_token.length = spnegoTokenLength; free(spnegoToken); spnegoToken = NULL; - infof(conn->data, "Make SPNEGO Initial Token succeded\n"); + infof(conn->data, "Make SPNEGO Initial Token succeeded\n"); } } #endif - len = Curl_base64_encode(neg_ctx->output_token.value, + len = Curl_base64_encode(conn->data, + neg_ctx->output_token.value, neg_ctx->output_token.length, &encoded); diff --git a/Utilities/cmcurl/http_ntlm.c b/Utilities/cmcurl/http_ntlm.c index fe0b653..aff1bb1 100644 --- a/Utilities/cmcurl/http_ntlm.c +++ b/Utilities/cmcurl/http_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,11 +27,15 @@ http://davenport.sourceforge.net/ntlm.html http://www.innovation.ch/java/ntlm.html + Another implementation: + http://lxr.mozilla.org/mozilla/source/security/manager/ssl/src/nsNTLMAuthModule.cpp + */ #ifndef CURL_DISABLE_HTTP -#ifdef USE_SSLEAY -/* We need OpenSSL for the crypto lib to provide us with MD4 and DES */ +#ifdef USE_NTLM + +#define DEBUG_ME 0 /* -- WIN32 approved -- */ #include <stdio.h> @@ -40,20 +44,33 @@ #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + #include "urldata.h" +#include "easyif.h" /* for Curl_convert_... prototypes */ #include "sendf.h" #include "strequal.h" #include "base64.h" #include "http_ntlm.h" #include "url.h" -#include "curl_memory.h" +#include "memory.h" +#include "ssluse.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> +/* "NTLMSSP" signature is always in ASCII regardless of the platform */ +#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" + +#ifndef USE_WINDOWS_SSPI + #include <openssl/des.h> #include <openssl/md4.h> +#include <openssl/md5.h> #include <openssl/ssl.h> +#include <openssl/rand.h> #if OPENSSL_VERSION_NUMBER < 0x00907001L #define DES_key_schedule des_key_schedule @@ -71,12 +88,119 @@ #define DESKEY(x) &x #endif +#else + +#include <rpc.h> + +/* Handle of security.dll or secur32.dll, depending on Windows version */ +static HMODULE s_hSecDll = NULL; +/* Pointer to SSPI dispatch table */ +static PSecurityFunctionTable s_pSecFn = NULL; + +#endif + /* The last #include file should be: */ #include "memdebug.h" /* Define this to make the type-3 message include the NT response message */ #define USE_NTRESPONSES 1 +/* Define this to make the type-3 message include the NTLM2Session response + message, requires USE_NTRESPONSES. */ +#define USE_NTLM2SESSION 1 + +#ifndef USE_WINDOWS_SSPI +/* this function converts from the little endian format used in the incoming + package to whatever endian format we're using natively */ +static unsigned int readint_le(unsigned char *buf) /* must point to a + 4 bytes buffer*/ +{ + return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) | + ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); +} +#endif + +#if DEBUG_ME +# define DEBUG_OUT(x) x +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 & (1<<11)) + fprintf(handle, "NTLMFLAG_UNKNOWN_11 "); + 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 "); +} + +static void print_hex(FILE *handle, const char *buf, size_t len) +{ + const char *p = buf; + fprintf(stderr, "0x"); + while (len-- > 0) + fprintf(stderr, "%02.2x", (unsigned int)*p++); +} +#else +# define DEBUG_OUT(x) +#endif + /* (*) = A "security buffer" is a triplet consisting of two shorts and one long: @@ -95,18 +219,20 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, { /* 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((int)*header)) + while(*header && ISSPACE(*header)) header++; if(checkprefix("NTLM", header)) { - unsigned char buffer[256]; header += strlen("NTLM"); - while(*header && isspace((int)*header)) + while(*header && ISSPACE(*header)) header++; if(*header) { @@ -123,17 +249,47 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, (40) Target Information (optional) security buffer(*) 32 (48) start of data block */ - - size_t size = Curl_base64_decode(header, (char *)buffer); + 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 */ - if(size >= 48) - /* the nonce of interest is index [24 .. 31], 8 bytes */ - memcpy(ntlm->nonce, &buffer[24], 8); +#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; + } - /* at index decimal 20, there's a 32bit NTLM flag field */ + 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) @@ -145,6 +301,8 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, return CURLNTLM_FINE; } +#ifndef USE_WINDOWS_SSPI + /* * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * key schedule ks is also set. @@ -155,13 +313,13 @@ static void setup_des_key(unsigned char *key_56, DES_cblock key; key[0] = key_56[0]; - key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); - key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); - key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); - key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); - key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); - key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); - key[7] = (key_56[6] << 1) & 0xFF; + key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); + key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); + key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); + key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); + key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); + key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); + key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); DES_set_odd_parity(&key); DES_set_key(&key, ks); @@ -172,7 +330,7 @@ static void setup_des_key(unsigned char *key_56, * 8 byte plaintext is encrypted with each key and the resulting 24 * bytes are stored in the results array. */ -static void calc_resp(unsigned char *keys, +static void lm_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results) { @@ -191,44 +349,44 @@ static void calc_resp(unsigned char *keys, DESKEY(ks), DES_ENCRYPT); } + /* - * Set up lanmanager and nt hashed passwords + * Set up lanmanager hashed password */ -static void mkhash(char *password, - unsigned char *nonce, /* 8 bytes */ - unsigned char *lmresp /* must fit 0x18 bytes */ -#ifdef USE_NTRESPONSES - , unsigned char *ntresp /* must fit 0x18 bytes */ -#endif - ) +static void mk_lm_hash(struct SessionHandle *data, + char *password, + unsigned char *lmbuffer /* 21 bytes */) { - unsigned char lmbuffer[21]; -#ifdef USE_NTRESPONSES - unsigned char ntbuffer[21]; -#endif - unsigned char *pw; + unsigned char pw[14]; static const unsigned char magic[] = { - 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 + 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ }; unsigned int i; size_t len = strlen(password); - /* make it fit at least 14 bytes */ - pw = malloc(len<7?14:len*2); - if(!pw) - return; /* this will lead to a badly generated package */ - if (len > 14) len = 14; for (i=0; i<len; i++) - pw[i] = toupper(password[i]); + pw[i] = (unsigned char)toupper(password[i]); for (; i<14; i++) pw[i] = 0; +#ifdef CURL_DOES_CONVERSIONS + /* + * The LanManager hashed password needs to be created using the + * password in the network encoding not the host encoding. + */ + if(data) + Curl_convert_to_network(data, (char *)pw, 14); +#else + (void)data; +#endif + { - /* create LanManager hashed password */ + /* Create LanManager hashed password. */ + DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); @@ -239,53 +397,108 @@ static void mkhash(char *password, DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8), DESKEY(ks), DES_ENCRYPT); - memset(lmbuffer+16, 0, 5); + memset(lmbuffer + 16, 0, 21 - 16); + } + } + +#if USE_NTRESPONSES +static void utf8_to_unicode_le(unsigned char *dest, const char *src, + size_t srclen) +{ + size_t i; + for (i=0; i<srclen; i++) { + dest[2*i] = (unsigned char)src[i]; + dest[2*i+1] = '\0'; } - /* create LM responses */ - calc_resp(lmbuffer, nonce, lmresp); +} -#ifdef USE_NTRESPONSES - { - /* create NT hashed password */ - MD4_CTX MD4; +/* + * Set up nt hashed passwords + */ +static void mk_nt_hash(struct SessionHandle *data, + char *password, + unsigned char *ntbuffer /* 21 bytes */) +{ + size_t len = strlen(password); + unsigned char *pw = malloc(len*2); - len = strlen(password); + utf8_to_unicode_le(pw, password, len); - for (i=0; i<len; i++) { - pw[2*i] = password[i]; - pw[2*i+1] = 0; - } +#ifdef CURL_DOES_CONVERSIONS + /* + * The NT hashed password needs to be created using the + * password in the network encoding not the host encoding. + */ + if(data) + Curl_convert_to_network(data, (char *)pw, len*2); +#else + (void)data; +#endif + + { + /* Create NT hashed password. */ + MD4_CTX MD4; MD4_Init(&MD4); MD4_Update(&MD4, pw, 2*len); MD4_Final(ntbuffer, &MD4); - memset(ntbuffer+16, 0, 8); + memset(ntbuffer + 16, 0, 21 - 16); } - calc_resp(ntbuffer, nonce, ntresp); + free(pw); +} #endif - free(pw); + +#endif + +#ifdef USE_WINDOWS_SSPI + +static void +ntlm_sspi_cleanup(struct ntlmdata *ntlm) +{ + if (ntlm->type_2) { + free(ntlm->type_2); + ntlm->type_2 = NULL; + } + if (ntlm->has_handles) { + s_pSecFn->DeleteSecurityContext(&ntlm->c_handle); + s_pSecFn->FreeCredentialsHandle(&ntlm->handle); + ntlm->has_handles = 0; + } + if (ntlm->p_identity) { + if (ntlm->identity.User) free(ntlm->identity.User); + if (ntlm->identity.Password) free(ntlm->identity.Password); + if (ntlm->identity.Domain) free(ntlm->identity.Domain); + ntlm->p_identity = NULL; + } } -#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8) +#endif + +#define SHORTPAIR(x) ((x) & 0xff), (((x) >> 8) & 0xff) #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \ - (((x) >>16)&0xff), ((x)>>24) + (((x) >>16)&0xff), (((x)>>24) & 0xff) + +#define HOSTNAME_MAX 1024 /* this is for creating ntlm header output */ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) { const char *domain=""; /* empty */ - const char *host=""; /* empty */ - int domlen=(int)strlen(domain); - int hostlen = (int)strlen(host); - int hostoff; /* host name offset */ - int domoff; /* domain name offset */ + 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[256]; /* enough, unless the host/domain is very long */ + 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 */ @@ -324,11 +537,128 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, if(!passwdp) passwdp=(char *)""; +#ifdef USE_WINDOWS_SSPI + /* If security interface is not yet initialized try to do this */ + if (s_hSecDll == NULL) { + /* Determine Windows version. Security functions are located in + * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP + * contain both these DLLs (security.dll just forwards calls to + * secur32.dll) + */ + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(osver); + GetVersionEx(&osver); + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT + && osver.dwMajorVersion == 4) + s_hSecDll = LoadLibrary("security.dll"); + else + s_hSecDll = LoadLibrary("secur32.dll"); + if (s_hSecDll != NULL) { + INIT_SECURITY_INTERFACE pInitSecurityInterface; + pInitSecurityInterface = + (INIT_SECURITY_INTERFACE)GetProcAddress(s_hSecDll, + "InitSecurityInterfaceA"); + if (pInitSecurityInterface != NULL) + s_pSecFn = pInitSecurityInterface(); + } + } + if (s_pSecFn == NULL) + return CURLE_RECV_ERROR; +#endif + switch(ntlm->state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ - hostoff = 32; - domoff = hostoff + hostlen; +#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: @@ -342,8 +672,12 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, 32 start of data block */ - - snprintf((char *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%c" +#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 */ @@ -360,9 +694,11 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, 0,0,0, /* part of type-1 long */ LONGQUARTET( - NTLMFLAG_NEGOTIATE_OEM| /* 2 */ - NTLMFLAG_NEGOTIATE_NTLM_KEY /* 200 */ - /* equals 0x0202 */ + NTLMFLAG_NEGOTIATE_OEM| + NTLMFLAG_REQUEST_TARGET| + NTLMFLAG_NEGOTIATE_NTLM_KEY| + NTLM2FLAG| + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ), SHORTPAIR(domlen), SHORTPAIR(domlen), @@ -372,19 +708,42 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, SHORTPAIR(hostlen), SHORTPAIR(hostoff), 0,0, - host, domain); + host /* this is empty */, domain /* this is empty */); /* initial packet length */ size = 32 + hostlen + domlen; +#endif - /* now keeper of the base64 encoded package size */ - size = Curl_base64_encode((char *)ntlmbuf, size, &base64); + 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 @@ -393,7 +752,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, break; case NTLMSTATE_TYPE2: - /* We received the type-2 already, create a type-3 message: + /* We received the type-2 message already, create a type-3 message: Index Description Content 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" @@ -411,15 +770,51 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, */ { +#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; - int useroff; - unsigned char lmresp[0x18]; /* fixed-size */ -#ifdef USE_NTRESPONSES - unsigned char ntresp[0x18]; /* fixed-size */ + unsigned char ntresp[24]; /* fixed-size */ #endif + size_t useroff; const char *user; - int userlen; + size_t userlen; user = strchr(userp, '\\'); if(!user) @@ -427,31 +822,99 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, if (user) { domain = userp; - domlen = (int)(user - domain); + domlen = (user - domain); user++; } else user = userp; - userlen = (int)strlen(user); + 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 MD5; + unsigned char random[8]; + + /* Need to create 8 bytes random data */ + Curl_ossl_seed(conn->data); /* Initiate the seed if not already done */ + RAND_bytes(random,8); + + /* 8 bytes random data as challenge in lmresp */ + memcpy(lmresp,random,8); + /* Pad with zeros */ + memset(lmresp+8,0,0x10); + + /* Fill tmp with challenge(nonce?) + random */ + memcpy(tmp,&ntlm->nonce[0],8); + memcpy(tmp+8,random,8); + + MD5_Init(&MD5); + MD5_Update(&MD5, tmp, 16); + MD5_Final(md5sum, &MD5); + /* We shall only use the first 8 bytes of md5sum, + but the des code in lm_resp only encrypt the first 8 bytes */ + mk_nt_hash(conn->data, passwdp, ntbuffer); + lm_resp(ntbuffer, md5sum, ntresp); + + /* End of NTLM2 Session code */ + } + else { +#endif - mkhash(passwdp, &ntlm->nonce[0], lmresp -#ifdef USE_NTRESPONSES - , ntresp +#if USE_NTRESPONSES + unsigned char ntbuffer[0x18]; #endif - ); + unsigned char lmbuffer[0x18]; - domoff = 64; /* always */ +#if USE_NTRESPONSES + mk_nt_hash(conn->data, passwdp, ntbuffer); + 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; - lmrespoff = hostoff + hostlen; - ntrespoff = lmrespoff + 0x18; /* Create the big type-3 message binary blob */ size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf), - "NTLMSSP%c" + NTLMSSP_SIGNATURE "%c" "\x03%c%c%c" /* type-3, 32 bits */ - "%c%c%c%c" /* LanManager length + allocated space */ + "%c%c" /* LanManager length */ + "%c%c" /* LanManager allocated space */ "%c%c" /* LanManager offset */ "%c%c" /* 2 zeroes */ @@ -473,14 +936,15 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, "%c%c" /* host length */ "%c%c" /* host allocated space */ "%c%c" /* host offset */ - "%c%c%c%c%c%c" /* 6 zeroes */ - - "\xff\xff" /* message length */ "%c%c" /* 2 zeroes */ - "\x01\x82" /* flags */ + "%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 */ @@ -495,16 +959,17 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, SHORTPAIR(lmrespoff), 0x0, 0x0, -#ifdef USE_NTRESPONSES +#if USE_NTRESPONSES SHORTPAIR(0x18), /* NT-response length, twice */ SHORTPAIR(0x18), + SHORTPAIR(ntrespoff), + 0x0, 0x0, #else 0x0, 0x0, 0x0, 0x0, -#endif - SHORTPAIR(ntrespoff), 0x0, 0x0, - + 0x0, 0x0, +#endif SHORTPAIR(domlen), SHORTPAIR(domlen), SHORTPAIR(domoff), @@ -518,46 +983,89 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, SHORTPAIR(hostlen), SHORTPAIR(hostlen), SHORTPAIR(hostoff), - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + + LONGQUARTET(ntlm->flags)); + DEBUG_OUT(assert(size==64)); + + DEBUG_OUT(assert(size == 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)) { + DEBUG_OUT(assert(size == 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"); + }); - 0x0, 0x0); - /* size is now 64 */ - size=64; - ntlmbuf[62]=ntlmbuf[63]=0; + /* 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; + } + curlassert(size == domoff); memcpy(&ntlmbuf[size], domain, domlen); size += domlen; + curlassert(size == useroff); memcpy(&ntlmbuf[size], user, userlen); size += userlen; - /* we append the binary hashes to the end of the blob */ - if(size < ((int)sizeof(ntlmbuf) - 0x18)) { - memcpy(&ntlmbuf[size], lmresp, 0x18); - size += 0x18; - } + curlassert(size == hostoff); + memcpy(&ntlmbuf[size], host, hostlen); + size += hostlen; -#ifdef USE_NTRESPONSES - if(size < ((int)sizeof(ntlmbuf) - 0x18)) { - memcpy(&ntlmbuf[size], ntresp, 0x18); - size += 0x18; +#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 +#endif /* CURL_DOES_CONVERSIONS */ - ntlmbuf[56] = (unsigned char)(size & 0xff); - ntlmbuf[57] = (unsigned char)(size >> 8); +#endif /* convert the binary blob into base64 */ - size = Curl_base64_encode((char *)ntlmbuf, size, &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 @@ -581,5 +1089,23 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, return CURLE_OK; } -#endif /* USE_SSLEAY */ + + +void +Curl_ntlm_cleanup(struct connectdata *conn) +{ +#ifdef USE_WINDOWS_SSPI + ntlm_sspi_cleanup(&conn->ntlm); + ntlm_sspi_cleanup(&conn->proxyntlm); + if (s_hSecDll != NULL) { + FreeLibrary(s_hSecDll); + s_hSecDll = NULL; + s_pSecFn = NULL; + } +#else + (void)conn; +#endif +} + +#endif /* USE_NTLM */ #endif /* !CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/http_ntlm.h b/Utilities/cmcurl/http_ntlm.h index 4386a1c..a8de220 100644 --- a/Utilities/cmcurl/http_ntlm.h +++ b/Utilities/cmcurl/http_ntlm.h @@ -1,18 +1,18 @@ #ifndef __HTTP_NTLM_H #define __HTTP_NTLM_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -38,7 +38,10 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, char *header); /* this is for creating ntlm header output */ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); -void Curl_ntlm_cleanup(struct SessionHandle *data); +void Curl_ntlm_cleanup(struct connectdata *conn); +#ifndef USE_NTLM +#define Curl_ntlm_cleanup(x) +#endif /* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */ diff --git a/Utilities/cmcurl/if2ip.c b/Utilities/cmcurl/if2ip.c index 8bccc94..b4a98c6 100644 --- a/Utilities/cmcurl/if2ip.c +++ b/Utilities/cmcurl/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,8 +31,15 @@ #include <unistd.h> #endif -#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN32__) && \ - !defined(__riscos__) && !defined(__INTERIX) && !defined(NETWARE) +#include "if2ip.h" + +/* + * This test can probably be simplified to #if defined(SIOCGIFADDR) and + * moved after the following includes. + */ +#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN__) && \ + !defined(__riscos__) && !defined(__INTERIX) && !defined(NETWARE) && \ + !defined(_AMIGASF) && !defined(__minix) #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> @@ -55,7 +62,6 @@ #include <sys/ioctl.h> #endif -/* -- if2ip() -- */ #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -64,16 +70,12 @@ #include <sys/sockio.h> #endif -#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) -#include "inet_ntoa_r.h" -#endif - -#ifdef VMS +#ifdef VMS #include <inet.h> #endif -#include "if2ip.h" -#include "curl_memory.h" +#include "inet_ntop.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -100,7 +102,7 @@ char *Curl_if2ip(const char *interface, char *buf, int buf_size) return NULL; /* this can't be a fine interface name */ memcpy(req.ifr_name, interface, len+1); req.ifr_addr.sa_family = AF_INET; -#ifdef IOCTL_3_ARGS +#ifdef IOCTL_3_ARGS if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req)) { #else if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req, sizeof(req))) { @@ -111,19 +113,9 @@ char *Curl_if2ip(const char *interface, char *buf, int buf_size) else { struct in_addr in; - union { - struct sockaddr_in *sin; - struct sockaddr *s; - } soadd; - - soadd.s = &req.ifr_dstaddr; - memcpy(&in, &(soadd.sin->sin_addr.s_addr), sizeof(in)); -#if defined(HAVE_INET_NTOA_R) - ip = inet_ntoa_r(in,buf,buf_size); -#else - ip = strncpy(buf,inet_ntoa(in),buf_size); - ip[buf_size - 1] = 0; -#endif + struct sockaddr_in *s = (struct sockaddr_in *)&req.ifr_dstaddr; + memcpy(&in, &s->sin_addr, sizeof(in)); + ip = (char *) Curl_inet_ntop(s->sin_family, &in, buf, buf_size); } sclose(dummy); } @@ -132,9 +124,9 @@ char *Curl_if2ip(const char *interface, char *buf, int buf_size) /* -- end of if2ip() -- */ #else -char *Curl_if2ip(const char *interface, char *buf, int buf_size) +char *Curl_if2ip(const char *interf, char *buf, int buf_size) { - (void) interface; + (void) interf; (void) buf; (void) buf_size; return NULL; diff --git a/Utilities/cmcurl/if2ip.h b/Utilities/cmcurl/if2ip.h index 45a1805..4e86e2b 100644 --- a/Utilities/cmcurl/if2ip.h +++ b/Utilities/cmcurl/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,13 +24,11 @@ ***************************************************************************/ #include "setup.h" -#if !defined(WIN32) && !defined(__BEOS__) && !defined(__CYGWIN32__) && \ - !defined(__riscos__) && !defined(__INTERIX) -extern char *Curl_if2ip(const char *interface, char *buf, int buf_size); -#else -#define Curl_if2ip(a,b,c) NULL -#endif +extern char *Curl_if2ip(const char *interf, char *buf, int buf_size); + #ifdef __INTERIX +#include <sys/socket.h> + /* Nedelcho Stanev's work-around for SFU 3.0 */ struct ifreq { #define IFNAMSIZ 16 diff --git a/Utilities/cmcurl/inet_ntoa_r.h b/Utilities/cmcurl/inet_ntoa_r.h index 7959c49..c6c9bd8 100644 --- a/Utilities/cmcurl/inet_ntoa_r.h +++ b/Utilities/cmcurl/inet_ntoa_r.h @@ -1,5 +1,38 @@ #ifndef __INET_NTOA_R_H #define __INET_NTOA_R_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" + +#ifdef HAVE_INET_NTOA_R_2_ARGS +/* + * uClibc 0.9.26 (at least) doesn't define this prototype. The buffer + * must be at least 16 characters long. + */ +char *inet_ntoa_r(const struct in_addr in, char buffer[]); + +#else /* * My solaris 5.6 system running gcc 2.8.1 does *not* have this prototype * in any system include file! Isn't that weird? @@ -7,3 +40,5 @@ char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen); #endif + +#endif diff --git a/Utilities/cmcurl/inet_ntop.c b/Utilities/cmcurl/inet_ntop.c index 6809a92..9381963 100644 --- a/Utilities/cmcurl/inet_ntop.c +++ b/Utilities/cmcurl/inet_ntop.c @@ -1,4 +1,20 @@ /* + * Copyright (C) 1996-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* * Original code by Paul Vixie. "curlified" by Gisle Vanem. */ @@ -39,7 +55,7 @@ #define INADDRSZ 4 #define INT16SZ 2 -#ifdef WIN32 +#ifdef USE_WINSOCK #define EAFNOSUPPORT WSAEAFNOSUPPORT #define SET_ERRNO(e) WSASetLastError(errno = (e)) #else @@ -52,20 +68,21 @@ * Returns `dst' (as a const) * Note: * - uses no statics - * - takes a u_char* not an in_addr as input + * - takes a unsigned char* not an in_addr as input */ -static const char *inet_ntop4 (const u_char *src, char *dst, size_t size) +static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) { -#ifdef HAVE_INET_NTOA_R +#if defined(HAVE_INET_NTOA_R_2_ARGS) + const char *ptr; + curlassert(size >= 16); + ptr = inet_ntoa_r(*(struct in_addr*)src, dst); + return (char *)memmove(dst, ptr, strlen(ptr)+1); + +#elif defined(HAVE_INET_NTOA_R) return inet_ntoa_r(*(struct in_addr*)src, dst, size); + #else - union { - const u_char* uch; - const struct in_addr* iad; - } srcaddr; - const char *addr; - srcaddr.uch = src; - addr = inet_ntoa(*srcaddr.iad); + const char *addr = inet_ntoa(*(struct in_addr*)src); if (strlen(addr) >= size) { @@ -80,7 +97,7 @@ static const char *inet_ntop4 (const u_char *src, char *dst, size_t size) /* * Convert IPv6 binary address into presentation (printable) format. */ -static const char *inet_ntop6 (const u_char *src, char *dst, size_t size) +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 @@ -89,25 +106,28 @@ static const char *inet_ntop6 (const u_char *src, char *dst, size_t size) * 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 tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; char *tp; struct { long base; long len; } best, cur; - u_long words [IN6ADDRSZ / INT16SZ]; - int i; + 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)); + 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) @@ -184,17 +204,17 @@ static const char *inet_ntop6 (const u_char *src, char *dst, size_t size) /* * Convert a network format address to presentation format. * - * Returns pointer to presentation format address (`dst'), + * Returns pointer to presentation format address (`buf'), * Returns NULL on error (see errno). */ -const char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) +char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) { switch (af) { case AF_INET: - return inet_ntop4((const u_char*)src, buf, size); + return inet_ntop4((const unsigned char*)src, buf, size); #ifdef ENABLE_IPV6 case AF_INET6: - return inet_ntop6((const u_char*)src, buf, size); + return inet_ntop6((const unsigned char*)src, buf, size); #endif default: SET_ERRNO(EAFNOSUPPORT); diff --git a/Utilities/cmcurl/inet_ntop.h b/Utilities/cmcurl/inet_ntop.h index 5948a12..54d64bd 100644 --- a/Utilities/cmcurl/inet_ntop.h +++ b/Utilities/cmcurl/inet_ntop.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,13 +25,13 @@ #include "setup.h" +char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); + #ifdef HAVE_INET_NTOP -#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af,addr,buf,size) #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#else -const char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); +#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af,addr,buf,size) #endif #endif /* __INET_NTOP_H */ diff --git a/Utilities/cmcurl/inet_pton.c b/Utilities/cmcurl/inet_pton.c index ed74c26..9b9f88b 100644 --- a/Utilities/cmcurl/inet_pton.c +++ b/Utilities/cmcurl/inet_pton.c @@ -44,7 +44,7 @@ #define INADDRSZ 4 #define INT16SZ 2 -#ifdef WIN32 +#ifdef USE_WINSOCK #define EAFNOSUPPORT WSAEAFNOSUPPORT #endif @@ -72,21 +72,21 @@ static int inet_pton6(const char *src, unsigned char *dst); int Curl_inet_pton(int af, const char *src, void *dst) { - switch (af) { - case AF_INET: - return (inet_pton4(src, dst)); + switch (af) { + case AF_INET: + return (inet_pton4(src, (unsigned char *)dst)); #ifdef ENABLE_IPV6 #ifndef AF_INET6 -#define AF_INET6 AF_MAX+1 /* just to let this compile */ +#define AF_INET6 (AF_MAX+1) /* just to let this compile */ #endif - case AF_INET6: - return (inet_pton6(src, dst)); + case AF_INET6: + return (inet_pton6(src, (unsigned char *)dst)); #endif - default: - errno = EAFNOSUPPORT; - return (-1); - } - /* NOTREACHED */ + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ } /* int @@ -102,40 +102,41 @@ Curl_inet_pton(int af, const char *src, void *dst) static int inet_pton4(const char *src, unsigned char *dst) { - static const char digits[] = "0123456789"; - int saw_digit, octets, ch; - unsigned char tmp[INADDRSZ], *tp; - - saw_digit = 0; - octets = 0; - *(tp = tmp) = 0; - while ((ch = *src++) != '\0') { - const char *pch; - - if ((pch = strchr(digits, ch)) != NULL) { - size_t new = *tp * 10 + (pch - digits); - - if (new > 255) - return (0); - *tp = (unsigned char)new; - if (! saw_digit) { - if (++octets > 4) - return (0); - saw_digit = 1; - } - } else if (ch == '.' && saw_digit) { - if (octets == 4) - return (0); - *++tp = 0; - saw_digit = 0; - } else - return (0); - } - if (octets < 4) - return (0); - /* bcopy(tmp, dst, INADDRSZ); */ - memcpy(dst, tmp, INADDRSZ); - return (1); + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + tp = tmp; + *tp = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + unsigned int val = *tp * 10 + (unsigned int)(pch - digits); + + if (val > 255) + return (0); + *tp = val; + if (! saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + /* bcopy(tmp, dst, INADDRSZ); */ + memcpy(dst, tmp, INADDRSZ); + return (1); } #ifdef ENABLE_IPV6 @@ -155,85 +156,85 @@ inet_pton4(const char *src, unsigned char *dst) 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; - u_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); + 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); } #endif /* ENABLE_IPV6 */ diff --git a/Utilities/cmcurl/inet_pton.h b/Utilities/cmcurl/inet_pton.h index b0a70d4..a659a97 100644 --- a/Utilities/cmcurl/inet_pton.h +++ b/Utilities/cmcurl/inet_pton.h @@ -1,18 +1,18 @@ #ifndef __INET_PTON_H #define __INET_PTON_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -25,13 +25,18 @@ #include "setup.h" +int Curl_inet_pton(int, const char *, void *); + #ifdef HAVE_INET_PTON -#define Curl_inet_pton(x,y,z) inet_pton(x,y,z) + +#if defined(HAVE_NO_INET_PTON_PROTO) +int inet_pton(int af, const char *src, void *dst); +#endif + #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#else -int Curl_inet_pton(int, const char *, void *); +#define Curl_inet_pton(x,y,z) inet_pton(x,y,z) #endif #endif /* __INET_PTON_H */ diff --git a/Utilities/cmcurl/krb4.c b/Utilities/cmcurl/krb4.c index 50467e3..f2b91df 100644 --- a/Utilities/cmcurl/krb4.c +++ b/Utilities/cmcurl/krb4.c @@ -1,14 +1,13 @@ /* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for - * use in Curl. His latest changes were done 2000-09-18. + * use in Curl. Martin's latest changes were done 2000-09-18. * - * It has since been patched away like a madman by Daniel Stenberg - * <daniel@haxx.se> to make it better applied to curl conditions, and to make - * it not use globals, pollute name space and more. This source code awaits a - * rewrite to work around the paragraph 2 in the BSD licenses as explained - * below. + * It has since been patched away like a madman by Daniel Stenberg to make it + * better applied to curl conditions, and to make it not use globals, pollute + * name space and more. * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). + * Copyright (c) 2004 - 2007 Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,15 +35,16 @@ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ + * SUCH DAMAGE. + * + * $Id$ + */ #include "setup.h" #ifndef CURL_DISABLE_FTP #ifdef HAVE_KRB4 -#include "security.h" -#include "base64.h" #include <stdlib.h> #ifdef HAVE_NETDB_H #include <netdb.h> @@ -57,10 +57,12 @@ #include <unistd.h> /* for getpid() */ #endif +#include "urldata.h" +#include "base64.h" #include "ftp.h" #include "sendf.h" #include "krb4.h" -#include "curl_memory.h" +#include "memory.h" #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) #include "inet_ntoa_r.h" @@ -199,7 +201,8 @@ krb4_auth(void *app_data, struct connectdata *conn) { int ret; char *p; - int len; + unsigned char *ptr; + size_t len; KTEXT_ST adat; MSG_DAT msg_data; int checksum; @@ -220,7 +223,7 @@ krb4_auth(void *app_data, struct connectdata *conn) if(ret == KDC_PR_UNKNOWN) ret = mk_auth(d, &adat, "rcmd", host, checksum); if(ret) { - Curl_infof(data, "%s\n", krb_get_err_text(ret)); + infof(data, "%s\n", krb_get_err_text(ret)); return AUTH_CONTINUE; } @@ -232,7 +235,7 @@ krb4_auth(void *app_data, struct connectdata *conn) if (krb_get_our_ip_for_realm(krb_realmofhost(host), &natAddr) != KSUCCESS && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) - Curl_infof(data, "Can't get address for realm %s\n", + infof(data, "Can't get address for realm %s\n", krb_realmofhost(host)); else { if (natAddr.s_addr != localaddr->sin_addr.s_addr) { @@ -242,14 +245,14 @@ krb4_auth(void *app_data, struct connectdata *conn) #else char *ip = (char *)inet_ntoa(natAddr); #endif - Curl_infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip); + infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip); localaddr->sin_addr = natAddr; } } } #endif - if(Curl_base64_encode((char *)adat.dat, adat.length, &p) < 1) { + if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) { Curl_failf(data, "Out of memory base64-encoding"); return AUTH_CONTINUE; } @@ -275,11 +278,17 @@ krb4_auth(void *app_data, struct connectdata *conn) return AUTH_ERROR; } p += 5; - len = Curl_base64_decode(p, (char *)adat.dat); - if(len < 0) { + 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, @@ -317,10 +326,11 @@ CURLcode Curl_krb_kauth(struct connectdata *conn) char *name; char *p; char passwd[100]; - int tmp; + size_t tmp; ssize_t nread; int save; CURLcode result; + unsigned char *ptr; save = Curl_set_command_prot(conn, prot_private); @@ -346,12 +356,18 @@ CURLcode Curl_krb_kauth(struct connectdata *conn) } p += 2; - tmp = Curl_base64_decode(p, (char *)tkt.dat); - if(tmp < 0) { + 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.\n"); 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; @@ -384,7 +400,8 @@ CURLcode Curl_krb_kauth(struct connectdata *conn) memset(key, 0, sizeof(key)); memset(schedule, 0, sizeof(schedule)); memset(passwd, 0, sizeof(passwd)); - if(Curl_base64_encode((char *)tktcopy.dat, tktcopy.length, &p) < 1) { + 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; diff --git a/Utilities/cmcurl/krb4.h b/Utilities/cmcurl/krb4.h index cded35b..f46416e 100644 --- a/Utilities/cmcurl/krb4.h +++ b/Utilities/cmcurl/krb4.h @@ -1,18 +1,18 @@ #ifndef __KRB4_H #define __KRB4_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -22,6 +22,49 @@ * * $Id$ ***************************************************************************/ + +struct Curl_sec_client_mech { + const char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, void*, int, int, void**, struct connectdata *); + int (*decode)(void *, void*, int, int, struct connectdata *); +}; + + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +extern struct Curl_sec_client_mech Curl_krb4_client_mech; + CURLcode Curl_krb_kauth(struct connectdata *conn); +int Curl_sec_fflush_fd(struct connectdata *conn, int fd); +int Curl_sec_fprintf (struct connectdata *, FILE *, const char *, ...); +int Curl_sec_getc (struct connectdata *conn, FILE *); +int Curl_sec_putc (struct connectdata *conn, int, FILE *); +int Curl_sec_read (struct connectdata *conn, int, void *, int); +int Curl_sec_read_msg (struct connectdata *conn, char *, int); + +int Curl_sec_vfprintf(struct connectdata *, FILE *, const char *, va_list); +int Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...); +int Curl_sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list); +ssize_t Curl_sec_send(struct connectdata *conn, int, char *, int); +int Curl_sec_write(struct connectdata *conn, int, char *, int); + +void Curl_sec_end (struct connectdata *); +int Curl_sec_login (struct connectdata *); +void Curl_sec_prot (int, char **); +int Curl_sec_request_prot (struct connectdata *conn, const char *level); +void Curl_sec_set_protection_level(struct connectdata *conn); +void Curl_sec_status (void); + +enum protection_level Curl_set_command_prot(struct connectdata *, + enum protection_level); + #endif diff --git a/Utilities/cmcurl/ldap.c b/Utilities/cmcurl/ldap.c index 62e5b26..3e1144d 100644 --- a/Utilities/cmcurl/ldap.c +++ b/Utilities/cmcurl/ldap.c @@ -3,9 +3,9 @@ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| + * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,14 +30,19 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif +#ifdef NEED_MALLOC_H +#include <malloc.h> +#endif #include <errno.h> #if defined(WIN32) -# include <windows.h> -# include <malloc.h> -# include <WinLdap.h> +# include <winldap.h> #endif #ifdef HAVE_UNISTD_H @@ -56,7 +61,8 @@ #include "strequal.h" #include "strtok.h" #include "ldap.h" -#include "curl_memory.h" +#include "memory.h" +#include "base64.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -64,7 +70,8 @@ #include "memdebug.h" /* WLdap32.dll functions are *not* stdcall. Must call these via __cdecl - * pointers in case libcurl was compiled as fastcall (-Gr). + * pointers in case libcurl was compiled as fastcall (cl -Gr). Watcom + * uses fastcall by default. */ #if !defined(WIN32) && !defined(__cdecl) #define __cdecl @@ -73,8 +80,17 @@ #ifndef LDAP_SIZELIMIT_EXCEEDED #define LDAP_SIZELIMIT_EXCEEDED 4 #endif +#ifndef LDAP_VERSION2 +#define LDAP_VERSION2 2 +#endif +#ifndef LDAP_VERSION3 +#define LDAP_VERSION3 3 +#endif +#ifndef LDAP_OPT_PROTOCOL_VERSION +#define LDAP_OPT_PROTOCOL_VERSION 0x0011 +#endif -#define DLOPEN_MODE RTLD_LAZY /*! assume all dlopen() implementations have +#define DLOPEN_MODE RTLD_LAZY /*! assume all dlopen() implementations have this */ #if defined(RTLD_LAZY_GLOBAL) /* It turns out some systems use this: */ @@ -99,43 +115,60 @@ #undef HAVE_LIBDL #endif +/* + * We use this ZERO_NULL to avoid picky compiler warnings, + * when assigning a NULL pointer to a function pointer var. + */ + +#define ZERO_NULL 0 + typedef void * (*dynafunc)(void *input); /*********************************************************************** */ +#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) || defined(WIN32) static void *libldap = NULL; -#ifndef WIN32 +#if defined(DL_LBER_FILE) static void *liblber = NULL; #endif +#endif + +struct bv { + unsigned long bv_len; + char *bv_val; +}; static int DynaOpen(const char **mod_name) { #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) if (libldap == NULL) { /* - * libldap.so should be able to resolve its dependency on - * liblber.so automatically, but since it does not we will + * libldap.so can normally resolve its dependency on liblber.so + * automatically, but in broken installation it does not so * handle it here by opening liblber.so as global. */ - *mod_name = "liblber.so"; +#ifdef DL_LBER_FILE + *mod_name = DL_LBER_FILE; liblber = dlopen(*mod_name, DLOPEN_MODE); + if (!liblber) + return 0; +#endif /* Assume loading libldap.so will fail if loading of liblber.so failed */ - if (liblber) { - *mod_name = "libldap.so"; - libldap = dlopen(*mod_name, RTLD_LAZY); - } + *mod_name = DL_LDAP_FILE; + libldap = dlopen(*mod_name, RTLD_LAZY); } - return (libldap != NULL && liblber != NULL); + return (libldap != NULL); #elif defined(WIN32) - *mod_name = "wldap32.dll"; + *mod_name = DL_LDAP_FILE; if (!libldap) libldap = (void*)LoadLibrary(*mod_name); return (libldap != NULL); #else + *mod_name = ""; return (0); #endif } @@ -147,10 +180,12 @@ static void DynaClose(void) dlclose(libldap); libldap=NULL; } +#ifdef DL_LBER_FILE if (liblber) { dlclose(liblber); liblber=NULL; } +#endif #elif defined(WIN32) if (libldap) { FreeLibrary ((HMODULE)libldap); @@ -161,7 +196,7 @@ static void DynaClose(void) static dynafunc DynaGetFunction(const char *name) { - dynafunc func = (dynafunc)NULL; + dynafunc func = (dynafunc)ZERO_NULL; #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) if (libldap) { @@ -175,6 +210,8 @@ static dynafunc DynaGetFunction(const char *name) if (libldap) { func = (dynafunc)GetProcAddress((HINSTANCE)libldap, name); } +#else + (void) name; #endif return func; } @@ -214,7 +251,7 @@ static void (*ldap_free_urldesc)(LDAPURLDesc *) = _ldap_free_urldesc; #endif -CURLcode Curl_ldap(struct connectdata *conn) +CURLcode Curl_ldap(struct connectdata *conn, bool *done) { CURLcode status = CURLE_OK; int rc = 0; @@ -233,10 +270,11 @@ CURLcode Curl_ldap(struct connectdata *conn) char *(__cdecl *ldap_get_dn)(void *, void *); char *(__cdecl *ldap_first_attribute)(void *, void *, void **); char *(__cdecl *ldap_next_attribute)(void *, void *, void *); - char **(__cdecl *ldap_get_values)(void *, void *, const char *); - void (__cdecl *ldap_value_free)(char **); + void **(__cdecl *ldap_get_values_len)(void *, void *, const char *); + void (__cdecl *ldap_value_free_len)(void **); void (__cdecl *ldap_memfree)(void *); void (__cdecl *ber_free)(void *, int); + int (__cdecl *ldap_set_option)(void *, int, void *); void *server; LDAPURLDesc *ludp = NULL; @@ -245,7 +283,11 @@ CURLcode Curl_ldap(struct connectdata *conn) void *entryIterator; /*! type should be 'LDAPMessage *' */ int num = 0; struct SessionHandle *data=conn->data; + int ldap_proto; + char *val_b64; + size_t val_b64_sz; + *done = TRUE; /* unconditionally */ infof(data, "LDAP local: %s\n", data->change.url); if (!DynaOpen(&mod_name)) { @@ -256,25 +298,30 @@ CURLcode Curl_ldap(struct connectdata *conn) /* The types are needed because ANSI C distinguishes between * pointer-to-object (data) and pointer-to-function. */ - DYNA_GET_FUNCTION(void *(*)(char *, int), ldap_init); - DYNA_GET_FUNCTION(int (*)(void *, char *, char *), ldap_simple_bind_s); - DYNA_GET_FUNCTION(int (*)(void *), ldap_unbind_s); + DYNA_GET_FUNCTION(void *(__cdecl *)(char *, int), ldap_init); + DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, char *), + ldap_simple_bind_s); + DYNA_GET_FUNCTION(int (__cdecl *)(void *), ldap_unbind_s); #ifndef WIN32 DYNA_GET_FUNCTION(int (*)(char *, LDAPURLDesc **), ldap_url_parse); DYNA_GET_FUNCTION(void (*)(void *), ldap_free_urldesc); #endif - DYNA_GET_FUNCTION(int (*)(void *, char *, int, char *, char **, int, - void **), ldap_search_s); - DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_first_entry); - DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_next_entry); - DYNA_GET_FUNCTION(char *(*)(int), ldap_err2string); - DYNA_GET_FUNCTION(char *(*)(void *, void *), ldap_get_dn); - DYNA_GET_FUNCTION(char *(*)(void *, void *, void **), ldap_first_attribute); - DYNA_GET_FUNCTION(char *(*)(void *, void *, void *), ldap_next_attribute); - DYNA_GET_FUNCTION(char **(*)(void *, void *, const char *), ldap_get_values); - DYNA_GET_FUNCTION(void (*)(char **), ldap_value_free); - DYNA_GET_FUNCTION(void (*)(void *), ldap_memfree); - DYNA_GET_FUNCTION(void (*)(void *, int), ber_free); + DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, int, char *, char **, int, + void **), ldap_search_s); + DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_first_entry); + DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_next_entry); + DYNA_GET_FUNCTION(char *(__cdecl *)(int), ldap_err2string); + DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *), ldap_get_dn); + DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void **), + ldap_first_attribute); + DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void *), + ldap_next_attribute); + DYNA_GET_FUNCTION(void **(__cdecl *)(void *, void *, const char *), + ldap_get_values_len); + DYNA_GET_FUNCTION(void (__cdecl *)(void **), ldap_value_free_len); + DYNA_GET_FUNCTION(void (__cdecl *)(void *), ldap_memfree); + DYNA_GET_FUNCTION(void (__cdecl *)(void *, int), ber_free); + DYNA_GET_FUNCTION(int (__cdecl *)(void *, int, void *), ldap_set_option); server = (*ldap_init)(conn->host.name, (int)conn->port); if (server == NULL) { @@ -284,10 +331,19 @@ CURLcode Curl_ldap(struct connectdata *conn) goto quit; } + ldap_proto = LDAP_VERSION3; + (*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) { + 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: %s", (*ldap_err2string)(rc)); status = CURLE_LDAP_CANNOT_BIND; goto quit; @@ -323,35 +379,51 @@ CURLcode Curl_ldap(struct connectdata *conn) char *dn = (*ldap_get_dn)(server, entryIterator); int i; - Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4); - Curl_client_write(data, CLIENTWRITE_BODY, (char *)dn, 0); - Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + 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)) { - char **vals = (*ldap_get_values)(server, entryIterator, attribute); + struct bv **vals = (struct bv **) + (*ldap_get_values_len)(server, entryIterator, attribute); if (vals != NULL) { for (i = 0; (vals[i] != NULL); i++) { - Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); - Curl_client_write(data, CLIENTWRITE_BODY, (char*) attribute, 0); - Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2); - Curl_client_write(data, CLIENTWRITE_BODY, vals[i], 0); - Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 0); + 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)(vals); + (*ldap_value_free_len)((void **)vals); } - Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); + Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); (*ldap_memfree)(attribute); - (*ldap_memfree)(dn); } + (*ldap_memfree)(dn); if (ber) (*ber_free)(ber, 0); } @@ -368,7 +440,8 @@ quit: DynaClose(); /* no data to transfer */ - Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + conn->bits.close = TRUE; return status; } @@ -436,36 +509,36 @@ static char **split_str (char *str) /* * Unescape the LDAP-URL components */ -static bool unescape_elements (LDAPURLDesc *ludp) +static bool unescape_elements (void *data, LDAPURLDesc *ludp) { int i; if (ludp->lud_filter) { - ludp->lud_filter = curl_unescape(ludp->lud_filter, 0); + 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_unescape(ludp->lud_attrs[i], 0); + 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_unescape(ludp->lud_exts[i], 0); + 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_unescape(dn, 0); + char *new_dn = curl_easy_unescape(data, dn, 0, NULL); free(dn); + ludp->lud_dn = new_dn; if (!new_dn) return (FALSE); - ludp->lud_dn = new_dn; } return (TRUE); } @@ -477,8 +550,10 @@ static bool unescape_elements (LDAPURLDesc *ludp) * * <hostname> already known from 'conn->host.name'. * <port> already known from 'conn->remote_port'. - * extract the rest from 'conn->path+1'. All fields are optional. e.g. - * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> yields ludp->lud_dn = "". + * extract the rest from 'conn->data->reqdata.path+1'. All fields are optional. + * e.g. + * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> + * yields ludp->lud_dn = "". * * Ref. http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm#2831915 */ @@ -487,7 +562,9 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) char *p, *q; int i; - if (!conn->path || conn->path[0] != '/' || + if (!conn->data || + !conn->data->reqdata.path || + conn->data->reqdata.path[0] != '/' || !checkprefix(conn->protostr, conn->data->change.url)) return LDAP_INVALID_SYNTAX; @@ -497,13 +574,13 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) /* parse DN (Distinguished Name). */ - ludp->lud_dn = strdup(conn->path+1); + ludp->lud_dn = strdup(conn->data->reqdata.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)); + LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) : + strlen(ludp->lud_dn), ludp->lud_dn)); if (!p) goto success; @@ -571,7 +648,7 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i])); success: - if (!unescape_elements(ludp)) + if (!unescape_elements(conn->data, ludp)) return LDAP_NO_MEMORY; return LDAP_SUCCESS; } diff --git a/Utilities/cmcurl/ldap.h b/Utilities/cmcurl/ldap.h index b95cf74..b2d4f39 100644 --- a/Utilities/cmcurl/ldap.h +++ b/Utilities/cmcurl/ldap.h @@ -2,18 +2,18 @@ #define __LDAP_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,6 +24,6 @@ * $Id$ ***************************************************************************/ #ifndef CURL_DISABLE_LDAP -CURLcode Curl_ldap(struct connectdata *conn); +CURLcode Curl_ldap(struct connectdata *conn, bool *done); #endif #endif /* __LDAP_H */ diff --git a/Utilities/cmcurl/llist.c b/Utilities/cmcurl/llist.c index 90ac1c8..921d1d1 100644 --- a/Utilities/cmcurl/llist.c +++ b/Utilities/cmcurl/llist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,13 +27,13 @@ #include <stdlib.h> #include "llist.h" -#include "curl_memory.h" +#include "memory.h" /* this must be the last include file */ #include "memdebug.h" void -Curl_llist_init(curl_llist *l, curl_llist_dtor dtor) +Curl_llist_init(struct curl_llist *l, curl_llist_dtor dtor) { l->size = 0; l->dtor = dtor; @@ -41,12 +41,12 @@ Curl_llist_init(curl_llist *l, curl_llist_dtor dtor) l->tail = NULL; } -curl_llist * +struct curl_llist * Curl_llist_alloc(curl_llist_dtor dtor) { - curl_llist *list; + struct curl_llist *list; - list = (curl_llist *)malloc(sizeof(curl_llist)); + list = (struct curl_llist *)malloc(sizeof(struct curl_llist)); if(NULL == list) return NULL; @@ -59,10 +59,11 @@ Curl_llist_alloc(curl_llist_dtor dtor) * Curl_llist_insert_next() returns 1 on success and 0 on failure. */ int -Curl_llist_insert_next(curl_llist *list, curl_llist_element *e, const void *p) +Curl_llist_insert_next(struct curl_llist *list, struct curl_llist_element *e, + const void *p) { - curl_llist_element *ne = - (curl_llist_element *) malloc(sizeof(curl_llist_element)); + struct curl_llist_element *ne = + (struct curl_llist_element *) malloc(sizeof(struct curl_llist_element)); if(!ne) return 0; @@ -91,7 +92,8 @@ Curl_llist_insert_next(curl_llist *list, curl_llist_element *e, const void *p) } int -Curl_llist_remove(curl_llist *list, curl_llist_element *e, void *user) +Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e, + void *user) { if (e == NULL || list->size == 0) return 1; @@ -119,7 +121,7 @@ Curl_llist_remove(curl_llist *list, curl_llist_element *e, void *user) } void -Curl_llist_destroy(curl_llist *list, void *user) +Curl_llist_destroy(struct curl_llist *list, void *user) { if(list) { while (list->size > 0) @@ -128,3 +130,9 @@ Curl_llist_destroy(curl_llist *list, void *user) free(list); } } + +size_t +Curl_llist_count(struct curl_llist *list) +{ + return list->size; +} diff --git a/Utilities/cmcurl/llist.h b/Utilities/cmcurl/llist.h index 4f76513..5c7a8a0 100644 --- a/Utilities/cmcurl/llist.h +++ b/Utilities/cmcurl/llist.h @@ -1,18 +1,18 @@ #ifndef __LLIST_H #define __LLIST_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -28,29 +28,33 @@ typedef void (*curl_llist_dtor)(void *, void *); -typedef struct _curl_llist_element { +struct curl_llist_element { void *ptr; - struct _curl_llist_element *prev; - struct _curl_llist_element *next; -} curl_llist_element; + struct curl_llist_element *prev; + struct curl_llist_element *next; +}; -typedef struct _curl_llist { - curl_llist_element *head; - curl_llist_element *tail; +struct curl_llist { + struct curl_llist_element *head; + struct curl_llist_element *tail; curl_llist_dtor dtor; size_t size; -} curl_llist; - -void Curl_llist_init(curl_llist *, curl_llist_dtor); -curl_llist *Curl_llist_alloc(curl_llist_dtor); -int Curl_llist_insert_next(curl_llist *, curl_llist_element *, const void *); -int Curl_llist_insert_prev(curl_llist *, curl_llist_element *, const void *); -int Curl_llist_remove(curl_llist *, curl_llist_element *, void *); -int Curl_llist_remove_next(curl_llist *, curl_llist_element *, void *); -size_t Curl_llist_count(curl_llist *); -void Curl_llist_destroy(curl_llist *, void *); +}; + +void Curl_llist_init(struct curl_llist *, curl_llist_dtor); +struct curl_llist *Curl_llist_alloc(curl_llist_dtor); +int Curl_llist_insert_next(struct curl_llist *, struct curl_llist_element *, + const void *); +int Curl_llist_insert_prev(struct curl_llist *, struct curl_llist_element *, + const void *); +int Curl_llist_remove(struct curl_llist *, struct curl_llist_element *, + void *); +int Curl_llist_remove_next(struct curl_llist *, struct curl_llist_element *, + void *); +size_t Curl_llist_count(struct curl_llist *); +void Curl_llist_destroy(struct curl_llist *, void *); #endif diff --git a/Utilities/cmcurl/md5.c b/Utilities/cmcurl/md5.c index a70b344..4cfb073 100644 --- a/Utilities/cmcurl/md5.c +++ b/Utilities/cmcurl/md5.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,7 +23,9 @@ #include "setup.h" -#ifndef USE_SSLEAY +#ifndef CURL_DISABLE_CRYPTO_AUTH + +#if !defined(USE_SSLEAY) || !defined(USE_OPENSSL) /* This code segment is only used if OpenSSL is not provided, as if it is we use the MD5-function provided there instead. No good duplicating code! */ @@ -65,7 +67,7 @@ struct md5_ctx { typedef struct md5_ctx MD5_CTX; static void MD5_Init(struct md5_ctx *); -static void MD5_Update(struct md5_ctx *, unsigned char *, unsigned int); +static void MD5_Update(struct md5_ctx *, const unsigned char *, unsigned int); static void MD5_Final(unsigned char [16], struct md5_ctx *); /* Constants for MD5Transform routine. @@ -88,11 +90,11 @@ static void MD5_Final(unsigned char [16], struct md5_ctx *); #define S43 15 #define S44 21 -static void MD5Transform(UINT4 [4], unsigned char [64]); +static void MD5Transform(UINT4 [4], const unsigned char [64]); static void Encode(unsigned char *, UINT4 *, unsigned int); -static void Decode(UINT4 *, unsigned char *, unsigned int); +static void Decode(UINT4 *, const unsigned char *, unsigned int); -static unsigned char PADDING[64] = { +static const unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -149,9 +151,9 @@ static void MD5_Init(struct md5_ctx *context) operation, processing another message block, and updating the context. */ -static void MD5_Update (struct md5_ctx *context, /* context */ - unsigned char *input, /* input block */ - unsigned int inputLen)/* length of input block */ +static void MD5_Update (struct md5_ctx *context, /* context */ + const unsigned char *input, /* input block */ + unsigned int inputLen) /* length of input block */ { unsigned int i, bufindex, partLen; @@ -212,7 +214,7 @@ static void MD5_Final(unsigned char digest[16], /* message digest */ /* MD5 basic transformation. Transforms state based on block. */ static void MD5Transform(UINT4 state[4], - unsigned char block[64]) + const unsigned char block[64]) { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; @@ -320,7 +322,7 @@ static void Encode (unsigned char *output, a multiple of 4. */ static void Decode (UINT4 *output, - unsigned char *input, + const unsigned char *input, unsigned int len) { unsigned int i, j; @@ -339,10 +341,12 @@ static void Decode (UINT4 *output, #include "md5.h" void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ - unsigned char *input) + const unsigned char *input) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, input, (unsigned int)strlen((char *)input)); MD5_Final(outbuffer, &ctx); } + +#endif diff --git a/Utilities/cmcurl/md5.h b/Utilities/cmcurl/md5.h index 12b3a5e..63cc70e 100644 --- a/Utilities/cmcurl/md5.h +++ b/Utilities/cmcurl/md5.h @@ -24,6 +24,6 @@ ***************************************************************************/ void Curl_md5it(unsigned char *output, - unsigned char *input); + const unsigned char *input); #endif diff --git a/Utilities/cmcurl/memdebug.c b/Utilities/cmcurl/memdebug.c index 799fe7c..a8cca44 100644 --- a/Utilities/cmcurl/memdebug.c +++ b/Utilities/cmcurl/memdebug.c @@ -6,7 +6,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,7 +42,7 @@ #endif #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */ -#include "curl_memory.h" +#include "memory.h" #include "memdebug.h" struct memdebug { @@ -61,25 +61,29 @@ struct memdebug { */ #define logfile curl_debuglogfile -FILE *curl_debuglogfile; -static bool memlimit; /* enable memory limit */ -static long memsize; /* set number of mallocs allowed */ +FILE *curl_debuglogfile = NULL; +static bool memlimit = FALSE; /* enable memory limit */ +static long memsize = 0; /* set number of mallocs allowed */ /* this sets the log file name */ void curl_memdebug(const char *logname) { - if(logname) - logfile = fopen(logname, "w"); - else - logfile = stderr; + if (!logfile) { + if(logname) + logfile = fopen(logname, "w"); + else + logfile = stderr; + } } /* This function sets the number of malloc() calls that should return successfully! */ void curl_memlimit(long limit) { - memlimit = TRUE; - memsize = limit; + if (!memlimit) { + memlimit = TRUE; + memsize = limit; + } } /* returns TRUE if this isn't allowed! */ @@ -95,6 +99,7 @@ static bool countcheck(const char *func, int line, const char *source) if(source) fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", source, line, func); + errno = ENOMEM; return TRUE; /* RETURN ERROR! */ } else @@ -200,7 +205,7 @@ void *curl_dorealloc(void *ptr, size_t wantedsize, mem=(struct memdebug *)(Curl_crealloc)(mem, size); if(logfile) - fprintf(logfile, "MEM %s:%d realloc(0x%x, %zd) = %p\n", + fprintf(logfile, "MEM %s:%d realloc(%p, %zd) = %p\n", source, line, ptr, wantedsize, mem?mem->mem:NULL); if(mem) { diff --git a/Utilities/cmcurl/memdebug.h b/Utilities/cmcurl/memdebug.h index 42574cf..a4ce7e5 100644 --- a/Utilities/cmcurl/memdebug.h +++ b/Utilities/cmcurl/memdebug.h @@ -2,18 +2,18 @@ #ifndef _CURL_MEDEBUG_H #define _CURL_MEDEBUG_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -31,6 +31,8 @@ #include "setup.h" +#include <curl/curl.h> + #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -48,24 +50,24 @@ extern FILE *logfile; /* memory functions */ -void *curl_domalloc(size_t size, int line, const char *source); -void *curl_docalloc(size_t elements, size_t size, int line, const char *source); -void *curl_dorealloc(void *ptr, size_t size, int line, const char *source); -void curl_dofree(void *ptr, int line, const char *source); -char *curl_dostrdup(const char *str, int line, const char *source); -void curl_memdebug(const char *logname); -void curl_memlimit(long limit); +CURL_EXTERN void *curl_domalloc(size_t size, int line, const char *source); +CURL_EXTERN void *curl_docalloc(size_t elements, size_t size, int line, const char *source); +CURL_EXTERN void *curl_dorealloc(void *ptr, size_t size, int line, const char *source); +CURL_EXTERN void curl_dofree(void *ptr, int line, const char *source); +CURL_EXTERN char *curl_dostrdup(const char *str, int line, const char *source); +CURL_EXTERN void curl_memdebug(const char *logname); +CURL_EXTERN void curl_memlimit(long limit); /* file descriptor manipulators */ -int curl_socket(int domain, int type, int protocol, int line , const char *); -int curl_sclose(int sockfd, int, const char *source); -int curl_accept(int s, void *addr, void *addrlen, - int line, const char *source); +CURL_EXTERN int curl_socket(int domain, int type, int protocol, int line , const char *); +CURL_EXTERN int curl_sclose(int sockfd, int, const char *source); +CURL_EXTERN int curl_accept(int s, void *addr, void *addrlen, + int line, const char *source); /* FILE functions */ -FILE *curl_fopen(const char *file, const char *mode, int line, - const char *source); -int curl_fclose(FILE *file, int line, const char *source); +CURL_EXTERN FILE *curl_fopen(const char *file, const char *mode, int line, + const char *source); +CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); #ifndef MEMDEBUG_NODEFINES @@ -83,11 +85,26 @@ int curl_fclose(FILE *file, int line, const char *source); #define accept(sock,addr,len)\ curl_accept(sock,addr,len,__LINE__,__FILE__) +#if defined(getaddrinfo) && defined(__osf__) +/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define + our macro as for other platforms. Instead, we redefine the new name they + define getaddrinfo to become! */ +#define ogetaddrinfo(host,serv,hint,res) \ + curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__) +#else +#undef getaddrinfo #define getaddrinfo(host,serv,hint,res) \ curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__) +#endif + +#ifdef HAVE_GETNAMEINFO +#undef getnameinfo #define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \ curl_dogetnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \ __FILE__) +#endif + +#undef freeaddrinfo #define freeaddrinfo(data) \ curl_dofreeaddrinfo(data,__LINE__,__FILE__) diff --git a/Utilities/cmcurl/curl_memory.h b/Utilities/cmcurl/memory.h index 4e32a67..076d20a 100644 --- a/Utilities/cmcurl/curl_memory.h +++ b/Utilities/cmcurl/memory.h @@ -1,10 +1,10 @@ #ifndef _CURL_MEMORY_H #define _CURL_MEMORY_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. @@ -12,7 +12,7 @@ * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. diff --git a/Utilities/cmcurl/mprintf.c b/Utilities/cmcurl/mprintf.c index 2bb9732..6103953 100644 --- a/Utilities/cmcurl/mprintf.c +++ b/Utilities/cmcurl/mprintf.c @@ -38,6 +38,10 @@ #include <ctype.h> #include <string.h> +#if defined(DJGPP) && (DJGPP_MINOR < 4) +#undef _MPRINTF_REPLACE /* don't use x_was_used() here */ +#endif + #include <curl/mprintf.h> #ifndef SIZEOF_LONG_DOUBLE @@ -55,7 +59,7 @@ #define ENABLE_64BIT #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -75,6 +79,9 @@ # define BOOL char #endif +#ifdef _AMIGASF +# undef FORMAT_INT +#endif /* Lower-case digits. */ static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; @@ -164,7 +171,7 @@ int curl_msprintf(char *buffer, const char *format, ...); static long dprintf_DollarString(char *input, char **end) { int number=0; - while(isdigit((int)*input)) { + while(ISDIGIT(*input)) { number *= 10; number += *input-'0'; input++; @@ -619,10 +626,10 @@ static int dprintf_formatf( char alt; /* Width of a field. */ - ssize_t width; + long width; /* Precision of a field. */ - ssize_t prec; + long prec; /* Decimal integer is negative. */ char is_neg; @@ -759,8 +766,8 @@ static int dprintf_formatf( *w-- = digits[num % base]; num /= base; } - width -= workend - w; - prec -= workend - w; + width -= (long)(workend - w); + prec -= (long)(workend - w); if (alt && base == 8 && prec <= 0) { *w-- = '0'; @@ -816,8 +823,8 @@ static int dprintf_formatf( case FORMAT_STRING: /* String. */ { - static char null[] = "(nil)"; - char *str; + static const char null[] = "(nil)"; + const char *str; size_t len; str = (char *) p->data.str; @@ -830,7 +837,7 @@ static int dprintf_formatf( p->flags &= (~FLAGS_ALT); } else { - str = (char *)""; + str = ""; len = 0; } } @@ -839,7 +846,7 @@ static int dprintf_formatf( if (prec != -1 && (size_t) prec < len) len = prec; - width -= len; + width -= (long)len; if (p->flags & FLAGS_ALT) OUTCHAR('"'); @@ -869,14 +876,14 @@ static int dprintf_formatf( base = 16; digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; alt = 1; - num = (unsigned long) ptr; + num = (size_t) ptr; is_neg = 0; goto number; } else { /* Write "(nil)" for a nil pointer. */ - static char strnil[] = "(nil)"; - char *point; + static const char strnil[] = "(nil)"; + const char *point; width -= sizeof(strnil) - 1; if (p->flags & FLAGS_LEFT) @@ -933,7 +940,6 @@ static int dprintf_formatf( fptr += len; left -= len; } - (void)left; if (p->flags & FLAGS_LONG) *fptr++ = 'l'; @@ -1134,15 +1140,12 @@ int curl_msprintf(char *buffer, const char *format, ...) return retcode; } -#if !(defined( WIN32) || defined(__UCLIBC__)) /* not needed on win32 */ -extern int fputc(int, FILE *); -#endif - int curl_mprintf(const char *format, ...) { int retcode; va_list ap_save; /* argument pointer */ va_start(ap_save, format); + retcode = dprintf_formatf(stdout, fputc, format, ap_save); va_end(ap_save); return retcode; diff --git a/Utilities/cmcurl/multi.c b/Utilities/cmcurl/multi.c index a604af8..b501f29 100644 --- a/Utilities/cmcurl/multi.c +++ b/Utilities/cmcurl/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,7 +42,11 @@ #include "url.h" #include "connect.h" #include "progress.h" -#include "curl_memory.h" +#include "memory.h" +#include "easyif.h" +#include "multiif.h" +#include "sendf.h" +#include "timeval.h" /* The last #include file should be: */ #include "memdebug.h" @@ -54,19 +58,38 @@ struct Curl_message { }; typedef enum { - CURLM_STATE_INIT, + CURLM_STATE_INIT, /* start in this state */ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ - CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ - CURLM_STATE_DO, /* send off the request (part 1) */ + CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ + CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ + CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect + phase */ + CURLM_STATE_WAITDO, /* wait for our turn to send the request */ + CURLM_STATE_DO, /* start send off the request (part 1) */ + CURLM_STATE_DOING, /* sending off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* done sending off request */ + CURLM_STATE_WAITPERFORM, /* wait for our turn to read the response */ CURLM_STATE_PERFORM, /* transfer data */ + CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ + CURLM_STATE_CANCELLED, /* cancelled */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; +/* we support N sockets per easy handle. Set the corresponding bit to what + action we should wait for */ +#define MAX_SOCKSPEREASYHANDLE 5 +#define GETSOCK_READABLE (0x00ff) +#define GETSOCK_WRITABLE (0xff00) + +struct closure { + struct closure *next; /* a simple one-way list of structs */ + struct SessionHandle *easy_handle; +}; + struct Curl_one_easy { /* first, two fields for the linked list of these */ struct Curl_one_easy *next; @@ -84,13 +107,21 @@ struct Curl_one_easy { will be deleted when this handle is removed from the multi-handle */ int msg_num; /* number of messages left in 'msg' to return */ -}; + /* Array with the plain socket numbers this handle takes care of, in no + particular order. Note that all sockets are added to the sockhash, where + the state etc are also kept. This array is mostly used to detect when a + socket is to be removed from the hash. See singlesocket(). */ + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + int numsocks; +}; #define CURL_MULTI_HANDLE 0x000bab1e -#define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) -#define GOOD_EASY_HANDLE(x) (x) +#define GOOD_MULTI_HANDLE(x) \ + ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) +#define GOOD_EASY_HANDLE(x) \ + (((struct SessionHandle *)x)->magic == CURLEASY_MAGIC_NUMBER) /* This is the struct known as CURLM on the outside */ struct Curl_multi { @@ -100,35 +131,229 @@ struct Curl_multi { /* We have a linked list with easy handles */ struct Curl_one_easy easy; - /* This is the amount of entries in the linked list above. */ - int num_easy; - int num_msgs; /* total amount of messages in the easy handles */ + int num_easy; /* amount of entries in the linked list above. */ + int num_msgs; /* amount of messages in the easy handles */ + int num_alive; /* amount of easy handles that are added but have not yet + reached COMPLETE state */ + + /* callback function and user data pointer for the *socket() API */ + curl_socket_callback socket_cb; + void *socket_userp; /* Hostname cache */ - curl_hash *hostcache; + struct curl_hash *hostcache; + + /* timetree points to the splay-tree of time nodes to figure out expire + times of all currently set timers */ + struct Curl_tree *timetree; + + /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note + the pluralis form, there can be more than one easy handle waiting on the + same actual socket) */ + struct curl_hash *sockhash; + + /* Whether pipelining is enabled for this multi handle */ + bool pipelining_enabled; + + /* shared connection cache */ + struct conncache *connc; + + /* list of easy handles kept around for doing nice connection closures */ + struct closure *closure; + + /* timer callback and user data pointer for the *socket() API */ + curl_multi_timer_callback timer_cb; + void *timer_userp; + time_t timer_lastcall; /* the fixed time for the timeout for the previous + callback */ }; +static bool multi_conn_using(struct Curl_multi *multi, + struct SessionHandle *data); +static void singlesocket(struct Curl_multi *multi, + struct Curl_one_easy *easy); +static void add_closure(struct Curl_multi *multi, + struct SessionHandle *data); +static int update_timer(struct Curl_multi *multi); + +#ifdef CURLDEBUG +static const char *statename[]={ + "INIT", + "CONNECT", + "WAITRESOLVE", + "WAITCONNECT", + "PROTOCONNECT", + "WAITDO", + "DO", + "DOING", + "DO_MORE", + "DO_DONE", + "WAITPERFORM", + "PERFORM", + "TOOFAST", + "DONE", + "COMPLETED", + "CANCELLED" +}; -CURLM *curl_multi_init(void) +void curl_multi_dump(CURLM *multi_handle); +#endif + +/* always use this function to change state, to make debugging easier */ +static void multistate(struct Curl_one_easy *easy, CURLMstate state) { - struct Curl_multi *multi; +#ifdef CURLDEBUG + long index = -1; +#endif + CURLMstate oldstate = easy->state; + + if(oldstate == state) + /* don't bother when the new state is the same as the old state */ + return; + + easy->state = state; + +#ifdef CURLDEBUG + if(easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) + index = easy->easy_conn->connectindex; - multi = (void *)malloc(sizeof(struct Curl_multi)); + infof(easy->easy_handle, + "STATE: %s => %s handle %p; (connection #%ld) \n", + statename[oldstate], statename[easy->state], + (char *)easy, index); +#endif + if(state == CURLM_STATE_COMPLETED) + /* changing to COMPLETED means there's one less easy handle 'alive' */ + easy->easy_handle->multi->num_alive--; +} - if(multi) { - memset(multi, 0, sizeof(struct Curl_multi)); - multi->type = CURL_MULTI_HANDLE; +/* + * We add one of these structs to the sockhash for a particular socket + */ + +struct Curl_sh_entry { + struct SessionHandle *easy; + time_t timestamp; + long inuse; + int action; /* what action READ/WRITE this socket waits for */ + curl_socket_t socket; /* mainly to ease debugging */ + void *socketp; /* settable by users with curl_multi_assign() */ +}; +/* bits for 'action' having no bits means this socket is not expecting any + action */ +#define SH_READ 1 +#define SH_WRITE 2 + +/* make sure this socket is present in the hash for this handle */ +static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, + curl_socket_t s, + struct SessionHandle *data) +{ + struct Curl_sh_entry *there = + Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); + struct Curl_sh_entry *check; + + if(there) + /* it is present, return fine */ + return there; + + /* not present, add it */ + check = calloc(sizeof(struct Curl_sh_entry), 1); + if(!check) + return NULL; /* major failure */ + check->easy = data; + check->socket = s; + + /* make/add new hash entry */ + if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { + free(check); + return NULL; /* major failure */ } - else + + return check; /* things are good in sockhash land */ +} + + +/* delete the given socket + handle from the hash */ +static void sh_delentry(struct curl_hash *sh, curl_socket_t s) +{ + struct Curl_sh_entry *there = + Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); + + if(there) { + /* this socket is in the hash */ + /* We remove the hash entry. (This'll end up in a call to + sh_freeentry().) */ + Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); + } +} + +/* + * free a sockhash entry + */ +static void sh_freeentry(void *freethis) +{ + struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; + + free(p); +} + +/* + * sh_init() creates a new socket hash and returns the handle for it. + * + * Quote from README.multi_socket: + * + * "Some tests at 7000 and 9000 connections showed that the socket hash lookup + * is somewhat of a bottle neck. Its current implementation may be a bit too + * limiting. It simply has a fixed-size array, and on each entry in the array + * it has a linked list with entries. So the hash only checks which list to + * scan through. The code I had used so for used a list with merely 7 slots + * (as that is what the DNS hash uses) but with 7000 connections that would + * make an average of 1000 nodes in each list to run through. I upped that to + * 97 slots (I believe a prime is suitable) and noticed a significant speed + * increase. I need to reconsider the hash implementation or use a rather + * large default value like this. At 9000 connections I was still below 10us + * per call." + * + */ +static struct curl_hash *sh_init(void) +{ + return Curl_hash_alloc(97, sh_freeentry); +} + +CURLM *curl_multi_init(void) +{ + struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1); + + if(!multi) return NULL; + multi->type = CURL_MULTI_HANDLE; + multi->hostcache = Curl_mk_dnscache(); if(!multi->hostcache) { /* failure, free mem and bail out */ free(multi); - multi = NULL; + return NULL; + } + + multi->sockhash = sh_init(); + if(!multi->sockhash) { + /* failure, free mem and bail out */ + Curl_hash_destroy(multi->hostcache); + free(multi); + return NULL; } + + multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1); + if(!multi->connc) { + Curl_hash_destroy(multi->hostcache); + free(multi); + return NULL; + } + return (CURLM *) multi; } @@ -137,6 +362,8 @@ CURLMcode curl_multi_add_handle(CURLM *multi_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)) @@ -146,20 +373,66 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, 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 = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy)); + easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1); if(!easy) return CURLM_OUT_OF_MEMORY; - /* clean it all first (just to be sure) */ - memset(easy, 0, sizeof(struct Curl_one_easy)); + 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 */ + } + cl = next; + } /* set the easy handle */ easy->easy_handle = easy_handle; - easy->state = CURLM_STATE_INIT; + multistate(easy, CURLM_STATE_INIT); + + /* 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; - /* for multi interface connections, we share DNS cache automaticly */ - easy->easy_handle->hostcache = multi->hostcache; + /* Make sure the type is setup correctly */ + easy->easy_handle->state.connc->type = CONNCACHE_MULTI; /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ @@ -174,12 +447,48 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(easy->next) easy->next->prev = easy; + Curl_easy_addmulti(easy_handle, multi_handle); + + /* make the SessionHandle struct refer back to this struct */ + easy->easy_handle->set.one_easy = easy; + /* increase the node-counter */ multi->num_easy++; - return CURLM_CALL_MULTI_PERFORM; + 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. */ + CURLcode res = Curl_ch_connc(easy_handle, multi->connc, + multi->connc->num*4); + if(res != CURLE_OK) + /* TODO: we need to do some cleaning up here! */ + return CURLM_OUT_OF_MEMORY; + } + + /* increase the alive-counter */ + multi->num_alive++; + + update_timer(multi); + return CURLM_OK; } +#if 0 +/* Debug-function, used like this: + * + * Curl_hash_print(multi->sockhash, debug_print_sock_hash); + * + * Enable the hash print function first by editing hash.c + */ +static void debug_print_sock_hash(void *p) +{ + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; + + fprintf(stderr, " [easy %p/magic %x/socket %d]", + (void *)sh->easy, sh->easy->magic, sh->socket); +} +#endif + CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle) { @@ -201,12 +510,100 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, break; easy=easy->next; } + 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_handle->state.is_in_pipeline && + easy->state > CURLM_STATE_DO) { + /* If the handle is in a pipeline and has finished sending off its + request, we need to remember the fact that we want to remove this + handle but do the actual removal at a later time */ + easy->easy_handle->state.cancelled = TRUE; + return CURLM_OK; + } + + /* 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; + } + + /* if we have a connection we must call Curl_done() here so that we + don't leave a half-baked one around */ + if(easy->easy_conn) { + /* Set up the association right */ + easy->easy_conn->data = easy->easy_handle; - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + /* Curl_done() clears the conn->data field to lose the association + between the easy handle and the connection */ + 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->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) @@ -215,6 +612,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(easy->next) easy->next->prev = easy->prev; + easy->easy_handle->set.one_easy = NULL; /* detached */ + /* NOTE NOTE NOTE We do not touch the easy handle here! */ if (easy->msg) @@ -223,12 +622,91 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_easy--; /* one less to care about now */ + update_timer(multi); return CURLM_OK; } else return CURLM_BAD_EASY_HANDLE; /* twasn't found */ } +bool Curl_multi_canPipeline(struct Curl_multi* multi) +{ + return multi->pipelining_enabled; +} + +static int waitconnect_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) +{ + if(!numsocks) + return GETSOCK_BLANK; + + sock[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_WRITESOCK(0); +} + +static int domore_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) +{ + if(!numsocks) + return GETSOCK_BLANK; + + /* When in DO_MORE state, we could be either waiting for us + to connect to a remote site, or we could wait for that site + to connect to us. It makes a difference in the way: if we + connect to the site we wait for the socket to become writable, if + the site connects to us we wait for it to become readable */ + sock[0] = conn->sock[SECONDARYSOCKET]; + + return GETSOCK_WRITESOCK(0); +} + +/* returns bitmapped flags for this handle and its sockets */ +static int multi_getsock(struct Curl_one_easy *easy, + curl_socket_t *socks, /* points to numsocks number + of sockets */ + int numsocks) +{ + if (easy->easy_handle->state.pipe_broke) { + return 0; + } + + if (easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) { + /* Set up ownership correctly */ + easy->easy_conn->data = easy->easy_handle; + } + + switch(easy->state) { + case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ + default: + /* this will get called with CURLM_STATE_COMPLETED when a handle is + removed */ + return 0; + + case CURLM_STATE_WAITRESOLVE: + return Curl_resolv_getsock(easy->easy_conn, socks, numsocks); + + case CURLM_STATE_PROTOCONNECT: + return Curl_protocol_getsock(easy->easy_conn, socks, numsocks); + + case CURLM_STATE_DOING: + return Curl_doing_getsock(easy->easy_conn, socks, numsocks); + + case CURLM_STATE_WAITCONNECT: + return waitconnect_getsock(easy->easy_conn, socks, numsocks); + + case CURLM_STATE_DO_MORE: + return domore_getsock(easy->easy_conn, socks, numsocks); + + case CURLM_STATE_PERFORM: + case CURLM_STATE_WAITPERFORM: + return Curl_single_getsock(easy->easy_conn, socks, numsocks); + } + +} + CURLMcode curl_multi_fdset(CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd) @@ -239,367 +717,710 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; int this_max_fd=-1; + curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + int i; + (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - *max_fd = -1; /* so far none! */ - easy=multi->easy.next; while(easy) { - switch(easy->state) { - default: - break; - case CURLM_STATE_WAITRESOLVE: - /* waiting for a resolve to complete */ - Curl_fdset(easy->easy_conn, read_fd_set, write_fd_set, &this_max_fd); - if(this_max_fd > *max_fd) - *max_fd = this_max_fd; - break; + bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); - case CURLM_STATE_WAITCONNECT: - case CURLM_STATE_DO_MORE: - { - /* when we're waiting for a connect, we wait for the socket to - become writable */ - struct connectdata *conn = easy->easy_conn; - curl_socket_t sockfd; - - if(CURLM_STATE_WAITCONNECT == easy->state) { - sockfd = conn->sock[FIRSTSOCKET]; - FD_SET(sockfd, write_fd_set); - } - else { - /* When in DO_MORE state, we could be either waiting for us - to connect to a remote site, or we could wait for that site - to connect to us. It makes a difference in the way: if we - connect to the site we wait for the socket to become writable, if - the site connects to us we wait for it to become readable */ - sockfd = conn->sock[SECONDARYSOCKET]; - FD_SET(sockfd, write_fd_set); - } + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; - if((int)sockfd > *max_fd) - *max_fd = (int)sockfd; + if(bitmap & GETSOCK_READSOCK(i)) { + FD_SET(sockbunch[i], read_fd_set); + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + FD_SET(sockbunch[i], write_fd_set); + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) + /* this socket is unused, break out of loop */ + break; + else { + if((int)s > this_max_fd) + this_max_fd = (int)s; } - break; - case CURLM_STATE_PERFORM: - /* This should have a set of file descriptors for us to set. */ - /* after the transfer is done, go DONE */ - - Curl_single_fdset(easy->easy_conn, - read_fd_set, write_fd_set, - exc_fd_set, &this_max_fd); - - /* remember the maximum file descriptor */ - if(this_max_fd > *max_fd) - *max_fd = this_max_fd; - - break; } + easy = easy->next; /* check next handle */ } + *max_fd = this_max_fd; + return CURLM_OK; } -CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct Curl_one_easy *easy) { - struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; - bool done; - CURLMcode result=CURLM_OK; - struct Curl_message *msg; + struct Curl_message *msg = NULL; bool connected; bool async; + bool protocol_connect; + bool dophase_done; + bool done; + CURLMcode result = CURLM_OK; + struct Curl_transfer_keeper *k; + + do { + + if(!GOOD_EASY_HANDLE(easy->easy_handle)) + return CURLM_BAD_EASY_HANDLE; + + if (easy->easy_handle->state.pipe_broke) { + infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", + easy, easy->easy_handle->reqdata.path); + if(easy->easy_handle->state.is_in_pipeline) { + /* Head back to the CONNECT state */ + multistate(easy, CURLM_STATE_CONNECT); + result = CURLM_CALL_MULTI_PERFORM; + easy->result = CURLE_OK; + } else { + easy->result = CURLE_COULDNT_CONNECT; + multistate(easy, CURLM_STATE_COMPLETED); + } - *running_handles = 0; /* bump this once for every living handle */ - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + easy->easy_handle->state.pipe_broke = FALSE; + easy->easy_conn = NULL; + break; + } - easy=multi->easy.next; - while(easy) { -#if 0 - fprintf(stderr, "HANDLE %p: State: %x\n", - (char *)easy, easy->state); -#endif - do { - if (CURLM_STATE_WAITCONNECT <= easy->state && - easy->state <= CURLM_STATE_DO && - easy->easy_handle->change.url_changed) { - char *gotourl; - Curl_posttransfer(easy->easy_handle); + 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; + } - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); - if(CURLE_OK == easy->result) { - gotourl = strdup(easy->easy_handle->change.url); - if(gotourl) { - easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl); - if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_CONNECT; - else - free(gotourl); - } - else { - easy->result = CURLE_OUT_OF_MEMORY; - easy->state = CURLM_STATE_COMPLETED; - break; - } + if (CURLM_STATE_WAITCONNECT <= easy->state && + easy->state <= CURLM_STATE_DO && + easy->easy_handle->change.url_changed) { + char *gotourl; + Curl_posttransfer(easy->easy_handle); + + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + /* We make sure that the pipe broken flag is reset + because in this case, it isn't an actual break */ + easy->easy_handle->state.pipe_broke = FALSE; + if(CURLE_OK == easy->result) { + gotourl = strdup(easy->easy_handle->change.url); + if(gotourl) { + easy->easy_handle->change.url_changed = FALSE; + easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); + if(CURLE_OK == easy->result) + multistate(easy, CURLM_STATE_CONNECT); + else + free(gotourl); + } + else { + easy->result = CURLE_OUT_OF_MEMORY; + multistate(easy, CURLM_STATE_COMPLETED); + break; } } + } - easy->easy_handle->change.url_changed = FALSE; + easy->easy_handle->change.url_changed = FALSE; - switch(easy->state) { - case CURLM_STATE_INIT: - /* init this transfer. */ - easy->result=Curl_pretransfer(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 */ - easy->state = CURLM_STATE_CONNECT; - result = CURLM_CALL_MULTI_PERFORM; + 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; + 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); + 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) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - easy->state = CURLM_STATE_WAITRESOLVE; - else { - /* after the connect has been sent off, go WAITCONNECT */ - easy->state = CURLM_STATE_WAITCONNECT; - result = CURLM_CALL_MULTI_PERFORM; + if(CURLE_OK == easy->result) { + /* Add this handle to the send pipeline */ + Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + + 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! */ + result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) { + multistate(easy, CURLM_STATE_WAITDO); + } else { + multistate(easy, CURLM_STATE_WAITCONNECT); } } - break; - - case CURLM_STATE_WAITRESOLVE: - /* awaiting an asynch name resolve to complete */ - { - struct Curl_dns_entry *dns = NULL; + } + break; - /* check if we have the name resolved by now */ - easy->result = Curl_is_resolved(easy->easy_conn, &dns); + 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) { + /* 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, CURLM_STATE_DO); + else + multistate(easy, CURLM_STATE_WAITCONNECT); + } + } - if(dns) { - /* Perform the next step in the connection phase, and then move on - to the WAITCONNECT state */ - easy->result = Curl_async_resolved(easy->easy_conn); + if(CURLE_OK != easy->result) { + /* failure detected */ + Curl_disconnect(easy->easy_conn); /* disconnect properly */ + easy->easy_conn = NULL; /* no more connection */ + break; + } + } + break; - 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 */ + 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 */ + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + break; + } - easy->state = CURLM_STATE_WAITCONNECT; + 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. */ + multistate(easy, CURLM_STATE_PROTOCONNECT); } + else { + /* after the connect has completed, go WAITDO */ + multistate(easy, CURLM_STATE_WAITDO); - if(CURLE_OK != easy->result) { - /* failure detected */ - Curl_disconnect(easy->easy_conn); /* disconnect properly */ - easy->easy_conn = NULL; /* no more connection */ - break; + result = CURLM_CALL_MULTI_PERFORM; } } break; - 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); + case CURLM_STATE_PROTOCONNECT: + /* protocol-specific connect phase */ + easy->result = Curl_protocol_connecting(easy->easy_conn, + &protocol_connect); + if(protocol_connect) { + /* after the connect has completed, go WAITDO */ + multistate(easy, CURLM_STATE_WAITDO); + result = CURLM_CALL_MULTI_PERFORM; + } + else if(easy->result) { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } + 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, + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)); +#endif + if (!easy->easy_conn->writechannel_inuse && + Curl_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(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 */ + easy->result = Curl_readwrite_init(easy->easy_conn); + 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); Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ - break; - } - - if(connected) { - /* after the connect has completed, go DO */ - easy->state = CURLM_STATE_DO; - result = CURLM_CALL_MULTI_PERFORM; } - break; - - case CURLM_STATE_DO: - /* Do the fetch or put request */ - easy->result = Curl_do(&easy->easy_conn); - if(CURLE_OK == easy->result) { + } + break; - /* after do, go PERFORM... or DO_MORE */ + 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 */ - easy->state = CURLM_STATE_DO_MORE; + multistate(easy, CURLM_STATE_DO_MORE); result = CURLM_OK; } else { - /* we're done with the DO, now PERFORM */ + /* we're done with the DO, now DO_DONE */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; + multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } } - } - break; + } /* dophase_done */ + } + else { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } + break; - case CURLM_STATE_DO_MORE: + case CURLM_STATE_DO_MORE: + /* Ready to do more? */ + easy->result = Curl_is_connected(easy->easy_conn, + SECONDARYSOCKET, + &connected); + if(connected) { /* - * First, check if we really are ready to do more. + * When we are connected, DO MORE and then go DO_DONE */ - easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, - &connected); - if(connected) { - /* - * When we are connected, DO MORE and then go PERFORM - */ - easy->result = Curl_do_more(easy->easy_conn); + easy->result = Curl_do_more(easy->easy_conn); - if(CURLE_OK == easy->result) - easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); + else + /* Remove ourselves from the send pipeline */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); - if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; - result = CURLM_CALL_MULTI_PERFORM; - } + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } + } + break; + + case CURLM_STATE_DO_DONE: + /* Remove ourselves from the send pipeline */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + /* Add ourselves to the recv pipeline */ + Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + 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, + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)); +#endif + /* Wait for our turn to PERFORM */ + if (!easy->easy_conn->readchannel_inuse && + Curl_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; + } - case CURLM_STATE_PERFORM: - /* read/write data if it is ready to do so */ - easy->result = Curl_readwrite(easy->easy_conn, &done); - - if(easy->result) { - /* The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is becasue we can't - * possibly know if the connection is in a good shape or not now. */ - easy->easy_conn->bits.close = TRUE; - - 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]=-1; - } - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); - } + /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_conn, &done); - /* after the transfer is done, go DONE */ - else if(TRUE == done) { + k = &easy->easy_handle->reqdata.keep; - /* call this even if the readwrite function returned error */ - Curl_posttransfer(easy->easy_handle); + if (!(k->keepon & KEEP_READ)) { + /* We're done reading */ + easy->easy_conn->readchannel_inuse = FALSE; + } - /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_conn->newurl) { - char *newurl = easy->easy_conn->newurl; - easy->easy_conn->newurl = NULL; - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); - if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl); - if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_CONNECT; - result = CURLM_CALL_MULTI_PERFORM; - } + 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 becasue we can't + * possibly know if the connection is in a good shape or not now. */ + easy->easy_conn->bits.close = TRUE; + + 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); + + /* call this even if the readwrite function returned error */ + Curl_posttransfer(easy->easy_handle); + + /* When we follow redirects, must to go back to the CONNECT state */ + if(easy->easy_handle->reqdata.newurl || retry) { + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + 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->reqdata.newurl; + easy->easy_handle->reqdata.newurl = NULL; } - else { - easy->state = CURLM_STATE_DONE; + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + if(easy->result == CURLE_OK) + easy->result = Curl_follow(easy->easy_handle, newurl, retry); + 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); } - break; - case CURLM_STATE_DONE: + else { + /* after the transfer is done, go DONE */ + 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); + easy->easy_handle->state.is_in_pipeline = FALSE; + + 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; + } + + if (!easy->easy_handle->state.cancelled) { /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + 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! */ - easy->state = CURLM_STATE_COMPLETED; - break; + multistate(easy, CURLM_STATE_COMPLETED); + } - case CURLM_STATE_COMPLETED: - /* this is a completed transfer, it is likely to still be connected */ + break; - /* This node should be delinked from the list now and we should post - an information message that we are complete. */ - break; - default: - return CURLM_INTERNAL_ERROR; - } + case CURLM_STATE_COMPLETED: + if (easy->easy_handle->state.cancelled) + /* Go into the CANCELLED state if we were cancelled */ + multistate(easy, CURLM_STATE_CANCELLED); + + /* this is a completed transfer, it is likely to still be connected */ - 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. */ - easy->state = CURLM_STATE_COMPLETED; + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ + break; + + case CURLM_STATE_CANCELLED: + /* Cancelled transfer, wait to be cleaned up */ + 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. + */ + easy->easy_handle->state.is_in_pipeline = FALSE; + 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; } - else - /* this one still lives! */ - (*running_handles)++; + multistate(easy, CURLM_STATE_COMPLETED); } + } - } while (easy->easy_handle->change.url_changed); + } while (easy->easy_handle->change.url_changed); - if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + 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->hostcache = NULL; + 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 = (struct Curl_message *)malloc(sizeof(struct Curl_message)); - /* now add a node to the Curl_message linked list with this info */ - msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); + if(!msg) + return CURLM_OUT_OF_MEMORY; - 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; - 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 */ - easy->msg = msg; - easy->msg_num = 1; /* there is one unread message here */ + multi->num_msgs++; /* increase message counter */ + } - multi->num_msgs++; /* increase message counter */ + return result; +} + + +CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + CURLMcode returncode=CURLM_OK; + struct Curl_tree *t; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + easy=multi->easy.next; + while(easy) { + CURLMcode result; + + if (easy->easy_handle->state.cancelled && + easy->state == CURLM_STATE_CANCELLED) { + /* Remove cancelled handles once it's safe to do so */ + Curl_multi_rmeasy(multi_handle, easy->easy_handle); + easy->easy_handle = NULL; + easy = easy->next; + continue; } + result = multi_runsingle(multi, easy); + if(result) + returncode = result; + easy = easy->next; /* operate on next handle */ } - return result; + /* + * Simply remove all expired timers from the splay since handles are dealt + * with unconditionally by this function and curl_multi_timeout() requires + * that already passed/handled expire times are removed from the splay. + */ + do { + struct timeval now = Curl_tvnow(); + int key = now.tv_sec; /* drop the usec part */ + + multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); + if (t) { + struct SessionHandle *d = t->payload; + struct timeval* tv = &d->state.expiretime; + + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + + } while(t); + + *running_handles = multi->num_alive; + + if ( CURLM_OK == returncode ) + update_timer(multi); + return returncode; +} + +/* This is called when an easy handle is cleanup'ed that is part of a multi + handle */ +void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle) +{ + curl_multi_remove_handle(multi_handle, easy_handle); } + CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; struct Curl_one_easy *nexteasy; + int i; + struct closure *cl; + struct closure *n; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ Curl_hash_destroy(multi->hostcache); + Curl_hash_destroy(multi->sockhash); + + /* go over all connections that have close actions */ + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + multi->connc->connects[i]->protocol & PROT_CLOSEACTION) { + Curl_disconnect(multi->connc->connects[i]); + multi->connc->connects[i] = NULL; + } + } + /* now walk through the list of handles we kept around only to be + able to close connections "properly" */ + cl = multi->closure; + while(cl) { + cl->easy_handle->state.shared_conn = NULL; /* no more shared */ + if(cl->easy_handle->state.closed) + /* close handle only if curl_easy_cleanup() already has been called + for this easy handle */ + Curl_close(cl->easy_handle); + n = cl->next; + free(cl); + cl= n; + } + + Curl_rm_connc(multi->connc); /* remove all easy handles */ easy = multi->easy.next; while(easy) { nexteasy=easy->next; - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + 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; + } + + /* Clear the pointer to the connection cache */ + easy->easy_handle->state.connc = NULL; + + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ if (easy->msg) free(easy->msg); @@ -646,3 +1467,522 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) else return NULL; } + +/* + * singlesocket() checks what sockets we deal with and their "action state" + * and if we have a different state in any of those sockets from last time we + * call the callback accordingly. + */ +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<num; j++) { + if(s == socks[j]) { + /* this is still supervised */ + s = CURL_SOCKET_BAD; + break; + } + } + if(s != CURL_SOCKET_BAD) { + /* this socket has been removed. Remove it */ + + entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + if(entry) { + /* just a precaution, this socket really SHOULD be in the hash already + but in case it isn't, we don't have to tell the app to remove it + either since it never got to know about it */ + multi->socket_cb(easy->easy_handle, + s, + CURL_POLL_REMOVE, + multi->socket_userp, + entry ? entry->socketp : NULL); + + sh_delentry(multi->sockhash, s); + } + } + } + + memcpy(easy->sockets, socks, num*sizeof(curl_socket_t)); + easy->numsocks = num; +} + +static CURLMcode multi_socket(struct Curl_multi *multi, + bool checkall, + curl_socket_t s, + 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) { + 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, major problemo! */ + return CURLM_BAD_SOCKET; /* better return code? */ + + data = entry->easy; + + if(data->magic != CURLEASY_MAGIC_NUMBER) + /* bad bad bad bad bad bad bad */ + return CURLM_INTERNAL_ERROR; + + result = multi_runsingle(multi, data->set.one_easy); + + if(result == CURLM_OK) + /* 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 { + int key; + struct timeval now; + + /* the first loop lap 'data' can be NULL */ + if(data) { + result = multi_runsingle(multi, data->set.one_easy); + + if(result == CURLM_OK) + /* 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(); + key = now.tv_sec; /* drop the usec part */ + + multi->timetree = Curl_splaygetbest(key, 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; +} + +CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + CURLMcode res = CURLM_OK; + va_list param; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + va_start(param, option); + + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + multi->socket_cb = va_arg(param, curl_socket_callback); + break; + case CURLMOPT_SOCKETDATA: + multi->socket_userp = va_arg(param, void *); + break; + case CURLMOPT_PIPELINING: + multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); + break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; + default: + res = CURLM_UNKNOWN_OPTION; + break; + } + va_end(param); + return res; +} + + +CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles) +{ + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; +} + +CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) + +{ + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, + TRUE, CURL_SOCKET_BAD, running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; +} + +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms) +{ + if(multi->timetree) { + /* we have a tree of expire times */ + struct timeval now = Curl_tvnow(); + + /* splay the lowest to the bottom */ + multi->timetree = Curl_splay(0, multi->timetree); + + /* At least currently, the splay key is a time_t for the expire time */ + *timeout_ms = (multi->timetree->key - now.tv_sec) * 1000 - + now.tv_usec/1000; + if(*timeout_ms < 0) + /* 0 means immediately */ + *timeout_ms = 0; + } + else + *timeout_ms = -1; + + return CURLM_OK; +} + +CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *timeout_ms) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + return multi_timeout(multi, timeout_ms); +} + +/* + * Tell the application it should update its timers, if it subscribes to the + * update timer callback. + */ +static int update_timer(struct Curl_multi *multi) +{ + long timeout_ms; + if (!multi->timer_cb) + return 0; + if ( multi_timeout(multi, &timeout_ms) != CURLM_OK ) + return -1; + if ( timeout_ms < 0 ) + return 0; + + /* When multi_timeout() is done, multi->timetree points to the node with the + * timeout we got the (relative) time-out time for. We can thus easily check + * if this is the same (fixed) time as we got in a previous call and then + * avoid calling the callback again. */ + if(multi->timetree->key == multi->timer_lastcall) + return 0; + + multi->timer_lastcall = multi->timetree->key; + + return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); +} + +/* given a number of milliseconds from now to use to set the 'act before + this'-time for the transfer, to be extracted by curl_multi_timeout() */ +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) { + /* 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) { + /* 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)\n", + (long)nowp->tv_sec, (long)nowp->tv_usec, milli); +#endif + data->state.timenode.payload = data; + multi->timetree = Curl_splayinsert((int)nowp->tv_sec, + multi->timetree, + &data->state.timenode); + } +#if 0 + Curl_splayprint(multi->timetree, 0, TRUE); +#endif +} + +CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t s, void *hashp) +{ + struct Curl_sh_entry *there = NULL; + struct Curl_multi *multi = (struct Curl_multi *)multi_handle; + + if(s != CURL_SOCKET_BAD) + there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t)); + + if(!there) + return CURLM_BAD_SOCKET; + + there->socketp = hashp; + + return CURLM_OK; +} + +static bool multi_conn_using(struct Curl_multi *multi, + struct SessionHandle *data) +{ + /* any live CLOSEACTION-connections pointing to the give 'data' ? */ + int i; + + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + (multi->connc->connects[i]->data == data) && + multi->connc->connects[i]->protocol & PROT_CLOSEACTION) + return TRUE; + } + + return FALSE; +} + +/* Add the given data pointer to the list of 'closure handles' that are kept + around only to be able to close some connections nicely - just make sure + that this handle isn't already added, like for the cases when an easy + handle is removed, added and removed again... */ +static void add_closure(struct Curl_multi *multi, + struct SessionHandle *data) +{ + int i; + struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1); + struct closure *p=NULL; + struct closure *n; + if(cl) { + cl->easy_handle = data; + cl->next = multi->closure; + multi->closure = cl; + } + + p = multi->closure; + cl = p->next; /* start immediately on the second since the first is the one + we just added and it is _very_ likely to actually exist + used in the cache since that's the whole purpose of adding + it to this list! */ + + /* When adding, scan through all the other currently kept handles and see if + there are any connections still referring to them and kill them if not. */ + while(cl) { + bool inuse = FALSE; + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + (multi->connc->connects[i]->data == cl->easy_handle)) { + inuse = TRUE; + break; + } + } + + n = cl->next; + + if(!inuse) { + /* cl->easy_handle is now killable */ + infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle); + /* unmark it as not having a connection around that uses it anymore */ + cl->easy_handle->state.shared_conn= NULL; + Curl_close(cl->easy_handle); + if(p) + p->next = n; + else + multi->closure = n; + free(cl); + } + else + p = cl; + + cl = n; + } + +} + +#ifdef CURLDEBUG +void curl_multi_dump(CURLM *multi_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + int i; + fprintf(stderr, "* Multi status: %d handles, %d alive\n", + multi->num_easy, multi->num_alive); + for(easy=multi->easy.next; easy; easy = easy->next) { + if(easy->state != CURLM_STATE_COMPLETED) { + /* only display handles that are not completed */ + fprintf(stderr, "handle %p, state %s, %d sockets\n", + (void *)easy->easy_handle, + statename[easy->state], easy->numsocks); + for(i=0; i < easy->numsocks; i++) { + curl_socket_t s = easy->sockets[i]; + struct Curl_sh_entry *entry = + Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + + fprintf(stderr, "%d ", (int)s); + if(!entry) { + fprintf(stderr, "INTERNAL CONFUSION\n"); + continue; + } + fprintf(stderr, "[%s %s] ", + entry->action&CURL_POLL_IN?"RECVING":"", + entry->action&CURL_POLL_OUT?"SENDING":""); + } + if(easy->numsocks) + fprintf(stderr, "\n"); + } + } +} +#endif diff --git a/Utilities/cmcurl/multiif.h b/Utilities/cmcurl/multiif.h new file mode 100644 index 0000000..800aa0f --- /dev/null +++ b/Utilities/cmcurl/multiif.h @@ -0,0 +1,46 @@ +#ifndef __MULTIIF_H +#define __MULTIIF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* + * Prototypes for library-wide functions provided by multi.c + */ +void Curl_expire(struct SessionHandle *data, long milli); + +void Curl_multi_rmeasy(void *multi, CURL *data); + +bool Curl_multi_canPipeline(struct Curl_multi* multi); + +/* the write bits start at bit 16 for the *getsock() bitmap */ +#define GETSOCK_WRITEBITSTART 16 + +#define GETSOCK_BLANK 0 /* no bits set */ + +/* set the bit for the given sock number to make the bitmap for writable */ +#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x))) + +/* set the bit for the given sock number to make the bitmap for readable */ +#define GETSOCK_READSOCK(x) (1 << (x)) + +#endif /* __MULTIIF_H */ diff --git a/Utilities/cmcurl/netrc.c b/Utilities/cmcurl/netrc.c index 770bfb5..54d1759 100644 --- a/Utilities/cmcurl/netrc.c +++ b/Utilities/cmcurl/netrc.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -45,7 +45,7 @@ #include "strequal.h" #include "strtok.h" -#include "curl_memory.h" +#include "memory.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -83,7 +83,7 @@ int Curl_parsenetrc(char *host, FILE *file; int retcode=1; int specific_login = (login[0] != 0); - char *home = NULL; + char *home = NULL; bool home_alloc = FALSE; bool netrc_alloc = FALSE; int state=NOTHING; @@ -103,7 +103,7 @@ int Curl_parsenetrc(char *host, char *override = curl_getenv("CURL_DEBUG_NETRC"); if (override) { - printf("NETRC: overridden " NETRC " file: %s\n", home); + fprintf(stderr, "NETRC: overridden " NETRC " file: %s\n", override); netrcfile = override; netrc_alloc = TRUE; } @@ -171,7 +171,7 @@ int Curl_parsenetrc(char *host, /* and yes, this is our host! */ state=HOSTVALID; #ifdef _NETRC_DEBUG - printf("HOST: %s\n", tok); + fprintf(stderr, "HOST: %s\n", tok); #endif retcode=0; /* we did find our host */ } @@ -188,7 +188,7 @@ int Curl_parsenetrc(char *host, else { strncpy(login, tok, LOGINSIZE-1); #ifdef _NETRC_DEBUG - printf("LOGIN: %s\n", login); + fprintf(stderr, "LOGIN: %s\n", login); #endif } state_login=0; @@ -197,7 +197,7 @@ int Curl_parsenetrc(char *host, if (state_our_login || !specific_login) { strncpy(password, tok, PASSWORDSIZE-1); #ifdef _NETRC_DEBUG - printf("PASSWORD: %s\n", password); + fprintf(stderr, "PASSWORD: %s\n", password); #endif } state_password=0; diff --git a/Utilities/cmcurl/nwlib.c b/Utilities/cmcurl/nwlib.c index a999dfd..b0eea56 100644 --- a/Utilities/cmcurl/nwlib.c +++ b/Utilities/cmcurl/nwlib.c @@ -30,7 +30,7 @@ #include <nks/thread.h> #include <nks/synch.h> -#include "curl_memory.h" +#include "memory.h" #include "memdebug.h" typedef struct diff --git a/Utilities/cmcurl/parsedate.c b/Utilities/cmcurl/parsedate.c new file mode 100644 index 0000000..ef91585 --- /dev/null +++ b/Utilities/cmcurl/parsedate.c @@ -0,0 +1,425 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ +/* + A brief summary of the date string formats this parser groks: + + RFC 2616 3.3.1 + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + we support dates without week day name: + + 06 Nov 1994 08:49:37 GMT + 06-Nov-94 08:49:37 GMT + Nov 6 08:49:37 1994 + + without the time zone: + + 06 Nov 1994 08:49:37 + 06-Nov-94 08:49:37 + + weird order: + + 1994 Nov 6 08:49:37 (GNU date fails) + GMT 08:49:37 06-Nov-94 Sunday + 94 6 Nov 08:49:37 (GNU date fails) + + time left out: + + 1994 Nov 6 + 06-Nov-94 + Sun Nov 6 94 + + unusual separators: + + 1994.Nov.6 + Sun/Nov/6/94/GMT + + commonly used time zone names: + + Sun, 06 Nov 1994 08:49:37 CET + 06 Nov 1994 08:49:37 EST + + time zones specified using RFC822 style: + + Sun, 12 Sep 2004 15:05:58 -0700 + Sat, 11 Sep 2004 21:32:11 +0200 + + compact numerical date strings: + + 20040912 15:05:58 -0700 + 20040911 +0200 + +*/ +#include "setup.h" +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* for strtol() */ +#endif + +#include <curl/curl.h> + +static time_t Curl_parsedate(const char *date); + +const char * const Curl_wkday[] = +{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +static const char * const weekday[] = +{ "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" }; +const char * const Curl_month[]= +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +struct tzinfo { + const char *name; + int offset; /* +/- in minutes */ +}; + +/* Here's a bunch of frequently used time zone names. These were supported + by the old getdate parser. */ +#define tDAYZONE -60 /* offset for daylight savings time */ +static const struct tzinfo tz[]= { + {"GMT", 0}, /* Greenwich Mean */ + {"UTC", 0}, /* Universal (Coordinated) */ + {"WET", 0}, /* Western European */ + {"BST", 0 tDAYZONE}, /* British Summer */ + {"WAT", 60}, /* West Africa */ + {"AST", 240}, /* Atlantic Standard */ + {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */ + {"EST", 300}, /* Eastern Standard */ + {"EDT", 300 tDAYZONE}, /* Eastern Daylight */ + {"CST", 360}, /* Central Standard */ + {"CDT", 360 tDAYZONE}, /* Central Daylight */ + {"MST", 420}, /* Mountain Standard */ + {"MDT", 420 tDAYZONE}, /* Mountain Daylight */ + {"PST", 480}, /* Pacific Standard */ + {"PDT", 480 tDAYZONE}, /* Pacific Daylight */ + {"YST", 540}, /* Yukon Standard */ + {"YDT", 540 tDAYZONE}, /* Yukon Daylight */ + {"HST", 600}, /* Hawaii Standard */ + {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */ + {"CAT", 600}, /* Central Alaska */ + {"AHST", 600}, /* Alaska-Hawaii Standard */ + {"NT", 660}, /* Nome */ + {"IDLW", 720}, /* International Date Line West */ + {"CET", -60}, /* Central European */ + {"MET", -60}, /* Middle European */ + {"MEWT", -60}, /* Middle European Winter */ + {"MEST", -60 tDAYZONE}, /* Middle European Summer */ + {"CEST", -60 tDAYZONE}, /* Central European Summer */ + {"MESZ", -60 tDAYZONE}, /* Middle European Summer */ + {"FWT", -60}, /* French Winter */ + {"FST", -60 tDAYZONE}, /* French Summer */ + {"EET", -120}, /* Eastern Europe, USSR Zone 1 */ + {"WAST", -420}, /* West Australian Standard */ + {"WADT", -420 tDAYZONE}, /* West Australian Daylight */ + {"CCT", -480}, /* China Coast, USSR Zone 7 */ + {"JST", -540}, /* Japan Standard, USSR Zone 8 */ + {"EAST", -600}, /* Eastern Australian Standard */ + {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */ + {"GST", -600}, /* Guam Standard, USSR Zone 9 */ + {"NZT", -720}, /* New Zealand */ + {"NZST", -720}, /* New Zealand Standard */ + {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */ + {"IDLE", -720}, /* International Date Line East */ +}; + +/* returns: + -1 no day + 0 monday - 6 sunday +*/ + +static int checkday(char *check, size_t len) +{ + int i; + const char * const *what; + bool found= FALSE; + if(len > 3) + what = &weekday[0]; + else + what = &Curl_wkday[0]; + for(i=0; i<7; i++) { + if(curl_strequal(check, what[0])) { + found=TRUE; + break; + } + what++; + } + return found?i:-1; +} + +static int checkmonth(char *check) +{ + int i; + const char * const *what; + bool found= FALSE; + + what = &Curl_month[0]; + for(i=0; i<12; i++) { + if(curl_strequal(check, what[0])) { + found=TRUE; + break; + } + what++; + } + return found?i:-1; /* return the offset or -1, no real offset is -1 */ +} + +/* return the time zone offset between GMT and the input one, in number + of seconds or -1 if the timezone wasn't found/legal */ + +static int checktz(char *check) +{ + unsigned int i; + const struct tzinfo *what; + bool found= FALSE; + + what = tz; + for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) { + if(curl_strequal(check, what->name)) { + found=TRUE; + break; + } + what++; + } + return found?what->offset*60:-1; +} + +static void skip(const char **date) +{ + /* skip everything that aren't letters or digits */ + while(**date && !ISALNUM(**date)) + (*date)++; +} + +enum assume { + DATE_MDAY, + DATE_YEAR, + DATE_TIME +}; + +static time_t Curl_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[A-Za-z]", 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 < 1300) && + (indate< date) && + ((date[-1] == '+' || date[-1] == '-'))) { + /* four digits and a value less than 1300 and it is preceeded with + a plus or minus. This is a time zone indication. */ + 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; + + /* mktime() 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 some of the systems with 64 bit time_t returns -1 for dates beyond + 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06) + */ + t = mktime(&tm); + + /* time zone adjust (cast t to int to compare to negative one) */ + if(-1 != (int)t) { + struct tm *gmt; + long delta; + time_t t2; + +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime2; + gmt = (struct tm *)gmtime_r(&t, &keeptime2); + if(!gmt) + return -1; /* illegal date/time */ + t2 = mktime(gmt); +#else + /* It seems that at least the MSVC version of mktime() doesn't work + properly if it gets the 'gmt' pointer passed in (which is a pointer + returned from gmtime() pointing to static memory), so instead we copy + the tm struct to a local struct and pass a pointer to that struct as + input to mktime(). */ + struct tm gmt2; + gmt = gmtime(&t); /* use gmtime_r() if available */ + if(!gmt) + return -1; /* illegal date/time */ + gmt2 = *gmt; + t2 = mktime(&gmt2); +#endif + + /* Add the time zone diff (between the given timezone and GMT) and the + diff between the local time zone and GMT. */ + delta = (long)((tzoff!=-1?tzoff:0) + (t - t2)); + + if((delta>0) && (t + delta < t)) + return -1; /* time_t overflow */ + + t += delta; + } + + return t; +} + +time_t curl_getdate(const char *p, const time_t *now) +{ + (void)now; + return Curl_parsedate(p); +} diff --git a/Utilities/cmcurl/parsedate.h b/Utilities/cmcurl/parsedate.h new file mode 100644 index 0000000..0ea2d83 --- /dev/null +++ b/Utilities/cmcurl/parsedate.h @@ -0,0 +1,28 @@ +#ifndef __PARSEDATE_H +#define __PARSEDATEL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ +extern const char * const Curl_wkday[7]; +extern const char * const Curl_month[12]; + +#endif diff --git a/Utilities/cmcurl/progress.c b/Utilities/cmcurl/progress.c index 36be56e..5ba829a 100644 --- a/Utilities/cmcurl/progress.c +++ b/Utilities/cmcurl/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -139,10 +139,8 @@ void Curl_pgrsDone(struct connectdata *conn) struct SessionHandle *data = conn->data; data->progress.lastshow=0; Curl_pgrsUpdate(conn); /* the final (forced) update */ - if(!(data->progress.flags & PGRS_HIDE) && - !data->progress.callback) - /* only output if we don't use a progress callback and we're not hidden */ - fprintf(data->set.err, "\n"); + + data->progress.speeder_c = 0; /* reset the progress meter display */ } /* reset all times except redirect */ @@ -254,11 +252,11 @@ int Curl_pgrsUpdate(struct connectdata *conn) even when not displayed! */ else if(!(data->progress.flags & PGRS_HEADERS_OUT)) { if (!data->progress.callback) { - if(conn->resume_from) + if(data->reqdata.resume_from) fprintf(data->set.err, "** Resuming transfer from byte position %" FORMAT_OFF_T "\n", - conn->resume_from); + data->reqdata.resume_from); fprintf(data->set.err, " %% Total %% Received %% Xferd Average Speed Time Time Time Current\n" " Dload Upload Total Spent Left Speed\n"); @@ -328,7 +326,7 @@ int Curl_pgrsUpdate(struct connectdata *conn) curl_off_t amount = data->progress.speeder[nowindex]- data->progress.speeder[checkindex]; - if(amount > 0xffffffff/1000) + if(amount > 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) diff --git a/Utilities/cmcurl/progress.h b/Utilities/cmcurl/progress.h index dcfcaf7..ad9d662 100644 --- a/Utilities/cmcurl/progress.h +++ b/Utilities/cmcurl/progress.h @@ -1,10 +1,10 @@ #ifndef __PROGRESS_H #define __PROGRESS_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. @@ -12,7 +12,7 @@ * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -37,7 +37,7 @@ typedef enum { TIMER_REDIRECT, TIMER_LAST /* must be last */ } timerid; - + void Curl_pgrsDone(struct connectdata *); void Curl_pgrsStartNow(struct SessionHandle *data); void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size); diff --git a/Utilities/cmcurl/security.c b/Utilities/cmcurl/security.c index 861f953..4c9aed8 100644 --- a/Utilities/cmcurl/security.c +++ b/Utilities/cmcurl/security.c @@ -46,7 +46,6 @@ #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */ #include <curl/mprintf.h> -#include "security.h" #include <stdlib.h> #include <string.h> #include <netdb.h> @@ -55,17 +54,19 @@ #include <unistd.h> #endif +#include "urldata.h" +#include "krb4.h" #include "base64.h" #include "sendf.h" #include "ftp.h" -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" #define min(a, b) ((a) < (b) ? (a) : (b)) -static struct { +static const struct { enum protection_level level; const char *name; } level_names[] = { @@ -80,12 +81,12 @@ name_to_level(const char *name) { int i; for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) - if(!strncasecmp(level_names[i].name, name, strlen(name))) + if(curl_strnequal(level_names[i].name, name, strlen(name))) return level_names[i].level; return (enum protection_level)-1; } -static struct Curl_sec_client_mech *mechs[] = { +static const struct Curl_sec_client_mech * const mechs[] = { #ifdef KRB5 /* not supported */ #endif @@ -277,6 +278,13 @@ Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length) return tx; } +ssize_t +Curl_sec_send(struct connectdata *conn, int num, char *buffer, int length) +{ + curl_socket_t fd = conn->sock[num]; + return (ssize_t)Curl_sec_write(conn, fd, buffer, length); +} + int Curl_sec_putc(struct connectdata *conn, int c, FILE *F) { @@ -297,13 +305,15 @@ int Curl_sec_read_msg(struct connectdata *conn, char *s, int level) { int len; - char *buf; + unsigned char *buf; int code; - buf = malloc(strlen(s)); - len = Curl_base64_decode(s + 4, buf); /* XXX */ + len = Curl_base64_decode(s + 4, &buf); /* XXX */ + if(len > 0) + len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); + else + return -1; - len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); if(len < 0) { free(buf); return -1; @@ -314,10 +324,10 @@ Curl_sec_read_msg(struct connectdata *conn, char *s, int level) if(buf[3] == '-') code = 0; else - sscanf(buf, "%d", &code); + sscanf((char *)buf, "%d", &code); if(buf[len-1] == '\n') buf[len-1] = '\0'; - strcpy(s, buf); + strcpy(s, (char *)buf); free(buf); return code; } @@ -400,7 +410,7 @@ int Curl_sec_login(struct connectdata *conn) { int ret; - struct Curl_sec_client_mech **m; + const struct Curl_sec_client_mech * const *m; ssize_t nread; struct SessionHandle *data=conn->data; int ftpcode; diff --git a/Utilities/cmcurl/security.h b/Utilities/cmcurl/security.h deleted file mode 100644 index 5213ba5..0000000 --- a/Utilities/cmcurl/security.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef __SECURITY_H -#define __SECURITY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * $Id$ - ***************************************************************************/ - -/* this is a re-write */ - -#include <stdarg.h> -#include "urldata.h" /* for struct connectdata * */ - -struct Curl_sec_client_mech { - const char *name; - size_t size; - int (*init)(void *); - int (*auth)(void *, struct connectdata *); - void (*end)(void *); - int (*check_prot)(void *, int); - int (*overhead)(void *, int, int); - int (*encode)(void *, void*, int, int, void**, struct connectdata *); - int (*decode)(void *, void*, int, int, struct connectdata *); -}; - - -#define AUTH_OK 0 -#define AUTH_CONTINUE 1 -#define AUTH_ERROR 2 - -extern struct Curl_sec_client_mech Curl_krb4_client_mech; - -int Curl_sec_fflush_fd(struct connectdata *conn, int fd); -int Curl_sec_fprintf (struct connectdata *, FILE *, const char *, ...); -int Curl_sec_getc (struct connectdata *conn, FILE *); -int Curl_sec_putc (struct connectdata *conn, int, FILE *); -int Curl_sec_read (struct connectdata *conn, int, void *, int); -int Curl_sec_read_msg (struct connectdata *conn, char *, int); - -int Curl_sec_vfprintf(struct connectdata *, FILE *, const char *, va_list); -int Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...); -int Curl_sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list); -int Curl_sec_write (struct connectdata *conn, int, char *, int); - -void Curl_sec_end (struct connectdata *); -int Curl_sec_login (struct connectdata *); -void Curl_sec_prot (int, char **); -int Curl_sec_request_prot (struct connectdata *conn, const char *level); -void Curl_sec_set_protection_level(struct connectdata *conn); -void Curl_sec_status (void); - -enum protection_level Curl_set_command_prot(struct connectdata *, - enum protection_level); - -#endif diff --git a/Utilities/cmcurl/select.c b/Utilities/cmcurl/select.c new file mode 100644 index 0000000..04a6fda --- /dev/null +++ b/Utilities/cmcurl/select.c @@ -0,0 +1,315 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" + +#include <errno.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifndef HAVE_SELECT +#error "We can't compile without select() support!" +#endif + +#ifdef __BEOS__ +/* BeOS has FD_SET defined in socket.h */ +#include <socket.h> +#endif + +#ifdef __MSDOS__ +#include <dos.h> /* delay() */ +#endif + +#include <curl/curl.h> + +#include "urldata.h" +#include "connect.h" +#include "select.h" + +#if defined(USE_WINSOCK) || defined(TPF) +#define VERIFY_SOCK(x) /* sockets are not in range [0..FD_SETSIZE] */ +#else +#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE)) +#define VERIFY_SOCK(x) do { \ + if(!VALID_SOCK(x)) { \ + errno = EINVAL; \ + return -1; \ + } \ +} while(0) +#endif + +/* + * This is an internal function used for waiting for read or write + * events on single file descriptors. It attempts to replace select() + * in order to avoid limits with FD_SETSIZE. + * + * Return values: + * -1 = system call error + * 0 = timeout + * CSELECT_IN | CSELECT_OUT | CSELECT_ERR + */ +int Curl_select(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms) +{ +#if defined(HAVE_POLL_FINE) || defined(CURL_HAVE_WSAPOLL) + struct pollfd pfd[2]; + int num; + int r; + int ret; + + num = 0; + if (readfd != CURL_SOCKET_BAD) { + pfd[num].fd = readfd; + pfd[num].events = POLLIN; + num++; + } + if (writefd != CURL_SOCKET_BAD) { + pfd[num].fd = writefd; + pfd[num].events = POLLOUT; + num++; + } + +#ifdef HAVE_POLL_FINE + do { + r = poll(pfd, num, timeout_ms); + } while((r == -1) && (errno == EINTR)); +#else + r = WSAPoll(pfd, num, timeout_ms); +#endif + + if (r < 0) + return -1; + if (r == 0) + return 0; + + ret = 0; + num = 0; + if (readfd != CURL_SOCKET_BAD) { + if (pfd[num].revents & (POLLIN|POLLHUP)) + ret |= CSELECT_IN; + if (pfd[num].revents & POLLERR) { +#ifdef __CYGWIN__ + /* Cygwin 1.5.21 needs this hack to pass test 160 */ + if (errno == EINPROGRESS) + ret |= CSELECT_IN; + else +#endif + ret |= CSELECT_ERR; + } + num++; + } + if (writefd != CURL_SOCKET_BAD) { + if (pfd[num].revents & POLLOUT) + ret |= CSELECT_OUT; + if (pfd[num].revents & (POLLERR|POLLHUP)) + ret |= CSELECT_ERR; + } + + return ret; +#else + struct timeval timeout; + fd_set fds_read; + fd_set fds_write; + fd_set fds_err; + curl_socket_t maxfd; + int r; + int ret; + + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + + if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { + /* According to POSIX we should pass in NULL pointers if we don't want to + wait for anything in particular but just use the timeout function. + Windows however returns immediately if done so. I copied the MSDOS + delay() use from src/main.c that already had this work-around. */ +#ifdef WIN32 + Sleep(timeout_ms); +#elif defined(__MSDOS__) + delay(timeout_ms); +#else + select(0, NULL, NULL, NULL, &timeout); +#endif + return 0; + } + + 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; + } + + do { + r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); + } while((r == -1) && (Curl_sockerrno() == EINTR)); + + if (r < 0) + return -1; + if (r == 0) + return 0; + + ret = 0; + if (readfd != CURL_SOCKET_BAD) { + if (FD_ISSET(readfd, &fds_read)) + ret |= CSELECT_IN; + if (FD_ISSET(readfd, &fds_err)) + ret |= CSELECT_ERR; + } + if (writefd != CURL_SOCKET_BAD) { + if (FD_ISSET(writefd, &fds_write)) + ret |= CSELECT_OUT; + if (FD_ISSET(writefd, &fds_err)) + ret |= CSELECT_ERR; + } + + return ret; +#endif +} + +/* + * This is a wrapper around poll(). If poll() does not exist, then + * select() is used instead. An error is returned if select() is + * being used and a file descriptor too large for FD_SETSIZE. + * + * Return values: + * -1 = system call error or fd >= FD_SETSIZE + * 0 = timeout + * 1 = number of structures with non zero revent fields + */ +int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) +{ + int r; +#ifdef HAVE_POLL_FINE + do { + r = poll(ufds, nfds, timeout_ms); + } while((r == -1) && (errno == EINTR)); +#elif defined(CURL_HAVE_WSAPOLL) + r = WSAPoll(ufds, nfds, timeout_ms); +#else + struct timeval timeout; + struct timeval *ptimeout; + fd_set fds_read; + fd_set fds_write; + fd_set fds_err; + curl_socket_t maxfd; + unsigned int i; + + FD_ZERO(&fds_read); + FD_ZERO(&fds_write); + FD_ZERO(&fds_err); + maxfd = (curl_socket_t)-1; + + for (i = 0; i < nfds; i++) { + if (ufds[i].fd == CURL_SOCKET_BAD) + continue; +#ifndef USE_WINSOCK /* winsock sockets are not in range [0..FD_SETSIZE] */ + if (ufds[i].fd >= FD_SETSIZE) { + errno = EINVAL; + return -1; + } +#endif + if (ufds[i].fd > maxfd) + maxfd = ufds[i].fd; + if (ufds[i].events & POLLIN) + FD_SET(ufds[i].fd, &fds_read); + if (ufds[i].events & POLLOUT) + FD_SET(ufds[i].fd, &fds_write); + if (ufds[i].events & POLLERR) + FD_SET(ufds[i].fd, &fds_err); + } + + if (timeout_ms < 0) { + ptimeout = NULL; /* wait forever */ + } else { + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + ptimeout = &timeout; + } + + do { + r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); + } while((r == -1) && (Curl_sockerrno() == EINTR)); + + 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 |= POLLERR; + if (ufds[i].revents != 0) + r++; + } +#endif + return r; +} + +#ifdef TPF +/* + * This is a replacement for select() on the TPF platform. + * It is used whenever libcurl calls select(). + * The call below to tpf_process_signals() is required because + * TPF's select calls are not signal interruptible. + * + * Return values are the same as select's. + */ +int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, + fd_set* excepts, struct timeval* tv) +{ + int rc; + + rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv); + tpf_process_signals(); + return(rc); +} +#endif /* TPF */ diff --git a/Utilities/cmcurl/select.h b/Utilities/cmcurl/select.h new file mode 100644 index 0000000..e0844b1 --- /dev/null +++ b/Utilities/cmcurl/select.h @@ -0,0 +1,63 @@ +#ifndef __SELECT_H +#define __SELECT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#ifdef HAVE_SYS_POLL_H +#include <sys/poll.h> +#elif defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) +/* for Vista, use WSAPoll(). */ +#include <winsock2.h> +#define CURL_HAVE_WSAPOLL +#else + +#define POLLIN 0x01 +#define POLLPRI 0x02 +#define POLLOUT 0x04 +#define POLLERR 0x08 +#define POLLHUP 0x10 +#define POLLNVAL 0x20 + +struct pollfd +{ + curl_socket_t fd; + short events; + short revents; +}; + +#endif + +#define CSELECT_IN 0x01 +#define CSELECT_OUT 0x02 +#define CSELECT_ERR 0x04 + +int Curl_select(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms); + +int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms); + +#ifdef TPF +int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, + fd_set* excepts, struct timeval* tv); +#endif + +#endif diff --git a/Utilities/cmcurl/sendf.c b/Utilities/cmcurl/sendf.c index fae2ff3..500bf66 100644 --- a/Utilities/cmcurl/sendf.c +++ b/Utilities/cmcurl/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -43,16 +43,25 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "connect.h" /* for the Curl_ourerrno() proto */ +#include "connect.h" /* for the Curl_sockerrno() proto */ +#include "sslgen.h" +#include "ssh.h" +#include "multiif.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include <curl/mprintf.h> #ifdef HAVE_KRB4 -#include "security.h" +#include "krb4.h" +#else +#define Curl_sec_send(a,b,c,d) -1 +#define Curl_sec_read(a,b,c,d) -1 #endif + #include <string.h> -#include "curl_memory.h" +#include "memory.h" +#include "strerror.h" +#include "easyif.h" /* for the Curl_convert_from_network prototype */ /* The last #include file should be: */ #include "memdebug.h" @@ -88,10 +97,10 @@ struct curl_slist *curl_slist_append(struct curl_slist *list, new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist)); if (new_item) { - char *cuDup = strdup(data); - if(cuDup) { + char *dup = strdup(data); + if(dup) { new_item->next = NULL; - new_item->data = cuDup; + new_item->data = dup; } else { free(new_item); @@ -132,17 +141,102 @@ void curl_slist_free_all(struct curl_slist *list) } while (next); } +#ifdef CURL_DO_LINEEND_CONV +/* + * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF + * (\n), with special processing for CRLF sequences that are split between two + * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new + * size of the data is returned. + */ +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 */ + memcpy(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); +} +#endif /* CURL_DO_LINEEND_CONV */ + /* Curl_infof() is for info message along the way */ void Curl_infof(struct SessionHandle *data, const char *fmt, ...) { if(data && data->set.verbose) { va_list ap; + size_t len; char print_buffer[1024 + 1]; va_start(ap, fmt); vsnprintf(print_buffer, 1024, fmt, ap); va_end(ap); - Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer), NULL); + len = strlen(print_buffer); + Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL); } } @@ -153,25 +247,24 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...) void Curl_failf(struct SessionHandle *data, const char *fmt, ...) { va_list ap; + size_t len; va_start(ap, fmt); + + vsnprintf(data->state.buffer, BUFSIZE, fmt, ap); + if(data->set.errorbuffer && !data->state.errorbuf) { - vsnprintf(data->set.errorbuffer, CURL_ERROR_SIZE, fmt, ap); + snprintf(data->set.errorbuffer, CURL_ERROR_SIZE, "%s", data->state.buffer); data->state.errorbuf = TRUE; /* wrote error string */ - - if(data->set.verbose) { - size_t len = strlen(data->set.errorbuffer); - bool doneit=FALSE; - if(len < CURL_ERROR_SIZE - 1) { - doneit = TRUE; - data->set.errorbuffer[len] = '\n'; - data->set.errorbuffer[++len] = '\0'; - } - Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len, NULL); - if(doneit) - /* cut off the newline again */ - data->set.errorbuffer[--len]=0; + } + if(data->set.verbose) { + len = strlen(data->state.buffer); + if(len < BUFSIZE - 1) { + data->state.buffer[len] = '\n'; + data->state.buffer[++len] = '\0'; } + Curl_debug(data, CURLINFO_TEXT, data->state.buffer, len, NULL); } + va_end(ap); } @@ -182,7 +275,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, struct SessionHandle *data = conn->data; ssize_t bytes_written; size_t write_len; - CURLcode res; + CURLcode res = CURLE_OK; char *s; char *sptr; va_list ap; @@ -204,8 +297,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, break; if(data->set.verbose) - Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, - conn->host.dispname); + Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written, conn); if((size_t)bytes_written != write_len) { /* if not all was written at once, we must advance the pointer, decrease @@ -222,9 +314,40 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, return res; } +static ssize_t Curl_plain_send(struct connectdata *conn, + int num, + void *mem, + size_t len) +{ + curl_socket_t sockfd = conn->sock[num]; + ssize_t bytes_written = swrite(sockfd, mem, len); + + if(-1 == bytes_written) { + int err = Curl_sockerrno(); + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == err) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefor + treat both error codes the same here */ + (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) +#endif + ) + /* this is just a case of EWOULDBLOCK */ + bytes_written=0; + else + failf(conn->data, "Send failure: %s", + Curl_strerror(conn, err)); + } + return bytes_written; +} + /* - * Curl_write() is an internal write function that sends plain (binary) data - * to the server. Works with plain sockets, SSL or kerberos. + * Curl_write() is an internal write function that sends data to the + * server. Works with plain sockets, SCP, SSL or kerberos. */ CURLcode Curl_write(struct connectdata *conn, curl_socket_t sockfd, @@ -234,84 +357,22 @@ CURLcode Curl_write(struct connectdata *conn, { ssize_t bytes_written; CURLcode retcode; - -#ifdef USE_SSLEAY - /* 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]); - /* SSL_write() is said to return 'int' while write() and send() returns - 'size_t' */ - if (conn->ssl[num].use) { - int err; - char error_buffer[120]; /* OpenSSL documents that this must be at least - 120 bytes long. */ - unsigned long sslerror; - int rc = SSL_write(conn->ssl[num].handle, mem, (int)len); - - if(rc < 0) { - err = SSL_get_error(conn->ssl[num].handle, rc); - - switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* The operation did not complete; the same TLS/SSL I/O function - should be called again later. This is basicly an EWOULDBLOCK - equivalent. */ - *written = 0; - return CURLE_OK; - case SSL_ERROR_SYSCALL: - failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n", - Curl_ourerrno()); - return CURLE_SEND_ERROR; - case SSL_ERROR_SSL: - /* A failure in the SSL library occurred, usually a protocol error. - The OpenSSL error queue contains more information on the error. */ - sslerror = ERR_get_error(); - failf(conn->data, "SSL_write() error: %s\n", - ERR_error_string(sslerror, error_buffer)); - return CURLE_SEND_ERROR; - } - /* a true error */ - failf(conn->data, "SSL_write() return error %d\n", err); - return CURLE_SEND_ERROR; - } - bytes_written = rc; - } - else { -#else - (void)conn; -#endif -#ifdef HAVE_KRB4 - if(conn->sec_complete) { - bytes_written = Curl_sec_write(conn, sockfd, mem, len); - } - else -#endif /* HAVE_KRB4 */ - { - bytes_written = (ssize_t)swrite(sockfd, mem, len); - } - if(-1 == bytes_written) { - int err = Curl_ourerrno(); - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#else - /* As pointed out by Christophe Demory on March 11 2003, errno - may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We - therefor treat both error codes the same here */ - (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) -#endif - ) - /* this is just a case of EWOULDBLOCK */ - bytes_written=0; - } -#ifdef USE_SSLEAY - } -#endif + if (conn->ssl[num].use) + /* only TRUE if SSL enabled */ + bytes_written = Curl_ssl_send(conn, num, mem, len); +#ifdef USE_LIBSSH2 + else if (conn->protocol & PROT_SCP) + bytes_written = Curl_scp_send(conn, num, mem, len); + else if (conn->protocol & PROT_SFTP) + bytes_written = Curl_sftp_send(conn, num, mem, len); +#endif /* !USE_LIBSSH2 */ + else if(conn->sec_complete) + /* only TRUE if krb4 enabled */ + bytes_written = Curl_sec_send(conn, num, mem, len); + else + bytes_written = Curl_plain_send(conn, num, mem, len); *written = bytes_written; retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR; @@ -324,23 +385,53 @@ CURLcode Curl_write(struct connectdata *conn, The bit pattern defines to what "streams" to write to. Body and/or header. The defines are in sendf.h of course. */ -CURLcode Curl_client_write(struct SessionHandle *data, +CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len) { + struct SessionHandle *data = conn->data; size_t wrote; + if (data->state.cancelled) { + /* We just suck everything into a black hole */ + return CURLE_OK; + } + if(0 == len) len = strlen(ptr); if(type & CLIENTWRITE_BODY) { - wrote = data->set.fwrite(ptr, 1, len, data->set.out); + 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(ptr, 1, len, data->set.out); + } + else { + wrote = len; + } + if(wrote != len) { failf (data, "Failed writing body"); return CURLE_WRITE_ERROR; } } + if((type & CLIENTWRITE_HEADER) && (data->set.fwrite_header || data->set.writeheader) ) { /* @@ -350,6 +441,9 @@ CURLcode Curl_client_write(struct SessionHandle *data, curl_write_callback writeit= data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite; + /* 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(wrote != len) { failf (data, "Failed writing header"); @@ -360,6 +454,10 @@ CURLcode Curl_client_write(struct SessionHandle *data, return CURLE_OK; } +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. @@ -370,11 +468,15 @@ CURLcode Curl_client_write(struct SessionHandle *data, int Curl_read(struct connectdata *conn, /* connection data */ curl_socket_t sockfd, /* read from this socket */ char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ + size_t sizerequested, /* max amount to read */ ssize_t *n) /* amount bytes read */ { ssize_t nread; -#ifdef USE_SSLEAY + 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. */ @@ -382,61 +484,75 @@ int Curl_read(struct connectdata *conn, /* connection data */ *n=0; /* reset amount to zero */ - if (conn->ssl[num].use) { - nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, (int)buffersize); - - if(nread < 0) { - /* failed SSL_read */ - int err = SSL_get_error(conn->ssl[num].handle, (int)nread); - - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - case SSL_ERROR_ZERO_RETURN: /* no more data */ - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - return -1; /* basicly EWOULDBLOCK */ - default: - /* openssl/ssl.h says "look at error stack/return value/errno" */ - { - char error_buffer[120]; /* OpenSSL documents that this must be at - least 120 bytes long. */ - unsigned long sslerror = ERR_get_error(); - failf(conn->data, "SSL read: %s, errno %d", - ERR_error_string(sslerror, error_buffer), - Curl_ourerrno() ); - } - return CURLE_RECV_ERROR; - } + /* 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, sizeof(conn->master_buffer)); + buffertofill = conn->master_buffer; } else { -#else - (void)conn; -#endif - *n=0; /* reset amount to zero */ -#ifdef HAVE_KRB4 + bytesfromsocket = MIN((long)sizerequested, conn->data->set.buffer_size ? + conn->data->set.buffer_size : BUFSIZE); + buffertofill = buf; + } + + if(conn->ssl[num].use) { + nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket); + + if(nread == -1) { + return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */ + } + } +#ifdef USE_LIBSSH2 + else if (conn->protocol & PROT_SCP) { + nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket); + /* TODO: return CURLE_OK also for nread <= 0 + read failures and timeouts ? */ + } + else if (conn->protocol & PROT_SFTP) { + nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket); + } +#endif /* !USE_LIBSSH2 */ + else { if(conn->sec_complete) - nread = Curl_sec_read(conn, sockfd, buf, buffersize); + nread = Curl_sec_read(conn, sockfd, buffertofill, + bytesfromsocket); else -#endif - nread = sread(sockfd, buf, buffersize); + nread = sread(sockfd, buffertofill, bytesfromsocket); if(-1 == nread) { - int err = Curl_ourerrno(); -#ifdef WIN32 + int err = Curl_sockerrno(); +#ifdef USE_WINSOCK if(WSAEWOULDBLOCK == err) #else if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)) #endif return -1; } + } + + if (nread >= 0) { + if(pipelining) { + memcpy(buf, conn->master_buffer, nread); + conn->buf_len = nread; + conn->read_pos = nread; + } -#ifdef USE_SSLEAY + *n += nread; } -#endif /* USE_SSLEAY */ - *n = nread; + return CURLE_OK; } @@ -447,6 +563,46 @@ static int showit(struct SessionHandle *data, curl_infotype type, static const char * const s_infotype[CURLINFO_END] = { "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; +#ifdef CURL_DOES_CONVERSIONS + char buf[BUFSIZE+1]; + size_t conv_size = 0; + + switch(type) { + case CURLINFO_HEADER_OUT: + /* assume output headers are ASCII */ + /* copy the data into my buffer so the original is unchanged */ + if (size > BUFSIZE) { + size = BUFSIZE; /* truncate if necessary */ + buf[BUFSIZE] = '\0'; + } + conv_size = size; + memcpy(buf, ptr, size); + /* Special processing is needed for this block if it + * contains both headers and data (separated by CRLFCRLF). + * We want to convert just the headers, leaving the data as-is. + */ + if(size > 4) { + size_t i; + for(i = 0; i < size-4; i++) { + if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) { + /* convert everthing through this CRLFCRLF but no further */ + conv_size = i + 4; + break; + } + } + } + + Curl_convert_from_network(data, buf, conv_size); + /* Curl_convert_from_network calls failf if unsuccessful */ + /* we might as well continue even if it fails... */ + ptr = buf; /* switch pointer to use my buffer instead */ + break; + default: + /* leave everything else as-is */ + break; + } +#endif /* CURL_DOES_CONVERSIONS */ + if(data->set.fdebug) return (*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); @@ -457,6 +613,12 @@ static int showit(struct SessionHandle *data, curl_infotype type, case CURLINFO_HEADER_IN: fwrite(s_infotype[type], 2, 1, data->set.err); fwrite(ptr, size, 1, data->set.err); +#ifdef CURL_DOES_CONVERSIONS + if(size != conv_size) { + /* we had untranslated data so we need an explicit newline */ + fwrite("\n", 1, 1, data->set.err); + } +#endif break; default: /* nada */ break; @@ -465,18 +627,22 @@ static int showit(struct SessionHandle *data, curl_infotype type, } int Curl_debug(struct SessionHandle *data, curl_infotype type, - char *ptr, size_t size, char *host) + char *ptr, size_t size, + struct connectdata *conn) { int rc; - if(data->set.printhost && host) { + if(data->set.printhost && conn && conn->host.dispname) { char buffer[160]; const char *t=NULL; + const char *w="Data"; switch (type) { case CURLINFO_HEADER_IN: + w = "Header"; case CURLINFO_DATA_IN: t = "from"; break; case CURLINFO_HEADER_OUT: + w = "Header"; case CURLINFO_DATA_OUT: t = "to"; break; @@ -485,7 +651,8 @@ int Curl_debug(struct SessionHandle *data, curl_infotype type, } if(t) { - snprintf(buffer, sizeof(buffer), "[Data %s %s]", t, host); + snprintf(buffer, sizeof(buffer), "[%s %s %s]", w, t, + conn->host.dispname); rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer)); if(rc) return rc; diff --git a/Utilities/cmcurl/sendf.h b/Utilities/cmcurl/sendf.h index bdd3a79..7877df8 100644 --- a/Utilities/cmcurl/sendf.h +++ b/Utilities/cmcurl/sendf.h @@ -1,18 +1,18 @@ #ifndef __SENDF_H #define __SENDF_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -28,16 +28,31 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *, void Curl_infof(struct SessionHandle *, const char *fmt, ...); void Curl_failf(struct SessionHandle *, const char *fmt, ...); +#if defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(__GNUC__) +/* This style of variable argument macros is a gcc extension */ +#define infof(x...) /*ignore*/ +#else +/* C99 compilers could use this if we could detect them */ +/*#define infof(...) */ +/* Cast the args to void to make them a noop, side effects notwithstanding */ +#define infof (void) +#endif +#else #define infof Curl_infof +#endif #define failf Curl_failf #define CLIENTWRITE_BODY 1 #define CLIENTWRITE_HEADER 2 #define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) -CURLcode Curl_client_write(struct SessionHandle *data, int type, char *ptr, +CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len); +void Curl_read_rewind(struct connectdata *conn, + size_t extraBytesRead); + /* internal read-function, does plain socket, SSL and krb4 */ int Curl_read(struct connectdata *conn, curl_socket_t sockfd, char *buf, size_t buffersize, @@ -50,7 +65,8 @@ CURLcode Curl_write(struct connectdata *conn, /* the function used to output verbose information */ int Curl_debug(struct SessionHandle *handle, curl_infotype type, - char *data, size_t size, char *host); + char *data, size_t size, + struct connectdata *conn); #endif diff --git a/Utilities/cmcurl/setup.h b/Utilities/cmcurl/setup.h index fff6974..851c5a9 100644 --- a/Utilities/cmcurl/setup.h +++ b/Utilities/cmcurl/setup.h @@ -1,5 +1,5 @@ -#ifndef __SETUP_H -#define __SETUP_H +#ifndef __LIB_CURL_SETUP_H +#define __LIB_CURL_SETUP_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,37 +24,104 @@ ***************************************************************************/ #ifdef HTTP_ONLY +#define CURL_DISABLE_TFTP #define CURL_DISABLE_FTP #define CURL_DISABLE_LDAP #define CURL_DISABLE_TELNET #define CURL_DISABLE_DICT #define CURL_DISABLE_FILE -#define CURL_DISABLE_GOPHER -#endif +#endif /* HTTP_ONLY */ #if !defined(WIN32) && defined(__WIN32__) -/* This should be a good Borland fix. Alexander J. Oss told us! */ +/* Borland fix */ +#define WIN32 +#endif + +#if !defined(WIN32) && defined(_WIN32) +/* VS2005 on x64 fix */ #define WIN32 #endif +/* + * Include configuration script results or hand-crafted + * configuration file for platforms which lack config tool. + */ + #ifdef HAVE_CONFIG_H -#include "config.h" /* the configure script results */ +#include "config.h" +#else + +/* +#ifdef _WIN32_WCE +#include "config-win32ce.h" #else #ifdef WIN32 -/* hand-modified win32 config.h! */ #include "config-win32.h" #endif #endif +*/ #ifdef macintosh -/* hand-modified MacOS config.h! */ #include "config-mac.h" #endif + #ifdef AMIGA -/* hand-modified AmigaOS config.h! */ #include "amigaos.h" #endif +#ifdef TPF +#include "config-tpf.h" /* hand-modified TPF config.h */ +/* change which select is used for libcurl */ +#define select(a,b,c,d,e) tpf_select_libcurl(a,b,c,d,e) +#endif + +#endif /* HAVE_CONFIG_H */ + +/* + * Include header files for windows builds before redefining anything. + * Use this preproessor block only to include or exclude windows.h, + * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs + * to any other further and independant block. Under Cygwin things work + * just as under linux (e.g. <sys/socket.h>) and the winsock headers should + * never be included when __CYGWIN__ is defined. configure script takes + * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H, + * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined. + */ + +#ifdef HAVE_WINDOWS_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include <windows.h> +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# ifdef HAVE_WS2TCPIP_H +# include <ws2tcpip.h> +# endif +# else +# ifdef HAVE_WINSOCK_H +# include <winsock.h> +# endif +# endif +#endif + +/* + * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else + * define USE_WINSOCK to 1 if we have and use WINSOCK API, else + * undefine USE_WINSOCK. + */ + +#undef USE_WINSOCK + +#ifdef HAVE_WINSOCK2_H +# define USE_WINSOCK 2 +#else +# ifdef HAVE_WINSOCK_H +# define USE_WINSOCK 1 +# endif +#endif + + #ifndef TRUE #define TRUE 1 #endif @@ -62,7 +129,7 @@ #define FALSE 0 #endif -#if !defined(__cplusplus) && !defined(__BEOS__) +#if !defined(__cplusplus) && !defined(__BEOS__) && !defined(__ECOS) && !defined(typedef_bool) typedef unsigned char bool; #define typedef_bool #endif @@ -74,7 +141,7 @@ typedef unsigned char bool; #ifdef _MSC_VER #define LONG_LONG __int64 #define ENABLE_64BIT -#endif +#endif /* _MSC_VER */ #endif /* HAVE_LONGLONG */ #ifndef SIZEOF_CURL_OFF_T @@ -89,12 +156,13 @@ typedef unsigned char bool; #define FORMAT_OFF_T "lld" #else #define FORMAT_OFF_T "ld" -#endif +#endif /* SIZEOF_CURL_OFF_T */ -#ifdef NEED_REENTRANT -/* Solaris machines needs _REENTRANT set for a few function prototypes and - things to appear in the #include files. We need to #define it before all - #include files */ +#ifndef _REENTRANT +/* Solaris needs _REENTRANT set for a few function prototypes and things to + appear in the #include files. We need to #define it before all #include + files. Unixware needs it to build proper reentrant code. Others may also + need it. */ #define _REENTRANT #endif @@ -108,25 +176,34 @@ typedef unsigned char bool; #include <floss.h> #endif -#if defined(HAVE_X509_H) && defined(HAVE_SSL_H) && defined(HAVE_RSA_H) && \ -defined(HAVE_PEM_H) && defined(HAVE_ERR_H) && defined(HAVE_CRYPTO_H) && \ -defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) - /* the six important includes files all exist and so do both libs, - defined SSLeay usage */ -#define USE_SSLEAY 1 +#ifndef STDC_HEADERS /* no standard C headers! */ +#include <curl/stdcheaders.h> #endif -#if defined(HAVE_OPENSSL_X509_H) && defined(HAVE_OPENSSL_SSL_H) && \ -defined(HAVE_OPENSSL_RSA_H) && defined(HAVE_OPENSSL_PEM_H) && \ -defined(HAVE_OPENSSL_ERR_H) && defined(HAVE_OPENSSL_CRYPTO_H) && \ -defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) - /* the six important includes files all exist and so do both libs, - defined SSLeay usage */ -#define USE_SSLEAY 1 -#define USE_OPENSSL 1 + +/* + * PellesC cludge section (yikes); + * - It has 'ssize_t', but it is in <unistd.h>. The way the headers + * on Win32 are included, forces me to include this header here. + * - sys_nerr, EINTR is missing in v4.0 or older. + */ +#ifdef __POCC__ + #include <sys/types.h> + #include <unistd.h> + #if (__POCC__ <= 400) + #define sys_nerr EILSEQ /* for strerror.c */ + #define EINTR -1 /* for select.c */ + #endif #endif -#ifndef STDC_HEADERS /* no standard C headers! */ -#include <curl/stdcheaders.h> +/* + * Salford-C cludge section (mostly borrowed from wxWidgets). + */ +#ifdef __SALFORDC__ + #pragma suppress 353 /* Possible nested comments */ + #pragma suppress 593 /* Define not used */ + #pragma suppress 61 /* enum has no name */ + #pragma suppress 106 /* unnamed, unused parameter */ + #include <clib.h> #endif #if defined(CURLDEBUG) && defined(HAVE_ASSERT_H) @@ -136,20 +213,22 @@ defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) #define curlassert(x) #endif -#ifdef MSG_NOSIGNAL -/* If we have the MSG_NOSIGNAL define, we make sure to use that in the forth - argument to send() and recv() */ -#define SEND_4TH_ARG MSG_NOSIGNAL -#define HAVE_MSG_NOSIGNAL 1 /* we have MSG_NOSIGNAL */ + +/* To make large file support transparent even on Windows */ +#if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4) +#include <sys/stat.h> /* must come first before we redefine stat() */ +#include <io.h> +#define lseek(x,y,z) _lseeki64(x, y, z) +#define struct_stat struct _stati64 +#define stat(file,st) _stati64(file,st) +#define fstat(fd,st) _fstati64(fd,st) #else -#define SEND_4TH_ARG 0 -#endif +#define struct_stat struct stat +#endif /* Win32 with large file support */ -/* Below we define four functions. They should +/* Below we define some functions. They should 1. close a socket - 2. read from a socket - 3. write to a socket 4. set the SIGALRM signal timeout 5. set dir/file naming defines @@ -157,49 +236,24 @@ defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) #ifdef WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN /* Prevent including <winsock*.h> in <windows.h> */ -#endif - -#if (defined(ENABLE_IPV6) || defined(CURLDEBUG)) && defined(_MSC_VER) && \ - (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0500) -/* - * Needed to pull in the real getaddrinfo() and not the inline version - * in <wspiAPI.H> which doesn't support IPv6 (IPv4 only). <wspiAPI.H> is - * included from <ws2tcpip.h> for <= 0x0500 SDKs. - */ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif - -#include <winsock2.h> /* required by telnet.c */ - -#if defined(ENABLE_IPV6) || defined(USE_SSLEAY) -#include <ws2tcpip.h> -#endif - -#if !defined(__GNUC__) || defined(__MINGW32__) +#if !defined(__CYGWIN__) #define sclose(x) closesocket(x) -#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) -#define swrite(x,y,z) (size_t)send(x,y,z, SEND_4TH_ARG) + #undef HAVE_ALARM #else /* gcc-for-win is still good :) */ #define sclose(x) close(x) -#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) -#define swrite(x,y,z) send(x,y,z, SEND_4TH_ARG) #define HAVE_ALARM -#endif +#endif /* !GNU or mingw */ #define DIR_CHAR "\\" #define DOT_CHAR "_" -#else +#else /* WIN32 */ -#ifdef DJGPP +#ifdef MSDOS /* Watt-32 */ +#include <sys/ioctl.h> #define sclose(x) close_s(x) -#define sread(x,y,z) read_s(x,y,z) -#define swrite(x,y,z) write_s(x,y,z) #define select(n,r,w,x,t) select_s(n,r,w,x,t) #define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z)) #define IOCTL_3_ARGS @@ -208,21 +262,17 @@ defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) #undef word #endif -#else +#else /* MSDOS */ #ifdef __BEOS__ #define sclose(x) closesocket(x) -#define sread(x,y,z) (ssize_t)recv(x,y,z, SEND_4TH_ARG) -#define swrite(x,y,z) (ssize_t)send(x,y,z, SEND_4TH_ARG) -#else +#else /* __BEOS__ */ #define sclose(x) close(x) -#define sread(x,y,z) recv(x,y,z, SEND_4TH_ARG) -#define swrite(x,y,z) send(x,y,z, SEND_4TH_ARG) -#endif +#endif /* __BEOS__ */ #define HAVE_ALARM -#endif +#endif /* MSDOS */ #ifdef _AMIGASF #undef HAVE_ALARM @@ -231,9 +281,11 @@ defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) #endif #define DIR_CHAR "/" +#ifndef DOT_CHAR #define DOT_CHAR "." +#endif -#ifdef DJGPP +#ifdef MSDOS #undef DOT_CHAR #define DOT_CHAR "_" #endif @@ -242,20 +294,7 @@ defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO) int fileno( FILE *stream); #endif -#endif - -/* now typedef our socket type */ -#ifdef WIN32 -typedef SOCKET curl_socket_t; -#define CURL_SOCKET_BAD INVALID_SOCKET -#else -typedef int curl_socket_t; -#define CURL_SOCKET_BAD -1 -#endif - -#if defined(ENABLE_IPV6) && defined(USE_ARES) -#error "ares does not yet support IPv6. Disable IPv6 or ares and rebuild" -#endif +#endif /* WIN32 */ #if defined(WIN32) && !defined(__CYGWIN__) && !defined(USE_ARES) && \ !defined(__LCC__) /* lcc-win32 doesn't have _beginthreadex() */ @@ -266,29 +305,78 @@ typedef int curl_socket_t; #endif #endif -#ifdef mpeix -#define IOCTL_3_ARGS +/* "cl -ML" or "cl -MLd" implies a single-threaded runtime library where + _beginthreadex() is not available */ +#if (defined(_MSC_VER) && !defined(__POCC__)) && !defined(_MT) && !defined(USE_ARES) +#undef USE_THREADING_GETADDRINFO +#undef USE_THREADING_GETHOSTBYNAME +#define CURL_NO__BEGINTHREADEX #endif -#ifndef ECONNRESET -#ifdef WSAECONNRESET -#define ECONNRESET WSAECONNRESET -#else -/* This will effectively prevent the code from working in this particular - aspect, but it still compile fine! */ -#define ECONNRESET 10000 +/* + * msvc 6.0 does not have struct sockaddr_storage and + * does not define IPPROTO_ESP in winsock2.h. But both + * are available if PSDK is properly installed. + */ + +#ifdef _MSC_VER +#if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP)) +#undef HAVE_STRUCT_SOCKADDR_STORAGE #endif #endif +#ifdef mpeix +#define IOCTL_3_ARGS +#endif + #ifdef NETWARE #undef HAVE_ALARM #endif -#ifdef HAVE_LIBIDN -/* This could benefit from additional checks that some of the used/important - header files are present as well before we define the USE_* define. */ +#if defined(HAVE_LIBIDN) && defined(HAVE_TLD_H) +/* The lib was present and the tld.h header (which is missing in libidn 0.3.X + but we only work with libidn 0.4.1 or later) */ #define USE_LIBIDN +#endif + +#ifndef SIZEOF_TIME_T +/* assume default size of time_t to be 32 bit */ +#define SIZEOF_TIME_T 4 +#endif + #define LIBIDN_REQUIRED_VERSION "0.4.1" + +#ifdef __UCLIBC__ +#define HAVE_INET_NTOA_R_2_ARGS 1 +#endif + +#if defined(USE_GNUTLS) || defined(USE_SSLEAY) +#define USE_SSL /* Either OpenSSL || GnuTLS */ +#endif + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) +#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#define USE_NTLM +#endif +#endif + +#ifdef CURLDEBUG +#define DEBUGF(x) x +#else +#define DEBUGF(x) +#endif + +/* non-configure builds may define CURL_WANTS_CA_BUNDLE_ENV */ +#if defined(CURL_WANTS_CA_BUNDLE_ENV) && !defined(CURL_CA_BUNDLE) +#define CURL_CA_BUNDLE getenv("CURL_CA_BUNDLE") +#endif + +/* + * Include macros and defines that should only be processed once. + */ + +#ifndef __SETUP_ONCE_H +#include "setup_once.h" #endif -#endif /* __CONFIG_H */ +#endif /* __LIB_CURL_SETUP_H */ diff --git a/Utilities/cmcurl/setup_once.h b/Utilities/cmcurl/setup_once.h new file mode 100644 index 0000000..ee68641 --- /dev/null +++ b/Utilities/cmcurl/setup_once.h @@ -0,0 +1,153 @@ +#ifndef __SETUP_ONCE_H +#define __SETUP_ONCE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + + +/******************************************************************** + * NOTICE * + * ======== * + * * + * Content of header files lib/setup_once.h and ares/setup_once.h * + * must be kept in sync. Modify the other one if you change this. * + * * + ********************************************************************/ + + +/* + * If we have the MSG_NOSIGNAL define, make sure we use + * it as the fourth argument of function send() + */ + +#ifdef HAVE_MSG_NOSIGNAL +#define SEND_4TH_ARG MSG_NOSIGNAL +#else +#define SEND_4TH_ARG 0 +#endif + + +/* + * The definitions for the return type and arguments types + * of functions recv() and send() belong and come from the + * configuration file. Do not define them in any other place. + * + * HAVE_RECV is defined if you have a function named recv() + * which is used to read incoming data from sockets. If your + * function has another name then don't define HAVE_RECV. + * + * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, + * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also + * be defined. + * + * HAVE_SEND is defined if you have a function named send() + * which is used to write outgoing data on a connected socket. + * If yours has another name then don't define HAVE_SEND. + * + * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2, + * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and + * SEND_TYPE_RETV must also be defined. + */ + +#ifdef HAVE_RECV +#if !defined(RECV_TYPE_ARG1) || \ + !defined(RECV_TYPE_ARG2) || \ + !defined(RECV_TYPE_ARG3) || \ + !defined(RECV_TYPE_ARG4) || \ + !defined(RECV_TYPE_RETV) + /* */ + Error Missing_definition_of_return_and_arguments_types_of_recv + /* */ +#else +#define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z), \ + (RECV_TYPE_ARG4)(0)) +#endif +#else /* HAVE_RECV */ +#ifndef sread + /* */ + Error Missing_definition_of_macro_sread + /* */ +#endif +#endif /* HAVE_RECV */ + +#ifdef HAVE_SEND +#if !defined(SEND_TYPE_ARG1) || \ + !defined(SEND_QUAL_ARG2) || \ + !defined(SEND_TYPE_ARG2) || \ + !defined(SEND_TYPE_ARG3) || \ + !defined(SEND_TYPE_ARG4) || \ + !defined(SEND_TYPE_RETV) + /* */ + Error Missing_definition_of_return_and_arguments_types_of_send + /* */ +#else +#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#endif +#else /* HAVE_SEND */ +#ifndef swrite + /* */ + Error Missing_definition_of_macro_swrite + /* */ +#endif +#endif /* HAVE_SEND */ + + +/* + * Uppercase macro versions of ANSI/ISO is*() functions/macros which + * avoid negative number inputs with argument byte codes > 127. + */ + +#define ISSPACE(x) (isspace((int) ((unsigned char)x))) +#define ISDIGIT(x) (isdigit((int) ((unsigned char)x))) +#define ISALNUM(x) (isalnum((int) ((unsigned char)x))) +#define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x))) +#define ISGRAPH(x) (isgraph((int) ((unsigned char)x))) +#define ISALPHA(x) (isalpha((int) ((unsigned char)x))) +#define ISPRINT(x) (isprint((int) ((unsigned char)x))) + + +/* + * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type. + */ + +#ifndef HAVE_SIG_ATOMIC_T +typedef int sig_atomic_t; +#define HAVE_SIG_ATOMIC_T +#endif + + +/* + * Default return type for signal handlers. + */ + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + + +#endif /* __SETUP_ONCE_H */ + diff --git a/Utilities/cmcurl/share.c b/Utilities/cmcurl/share.c index 1022d97..de13b60 100644 --- a/Utilities/cmcurl/share.c +++ b/Utilities/cmcurl/share.c @@ -28,7 +28,7 @@ #include <curl/curl.h> #include "urldata.h" #include "share.h" -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -77,7 +77,7 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) } break; -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) case CURL_LOCK_DATA_COOKIE: if (!share->cookies) { share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE ); @@ -108,7 +108,7 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) } break; -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) case CURL_LOCK_DATA_COOKIE: if (share->cookies) { Curl_cookie_cleanup(share->cookies); @@ -171,7 +171,7 @@ curl_share_cleanup(CURLSH *sh) if(share->hostcache) Curl_hash_destroy(share->hostcache); -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(share->cookies) Curl_cookie_cleanup(share->cookies); #endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/share.h b/Utilities/cmcurl/share.h index 5c85c80..5cfe8c7 100644 --- a/Utilities/cmcurl/share.h +++ b/Utilities/cmcurl/share.h @@ -2,18 +2,18 @@ #define __CURL_SHARE_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -28,28 +28,29 @@ #include <curl/curl.h> #include "cookie.h" +/* SalfordC says "A structure member may not be volatile". Hence: + */ +#ifdef __SALFORDC__ +#define CURL_VOLATILE +#else +#define CURL_VOLATILE volatile +#endif + /* this struct is libcurl-private, don't export details */ struct Curl_share { unsigned int specifier; - volatile unsigned int dirty; - + CURL_VOLATILE unsigned int dirty; + curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; - curl_hash *hostcache; + struct curl_hash *hostcache; struct CookieInfo *cookies; }; -CURLSHcode Curl_share_lock ( - struct SessionHandle *, - curl_lock_data, - curl_lock_access - ); - -CURLSHcode Curl_share_unlock ( - struct SessionHandle *, - curl_lock_data - ); +CURLSHcode Curl_share_lock (struct SessionHandle *, curl_lock_data, + curl_lock_access); +CURLSHcode Curl_share_unlock (struct SessionHandle *, curl_lock_data); #endif /* __CURL_SHARE_H */ diff --git a/Utilities/cmcurl/sockaddr.h b/Utilities/cmcurl/sockaddr.h new file mode 100644 index 0000000..78dad4d --- /dev/null +++ b/Utilities/cmcurl/sockaddr.h @@ -0,0 +1,38 @@ +#ifndef __SOCKADDR_H +#define __SOCKADDR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE +struct Curl_sockaddr_storage { + struct sockaddr_storage buffer; +}; +#else +struct Curl_sockaddr_storage { + char buffer[256]; /* this should be big enough to fit a lot */ +}; +#endif + +#endif /* __SOCKADDR_H */ diff --git a/Utilities/cmcurl/socks.c b/Utilities/cmcurl/socks.c new file mode 100644 index 0000000..3319e69 --- /dev/null +++ b/Utilities/cmcurl/socks.c @@ -0,0 +1,585 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" + +#include <string.h> + +#ifdef NEED_MALLOC_H +#include <malloc.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include "urldata.h" +#include "sendf.h" +#include "strequal.h" +#include "select.h" +#include "connect.h" +#include "timeval.h" +#include "socks.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * Helper read-from-socket functions. Does the same as Curl_read() but it + * blocks until all bytes amount of buffersize will be read. No more, no less. + * + * This is STUPID BLOCKING behaviour which we frown upon, but right now this + * is what we have... + */ +static int blockread_all(struct connectdata *conn, /* connection data */ + curl_socket_t sockfd, /* read from this socket */ + char *buf, /* store read data here */ + ssize_t buffersize, /* max amount to read */ + ssize_t *n, /* amount bytes read */ + long conn_timeout) /* timeout for data wait + relative to + conn->created */ +{ + ssize_t nread; + ssize_t allread = 0; + int result; + struct timeval tvnow; + long conntime; + *n = 0; + do { + tvnow = Curl_tvnow(); + /* calculating how long connection is establishing */ + conntime = Curl_tvdiff(tvnow, conn->created); + if(conntime > conn_timeout) { + /* we already got the timeout */ + result = ~CURLE_OK; + break; + } + if(Curl_select(sockfd, CURL_SOCKET_BAD, + (int)(conn_timeout - conntime)) <= 0) { + result = ~CURLE_OK; + break; + } + result = Curl_read(conn, sockfd, buf, buffersize, &nread); + if(result) + break; + + if(buffersize == nread) { + allread += nread; + *n = allread; + result = CURLE_OK; + break; + } + buffersize -= nread; + buf += nread; + allread += nread; + } while(1); + return result; +} + +/* +* This function logs in to a SOCKS4 proxy and sends the specifics to the final +* destination server. +* +* Reference : +* http://socks.permeo.com/protocol/socks4.protocol +* +* Note : +* Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" +* Nonsupport "Identification Protocol (RFC1413)" +*/ +CURLcode Curl_SOCKS4(const char *proxy_name, + struct connectdata *conn) +{ + unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */ + int result; + CURLcode code; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + long timeout; + struct SessionHandle *data = conn->data; + + /* get timeout */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + timeout = data->set.timeout*1000; + else + timeout = data->set.connecttimeout*1000; + } + else if(data->set.timeout) + timeout = data->set.timeout*1000; + else if(data->set.connecttimeout) + timeout = data->set.connecttimeout*1000; + else + timeout = DEFAULT_CONNECT_TIMEOUT; + + 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(conn->remote_port); + + /* DNS resolve */ + { + struct Curl_dns_entry *dns; + Curl_addrinfo *hp=NULL; + int rc; + + rc = Curl_resolv(conn, conn->host.name, (int)conn->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.", + conn->host.name); + 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; + int packetsize = 9 + + (int)strlen((char*)socksreq + 8); /* size including NUL */ + + /* Send request */ + code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); + if ((code != CURLE_OK) || (written != packetsize)) { + 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: + 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! */ +} + +/* + * This function logs in to a SOCKS5 proxy and sends the specifics to the final + * destination server. + */ +CURLcode Curl_SOCKS5(const char *proxy_name, + const char *proxy_password, + 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[FIRSTSOCKET]; + struct SessionHandle *data = conn->data; + long timeout; + + /* get timeout */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + timeout = data->set.timeout*1000; + else + timeout = data->set.connecttimeout*1000; + } + else if(data->set.timeout) + timeout = data->set.timeout*1000; + else if(data->set.connecttimeout) + timeout = data->set.connecttimeout*1000; + else + timeout = DEFAULT_CONNECT_TIMEOUT; + + Curl_nonblock(sock, TRUE); + + /* wait until socket gets connected */ + result = Curl_select(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 & 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(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_select(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 & 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 = proxy_password?strlen(proxy_password):0; + } + 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(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 */ + socksreq[3] = 1; /* IPv4 = 1 */ + + { + struct Curl_dns_entry *dns; + Curl_addrinfo *hp=NULL; + int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); + + 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.", + conn->host.name); + return CURLE_COULDNT_RESOLVE_HOST; + } + } + + *((unsigned short*)&socksreq[8]) = htons(conn->remote_port); + + { + const int packetsize = 10; + + code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); + if ((code != CURLE_OK) || (written != packetsize)) { + failf(data, "Failed to send SOCKS5 connect request."); + return CURLE_COULDNT_CONNECT; + } + + 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; + } + } + + Curl_nonblock(sock, TRUE); + return CURLE_OK; /* Proxy was successful! */ +} diff --git a/Utilities/cmcurl/socks.h b/Utilities/cmcurl/socks.h new file mode 100644 index 0000000..0da9879 --- /dev/null +++ b/Utilities/cmcurl/socks.h @@ -0,0 +1,41 @@ +#ifndef __SOCKS_H +#define __SOCKS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* + * This function logs in to a SOCKS4 proxy and sends the specifics to the + * final destination server. + */ +CURLcode Curl_SOCKS4(const char *proxy_name, + struct connectdata *conn); + +/* + * This function logs in to a SOCKS5 proxy and sends the specifics to the + * final destination server. + */ +CURLcode Curl_SOCKS5(const char *proxy_name, + const char *proxy_password, + struct connectdata *conn); + +#endif diff --git a/Utilities/cmcurl/speedcheck.c b/Utilities/cmcurl/speedcheck.c index 33a8e5d..adda8a9 100644 --- a/Utilities/cmcurl/speedcheck.c +++ b/Utilities/cmcurl/speedcheck.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -29,6 +29,7 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" +#include "multiif.h" #include "speedcheck.h" void Curl_speedinit(struct SessionHandle *data) @@ -43,13 +44,13 @@ CURLcode Curl_speedcheck(struct SessionHandle *data, data->set.low_speed_time && (Curl_tvlong(data->state.keeps_speed) != 0) && (data->progress.current_speed < data->set.low_speed_limit)) { + long howlong = Curl_tvdiff(now, data->state.keeps_speed); /* We are now below the "low speed limit". If we are below it for "low speed time" seconds we consider that enough reason to abort the download. */ - - if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) > - data->set.low_speed_time) { + + if( (howlong/1000) > data->set.low_speed_time) { /* we have been this slow for long enough, now die */ failf(data, "Operation too slow. " @@ -58,10 +59,17 @@ CURLcode Curl_speedcheck(struct SessionHandle *data, data->set.low_speed_time); return CURLE_OPERATION_TIMEOUTED; } + Curl_expire(data, howlong); } else { /* we keep up the required speed all right */ data->state.keeps_speed = now; + + if(data->set.low_speed_limit) + /* if there is a low speed limit enabled, we set the expire timer to + make this connection's speed get checked again no later than when + this time is up */ + Curl_expire(data, data->set.low_speed_time*1000); } return CURLE_OK; } diff --git a/Utilities/cmcurl/splay.c b/Utilities/cmcurl/splay.c new file mode 100644 index 0000000..9fb66c7 --- /dev/null +++ b/Utilities/cmcurl/splay.c @@ -0,0 +1,425 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1997 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> + +#include "splay.h" + +#define compare(i,j) ((i)-(j)) + +/* Set this to a key value that will *NEVER* appear otherwise */ +#define KEY_NOTUSED -1 + +/* + * Splay using the key i (which may or may not be in the tree.) The starting + * root is t. + */ +struct Curl_tree *Curl_splay(int i, struct Curl_tree *t) +{ + struct Curl_tree N, *l, *r, *y; + int 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; +} + +/* Insert key i into the tree t. Return a pointer to the resulting tree or + NULL if something went wrong. */ +struct Curl_tree *Curl_splayinsert(int i, + struct Curl_tree *t, + struct Curl_tree *node) +{ + if (node == NULL) + return t; + + if (t != NULL) { + t = Curl_splay(i,t); + if (compare(i, t->key)==0) { + /* There already exists a node in the tree with the very same key. Build + a linked list of nodes. We make the new 'node' struct the new master + node and make the previous node the first one in the 'same' list. */ + + node->same = t; + node->key = i; + node->smaller = t->smaller; + node->larger = t->larger; + + t->smaller = node; /* in the sub node for this same key, we use the + smaller pointer to point back to the master + node */ + + t->key = KEY_NOTUSED; /* and we set the key in the sub node to NOTUSED + to quickly identify this node as a subnode */ + + return node; /* new root node */ + } + } + + if (t == NULL) { + node->smaller = node->larger = NULL; + } + else if (compare(i, t->key) < 0) { + node->smaller = t->smaller; + node->larger = t; + t->smaller = NULL; + + } + else { + node->larger = t->larger; + node->smaller = t; + t->larger = NULL; + } + node->key = i; + + node->same = NULL; /* no identical node (yet) */ + return node; +} + +#if 0 +/* Deletes 'i' from the tree if it's there (with an exact match). Returns a + pointer to the resulting tree. + + Function not used in libcurl. +*/ +struct Curl_tree *Curl_splayremove(int i, struct Curl_tree *t, + struct Curl_tree **removed) +{ + struct Curl_tree *x; + + *removed = NULL; /* default to no removed */ + + if (t==NULL) + return NULL; + + t = Curl_splay(i,t); + if (compare(i, t->key) == 0) { /* found it */ + + /* FIRST! Check if there is a list with identical sizes */ + if((x = t->same)) { + /* there is, pick one from the list */ + + /* 'x' is the new root node */ + + x->key = t->key; + x->larger = t->larger; + x->smaller = t->smaller; + + *removed = t; + return x; /* new root */ + } + + if (t->smaller == NULL) { + x = t->larger; + } + else { + x = Curl_splay(i, t->smaller); + x->larger = t->larger; + } + *removed = t; + + return x; + } + else + return t; /* It wasn't there */ +} +#endif + +/* Finds and deletes the best-fit node from the tree. Return a pointer to the + resulting tree. best-fit means the node with the given or lower number */ +struct Curl_tree *Curl_splaygetbest(int i, struct Curl_tree *t, + struct Curl_tree **removed) +{ + struct Curl_tree *x; + + if (!t) { + *removed = NULL; /* none removed since there was no root */ + return NULL; + } + + t = Curl_splay(i,t); + if(compare(i, t->key) < 0) { + /* too big node, try the smaller chain */ + if(t->smaller) + t=Curl_splay(t->smaller->key, t); + else { + /* fail */ + *removed = NULL; + return t; + } + } + + if (compare(i, t->key) >= 0) { /* found it */ + /* FIRST! Check if there is a list with identical sizes */ + x = t->same; + if(x) { + /* there is, pick one from the list */ + + /* 'x' is the new root node */ + + x->key = t->key; + x->larger = t->larger; + x->smaller = t->smaller; + + *removed = t; + return x; /* new root */ + } + + if (t->smaller == NULL) { + x = t->larger; + } + else { + x = Curl_splay(i, t->smaller); + x->larger = t->larger; + } + *removed = t; + + return x; + } + else { + *removed = NULL; /* no match */ + return t; /* It wasn't there */ + } +} + + +/* Deletes the very node we point out from the tree if it's there. Stores a + pointer to the new resulting tree in 'newroot'. + + Returns zero on success and non-zero on errors! TODO: document error codes. + When returning error, it does not touch the 'newroot' pointer. + + NOTE: when the last node of the tree is removed, there's no tree left so + 'newroot' will be made to point to NULL. +*/ +int Curl_splayremovebyaddr(struct Curl_tree *t, + struct Curl_tree *remove, + struct Curl_tree **newroot) +{ + struct Curl_tree *x; + + if (!t || !remove) + return 1; + + if(KEY_NOTUSED == remove->key) { + /* Key set to NOTUSED means it is a subnode within a 'same' linked list + and thus we can unlink it easily. The 'smaller' link of a subnode + links to the parent node. */ + if (remove->smaller == NULL) + return 3; + + remove->smaller->same = remove->same; + if(remove->same) + remove->same->smaller = remove->smaller; + + /* Ensures that double-remove gets caught. */ + remove->smaller = NULL; + + /* voila, we're done! */ + *newroot = t; /* return the same root */ + return 0; + } + + t = Curl_splay(remove->key, t); + + /* First make sure that we got the same root node as the one we want + to remove, as otherwise we might be trying to remove a node that + isn't actually in the tree. + + We cannot just compare the keys here as a double remove in quick + succession of a node with key != KEY_NOTUSED && same != NULL + could return the same key but a different node. */ + if(t != remove) + return 2; + + /* Check if there is a list with identical sizes, as then we're trying to + remove the root node of a list of nodes with identical keys. */ + x = t->same; + if(x) { + /* 'x' is the new root node, we just make it use the root node's + smaller/larger links */ + + x->key = t->key; + x->larger = t->larger; + x->smaller = t->smaller; + } + else { + /* Remove the root node */ + if (t->smaller == NULL) + x = t->larger; + else { + x = Curl_splay(remove->key, t->smaller); + x->larger = t->larger; + } + } + + *newroot = x; /* store new root pointer */ + + return 0; +} + +#ifdef CURLDEBUG + +void Curl_splayprint(struct Curl_tree * t, int d, char output) +{ + struct Curl_tree *node; + int i; + int count; + if (t == NULL) + return; + + Curl_splayprint(t->larger, d+1, output); + for (i=0; i<d; i++) + if(output) + printf(" "); + + if(output) { + printf("%d[%d]", t->key, i); + } + + for(count=0, node = t->same; node; node = node->same, count++) + ; + + if(output) { + if(count) + printf(" [%d more]\n", count); + else + printf("\n"); + } + + Curl_splayprint(t->smaller, d+1, output); +} +#endif + +#ifdef TEST_SPLAY + +/*#define TEST2 */ +#define MAX 50 +#define TEST2 + +/* A sample use of these functions. Start with the empty tree, insert some + stuff into it, and then delete it */ +int main(int argc, char **argv) +{ + struct Curl_tree *root, *t; + void *ptrs[MAX]; + int adds=0; + int rc; + + long sizes[]={ + 50, 60, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, 300, + 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, + 300, 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, + 200, 300, 220, 80, 90}; + int i; + root = NULL; /* the empty tree */ + + for (i = 0; i < MAX; i++) { + int key; + ptrs[i] = t = (struct Curl_tree *)malloc(sizeof(struct Curl_tree)); + +#ifdef TEST2 + key = sizes[i]; +#elif defined(TEST1) + key = (541*i)%1023; +#elif defined(TEST3) + key = 100; +#endif + + t->payload = (void *)key; /* for simplicity */ + if(!t) { + puts("out of memory!"); + return 0; + } + root = Curl_splayinsert(key, root, t); + } + +#if 0 + puts("Result:"); + Curl_splayprint(root, 0, 1); +#endif + +#if 1 + for (i = 0; i < MAX; i++) { + int rem = (i+7)%MAX; + struct Curl_tree *r; + printf("Tree look:\n"); + Curl_splayprint(root, 0, 1); + printf("remove pointer %d, payload %d\n", rem, + (int)((struct Curl_tree *)ptrs[rem])->payload); + rc = Curl_splayremovebyaddr(root, (struct Curl_tree *)ptrs[rem], &root); + if(rc) + /* failed! */ + printf("remove %d failed!\n", rem); + } +#endif + + return 0; +} + +#endif /* TEST_SPLAY */ diff --git a/Utilities/cmcurl/splay.h b/Utilities/cmcurl/splay.h new file mode 100644 index 0000000..1674534 --- /dev/null +++ b/Utilities/cmcurl/splay.h @@ -0,0 +1,54 @@ +#ifndef __SPLAY_H +#define __SPLAY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1997 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +struct Curl_tree { + struct Curl_tree *smaller; /* smaller node */ + struct Curl_tree *larger; /* larger node */ + struct Curl_tree *same; /* points to a node with identical key */ + int key; /* the "sort" key */ + void *payload; /* data the splay code doesn't care about */ +}; + +struct Curl_tree *Curl_splay(int i, struct Curl_tree *t); +struct Curl_tree *Curl_splayinsert(int key, struct Curl_tree *t, + struct Curl_tree *newnode); +#if 0 +struct Curl_tree *Curl_splayremove(int key, struct Curl_tree *t, + struct Curl_tree **removed); +#endif + +struct Curl_tree *Curl_splaygetbest(int key, struct Curl_tree *t, + struct Curl_tree **removed); +int Curl_splayremovebyaddr(struct Curl_tree *t, + struct Curl_tree *remove, + struct Curl_tree **newroot); + +#ifdef CURLDEBUG +void Curl_splayprint(struct Curl_tree * t, int d, char output); +#else +#define Curl_splayprint(x,y,z) +#endif + +#endif diff --git a/Utilities/cmcurl/ssh.c b/Utilities/cmcurl/ssh.c new file mode 100644 index 0000000..24cba1b --- /dev/null +++ b/Utilities/cmcurl/ssh.c @@ -0,0 +1,979 @@ +/*************************************************************************** +* _ _ ____ _ +* Project ___| | | | _ \| | +* / __| | | | |_) | | +* | (__| |_| | _ <| |___ +* \___|\___/|_| \_\_____| +* +* Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. +* +* This software is licensed as described in the file COPYING, which +* you should have received as part of this distribution. The terms +* are also available at http://curl.haxx.se/docs/copyright.html. +* +* You may opt to use, copy, modify, merge, publish, distribute and/or sell +* copies of the Software, and permit persons to whom the Software is +* furnished to do so, under the terms of the COPYING file. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +* $Id$ +***************************************************************************/ + +/* #define CURL_LIBSSH2_DEBUG */ + +#include "setup.h" + +#ifdef USE_LIBSSH2 +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <limits.h> + +#include <libssh2.h> +#include <libssh2_sftp.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#ifdef WIN32 + +#else /* probably some kind of unix */ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <sys/types.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_UTSNAME_H +#include <sys/utsname.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef VMS +#include <in.h> +#include <inet.h> +#endif +#endif + +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "easyif.h" /* for Curl_convert_... prototypes */ + +#include "if2ip.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ssh.h" +#include "url.h" +#include "speedcheck.h" +#include "getinfo.h" + +#include "strtoofft.h" +#include "strequal.h" +#include "sslgen.h" +#include "connect.h" +#include "strerror.h" +#include "memory.h" +#include "inet_ntop.h" +#include "select.h" +#include "parsedate.h" /* for the week day and month names */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "multiif.h" + +#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) +#include "inet_ntoa_r.h" +#endif + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) +#define DIRSEP '\\' +#else +#define DIRSEP '/' +#endif + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef CURLDEBUG +#include "memdebug.h" +#endif + +#ifndef LIBSSH2_SFTP_S_IRUSR +/* Here's a work-around for those of you who happend to run a libssh2 version + that is 0.14 or older. We should remove this kludge as soon as we can + require a more recent libssh2 release. */ +#ifndef S_IRGRP +#define S_IRGRP 0 +#endif + +#ifndef S_IROTH +#define S_IROTH 0 +#endif + +#define LIBSSH2_SFTP_S_IRUSR S_IRUSR +#define LIBSSH2_SFTP_S_IWUSR S_IWUSR +#define LIBSSH2_SFTP_S_IRGRP S_IRGRP +#define LIBSSH2_SFTP_S_IROTH S_IROTH +#define LIBSSH2_SFTP_S_IRUSR S_IRUSR +#define LIBSSH2_SFTP_S_IWUSR S_IWUSR +#define LIBSSH2_SFTP_S_IRGRP S_IRGRP +#define LIBSSH2_SFTP_S_IROTH S_IROTH +#define LIBSSH2_SFTP_S_IFMT S_IFMT +#define LIBSSH2_SFTP_S_IFDIR S_IFDIR +#define LIBSSH2_SFTP_S_IFLNK S_IFLNK +#define LIBSSH2_SFTP_S_IFSOCK S_IFSOCK +#define LIBSSH2_SFTP_S_IFCHR S_IFCHR +#define LIBSSH2_SFTP_S_IFBLK S_IFBLK +#define LIBSSH2_SFTP_S_IXUSR S_IXUSR +#define LIBSSH2_SFTP_S_IWGRP S_IWGRP +#define LIBSSH2_SFTP_S_IXGRP S_IXGRP +#define LIBSSH2_SFTP_S_IWOTH S_IWOTH +#define LIBSSH2_SFTP_S_IXOTH S_IXOTH +#endif + +static LIBSSH2_ALLOC_FUNC(libssh2_malloc); +static LIBSSH2_REALLOC_FUNC(libssh2_realloc); +static LIBSSH2_FREE_FUNC(libssh2_free); + +static void +kbd_callback(const char *name, int name_len, const char *instruction, + int instruction_len, int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) +{ + struct SSHPROTO *ssh = (struct SSHPROTO *)*abstract; + +#ifdef CURL_LIBSSH2_DEBUG + fprintf(stderr, "name=%s\n", name); + fprintf(stderr, "name_len=%d\n", name_len); + fprintf(stderr, "instruction=%s\n", instruction); + fprintf(stderr, "instruction_len=%d\n", instruction_len); + fprintf(stderr, "num_prompts=%d\n", num_prompts); +#else + (void)name; + (void)name_len; + (void)instruction; + (void)instruction_len; +#endif /* CURL_LIBSSH2_DEBUG */ + if (num_prompts == 1) { + responses[0].text = strdup(ssh->passwd); + responses[0].length = strlen(ssh->passwd); + } + (void)prompts; + (void)abstract; +} /* kbd_callback */ + +static CURLcode libssh2_error_to_CURLE(struct connectdata *conn) +{ + int errorcode; + struct SSHPROTO *scp = conn->data->reqdata.proto.ssh; + + /* Get the libssh2 error code and string */ + errorcode = libssh2_session_last_error(scp->ssh_session, &scp->errorstr, + NULL, 0); + if (errorcode == LIBSSH2_FX_OK) + return CURLE_OK; + + infof(conn->data, "libssh2 error %d, '%s'\n", errorcode, scp->errorstr); + + /* TODO: map some of the libssh2 errors to the more appropriate CURLcode + error code, and possibly add a few new SSH-related one. We must however + not return or even depend on libssh2 errors in the public libcurl API */ + + return CURLE_SSH; +} + +static LIBSSH2_ALLOC_FUNC(libssh2_malloc) +{ + return malloc(count); + (void)abstract; +} + +static LIBSSH2_REALLOC_FUNC(libssh2_realloc) +{ + return realloc(ptr, count); + (void)abstract; +} + +static LIBSSH2_FREE_FUNC(libssh2_free) +{ + free(ptr); + (void)abstract; +} + +static CURLcode ssh_init(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct SSHPROTO *ssh; + if (data->reqdata.proto.ssh) + return CURLE_OK; + + ssh = (struct SSHPROTO *)calloc(sizeof(struct SSHPROTO), 1); + if (!ssh) + return CURLE_OUT_OF_MEMORY; + + data->reqdata.proto.ssh = ssh; + + /* get some initial data into the ssh struct */ + ssh->bytecountp = &data->reqdata.keep.bytecount; + + /* no need to duplicate them, this connectdata struct won't change */ + ssh->user = conn->user; + ssh->passwd = conn->passwd; + + ssh->errorstr = NULL; + + ssh->ssh_session = NULL; + ssh->ssh_channel = NULL; + ssh->sftp_session = NULL; + ssh->sftp_handle = NULL; + + return CURLE_OK; +} + +/* + * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to + * do protocol-specific actions at connect-time. + */ +CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done) +{ + int i; + struct SSHPROTO *ssh; + const char *fingerprint; + const char *authlist; + char *home; + char rsa_pub[PATH_MAX]; + char rsa[PATH_MAX]; + char tempHome[PATH_MAX]; + curl_socket_t sock; + char *real_path; + char *working_path; + int working_path_len; + bool authed = FALSE; + CURLcode result; + struct SessionHandle *data = conn->data; + + rsa_pub[0] = rsa[0] = '\0'; + + result = ssh_init(conn); + if (result) + return result; + + ssh = data->reqdata.proto.ssh; + + working_path = curl_easy_unescape(data, data->reqdata.path, 0, + &working_path_len); + if (!working_path) + return CURLE_OUT_OF_MEMORY; + +#ifdef CURL_LIBSSH2_DEBUG + if (ssh->user) { + infof(data, "User: %s\n", ssh->user); + } + if (ssh->passwd) { + infof(data, "Password: %s\n", ssh->passwd); + } +#endif /* CURL_LIBSSH2_DEBUG */ + sock = conn->sock[FIRSTSOCKET]; + ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free, + libssh2_realloc, ssh); + if (ssh->ssh_session == NULL) { + failf(data, "Failure initialising ssh session\n"); + Curl_safefree(ssh->path); + return CURLE_FAILED_INIT; + } +#ifdef CURL_LIBSSH2_DEBUG + infof(data, "SSH socket: %d\n", sock); +#endif /* CURL_LIBSSH2_DEBUG */ + + if (libssh2_session_startup(ssh->ssh_session, sock)) { + failf(data, "Failure establishing ssh session\n"); + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + Curl_safefree(ssh->path); + return CURLE_FAILED_INIT; + } + + /* + * 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(ssh->ssh_session, + LIBSSH2_HOSTKEY_HASH_MD5); + +#ifdef CURL_LIBSSH2_DEBUG + /* The fingerprint points to static storage (!), don't free() it. */ + infof(data, "Fingerprint: "); + for (i = 0; i < 16; i++) { + infof(data, "%02X ", (unsigned char) fingerprint[i]); + } + infof(data, "\n"); +#endif /* CURL_LIBSSH2_DEBUG */ + + /* 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. + */ + authlist = libssh2_userauth_list(ssh->ssh_session, ssh->user, + strlen(ssh->user)); + + /* + * Check the supported auth types in the order I feel is most secure with the + * requested type of authentication + */ + if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && + (strstr(authlist, "publickey") != NULL)) { + /* To ponder about: should really the lib be messing about with the HOME + environment variable etc? */ + home = curl_getenv("HOME"); + + if (data->set.ssh_public_key) + snprintf(rsa_pub, sizeof(rsa_pub), "%s", data->set.ssh_public_key); + else if (home) + snprintf(rsa_pub, sizeof(rsa_pub), "%s/.ssh/id_dsa.pub", home); + + if (data->set.ssh_private_key) + snprintf(rsa, sizeof(rsa), "%s", data->set.ssh_private_key); + else if (home) + snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home); + + curl_free(home); + + if (rsa_pub[0]) { + /* The function below checks if the files exists, no need to stat() here. + */ + if (libssh2_userauth_publickey_fromfile(ssh->ssh_session, ssh->user, + rsa_pub, rsa, "") == 0) { + authed = TRUE; + } + } + } + if (!authed && + (data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && + (strstr(authlist, "password") != NULL)) { + if (!libssh2_userauth_password(ssh->ssh_session, ssh->user, ssh->passwd)) + authed = TRUE; + } + if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && + (strstr(authlist, "hostbased") != NULL)) { + } + if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) + && (strstr(authlist, "keyboard-interactive") != NULL)) { + /* Authentication failed. Continue with keyboard-interactive now. */ + if (libssh2_userauth_keyboard_interactive_ex(ssh->ssh_session, ssh->user, + strlen(ssh->user), + &kbd_callback) == 0) { + authed = TRUE; + } + } + + if (!authed) { + failf(data, "Authentication failure\n"); + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + Curl_safefree(ssh->path); + return CURLE_FAILED_INIT; + } + + /* + * At this point we have an authenticated ssh session. + */ + conn->sockfd = sock; + conn->writesockfd = CURL_SOCKET_BAD; + + if (conn->protocol == PROT_SFTP) { + /* + * Start the libssh2 sftp session + */ + ssh->sftp_session = libssh2_sftp_init(ssh->ssh_session); + if (ssh->sftp_session == NULL) { + failf(data, "Failure initialising sftp session\n"); + libssh2_sftp_shutdown(ssh->sftp_session); + ssh->sftp_session = NULL; + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + return CURLE_FAILED_INIT; + } + + /* + * Get the "home" directory + */ + i = libssh2_sftp_realpath(ssh->sftp_session, ".", tempHome, PATH_MAX-1); + if (i > 0) { + /* It seems that this string is not always NULL terminated */ + tempHome[i] = '\0'; + ssh->homedir = (char *)strdup(tempHome); + if (!ssh->homedir) { + libssh2_sftp_shutdown(ssh->sftp_session); + ssh->sftp_session = NULL; + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + return CURLE_OUT_OF_MEMORY; + } + } + else { + /* Return the error type */ + i = libssh2_sftp_last_error(ssh->sftp_session); + DEBUGF(infof(data, "error = %d\n", i)); + } + } + + /* Check for /~/ , indicating realative to the users home directory */ + if (conn->protocol == PROT_SCP) { + real_path = (char *)malloc(working_path_len+1); + if (real_path == NULL) { + Curl_safefree(working_path); + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + return CURLE_OUT_OF_MEMORY; + } + if (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[1] == '~') { + real_path = (char *)malloc(strlen(ssh->homedir) + + working_path_len + 1); + if (real_path == NULL) { + libssh2_sftp_shutdown(ssh->sftp_session); + ssh->sftp_session = NULL; + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + Curl_safefree(working_path); + return CURLE_OUT_OF_MEMORY; + } + /* It is referenced to the home directory, so strip the leading '/' */ + memcpy(real_path, ssh->homedir, strlen(ssh->homedir)); + real_path[strlen(ssh->homedir)] = '/'; + real_path[strlen(ssh->homedir)+1] = '\0'; + if (working_path_len > 3) { + memcpy(real_path+strlen(ssh->homedir)+1, working_path + 3, + 1 + working_path_len -3); + } + } + else { + real_path = (char *)malloc(working_path_len+1); + if (real_path == NULL) { + libssh2_session_free(ssh->ssh_session); + ssh->ssh_session = NULL; + Curl_safefree(working_path); + return CURLE_OUT_OF_MEMORY; + } + memcpy(real_path, working_path, 1+working_path_len); + } + } + else + return CURLE_FAILED_INIT; + + Curl_safefree(working_path); + ssh->path = real_path; + + *done = TRUE; + return CURLE_OK; +} + +CURLcode Curl_scp_do(struct connectdata *conn, bool *done) +{ + struct stat sb; + struct SSHPROTO *scp = conn->data->reqdata.proto.ssh; + CURLcode res = CURLE_OK; + + *done = TRUE; /* unconditionally */ + + if (conn->data->set.upload) { + /* + * 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. + */ + scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path, + LIBSSH2_SFTP_S_IRUSR| + LIBSSH2_SFTP_S_IWUSR| + LIBSSH2_SFTP_S_IRGRP| + LIBSSH2_SFTP_S_IROTH, + conn->data->set.infilesize, 0, 0); + if (!scp->ssh_channel) + return CURLE_FAILED_INIT; + + /* upload data */ + res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); + } + else { + /* + * We must check the remote file, if it is a directory no vaules will + * be set in sb + */ + curl_off_t bytecount; + memset(&sb, 0, sizeof(struct stat)); + scp->ssh_channel = libssh2_scp_recv(scp->ssh_session, scp->path, &sb); + if (!scp->ssh_channel) { + if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) && + (sb.st_size == 0)) { + /* Since sb is still empty, it is likely the file was not found */ + return CURLE_REMOTE_FILE_NOT_FOUND; + } + return libssh2_error_to_CURLE(conn); + } + /* download data */ + bytecount = (curl_off_t) sb.st_size; + conn->data->reqdata.maxdownload = (curl_off_t) sb.st_size; + res = Curl_setup_transfer(conn, FIRSTSOCKET, + bytecount, FALSE, NULL, -1, NULL); + } + + return res; +} + +CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + struct SSHPROTO *scp = conn->data->reqdata.proto.ssh; + (void)premature; /* not used */ + + Curl_safefree(scp->path); + scp->path = NULL; + + if (scp->ssh_channel) { + if (libssh2_channel_close(scp->ssh_channel) < 0) { + infof(conn->data, "Failed to stop libssh2 channel subsystem\n"); + } + } + + if (scp->ssh_session) { + libssh2_session_disconnect(scp->ssh_session, "Shutdown"); + libssh2_session_free(scp->ssh_session); + scp->ssh_session = NULL; + } + + free(conn->data->reqdata.proto.ssh); + conn->data->reqdata.proto.ssh = NULL; + Curl_pgrsDone(conn); + + (void)status; /* unused */ + + return CURLE_OK; +} + +/* return number of received (decrypted) bytes */ +ssize_t Curl_scp_send(struct connectdata *conn, int sockindex, + void *mem, size_t len) +{ + ssize_t nwrite; + + /* libssh2_channel_write() returns int + * + * NOTE: we should not store nor rely on connection-related data to be + * in the SessionHandle struct + */ + nwrite = (ssize_t) + libssh2_channel_write(conn->data->reqdata.proto.ssh->ssh_channel, + mem, len); + (void)sockindex; + return nwrite; +} + +/* + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len) +{ + ssize_t nread; + + /* libssh2_channel_read() returns int + * + * NOTE: we should not store nor rely on connection-related data to be + * in the SessionHandle struct + */ + + nread = (ssize_t) + libssh2_channel_read(conn->data->reqdata.proto.ssh->ssh_channel, + mem, len); + (void)sockindex; + return nread; +} + +/* + * =============== SFTP =============== + */ + +CURLcode Curl_sftp_do(struct connectdata *conn, bool *done) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh; + CURLcode res = CURLE_OK; + struct SessionHandle *data = conn->data; + curl_off_t bytecount = 0; + char *buf = data->state.buffer; + + *done = TRUE; /* unconditionally */ + + if (data->set.upload) { + /* + * 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. + */ + sftp->sftp_handle = + libssh2_sftp_open(sftp->sftp_session, sftp->path, + LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT, + LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| + LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); + if (!sftp->sftp_handle) + return CURLE_FAILED_INIT; + + /* upload data */ + res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); + } + else { + if (sftp->path[strlen(sftp->path)-1] == '/') { + /* + * This is a directory that we are trying to get, so produce a + * directory listing + * + * **BLOCKING behaviour** This should be made into a state machine and + * get a separate function called from Curl_sftp_recv() when there is + * data to read from the network, instead of "hanging" here. + */ + char filename[PATH_MAX+1]; + int len, totalLen, currLen; + char *line; + + sftp->sftp_handle = + libssh2_sftp_opendir(sftp->sftp_session, sftp->path); + if (!sftp->sftp_handle) + return CURLE_SSH; + + while ((len = libssh2_sftp_readdir(sftp->sftp_handle, filename, + PATH_MAX, &attrs)) > 0) { + filename[len] = '\0'; + + if (data->set.ftp_list_only) { + if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && + ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFDIR)) { + infof(data, "%s\n", filename); + } + } + else { + totalLen = 80 + len; + line = (char *)malloc(totalLen); + if (!line) + return CURLE_OUT_OF_MEMORY; + + if (!(attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID)) + attrs.uid = attrs.gid =0; + + currLen = snprintf(line, totalLen, "---------- 1 %5d %5d", + attrs.uid, attrs.gid); + + if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFDIR) { + line[0] = 'd'; + } + else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFLNK) { + line[0] = 'l'; + } + else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFSOCK) { + line[0] = 's'; + } + else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFCHR) { + line[0] = 'c'; + } + else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFBLK) { + line[0] = 'b'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IRUSR) { + line[1] = 'r'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IWUSR) { + line[2] = 'w'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IXUSR) { + line[3] = 'x'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IRGRP) { + line[4] = 'r'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IWGRP) { + line[5] = 'w'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IXGRP) { + line[6] = 'x'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IROTH) { + line[7] = 'r'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IWOTH) { + line[8] = 'w'; + } + if (attrs.permissions & LIBSSH2_SFTP_S_IXOTH) { + line[9] = 'x'; + } + } + if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { + currLen += snprintf(line+currLen, totalLen-currLen, "%11lld", + attrs.filesize); + } + if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + const char *months[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + struct tm *nowParts; + time_t now, remoteTime; + + now = time(NULL); + remoteTime = (time_t)attrs.mtime; + nowParts = localtime(&remoteTime); + + if ((time_t)attrs.mtime > (now - (3600 * 24 * 180))) { + currLen += snprintf(line+currLen, totalLen-currLen, + " %s %2d %2d:%02d", months[nowParts->tm_mon], + nowParts->tm_mday, nowParts->tm_hour, + nowParts->tm_min); + } + else { + currLen += snprintf(line+currLen, totalLen-currLen, + " %s %2d %5d", months[nowParts->tm_mon], + nowParts->tm_mday, 1900+nowParts->tm_year); + } + } + currLen += snprintf(line+currLen, totalLen-currLen, " %s", filename); + if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && + ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFLNK)) { + char linkPath[PATH_MAX + 1]; + + snprintf(linkPath, PATH_MAX, "%s%s", sftp->path, filename); + len = libssh2_sftp_readlink(sftp->sftp_session, linkPath, filename, + PATH_MAX); + line = realloc(line, totalLen + 4 + len); + if (!line) + return CURLE_OUT_OF_MEMORY; + + currLen += snprintf(line+currLen, totalLen-currLen, " -> %s", + filename); + } + + currLen += snprintf(line+currLen, totalLen-currLen, "\n"); + res = Curl_client_write(conn, CLIENTWRITE_BODY, line, 0); + free(line); + } + } + libssh2_sftp_closedir(sftp->sftp_handle); + sftp->sftp_handle = NULL; + + /* no data to transfer */ + res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + } + else { + /* + * Work on getting the specified file + */ + sftp->sftp_handle = + libssh2_sftp_open(sftp->sftp_session, sftp->path, LIBSSH2_FXF_READ, + LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| + LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); + if (!sftp->sftp_handle) + return CURLE_SSH; + + if (libssh2_sftp_stat(sftp->sftp_session, sftp->path, &attrs)) { + /* + * libssh2_sftp_open() didn't return an error, so maybe the server + * just doesn't support stat() + */ + data->reqdata.size = -1; + data->reqdata.maxdownload = -1; + } + else { + data->reqdata.size = attrs.filesize; + data->reqdata.maxdownload = attrs.filesize; + Curl_pgrsSetDownloadSize(data, attrs.filesize); + } + + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + + /* Now download data. The libssh2 0.14 doesn't offer any way to do this + without using this BLOCKING approach, so here's room for improvement + once libssh2 can return EWOULDBLOCK to us. */ +#if 0 + /* code left here just because this is what this function will use the + day libssh2 is improved */ + res = Curl_setup_transfer(conn, FIRSTSOCKET, + bytecount, FALSE, NULL, -1, NULL); +#endif + while (res == CURLE_OK) { + size_t nread; + /* NOTE: most *read() functions return ssize_t but this returns size_t + which normally is unsigned! */ + nread = libssh2_sftp_read(data->reqdata.proto.ssh->sftp_handle, + buf, BUFSIZE-1); + + if (nread > 0) + buf[nread] = 0; + + /* this check can be changed to a <= 0 when nread is changed to a + signed variable type */ + if ((nread == 0) || (nread == (size_t)~0)) + break; + + bytecount += 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 { + struct timeval now = Curl_tvnow(); + res = Curl_speedcheck(data, now); + } + } + if(Curl_pgrsUpdate(conn)) + res = CURLE_ABORTED_BY_CALLBACK; + + /* no (more) data to transfer */ + res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + } + } + + return res; +} + +CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh; + (void)premature; /* not used */ + + Curl_safefree(sftp->path); + sftp->path = NULL; + + Curl_safefree(sftp->homedir); + sftp->homedir = NULL; + + if (sftp->sftp_handle) { + if (libssh2_sftp_close(sftp->sftp_handle) < 0) { + infof(conn->data, "Failed to close libssh2 file\n"); + } + } + + if (sftp->sftp_session) { + if (libssh2_sftp_shutdown(sftp->sftp_session) < 0) { + infof(conn->data, "Failed to stop libssh2 sftp subsystem\n"); + } + } + + if (sftp->ssh_channel) { + if (libssh2_channel_close(sftp->ssh_channel) < 0) { + infof(conn->data, "Failed to stop libssh2 channel subsystem\n"); + } + } + + if (sftp->ssh_session) { + libssh2_session_disconnect(sftp->ssh_session, "Shutdown"); + libssh2_session_free(sftp->ssh_session); + sftp->ssh_session = NULL; + } + + free(conn->data->reqdata.proto.ssh); + conn->data->reqdata.proto.ssh = NULL; + Curl_pgrsDone(conn); + + (void)status; /* unused */ + + return CURLE_OK; +} + +/* return number of received (decrypted) bytes */ +ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex, + void *mem, size_t len) +{ + ssize_t nwrite; + + /* libssh2_sftp_write() returns size_t !*/ + + nwrite = (ssize_t) + libssh2_sftp_write(conn->data->reqdata.proto.ssh->sftp_handle, mem, len); + (void)sockindex; + return nwrite; +} + +/* + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len) +{ + ssize_t nread; + + /* libssh2_sftp_read() returns size_t !*/ + + nread = (ssize_t) + libssh2_sftp_read(conn->data->reqdata.proto.ssh->sftp_handle, mem, len); + (void)sockindex; + return nread; +} + +#endif /* USE_LIBSSH2 */ diff --git a/Utilities/cmcurl/ssh.h b/Utilities/cmcurl/ssh.h new file mode 100644 index 0000000..d914490 --- /dev/null +++ b/Utilities/cmcurl/ssh.h @@ -0,0 +1,49 @@ +#ifndef __SSH_H +#define __SSH_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#ifdef USE_LIBSSH2 + +CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done); + +CURLcode Curl_scp_do(struct connectdata *conn, bool *done); +CURLcode Curl_scp_done(struct connectdata *conn, CURLcode, bool premature); + +ssize_t Curl_scp_send(struct connectdata *conn, int sockindex, + void *mem, size_t len); +ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len); + +CURLcode Curl_sftp_do(struct connectdata *conn, bool *done); +CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode, bool premature); + +ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex, + void *mem, size_t len); +ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len); + +#endif /* USE_LIBSSH2 */ + +#endif /* __SSH_H */ diff --git a/Utilities/cmcurl/sslgen.c b/Utilities/cmcurl/sslgen.c new file mode 100644 index 0000000..f110a51 --- /dev/null +++ b/Utilities/cmcurl/sslgen.c @@ -0,0 +1,618 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* This file is for "generic" SSL functions that all libcurl internals should + use. It is responsible for calling the proper 'ossl' function in ssluse.c + (OpenSSL based) or the 'gtls' function in gtls.c (GnuTLS based). + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + Curl_ossl_ - prefix for OpenSSL ones + Curl_gtls_ - prefix for GnuTLS ones + + "SSL/TLS Strong Encryption: An Introduction" + http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html +*/ + +#include "setup.h" +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "urldata.h" +#define SSLGEN_C +#include "sslgen.h" /* generic SSL protos etc */ +#include "ssluse.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "sendf.h" +#include "strequal.h" +#include "url.h" +#include "memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* "global" init done? */ +static bool init_ssl=FALSE; + +static bool safe_strequal(char* str1, char* str2); + +static bool safe_strequal(char* str1, char* str2) +{ + if(str1 && str2) + /* both pointers point to something then compare them */ + return (bool)(0 != strequal(str1, str2)); + else + /* if both pointers are NULL then treat them as equal */ + return (bool)(!str1 && !str2); +} + +bool +Curl_ssl_config_matches(struct ssl_config_data* data, + struct ssl_config_data* needle) +{ + if((data->version == needle->version) && + (data->verifypeer == needle->verifypeer) && + (data->verifyhost == needle->verifyhost) && + safe_strequal(data->CApath, needle->CApath) && + safe_strequal(data->CAfile, needle->CAfile) && + safe_strequal(data->random_file, needle->random_file) && + safe_strequal(data->egdsocket, needle->egdsocket) && + safe_strequal(data->cipher_list, needle->cipher_list)) + return TRUE; + + return FALSE; +} + +bool +Curl_clone_ssl_config(struct ssl_config_data *source, + struct ssl_config_data *dest) +{ + 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; +} + +void Curl_free_ssl_config(struct ssl_config_data* sslc) +{ + if(sslc->CAfile) + free(sslc->CAfile); + + if(sslc->CApath) + free(sslc->CApath); + + if(sslc->cipher_list) + free(sslc->cipher_list); + + if(sslc->egdsocket) + free(sslc->egdsocket); + + if(sslc->random_file) + free(sslc->random_file); +} + +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +int Curl_ssl_init(void) +{ + /* make sure this is only done once */ + if(init_ssl) + return 1; + init_ssl = TRUE; /* never again */ + +#ifdef USE_SSLEAY + return Curl_ossl_init(); +#else +#ifdef USE_GNUTLS + return Curl_gtls_init(); +#else + /* no SSL support */ + return 1; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + + +/* Global cleanup */ +void Curl_ssl_cleanup(void) +{ + if(init_ssl) { + /* only cleanup if we did a previous init */ +#ifdef USE_SSLEAY + Curl_ossl_cleanup(); +#else +#ifdef USE_GNUTLS + Curl_gtls_cleanup(); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + init_ssl = FALSE; + } +} + +CURLcode +Curl_ssl_connect(struct connectdata *conn, int sockindex) +{ +#ifdef USE_SSL + /* mark this is being ssl enabled from here on. */ + conn->ssl[sockindex].use = TRUE; + +#ifdef USE_SSLEAY + return Curl_ossl_connect(conn, sockindex); +#else +#ifdef USE_GNUTLS + return Curl_gtls_connect(conn, sockindex); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + +#else + /* without SSL */ + (void)conn; + (void)sockindex; + return CURLE_OK; +#endif /* USE_SSL */ +} + +CURLcode +Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex, + bool *done) +{ +#if defined(USE_SSL) && defined(USE_SSLEAY) + /* mark this is being ssl enabled from here on. */ + conn->ssl[sockindex].use = TRUE; + return Curl_ossl_connect_nonblocking(conn, sockindex, done); + +#else + /* not implemented! + fallback to BLOCKING call. */ + *done = TRUE; + return Curl_ssl_connect(conn, sockindex); +#endif +} + +#ifdef USE_SSL + +/* + * Check if there's a session ID for the given connection in the cache, and if + * there's one suitable, it is provided. Returns TRUE when no entry matched. + */ +int Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */ +{ + struct curl_ssl_session *check; + struct SessionHandle *data = conn->data; + long i; + + if(!conn->ssl_config.sessionid) + /* session ID re-use is disabled */ + return TRUE; + + for(i=0; i< data->set.ssl.numsessions; i++) { + check = &data->state.session[i]; + if(!check->sessionid) + /* not session ID means blank entry */ + continue; + if(curl_strequal(conn->host.name, check->name) && + (conn->remote_port == check->remote_port) && + Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { + /* yes, we have a session ID! */ + data->state.sessionage++; /* increase general age */ + check->age = data->state.sessionage; /* set this as used in this age */ + *ssl_sessionid = check->sessionid; + if(idsize) + *idsize = check->idsize; + return FALSE; + } + } + *ssl_sessionid = NULL; + return TRUE; +} + +/* + * Kill a single session ID entry in the cache. + */ +static int kill_session(struct curl_ssl_session *session) +{ + if(session->sessionid) { + /* defensive check */ + + /* free the ID the SSL-layer specific way */ +#ifdef USE_SSLEAY + Curl_ossl_session_free(session->sessionid); +#else + Curl_gtls_session_free(session->sessionid); +#endif + session->sessionid=NULL; + session->age = 0; /* fresh */ + + Curl_free_ssl_config(&session->ssl_config); + + Curl_safefree(session->name); + session->name = NULL; /* no name */ + + return 0; /* ok */ + } + else + return 1; +} + +/* + * Store session id in the session cache. The ID passed on to this function + * must already have been extracted and allocated the proper way for the SSL + * layer. Curl_XXXX_session_free() will be called to free/kill the session ID + * later on. + */ +CURLcode Curl_ssl_addsessionid(struct connectdata *conn, + void *ssl_sessionid, + size_t idsize) +{ + int i; + struct SessionHandle *data=conn->data; /* the mother of all structs */ + struct curl_ssl_session *store = &data->state.session[0]; + long oldest_age=data->state.session[0].age; /* zero if unused */ + char *clone_host; + + /* Even though session ID re-use might be disabled, that only disables USING + IT. We still store it here in case the re-using is again enabled for an + upcoming transfer */ + + clone_host = strdup(conn->host.name); + if(!clone_host) + return CURLE_OUT_OF_MEMORY; /* bail out */ + + /* Now we should add the session ID and the host name to the cache, (remove + the oldest if necessary) */ + + /* find an empty slot for us, or find the oldest */ + for(i=1; (i<data->set.ssl.numsessions) && + data->state.session[i].sessionid; i++) { + if(data->state.session[i].age < oldest_age) { + oldest_age = data->state.session[i].age; + store = &data->state.session[i]; + } + } + if(i == data->set.ssl.numsessions) + /* cache is full, we must "kill" the oldest entry! */ + kill_session(store); + else + store = &data->state.session[i]; /* use this slot */ + + /* now init the session struct wisely */ + store->sessionid = ssl_sessionid; + store->idsize = idsize; + store->age = data->state.sessionage; /* set current age */ + store->name = clone_host; /* clone host name */ + store->remote_port = conn->remote_port; /* port number */ + + if (!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + + +#endif + +void Curl_ssl_close_all(struct SessionHandle *data) +{ +#ifdef USE_SSL + int i; + /* kill the session ID cache */ + if(data->state.session) { + for(i=0; i< data->set.ssl.numsessions; i++) + /* the single-killer function handles empty table slots */ + kill_session(&data->state.session[i]); + + /* free the cache data */ + free(data->state.session); + data->state.session = NULL; + } +#ifdef USE_SSLEAY + Curl_ossl_close_all(data); +#else +#ifdef USE_GNUTLS + Curl_gtls_close_all(data); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +#else /* USE_SSL */ + (void)data; +#endif /* USE_SSL */ +} + +void Curl_ssl_close(struct connectdata *conn) +{ + if(conn->ssl[FIRSTSOCKET].use) { +#ifdef USE_SSLEAY + Curl_ossl_close(conn); +#else +#ifdef USE_GNUTLS + Curl_gtls_close(conn); +#else + (void)conn; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + } +} + +CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex) +{ + if(conn->ssl[sockindex].use) { +#ifdef USE_SSLEAY + if(Curl_ossl_shutdown(conn, sockindex)) + return CURLE_SSL_SHUTDOWN_FAILED; +#else +#ifdef USE_GNUTLS + if(Curl_gtls_shutdown(conn, sockindex)) + return CURLE_SSL_SHUTDOWN_FAILED; +#else + (void)conn; + (void)sockindex; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + } + return CURLE_OK; +} + +/* Selects an (Open)SSL crypto engine + */ +CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine) +{ +#ifdef USE_SSLEAY + return Curl_ossl_set_engine(data, engine); +#else +#ifdef USE_GNUTLS + /* FIX: add code here */ + (void)data; + (void)engine; + return CURLE_FAILED_INIT; +#else + /* no SSL layer */ + (void)data; + (void)engine; + return CURLE_FAILED_INIT; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* Selects an (Open?)SSL crypto engine + */ +CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data) +{ +#ifdef USE_SSLEAY + return Curl_ossl_set_engine_default(data); +#else +#ifdef USE_GNUTLS + /* FIX: add code here */ + (void)data; + return CURLE_FAILED_INIT; +#else + /* No SSL layer */ + (void)data; + return CURLE_FAILED_INIT; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* Return list of OpenSSL crypto engine names. */ +struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data) +{ +#ifdef USE_SSLEAY + return Curl_ossl_engines_list(data); +#else +#ifdef USE_GNUTLS + /* FIX: add code here? */ + (void)data; + return NULL; +#else + (void)data; + return NULL; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* return number of sent (non-SSL) bytes */ +ssize_t Curl_ssl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ +#ifdef USE_SSLEAY + return Curl_ossl_send(conn, sockindex, mem, len); +#else +#ifdef USE_GNUTLS + return Curl_gtls_send(conn, sockindex, mem, len); +#else + (void)conn; + (void)sockindex; + (void)mem; + (void)len; + return 0; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* return number of received (decrypted) bytes */ + +/* + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +ssize_t Curl_ssl_recv(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + char *mem, /* store read data here */ + size_t len) /* max amount to read */ +{ +#ifdef USE_SSL + ssize_t nread; + bool block = FALSE; + +#ifdef USE_SSLEAY + nread = Curl_ossl_recv(conn, sockindex, mem, len, &block); +#else +#ifdef USE_GNUTLS + nread = Curl_gtls_recv(conn, sockindex, mem, len, &block); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + if(nread == -1) { + if(!block) + return 0; /* this is a true error, not EWOULDBLOCK */ + else + return -1; + } + + return (int)nread; + +#else /* USE_SSL */ + (void)conn; + (void)sockindex; + (void)mem; + (void)len; + return 0; +#endif /* USE_SSL */ +} + + +/* + * This sets up a session ID cache to the specified size. Make sure this code + * is agnostic to what underlying SSL technology we use. + */ +CURLcode Curl_ssl_initsessions(struct SessionHandle *data, long amount) +{ +#ifdef USE_SSL + struct curl_ssl_session *session; + + if(data->state.session) + /* this is just a precaution to prevent multiple inits */ + return CURLE_OK; + + session = (struct curl_ssl_session *) + calloc(sizeof(struct curl_ssl_session), amount); + if(!session) + return CURLE_OUT_OF_MEMORY; + + /* store the info in the SSL section */ + data->set.ssl.numsessions = amount; + data->state.session = session; + data->state.sessionage = 1; /* this is brand new */ +#else + /* without SSL, do nothing */ + (void)data; + (void)amount; +#endif + + return CURLE_OK; +} + +size_t Curl_ssl_version(char *buffer, size_t size) +{ +#ifdef USE_SSLEAY + return Curl_ossl_version(buffer, size); +#else +#ifdef USE_GNUTLS + return Curl_gtls_version(buffer, size); +#else + (void)buffer; + (void)size; + return 0; /* no SSL support */ +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + + +/* + * This function tries to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ +int Curl_ssl_check_cxn(struct connectdata *conn) +{ +#ifdef USE_SSLEAY + return Curl_ossl_check_cxn(conn); +#else + (void)conn; + /* TODO: we lack implementation of this for GnuTLS */ + return -1; /* connection status unknown */ +#endif /* USE_SSLEAY */ +} + +bool Curl_ssl_data_pending(struct connectdata *conn, + int connindex) +{ +#ifdef USE_SSLEAY + /* OpenSSL-specific */ + if(conn->ssl[connindex].handle) + /* SSL is in use */ + return SSL_pending(conn->ssl[connindex].handle); +#else + (void)conn; + (void)connindex; +#endif + return FALSE; /* nothing pending */ + +} diff --git a/Utilities/cmcurl/sslgen.h b/Utilities/cmcurl/sslgen.h new file mode 100644 index 0000000..c24d46b --- /dev/null +++ b/Utilities/cmcurl/sslgen.h @@ -0,0 +1,84 @@ +#ifndef __SSLGEN_H +#define __SSLGEN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +bool Curl_ssl_config_matches(struct ssl_config_data* data, + struct ssl_config_data* needle); +bool Curl_clone_ssl_config(struct ssl_config_data* source, + struct ssl_config_data* dest); +void Curl_free_ssl_config(struct ssl_config_data* sslc); + +int Curl_ssl_init(void); +void Curl_ssl_cleanup(void); +CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex); +CURLcode Curl_ssl_connect_nonblocking(struct connectdata *conn, + int sockindex, + bool *done); +void Curl_ssl_close(struct connectdata *conn); +/* tell the SSL stuff to close down all open information regarding + connections (and thus session ID caching etc) */ +void Curl_ssl_close_all(struct SessionHandle *data); +CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine); +/* Sets engine as default for all SSL operations */ +CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data); +ssize_t Curl_ssl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len); +ssize_t Curl_ssl_recv(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + char *mem, /* store read data here */ + size_t len); /* max amount to read */ + +/* init the SSL session ID cache */ +CURLcode Curl_ssl_initsessions(struct SessionHandle *, long); +/* extract a session ID */ +int Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */; +/* add a new session ID */ +CURLcode Curl_ssl_addsessionid(struct connectdata *conn, + void *ssl_sessionid, + size_t idsize); + + +struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data); + +size_t Curl_ssl_version(char *buffer, size_t size); + +int Curl_ssl_check_cxn(struct connectdata *conn); + +CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex); + +bool Curl_ssl_data_pending(struct connectdata *conn, + int connindex); + +#if !defined(USE_SSL) && !defined(SSLGEN_C) +/* set up blank macros for none-SSL builds */ +#define Curl_ssl_close_all(x) +#endif + +#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ + +#endif diff --git a/Utilities/cmcurl/ssluse.c b/Utilities/cmcurl/ssluse.c index 04d1c94..55afb24 100644 --- a/Utilities/cmcurl/ssluse.c +++ b/Utilities/cmcurl/ssluse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,8 +22,13 @@ ***************************************************************************/ /* - * The original SSL code for curl was written by - * Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi> + * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + */ + +/* + * The original SSLeay-using code for curl was written by Linas Vepstas and + * Sampo Kellomaki 1998. */ #include "setup.h" @@ -44,17 +49,26 @@ #include "url.h" /* for the ssl config check function */ #include "inet_pton.h" #include "ssluse.h" -#include "connect.h" /* Curl_ourerrno() proto */ +#include "connect.h" /* Curl_sockerrno() proto */ #include "strequal.h" +#include "select.h" +#include "sslgen.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include <curl/mprintf.h> #ifdef USE_SSLEAY + +#ifdef USE_OPENSSL #include <openssl/rand.h> #include <openssl/x509v3.h> +#else +#include <rand.h> +#include <x509v3.h> +#endif -#include "curl_memory.h" +#include "memory.h" +#include "easyif.h" /* for Curl_convert_from_utf8 prototype */ /* The last #include file should be: */ #include "memdebug.h" @@ -83,10 +97,31 @@ #undef HAVE_ENGINE_LOAD_FOUR_ARGS #endif +#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H) +/* OpenSSL has PKCS 12 support */ +#define HAVE_PKCS12_SUPPORT +#else +/* OpenSSL/SSLEay does not have PKCS12 support */ +#undef HAVE_PKCS12_SUPPORT +#endif + #if OPENSSL_VERSION_NUMBER >= 0x00906001L #define HAVE_ERR_ERROR_STRING_N 1 #endif +#if OPENSSL_VERSION_NUMBER >= 0x00909000L +#define SSL_METHOD_QUAL const +#else +#define SSL_METHOD_QUAL +#endif + +/* + * Number of bytes to read from the random number seed file. This must be + * a finite value (because some entropy "files" like /dev/urandom have + * an infinite length), but must be large enough to provide enough + * entopy to properly seed OpenSSL's PRNG. + */ +#define RAND_LOAD_LENGTH 1024 #ifndef HAVE_USERDATA_IN_PWD_CALLBACK static char global_passwd[64]; @@ -123,19 +158,18 @@ static int passwd_callback(char *buf, int num, int verify #define seed_enough(x) rand_enough() static bool rand_enough(void) { - return RAND_status()?TRUE:FALSE; + return (bool)(0 != RAND_status()); } #else #define seed_enough(x) rand_enough(x) static bool rand_enough(int nread) { /* this is a very silly decision to make */ - return (nread > 500)?TRUE:FALSE; + return (bool)(nread > 500); } #endif -static -int random_the_seed(struct SessionHandle *data) +static int ossl_seed(struct SessionHandle *data) { char *buf = data->state.buffer; /* point to the big buffer */ int nread=0; @@ -153,7 +187,7 @@ int random_the_seed(struct SessionHandle *data) /* let the option override the define */ nread += RAND_load_file((data->set.ssl.random_file? data->set.ssl.random_file:RANDOM_FILE), - 16384); + RAND_LOAD_LENGTH); if(seed_enough(nread)) return nread; } @@ -215,7 +249,7 @@ int random_the_seed(struct SessionHandle *data) RAND_file_name(buf, BUFSIZE); if(buf[0]) { /* we got a file name to try */ - nread += RAND_load_file(buf, 16384); + nread += RAND_load_file(buf, RAND_LOAD_LENGTH); if(seed_enough(nread)) return nread; } @@ -224,9 +258,26 @@ int random_the_seed(struct SessionHandle *data) return nread; } +int Curl_ossl_seed(struct SessionHandle *data) +{ + /* we have the "SSL is seeded" boolean static to prevent multiple + time-consuming seedings in vain */ + static bool ssl_seeded = FALSE; + + if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { + ossl_seed(data); + ssl_seeded = TRUE; + } + return 0; +} + + #ifndef SSL_FILETYPE_ENGINE #define SSL_FILETYPE_ENGINE 42 #endif +#ifndef SSL_FILETYPE_PKCS12 +#define SSL_FILETYPE_PKCS12 43 +#endif static int do_file_type(const char *type) { if(!type || !type[0]) @@ -237,6 +288,8 @@ static int do_file_type(const char *type) return SSL_FILETYPE_ASN1; if(curl_strequal(type, "ENG")) return SSL_FILETYPE_ENGINE; + if(curl_strequal(type, "P12")) + return SSL_FILETYPE_PKCS12; return -1; } @@ -254,6 +307,7 @@ int cert_stuff(struct connectdata *conn, if(cert_file != NULL) { SSL *ssl; X509 *x509; + int cert_done = 0; if(data->set.key_passwd) { #ifndef HAVE_USERDATA_IN_PWD_CALLBACK @@ -277,12 +331,15 @@ int cert_stuff(struct connectdata *conn, file_type = do_file_type(cert_type); +#define SSL_CLIENT_CERT_ERR \ + "unable to use client certificate (no key found or wrong pass phrase?)" + switch(file_type) { case SSL_FILETYPE_PEM: /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ if(SSL_CTX_use_certificate_chain_file(ctx, cert_file) != 1) { - failf(data, "unable to set certificate file (wrong password?)"); + failf(data, SSL_CLIENT_CERT_ERR); return 0; } break; @@ -294,7 +351,7 @@ int cert_stuff(struct connectdata *conn, if(SSL_CTX_use_certificate_file(ctx, cert_file, file_type) != 1) { - failf(data, "unable to set certificate file (wrong password?)"); + failf(data, SSL_CLIENT_CERT_ERR); return 0; } break; @@ -302,6 +359,56 @@ int cert_stuff(struct connectdata *conn, failf(data, "file type ENG for certificate not implemented"); return 0; + case SSL_FILETYPE_PKCS12: + { +#ifdef HAVE_PKCS12_SUPPORT + FILE *f; + PKCS12 *p12; + EVP_PKEY *pri; + + f = fopen(cert_file,"rb"); + if (!f) { + failf(data, "could not open PKCS12 file '%s'", cert_file); + return 0; + } + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + PKCS12_PBE_add(); + + if (!PKCS12_parse(p12, data->set.key_passwd, &pri, &x509, NULL)) { + failf(data, + "could not parse PKCS12 file, check password, OpenSSL error %s", + ERR_error_string(ERR_get_error(), NULL) ); + return 0; + } + + PKCS12_free(p12); + + if(SSL_CTX_use_certificate(ctx, x509) != 1) { + failf(data, SSL_CLIENT_CERT_ERR); + EVP_PKEY_free(pri); + X509_free(x509); + return 0; + } + + if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) { + failf(data, "unable to use private key from PKCS12 file '%s'", + cert_file); + EVP_PKEY_free(pri); + X509_free(x509); + return 0; + } + + EVP_PKEY_free(pri); + X509_free(x509); + cert_done = 1; + break; +#else + failf(data, "file type P12 for certificate not supported"); + return 0; +#endif + } default: failf(data, "not supported file type '%s' for certificate", cert_type); return 0; @@ -311,6 +418,8 @@ int cert_stuff(struct connectdata *conn, switch(file_type) { case SSL_FILETYPE_PEM: + if(cert_done) + break; if(key_file == NULL) /* cert & key can only be in PEM case in the same file */ key_file=cert_file; @@ -325,7 +434,7 @@ int cert_stuff(struct connectdata *conn, #ifdef HAVE_OPENSSL_ENGINE_H { /* XXXX still needs some work */ EVP_PKEY *priv_key = NULL; - if(conn && conn->data && conn->data->engine) { + if(conn && conn->data && conn->data->state.engine) { #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS UI_METHOD *ui_method = UI_OpenSSL(); #endif @@ -335,7 +444,7 @@ int cert_stuff(struct connectdata *conn, } /* the typecast below was added to please mingw32 */ priv_key = (EVP_PKEY *) - ENGINE_load_private_key(conn->data->engine,key_file, + ENGINE_load_private_key(conn->data->state.engine,key_file, #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS ui_method, #endif @@ -361,12 +470,23 @@ int cert_stuff(struct connectdata *conn, failf(data, "file type ENG for private key not supported\n"); return 0; #endif + case SSL_FILETYPE_PKCS12: + if(!cert_done) { + failf(data, "file type P12 for private key not supported\n"); + return 0; + } + break; default: failf(data, "not supported file type for private key\n"); return 0; } ssl=SSL_new(ctx); + if (NULL == ssl) { + failf(data,"unable to create an SSL structure\n"); + return 0; + } + x509=SSL_get_certificate(ssl); /* This version was provided by Evan Jordan and is supposed to not @@ -408,24 +528,33 @@ int cert_verify_callback(int ok, X509_STORE_CTX *ctx) return ok; } -/* "global" init done? */ -static int init_ssl=0; +/* Return error string for last OpenSSL error + */ +static char *SSL_strerror(unsigned long error, char *buf, size_t size) +{ +#ifdef HAVE_ERR_ERROR_STRING_N + /* OpenSSL 0.9.6 and later has a function named + ERRO_error_string_n() that takes the size of the buffer as a + third argument */ + ERR_error_string_n(error, buf, size); +#else + (void) size; + ERR_error_string(error, buf); +#endif + return (buf); +} -/* we have the "SSL is seeded" boolean global for the application to - prevent multiple time-consuming seedings in vain */ -static bool ssl_seeded = FALSE; #endif /* USE_SSLEAY */ -/* Global init */ -void Curl_SSL_init(void) -{ #ifdef USE_SSLEAY - /* make sure this is only done once */ - if(0 != init_ssl) - return; - - init_ssl++; /* never again */ - +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +int Curl_ossl_init(void) +{ #ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES ENGINE_load_builtin_engines(); #endif @@ -434,261 +563,287 @@ void Curl_SSL_init(void) SSL_load_error_strings(); /* Setup all the global SSL stuff */ - SSLeay_add_ssl_algorithms(); -#else - /* SSL disabled, do nothing */ -#endif + if (!SSLeay_add_ssl_algorithms()) + return 0; + + return 1; } -/* Global cleanup */ -void Curl_SSL_cleanup(void) -{ +#endif /* USE_SSLEAY */ + #ifdef USE_SSLEAY - if(init_ssl) { - /* only cleanup if we did a previous init */ - /* Free the SSL error strings */ - ERR_free_strings(); +/* Global cleanup */ +void Curl_ossl_cleanup(void) +{ + /* Free the SSL error strings */ + ERR_free_strings(); - /* EVP_cleanup() removes all ciphers and digests from the - table. */ - EVP_cleanup(); + /* EVP_cleanup() removes all ciphers and digests from the + table. */ + EVP_cleanup(); #ifdef HAVE_ENGINE_cleanup - ENGINE_cleanup(); + ENGINE_cleanup(); #endif #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA - /* this function was not present in 0.9.6b, but was added sometimes - later */ - CRYPTO_cleanup_all_ex_data(); -#endif - - init_ssl=0; /* not inited any more */ - } -#else - /* SSL disabled, do nothing */ + /* this function was not present in 0.9.6b, but was added sometimes + later */ + CRYPTO_cleanup_all_ex_data(); #endif } -#ifndef USE_SSLEAY -void Curl_SSL_Close(struct connectdata *conn) -{ - (void)conn; -} -#endif - -#ifdef USE_SSLEAY - /* - * This function is called when an SSL connection is closed. + * This function uses SSL_peek to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown */ -void Curl_SSL_Close(struct connectdata *conn) +int Curl_ossl_check_cxn(struct connectdata *conn) { - if(conn->ssl[FIRSTSOCKET].use) { - int i; - /* - ERR_remove_state() frees the error queue associated with - thread pid. If pid == 0, the current thread will have its - error queue removed. - - Since error queue data structures are allocated - automatically for new threads, they must be freed when - threads are terminated in oder to avoid memory leaks. - */ - ERR_remove_state(0); + int rc; + char buf; - for(i=0; i<2; i++) { - struct ssl_connect_data *connssl = &conn->ssl[i]; + rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1); + if (rc > 0) + return 1; /* connection still in place */ - if(connssl->handle) { - (void)SSL_shutdown(connssl->handle); - SSL_set_connect_state(connssl->handle); + if (rc == 0) + return 0; /* connection has been closed */ - SSL_free (connssl->handle); - connssl->handle = NULL; - } - if(connssl->ctx) { - SSL_CTX_free (connssl->ctx); - connssl->ctx = NULL; - } - connssl->use = FALSE; /* get back to ordinary socket usage */ - } - } + return -1; /* connection status unknown */ } +#endif /* USE_SSLEAY */ -/* - * This sets up a session cache to the specified size. +/* Selects an OpenSSL crypto engine */ -CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount) +CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine) { - struct curl_ssl_session *session; +#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) + ENGINE *e = ENGINE_by_id(engine); - if(data->state.session) - /* this is just a precaution to prevent multiple inits */ - return CURLE_OK; - - session = (struct curl_ssl_session *) - malloc(amount * sizeof(struct curl_ssl_session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - - /* "blank out" the newly allocated memory */ - memset(session, 0, amount * sizeof(struct curl_ssl_session)); + if (!e) { + failf(data, "SSL Engine '%s' not found", engine); + return (CURLE_SSL_ENGINE_NOTFOUND); + } - /* store the info in the SSL section */ - data->set.ssl.numsessions = amount; - data->state.session = session; - data->state.sessionage = 1; /* this is brand new */ + if (data->state.engine) { + ENGINE_finish(data->state.engine); + ENGINE_free(data->state.engine); + data->state.engine = NULL; + } + if (!ENGINE_init(e)) { + char buf[256]; - return CURLE_OK; + ENGINE_free(e); + failf(data, "Failed to initialise SSL Engine '%s':\n%s", + engine, SSL_strerror(ERR_get_error(), buf, sizeof(buf))); + return (CURLE_SSL_ENGINE_INITFAILED); + } + data->state.engine = e; + return (CURLE_OK); +#else + (void)engine; + failf(data, "SSL Engine not supported"); + return (CURLE_SSL_ENGINE_NOTFOUND); +#endif } -/* - * Check if there's a session ID for the given connection in the cache, - * and if there's one suitable, it is returned. +#ifdef USE_SSLEAY +/* Sets engine as default for all SSL operations */ -static int Get_SSL_Session(struct connectdata *conn, - SSL_SESSION **ssl_sessionid) +CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data) { - struct curl_ssl_session *check; - struct SessionHandle *data = conn->data; - long i; - - for(i=0; i< data->set.ssl.numsessions; i++) { - check = &data->state.session[i]; - if(!check->sessionid) - /* not session ID means blank entry */ - continue; - if(curl_strequal(conn->host.name, check->name) && - (conn->remote_port == check->remote_port) && - Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { - /* yes, we have a session ID! */ - data->state.sessionage++; /* increase general age */ - check->age = data->state.sessionage; /* set this as used in this age */ - *ssl_sessionid = check->sessionid; - return FALSE; +#ifdef HAVE_OPENSSL_ENGINE_H + if (data->state.engine) { + if (ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { + infof(data,"set default crypto engine '%s'\n", ENGINE_get_id(data->state.engine)); + } + else { + failf(data, "set default crypto engine '%s' failed", ENGINE_get_id(data->state.engine)); + return CURLE_SSL_ENGINE_SETFAILED; } } - *ssl_sessionid = (SSL_SESSION *)NULL; - return TRUE; +#else + (void) data; +#endif + return CURLE_OK; } +#endif /* USE_SSLEAY */ -/* - * Kill a single session ID entry in the cache. +/* Return list of OpenSSL crypto engine names. */ -static int Kill_Single_Session(struct curl_ssl_session *session) +struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data) { - if(session->sessionid) { - /* defensive check */ - - /* free the ID */ - SSL_SESSION_free(session->sessionid); - session->sessionid=NULL; - session->age = 0; /* fresh */ + struct curl_slist *list = NULL; +#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) + ENGINE *e; - Curl_free_ssl_config(&session->ssl_config); + for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) + list = curl_slist_append(list, ENGINE_get_id(e)); +#endif + (void) data; + return (list); +} - Curl_safefree(session->name); - session->name = NULL; /* no name */ - return 0; /* ok */ - } - else - return 1; -} +#ifdef USE_SSLEAY /* - * This function is called when the 'data' struct is going away. Close - * down everything and free all resources! + * This function is called when an SSL connection is closed. */ -int Curl_SSL_Close_All(struct SessionHandle *data) +void Curl_ossl_close(struct connectdata *conn) { int i; + /* + ERR_remove_state() frees the error queue associated with + thread pid. If pid == 0, the current thread will have its + error queue removed. - if(data->state.session) { - for(i=0; i< data->set.ssl.numsessions; i++) - /* the single-killer function handles empty table slots */ - Kill_Single_Session(&data->state.session[i]); + Since error queue data structures are allocated + automatically for new threads, they must be freed when + threads are terminated in oder to avoid memory leaks. + */ + ERR_remove_state(0); - /* free the cache data */ - free(data->state.session); - } -#ifdef HAVE_OPENSSL_ENGINE_H - if(data->engine) - { - ENGINE_free(data->engine); - data->engine = NULL; + for(i=0; i<2; i++) { + struct ssl_connect_data *connssl = &conn->ssl[i]; + + if(connssl->handle) { + (void)SSL_shutdown(connssl->handle); + SSL_set_connect_state(connssl->handle); + + SSL_free (connssl->handle); + connssl->handle = NULL; + } + if(connssl->ctx) { + SSL_CTX_free (connssl->ctx); + connssl->ctx = NULL; + } + connssl->use = FALSE; /* get back to ordinary socket usage */ } -#endif - return 0; } /* - * Extract the session id and store it in the session cache. + * This function is called to shut down the SSL layer but keep the + * socket open (CCC - Clear Command Channel) */ -static int Store_SSL_Session(struct connectdata *conn, - struct ssl_connect_data *ssl) +int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) { - SSL_SESSION *ssl_sessionid; - int i; - struct SessionHandle *data=conn->data; /* the mother of all structs */ - struct curl_ssl_session *store = &data->state.session[0]; - long oldest_age=data->state.session[0].age; /* zero if unused */ - char *clone_host; - - clone_host = strdup(conn->host.name); - if(!clone_host) - return -1; /* bail out */ - - /* ask OpenSSL, say please */ - -#ifdef HAVE_SSL_GET1_SESSION - ssl_sessionid = SSL_get1_session(ssl->handle); - - /* SSL_get1_session() will increment the reference - count and the session will stay in memory until explicitly freed with - SSL_SESSION_free(3), regardless of its state. - This function was introduced in openssl 0.9.5a. */ -#else - ssl_sessionid = SSL_get_session(ssl->handle); - - /* if SSL_get1_session() is unavailable, use SSL_get_session(). - This is an inferior option because the session can be flushed - at any time by openssl. It is included only so curl compiles - under versions of openssl < 0.9.5a. + int retval = 0; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct SessionHandle *data = conn->data; + char buf[120]; /* We will use this for the OpenSSL error buffer, so it has + to be at least 120 bytes long. */ + unsigned long sslerror; + ssize_t nread; + int err; + int done = 0; + + /* This has only been tested on the proftpd server, and the mod_tls code + sends a close notify alert without waiting for a close notify alert in + response. Thus we wait for a close notify alert from the server, but + we do not send one. Let's hope other servers do the same... */ + + if(connssl->handle) { + while(!done) { + int what = Curl_select(conn->sock[sockindex], + CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); + if(what > 0) { + /* Something to read, let's do it and hope that it is the close + notify alert from the server */ + nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf, + sizeof(buf)); + err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread); + + switch(err) { + case SSL_ERROR_NONE: /* this is not an error */ + case SSL_ERROR_ZERO_RETURN: /* no more data */ + /* This is the expected response. There was no data but only + the close notify alert */ + done = 1; + break; + case SSL_ERROR_WANT_READ: + /* there's data pending, re-invoke SSL_read() */ + infof(data, "SSL_ERROR_WANT_READ\n"); + break; + case SSL_ERROR_WANT_WRITE: + /* SSL wants a write. Really odd. Let's bail out. */ + infof(data, "SSL_ERROR_WANT_WRITE\n"); + done = 1; + break; + default: + /* openssl/ssl.h says "look at error stack/return value/errno" */ + sslerror = ERR_get_error(); + failf(conn->data, "SSL read: %s, errno %d", + ERR_error_string(sslerror, buf), + Curl_sockerrno() ); + done = 1; + break; + } + } + else if(0 == what) { + /* timeout */ + failf(data, "SSL shutdown timeout"); + done = 1; + break; + } + else { + /* anything that gets here is fatally bad */ + failf(data, "select on SSL socket, errno: %d", Curl_sockerrno()); + retval = -1; + done = 1; + } + } /* while()-loop for the select() */ - WARNING: How curl behaves if it's session is flushed is - untested. - */ -#endif + if(data->set.verbose) { + switch(SSL_get_shutdown(connssl->handle)) { + case SSL_SENT_SHUTDOWN: + infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n"); + break; + case SSL_RECEIVED_SHUTDOWN: + infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n"); + break; + case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN: + infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|" + "SSL_RECEIVED__SHUTDOWN\n"); + break; + } + } - /* Now we should add the session ID and the host name to the cache, (remove - the oldest if necessary) */ + connssl->use = FALSE; /* get back to ordinary socket usage */ - /* find an empty slot for us, or find the oldest */ - for(i=1; (i<data->set.ssl.numsessions) && - data->state.session[i].sessionid; i++) { - if(data->state.session[i].age < oldest_age) { - oldest_age = data->state.session[i].age; - store = &data->state.session[i]; - } + SSL_free (connssl->handle); + connssl->handle = NULL; } - if(i == data->set.ssl.numsessions) - /* cache is full, we must "kill" the oldest entry! */ - Kill_Single_Session(store); - else - store = &data->state.session[i]; /* use this slot */ - - /* now init the session struct wisely */ - store->sessionid = ssl_sessionid; - store->age = data->state.sessionage; /* set current age */ - store->name = clone_host; /* clone host name */ - store->remote_port = conn->remote_port; /* port number */ + return retval; +} - Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config); +void Curl_ossl_session_free(void *ptr) +{ + /* free the ID */ + SSL_SESSION_free(ptr); +} +/* + * This function is called when the 'data' struct is going away. Close + * down everything and free all resources! + */ +int Curl_ossl_close_all(struct SessionHandle *data) +{ +#ifdef HAVE_OPENSSL_ENGINE_H + if(data->state.engine) { + ENGINE_finish(data->state.engine); + ENGINE_free(data->state.engine); + data->state.engine = NULL; + } +#else + (void)data; +#endif return 0; } @@ -776,12 +931,13 @@ static int hostmatch(const char *hostname, const char *pattern) if (hostmatch(hostname++,pattern) == HOST_MATCH) return HOST_MATCH; } - return HOST_NOMATCH; + break; } if (toupper(c) != toupper(*hostname++)) - return HOST_NOMATCH; + break; } + return HOST_NOMATCH; } static int @@ -833,6 +989,7 @@ static CURLcode verifyhost(struct connectdata *conn, #else struct in_addr addr; #endif + CURLcode res = CURLE_OK; #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -944,6 +1101,17 @@ static CURLcode verifyhost(struct connectdata *conn, if (peer_CN == nulstr) peer_CN = NULL; +#ifdef CURL_DOES_CONVERSIONS + else { + /* convert peer_CN from UTF8 */ + size_t rc; + rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN)); + /* Curl_convert_from_utf8 calls failf if unsuccessful */ + if (rc != CURLE_OK) { + return(rc); + } + } +#endif /* CURL_DOES_CONVERSIONS */ if (!peer_CN) { if(data->set.ssl.verifyhost > 1) { @@ -961,8 +1129,7 @@ static CURLcode verifyhost(struct connectdata *conn, if(data->set.ssl.verifyhost > 1) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, conn->host.dispname); - OPENSSL_free(peer_CN); - return CURLE_SSL_PEER_CERTIFICATE ; + res = CURLE_SSL_PEER_CERTIFICATE; } else infof(data, "\t common name: %s (does not match '%s')\n", @@ -970,10 +1137,11 @@ static CURLcode verifyhost(struct connectdata *conn, } else { infof(data, "\t common name: %s (matched)\n", peer_CN); - OPENSSL_free(peer_CN); } + if(peer_CN) + OPENSSL_free(peer_CN); } - return CURLE_OK; + return res; } #endif @@ -1050,7 +1218,7 @@ 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 = conn->data; + struct SessionHandle *data; const char *msg_name, *tls_rt_name; char ssl_buf[1024]; int ver, msg_type, txt_len; @@ -1076,9 +1244,9 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, msg_type = *(char*)buf; msg_name = ssl_msg_type(ssl_ver, msg_type); - txt_len = 1 + 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, txt_len, NULL); + 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); @@ -1086,34 +1254,25 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, } #endif +#ifdef USE_SSLEAY /* ====================================================== */ -CURLcode -Curl_SSLConnect(struct connectdata *conn, - int sockindex) + +static CURLcode +Curl_ossl_connect_step1(struct connectdata *conn, + int sockindex) { CURLcode retcode = CURLE_OK; -#ifdef USE_SSLEAY struct SessionHandle *data = conn->data; - int err; - long lerr; - int what; - char * str; - SSL_METHOD *req_method; - SSL_SESSION *ssl_sessionid=NULL; - ASN1_TIME *certdate; + SSL_METHOD_QUAL SSL_METHOD *req_method=NULL; + void *ssl_sessionid=NULL; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - /* mark this is being ssl enabled from here on out. */ - connssl->use = TRUE; + curlassert(ssl_connect_1 == connssl->connecting_state); - if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { - /* Make funny stuff to get random input */ - random_the_seed(data); - - ssl_seeded = TRUE; - } + /* 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) { @@ -1133,6 +1292,8 @@ Curl_SSLConnect(struct connectdata *conn, break; } + if (connssl->ctx) + SSL_CTX_free(connssl->ctx); connssl->ctx = SSL_CTX_new(req_method); if(!connssl->ctx) { @@ -1141,17 +1302,24 @@ Curl_SSLConnect(struct connectdata *conn, } #ifdef SSL_CTRL_SET_MSG_CALLBACK - if (data->set.fdebug) { - SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK, - ssl_tls_trace); - SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn); + 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 - ll the work-arounds and that "It is usually safe to use SSL_OP_ALL to + 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." @@ -1199,7 +1367,7 @@ Curl_SSLConnect(struct connectdata *conn, " CAfile: %s\n CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); - return CURLE_SSL_CACERT; + return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification @@ -1237,175 +1405,210 @@ Curl_SSLConnect(struct connectdata *conn, } /* 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; - if(!conn->bits.reuse) { - /* We're not re-using a connection, check if there's a cached ID we - can/should use here! */ - if(!Get_SSL_Session(conn, &ssl_sessionid)) { - /* we got a session id, use it! */ - SSL_set_session(connssl->handle, ssl_sessionid); - /* Informational message */ - infof (data, "SSL re-using session ID\n"); + /* 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 */ - SSL_set_fd(connssl->handle, sockfd); - - while(1) { - fd_set writefd; - fd_set readfd; - struct timeval interval; - long timeout_ms; - - /* Find out if any timeout is set. If not, use 300 seconds. - Otherwise, figure out the most strict timeout of the two possible one - and then how much time that has elapsed to know how much time we - allow for the connect call */ - if(data->set.timeout || data->set.connecttimeout) { - long has_passed; - - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); - - /* get the most strict timeout of the ones converted to milliseconds */ - if(data->set.timeout && - (data->set.timeout>data->set.connecttimeout)) - timeout_ms = data->set.timeout*1000; - else - timeout_ms = data->set.connecttimeout*1000; + 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; + } - /* subtract the passed time */ - timeout_ms -= has_passed; + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; - } - } +static CURLcode +Curl_ossl_connect_step2(struct connectdata *conn, + int sockindex, long *timeout_ms) +{ + struct SessionHandle *data = conn->data; + int err; + long has_passed; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + curlassert(ssl_connect_2 == connssl->connecting_state + || ssl_connect_2_reading == connssl->connecting_state + || ssl_connect_2_writing == connssl->connecting_state); + + /* Find out if any timeout is set. If not, use 300 seconds. + Otherwise, figure out the most strict timeout of the two possible one + and then how much time that has elapsed to know how much time we + allow for the connect call */ + if(data->set.timeout && data->set.connecttimeout) { + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout<data->set.connecttimeout) + *timeout_ms = data->set.timeout*1000; else - /* no particular time-out has been set */ - timeout_ms= DEFAULT_CONNECT_TIMEOUT; + *timeout_ms = data->set.connecttimeout*1000; + } + else if(data->set.timeout) + *timeout_ms = data->set.timeout*1000; + else if(data->set.connecttimeout) + *timeout_ms = data->set.connecttimeout*1000; + else + /* no particular time-out has been set */ + *timeout_ms= DEFAULT_CONNECT_TIMEOUT; + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - FD_ZERO(&writefd); - FD_ZERO(&readfd); + /* subtract the passed time */ + *timeout_ms -= has_passed; - err = SSL_connect(connssl->handle); + if(*timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } - /* 1 is fine - 0 is "not successful but was shut down controlled" - <0 is "handshake was not successful, because a fatal error occurred" */ - if(1 != err) { - int detail = SSL_get_error(connssl->handle, err); + err = SSL_connect(connssl->handle); - if(SSL_ERROR_WANT_READ == detail) - FD_SET(sockfd, &readfd); - else if(SSL_ERROR_WANT_WRITE == detail) - FD_SET(sockfd, &writefd); - else { - /* untreated error */ - unsigned long errdetail; - char error_buffer[120]; /* OpenSSL documents that this must be at least - 120 bytes long. */ - CURLcode rc; - const char *cert_problem = NULL; - - errdetail = ERR_get_error(); /* Gets the earliest error code from the - thread's error queue and removes the - entry. */ - - switch(errdetail) { - case 0x1407E086: - /* 1407E086: - SSL routines: - SSL2_SET_CERTIFICATE: - certificate verify failed */ - /* fall-through */ - case 0x14090086: - /* 14090086: - SSL routines: - SSL3_GET_SERVER_CERTIFICATE: - certificate verify failed */ - cert_problem = "SSL certificate problem, verify that the CA cert is" - " OK. Details:\n"; - rc = CURLE_SSL_CACERT; - break; - default: - rc = CURLE_SSL_CONNECT_ERROR; - break; - } + /* 1 is fine + 0 is "not successful but was shut down controlled" + <0 is "handshake was not successful, because a fatal error occurred" */ + if(1 != err) { + int detail = SSL_get_error(connssl->handle, err); - /* detail is already set to the SSL error above */ + if(SSL_ERROR_WANT_READ == detail) { + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_OK; + } + else if(SSL_ERROR_WANT_WRITE == detail) { + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_OK; + } + else { + /* untreated error */ + unsigned long errdetail; + char error_buffer[256]; /* OpenSSL documents that this must be at least + 256 bytes long. */ + CURLcode rc; + const char *cert_problem = NULL; + + connssl->connecting_state = ssl_connect_2; /* the connection failed, + we're not waiting for + anything else. */ + + errdetail = ERR_get_error(); /* Gets the earliest error code from the + thread's error queue and removes the + entry. */ + + switch(errdetail) { + case 0x1407E086: + /* 1407E086: + SSL routines: + SSL2_SET_CERTIFICATE: + certificate verify failed */ + /* fall-through */ + case 0x14090086: + /* 14090086: + SSL routines: + SSL3_GET_SERVER_CERTIFICATE: + certificate verify failed */ + cert_problem = "SSL certificate problem, verify that the CA cert is" + " OK. Details:\n"; + rc = CURLE_SSL_CACERT; + break; + default: + rc = CURLE_SSL_CONNECT_ERROR; + break; + } - /* If we e.g. use SSLv2 request-method and the server doesn't like us - * (RST connection etc.), OpenSSL gives no explanation whatsoever and - * the SO_ERROR is also lost. - */ - if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { - failf(data, "Unknown SSL protocol error in connection to %s:%d ", - conn->host.name, conn->port); - return rc; - } - /* Could be a CERT problem */ + /* detail is already set to the SSL error above */ -#ifdef HAVE_ERR_ERROR_STRING_N - /* OpenSSL 0.9.6 and later has a function named - ERRO_error_string_n() that takes the size of the buffer as a - third argument */ - ERR_error_string_n(errdetail, error_buffer, sizeof(error_buffer)); -#else - ERR_error_string(errdetail, error_buffer); -#endif - failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + /* If we e.g. use SSLv2 request-method and the server doesn't like us + * (RST connection etc.), OpenSSL gives no explanation whatsoever and + * the SO_ERROR is also lost. + */ + if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { + failf(data, "Unknown SSL protocol error in connection to %s:%d ", + conn->host.name, conn->port); return rc; } + /* Could be a CERT problem */ + + SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + return rc; } - else - /* we have been connected fine, get out of the connect loop */ - break; + } + else { + /* we have been connected fine, we're not waiting for anything else. */ + connssl->connecting_state = ssl_connect_3; - interval.tv_sec = (int)(timeout_ms/1000); - timeout_ms -= interval.tv_sec*1000; + /* Informational message */ + infof (data, "SSL connection using %s\n", + SSL_get_cipher(connssl->handle)); - interval.tv_usec = timeout_ms*1000; + return CURLE_OK; + } +} - while(1) { - what = select(sockfd+1, &readfd, &writefd, NULL, &interval); - if(what > 0) - /* reabable or writable, go loop in the outer loop */ - break; - else if(0 == what) { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - else { -#if !defined(WIN32) && defined(EINTR) - /* For platforms without EINTR all errnos are bad */ - if (errno == EINTR) - continue; /* retry the select() */ -#endif - /* anything other than the unimportant EINTR is fatally bad */ - failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); - return CURLE_SSL_CONNECT_ERROR; - } - } /* while()-loop for the select() */ - } /* while()-loop for the SSL_connect() */ +static CURLcode +Curl_ossl_connect_step3(struct connectdata *conn, + int sockindex) +{ + CURLcode retcode = CURLE_OK; + char * str; + long lerr; + ASN1_TIME *certdate; + void *ssl_sessionid=NULL; + struct SessionHandle *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - /* Informational message */ - infof (data, "SSL connection using %s\n", - SSL_get_cipher(connssl->handle)); + curlassert(ssl_connect_3 == connssl->connecting_state); - if(!ssl_sessionid) { + if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ - Store_SSL_Session(conn, connssl); + SSL_SESSION *our_ssl_sessionid; +#ifdef HAVE_SSL_GET1_SESSION + our_ssl_sessionid = SSL_get1_session(connssl->handle); + + /* SSL_get1_session() will increment the reference + count and the session will stay in memory until explicitly freed with + SSL_SESSION_free(3), regardless of its state. + This function was introduced in openssl 0.9.5a. */ +#else + our_ssl_sessionid = SSL_get_session(connssl->handle); + + /* if SSL_get1_session() is unavailable, use SSL_get_session(). + This is an inferior option because the session can be flushed + at any time by openssl. It is included only so curl compiles + under versions of openssl < 0.9.5a. + + WARNING: How curl behaves if it's session is flushed is + untested. + */ +#endif + retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, + 0 /* unknown size */); + if(retcode) { + failf(data, "failed to store ssl session"); + return retcode; + } } @@ -1427,6 +1630,7 @@ Curl_SSLConnect(struct connectdata *conn, if(!str) { 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", str); @@ -1442,6 +1646,7 @@ Curl_SSLConnect(struct connectdata *conn, retcode = verifyhost(conn, connssl->server_cert); if(retcode) { X509_free(connssl->server_cert); + connssl->server_cert = NULL; return retcode; } } @@ -1472,16 +1677,269 @@ Curl_SSLConnect(struct connectdata *conn, else infof(data, "SSL certificate verify result: %s (%ld)," " continuing anyway.\n", - X509_verify_cert_error_string(err), lerr); + X509_verify_cert_error_string(lerr), lerr); } else infof(data, "SSL certificate verify ok.\n"); } X509_free(connssl->server_cert); -#else /* USE_SSLEAY */ - (void)conn; - (void)sockindex; -#endif + connssl->server_cert = NULL; + connssl->connecting_state = ssl_connect_done; return retcode; } + +static CURLcode +Curl_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) { + retcode = Curl_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) { + + /* 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_select(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 on SSL socket, errno: %d", Curl_sockerrno()); + return CURLE_SSL_CONNECT_ERROR; + } + } /* while()-loop for the select() */ + } + + /* get the timeout from step2 to avoid computing it twice. */ + retcode = Curl_ossl_connect_step2(conn, sockindex, &timeout_ms); + if (retcode) + return retcode; + + } /* repeat step2 until all transactions are done. */ + + + if (ssl_connect_3==connssl->connecting_state) { + retcode = Curl_ossl_connect_step3(conn, sockindex); + if (retcode) + return retcode; + } + + if (ssl_connect_done==connssl->connecting_state) { + *done = TRUE; + } + else { + *done = FALSE; + } + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +CURLcode +Curl_ossl_connect_nonblocking(struct connectdata *conn, + int sockindex, + bool *done) +{ + return Curl_ossl_connect_common(conn, sockindex, TRUE, done); +} + +CURLcode +Curl_ossl_connect(struct connectdata *conn, + int sockindex) +{ + CURLcode retcode; + bool done = FALSE; + + retcode = Curl_ossl_connect_common(conn, sockindex, FALSE, &done); + if (retcode) + return retcode; + + curlassert(done); + + return CURLE_OK; +} + +/* return number of sent (non-SSL) bytes */ +ssize_t Curl_ossl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ + /* SSL_write() is said to return 'int' while write() and send() returns + 'size_t' */ + int err; + char error_buffer[120]; /* OpenSSL documents that this must be at least 120 + bytes long. */ + unsigned long sslerror; + int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len); + + if(rc < 0) { + err = SSL_get_error(conn->ssl[sockindex].handle, rc); + + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* The operation did not complete; the same TLS/SSL I/O function + should be called again later. This is basicly an EWOULDBLOCK + equivalent. */ + return 0; + case SSL_ERROR_SYSCALL: + failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n", + Curl_sockerrno()); + return -1; + case SSL_ERROR_SSL: + /* A failure in the SSL library occurred, usually a protocol error. + The OpenSSL error queue contains more information on the error. */ + sslerror = ERR_get_error(); + failf(conn->data, "SSL_write() error: %s\n", + ERR_error_string(sslerror, error_buffer)); + return -1; + } + /* a true error */ + failf(conn->data, "SSL_write() return error %d\n", err); + return -1; + } + return (ssize_t)rc; /* number of bytes */ +} + +/* + * If the read would block we return -1 and set 'wouldblock' to TRUE. + * Otherwise we return the amount of data read. Other errors should return -1 + * and set 'wouldblock' to FALSE. + */ +ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock) +{ + char error_buffer[120]; /* OpenSSL documents that this must be at + least 120 bytes long. */ + unsigned long sslerror; + ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, + (int)buffersize); + *wouldblock = FALSE; + if(nread < 0) { + /* failed SSL_read */ + int err = SSL_get_error(conn->ssl[num].handle, (int)nread); + + switch(err) { + case SSL_ERROR_NONE: /* this is not an error */ + case SSL_ERROR_ZERO_RETURN: /* no more data */ + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there's data pending, re-invoke SSL_read() */ + *wouldblock = TRUE; + return -1; /* basically EWOULDBLOCK */ + default: + /* openssl/ssl.h says "look at error stack/return value/errno" */ + sslerror = ERR_get_error(); + failf(conn->data, "SSL read: %s, errno %d", + ERR_error_string(sslerror, error_buffer), + Curl_sockerrno() ); + return -1; + } + } + return nread; +} + +size_t Curl_ossl_version(char *buffer, size_t size) +{ +#ifdef YASSL_VERSION + /* yassl provides an OpenSSL API compatiblity layer so it looks identical + to OpenSSL in all other aspects */ + return snprintf(buffer, size, " yassl/%s", YASSL_VERSION); +#else /* YASSL_VERSION */ + +#if (SSLEAY_VERSION_NUMBER >= 0x905000) + { + char sub[2]; + unsigned long ssleay_value; + sub[1]='\0'; + ssleay_value=SSLeay(); + if(ssleay_value < 0x906000) { + ssleay_value=SSLEAY_VERSION_NUMBER; + sub[0]='\0'; + } + else { + if(ssleay_value&0xff0) { + sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1); + } + else + sub[0]='\0'; + } + + return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx%s", + (ssleay_value>>28)&0xf, + (ssleay_value>>20)&0xff, + (ssleay_value>>12)&0xff, + sub); + } + +#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ + +#if (SSLEAY_VERSION_NUMBER >= 0x900000) + return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx", + (SSLEAY_VERSION_NUMBER>>28)&0xff, + (SSLEAY_VERSION_NUMBER>>20)&0xff, + (SSLEAY_VERSION_NUMBER>>12)&0xf); + +#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ + { + char sub[2]; + sub[1]='\0'; + if(SSLEAY_VERSION_NUMBER&0x0f) { + sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; + } + else + sub[0]='\0'; + + return snprintf(buffer, size, " SSL/%x.%x.%x%s", + (SSLEAY_VERSION_NUMBER>>12)&0xff, + (SSLEAY_VERSION_NUMBER>>8)&0xf, + (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); + } +#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ +#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ + +#endif /* YASSL_VERSION */ +} +#endif /* USE_SSLEAY */ diff --git a/Utilities/cmcurl/ssluse.h b/Utilities/cmcurl/ssluse.h index 886d2ca..5bb7090 100644 --- a/Utilities/cmcurl/ssluse.h +++ b/Utilities/cmcurl/ssluse.h @@ -1,18 +1,18 @@ #ifndef __SSLUSE_H #define __SSLUSE_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -22,17 +22,50 @@ * * $Id$ ***************************************************************************/ + +/* + * This header should only be needed to get included by sslgen.c and ssluse.c + */ + #include "urldata.h" -CURLcode Curl_SSLConnect(struct connectdata *conn, int sockindex); +CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex); +CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn, + int sockindex, + bool *done); +void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */ +/* tell OpenSSL to close down all open information regarding connections (and + thus session ID caching etc) */ +int Curl_ossl_close_all(struct SessionHandle *data); +/* Sets an OpenSSL engine */ +CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine); + +/* function provided for the generic SSL-layer, called when a session id + should be freed */ +void Curl_ossl_session_free(void *ptr); + +/* Sets engine as default for all SSL operations */ +CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data); + +/* Build list of OpenSSL engines */ +struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data); + +int Curl_ossl_init(void); +void Curl_ossl_cleanup(void); + +ssize_t Curl_ossl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len); +ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock); -void Curl_SSL_init(void); /* Global SSL init */ -void Curl_SSL_cleanup(void); /* Global SSL cleanup */ +size_t Curl_ossl_version(char *buffer, size_t size); +int Curl_ossl_check_cxn(struct connectdata *cxn); +int Curl_ossl_seed(struct SessionHandle *data); -/* init the SSL session ID cache */ -CURLcode Curl_SSL_InitSessions(struct SessionHandle *, long); -void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */ +int Curl_ossl_shutdown(struct connectdata *conn, int sockindex); -/* tell the SSL stuff to close down all open information regarding - connections (and thus session ID caching etc) */ -int Curl_SSL_Close_All(struct SessionHandle *data); #endif diff --git a/Utilities/cmcurl/strdup.c b/Utilities/cmcurl/strdup.c new file mode 100644 index 0000000..e16e08a --- /dev/null +++ b/Utilities/cmcurl/strdup.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" +#include "strdup.h" + +#ifndef HAVE_STRDUP +char *curlx_strdup(const char *str) +{ + int len; + char *newstr; + + if (!str) + return (char *)NULL; + + len = strlen(str); + newstr = (char *) malloc((len+1)*sizeof(char)); + if (!newstr) + return (char *)NULL; + + strcpy(newstr,str); + + return newstr; + +} +#endif diff --git a/Utilities/cmcurl/strdup.h b/Utilities/cmcurl/strdup.h new file mode 100644 index 0000000..3206db3 --- /dev/null +++ b/Utilities/cmcurl/strdup.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#ifndef _CURL_STRDUP_H +#define _CURL_STRDUP_H + +#include "setup.h" + +#ifndef HAVE_STRDUP +extern char *curlx_strdup(const char *str); +#endif + +#endif + diff --git a/Utilities/cmcurl/strequal.c b/Utilities/cmcurl/strequal.c index 66e2e4f..1bff429 100644 --- a/Utilities/cmcurl/strequal.c +++ b/Utilities/cmcurl/strequal.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,7 +28,7 @@ #include "strequal.h" -#ifdef HAVE_STRCASECMP +#if defined(HAVE_STRCASECMP) && defined(__STRICT_ANSI__) /* this is for "-ansi -Wall -pedantic" to stop complaining! */ extern int (strcasecmp)(const char *s1, const char *s2); extern int (strncasecmp)(const char *s1, const char *s2, size_t n); @@ -38,10 +38,10 @@ int curl_strequal(const char *first, const char *second) { #if defined(HAVE_STRCASECMP) return !(strcasecmp)(first, second); -#elif defined(HAVE_STRICMP) - return !(stricmp)(first, second); #elif defined(HAVE_STRCMPI) return !(strcmpi)(first, second); +#elif defined(HAVE_STRICMP) + return !(stricmp)(first, second); #else while (*first && *second) { if (toupper(*first) != toupper(*second)) { @@ -58,10 +58,10 @@ int curl_strnequal(const char *first, const char *second, size_t max) { #if defined(HAVE_STRCASECMP) return !strncasecmp(first, second, max); -#elif defined(HAVE_STRICMP) - return !strnicmp(first, second, max); #elif defined(HAVE_STRCMPI) return !strncmpi(first, second, max); +#elif defined(HAVE_STRICMP) + return !strnicmp(first, second, max); #else while (*first && *second && max) { if (toupper(*first) != toupper(*second)) { diff --git a/Utilities/cmcurl/strequal.h b/Utilities/cmcurl/strequal.h index 5865211..b3caa73 100644 --- a/Utilities/cmcurl/strequal.h +++ b/Utilities/cmcurl/strequal.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,11 +23,7 @@ * $Id$ ***************************************************************************/ -/* - * These two actually are public functions. - */ -int curl_strequal(const char *first, const char *second); -int curl_strnequal(const char *first, const char *second, size_t max); +#include <curl/curl.h> #define strequal(a,b) curl_strequal(a,b) #define strnequal(a,b,c) curl_strnequal(a,b,c) @@ -41,7 +37,7 @@ char *Curl_strcasestr(const char *haystack, const char *needle); #ifndef HAVE_STRLCAT #define strlcat(x,y,z) Curl_strlcat(x,y,z) -size_t Curl_strlcat(char *dst, const char *src, size_t siz); #endif +size_t strlcat(char *dst, const char *src, size_t siz); #endif diff --git a/Utilities/cmcurl/strerror.c b/Utilities/cmcurl/strerror.c index ea5bcd8..6304fe8 100644 --- a/Utilities/cmcurl/strerror.c +++ b/Utilities/cmcurl/strerror.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2004 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -18,21 +18,32 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * + * $Id$ ***************************************************************************/ #include "setup.h" +#ifdef HAVE_STRERROR_R +#if !defined(HAVE_POSIX_STRERROR_R) && !defined(HAVE_GLIBC_STRERROR_R) +#error "you MUST have either POSIX or glibc strerror_r if strerror_r is found" +#endif /* !POSIX && !glibc */ +#endif /* HAVE_STRERROR_R */ + #include <curl/curl.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#ifdef USE_LIBIDN +#include <idna.h> +#endif + #include "strerror.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> -#ifdef HAVE_NO_STRERROR_R_DECL +#if defined(HAVE_STRERROR_R) && defined(HAVE_NO_STRERROR_R_DECL) #ifdef HAVE_POSIX_STRERROR_R /* seen on AIX 5100-02 gcc 2.9 */ extern int strerror_r(int errnum, char *strerrbuf, size_t buflen); @@ -44,6 +55,7 @@ extern char *strerror_r(int errnum, char *buf, size_t buflen); const char * curl_easy_strerror(CURLcode error) { +#ifndef CURL_DISABLE_VERBOSE_STRINGS switch (error) { case CURLE_OK: return "no error"; @@ -58,13 +70,13 @@ curl_easy_strerror(CURLcode error) return "URL using bad/illegal format or missing URL"; case CURLE_COULDNT_RESOLVE_PROXY: - return "couldnt resolve proxy"; + return "couldn't resolve proxy name"; case CURLE_COULDNT_RESOLVE_HOST: - return "couldnt resolve host"; + return "couldn't resolve host name"; case CURLE_COULDNT_CONNECT: - return "couldn't connect"; + return "couldn't connect to server"; case CURLE_FTP_WEIRD_SERVER_REPLY: return "FTP: weird server reply"; @@ -72,9 +84,6 @@ curl_easy_strerror(CURLcode error) case CURLE_FTP_ACCESS_DENIED: return "FTP: access denied"; - case CURLE_FTP_USER_PASSWORD_INCORRECT: - return "FTP: user and/or password incorrect"; - case CURLE_FTP_WEIRD_PASS_REPLY: return "FTP: unknown PASS reply"; @@ -121,7 +130,11 @@ curl_easy_strerror(CURLcode error) return "failed to open/read local data from file/application"; case CURLE_OUT_OF_MEMORY: +#ifdef CURL_DOES_CONVERSIONS + return "conversion failed -or- out of memory"; +#else return "out of memory"; +#endif /* CURL_DOES_CONVERSIONS */ case CURLE_OPERATION_TIMEOUTED: return "a timeout was reached"; @@ -147,8 +160,8 @@ curl_easy_strerror(CURLcode error) case CURLE_SSL_CONNECT_ERROR: return "SSL connect error"; - case CURLE_FTP_BAD_DOWNLOAD_RESUME: - return "couldn't resume FTP download"; + case CURLE_BAD_DOWNLOAD_RESUME: + return "couldn't resume download"; case CURLE_FILE_COULDNT_READ_FILE: return "couldn't read a file:// file"; @@ -195,6 +208,9 @@ curl_easy_strerror(CURLcode error) case CURLE_SSL_ENGINE_SETFAILED: return "can not set SSL crypto engine as default"; + case CURLE_SSL_ENGINE_INITFAILED: + return "failed to initialise SSL crypto engine"; + case CURLE_SEND_ERROR: return "failed sending data to the peer"; @@ -211,6 +227,9 @@ curl_easy_strerror(CURLcode error) return "couldn't use specified SSL cipher"; case CURLE_SSL_CACERT: + return "peer certificate cannot be authenticated with known CA certificates"; + + case CURLE_SSL_CACERT_BADFILE: return "problem with the SSL CA cert (path? access rights?)"; case CURLE_BAD_CONTENT_ENCODING: @@ -225,11 +244,55 @@ curl_easy_strerror(CURLcode error) case CURLE_FTP_SSL_FAILED: return "Requested FTP SSL level failed"; - case CURLE_URL_MALFORMAT_USER: /* not used by current libcurl */ - case CURLE_MALFORMAT_USER: /* not used by current libcurl */ - case CURLE_BAD_CALLING_ORDER: /* not used by current libcurl */ - case CURLE_BAD_PASSWORD_ENTERED:/* not used by current libcurl */ - case CURLE_OBSOLETE: /* not used by current libcurl */ + case CURLE_SSL_SHUTDOWN_FAILED: + return "Failed to shut down the SSL connection"; + + case CURLE_SEND_FAIL_REWIND: + return "Send failed since rewinding of the data stream failed"; + + case CURLE_LOGIN_DENIED: + return "FTP: login denied"; + + case CURLE_TFTP_NOTFOUND: + return "TFTP: File Not Found"; + + case CURLE_TFTP_PERM: + return "TFTP: Access Violation"; + + case CURLE_TFTP_DISKFULL: + return "TFTP: Disk full or allocation exceeded"; + + case CURLE_TFTP_ILLEGAL: + return "TFTP: Illegal operation"; + + case CURLE_TFTP_UNKNOWNID: + return "TFTP: Unknown transfer ID"; + + case CURLE_TFTP_EXISTS: + return "TFTP: File already exists"; + + case CURLE_TFTP_NOSUCHUSER: + return "TFTP: No such user"; + + case CURLE_CONV_FAILED: + return "conversion failed"; + + case CURLE_CONV_REQD: + return "caller must register CURLOPT_CONV_ callback options"; + + case CURLE_REMOTE_FILE_NOT_FOUND: + return "Remote file not found"; + + case CURLE_SSH: + return "Error in the SSH layer"; + + /* error codes not used by current libcurl */ + case CURLE_URL_MALFORMAT_USER: + case CURLE_FTP_USER_PASSWORD_INCORRECT: + case CURLE_MALFORMAT_USER: + case CURLE_BAD_CALLING_ORDER: + case CURLE_BAD_PASSWORD_ENTERED: + case CURLE_OBSOLETE: case CURL_LAST: break; } @@ -248,18 +311,25 @@ curl_easy_strerror(CURLcode error) * is why it is here, and not at the start of the switch. */ return "unknown error"; +#else + if (error == CURLE_OK) + return "no error"; + else + return "error"; +#endif } const char * curl_multi_strerror(CURLMcode error) { +#ifndef CURL_DISABLE_VERBOSE_STRINGS switch (error) { case CURLM_CALL_MULTI_PERFORM: return "please call curl_multi_perform() soon"; - + case CURLM_OK: return "no error"; - + case CURLM_BAD_HANDLE: return "invalid multi handle"; @@ -272,16 +342,29 @@ curl_multi_strerror(CURLMcode error) case CURLM_INTERNAL_ERROR: return "internal error"; + case CURLM_BAD_SOCKET: + return "invalid socket argument"; + + case CURLM_UNKNOWN_OPTION: + return "unknown option"; + case CURLM_LAST: break; } return "unknown error"; +#else + if (error == CURLM_OK) + return "no error"; + else + return "error"; +#endif } const char * curl_share_strerror(CURLSHcode error) { +#ifndef CURL_DISABLE_VERBOSE_STRINGS switch (error) { case CURLSHE_OK: return "no error"; @@ -303,17 +386,24 @@ curl_share_strerror(CURLSHcode error) } return "CURLSH unknown"; +#else + if (error == CURLSHE_OK) + return "no error"; + else + return "error"; +#endif } -#if defined(WIN32) && !defined(__CYGWIN__) +#ifdef USE_WINSOCK /* This function handles most / all (?) Winsock errors cURL is able to produce. */ static const char * get_winsock_error (int err, char *buf, size_t len) { - char *p; + const char *p; +#ifndef CURL_DISABLE_VERBOSE_STRINGS switch (err) { case WSAEINTR: p = "Call interrupted."; @@ -442,16 +532,17 @@ get_winsock_error (int err, char *buf, size_t len) case WSAEREMOTE: p = "Remote error"; break; +#ifdef WSAEDISCON /* missing in SalfordC! */ case WSAEDISCON: p = "Disconnected"; break; - +#endif /* Extended Winsock errors */ case WSASYSNOTREADY: p = "Winsock library is not ready"; break; case WSANOTINITIALISED: - p = "Winsock library not initalised"; + p = "Winsock library not initialised"; break; case WSAVERNOTSUPPORTED: p = "Winsock version not supported."; @@ -481,11 +572,17 @@ get_winsock_error (int err, char *buf, size_t len) default: return NULL; } +#else + if (error == CURLE_OK) + return NULL; + else + p = "error"; +#endif strncpy (buf, p, len); buf [len-1] = '\0'; return buf; } -#endif /* WIN32 && !__CYGWIN__ */ +#endif /* USE_WINSOCK */ /* * Our thread-safe and smart strerror() replacement. @@ -510,24 +607,38 @@ const char *Curl_strerror(struct connectdata *conn, int err) max = sizeof(conn->syserr_buf)-1; *buf = '\0'; -#if defined(WIN32) && !defined(__CYGWIN__) +#ifdef USE_WINSOCK + +#ifdef _WIN32_WCE + buf[0]=0; + { + wchar_t wbuf[256]; + + 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, max, NULL)) + 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); } -#else /* not native Windows coming up */ - +#endif +#else /* not USE_WINSOCK coming up */ + /* These should be atomic and hopefully thread-safe */ #ifdef HAVE_STRERROR_R /* There are two different APIs for strerror_r(). The POSIX and the GLIBC versions. */ #ifdef HAVE_POSIX_STRERROR_R - strerror_r(err, buf, max); + strerror_r(err, buf, max); /* this may set errno to ERANGE if insufficient storage was supplied via 'strerrbuf' and 'buflen' to contain the generated message string, or EINVAL if the value of 'errnum' is not a valid error number.*/ @@ -538,25 +649,17 @@ const char *Curl_strerror(struct connectdata *conn, int err) char *msg = strerror_r(err, buffer, sizeof(buffer)); /* this version of 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 ( !msg ) - { - msg = "Unknown System Error"; - } - strncpy(buf, msg, max); + so we must copy that string unconditionally (if non-NULL) */ + if(msg) + strncpy(buf, msg, max); + else + snprintf(buf, max, "Unknown error %d", err); } #endif /* end of HAVE_GLIBC_STRERROR_R */ #else /* HAVE_STRERROR_R */ - { - char *msg = strerror(err); - if ( !msg ) - { - msg = "Unknown System Error"; - } - strncpy(buf, msg, max); - } + strncpy(buf, strerror(err), max); #endif /* end of HAVE_STRERROR_R */ -#endif /* end of ! Windows */ +#endif /* end of ! USE_WINSOCK */ buf[max] = '\0'; /* make sure the string is zero terminated */ @@ -567,3 +670,79 @@ const char *Curl_strerror(struct connectdata *conn, int err) *p = '\0'; return buf; } + +#ifdef USE_LIBIDN +/* + * Return error-string for libidn status as returned from idna_to_ascii_lz(). + */ +const char *Curl_idn_strerror (struct connectdata *conn, int err) +{ +#ifdef HAVE_IDNA_STRERROR + (void)conn; + return idna_strerror((Idna_rc) err); +#else + const char *str; + char *buf; + size_t max; + + curlassert(conn); + + buf = conn->syserr_buf; + max = sizeof(conn->syserr_buf)-1; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch ((Idna_rc)err) { + case IDNA_SUCCESS: + str = "No error"; + break; + case IDNA_STRINGPREP_ERROR: + str = "Error in string preparation"; + break; + case IDNA_PUNYCODE_ERROR: + str = "Error in Punycode operation"; + break; + case IDNA_CONTAINS_NON_LDH: + str = "Illegal ASCII characters"; + break; + case IDNA_CONTAINS_MINUS: + str = "Contains minus"; + break; + case IDNA_INVALID_LENGTH: + str = "Invalid output length"; + break; + case IDNA_NO_ACE_PREFIX: + str = "No ACE prefix (\"xn--\")"; + break; + case IDNA_ROUNDTRIP_VERIFY_ERROR: + str = "Roundtrip verify error"; + break; + case IDNA_CONTAINS_ACE_PREFIX: + str = "Already have ACE prefix (\"xn--\")"; + break; + case IDNA_ICONV_ERROR: + str = "Locale conversion failed"; + break; + case IDNA_MALLOC_ERROR: + str = "Allocation failed"; + break; + case IDNA_DLOPEN_ERROR: + str = "dlopen() error"; + break; + default: + snprintf(buf, max, "error %d", (int)err); + str = NULL; + break; + } +#else + if ((Idna_rc)err == IDNA_SUCCESS) + str = "No error"; + else + str = "error"; +#endif + if (str) + strncpy(buf, str, max); + buf[max] = '\0'; + return (buf); +#endif +} +#endif /* USE_LIBIDN */ diff --git a/Utilities/cmcurl/strerror.h b/Utilities/cmcurl/strerror.h index 7d68723..b280504 100644 --- a/Utilities/cmcurl/strerror.h +++ b/Utilities/cmcurl/strerror.h @@ -1,10 +1,10 @@ #ifndef __CURL_STRERROR_H #define __CURL_STRERROR_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. @@ -12,7 +12,7 @@ * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -27,4 +27,8 @@ const char *Curl_strerror (struct connectdata *conn, int err); +#ifdef USE_LIBIDN +const char *Curl_idn_strerror (struct connectdata *conn, int err); +#endif + #endif diff --git a/Utilities/cmcurl/strtoofft.c b/Utilities/cmcurl/strtoofft.c index e2b02c4..3ab1bfd 100644 --- a/Utilities/cmcurl/strtoofft.c +++ b/Utilities/cmcurl/strtoofft.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,6 +24,14 @@ #include "setup.h" #include "strtoofft.h" +/* + * NOTE: + * + * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we + * could use in case strtoll() doesn't exist... See + * http://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html + */ + #ifdef NEED_CURL_STRTOLL #include <stdlib.h> #include <ctype.h> @@ -47,7 +55,7 @@ curlx_strtoll(const char *nptr, char **endptr, int base) /* Skip leading whitespace. */ end = (char *)nptr; - while (isspace((int)end[0])) { + while (ISSPACE(end[0])) { end++; } @@ -111,17 +119,10 @@ curlx_strtoll(const char *nptr, char **endptr, int base) } } else { -#ifdef HAVE_LONG_LONG_CONSTANT - if (is_negative) - value = 0x8000000000000000LL; - else - value = 0x7FFFFFFFFFFFFFFFLL; -#else if (is_negative) - value = 0x8000000000000000L; + value = CURL_LLONG_MIN; else - value = 0x7FFFFFFFFFFFFFFFL; -#endif + value = CURL_LLONG_MAX; errno = ERANGE; } diff --git a/Utilities/cmcurl/strtoofft.h b/Utilities/cmcurl/strtoofft.h index 4c5d265..e27b432 100644 --- a/Utilities/cmcurl/strtoofft.h +++ b/Utilities/cmcurl/strtoofft.h @@ -1,10 +1,10 @@ #ifndef _CURL_STRTOOFFT_H #define _CURL_STRTOOFFT_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. @@ -12,7 +12,7 @@ * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -38,7 +38,7 @@ * not, should try to emulate its functionality. At any rate, we define * 'strtoofft' such that it can be used to work with curl_off_t's regardless. */ -#if SIZEOF_CURL_OFF_T > 4 +#if (SIZEOF_CURL_OFF_T > 4) && (SIZEOF_LONG < 8) #if HAVE_STRTOLL #define curlx_strtoofft strtoll #else /* HAVE_STRTOLL */ @@ -53,10 +53,21 @@ curl_off_t curlx_strtoll(const char *nptr, char **endptr, int base); #endif /* MSVC7 or later */ #endif /* HAVE_STRTOLL */ -#else /* SIZEOF_CURL_OFF_T > 4 */ -/* simply use strtol() to get 32bit numbers */ +#else /* (SIZEOF_CURL_OFF_T > 4) && (SIZEOF_LONG < 8) */ +/* simply use strtol() to get numbers, either 32 or 64 bit */ #define curlx_strtoofft strtol #endif +#if defined(_MSC_VER) || defined(__WATCOMC__) +#define CURL_LLONG_MIN 0x8000000000000000i64 +#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFi64 +#elif defined(HAVE_LL) +#define CURL_LLONG_MIN 0x8000000000000000LL +#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFLL +#else +#define CURL_LLONG_MIN 0x8000000000000000L +#define CURL_LLONG_MAX 0x7FFFFFFFFFFFFFFFL +#endif + #endif diff --git a/Utilities/cmcurl/telnet.c b/Utilities/cmcurl/telnet.c index 98a1adf..97d22b7 100644 --- a/Utilities/cmcurl/telnet.c +++ b/Utilities/cmcurl/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,10 +30,12 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> - -#include <errno.h> +#endif #if defined(WIN32) #include <time.h> @@ -43,7 +45,9 @@ #include <sys/socket.h> #endif #include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -61,10 +65,6 @@ #include <sys/param.h> #endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - #endif @@ -73,6 +73,7 @@ #include "transfer.h" #include "sendf.h" #include "telnet.h" +#include "connect.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -81,7 +82,8 @@ #define TELCMDS #include "arpa_telnet.h" -#include "curl_memory.h" +#include "memory.h" +#include "select.h" /* The last #include file should be: */ #include "memdebug.h" @@ -100,7 +102,7 @@ #define CURL_SB_EOF(x) (x->subpointer >= x->subend) #define CURL_SB_LEN(x) (x->subend - x->subpointer) -#ifdef WIN32 +#ifdef USE_WINSOCK typedef FARPROC WSOCK2_FUNC; static CURLcode check_wsock2 ( struct SessionHandle *data ); #endif @@ -163,13 +165,13 @@ struct TELNET { struct curl_slist *telnet_vars; /* Environment variables */ /* suboptions */ - char subbuffer[SUBBUFSIZE]; - char *subpointer, *subend; /* buffer for sub-options */ + unsigned char subbuffer[SUBBUFSIZE]; + unsigned char *subpointer, *subend; /* buffer for sub-options */ TelnetReceive telrcv_state; }; -#ifdef WIN32 +#ifdef USE_WINSOCK static CURLcode check_wsock2 ( struct SessionHandle *data ) { @@ -208,6 +210,7 @@ check_wsock2 ( struct SessionHandle *data ) return CURLE_OK; } #endif + static CURLcode init_telnet(struct connectdata *conn) { @@ -217,7 +220,7 @@ CURLcode init_telnet(struct connectdata *conn) if(!tn) return CURLE_OUT_OF_MEMORY; - conn->proto.telnet = (void *)tn; /* make us known */ + conn->data->reqdata.proto.telnet = (void *)tn; /* make us known */ tn->telrcv_state = CURL_TS_DATA; @@ -236,7 +239,7 @@ CURLcode init_telnet(struct connectdata *conn) static void negotiate(struct connectdata *conn) { int i; - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *) conn->data->reqdata.proto.telnet; for(i = 0;i < CURL_NTELOPTS;i++) { @@ -259,9 +262,9 @@ static void printoption(struct SessionHandle *data, if (cmd == CURL_IAC) { if (CURL_TELCMD_OK(option)) - Curl_infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option)); + infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option)); else - Curl_infof(data, "%s IAC %d\n", direction, option); + infof(data, "%s IAC %d\n", direction, option); } else { @@ -277,12 +280,12 @@ static void printoption(struct SessionHandle *data, opt = NULL; if(opt) - Curl_infof(data, "%s %s %s\n", direction, fmt, opt); + infof(data, "%s %s %s\n", direction, fmt, opt); else - Curl_infof(data, "%s %s %d\n", direction, fmt, option); + infof(data, "%s %s %d\n", direction, fmt, option); } else - Curl_infof(data, "%s %d %d\n", direction, cmd, option); + infof(data, "%s %d %d\n", direction, cmd, option); } } } @@ -290,12 +293,19 @@ static void printoption(struct SessionHandle *data, static void send_negotiation(struct connectdata *conn, int cmd, int option) { unsigned char buf[3]; + ssize_t bytes_written; + int err; + struct SessionHandle *data = conn->data; buf[0] = CURL_IAC; - buf[1] = cmd; - buf[2] = option; + buf[1] = (unsigned char)cmd; + buf[2] = (unsigned char)option; - (void)swrite(conn->sock[FIRSTSOCKET], buf, 3); + bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); + if(bytes_written < 0) { + err = Curl_sockerrno(); + failf(data,"Sending data failed (%d)",err); + } printoption(conn->data, "SENT", cmd, option); } @@ -303,7 +313,7 @@ static void send_negotiation(struct connectdata *conn, int cmd, int option) static void set_remote_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; if(newstate == CURL_YES) { switch(tn->him[option]) @@ -385,7 +395,7 @@ void set_remote_option(struct connectdata *conn, int option, int newstate) static void rec_will(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; switch(tn->him[option]) { case CURL_NO: @@ -438,7 +448,7 @@ void rec_will(struct connectdata *conn, int option) static void rec_wont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; switch(tn->him[option]) { case CURL_NO: @@ -483,7 +493,7 @@ void rec_wont(struct connectdata *conn, int option) static void set_local_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; if(newstate == CURL_YES) { switch(tn->us[option]) @@ -565,7 +575,7 @@ set_local_option(struct connectdata *conn, int option, int newstate) static void rec_do(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; switch(tn->us[option]) { case CURL_NO: @@ -618,7 +628,7 @@ void rec_do(struct connectdata *conn, int option) static void rec_dont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; switch(tn->us[option]) { case CURL_NO: @@ -672,7 +682,7 @@ static void printsub(struct SessionHandle *data, { if (direction) { - Curl_infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); + infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); if (length >= 3) { int j; @@ -682,27 +692,27 @@ static void printsub(struct SessionHandle *data, if (i != CURL_IAC || j != CURL_SE) { - Curl_infof(data, "(terminated by "); + infof(data, "(terminated by "); if (CURL_TELOPT_OK(i)) - Curl_infof(data, "%s ", CURL_TELOPT(i)); + infof(data, "%s ", CURL_TELOPT(i)); else if (CURL_TELCMD_OK(i)) - Curl_infof(data, "%s ", CURL_TELCMD(i)); + infof(data, "%s ", CURL_TELCMD(i)); else - Curl_infof(data, "%d ", i); + infof(data, "%d ", i); if (CURL_TELOPT_OK(j)) - Curl_infof(data, "%s", CURL_TELOPT(j)); + infof(data, "%s", CURL_TELOPT(j)); else if (CURL_TELCMD_OK(j)) - Curl_infof(data, "%s", CURL_TELCMD(j)); + infof(data, "%s", CURL_TELCMD(j)); else - Curl_infof(data, "%d", j); - Curl_infof(data, ", not IAC SE!) "); + infof(data, "%d", j); + infof(data, ", not IAC SE!) "); } } length -= 2; } if (length < 1) { - Curl_infof(data, "(Empty suboption?)"); + infof(data, "(Empty suboption?)"); return; } @@ -711,28 +721,28 @@ static void printsub(struct SessionHandle *data, case CURL_TELOPT_TTYPE: case CURL_TELOPT_XDISPLOC: case CURL_TELOPT_NEW_ENVIRON: - Curl_infof(data, "%s", CURL_TELOPT(pointer[0])); + infof(data, "%s", CURL_TELOPT(pointer[0])); break; default: - Curl_infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); + infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); break; } } else - Curl_infof(data, "%d (unknown)", pointer[i]); + infof(data, "%d (unknown)", pointer[i]); switch(pointer[1]) { case CURL_TELQUAL_IS: - Curl_infof(data, " IS"); + infof(data, " IS"); break; case CURL_TELQUAL_SEND: - Curl_infof(data, " SEND"); + infof(data, " SEND"); break; case CURL_TELQUAL_INFO: - Curl_infof(data, " INFO/REPLY"); + infof(data, " INFO/REPLY"); break; case CURL_TELQUAL_NAME: - Curl_infof(data, " NAME"); + infof(data, " NAME"); break; } @@ -740,21 +750,21 @@ static void printsub(struct SessionHandle *data, case CURL_TELOPT_TTYPE: case CURL_TELOPT_XDISPLOC: pointer[length] = 0; - Curl_infof(data, " \"%s\"", &pointer[2]); + infof(data, " \"%s\"", &pointer[2]); break; case CURL_TELOPT_NEW_ENVIRON: if(pointer[1] == CURL_TELQUAL_IS) { - Curl_infof(data, " "); + infof(data, " "); for(i = 3;i < length;i++) { switch(pointer[i]) { case CURL_NEW_ENV_VAR: - Curl_infof(data, ", "); + infof(data, ", "); break; case CURL_NEW_ENV_VALUE: - Curl_infof(data, " = "); + infof(data, " = "); break; default: - Curl_infof(data, "%c", pointer[i]); + infof(data, "%c", pointer[i]); break; } } @@ -762,13 +772,13 @@ static void printsub(struct SessionHandle *data, break; default: for (i = 2; i < length; i++) - Curl_infof(data, " %.2x", pointer[i]); + infof(data, " %.2x", pointer[i]); break; } if (direction) { - Curl_infof(data, "\n"); + infof(data, "\n"); } } } @@ -780,7 +790,7 @@ static CURLcode check_telnet_options(struct connectdata *conn) char option_arg[256]; char *buf; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; /* Add the user name as an environment variable if it was given on the command line */ @@ -844,12 +854,14 @@ static void suboption(struct connectdata *conn) { struct curl_slist *v; unsigned char temp[2048]; + ssize_t bytes_written; size_t len; size_t tmplen; + int err; char varname[128]; char varval[128]; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)data->reqdata.proto.telnet; printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2); switch (CURL_SB_GET(tn)) { @@ -858,7 +870,11 @@ static void suboption(struct connectdata *conn) snprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE); - (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = Curl_sockerrno(); + failf(data,"Sending data failed (%d)",err); + } printsub(data, '>', &temp[2], len-2); break; case CURL_TELOPT_XDISPLOC: @@ -866,7 +882,11 @@ static void suboption(struct connectdata *conn) snprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE); - (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = Curl_sockerrno(); + failf(data,"Sending data failed (%d)",err); + } printsub(data, '>', &temp[2], len-2); break; case CURL_TELOPT_NEW_ENVIRON: @@ -889,7 +909,11 @@ static void suboption(struct connectdata *conn) snprintf((char *)&temp[len], sizeof(temp) - len, "%c%c", CURL_IAC, CURL_SE); len += 2; - (void)swrite(conn->sock[FIRSTSOCKET], temp, len); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = Curl_sockerrno(); + failf(data,"Sending data failed (%d)",err); + } printsub(data, '>', &temp[2], len-2); break; } @@ -904,7 +928,7 @@ void telrcv(struct connectdata *conn, unsigned char c; int in = 0; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)data->reqdata.proto.telnet; while(count--) { @@ -919,7 +943,7 @@ void telrcv(struct connectdata *conn, break; /* Ignore \0 after CR */ } - Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1); continue; case CURL_TS_DATA: @@ -933,7 +957,7 @@ void telrcv(struct connectdata *conn, tn->telrcv_state = CURL_TS_CR; } - Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1); continue; case CURL_TS_IAC: @@ -957,7 +981,7 @@ void telrcv(struct connectdata *conn, tn->telrcv_state = CURL_TS_SB; continue; case CURL_IAC: - Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&c, 1); break; case CURL_DM: case CURL_NOP: @@ -1014,18 +1038,15 @@ void telrcv(struct connectdata *conn, if (c != CURL_IAC) { /* - * This is an error. We only expect to get - * "IAC IAC" or "IAC SE". Several things may - * have happend. An IAC was not doubled, the - * IAC SE was left off, or another option got - * inserted into the suboption are all possibilities. - * If we assume that the IAC was not doubled, - * and really the IAC SE was left off, we could - * get into an infinate loop here. So, instead, - * we terminate the suboption, and process the - * partial suboption if we can. + * This is an error. We only expect to get "IAC IAC" or "IAC SE". + * Several things may have happend. An IAC was not doubled, the + * IAC SE was left off, or another option got inserted into the + * suboption are all possibilities. If we assume that the IAC was + * not doubled, and really the IAC SE was left off, we could get + * into an infinate loop here. So, instead, we terminate the + * suboption, and process the partial suboption if we can. */ - CURL_SB_ACCUM(tn, (unsigned char)CURL_IAC); + CURL_SB_ACCUM(tn, CURL_IAC); CURL_SB_ACCUM(tn, c); tn->subpointer -= 2; CURL_SB_TERM(tn); @@ -1040,8 +1061,8 @@ void telrcv(struct connectdata *conn, } else { - CURL_SB_ACCUM(tn, (unsigned char)CURL_IAC); - CURL_SB_ACCUM(tn, (unsigned char)CURL_SE); + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); tn->subpointer -= 2; CURL_SB_TERM(tn); suboption(conn); /* handle sub-option */ @@ -1052,25 +1073,26 @@ void telrcv(struct connectdata *conn, } } -CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode status) +CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode status, bool premature) { - struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->reqdata.proto.telnet; (void)status; /* unused */ + (void)premature; /* not used */ curl_slist_free_all(tn->telnet_vars); - free(conn->proto.telnet); - conn->proto.telnet = NULL; + free(conn->data->reqdata.proto.telnet); + conn->data->reqdata.proto.telnet = NULL; return CURLE_OK; } -CURLcode Curl_telnet(struct connectdata *conn) +CURLcode Curl_telnet(struct connectdata *conn, bool *done) { CURLcode code; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; -#ifdef WIN32 +#ifdef USE_WINSOCK HMODULE wsock2; WSOCK2_FUNC close_event_func; WSOCK2_FUNC create_event_func; @@ -1085,25 +1107,27 @@ CURLcode Curl_telnet(struct connectdata *conn) DWORD waitret; DWORD readfile_read; #else - fd_set readfd; - fd_set keepfd; + int interval_ms; + struct pollfd pfd[2]; #endif ssize_t nread; bool keepon = TRUE; char *buf = data->state.buffer; struct TELNET *tn; + *done = TRUE; /* uncontionally */ + code = init_telnet(conn); if(code) return code; - tn = (struct TELNET *)conn->proto.telnet; + tn = (struct TELNET *)data->reqdata.proto.telnet; code = check_telnet_options(conn); if(code) return code; -#ifdef WIN32 +#ifdef USE_WINSOCK /* ** This functionality only works with WinSock >= 2.0. So, ** make sure have it. @@ -1131,7 +1155,7 @@ CURLcode Curl_telnet(struct connectdata *conn) /* And WSACloseEvent */ close_event_func = GetProcAddress(wsock2,"WSACloseEvent"); - if (create_event_func == NULL) { + if (close_event_func == NULL) { failf(data,"failed to find WSACloseEvent function (%d)", GetLastError()); FreeLibrary(wsock2); @@ -1202,11 +1226,11 @@ CURLcode Curl_telnet(struct connectdata *conn) case WAIT_TIMEOUT: { unsigned char outbuf[2]; - int out_count; + int out_count = 0; ssize_t bytes_written; char *buffer = buf; - for(;;) { + while(1) { if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) { keepon = FALSE; break; @@ -1239,7 +1263,7 @@ CURLcode Curl_telnet(struct connectdata *conn) case WAIT_OBJECT_0 + 1: { unsigned char outbuf[2]; - int out_count; + int out_count = 0; ssize_t bytes_written; char *buffer = buf; @@ -1300,36 +1324,26 @@ CURLcode Curl_telnet(struct connectdata *conn) close_event_func = NULL; event_select_func = NULL; enum_netevents_func = NULL; - (void)create_event_func; - (void)close_event_func; - (void)event_select_func; - (void)enum_netevents_func; /* We called LoadLibrary, so call FreeLibrary */ if (!FreeLibrary(wsock2)) infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError()); #else - FD_ZERO (&readfd); /* clear it */ - FD_SET (sockfd, &readfd); - FD_SET (0, &readfd); - - keepfd = readfd; + pfd[0].fd = sockfd; + pfd[0].events = POLLIN; + pfd[1].fd = 0; + pfd[1].events = POLLIN; + interval_ms = 1 * 1000; while (keepon) { - struct timeval interval; - - readfd = keepfd; /* set this every lap in the loop */ - interval.tv_sec = 1; - interval.tv_usec = 0; - - switch (select (sockfd + 1, &readfd, NULL, NULL, &interval)) { + switch (Curl_poll(pfd, 2, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; continue; case 0: /* timeout */ break; default: /* read! */ - if(FD_ISSET(0, &readfd)) { /* read from stdin */ + if(pfd[1].revents & POLLIN) { /* read from stdin */ unsigned char outbuf[2]; int out_count = 0; ssize_t bytes_written; @@ -1348,7 +1362,7 @@ CURLcode Curl_telnet(struct connectdata *conn) } } - if(FD_ISSET(sockfd, &readfd)) { + if(pfd[0].revents & POLLIN) { /* This OUGHT to check the return code... */ (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); @@ -1382,7 +1396,7 @@ CURLcode Curl_telnet(struct connectdata *conn) } #endif /* mark this as "no further transfer wanted" */ - Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); return code; } diff --git a/Utilities/cmcurl/telnet.h b/Utilities/cmcurl/telnet.h index 86dd99b..693abf3 100644 --- a/Utilities/cmcurl/telnet.h +++ b/Utilities/cmcurl/telnet.h @@ -2,18 +2,18 @@ #define __TELNET_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -24,7 +24,7 @@ * $Id$ ***************************************************************************/ #ifndef CURL_DISABLE_TELNET -CURLcode Curl_telnet(struct connectdata *conn); -CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode); +CURLcode Curl_telnet(struct connectdata *conn, bool *done); +CURLcode Curl_telnet_done(struct connectdata *conn, CURLcode, bool premature); #endif #endif diff --git a/Utilities/cmcurl/tftp.c b/Utilities/cmcurl/tftp.c new file mode 100644 index 0000000..9ec1a15 --- /dev/null +++ b/Utilities/cmcurl/tftp.c @@ -0,0 +1,816 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +#include "setup.h" + +#ifndef CURL_DISABLE_TFTP +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#if defined(WIN32) +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" +#include "tftp.h" +#include "progress.h" +#include "connect.h" +#include "strerror.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "url.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "memory.h" +#include "select.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +/* RFC2348 allows the block size to be negotiated, but we don't support that */ +#define TFTP_BLOCKSIZE 512 + +typedef enum { + TFTP_MODE_NETASCII=0, + TFTP_MODE_OCTET +} tftp_mode_t; + +typedef enum { + TFTP_STATE_START=0, + TFTP_STATE_RX, + TFTP_STATE_TX, + TFTP_STATE_FIN +} tftp_state_t; + +typedef enum { + TFTP_EVENT_INIT=0, + TFTP_EVENT_RRQ = 1, + TFTP_EVENT_WRQ = 2, + TFTP_EVENT_DATA = 3, + TFTP_EVENT_ACK = 4, + TFTP_EVENT_ERROR = 5, + TFTP_EVENT_TIMEOUT +} tftp_event_t; + +typedef enum { + TFTP_ERR_UNDEF=0, + TFTP_ERR_NOTFOUND, + TFTP_ERR_PERM, + TFTP_ERR_DISKFULL, + TFTP_ERR_ILLEGAL, + TFTP_ERR_UNKNOWNID, + TFTP_ERR_EXISTS, + TFTP_ERR_NOSUCHUSER, + TFTP_ERR_TIMEOUT, + TFTP_ERR_NORESPONSE +} tftp_error_t; + +typedef struct tftp_packet { + unsigned char data[2 + 2 + TFTP_BLOCKSIZE]; +} tftp_packet_t; + +typedef struct tftp_state_data { + tftp_state_t state; + tftp_mode_t mode; + tftp_error_t error; + struct connectdata *conn; + curl_socket_t sockfd; + int retries; + int retry_time; + int retry_max; + time_t start_time; + time_t max_time; + unsigned short block; + struct Curl_sockaddr_storage local_addr; + socklen_t local_addrlen; + struct Curl_sockaddr_storage remote_addr; + socklen_t remote_addrlen; + int rbytes; + int sbytes; + tftp_packet_t rpacket; + tftp_packet_t spacket; +} tftp_state_data_t; + + +/* Forward declarations */ +static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ; +static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ; +void tftp_set_timeouts(tftp_state_data_t *state) ; + +/********************************************************** + * + * tftp_set_timeouts - + * + * Set timeouts based on state machine state. + * Use user provided connect timeouts until DATA or ACK + * packet is received, then use user-provided transfer timeouts + * + * + **********************************************************/ +void tftp_set_timeouts(tftp_state_data_t *state) +{ + + struct SessionHandle *data = state->conn->data; + time_t maxtime, timeout; + + time(&state->start_time); + if(state->state == TFTP_STATE_START) { + /* Compute drop-dead time */ + maxtime = (time_t)(data->set.connecttimeout?data->set.connecttimeout:30); + state->max_time = state->start_time+maxtime; + + /* Set per-block timeout to total */ + timeout = maxtime ; + + /* Average restart after 5 seconds */ + state->retry_max = timeout/5; + + /* Compute the re-start interval to suit the timeout */ + state->retry_time = timeout/state->retry_max; + if(state->retry_time<1) + state->retry_time=1; + + } + else { + + /* Compute drop-dead time */ + maxtime = (time_t)(data->set.timeout?data->set.timeout:3600); + state->max_time = state->start_time+maxtime; + + /* Set per-block timeout to 10% of total */ + timeout = maxtime/10 ; + + /* Average reposting an ACK after 15 seconds */ + state->retry_max = timeout/15; + } + /* But bound the total number */ + if(state->retry_max<3) + state->retry_max=3; + + if(state->retry_max>50) + state->retry_max=50; + + /* Compute the re-ACK interval to suit the timeout */ + state->retry_time = timeout/state->retry_max; + if(state->retry_time<1) + state->retry_time=1; + + infof(data, "set timeouts for state %d; Total %d, retry %d maxtry %d\n", + state->state, (state->max_time-state->start_time), + state->retry_time, state->retry_max); +} + +/********************************************************** + * + * tftp_set_send_first + * + * Event handler for the START state + * + **********************************************************/ + +static void setpacketevent(tftp_packet_t *packet, unsigned short num) +{ + packet->data[0] = (unsigned char)(num >> 8); + packet->data[1] = (unsigned char)(num & 0xff); +} + + +static void setpacketblock(tftp_packet_t *packet, unsigned short num) +{ + packet->data[2] = (unsigned char)(num >> 8); + packet->data[3] = (unsigned char)(num & 0xff); +} + +static unsigned short getrpacketevent(tftp_packet_t *packet) +{ + return (unsigned short)((packet->data[0] << 8) | packet->data[1]); +} + +static unsigned short getrpacketblock(tftp_packet_t *packet) +{ + return (unsigned short)((packet->data[2] << 8) | packet->data[3]); +} + +static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) +{ + int sbytes; + const char *mode = "octet"; + char *filename; + struct SessionHandle *data = state->conn->data; + CURLcode res = CURLE_OK; + + /* Set ascii mode if -B flag was used */ + if(data->set.prefer_ascii) + mode = "netascii"; + + switch(event) { + + case TFTP_EVENT_INIT: /* Send the first packet out */ + case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ + /* Increment the retry counter, quit if over the limit */ + state->retries++; + if(state->retries>state->retry_max) { + state->error = TFTP_ERR_NORESPONSE; + state->state = TFTP_STATE_FIN; + return res; + } + + if(data->set.upload) { + /* If we are uploading, send an WRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_WRQ); + state->conn->data->reqdata.upload_fromhere = (char *)&state->spacket.data[4]; + if(data->set.infilesize != -1) + Curl_pgrsSetUploadSize(data, data->set.infilesize); + } + else { + /* If we are downloading, send an RRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_RRQ); + } + /* As RFC3617 describes the separator slash is not actually part of the + file name so we skip the always-present first letter of the path string. */ + filename = curl_easy_unescape(data, &state->conn->data->reqdata.path[1], 0, + NULL); + snprintf((char *)&state->spacket.data[2], + TFTP_BLOCKSIZE, + "%s%c%s%c", filename, '\0', mode, '\0'); + sbytes = 4 + (int)strlen(filename) + (int)strlen(mode); + sbytes = sendto(state->sockfd, (void *)&state->spacket, + sbytes, 0, + state->conn->ip_addr->ai_addr, + state->conn->ip_addr->ai_addrlen); + if(sbytes < 0) { + failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno())); + } + Curl_safefree(filename); + break; + + case TFTP_EVENT_ACK: /* Connected for transmit */ + infof(data, "%s\n", "Connected for transmit"); + state->state = TFTP_STATE_TX; + tftp_set_timeouts(state); + return tftp_tx(state, event); + + case TFTP_EVENT_DATA: /* connected for receive */ + infof(data, "%s\n", "Connected for receive"); + state->state = TFTP_STATE_RX; + tftp_set_timeouts(state); + return tftp_rx(state, event); + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + break; + + default: + failf(state->conn->data, "tftp_send_first: internal error\n"); + break; + } + return res; +} + +/********************************************************** + * + * tftp_rx + * + * Event handler for the RX state + * + **********************************************************/ +static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) +{ + int sbytes; + int rblock; + struct SessionHandle *data = state->conn->data; + + switch(event) { + + case TFTP_EVENT_DATA: + + /* Is this the block we expect? */ + rblock = getrpacketblock(&state->rpacket); + if ((state->block+1) != rblock) { + /* No, log it, up the retry count and fail if over the limit */ + infof(data, + "Received unexpected DATA packet block %d\n", rblock); + state->retries++; + if (state->retries>state->retry_max) { + failf(data, "tftp_rx: giving up waiting for block %d\n", + state->block+1); + return CURLE_TFTP_ILLEGAL; + } + } + /* This is the expected block. Reset counters and ACK it. */ + state->block = (unsigned short)rblock; + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_ACK); + setpacketblock(&state->spacket, state->block); + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + if(sbytes < 0) { + failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno())); + } + + /* Check if completed (That is, a less than full packet is received) */ + if (state->rbytes < (int)sizeof(state->spacket)){ + state->state = TFTP_STATE_FIN; + } + else { + state->state = TFTP_STATE_RX; + } + break; + + case TFTP_EVENT_TIMEOUT: + /* Increment the retry count and fail if over the limit */ + state->retries++; + infof(data, + "Timeout waiting for block %d ACK. Retries = %d\n", state->retries); + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; + } + else { + /* Resend the previous ACK */ + sbytes = sendto(state->sockfd, (void *)&state->spacket, + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes<0) { + failf(data, "%s\n", Curl_strerror(state->conn, Curl_sockerrno())); + } + } + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + break; + + default: + failf(data, "%s\n", "tftp_rx: internal error"); + break; + } + Curl_pgrsSetDownloadCounter(data, + (curl_off_t) state->block*TFTP_BLOCKSIZE); + return CURLE_OK; +} + +/********************************************************** + * + * tftp_tx + * + * Event handler for the TX state + * + **********************************************************/ +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; + + 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\n", Curl_strerror(state->conn, Curl_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\n", Curl_strerror(state->conn, Curl_sockerrno())); + } + 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\n", Curl_strerror(state->conn, Curl_sockerrno())); + } + } + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; + break; + + default: + failf(data, "%s\n", "tftp_tx: internal error"); + break; + } + + /* Update the progress meter */ + Curl_pgrsSetUploadCounter(data, (curl_off_t) state->block*TFTP_BLOCKSIZE); + + return res; +} + +/********************************************************** + * + * tftp_state_machine + * + * The tftp state machine event dispatcher + * + **********************************************************/ +static CURLcode tftp_state_machine(tftp_state_data_t *state, + tftp_event_t event) +{ + CURLcode res = CURLE_OK; + struct SessionHandle *data = state->conn->data; + switch(state->state) { + case TFTP_STATE_START: + DEBUGF(infof(data, "TFTP_STATE_START\n")); + res = tftp_send_first(state, event); + break; + case TFTP_STATE_RX: + DEBUGF(infof(data, "TFTP_STATE_RX\n")); + res = tftp_rx(state, event); + break; + case TFTP_STATE_TX: + DEBUGF(infof(data, "TFTP_STATE_TX\n")); + res = tftp_tx(state, event); + break; + case TFTP_STATE_FIN: + infof(data, "%s\n", "TFTP finished"); + break; + default: + DEBUGF(infof(data, "STATE: %d\n", state->state)); + failf(data, "%s\n", "Internal state machine error"); + res = CURLE_TFTP_ILLEGAL; + break; + } + return res; +} + + +/********************************************************** + * + * Curl_tftp_connect + * + * The connect callback + * + **********************************************************/ +CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done) +{ + CURLcode code; + tftp_state_data_t *state; + int rc; + + state = conn->data->reqdata.proto.tftp = calloc(sizeof(tftp_state_data_t), + 1); + if(!state) + return CURLE_OUT_OF_MEMORY; + + conn->bits.close = FALSE; /* keep it open if possible */ + + state->conn = conn; + state->sockfd = state->conn->sock[FIRSTSOCKET]; + state->state = TFTP_STATE_START; + + ((struct sockaddr *)&state->local_addr)->sa_family = + conn->ip_addr->ai_family; + + tftp_set_timeouts(state); + + if(!conn->bits.reuse) { + /* If not reused, bind to any interface, random UDP port. If it is reused, + * this has already been done! + * + * We once used the size of the local_addr struct as the third argument for + * bind() to better work with IPv6 or whatever size the struct could have, + * but we learned that at least Tru64, AIX and IRIX *requires* the size of + * that argument to match the exact size of a 'sockaddr_in' struct when + * running IPv4-only. + * + * Therefore we use the size from the address we connected to, which we + * assume uses the same IP version and thus hopefully this works for both + * IPv4 and IPv6... + */ + rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, + conn->ip_addr->ai_addrlen); + if(rc) { + failf(conn->data, "bind() failed; %s\n", + Curl_strerror(conn, Curl_sockerrno())); + return CURLE_COULDNT_CONNECT; + } + } + + Curl_pgrsStartNow(conn->data); + + *done = TRUE; + code = CURLE_OK; + return(code); +} + +/********************************************************** + * + * Curl_tftp_done + * + * The done callback + * + **********************************************************/ +CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + (void)status; /* unused */ + (void)premature; /* not used */ + +#if 0 + free(conn->data->reqdata.proto.tftp); + conn->data->reqdata.proto.tftp = NULL; +#endif + Curl_pgrsDone(conn); + + return CURLE_OK; +} + + +/********************************************************** + * + * Curl_tftp + * + * The do callback + * + * This callback handles the entire TFTP transfer + * + **********************************************************/ + +CURLcode Curl_tftp(struct connectdata *conn, bool *done) +{ + struct SessionHandle *data = conn->data; + tftp_state_data_t *state = + (tftp_state_data_t *) conn->data->reqdata.proto.tftp; + tftp_event_t event; + CURLcode code; + int rc; + struct Curl_sockaddr_storage fromaddr; + socklen_t fromlen; + int check_time = 0; + + *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 Curl_tftp_connect() function. + */ + if(!state) { + code = Curl_tftp_connect(conn, done); + if(code) + return code; + state = (tftp_state_data_t *)conn->data->reqdata.proto.tftp; + } + + /* Run the TFTP State Machine */ + for(tftp_state_machine(state, TFTP_EVENT_INIT); + state->state != TFTP_STATE_FIN; + tftp_state_machine(state, event) ) { + + /* Wait until ready to read or timeout occurs */ + rc=Curl_select(state->sockfd, CURL_SOCKET_BAD, state->retry_time * 1000); + + if(rc == -1) { + /* bail out */ + int error = Curl_sockerrno(); + failf(data, "%s\n", 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 = 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(conn->data, "Received too short packet\n"); + /* 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; + } + break; + case TFTP_EVENT_ERROR: + state->error = (tftp_error_t)getrpacketblock(&state->rpacket); + infof(conn->data, "%s\n", (char *)&state->rpacket.data[4]); + break; + case TFTP_EVENT_ACK: + break; + case TFTP_EVENT_RRQ: + case TFTP_EVENT_WRQ: + default: + failf(conn->data, "%s\n", "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; + } + } + + } + + /* 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) { + + /* 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_TFTP_DISKFULL; + break; + case TFTP_ERR_ILLEGAL: + code = CURLE_TFTP_ILLEGAL; + break; + case TFTP_ERR_UNKNOWNID: + code = CURLE_TFTP_UNKNOWNID; + break; + case TFTP_ERR_EXISTS: + code = CURLE_TFTP_EXISTS; + break; + case TFTP_ERR_NOSUCHUSER: + code = CURLE_TFTP_NOSUCHUSER; + break; + case TFTP_ERR_TIMEOUT: + code = CURLE_OPERATION_TIMEOUTED; + break; + case TFTP_ERR_NORESPONSE: + code = CURLE_COULDNT_CONNECT; + break; + default: + code= CURLE_ABORTED_BY_CALLBACK; + break; + } + } + else + code = CURLE_OK; + return code; +} +#endif diff --git a/Utilities/cmcurl/tftp.h b/Utilities/cmcurl/tftp.h new file mode 100644 index 0000000..c712541 --- /dev/null +++ b/Utilities/cmcurl/tftp.h @@ -0,0 +1,31 @@ +#ifndef __TFTP_H +#define __TFTP_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ +#ifndef CURL_DISABLE_TFTP +CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done); +CURLcode Curl_tftp(struct connectdata *conn, bool *done); +CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode, bool premature); +#endif +#endif diff --git a/Utilities/cmcurl/timeval.c b/Utilities/cmcurl/timeval.c index 499fba5..bb9c0a1 100644 --- a/Utilities/cmcurl/timeval.c +++ b/Utilities/cmcurl/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -53,9 +53,9 @@ static int gettimeofday(struct timeval *tp, void *nothing) ** of multimedia apis offer a better time resolution ** of 1ms.Need to link against winmm.lib for this **/ - unsigned long Ticks; - unsigned long Sec; - unsigned long Usec; + unsigned long Ticks = 0; + unsigned long Sec =0; + unsigned long Usec = 0; Ticks = timeGetTime(); Sec = Ticks/1000; diff --git a/Utilities/cmcurl/timeval.h b/Utilities/cmcurl/timeval.h index efe377e..effd741 100644 --- a/Utilities/cmcurl/timeval.h +++ b/Utilities/cmcurl/timeval.h @@ -1,18 +1,18 @@ #ifndef __TIMEVAL_H #define __TIMEVAL_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. - * + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. @@ -30,21 +30,23 @@ #include "setup.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#ifdef TIME_WITH_SYS_TIME #include <time.h> +#endif #else -#include <sys/time.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif #endif -#ifndef HAVE_GETTIMEOFDAY -#if !defined(_WINSOCKAPI_) && !defined(__WATCOMC__) && !defined(__MINGW32__) && !defined(_AMIGASF) && \ - !defined(__LCC__) +#ifndef HAVE_STRUCT_TIMEVAL struct timeval { long tv_sec; long tv_usec; }; #endif -#endif struct timeval curlx_tvnow(void); diff --git a/Utilities/cmcurl/transfer.c b/Utilities/cmcurl/transfer.c index 4a108cc..bff16b4 100644 --- a/Utilities/cmcurl/transfer.c +++ b/Utilities/cmcurl/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,14 +32,16 @@ #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> +#endif #include <errno.h> #include "strtoofft.h" #include "strequal.h" -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -49,7 +51,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -73,9 +77,6 @@ #include <sys/select.h> #endif -#ifndef HAVE_SELECT -#error "We can't compile without select() support!" -#endif #ifndef HAVE_SOCKET #error "We can't compile without socket() support!" #endif @@ -92,16 +93,18 @@ #include "sendf.h" #include "speedcheck.h" #include "progress.h" -#include "getdate.h" #include "http.h" #include "url.h" #include "getinfo.h" -#include "ssluse.h" +#include "sslgen.h" #include "http_digest.h" #include "http_ntlm.h" #include "http_negotiate.h" #include "share.h" -#include "curl_memory.h" +#include "memory.h" +#include "select.h" +#include "multiif.h" +#include "easyif.h" /* for Curl_convert_to_network prototype */ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -111,16 +114,6 @@ #define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */ -enum { - KEEP_NONE, - KEEP_READ, - KEEP_WRITE -}; - -/* We keep this static and global since this is read-only and NEVER - changed. It should just remain a blanked-out timeout value. */ -static struct timeval notimeout={0,0}; - /* * This function will call the read callback to fill our buffer with data * to upload. @@ -134,12 +127,12 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) if(conn->bits.upload_chunky) { /* if chunked Transfer-Encoding */ buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */ - conn->upload_fromhere += 10; /* 32bit hex + CRLF */ + data->reqdata.upload_fromhere += 10; /* 32bit hex + CRLF */ } /* this function returns a size_t, so we typecast to int to prevent warnings with picky compilers */ - nread = (int)conn->fread(conn->upload_fromhere, 1, + nread = (int)conn->fread(data->reqdata.upload_fromhere, 1, buffersize, conn->fread_in); if(nread == CURL_READFUNC_ABORT) { @@ -153,18 +146,18 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) int hexlen = snprintf(hexbuffer, sizeof(hexbuffer), "%x\r\n", nread); /* move buffer pointer */ - conn->upload_fromhere -= hexlen; + data->reqdata.upload_fromhere -= hexlen; nread += hexlen; /* copy the prefix to the buffer */ - memcpy(conn->upload_fromhere, hexbuffer, hexlen); + memcpy(data->reqdata.upload_fromhere, hexbuffer, hexlen); /* always append CRLF to the data */ - memcpy(conn->upload_fromhere + nread, "\r\n", 2); + memcpy(data->reqdata.upload_fromhere + nread, "\r\n", 2); if((nread - hexlen) == 0) { /* mark this as done once this chunk is transfered */ - conn->keep.upload_done = TRUE; + data->reqdata.keep.upload_done = TRUE; } nread+=2; /* for the added CRLF */ @@ -172,6 +165,17 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) *nreadp = nread; +#ifdef CURL_DOES_CONVERSIONS + if(data->set.prefer_ascii) { + CURLcode res; + res = Curl_convert_to_network(data, data->reqdata.upload_fromhere, nread); + /* Curl_convert_to_network calls failf if unsuccessful */ + if(res != CURLE_OK) { + return(res); + } + } +#endif /* CURL_DOES_CONVERSIONS */ + return CURLE_OK; } @@ -185,19 +189,120 @@ checkhttpprefix(struct SessionHandle *data, const char *s) { struct curl_slist *head = data->set.http200aliases; + bool rc = FALSE; +#ifdef CURL_DOES_CONVERSIONS + /* convert from the network encoding using a scratch area */ + char *scratch = calloc(1, strlen(s)+1); + if (NULL == scratch) { + failf (data, "Failed to calloc memory for conversion!"); + return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */ + } + strcpy(scratch, s); + if (CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) { + /* Curl_convert_from_network calls failf if unsuccessful */ + free(scratch); + return FALSE; /* can't return CURLE_foobar so return FALSE */ + } + s = scratch; +#endif /* CURL_DOES_CONVERSIONS */ while (head) { - if (checkprefix(head->data, s)) - return TRUE; + if (checkprefix(head->data, s)) { + rc = TRUE; + break; + } head = head->next; } - if(checkprefix("HTTP/", s)) - return TRUE; + if ((rc != TRUE) && (checkprefix("HTTP/", s))) { + rc = TRUE; + } - return FALSE; +#ifdef CURL_DOES_CONVERSIONS + free(scratch); +#endif /* CURL_DOES_CONVERSIONS */ + return rc; } +/* + * Curl_readrewind() rewinds the read stream. This typically (so far) only + * used for HTTP POST/PUT with multi-pass authentication when a sending was + * denied and a resend is necessary. + */ +CURLcode Curl_readrewind(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + + conn->bits.rewindaftersend = FALSE; /* we rewind now */ + + /* We have sent away data. If not using CURLOPT_POSTFIELDS or + CURLOPT_HTTPPOST, call app to rewind + */ + if(data->set.postfields || + (data->set.httpreq == HTTPREQ_POST_FORM)) + ; /* do nothing */ + else { + if(data->set.ioctl) { + curlioerr err; + + err = (data->set.ioctl) (data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); + infof(data, "the ioctl callback returned %d\n", (int)err); + + if(err) { + /* FIXME: convert to a human readable error message */ + failf(data, "ioctl callback returned error %d\n", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else { + /* If no CURLOPT_READFUNCTION is used, we know that we operate on a + given FILE * stream and we can actually attempt to rewind that + ourself with fseek() */ + if(data->set.fread == (curl_read_callback)fread) { + if(-1 != fseek(data->set.in, 0, SEEK_SET)) + /* successful rewind */ + return CURLE_OK; + } + + /* no callback set or failure aboe, makes us fail at once */ + failf(data, "necessary data rewind wasn't possible\n"); + return CURLE_SEND_FAIL_REWIND; + } + } + return CURLE_OK; +} + +static int data_pending(struct connectdata *conn) +{ + return Curl_ssl_data_pending(conn, FIRSTSOCKET); +} + +#ifndef MIN +#define MIN(a,b) (a < b ? a : b) +#endif + +static void read_rewind(struct connectdata *conn, + size_t thismuch) +{ + conn->read_pos -= thismuch; + conn->bits.stream_was_rewound = TRUE; + +#ifdef CURLDEBUG + { + char buf[512 + 1]; + size_t show; + + show = MIN(conn->buf_len - conn->read_pos, sizeof(buf)-1); + memcpy(buf, conn->master_buffer + conn->read_pos, show); + buf[show] = '\0'; + + DEBUGF(infof(conn->data, + "Buffer after stream rewind (read_pos = %d): [%s]", + conn->read_pos, buf)); + } +#endif +} /* * Curl_readwrite() is the low-level function to be called when data is to @@ -206,63 +311,68 @@ checkhttpprefix(struct SessionHandle *data, CURLcode Curl_readwrite(struct connectdata *conn, bool *done) { - struct Curl_transfer_keeper *k = &conn->keep; struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &data->reqdata.keep; CURLcode result; ssize_t nread; /* number of bytes read */ int didwhat=0; - /* These two are used only if no other select() or _fdset() have been - invoked before this. This typicly happens if you use the multi interface - and call curl_multi_perform() without calling curl_multi_fdset() - first. */ - fd_set extrareadfd; - fd_set extrawritefd; + curl_socket_t fd_read; + curl_socket_t fd_write; + int select_res; - fd_set *readfdp = k->readfdp; - fd_set *writefdp = k->writefdp; curl_off_t contentlength; - if((k->keepon & KEEP_READ) && !readfdp) { - /* reading is requested, but no socket descriptor pointer was set */ - FD_ZERO(&extrareadfd); - FD_SET(conn->sockfd, &extrareadfd); - readfdp = &extrareadfd; + /* 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 */ - /* no write, no exceptions, no timeout */ - select(conn->sockfd+1, readfdp, NULL, NULL, ¬imeout); - } - if((k->keepon & KEEP_WRITE) && !writefdp) { - /* writing is requested, but no socket descriptor pointer was set */ - FD_ZERO(&extrawritefd); - FD_SET(conn->writesockfd, &extrawritefd); - writefdp = &extrawritefd; - - /* no read, no exceptions, no timeout */ - select(conn->writesockfd+1, NULL, writefdp, NULL, ¬imeout); + if((k->keepon & (KEEP_READ|KEEP_READ_HOLD)) == KEEP_READ) + fd_read = conn->sockfd; + else + fd_read = CURL_SOCKET_BAD; + + if((k->keepon & (KEEP_WRITE|KEEP_WRITE_HOLD)) == KEEP_WRITE) + fd_write = conn->writesockfd; + else + fd_write = CURL_SOCKET_BAD; + + select_res = Curl_select(fd_read, fd_write, 0); + if(select_res == CSELECT_ERR) { + failf(data, "select/poll returned error"); + return CURLE_SEND_ERROR; } do { - /* If we still have reading to do, we check if we have a readable - socket. Sometimes the reafdp is NULL, if no fd_set was done using - the multi interface and then we can do nothing but to attempt a - read to be sure. */ + /* 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) && - (!readfdp || FD_ISSET(conn->sockfd, readfdp))) { - - bool readdone = TRUE; + ((select_res & CSELECT_IN) || conn->bits.stream_was_rewound)) { + /* 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 -1; + 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; + } /* receive data from the network! */ - int readrc = Curl_read(conn, conn->sockfd, k->buf, buffersize, &nread); + readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread); /* subzero, this would've blocked */ - if(0>readrc) + if(0 > readrc) break; /* get out of loop */ /* get the CURLcode from the int */ @@ -279,17 +389,18 @@ CURLcode Curl_readwrite(struct connectdata *conn, } didwhat |= KEEP_READ; + /* indicates data of zero size, i.e. empty file */ + is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0)); /* NULL terminate, allowing string ops to be used */ - if (0 < nread) + if (0 < nread || is_empty_data) { k->buf[nread] = 0; - - /* if we receive 0 or less here, the server closed the connection and - we bail out from this! */ + } else if (0 >= nread) { + /* if we receive 0 or less here, the server closed the connection + and we bail out from this! */ + k->keepon &= ~KEEP_READ; - FD_ZERO(&k->rkeepfd); - readdone = TRUE; break; } @@ -313,7 +424,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* str_start is start of line within buf */ k->str_start = k->str; - k->end_ptr = strchr (k->str_start, '\n'); + /* 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 @@ -352,7 +464,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* decrease the size of the remaining (supposed) header line */ rest_length = (k->end_ptr - k->str)+1; - nread -= rest_length; + nread -= (ssize_t)rest_length; k->str = k->end_ptr + 1; /* move past new line */ @@ -381,7 +493,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, } /* copy to end of line */ - strncpy (k->hbufp, k->str_start, full_length); + memcpy(k->hbufp, k->str_start, full_length); k->hbufp += full_length; k->hbuflen += full_length; *k->hbufp = 0; @@ -411,14 +523,27 @@ CURLcode Curl_readwrite(struct connectdata *conn, } } - if (('\n' == *k->p) || ('\r' == *k->p)) { + /* 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 */ if(100 == k->httpcode) { /* @@ -434,14 +559,22 @@ CURLcode Curl_readwrite(struct connectdata *conn, if (k->write_after_100_header) { k->write_after_100_header = FALSE; - FD_SET (conn->writesockfd, &k->writefd); /* write */ k->keepon |= KEEP_WRITE; - k->wkeepfd = k->writefd; } } - else + else { k->header = FALSE; /* no more header to parse! */ + if((k->size == -1) && !conn->bits.chunk && !conn->bits.close) + /* When connection is not to get closed, but no + Content-Length nor Content-Encoding chunked have been + received, there is no body in this response. We don't set + stop_reading TRUE since that would also prevent necessary + authentication actions to take place. */ + conn->bits.no_body = TRUE; + + } + if (417 == k->httpcode) { /* * we got: "417 Expectation Failed" this means: @@ -451,7 +584,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, */ k->write_after_100_header = FALSE; k->keepon &= ~KEEP_WRITE; - FD_ZERO(&k->wkeepfd); } #ifndef CURL_DISABLE_HTTP @@ -474,34 +606,25 @@ CURLcode Curl_readwrite(struct connectdata *conn, headerlen = k->p - data->state.headerbuff; - result = Curl_client_write(data, writetype, + result = Curl_client_write(conn, writetype, data->state.headerbuff, headerlen); if(result) return result; - data->info.header_size += headerlen; - conn->headerbytecount += headerlen; + data->info.header_size += (long)headerlen; + conn->headerbytecount += (long)headerlen; conn->deductheadercount = (100 == k->httpcode)?conn->headerbytecount:0; - if (conn->resume_from && - !k->content_range && - (data->set.httpreq==HTTPREQ_GET)) { - if(k->httpcode == 416) { - /* "Requested Range Not Satisfiable" */ - stop_reading = TRUE; - } - else { - /* 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_HTTP_RANGE_ERROR; - } + if (data->reqdata.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 @@ -511,6 +634,13 @@ CURLcode Curl_readwrite(struct connectdata *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 */ @@ -538,26 +668,25 @@ CURLcode Curl_readwrite(struct connectdata *conn, using chunked Transfer-Encoding. */ if(conn->bits.chunk) - conn->size=-1; + k->size=-1; } - if(-1 != conn->size) { + 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, conn->size); - conn->maxdownload = conn->size; + 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 == conn->maxdownload) + 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; - FD_ZERO(&k->rkeepfd); } break; /* exit header line loop */ @@ -576,12 +705,37 @@ CURLcode Curl_readwrite(struct connectdata *conn, if (!k->headerline++) { /* This is the first header, it MUST be the error code line - or else we consiser this to be the body right away! */ + or else we consider this to be the body right away! */ int httpversion_major; - int nc=sscanf(k->p, " HTTP/%d.%d %3d", - &httpversion_major, - &k->httpversion, - &k->httpcode); + 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, + &k->httpversion, + &k->httpcode); if (nc==3) { k->httpversion += 10 * httpversion_major; } @@ -589,7 +743,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* this is the real world, not a Nirvana NCSA 1.5.x returns this crap when asked for HTTP/1.1 */ - nc=sscanf(k->p, " HTTP %3d", &k->httpcode); + nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode); k->httpversion = 10; /* If user has set option HTTP200ALIASES, @@ -617,14 +771,22 @@ CURLcode Curl_readwrite(struct connectdata *conn, * 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) && - (k->httpcode != 407)) { - /* serious error, go home! */ - failf (data, "The requested URL returned error: %d", - k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; + if (data->set.http_fail_on_error && (k->httpcode >= 400) && + ((k->httpcode != 401) || !data->set.userpwd) && + ((k->httpcode != 407) || !data->set.proxyuserpwd) ) { + + if (data->reqdata.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(k->httpversion == 10) @@ -649,8 +811,9 @@ CURLcode Curl_readwrite(struct connectdata *conn, * MUST NOT contain a message-body, and thus is always * terminated by the first empty line after the header * fields. */ - conn->size=0; - conn->maxdownload=0; + k->size=0; + k->maxdownload=0; + k->ignorecl = TRUE; /* ignore Content-Length headers */ break; default: /* nothing */ @@ -663,11 +826,20 @@ CURLcode Curl_readwrite(struct connectdata *conn, } } +#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 */ + /* 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->httpcode != 416) && + if (!k->ignorecl && !data->set.ignorecl && checkprefix("Content-Length:", k->p)) { contentlength = curlx_strtoofft(k->p+15, NULL, 10); if (data->set.max_filesize && @@ -675,8 +847,10 @@ CURLcode Curl_readwrite(struct connectdata *conn, failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; } - if(contentlength >= 0) - conn->size = contentlength; + if(contentlength >= 0) { + k->size = contentlength; + k->maxdownload = k->size; + } else { /* Negative Content-Length is really odd, and we know it happens for example when older Apache servers send large @@ -694,16 +868,20 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* Find the first non-space letter */ for(start=k->p+13; - *start && isspace((int)*start); - start++); + *start && ISSPACE(*start); + start++) + ; /* empty loop */ + /* data is now 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) { /* skip all trailing space letters */ - for(; isspace((int)*end) && (end > start); end--); + for(; ISSPACE(*end) && (end > start); end--) + ; /* empty loop */ /* get length of the type */ len = end-start+1; @@ -734,6 +912,17 @@ CURLcode Curl_readwrite(struct connectdata *conn, conn->bits.close = FALSE; /* don't close when done */ infof(data, "HTTP/1.0 proxy connection set to keep alive!\n"); } + else if((k->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((k->httpversion == 10) && Curl_compareheader(k->p, "Connection:", "keep-alive")) { /* @@ -769,6 +958,20 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* 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. + */ + conn->bits.trailerHdrPresent = TRUE; + } + else if (checkprefix("Content-Encoding:", k->p) && data->set.encoding) { /* @@ -782,8 +985,9 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* Find the first non-space letter */ for(start=k->p+17; - *start && isspace((int)*start); - start++); + *start && ISSPACE(*start); + start++) + ; /* empty loop */ /* Record the content-encoding for later use */ if (checkprefix("identity", start)) @@ -797,27 +1001,29 @@ CURLcode Curl_readwrite(struct connectdata *conn, || checkprefix("x-compress", start)) k->content_encoding = COMPRESS; } - else if (Curl_compareheader(k->p, "Content-Range:", "bytes")) { + else if (checkprefix("Content-Range:", k->p)) { /* Content-Range: bytes [num]- Content-Range: bytes: [num]- + Content-Range: [num]- - The second format was added August 1st 2000 by Igor - Khristophorov since Sun's webserver JavaWebServer/1.1.1 - obviously sends the header this way! :-( */ + 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 = strstr(k->p, "bytes"); - ptr+=5; + char *ptr = k->p + 14; - if(*ptr == ':') - /* stupid colon skip */ + /* Move forward until first digit */ + while(*ptr && !ISDIGIT(*ptr)) ptr++; k->offset = curlx_strtoofft(ptr, NULL, 10); - if (conn->resume_from == k->offset) + if (data->reqdata.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, @@ -828,16 +1034,17 @@ CURLcode Curl_readwrite(struct connectdata *conn, here, or else use real peer host name. */ conn->allocptr.cookiehost? conn->allocptr.cookiehost:conn->host.name, - conn->path); + data->reqdata.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 = k->timeofdoc; + data->info.filetime = (long)k->timeofdoc; } else if((checkprefix("WWW-Authenticate:", k->p) && (401 == k->httpcode)) || @@ -859,7 +1066,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* Skip spaces and tabs. We do this to support multiple white spaces after the "Location:" keyword. */ - while(*start && isspace((int)*start )) + while(*start && ISSPACE(*start )) start++; /* Scan through the string from the end to find the last @@ -868,16 +1075,16 @@ CURLcode Curl_readwrite(struct connectdata *conn, there. This logic strips off trailing whitespace, but keeps any embedded whitespace. */ ptr = k->end_ptr-1; - while((ptr>=start) && isspace((int)*ptr)) + while((ptr>=start) && ISSPACE(*ptr)) ptr--; ptr++; backup = *ptr; /* store the ending letter */ if(ptr != start) { *ptr = '\0'; /* zero terminate */ - conn->newurl = strdup(start); /* clone string */ + data->reqdata.newurl = strdup(start); /* clone string */ *ptr = backup; /* restore ending letter */ - if(!conn->newurl) + if(!data->reqdata.newurl) return CURLE_OUT_OF_MEMORY; } } @@ -894,14 +1101,14 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->set.verbose) Curl_debug(data, CURLINFO_HEADER_IN, - k->p, k->hbuflen, conn->host.dispname); + k->p, (size_t)k->hbuflen, conn); - result = Curl_client_write(data, writetype, k->p, k->hbuflen); + result = Curl_client_write(conn, writetype, k->p, k->hbuflen); if(result) return result; - data->info.header_size += k->hbuflen; - conn->headerbytecount += k->hbuflen; + data->info.header_size += (long)k->hbuflen; + conn->headerbytecount += (long)k->hbuflen; /* reset hbufp pointer && hbuflen */ k->hbufp = data->state.headerbuff; @@ -922,20 +1129,19 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* 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)) { + if (k->str && !k->header && (nread > 0 || is_empty_data)) { - if(0 == k->bodywrites) { + 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 (conn->newurl) { + if (data->reqdata.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; - FD_ZERO(&k->rkeepfd); *done = TRUE; return CURLE_OK; } @@ -945,7 +1151,18 @@ CURLcode Curl_readwrite(struct connectdata *conn, k->ignorebody = TRUE; infof(data, "Ignoring the response-body\n"); } - if(data->set.timecondition && !conn->range) { + if (data->reqdata.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_HTTP_RANGE_ERROR; + } + + if(data->set.timecondition && !data->reqdata.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 @@ -981,14 +1198,14 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->set.verbose) { if(k->badheader) { Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff, - k->hbuflen, conn->host.dispname); + (size_t)k->hbuflen, conn); if(k->badheader == HEADER_PARTHEADER) - Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, - conn->host.dispname); + Curl_debug(data, CURLINFO_DATA_IN, + k->str, (size_t)nread, conn); } else - Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, - conn->host.dispname); + Curl_debug(data, CURLINFO_DATA_IN, + k->str, (size_t)nread, conn); } #ifndef CURL_DISABLE_HTTP @@ -999,6 +1216,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, * 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); @@ -1013,7 +1231,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, else if(CHUNKE_STOP == res) { /* we're done reading chunks! */ k->keepon &= ~KEEP_READ; /* read no more */ - FD_ZERO(&k->rkeepfd); /* There are now possibly N number of bytes at the end of the str buffer that weren't written to the client, but we don't @@ -1023,29 +1240,44 @@ CURLcode Curl_readwrite(struct connectdata *conn, } #endif /* CURL_DISABLE_HTTP */ - if((-1 != conn->maxdownload) && - (k->bytecount + nread >= conn->maxdownload)) { - nread = (ssize_t) (conn->maxdownload - k->bytecount); + 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 = 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, conn->data->reqdata.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 */ - FD_ZERO(&k->rkeepfd); } k->bytecount += nread; Curl_pgrsSetDownloadCounter(data, k->bytecount); - if(!conn->bits.chunk && (nread || k->badheader)) { + if(!conn->bits.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(data, CLIENTWRITE_BODY, + 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 @@ -1061,19 +1293,21 @@ CURLcode Curl_readwrite(struct connectdata *conn, Content-Encoding header. See Curl_readwrite_init; the memset() call initializes k->content_encoding to zero. */ if(!k->ignorebody) - result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, + result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str, nread); #ifdef HAVE_LIBZ break; case DEFLATE: /* Assume CLIENTWRITE_BODY; headers are not encoded. */ - result = Curl_unencode_deflate_write(data, k, nread); + if(!k->ignorebody) + result = Curl_unencode_deflate_write(conn, k, nread); break; case GZIP: /* Assume CLIENTWRITE_BODY; headers are not encoded. */ - result = Curl_unencode_gzip_write(data, k, nread); + if(!k->ignorebody) + result = Curl_unencode_gzip_write(conn, k, nread); break; case COMPRESS: @@ -1094,16 +1328,19 @@ CURLcode Curl_readwrite(struct connectdata *conn, } /* if (! header and data to read ) */ - } while(!readdone); + 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( read from socket ) */ /* If we still have writing to do, we check if we have a writable - socket. Sometimes the writefdp is NULL, if no fd_set was done using - the multi interface and then we can do nothing but to attempt a - write to be sure. */ - if((k->keepon & KEEP_WRITE) && - (!writefdp || FD_ISSET(conn->writesockfd, writefdp)) ) { + socket. */ + if((k->keepon & KEEP_WRITE) && (select_res & CSELECT_OUT)) { /* write */ int i, si; @@ -1123,9 +1360,9 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* only read more data if there's no upload data already present in the upload buffer */ - if(0 == conn->upload_present) { + if(0 == data->reqdata.upload_present) { /* init the "upload from here" pointer */ - conn->upload_fromhere = k->uploadbuf; + data->reqdata.upload_fromhere = k->uploadbuf; if(!k->upload_done) { /* HTTP pollution, this should be written nicer to become more @@ -1133,14 +1370,12 @@ CURLcode Curl_readwrite(struct connectdata *conn, int fillcount; if(k->wait100_after_headers && - (conn->proto.http->sending == HTTPSEND_BODY)) { + (data->reqdata.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->wait100_after_headers = FALSE; /* headers sent */ k->write_after_100_header = TRUE; /* wait for the header */ - FD_ZERO (&k->writefd); /* clear it */ - k->wkeepfd = k->writefd; /* set the keeper variable */ k->keepon &= ~KEEP_WRITE; /* disable writing */ k->start100 = Curl_tvnow(); /* timeout count starts now */ didwhat &= ~KEEP_WRITE; /* we didn't write anything actually */ @@ -1161,30 +1396,50 @@ CURLcode Curl_readwrite(struct connectdata *conn, if (nread<=0) { /* done */ k->keepon &= ~KEEP_WRITE; /* we're done writing */ - FD_ZERO(&k->wkeepfd); writedone = TRUE; - (void)writedone; + + if(conn->bits.rewindaftersend) { + result = Curl_readrewind(conn); + if(result) + return result; + } break; } /* store number of bytes available for upload */ - conn->upload_present = nread; + data->reqdata.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 (conn->upload_fromhere[i] == 0x0a) { + if (data->reqdata.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] = conn->upload_fromhere[i]; + data->state.scratch[si] = data->reqdata.upload_fromhere[i]; } if(si != nread) { /* only perform the special operation if we really did replace @@ -1192,10 +1447,10 @@ CURLcode Curl_readwrite(struct connectdata *conn, nread = si; /* upload from the new (replaced) buffer instead */ - conn->upload_fromhere = data->state.scratch; + data->reqdata.upload_fromhere = data->state.scratch; /* set the new amount too */ - conn->upload_present = nread; + data->reqdata.upload_present = nread; } } } @@ -1207,38 +1462,37 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* write to socket (send away data) */ result = Curl_write(conn, conn->writesockfd, /* socket to send to */ - conn->upload_fromhere, /* buffer pointer */ - conn->upload_present, /* buffer size */ + data->reqdata.upload_fromhere, /* buffer pointer */ + data->reqdata.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, conn->upload_fromhere, - bytes_written, conn->host.dispname); + Curl_debug(data, CURLINFO_DATA_OUT, data->reqdata.upload_fromhere, + (size_t)bytes_written, conn); - if(conn->upload_present != bytes_written) { + if(data->reqdata.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 */ - conn->upload_present -= bytes_written; + data->reqdata.upload_present -= bytes_written; /* advance the pointer where to find the buffer when the next send is to happen */ - conn->upload_fromhere += bytes_written; + data->reqdata.upload_fromhere += bytes_written; writedone = TRUE; /* we are done, stop the loop */ } else { /* we've uploaded that buffer now */ - conn->upload_fromhere = k->uploadbuf; - conn->upload_present = 0; /* no more bytes left */ + data->reqdata.upload_fromhere = k->uploadbuf; + data->reqdata.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 */ - FD_ZERO(&k->wkeepfd); writedone = TRUE; } } @@ -1255,10 +1509,10 @@ CURLcode Curl_readwrite(struct connectdata *conn, k->now = Curl_tvnow(); if(didwhat) { /* Update read/write counters */ - if(conn->bytecountp) - *conn->bytecountp = k->bytecount; /* read count */ - if(conn->writebytecountp) - *conn->writebytecountp = k->writebytecount; /* write count */ + 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? */ @@ -1280,9 +1534,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(ms > CURL_TIMEOUT_EXPECT_100) { /* we've waited long enough, continue anyway */ k->write_after_100_header = FALSE; - FD_SET (conn->writesockfd, &k->writefd); /* write socket */ k->keepon |= KEEP_WRITE; - k->wkeepfd = k->writefd; } } } @@ -1296,9 +1548,15 @@ CURLcode Curl_readwrite(struct connectdata *conn, if (data->set.timeout && ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { - failf(data, "Operation timed out with %" FORMAT_OFF_T - " out of %" FORMAT_OFF_T " bytes received", - k->bytecount, conn->size); + if (k->size != -1) { + failf(data, "Operation timed out after %d seconds 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 %d seconds with %" + FORMAT_OFF_T " bytes received", + data->set.timeout, k->bytecount); + } return CURLE_OPERATION_TIMEOUTED; } @@ -1308,17 +1566,34 @@ CURLcode Curl_readwrite(struct connectdata *conn, * returning. */ - if(!(conn->bits.no_body) && (conn->size != -1) && - (k->bytecount != conn->size) && - !conn->newurl) { + if(!(conn->bits.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->reqdata.newurl) { failf(data, "transfer closed with %" FORMAT_OFF_T " bytes remaining to read", - conn->size - k->bytecount); + k->size - k->bytecount); return CURLE_PARTIAL_FILE; } - else if(conn->bits.chunk && conn->proto.http->chunk.datasize) { - failf(data, "transfer closed with at least %d bytes remaining", - conn->proto.http->chunk.datasize); + else if(!(conn->bits.no_body) && + conn->bits.chunk && + (data->reqdata.proto.http->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)) @@ -1326,23 +1601,24 @@ CURLcode Curl_readwrite(struct connectdata *conn, } /* Now update the "done" boolean we return */ - *done = !k->keepon; + *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE))); return CURLE_OK; } /* - * Curl_readwrite_init() inits the readwrite session. + * Curl_readwrite_init() inits the readwrite session. This is inited each time for a + * transfer, sometimes multiple times on the same SessionHandle */ CURLcode Curl_readwrite_init(struct connectdata *conn) { - struct SessionHandle *data; - struct Curl_transfer_keeper *k = &conn->keep; + struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &data->reqdata.keep; /* NB: the content encoding software depends on this initialization of - Curl_transfer_keeper. */ + Curl_transfer_keeper.*/ memset(k, 0, sizeof(struct Curl_transfer_keeper)); k->start = Curl_tvnow(); /* start time */ @@ -1350,7 +1626,13 @@ CURLcode Curl_readwrite_init(struct connectdata *conn) k->header = TRUE; /* assume header */ k->httpversion = -1; /* unknown at this point */ - data = conn->data; /* there's the root struct */ + k->size = data->reqdata.size; + k->maxdownload = data->reqdata.maxdownload; + k->bytecountp = data->reqdata.bytecountp; + k->writebytecountp = data->reqdata.writebytecountp; + + k->bytecount = 0; + k->buf = data->state.buffer; k->uploadbuf = data->state.uploadbuffer; k->maxfd = (conn->sockfd>conn->writesockfd? @@ -1366,19 +1648,16 @@ CURLcode Curl_readwrite_init(struct connectdata *conn) if (!conn->bits.getheader) { k->header = FALSE; - if(conn->size > 0) - Curl_pgrsSetDownloadSize(data, conn->size); + if(k->size > 0) + Curl_pgrsSetDownloadSize(data, k->size); } /* we want header and/or body, if neither then don't do this! */ if(conn->bits.getheader || !conn->bits.no_body) { - FD_ZERO (&k->readfd); /* clear it */ if(conn->sockfd != CURL_SOCKET_BAD) { - FD_SET (conn->sockfd, &k->readfd); /* read socket */ k->keepon |= KEEP_READ; } - FD_ZERO (&k->writefd); /* clear it */ if(conn->writesockfd != CURL_SOCKET_BAD) { /* HTTP 1.1 magic: @@ -1389,63 +1668,65 @@ CURLcode Curl_readwrite_init(struct connectdata *conn) 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->set.expect100header && - (conn->proto.http->sending == HTTPSEND_BODY)) { + if (data->state.expect100header && + (data->reqdata.proto.http->sending == HTTPSEND_BODY)) { /* wait with write until we either got 100-continue or a timeout */ k->write_after_100_header = TRUE; k->start100 = k->start; } else { - if(data->set.expect100header) + if(data->state.expect100header) /* when we've sent off the rest of the headers, we must await a 100-continue */ k->wait100_after_headers = TRUE; - FD_SET (conn->writesockfd, &k->writefd); /* write socket */ k->keepon |= KEEP_WRITE; } } - - /* get these in backup variables to be able to restore them on each lap in - the select() loop */ - k->rkeepfd = k->readfd; - k->wkeepfd = k->writefd; - } return CURLE_OK; } /* - * Curl_single_fdset() gets called by the multi interface code when the app - * has requested to get the fd_sets for the current connection. This function + * Curl_single_getsock() gets called by the multi interface code when the app + * has requested to get the sockets for the current connection. This function * will then be called once for every connection that the multi interface * keeps track of. This function will only be called for connections that are * in the proper state to have this information available. */ -void Curl_single_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *exc_fd_set, - int *max_fd) +int Curl_single_getsock(struct connectdata *conn, + curl_socket_t *sock, /* points to numsocks number + of sockets */ + int numsocks) { - *max_fd = -1; /* init */ - if(conn->keep.keepon & KEEP_READ) { - FD_SET(conn->sockfd, read_fd_set); - *max_fd = conn->sockfd; - conn->keep.readfdp = read_fd_set; /* store the address of the set */ + struct SessionHandle *data = conn->data; + int bitmap = GETSOCK_BLANK; + int index = 0; + + if(numsocks < 2) + /* simple check but we might need two slots */ + return GETSOCK_BLANK; + + if(data->reqdata.keep.keepon & KEEP_READ) { + bitmap |= GETSOCK_READSOCK(index); + sock[index] = conn->sockfd; } - if(conn->keep.keepon & KEEP_WRITE) { - FD_SET(conn->writesockfd, write_fd_set); - - /* since sockets are curl_socket_t nowadays, we typecast it to int here - to compare it nicely */ - if((int)conn->writesockfd > *max_fd) - *max_fd = conn->writesockfd; - conn->keep.writefdp = write_fd_set; /* store the address of the set */ + + if(data->reqdata.keep.keepon & KEEP_WRITE) { + + if((conn->sockfd != conn->writesockfd) || + !(data->reqdata.keep.keepon & KEEP_READ)) { + /* only if they are not the same socket or we didn't have a readable + one, we increase index */ + if(data->reqdata.keep.keepon & KEEP_READ) + index++; /* increase index if we need two entries */ + sock[index] = conn->writesockfd; + } + + bitmap |= GETSOCK_WRITESOCK(index); } - /* we don't use exceptions, only touch that one to prevent compiler - warnings! */ - *exc_fd_set = *exc_fd_set; + + return bitmap; } @@ -1454,7 +1735,7 @@ void Curl_single_fdset(struct connectdata *conn, * * This function is what performs the actual transfer. It is capable of * doing both ways simultaneously. - * The transfer must already have been setup by a call to Curl_Transfer(). + * The transfer must already have been setup by a call to Curl_setup_transfer(). * * Note that headers are created in a preallocated buffer of a default size. * That buffer can be enlarged on demand, but it is never shrunken again. @@ -1467,7 +1748,8 @@ static CURLcode Transfer(struct connectdata *conn) { CURLcode result; - struct Curl_transfer_keeper *k = &conn->keep; + struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &data->reqdata.keep; bool done=FALSE; if(!(conn->protocol & PROT_FILE)) @@ -1484,17 +1766,43 @@ Transfer(struct connectdata *conn) if(!conn->bits.getheader && conn->bits.no_body) return CURLE_OK; - k->writefdp = &k->writefd; /* store the address of the set */ - k->readfdp = &k->readfd; /* store the address of the set */ - while (!done) { - struct timeval interval; - k->readfd = k->rkeepfd; /* set these every lap in the loop */ - k->writefd = k->wkeepfd; - interval.tv_sec = 1; - interval.tv_usec = 0; + 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 */ + } + + /* The *_HOLD 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 (select (k->maxfd, k->readfdp, k->writefdp, NULL, &interval)) { + switch (Curl_select(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 @@ -1507,6 +1815,7 @@ Transfer(struct connectdata *conn) continue; case 0: /* timeout */ default: /* readable descriptors */ + result = Curl_readwrite(conn, &done); break; } @@ -1524,20 +1833,19 @@ Transfer(struct connectdata *conn) */ CURLcode Curl_pretransfer(struct SessionHandle *data) { - if(!data->change.url) + CURLcode res; + if(!data->change.url) { /* we can't do anything wihout URL */ + failf(data, "No URL set!\n"); return CURLE_URL_MALFORMAT; - -#ifdef USE_SSLEAY - { - /* Init the SSL session ID cache here. We do it here since we want to do - it after the *_setopt() calls (that could change the size of the cache) - but before any transfer takes place. */ - CURLcode res = Curl_SSL_InitSessions(data, data->set.ssl.numsessions); - if(res) - return res; } -#endif + + /* Init the SSL session ID cache here. We do it here since we want to do it + after the *_setopt() calls (that could change the size of the cache) but + before any transfer takes place. */ + res = Curl_ssl_initsessions(data, data->set.ssl.numsessions); + if(res) + return res; data->set.followlocation=0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ @@ -1547,25 +1855,10 @@ CURLcode Curl_pretransfer(struct SessionHandle *data) data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; -#ifndef CURL_DISABLE_HTTP - /* If there was a list of cookie files to read and we haven't done it before, - do it now! */ + /* If there is a list of cookie files to read, do it now! */ if(data->change.cookielist) { - struct curl_slist *list = data->change.cookielist; - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - while(list) { - data->cookies = Curl_cookie_init(data, - list->data, - data->cookies, - data->set.cookiesession); - list = list->next; - } - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - curl_slist_free_all(data->change.cookielist); /* clean up list */ - data->change.cookielist = NULL; /* don't do this again! */ + Curl_cookie_loadfiles(data); } -#endif /* CURL_DISABLE_HTTP */ - /* Allow data->set.use_port to set which port to use. This needs to be * disabled for example when we follow Location: headers to URLs using @@ -1599,6 +1892,11 @@ CURLcode Curl_posttransfer(struct SessionHandle *data) (void)data; /* unused parameter */ #endif + if(!(data->progress.flags & PGRS_HIDE) && + !data->progress.callback) + /* only output if we don't use a progress callback and we're not hidden */ + fprintf(data->set.err, "\n"); + return CURLE_OK; } @@ -1668,9 +1966,11 @@ static void strcpy_url(char *output, char *url) * as given by the remote server and set up the new URL to request. */ CURLcode Curl_follow(struct SessionHandle *data, - char *newurl) /* this 'newurl' is the Location: string, + char *newurl, /* this 'newurl' is the Location: string, and it must be malloc()ed before passed here */ + bool retry) /* set TRUE if this is a request retry as + opposed to a real redirect following */ { /* Location: redirect */ char prot[16]; /* URL protocol string storage */ @@ -1678,16 +1978,18 @@ CURLcode Curl_follow(struct SessionHandle *data, size_t newlen; char *newest; - if (data->set.maxredirs && - (data->set.followlocation >= data->set.maxredirs)) { - failf(data,"Maximum (%d) redirects followed", data->set.maxredirs); - return CURLE_TOO_MANY_REDIRECTS; - } + if(!retry) { + 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; + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; - data->set.followlocation++; /* count location-followers */ + data->set.followlocation++; /* count location-followers */ + } if(data->set.http_auto_referer) { /* We are asked to automatically set the previous URL as the @@ -1736,7 +2038,7 @@ CURLcode Curl_follow(struct SessionHandle *data, /* 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 = strrchr(protsep, '?'); + pathsep = strchr(protsep, '?'); if(pathsep) *pathsep=0; @@ -1784,8 +2086,15 @@ CURLcode Curl_follow(struct SessionHandle *data, /* We got a new absolute path for this server, cut off from the first slash */ pathsep = strchr(protsep, '/'); - if(pathsep) + 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 @@ -1857,7 +2166,6 @@ CURLcode Curl_follow(struct SessionHandle *data, data->change.url = newurl; newurl = NULL; /* don't free! */ - (void)newurl; infof(data, "Issue another request to this URL: '%s'\n", data->change.url); @@ -1955,14 +2263,16 @@ static CURLcode Curl_connect_host(struct SessionHandle *data, struct connectdata **conn) { - CURLcode res; - int urlchanged; + CURLcode res = CURLE_OK; + int urlchanged = FALSE; do { bool async; + bool protocol_done=TRUE; /* will be TRUE always since this is only used + within the easy interface */ Curl_pgrsTime(data, TIMER_STARTSINGLE); data->change.url_changed = FALSE; - res = Curl_connect(data, conn, &async); + res = Curl_connect(data, conn, &async, &protocol_done); if((CURLE_OK == res) && async) { /* Now, if async is TRUE here, we need to wait for the name @@ -1970,7 +2280,10 @@ Curl_connect_host(struct SessionHandle *data, res = Curl_wait_for_resolv(*conn, NULL); if(CURLE_OK == res) /* Resolved, continue with the connection */ - res = Curl_async_resolved(*conn); + res = Curl_async_resolved(*conn, &protocol_done); + else + /* if we can't resolve, we kill this "connection" now */ + (void)Curl_disconnect(*conn); } if(res) break; @@ -1980,10 +2293,10 @@ Curl_connect_host(struct SessionHandle *data, to the new URL */ urlchanged = data->change.url_changed; if ((CURLE_OK == res) && urlchanged) { - res = Curl_done(conn, res); + res = Curl_done(conn, res, FALSE); if(CURLE_OK == res) { char *gotourl = strdup(data->change.url); - res = Curl_follow(data, gotourl); + res = Curl_follow(data, gotourl, FALSE); if(res) free(gotourl); } @@ -1993,7 +2306,34 @@ Curl_connect_host(struct SessionHandle *data, return res; } +/* Returns TRUE and sets '*url' if a request retry is wanted */ +bool Curl_retry_request(struct connectdata *conn, + char **url) +{ + bool retry = FALSE; + struct SessionHandle *data = conn->data; + if((data->reqdata.keep.bytecount+conn->headerbytecount == 0) && + conn->bits.reuse && + !conn->bits.no_body) { + /* We got no data, we attempted to re-use a connection and yet we want a + "body". This might happen if the connection was left alive when we were + done using it before, but that was closed when we wanted to read from + it again. Bad luck. Retry the same request on a fresh connect! */ + infof(conn->data, "Connection died, retrying a fresh connect\n"); + *url = strdup(conn->data->change.url); + + conn->bits.close = TRUE; /* close this connection */ + conn->bits.retry = TRUE; /* mark this as a connection we're about + to retry. Marking it this way should + prevent i.e HTTP transfers to return + error just because nothing has been + transfered! */ + retry = TRUE; + } + + return retry; +} /* * Curl_perform() is the internal high-level function that gets called by the @@ -2006,6 +2346,7 @@ CURLcode Curl_perform(struct SessionHandle *data) CURLcode res2; struct connectdata *conn=NULL; char *newurl = NULL; /* possibly a new URL to follow to! */ + bool retry = FALSE; data->state.used_interface = Curl_if_easy; @@ -2024,44 +2365,26 @@ CURLcode Curl_perform(struct SessionHandle *data) res = Curl_connect_host(data, &conn); /* primary connection */ if(res == CURLE_OK) { - if (data->set.source_host) /* 3rd party transfer */ - res = Curl_pretransfersec(conn); - else - conn->sec_conn = NULL; - } - - if(res == CURLE_OK) { - - res = Curl_do(&conn); + 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); - /* for non 3rd party transfer only */ - if(res == CURLE_OK && !data->set.source_host) { + if(res == CURLE_OK) { res = Transfer(conn); /* now fetch that URL please */ if(res == CURLE_OK) { + retry = Curl_retry_request(conn, &newurl); - if((conn->keep.bytecount+conn->headerbytecount == 0) && - conn->bits.reuse) { - /* We got no data and we attempted to re-use a connection. This - might happen if the connection was left alive when we were done - using it before, but that was closed when we wanted to read - from it again. Bad luck. Retry the same request on a fresh - connect! */ - infof(data, "Connection died, retrying a fresh connect\n"); - newurl = strdup(conn->data->change.url); - - conn->bits.close = TRUE; /* close this connection */ - conn->bits.retry = TRUE; /* mark this as a connection we're about - to retry. Marking it this way should - prevent i.e HTTP transfers to return - error just because nothing has been - transfered! */ - } - else + if(!retry) /* - * We must duplicate the new URL here as the connection data - * may be free()ed in the Curl_done() function. + * We must duplicate the new URL here as the connection data may + * be free()ed in the Curl_done() function. */ - newurl = conn->newurl?strdup(conn->newurl):NULL; + newurl = data->reqdata.newurl?strdup(data->reqdata.newurl):NULL; } else { /* The transfer phase returned error, we mark the connection to get @@ -2079,16 +2402,14 @@ CURLcode Curl_perform(struct SessionHandle *data) /* Always run Curl_done(), even if some of the previous calls failed, but return the previous (original) error code */ - res2 = Curl_done(&conn, res); + res2 = Curl_done(&conn, res, FALSE); if(CURLE_OK == res) res = res2; } else /* Curl_do() failed, clean up left-overs in the done-call */ - res2 = Curl_done(&conn, res); - - (void)res2; + res2 = Curl_done(&conn, res, FALSE); /* * Important: 'conn' cannot be used here, since it may have been closed @@ -2096,7 +2417,7 @@ CURLcode Curl_perform(struct SessionHandle *data) */ if((res == CURLE_OK) && newurl) { - res = Curl_follow(data, newurl); + res = Curl_follow(data, newurl, retry); if(CURLE_OK == res) { newurl = NULL; continue; @@ -2110,6 +2431,19 @@ CURLcode Curl_perform(struct SessionHandle *data) 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 uncondionally, but don't clobber the return code if we already have an error code recorder */ res2 = Curl_posttransfer(data); @@ -2120,72 +2454,41 @@ CURLcode Curl_perform(struct SessionHandle *data) } /* - * Curl_Transfer() is called to setup some basic properties for the upcoming - * transfer. + * Curl_setup_transfer() is called to setup some basic properties for the + * upcoming transfer. */ CURLcode -Curl_Transfer(struct connectdata *c_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 */ - ) +Curl_setup_transfer( + struct connectdata *c_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 connectdata *conn = (struct connectdata *)c_conn; + struct SessionHandle *data = conn->data; + if(!conn) return CURLE_BAD_FUNCTION_ARGUMENT; curlassert((sockindex <= 1) && (sockindex >= -1)); /* now copy all input parameters */ - conn->sockfd = sockindex==-1? - CURL_SOCKET_BAD:conn->sock[sockindex]; - conn->size = size; + conn->sockfd = sockindex == -1 ? + CURL_SOCKET_BAD : conn->sock[sockindex]; + conn->writesockfd = writesockindex == -1 ? + CURL_SOCKET_BAD:conn->sock[writesockindex]; conn->bits.getheader = getheader; - conn->bytecountp = bytecountp; - conn->writesockfd = writesockindex==-1? - CURL_SOCKET_BAD:conn->sock[writesockindex]; - conn->writebytecountp = writecountp; - - return CURLE_OK; - -} - -/* - * Curl_pretransfersec() prepares the secondary connection (used for 3rd party - * FTP transfers). - */ -CURLcode Curl_pretransfersec(struct connectdata *conn) -{ - CURLcode status; - struct SessionHandle *data = conn->data; - struct connectdata *sec_conn = NULL; /* secondary connection */ - - /* update data with source host options */ - char *url = aprintf( "%s://%s/", conn->protostr, data->set.source_host); - - if(!url) - return CURLE_OUT_OF_MEMORY; - if(data->change.url_alloc) - free(data->change.url); - - data->change.url_alloc = TRUE; - data->change.url = url; - data->set.ftpport = data->set.source_port; - data->set.userpwd = data->set.source_userpwd; + data->reqdata.size = size; + data->reqdata.bytecountp = bytecountp; + data->reqdata.writebytecountp = writecountp; - /* secondary connection */ - status = Curl_connect_host(data, &sec_conn); - if(CURLE_OK == status) { - sec_conn->data = data; - conn->sec_conn = sec_conn; - } - - return status; + return CURLE_OK; } diff --git a/Utilities/cmcurl/transfer.h b/Utilities/cmcurl/transfer.h index 1b7eb82..3c415cc 100644 --- a/Utilities/cmcurl/transfer.h +++ b/Utilities/cmcurl/transfer.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,22 +24,21 @@ ***************************************************************************/ CURLcode Curl_perform(struct SessionHandle *data); CURLcode Curl_pretransfer(struct SessionHandle *data); -CURLcode Curl_pretransfersec(struct connectdata *conn); +CURLcode Curl_second_connect(struct connectdata *conn); CURLcode Curl_posttransfer(struct SessionHandle *data); -CURLcode Curl_follow(struct SessionHandle *data, char *newurl); +CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry); CURLcode Curl_readwrite(struct connectdata *conn, bool *done); -void Curl_single_fdset(struct connectdata *conn, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *exc_fd_set, - int *max_fd); +int Curl_single_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); CURLcode Curl_readwrite_init(struct connectdata *conn); - +CURLcode Curl_readrewind(struct connectdata *conn); CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp); +bool Curl_retry_request(struct connectdata *conn, char **url); /* This sets up a forthcoming transfer */ CURLcode -Curl_Transfer (struct connectdata *data, +Curl_setup_transfer (struct connectdata *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 */ diff --git a/Utilities/cmcurl/url.c b/Utilities/cmcurl/url.c index b532e20..bc896b3 100644 --- a/Utilities/cmcurl/url.c +++ b/Utilities/cmcurl/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,12 +30,15 @@ #include <stdarg.h> #include <stdlib.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> - +#endif #include <errno.h> -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef WIN32 #include <time.h> #include <io.h> #else @@ -43,7 +46,9 @@ #include <sys/socket.h> #endif #include <netinet/in.h> +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -61,10 +66,6 @@ #include <sys/param.h> #endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - #ifdef VMS #include <in.h> #include <inet.h> @@ -74,18 +75,14 @@ #include <setjmp.h> #endif -#ifndef HAVE_SELECT -#error "We can't compile without select() support!" -#endif #ifndef HAVE_SOCKET #error "We can't compile without socket() support!" #endif - - #endif #ifdef USE_LIBIDN #include <idna.h> +#include <tld.h> #include <stringprep.h> #ifdef HAVE_IDN_FREE_H #include <idn-free.h> @@ -98,41 +95,45 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by instead */ #define idn_free(x) (free)(x) #endif -#endif +#endif /* USE_LIBIDN */ -#ifdef HAVE_OPENSSL_ENGINE_H -#include <openssl/engine.h> -#endif #include "urldata.h" #include "netrc.h" #include "formdata.h" #include "base64.h" -#include "ssluse.h" +#include "sslgen.h" #include "hostip.h" -#include "if2ip.h" #include "transfer.h" #include "sendf.h" #include "progress.h" #include "cookie.h" #include "strequal.h" +#include "strerror.h" #include "escape.h" #include "strtok.h" #include "share.h" #include "content_encoding.h" #include "http_digest.h" #include "http_negotiate.h" +#include "select.h" +#include "multiif.h" +#include "easyif.h" /* And now for the protocols */ #include "ftp.h" #include "dict.h" #include "telnet.h" +#include "tftp.h" #include "http.h" #include "file.h" #include "ldap.h" +#include "ssh.h" #include "url.h" #include "connect.h" #include "inet_ntop.h" +#include "http_ntlm.h" +#include "socks.h" #include <ca-bundle.h> #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) @@ -143,9 +144,9 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by #include <curl/mprintf.h> #ifdef HAVE_KRB4 -#include "security.h" +#include "krb4.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -157,19 +158,32 @@ static bool ConnectionExists(struct SessionHandle *data, struct connectdata **usethis); static long ConnectionStore(struct SessionHandle *data, struct connectdata *conn); -static bool safe_strequal(char* str1, char* str2); +static bool IsPipeliningPossible(struct SessionHandle *handle); +static bool IsPipeliningEnabled(struct SessionHandle *handle); +static void conn_free(struct connectdata *conn); + +static void signalPipeClose(struct curl_llist *pipe); + +#define MAX_PIPELINE_LENGTH 5 + +/* + * We use this ZERO_NULL to avoid picky compiler warnings, + * when assigning a NULL pointer to a function pointer var. + */ + +#define ZERO_NULL 0 #ifndef USE_ARES -/* not for Win32, unless it is cygwin - not for ares builds */ -#if !defined(WIN32) || defined(__CYGWIN32__) +/* not for ares builds */ + +#ifndef WIN32 +/* not for WIN32 builds */ -#ifndef RETSIGTYPE -#define RETSIGTYPE void -#endif #ifdef HAVE_SIGSETJMP extern sigjmp_buf curl_jmpenv; #endif + +#ifdef SIGALRM static RETSIGTYPE alarmfunc(int sig) { @@ -180,7 +194,8 @@ RETSIGTYPE alarmfunc(int sig) #endif return; } -#endif +#endif /* SIGALRM */ +#endif /* WIN32 */ #endif /* USE_ARES */ void Curl_safefree(void *ptr) @@ -189,6 +204,13 @@ void Curl_safefree(void *ptr) free(ptr); } +static void close_connections(struct SessionHandle *data) +{ + /* Loop through all open connections and kill them one by one */ + while(-1 != ConnectionKillOne(data)) + ; /* empty loop */ +} + /* * This is the internal function curl_easy_cleanup() calls. This should * cleanup and free all resources associated with this sessionhandle. @@ -200,22 +222,93 @@ void Curl_safefree(void *ptr) CURLcode Curl_close(struct SessionHandle *data) { - /* Loop through all open connections and kill them one by one */ - while(-1 != ConnectionKillOne(data)); + struct Curl_multi *m = data->multi; -#ifdef USE_SSLEAY - /* Close down all open SSL info and sessions */ - Curl_SSL_Close_All(data); +#ifdef CURLDEBUG + /* only for debugging, scan through all connections and see if there's a + pipe reference still identifying this handle */ + + if(data->state.is_in_pipeline) + fprintf(stderr, "CLOSED when in pipeline!\n"); + + if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) { + struct conncache *c = data->state.connc; + int i; + struct curl_llist *pipe; + struct curl_llist_element *curr; + struct connectdata *connptr; + + for(i=0; i< c->num; i++) { + connptr = c->connects[i]; + if(!connptr) + continue; + + pipe = connptr->send_pipe; + if(pipe) { + for (curr = pipe->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, connptr->bits.done); + } + } + } + pipe = connptr->recv_pipe; + if(pipe) { + for (curr = pipe->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, connptr->bits.done); + } + } + } + } + } #endif - if(data->change.cookielist) /* clean up list if any */ - curl_slist_free_all(data->change.cookielist); + 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); - Curl_safefree(data->state.auth_host); - Curl_safefree(data->state.scratch); + 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->change.proxy_alloc) - free(data->change.proxy); + 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->share && data->share->hostcache) ) { + if ( !Curl_global_host_cache_use(data)) { + Curl_hash_destroy(data->dns.hostcache); + } + } + + /* Free the pathbuffer */ + Curl_safefree(data->reqdata.pathbuffer); + Curl_safefree(data->reqdata.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); if(data->change.referer_alloc) free(data->change.referer); @@ -225,32 +318,52 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_safefree(data->state.headerbuff); -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); if(data->set.cookiejar) { + if(data->change.cookielist) { + /* If there is a list of cookie files to read, do it first so that + we have all the told files read before we write the new jar */ + Curl_cookie_loadfiles(data); + } + /* we have a "destination" for all the cookies to get dumped to */ if(Curl_cookie_output(data->cookies, data->set.cookiejar)) infof(data, "WARNING: failed to save cookies in %s\n", data->set.cookiejar); } + else { + if(data->change.cookielist) + /* since nothing is written, we can just free the list of cookie file + names */ + curl_slist_free_all(data->change.cookielist); /* clean up list */ + } if( !data->share || (data->cookies != data->share->cookies) ) { Curl_cookie_cleanup(data->cookies); } Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - - Curl_digest_cleanup(data); #endif - /* free the connection cache */ - free(data->state.connects); + Curl_digest_cleanup(data); Curl_safefree(data->info.contenttype); -#ifdef USE_ARES /* this destroys the channel and we cannot use it anymore after this */ ares_destroy(data->state.areschannel); -#endif + +#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) @@ -260,6 +373,103 @@ CURLcode Curl_close(struct SessionHandle *data) return CURLE_OK; } +/* create a connection cache of a private or multi type */ +struct conncache *Curl_mk_connc(int type, + int amount) /* set -1 to use default */ +{ + /* It is subject for debate how many default connections to have for a multi + connection cache... */ + int default_amount = amount == -1? + ((type == CONNCACHE_PRIVATE)?5:10):amount; + struct conncache *c; + + c= calloc(sizeof(struct conncache), 1); + if(!c) + return NULL; + + c->connects = calloc(sizeof(struct connectdata *), default_amount); + if(!c->connects) { + free(c); + return NULL; + } + + c->num = default_amount; + + return c; +} + +/* Change number of entries of a connection cache */ +CURLcode Curl_ch_connc(struct SessionHandle *data, + struct conncache *c, + long newamount) +{ + long i; + struct connectdata **newptr; + + if(newamount < 1) + newamount = 1; /* we better have at least one entry */ + + if(!c) { + /* we get a NULL pointer passed in as connection cache, which means that + there is no cache created for this SessionHandle just yet, we create a + brand new with the requested size. + */ + data->state.connc = Curl_mk_connc(CONNCACHE_PRIVATE, newamount); + if(!data->state.connc) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; + } + + if(newamount < c->num) { + /* Since this number is *decreased* from the existing number, we must + close the possibly open connections that live on the indexes that + are being removed! + + NOTE: for conncache_multi cases we must make sure that we only + close handles not in use. + */ + for(i=newamount; i< c->num; i++) + Curl_disconnect(c->connects[i]); + + /* If the most recent connection is no longer valid, mark it + invalid. */ + if(data->state.lastconnect <= newamount) + data->state.lastconnect = -1; + } + if(newamount > 0) { + newptr= (struct connectdata **) + realloc(c->connects, sizeof(struct connectdata *) * newamount); + if(!newptr) + /* we closed a few connections in vain, but so what? */ + return CURLE_OUT_OF_MEMORY; + + /* nullify the newly added pointers */ + for(i=c->num; i<newamount; i++) + newptr[i] = NULL; + + c->connects = newptr; + c->num = newamount; + } + /* we no longer support less than 1 as size for the connection cache, and + I'm not sure it ever worked to set it to zero */ + return CURLE_OK; +} + +/* Free a connection cache. This is called from Curl_close() and + curl_multi_cleanup(). */ +void Curl_rm_connc(struct conncache *c) +{ + if(c->connects) { + int i; + for(i = 0; i < c->num; ++i) + conn_free(c->connects[i]); + + free(c->connects); + } + + free(c); +} + /** * Curl_open() * @@ -272,12 +482,15 @@ CURLcode Curl_open(struct SessionHandle **curl) { CURLcode res = CURLE_OK; struct SessionHandle *data; + /* Very simple start-up: alloc the struct, init it with zeroes and return */ data = (struct SessionHandle *)calloc(1, sizeof(struct SessionHandle)); if(!data) /* this is a very serious error */ return CURLE_OUT_OF_MEMORY; + data->magic = CURLEASY_MAGIC_NUMBER; + #ifdef USE_ARES if(ARES_SUCCESS != ares_init(&data->state.areschannel)) { free(data); @@ -305,14 +518,27 @@ CURLcode Curl_open(struct SessionHandle **curl) /* use fread as default function to read input */ data->set.fread = (curl_read_callback)fread; - data->set.infilesize = -1; /* we don't know any size */ + /* conversion callbacks for non-ASCII hosts */ + data->set.convfromnetwork = (curl_conv_callback)ZERO_NULL; + data->set.convtonetwork = (curl_conv_callback)ZERO_NULL; + data->set.convfromutf8 = (curl_conv_callback)ZERO_NULL; + +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) + /* conversion descriptors for iconv calls */ + data->outbound_cd = (iconv_t)-1; + data->inbound_cd = (iconv_t)-1; + data->utf8_cd = (iconv_t)-1; +#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ + data->set.infilesize = -1; /* we don't know any size */ + data->set.postfieldsize = -1; + data->set.maxredirs = -1; /* allow any amount by default */ data->state.current_speed = -1; /* init to negative == impossible */ data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */ data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ data->set.ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ - + data->set.ftp_filemethod = FTPFILE_MULTICWD; data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ /* make libcurl quiet by default: */ @@ -327,16 +553,17 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic */ data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */ - /* create an array with connection data struct pointers */ - data->state.numconnects = 5; /* hard-coded right now */ - data->state.connects = (struct connectdata **) - malloc(sizeof(struct connectdata *) * data->state.numconnects); + /* This no longer creates a connection cache here. It is instead made on + the first call to curl_easy_perform() or when the handle is added to a + multi stack. */ - if(!data->state.connects) - res = CURLE_OUT_OF_MEMORY; - else - memset(data->state.connects, 0, - sizeof(struct connectdata *)*data->state.numconnects); + data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth + type */ + + /* most recent connection is not yet defined */ + data->state.lastconnect = -1; + + Curl_easy_initHandleData(data); /* * libcurl 7.10 introduced SSL verification *by default*! This needs to be @@ -344,32 +571,31 @@ CURLcode Curl_open(struct SessionHandle **curl) */ data->set.ssl.verifypeer = TRUE; data->set.ssl.verifyhost = 2; + data->set.ssl.sessionid = TRUE; /* session ID caching enabled by default */ #ifdef CURL_CA_BUNDLE - /* This is our prefered CA cert bundle since install time */ + /* This is our preferred CA cert bundle since install time */ data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; #endif } if(res) { -#ifdef USE_ARES ares_destroy(data->state.areschannel); -#endif if(data->state.headerbuff) free(data->state.headerbuff); free(data); data = NULL; } + else + *curl = data; - *curl = data; - return CURLE_OK; + return res; } -CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, + va_list param) { - va_list param; - char *cookiefile; - - va_start(param, option); + char *argptr; + CURLcode result = CURLE_OK; switch(option) { case CURLOPT_DNS_CACHE_TIMEOUT: @@ -382,7 +608,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) Curl_global_host_cache_init(); } - data->set.global_dns_cache = use_cache; + data->set.global_dns_cache = (bool)(0 != use_cache); } break; case CURLOPT_SSL_CIPHER_LIST: @@ -408,75 +634,40 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * Set the absolute number of maximum simultaneous alive connection that * libcurl is allowed to have. */ - { - long newconnects= va_arg(param, long); - struct connectdata **newptr; - long i; - - if(newconnects < data->state.numconnects) { - /* Since this number is *decreased* from the existing number, we must - close the possibly open connections that live on the indexes that - are being removed! */ - for(i=newconnects; i< data->state.numconnects; i++) - Curl_disconnect(data->state.connects[i]); - } - if(newconnects) { - newptr= (struct connectdata **) - realloc(data->state.connects, - sizeof(struct connectdata *) * newconnects); - if(!newptr) - /* we closed a few connections in vain, but so what? */ - return CURLE_OUT_OF_MEMORY; - - /* nullify the newly added pointers */ - for(i=data->state.numconnects; i<newconnects; i++) { - newptr[i] = NULL; - } - - data->state.connects = newptr; - data->state.numconnects = newconnects; - } - else { - /* zero makes NO cache at all */ - if(data->state.connects) - free(data->state.connects); - data->state.connects=NULL; - data->state.numconnects=0; - } - } + 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 = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg(param, long)?TRUE:FALSE; + data->set.hide_progress = (bool)(0 != va_arg(param, long)); if(data->set.hide_progress) data->progress.flags |= PGRS_HIDE; else @@ -486,7 +677,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Do not include the body part in the output data stream. */ - data->set.opt_no_body = va_arg(param, long)?TRUE:FALSE; + data->set.opt_no_body = (bool)(0 != va_arg(param, long)); if(data->set.opt_no_body) /* in HTTP lingo, this means using the HEAD request */ data->set.httpreq = HTTPREQ_HEAD; @@ -496,7 +687,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * Don't output the >=300 error code HTML-page, but instead only * return error. */ - data->set.http_fail_on_error = va_arg(param, long)?TRUE:FALSE; + data->set.http_fail_on_error = (bool)(0 != va_arg(param, long)); break; case CURLOPT_UPLOAD: case CURLOPT_PUT: @@ -504,7 +695,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * We want to sent data to the remote host. If this is HTTP, that equals * using the PUT request. */ - data->set.upload = va_arg(param, long)?TRUE:FALSE; + 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; @@ -514,14 +705,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * 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 = va_arg(param, long)?TRUE:FALSE; + 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 = va_arg( param , long )?TRUE:FALSE; + data->set.ftp_create_missing_dirs = (bool)(0 != va_arg(param, long)); break; case CURLOPT_FTP_RESPONSE_TIMEOUT: /* @@ -535,13 +726,19 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * An FTP option that changes the command to one that asks for a list * only, no file info details. */ - data->set.ftp_list_only = va_arg(param, long)?TRUE:FALSE; + data->set.ftp_list_only = (bool)(0 != va_arg(param, long)); break; case CURLOPT_FTPAPPEND: /* * We want to upload and append to an existing (FTP) file. */ - data->set.ftp_append = va_arg(param, long)?TRUE:FALSE; + 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: /* @@ -562,7 +759,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * * Transfer using ASCII (instead of BINARY). */ - data->set.ftp_ascii = va_arg(param, long)?TRUE:FALSE; + data->set.prefer_ascii = (bool)(0 != va_arg(param, long)); break; case CURLOPT_TIMECONDITION: /* @@ -591,7 +788,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Switch on automatic referer that gets set if curl follows locations. */ - data->set.http_auto_referer = va_arg(param, long)?1:0; + data->set.http_auto_referer = (bool)(0 != va_arg(param, long)); break; case CURLOPT_ENCODING: @@ -613,7 +810,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Follow Location: header hints on a HTTP-server. */ - data->set.http_follow_location = va_arg(param, long)?TRUE:FALSE; + data->set.http_follow_location = (bool)(0 != va_arg(param, long)); break; case CURLOPT_UNRESTRICTED_AUTH: @@ -622,7 +819,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * hostname changed. */ data->set.http_disable_hostname_check_before_authentication = - va_arg(param, long)?TRUE:FALSE; + (bool)(0 != va_arg(param, long)); break; case CURLOPT_MAXREDIRS: @@ -637,17 +834,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* 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)) + 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_POSTFIELDS: /* - * A string with POST data. Makes curl HTTP POST. + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. */ data->set.postfields = va_arg(param, char *); - if(data->set.postfields) - data->set.httpreq = HTTPREQ_POST; + data->set.httpreq = HTTPREQ_POST; break; case CURLOPT_POSTFIELDSIZE: @@ -671,8 +871,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * Set to make us do HTTP POST */ data->set.httppost = va_arg(param, struct curl_httppost *); - if(data->set.httppost) - data->set.httpreq = HTTPREQ_POST_FORM; + data->set.httpreq = HTTPREQ_POST_FORM; + data->set.opt_no_body = FALSE; /* this is implied */ break; case CURLOPT_REFERER: @@ -708,6 +908,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) 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. @@ -719,12 +920,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Set cookie file to read and parse. Can be used multiple times. */ - cookiefile = (char *)va_arg(param, void *); - if(cookiefile) { + 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, cookiefile); + cl = curl_slist_append(data->change.cookielist, argptr); if(!cl) return CURLE_OUT_OF_MEMORY; @@ -763,8 +964,47 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * We run mostly with the original cookie spec, as hardly anyone implements * anything else. */ - data->set.cookiesession = (bool)va_arg(param, long); + 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; + } + + 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: /* @@ -773,6 +1013,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) 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; @@ -788,7 +1029,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Tunnel operations through the proxy instead of normal proxy use */ - data->set.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE; + data->set.tunnel_thru_httpproxy = (bool)(0 != va_arg(param, long)); break; case CURLOPT_CUSTOMREQUEST: @@ -803,27 +1044,6 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) and this just changes the actual request keyword */ break; - 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. - */ - if(data->change.proxy_alloc) { - /* - * The already set string is allocated, free that first - */ - data->change.proxy_alloc=FALSE;; - free(data->change.proxy); - } - data->set.set_proxy = va_arg(param, char *); - data->change.proxy = data->set.set_proxy; - break; - case CURLOPT_PROXYPORT: /* * Explicitly set HTTP proxy port number. @@ -838,7 +1058,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) { long auth = va_arg(param, long); /* switch off bits we can't support */ -#ifndef USE_SSLEAY +#ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif #ifndef HAVE_GSSAPI @@ -858,7 +1078,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) { long auth = va_arg(param, long); /* switch off bits we can't support */ -#ifndef USE_SSLEAY +#ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif #ifndef HAVE_GSSAPI @@ -872,6 +1092,19 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) 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. + */ + data->set.proxy = va_arg(param, char *); + break; + case CURLOPT_WRITEHEADER: /* * Custom pointer to pass the header write callback function @@ -896,15 +1129,27 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * Use FTP PORT, this also specifies which IP address to use */ data->set.ftpport = va_arg(param, char *); - data->set.ftp_use_port = data->set.ftpport?1:0; + data->set.ftp_use_port = (bool)(NULL != data->set.ftpport); break; case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = va_arg(param, long)?TRUE:FALSE; + data->set.ftp_use_eprt = (bool)(0 != va_arg(param, long)); break; case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE; + data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_SSL_CCC: + data->set.ftp_use_ccc = (bool)(0 != 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: @@ -935,6 +1180,22 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) */ 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 @@ -1089,6 +1350,36 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* When set to NULL, reset to our internal default function */ data->set.fread = (curl_read_callback)fread; 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 = 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 @@ -1123,67 +1414,49 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * String that holds the SSL crypto engine. */ -#ifdef HAVE_OPENSSL_ENGINE_H - { - const char *cpTemp = va_arg(param, char *); - ENGINE *e; - if (cpTemp && cpTemp[0]) { - e = ENGINE_by_id(cpTemp); - if (e) { - if (data->engine) { - ENGINE_free(data->engine); - } - data->engine = e; - } - else { - failf(data, "SSL Engine '%s' not found", cpTemp); - return CURLE_SSL_ENGINE_NOTFOUND; - } - } - } + argptr = va_arg(param, char *); + if (argptr && argptr[0]) + result = Curl_ssl_set_engine(data, argptr); break; -#else - return CURLE_SSL_ENGINE_NOTFOUND; -#endif + case CURLOPT_SSLENGINE_DEFAULT: /* * flag to set engine as default. */ -#ifdef HAVE_OPENSSL_ENGINE_H - if (data->engine) { - if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) { -#ifdef DEBUG - fprintf(stderr,"set default crypto engine\n"); -#endif - } - else { -#ifdef DEBUG - failf(data, "set default crypto engine failed"); -#endif - return CURLE_SSL_ENGINE_SETFAILED; - } - } -#endif + result = Curl_ssl_set_engine_default(data); break; case CURLOPT_CRLF: /* - * Kludgy option to enable CRLF convertions. Subject for removal. + * Kludgy option to enable CRLF conversions. Subject for removal. */ - data->set.crlf = va_arg(param, long)?TRUE:FALSE; + data->set.crlf = (bool)(0 != va_arg(param, long)); break; + case CURLOPT_INTERFACE: /* - * Set what interface to bind to when performing an operation and thus - * what from-IP your connection will use. + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. */ data->set.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_KRB4LEVEL: /* * A string that defines the krb4 security level. */ data->set.krb4_level = va_arg(param, char *); - data->set.krb4=data->set.krb4_level?TRUE:FALSE; + data->set.krb4 = (bool)(NULL != data->set.krb4_level); break; case CURLOPT_SSL_VERIFYPEER: /* @@ -1201,7 +1474,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Set a SSL_CTX callback */ - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); + data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); break; case CURLOPT_SSL_CTX_DATA: /* @@ -1248,7 +1521,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * The application asks not to set any signal() or alarm() handlers, * even when using a timeout. */ - data->set.no_signal = va_arg(param, long) ? TRUE : FALSE; + data->set.no_signal = (bool)(0 != va_arg(param, long)); break; case CURLOPT_SHARE: @@ -1260,8 +1533,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) if(data->share) { Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - if(data->share->hostcache == data->hostcache) - data->hostcache = NULL; + if(data->dns.hostcachetype == HCACHE_SHARED) { + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } if(data->share->cookies == data->cookies) data->cookies = NULL; @@ -1281,13 +1556,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) data->share->dirty++; if(data->share->hostcache) { - /* use shared host cache, first free own one if any */ - if(data->hostcache) - Curl_hash_destroy(data->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->hostcache = data->share->hostcache; + data->dns.hostcache = data->share->hostcache; + data->dns.hostcachetype = HCACHE_SHARED; } -#ifndef CURL_DISABLE_HTTP +#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) @@ -1298,7 +1574,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) /* check cookie list is set */ if(!data->cookies) data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE ); @@ -1319,7 +1595,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) /* * Set private data pointer. */ - data->set.private = va_arg(param, char *); + data->set.private_data = va_arg(param, char *); break; case CURLOPT_MAXFILESIZE: @@ -1336,6 +1612,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) data->set.ftp_ssl = (curl_ftpssl)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; @@ -1352,65 +1635,123 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) * Enable or disable TCP_NODELAY, which will disable/enable the Nagle * algorithm */ - data->set.tcp_nodelay = (bool)va_arg(param, long); + data->set.tcp_nodelay = (bool)(0 != va_arg(param, long)); break; - /*********** 3rd party transfer options ***********/ - case CURLOPT_SOURCE_HOST: /* - * Use SOURCE HOST - */ - data->set.source_host = va_arg(param, char *); - data->set.printhost = (data->set.source_host != NULL); + case CURLOPT_SOURCE_URL: + case CURLOPT_SOURCE_USERPWD: + case CURLOPT_SOURCE_QUOTE: + case CURLOPT_SOURCE_PREQUOTE: + case CURLOPT_SOURCE_POSTQUOTE: + These former 3rd party transfer options are deprecated */ + + case CURLOPT_FTP_ACCOUNT: + data->set.ftp_account = va_arg(param, char *); break; - case CURLOPT_SOURCE_PORT: - /* - * Use SOURCE PORT - */ - data->set.source_port = va_arg(param, char *); + case CURLOPT_IGNORE_CONTENT_LENGTH: + data->set.ignorecl = (bool)(0 != va_arg(param, long)); break; - case CURLOPT_SOURCE_USERPWD: + case CURLOPT_CONNECT_ONLY: /* - * Use SOURCE USER[:PASSWORD] + * No data transfer, set up connection and let application use the socket */ - data->set.source_userpwd = va_arg(param, char *); + data->set.connect_only = (bool)(0 != va_arg(param, long)); + break; + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + data->set.ftp_alternative_to_user = va_arg(param, char *); break; - case CURLOPT_SOURCE_PATH: + case CURLOPT_SOCKOPTFUNCTION: /* - * Use SOURCE PATH + * socket callback function: called after socket() but before connect() */ - data->set.source_path = va_arg(param, char *); + data->set.fsockopt = va_arg(param, curl_sockopt_callback); break; - case CURLOPT_PASV_HOST: + case CURLOPT_SOCKOPTDATA: /* - * Indicates whether source or target host is passive + * socket callback data pointer. Might be NULL. */ - data->set.pasvHost = va_arg(param, long)?CURL_SOURCE_PASV:CURL_TARGET_PASV; + data->set.sockopt_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_SOURCE_PREQUOTE: + case CURLOPT_SSH_PUBLIC_KEYFILE: /* - * List of RAW FTP commands to use before a transfer on the source host + * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - data->set.source_prequote = va_arg(param, struct curl_slist *); + data->set.ssh_public_key = va_arg(param, char *); break; - case CURLOPT_SOURCE_POSTQUOTE: + case CURLOPT_SSH_PRIVATE_KEYFILE: /* - * List of RAW FTP commands to use after a transfer on the source host + * Use this file instead of the $HOME/.ssh/id_dsa file */ - data->set.source_postquote = va_arg(param, struct curl_slist *); + data->set.ssh_private_key = va_arg(param, char *); break; default: /* unknown tag and its companion, just ignore: */ - return CURLE_FAILED_INIT; /* correct this */ + result = CURLE_FAILED_INIT; /* correct this */ + break; } - return CURLE_OK; + + return result; +} + +static void conn_free(struct connectdata *conn) +{ + if (!conn) + return; + + /* close possibly still open sockets */ + if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) + sclose(conn->sock[SECONDARYSOCKET]); + if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) + sclose(conn->sock[FIRSTSOCKET]); + + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + Curl_safefree(conn->proxyuser); + Curl_safefree(conn->proxypasswd); + Curl_safefree(conn->allocptr.proxyuserpwd); + Curl_safefree(conn->allocptr.uagent); + Curl_safefree(conn->allocptr.userpwd); + Curl_safefree(conn->allocptr.accept_encoding); + Curl_safefree(conn->allocptr.rangeline); + Curl_safefree(conn->allocptr.ref); + Curl_safefree(conn->allocptr.host); + Curl_safefree(conn->allocptr.cookiehost); + Curl_safefree(conn->ip_addr_str); + Curl_safefree(conn->trailer); + Curl_safefree(conn->host.rawalloc); /* host name buffer */ + Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ + + Curl_llist_destroy(conn->send_pipe, NULL); + Curl_llist_destroy(conn->recv_pipe, NULL); + + /* possible left-overs from the async name resolvers */ +#if defined(USE_ARES) + Curl_safefree(conn->async.hostname); + Curl_safefree(conn->async.os_specific); +#elif defined(CURLRES_THREADED) + Curl_destroy_thread_data(&conn->async); +#endif + + Curl_free_ssl_config(&conn->ssl_config); + + free(conn); /* free all the connection oriented data */ } CURLcode Curl_disconnect(struct connectdata *conn) @@ -1418,17 +1759,30 @@ 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 */ + /* * The range string is usually freed in curl_done(), but we might * get here *instead* if we fail prematurely. Thus we need to be able * to free this resource here as well. */ - if(conn->bits.rangestringalloc) { - free(conn->range); - conn->bits.rangestringalloc = FALSE; + if(data->reqdata.rangestringalloc) { + free(data->reqdata.range); + data->reqdata.rangestringalloc = FALSE; } if((conn->ntlm.state != NTLMSTATE_NONE) || @@ -1442,9 +1796,11 @@ CURLcode Curl_disconnect(struct connectdata *conn) data->state.authproxy.done = FALSE; data->state.authproxy.picked = - data->state.authhost.want; + data->state.authproxy.want; data->state.authproblem = FALSE; + + Curl_ntlm_cleanup(conn); } if(conn->curl_disconnect) @@ -1454,15 +1810,12 @@ CURLcode Curl_disconnect(struct connectdata *conn) if(-1 != conn->connectindex) { /* unlink ourselves! */ infof(data, "Closing connection #%ld\n", conn->connectindex); - data->state.connects[conn->connectindex] = NULL; + 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; } - Curl_safefree(conn->proto.generic); - Curl_safefree(conn->newurl); - Curl_safefree(conn->pathbuffer); /* the URL path buffer */ - - Curl_safefree(conn->host.rawalloc); /* host name buffer */ - Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ #ifdef USE_LIBIDN if(conn->host.encalloc) idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed @@ -1473,37 +1826,16 @@ CURLcode Curl_disconnect(struct connectdata *conn) freed with idn_free() since this was allocated by libidn */ #endif - Curl_SSL_Close(conn); - - /* close possibly still open sockets */ - if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) - sclose(conn->sock[SECONDARYSOCKET]); - if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) - sclose(conn->sock[FIRSTSOCKET]); - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->proxyuser); - Curl_safefree(conn->proxypasswd); - Curl_safefree(conn->allocptr.proxyuserpwd); - Curl_safefree(conn->allocptr.uagent); - Curl_safefree(conn->allocptr.userpwd); - Curl_safefree(conn->allocptr.accept_encoding); - Curl_safefree(conn->allocptr.rangeline); - Curl_safefree(conn->allocptr.ref); - Curl_safefree(conn->allocptr.host); - Curl_safefree(conn->allocptr.cookiehost); + Curl_ssl_close(conn); -#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \ - defined(USE_THREADING_GETADDRINFO) - /* possible left-overs from the async name resolve */ - Curl_safefree(conn->async.hostname); - Curl_safefree(conn->async.os_specific); -#endif - - Curl_free_ssl_config(&conn->ssl_config); + /* Indicate to all handles on the pipe that we're dead */ + if (IsPipeliningEnabled(data)) { + signalPipeClose(conn->send_pipe); + signalPipeClose(conn->recv_pipe); + } - free(conn); /* free all the connection oriented data */ + conn_free(conn); return CURLE_OK; } @@ -1517,16 +1849,8 @@ static bool SocketIsDead(curl_socket_t sock) { int sval; bool ret_val = TRUE; - fd_set check_set; - struct timeval to; - - FD_ZERO(&check_set); - FD_SET(sock, &check_set); - to.tv_sec = 0; - to.tv_usec = 0; - - sval = select(sock + 1, &check_set, 0, 0, &to); + sval = Curl_select(sock, CURL_SOCKET_BAD, 0); if(sval == 0) /* timeout */ ret_val = FALSE; @@ -1534,10 +1858,114 @@ static bool SocketIsDead(curl_socket_t sock) return ret_val; } +static bool IsPipeliningPossible(struct SessionHandle *handle) +{ + if (handle->multi && Curl_multi_canPipeline(handle->multi) && + (handle->set.httpreq == HTTPREQ_GET || + handle->set.httpreq == HTTPREQ_HEAD) && + handle->set.httpversion != CURL_HTTP_VERSION_1_0) + return TRUE; + + return FALSE; +} + +static bool IsPipeliningEnabled(struct SessionHandle *handle) +{ + if (handle->multi && Curl_multi_canPipeline(handle->multi)) + return TRUE; + + return FALSE; +} + +void Curl_addHandleToPipeline(struct SessionHandle *data, + struct curl_llist *pipe) +{ +#ifdef CURLDEBUG + if(!IsPipeliningPossible(data)) { + /* when not pipelined, there MUST be no handle in the list already */ + if(pipe->head) + infof(data, "PIPE when no PIPE supposed!\n"); + } +#endif + Curl_llist_insert_next(pipe, pipe->tail, data); +} + + +int Curl_removeHandleFromPipeline(struct SessionHandle *handle, + struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + if (curr->ptr == handle) { + Curl_llist_remove(pipe, curr, NULL); + return 1; /* we removed a handle */ + } + curr = curr->next; + } + + return 0; +} + +#if 0 /* this code is saved here as it is useful for debugging purposes */ +static void Curl_printPipeline(struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + infof(data, "Handle in pipeline: %s\n", + data->reqdata.path); + curr = curr->next; + } +} +#endif + +bool Curl_isHandleAtHead(struct SessionHandle *handle, + struct curl_llist *pipe) +{ + struct curl_llist_element *curr = pipe->head; + if (curr) { + return (bool)(curr->ptr == handle); + } + + return FALSE; +} + +static void signalPipeClose(struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + struct curl_llist_element *next = curr->next; + struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + +#ifdef CURLDEBUG /* debug-only code */ + if(data->magic != CURLEASY_MAGIC_NUMBER) { + /* MAJOR BADNESS */ + fprintf(stderr, "signalPipeClose() found BAAD easy handle\n"); + } + else +#endif + + data->state.pipe_broke = TRUE; + Curl_llist_remove(pipe, curr, NULL); + curr = next; + } +} + + /* * Given one filled in connection struct (named needle), this function should - * detect if there already is one that have all the significant details + * detect if there already is one that has all the significant details * exactly the same and thus should be used instead. + * + * If there is a match, this function returns TRUE - and has marked the + * connection as 'in-use'. It must later be called with ConnectionDone() to + * return back to 'idle' (unused) state. */ static bool ConnectionExists(struct SessionHandle *data, @@ -1546,18 +1974,58 @@ ConnectionExists(struct SessionHandle *data, { long i; struct connectdata *check; + bool canPipeline = IsPipeliningPossible(data); - for(i=0; i< data->state.numconnects; i++) { + for(i=0; i< data->state.connc->num; i++) { bool match = FALSE; /* * Note that if we use a HTTP proxy, we check connections to that * proxy and not to the actual remote server. */ - check = data->state.connects[i]; + check = data->state.connc->connects[i]; if(!check) /* NULL pointer means not filled-in entry */ continue; + 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 */ + } + + infof(data, "Examining connection #%ld for reuse\n", check->connectindex); + + if(check->inuse && !canPipeline) { + /* 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 has not finished name resolve, can't reuse\n", + check->connectindex); + continue; + } +#endif + + if (check->send_pipe->size + + check->recv_pipe->size >= MAX_PIPELINE_LENGTH) { + infof(data, "Connection #%ld has its pipeline full, can't reuse\n", + check->connectindex); + continue; + } + + if (data->state.is_in_pipeline && check->bits.close) { + /* Don't pick a connection that is going to be closed */ + infof(data, "Connection #%ld has been marked for close, can't reuse\n", + check->connectindex); + continue; + } + if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL)) /* don't do mixed SSL and non-SSL connections */ continue; @@ -1579,12 +2047,16 @@ ConnectionExists(struct SessionHandle *data, ssl options as well */ if(!Curl_ssl_config_matches(&needle->ssl_config, &check->ssl_config)) { + infof(data, + "Connection #%ld has different SSL parameters, " + "can't reuse\n", + check->connectindex ); continue; } } if((needle->protocol & PROT_FTP) || ((needle->protocol & PROT_HTTP) && - (needle->data->state.authhost.want==CURLAUTH_NTLM))) { + (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) || @@ -1607,26 +2079,42 @@ ConnectionExists(struct SessionHandle *data, } if(match) { - bool dead = SocketIsDead(check->sock[FIRSTSOCKET]); - if(dead) { - /* - */ - infof(data, "Connection %d seems to be dead!\n", i); - Curl_disconnect(check); /* disconnect resources */ - data->state.connects[i]=NULL; /* nothing here */ + if (!IsPipeliningEnabled(data)) { + /* The check for a dead socket makes sense only in the + non-pipelining case */ + 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; + } + } - /* There's no need to continue searching, because we only store - one connection for each unique set of identifiers */ - return FALSE; + check->inuse = TRUE; /* mark this as being in use so that no other + handle in a multi stack may nick it */ + + if (canPipeline) { + /* Mark the connection as being in a pipeline */ + check->is_in_pipeline = TRUE; } + check->connectindex = i; /* Set this appropriately since it might have + been set to -1 when the easy was removed + from the multi */ *usethis = check; return TRUE; /* yes, we found one to use! */ } } + return FALSE; /* no matching connecting exists */ } + + /* * This function frees/closes a connection in the connection cache. This * should take the previously set policy into account when deciding which @@ -1644,32 +2132,14 @@ ConnectionKillOne(struct SessionHandle *data) now = Curl_tvnow(); - for(i=0; i< data->state.numconnects; i++) { - conn = data->state.connects[i]; + for(i=0; data->state.connc && (i< data->state.connc->num); i++) { + conn = data->state.connc->connects[i]; - if(!conn) + if(!conn || conn->inuse) continue; - /* - * By using the set policy, we score each connection. - */ - switch(data->set.closepolicy) { - case CURLCLOSEPOLICY_LEAST_RECENTLY_USED: - default: - /* - * Set higher score for the age passed since the connection - * was used. - */ - score = Curl_tvdiff(now, conn->now); - break; - case CURLCLOSEPOLICY_OLDEST: - /* - * Set higher score for the age passed since the connection - * was created. - */ - score = Curl_tvdiff(now, conn->created); - break; - } + /* Set higher score for the age passed since the connection was used */ + score = Curl_tvdiff(now, conn->now); if(score > highscore) { highscore = score; @@ -1677,17 +2147,29 @@ ConnectionKillOne(struct SessionHandle *data) } } if(connindex >= 0) { + /* Set the connection's owner correctly */ + conn = data->state.connc->connects[connindex]; + conn->data = data; /* the winner gets the honour of being disconnected */ - (void) Curl_disconnect(data->state.connects[connindex]); + (void)Curl_disconnect(conn); /* clean the array entry */ - data->state.connects[connindex] = NULL; + data->state.connc->connects[connindex] = NULL; } return connindex; /* return the available index or -1 */ } +/* this connection can now be marked 'idle' */ +static void +ConnectionDone(struct connectdata *conn) +{ + conn->inuse = FALSE; + if (!conn->send_pipe && !conn->recv_pipe) + conn->is_in_pipeline = FALSE; +} + /* * The given input connection struct pointer is to be stored. If the "cache" * is already full, we must clean out the most suitable using the previously @@ -1701,260 +2183,53 @@ ConnectionStore(struct SessionHandle *data, struct connectdata *conn) { long i; - for(i=0; i< data->state.numconnects; i++) { - if(!data->state.connects[i]) + for(i=0; i< data->state.connc->num; i++) { + if(!data->state.connc->connects[i]) break; } - if(i == data->state.numconnects) { + if(i == data->state.connc->num) { /* there was no room available, kill one */ i = ConnectionKillOne(data); - infof(data, "Connection (#%d) was killed to make room\n", i); + if(-1 != i) + infof(data, "Connection (#%d) was killed to make room (holds %d)\n", + i, data->state.connc->num); + else + infof(data, "This connection did not fit in the connection cache\n"); } + conn->connectindex = i; /* Make the child know where the pointer to this + particular data is stored. But note that this -1 + if this is not within the cache and this is + probably not checked for everywhere (yet). */ + conn->inuse = TRUE; if(-1 != i) { - /* only do this if a true index was returned, if -1 was returned there + /* Only do this if a true index was returned, if -1 was returned there is no room in the cache for an unknown reason and we cannot store - this there. */ - data->state.connects[i] = conn; /* fill in this */ - conn->connectindex = i; /* make the child know where the pointer to this - particular data is stored */ - } - return i; -} - -/* - * This function logs in to a SOCKS5 proxy and sends the specifies the final - * desitination server. - */ -static int handleSock5Proxy(const char *proxy_name, - const char *proxy_password, - 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; - int sock = conn->sock[FIRSTSOCKET]; - - Curl_nonblock(sock, FALSE); - - 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 */ - - code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), - &written); - if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { - failf(conn->data, "Unable to send initial SOCKS5 request."); - return 1; - } - - result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); - if ((result != CURLE_OK) || (actualread != 2)) { - failf(conn->data, "Unable to receive initial SOCKS5 response."); - return 1; - } - - if (socksreq[0] != 5) { - failf(conn->data, "Received invalid version in initial SOCKS5 response."); - return 1; - } - if (socksreq[1] == 0) { - /* Nothing to do, no authentication needed */ - ; - } - else if (socksreq[1] == 2) { - /* Needs user name and password */ - int userlen, pwlen, len; - - userlen = (int)strlen(proxy_name); - pwlen = proxy_password?(int)strlen(proxy_password):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(conn, sock, (char *)socksreq, len, &written); - if ((code != CURLE_OK) || (len != written)) { - failf(conn->data, "Failed to send SOCKS5 sub-negotiation request."); - return 1; - } - - result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); - if ((result != CURLE_OK) || (actualread != 2)) { - failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response."); - return 1; - } - - if ((socksreq[0] != 5) || /* version */ - (socksreq[1] != 0)) { /* status */ - failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); - return 1; - } - - /* Everything is good so far, user was authenticated! */ - } - else { - /* error */ - if (socksreq[1] == 1) { - failf(conn->data, - "SOCKS5 GSSAPI per-message authentication is not supported."); - return 1; - } - else if (socksreq[1] == 255) { - if (proxy_name[0] == 0) { - failf(conn->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(conn->data, "No authentication method was acceptable."); - } - return 1; - } - else { - failf(conn->data, - "Undocumented SOCKS5 mode attempted to be used by server."); - return 1; - } - } - - /* Authentication is complete, now specify destination to the proxy */ - socksreq[0] = 5; /* version (SOCKS5) */ - socksreq[1] = 1; /* connect */ - socksreq[2] = 0; /* must be zero */ - socksreq[3] = 1; /* IPv4 = 1 */ - - { - struct Curl_dns_entry *dns; - Curl_addrinfo *hp=NULL; - int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); - - if(rc == CURLRESOLV_ERROR) - return 1; - - if(rc == CURLRESOLV_PENDING) - /* this requires that we're in "wait for resolve" state */ - rc = Curl_wait_for_resolv(conn, &dns); - (void)rc; - - /* - * 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(conn->data, dns); /* not used anymore from now on */ - } - if(!hp) { - failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", - conn->host.name); - return 1; - } - } - - { - unsigned short s = htons(conn->remote_port); - memcpy(socksreq+8, &s, sizeof(unsigned short)); - } - - { - const int packetsize = 10; - - code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); - if ((code != CURLE_OK) || (written != packetsize)) { - failf(conn->data, "Failed to send SOCKS5 connect request."); - return 1; - } - - result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); - if ((result != CURLE_OK) || (actualread != packetsize)) { - failf(conn->data, "Failed to receive SOCKS5 connect request ack."); - return 1; - } - - if (socksreq[0] != 5) { /* version */ - failf(conn->data, - "SOCKS5 reply has wrong version, version should be 5."); - return 1; - } - if (socksreq[1] != 0) { /* Anything besides 0 is an error */ - unsigned short sh; - memcpy(&sh, socksreq+8, sizeof(unsigned short)); - - failf(conn->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(sh), - socksreq[1]); - return 1; - } + this there. + + TODO: make sure we really can work with more handles than positions in + the cache, or possibly we should (allow to automatically) resize the + connection cache when we add more easy handles to a multi handle! + */ + data->state.connc->connects[i] = conn; /* fill in this */ + conn->data = data; } - Curl_nonblock(sock, TRUE); - return 0; /* Proxy was successful! */ + return i; } -static CURLcode ConnectPlease(struct connectdata *conn, +static CURLcode ConnectPlease(struct SessionHandle *data, + struct connectdata *conn, struct Curl_dns_entry *hostaddr, bool *connected) { CURLcode result; Curl_addrinfo *addr; - struct SessionHandle *data = conn->data; - char *hostname = data->change.proxy?conn->proxy.name:conn->host.name; + char *hostname = conn->bits.httpproxy?conn->proxy.name:conn->host.name; - infof(data, "About to connect() to %s port %d\n", - hostname, conn->port); + infof(data, "About to connect() to %s%s port %d (#%d)\n", + conn->bits.httpproxy?"proxy ":"", + hostname, conn->port, conn->connectindex); /************************************************************* * Connect to server/proxy @@ -1969,18 +2244,22 @@ static CURLcode ConnectPlease(struct connectdata *conn, conn->dns_entry = hostaddr; conn->ip_addr = addr; - if (conn->data->set.proxytype == CURLPROXY_SOCKS5) { - return handleSock5Proxy(conn->proxyuser, - conn->proxypasswd, - conn) ? - CURLE_COULDNT_CONNECT : CURLE_OK; - } - else if (conn->data->set.proxytype == CURLPROXY_HTTP) { + Curl_store_ip_addr(conn); + + switch(data->set.proxytype) { + case CURLPROXY_SOCKS5: + result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn); + break; + case CURLPROXY_HTTP: /* do nothing here. handled later. */ - } - else { - failf(conn->data, "unknown proxytype option given"); - return CURLE_COULDNT_CONNECT; + break; + case CURLPROXY_SOCKS4: + result = Curl_SOCKS4(conn->proxyuser, conn); + break; + default: + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + break; } } @@ -1992,51 +2271,121 @@ static CURLcode ConnectPlease(struct connectdata *conn, */ static void verboseconnect(struct connectdata *conn) { - struct SessionHandle *data = conn->data; - char addrbuf[256]; - - /* Get a printable version of the network address. */ - Curl_printable_address(conn->ip_addr, addrbuf, sizeof(addrbuf)); - infof(data, "Connected to %s (%s) port %d\n", + infof(conn->data, "Connected to %s (%s) port %d (#%d)\n", conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname, - addrbuf[0] ? addrbuf : "??", conn->port); + conn->ip_addr_str, conn->port, conn->connectindex); +} + +int Curl_protocol_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) +{ + if(conn->curl_proto_getsock) + return conn->curl_proto_getsock(conn, socks, numsocks); + return GETSOCK_BLANK; +} + +int Curl_doing_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) +{ + if(conn && conn->curl_doing_getsock) + return conn->curl_doing_getsock(conn, socks, numsocks); + return GETSOCK_BLANK; +} + +/* + * We are doing protocol-specific connecting and this is being called over and + * over from the multi interface until the connection phase is done on + * protocol layer. + */ + +CURLcode Curl_protocol_connecting(struct connectdata *conn, + bool *done) +{ + CURLcode result=CURLE_OK; + + if(conn && conn->curl_connecting) { + *done = FALSE; + result = conn->curl_connecting(conn, done); + } + else + *done = TRUE; + + return result; +} + +/* + * We are DOING this is being called over and over from the multi interface + * until the DOING phase is done on protocol layer. + */ + +CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) +{ + CURLcode result=CURLE_OK; + + if(conn && conn->curl_doing) { + *done = FALSE; + result = conn->curl_doing(conn, done); + } + else + *done = TRUE; + + return result; } /* * We have discovered that the TCP connection has been successful, we can now * proceed with some action. * - * If we're using the multi interface, this host address pointer is most - * likely NULL at this point as we can't keep the resolved info around. This - * may call for some reworking, like a reference counter in the struct or - * something. */ -CURLcode Curl_protocol_connect(struct connectdata *conn) +CURLcode Curl_protocol_connect(struct connectdata *conn, + bool *protocol_done) { - struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; + struct SessionHandle *data = conn->data; + + *protocol_done = FALSE; - if(conn->bits.tcpconnect) + if(conn->bits.tcpconnect && conn->bits.protoconnstart) { /* We already are connected, get back. This may happen when the connect worked fine in the first call, like when we connect to a local server - or proxy. */ + or proxy. Note that we don't know if the protocol is actually done. + + Unless this protocol doesn't have any protocol-connect callback, as + then we know we're done. */ + if(!conn->curl_connecting) + *protocol_done = TRUE; + return CURLE_OK; + } + + if(!conn->bits.tcpconnect) { - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + + if(data->set.verbose) + verboseconnect(conn); + } - if(data->set.verbose) - verboseconnect(conn); + if(!conn->bits.protoconnstart) { + if(conn->curl_connect) { + /* is there a protocol-specific connect() procedure? */ - if(conn->curl_connect) { - /* is there a protocol-specific connect() procedure? */ + /* Set start time here for timeout purposes in the connect procedure, it + is later set again for the progress meter purpose */ + conn->now = Curl_tvnow(); - /* set start time here for timeout purposes in the - * connect procedure, it is later set again for the - * progress meter purpose */ - conn->now = Curl_tvnow(); + /* Call the protocol-specific connect function */ + result = conn->curl_connect(conn, protocol_done); + } + else + *protocol_done = TRUE; - /* Call the protocol-specific connect function */ - result = conn->curl_connect(conn); + /* it has started, possibly even completed but that knowledge isn't stored + in this bit! */ + if (!result) + conn->bits.protoconnstart = TRUE; } return result; /* pass back status */ @@ -2046,7 +2395,7 @@ CURLcode Curl_protocol_connect(struct connectdata *conn) * Helpers for IDNA convertions. */ #ifdef USE_LIBIDN -static bool is_ASCII_name (const char *hostname) +static bool is_ASCII_name(const char *hostname) { const unsigned char *ch = (const unsigned char*)hostname; @@ -2056,9 +2405,49 @@ static bool is_ASCII_name (const char *hostname) } return TRUE; } + +/* + * Check if characters in hostname is allowed in Top Level Domain. + */ +static bool tld_check_name(struct SessionHandle *data, + const char *ace_hostname) +{ + size_t err_pos; + char *uc_name = NULL; + int rc; + + /* Convert (and downcase) ACE-name back into locale's character set */ + rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0); + if (rc != IDNA_SUCCESS) + return (FALSE); + + rc = tld_check_lz(uc_name, &err_pos, NULL); + if (rc == TLD_INVALID) + infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n", +#ifdef HAVE_TLD_STRERROR + tld_strerror((Tld_rc)rc), +#else + "<no msg>", +#endif + err_pos, uc_name[err_pos], + uc_name[err_pos] & 255); + else if (rc != TLD_SUCCESS) + infof(data, "WARNING: TLD check for %s failed; %s\n", + uc_name, +#ifdef HAVE_TLD_STRERROR + tld_strerror((Tld_rc)rc) +#else + "<no msg>" +#endif + ); + if (uc_name) + idn_free(uc_name); + return (bool)(rc == TLD_SUCCESS); +} #endif -static void fix_hostname(struct connectdata *conn, struct hostname *host) +static void fix_hostname(struct SessionHandle *data, + struct connectdata *conn, struct hostname *host) { /* set the name we use to display the host name */ host->dispname = host->name; @@ -2070,24 +2459,208 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) if (!is_ASCII_name(host->name) && stringprep_check_version(LIBIDN_REQUIRED_VERSION)) { char *ace_hostname = NULL; - struct SessionHandle *data = conn->data; int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0); infof (data, "Input domain encoded as `%s'\n", stringprep_locale_charset ()); if (rc != IDNA_SUCCESS) - infof(data, "Failed to convert %s to ACE; IDNA error %d\n", - host->name, rc); + infof(data, "Failed to convert %s to ACE; %s\n", + host->name, Curl_idn_strerror(conn,rc)); else { + /* tld_check_name() displays a warning if the host name contains + "illegal" characters for this TLD */ + (void)tld_check_name(data, ace_hostname); + host->encalloc = ace_hostname; /* change the name pointer to point to the encoded hostname */ host->name = host->encalloc; } } #else + (void)data; /* never used */ (void)conn; /* never used */ #endif } +/* + * Parse URL and fill in the relevant members of the connection struct. + */ +static CURLcode ParseURLAndFillConnection(struct SessionHandle *data, + struct connectdata *conn) +{ + char *at; + char *tmp; + + char *path = data->reqdata.path; + + /************************************************************* + * 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:/<path>). This is not strictly + * speaking a valid file: URL by RFC 1738, but treating file:/<path> as + * file://localhost/<path> is similar to how other schemes treat missing + * hostnames. See RFC 1808. */ + + /* This cannot be done with strcpy() in a portable manner, since the + memory areas overlap! */ + memmove(path, path + 2, strlen(path + 2)+1); + } + /* + * we deal with file://<host>/<path> differently since it supports no + * hostname other than "localhost" and "127.0.0.1", which is unique among + * the URL protocols specified in RFC 1738 + */ + if(path[0] != '/') { + /* the URL included a host name, we ignore host names in file:// URLs + as the standards don't define what to do with them */ + char *ptr=strchr(path, '/'); + if(ptr) { + /* there was a slash present + + RFC1738 (section 3.1, page 5) says: + + The rest of the locator consists of data specific to the scheme, + and is known as the "url-path". It supplies the details of how the + specified resource can be accessed. Note that the "/" between the + host (or port) and the url-path is NOT part of the url-path. + + As most agents use file://localhost/foo to get '/foo' although the + slash preceding foo is a separator and not a slash for the path, + a URL as file://localhost//foo must be valid as well, to refer to + the same file with an absolute path. + */ + + if(ptr[1] && ('/' == ptr[1])) + /* if there was two slashes, we skip the first one as that is then + used truly as a separator */ + ptr++; + + /* This cannot be made with strcpy, as the memory chunks overlap! */ + memmove(path, ptr, strlen(ptr)+1); + } + } + + strcpy(conn->protostr, "file"); /* store protocol string lowercase */ + } + else { + /* clear path */ + path[0]=0; + + if (2 > sscanf(data->change.url, + "%15[^\n:]://%[^\n/]%[^\n]", + conn->protostr, + conn->host.name, path)) { + + /* + * The URL was badly formatted, let's try the browser-style _without_ + * protocol specified like 'http://'. + */ + if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]", + conn->host.name, path)) ) { + /* + * We couldn't even get this format. + */ + failf(data, "<url> malformed"); + return CURLE_URL_MALFORMAT; + } + + /* + * Since there was no protocol part specified, we guess what protocol it + * is based on the first letters of the server name. + */ + + /* Note: if you add a new protocol, please update the list in + * lib/version.c too! */ + + if(checkprefix("FTP.", conn->host.name)) + strcpy(conn->protostr, "ftp"); + else if (checkprefix("DICT.", conn->host.name)) + strcpy(conn->protostr, "DICT"); + else if (checkprefix("LDAP.", conn->host.name)) + strcpy(conn->protostr, "LDAP"); + else { + strcpy(conn->protostr, "http"); + } + + conn->protocol |= PROT_MISSING; /* not given in URL */ + } + } + + /* We search for '?' in the host name (but only on the right side of a + * @-letter to allow ?-letters in username and password) to handle things + * like http://example.com?param= (notice the missing '/'). + */ + at = strchr(conn->host.name, '@'); + if(at) + tmp = strchr(at+1, '?'); + else + tmp = strchr(conn->host.name, '?'); + + if(tmp) { + /* We must insert a slash before the '?'-letter in the URL. If the URL had + a slash after the '?', that is where the path currently begins and the + '?string' is still part of the host name. + + We must move the trailing part from the host name and put it first in + the path. And have it all prefixed with a slash. + */ + + size_t hostlen = strlen(tmp); + size_t pathlen = strlen(path); + + /* move the existing path plus the zero byte forward, to make room for + the host-name part */ + memmove(path+hostlen+1, path, pathlen+1); + + /* now copy the trailing host part in front of the existing path */ + memcpy(path+1, tmp, hostlen); + + path[0]='/'; /* prepend the missing slash */ + + *tmp=0; /* now cut off the hostname at the ? */ + } + else if(!path[0]) { + /* if there's no path set, use a single slash */ + strcpy(path, "/"); + } + + /* If the URL is malformatted (missing a '/' after hostname before path) we + * insert a slash here. The only letter except '/' we accept to start a path + * is '?'. + */ + if(path[0] == '?') { + /* We need this function to deal with overlapping memory areas. We know + that the memory area 'path' points to is 'urllen' bytes big and that + is bigger than the path. Use +1 to move the zero byte too. */ + memmove(&path[1], path, strlen(path)+1); + path[0] = '/'; + } + + /* + * So if the URL was A://B/C, + * conn->protostr is A + * conn->host.name is B + * data->reqdata.path is /C + */ + + return CURLE_OK; +} + +static void llist_dtor(void *user, void *element) +{ + (void)user; + (void)element; + /* Do nothing */ +} + /** * CreateConnection() sets up a new connectdata struct, or re-uses an already @@ -2099,10 +2672,13 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) * * @param data The sessionhandle pointer * @param in_connect is set to the next connection data pointer - * @param addr is set to the new dns entry for this connection + * @param addr is set to the new dns entry for this connection. If this + * connection is re-used it will be NULL. * @param async is set TRUE/FALSE depending on the nature of this lookup * @return CURLcode * @see SetupConnection() + * + * *NOTE* this function assigns the conn->data pointer! */ static CURLcode CreateConnection(struct SessionHandle *data, @@ -2110,10 +2686,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, struct Curl_dns_entry **addr, bool *async) { + char *tmp; CURLcode result=CURLE_OK; struct connectdata *conn; - struct connectdata *conn_temp; + struct connectdata *conn_temp = NULL; size_t urllen; struct Curl_dns_entry *hostaddr; #if defined(HAVE_ALARM) && !defined(USE_ARES) @@ -2124,6 +2701,8 @@ static CURLcode CreateConnection(struct SessionHandle *data, char passwd[MAX_CURL_PASSWORD_LENGTH]; int rc; bool reuse; + char *proxy; + bool proxy_alloc = FALSE; #ifndef USE_ARES #ifdef SIGALRM @@ -2132,7 +2711,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, bool keep_copysig=FALSE; /* did copy it? */ #else #ifdef HAVE_SIGNAL - void *keep_sigact; /* store the old handler here */ + void (*keep_sigact)(int); /* store the old handler here */ #endif /* HAVE_SIGNAL */ #endif /* HAVE_SIGACTION */ #endif /* SIGALRM */ @@ -2153,7 +2732,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, to not have to modify everything at once, we allocate a temporary connection data struct and fill in for comparison purposes. */ - conn = (struct connectdata *)malloc(sizeof(struct connectdata)); + conn = (struct connectdata *)calloc(sizeof(struct connectdata), 1); if(!conn) { *in_connect = NULL; /* clear the pointer */ return CURLE_OUT_OF_MEMORY; @@ -2163,42 +2742,50 @@ static CURLcode CreateConnection(struct SessionHandle *data, any failure */ *in_connect = conn; - /* we have to init the struct */ - memset(conn, 0, sizeof(struct connectdata)); - /* and we setup a few fields in case we end up actually using this struct */ - conn->data = data; /* remember our daddy */ + + conn->data = data; /* Setup the association between this connection + and the SessionHandle */ + conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ conn->connectindex = -1; /* no index */ - conn->bits.httpproxy = (data->change.proxy && *data->change.proxy && - (data->set.proxytype == CURLPROXY_HTTP))? - TRUE:FALSE; /* http proxy or not */ - /* Default protocol-independent behavior doesn't support persistant + conn->bits.httpproxy = (bool)(data->set.proxy /* http proxy or not */ + && *data->set.proxy + && (data->set.proxytype == CURLPROXY_HTTP)); + proxy = data->set.proxy; /* if global proxy is set, this is it */ + + /* Default protocol-independent behavior doesn't support persistent connections, so we set this to force-close. Protocols that support this need to set this to FALSE in their "curl_do" functions. */ conn->bits.close = TRUE; - /* maxdownload must be -1 on init, as 0 is a valid value! */ - conn->maxdownload = -1; /* might have been used previously! */ + conn->readchannel_inuse = FALSE; + conn->writechannel_inuse = FALSE; + + conn->read_pos = 0; + conn->buf_len = 0; + + /* 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); /* Store creation time to help future close decision making */ conn->created = Curl_tvnow(); - conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */ - conn->range = data->set.set_range; /* clone the range setting */ - conn->resume_from = data->set.set_resume_from; /* inherite resume_from */ + /* range status */ + data->reqdata.use_range = (bool)(NULL != data->set.set_range); - /* Set the start time temporary to this creation time to allow easier - timeout checks before the transfer has started for real. The start time - is later set "for real" using Curl_pgrsStartNow(). */ - conn->data->progress.start = conn->created; + data->reqdata.range = data->set.set_range; /* clone the range setting */ + data->reqdata.resume_from = data->set.set_resume_from; - conn->bits.user_passwd = data->set.userpwd?1:0; - conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0; + conn->bits.user_passwd = (bool)(NULL != data->set.userpwd); + conn->bits.proxy_user_passwd = (bool)(NULL != data->set.proxyuserpwd); conn->bits.no_body = data->set.opt_no_body; 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; /* This initing continues below, see the comment "Continue connectdata * initialization here" */ @@ -2213,152 +2800,35 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(urllen < LEAST_PATH_ALLOC) urllen=LEAST_PATH_ALLOC; - conn->pathbuffer=(char *)malloc(urllen); - if(NULL == conn->pathbuffer) - return CURLE_OUT_OF_MEMORY; /* really bad error */ - conn->path = conn->pathbuffer; - - conn->host.rawalloc=(char *)malloc(urllen); - if(NULL == conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.name = conn->host.rawalloc; - - /************************************************************* - * 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, - conn->path)) && strequal(conn->protostr, "file")) { - if(conn->path[0] == '/' && conn->path[1] == '/') { - /* Allow omitted hostname (e.g. file:/<path>). This is not strictly - * speaking a valid file: URL by RFC 1738, but treating file:/<path> as - * file://localhost/<path> is similar to how other schemes treat missing - * hostnames. See RFC 1808. */ - - /* This cannot be done with strcpy() in a portable manner, since the - memory areas overlap! */ - memmove(conn->path, conn->path + 2, strlen(conn->path + 2)+1); - } - /* - * we deal with file://<host>/<path> differently since it supports no - * hostname other than "localhost" and "127.0.0.1", which is unique among - * the URL protocols specified in RFC 1738 - */ - if(conn->path[0] != '/') { - /* the URL included a host name, we ignore host names in file:// URLs - as the standards don't define what to do with them */ - char *ptr=strchr(conn->path, '/'); - if(ptr) { - /* there was a slash present - - RFC1738 (section 3.1, page 5) says: - - The rest of the locator consists of data specific to the scheme, - and is known as the "url-path". It supplies the details of how the - specified resource can be accessed. Note that the "/" between the - host (or port) and the url-path is NOT part of the url-path. - - As most agents use file://localhost/foo to get '/foo' although the - slash preceeding foo is a separator and not a slash for the path, - a URL as file://localhost//foo must be valid as well, to refer to - the same file with an absolute path. - */ - - if(ptr[1] && ('/' == ptr[1])) - /* if there was two slashes, we skip the first one as that is then - used truly as a separator */ - ptr++; - - /* This cannot be made with strcpy, as the memory chunks overlap! */ - memmove(conn->path, ptr, strlen(ptr)+1); - } - } - - strcpy(conn->protostr, "file"); /* store protocol string lowercase */ + if (!data->set.source_url /* 3rd party FTP */ + && data->reqdata.pathbuffer) { + /* Free the old buffer */ + free(data->reqdata.pathbuffer); } - else { - /* Set default path */ - strcpy(conn->path, "/"); - /* We need to search for '/' OR '?' - whichever comes first after host - * name but before the path. We need to change that to handle things like - * http://example.com?param= (notice the missing '/'). Later we'll insert - * that missing slash at the beginning of the path. - */ - if (2 > sscanf(data->change.url, - "%15[^\n:]://%[^\n/?]%[^\n]", - conn->protostr, - conn->host.name, conn->path)) { - - /* - * The URL was badly formatted, let's try the browser-style _without_ - * protocol specified like 'http://'. - */ - if((1 > sscanf(data->change.url, "%[^\n/?]%[^\n]", - conn->host.name, conn->path)) ) { - /* - * We couldn't even get this format. - */ - failf(data, "<url> malformed"); - return CURLE_URL_MALFORMAT; - } - - /* - * Since there was no protocol part specified, we guess what protocol it - * is based on the first letters of the server name. - */ + /* + * 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) + */ - /* Note: if you add a new protocol, please update the list in - * lib/version.c too! */ + data->reqdata.pathbuffer=(char *)malloc(urllen+2); + if(NULL == data->reqdata.pathbuffer) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + data->reqdata.path = data->reqdata.pathbuffer; - if(checkprefix("GOPHER", conn->host.name)) - strcpy(conn->protostr, "gopher"); -#ifdef USE_SSLEAY - else if(checkprefix("HTTPS", conn->host.name)) - strcpy(conn->protostr, "https"); - else if(checkprefix("FTPS", conn->host.name)) - strcpy(conn->protostr, "ftps"); -#endif /* USE_SSLEAY */ - else if(checkprefix("FTP", conn->host.name)) - strcpy(conn->protostr, "ftp"); - else if(checkprefix("TELNET", conn->host.name)) - strcpy(conn->protostr, "telnet"); - else if (checkprefix("DICT", conn->host.name)) - strcpy(conn->protostr, "DICT"); - else if (checkprefix("LDAP", conn->host.name)) - strcpy(conn->protostr, "LDAP"); - else { - strcpy(conn->protostr, "http"); - } + conn->host.rawalloc=(char *)malloc(urllen+2); + if(NULL == conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; - conn->protocol |= PROT_MISSING; /* not given in URL */ - } - } + conn->host.name = conn->host.rawalloc; + conn->host.name[0] = 0; - /* If the URL is malformatted (missing a '/' after hostname before path) we - * insert a slash here. The only letter except '/' we accept to start a path - * is '?'. - */ - if(conn->path[0] == '?') { - /* We need this function to deal with overlapping memory areas. We know - that the memory area 'path' points to is 'urllen' bytes big and that - is bigger than the path. Use +1 to move the zero byte too. */ - memmove(&conn->path[1], conn->path, strlen(conn->path)+1); - conn->path[0] = '/'; + result = ParseURLAndFillConnection(data, conn); + if (result != CURLE_OK) { + return result; } - /* - * So if the URL was A://B/C, - * conn->protostr is A - * conn->host.name is B - * conn->path is /C - */ - /************************************************************* * Take care of proxy authentication stuff *************************************************************/ @@ -2371,26 +2841,26 @@ static CURLcode CreateConnection(struct SessionHandle *data, "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]", proxyuser, proxypasswd); - conn->proxyuser = strdup(proxyuser); + conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); if(!conn->proxyuser) return CURLE_OUT_OF_MEMORY; - conn->proxypasswd = strdup(proxypasswd); + conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); if(!conn->proxypasswd) return CURLE_OUT_OF_MEMORY; } +#ifndef CURL_DISABLE_HTTP /************************************************************* * Detect what (if any) proxy to use *************************************************************/ - if(!data->change.proxy) { + if(!conn->bits.httpproxy) { /* 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/ - * gopher_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 @@ -2402,11 +2872,9 @@ static CURLcode CreateConnection(struct SessionHandle *data, * For compatibility, the all-uppercase versions of these variables are * checked if the lowercase versions don't exist. */ - char *no_proxy; + char *no_proxy=NULL; char *no_proxy_tok_buf; - char *proxy=NULL; char proxy_env[128]; - (void)proxy; no_proxy=curl_getenv("no_proxy"); if(!no_proxy) @@ -2443,7 +2911,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* Now, build <protocol>_proxy and check for such a one to use */ while(*protop) - *envp++ = tolower((int)*protop++); + *envp++ = (char)tolower((int)*protop++); /* append _proxy */ strcpy(envp, "_proxy"); @@ -2466,7 +2934,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(!prox && !strequal("http_proxy", proxy_env)) { /* There was no lowercase variable, try the uppercase version: */ for(envp = proxy_env; *envp; envp++) - *envp = toupper((int)*envp); + *envp = (char)toupper((int)*envp); prox=curl_getenv(proxy_env); } @@ -2480,64 +2948,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, } if(proxy && *proxy) { - /* we have a proxy here to set */ - char *ptr; - char proxyuser[MAX_CURL_USER_LENGTH]; - char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; - - char *fineptr; - - /* skip the possible protocol piece */ - ptr=strstr(proxy, "://"); - if(ptr) - ptr += 3; - else - ptr = proxy; - - fineptr = ptr; - - /* check for an @-letter */ - ptr = strchr(ptr, '@'); - if(ptr && (2 == sscanf(fineptr, - "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", - proxyuser, proxypasswd))) { - CURLcode res = CURLE_OK; - - /* found user and password, rip them out */ - Curl_safefree(conn->proxyuser); - conn->proxyuser = strdup(proxyuser); - - if(!conn->proxyuser) - res = CURLE_OUT_OF_MEMORY; - else { - Curl_safefree(conn->proxypasswd); - conn->proxypasswd = strdup(proxypasswd); - - if(!conn->proxypasswd) - res = CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK == res) { - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - ptr = strdup(ptr+1); /* the right side of the @-letter */ - - if(ptr) { - free(proxy); /* free the former proxy string */ - proxy = ptr; /* now use this instead */ - } - else - res = CURLE_OUT_OF_MEMORY; - } - - if(res) { - free(proxy); /* free the allocated proxy string */ - return res; - } - } + long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); + /* force this to become HTTP */ + conn->protocol = PROT_HTTP | bits; - data->change.proxy = proxy; - data->change.proxy_alloc=TRUE; /* this needs to be freed later */ + proxy_alloc=TRUE; /* this needs to be freed later */ conn->bits.httpproxy = TRUE; } } /* if (!nope) - it wasn't specified non-proxy */ @@ -2545,6 +2960,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(no_proxy) free(no_proxy); } /* if not using proxy */ +#endif /* CURL_DISABLE_HTTP */ /************************************************************* * No protocol part in URL was used, add it! @@ -2575,14 +2991,14 @@ static CURLcode CreateConnection(struct SessionHandle *data, * server, we just fail since we can't rewind the file writing from within * this function. ***********************************************************/ - if(conn->resume_from) { - if(!conn->bits.use_range) { + if(data->reqdata.resume_from) { + if(!data->reqdata.use_range) { /* if it already was in use, we just skip this */ - conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); - if(!conn->range) + data->reqdata.range = aprintf("%" FORMAT_OFF_T "-", data->reqdata.resume_from); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; - conn->bits.rangestringalloc = TRUE; /* mark as allocated */ - conn->bits.use_range = 1; /* switch on range usage */ + data->reqdata.rangestringalloc = TRUE; /* mark as allocated */ + data->reqdata.use_range = 1; /* switch on range usage */ } } #endif @@ -2590,14 +3006,15 @@ static CURLcode CreateConnection(struct SessionHandle *data, * Setup internals depending on protocol *************************************************************/ + conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ + if (strequal(conn->protostr, "HTTP")) { #ifndef CURL_DISABLE_HTTP - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:PORT_HTTP; + conn->port = PORT_HTTP; conn->remote_port = PORT_HTTP; conn->protocol |= PROT_HTTP; conn->curl_do = Curl_http; - conn->curl_do_more = NULL; + conn->curl_do_more = (Curl_do_more_func)ZERO_NULL; conn->curl_done = Curl_http_done; conn->curl_connect = Curl_http_connect; #else @@ -2607,43 +3024,24 @@ static CURLcode CreateConnection(struct SessionHandle *data, #endif } else if (strequal(conn->protostr, "HTTPS")) { -#if defined(USE_SSLEAY) && !defined(CURL_DISABLE_HTTP) +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:PORT_HTTPS; + conn->port = PORT_HTTPS; conn->remote_port = PORT_HTTPS; conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL; conn->curl_do = Curl_http; - conn->curl_do_more = NULL; + conn->curl_do_more = (Curl_do_more_func)ZERO_NULL; conn->curl_done = Curl_http_done; conn->curl_connect = Curl_http_connect; + conn->curl_connecting = Curl_https_connecting; + conn->curl_proto_getsock = Curl_https_getsock; -#else /* USE_SSLEAY */ +#else /* USE_SSL */ failf(data, LIBCURL_NAME " was built with SSL disabled, https: not supported!"); return CURLE_UNSUPPORTED_PROTOCOL; -#endif /* !USE_SSLEAY */ - } - else if (strequal(conn->protostr, "GOPHER")) { -#ifndef CURL_DISABLE_GOPHER - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:PORT_GOPHER; - conn->remote_port = PORT_GOPHER; - /* Skip /<item-type>/ in path if present */ - if (isdigit((int)conn->path[1])) { - conn->path = strchr(&conn->path[1], '/'); - if (conn->path == NULL) - conn->path = conn->pathbuffer; - } - conn->protocol |= PROT_GOPHER; - conn->curl_do = Curl_http; - conn->curl_do_more = NULL; - conn->curl_done = Curl_http_done; -#else - failf(data, LIBCURL_NAME - " was built with GOPHER disabled, gopher: not supported!"); -#endif +#endif /* !USE_SSL */ } else if(strequal(conn->protostr, "FTP") || strequal(conn->protostr, "FTPS")) { @@ -2653,7 +3051,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, int port = PORT_FTP; if(strequal(conn->protostr, "FTPS")) { -#ifdef USE_SSLEAY +#ifdef USE_SSL conn->protocol |= PROT_FTPS|PROT_SSL; conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */ port = PORT_FTPS; @@ -2661,28 +3059,20 @@ static CURLcode CreateConnection(struct SessionHandle *data, failf(data, LIBCURL_NAME " was built with SSL disabled, ftps: not supported!"); return CURLE_UNSUPPORTED_PROTOCOL; -#endif /* !USE_SSLEAY */ +#endif /* !USE_SSL */ } - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:port; - conn->remote_port = port; + conn->port = port; + conn->remote_port = (unsigned short)port; conn->protocol |= PROT_FTP; - if(data->change.proxy && - *data->change.proxy && - !data->set.tunnel_thru_httpproxy) { + if(proxy && *proxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel ftp operations through the proxy, we switch and use HTTP operations only */ - if(conn->protocol & PROT_FTPS) { - /* FTPS is a hacked protocol and does not work through your - ordinary http proxy! */ - failf(data, "ftps does not work through http proxy!"); - return CURLE_UNSUPPORTED_PROTOCOL; - } #ifndef CURL_DISABLE_HTTP conn->curl_do = Curl_http; conn->curl_done = Curl_http_done; + conn->protocol = PROT_HTTP; /* switch to HTTP */ #else failf(data, "FTP over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -2693,32 +3083,36 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->curl_do_more = Curl_ftp_nextconnect; conn->curl_done = Curl_ftp_done; conn->curl_connect = Curl_ftp_connect; + conn->curl_connecting = Curl_ftp_multi_statemach; + conn->curl_doing = Curl_ftp_doing; + conn->curl_proto_getsock = Curl_ftp_getsock; + conn->curl_doing_getsock = Curl_ftp_getsock; conn->curl_disconnect = Curl_ftp_disconnect; } - conn->path++; /* don't include the initial slash */ + data->reqdata.path++; /* don't include the initial slash */ /* FTP URLs support an extension like ";type=<typecode>" that * we'll try to get now! */ - type=strstr(conn->path, ";type="); + type=strstr(data->reqdata.path, ";type="); if(!type) { type=strstr(conn->host.rawalloc, ";type="); } if(type) { char command; *type=0; /* it was in the middle of the hostname */ - command = toupper((int)type[6]); + command = (char)toupper((int)type[6]); switch(command) { case 'A': /* ASCII mode */ - data->set.ftp_ascii = 1; + data->set.prefer_ascii = TRUE; break; case 'D': /* directory mode */ - data->set.ftp_list_only = 1; + data->set.ftp_list_only = TRUE; break; case 'I': /* binary mode */ default: /* switch off ASCII */ - data->set.ftp_ascii = 0; + data->set.prefer_ascii = FALSE; break; } } @@ -2733,8 +3127,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* telnet testing factory */ conn->protocol |= PROT_TELNET; - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port: PORT_TELNET; + conn->port = PORT_TELNET; conn->remote_port = PORT_TELNET; conn->curl_do = Curl_telnet; conn->curl_done = Curl_telnet_done; @@ -2746,11 +3139,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, else if (strequal(conn->protostr, "DICT")) { #ifndef CURL_DISABLE_DICT conn->protocol |= PROT_DICT; - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:PORT_DICT; + conn->port = PORT_DICT; conn->remote_port = PORT_DICT; conn->curl_do = Curl_dict; - conn->curl_done = NULL; /* no DICT-specific done */ + /* no DICT-specific done */ + conn->curl_done = (Curl_done_func)ZERO_NULL; #else failf(data, LIBCURL_NAME " was built with DICT disabled!"); @@ -2759,11 +3152,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, else if (strequal(conn->protostr, "LDAP")) { #ifndef CURL_DISABLE_LDAP conn->protocol |= PROT_LDAP; - conn->port = (data->set.use_port && data->state.allow_port)? - data->set.use_port:PORT_LDAP; + conn->port = PORT_LDAP; conn->remote_port = PORT_LDAP; conn->curl_do = Curl_ldap; - conn->curl_done = NULL; /* no LDAP-specific done */ + /* no LDAP-specific done */ + conn->curl_done = (Curl_done_func)ZERO_NULL; #else failf(data, LIBCURL_NAME " was built with LDAP disabled!"); @@ -2782,9 +3175,12 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* Setup a "faked" transfer that'll do nothing */ if(CURLE_OK == result) { + conn->data = data; conn->bits.tcpconnect = TRUE; /* we are "connected */ - result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ - -1, NULL); /* no upload */ + ConnectionStore(data, conn); + + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ + -1, NULL); /* no upload */ } return result; @@ -2793,62 +3189,82 @@ static CURLcode CreateConnection(struct SessionHandle *data, " was built with FILE disabled!"); #endif } - else { + else if (strequal(conn->protostr, "TFTP")) { +#ifndef CURL_DISABLE_TFTP + char *type; + conn->socktype = SOCK_DGRAM; /* UDP datagram based */ + conn->protocol |= PROT_TFTP; + conn->port = PORT_TFTP; + conn->remote_port = PORT_TFTP; + conn->curl_connect = Curl_tftp_connect; + conn->curl_do = Curl_tftp; + conn->curl_done = Curl_tftp_done; + /* TFTP URLs support an extension like ";mode=<typecode>" that + * we'll try to get now! */ + type=strstr(data->reqdata.path, ";mode="); + if(!type) { + type=strstr(conn->host.rawalloc, ";mode="); + } + if(type) { + char command; + *type=0; /* it was in the middle of the hostname */ + command = (char)toupper((int)type[6]); + switch(command) { + case 'A': /* ASCII mode */ + case 'N': /* NETASCII mode */ + data->set.prefer_ascii = TRUE; + break; + case 'O': /* octet mode */ + case 'I': /* binary mode */ + default: + /* switch off ASCII */ + data->set.prefer_ascii = FALSE; + break; + } + } +#else + failf(data, LIBCURL_NAME + " was built with TFTP disabled!"); +#endif + } + else if (strequal(conn->protostr, "SCP")) { +#ifdef USE_LIBSSH2 + conn->port = PORT_SSH; + conn->remote_port = PORT_SSH; + conn->protocol = PROT_SCP; + conn->curl_connect = Curl_ssh_connect; /* ssh_connect? */ + conn->curl_do = Curl_scp_do; + conn->curl_done = Curl_scp_done; + conn->curl_do_more = (Curl_do_more_func)ZERO_NULL; +#else + failf(data, LIBCURL_NAME + " was built without LIBSSH2, scp: not supported!"); + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + } + else if (strequal(conn->protostr, "SFTP")) { +#ifdef USE_LIBSSH2 + conn->port = PORT_SSH; + conn->remote_port = PORT_SSH; + conn->protocol = PROT_SFTP; + conn->curl_connect = Curl_ssh_connect; /* ssh_connect? */ + conn->curl_do = Curl_sftp_do; + conn->curl_done = Curl_sftp_done; + conn->curl_do_more = (Curl_do_more_func)NULL; +#else + failf(data, LIBCURL_NAME + " was built without LIBSSH2, scp: not supported!"); + return CURLE_UNSUPPORTED_PROTOCOL; +#endif +} +else { /* We fell through all checks and thus we don't support the specified protocol */ failf(data, "Unsupported protocol: %s", conn->protostr); return CURLE_UNSUPPORTED_PROTOCOL; } - /************************************************************* - * Figure out the remote port number - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * To be able to detect port number flawlessly, we must not confuse them - * IPv6-specified addresses in the [0::1] style. (RFC2732) - * - * The conn->host.name is currently [user:passwd@]host[:port] where host - * could be a hostname, IPv4 address or IPv6 address. - *************************************************************/ - if((1 == sscanf(conn->host.name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) && - (']' == endbracket)) { - /* this is a RFC2732-style specified IP-address */ - conn->bits.ipv6_ip = TRUE; - - conn->host.name++; /* pass the starting bracket */ - tmp = strchr(conn->host.name, ']'); - *tmp = 0; /* zero terminate */ - tmp++; /* pass the ending bracket */ - if(':' != *tmp) - tmp = NULL; /* no port number available */ - } - else - tmp = strrchr(conn->host.name, ':'); - - if (tmp) { - char *rest; - unsigned long port; - - port=strtoul(tmp+1, &rest, 10); /* Port number must be decimal */ - - if (rest != (tmp+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; - } - - *tmp = '\0'; /* cut off the name there */ - conn->remote_port = (unsigned short)port; - } - } - - if(data->change.proxy && *data->change.proxy) { + if(proxy && *proxy) { /* If this is supposed to use a proxy, we need to figure out the proxy host name name, so that we can re-use an existing connection that may exist registered to the same proxy host. */ @@ -2857,30 +3273,100 @@ static CURLcode CreateConnection(struct SessionHandle *data, char *endofprot; /* We need to make a duplicate of the proxy so that we can modify the - string safely. */ - char *proxydup=strdup(data->change.proxy); + string safely. If 'proxy_alloc' is TRUE, the string is already + allocated and we can treat it as duplicated. */ + char *proxydup=proxy_alloc?proxy:strdup(proxy); /* We use 'proxyptr' to point to the proxy name from now on... */ char *proxyptr=proxydup; + char *portptr; + char *atsign; if(NULL == proxydup) { failf(data, "memory shortage"); return CURLE_OUT_OF_MEMORY; } - /* Daniel Dec 10, 1998: - 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. */ + /* 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. + */ - /* 1. skip the protocol part if present */ + /* Skip the protocol part if present */ endofprot=strstr(proxyptr, "://"); - if(endofprot) { + 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(proxydup); /* free the former proxy string */ + proxydup = proxyptr = atsign; /* now use this instead */ + } + else + res = CURLE_OUT_OF_MEMORY; + } + + if(res) { + free(proxydup); /* free the allocated proxy string */ + return res; + } + } } - /* allow user to specify proxy.server.com:1080 if desired */ - prox_portno = strchr (proxyptr, ':'); + /* 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++; + if(*ptr == ']') { + /* yeps, it ended nicely with a bracket as well */ + *ptr = 0; + portptr = ptr+1; + } + /* 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 ++; @@ -2898,12 +3384,13 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->proxy.name = conn->proxy.rawalloc; free(proxydup); /* free the duplicate pointer and not the modified */ + proxy = NULL; /* this may have just been freed */ if(!conn->proxy.rawalloc) return CURLE_OUT_OF_MEMORY; } /************************************************************* - * If the protcol is using SSL and HTTP proxy is used, we set + * 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) @@ -2938,9 +3425,9 @@ static CURLcode CreateConnection(struct SessionHandle *data, user[0] =0; /* to make everything well-defined */ passwd[0]=0; - if (conn->protocol & (PROT_FTP|PROT_HTTP)) { - /* This is a FTP or HTTP URL, we will now try to extract the possible - * user+password pair in a string like: + 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; @@ -2962,15 +3449,16 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(*userpass != ':') { /* the name is given, get user+password */ - sscanf(userpass, "%127[^:@]:%127[^@]", + sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", user, passwd); } else /* no name given, get the password only */ - sscanf(userpass, ":%127[^@]", passwd); + sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd); if(user[0]) { - char *newname=curl_unescape(user, 0); + char *newname=curl_easy_unescape(data, user, 0, NULL); if(!newname) return CURLE_OUT_OF_MEMORY; if(strlen(newname) < sizeof(user)) @@ -2982,7 +3470,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, } if (passwd[0]) { /* we have a password found in the URL, decode it! */ - char *newpasswd=curl_unescape(passwd, 0); + char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL); if(!newpasswd) return CURLE_OUT_OF_MEMORY; if(strlen(newpasswd) < sizeof(passwd)) @@ -2994,13 +3482,85 @@ static CURLcode CreateConnection(struct SessionHandle *data, } } + /************************************************************* + * Figure out the remote port number + * + * No matter if we use a proxy or not, we have to figure out the remote + * port number of various reasons. + * + * To be able to detect port number flawlessly, we must not confuse them + * IPv6-specified addresses in the [0::1] style. (RFC2732) + * + * The conn->host.name is currently [user:passwd@]host[:port] where host + * could be a hostname, IPv4 address or IPv6 address. + *************************************************************/ + if((1 == sscanf(conn->host.name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) && + (']' == endbracket)) { + /* this is a RFC2732-style specified IP-address */ + conn->bits.ipv6_ip = TRUE; + + conn->host.name++; /* pass the starting bracket */ + tmp = strchr(conn->host.name, ']'); + *tmp = 0; /* zero terminate */ + tmp++; /* pass the ending bracket */ + if(':' != *tmp) + tmp = NULL; /* no port number available */ + } + else + tmp = 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(tmp) + *tmp = '\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; + + url = aprintf("http://%s:%d%s", conn->host.name, conn->remote_port, + data->reqdata.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 (tmp) { + /* no CURLOPT_PORT given, extract the one from the URL */ + + char *rest; + unsigned long port; + + port=strtoul(tmp+1, &rest, 10); /* Port number must be decimal */ + + if (rest != (tmp+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; + } + + *tmp = '\0'; /* cut off the name there */ + conn->remote_port = (unsigned short)port; + } + } + /* Programmatically set password: * - always applies, if available * - takes precedence over the values we just set above * so scribble it over the top. * User-supplied passwords are assumed not to need unescaping. * - * user_password is set in "inherite initial knowledge' above, + * user_password is set in "inherit initial knowledge' above, * so it doesn't have to be set in this block */ if (data->set.userpwd != NULL) { @@ -3011,15 +3571,23 @@ static CURLcode CreateConnection(struct SessionHandle *data, user, passwd); } + conn->bits.netrc = FALSE; if (data->set.use_netrc != CURL_NETRC_IGNORED) { if(Curl_parsenetrc(conn->host.name, user, passwd, data->set.netrc_file)) { - infof(data, "Couldn't find host %s in the .netrc file, using defaults\n", + infof(data, "Couldn't find host %s in the " DOT_CHAR + "netrc file, using defaults\n", conn->host.name); } - else + else { + /* set bits.netrc TRUE to remember that we got the name from a .netrc + file, so that it is safe to use even if we followed a Location: to a + different host or similar. */ + conn->bits.netrc = TRUE; + conn->bits.user_passwd = 1; /* enable user+password */ + } } /* If our protocol needs a password and we have none, use the defaults */ @@ -3076,10 +3644,31 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn = conn_temp; /* use this connection from now on */ + conn->data = old_conn->data; + /* get the user+password information from the old_conn struct since it may * be new for this request even when we re-use an existing connection */ conn->bits.user_passwd = old_conn->bits.user_passwd; + if (conn->bits.user_passwd) { + /* use the new user namd and password though */ + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + conn->user = old_conn->user; + conn->passwd = old_conn->passwd; + old_conn->user = NULL; + old_conn->passwd = NULL; + } + conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; + if (conn->bits.proxy_user_passwd) { + /* use the new proxy user name and proxy password though */ + Curl_safefree(conn->proxyuser); + Curl_safefree(conn->proxypasswd); + conn->proxyuser = old_conn->proxyuser; + conn->proxypasswd = old_conn->proxypasswd; + old_conn->proxyuser = NULL; + old_conn->proxypasswd = NULL; + } /* host can change, when doing keepalive with a proxy ! */ if (conn->bits.httpproxy) { @@ -3093,23 +3682,17 @@ static CURLcode CreateConnection(struct SessionHandle *data, if (!conn->bits.httpproxy) free(old_conn->host.rawalloc); /* free the newly allocated name buffer */ - free(conn->pathbuffer); /* free the newly allocated path pointer */ - conn->pathbuffer = old_conn->pathbuffer; /* use the old one */ - conn->path = old_conn->path; - /* re-use init */ conn->bits.reuse = TRUE; /* yes, we're re-using here */ conn->bits.chunk = FALSE; /* always assume not chunked unless told otherwise */ - conn->maxdownload = -1; /* might have been used previously! */ Curl_safefree(old_conn->user); Curl_safefree(old_conn->passwd); Curl_safefree(old_conn->proxyuser); Curl_safefree(old_conn->proxypasswd); - - if(old_conn->bits.rangestringalloc) - free(old_conn->range); + Curl_llist_destroy(old_conn->send_pipe, NULL); + Curl_llist_destroy(old_conn->recv_pipe, NULL); free(old_conn); /* we don't need this anymore */ @@ -3117,28 +3700,29 @@ static CURLcode CreateConnection(struct SessionHandle *data, * If we're doing a resumed transfer, we need to setup our stuff * properly. */ - conn->resume_from = data->set.set_resume_from; - if (conn->resume_from) { - if (conn->bits.rangestringalloc == TRUE) - free(conn->range); - conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); - if(!conn->range) + data->reqdata.resume_from = data->set.set_resume_from; + if (data->reqdata.resume_from) { + if (data->reqdata.rangestringalloc == TRUE) + free(data->reqdata.range); + data->reqdata.range = aprintf("%" FORMAT_OFF_T "-", + data->reqdata.resume_from); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; /* tell ourselves to fetch this range */ - conn->bits.use_range = TRUE; /* enable range download */ - conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + data->reqdata.use_range = TRUE; /* enable range download */ + data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */ } else if (data->set.set_range) { /* There is a range, but is not a resume, useful for random ftp access */ - conn->range = strdup(data->set.set_range); - if(!conn->range) + data->reqdata.range = strdup(data->set.set_range); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; - conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ - conn->bits.use_range = TRUE; /* enable range download */ + data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */ + data->reqdata.use_range = TRUE; /* enable range download */ } else - conn->bits.use_range = FALSE; /* disable range download */ + data->reqdata.use_range = FALSE; /* disable range download */ *in_connect = conn; /* return this instead! */ @@ -3154,22 +3738,26 @@ static CURLcode CreateConnection(struct SessionHandle *data, ConnectionStore(data, conn); } - /* Continue connectdata initialization here. + /* Continue connectdata initialization here. */ + + /* * * Inherit the proper values from the urldata struct AFTER we have arranged - * the persistant conncetion stuff */ + * the persistent connection stuff */ conn->fread = data->set.fread; conn->fread_in = data->set.in; - conn->bits.upload_chunky = - ((conn->protocol&PROT_HTTP) && - data->set.upload && - (data->set.infilesize == -1) && - (data->set.httpversion != CURL_HTTP_VERSION_1_0))? + if ((conn->protocol&PROT_HTTP) && + data->set.upload && + (data->set.infilesize == -1) && + (data->set.httpversion != CURL_HTTP_VERSION_1_0)) { /* HTTP, upload, unknown file size and not HTTP 1.0 */ - TRUE: - /* else, no chunky upload */ - FALSE; + conn->bits.upload_chunky = TRUE; + } + else { + /* else, no chunky upload */ + conn->bits.upload_chunky = FALSE; + } #ifndef USE_ARES /************************************************************* @@ -3183,6 +3771,9 @@ static CURLcode CreateConnection(struct SessionHandle *data, *************************************************************/ #ifdef SIGALRM +#ifdef HAVE_ALARM + long shortest; +#endif #ifdef HAVE_SIGACTION struct sigaction sigact; sigaction(SIGALRM, NULL, &sigact); @@ -3208,11 +3799,18 @@ static CURLcode CreateConnection(struct SessionHandle *data, * multi-threaded environments. */ #ifdef HAVE_ALARM + 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; + /* alarm() makes a signal get sent when the timeout fires off, and that will abort system calls */ - prev_alarm = alarm(data->set.connecttimeout? - data->set.connecttimeout: - data->set.timeout); + prev_alarm = alarm((unsigned int) shortest); /* 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. */ @@ -3227,19 +3825,18 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(conn->bits.reuse) { /* re-used connection, no resolving is necessary */ hostaddr = NULL; - conn->dns_entry = NULL; /* we don't connect now so we don't have any fresh - dns entry struct to point to */ + /* we'll need to clear conn->dns_entry later in Curl_disconnect() */ if (conn->bits.httpproxy) - fix_hostname(conn, &conn->host); + fix_hostname(data, conn, &conn->host); } else { /* this is a fresh connect */ /* set a pointer to the hostname we display */ - fix_hostname(conn, &conn->host); + fix_hostname(data, conn, &conn->host); - if(!data->change.proxy || !*data->change.proxy) { + 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 */ @@ -3259,7 +3856,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* This is a proxy that hasn't been resolved yet. */ /* IDN-fix the proxy name */ - fix_hostname(conn, &conn->proxy); + fix_hostname(data, conn, &conn->proxy); /* resolve proxy */ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr); @@ -3322,72 +3919,92 @@ static CURLcode CreateConnection(struct SessionHandle *data, return result; } -/* SetupConnection() should be called after the name resolve initiated in +/* SetupConnection() is called after the name resolve initiated in * CreateConnection() is all done. + * + * NOTE: the argument 'hostaddr' is NULL when this function is called for a + * re-used connection. + * + * conn->data MUST already have been setup fine (in CreateConnection) */ static CURLcode SetupConnection(struct connectdata *conn, - struct Curl_dns_entry *hostaddr) + struct Curl_dns_entry *hostaddr, + bool *protocol_done) { - struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; + struct SessionHandle *data = conn->data; Curl_pgrsTime(data, TIMER_NAMELOOKUP); - if(conn->protocol & PROT_FILE) + 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 */ /************************************************************* * Send user-agent to HTTP proxies even if the target protocol * isn't HTTP. *************************************************************/ - if((conn->protocol&PROT_HTTP) || - (data->change.proxy && *data->change.proxy)) { + if((conn->protocol&PROT_HTTP) || conn->bits.httpproxy) { if(data->set.useragent) { Curl_safefree(conn->allocptr.uagent); conn->allocptr.uagent = - aprintf("User-Agent: %s\015\012", data->set.useragent); + aprintf("User-Agent: %s\r\n", data->set.useragent); if(!conn->allocptr.uagent) return CURLE_OUT_OF_MEMORY; } } - if(data->set.encoding) { - Curl_safefree(conn->allocptr.accept_encoding); - conn->allocptr.accept_encoding = - aprintf("Accept-Encoding: %s\015\012", data->set.encoding); - if(!conn->allocptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - } - - conn->bytecount = 0; conn->headerbytecount = 0; - if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { - bool connected; +#ifdef CURL_DO_LINEEND_CONV + data->state.crlf_conversions = 0; /* reset CRLF conversion counter */ +#endif /* CURL_DO_LINEEND_CONV */ - /* Connect only if not already connected! */ - result = ConnectPlease(conn, hostaddr, &connected); + for(;;) { + /* loop for CURL_SERVER_CLOSED_CONNECTION */ - if(connected) { - result = Curl_protocol_connect(conn); - if(CURLE_OK == result) - conn->bits.tcpconnect = TRUE; - } - else - conn->bits.tcpconnect = FALSE; + if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { + bool connected = FALSE; + /* Connect only if not already connected! */ + result = ConnectPlease(data, conn, hostaddr, &connected); - if(CURLE_OK != result) - return result; - } - else { - Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - conn->bits.tcpconnect = TRUE; - if(data->set.verbose) - verboseconnect(conn); + 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 */ + 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 @@ -3410,7 +4027,8 @@ static CURLcode SetupConnection(struct connectdata *conn, CURLcode Curl_connect(struct SessionHandle *data, struct connectdata **in_connect, - bool *asyncp) + bool *asyncp, + bool *protocol_done) { CURLcode code; struct Curl_dns_entry *dns; @@ -3424,11 +4042,11 @@ CURLcode Curl_connect(struct SessionHandle *data, /* no error */ if(dns || !*asyncp) /* If an address is available it means that we already have the name - resolved, OR it isn't async. - If so => continue connecting from here */ - code = SetupConnection(*in_connect, dns); + resolved, OR it isn't async. if this is a re-used connection 'dns' + will be NULL here. Continue connecting from here */ + code = SetupConnection(*in_connect, dns, protocol_done); /* else - response will be received and treated async wise */ + response will be received and treated async wise */ } if(CURLE_OK != code) { @@ -3439,17 +4057,25 @@ CURLcode Curl_connect(struct SessionHandle *data, *in_connect = NULL; /* return a NULL */ } } + else { + if ((*in_connect)->is_in_pipeline) + data->state.is_in_pipeline = TRUE; + } return code; } /* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received */ -CURLcode Curl_async_resolved(struct connectdata *conn) + then a successful name resolve has been received. + + Note: this function disconnects and frees the conn data in case of + resolve failure */ +CURLcode Curl_async_resolved(struct connectdata *conn, + bool *protocol_done) { #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \ defined(USE_THREADING_GETADDRINFO) - CURLcode code = SetupConnection(conn, conn->async.dns); + CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done); if(code) /* We're not allowed to return failure with memory left allocated @@ -3459,51 +4085,63 @@ CURLcode Curl_async_resolved(struct connectdata *conn) return code; #else (void)conn; + (void)protocol_done; return CURLE_OK; #endif } CURLcode Curl_done(struct connectdata **connp, - CURLcode status) /* an error if this is called after an + CURLcode status, bool premature) /* an error if this is called after an error was detected */ { CURLcode result; struct connectdata *conn = *connp; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; - /* cleanups done even if the connection is re-used */ + Curl_expire(data, 0); /* stop timer */ + + if(conn->bits.done) + return CURLE_OK; /* Curl_done() has already been called */ - if(conn->bits.rangestringalloc) { - free(conn->range); - conn->bits.rangestringalloc = FALSE; + conn->bits.done = TRUE; /* called just now! */ + + 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; + + /* cleanups done even if the connection is re-used */ + if(data->reqdata.rangestringalloc) { + free(data->reqdata.range); + data->reqdata.rangestringalloc = FALSE; } /* Cleanup possible redirect junk */ - if(conn->newurl) { - free(conn->newurl); - conn->newurl = NULL; + if(data->reqdata.newurl) { + free(data->reqdata.newurl); + data->reqdata.newurl = NULL; } - if(conn->dns_entry) - Curl_resolv_unlock(conn->data, conn->dns_entry); /* done with this */ - -#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_hostcache_prune(data); /* kill old DNS cache entries */ + 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->curl_done) - result = conn->curl_done(conn, status); + result = conn->curl_done(conn, status, premature); else result = CURLE_OK; Curl_pgrsDone(conn); /* done with the operation */ + /* 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. @@ -3511,8 +4149,7 @@ CURLcode Curl_done(struct connectdata **connp, closed in spite of all our efforts to be nice, due to protocol restrictions in our or the server's end */ if(data->set.reuse_forbid || conn->bits.close) { - CURLcode res2; - res2 = Curl_disconnect(conn); /* close the connection */ + CURLcode res2 = Curl_disconnect(conn); /* close the connection */ *connp = NULL; /* to make the caller of this function better detect that this was actually killed here */ @@ -3522,25 +4159,32 @@ CURLcode Curl_done(struct connectdata **connp, if(!result && res2) result = res2; } - else + 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); + } return result; } -CURLcode Curl_do(struct connectdata **connp) +CURLcode Curl_do(struct connectdata **connp, bool *done) { CURLcode result=CURLE_OK; struct connectdata *conn = *connp; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; + conn->bits.done = FALSE; /* Curl_done() is not called yet */ conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ if(conn->curl_do) { /* generic protocol-specific function pointer set in curl_connect() */ - result = conn->curl_do(conn); + result = conn->curl_do(conn, done); /* This was formerly done in transfer.c, but we better do it here */ @@ -3554,14 +4198,23 @@ CURLcode Curl_do(struct connectdata **connp) 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); /* we are so done with this */ + result = Curl_done(&conn, result, FALSE); /* we are so done with this */ /* conn may no longer be a good pointer */ - if(CURLE_OK == result) { + /* + * 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); + result = Curl_connect(data, connp, &async, &protocol_done); if(CURLE_OK == result) { /* We have connected or sent away a name resolve query fine */ @@ -3574,13 +4227,13 @@ CURLcode Curl_do(struct connectdata **connp) return result; /* Resolved, continue with the connection */ - result = Curl_async_resolved(conn); + result = Curl_async_resolved(conn, &protocol_done); if(result) return result; } /* ... finally back to actually retry the DO phase */ - result = conn->curl_do(conn); + result = conn->curl_do(conn, done); } } } @@ -3597,90 +4250,3 @@ CURLcode Curl_do_more(struct connectdata *conn) return result; } - -static bool safe_strequal(char* str1, char* str2) -{ - if(str1 && str2) - /* both pointers point to something then compare them */ - return strequal(str1, str2); - else - /* if both pointers are NULL then treat them as equal */ - return (!str1 && !str2); -} - -bool -Curl_ssl_config_matches(struct ssl_config_data* data, - struct ssl_config_data* needle) -{ - if((data->version == needle->version) && - (data->verifypeer == needle->verifypeer) && - (data->verifyhost == needle->verifyhost) && - safe_strequal(data->CApath, needle->CApath) && - safe_strequal(data->CAfile, needle->CAfile) && - safe_strequal(data->random_file, needle->random_file) && - safe_strequal(data->egdsocket, needle->egdsocket) && - safe_strequal(data->cipher_list, needle->cipher_list)) - return TRUE; - - return FALSE; -} - -bool -Curl_clone_ssl_config(struct ssl_config_data *source, - struct ssl_config_data *dest) -{ - 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; -} - -void Curl_free_ssl_config(struct ssl_config_data* sslc) -{ - if(sslc->CAfile) - free(sslc->CAfile); - - if(sslc->CApath) - free(sslc->CApath); - - if(sslc->cipher_list) - free(sslc->cipher_list); - - if(sslc->egdsocket) - free(sslc->egdsocket); - - if(sslc->random_file) - free(sslc->random_file); -} - diff --git a/Utilities/cmcurl/url.h b/Utilities/cmcurl/url.h index 8fd4795..446b3d9 100644 --- a/Utilities/cmcurl/url.h +++ b/Utilities/cmcurl/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,25 +23,63 @@ * $Id$ ***************************************************************************/ +#include <stdarg.h> /* to make sure we have ap_list */ + /* * Prototypes for library-wide functions provided by url.c */ CURLcode Curl_open(struct SessionHandle **curl); -CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...); +CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, + va_list arg); CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ CURLcode Curl_connect(struct SessionHandle *, struct connectdata **, - bool *async); -CURLcode Curl_async_resolved(struct connectdata *conn); -CURLcode Curl_do(struct connectdata **); + bool *async, bool *protocol_connect); +CURLcode Curl_async_resolved(struct connectdata *conn, + bool *protocol_connect); +CURLcode Curl_do(struct connectdata **, bool *done); CURLcode Curl_do_more(struct connectdata *); -CURLcode Curl_done(struct connectdata **, CURLcode); +CURLcode Curl_done(struct connectdata **, CURLcode, bool premature); CURLcode Curl_disconnect(struct connectdata *); -CURLcode Curl_protocol_connect(struct connectdata *conn); -bool Curl_ssl_config_matches(struct ssl_config_data* data, - struct ssl_config_data* needle); -bool Curl_clone_ssl_config(struct ssl_config_data* source, - struct ssl_config_data* dest); -void Curl_free_ssl_config(struct ssl_config_data* sslc); +CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); +CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); +CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done); void Curl_safefree(void *ptr); + +/* create a connection cache */ +struct conncache *Curl_mk_connc(int type, int amount); +/* free a connection cache */ +void Curl_rm_connc(struct conncache *c); +/* Change number of entries of a connection cache */ +CURLcode Curl_ch_connc(struct SessionHandle *data, + struct conncache *c, + long newamount); + +int Curl_protocol_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); +int Curl_doing_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); + +void Curl_addHandleToPipeline(struct SessionHandle *handle, + struct curl_llist *pipe); +int Curl_removeHandleFromPipeline(struct SessionHandle *handle, + struct curl_llist *pipe); +bool Curl_isHandleAtHead(struct SessionHandle *handle, + struct curl_llist *pipe); + +void Curl_close_connections(struct SessionHandle *data); + +#if 0 +CURLcode Curl_protocol_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp); +CURLcode Curl_doing_fdset(struct connectdata *conn, + fd_set *read_fd_set, + fd_set *write_fd_set, + int *max_fdp); +#endif + #endif diff --git a/Utilities/cmcurl/urldata.h b/Utilities/cmcurl/urldata.h index 07aff9b..9537ce3 100644 --- a/Utilities/cmcurl/urldata.h +++ b/Utilities/cmcurl/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,11 +30,12 @@ #define PORT_FTP 21 #define PORT_FTPS 990 #define PORT_TELNET 23 -#define PORT_GOPHER 70 #define PORT_HTTP 80 #define PORT_HTTPS 443 #define PORT_DICT 2628 #define PORT_LDAP 389 +#define PORT_TFTP 69 +#define PORT_SSH 22 #define DICT_MATCH "/MATCH:" #define DICT_MATCH2 "/M:" @@ -50,7 +51,6 @@ #include "formdata.h" #ifdef USE_SSLEAY -/* SSLeay stuff usually in /usr/local/ssl/include */ #ifdef USE_OPENSSL #include "openssl/rsa.h" #include "openssl/crypto.h" @@ -61,14 +61,21 @@ #ifdef HAVE_OPENSSL_ENGINE_H #include <openssl/engine.h> #endif -#else +#ifdef HAVE_OPENSSL_PKCS12_H +#include <openssl/pkcs12.h> +#endif +#else /* SSLeay-style includes */ #include "rsa.h" #include "crypto.h" #include "x509.h" #include "pem.h" #include "ssl.h" #include "err.h" -#endif +#endif /* USE_OPENSSL */ +#endif /* USE_SSLEAY */ + +#ifdef USE_GNUTLS +#include <gnutls/gnutls.h> #endif #ifdef HAVE_NETINET_IN_H @@ -81,10 +88,6 @@ #include <zlib.h> /* for content-encoding */ #endif -#ifdef CURL_SPECIAL_ZLIB_H -#include CURL_SPECIAL_ZLIB_H -#endif - #ifdef USE_ARES #include <ares.h> #endif @@ -94,23 +97,34 @@ #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" +#include "splay.h" #ifdef HAVE_GSSAPI -#ifdef HAVE_GSSMIT -#include <gssapi/gssapi.h> -#include <gssapi/gssapi_generic.h> -#else -#include <gssapi.h> -#endif +# ifdef HAVE_GSSGNU +# include <gss.h> +# elif defined HAVE_GSSMIT +# include <gssapi/gssapi.h> +# include <gssapi/gssapi_generic.h> +# else +# include <gssapi.h> +# endif #endif +#ifdef HAVE_LIBSSH2_H +#include <libssh2.h> +#include <libssh2_sftp.h> +#endif /* HAVE_LIBSSH2_H */ + /* Download buffer size, keep it fairly big for speed reasons */ +#undef BUFSIZE #define BUFSIZE CURL_MAX_WRITE_SIZE /* Initial size of the buffer to store headers in, it'll be enlarged in case of need. */ #define HEADERSIZE 256 +#define CURLEASY_MAGIC_NUMBER 0xc0dedbad + /* Just a convenience macro to get the larger value out of two given. We prefix with CURL to prevent name collisions. */ #define CURLMAX(x,y) ((x)>(y)?(x):(y)) @@ -124,13 +138,23 @@ struct krb4buffer { int eof_flag; }; enum protection_level { - prot_clear, - prot_safe, - prot_confidential, - prot_private + prot_clear, + prot_safe, + prot_confidential, + prot_private }; #endif +/* enum for the nonblocking SSL connection state machine */ +typedef enum { + ssl_connect_1, + ssl_connect_2, + ssl_connect_2_reading, + ssl_connect_2_writing, + ssl_connect_3, + ssl_connect_done +} ssl_connect_state; + /* struct for data related to each SSL connection */ struct ssl_connect_data { bool use; /* use ssl encrypted communications TRUE/FALSE */ @@ -139,7 +163,12 @@ struct ssl_connect_data { SSL_CTX* ctx; SSL* handle; X509* server_cert; + ssl_connect_state connecting_state; #endif /* USE_SSLEAY */ +#ifdef USE_GNUTLS + gnutls_session session; + gnutls_certificate_credentials cred; +#endif /* USE_GNUTLS */ }; struct ssl_config_data { @@ -155,14 +184,16 @@ struct ssl_config_data { char *egdsocket; /* path to file containing the EGD daemon socket */ char *cipher_list; /* list of ciphers to use */ long numsessions; /* SSL session id cache size */ - curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ - void *fsslctxp; /*parameter for call back */ + curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ + void *fsslctxp; /* parameter for call back */ + bool sessionid; /* cache session IDs or not */ }; /* information stored about one single SSL session */ struct curl_ssl_session { char *name; /* host name for which this ID was used */ void *sessionid; /* as returned from the SSL layer */ + size_t idsize; /* if known, otherwise 0 */ long age; /* just a number, the higher the more recent */ unsigned short remote_port; /* remote port to connect to */ struct ssl_config_data ssl_config; /* setup for this session */ @@ -189,16 +220,35 @@ typedef enum { NTLMSTATE_LAST } curlntlm; -/* for 3rd party transfers to decide which side that issues PASV */ -typedef enum { - CURL_TARGET_PASV, - CURL_SOURCE_PASV -} curl_pasv_side; +#ifdef USE_WINDOWS_SSPI +/* When including these headers, you must define either SECURITY_WIN32 + * or SECURITY_KERNEL, indicating who is compiling the code. + */ +#define SECURITY_WIN32 1 +#include <security.h> +#include <sspi.h> +#include <rpc.h> +#endif + +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) +#include <iconv.h> +#endif /* Struct used for NTLM challenge-response authentication */ struct ntlmdata { curlntlm state; +#ifdef USE_WINDOWS_SSPI + CredHandle handle; + CtxtHandle c_handle; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + int has_handles; + void *type_2; + int n_type_2; +#else + unsigned int flags; unsigned char nonce[8]; +#endif }; #ifdef HAVE_GSSAPI @@ -250,34 +300,121 @@ struct HTTP { /**************************************************************************** * FTP unique setup ***************************************************************************/ +typedef enum { + FTP_STOP, /* do nothing state, stops the state machine */ + FTP_WAIT220, /* waiting for the initial 220 response immediately after + a connect */ + FTP_AUTH, + FTP_USER, + FTP_PASS, + FTP_ACCT, + FTP_PBSZ, + FTP_PROT, + FTP_CCC, + FTP_PWD, + FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ + FTP_RETR_PREQUOTE, + FTP_STOR_PREQUOTE, + FTP_POSTQUOTE, + FTP_CWD, /* change dir */ + FTP_MKD, /* if the dir didn't exist */ + FTP_MDTM, /* to figure out the datestamp */ + FTP_TYPE, /* to set type when doing a head-like request */ + FTP_LIST_TYPE, /* set type when about to do a dir list */ + FTP_RETR_TYPE, /* set type when about to RETR a file */ + FTP_STOR_TYPE, /* set type when about to STOR a file */ + FTP_SIZE, /* get the remote file's size for head-like request */ + FTP_RETR_SIZE, /* get the remote file's size for RETR */ + FTP_STOR_SIZE, /* get the size for (resumed) STOR */ + FTP_REST, /* when used to check if the server supports it in head-like */ + FTP_RETR_REST, /* when asking for "resume" in for RETR */ + FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PASV, /* generic state for PASV and EPSV, check count1 */ + FTP_LIST, /* generic state for LIST, NLST or a custom list command */ + FTP_RETR, + FTP_STOR, /* generic state for STOR and APPE */ + FTP_QUIT, + FTP_LAST /* never used */ +} ftpstate; + +typedef enum { + FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ + FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ + FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */ +} curl_ftpfile; + +/* This FTP struct is used in the SessionHandle. All FTP data that is + connection-oriented must be in FTP_conn to properly deal with the fact that + perhaps the SessionHandle is changed between the times the connection is + used. */ struct FTP { curl_off_t *bytecountp; char *user; /* user name string */ char *passwd; /* password string */ char *urlpath; /* the originally given path part of the URL */ - char **dirs; /* realloc()ed array for path components */ - int dirdepth; /* number of entries used in the 'dirs' array */ - int diralloc; /* number of entries allocated for the 'dirs' array */ char *file; /* decoded file */ + bool no_transfer; /* nothing was transfered, (possibly because a resumed + transfer already was complete) */ + curl_off_t downloadsize; +}; +/* ftp_conn is used for striuct connection-oriented data in the connectdata + struct */ +struct ftp_conn { char *entrypath; /* the PWD reply when we logged on */ - + char **dirs; /* realloc()ed array for path components */ + int dirdepth; /* number of entries used in the 'dirs' array */ + int diralloc; /* number of entries allocated for the 'dirs' array */ char *cache; /* data cache between getresponse()-calls */ curl_off_t cache_size; /* size of cache in bytes */ bool dont_check; /* Set to TRUE to prevent the final (post-transfer) file size and 226/250 status check. It should still read the line, just ignore the result. */ - bool no_transfer; /* nothing was transfered, (possibly because a resumed - transfer already was complete) */ long response_time; /* When no timeout is given, this is the amount of seconds we await for an FTP response. Initialized in Curl_ftp_connect() */ - bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do - anything. If the connection has timed out or - been closed, this should be FALSE when it gets - to Curl_ftp_quit() */ + bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + bool cwddone; /* if it has been determined that the proper CWD combo + already has been done */ + bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + char *prevpath; /* conn->path from the previous transfer */ + char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a + and others (A/I or zero) */ + size_t nread_resp; /* number of bytes currently read of a server response */ + char *linestart_resp; /* line start pointer for the FTP server response + reader function */ + + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + char *sendthis; /* allocated pointer to a buffer that is to be sent to the + ftp server */ + size_t sendleft; /* number of bytes left to send from the sendthis buffer */ + size_t sendsize; /* total size of the sendthis buffer */ + struct timeval response; /* set to Curl_tvnow() when a command has been sent + off, used to time-out response reading */ + ftpstate state; /* always use ftp.c:state() to change state! */ +}; + +struct SSHPROTO { + curl_off_t *bytecountp; + char *user; + char *passwd; + char *path; /* the path we operate on */ + char *homedir; + char *errorstr; +#ifdef USE_LIBSSH2 + LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ + LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ + LIBSSH2_SFTP *sftp_session; /* SFTP handle */ + LIBSSH2_SFTP_HANDLE *sftp_handle; +#endif /* USE_LIBSSH2 */ }; + /**************************************************************************** * FILE unique setup ***************************************************************************/ @@ -298,10 +435,9 @@ struct ConnectBits { bool httpproxy; /* if set, this transfer is done through a http proxy */ bool user_passwd; /* do we use user+password for this connection? */ bool proxy_user_passwd; /* user+password for the proxy? */ - bool ipv6_ip; /* we communicate with a remove site specified with pure IPv6 + bool ipv6_ip; /* we communicate with a remote site specified with pure IPv6 IP address */ - bool use_range; - bool rangestringalloc; /* the range string is malloc()'ed */ + bool ipv6; /* we communicate with a site using an IPv6 address */ bool do_more; /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -313,9 +449,12 @@ struct ConnectBits { bool forbidchunk; /* used only to explicitly forbid chunk-upload for specific upload buffers. See readmoredata() in http.c for details. */ - bool tcpconnect; /* the tcp stream (or simimlar) is connected, this - is set the first time on the first connect function - call */ + + bool tcpconnect; /* the TCP layer (or simimlar) is connected, this is set + the first time on the first connect function call */ + bool protoconnstart;/* the protocol layer has STARTED its operation after + the TCP layer connect */ + bool retry; /* this connection is about to get closed and then re-attempted at another connection. */ bool no_body; /* CURLOPT_NO_BODY (or similar) was set */ @@ -323,9 +462,36 @@ struct ConnectBits { This is implicit when SSL-protocols are used through proxies, but can also be enabled explicitly by apps */ - bool authprobe; /* set TRUE when this transfer is done to probe for auth - types, as when asking for "any" type when speaking - HTTP */ + bool authneg; /* TRUE when the auth phase has started, which means + that we are creating a request with an auth header, + but it is not the final request in the auth + negotiation. */ + bool rewindaftersend;/* TRUE when the sending couldn't be stopped even + though it will be discarded. When the whole send + operation is done, we must call the data rewind + callback. */ + bool ftp_use_epsv; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out + EPSV doesn't work we disable it for the forthcoming + requests */ + + bool ftp_use_eprt; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out + EPRT doesn't work we disable it for the forthcoming + requests */ + bool netrc; /* name+password provided by netrc */ + + bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response. + Required to determine whether to look for trailers + in case of Transfer-Encoding: chunking */ + bool done; /* set to FALSE when Curl_do() is called and set to TRUE + when Curl_done() is called, to prevent Curl_done() to + get invoked twice when the multi interface is + used. */ + bool stream_was_rewound; /* Indicates that the stream was rewound after a + request read past the end of its response byte + boundary */ + bool proxy_connect_closed; /* set true if a proxy disconnected the + connection in a CONNECT request with auth, so + that libcurl should reconnect and continue. */ }; struct hostname { @@ -336,14 +502,39 @@ struct hostname { }; /* + * Flags on the keepon member of the Curl_transfer_keeper + */ + +#define KEEP_NONE 0 +#define KEEP_READ 1 /* there is or may be data to read */ +#define KEEP_WRITE 2 /* there is or may be data to write */ +#define KEEP_READ_HOLD 4 /* when set, no reading should be done but there + might still be data to read */ +#define KEEP_WRITE_HOLD 8 /* when set, no writing should be done but there + might still be data to write */ + +/* * This struct is all the previously local variables from Curl_perform() moved * to struct to allow the function to return and get re-invoked better without * losing state. */ struct Curl_transfer_keeper { + + /** Values copied over from the HandleData struct each time on init **/ + + curl_off_t size; /* -1 if unknown at this point */ + curl_off_t *bytecountp; /* return number of bytes read or NULL */ + + curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0 + means unlimited */ + curl_off_t *writebytecountp; /* return number of bytes written or NULL */ + + /** End of HandleData struct copies **/ + curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ + struct timeval start; /* transfer started at this time */ struct timeval now; /* current time */ bool header; /* incoming data has HTTP header */ @@ -394,21 +585,14 @@ struct Curl_transfer_keeper { char *uploadbuf; curl_socket_t maxfd; - /* pointers to the actual descriptors we check */ - fd_set *readfdp; - fd_set *writefdp; - - /* the file descriptors to play with */ - fd_set readfd; - fd_set writefd; - fd_set rkeepfd; - fd_set wkeepfd; int keepon; bool upload_done; /* set to TRUE when doing chunked transfer-encoding upload and we're uploading the last chunk */ bool ignorebody; /* we read a response-body but we ignore it! */ + bool ignorecl; /* This HTTP response has no body so we ignore the Content- + Length: header */ }; #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \ @@ -426,19 +610,89 @@ struct Curl_async { #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 +/* These function pointer types are here only to allow easier typecasting + within the source when we need to cast between data pointers (such as NULL) + and function pointers. */ +typedef CURLcode (*Curl_do_more_func)(struct connectdata *); +typedef CURLcode (*Curl_done_func)(struct connectdata *, CURLcode, bool); + + +/* + * Store's request specific data in the easy handle (SessionHandle). + * Previously, these members were on the connectdata struct but since + * a conn struct may now be shared between different SessionHandles, + * we store connection-specifc data here. + * + */ +struct HandleData { + char *pathbuffer;/* allocated buffer to store the URL's path part in */ + char *path; /* path to use, points to somewhere within the pathbuffer + area */ + + char *newurl; /* This can only be set if a Location: was in the + document headers */ + + /* This struct is inited when needed */ + struct Curl_transfer_keeper keep; + + /* 'upload_present' is used to keep a byte counter of how much data there is + still left in the buffer, aimed for upload. */ + ssize_t upload_present; + + /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a + buffer, so the next read should read from where this pointer points to, + and the 'upload_present' contains the number of bytes available at this + position */ + char *upload_fromhere; + + curl_off_t size; /* -1 if unknown at this point */ + curl_off_t *bytecountp; /* return number of bytes read or NULL */ + + curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0 + means unlimited */ + curl_off_t *writebytecountp; /* return number of bytes written or NULL */ + + bool use_range; + bool rangestringalloc; /* the range string is malloc()'ed */ + + char *range; /* range, if used. See README for detailed specification on + this syntax. */ + curl_off_t resume_from; /* continue [ftp] transfer from here */ + + /* Protocol specific data */ + + union { + struct HTTP *http; + struct HTTP *https; /* alias, just for the sake of being more readable */ + struct FTP *ftp; + void *tftp; /* private for tftp.c-eyes only */ + struct FILEPROTO *file; + void *telnet; /* private for telnet.c-eyes only */ + void *generic; + struct SSHPROTO *ssh; + } proto; +}; + /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. */ struct connectdata { - /**** Fields set when inited and not modified again */ - struct SessionHandle *data; /* link to the root CURL struct */ - long connectindex; /* what index in the connects index this particular - struct has */ + /* 'data' is the CURRENT SessionHandle using this connection -- take great + caution that this might very well vary between different times this + connection is used! */ + struct SessionHandle *data; + + bool inuse; /* This is a marker for the connection cache logic. If this is + TRUE this handle is being used by an easy handle and cannot + be used by any other easy handle without careful + consideration (== only for pipelining). */ + /**** Fields set when inited and not modified again */ + long connectindex; /* what index in the connection cache connects index this + particular struct has */ long protocol; /* PROT_* flags concerning the protocol set */ #define PROT_MISSING (1<<0) -#define PROT_GOPHER (1<<1) #define PROT_HTTP (1<<2) #define PROT_HTTPS (1<<3) #define PROT_FTP (1<<4) @@ -448,23 +702,40 @@ struct connectdata { #define PROT_FILE (1<<8) #define PROT_FTPS (1<<9) #define PROT_SSL (1<<10) /* protocol requires SSL */ +#define PROT_TFTP (1<<11) +#define PROT_SCP (1<<12) +#define PROT_SFTP (1<<13) + +#define PROT_CLOSEACTION PROT_FTP /* these ones need action before socket + close */ - /* the particular host we use, in two different ways */ + /* 'dns_entry' is the particular host we use. This points to an entry in the + DNS cache and it will not get pruned while locked. It gets unlocked in + Curl_done(). This entry will be NULL if the connection is re-used as then + there is no name resolve done. */ struct Curl_dns_entry *dns_entry; - Curl_addrinfo *ip_addr; /* the particular IP we connected to */ + + /* 'ip_addr' is the particular IP we connected to. It points to a struct + within the DNS cache, so this pointer is only valid as long as the DNS + cache entry remains locked. It gets unlocked in Curl_done() */ + Curl_addrinfo *ip_addr; + + /* 'ip_addr_str' is the ip_addr data as a human readable malloc()ed string. + It remains available as long as the connection does, which is longer than + the ip_addr itself. Set with Curl_store_ip_addr() when ip_addr has been + set. */ + char *ip_addr_str; char protostr[16]; /* store the protocol string in this buffer */ + int socktype; /* SOCK_STREAM or SOCK_DGRAM */ struct hostname host; struct hostname proxy; - char *pathbuffer;/* allocated buffer to store the URL's path part in */ - char *path; /* path to use, points to somewhere within the pathbuffer - area */ long port; /* which port to use locally */ unsigned short remote_port; /* what remote port to connect to, not the proxy port! */ - curl_off_t bytecount; + long headerbytecount; /* only count received headers */ long deductheadercount; /* this amount of bytes doesn't count when we check if anything has been transfered at the end of @@ -472,10 +743,6 @@ struct connectdata { a 100 reply (without a following second response code) result in a CURLE_GOT_NOTHING error code */ - char *range; /* range, if used. See README for detailed specification on - this syntax. */ - curl_off_t resume_from; /* continue [ftp] transfer from here */ - char *user; /* user name string, allocated */ char *passwd; /* password string, allocated */ @@ -486,8 +753,6 @@ struct connectdata { struct timeval created; /* creation time */ curl_socket_t sock[2]; /* two sockets, the second is used for the data transfer when doing FTP */ - curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, 0 - means unlimited */ struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ struct ssl_config_data ssl_config; @@ -496,19 +761,38 @@ struct connectdata { /* These two functions MUST be set by the curl_connect() function to be be protocol dependent */ - CURLcode (*curl_do)(struct connectdata *); - CURLcode (*curl_done)(struct connectdata *, CURLcode); + CURLcode (*curl_do)(struct connectdata *, bool *done); + Curl_done_func curl_done; /* If the curl_do() function is better made in two halves, this * curl_do_more() function will be called afterwards, if set. For example * for doing the FTP stuff after the PASV/PORT command. */ - CURLcode (*curl_do_more)(struct connectdata *); + Curl_do_more_func curl_do_more; /* This function *MAY* be set to a protocol-dependent function that is run * after the connect() and everything is done, as a step in the connection. + * The 'done' pointer points to a bool that should be set to TRUE if the + * function completes before return. If it doesn't complete, the caller + * should call the curl_connecting() function until it is. */ - CURLcode (*curl_connect)(struct connectdata *); + CURLcode (*curl_connect)(struct connectdata *, bool *done); + + /* See above. Currently only used for FTP. */ + CURLcode (*curl_connecting)(struct connectdata *, bool *done); + CURLcode (*curl_doing)(struct connectdata *, bool *done); + + /* Called from the multi interface during the PROTOCONNECT phase, and it + should then return a proper fd set */ + int (*curl_proto_getsock)(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); + + /* Called from the multi interface during the DOING phase, and it should + then return a proper fd set */ + int (*curl_doing_getsock)(struct connectdata *conn, + curl_socket_t *socks, + int numsocks); /* This function *MAY* be set to a protocol-dependent function that is run * by the curl_disconnect(), as a step in the disconnection. @@ -522,16 +806,10 @@ struct connectdata { /**** curl_get() phase fields */ - /* READ stuff */ curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ - curl_off_t size; /* -1 if unknown at this point */ - curl_off_t *bytecountp; /* return number of bytes read or NULL */ - - /* WRITE stuff */ curl_socket_t writesockfd; /* socket to write to, it may very well be the same we read from. CURL_SOCKET_BAD disables */ - curl_off_t *writebytecountp; /* return number of bytes written or NULL */ /** Dynamicly allocated strings, may need to be freed before this **/ /** struct is killed. **/ @@ -546,50 +824,37 @@ struct connectdata { char *cookiehost; /* free later if not NULL */ } allocptr; - char *newurl; /* This can only be set if a Location: was in the - document headers */ - + int sec_complete; /* if krb4 is enabled for this connection */ #ifdef HAVE_KRB4 enum protection_level command_prot; enum protection_level data_prot; enum protection_level request_data_prot; - size_t buffer_size; - struct krb4buffer in_buffer, out_buffer; - int sec_complete; void *app_data; - - struct Curl_sec_client_mech *mech; + const struct Curl_sec_client_mech *mech; struct sockaddr_in local_addr; - #endif - /*************** Request - specific items ************/ - /* previously this was in the urldata struct */ - union { - struct HTTP *http; - struct HTTP *gopher; /* alias, just for the sake of being more readable */ - struct HTTP *https; /* alias, just for the sake of being more readable */ - struct FTP *ftp; - struct FILEPROTO *file; - void *telnet; /* private for telnet.c-eyes only */ - void *generic; - } proto; + bool readchannel_inuse; /* whether the read channel is in use by an easy + handle */ + bool writechannel_inuse; /* whether the write channel is in use by an easy + handle */ + bool is_in_pipeline; /* TRUE if this connection is in a pipeline */ - /* This struct is inited when needed */ - struct Curl_transfer_keeper keep; + struct curl_llist *send_pipe; /* List of handles waiting to + send on this pipeline */ + struct curl_llist *recv_pipe; /* List of handles waiting to read + their responses on this pipeline */ - /* 'upload_present' is used to keep a byte counter of how much data there is - still left in the buffer, aimed for upload. */ - ssize_t upload_present; + char master_buffer[BUFSIZE]; /* The master buffer for this connection. */ + size_t read_pos; /* Current read position in the master buffer */ + size_t buf_len; /* Length of the buffer?? */ - /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a - buffer, so the next read should read from where this pointer points to, - and the 'upload_present' contains the number of bytes available at this - position */ - char *upload_fromhere; + /*************** Request - specific items ************/ + + /* previously this was in the urldata struct */ curl_read_callback fread; /* function that reads the input */ void *fread_in; /* pointer to pass to the fread() above */ @@ -605,8 +870,15 @@ struct connectdata { /* data used for the asynch name resolve callback */ struct Curl_async async; #endif - struct connectdata *sec_conn; /* secondary connection for 3rd party - transfer */ + + /* These three are used for chunked-encoding trailer support */ + char *trailer; /* allocated buffer to store trailer in */ + int trlMax; /* allocated buffer size */ + int trlPos; /* index of where to store data */ + + union { + struct ftp_conn ftpc; + } proto; }; /* The end of connectdata. */ @@ -628,6 +900,8 @@ struct PureInfo { long proxyauthavail; long httpauthavail; + long numconnects; /* how many new connection did libcurl created */ + char *contenttype; /* the content type of the object */ }; @@ -679,7 +953,7 @@ typedef enum { /* * Values that are generated, temporary or calculated internally for a - * "session handle" must be defined within the 'struct urlstate'. This struct + * "session handle" must be defined within the 'struct UrlState'. This struct * will be used within the SessionHandle struct. When the 'SessionHandle' * struct is cloned, this data MUST NOT be copied. * @@ -698,8 +972,23 @@ struct auth { resource */ bool done; /* TRUE when the auth phase is done and ready to do the *actual* request */ + bool multi; /* TRUE if this is not yet authenticated but within the auth + multipass negotiation */ + +}; + +struct conncache { + /* 'connects' will be an allocated array with pointers. If the pointer is + set, it holds an allocated connection. */ + struct connectdata **connects; + long num; /* number of entries of the 'connects' array */ + enum { + CONNCACHE_PRIVATE, /* used for an easy handle alone */ + CONNCACHE_MULTI /* shared within a multi handle */ + } type; }; + struct UrlState { enum { Curl_if_none, @@ -707,13 +996,13 @@ struct UrlState { Curl_if_multi } used_interface; + struct conncache *connc; /* points to the connection cache this handle + uses */ + /* buffers to store authentication data in, as parsed from input options */ struct timeval keeps_speed; /* for the progress meter really */ - /* 'connects' will be an allocated array with pointers. If the pointer is - set, it holds an allocated connection. */ - struct connectdata **connects; - long numconnects; /* size of the 'connects' array */ + long lastconnect; /* index of most recent connect or -1 if undefined */ char *headerbuff; /* allocated buffer to store headers in */ size_t headersize; /* size of the allocation */ @@ -724,10 +1013,12 @@ struct UrlState { bytes / second */ bool this_is_a_follow; /* this is a followed Location: request */ - char *auth_host; /* if set, this should be the host name that we will - sent authorization to, no else. Used to make Location: - following not keep sending user+password... This is - strdup() data. + bool is_in_pipeline; /* Indicates whether this handle is part of a pipeline */ + + char *first_host; /* if set, this should be the host name that we will + sent authorization to, no else. Used to make Location: + following not keep sending user+password... This is + strdup() data. */ struct curl_ssl_session *session; /* array of 'numsessions' size */ @@ -737,7 +1028,7 @@ struct UrlState { bool errorbuf; /* Set to TRUE if the error buffer is already filled in. This must be set to FALSE every time _easy_perform() is called. */ - + int os_errno; /* filled in with errno whenever an error occurs */ #ifdef HAVE_SIGNAL /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ void (*prev_signal)(int sig); @@ -756,9 +1047,45 @@ struct UrlState { struct auth authproxy; bool authproblem; /* TRUE if there's some problem authenticating */ + #ifdef USE_ARES ares_channel areschannel; /* for name resolves */ #endif + +#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) + ENGINE *engine; +#endif /* USE_SSLEAY */ + struct timeval expiretime; /* set this with Curl_expire() only */ + struct Curl_tree timenode; /* for the splay stuff */ + + /* a place to store the most recenlty set FTP entrypath */ + char *most_recent_ftp_entrypath; + + /* set after initial USER failure, to prevent an authentication loop */ + bool ftp_trying_alternative; + + bool expect100header; /* TRUE if we added Expect: 100-continue */ + + bool pipe_broke; /* TRUE if the connection we were pipelined on broke + and we need to restart from the beginning */ + bool cancelled; /* TRUE if the request was cancelled */ + +#ifndef WIN32 +/* do FTP line-end conversions on most platforms */ +#define CURL_DO_LINEEND_CONV + /* for FTP downloads: track CRLF sequences that span blocks */ + bool prev_block_had_trailing_cr; + /* for FTP downloads: how many CRLFs did we converted to LFs? */ + curl_off_t crlf_conversions; +#endif + /* If set to non-NULL, there's a connection in a shared connection cache + that uses this handle so we can't kill this SessionHandle just yet but + must keep it around and add it to the list of handles to kill once all + its connections are gone */ + void *shared_conn; + bool closed; /* set to TRUE when curl_easy_cleanup() has been called on this + handle, but it is kept around as mentioned for + shared_conn */ }; @@ -776,8 +1103,6 @@ struct DynamicStatic { changed after the connect phase, as we allow callback to change it and if so, we reconnect to use the new URL instead */ - char *proxy; /* work proxy, copied from UserDefined */ - bool proxy_alloc; /* http proxy string is malloc()'ed */ char *referer; /* referer string */ bool referer_alloc; /* referer sting is malloc()ed */ struct curl_slist *cookielist; /* list of cookie files set by @@ -788,9 +1113,11 @@ struct DynamicStatic { * This 'UserDefined' struct must only contain data that is set once to go * for many (perhaps) independent connections. Values that are generated or * calculated internally for the "session handle" MUST be defined within the - * 'struct urlstate' instead. The only exceptions MUST note the changes in + * 'struct UrlState' instead. The only exceptions MUST note the changes in * the 'DynamicStatic' struct. */ +struct Curl_one_easy; /* declared and used only in multi.c */ +struct Curl_multi; /* declared and used only in multi.c */ struct UserDefined { FILE *err; /* the stderr user data goes here */ @@ -802,9 +1129,9 @@ struct UserDefined { this. */ void *out; /* the fetched file goes here */ void *in; /* the uploaded file is read from here */ - void *writeheader; /* write the header to this is non-NULL */ + void *writeheader; /* write the header to this if non-NULL */ char *set_url; /* what original URL to work on */ - char *set_proxy; /* proxy to use */ + char *proxy; /* proxy to use */ long use_port; /* which port to use (when not using default) */ char *userpwd; /* <user:password>, if used */ long httpauth; /* what kind of HTTP authentication to use (bitmask) */ @@ -812,7 +1139,8 @@ struct UserDefined { char *set_range; /* range, if used. See README for detailed specification on this syntax. */ long followlocation; /* as in HTTP Location: */ - long maxredirs; /* maximum no. of http(s) redirects to follow */ + long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 + for infinity */ char *set_referer; /* custom string */ bool free_referer; /* set TRUE if 'referer' points to a string we allocated */ @@ -823,19 +1151,37 @@ struct UserDefined { of strlen(), and then the data *may* be binary (contain zero bytes) */ char *ftpport; /* port to send with the FTP PORT command */ - char *device; /* network interface to use */ + char *device; /* local network interface/address to use */ + unsigned short localport; /* local port number to bind to */ + int localportrange; /* number of additional port numbers to test in case the + 'localport' one can't be bind()ed */ curl_write_callback fwrite; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_read_callback fread; /* function that reads the input */ curl_progress_callback fprogress; /* function for progress information */ curl_debug_callback fdebug; /* function that write informational data */ + curl_ioctl_callback ioctl; /* function for I/O control */ + curl_sockopt_callback fsockopt; /* function for setting socket options */ + void *sockopt_client; /* pointer to pass to the socket options callback */ + + /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */ + /* function to convert from the network encoding: */ + curl_conv_callback convfromnetwork; + /* function to convert to the network encoding: */ + curl_conv_callback convtonetwork; + /* function to convert from UTF-8 encoding: */ + curl_conv_callback convfromutf8; + void *progress_client; /* pointer to pass to the progress callback */ + void *ioctl_client; /* pointer to pass to the ioctl callback */ long timeout; /* in seconds, 0 means no timeout */ long connecttimeout; /* in seconds, 0 means no timeout */ long ftp_response_timeout; /* in seconds, 0 means no timeout */ curl_off_t infilesize; /* size of file to upload, -1 means unknown */ long low_speed_limit; /* bytes/second */ long low_speed_time; /* number of seconds */ + curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */ + curl_off_t max_recv_speed; /* high speed limit in bytes/second for download */ curl_off_t set_resume_from; /* continue [ftp] transfer from here */ char *cookie; /* HTTP cookie string to send */ struct curl_slist *headers; /* linked list of extra headers */ @@ -845,13 +1191,15 @@ struct UserDefined { char *key; /* private key */ char *key_type; /* format for private key (default: PEM) */ char *key_passwd; /* plain text private key password */ - char *crypto_engine; /* name of the crypto engine to use */ char *cookiejar; /* dump all cookies to this file */ bool cookiesession; /* new cookie session? */ bool crlf; /* convert crlf on ftp upload(?) */ + char *ftp_account; /* ftp account data */ + char *ftp_alternative_to_user; /* command to send if USER/PASS fails */ struct curl_slist *quote; /* after connection is established */ struct curl_slist *postquote; /* after the transfer */ struct curl_slist *prequote; /* before the transfer, after type */ + struct curl_slist *source_quote; /* 3rd party quote */ struct curl_slist *source_prequote; /* in 3rd party transfer mode - before the transfer on source host */ struct curl_slist *source_postquote; /* in 3rd party transfer mode - after @@ -859,7 +1207,6 @@ struct UserDefined { struct curl_slist *telnet_options; /* linked list of telnet options */ curl_TimeCond timecondition; /* kind of time/date comparison */ time_t timevalue; /* what time to compare with */ - curl_closepolicy closepolicy; /* connection cache close concept */ Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ char *customrequest; /* HTTP/FTP request to use */ long httpversion; /* when non-zero, a specific HTTP version requested to @@ -876,7 +1223,13 @@ struct UserDefined { int dns_cache_timeout; /* DNS cache timeout */ long buffer_size; /* size of receive buffer to use */ - char *private; /* Private data */ + char *private_data; /* Private data */ + + struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi + handle, an internal 'Curl_one_easy' + struct is created and this is a pointer + to the particular struct associated with + this SessionHandle */ struct curl_slist *http200aliases; /* linked list of aliases for http200 */ @@ -884,11 +1237,10 @@ struct UserDefined { curl_off_t max_filesize; /* Maximum file size to download */ - char *source_host; /* for 3rd party transfer */ - char *source_port; /* for 3rd party transfer */ + char *source_url; /* for 3rd party transfer */ char *source_userpwd; /* for 3rd party transfer */ - char *source_path; /* for 3rd party transfer */ - curl_pasv_side pasvHost; /* for 3rd party transfer indicates passive host */ + + curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used */ /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially @@ -897,8 +1249,8 @@ struct UserDefined { bool printhost; /* printing host name in debug info */ bool get_filetime; bool tunnel_thru_httpproxy; + bool prefer_ascii; /* ASCII rather than binary */ bool ftp_append; - bool ftp_ascii; bool ftp_list_only; bool ftp_create_missing_dirs; bool ftp_use_port; @@ -920,30 +1272,53 @@ struct UserDefined { bool krb4; /* kerberos4 connection requested */ bool reuse_forbid; /* forbidden to be reused, close after use */ bool reuse_fresh; /* do not re-use an existing connection */ - bool expect100header; /* TRUE if we added Expect: 100-continue */ bool ftp_use_epsv; /* if EPSV is to be attempted or not */ bool ftp_use_eprt; /* if EPRT is to be attempted or not */ + bool ftp_use_ccc; /* if CCC is to be attempted or not */ + curl_ftpssl ftp_ssl; /* if AUTH TLS is to be attempted etc */ + curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */ bool no_signal; /* do not use any signal/alarm handler */ bool global_dns_cache; /* subject for future removal */ bool tcp_nodelay; /* whether to enable TCP_NODELAY or not */ + bool ignorecl; /* ignore content length */ + bool ftp_skip_ip; /* skip the IP address the FTP server passes on to + us */ + bool connect_only; /* make connection, let application use the socket */ + long ssh_auth_types; /* allowed SSH auth types */ + char *ssh_public_key; /* the path to the public key file for + authentication */ + char *ssh_private_key; /* the path to the private key file for + authentication */ +}; +struct Names { + struct curl_hash *hostcache; + enum { + HCACHE_NONE, /* not pointing to anything */ + HCACHE_PRIVATE, /* points to our own */ + HCACHE_GLOBAL, /* points to the (shrug) global one */ + HCACHE_MULTI, /* points to a shared one in the multi handle */ + HCACHE_SHARED /* points to a shared one in a shared object */ + } hostcachetype; }; /* - * In August 2001, this struct was redesigned and is since stricter than - * before. The 'connectdata' struct MUST have all the connection oriented - * stuff as we may now have several simultaneous connections and connection - * structs in memory. + * The 'connectdata' struct MUST have all the connection oriented stuff as we + * may have several simultaneous connections and connection structs in memory. * - * From now on, the 'SessionHandle' must only contain data that is set once to - * go for many (perhaps) independent connections. Values that are generated or + * The 'struct UserDefined' must only contain data that is set once to go for + * many (perhaps) independent connections. Values that are generated or * calculated internally for the "session handle" must be defined within the - * 'struct urlstate' instead. */ + * 'struct UrlState' instead. + */ struct SessionHandle { - curl_hash *hostcache; + struct Names dns; + struct Curl_multi *multi; /* if non-NULL, points to the multi handle + struct to which this "belongs" */ struct Curl_share *share; /* Share, handles global variable mutexing */ + struct HandleData reqdata; /* Request-specific data */ struct UserDefined set; /* values set by the libcurl user */ struct DynamicStatic change; /* possibly modified userdefined data */ @@ -952,9 +1327,12 @@ struct SessionHandle { struct UrlState state; /* struct for fields used for state info and other dynamic purposes */ struct PureInfo info; /* stats, reports and info data */ -#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) - ENGINE* engine; -#endif /* USE_SSLEAY */ +#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) + iconv_t outbound_cd; /* for translating to the network encoding */ + iconv_t inbound_cd; /* for translating from the network encoding */ + iconv_t utf8_cd; /* for translating to UTF8 */ +#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ + unsigned int magic; /* set to a CURLEASY_MAGIC_NUMBER */ }; #define LIBCURL_NAME "libcurl" diff --git a/Utilities/cmcurl/version.c b/Utilities/cmcurl/version.c index 4af298b..9085f7d 100644 --- a/Utilities/cmcurl/version.c +++ b/Utilities/cmcurl/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,6 +28,7 @@ #include <curl/curl.h> #include "urldata.h" +#include "sslgen.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include <curl/mprintf.h> @@ -40,88 +41,28 @@ #include <stringprep.h> #endif -#ifdef USE_SSLEAY -static int getssl_version(char *ptr, size_t left, long *num) -{ - -#if (SSLEAY_VERSION_NUMBER >= 0x905000) - { - char sub[2]; - unsigned long ssleay_value; - sub[1]='\0'; - ssleay_value=SSLeay(); - *num = (long)ssleay_value; - if(ssleay_value < 0x906000) { - ssleay_value=SSLEAY_VERSION_NUMBER; - sub[0]='\0'; - } - else { - if(ssleay_value&0xff0) { - sub[0]=(char)((ssleay_value>>4)&0xff) + 'a' -1; - } - else - sub[0]='\0'; - } - - return snprintf(ptr, left, " OpenSSL/%lx.%lx.%lx%s", - (ssleay_value>>28)&0xf, - (ssleay_value>>20)&0xff, - (ssleay_value>>12)&0xff, - sub); - } - -#else - *num = SSLEAY_VERSION_NUMBER; -#if (SSLEAY_VERSION_NUMBER >= 0x900000) - return snprintf(ptr, left, " OpenSSL/%lx.%lx.%lx", - (SSLEAY_VERSION_NUMBER>>28)&0xff, - (SSLEAY_VERSION_NUMBER>>20)&0xff, - (SSLEAY_VERSION_NUMBER>>12)&0xf); -#else - { - char sub[2]; - sub[1]='\0'; - if(SSLEAY_VERSION_NUMBER&0x0f) { - sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; - } - else - sub[0]='\0'; - - return snprintf(ptr, left, " SSL/%x.%x.%x%s", - (SSLEAY_VERSION_NUMBER>>12)&0xff, - (SSLEAY_VERSION_NUMBER>>8)&0xf, - (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); - } -#endif +#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) +#include <iconv.h> #endif -} +#ifdef USE_LIBSSH2 +#include <libssh2.h> #endif + char *curl_version(void) { static char version[200]; char *ptr=version; - /* to prevent compier warnings, we only declare len if we have code - that uses it */ -#if defined(USE_SSLEAY) || defined(HAVE_LIBZ) || defined(USE_ARES) || \ - defined(USE_LIBIDN) - int len; -#endif + size_t len; size_t left = sizeof(version); strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION ); ptr=strchr(ptr, '\0'); left -= strlen(ptr); - (void)left; -#ifdef USE_SSLEAY - { - long num; - len = getssl_version(ptr, left, &num); - left -= len; - ptr += len; - } -#endif + len = Curl_ssl_version(ptr, left); + left -= len; + ptr += len; #ifdef HAVE_LIBZ len = snprintf(ptr, left, " zlib/%s", zlibVersion()); @@ -141,19 +82,35 @@ char *curl_version(void) ptr += len; } #endif +#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) +#ifdef _LIBICONV_VERSION + len = snprintf(ptr, left, " iconv/%d.%d", + _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255); +#else + /* version unknown */ + len = snprintf(ptr, left, " iconv"); +#endif /* _LIBICONV_VERSION */ + left -= len; + ptr += len; +#endif +#ifdef USE_LIBSSH2 + len = snprintf(ptr, left, " libssh2/%s", LIBSSH2_VERSION); + left -= len; + ptr += len; +#endif return version; } /* data for curl_version_info */ -static const char *protocols[] = { +static const char * const protocols[] = { +#ifndef CURL_DISABLE_TFTP + "tftp", +#endif #ifndef CURL_DISABLE_FTP "ftp", #endif -#ifndef CURL_DISABLE_GOPHER - "gopher", -#endif #ifndef CURL_DISABLE_TELNET "telnet", #endif @@ -170,7 +127,7 @@ static const char *protocols[] = { "file", #endif -#ifdef USE_SSLEAY +#ifdef USE_SSL #ifndef CURL_DISABLE_HTTP "https", #endif @@ -178,6 +135,12 @@ static const char *protocols[] = { "ftps", #endif #endif + +#ifdef USE_LIBSSH2 + "scp", + "sftp", +#endif + NULL }; @@ -193,9 +156,14 @@ static curl_version_info_data version_info = { #ifdef HAVE_KRB4 | CURL_VERSION_KERBEROS4 #endif -#ifdef USE_SSLEAY +#ifdef USE_SSL | CURL_VERSION_SSL - | CURL_VERSION_NTLM /* since this requires OpenSSL */ +#endif +#ifdef USE_NTLM + | CURL_VERSION_NTLM +#endif +#ifdef USE_WINDOWS_SSPI + | CURL_VERSION_SSPI #endif #ifdef HAVE_LIBZ | CURL_VERSION_LIBZ @@ -215,26 +183,31 @@ static curl_version_info_data version_info = { #if defined(ENABLE_64BIT) && (SIZEOF_CURL_OFF_T > 4) | CURL_VERSION_LARGEFILE #endif +#if defined(CURL_DOES_CONVERSIONS) + | CURL_VERSION_CONV +#endif , NULL, /* ssl_version */ - 0, /* ssl_version_num */ + 0, /* ssl_version_num, this is kept at zero */ NULL, /* zlib_version */ protocols, NULL, /* c-ares version */ 0, /* c-ares version numerical */ NULL, /* libidn version */ + 0, /* iconv version */ + NULL, /* ssh lib version */ }; curl_version_info_data *curl_version_info(CURLversion stamp) { -#ifdef USE_SSLEAY - static char ssl_buffer[80]; - long num; - getssl_version(ssl_buffer, sizeof(ssl_buffer), &num); +#ifdef USE_LIBSSH2 + static char ssh_buffer[80]; +#endif +#ifdef USE_SSL + static char ssl_buffer[80]; + Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); version_info.ssl_version = ssl_buffer; - version_info.ssl_version_num = num; - /* SSL stuff is left zero if undefined */ #endif #ifdef HAVE_LIBZ @@ -255,6 +228,21 @@ curl_version_info_data *curl_version_info(CURLversion stamp) if(version_info.libidn) version_info.features |= CURL_VERSION_IDN; #endif + +#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) +#ifdef _LIBICONV_VERSION + version_info.iconv_ver_num = _LIBICONV_VERSION; +#else + /* version unknown */ + version_info.iconv_ver_num = -1; +#endif /* _LIBICONV_VERSION */ +#endif + +#ifdef USE_LIBSSH2 + snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION); + version_info.libssh_version = ssh_buffer; +#endif + (void)stamp; /* avoid compiler warnings, we don't use this */ return &version_info; |