44 * SPDX-License-Identifier: BSD-3-Clause
55 */
66
7- #include <string.h>
8- #include <time.h>
9-
107#include "pico/stdlib.h"
118#include "pico/cyw43_arch.h"
12- #include "lwip/pbuf.h"
13- #include "lwip/altcp_tcp.h"
14- #include "lwip/altcp_tls.h"
15- #include "lwip/dns.h"
169
1710#define TLS_CLIENT_SERVER "worldtimeapi.org"
1811#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \
2114 "\r\n"
2215#define TLS_CLIENT_TIMEOUT_SECS 15
2316
24-
25- typedef struct TLS_CLIENT_T_ {
26- struct altcp_pcb * pcb ;
27- bool complete ;
28- } TLS_CLIENT_T ;
29-
30- static struct altcp_tls_config * tls_config = NULL ;
31-
32- static err_t tls_client_close (void * arg ) {
33- TLS_CLIENT_T * state = (TLS_CLIENT_T * )arg ;
34- err_t err = ERR_OK ;
35-
36- state -> complete = true;
37- if (state -> pcb != NULL ) {
38- altcp_arg (state -> pcb , NULL );
39- altcp_poll (state -> pcb , NULL , 0 );
40- altcp_recv (state -> pcb , NULL );
41- altcp_err (state -> pcb , NULL );
42- err = altcp_close (state -> pcb );
43- if (err != ERR_OK ) {
44- printf ("close failed %d, calling abort\n" , err );
45- altcp_abort (state -> pcb );
46- err = ERR_ABRT ;
47- }
48- state -> pcb = NULL ;
49- }
50- return err ;
51- }
52-
53- static err_t tls_client_connected (void * arg , struct altcp_pcb * pcb , err_t err ) {
54- TLS_CLIENT_T * state = (TLS_CLIENT_T * )arg ;
55- if (err != ERR_OK ) {
56- printf ("connect failed %d\n" , err );
57- return tls_client_close (state );
58- }
59-
60- printf ("connected to server, sending request\n" );
61- err = altcp_write (state -> pcb , TLS_CLIENT_HTTP_REQUEST , strlen (TLS_CLIENT_HTTP_REQUEST ), TCP_WRITE_FLAG_COPY );
62- if (err != ERR_OK ) {
63- printf ("error writing data, err=%d" , err );
64- return tls_client_close (state );
65- }
66-
67- return ERR_OK ;
68- }
69-
70- static err_t tls_client_poll (void * arg , struct altcp_pcb * pcb ) {
71- printf ("timed out" );
72- return tls_client_close (arg );
73- }
74-
75- static void tls_client_err (void * arg , err_t err ) {
76- TLS_CLIENT_T * state = (TLS_CLIENT_T * )arg ;
77- printf ("tls_client_err %d\n" , err );
78- state -> pcb = NULL ; /* pcb freed by lwip when _err function is called */
79- }
80-
81- static err_t tls_client_recv (void * arg , struct altcp_pcb * pcb , struct pbuf * p , err_t err ) {
82- TLS_CLIENT_T * state = (TLS_CLIENT_T * )arg ;
83- if (!p ) {
84- printf ("connection closed\n" );
85- return tls_client_close (state );
86- }
87-
88- if (p -> tot_len > 0 ) {
89- /* For simplicity this examples creates a buffer on stack the size of the data pending here,
90- and copies all the data to it in one go.
91- Do be aware that the amount of data can potentially be a bit large (TLS record size can be 16 KB),
92- so you may want to use a smaller fixed size buffer and copy the data to it using a loop, if memory is a concern */
93- char buf [p -> tot_len + 1 ];
94-
95- pbuf_copy_partial (p , buf , p -> tot_len , 0 );
96- buf [p -> tot_len ] = 0 ;
97-
98- printf ("***\nnew data received from server:\n***\n\n%s\n" , buf );
99-
100- altcp_recved (pcb , p -> tot_len );
101- }
102- pbuf_free (p );
103-
104- return ERR_OK ;
105- }
106-
107- static void tls_client_connect_to_server_ip (const ip_addr_t * ipaddr , TLS_CLIENT_T * state )
108- {
109- err_t err ;
110- u16_t port = 443 ;
111-
112- printf ("connecting to server IP %s port %d\n" , ipaddr_ntoa (ipaddr ), port );
113- err = altcp_connect (state -> pcb , ipaddr , port , tls_client_connected );
114- if (err != ERR_OK )
115- {
116- fprintf (stderr , "error initiating connect, err=%d\n" , err );
117- tls_client_close (state );
118- }
119- }
120-
121- static void tls_client_dns_found (const char * hostname , const ip_addr_t * ipaddr , void * arg )
122- {
123- if (ipaddr )
124- {
125- printf ("DNS resolving complete\n" );
126- tls_client_connect_to_server_ip (ipaddr , (TLS_CLIENT_T * ) arg );
127- }
128- else
129- {
130- printf ("error resolving hostname %s\n" , hostname );
131- tls_client_close (arg );
132- }
133- }
134-
135-
136- static bool tls_client_open (const char * hostname , void * arg ) {
137- err_t err ;
138- ip_addr_t server_ip ;
139- TLS_CLIENT_T * state = (TLS_CLIENT_T * )arg ;
140-
141- state -> pcb = altcp_tls_new (tls_config , IPADDR_TYPE_ANY );
142- if (!state -> pcb ) {
143- printf ("failed to create pcb\n" );
144- return false;
145- }
146-
147- altcp_arg (state -> pcb , state );
148- altcp_poll (state -> pcb , tls_client_poll , TLS_CLIENT_TIMEOUT_SECS * 2 );
149- altcp_recv (state -> pcb , tls_client_recv );
150- altcp_err (state -> pcb , tls_client_err );
151-
152- /* Set SNI */
153- mbedtls_ssl_set_hostname (altcp_tls_context (state -> pcb ), hostname );
154-
155- printf ("resolving %s\n" , hostname );
156-
157- // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
158- // You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
159- // these calls are a no-op and can be omitted, but it is a good practice to use them in
160- // case you switch the cyw43_arch type later.
161- cyw43_arch_lwip_begin ();
162-
163- err = dns_gethostbyname (hostname , & server_ip , tls_client_dns_found , state );
164- if (err == ERR_OK )
165- {
166- /* host is in DNS cache */
167- tls_client_connect_to_server_ip (& server_ip , state );
168- }
169- else if (err != ERR_INPROGRESS )
170- {
171- printf ("error initiating DNS resolving, err=%d\n" , err );
172- tls_client_close (state -> pcb );
173- }
174-
175- cyw43_arch_lwip_end ();
176-
177- return err == ERR_OK || err == ERR_INPROGRESS ;
178- }
179-
180- // Perform initialisation
181- static TLS_CLIENT_T * tls_client_init (void ) {
182- TLS_CLIENT_T * state = calloc (1 , sizeof (TLS_CLIENT_T ));
183- if (!state ) {
184- printf ("failed to allocate state\n" );
185- return NULL ;
186- }
187-
188- return state ;
189- }
190-
191- void run_tls_client_test (void ) {
192- /* No CA certificate checking */
193- tls_config = altcp_tls_create_config_client (NULL , 0 );
194-
195- TLS_CLIENT_T * state = tls_client_init ();
196- if (!state ) {
197- return ;
198- }
199- if (!tls_client_open (TLS_CLIENT_SERVER , state )) {
200- return ;
201- }
202- while (!state -> complete ) {
203- // the following #ifdef is only here so this same example can be used in multiple modes;
204- // you do not need it in your code
205- #if PICO_CYW43_ARCH_POLL
206- // if you are using pico_cyw43_arch_poll, then you must poll periodically from your
207- // main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done.
208- cyw43_arch_poll ();
209- // you can poll as often as you like, however if you have nothing else to do you can
210- // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
211- cyw43_arch_wait_for_work_until (make_timeout_time_ms (1000 ));
212- #else
213- // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
214- // is done via interrupt in the background. This sleep is just an example of some (blocking)
215- // work you might be doing.
216- sleep_ms (1000 );
217- #endif
218- }
219- free (state );
220- altcp_tls_free_config (tls_config );
221- }
17+ extern bool run_tls_client_test (const uint8_t * cert , size_t cert_len , const char * server , const char * request , int timeout );
22218
22319int main () {
22420 stdio_init_all ();
@@ -233,12 +29,17 @@ int main() {
23329 printf ("failed to connect\n" );
23430 return 1 ;
23531 }
236- run_tls_client_test ();
237-
32+ bool pass = run_tls_client_test (NULL , 0 , TLS_CLIENT_SERVER , TLS_CLIENT_HTTP_REQUEST , TLS_CLIENT_TIMEOUT_SECS );
33+ if (pass ) {
34+ printf ("Test passed\n" );
35+ } else {
36+ printf ("Test failed\n" );
37+ }
23838 /* sleep a bit to let usb stdio write out any buffer to host */
23939 sleep_ms (100 );
24040
24141 cyw43_arch_deinit ();
242- return 0 ;
42+ printf ("All done\n" );
43+ return pass ? 0 : 1 ;
24344}
24445
0 commit comments