mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-22 00:57:52 +00:00
663 lines
16 KiB
C
663 lines
16 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#endif
|
|
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
|
#include "private-lib-core.h"
|
|
|
|
#if defined(LWS_WITH_MBEDTLS)
|
|
#if defined(LWS_HAVE_MBEDTLS_NET_SOCKETS)
|
|
#include "mbedtls/net_sockets.h"
|
|
#else
|
|
#include "mbedtls/net.h"
|
|
#endif
|
|
#endif
|
|
|
|
int
|
|
lws_send_pipe_choked(struct lws *wsi)
|
|
{ struct lws *wsi_eff;
|
|
|
|
#if defined(LWS_WITH_HTTP2)
|
|
wsi_eff = lws_get_network_wsi(wsi);
|
|
#else
|
|
wsi_eff = wsi;
|
|
#endif
|
|
/* the fact we checked implies we avoided back-to-back writes */
|
|
wsi_eff->could_have_pending = 0;
|
|
|
|
/* treat the fact we got a truncated send pending as if we're choked */
|
|
if (lws_has_buffered_out(wsi_eff)
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
||wsi->http.comp_ctx.buflist_comp ||
|
|
wsi->http.comp_ctx.may_have_more
|
|
#endif
|
|
)
|
|
return 1;
|
|
|
|
return (int)wsi_eff->sock_send_blocking;
|
|
}
|
|
|
|
int
|
|
lws_poll_listen_fd(struct lws_pollfd *fd)
|
|
{
|
|
fd_set readfds;
|
|
struct timeval tv = { 0, 0 };
|
|
|
|
assert((fd->events & LWS_POLLIN) == LWS_POLLIN);
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(fd->fd, &readfds);
|
|
|
|
return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
|
|
}
|
|
|
|
int
|
|
lws_plat_set_nonblocking(lws_sockfd_type fd)
|
|
{
|
|
u_long optl = 1;
|
|
int result = !!ioctlsocket(fd, FIONBIO, &optl);
|
|
#if (_LWS_ENABLED_LOGS & LLL_ERR)
|
|
if (result)
|
|
lwsl_err("ioctlsocket FIONBIO 1 failed with error %d\n", LWS_ERRNO);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
int
|
|
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd,
|
|
int unix_skt)
|
|
{
|
|
int optval = 1;
|
|
int optlen = sizeof(optval);
|
|
DWORD dwBytesRet;
|
|
struct tcp_keepalive alive;
|
|
int protonbr;
|
|
#ifndef _WIN32_WCE
|
|
struct protoent *tcp_proto;
|
|
#endif
|
|
|
|
if (vhost->ka_time) {
|
|
/* enable keepalive on this socket */
|
|
optval = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
|
(const char *)&optval, optlen) < 0) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_ERR)
|
|
lwsl_err("setsockopt SO_KEEPALIVE 1 failed with error %d\n", LWS_ERRNO);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
alive.onoff = TRUE;
|
|
alive.keepalivetime = vhost->ka_time * 1000;
|
|
alive.keepaliveinterval = vhost->ka_interval * 1000;
|
|
|
|
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
|
|
NULL, 0, &dwBytesRet, NULL, NULL)) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_ERR)
|
|
lwsl_err("WSAIoctl SIO_KEEPALIVE_VALS 1 %lu %lu failed with error %d\n", alive.keepalivetime, alive.keepaliveinterval, LWS_ERRNO);
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Disable Nagle */
|
|
optval = 1;
|
|
#ifndef _WIN32_WCE
|
|
tcp_proto = getprotobyname("TCP");
|
|
if (!tcp_proto) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_WARN)
|
|
lwsl_warn("getprotobyname(\"TCP\") failed with error, falling back to 6 %d\n", LWS_ERRNO);
|
|
#endif
|
|
protonbr = 6; /* IPPROTO_TCP */
|
|
} else
|
|
protonbr = tcp_proto->p_proto;
|
|
#else
|
|
protonbr = 6;
|
|
#endif
|
|
|
|
if (setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen) ) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_WARN)
|
|
lwsl_warn("setsockopt TCP_NODELAY 1 failed with error %d\n", LWS_ERRNO);
|
|
#endif
|
|
}
|
|
|
|
return lws_plat_set_nonblocking(fd);
|
|
}
|
|
|
|
int
|
|
lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags)
|
|
{
|
|
int optval = 1, ret = 0;
|
|
socklen_t optlen = sizeof(optval);
|
|
#if (_LWS_ENABLED_LOGS & LLL_WARN)
|
|
int en;
|
|
#endif
|
|
|
|
/*
|
|
* Seems to require "differeniated services" but no docs
|
|
*
|
|
* https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
|
|
* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/qos/differentiated-services
|
|
*/
|
|
lwsl_warn("%s: priority and ip sockets options not implemented on windows platform\n", __func__);
|
|
|
|
|
|
/*
|
|
* only accept that we are the only listener on the port
|
|
* https://msdn.microsoft.com/zh-tw/library/
|
|
* windows/desktop/ms740621(v=vs.85).aspx
|
|
*
|
|
* for lws, to match Linux, we default to exclusive listen
|
|
*/
|
|
if (lws_flags & LCCSCF_ALLOW_REUSE_ADDR) {
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const void *)&optval, optlen) < 0) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_WARN)
|
|
en = errno;
|
|
lwsl_warn("%s: unable to reuse local addresses: errno %d\n",
|
|
__func__, en);
|
|
#endif
|
|
ret = 1;
|
|
} else
|
|
lwsl_notice("%s: set reuse addresses\n", __func__);
|
|
|
|
} else {
|
|
if (setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
|
(const void *)&optval, optlen) < 0) {
|
|
#if (_LWS_ENABLED_LOGS & LLL_WARN)
|
|
en = errno;
|
|
lwsl_warn("%s: unable to use exclusive addresses: errno %d\n",
|
|
__func__, en);
|
|
#endif
|
|
ret = 1;
|
|
} else
|
|
lwsl_notice("%s: set use exclusive addresses\n", __func__);
|
|
}
|
|
|
|
|
|
#if defined(LWS_WITH_IPV6)
|
|
/* I do not believe Microsoft supports RFC5014
|
|
* Instead, you must set lws_client_connect_info::iface */
|
|
if (lws_flags & LCCSCF_IPV6_PREFER_PUBLIC_ADDR) {
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
lws_interface_to_sa(int ipv6,
|
|
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
|
|
{
|
|
long long address;
|
|
#ifdef LWS_WITH_IPV6
|
|
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
|
|
|
|
if (ipv6) {
|
|
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
|
|
return LWS_ITOSA_USABLE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
address = inet_addr(ifname);
|
|
|
|
if (address == INADDR_NONE) {
|
|
struct hostent *entry = gethostbyname(ifname);
|
|
if (entry)
|
|
address = ((struct in_addr *)entry->h_addr_list[0])->s_addr;
|
|
}
|
|
|
|
if (address == INADDR_NONE)
|
|
return LWS_ITOSA_NOT_EXIST;
|
|
|
|
addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
|
|
|
|
return LWS_ITOSA_USABLE;
|
|
}
|
|
|
|
void
|
|
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|
{
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
|
|
|
#if defined(LWS_WITH_UDP)
|
|
if (wsi->udp) {
|
|
lwsl_info("%s: UDP\n", __func__);
|
|
pt->fds[pt->fds_count].events |= LWS_POLLIN;
|
|
}
|
|
#endif
|
|
|
|
if (context->event_loop_ops->io)
|
|
context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
|
|
|
|
pt->fds[pt->fds_count++].revents = 0;
|
|
|
|
lws_plat_change_pollfd(context, wsi, &pt->fds[pt->fds_count - 1]);
|
|
}
|
|
|
|
void
|
|
lws_plat_delete_socket_from_fds(struct lws_context *context,
|
|
struct lws *wsi, int m)
|
|
{
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
|
|
|
pt->fds_count--;
|
|
}
|
|
|
|
|
|
int
|
|
lws_plat_check_connection_error(struct lws *wsi)
|
|
{
|
|
int optVal;
|
|
int optLen = sizeof(int);
|
|
|
|
if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
|
|
(char*)&optVal, &optLen) != SOCKET_ERROR && optVal &&
|
|
optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS &&
|
|
optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) {
|
|
lwsl_debug("Connect failed SO_ERROR=%d\n", optVal);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
|
|
struct lws_pollfd *pfd)
|
|
{
|
|
//struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(LWS_WITH_TLS)
|
|
|
|
int
|
|
lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost)
|
|
{
|
|
#if !defined(LWS_WITH_MBEDTLS) && defined(LWS_SSL_CLIENT_USE_OS_CA_CERTS)
|
|
PCCERT_CONTEXT pcc = NULL;
|
|
CERT_ENHKEY_USAGE* ceu = NULL;
|
|
DWORD ceu_alloc = 0;
|
|
X509_STORE* store;
|
|
HCERTSTORE hStore;
|
|
int imps = 0;
|
|
|
|
if (lws_check_opt(vhost->options,
|
|
LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
|
|
return 0;
|
|
|
|
/*
|
|
* Windows Trust Store code adapted from curl (MIT) openssl.c
|
|
* https://github.com/warmcat/libwebsockets/pull/2233
|
|
*/
|
|
|
|
store = SSL_CTX_get_cert_store(vhost->tls.ssl_client_ctx);
|
|
hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL, TEXT("ROOT"));
|
|
|
|
if (!hStore) {
|
|
lwsl_notice("%s: no store\n", __func__);
|
|
return 1;
|
|
}
|
|
|
|
do {
|
|
const unsigned char* ecert;
|
|
char cert_name[256];
|
|
DWORD req_size = 0;
|
|
BYTE key_usage[2];
|
|
FILETIME ft;
|
|
X509* x509;
|
|
|
|
pcc = CertEnumCertificatesInStore(hStore, pcc);
|
|
if (!pcc)
|
|
break;
|
|
|
|
if (!CertGetNameStringA(pcc, CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0, NULL, cert_name, sizeof(cert_name)))
|
|
strcpy(cert_name, "Unknown");
|
|
|
|
lwsl_debug("%s: Checking cert \"%s\"\n", __func__, cert_name);
|
|
|
|
ecert = (const unsigned char*)pcc->pbCertEncoded;
|
|
if (!ecert)
|
|
continue;
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
if (CompareFileTime(&pcc->pCertInfo->NotBefore, &ft) > 0 ||
|
|
CompareFileTime(&ft, &pcc->pCertInfo->NotAfter) > 0)
|
|
continue;
|
|
|
|
/* If key usage exists check for signing attribute */
|
|
if (CertGetIntendedKeyUsage(pcc->dwCertEncodingType,
|
|
pcc->pCertInfo,
|
|
key_usage, sizeof(key_usage))) {
|
|
if (!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
|
|
continue;
|
|
} else
|
|
if (GetLastError())
|
|
continue;
|
|
|
|
/*
|
|
* If enhanced key usage exists check for server auth attribute.
|
|
*
|
|
* Note "In a Microsoft environment, a certificate might also
|
|
* have EKU extended properties that specify valid uses for the
|
|
* certificate."
|
|
* The call below checks both, and behavior varies depending on
|
|
* what is found. For more details see CertGetEnhancedKeyUsage
|
|
* doc.
|
|
*/
|
|
if (!CertGetEnhancedKeyUsage(pcc, 0, NULL, &req_size))
|
|
continue;
|
|
|
|
if (req_size && req_size > ceu_alloc) {
|
|
void* tmp = lws_realloc(ceu, req_size, __func__);
|
|
|
|
if (!tmp) {
|
|
lwsl_err("%s: OOM", __func__);
|
|
break;
|
|
}
|
|
|
|
ceu = (CERT_ENHKEY_USAGE*)tmp;
|
|
ceu_alloc = req_size;
|
|
}
|
|
|
|
if (!CertGetEnhancedKeyUsage(pcc, 0, ceu, &req_size))
|
|
continue;
|
|
|
|
if (!ceu || (ceu && !ceu->cUsageIdentifier)) {
|
|
/*
|
|
* "If GetLastError returns CRYPT_E_NOT_FOUND, the
|
|
* certificate is good for all uses. If it returns
|
|
* zero, the certificate has no valid uses."
|
|
*/
|
|
if ((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
|
|
continue;
|
|
|
|
/* ... allow it... */
|
|
|
|
} else
|
|
if (ceu) {
|
|
BOOL found = FALSE;
|
|
DWORD i;
|
|
|
|
/*
|
|
* If there is a CEU, check that it specifies
|
|
* we can use the cert for server validation
|
|
*/
|
|
|
|
for (i = 0; i < ceu->cUsageIdentifier; i++) {
|
|
if (strcmp("1.3.6.1.5.5.7.3.1"
|
|
/* OID server auth */,
|
|
ceu->rgpszUsageIdentifier[i]))
|
|
continue;
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
/* Don't use cert if no usage match */
|
|
continue;
|
|
}
|
|
|
|
x509 = d2i_X509(NULL, &ecert, pcc->cbCertEncoded);
|
|
if (!x509)
|
|
/* We can't parse it as am X.509, skip it */
|
|
continue;
|
|
|
|
if (X509_STORE_add_cert(store, x509) == 1) {
|
|
lwsl_debug("%s: Imported cert \"%s\"\n", __func__,
|
|
cert_name);
|
|
imps++;
|
|
}
|
|
|
|
/*
|
|
* Treat failure as nonfatal, eg, may be dupe
|
|
*/
|
|
|
|
X509_free(x509);
|
|
} while (1);
|
|
|
|
lws_free(ceu);
|
|
CertFreeCertificateContext(pcc);
|
|
CertCloseStore(hStore, 0);
|
|
|
|
lwsl_notice("%s: Imported %d certs from plat store\n", __func__, imps);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
const char *
|
|
lws_plat_inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
|
|
{
|
|
WCHAR *buffer;
|
|
size_t bufferlen = (size_t)cnt;
|
|
BOOL ok = FALSE;
|
|
|
|
buffer = lws_malloc(bufferlen * 2, "inet_ntop");
|
|
if (!buffer) {
|
|
lwsl_err("Out of memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (af == AF_INET) {
|
|
struct sockaddr_in srcaddr;
|
|
memset(&srcaddr, 0, sizeof(srcaddr));
|
|
srcaddr.sin_family = AF_INET;
|
|
memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
|
|
|
|
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr,
|
|
sizeof(srcaddr), 0, buffer,
|
|
(LPDWORD)&bufferlen))
|
|
ok = TRUE;
|
|
#ifdef LWS_WITH_IPV6
|
|
} else if (af == AF_INET6) {
|
|
struct sockaddr_in6 srcaddr;
|
|
memset(&srcaddr, 0, sizeof(srcaddr));
|
|
srcaddr.sin6_family = AF_INET6;
|
|
memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr));
|
|
|
|
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr,
|
|
sizeof(srcaddr), 0, buffer,
|
|
(LPDWORD)&bufferlen))
|
|
ok = TRUE;
|
|
#endif
|
|
} else
|
|
lwsl_err("Unsupported type\n");
|
|
|
|
if (!ok) {
|
|
int rv = WSAGetLastError();
|
|
lwsl_err("WSAAddressToString() : %d\n", rv);
|
|
} else {
|
|
if (WideCharToMultiByte(CP_ACP, 0, buffer, (int)bufferlen, dst,
|
|
cnt, 0, NULL) <= 0)
|
|
ok = FALSE;
|
|
}
|
|
|
|
lws_free(buffer);
|
|
return ok ? dst : NULL;
|
|
}
|
|
|
|
int
|
|
lws_plat_inet_pton(int af, const char *src, void *dst)
|
|
{
|
|
WCHAR *buffer;
|
|
size_t bufferlen = strlen(src) + 1;
|
|
BOOL ok = FALSE;
|
|
|
|
buffer = lws_malloc(bufferlen * 2, "inet_pton");
|
|
if (!buffer) {
|
|
lwsl_err("Out of memory\n");
|
|
return -1;
|
|
}
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, src, (int)bufferlen, buffer,
|
|
(int)bufferlen) <= 0) {
|
|
lwsl_err("Failed to convert multi byte to wide char\n");
|
|
lws_free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
if (af == AF_INET) {
|
|
struct sockaddr_in dstaddr;
|
|
int dstaddrlen = sizeof(dstaddr);
|
|
|
|
memset(&dstaddr, 0, sizeof(dstaddr));
|
|
dstaddr.sin_family = AF_INET;
|
|
|
|
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
|
|
ok = TRUE;
|
|
memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
|
|
}
|
|
#ifdef LWS_WITH_IPV6
|
|
} else if (af == AF_INET6) {
|
|
struct sockaddr_in6 dstaddr;
|
|
int dstaddrlen = sizeof(dstaddr);
|
|
|
|
memset(&dstaddr, 0, sizeof(dstaddr));
|
|
dstaddr.sin6_family = AF_INET6;
|
|
|
|
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
|
|
ok = TRUE;
|
|
memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr));
|
|
}
|
|
#endif
|
|
} else
|
|
lwsl_err("Unsupported type\n");
|
|
|
|
if (!ok) {
|
|
int rv = WSAGetLastError();
|
|
lwsl_err("WSAAddressToString() : %d\n", rv);
|
|
}
|
|
|
|
lws_free(buffer);
|
|
return ok ? 1 : -1;
|
|
}
|
|
|
|
int
|
|
lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len)
|
|
{
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, size_t canned_len,
|
|
size_t n, int fd, const char *iface)
|
|
{
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
lws_plat_if_up(const char *ifname, int fd, int up)
|
|
{
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname)
|
|
{
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
lws_plat_ifconfig(int fd, uint8_t *ip, lws_dhcpc_ifstate_t *is)
|
|
{
|
|
lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
#if defined(LWS_WITH_MBEDTLS)
|
|
int
|
|
lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len)
|
|
{
|
|
int fd = ((mbedtls_net_context *) ctx)->fd;
|
|
int ret, en;
|
|
|
|
if (fd < 0)
|
|
return MBEDTLS_ERR_NET_INVALID_CONTEXT;
|
|
|
|
ret = send(fd, (const char *)buf, (unsigned int)len, 0);
|
|
if (ret >= 0)
|
|
return ret;
|
|
|
|
en = LWS_ERRNO;
|
|
if (en == EAGAIN || en == EWOULDBLOCK)
|
|
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
|
|
ret = WSAGetLastError();
|
|
lwsl_notice("%s: errno %d, GLE %d\n", __func__, en, ret);
|
|
if (ret == WSAECONNRESET )
|
|
return( MBEDTLS_ERR_NET_CONN_RESET );
|
|
|
|
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
}
|
|
|
|
int
|
|
lws_plat_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
|
|
{
|
|
int fd = ((mbedtls_net_context *) ctx)->fd;
|
|
int ret, en;
|
|
|
|
if (fd < 0)
|
|
return MBEDTLS_ERR_NET_INVALID_CONTEXT;
|
|
|
|
ret = (int)recv(fd, (char *)buf, (unsigned int)len, 0);
|
|
if (ret >= 0)
|
|
return ret;
|
|
|
|
en = LWS_ERRNO;
|
|
if (en == EAGAIN || en == EWOULDBLOCK || en == WSAEWOULDBLOCK)
|
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
|
|
ret = WSAGetLastError();
|
|
lwsl_notice("%s: errno %d, GLE %d\n", __func__, en, ret);
|
|
|
|
if (ret == WSAECONNRESET)
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
|
|
return MBEDTLS_ERR_NET_RECV_FAILED;
|
|
}
|
|
#endif
|
|
|