1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Add IoClient and IoSession interfaces.

These interfaces allow the HttpClient and HttpSession objects to work with protocols other than TLS, .e.g. plain sockets. This is necessary to allow standard HTTP -- right now only HTTPS is allowed, i.e. HTTP over TLS.

For now only TlsClient and TlsSession have been converted to the new interfaces. SocketClient and SocketSession will also need to be converted but first sckSessionReadyRead() and sckSessionReadyWrite() need to be moved into the IoRead and IoWrite interfaces, since they are not a good fit for IoSession.
This commit is contained in:
David Steele 2020-08-08 10:39:39 -04:00 committed by GitHub
parent 9b7fd1a894
commit 111d33c123
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 678 additions and 272 deletions

View File

@ -74,6 +74,19 @@
<p>Improve TLS error reporting.</p>
</release-item>
</release-improvement-list>
<release-development-list>
<release-item>
<commit subject="Add IoClient and IoSession interfaces."/>
<release-item-contributor-list>
<release-item-reviewer id="stephen.frost"/>
<release-item-reviewer id="cynthia.shang"/>
</release-item-contributor-list>
<p>Allow <code>HttpClient</code>/<code>HttpSession</code> to work on plain sockets.</p>
</release-item>
</release-development-list>
</release-core-list>
<release-doc-list>

View File

@ -66,6 +66,7 @@ SRCS = \
common/fork.c \
common/io/bufferRead.c \
common/io/bufferWrite.c \
common/io/client.c \
common/io/fdRead.c \
common/io/fdWrite.c \
common/io/filter/buffer.c \
@ -82,6 +83,7 @@ SRCS = \
common/io/http/session.c \
common/io/io.c \
common/io/read.c \
common/io/session.c \
common/io/socket/client.c \
common/io/socket/common.c \
common/io/socket/session.c \

69
src/common/io/client.c Normal file
View File

@ -0,0 +1,69 @@
/***********************************************************************************************************************************
Io Client Interface
***********************************************************************************************************************************/
#include "build.auto.h"
#include "common/debug.h"
#include "common/io/client.intern.h"
#include "common/log.h"
#include "common/memContext.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct IoClient
{
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoClientInterface *interface; // Driver interface
};
OBJECT_DEFINE_FREE(IO_CLIENT);
/**********************************************************************************************************************************/
IoClient *
ioClientNew(void *driver, const IoClientInterface *interface)
{
FUNCTION_LOG_BEGIN(logLevelTrace)
FUNCTION_LOG_PARAM_P(VOID, driver);
FUNCTION_LOG_PARAM(IO_CLIENT_INTERFACE, interface);
FUNCTION_LOG_END();
ASSERT(driver != NULL);
ASSERT(interface != NULL);
ASSERT(interface->type != NULL);
ASSERT(interface->open != NULL);
ASSERT(interface->toLog != NULL);
IoClient *this = memNew(sizeof(IoClient));
*this = (IoClient)
{
.memContext = memContextCurrent(),
.driver = driver,
.interface = interface,
};
FUNCTION_LOG_RETURN(IO_CLIENT, this);
}
/**********************************************************************************************************************************/
IoSession *
ioClientOpen(IoClient *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
FUNCTION_LOG_RETURN(IO_SESSION, this->interface->open(this->driver));
}
/**********************************************************************************************************************************/
String *
ioClientToLog(const IoClient *this)
{
return strNewFmt("{type: %s, driver: %s}", strZ(*this->interface->type), strZ(this->interface->toLog(this->driver)));
}

41
src/common/io/client.h Normal file
View File

