Skip to content

Commit 47c70f8

Browse files
committed
add IPv6 support and TLS SNI
1 parent 7a92e0d commit 47c70f8

File tree

3 files changed

+126
-34
lines changed

3 files changed

+126
-34
lines changed

common.h

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
Copyright (c) 2017 Darren Smith
2+
Copyright (c) 2017 Darren Smith
33
4-
ssl_examples is free software; you can redistribute it and/or modify
5-
it under the terms of the MIT license. See LICENSE for details.
4+
ssl_examples is free software; you can redistribute it and/or modify
5+
it under the terms of the MIT license. See LICENSE for details.
66
*/
77

88
#include <openssl/bio.h>
@@ -63,6 +63,9 @@ struct ssl_client
6363
char* encrypt_buf;
6464
size_t encrypt_len;
6565

66+
/* Store the previous state string */
67+
const char * last_state;
68+
6669
/* Method to invoke when unencrypted bytes are available. */
6770
void (*io_on_read)(char *buf, size_t len);
6871
} client;
@@ -71,6 +74,7 @@ struct ssl_client
7174
* handshake. */
7275
enum ssl_mode { SSLMODE_SERVER, SSLMODE_CLIENT };
7376

77+
7478
void ssl_client_init(struct ssl_client *p,
7579
int fd,
7680
enum ssl_mode mode)
@@ -93,21 +97,25 @@ void ssl_client_init(struct ssl_client *p,
9397
p->io_on_read = print_unencrypted_data;
9498
}
9599

100+
96101
void ssl_client_cleanup(struct ssl_client *p)
97102
{
98103
SSL_free(p->ssl); /* free the SSL object and its BIO's */
99104
free(p->write_buf);
100105
free(p->encrypt_buf);
101106
}
102107

108+
103109
int ssl_client_want_write(struct ssl_client *cp) {
104110
return (cp->write_len>0);
105111
}
106112

113+
107114
/* Obtain the return value of an SSL operation and convert into a simplified
108115
* error code, which is easier to examine for failure. */
109116
enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL};
110117

118+
111119
static enum sslstatus get_sslstatus(SSL* ssl, int n)
112120
{
113121
switch (SSL_get_error(ssl, n))
@@ -124,6 +132,7 @@ static enum sslstatus get_sslstatus(SSL* ssl, int n)
124132
}
125133
}
126134

135+
127136
/* Handle request to send unencrypted data to the SSL. All we do here is just
128137
* queue the data into the encrypt_buf for later processing by the SSL
129138
* object. */
@@ -134,6 +143,7 @@ void send_unencrypted_bytes(const char *buf, size_t len)
134143
client.encrypt_len += len;
135144
}
136145

146+
137147
/* Queue encrypted bytes. Should only be used when the SSL object has requested a
138148
* write operation. */
139149
void queue_encrypted_bytes(const char *buf, size_t len)
@@ -143,12 +153,38 @@ void queue_encrypted_bytes(const char *buf, size_t len)
143153
client.write_len += len;
144154
}
145155

156+
157+
void print_ssl_state()
158+
{
159+
const char * current_state = SSL_state_string_long(client.ssl);
160+
if (current_state != client.last_state) {
161+
if (current_state)
162+
printf("SSL-STATE: %s\n", current_state);
163+
client.last_state = current_state;
164+
}
165+
}
166+
167+
168+
void print_ssl_error()
169+
{
170+
BIO *bio = BIO_new(BIO_s_mem());
171+
ERR_print_errors(bio);
172+
char *buf;
173+
size_t len = BIO_get_mem_data(bio, &buf);
174+
if (len > 0)
175+
printf("SSL-ERROR: %s", buf);
176+
BIO_free(bio);
177+
}
178+
179+
146180
enum sslstatus do_ssl_handshake()
147181
{
148182
char buf[DEFAULT_BUF_SIZE];
149183
enum sslstatus status;
150184

185+
print_ssl_state();
151186
int n = SSL_do_handshake(client.ssl);
187+
print_ssl_state();
152188
status = get_sslstatus(client.ssl, n);
153189

154190
/* Did SSL request to write bytes? */
@@ -259,6 +295,7 @@ int do_encrypt()
259295
return 0;
260296
}
261297

