arduino-emulator
Loading...
Searching...
No Matches
NetworkClientSecure.h
1/*
2 NetworkClientSecure.h
3 Copyright (c) 2025 Phil Schatzmann. All right reserved.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#pragma once
21#if defined(USE_HTTPS)
22#include <errno.h>
23#include <sys/select.h>
24#include <wolfssl/options.h>
25#include <wolfssl/ssl.h>
26
27#include "Ethernet.h"
28#include "SocketImpl.h"
29
30namespace arduino {
31
32#define SOCKET_IMPL_SEC "SocketImplSecure"
33
34static int wolf_ssl_counter = 0;
35static WOLFSSL_CTX* wolf_ctx = nullptr;
36
42 public:
44 if (wolf_ssl_counter++ == 0 || wolf_ctx == nullptr) {
46 if ((wolf_ctx = wolfSSL_CTX_new(wolfTLS_client_method())) == NULL) {
47 Logger.error(SOCKET_IMPL_SEC, "wolfSSL_CTX_new error.");
48 }
49 }
50 if ((ssl = wolfSSL_new(wolf_ctx)) == NULL) {
51 Logger.error(SOCKET_IMPL_SEC, "wolfSSL_new error.");
52 }
53 }
54
56 if (ssl) {
57 wolfSSL_free(ssl);
58 ssl = nullptr;
59 }
60 if (--wolf_ssl_counter == 0 && wolf_ctx) {
61 wolfSSL_CTX_free(wolf_ctx);
62 wolf_ctx = nullptr;
64 }
65 }
66 // direct read
67 int read(uint8_t* buffer, size_t len) override {
68 // size_t result = ::recv(sock, buffer, len, MSG_DONTWAIT );
69 if (ssl == nullptr) {
70 wolfSSL_set_fd(ssl, sock);
71 }
72 int result = ::wolfSSL_read(ssl, buffer, len);
73
74 if (result < 0) {
75 int error = wolfSSL_get_error(ssl, result);
76 if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
77 return 0;
78 }
79
80 is_connected = false;
81 return -1;
82 }
83
84 if (result == 0) {
85 is_connected = false;
86 return -1;
87 }
88
89 //
90 char lenStr[80];
91 sprintf(lenStr, "%ld -> %d", len, result);
92 Logger.debug(SOCKET_IMPL_SEC, "read->", lenStr);
93
94 return result;
95 }
96
97 int connect(const char* address, uint16_t port) override {
98 return connect(address, port, -1);
99 }
100
101 // opens a connection with a timeout in milliseconds for the TCP handshake.
102 // The TLS handshake below still runs to completion without its own
103 // timeout, matching the previous (2-arg) behavior.
104 int connect(const char* address, uint16_t port, int32_t timeout_ms) override {
105 // Create socket
106 sock = ::socket(AF_INET, SOCK_STREAM, 0);
107 if (sock < 0) {
108 Logger.error(SOCKET_IMPL_SEC, "Socket creation failed");
109 return -1;
110 }
111
112 // Setup server address
113 memset(&serv_addr, 0, sizeof(serv_addr));
114 serv_addr.sin_family = AF_INET;
115 serv_addr.sin_port = htons(port);
116 if (::inet_pton(AF_INET, address, &serv_addr.sin_addr) <= 0) {
117 Logger.error(SOCKET_IMPL_SEC, "Invalid address", address);
118 ::close(sock);
119 sock = -1;
120 return -1;
121 }
122
123 // Connect to server, honoring timeout_ms when >= 0
124 if (timeout_ms < 0) {
125 if (::connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
126 Logger.error(SOCKET_IMPL_SEC, "Connection failed");
127 ::close(sock);
128 sock = -1;
129 return -1;
130 }
131 } else {
132 setSocketNonBlocking(sock, true);
133
134 int result = ::connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
135 if (result < 0 && errno != EINPROGRESS && errno != EWOULDBLOCK) {
136 Logger.error(SOCKET_IMPL_SEC, "Connection failed");
137 setSocketNonBlocking(sock, false);
138 ::close(sock);
139 sock = -1;
140 return -1;
141 }
142
143 if (result < 0) {
146 FD_SET(sock, &writefds);
148 FD_SET(sock, &errorfds);
149 timeval timeout{.tv_sec = timeout_ms / 1000,
150 .tv_usec = (timeout_ms % 1000) * 1000};
151
152 result = select(sock + 1, nullptr, &writefds, &errorfds, &timeout);
153 int error_code = 0;
155 if (result <= 0 ||
157 error_code != 0) {
158 Logger.error(SOCKET_IMPL_SEC,
159 result == 0 ? "Connection timeout" : "Connection failed");
160 setSocketNonBlocking(sock, false);
161 ::close(sock);
162 sock = -1;
163 return -1;
164 }
165 }
166
167 setSocketNonBlocking(sock, false);
168 }
169
170 // Reset the TLS session for this connection - reusing ssl as-is
171 // across a stop()/connect() cycle (this object's owner, e.g.
172 // URLStream's own client_secure, is typically kept and reconnected
173 // rather than recreated) leaves it in stale post-handshake state, so
174 // a second connect() would send a request the server never replies
175 // to instead of doing a fresh handshake.
176 if (ssl != nullptr) {
177 wolfSSL_free(ssl);
178 }
179 ssl = wolfSSL_new(wolf_ctx);
180 if (is_insecure && ssl != nullptr) {
181 wolfSSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
182 }
183
184 // Set SSL file descriptor
185 wolfSSL_set_fd(ssl, sock);
186
187 // Set SNI (Server Name Indication) if needed
188 wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, address, strlen(address));
189
190 // Perform SSL handshake
191 int rc = wolfSSL_connect(ssl);
192 if (rc != SSL_SUCCESS) {
193 int err = wolfSSL_get_error(ssl, rc);
195 char msg[160];
196 snprintf(msg, sizeof(msg),
197 "SSL handshake failed, error code: %d reason: %s", err,
198 errStr ? errStr : "unknown");
199 Logger.error(SOCKET_IMPL_SEC, msg);
200 ::close(sock);
201 sock = -1;
202 return -1;
203 }
204
205 is_connected = true;
206 return 1;
207 }
208
209 // send the data via the socket - returns the number of characters written or
210 // -1=>Error
211 size_t write(const uint8_t* str, size_t len) {
212 Logger.debug(SOCKET_IMPL_SEC, "write");
213 if (ssl == nullptr) {
214 wolfSSL_set_fd(ssl, sock);
215 }
216 // return ::send(sock , str , len , 0 );
217 return ::wolfSSL_write(ssl, str, len);
218 }
219
220 void setCACert(const char* cert) override {
221 if (wolf_ctx == nullptr) return;
222 // Load CA certificate from a PEM string
223 int ret =
224 wolfSSL_CTX_load_verify_buffer(wolf_ctx, (const unsigned char*)cert,
226 if (ret != SSL_SUCCESS) {
227 Logger.error(SOCKET_IMPL_SEC, "Failed to load CA certificate");
228 }
229 }
230
231 void setInsecure() {
232 is_insecure = true;
233 if (wolf_ctx == nullptr) return;
234 // Disable certificate verification on context
235 wolfSSL_CTX_set_verify(wolf_ctx, SSL_VERIFY_NONE, nullptr);
236 // Also disable on SSL object if already created
237 if (ssl) wolfSSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
238 }
239
240 protected:
241 WOLFSSL* ssl = nullptr;
242 bool is_insecure = false;
243};
244
249 public:
250 NetworkClientSecure(int bufferSize = 256, long timeout = 2000)
251 : EthernetClient(std::make_shared<SocketImplSecure>(), bufferSize, timeout) {}
252 void setCACert(const char* cert) override { p_sock->setCACert(cert); }
253 void setInsecure() override { p_sock->setInsecure(); }
254};
255
256} // namespace arduino
257
258#endif
Definition DMAPool.h:103
Definition Ethernet.h:131
NetworkClientSecure based on wolf ssl.
Definition NetworkClientSecure.h:248
Definition SocketImpl.h:30
SSL Socket using wolf ssl. For error codes see https://wolfssl.jp/docs-3/wolfssl-manual/appendix-c.
Definition NetworkClientSecure.h:41
We provide the WiFi class to simulate the Arduino WIFI. In in Linux we can expect that networking is ...
Definition CanMsg.cpp:31