@ -0,0 +1,41 @@
/***********************************************************************************************************************************
Io Client Interface
Create sessions for protocol clients. For example, a TLS client can be created with tlsClientNew() and then new TLS sessions can be
opened with ioClientOpen().
***********************************************************************************************************************************/
#ifndef COMMON_IO_CLIENT_H
#define COMMON_IO_CLIENT_H
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define IO_CLIENT_TYPE IoClient
#define IO_CLIENT_PREFIX ioClient
typedef struct IoClient IoClient;
#include "common/io/session.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Open session
IoSession *ioClientOpen(IoClient *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void ioClientFree(IoClient *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
String *ioClientToLog(const IoClient *this);
#define FUNCTION_LOG_IO_CLIENT_TYPE \
IoClient *
#define FUNCTION_LOG_IO_CLIENT_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, ioClientToLog, buffer, bufferSize)
#endif

View File

@ -0,0 +1,38 @@
/***********************************************************************************************************************************
Io Client Interface Internal
***********************************************************************************************************************************/
#ifndef COMMON_IO_CLIENT_INTERN_H
#define COMMON_IO_CLIENT_INTERN_H
#include "common/io/client.h"
/***********************************************************************************************************************************
Interface
***********************************************************************************************************************************/
typedef struct IoClientInterface
{
// Type used to identify the client. This is stored as a pointer to a String pointer so it can be used with an existing String
// constant (e.g. created with STRING_EXTERN()) without needing to be copied.
const String *const *type;
// Open a session
IoSession *(*open)(void *driver);
// Driver log function
String *(*toLog)(const void *driver);
} IoClientInterface;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoClient *ioClientNew(void *driver, const IoClientInterface *interface);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_IO_CLIENT_INTERFACE_TYPE \
IoClientInterface *
#define FUNCTION_LOG_IO_CLIENT_INTERFACE_FORMAT(value, buffer, bufferSize) \
objToLog(&value, "IoClientInterface", buffer, bufferSize)
#endif

View File

@ -4,8 +4,8 @@ HTTP Client
#include "build.auto.h"
#include "common/debug.h"
#include "common/io/client.h"
#include "common/io/http/client.h"
#include "common/io/tls/client.h"
#include "common/log.h"
#include "common/type/object.h"
@ -21,7 +21,7 @@ struct HttpClient
{
MemContext *memContext; // Mem context
TimeMSec timeout; // Request timeout
TlsClient *tlsClient; // TLS client
IoClient *ioClient; // Io client (e.g. TLS or socket client)
List *sessionReuseList; // List of HTTP sessions that can be reused
};
@ -30,19 +30,14 @@ OBJECT_DEFINE_GET(Timeout, const, HTTP_CLIENT, TimeMSec, timeout);
/**********************************************************************************************************************************/
HttpClient *
httpClientNew(
const String *host, unsigned int port, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
httpClientNew(IoClient *ioClient, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(IO_CLIENT, ioClient);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(BOOL, verifyPeer);
FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, caPath);
FUNCTION_LOG_END();
ASSERT(host != NULL);
ASSERT(ioClient != NULL);
HttpClient *this = NULL;
@ -54,7 +49,7 @@ httpClientNew(
{
.memContext = MEM_CONTEXT_NEW(),
.timeout = timeout,
.tlsClient = tlsClientNew(sckClientNew(host, port, timeout), timeout, verifyPeer, caFile, caPath),
.ioClient = ioClient,
.sessionReuseList = lstNewP(sizeof(HttpSession *)),
};
@ -90,7 +85,7 @@ httpClientOpen(HttpClient *this)
// Else create a new session
else
{
result = httpSessionNew(this, tlsClientOpen(this->tlsClient));
result = httpSessionNew(this, ioClientOpen(this->ioClient));
httpClientStat.session++;
}

View File

@ -24,6 +24,7 @@ Object type
typedef struct HttpClient HttpClient;
#include "common/io/client.h"
#include "common/io/http/session.h"
#include "common/time.h"
@ -33,7 +34,7 @@ Statistics
typedef struct HttpClientStat
{
uint64_t object; // Objects created
uint64_t session; // TLS sessions created
uint64_t session; // Sessions created
uint64_t request; // Requests (i.e. calls to httpRequestNew())
uint64_t retry; // Request retries
uint64_t close; // Closes forced by server
@ -44,8 +45,7 @@ extern HttpClientStat httpClientStat;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
HttpClient *httpClientNew(
const String *host, unsigned int port, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath);
HttpClient *httpClientNew(IoClient *ioClient, TimeMSec timeout);
/***********************************************************************************************************************************
Functions

View File

@ -10,7 +10,6 @@ HTTP Response
#include "common/io/http/response.h"
#include "common/io/io.h"
#include "common/io/read.intern.h"
#include "common/io/tls/client.h"
#include "common/log.h"
#include "common/type/object.h"
#include "common/wait.h"

View File

@ -17,7 +17,7 @@ struct HttpSession
{
MemContext *memContext; // Mem context
HttpClient *httpClient; // HTTP client
TlsSession *tlsSession; // TLS session
IoSession *ioSession; // IO session
};
OBJECT_DEFINE_MOVE(HTTP_SESSION);
@ -25,15 +25,15 @@ OBJECT_DEFINE_FREE(HTTP_SESSION);
/**********************************************************************************************************************************/
HttpSession *
httpSessionNew(HttpClient *httpClient, TlsSession *tlsSession)
httpSessionNew(HttpClient *httpClient, IoSession *ioSession)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(HTTP_CLIENT, httpClient);
FUNCTION_LOG_PARAM(TLS_SESSION, tlsSession);
FUNCTION_LOG_PARAM(IO_SESSION, ioSession);
FUNCTION_LOG_END();
ASSERT(httpClient != NULL);
ASSERT(tlsSession != NULL);
ASSERT(ioSession != NULL);
HttpSession *this = NULL;
@ -45,7 +45,7 @@ httpSessionNew(HttpClient *httpClient, TlsSession *tlsSession)
{
.memContext = MEM_CONTEXT_NEW(),
.httpClient = httpClient,
.tlsSession = tlsSessionMove(tlsSession, memContextCurrent()),
.ioSession = ioSessionMove(ioSession, memContextCurrent()),
};
}
MEM_CONTEXT_NEW_END();
@ -78,7 +78,7 @@ httpSessionIoRead(HttpSession *this)
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(tlsSessionIoRead(this->tlsSession));
FUNCTION_TEST_RETURN(ioSessionIoRead(this->ioSession));
}
/**********************************************************************************************************************************/
@ -91,5 +91,5 @@ httpSessionIoWrite(HttpSession *this)
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(tlsSessionIoWrite(this->tlsSession));
FUNCTION_TEST_RETURN(ioSessionIoWrite(this->ioSession));
}

View File

@ -16,13 +16,13 @@ typedef struct HttpSession HttpSession;
#include "common/io/read.h"
#include "common/io/http/client.h"
#include "common/io/tls/session.h"
#include "common/io/session.h"
#include "common/io/write.h"
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
HttpSession *httpSessionNew(HttpClient *client, TlsSession *session);
HttpSession *httpSessionNew(HttpClient *client, IoSession *session);
/***********************************************************************************************************************************
Functions

100
src/common/io/session.c Normal file
View File

@ -0,0 +1,100 @@
/***********************************************************************************************************************************
Io Session Interface
***********************************************************************************************************************************/
#include "build.auto.h"
#include "common/debug.h"
#include "common/io/session.intern.h"
#include "common/log.h"
#include "common/memContext.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct IoSession
{
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoSessionInterface *interface; // Driver interface
};
OBJECT_DEFINE_MOVE(IO_SESSION);
OBJECT_DEFINE_FREE(IO_SESSION);
/**********************************************************************************************************************************/
IoSession *
ioSessionNew(void *driver, const IoSessionInterface *interface)
{
FUNCTION_LOG_BEGIN(logLevelTrace)
FUNCTION_LOG_PARAM_P(VOID, driver);
FUNCTION_LOG_PARAM(IO_SESSION_INTERFACE, interface);
FUNCTION_LOG_END();
ASSERT(driver != NULL);
ASSERT(interface != NULL);
ASSERT(interface->type != NULL);
ASSERT(interface->close != NULL);
ASSERT(interface->ioRead != NULL);
ASSERT(interface->ioWrite != NULL);
ASSERT(interface->toLog != NULL);
IoSession *this = memNew(sizeof(IoSession));
*this = (IoSession)
{
.memContext = memContextCurrent(),
.driver = driver,
.interface = interface,
};
FUNCTION_LOG_RETURN(IO_SESSION, this);
}
/**********************************************************************************************************************************/
void
ioSessionClose(IoSession *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
this->interface->close(this->driver);
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
IoRead *
ioSessionIoRead(IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->ioRead(this->driver));
}
/**********************************************************************************************************************************/
IoWrite *
ioSessionIoWrite(IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->ioWrite(this->driver));
}
/**********************************************************************************************************************************/
String *
ioSessionToLog(const IoSession *this)
{
return strNewFmt("{type: %s, driver: %s}", strZ(*this->interface->type), strZ(this->interface->toLog(this->driver)));
}

54
src/common/io/session.h Normal file
View File

@ -0,0 +1,54 @@
/***********************************************************************************************************************************
Io Session Interface
Provides access to IoRead and IoWrite interfaces for interacting with the session returned by ioClientOpen(). Sessions should always
be closed when work with them is done but they also contain destructors to do cleanup if there is an error.
***********************************************************************************************************************************/
#ifndef COMMON_IO_SESSION_H
#define COMMON_IO_SESSION_H
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define IO_SESSION_TYPE IoSession
#define IO_SESSION_PREFIX ioSession
typedef struct IoSession IoSession;
#include "common/io/read.h"
#include "common/io/write.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Close the session
void ioSessionClose(IoSession *this);
// Move to a new parent mem context
IoSession *ioSessionMove(IoSession *this, MemContext *parentNew);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Read interface
IoRead *ioSessionIoRead(IoSession *this);
// Write interface
IoWrite *ioSessionIoWrite(IoSession *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void ioSessionFree(IoSession *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
String *ioSessionToLog(const IoSession *this);
#define FUNCTION_LOG_IO_SESSION_TYPE \
IoSession *
#define FUNCTION_LOG_IO_SESSION_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, ioSessionToLog, buffer, bufferSize)
#endif

View File

@ -0,0 +1,44 @@
/***********************************************************************************************************************************
Io Session Interface Internal
***********************************************************************************************************************************/
#ifndef COMMON_IO_SESSION_INTERN_H
#define COMMON_IO_SESSION_INTERN_H
#include "common/io/session.h"
/***********************************************************************************************************************************
Interface
***********************************************************************************************************************************/
typedef struct IoSessionInterface
{
// Type used to identify the session. This is stored as a pointer to a String pointer so it can be used with an existing String
// constant (e.g. created with STRING_EXTERN()) without needing to be copied.
const String *const *type;
// Close the session
void (*close)(void *driver);
// IoRead interface for the session
IoRead *(*ioRead)(void *driver);
// IoWrite interface for the session
IoWrite *(*ioWrite)(void *driver);
// Driver log function
String *(*toLog)(const void *driver);
} IoSessionInterface;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoSession *ioSessionNew(void *driver, const IoSessionInterface *interface);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_IO_SESSION_INTERFACE_TYPE \
IoSessionInterface *
#define FUNCTION_LOG_IO_SESSION_INTERFACE_FORMAT(value, buffer, bufferSize) \
objToLog(&value, "IoSessionInterface", buffer, bufferSize)
#endif

View File

@ -11,13 +11,19 @@ TLS Client
#include "common/crypto/common.h"
#include "common/debug.h"
#include "common/log.h"
#include "common/io/client.intern.h"
#include "common/io/io.h"
#include "common/io/tls/client.h"
#include "common/io/tls/session.intern.h"
#include "common/io/tls/session.h"
#include "common/memContext.h"
#include "common/type/object.h"
#include "common/wait.h"
/***********************************************************************************************************************************
Io client type
***********************************************************************************************************************************/
STRING_EXTERN(IO_CLIENT_TLS_TYPE_STR, IO_CLIENT_TLS_TYPE);
/***********************************************************************************************************************************
Statistics
***********************************************************************************************************************************/
@ -26,7 +32,10 @@ static TlsClientStat tlsClientStatLocal;
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct TlsClient
#define TLS_CLIENT_TYPE TlsClient
#define TLS_CLIENT_PREFIX tlsClient
typedef struct TlsClient
{
MemContext *memContext; // Mem context
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
@ -34,9 +43,26 @@ struct TlsClient
SocketClient *socketClient; // Socket client
SSL_CTX *context; // TLS context
};
} TlsClient;
OBJECT_DEFINE_FREE(TLS_CLIENT);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
tlsClientToLog(const THIS_VOID)
{
THIS(const TlsClient);
return strNewFmt(
"{socketClient: %s, timeout: %" PRIu64", verifyPeer: %s}",
memContextFreeing(this->memContext) ? NULL_Z : strZ(sckClientToLog(this->socketClient)), this->timeout,
cvtBoolToConstZ(this->verifyPeer));
}
#define FUNCTION_LOG_TLS_CLIENT_TYPE \
TlsClient *
#define FUNCTION_LOG_TLS_CLIENT_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, tlsClientToLog, buffer, bufferSize)
/***********************************************************************************************************************************
Free connection
@ -47,78 +73,6 @@ OBJECT_DEFINE_FREE_RESOURCE_BEGIN(TLS_CLIENT, LOG, logLevelTrace)
}
OBJECT_DEFINE_FREE_RESOURCE_END(LOG);
/**********************************************************************************************************************************/
TlsClient *
tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(SOCKET_CLIENT, socket);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(BOOL, verifyPeer);
FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, caPath);
FUNCTION_LOG_END();
ASSERT(socket != NULL);
TlsClient *this = NULL;
MEM_CONTEXT_NEW_BEGIN("TlsClient")
{
this = memNew(sizeof(TlsClient));
*this = (TlsClient)
{
.memContext = MEM_CONTEXT_NEW(),
.socketClient = sckClientMove(socket, MEM_CONTEXT_NEW()),
.timeout = timeout,
.verifyPeer = verifyPeer,
};
// Setup TLS context
// -------------------------------------------------------------------------------------------------------------------------
cryptoInit();
// Select the TLS method to use. To maintain compatibility with older versions of OpenSSL we need to use an SSL method,
// but SSL versions will be excluded in SSL_CTX_set_options().
const SSL_METHOD *method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
// Create the TLS context
this->context = SSL_CTX_new(method);
cryptoError(this->context == NULL, "unable to create TLS context");
memContextCallbackSet(this->memContext, tlsClientFreeResource, this);
// Exclude SSL versions to only allow TLS and also disable compression
SSL_CTX_set_options(this->context, (long)(SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION));
// Disable auto-retry to prevent SSL_read() from hanging
SSL_CTX_clear_mode(this->context, SSL_MODE_AUTO_RETRY);
// Set location of CA certificates if the server certificate will be verified
// -------------------------------------------------------------------------------------------------------------------------
if (this->verifyPeer)
{
// If the user specified a location
if (caFile != NULL || caPath != NULL) // {vm_covered}
{
cryptoError( // {vm_covered}
SSL_CTX_load_verify_locations(this->context, strZNull(caFile), strZNull(caPath)) != 1, // {vm_covered}
"unable to set user-defined CA certificate location"); // {vm_covered}
}
// Else use the defaults
else
cryptoError(SSL_CTX_set_default_verify_paths(this->context) != 1, "unable to set default CA certificate location");
}
tlsClientStatLocal.object++;
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(TLS_CLIENT, this);
}
/***********************************************************************************************************************************
Convert an ASN1 string used in certificates to a String
***********************************************************************************************************************************/
@ -259,16 +213,18 @@ tlsClientHostVerify(const String *host, X509 *certificate)
/***********************************************************************************************************************************
Open connection if this is a new client or if the connection was closed by the server
***********************************************************************************************************************************/
TlsSession *
tlsClientOpen(TlsClient *this)
static IoSession *
tlsClientOpen(THIS_VOID)
{
THIS(TlsClient);
FUNCTION_LOG_BEGIN(logLevelTrace)
FUNCTION_LOG_PARAM(TLS_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
TlsSession *result = NULL;
IoSession *result = NULL;
SSL *session = NULL;
MEM_CONTEXT_TEMP_BEGIN()
@ -317,7 +273,7 @@ tlsClientOpen(TlsClient *this)
}
while (retry);
tlsSessionMove(result, memContextPrior());
ioSessionMove(result, memContextPrior());
}
MEM_CONTEXT_TEMP_END();
@ -351,7 +307,92 @@ tlsClientOpen(TlsClient *this)
}
}
FUNCTION_LOG_RETURN(TLS_SESSION, result);
FUNCTION_LOG_RETURN(IO_SESSION, result);
}
/**********************************************************************************************************************************/
static const IoClientInterface tlsClientInterface =
{
.type = &IO_CLIENT_TLS_TYPE_STR,
.open = tlsClientOpen,
.toLog = tlsClientToLog,
};
IoClient *
tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(SOCKET_CLIENT, socket);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(BOOL, verifyPeer);
FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, caPath);
FUNCTION_LOG_END();
ASSERT(socket != NULL);
IoClient *this = NULL;
MEM_CONTEXT_NEW_BEGIN("TlsClient")
{
TlsClient *driver = memNew(sizeof(TlsClient));
*driver = (TlsClient)
{
.memContext = MEM_CONTEXT_NEW(),
.socketClient = sckClientMove(socket, MEM_CONTEXT_NEW()),
.timeout = timeout,
.verifyPeer = verifyPeer,
};
// Setup TLS context
// -------------------------------------------------------------------------------------------------------------------------
cryptoInit();
// Select the TLS method to use. To maintain compatibility with older versions of OpenSSL we need to use an SSL method,
// but SSL versions will be excluded in SSL_CTX_set_options().
const SSL_METHOD *method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
// Create the TLS context
driver->context = SSL_CTX_new(method);
cryptoError(driver->context == NULL, "unable to create TLS context");
memContextCallbackSet(driver->memContext, tlsClientFreeResource, driver);
// Exclude SSL versions to only allow TLS and also disable compression
SSL_CTX_set_options(driver->context, (long)(SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION));
// Disable auto-retry to prevent SSL_read() from hanging
SSL_CTX_clear_mode(driver->context, SSL_MODE_AUTO_RETRY);
// Set location of CA certificates if the server certificate will be verified
// -------------------------------------------------------------------------------------------------------------------------
if (driver->verifyPeer)
{
// If the user specified a location
if (caFile != NULL || caPath != NULL) // {vm_covered}
{
cryptoError( // {vm_covered}
SSL_CTX_load_verify_locations(driver->context, strZNull(caFile), strZNull(caPath)) != 1, // {vm_covered}
"unable to set user-defined CA certificate location"); // {vm_covered}
}
// Else use the defaults
else
{
cryptoError(
SSL_CTX_set_default_verify_paths(driver->context) != 1, "unable to set default CA certificate location");
}
}
tlsClientStatLocal.object++;
// Create client interface
this = ioClientNew(driver, &tlsClientInterface);
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(IO_CLIENT, this);
}
/**********************************************************************************************************************************/

