You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by pr...@apache.org on 2005/01/07 16:23:19 UTC
cvs commit: ws-axis/c/src/transport/axis3/HTTPSSLChannel HTTPSSLChannel.cpp HTTPSSLChannel.hpp
prestonf 2005/01/07 07:23:19
Modified: c/src/transport/axis3/HTTPSSLChannel HTTPSSLChannel.cpp
HTTPSSLChannel.hpp
Log:
Hi All,
These are modifications needed for the draft of the new http transport implementation AXIS3 (see AXISCPP-361).
Regards,
Fred Preston.
Revision Changes Path
1.2 +503 -3 ws-axis/c/src/transport/axis3/HTTPSSLChannel/HTTPSSLChannel.cpp
Index: HTTPSSLChannel.cpp
===================================================================
RCS file: /home/cvs/ws-axis/c/src/transport/axis3/HTTPSSLChannel/HTTPSSLChannel.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- HTTPSSLChannel.cpp 6 Jan 2005 17:33:15 -0000 1.1
+++ HTTPSSLChannel.cpp 7 Jan 2005 15:23:19 -0000 1.2
@@ -1,15 +1,57 @@
#include "HTTPSSLChannel.hpp"
+static int cert_verify_callback( int ok, X509_STORE_CTX * ctx)
+{
+ X509 * err_cert;
+ char buf[256];
+
+ err_cert = X509_STORE_CTX_get_current_cert( ctx);
+ X509_NAME_oneline( X509_get_subject_name( err_cert), buf, 256);
+
+ return ok;
+}
HTTPSSLChannel::HTTPSSLChannel()
{
memset( &m_URL, 0, sizeof( URL));
m_LastError = "No Errors";
+
+ m_Sock = INVALID_SOCKET;
+
+ m_bUseProxy = false;
+ m_strProxyHost = "";
+ m_uiProxyPort = 0;
+
+#ifdef WIN32
+ m_lTimeoutSeconds = 10;
+#else
+ m_lTimeoutSeconds = 0;
+#endif
+
+ if( !StartSockets())
+ {
+ throw HTTPTransportException( SERVER_TRANSPORT_CHANNEL_INIT_ERROR);
+ }
+
+ OpenSSL_Initialise();
+
+ m_sslContext = NULL;
+ m_sslHandle = NULL;
}
HTTPSSLChannel::~HTTPSSLChannel()
{
+ OpenSSL_Close();
+
+// If the socket value is not invalid, then close the socket before
+// deleting the Channel object.
+ if( m_Sock != INVALID_SOCKET)
+ {
+ CloseChannel();
+ }
+
+ StopSockets();
}
const char * HTTPSSLChannel::getURL()
@@ -31,7 +73,20 @@
bool HTTPSSLChannel::open() throw (HTTPTransportException&)
{
- bool bSuccess = false;
+ bool bSuccess = (bool) AXIS_FAIL;
+
+ if( m_Sock != INVALID_SOCKET)
+ {
+ CloseChannel();
+ }
+
+ if( (bSuccess = OpenChannel()) != AXIS_SUCCESS)
+ {
+ throw HTTPTransportException( SERVER_TRANSPORT_SOCKET_CONNECT_ERROR,
+ (char *) m_LastError.c_str());
+ }
+
+ bSuccess = OpenSSL_Open();
return bSuccess;
}
@@ -41,22 +96,53 @@
return m_LastError;
}
-const IChannel & HTTPSSLChannel::operator >> (std::string& msg)
+const IChannel & HTTPSSLChannel::operator >> (const char * msg)
{
+ if (INVALID_SOCKET == m_Sock)
+ {
+// Error - Reading cannot be done without having a open socket Input
+// streaming error on undefined channel; please open the
+// channel first
+
+ m_LastError = "No open socket to read from.";
+
+ throw HTTPTransportException( SERVER_TRANSPORT_INVALID_SOCKET,
+ (char *) m_LastError.c_str());
+ }
+
+ int nRxBytes = ReadFromSocket( msg);
+
return *this;
}
-const IChannel & HTTPSSLChannel::operator << (const char* msg)
+const IChannel & HTTPSSLChannel::operator << (const char * msg)
{
+// Check that the Tx/Rx sockets are valid (this will have been done if the
+// application has called the open method first.
+ if( INVALID_SOCKET == m_Sock)
+ {
+// Error - Writing cannot be done without having a open socket to
+// remote end. Throw an exception.
+
+ m_LastError = "No valid socket open";
+
+ throw HTTPTransportException( SERVER_TRANSPORT_INVALID_SOCKET,
+ (char *) m_LastError.c_str());
+ }
+
+ int nByteSent = WriteToSocket( msg, strlen( msg));
+
return *this;
}
void HTTPSSLChannel::setTimeout( const long lSeconds)
{
+ m_lTimeoutSeconds = lSeconds;
}
void HTTPSSLChannel::setSocket( unsigned int uiNewSocket)
{
+ m_Sock = uiNewSocket;
}
bool HTTPSSLChannel::setTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type, const char* value)
@@ -71,3 +157,417 @@
return NULL;
}
+void HTTPSSLChannel::setProxy (const char *pcProxyHost, unsigned int uiProxyPort)
+{
+ m_strProxyHost = pcProxyHost;
+ m_uiProxyPort = uiProxyPort;
+ m_bUseProxy = true;
+}
+
+// Protected methods
+// -----------------
+bool HTTPSSLChannel::OpenChannel()
+{
+ bool bSuccess = (bool) AXIS_FAIL;
+
+// Create the Client (Rx) side first.
+ if( (m_Sock = socket( PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ {
+ m_LastError = "Could not Create a socket.";
+
+ return bSuccess;
+ }
+
+// If the transport was initilised, then create client and server sockets.
+ sockaddr_in clAddr;
+
+ clAddr.sin_family = AF_INET; // AF_INET (address family Internet).
+ clAddr.sin_port = 0; // No Specify Port required.
+ clAddr.sin_addr.s_addr = INADDR_ANY;
+
+// Attempt to bind the client to the client socket.
+ if( bind( m_Sock, (struct sockaddr *) &clAddr, sizeof( clAddr)) == SOCKET_ERROR)
+ {
+// Error whilst binding. Cannot open a channel to the remote end,
+// shutting down the channel and then throw an exception.
+ CloseChannel();
+
+ m_LastError = "Error whilst binding. Cannot open a channel to the remote end,";
+
+ return bSuccess;
+ }
+
+// Although the above fragment makes use of the bind() API, it would be
+// just as effective to skip over this call as there are no specific
+// local port ID requirements for this client. The only advantage that
+// bind() offers is the accessibility of the port which the system
+// chose via the .sin_port member of the cli_addr structure which will
+// be set upon success of the bind() call.
+
+// Create the Server (Tx) side.
+
+ sockaddr_in svAddr;
+ struct hostent * pHostEntry = NULL;
+ const char * host = m_URL.getHostName();
+ unsigned int port = m_URL.getPort();
+
+ if( m_bUseProxy)
+ {
+ port = m_uiProxyPort;
+ host = m_strProxyHost.c_str();
+ }
+
+ svAddr.sin_family = AF_INET;
+ svAddr.sin_port = htons( port);
+
+// Probably this is the host-name of the server we are connecting to...
+ if( (pHostEntry = gethostbyname( host)))
+ {
+ svAddr.sin_addr.s_addr = ((struct in_addr *) pHostEntry->h_addr)->s_addr;
+ }
+ else
+ {
+// No this is the IP address
+ svAddr.sin_addr.s_addr = inet_addr( host);
+ }
+
+// Attempt to connect to the remote server.
+ if( connect( m_Sock, (struct sockaddr *) &svAddr, sizeof (svAddr)) == SOCKET_ERROR)
+ {
+// Cannot open a channel to the remote end, shutting down the
+// channel and then throw an exception.
+
+#ifdef WIN32
+// Before we do anything else get the last error message;
+// I'd like to put the getting of the error message into platform specifics
+// but not sure how! I think it would be nicer to make the platform
+// specifics a class and not just macros. That way we could have e.g.
+// char * Windows#getLastErrorMessage().
+ long lLastError = GetLastError();
+#endif
+ CloseChannel();
+
+#ifdef WIN32
+ char szErrorBuffer[200];
+ LPVOID lpErrorBuffer;
+
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ lLastError,
+ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpErrorBuffer,
+ 0,
+ NULL);
+
+ sprintf( szErrorBuffer,
+ "Failed to open connection to server:\n\
+ hostname='%s'\n\
+ port='%d'\n\
+ Error Message='%s'\
+ Error Code='%d'\n", \
+ m_URL.getHostName(), m_URL.getPort(), lpErrorBuffer, lLastError);
+
+ LocalFree( lpErrorBuffer);
+
+ m_LastError = szErrorBuffer;
+#else
+ m_LastError = "Cannot open a channel to the remote end.";
+
+#endif
+ return bSuccess;
+ }
+ else
+ {
+ bSuccess = AXIS_SUCCESS;
+ }
+
+ /* Turn off the Nagle algorithm - Patch by Steve Hardy */
+
+ /* This is needed, because our TCP stack would otherwise wait at most
+ * 200 ms before actually sending data to the server (while waiting for
+ * a full packet). This limits performance to around 5 requests per
+ * second, which is not acceptable. Turning off the Nagle algorithm
+ * allows for much faster transmission of small packets, but may
+ * degrade high-bandwidth transmissions.
+ */
+
+ int one = 1;
+
+ setsockopt( m_Sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(int));
+
+ return bSuccess;
+}
+
+void HTTPSSLChannel::CloseChannel()
+{
+ if( INVALID_SOCKET != m_Sock) // Check if socket already closed : AXISCPP-185
+ {
+#ifdef WIN32
+ closesocket( m_Sock);
+#else
+ ::close( m_Sock);
+#endif
+ m_Sock = INVALID_SOCKET; // fix for AXISCPP-185
+ }
+}
+
+bool HTTPSSLChannel::StartSockets()
+{
+ bool bSuccess = false;
+#ifdef WIN32
+ WSADATA wsaData; // Contains vendor-specific information, such as the
+ // maximum number of sockets available and the maximum
+ // datagram size.
+
+// wsaData filled by Windows Sockets DLLs.
+ if( WSAStartup( WS_VERSION_REQD, &wsaData))
+ {
+// Error - Could not setup underlying Windows socket transport mechanism.
+ m_LastError = "WinSock DLL not responding.";
+ }
+ else
+ {
+// Query to see whether the available version matches what is required
+ if ((LOBYTE( wsaData.wVersion) < WS_VERSION_MAJOR()) ||
+ (LOBYTE( wsaData.wVersion) == WS_VERSION_MAJOR() &&
+ HIBYTE( wsaData.wVersion) < WS_VERSION_MINOR()))
+ {
+// Error - Underlying Windows socket transport version is not compatible with what is required.
+ char szErrorBuffer[100];
+
+ sprintf( szErrorBuffer, "Windows Sockets version %d.%d is not supported by winsock2.dll", LOBYTE( wsaData.wVersion), HIBYTE( wsaData.wVersion));
+
+ m_LastError = szErrorBuffer;
+
+ StopSockets();
+ }
+ else
+ {
+ bSuccess = true;
+ }
+ }
+#else
+ /* cout << "no need for linux" << endl; */
+ /* other OS specific Intitialization goes here */
+ bSuccess = true;
+#endif
+
+ return bSuccess;
+}
+
+void HTTPSSLChannel::StopSockets()
+{
+ WSACleanup();
+}
+
+/**
+ * Channel::applyTimeout()
+ *
+ * @return int
+ */
+int HTTPSSLChannel::applyTimeout()
+{
+ fd_set set;
+ struct timeval timeout;
+
+ // Initialize the file descriptor set.
+ FD_ZERO( &set);
+ FD_SET( m_Sock, &set);
+
+ /* Initialize the timeout data structure. */
+ timeout.tv_sec = m_lTimeoutSeconds;
+ timeout.tv_usec = 0;
+
+ /* select returns 0 if timeout, 1 if input available, -1 if error. */
+ return select( FD_SETSIZE, &set, NULL, NULL, &timeout);
+}
+
+int HTTPSSLChannel::ReadFromSocket( const char * pszRxBuffer)
+{
+ int nByteRecv = 0;
+
+ nByteRecv = SSL_read( m_sslHandle, (void *) pszRxBuffer, BUF_SIZE - 1);
+
+ if(nByteRecv < 0)
+ {
+// failed SSL_read
+ OpenSSL_SetSecureError( SSL_get_error( m_sslHandle, nByteRecv));
+
+ OpenSSL_Close();
+ }
+ else
+ {
+ *(((char *)pszRxBuffer) + nByteRecv) = '\0';
+ }
+
+ return nByteRecv;
+}
+
+int HTTPSSLChannel::WriteToSocket( const char * psTxBuffer, int iSize)
+{
+ int nByteSent;
+
+ nByteSent = SSL_write( m_sslHandle, (char *) psTxBuffer, iSize);
+
+ if(nByteSent < 0)
+ {
+// failed SSL write
+ OpenSSL_SetSecureError( SSL_get_error( m_sslHandle, nByteSent));
+
+ OpenSSL_Close();
+ }
+
+ return nByteSent;
+}
+
+void HTTPSSLChannel::OpenSSL_Initialise()
+{
+// Lets get nice error messages
+ SSL_load_error_strings();
+
+// Setup all the global SSL stuff
+ SSLeay_add_ssl_algorithms();
+}
+
+bool HTTPSSLChannel::OpenSSL_Open()
+{
+ SSL_METHOD * req_method = SSLv23_client_method();
+ SSL_SESSION * ssl_sessionid = NULL;
+ bool bSuccess = (bool) AXIS_FAIL;
+ int iSSLErrorIndex = 0;
+
+ m_sslContext = SSL_CTX_new( req_method);
+
+ if(!m_sslContext)
+ {
+ iSSLErrorIndex = ERR_get_error();
+
+// OpenSSL documents that this must be at least 120 bytes long.
+ char szSSLErrorBuffer[120];
+
+ ERR_error_string( iSSLErrorIndex, szSSLErrorBuffer);
+
+ m_LastError = szSSLErrorBuffer;
+
+ OpenSSL_Close();
+
+ throw HTTPTransportException( CLIENT_SSLCHANNEL_CONTEXT_CREATE_ERROR,
+ szSSLErrorBuffer);
+ }
+
+ SSL_CTX_set_verify( m_sslContext,
+ SSL_VERIFY_NONE, // SSL_VERIFY_PEER
+ cert_verify_callback);
+
+// Lets make an SSL structure
+ m_sslHandle = SSL_new( m_sslContext);
+ SSL_set_connect_state( m_sslHandle);
+
+// pass the raw socket into the SSL layers
+ SSL_set_fd( m_sslHandle, m_Sock);
+
+ iSSLErrorIndex = SSL_connect( m_sslHandle);
+
+// 1 is fine
+// 0 is "not successful but was shut down controlled"
+// <0 is "handshake was not successful, because a fatal error occurred"
+ if( iSSLErrorIndex <= 0)
+ {
+ OpenSSL_SetSecureError( iSSLErrorIndex);
+ }
+ else
+ {
+ bSuccess = (bool) AXIS_SUCCESS;
+ }
+
+ return bSuccess;
+}
+
+int HTTPSSLChannel::OpenSSL_Close()
+{
+ if( m_sslHandle)
+ {
+ SSL_shutdown( m_sslHandle);
+ SSL_set_connect_state( m_sslHandle);
+
+ SSL_free( m_sslHandle);
+ m_sslHandle = NULL;
+ }
+
+ if( m_sslContext)
+ {
+ SSL_CTX_free( m_sslContext);
+ m_sslContext = NULL;
+ }
+
+ return 0;
+}
+
+void HTTPSSLChannel::OpenSSL_SetSecureError( int iError)
+{
+ switch( iError)
+ {
+ 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:
+ case SSL_ERROR_SYSCALL:
+ {
+ #ifdef WIN32
+ iError = ::GetLastError();
+ #else
+ iError = errno;
+ #endif
+
+ OpenSSL_Close();
+
+ m_LastError = "SSL_ERROR_SYSCALL";
+
+ throw HTTPTransportException( CLIENT_SSLCHANNEL_ERROR,
+ (char *) m_LastError.c_str());
+
+ break;
+ }
+
+ 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.
+ int sslerror = ERR_get_error();
+
+// OpenSSL documents that this must be at least 120 bytes long.
+ char error_buffer[120];
+
+ ERR_error_string( sslerror, error_buffer);
+
+ OpenSSL_Close();
+
+ m_LastError = error_buffer;
+
+ throw HTTPTransportException( CLIENT_SSLCHANNEL_ERROR, error_buffer);
+
+ break;
+ }
+
+ default:
+// openssl/ssl.h says "look at error stack/return value/errno"
+ {
+// A failure in the SSL library occurred, usually a protocol error. The
+// OpenSSL error queue contains more information on the error.
+ int sslerror = ERR_get_error();
+
+// OpenSSL documents that this must be at least 120 bytes long.
+ char error_buffer[120];
+
+ ERR_error_string( sslerror, error_buffer);
+
+ OpenSSL_Close();
+
+ m_LastError = error_buffer;
+
+ throw HTTPTransportException( CLIENT_SSLCHANNEL_ERROR, error_buffer);
+ }
+ }
+}
1.2 +62 -5 ws-axis/c/src/transport/axis3/HTTPSSLChannel/HTTPSSLChannel.hpp
Index: HTTPSSLChannel.hpp
===================================================================
RCS file: /home/cvs/ws-axis/c/src/transport/axis3/HTTPSSLChannel/HTTPSSLChannel.hpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- HTTPSSLChannel.hpp 6 Jan 2005 17:33:15 -0000 1.1
+++ HTTPSSLChannel.hpp 7 Jan 2005 15:23:19 -0000 1.2
@@ -1,8 +1,45 @@
#if !defined(_AXIS_AXIS_SSL_CHANNEL_HPP)
#define _AXIS_AXIS_SSL_CHANNEL_HPP
+#include <stdio.h>
+#include <string>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+// Platform specific stuff
+#ifdef WIN32
#include "..\IChannel.hpp"
#include "..\URL.hpp"
+#include "..\HTTPTransportException.hpp"
+#include <winsock2.h>
+
+// What version of WinSock is required
+const int WS_VERSION_REQD = 0x0101;
+
+// Macros to get version major & minor
+inline WS_VERSION_MAJOR() {return HIBYTE(WS_VERSION_REQD);}
+inline WS_VERSION_MINOR() {return LOBYTE(WS_VERSION_REQD);}
+
+#else
+#include "../IChannel.hpp"
+#include "../URL.hpp"
+#include "../HTTPTransportException.hpp"
+
+#include <unistd.h>
+#include <sys/types.h> // basic system data types
+#include <sys/socket.h> // basic socket definitions
+#include <netinet/tcp.h>
+#include <fcntl.h> // for nonblocking if need
+#include <sys/time.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> // inet(3) functions
+
+const unsigned int INVALID_SOCKET = 0;
+const int SOCKET_ERROR = -1;
+
+// Other OS specific stuff goes here
+#endif
AXIS_CPP_NAMESPACE_USE using namespace std;
@@ -17,17 +54,37 @@
virtual URL & getURLObject();
bool open() throw (HTTPTransportException&);
const std::string & GetLastErrorMsg();
- const IChannel & operator >> (std::string& msg);
- const IChannel & operator << (const char* msg);
+ const IChannel & operator >> (const char * msg);
+ const IChannel & operator << (const char * msg);
void setTimeout( const long lSeconds);
void setSocket( unsigned int uiNewSocket);
bool setTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type, const char* value);
const char * getTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type);
+ void setProxy( const char * pcProxyHost, unsigned int uiProxyPort);
-private:
- URL m_URL;
- string m_LastError;
+protected:
+ bool OpenChannel();
+ void CloseChannel();
+ bool StartSockets();
+ void StopSockets();
+ int applyTimeout();
+ int ReadFromSocket( const char * pszRxBuffer);
+ int WriteToSocket( const char * psTxBuffer, int iSize);
+ void OpenSSL_Initialise();
+ bool OpenSSL_Open();
+ int OpenSSL_Close();
+ void OpenSSL_SetSecureError( int iError);
+private:
+ URL m_URL; // URL
+ string m_LastError; // Last error
+ unsigned int m_Sock; // Socket descriptor
+ bool m_bUseProxy; // Use a Proxy?
+ string m_strProxyHost; // Proxy server name.
+ unsigned int m_uiProxyPort; // Proxy server port.
+ long m_lTimeoutSeconds; // Timeout in seconds
+ SSL_CTX * m_sslContext;
+ SSL * m_sslHandle;
};
#endif