298+
262299
/* Read bytes from stdin and queue for later encryption. */
263300
void do_stdin_read()
264301
{
@@ -268,6 +305,7 @@ void do_stdin_read()
268305
send_unencrypted_bytes(buf, (size_t)n);
269306
}
270307

308+
271309
/* Read encrypted bytes from socket. */
272310
int do_sock_read()
273311
{
@@ -280,6 +318,7 @@ int do_sock_read()
280318
return -1;
281319
}
282320

321+
283322
/* Write encrypted bytes to the socket. */
284323
int do_sock_write()
285324
{
@@ -295,19 +334,21 @@ int do_sock_write()
295334
return -1;
296335
}
297336

337+
298338
void ssl_init(const char * certfile, const char* keyfile)
299339
{
300-
printf("initialising SSL\n");
301-
302340
/* SSL library initialisation */
341+
303342
SSL_library_init();
304343
OpenSSL_add_all_algorithms();
305344
SSL_load_error_strings();
306-
ERR_load_BIO_strings();
345+
#if OPENSSL_VERSION_MAJOR < 3
346+
ERR_load_BIO_strings(); // deprecated since OpenSSL 3.0
347+
#endif
307348
ERR_load_crypto_strings();
308349

309350
/* create the SSL server context */
310-
ctx = SSL_CTX_new(SSLv23_method());
351+
ctx = SSL_CTX_new(TLS_method());
311352
if (!ctx)
312353
die("SSL_CTX_new()");
313354

@@ -326,6 +367,7 @@ void ssl_init(const char * certfile, const char* keyfile)
326367
printf("certificate and private key loaded and verified\n");
327368
}
328369

370+
329371
/* Recommended to avoid SSLv2 & SSLv3 */
330372
SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
331373
}

makefile

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
#
22
# Copyright (c) 2017 Darren Smith
33
#
4-
# wampcc is free software; you can redistribute it and/or modify
5-
# it under the terms of the MIT license. See LICENSE for details.
4+
# ssl_examples is free software; you can redistribute it and/or modify it under
5+
# the terms of the MIT license. See LICENSE for details.
66
#
77

88

9-
CFLAGS += -MMD -MP -Wall -O0 -g3 -ggdb
9+
CFLAGS += -MMD -MP -Wall -Wextra -O0 -g3 -ggdb
1010

1111
LDLIBS += -lcrypto -lssl
1212

13-
# default: callee
14-
1513
all_srcs := $(shell find . -name \*.c)
16-
app_srcs := $(shell find . -name \*.c)
14+
1715

1816
all : apps
19-
apps : $(app_srcs:%.c=%)
17+
apps : $(all_srcs:%.c=%)
2018
test : obj/test/main ; @$<
21-
clean : ; rm -f $(app_srcs:%.c=%) $(app_srcs:%.c=%.d)
19+
clean : ; rm -f $(all_srcs:%.c=%) $(all_srcs:%.c=%.d)
2220

23-
#-include $(all_srcs:%.cc=%.d)
2421
.PRECIOUS : %.o
2522

26-
%.o : %.cc ; $(COMPILE.cpp) $(OUTPUT_OPTION) $<
27-
%.o : %.c ; $(COMPILE.c) $(OUTPUT_OPTION) $<
28-
% : %.o ; @$(LINK.cpp) $(OUTPUT_OPTION) $^ $(LDLIBS)
23+
ssl_server_nonblock: ssl_server_nonblock.c common.h
24+
$(CC) $(CFLAGS) ssl_server_nonblock.c $(OUTPUT_OPTION) $(LDLIBS)
25+
26+
ssl_client_nonblock: ssl_client_nonblock.c common.h
27+
$(CC) $(CFLAGS) ssl_client_nonblock.c $(OUTPUT_OPTION) $(LDLIBS)
28+
29+
#-include $(all_srcs:%.c=%.d)