View File

@ -4,21 +4,19 @@ TLS Client
A simple, secure TLS client intended to allow access to services that are exposed via HTTPS. We call it TLS instead of SSL because
SSL methods are disabled so only TLS connections are allowed.
This object is intended to be used for multiple TLS sessions so tlsClientOpen() can be called each time a new session is needed.
This object is intended to be used for multiple TLS sessions so ioClientOpen() can be called each time a new session is needed.
***********************************************************************************************************************************/
#ifndef COMMON_IO_TLS_CLIENT_H
#define COMMON_IO_TLS_CLIENT_H
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define TLS_CLIENT_TYPE TlsClient
#define TLS_CLIENT_PREFIX tlsClient
typedef struct TlsClient TlsClient;
#include "common/io/client.h"
#include "common/io/socket/client.h"
#include "common/io/tls/session.h"
/***********************************************************************************************************************************
Io client type
***********************************************************************************************************************************/
#define IO_CLIENT_TLS_TYPE "tls"
STRING_DECLARE(IO_CLIENT_TLS_TYPE_STR);
/***********************************************************************************************************************************
Statistics
@ -33,28 +31,12 @@ typedef struct TlsClientStat
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
TlsClient *tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath);
IoClient *tlsClientNew(SocketClient *socket, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath);
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Open tls session
TlsSession *tlsClientOpen(TlsClient *this);
// Statistics as a formatted string
String *tlsClientStatStr(void);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void tlsClientFree(TlsClient *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_TLS_CLIENT_TYPE \
TlsClient *
#define FUNCTION_LOG_TLS_CLIENT_FORMAT(value, buffer, bufferSize) \
objToLog(value, "TlsClient", buffer, bufferSize)
#endif

View File

@ -9,7 +9,10 @@ TLS Session
#include "common/debug.h"
#include "common/io/io.h"
#include "common/io/read.intern.h"
#include "common/io/tls/session.intern.h"
#include "common/io/session.intern.h"
#include "common/io/socket/session.h"
#include "common/io/tls/client.h"
#include "common/io/tls/session.h"
#include "common/io/write.intern.h"
#include "common/log.h"
#include "common/memContext.h"
@ -18,23 +21,39 @@ TLS Session
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct TlsSession
#define TLS_SESSION_TYPE TlsSession
#define TLS_SESSION_PREFIX tlsSession
typedef struct TlsSession
{
MemContext *memContext; // Mem context
SocketSession *socketSession; // Socket session
SSL *session; // TLS session on the socket
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
bool shutdownOnClose; // Shutdown the TLS connection when closing the socket
IoRead *read; // Read interface
IoWrite *write; // Write interface
};
} TlsSession;
OBJECT_DEFINE_MOVE(TLS_SESSION);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
tlsSessionToLog(const THIS_VOID)
{
THIS(const TlsSession);
OBJECT_DEFINE_GET(IoRead, , TLS_SESSION, IoRead *, read);
OBJECT_DEFINE_GET(IoWrite, , TLS_SESSION, IoWrite *, write);
return strNewFmt(
"{socketSession: %s, timeout: %" PRIu64", shutdownOnClose: %s}",
this->socketSession == NULL || memContextFreeing(this->memContext) ? NULL_Z : strZ(sckSessionToLog(this->socketSession)),
this->timeout, cvtBoolToConstZ(this->shutdownOnClose));
}
OBJECT_DEFINE_FREE(TLS_SESSION);
#define FUNCTION_LOG_TLS_SESSION_TYPE \
TlsSession *
#define FUNCTION_LOG_TLS_SESSION_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, tlsSessionToLog, buffer, bufferSize)
/***********************************************************************************************************************************
Free connection
@ -46,12 +65,13 @@ OBJECT_DEFINE_FREE_RESOURCE_BEGIN(TLS_SESSION, LOG, logLevelTrace)
OBJECT_DEFINE_FREE_RESOURCE_END(LOG);
/**********************************************************************************************************************************/
void
tlsSessionClose(TlsSession *this, bool shutdown)
static void
tlsSessionClose(THIS_VOID)
{
THIS(TlsSession);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_SESSION, this);
FUNCTION_LOG_PARAM(BOOL, shutdown);
FUNCTION_LOG_END();
ASSERT(this != NULL);
@ -60,7 +80,7 @@ tlsSessionClose(TlsSession *this, bool shutdown)
if (this->session != NULL)
{
// Shutdown on request
if (shutdown)
if (this->shutdownOnClose)
SSL_shutdown(this->session);
// Free the socket session
@ -109,7 +129,8 @@ tlsSessionResultProcess(TlsSession *this, int errorTls, long unsigned int errorT
if (!closeOk)
THROW(ProtocolError, "unexpected TLS eof");
tlsSessionClose(this, false);
this->shutdownOnClose = false;
tlsSessionClose(this);
break;
}
@ -272,7 +293,46 @@ tlsSessionEof(THIS_VOID)
}
/**********************************************************************************************************************************/
TlsSession *
static IoRead *
tlsSessionIoRead(THIS_VOID)
{
THIS(TlsSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TLS_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->read);
}
/**********************************************************************************************************************************/
static IoWrite *
tlsSessionIoWrite(THIS_VOID)
{
THIS(TlsSession);
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TLS_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->write);
}
/**********************************************************************************************************************************/
static const IoSessionInterface tlsSessionInterface =
{
.type = &IO_CLIENT_TLS_TYPE_STR,
.close = tlsSessionClose,
.ioRead = tlsSessionIoRead,
.ioWrite = tlsSessionIoWrite,
.toLog = tlsSessionToLog,
};
IoSession *
tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
@ -284,26 +344,27 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
ASSERT(session != NULL);
ASSERT(socketSession != NULL);
TlsSession *this = NULL;
IoSession *this = NULL;
MEM_CONTEXT_NEW_BEGIN("TlsSession")
{
this = memNew(sizeof(TlsSession));
TlsSession *driver = memNew(sizeof(TlsSession));
*this = (TlsSession)
*driver = (TlsSession)
{
.memContext = MEM_CONTEXT_NEW(),
.session = session,
.socketSession = sckSessionMove(socketSession, MEM_CONTEXT_NEW()),
.timeout = timeout,
.shutdownOnClose = true,
};
// Ensure session is freed
memContextCallbackSet(this->memContext, tlsSessionFreeResource, this);
memContextCallbackSet(driver->memContext, tlsSessionFreeResource, driver);
// Assign socket to TLS session
cryptoError(
SSL_set_fd(this->session, sckSessionFd(this->socketSession)) != 1, "unable to add socket to TLS session");
SSL_set_fd(driver->session, sckSessionFd(driver->socketSession)) != 1, "unable to add socket to TLS session");
// Negotiate TLS session. The error queue must be cleared before this operation.
int result = 0;
@ -312,19 +373,22 @@ tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout)
{
ERR_clear_error();
if (sckSessionType(this->socketSession) == sckSessionTypeClient)
result = tlsSessionResult(this, SSL_connect(this->session), false);
if (sckSessionType(driver->socketSession) == sckSessionTypeClient)
result = tlsSessionResult(driver, SSL_connect(driver->session), false);
else
result = tlsSessionResult(this, SSL_accept(this->session), false);
result = tlsSessionResult(driver, SSL_accept(driver->session), false);
}
// Create read and write interfaces
this->write = ioWriteNewP(this, .write = tlsSessionWrite);
ioWriteOpen(this->write);
this->read = ioReadNewP(this, .block = true, .eof = tlsSessionEof, .read = tlsSessionRead);
ioReadOpen(this->read);
driver->write = ioWriteNewP(driver, .write = tlsSessionWrite);
ioWriteOpen(driver->write);
driver->read = ioReadNewP(driver, .block = true, .eof = tlsSessionEof, .read = tlsSessionRead);
ioReadOpen(driver->read);
// Create session interface
this = ioSessionNew(driver, &tlsSessionInterface);
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(TLS_SESSION, this);
FUNCTION_LOG_RETURN(IO_SESSION, this);
}

