#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <AssertMacros.h>
#include <CommonCrypto/CommonRandomSPI.h>
#define CCRNGSTATE() ccDRBGGetRngState()
#include <tls_ciphersuites.h>
#include <tls_helpers.h>
#include <tls_cache.h>
#include <Security/CipherSuite.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecIdentity.h>
#include "appleSession.h"
#include "secCrypto.h"
#include "tls_client.h"
#include "tls_alloc.h"
#include "sockets.h"
static
__attribute__((format(printf, 3, 4)))
void _log(const tls_client_ctx_t *cc, const char *function, const char *str, ...)
{
va_list ap;
va_start(ap, str);
if(cc) {
printf("[%p] ", cc);
}
printf("%s: ", function);
vprintf(str, ap);
}
#define session_log(...) _log(cc, __FUNCTION__, __VA_ARGS__);
#define client_log(...) _log(NULL, __FUNCTION__, __VA_ARGS__);
#define test_printf printf
#define DEBUG_ONLY __attribute__((unused))
static int tcp_write(tls_client_ctx_t *cc, tls_buffer out)
{
while(out.length) {
ssize_t nwr;
nwr = send(cc->sock, out.data, out.length, 0);
if (nwr == -1) {
session_log("Error writing %zd bytes to socket : %s\n", out.length, strerror(errno));
return errno;
}
out.data += nwr;
out.length -= nwr;
}
return 0;
}
static int udp_write(tls_client_ctx_t *cc, tls_buffer out)
{
ssize_t nwr;
nwr = send(cc->sock, out.data, out.length, 0);
if (nwr == -1) {
session_log("Error writing %zd bytes to socket : %s\n", out.length, strerror(errno));
return errno;
}
return 0;
}
static
int encrypt_and_write(tls_client_ctx_t *cc, const tls_buffer data, uint8_t content_type)
{
int err;
tls_buffer encrypted = {0, };
err=mySSLAlloc(&encrypted, tls_record_encrypted_size(cc->rec, content_type, data.length));
require_noerr(err, fail);
err=tls_record_encrypt(cc->rec, data, content_type, &encrypted);
require_noerr(err, fail);
session_log("Writing %5zd encrypted bytes\n", encrypted.length);
if(cc->params->dtls) {
err = udp_write(cc, encrypted);
} else {
err = tcp_write(cc, encrypted);
}
fail:
mySSLFree(&encrypted);
return err;
}
static
int tls_handshake_write_callback(tls_handshake_ctx_t ctx, const tls_buffer data, uint8_t content_type)
{
__block int err;
tls_client_ctx_t *cc = (tls_client_ctx_t *)ctx;
err = encrypt_and_write(cc, data, content_type);
return err;
}
static uint8_t alpn_http_1_1[] = {0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31};
__unused static tls_buffer alpnData = {
.data = alpn_http_1_1,
.length = sizeof(alpn_http_1_1),
};
static uint8_t npn_http_1_1[] = {0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31};
static tls_buffer npnHttpData = {
.data = npn_http_1_1,
.length = sizeof(npn_http_1_1),
};
static int
tls_handshake_message_callback(tls_handshake_ctx_t ctx, tls_handshake_message_t event)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ctx;
int err = 0;
session_log("event = %d\n", event);
switch(event) {
case tls_handshake_message_client_hello:
break;
case tls_handshake_message_certificate:
require_noerr((err = tls_helper_set_peer_pubkey(cc->hdsk)), errOut);
break;
case tls_handshake_message_certificate_request:
cc->certificate_requested++;
require_noerr((err = tls_handshake_set_client_auth_type(cc->hdsk, tls_client_auth_type_RSASign)), errOut);
require_noerr((err = tls_handshake_set_identity(cc->hdsk, &cc->params->certs, cc->params->key)), errOut);
break;
case tls_handshake_message_server_hello_done:
require_noerr((err = tls_evaluate_trust(cc->hdsk, cc->trustRef)), errOut);
break;
case tls_handshake_message_server_hello:
{
const tls_buffer *data;
data = tls_handshake_get_peer_npn_data(cc->hdsk);
if(data) {
session_log("NPN Data = %p, %zd\n", data->data, data->length);
require_noerr((err=tls_handshake_set_npn_data(cc->hdsk, npnHttpData)), errOut);
}
data = tls_handshake_get_peer_alpn_data(cc->hdsk);
if(data) {
session_log("ALPN Data = %p, %zd\n", data->data, data->length);
}
}
break;
default:
break;
}
errOut:
return err;
}
static void
tls_handshake_ready_callback(tls_handshake_ctx_t ctx, bool write, bool ready)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ctx;
session_log("%s ready=%d\n", write?"write":"read", ready);
if(ready) {
if(write) {
cc->write_ready_received++;
} else {
cc->read_ready_received++;
}
}
}
static int
tls_handshake_set_retransmit_timer_callback(tls_handshake_ctx_t ctx, int attempt)
{
tls_client_ctx_t DEBUG_ONLY *cc = (tls_client_ctx_t *)ctx;
session_log("attempt=%d\n", attempt);
return ENOTSUP;
}
static
int mySSLRecordInitPendingCiphersFunc(tls_handshake_ctx_t ref,
uint16_t selectedCipher,
bool server,
tls_buffer key)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ref;
session_log("%s: %s, cipher=%04x, server=%d\n", __FUNCTION__, ref, selectedCipher, server);
return tls_record_init_pending_ciphers(cc->rec, selectedCipher, server, key);
}
static
int mySSLRecordAdvanceWriteCipherFunc(tls_handshake_ctx_t ref)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ref;
session_log("\n");
return tls_record_advance_write_cipher(cc->rec);
}
static
int mySSLRecordRollbackWriteCipherFunc(tls_handshake_ctx_t ref)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ref;
session_log("\n");
return tls_record_rollback_write_cipher(cc->rec);
}
static
int mySSLRecordAdvanceReadCipherFunc(tls_handshake_ctx_t ref)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ref;
session_log("\n");
return tls_record_advance_read_cipher(cc->rec);
}
static
int mySSLRecordSetProtocolVersionFunc(tls_handshake_ctx_t ref,
tls_protocol_version protocolVersion)
{
tls_client_ctx_t *cc = (tls_client_ctx_t *)ref;
session_log("pv=%04x\n", protocolVersion);
return tls_record_set_protocol_version(cc->rec, protocolVersion);
}
static tls_cache_t g_cache = NULL;
static int
tls_handshake_save_session_data_callback(tls_handshake_ctx_t ctx, tls_buffer sessionKey, tls_buffer sessionData)
{
tls_client_ctx_t DEBUG_ONLY *cc = (tls_client_ctx_t *)ctx;
session_log("key = %s data=[%p,%zd]\n", sessionKey.data, sessionData.data, sessionData.length);
return tls_cache_save_session_data(g_cache, &sessionKey, &sessionData, 0);
}
static int
tls_handshake_load_session_data_callback(tls_handshake_ctx_t ctx, tls_buffer sessionKey, tls_buffer *sessionData)
{
tls_client_ctx_t DEBUG_ONLY *cc = (tls_client_ctx_t *)ctx;
int err = tls_cache_load_session_data(g_cache, &sessionKey, sessionData);
session_log("key = %s data=[%p,%zd], err=%d\n", sessionKey.data, sessionData->data, sessionData->length, err);
return err;
}
static int
tls_handshake_delete_session_data_callback(tls_handshake_ctx_t ctx, tls_buffer sessionKey)
{
tls_client_ctx_t DEBUG_ONLY *cc = (tls_client_ctx_t *)ctx;
session_log("\n");
return tls_cache_delete_session_data(g_cache, &sessionKey);
}
static int
tls_handshake_delete_all_sessions_callback(tls_handshake_ctx_t ctx)
{
tls_client_ctx_t DEBUG_ONLY *cc = (tls_client_ctx_t *)ctx;
session_log("\n");
return -1;
}
tls_handshake_callbacks_t tls_handshake_callbacks = {
.write = tls_handshake_write_callback,
.message = tls_handshake_message_callback,
.ready = tls_handshake_ready_callback,
.set_retransmit_timer = tls_handshake_set_retransmit_timer_callback,
.init_pending_cipher = mySSLRecordInitPendingCiphersFunc,
.advance_write_cipher = mySSLRecordAdvanceWriteCipherFunc,
.rollback_write_cipher = mySSLRecordRollbackWriteCipherFunc,
.advance_read_cipher = mySSLRecordAdvanceReadCipherFunc,
.set_protocol_version = mySSLRecordSetProtocolVersionFunc,
.load_session_data = tls_handshake_load_session_data_callback,
.save_session_data = tls_handshake_save_session_data_callback,
.delete_session_data = tls_handshake_delete_session_data_callback,
.delete_all_sessions = tls_handshake_delete_all_sessions_callback,
};
static
int tls_stream_parser_process(tls_stream_parser_ctx_t ctx, tls_buffer record)
{
int err = errSecAllocate;
tls_client_ctx_t *cc = (tls_client_ctx_t *)ctx;
tls_buffer out;
uint8_t content_type;
session_log("len = %zu\n", record.length);
size_t dlen = tls_record_decrypted_size(cc->rec, record.length);
mySSLAlloc(&out, dlen+1); require(out.data, fail);
require_noerr((err=tls_record_decrypt(cc->rec, record, &out, &content_type)), fail);
if(content_type!=tls_record_type_AppData) {
session_log("processing protocol message of type %d, len=%zu\n", content_type, out.length);
require_noerr_quiet((err = tls_handshake_process(cc->hdsk, out, content_type)), fail);
} else {
if(cc->read_ready_received<0)
session_log("Received data before read_ready\n");
session_log("received data record, len = %zu\n", out.length);
out.data[out.length]=0;
printf("*** DATA ***\n%s\n*** END DATA ***\n", out.data);
}
fail:
mySSLFree(&out);
cc->err = err; return err;
}
typedef struct _CipherSuiteName {
uint16_t cipher;
const char *name;
} CipherSuiteName;
#define CIPHER(cipher) {cipher, #cipher}
const CipherSuiteName ciphers[] = {
#if 1
CIPHER(SSL_RSA_WITH_NULL_MD5),
CIPHER(SSL_RSA_WITH_NULL_SHA),
CIPHER(TLS_RSA_WITH_NULL_SHA256),
#endif
#if 1
CIPHER(SSL_RSA_WITH_RC4_128_MD5),
CIPHER(SSL_RSA_WITH_RC4_128_SHA),
CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA),
CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA),
CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA256),
#endif
#if 1
CIPHER(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA),
CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA),
CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256),
#endif
#if 1
CIPHER(SSL_DH_anon_WITH_RC4_128_MD5),
CIPHER(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA),
CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA),
CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA256),
#endif
#if 1
CIPHER(TLS_ECDHE_ECDSA_WITH_NULL_SHA),
CIPHER(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA),
CIPHER(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384),
#endif
#if 1
CIPHER(TLS_ECDHE_RSA_WITH_NULL_SHA),
CIPHER(TLS_ECDHE_RSA_WITH_RC4_128_SHA),
CIPHER(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA),
CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA),
CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384), #endif
#if 1
CIPHER(TLS_PSK_WITH_RC4_128_SHA),
CIPHER(TLS_PSK_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA),
CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA),
CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA256),
CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA384),
CIPHER(TLS_PSK_WITH_NULL_SHA),
CIPHER(TLS_PSK_WITH_NULL_SHA256),
CIPHER(TLS_PSK_WITH_NULL_SHA384),
#endif
#if 1
CIPHER(TLS_RSA_WITH_AES_128_GCM_SHA256),
CIPHER(TLS_RSA_WITH_AES_256_GCM_SHA384),
CIPHER(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256),
CIPHER(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384),
CIPHER(TLS_DH_anon_WITH_AES_128_GCM_SHA256),
CIPHER(TLS_DH_anon_WITH_AES_256_GCM_SHA384),
CIPHER(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
CIPHER(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
#endif
CIPHER(TLS_ECDH_anon_WITH_NULL_SHA),
CIPHER(TLS_ECDH_anon_WITH_RC4_128_SHA),
CIPHER(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA),
CIPHER(TLS_ECDH_anon_WITH_AES_128_CBC_SHA),
CIPHER(TLS_ECDH_anon_WITH_AES_256_CBC_SHA),
{ 0, NULL }
};
static uint16_t sslcipher_atoi(char* cipherstring){
const CipherSuiteName *a = ciphers;
while (a->cipher > 0) {
if (strcmp(cipherstring, a->name) == 0) break;
a++;
}
return a->cipher;
}
static const char *sslcipher_itoa(uint16_t cs)
{
const CipherSuiteName *a = ciphers;
while(a->cipher > 0) {
if (cs == a->cipher) break;
a++;
}
return a->name;
}
static
int init_context(tls_client_ctx_t *cc, tls_client_params *params)
{
int err = errSecAllocate;
memset(cc, 0, sizeof(tls_client_ctx_t));
struct ccrng_state *rng = CCRNGSTATE();
if (!rng) {
abort();
}
cc->params = params;
require((cc->sock = SocketConnect(params->hostname, params->service, params->dtls))>=0, fail);
require((cc->rec = tls_record_create(params->dtls, rng)), fail);
require((cc->hdsk = tls_handshake_create(params->dtls, false)), fail);
require((cc->parser = tls_stream_parser_create(cc, tls_stream_parser_process)), fail);
require_noerr((err=tls_handshake_set_callbacks(cc->hdsk,
&tls_handshake_callbacks,
cc)),
fail);
require_noerr((err=tls_handshake_set_false_start(cc->hdsk, true)), fail);
require_noerr((err=tls_handshake_set_peer_hostname(cc->hdsk, params->hostname, strlen(params->hostname))), fail);
require_noerr((err=tls_handshake_set_npn_enable(cc->hdsk, true)), fail);
if(params->alpn_string) {
tls_buffer alpnData;
alpnData.length = strlen(params->alpn_string)+1;
alpnData.data = malloc(alpnData.length);
alpnData.data[0] = alpnData.length-1;
memcpy(alpnData.data+1, params->alpn_string, alpnData.length-1);
require_noerr((err=tls_handshake_set_alpn_data(cc->hdsk, alpnData)), fail);
}
if(params->config)
require_noerr((err=tls_handshake_set_config(cc->hdsk, atoi(params->config))), fail);
if(params->num_ciphersuites)
require_noerr((err=tls_handshake_set_ciphersuites(cc->hdsk, params->ciphersuites, params->num_ciphersuites)), fail);
if(params->protocol_min)
require_noerr((err=tls_handshake_set_min_protocol_version(cc->hdsk, params->protocol_min)), fail);
if(params->protocol_max)
require_noerr((err=tls_handshake_set_max_protocol_version(cc->hdsk, params->protocol_max)), fail);
require_noerr((err=tls_handshake_set_resumption(cc->hdsk,params->allow_resumption)), fail);
require_noerr((err=tls_handshake_set_session_ticket_enabled(cc->hdsk, params->session_tickets_enabled)), fail);
require_noerr((err=tls_handshake_set_ocsp_enable(cc->hdsk, params->ocsp_enabled)), fail);
require_noerr((err=tls_handshake_set_min_dh_group_size(cc->hdsk, params->min_dh_size)), fail);
require_noerr((err=tls_handshake_set_fallback(cc->hdsk, params->fallback)), fail);
require_noerr((err=tls_handshake_set_ems_enable(cc->hdsk, params->allow_ext_master_secret)), fail);
fail:
return err;
}
static
void clean_context(tls_client_ctx_t *cc)
{
if(cc->hdsk) tls_handshake_destroy(cc->hdsk);
if(cc->rec) tls_record_destroy(cc->rec);
if(cc->parser) tls_stream_parser_destroy(cc->parser);
if(cc->sock>=0) close(cc->sock);
}
static int session_status(tls_client_ctx_t *cc)
{
int err = 0;
printf("read_ready received: %d\n",cc->read_ready_received);
printf("write_ready received: %d\n",cc->write_ready_received);
printf("certificate requested: %d\n", cc->certificate_requested);
printf("negotiated ciphersuite: %04x\n", tls_handshake_get_negotiated_cipherspec(cc->hdsk));
return err;
}
#define MAX_READ 16384
static unsigned char databuffer[MAX_READ];
static
int read_and_process_socket(tls_client_ctx_t *cc)
{
ssize_t nr;
tls_buffer readbuffer;
nr = recv(cc->sock, databuffer, MAX_READ, 0);
if(nr==0) return -1; if(nr<0) {
perror("recv");
return errno;
}
readbuffer.data = databuffer;
readbuffer.length = nr;
printf("recvd %zd bytes, parse it\n", nr);
if(cc->params->dtls) {
return tls_stream_parser_process(cc, readbuffer);
} else {
return tls_stream_parser_parse(cc->parser, readbuffer);
}
}
static
int read_and_process_stdin(tls_client_ctx_t *cc)
{
ssize_t nr;
tls_buffer readbuffer;
nr = read(STDIN_FILENO, databuffer, MAX_READ);
if(nr==0) return -1; if(nr<=0) return errno;
if(databuffer[0]=='R') { return tls_handshake_negotiate(cc->hdsk, NULL);
} else if(databuffer[0]=='Q') {
return -2; } else {
readbuffer.data = databuffer;
readbuffer.length = nr;
printf("input %zd bytes, send it\n", nr);
return tls_handshake_callbacks.write(cc, readbuffer, tls_record_type_AppData);
}
}
static int client_connect(tls_client_params *params)
{
int err;
tls_client_ctx_t client;
tls_client_ctx_t *cc = &client;
require_noerr((err=init_context(cc, params)), errOut);
tls_buffer peerID = { sizeof(params->peer_id), (uint8_t *)¶ms->peer_id};
unsigned enabled_ciphers_count = 0;
const uint16_t *enabled_ciphers = NULL;
tls_handshake_get_ciphersuites(cc->hdsk, &enabled_ciphers, &enabled_ciphers_count);
session_log("Enabled %d ciphersuites:\n", enabled_ciphers_count);
for(unsigned i=0; i<enabled_ciphers_count; i++) {
session_log("%02d: (%04x) %s\n", i, enabled_ciphers[i], sslcipher_itoa(enabled_ciphers[i]));
}
if(params->peer_id) {
require_noerr((err=tls_handshake_negotiate(cc->hdsk, &peerID)), errOut);
} else {
require_noerr((err=tls_handshake_negotiate(cc->hdsk, NULL)), errOut);
}
while(cc->write_ready_received==0 || cc->read_ready_received==0) {
require_noerr((err=read_and_process_socket(cc)), errOut);
}
session_status(cc);
printf("Sending request: %s\n", cc->params->request);
tls_buffer request = {
.data = (uint8_t *)cc->params->request,
.length = strlen(cc->params->request),
};
tls_handshake_callbacks.write(cc, request, tls_record_type_AppData);
while(true){
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO,&read_fds);
FD_SET(cc->sock,&read_fds);
if (select(cc->sock+1,&read_fds,NULL,NULL,NULL) == -1){
perror("select:");
goto errClose;
}
if (FD_ISSET(cc->sock, &read_fds)){
session_log("Data on socket\n");
if((err=read_and_process_socket(cc))) {
session_log("Error while reading socket : %d\n", err);
goto errClose;
}
}
if (FD_ISSET(STDIN_FILENO, &read_fds)){
printf("Data on stdin\n");
if((err=read_and_process_stdin(cc))) {
if(err==-2) {
session_log("Quitting\n");
goto errClose;
}
session_log("Error while reading stdin : %d\n", err);
if(err!=-1) perror("stdin");
}
}
}
errClose:
tls_handshake_close(cc->hdsk);
errOut:
clean_context(cc);
return err;
}
static void
logger(void * __unused ctx, const char *scope, const char *function, const char *str)
{
printf("[%s] %s: %s\n", scope, function, str);
}
#define HOSTNAME "www.google.com"
int main(int argc, const char * argv[])
{
int reconnects = 0;
int reconnect_delay = 0;
uint16_t cipher_to_use;
tls_client_params params;
int err;
static char get_request[100];
bool resume_with_higher_version = false;
memset(¶ms, 0, sizeof(params));
params.hostname=HOSTNAME;
params.service="https";
params.request = get_request;
params.ocsp_enabled = true;
params.session_tickets_enabled = true;
params.allow_resumption = true;
params.allow_ext_master_secret = true;
params.peer_id = 'P';
if (argc > 1) {
int i = 0;
for (i = 0 ; i < argc ; i+=1){
if (strcmp(argv[i], "--host") == 0 && argv[i+1] != NULL){
params.hostname = argv[++i];
}
if (strcmp(argv[i], "--port") == 0 && argv[i+1] != NULL){
params.service = argv[++i];
}
if (strcmp(argv[i], "--config") == 0 && argv[i+1] != NULL){
params.config = argv[++i];
}
if (strcmp(argv[i], "--alpn") == 0 && argv[i+1] != NULL){
params.alpn_string = argv[++i];
}
if (strcmp(argv[i], "--cipher") == 0 && argv[i+1] != NULL){
cipher_to_use = sslcipher_atoi((char*)argv[++i]);
params.ciphersuites = &(cipher_to_use);
params.num_ciphersuites = 1;
}
if ((strcmp(argv[i], "--protocol_min") == 0) && (argv[i+1] != NULL)){
params.protocol_min = (tls_protocol_version)strtoul(argv[++i], NULL, 0);
}
if ((strcmp(argv[i], "--protocol_max") == 0) && (argv[i+1] != NULL)){
params.protocol_max = (tls_protocol_version)strtoul(argv[++i], NULL, 0);
}
if (strcmp(argv[i], "--resume_with_higher_version") == 0) {
reconnects = 1;
resume_with_higher_version = true;
params.protocol_max = tls_protocol_version_TLS_1_0;
}
if (strcmp(argv[i], "--reconnect") == 0 && argv[i+1] != NULL){
reconnects = atoi(argv[++i]);
}
if (strcmp(argv[i], "--reconnect_delay") == 0 && argv[i+1] != NULL){
reconnect_delay = atoi(argv[++i]);
}
if (strcmp(argv[i], "--no-resumption") == 0){
params.allow_resumption = false;
}
if (strcmp(argv[i], "--no-tickets") == 0){
params.session_tickets_enabled = false;
}
if (strcmp(argv[i], "--no-ocsp") == 0){
params.ocsp_enabled = false;
}
if (strcmp(argv[i], "--min-dh-size") == 0 && argv[i+1] != NULL){
params.min_dh_size = atoi(argv[++i]);;
}
if (strcmp(argv[i], "--dtls") == 0){
params.dtls = true;
}
if (strcmp(argv[i], "--fallback") == 0){
params.fallback = true;
}
if (strcmp(argv[i], "--no-extended-ms") == 0){
params.allow_ext_master_secret = false;
}
if (strcmp(argv[i], "--debug") == 0){
tls_add_debug_logger(logger, NULL);
}
}
}
memset(get_request, 0, sizeof(get_request));
snprintf(get_request, sizeof(get_request), "GET / HTTP/1.1\r\nHost: %s:%s\r\nConnection: close\r\n\r\n", params.hostname, params.service);
g_cache = tls_cache_create();
do {
client_log("***** Connection to %s:%s (reconnects=%d) starting\n", params.hostname, params.service, reconnects);
err = client_connect(¶ms);
client_log("***** Connection to %s:%s (reconnects=%d) ended with err = %d\n\n", params.hostname, params.service, reconnects, err);
if(resume_with_higher_version) {
params.protocol_max = tls_protocol_version_TLS_1_2;
}
if(reconnects)
sleep(reconnect_delay);
} while(reconnects--);
tls_cache_destroy(g_cache);
}