mirror of https://github.com/eclipse/paho.mqtt.c
253 lines
5.8 KiB
C
253 lines
5.8 KiB
C
/*******************************************************************************
|
|
* Copyright (c) 2024, 2026 Ian Craggs and others
|
|
*
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v2.0
|
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
|
*
|
|
* The Eclipse Public License is available at
|
|
* https://www.eclipse.org/legal/epl-2.0/
|
|
* and the Eclipse Distribution License is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* Contributors:
|
|
* Unit test for SSL_ERROR_WANT_WRITE/WANT_READ handling during SSL handshake
|
|
*******************************************************************************/
|
|
|
|
/**
|
|
* @file
|
|
* Test for SSL_ERROR_WANT_WRITE and SSL_ERROR_WANT_READ handling in SSLSocket_connect()
|
|
*
|
|
* This test verifies that SSL connections complete successfully even when
|
|
* SSL_ERROR_WANT_WRITE or SSL_ERROR_WANT_READ occur during the handshake.
|
|
* The fix ensures proper socket polling by calling Socket_addPendingWrite()
|
|
* when WANT_WRITE occurs.
|
|
*/
|
|
|
|
#include "MQTTClient.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
|
|
#if !defined(_WINDOWS)
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
/* Test configuration */
|
|
struct Options
|
|
{
|
|
char* connection;
|
|
char* client_key_file;
|
|
char* server_key_file;
|
|
int verbose;
|
|
} options =
|
|
{
|
|
"ssl://localhost:18883",
|
|
NULL,
|
|
NULL,
|
|
1
|
|
};
|
|
|
|
void usage(void)
|
|
{
|
|
printf("SSL WANT_WRITE Handshake Test\n");
|
|
printf("Options:\n");
|
|
printf("\t--hostname <hostname> - SSL server hostname (default: ssl://localhost:18883)\n");
|
|
printf("\t--client_key <file> - Client certificate file\n");
|
|
printf("\t--server_key <file> - Server CA certificate file\n");
|
|
printf("\t--verbose - Enable verbose output\n");
|
|
printf("\t--help - This help\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void getopts(int argc, char** argv)
|
|
{
|
|
int count = 1;
|
|
|
|
while (count < argc)
|
|
{
|
|
if (strcmp(argv[count], "--hostname") == 0)
|
|
{
|
|
if (++count < argc)
|
|
options.connection = argv[count];
|
|
else
|
|
usage();
|
|
}
|
|
else if (strcmp(argv[count], "--client_key") == 0)
|
|
{
|
|
if (++count < argc)
|
|
options.client_key_file = argv[count];
|
|
else
|
|
usage();
|
|
}
|
|
else if (strcmp(argv[count], "--server_key") == 0)
|
|
{
|
|
if (++count < argc)
|
|
options.server_key_file = argv[count];
|
|
else
|
|
usage();
|
|
}
|
|
else if (strcmp(argv[count], "--verbose") == 0)
|
|
{
|
|
options.verbose = 1;
|
|
}
|
|
else if (strcmp(argv[count], "--help") == 0)
|
|
{
|
|
usage();
|
|
}
|
|
else
|
|
{
|
|
printf("Unknown option: %s\n", argv[count]);
|
|
usage();
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/* Logging */
|
|
#define LOGA_DEBUG 0
|
|
#define LOGA_INFO 1
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <sys/timeb.h>
|
|
void MyLog(int LOGA_level, char* format, ...)
|
|
{
|
|
static char msg_buf[256];
|
|
va_list args;
|
|
#if defined(_WIN32) || defined(_WINDOWS)
|
|
struct timeb ts;
|
|
#else
|
|
struct timeval ts;
|
|
#endif
|
|
struct tm timeinfo;
|
|
|
|
if (LOGA_level == LOGA_DEBUG && options.verbose == 0)
|
|
return;
|
|
|
|
#if defined(_WIN32) || defined(_WINDOWS)
|
|
ftime(&ts);
|
|
localtime_s(&timeinfo, &ts.time);
|
|
#else
|
|
gettimeofday(&ts, NULL);
|
|
localtime_r(&ts.tv_sec, &timeinfo);
|
|
#endif
|
|
strftime(msg_buf, 80, "%Y%m%d %H%M%S", &timeinfo);
|
|
|
|
#if defined(_WIN32) || defined(_WINDOWS)
|
|
sprintf(&msg_buf[strlen(msg_buf)], ".%.3hu ", ts.millitm);
|
|
#else
|
|
sprintf(&msg_buf[strlen(msg_buf)], ".%.3lu ", ts.tv_usec / 1000L);
|
|
#endif
|
|
|
|
va_start(args, format);
|
|
vsnprintf(&msg_buf[strlen(msg_buf)], sizeof(msg_buf) - strlen(msg_buf), format, args);
|
|
va_end(args);
|
|
|
|
printf("%s\n", msg_buf);
|
|
fflush(stdout);
|
|
}
|
|
|
|
/* Test tracking */
|
|
int tests = 0;
|
|
int failures = 0;
|
|
|
|
#if !defined(__func__)
|
|
#define __func__ __FUNCTION__
|
|
#endif
|
|
|
|
#define ASSERT(condition, message) \
|
|
do { \
|
|
tests++; \
|
|
if (!(condition)) { \
|
|
failures++; \
|
|
MyLog(1, "FAIL: %s (line %d): %s", __func__, __LINE__, message); \
|
|
return -1; \
|
|
} \
|
|
} while(0)
|
|
|
|
/**
|
|
* Test: Basic SSL connection
|
|
*
|
|
* Verifies that SSL connections complete successfully.
|
|
* This validates that the SSL_ERROR_WANT_WRITE/WANT_READ handling
|
|
* in SSLSocket_connect() works correctly.
|
|
*/
|
|
int test_ssl_connect_basic(void)
|
|
{
|
|
MQTTClient client;
|
|
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
|
|
MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer;
|
|
int rc;
|
|
|
|
MyLog(1, "Test: Basic SSL connection");
|
|
|
|
rc = MQTTClient_create(&client, options.connection, "test_ssl_want_write",
|
|
MQTTCLIENT_PERSISTENCE_NONE, NULL);
|
|
ASSERT(rc == MQTTCLIENT_SUCCESS, "MQTTClient_create failed");
|
|
|
|
conn_opts.keepAliveInterval = 20;
|
|
conn_opts.cleansession = 1;
|
|
|
|
if (options.server_key_file)
|
|
{
|
|
ssl_opts.trustStore = options.server_key_file;
|
|
conn_opts.ssl = &ssl_opts;
|
|
}
|
|
|
|
if (options.client_key_file)
|
|
{
|
|
ssl_opts.keyStore = options.client_key_file;
|
|
}
|
|
|
|
MyLog(1, "Connecting to %s", options.connection);
|
|
rc = MQTTClient_connect(client, &conn_opts);
|
|
|
|
if (rc != MQTTCLIENT_SUCCESS)
|
|
{
|
|
MyLog(1, "Connection failed: rc=%d", rc);
|
|
MyLog(1, "Note: This is expected if no SSL broker is running on %s", options.connection);
|
|
/* Not a test failure - just means no broker available */
|
|
}
|
|
else
|
|
{
|
|
MyLog(1, "Connection succeeded");
|
|
MQTTClient_disconnect(client, 1000);
|
|
}
|
|
|
|
MQTTClient_destroy(&client);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Main test runner
|
|
*/
|
|
int main(int argc, char** argv)
|
|
{
|
|
int rc = 0;
|
|
|
|
getopts(argc, argv);
|
|
|
|
printf("SSL Connection Test\n");
|
|
printf("===================\n");
|
|
printf("Connection: %s\n", options.connection);
|
|
if (options.client_key_file)
|
|
printf("Client Key: %s\n", options.client_key_file);
|
|
if (options.server_key_file)
|
|
printf("Server Key: %s\n", options.server_key_file);
|
|
printf("\n");
|
|
|
|
rc = test_ssl_connect_basic();
|
|
|
|
printf("\nTest Results: %d tests, %d failures\n", tests, failures);
|
|
|
|
if (failures == 0)
|
|
printf("SUCCESS\n");
|
|
else
|
|
printf("FAILURE: %d test(s) failed\n", failures);
|
|
|
|
return (failures == 0) ? 0 : 1;
|
|
}
|