📄 sslsocket.cpp
字号:
/*
Copyright 2005 Matthew J. Battey
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
This software implements a platform independent C++ interface to TCP/IP socket
communications.
*/
#include "sslsocket.h"
#ifdef _WIN32
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define ENOTSOCK WSAENOTSOCK
#define EDESTADDRREQ WSAEDESTADDRREQ
#define EMSGSIZE WSAEMSGSIZE
#define EPROTOTYPE WSAEPROTOTYPE
#define ENOPROTOOPT WSAENOPROTOOPT
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define EOPNOTSUPP WSAEOPNOTSUPP
#define EPFNOSUPPORT WSAEPFNOSUPPORT
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#define EADDRINUSE WSAEADDRINUSE
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define ENETDOWN WSAENETDOWN
#define ENETUNREACH WSAENETUNREACH
#define ENETRESET WSAENETRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ENOBUFS WSAENOBUFS
#define EISCONN WSAEISCONN
#define ENOTCONN WSAENOTCONN
#define ESHUTDOWN WSAESHUTDOWN
#define ETOOMANYREFS WSAETOOMANYREFS
#define ETIMEDOUT WSAETIMEDOUT
#define ECONNREFUSED WSAECONNREFUSED
#define ELOOP WSAELOOP
#define EHOSTDOWN WSAEHOSTDOWN
#define EHOSTUNREACH WSAEHOSTUNREACH
#define EPROCLIM WSAEPROCLIM
#define EUSERS WSAEUSERS
#define EDQUOT WSAEDQUOT
#define ESTALE WSAESTALE
#define EREMOTE WSAEREMOTE
typedef int socklen_t;
#define SOCERRNO h_errno
#ifdef min
#undef min
#define min _cpp_min
#endif
#ifdef max
#undef max
#define max _cpp_max
#endif
#else
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#define SOCERRNO errno
#endif
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
namespace tcpsocket {
struct _SSLInitializer {
_SSLInitializer() {
SSL_library_init();
SSL_load_error_strings();
}
};
static _SSLInitializer _initializer;
struct X509Data {
X509Data() {
certificate = NULL;
}
X509Data(const X509Data& cert) {
certificate = cert.certificate;
}
X509Data(X509* cert) {
certificate = cert;
}
X509* certificate;
};
struct SSLSocketData {
SSLSocketData() {
ctx = NULL;
ssl = NULL;
}
SSL_CTX* ctx;
SSL* ssl;
int mrc;
};
/**
Constructs an X509Certificate object from the
masked X509Data structrure.
@param certificate the source data
*/
X509Certificate::X509Certificate(const X509Data* certificate)
{
this->certificate = new X509Data(*certificate);
}
/**
Destroys the certificate object
*/
X509Certificate::~X509Certificate()
{
X509_free(certificate->certificate);
delete certificate;
}
/**
Providse the X509 certificate serial number
@return the X509 certificate serial number
*/
std::string X509Certificate::getSerialNumber()
{
BIO* bp = BIO_new(BIO_s_mem());
char buffer[1024];
i2a_ASN1_INTEGER(bp,X509_get_serialNumber(certificate->certificate));
memset(buffer,0,sizeof(buffer));
BIO_read(bp,buffer,sizeof(buffer));
buffer[sizeof(buffer)-1]=0;
BIO_free(bp);
return buffer;
}
/**
Provides the Issuer Name
@return the Issuer Name
*/
std::string X509Certificate::getIssuerName()
{
char buffer[1024];
return X509_NAME_oneline(X509_get_issuer_name(certificate->certificate),buffer,sizeof(buffer));
}
/**
Provides the subject name
@return The subject name
*/
std::string X509Certificate::getSubjectName()
{
char buffer[1024];
return X509_NAME_oneline(X509_get_subject_name(certificate->certificate),buffer,sizeof(buffer));
}
/**
Provides the certificate not before date
@return The certificate not before date
*/
std::string X509Certificate::getNotBefore()
{
BIO* bp = BIO_new(BIO_s_mem());
char buffer[1024];
ASN1_TIME_print(bp,X509_get_notBefore(certificate->certificate));
memset(buffer,0,sizeof(buffer));
BIO_read(bp,buffer,sizeof(buffer));
buffer[sizeof(buffer)-1]=0;
BIO_free(bp);
return buffer;
}
/**
Provides the certificate not after date
@return The certificate not after date
*/
std::string X509Certificate::getNotAfter()
{
BIO* bp = BIO_new(BIO_s_mem());
char buffer[1024];
ASN1_TIME_print(bp,X509_get_notAfter(certificate->certificate));
memset(buffer,0,sizeof(buffer));
BIO_read(bp,buffer,sizeof(buffer));
buffer[sizeof(buffer)-1]=0;
BIO_free(bp);
return buffer;
}
/**
Converts the certificate to a string
@return The PEM string representation of the certificate
*/
std::string X509Certificate::toString()
{
BIO* bp = BIO_new(BIO_s_mem());
char buffer[512];
std::string ret;
int read;
PEM_write_bio_X509(bp, certificate->certificate);
while ((read = BIO_read(bp,buffer,sizeof(buffer))) == sizeof(buffer)) {
ret.append(buffer,read);
}
if (read > 0)
ret.append(buffer,read);
BIO_free(bp);
return ret;
}
/**
Provides a pointer to the X509 data structure
@return the X509 pointer cast to void*
*/
void* X509Certificate::getX509()
{
return certificate->certificate;
}
// /////////////////////////////////////////////////////////////////////////////
// SSLSocket
// /////////////////////////////////////////////////////////////////////////////
/**
Constructs the SSLSocket object
*/
SSLSocket::SSLSocket()
{
init();
}
/**
Constructs the SSLSocket and uses the provided socket handle for communications
@param socket_handle [in] A previously created socket handle
@param type [in] Flags the object as an SSL client
@param version [in] The SSL Protocol version to use
*/
SSLSocket::SSLSocket(int socket_handle, SSLSocket::con_type type, SSLSocket::ssl_ver version)
{
init();
init_ssl(version);
m_socket = socket_handle;
if (!pdata->ctx || !pdata->ssl) {
throw SocketException(-1);
}
pdata->mrc = SSL_set_fd(pdata->ssl,m_socket);
if (pdata->mrc < 1)
throw SSLSocketException(getErrorMessage().c_str());
if (type == con_client)
SSL_set_connect_state(pdata->ssl);
else
SSL_set_accept_state(pdata->ssl);
}
/**
Constructs the socket from a socket handle.
@param socket_handle [in] A previously created socket handle
@param pData [in] SSLSocketData to use, contains the CTX and/or the SSL object
@param type [in] Flags the object as an SSL client
*/
SSLSocket::SSLSocket(int socket_handle, SSLSocketData* pData, SSLSocket::con_type type) throw (SSLSocketException)
{
init();
m_socket = socket_handle;
this->pdata->ctx = pData->ctx;
this->pdata->ssl = pData->ssl;
if (this->pdata->ssl == NULL && this->pdata->ctx != NULL)
this->pdata->ssl = SSL_new(this->pdata->ctx);
if (!this->pdata->ssl) {
close();
throw SSLSocketException("Unable to retreive SSL object");
}
this->pdata->mrc = SSL_set_fd(this->pdata->ssl,m_socket);
if (pdata->mrc < 1) {
close();
throw SSLSocketException(getErrorMessage().c_str());
}
if (type == con_client)
SSL_set_connect_state(pdata->ssl);
else if (type == con_server)
SSL_set_accept_state(pdata->ssl);
}
/**
Copy constructor, copies the socket handle
@param s [in] The source socket
*/
SSLSocket::SSLSocket(const SSLSocket& s)
{
init();
*this = s;
}
/**
Constructs the socket and opens a connection to a server.
@param host [in] The address of the server or DNS name
@param port [in] The TCP port to connect to
@param version [in] The SSL Protocol version to use
*/
SSLSocket::SSLSocket(const char* host, short port, SSLSocket::ssl_ver version) throw (SSLSocketException, SocketException)
: Socket(host,port)
{
init();
init_ssl(version);
if (!pdata->ctx || !pdata->ssl) {
throw SSLSocketException(getErrorMessage().c_str());
}
pdata->mrc = SSL_set_fd(pdata->ssl,m_socket);
if (pdata->mrc < 0) {
close();
throw SSLSocketException(getErrorMessage().c_str());
}
SSL_set_connect_state(pdata->ssl);
}
/**
Destroys the SSL socket
*/
SSLSocket::~SSLSocket()
{
delete pdata;
}
/**
Copy oeprator, copies the socket handle and SSL information
@param s [in] The source socket
@return A reference tot his object.
*/
SSLSocket& SSLSocket::operator=(const SSLSocket& s)
{
pdata->ctx = s.pdata->ctx;
pdata->ssl = s.pdata->ssl;
m_socket = s.m_socket;
m_throwOnClose = s.m_throwOnClose;
return *this;
}
/**
Sends data over the socket. Sends exactly <code>length</code> bytes.
@param sendBuffer [in] A buffer containing the data to be sent
@param length [in] The number of bytes in the buffer
@return The number of bytes sent, -1 on error, and 0 if the connection was closed
@exception SocketException if the connection was closed and the throw on close flag was set.
*/
size_t SSLSocket::send(const char* sendBuffer, size_t length) throw (SSLSocketException)
{
int sent = 0;
int isent;
while ( sent < length ) {
pdata->mrc = isent = SSL_write(pdata->ssl,sendBuffer+sent,length-sent);
if (isent == 0) {
if (m_throwOnClose)
throw SocketException(ECONNRESET);
break;
} else if (isent < 0) {
int err = SSL_get_error(pdata->ssl,isent);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
sleep(200);
} else {
throw SSLSocketException(getErrorMessage().c_str());
}
} else {
sent += isent;
}
}
return sent;
}
/**
Receives data from the socket. Receives exactly <code>length</code> bytes or
less if the socket is closed early.
@param sendBuffer [out] A buffer receiving the data
@param length [in] The maximum number of bytes in the buffer
@return The number of bytes received, -1 on error, and 0 if the connection was closed
@exception SocketException if the connection was closed and the throw on close flag was set.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -