]>
Commit | Line | Data |
---|---|---|
23fe02e5 MC |
1 | /* |
2 | * Copyright 2023 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 | ||
10 | /* | |
11 | * NB: Changes to this file should also be reflected in | |
12 | * doc/man7/ossl-guide-quic-client-block.pod | |
13 | */ | |
14 | ||
15 | #include <string.h> | |
16 | ||
17 | /* Include the appropriate header file for SOCK_DGRAM */ | |
18 | #ifdef _WIN32 /* Windows */ | |
19 | # include <winsock2.h> | |
20 | #else /* Linux/Unix */ | |
21 | # include <sys/socket.h> | |
22 | #endif | |
23 | ||
24 | #include <openssl/bio.h> | |
25 | #include <openssl/ssl.h> | |
26 | #include <openssl/err.h> | |
27 | ||
28 | /* Helper function to create a BIO connected to the server */ | |
29 | static BIO *create_socket_bio(const char *hostname, const char *port, | |
30 | BIO_ADDR **peer_addr) | |
31 | { | |
32 | int sock = -1; | |
33 | BIO_ADDRINFO *res; | |
34 | const BIO_ADDRINFO *ai = NULL; | |
35 | BIO *bio; | |
36 | ||
37 | /* | |
38 | * Lookup IP address info for the server. | |
39 | */ | |
40 | if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_DGRAM, 0, | |
41 | &res)) | |
42 | return NULL; | |
43 | ||
44 | /* | |
45 | * Loop through all the possible addresses for the server and find one | |
46 | * we can connect to. | |
47 | */ | |
48 | for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) { | |
49 | /* | |
50 | * Create a TCP socket. We could equally use non-OpenSSL calls such | |
51 | * as "socket" here for this and the subsequent connect and close | |
52 | * functions. But for portability reasons and also so that we get | |
53 | * errors on the OpenSSL stack in the event of a failure we use | |
54 | * OpenSSL's versions of these functions. | |
55 | */ | |
56 | sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0); | |
57 | if (sock == -1) | |
58 | continue; | |
59 | ||
60 | /* Connect the socket to the server's address */ | |
61 | if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) { | |
62 | BIO_closesocket(sock); | |
63 | sock = -1; | |
64 | continue; | |
65 | } | |
66 | ||
67 | /* Set to nonblocking mode */ | |
68 | if (!BIO_socket_nbio(sock, 1)) { | |
69 | sock = -1; | |
70 | continue; | |
71 | } | |
72 | ||
73 | break; | |
74 | } | |
75 | ||
76 | if (sock != -1) { | |
77 | *peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai)); | |
78 | if (*peer_addr == NULL) { | |
79 | BIO_closesocket(sock); | |
80 | return NULL; | |
81 | } | |
82 | } | |
83 | ||
84 | ||
85 | /* Free the address information resources we allocated earlier */ | |
86 | BIO_ADDRINFO_free(res); | |
87 | ||
88 | /* If sock is -1 then we've been unable to connect to the server */ | |
89 | if (sock == -1) | |
90 | return NULL; | |
91 | ||
92 | /* Create a BIO to wrap the socket*/ | |
93 | bio = BIO_new(BIO_s_datagram()); | |
94 | if (bio == NULL) | |
95 | BIO_closesocket(sock); | |
96 | ||
97 | /* | |
98 | * Associate the newly created BIO with the underlying socket. By | |
99 | * passing BIO_CLOSE here the socket will be automatically closed when | |
100 | * the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which | |
101 | * case you must close the socket explicitly when it is no longer | |
102 | * needed. | |
103 | */ | |
104 | BIO_set_fd(bio, sock, BIO_CLOSE); | |
105 | ||
106 | return bio; | |
107 | } | |
108 | ||
109 | /* Server hostname and port details. Must be in quotes */ | |
110 | #ifndef HOSTNAME | |
111 | # define HOSTNAME "www.example.com" | |
112 | #endif | |
113 | #ifndef PORT | |
114 | # define PORT "443" | |
115 | #endif | |
116 | ||
117 | /* | |
118 | * Simple application to send a basic HTTP/1.0 request to a server and | |
119 | * print the response on the screen. Note that HTTP/1.0 over QUIC is | |
120 | * non-standard and will not typically be supported by real world servers. This | |
121 | * is for demonstration purposes only. | |
122 | */ | |
123 | int main(void) | |
124 | { | |
125 | SSL_CTX *ctx = NULL; | |
126 | SSL *ssl; | |
127 | BIO *bio = NULL; | |
128 | int res = EXIT_FAILURE; | |
129 | int ret; | |
130 | unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' }; | |
131 | const char *request = | |
132 | "GET / HTTP/1.0\r\nConnection: close\r\nHost: "HOSTNAME"\r\n\r\n"; | |
133 | size_t written, readbytes; | |
134 | char buf[160]; | |
135 | BIO_ADDR *peer_addr = NULL; | |
136 | ||
137 | /* | |
138 | * Create an SSL_CTX which we can use to create SSL objects from. We | |
139 | * want an SSL_CTX for creating clients so we use | |
140 | * OSSL_QUIC_client_method() here. | |
141 | */ | |
142 | ctx = SSL_CTX_new(OSSL_QUIC_client_method()); | |
143 | if (ctx == NULL) { | |
144 | printf("Failed to create the SSL_CTX\n"); | |
145 | goto end; | |
146 | } | |
147 | ||
148 | /* | |
149 | * Configure the client to abort the handshake if certificate | |
150 | * verification fails. Virtually all clients should do this unless you | |
151 | * really know what you are doing. | |
152 | */ | |
153 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); | |
154 | ||
155 | /* Use the default trusted certificate store */ | |
156 | if (!SSL_CTX_set_default_verify_paths(ctx)) { | |
157 | printf("Failed to set the default trusted certificate store\n"); | |
158 | goto end; | |
159 | } | |
160 | ||
161 | /* Create an SSL object to represent the TLS connection */ | |
162 | ssl = SSL_new(ctx); | |
163 | if (ssl == NULL) { | |
164 | printf("Failed to create the SSL object\n"); | |
165 | goto end; | |
166 | } | |
167 | ||
168 | /* | |
169 | * Create the underlying transport socket/BIO and associate it with the | |
170 | * connection. | |
171 | */ | |
172 | bio = create_socket_bio(HOSTNAME, PORT, &peer_addr); | |
173 | if (bio == NULL) { | |
174 | printf("Failed to crete the BIO\n"); | |
175 | goto end; | |
176 | } | |
177 | SSL_set_bio(ssl, bio, bio); | |
178 | ||
179 | /* | |
180 | * Tell the server during the handshake which hostname we are attempting | |
181 | * to connect to in case the server supports multiple hosts. | |
182 | */ | |
183 | if (!SSL_set_tlsext_host_name(ssl, HOSTNAME)) { | |
184 | printf("Failed to set the SNI hostname\n"); | |
185 | goto end; | |
186 | } | |
187 | ||
188 | /* | |
189 | * Ensure we check during certificate verification that the server has | |
190 | * supplied a certificate for the hostname that we were expecting. | |
191 | * Virtually all clients should do this unless you really know what you | |
192 | * are doing. | |
193 | */ | |
194 | if (!SSL_set1_host(ssl, HOSTNAME)) { | |
195 | printf("Failed to set the certificate verification hostname"); | |
196 | goto end; | |
197 | } | |
198 | ||
199 | /* SSL_set_alpn_protos returns 0 for success! */ | |
200 | if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) { | |
201 | printf("Failed to set the ALPN for the connection\n"); | |
202 | goto end; | |
203 | } | |
204 | ||
ce7a9e23 | 205 | if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) { |
4409e152 | 206 | printf("Failed to set the initial peer address\n"); |
23fe02e5 MC |
207 | goto end; |
208 | } | |
209 | ||
210 | if ((ret = SSL_connect(ssl)) < 1) { | |
211 | /* | |
212 | * If the failure is due to a verification error we can get more | |
213 | * information about it from SSL_get_verify_result(). | |
214 | */ | |
215 | if (SSL_get_verify_result(ssl) != X509_V_OK) | |
216 | printf("Verify error: %s\n", | |
217 | X509_verify_cert_error_string(SSL_get_verify_result(ssl))); | |
218 | goto end; | |
219 | } | |
220 | ||
221 | /* Write an HTTP GET request to the peer */ | |
222 | if (!SSL_write_ex(ssl, request, strlen(request), &written)) { | |
223 | printf("Failed to write HTTP request\n"); | |
224 | goto end; | |
225 | } | |
226 | ||
227 | /* | |
228 | * Get up to sizeof(buf) bytes of the response. We keep reading until the | |
229 | * server closes the connection. | |
230 | */ | |
231 | while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) { | |
232 | /* | |
233 | * OpenSSL does not guarantee that the returned data is a string or | |
234 | * that it is NUL terminated so we use fwrite() to write the exact | |
235 | * number of bytes that we read. The data could be non-printable or | |
236 | * have NUL characters in the middle of it. For this simple example | |
237 | * we're going to print it to stdout anyway. | |
238 | */ | |
239 | fwrite(buf, 1, readbytes, stdout); | |
240 | } | |
241 | /* In case the response didn't finish with a newline we add one now */ | |
242 | printf("\n"); | |
243 | ||
244 | /* | |
245 | * Check whether we finished the while loop above normally or as the | |
246 | * result of an error. The 0 argument to SSL_get_error() is the return | |
247 | * code we received from the SSL_read_ex() call. It must be 0 in order | |
248 | * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. | |
249 | */ | |
250 | if (SSL_get_error(ssl, 0) != SSL_ERROR_ZERO_RETURN) { | |
251 | /* | |
252 | * Some error occurred other than a graceful close down by the | |
253 | * peer. | |
254 | */ | |
255 | printf ("Failed reading remaining data\n"); | |
256 | goto end; | |
257 | } | |
258 | ||
259 | /* | |
260 | * Repeatedly call SSL_shutdown() until the connection is fully | |
261 | * closed. | |
262 | */ | |
263 | do { | |
264 | ret = SSL_shutdown(ssl); | |
265 | if (ret < 0) { | |
4409e152 | 266 | printf("Error shutting down: %d\n", ret); |
23fe02e5 MC |
267 | goto end; |
268 | } | |
269 | } while (ret != 1); | |
270 | ||
271 | /* Success! */ | |
272 | res = EXIT_SUCCESS; | |
273 | end: | |
274 | /* | |
275 | * If something bad happened then we will dump the contents of the | |
276 | * OpenSSL error stack to stderr. There might be some useful diagnostic | |
277 | * information there. | |
278 | */ | |
279 | if (res == EXIT_FAILURE) | |
280 | ERR_print_errors_fp(stderr); | |
281 | ||
282 | /* | |
283 | * Free the resources we allocated. We do not free the BIO object here | |
284 | * because ownership of it was immediately transferred to the SSL object | |
285 | * via SSL_set_bio(). The BIO will be freed when we free the SSL object. | |
286 | */ | |
287 | SSL_free(ssl); | |
288 | SSL_CTX_free(ctx); | |
289 | BIO_ADDR_free(peer_addr); | |
290 | return res; | |
291 | } |