View File

@ -1,10 +1,10 @@
/***********************************************************************************************************************************
TLS Session
TLS sessions are created by calling tlsClientOpen().
TLS sessions are created by calling ioClientOpen().
TLS sessions are generally reused so the user must be prepared to retry their transaction on a read/write error if the server closes
the session before it is reused. If this behavior is not desirable then tlsSessionFree()/tlsClientOpen() can be called to get a new
the session before it is reused. If this behavior is not desirable then ioSessionFree()/ioClientOpen() can be called to get a new
session.
***********************************************************************************************************************************/
#ifndef COMMON_IO_TLS_SESSION_H
@ -13,45 +13,16 @@ session.
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
#define TLS_SESSION_TYPE TlsSession
#define TLS_SESSION_PREFIX tlsSession
#include <openssl/ssl.h>
typedef struct TlsSession TlsSession;
#include "common/io/read.h"
#include "common/io/session.h"
#include "common/io/socket/session.h"
#include "common/io/write.h"
#include "common/time.h"
/***********************************************************************************************************************************
Functions
Constructors
***********************************************************************************************************************************/
// Close the session. Shutdown should not be attempted after an error, which means the client never has the oppottunity to do a
// shutdown since the connection is held open until it is disconnected by the server.
void tlsSessionClose(TlsSession *this, bool shutdown);
// Move to a new parent mem context
TlsSession *tlsSessionMove(TlsSession *this, MemContext *parentNew);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Read interface
IoRead *tlsSessionIoRead(TlsSession *this);
// Write interface
IoWrite *tlsSessionIoWrite(TlsSession *this);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void tlsSessionFree(TlsSession *this);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_TLS_SESSION_TYPE \
TlsSession *
#define FUNCTION_LOG_TLS_SESSION_FORMAT(value, buffer, bufferSize) \
objToLog(value, "TlsSession", buffer, bufferSize)
// Only called by TLS client/server code
IoSession *tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout);
#endif

