1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
26 #define _WINSOCK_DEPRECATED_NO_WARNINGS
27 #endif
28 #define MBEDTLS_ALLOW_PRIVATE_ACCESS
29 #include "private-lib-core.h"
30 
31 #if defined(LWS_WITH_MBEDTLS)
32 #if defined(LWS_HAVE_MBEDTLS_NET_SOCKETS)
33 #include "mbedtls/net_sockets.h"
34 #else
35 #include "mbedtls/net.h"
36 #endif
37 #endif
38 
39 int
lws_send_pipe_choked(struct lws *wsi)40 lws_send_pipe_choked(struct lws *wsi)
41 {	struct lws *wsi_eff;
42 
43 #if defined(LWS_WITH_HTTP2)
44 	wsi_eff = lws_get_network_wsi(wsi);
45 #else
46 	wsi_eff = wsi;
47 #endif
48 	/* the fact we checked implies we avoided back-to-back writes */
49 	wsi_eff->could_have_pending = 0;
50 
51 	/* treat the fact we got a truncated send pending as if we're choked */
52 	if (lws_has_buffered_out(wsi_eff)
53 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
54 	    ||wsi->http.comp_ctx.buflist_comp ||
55 	      wsi->http.comp_ctx.may_have_more
56 #endif
57 	)
58 		return 1;
59 
60 	return (int)wsi_eff->sock_send_blocking;
61 }
62 
63 int
lws_poll_listen_fd(struct lws_pollfd *fd)64 lws_poll_listen_fd(struct lws_pollfd *fd)
65 {
66 	fd_set readfds;
67 	struct timeval tv = { 0, 0 };
68 
69 	assert((fd->events & LWS_POLLIN) == LWS_POLLIN);
70 
71 	FD_ZERO(&readfds);
72 	FD_SET(fd->fd, &readfds);
73 
74 	return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
75 }
76 
77 int
lws_plat_set_nonblocking(lws_sockfd_type fd)78 lws_plat_set_nonblocking(lws_sockfd_type fd)
79 {
80 	u_long optl = 1;
81 	int result = !!ioctlsocket(fd, FIONBIO, &optl);
82 #if (_LWS_ENABLED_LOGS & LLL_ERR)
83 	if (result)
84 	{
85 		int error = LWS_ERRNO;
86 		lwsl_err("ioctlsocket FIONBIO 1 failed with error %d\n", error);
87 	}
88 #endif
89 	return result;
90 }
91 
92 int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, int unix_skt)93 lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd,
94 			    int unix_skt)
95 {
96 	int optval = 1;
97 	int optlen = sizeof(optval);
98 	DWORD dwBytesRet;
99 	struct tcp_keepalive alive;
100 	int protonbr;
101 #ifndef _WIN32_WCE
102 	struct protoent *tcp_proto;
103 #endif
104 
105 	if (vhost->ka_time) {
106 		/* enable keepalive on this socket */
107 		optval = 1;
108 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
109 			       (const char *)&optval, optlen) < 0) {
110 #if (_LWS_ENABLED_LOGS & LLL_ERR)
111 			int error = LWS_ERRNO;
112 			lwsl_err("setsockopt SO_KEEPALIVE 1 failed with error %d\n", error);
113 #endif
114 			return 1;
115 		}
116 
117 		alive.onoff = TRUE;
118 		alive.keepalivetime = vhost->ka_time * 1000;
119 		alive.keepaliveinterval = vhost->ka_interval * 1000;
120 
121 		if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
122 			     NULL, 0, &dwBytesRet, NULL, NULL)) {
123 #if (_LWS_ENABLED_LOGS & LLL_ERR)
124 			int error = LWS_ERRNO;
125 			lwsl_err("WSAIoctl SIO_KEEPALIVE_VALS 1 %lu %lu failed with error %d\n", alive.keepalivetime, alive.keepaliveinterval, error);
126 #endif
127 			return 1;
128 		}
129 	}
130 
131 	/* Disable Nagle */
132 	optval = 1;
133 #ifndef _WIN32_WCE
134 	tcp_proto = getprotobyname("TCP");
135 	if (!tcp_proto) {
136 #if (_LWS_ENABLED_LOGS & LLL_WARN)
137 		int error = LWS_ERRNO;
138 		lwsl_warn("getprotobyname(\"TCP\") failed with error, falling back to 6 %d\n", error);
139 #endif
140 		protonbr = 6;  /* IPPROTO_TCP */
141 	} else
142 		protonbr = tcp_proto->p_proto;
143 #else
144 	protonbr = 6;
145 #endif
146 
147 	if (setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen) ) {
148 #if (_LWS_ENABLED_LOGS & LLL_WARN)
149 		int error = LWS_ERRNO;
150 		lwsl_warn("setsockopt TCP_NODELAY 1 failed with error %d\n", error);
151 #endif
152 	}
153 
154 	return lws_plat_set_nonblocking(fd);
155 }
156 
157 int
lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags)158 lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags)
159 {
160 	/*
161 	 * Seems to require "differeniated services" but no docs
162 	 *
163 	 * https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
164 	 * https://docs.microsoft.com/en-us/previous-versions/windows/desktop/qos/differentiated-services
165 	 */
166 	lwsl_warn("%s: not implemented on windows platform\n", __func__);
167 
168 	return 0;
169 }
170 
171 int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, size_t addrlen)172 lws_interface_to_sa(int ipv6,
173 		const char *ifname, struct sockaddr_in *addr, size_t addrlen)
174 {
175 	long long address;
176 #ifdef LWS_WITH_IPV6
177 	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
178 
179 	if (ipv6) {
180 		if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
181 			return LWS_ITOSA_USABLE;
182 		}
183 	}
184 #endif
185 
186 	address = inet_addr(ifname);
187 
188 	if (address == INADDR_NONE) {
189 		struct hostent *entry = gethostbyname(ifname);
190 		if (entry)
191 			address = ((struct in_addr *)entry->h_addr_list[0])->s_addr;
192 	}
193 
194 	if (address == INADDR_NONE)
195 		return LWS_ITOSA_NOT_EXIST;
196 
197 	addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
198 
199 	return LWS_ITOSA_USABLE;
200 }
201 
202 void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)203 lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
204 {
205 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
206 
207 #if defined(LWS_WITH_UDP)
208 	if (wsi->udp) {
209 		lwsl_info("%s: UDP\n", __func__);
210 		pt->fds[pt->fds_count].events |= LWS_POLLIN;
211 	}
212 #endif
213 
214 	if (context->event_loop_ops->io)
215 		context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
216 
217 	pt->fds[pt->fds_count++].revents = 0;
218 
219 	lws_plat_change_pollfd(context, wsi, &pt->fds[pt->fds_count - 1]);
220 }
221 
222 void
lws_plat_delete_socket_from_fds(struct lws_context *context, struct lws *wsi, int m)223 lws_plat_delete_socket_from_fds(struct lws_context *context,
224 						struct lws *wsi, int m)
225 {
226 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
227 
228 	pt->fds_count--;
229 }
230 
231 
232 int
lws_plat_check_connection_error(struct lws *wsi)233 lws_plat_check_connection_error(struct lws *wsi)
234 {
235 	int optVal;
236 	int optLen = sizeof(int);
237 
238 	if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
239 			   (char*)&optVal, &optLen) != SOCKET_ERROR && optVal &&
240 		optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS &&
241 		optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) {
242 		   lwsl_debug("Connect failed SO_ERROR=%d\n", optVal);
243 		   return 1;
244 	}
245 
246 	return 0;
247 }
248 
249 int
lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pfd)250 lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
251 		       struct lws_pollfd *pfd)
252 {
253 	//struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
254 
255 	return 0;
256 }
257 
258 #if defined(LWS_WITH_TLS)
259 
260 int
lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost)261 lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost)
262 {
263 #if !defined(LWS_WITH_MBEDTLS) && defined(LWS_SSL_CLIENT_USE_OS_CA_CERTS)
264 	PCCERT_CONTEXT pcc = NULL;
265 	CERT_ENHKEY_USAGE* ceu = NULL;
266 	DWORD ceu_alloc = 0;
267 	X509_STORE* store;
268 	HCERTSTORE hStore;
269 	int imps = 0;
270 
271 	if (lws_check_opt(vhost->options,
272 			  LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
273 		return 0;
274 
275 	/*
276 	 * Windows Trust Store code adapted from curl (MIT) openssl.c
277 	 * https://github.com/warmcat/libwebsockets/pull/2233
278 	 */
279 
280 	store = SSL_CTX_get_cert_store(vhost->tls.ssl_client_ctx);
281 	hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL, TEXT("ROOT"));
282 
283 	if (!hStore) {
284 		lwsl_notice("%s: no store\n", __func__);
285 		return 1;
286 	}
287 
288 	do {
289 		const unsigned char* ecert;
290 		char cert_name[256];
291 		DWORD req_size = 0;
292 		BYTE key_usage[2];
293 		FILETIME ft;
294 		X509* x509;
295 
296 		pcc = CertEnumCertificatesInStore(hStore, pcc);
297 		if (!pcc)
298 			break;
299 
300 		if (!CertGetNameStringA(pcc, CERT_NAME_SIMPLE_DISPLAY_TYPE,
301 					0, NULL, cert_name, sizeof(cert_name)))
302 			strcpy(cert_name, "Unknown");
303 
304 		lwsl_debug("%s: Checking cert \"%s\"\n", __func__, cert_name);
305 
306 		ecert = (const unsigned char*)pcc->pbCertEncoded;
307 		if (!ecert)
308 			continue;
309 
310 		GetSystemTimeAsFileTime(&ft);
311 		if (CompareFileTime(&pcc->pCertInfo->NotBefore, &ft) > 0 ||
312 		    CompareFileTime(&ft, &pcc->pCertInfo->NotAfter) > 0)
313 			continue;
314 
315 		/* If key usage exists check for signing attribute */
316 		if (CertGetIntendedKeyUsage(pcc->dwCertEncodingType,
317 			pcc->pCertInfo,
318 			key_usage, sizeof(key_usage))) {
319 			if (!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
320 				continue;
321 		} else
322 			if (GetLastError())
323 				continue;
324 
325 		/*
326 		 * If enhanced key usage exists check for server auth attribute.
327 		 *
328 		 * Note "In a Microsoft environment, a certificate might also
329 		 * have EKU extended properties that specify valid uses for the
330 		 * certificate."
331 		 * The call below checks both, and behavior varies depending on
332 		 * what is found. For more details see CertGetEnhancedKeyUsage
333 		 * doc.
334 		 */
335 		if (!CertGetEnhancedKeyUsage(pcc, 0, NULL, &req_size))
336 			continue;
337 
338 		if (req_size && req_size > ceu_alloc) {
339 			void* tmp = lws_realloc(ceu, req_size, __func__);
340 
341 			if (!tmp) {
342 				lwsl_err("%s: OOM", __func__);
343 				break;
344 			}
345 
346 			ceu = (CERT_ENHKEY_USAGE*)tmp;
347 			ceu_alloc = req_size;
348 		}
349 
350 		if (!CertGetEnhancedKeyUsage(pcc, 0, ceu, &req_size))
351 			continue;
352 
353 		if (!ceu || (ceu && !ceu->cUsageIdentifier)) {
354 			/*
355 			 * "If GetLastError returns CRYPT_E_NOT_FOUND, the
356 			 * certificate is good for all uses. If it returns
357 			 * zero, the certificate has no valid uses."
358 			 */
359 			if ((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
360 				continue;
361 
362 			/* ... allow it... */
363 
364 		} else
365 			if (ceu) {
366 				BOOL found = FALSE;
367 				DWORD i;
368 
369 				/*
370 				 * If there is a CEU, check that it specifies
371 				 * we can use the cert for server validation
372 				 */
373 
374 				for (i = 0; i < ceu->cUsageIdentifier; i++) {
375 					if (strcmp("1.3.6.1.5.5.7.3.1"
376 						   /* OID server auth */,
377 						   ceu->rgpszUsageIdentifier[i]))
378 						continue;
379 
380 					found = TRUE;
381 					break;
382 				}
383 
384 				if (!found)
385 					/* Don't use cert if no usage match */
386 					continue;
387 			}
388 
389 		x509 = d2i_X509(NULL, &ecert, pcc->cbCertEncoded);
390 		if (!x509)
391 			/* We can't parse it as am X.509, skip it */
392 			continue;
393 
394 		if (X509_STORE_add_cert(store, x509) == 1) {
395 			lwsl_debug("%s: Imported cert \"%s\"\n", __func__,
396 				  cert_name);
397 			imps++;
398 		}
399 
400 		/*
401 		 * Treat failure as nonfatal, eg, may be dupe
402 		 */
403 
404 		X509_free(x509);
405 	} while (1);
406 
407 	lws_free(ceu);
408 	CertFreeCertificateContext(pcc);
409 	CertCloseStore(hStore, 0);
410 
411 	lwsl_notice("%s: Imported %d certs from plat store\n", __func__, imps);
412 #endif
413 
414 	return 0;
415 }
416 
417 #endif
418 
419 const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, socklen_t cnt)420 lws_plat_inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
421 {
422 	WCHAR *buffer;
423 	size_t bufferlen = (size_t)cnt;
424 	BOOL ok = FALSE;
425 
426 	buffer = lws_malloc(bufferlen * 2, "inet_ntop");
427 	if (!buffer) {
428 		lwsl_err("Out of memory\n");
429 		return NULL;
430 	}
431 
432 	if (af == AF_INET) {
433 		struct sockaddr_in srcaddr;
434 		memset(&srcaddr, 0, sizeof(srcaddr));
435 		srcaddr.sin_family = AF_INET;
436 		memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
437 
438 		if (!WSAAddressToStringW((struct sockaddr*)&srcaddr,
439 					sizeof(srcaddr), 0, buffer,
440 					(LPDWORD)&bufferlen))
441 			ok = TRUE;
442 #ifdef LWS_WITH_IPV6
443 	} else if (af == AF_INET6) {
444 		struct sockaddr_in6 srcaddr;
445 		memset(&srcaddr, 0, sizeof(srcaddr));
446 		srcaddr.sin6_family = AF_INET6;
447 		memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr));
448 
449 		if (!WSAAddressToStringW((struct sockaddr*)&srcaddr,
450 					 sizeof(srcaddr), 0, buffer,
451 					 (LPDWORD)&bufferlen))
452 			ok = TRUE;
453 #endif
454 	} else
455 		lwsl_err("Unsupported type\n");
456 
457 	if (!ok) {
458 		int rv = WSAGetLastError();
459 		lwsl_err("WSAAddressToString() : %d\n", rv);
460 	} else {
461 		if (WideCharToMultiByte(CP_ACP, 0, buffer, (int)bufferlen, dst,
462 					cnt, 0, NULL) <= 0)
463 			ok = FALSE;
464 	}
465 
466 	lws_free(buffer);
467 	return ok ? dst : NULL;
468 }
469 
470 int
lws_plat_inet_pton(int af, const char *src, void *dst)471 lws_plat_inet_pton(int af, const char *src, void *dst)
472 {
473 	WCHAR *buffer;
474 	size_t bufferlen = strlen(src) + 1;
475 	BOOL ok = FALSE;
476 
477 	buffer = lws_malloc(bufferlen * 2, "inet_pton");
478 	if (!buffer) {
479 		lwsl_err("Out of memory\n");
480 		return -1;
481 	}
482 
483 	if (MultiByteToWideChar(CP_ACP, 0, src, (int)bufferlen, buffer,
484 				(int)bufferlen) <= 0) {
485 		lwsl_err("Failed to convert multi byte to wide char\n");
486 		lws_free(buffer);
487 		return -1;
488 	}
489 
490 	if (af == AF_INET) {
491 		struct sockaddr_in dstaddr;
492 		int dstaddrlen = sizeof(dstaddr);
493 
494 		memset(&dstaddr, 0, sizeof(dstaddr));
495 		dstaddr.sin_family = AF_INET;
496 
497 		if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
498 			ok = TRUE;
499 			memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
500 		}
501 #ifdef LWS_WITH_IPV6
502 	} else if (af == AF_INET6) {
503 		struct sockaddr_in6 dstaddr;
504 		int dstaddrlen = sizeof(dstaddr);
505 
506 		memset(&dstaddr, 0, sizeof(dstaddr));
507 		dstaddr.sin6_family = AF_INET6;
508 
509 		if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
510 			ok = TRUE;
511 			memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr));
512 		}
513 #endif
514 	} else
515 		lwsl_err("Unsupported type\n");
516 
517 	if (!ok) {
518 		int rv = WSAGetLastError();
519 		lwsl_err("WSAAddressToString() : %d\n", rv);
520 	}
521 
522 	lws_free(buffer);
523 	return ok ? 1 : -1;
524 }
525 
526 int
lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len)527 lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len)
528 {
529 	lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
530 
531 	return -1;
532 }
533 
534 int
lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, size_t canned_len, size_t n, int fd, const char *iface)535 lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, size_t canned_len,
536 			  size_t n, int fd, const char *iface)
537 {
538 	lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
539 
540 	return -1;
541 }
542 
543 int
lws_plat_if_up(const char *ifname, int fd, int up)544 lws_plat_if_up(const char *ifname, int fd, int up)
545 {
546 	lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
547 
548 	return -1;
549 }
550 
551 int
lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname)552 lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname)
553 {
554 	lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
555 
556 	return -1;
557 }
558 
559 int
lws_plat_ifconfig(int fd, uint8_t *ip, lws_dhcpc_ifstate_t *is)560 lws_plat_ifconfig(int fd, uint8_t *ip, lws_dhcpc_ifstate_t *is)
561 {
562 	lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
563 
564 	return -1;
565 }
566 
567 #if defined(LWS_WITH_MBEDTLS)
568 int
lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len)569 lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len)
570 {
571 	int fd = ((mbedtls_net_context *) ctx)->fd;
572 	int ret, en;
573 
574 	if (fd < 0)
575 		return MBEDTLS_ERR_NET_INVALID_CONTEXT;
576 
577 	ret = send(fd, (const char *)buf, (unsigned int)len, 0);
578 	if (ret >= 0)
579 		return ret;
580 
581 	en = LWS_ERRNO;
582 	if (en == EAGAIN || en == EWOULDBLOCK || en == WSAEWOULDBLOCK)
583 		return MBEDTLS_ERR_SSL_WANT_WRITE;
584 
585 	ret = WSAGetLastError();
586 	lwsl_notice("%s: errno %d, GLE %d\n", __func__, en, ret);
587 	if (ret == WSAECONNRESET )
588             return( MBEDTLS_ERR_NET_CONN_RESET );
589 
590 	return MBEDTLS_ERR_NET_SEND_FAILED;
591 }
592 
593 int
lws_plat_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)594 lws_plat_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
595 {
596 	int fd = ((mbedtls_net_context *) ctx)->fd;
597 	int ret, en;
598 
599 	if (fd < 0)
600 		return MBEDTLS_ERR_NET_INVALID_CONTEXT;
601 
602 	ret = (int)recv(fd, (char *)buf, (unsigned int)len, 0);
603 	if (ret >= 0)
604 		return ret;
605 
606 	en = LWS_ERRNO;
607 	if (en == EAGAIN || en == EWOULDBLOCK || en == WSAEWOULDBLOCK)
608 		return MBEDTLS_ERR_SSL_WANT_READ;
609 
610 	ret = WSAGetLastError();
611 	lwsl_notice("%s: errno %d, GLE %d\n", __func__, en, ret);
612 
613         if (ret == WSAECONNRESET)
614             return MBEDTLS_ERR_NET_CONN_RESET;
615 
616 	return MBEDTLS_ERR_NET_RECV_FAILED;
617 }
618 #endif
619 
620