ssl_client_nonblock.c

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,74 @@
11
/*
2-
Copyright (c) 2017 Darren Smith
2+
Copyright (c) 2017 Darren Smith
33
4-
ssl_examples is free software; you can redistribute it and/or modify
5-
it under the terms of the MIT license. See LICENSE for details.
4+
ssl_examples is free software; you can redistribute it and/or modify
5+
it under the terms of the MIT license. See LICENSE for details.
66
*/
77

88
#include "common.h"
99

1010
int main(int argc, char **argv)
1111
{
12-
int port = argc>1? atoi(argv[1]):55555;
13-
char* host="127.0.0.1";
12+
/* --- CONFIGURE PEER SOCKET --- */
13+
14+
// port name, optionally take from args
15+
int port = argc>1? atoi(argv[1]):443;
16+
17+
// host IP address. Attention! This must be a numeric address, not a server
18+
// host name, because this example code does not perform address lookup.
19+
char* host_ip = "2600:9000:225d:600:14:c251:2440:93a1";
20+
21+
// provide the hostname if this SSL client needs to use SNI to tell the server
22+
// what certificate to use
23+
const char * host_name = "api.huobi.pro";
24+
25+
// socket family, AF_INET (ipv4) or AF_INET6 (ipv6), must match host_ip above
26+
int ip_family = AF_INET6;
27+
28+
/* Example for localhost connection
29+
int port = argc>1? atoi(argv[1]):55555;
30+
const char* host_ip = "127.0.0.1";
31+
const char * host_name = NULL;
32+
int ip_family = AF_INET;
33+
*/
34+
35+
36+
/* --- CONFIGURAITON ENDS --- */
37+
38+
int sockfd = socket(ip_family, SOCK_STREAM, 0);
1439

15-
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
1640
if (sockfd < 0)
1741
die("socket()");
1842

1943
/* Specify socket address */
20-
struct sockaddr_in addr;
21-
memset(&addr, 0, sizeof(addr));
22-
addr.sin_family = AF_INET;
23-
addr.sin_port = htons(port);
24-
if (inet_pton(AF_INET, host, &(addr.sin_addr)) <= 0)
25-
die("inet_pton()");
2644

27-
if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
28-
die("connect()");
45+
if (ip_family == AF_INET6) {
46+
struct sockaddr_in6 addr;
47+
memset(&addr, 0, sizeof(addr));
48+
addr.sin6_family = ip_family;
49+
addr.sin6_port = htons(port);
50+
51+
if (inet_pton(ip_family, host_ip, &(addr.sin6_addr)) <= 0)
52+
die("inet_pton()");
53+
54+
if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
55+
die("connect()");
56+
}
57+
58+
if (ip_family == AF_INET) {
59+
struct sockaddr_in addr;
60+
memset(&addr, 0, sizeof(addr));
61+
addr.sin_family = ip_family;
62+
addr.sin_port = htons(port);
63+
64+
if (inet_pton(ip_family, host_ip, &(addr.sin_addr)) <= 0)
65+
die("inet_pton()");
66+
67+
if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
68+
die("connect()");
69+
}
70+
71+
printf("socket connected\n");
2972

3073
struct pollfd fdset[2];
3174
memset(&fdset, 0, sizeof(fdset));
@@ -36,6 +79,9 @@ int main(int argc, char **argv)
3679
ssl_init(0,0);
3780
ssl_client_init(&client, sockfd, SSLMODE_CLIENT);
3881

82+
if (host_name)
83+
SSL_set_tlsext_host_name(client.ssl, host_name); // TLS SNI
84+
3985
fdset[1].fd = sockfd;
4086
fdset[1].events = POLLERR | POLLHUP | POLLNVAL | POLLIN;
4187
#ifdef POLLRDHUP
@@ -71,10 +117,13 @@ int main(int argc, char **argv)
71117
if (fdset[0].revents & POLLIN)
72118
do_stdin_read();
73119
if (client.encrypt_len>0)
74-
do_encrypt();
120+
if (do_encrypt() < 0)
121+
break;
75122
}
76123

77124
close(fdset[1].fd);
125+
print_ssl_state();
126+
print_ssl_error();
78127
ssl_client_cleanup(&client);
79128

80129
return 0;

0 commit comments

Comments
 (0)