View File

@ -1,17 +0,0 @@
/***********************************************************************************************************************************
TLS Session Internal
***********************************************************************************************************************************/
#ifndef COMMON_IO_TLS_SESSION_INTERN_H
#define COMMON_IO_TLS_SESSION_INTERN_H
#include <openssl/ssl.h>
#include "common/io/tls/session.h"
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
// Only called by TLS client/server code
TlsSession *tlsSessionNew(SSL *session, SocketSession *socketSession, TimeMSec timeout);
#endif

View File

@ -11,6 +11,7 @@ Azure Storage
#include "common/debug.h"
#include "common/io/http/client.h"
#include "common/io/http/common.h"
#include "common/io/tls/client.h"
#include "common/log.h"
#include "common/memContext.h"
#include "common/regExp.h"
@ -739,7 +740,8 @@ storageAzureNew(
driver->sasKey = httpQueryNewStr(key);
// Create the http client used to service requests
driver->httpClient = httpClientNew(driver->host, port, timeout, verifyPeer, caFile, caPath);
driver->httpClient = httpClientNew(
tlsClientNew(sckClientNew(driver->host, port, timeout), timeout, verifyPeer, caFile, caPath), timeout);
// Create list of redacted headers
driver->headerRedactList = strLstNew();

View File

@ -10,6 +10,7 @@ S3 Storage
#include "common/debug.h"
#include "common/io/http/client.h"
#include "common/io/http/common.h"
#include "common/io/tls/client.h"
#include "common/log.h"
#include "common/memContext.h"
#include "common/regExp.h"
@ -851,7 +852,10 @@ storageS3New(
};
// Create the HTTP client used to service requests
driver->httpClient = httpClientNew(host == NULL ? driver->bucketEndpoint : host, port, timeout, verifyPeer, caFile, caPath);
driver->httpClient = httpClientNew(
tlsClientNew(
sckClientNew(host == NULL ? driver->bucketEndpoint : host, port, timeout), timeout, verifyPeer, caFile, caPath),
timeout);
// Create list of redacted headers
driver->headerRedactList = strLstNew();

View File

@ -915,10 +915,6 @@ src/common/io/tls/session.h:
class: core
type: c/h
src/common/io/tls/session.intern.h:
class: core
type: c/h
src/common/io/write.c:
class: core
type: c

View File

@ -242,6 +242,8 @@ unit:
total: 5
coverage:
common/io/client: full
common/io/session: full
common/io/tls/client: full
common/io/tls/session: full
common/io/socket/client: full

View File

@ -12,7 +12,7 @@ TLS Test Harness
#include "common/crypto/common.h"
#include "common/error.h"
#include "common/io/socket/session.h"
#include "common/io/tls/session.intern.h"
#include "common/io/tls/session.h"
#include "common/log.h"
#include "common/type/buffer.h"
#include "common/type/json.h"
@ -271,7 +271,7 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
THROW_SYS_ERROR(AssertError, "unable to listen on socket");
// Loop until no more commands
TlsSession *serverSession = NULL;
IoSession *serverSession = NULL;
bool done = false;
do
@ -283,8 +283,7 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
{
case hrnTlsCmdAbort:
{
tlsSessionClose(serverSession, false);
tlsSessionFree(serverSession);
ioSessionFree(serverSession);
serverSession = NULL;
break;
@ -313,8 +312,8 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
if (serverSession == NULL)
THROW(AssertError, "TLS session is already closed");
tlsSessionClose(serverSession, true);
tlsSessionFree(serverSession);
ioSessionClose(serverSession);
ioSessionFree(serverSession);
serverSession = NULL;
break;
@ -332,7 +331,7 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
const String *expected = varStr(data);
Buffer *buffer = bufNew(strSize(expected));
ioRead(tlsSessionIoRead(serverSession), buffer);
ioRead(ioSessionIoRead(serverSession), buffer);
// Treat any ? characters as wildcards so variable elements (e.g. auth hashes) can be ignored
String *actual = strNewBuf(buffer);
@ -352,8 +351,8 @@ void hrnTlsServerRunParam(IoRead *read, const String *certificate, const String
case hrnTlsCmdReply:
{
ioWrite(tlsSessionIoWrite(serverSession), BUFSTR(varStr(data)));
ioWriteFlush(tlsSessionIoWrite(serverSession));
ioWrite(ioSessionIoWrite(serverSession), BUFSTR(varStr(data)));
ioWriteFlush(ioSessionIoWrite(serverSession));
break;
}

View File

@ -121,7 +121,7 @@ testRun(void)
cfgOptionSet(cfgOptLogTimestamp, cfgSourceParam, varNewBool(true));
httpClientNew(strNew("BOGUS"), 443, 1000, true, NULL, NULL);
httpClientNew(tlsClientNew(sckClientNew(STRDEF("BOGUS"), 443, 1000), 1000, true, NULL, NULL), 1000);
harnessLogLevelSet(logLevelDetail);

View File

@ -5,6 +5,7 @@ Test HTTP
#include "common/io/fdRead.h"
#include "common/io/fdWrite.h"
#include "common/io/tls/client.h"
#include "common/harnessFork.h"
#include "common/harnessTls.h"
@ -176,7 +177,9 @@ testRun(void)
TEST_RESULT_STR(httpClientStatStr(), NULL, "no stats yet");
TEST_ASSIGN(
client, httpClientNew(strNew("localhost"), hrnTlsServerPort(), 500, testContainer(), NULL, NULL),
client,
httpClientNew(
tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 500), 500, testContainer(), NULL, NULL), 500),
"new client");
TEST_ERROR_FMT(
@ -204,7 +207,10 @@ testRun(void)
ioBufferSizeSet(35);
TEST_ASSIGN(
client, httpClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000, testContainer(), NULL, NULL),
client,
httpClientNew(
tlsClientNew(sckClientNew(hrnTlsServerHost(), hrnTlsServerPort(), 5000), 5000, testContainer(), NULL, NULL),
5000),
"new client");
// -----------------------------------------------------------------------------------------------------------------

View File

@ -211,7 +211,7 @@ testRun(void)
// *****************************************************************************************************************************
if (testBegin("TlsClient verification"))
{
TlsClient *client = NULL;
IoClient *client = NULL;
// Connection errors
// -------------------------------------------------------------------------------------------------------------------------
@ -219,20 +219,20 @@ testRun(void)
client, tlsClientNew(sckClientNew(strNew("99.99.99.99.99"), hrnTlsServerPort(), 0), 0, true, NULL, NULL),
"new client");
TEST_ERROR(
tlsClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known");
ioClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known");
TEST_ASSIGN(
client, tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 100), 100, true, NULL, NULL),
"new client");
TEST_ERROR_FMT(
tlsClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
hrnTlsServerPort());
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("bogus client cert/path");
TEST_ERROR(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, strNew("bogus.crt"), strNew("/bogus"))),
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
@ -273,7 +273,7 @@ testRun(void)
hrnTlsServerClose();
TEST_ERROR_FMT(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true, NULL, strNew("/bogus"))),
CryptoError,
@ -287,7 +287,7 @@ testRun(void)
hrnTlsServerClose();
TEST_RESULT_VOID(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("test.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
@ -300,7 +300,7 @@ testRun(void)
hrnTlsServerClose();
TEST_RESULT_VOID(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("host.test2.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
@ -313,7 +313,7 @@ testRun(void)
hrnTlsServerClose();
TEST_ERROR(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("test3.pgbackrest.org"), hrnTlsServerPort(), 5000), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)),
@ -327,7 +327,7 @@ testRun(void)
hrnTlsServerClose();
TEST_ERROR_FMT(
tlsClientOpen(
ioClientOpen(
tlsClientNew(
sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, true,
strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()),
@ -343,7 +343,7 @@ testRun(void)
hrnTlsServerClose();
TEST_RESULT_VOID(
tlsClientOpen(
ioClientOpen(
tlsClientNew(sckClientNew(strNew("localhost"), hrnTlsServerPort(), 5000), 0, false, NULL, NULL)),
"open connection");
@ -359,8 +359,8 @@ testRun(void)
// *****************************************************************************************************************************
if (testBegin("TlsClient general usage"))
{
TlsClient *client = NULL;
TlsSession *session = NULL;
IoClient *client = NULL;
IoSession *session = NULL;
// Reset statistics
sckClientStatLocal = (SocketClientStat){0};
@ -389,7 +389,8 @@ testRun(void)
hrnTlsServerAccept();
TEST_ASSIGN(session, tlsClientOpen(client), "open client");
TEST_ASSIGN(session, ioClientOpen(client), "open client");
TlsSession *tlsSession = (TlsSession *)session->driver;
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("socket read/write ready");
@ -406,22 +407,22 @@ testRun(void)
TEST_ERROR(
sckReadyRetry(-1, EINVAL, true, &timeout, 0), KernelError, "unable to poll socket: [22] Invalid argument");
TEST_RESULT_BOOL(sckReadyRead(session->socketSession->fd, 0), false, "socket is not read ready");
TEST_RESULT_BOOL(sckReadyWrite(session->socketSession->fd, 100), true, "socket is write ready");
TEST_RESULT_VOID(sckSessionReadyWrite(session->socketSession), "socket session is write ready");
TEST_RESULT_BOOL(sckReadyRead(tlsSession->socketSession->fd, 0), false, "socket is not read ready");
TEST_RESULT_BOOL(sckReadyWrite(tlsSession->socketSession->fd, 100), true, "socket is write ready");
TEST_RESULT_VOID(sckSessionReadyWrite(tlsSession->socketSession), "socket session is write ready");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("uncovered errors");
TEST_RESULT_INT(tlsSessionResultProcess(session, SSL_ERROR_WANT_WRITE, 0, 0, false), 0, "write ready");
TEST_RESULT_INT(tlsSessionResultProcess(tlsSession, SSL_ERROR_WANT_WRITE, 0, 0, false), 0, "write ready");
TEST_ERROR(
tlsSessionResultProcess(session, SSL_ERROR_WANT_X509_LOOKUP, 336031996, 0, false), ServiceError,
tlsSessionResultProcess(tlsSession, SSL_ERROR_WANT_X509_LOOKUP, 336031996, 0, false), ServiceError,
"TLS error [4:336031996] unknown protocol");
TEST_ERROR(
tlsSessionResultProcess(session, SSL_ERROR_WANT_X509_LOOKUP, 0, 0, false), ServiceError,
tlsSessionResultProcess(tlsSession, SSL_ERROR_WANT_X509_LOOKUP, 0, 0, false), ServiceError,
"TLS error [4:0] no details available");
TEST_ERROR(
tlsSessionResultProcess(session, SSL_ERROR_ZERO_RETURN, 0, 0, false), ProtocolError, "unexpected TLS eof");
tlsSessionResultProcess(tlsSession, SSL_ERROR_ZERO_RETURN, 0, 0, false), ProtocolError, "unexpected TLS eof");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("first protocol exchange");
@ -430,11 +431,11 @@ testRun(void)
hrnTlsServerReplyZ("something:0\n");
const Buffer *input = BUFSTRDEF("some protocol info");
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
ioWriteFlush(tlsSessionIoWrite(session));
TEST_RESULT_VOID(ioWrite(ioSessionIoWrite(session), input), "write input");
ioWriteFlush(ioSessionIoWrite(session));
TEST_RESULT_STR_Z(ioReadLine(tlsSessionIoRead(session)), "something:0", "read line");
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_STR_Z(ioReadLine(ioSessionIoRead(session)), "something:0", "read line");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
hrnTlsServerSleep(100);
hrnTlsServerReplyZ("some ");
@ -443,14 +444,14 @@ testRun(void)
hrnTlsServerReplyZ("contentAND MORE");
Buffer *output = bufNew(12);
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 12, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 12, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "some content", "check output");
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
output = bufNew(8);
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 8, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 8, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "AND MORE", "check output");
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("read eof");
@ -458,11 +459,11 @@ testRun(void)
hrnTlsServerSleep(500);
output = bufNew(12);
session->socketSession->timeout = 100;
tlsSession->socketSession->timeout = 100;
TEST_ERROR_FMT(
ioRead(tlsSessionIoRead(session), output), ProtocolError,
ioRead(ioSessionIoRead(session), output), ProtocolError,
"timeout after 100ms waiting for read from '%s:%u'", strZ(hrnTlsServerHost()), hrnTlsServerPort());
session->socketSession->timeout = 5000;
tlsSession->socketSession->timeout = 5000;
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("second protocol exchange");
@ -473,19 +474,19 @@ testRun(void)
hrnTlsServerClose();
input = BUFSTRDEF("more protocol info");
TEST_RESULT_VOID(ioWrite(tlsSessionIoWrite(session), input), "write input");
ioWriteFlush(tlsSessionIoWrite(session));
TEST_RESULT_VOID(ioWrite(ioSessionIoWrite(session), input), "write input");
ioWriteFlush(ioSessionIoWrite(session));
output = bufNew(12);
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 12, "read output");
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 12, "read output");
TEST_RESULT_STR_Z(strNewBuf(output), "0123456789AB", "check output");
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), false, "check eof = false");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), false, "check eof = false");
output = bufNew(12);
TEST_RESULT_UINT(ioRead(tlsSessionIoRead(session), output), 0, "read no output after eof");
TEST_RESULT_BOOL(ioReadEof(tlsSessionIoRead(session)), true, "check eof = true");
TEST_RESULT_UINT(ioRead(ioSessionIoRead(session), output), 0, "read no output after eof");
TEST_RESULT_BOOL(ioReadEof(ioSessionIoRead(session)), true, "check eof = true");
TEST_RESULT_VOID(tlsSessionClose(session, false), "close again");
TEST_RESULT_VOID(ioSessionClose(session), "close again");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("aborted connection before read complete (blocking socket)");
@ -495,16 +496,16 @@ testRun(void)
hrnTlsServerAbort();
socketLocal.block = true;
TEST_ASSIGN(session, tlsClientOpen(client), "open client again (was closed by server)");
TEST_ASSIGN(session, ioClientOpen(client), "open client again (was closed by server)");
socketLocal.block = false;
output = bufNew(13);
TEST_ERROR(ioRead(tlsSessionIoRead(session), output), KernelError, "TLS syscall error");
TEST_ERROR(ioRead(ioSessionIoRead(session), output), KernelError, "TLS syscall error");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("close connection");
TEST_RESULT_VOID(tlsClientFree(client), "free client");
TEST_RESULT_VOID(ioClientFree(client), "free client");
// -----------------------------------------------------------------------------------------------------------------
hrnTlsClientEnd();