1 | /*
|
---|
2 | * Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use
|
---|
5 | * this file except in compliance with the License. You can obtain a copy
|
---|
6 | * in the file LICENSE in the source distribution or at
|
---|
7 | * https://www.openssl.org/source/license.html
|
---|
8 | */
|
---|
9 | #include <stdio.h>
|
---|
10 | #include <openssl/ssl.h>
|
---|
11 | #include <openssl/quic.h>
|
---|
12 | #include <openssl/bio.h>
|
---|
13 | #include "internal/common.h"
|
---|
14 | #include "internal/sockets.h"
|
---|
15 | #include "internal/time.h"
|
---|
16 | #include "testutil.h"
|
---|
17 |
|
---|
18 | static const char msg1[] = "GET LICENSE.txt\r\n";
|
---|
19 | static char msg2[16000];
|
---|
20 |
|
---|
21 | #define DST_PORT 4433
|
---|
22 | #define DST_ADDR 0x7f000001UL
|
---|
23 |
|
---|
24 | static int is_want(SSL *s, int ret)
|
---|
25 | {
|
---|
26 | int ec = SSL_get_error(s, ret);
|
---|
27 |
|
---|
28 | return ec == SSL_ERROR_WANT_READ || ec == SSL_ERROR_WANT_WRITE;
|
---|
29 | }
|
---|
30 |
|
---|
31 | static int test_quic_client_ex(int fd_arg)
|
---|
32 | {
|
---|
33 | int testresult = 0, ret;
|
---|
34 | int c_fd;
|
---|
35 | BIO *c_net_bio = NULL, *c_net_bio_own = NULL;
|
---|
36 | BIO_ADDR *s_addr_ = NULL;
|
---|
37 | struct in_addr ina = {0};
|
---|
38 | SSL_CTX *c_ctx = NULL;
|
---|
39 | SSL *c_ssl = NULL;
|
---|
40 | short port = DST_PORT;
|
---|
41 | int c_connected = 0, c_write_done = 0, c_shutdown = 0;
|
---|
42 | size_t l = 0, c_total_read = 0;
|
---|
43 | OSSL_TIME start_time;
|
---|
44 | unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '0', '.', '9' };
|
---|
45 |
|
---|
46 |
|
---|
47 | if (fd_arg == INVALID_SOCKET) {
|
---|
48 | /* Setup test client. */
|
---|
49 | c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0);
|
---|
50 | if (!TEST_int_ne(c_fd, INVALID_SOCKET))
|
---|
51 | goto err;
|
---|
52 |
|
---|
53 | if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
|
---|
54 | goto err;
|
---|
55 |
|
---|
56 | if (!TEST_ptr(s_addr_ = BIO_ADDR_new()))
|
---|
57 | goto err;
|
---|
58 |
|
---|
59 | ina.s_addr = htonl(DST_ADDR);
|
---|
60 | if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina),
|
---|
61 | htons(port))))
|
---|
62 | goto err;
|
---|
63 | } else {
|
---|
64 | c_fd = fd_arg;
|
---|
65 | }
|
---|
66 |
|
---|
67 | if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0)))
|
---|
68 | goto err;
|
---|
69 |
|
---|
70 | /* connected socket does not need to set peer */
|
---|
71 | if (s_addr_ != NULL && !BIO_dgram_set_peer(c_net_bio, s_addr_))
|
---|
72 | goto err;
|
---|
73 |
|
---|
74 | if (!TEST_ptr(c_ctx = SSL_CTX_new(OSSL_QUIC_client_method())))
|
---|
75 | goto err;
|
---|
76 |
|
---|
77 | if (!TEST_ptr(c_ssl = SSL_new(c_ctx)))
|
---|
78 | goto err;
|
---|
79 |
|
---|
80 | /* 0 is a success for SSL_set_alpn_protos() */
|
---|
81 | if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn))))
|
---|
82 | goto err;
|
---|
83 |
|
---|
84 | /* Takes ownership of our reference to the BIO. */
|
---|
85 | SSL_set0_rbio(c_ssl, c_net_bio);
|
---|
86 |
|
---|
87 | /* Get another reference to be transferred in the SSL_set0_wbio call. */
|
---|
88 | if (!TEST_true(BIO_up_ref(c_net_bio))) {
|
---|
89 | c_net_bio_own = NULL; /* SSL_free will free the first reference. */
|
---|
90 | goto err;
|
---|
91 | }
|
---|
92 |
|
---|
93 | SSL_set0_wbio(c_ssl, c_net_bio);
|
---|
94 | c_net_bio_own = NULL;
|
---|
95 |
|
---|
96 | if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0)))
|
---|
97 | goto err;
|
---|
98 |
|
---|
99 | start_time = ossl_time_now();
|
---|
100 |
|
---|
101 | for (;;) {
|
---|
102 | if (ossl_time_compare(ossl_time_subtract(ossl_time_now(), start_time),
|
---|
103 | ossl_ms2time(10000)) >= 0) {
|
---|
104 | TEST_error("timeout while attempting QUIC client test");
|
---|
105 | goto err;
|
---|
106 | }
|
---|
107 |
|
---|
108 | if (!c_connected) {
|
---|
109 | ret = SSL_connect(c_ssl);
|
---|
110 | if (!TEST_true(ret == 1 || is_want(c_ssl, ret)))
|
---|
111 | goto err;
|
---|
112 |
|
---|
113 | if (ret == 1) {
|
---|
114 | c_connected = 1;
|
---|
115 | TEST_info("Connected!");
|
---|
116 | }
|
---|
117 | }
|
---|
118 |
|
---|
119 | if (c_connected && !c_write_done) {
|
---|
120 | if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1),
|
---|
121 | (int)sizeof(msg1) - 1))
|
---|
122 | goto err;
|
---|
123 |
|
---|
124 | if (!TEST_true(SSL_stream_conclude(c_ssl, 0)))
|
---|
125 | goto err;
|
---|
126 |
|
---|
127 | c_write_done = 1;
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (c_write_done && !c_shutdown && c_total_read < sizeof(msg2) - 1) {
|
---|
131 | ret = SSL_read_ex(c_ssl, msg2 + c_total_read,
|
---|
132 | sizeof(msg2) - 1 - c_total_read, &l);
|
---|
133 | if (ret != 1) {
|
---|
134 | if (SSL_get_error(c_ssl, ret) == SSL_ERROR_ZERO_RETURN) {
|
---|
135 | c_shutdown = 1;
|
---|
136 | TEST_info("Message: \n%s\n", msg2);
|
---|
137 | } else if (!TEST_true(is_want(c_ssl, ret))) {
|
---|
138 | goto err;
|
---|
139 | }
|
---|
140 | } else {
|
---|
141 | c_total_read += l;
|
---|
142 |
|
---|
143 | if (!TEST_size_t_lt(c_total_read, sizeof(msg2) - 1))
|
---|
144 | goto err;
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | if (c_shutdown) {
|
---|
149 | ret = SSL_shutdown(c_ssl);
|
---|
150 | if (ret == 1)
|
---|
151 | break;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /*
|
---|
155 | * This is inefficient because we spin until things work without
|
---|
156 | * blocking but this is just a test.
|
---|
157 | */
|
---|
158 | OSSL_sleep(0);
|
---|
159 | SSL_handle_events(c_ssl);
|
---|
160 | }
|
---|
161 |
|
---|
162 | testresult = 1;
|
---|
163 | err:
|
---|
164 | SSL_free(c_ssl);
|
---|
165 | SSL_CTX_free(c_ctx);
|
---|
166 | BIO_ADDR_free(s_addr_);
|
---|
167 | BIO_free(c_net_bio_own);
|
---|
168 | if (fd_arg == INVALID_SOCKET && c_fd != INVALID_SOCKET)
|
---|
169 | BIO_closesocket(c_fd);
|
---|
170 | return testresult;
|
---|
171 | }
|
---|
172 |
|
---|
173 | static int test_quic_client(void)
|
---|
174 | {
|
---|
175 | return (test_quic_client_ex(INVALID_SOCKET));
|
---|
176 | }
|
---|
177 |
|
---|
178 | static int test_quic_client_connect_first(void)
|
---|
179 | {
|
---|
180 | struct sockaddr_in sin = {0};
|
---|
181 | int c_fd;
|
---|
182 | int rv;
|
---|
183 |
|
---|
184 | #ifdef SA_LEN
|
---|
185 | sin.sin_len = sizeof(struct sockaddr_in);
|
---|
186 | #endif
|
---|
187 | sin.sin_family = AF_INET;
|
---|
188 | sin.sin_port = htons(DST_PORT);
|
---|
189 | sin.sin_addr.s_addr = htonl(DST_ADDR);
|
---|
190 |
|
---|
191 | c_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
---|
192 | if (!TEST_int_ne(c_fd, INVALID_SOCKET))
|
---|
193 | goto err;
|
---|
194 |
|
---|
195 | if (!TEST_int_eq(connect(c_fd, (const struct sockaddr *)&sin, sizeof(sin)), 0))
|
---|
196 | goto err;
|
---|
197 |
|
---|
198 | if (!TEST_true(BIO_socket_nbio(c_fd, 1)))
|
---|
199 | goto err;
|
---|
200 |
|
---|
201 | rv = test_quic_client_ex(c_fd);
|
---|
202 |
|
---|
203 | close(c_fd);
|
---|
204 |
|
---|
205 | return (rv);
|
---|
206 |
|
---|
207 | err:
|
---|
208 | if (c_fd != INVALID_SOCKET)
|
---|
209 | close(c_fd);
|
---|
210 | return (0);
|
---|
211 | }
|
---|
212 |
|
---|
213 | OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n")
|
---|
214 |
|
---|
215 | int setup_tests(void)
|
---|
216 | {
|
---|
217 | if (!test_skip_common_options()) {
|
---|
218 | TEST_error("Error parsing test options\n");
|
---|
219 | return 0;
|
---|
220 | }
|
---|
221 |
|
---|
222 | ADD_TEST(test_quic_client);
|
---|
223 | ADD_TEST(test_quic_client_connect_first);
|
---|
224 |
|
---|
225 | return 1;
|
---|
226 | }
|
---|