1773 lines
58 KiB
C
1773 lines
58 KiB
C
// lwIP stack provider glue. This file bridges the kernel network-device model into lwIP.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include <ntlite/ddk/ddk.h>
|
|
#include <ntlite/status.h>
|
|
#include <ntlite/ddi/io_driver.h>
|
|
#include <ntlite/ddk/debug.h>
|
|
#include <ntlite/ddk/scheduler.h>
|
|
#include <ntlite/ddk/settings.h>
|
|
#include <ntlite/ddk/semaphore.h>
|
|
#include <ntlite/ddk/object.h>
|
|
#include <ntlite/ddk/memory.h>
|
|
#include <ntlite/ddi/net_device.h>
|
|
#include <ntlite/ddi/net_stack.h>
|
|
|
|
#include <lwip/init.h>
|
|
#include <lwip/tcpip.h>
|
|
#include <lwip/sys.h>
|
|
#include <lwip/netif.h>
|
|
#include <lwip/pbuf.h>
|
|
#include <lwip/etharp.h>
|
|
#include <lwip/ethip6.h>
|
|
#include <lwip/dhcp.h>
|
|
#include <lwip/dns.h>
|
|
#include <lwip/ip4_addr.h>
|
|
#include <lwip/ip6_addr.h>
|
|
#include <lwip/ip_addr.h>
|
|
#include <lwip/api.h>
|
|
#include <lwip/err.h>
|
|
#include <lwip/prot/ip.h>
|
|
#include <lwip/prot/ip6.h>
|
|
#include <netif/ethernet.h>
|
|
|
|
#define LWIP_MAX_NETIFS 8u
|
|
#define LWIP_PROVIDER_SOCKET_IO_MAX_BYTES (1024u * 1024u)
|
|
#define LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES 65507u
|
|
#define LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES 65535u
|
|
#define LWIP_PROVIDER_CANCEL_POLL_MS 25u
|
|
|
|
#define LWIP_SETTINGS_DEFAULT_SECTION "network.default"
|
|
#define LWIP_SETTINGS_SECTION_PREFIX "network."
|
|
#define LWIP_SETTINGS_VALUE_MAX 64u
|
|
|
|
typedef struct LWIP_NETIF_CONFIG {
|
|
uint8_t UseDhcp;
|
|
uint8_t HasDns4;
|
|
uint8_t EnableIpv6;
|
|
uint8_t Ipv6Autoconfig;
|
|
uint8_t HasStaticIpv6;
|
|
uint8_t HasDns6;
|
|
char Section[64];
|
|
ip4_addr_t Address;
|
|
ip4_addr_t Netmask;
|
|
ip4_addr_t Gateway;
|
|
ip4_addr_t Dns4;
|
|
ip6_addr_t Address6;
|
|
ip6_addr_t Dns6;
|
|
} LWIP_NETIF_CONFIG;
|
|
|
|
typedef struct LWIP_BINDING {
|
|
NET_DEVICE* Device;
|
|
struct netif Netif;
|
|
LWIP_NETIF_CONFIG Config;
|
|
uint8_t Attached;
|
|
} LWIP_BINDING;
|
|
|
|
typedef struct LWIP_ATTACH_CTX {
|
|
LWIP_BINDING* Binding;
|
|
SEMAPHORE* Done;
|
|
err_t Result;
|
|
} LWIP_ATTACH_CTX;
|
|
|
|
typedef struct LWIP_PROVIDER_SOCKET {
|
|
SEMAPHORE* Lock;
|
|
struct netconn* Conn;
|
|
struct netbuf* ReceiveBuffer;
|
|
size_t ReceiveOffset;
|
|
|
|
uint32_t Domain;
|
|
uint32_t Type;
|
|
uint32_t Protocol;
|
|
uint8_t Bound;
|
|
uint8_t Listening;
|
|
uint8_t Connected;
|
|
uint8_t ShutdownRead;
|
|
uint8_t ShutdownWrite;
|
|
uint8_t NonBlocking;
|
|
|
|
uint32_t ReceiveTimeoutMs;
|
|
uint32_t SendTimeoutMs;
|
|
|
|
volatile int32_t PollReadEvents;
|
|
volatile int32_t PollWriteEvents;
|
|
volatile int32_t PollErrorEvents;
|
|
} LWIP_PROVIDER_SOCKET;
|
|
|
|
static LWIP_BINDING* gBindings[LWIP_MAX_NETIFS];
|
|
static volatile int gLwipInitialized = 0;
|
|
static const NTL_DDK* gLwipDdk;
|
|
|
|
static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device);
|
|
static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length);
|
|
static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket);
|
|
static void LwipProviderSocketClose(void* socket);
|
|
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
|
|
static KSTATUS LwipProviderListen(void* socket, uint32_t backlog);
|
|
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested);
|
|
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested);
|
|
static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
|
|
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested);
|
|
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
|
|
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested);
|
|
static KSTATUS LwipProviderShutdown(void* socket, uint32_t how);
|
|
static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize);
|
|
static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
|
|
static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut);
|
|
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
|
|
static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp);
|
|
|
|
static DRIVER_OBJECT gLwipDriver;
|
|
static DEVICE_OBJECT* gLwipDevice;
|
|
|
|
static NET_STACK_PROVIDER gLwipProvider = {
|
|
NET_STACK_PROVIDER_VERSION,
|
|
"lwIP",
|
|
NULL,
|
|
LwipProviderAttachDevice,
|
|
LwipProviderReceiveFrame,
|
|
};
|
|
|
|
// Implements the err To Status operation.
|
|
static KSTATUS LwipErrToStatus(err_t err) {
|
|
switch (err) {
|
|
case ERR_OK: return NTLSTATUS_SUCCESS;
|
|
case ERR_MEM: return NTLSTATUS_NO_MEMORY;
|
|
case ERR_BUF: return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
case ERR_TIMEOUT: return NTLSTATUS_TIMEOUT;
|
|
case ERR_WOULDBLOCK:return NTLSTATUS_WOULD_BLOCK;
|
|
case ERR_RTE: return NTLSTATUS_NOT_FOUND;
|
|
case ERR_VAL: return NTLSTATUS_INVALID_PARAMETER;
|
|
case ERR_ARG: return NTLSTATUS_INVALID_PARAMETER;
|
|
case ERR_USE: return NTLSTATUS_ALREADY_EXISTS;
|
|
case ERR_ISCONN: return NTLSTATUS_ALREADY_EXISTS;
|
|
case ERR_CONN: return NTLSTATUS_OBJECT_DEAD;
|
|
case ERR_CLSD: return NTLSTATUS_OBJECT_DEAD;
|
|
case ERR_ABRT: return NTLSTATUS_OBJECT_DEAD;
|
|
case ERR_RST: return NTLSTATUS_OBJECT_DEAD;
|
|
default: return NTLSTATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Implements the ascii Is Digit operation.
|
|
static int LwipAsciiIsDigit(char c) {
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
// Parses ipv4 Part.
|
|
static int LwipParseIpv4Part(const char** cursor, uint8_t* out) {
|
|
const char* p = *cursor;
|
|
uint32_t value = 0;
|
|
uint32_t digits = 0;
|
|
|
|
while (LwipAsciiIsDigit(*p)) {
|
|
value = value * 10u + (uint32_t)(*p - '0');
|
|
if (value > 255u)
|
|
return 0;
|
|
p++;
|
|
digits++;
|
|
}
|
|
|
|
if (digits == 0)
|
|
return 0;
|
|
|
|
*out = (uint8_t)value;
|
|
*cursor = p;
|
|
return 1;
|
|
}
|
|
|
|
// Parses ipv4.
|
|
static int LwipParseIpv4(const char* text, ip4_addr_t* out) {
|
|
if (!text || !out)
|
|
return 0;
|
|
|
|
const char* p = text;
|
|
uint8_t a = 0, b = 0, c = 0, d = 0;
|
|
if (!LwipParseIpv4Part(&p, &a) || *p++ != '.') return 0;
|
|
if (!LwipParseIpv4Part(&p, &b) || *p++ != '.') return 0;
|
|
if (!LwipParseIpv4Part(&p, &c) || *p++ != '.') return 0;
|
|
if (!LwipParseIpv4Part(&p, &d) || *p != '\0') return 0;
|
|
|
|
IP4_ADDR(out, a, b, c, d);
|
|
return 1;
|
|
}
|
|
|
|
static KSTATUS LwipGetSettingIpv4(const char* section, const char* key,
|
|
ip4_addr_t* out, int required) {
|
|
char value[LWIP_SETTINGS_VALUE_MAX];
|
|
KSTATUS st = KeGetSettingString(section, key, value, sizeof(value));
|
|
if (st == NTLSTATUS_NOT_FOUND && !required)
|
|
return NTLSTATUS_NOT_FOUND;
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
if (!LwipParseIpv4(value, out)) {
|
|
DbgPrint("lwIP: invalid IPv4 setting [%s] %s=%s\n", section, key, value);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS LwipGetSettingIpv6(const char* section, const char* key,
|
|
ip6_addr_t* out, int required) {
|
|
char value[LWIP_SETTINGS_VALUE_MAX];
|
|
KSTATUS st = KeGetSettingString(section, key, value, sizeof(value));
|
|
if (st == NTLSTATUS_NOT_FOUND && !required)
|
|
return NTLSTATUS_NOT_FOUND;
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
if (!ip6addr_aton(value, out)) {
|
|
DbgPrint("lwIP: invalid IPv6 setting [%s] %s=%s\n", section, key, value);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS LwipLoadIpv6Config(const char* section, LWIP_NETIF_CONFIG* cfg) {
|
|
char mode[LWIP_SETTINGS_VALUE_MAX];
|
|
KSTATUS st = KeGetSettingString(section, "ipv6", mode, sizeof(mode));
|
|
if (st == NTLSTATUS_NOT_FOUND) {
|
|
cfg->EnableIpv6 = 1;
|
|
cfg->Ipv6Autoconfig = 1;
|
|
} else if (!KSUCCESS(st)) {
|
|
return st;
|
|
} else if (strcmp(mode, "off") == 0 || strcmp(mode, "disabled") == 0) {
|
|
cfg->EnableIpv6 = 0;
|
|
} else if (strcmp(mode, "auto") == 0) {
|
|
cfg->EnableIpv6 = 1;
|
|
cfg->Ipv6Autoconfig = 1;
|
|
} else if (strcmp(mode, "static") == 0) {
|
|
cfg->EnableIpv6 = 1;
|
|
cfg->HasStaticIpv6 = 1;
|
|
st = LwipGetSettingIpv6(section, "address6", &cfg->Address6, 1);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
} else {
|
|
DbgPrint("lwIP: invalid IPv6 mode [%s] ipv6=%s\n", section, mode);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!cfg->EnableIpv6)
|
|
return NTLSTATUS_SUCCESS;
|
|
|
|
st = LwipGetSettingIpv6(section, "dns6", &cfg->Dns6, 0);
|
|
if (KSUCCESS(st))
|
|
cfg->HasDns6 = 1;
|
|
else if (st != NTLSTATUS_NOT_FOUND)
|
|
return st;
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Copies section Name.
|
|
static KSTATUS LwipCopySectionName(char* out, size_t outSize, const char* prefix, const char* name) {
|
|
if (!out || !outSize || !prefix || !name)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
size_t prefixLen = strlen(prefix);
|
|
size_t nameLen = strlen(name);
|
|
if (prefixLen + nameLen + 1 > outSize)
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
|
|
memcpy(out, prefix, prefixLen);
|
|
memcpy(out + prefixLen, name, nameLen + 1);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Loads config From Section.
|
|
static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG* cfg) {
|
|
if (!section || !cfg)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
char mode[LWIP_SETTINGS_VALUE_MAX];
|
|
KSTATUS st = KeGetSettingString(section, "mode", mode, sizeof(mode));
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
size_t sectionLen = strlen(section);
|
|
if (sectionLen >= sizeof(cfg->Section))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
memcpy(cfg->Section, section, sectionLen + 1);
|
|
|
|
if (strcmp(mode, "static") == 0) {
|
|
cfg->UseDhcp = 0;
|
|
st = LwipGetSettingIpv4(section, "address", &cfg->Address, 1);
|
|
if (!KSUCCESS(st)) return st;
|
|
st = LwipGetSettingIpv4(section, "netmask", &cfg->Netmask, 1);
|
|
if (!KSUCCESS(st)) return st;
|
|
st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, 1);
|
|
if (!KSUCCESS(st)) return st;
|
|
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 1);
|
|
if (!KSUCCESS(st)) return st;
|
|
cfg->HasDns4 = 1;
|
|
return LwipLoadIpv6Config(section, cfg);
|
|
}
|
|
|
|
if (strcmp(mode, "dhcp") == 0) {
|
|
cfg->UseDhcp = 1;
|
|
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 0);
|
|
if (KSUCCESS(st))
|
|
cfg->HasDns4 = 1;
|
|
else if (st != NTLSTATUS_NOT_FOUND)
|
|
return st;
|
|
return LwipLoadIpv6Config(section, cfg);
|
|
}
|
|
|
|
DbgPrint("lwIP: invalid network mode [%s] mode=%s\n", section, mode);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Loads netif Config.
|
|
static KSTATUS LwipLoadNetifConfig(const NET_DEVICE* device, LWIP_NETIF_CONFIG* cfg) {
|
|
if (!device || !cfg)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
char section[64];
|
|
KSTATUS st = LwipCopySectionName(section, sizeof(section),
|
|
LWIP_SETTINGS_SECTION_PREFIX, device->Name);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
st = LwipLoadConfigFromSection(section, cfg);
|
|
if (KSUCCESS(st))
|
|
return NTLSTATUS_SUCCESS;
|
|
|
|
if (st != NTLSTATUS_NOT_FOUND) {
|
|
DbgPrint("lwIP: invalid settings for [%s] (0x%x)\n", section, st);
|
|
return st;
|
|
}
|
|
|
|
st = LwipLoadConfigFromSection(LWIP_SETTINGS_DEFAULT_SECTION, cfg);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: missing or invalid settings for %s; expected [%s] or [%s] (0x%x)\n",
|
|
device->Name, section, LWIP_SETTINGS_DEFAULT_SECTION, st);
|
|
return st;
|
|
}
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the socket Lock operation.
|
|
static KSTATUS LwipSocketLock(LWIP_PROVIDER_SOCKET* s) {
|
|
if (!s || !s->Lock)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
return KeWaitSemaphore(s->Lock);
|
|
}
|
|
|
|
// Implements the socket Unlock operation.
|
|
static void LwipSocketUnlock(LWIP_PROVIDER_SOCKET* s) {
|
|
if (s && s->Lock)
|
|
KeSignalSemaphore(s->Lock);
|
|
}
|
|
|
|
// Implements the socket Callback operation.
|
|
static void LwipSocketCallback(struct netconn* conn, enum netconn_evt evt, u16_t len) {
|
|
(void)len;
|
|
if (!conn)
|
|
return;
|
|
|
|
void* callbackArg = netconn_get_callback_arg(conn);
|
|
uintptr_t rawArg = (uintptr_t)callbackArg;
|
|
if (!callbackArg)
|
|
return;
|
|
|
|
// Accepted netconns inherit the listener callback before the wrapper can
|
|
// replace lwIP's internal negative socket sentinel with our socket object.
|
|
if (rawArg >= 0x80000000ull && rawArg <= 0xffffffffull)
|
|
return;
|
|
|
|
LWIP_PROVIDER_SOCKET* socket = (LWIP_PROVIDER_SOCKET*)callbackArg;
|
|
|
|
switch (evt) {
|
|
case NETCONN_EVT_RCVPLUS:
|
|
socket->PollReadEvents++;
|
|
break;
|
|
case NETCONN_EVT_RCVMINUS:
|
|
if (socket->PollReadEvents > 0)
|
|
socket->PollReadEvents--;
|
|
break;
|
|
case NETCONN_EVT_SENDPLUS:
|
|
socket->PollWriteEvents++;
|
|
break;
|
|
case NETCONN_EVT_SENDMINUS:
|
|
if (socket->PollWriteEvents > 0)
|
|
socket->PollWriteEvents--;
|
|
break;
|
|
case NETCONN_EVT_ERROR:
|
|
socket->PollErrorEvents++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32_t protocol,
|
|
struct netconn* conn, LWIP_PROVIDER_SOCKET** out) {
|
|
if (!conn || !out)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*out = NULL;
|
|
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)MmAllocatePool(sizeof(*s));
|
|
if (!s)
|
|
return NTLSTATUS_NO_MEMORY;
|
|
memset(s, 0, sizeof(*s));
|
|
|
|
KSTATUS st = KeSemaphoreCreate(1, 1, &s->Lock);
|
|
if (!KSUCCESS(st)) {
|
|
MmFreePool(s);
|
|
return st;
|
|
}
|
|
|
|
s->Conn = conn;
|
|
s->Domain = domain;
|
|
s->Type = type;
|
|
s->Protocol = protocol;
|
|
netconn_set_callback_arg(conn, s);
|
|
conn->callback = LwipSocketCallback;
|
|
netconn_set_recvtimeout(conn, 0);
|
|
netconn_set_sendtimeout(conn, 0);
|
|
*out = s;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Returns whether the IRP cancel token has been set by SysCancelIo.
|
|
static int LwipSocketCancelRequested(volatile uint8_t* cancelRequested) {
|
|
return cancelRequested && *cancelRequested;
|
|
}
|
|
|
|
// Computes the bounded wait slice used by cancel-aware receive and accept loops.
|
|
static uint32_t LwipSocketCancelWaitSlice(uint32_t remainingMs) {
|
|
if (remainingMs != 0 && remainingMs < LWIP_PROVIDER_CANCEL_POLL_MS)
|
|
return remainingMs;
|
|
return LWIP_PROVIDER_CANCEL_POLL_MS;
|
|
}
|
|
|
|
// Waits for a receive-style netconn mailbox while periodically observing cancellation.
|
|
static KSTATUS LwipSocketWaitRecvNetbuf(LWIP_PROVIDER_SOCKET* s,
|
|
struct netbuf** bufferOut,
|
|
volatile uint8_t* cancelRequested,
|
|
err_t* errOut) {
|
|
if (!s || !s->Conn || !bufferOut || !errOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*bufferOut = NULL;
|
|
*errOut = ERR_OK;
|
|
|
|
if (!cancelRequested || s->NonBlocking) {
|
|
*errOut = netconn_recv(s->Conn, bufferOut);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
uint32_t originalTimeout = s->ReceiveTimeoutMs;
|
|
uint32_t elapsed = 0;
|
|
for (;;) {
|
|
if (LwipSocketCancelRequested(cancelRequested)) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
return NTLSTATUS_CANCELLED;
|
|
}
|
|
|
|
uint32_t remaining = 0;
|
|
if (originalTimeout != 0) {
|
|
if (elapsed >= originalTimeout) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
*errOut = ERR_TIMEOUT;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
remaining = originalTimeout - elapsed;
|
|
}
|
|
|
|
uint32_t slice = LwipSocketCancelWaitSlice(remaining);
|
|
netconn_set_recvtimeout(s->Conn, slice);
|
|
err_t err = netconn_recv(s->Conn, bufferOut);
|
|
if (err != ERR_TIMEOUT) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
*errOut = err;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
elapsed += slice;
|
|
}
|
|
}
|
|
|
|
// Waits for accept while preserving the caller's receive timeout model.
|
|
static KSTATUS LwipSocketWaitAccept(LWIP_PROVIDER_SOCKET* s,
|
|
struct netconn** acceptedOut,
|
|
volatile uint8_t* cancelRequested,
|
|
err_t* errOut) {
|
|
if (!s || !s->Conn || !acceptedOut || !errOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*acceptedOut = NULL;
|
|
*errOut = ERR_OK;
|
|
|
|
if (!cancelRequested || s->NonBlocking) {
|
|
*errOut = netconn_accept(s->Conn, acceptedOut);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
uint32_t originalTimeout = s->ReceiveTimeoutMs;
|
|
uint32_t elapsed = 0;
|
|
for (;;) {
|
|
if (LwipSocketCancelRequested(cancelRequested)) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
return NTLSTATUS_CANCELLED;
|
|
}
|
|
|
|
uint32_t remaining = 0;
|
|
if (originalTimeout != 0) {
|
|
if (elapsed >= originalTimeout) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
*errOut = ERR_TIMEOUT;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
remaining = originalTimeout - elapsed;
|
|
}
|
|
|
|
uint32_t slice = LwipSocketCancelWaitSlice(remaining);
|
|
netconn_set_recvtimeout(s->Conn, slice);
|
|
err_t err = netconn_accept(s->Conn, acceptedOut);
|
|
if (err != ERR_TIMEOUT) {
|
|
netconn_set_recvtimeout(s->Conn, originalTimeout);
|
|
*errOut = err;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
elapsed += slice;
|
|
}
|
|
}
|
|
|
|
static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
|
|
if (!address || addressSize != sizeof(SYS_SOCKET_ADDRESS_K))
|
|
return 0;
|
|
if (address->Size != sizeof(SYS_SOCKET_ADDRESS_K))
|
|
return 0;
|
|
return address->Family == SYS_AF_INET || address->Family == SYS_AF_INET6;
|
|
}
|
|
|
|
static int LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) {
|
|
if (!s || !address)
|
|
return 0;
|
|
if (s->Domain == SYS_AF_INET)
|
|
return address->Family == SYS_AF_INET;
|
|
if (s->Domain == SYS_AF_INET6)
|
|
return address->Family == SYS_AF_INET6;
|
|
return 0;
|
|
}
|
|
|
|
// Implements the socket Make Ip operation.
|
|
static KSTATUS LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_K* address, ip_addr_t* out) {
|
|
if (!address || !out)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (address->Family == SYS_AF_INET) {
|
|
IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
if (address->Family == SYS_AF_INET6) {
|
|
uint32_t a0 = ((uint32_t)address->Address[0] << 24) | ((uint32_t)address->Address[1] << 16) |
|
|
((uint32_t)address->Address[2] << 8) | (uint32_t)address->Address[3];
|
|
uint32_t a1 = ((uint32_t)address->Address[4] << 24) | ((uint32_t)address->Address[5] << 16) |
|
|
((uint32_t)address->Address[6] << 8) | (uint32_t)address->Address[7];
|
|
uint32_t a2 = ((uint32_t)address->Address[8] << 24) | ((uint32_t)address->Address[9] << 16) |
|
|
((uint32_t)address->Address[10] << 8) | (uint32_t)address->Address[11];
|
|
uint32_t a3 = ((uint32_t)address->Address[12] << 24) | ((uint32_t)address->Address[13] << 16) |
|
|
((uint32_t)address->Address[14] << 8) | (uint32_t)address->Address[15];
|
|
IP_ADDR6_HOST(out, a0, a1, a2, a3);
|
|
ip6_addr_set_zone(ip_2_ip6(out), (u8_t)address->ScopeId);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Implements the socket Fill Address operation.
|
|
static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_K* out, const ip_addr_t* ip, u16_t port) {
|
|
if (!out)
|
|
return;
|
|
memset(out, 0, sizeof(*out));
|
|
out->Size = sizeof(*out);
|
|
out->Port = port;
|
|
if (!ip)
|
|
return;
|
|
if (IP_IS_V6(ip)) {
|
|
out->Family = SYS_AF_INET6;
|
|
out->ScopeId = ip6_addr_zone(ip_2_ip6(ip));
|
|
for (size_t i = 0; i < 4; ++i) {
|
|
uint32_t word = lwip_htonl(ip_2_ip6(ip)->addr[i]);
|
|
out->Address[i * 4 + 0] = (uint8_t)(word >> 24);
|
|
out->Address[i * 4 + 1] = (uint8_t)(word >> 16);
|
|
out->Address[i * 4 + 2] = (uint8_t)(word >> 8);
|
|
out->Address[i * 4 + 3] = (uint8_t)word;
|
|
}
|
|
return;
|
|
}
|
|
|
|
out->Family = SYS_AF_INET;
|
|
out->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(ip));
|
|
out->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(ip));
|
|
out->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(ip));
|
|
out->Address[3] = (uint8_t)ip4_addr4(ip_2_ip4(ip));
|
|
}
|
|
|
|
// Implements the low Level Output operation.
|
|
static err_t LwipLowLevelOutput(struct netif* netif, struct pbuf* p) {
|
|
LWIP_BINDING* b = (LWIP_BINDING*)netif->state;
|
|
if (!b || !b->Device || !p || p->tot_len < NET_ETHERNET_HEADER_LENGTH)
|
|
return ERR_ARG;
|
|
if (p->tot_len > NET_ETHERNET_MAX_FRAME)
|
|
return ERR_BUF;
|
|
|
|
uint8_t* frame = (uint8_t*)MmAllocatePool(p->tot_len);
|
|
if (!frame)
|
|
return ERR_MEM;
|
|
|
|
size_t off = 0;
|
|
for (struct pbuf* q = p; q != NULL; q = q->next) {
|
|
if (off + q->len > p->tot_len) {
|
|
MmFreePool(frame);
|
|
return ERR_BUF;
|
|
}
|
|
memcpy(frame + off, q->payload, q->len);
|
|
off += q->len;
|
|
}
|
|
|
|
KSTATUS st = NetSendFrame(b->Device->Index, frame, p->tot_len);
|
|
MmFreePool(frame);
|
|
|
|
return KSUCCESS(st) ? ERR_OK : ERR_IF;
|
|
}
|
|
|
|
// Implements the netif Init operation.
|
|
static err_t LwipNetifInit(struct netif* netif) {
|
|
LWIP_BINDING* b = (LWIP_BINDING*)netif->state;
|
|
if (!b || !b->Device)
|
|
return ERR_ARG;
|
|
|
|
NET_DEVICE* device = b->Device;
|
|
netif->name[0] = 'e';
|
|
netif->name[1] = 'n';
|
|
netif->output = etharp_output;
|
|
#if LWIP_IPV6
|
|
netif->output_ip6 = ethip6_output;
|
|
#endif
|
|
netif->linkoutput = LwipLowLevelOutput;
|
|
netif->mtu = (u16_t)(device->Mtu ? device->Mtu : NET_MTU_ETHERNET);
|
|
netif->hwaddr_len = NET_ETHERNET_ADDRESS_LENGTH;
|
|
memcpy(netif->hwaddr, device->HardwareAddress, NET_ETHERNET_ADDRESS_LENGTH);
|
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
|
#if LWIP_IPV6_MLD
|
|
netif->flags |= NETIF_FLAG_MLD6;
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
// Attaches callback.
|
|
static void LwipAttachCallback(void* arg) {
|
|
LWIP_ATTACH_CTX* ctx = (LWIP_ATTACH_CTX*)arg;
|
|
LWIP_BINDING* b = ctx->Binding;
|
|
NET_DEVICE* device = b->Device;
|
|
|
|
ip4_addr_t ipaddr;
|
|
ip4_addr_t netmask;
|
|
ip4_addr_t gw;
|
|
|
|
if (b->Config.UseDhcp) {
|
|
ip4_addr_set_zero(&ipaddr);
|
|
ip4_addr_set_zero(&netmask);
|
|
ip4_addr_set_zero(&gw);
|
|
} else {
|
|
ipaddr = b->Config.Address;
|
|
netmask = b->Config.Netmask;
|
|
gw = b->Config.Gateway;
|
|
}
|
|
|
|
struct netif* n = netif_add(&b->Netif,
|
|
&ipaddr,
|
|
&netmask,
|
|
&gw,
|
|
b,
|
|
LwipNetifInit,
|
|
tcpip_input);
|
|
if (!n) {
|
|
ctx->Result = ERR_IF;
|
|
if (ctx->Done)
|
|
KeSignalSemaphore(ctx->Done);
|
|
return;
|
|
}
|
|
|
|
netif_set_link_up(n);
|
|
netif_set_up(n);
|
|
|
|
if (netif_default == NULL)
|
|
netif_set_default(n);
|
|
|
|
if (b->Config.HasDns4) {
|
|
ip_addr_t dns;
|
|
ip_addr_copy_from_ip4(dns, b->Config.Dns4);
|
|
dns_setserver(0, &dns);
|
|
}
|
|
|
|
#if LWIP_IPV6
|
|
if (b->Config.EnableIpv6) {
|
|
netif_create_ip6_linklocal_address(n, 1);
|
|
if (b->Config.Ipv6Autoconfig)
|
|
n->ip6_autoconfig_enabled = 1;
|
|
if (b->Config.HasStaticIpv6) {
|
|
netif_ip6_addr_set(n, 1, &b->Config.Address6);
|
|
netif_ip6_addr_set_state(n, 1, IP6_ADDR_PREFERRED);
|
|
}
|
|
if (b->Config.HasDns6) {
|
|
ip_addr_t dns6;
|
|
ip_addr_copy_from_ip6(dns6, b->Config.Dns6);
|
|
dns_setserver(1, &dns6);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (b->Config.UseDhcp) {
|
|
#if LWIP_DHCP
|
|
err_t dhcpErr = dhcp_start(n);
|
|
if (dhcpErr != ERR_OK) {
|
|
ctx->Result = dhcpErr;
|
|
if (ctx->Done)
|
|
KeSignalSemaphore(ctx->Done);
|
|
return;
|
|
}
|
|
#else
|
|
ctx->Result = ERR_IF;
|
|
if (ctx->Done)
|
|
KeSignalSemaphore(ctx->Done);
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
b->Attached = 1;
|
|
ctx->Result = ERR_OK;
|
|
if (ctx->Done)
|
|
KeSignalSemaphore(ctx->Done);
|
|
}
|
|
|
|
// Initializes done.
|
|
static void LwipInitDone(void* arg) {
|
|
SEMAPHORE* sem = (SEMAPHORE*)arg;
|
|
KeSignalSemaphore(sem);
|
|
}
|
|
|
|
// Delays the current provider thread through the DDK scheduler table.
|
|
static KSTATUS LwipDelayThread(uint64_t milliseconds) {
|
|
if (!gLwipDdk || !gLwipDdk->Scheduler || !gLwipDdk->Scheduler->DelayThread)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
return gLwipDdk->Scheduler->DelayThread(milliseconds);
|
|
}
|
|
|
|
// Implements the stack Initialize operation.
|
|
static KSTATUS LwipStackInitialize(const NTL_DDK* ddk) {
|
|
if (gLwipInitialized)
|
|
return NTLSTATUS_SUCCESS;
|
|
if (!ddk || !ddk->Timer || !ddk->Timer->QueryTicks ||
|
|
!ddk->Scheduler || !ddk->Scheduler->DelayThread)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
gLwipDdk = ddk;
|
|
LwipPortSetTimerQuery(ddk->Timer->QueryTicks);
|
|
|
|
SEMAPHORE* done = 0;
|
|
KSTATUS st = KeSemaphoreCreate(0, 1, &done);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
tcpip_init(LwipInitDone, done);
|
|
st = KeWaitSemaphore(done);
|
|
ObDereferenceObject(&done->Header);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
gLwipInitialized = 1;
|
|
DbgPrint("lwIP: tcpip thread initialized\n");
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Attach Device operation.
|
|
static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device) {
|
|
if (!gLwipInitialized)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (!device || device->Index >= LWIP_MAX_NETIFS)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (device->HardwareAddressLength != NET_ETHERNET_ADDRESS_LENGTH)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
if (gBindings[device->Index])
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
|
|
LWIP_BINDING* b = (LWIP_BINDING*)MmAllocatePool(sizeof(*b));
|
|
if (!b)
|
|
return NTLSTATUS_NO_MEMORY;
|
|
memset(b, 0, sizeof(*b));
|
|
b->Device = device;
|
|
|
|
KSTATUS st = LwipLoadNetifConfig(device, &b->Config);
|
|
if (!KSUCCESS(st)) {
|
|
MmFreePool(b);
|
|
return st;
|
|
}
|
|
|
|
SEMAPHORE* done = 0;
|
|
st = KeSemaphoreCreate(0, 1, &done);
|
|
if (!KSUCCESS(st)) {
|
|
MmFreePool(b);
|
|
return st;
|
|
}
|
|
|
|
LWIP_ATTACH_CTX ctx;
|
|
ctx.Binding = b;
|
|
ctx.Done = done;
|
|
ctx.Result = ERR_IF;
|
|
|
|
err_t err = tcpip_callback(LwipAttachCallback, &ctx);
|
|
if (err == ERR_OK)
|
|
st = KeWaitSemaphore(done);
|
|
else
|
|
st = NTLSTATUS_FAILURE;
|
|
|
|
ObDereferenceObject(&done->Header);
|
|
|
|
if (!KSUCCESS(st) || ctx.Result != ERR_OK) {
|
|
MmFreePool(b);
|
|
return KSUCCESS(st) ? NTLSTATUS_FAILURE : st;
|
|
}
|
|
|
|
gBindings[device->Index] = b;
|
|
DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s ipv6=%s\n",
|
|
device->Name,
|
|
b->Netif.name[0], b->Netif.name[1], b->Netif.num,
|
|
b->Config.Section,
|
|
b->Config.UseDhcp ? "dhcp" : "static",
|
|
b->Config.EnableIpv6 ? (b->Config.HasStaticIpv6 ? "static" : "auto") : "off");
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Receive Frame operation.
|
|
static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length) {
|
|
if (!gLwipInitialized)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (!device || device->Index >= LWIP_MAX_NETIFS || !frame)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
LWIP_BINDING* b = gBindings[device->Index];
|
|
if (!b || !b->Attached)
|
|
return NTLSTATUS_NOT_FOUND;
|
|
|
|
if (length < NET_ETHERNET_HEADER_LENGTH || length > NET_ETHERNET_MAX_FRAME)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
struct pbuf* p = pbuf_alloc(PBUF_RAW, (u16_t)length, PBUF_POOL);
|
|
if (!p)
|
|
p = pbuf_alloc(PBUF_RAW, (u16_t)length, PBUF_RAM);
|
|
if (!p)
|
|
return NTLSTATUS_NO_MEMORY;
|
|
|
|
if (pbuf_take(p, frame, (u16_t)length) != ERR_OK) {
|
|
pbuf_free(p);
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
err_t err = b->Netif.input(p, &b->Netif);
|
|
if (err != ERR_OK) {
|
|
pbuf_free(p);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Socket Create operation.
|
|
static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket) {
|
|
if (!outSocket)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*outSocket = NULL;
|
|
|
|
if (!gLwipInitialized)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (domain != SYS_AF_INET && domain != SYS_AF_INET6)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
int ipv6 = domain == SYS_AF_INET6;
|
|
enum netconn_type netconnType;
|
|
u8_t rawProtocol = 0;
|
|
if (type == SYS_SOCK_STREAM) {
|
|
if (protocol != 0 && protocol != SYS_IPPROTO_TCP)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
netconnType = ipv6 ? NETCONN_TCP_IPV6 : NETCONN_TCP;
|
|
protocol = SYS_IPPROTO_TCP;
|
|
} else if (type == SYS_SOCK_DGRAM) {
|
|
if (protocol != 0 && protocol != SYS_IPPROTO_UDP)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
netconnType = ipv6 ? NETCONN_UDP_IPV6 : NETCONN_UDP;
|
|
protocol = SYS_IPPROTO_UDP;
|
|
} else if (type == SYS_SOCK_RAW) {
|
|
if ((!ipv6 && protocol != SYS_IPPROTO_ICMP) || (ipv6 && protocol != SYS_IPPROTO_ICMPV6))
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
netconnType = ipv6 ? NETCONN_RAW_IPV6 : NETCONN_RAW;
|
|
rawProtocol = ipv6 ? IP6_NEXTH_ICMP6 : IP_PROTO_ICMP;
|
|
} else {
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
struct netconn* conn = netconn_new_with_proto_and_callback(netconnType, rawProtocol, NULL);
|
|
if (!conn)
|
|
return NTLSTATUS_NO_MEMORY;
|
|
|
|
LWIP_PROVIDER_SOCKET* s = NULL;
|
|
KSTATUS st = LwipSocketAllocateWithConn(domain, type, protocol, conn, &s);
|
|
if (!KSUCCESS(st)) {
|
|
netconn_delete(conn);
|
|
return st;
|
|
}
|
|
|
|
*outSocket = s;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Socket Close operation.
|
|
static void LwipProviderSocketClose(void* socket) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s)
|
|
return;
|
|
|
|
if (s->ReceiveBuffer) {
|
|
netbuf_delete(s->ReceiveBuffer);
|
|
s->ReceiveBuffer = NULL;
|
|
s->ReceiveOffset = 0;
|
|
}
|
|
|
|
if (s->Conn) {
|
|
netconn_set_callback_arg(s->Conn, NULL);
|
|
(void)netconn_close(s->Conn);
|
|
netconn_delete(s->Conn);
|
|
s->Conn = NULL;
|
|
}
|
|
|
|
if (s->Lock) {
|
|
ObDereferenceObject(&s->Lock->Header);
|
|
s->Lock = NULL;
|
|
}
|
|
|
|
MmFreePool(s);
|
|
}
|
|
|
|
// Implements the provider Bind operation.
|
|
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s || !address)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketAddressValid(address, addressSize))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketFamilyMatches(s, address))
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->Connected || s->Listening || s->ShutdownRead || s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ip_addr_t ip;
|
|
st = LwipSocketMakeIp(address, &ip);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
err_t err = netconn_bind(s->Conn, &ip, address->Port);
|
|
if (err == ERR_OK)
|
|
s->Bound = 1;
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
// Implements the provider Listen operation.
|
|
static KSTATUS LwipProviderListen(void* socket, uint32_t backlog) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (s->Type != SYS_SOCK_STREAM || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (backlog > 255u)
|
|
backlog = 255u;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->Connected || s->ShutdownRead || s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
err_t err = netconn_listen_with_backlog(s->Conn, (u8_t)backlog);
|
|
if (err == ERR_OK) {
|
|
s->Bound = 1;
|
|
s->Listening = 1;
|
|
}
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
// Implements the provider Accept operation.
|
|
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s || !acceptedOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*acceptedOut = NULL;
|
|
if (remoteOut)
|
|
memset(remoteOut, 0, sizeof(*remoteOut));
|
|
|
|
if (s->Type != SYS_SOCK_STREAM || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (!s->Listening || s->ShutdownRead || s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
struct netconn* acceptedConn = NULL;
|
|
err_t err = ERR_OK;
|
|
st = LwipSocketWaitAccept(s, &acceptedConn, cancelRequested, &err);
|
|
LwipSocketUnlock(s);
|
|
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
if (err != ERR_OK)
|
|
return LwipErrToStatus(err);
|
|
if (!acceptedConn)
|
|
return NTLSTATUS_FAILURE;
|
|
|
|
LWIP_PROVIDER_SOCKET* accepted = NULL;
|
|
st = LwipSocketAllocateWithConn(s->Domain, s->Type, s->Protocol, acceptedConn, &accepted);
|
|
if (!KSUCCESS(st)) {
|
|
(void)netconn_close(acceptedConn);
|
|
netconn_delete(acceptedConn);
|
|
return st;
|
|
}
|
|
accepted->Connected = 1;
|
|
|
|
if (remoteOut) {
|
|
ip_addr_t peerIp;
|
|
u16_t peerPort = 0;
|
|
err = netconn_peer(acceptedConn, &peerIp, &peerPort);
|
|
if (err == ERR_OK) {
|
|
LwipSocketFillAddress(remoteOut, &peerIp, peerPort);
|
|
} else {
|
|
memset(remoteOut, 0, sizeof(*remoteOut));
|
|
remoteOut->Size = sizeof(*remoteOut);
|
|
remoteOut->Family = s->Domain;
|
|
}
|
|
}
|
|
|
|
*acceptedOut = accepted;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Connect operation.
|
|
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s || !address)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketAddressValid(address, addressSize))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketFamilyMatches(s, address))
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->Listening) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
if (s->ShutdownRead || s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
|
|
ip_addr_t ip;
|
|
st = LwipSocketMakeIp(address, &ip);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
|
|
if (!cancelRequested || s->Type != SYS_SOCK_STREAM) {
|
|
err_t err = netconn_connect(s->Conn, &ip, address->Port);
|
|
if (err == ERR_OK)
|
|
s->Connected = 1;
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
u8_t wasNonBlocking = netconn_is_nonblocking(s->Conn) ? 1 : 0;
|
|
netconn_set_nonblocking(s->Conn, 1);
|
|
err_t err = netconn_connect(s->Conn, &ip, address->Port);
|
|
if (err == ERR_OK) {
|
|
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
|
|
s->Connected = 1;
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
if (err != ERR_INPROGRESS) {
|
|
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
for (;;) {
|
|
if (LwipSocketCancelRequested(cancelRequested)) {
|
|
(void)netconn_close(s->Conn);
|
|
s->ShutdownRead = 1;
|
|
s->ShutdownWrite = 1;
|
|
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_CANCELLED;
|
|
}
|
|
|
|
if (s->Conn->state != NETCONN_CONNECT) {
|
|
err = netconn_err(s->Conn);
|
|
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
|
|
if (err == ERR_OK) {
|
|
s->Connected = 1;
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
LwipSocketUnlock(s);
|
|
(void)LwipDelayThread(LWIP_PROVIDER_CANCEL_POLL_MS);
|
|
st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
}
|
|
}
|
|
|
|
// Implements the provider Send operation.
|
|
static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (sentOut)
|
|
*sentOut = 0;
|
|
if (!s || (!buffer && length))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (flags != 0)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (length > LWIP_PROVIDER_SOCKET_IO_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (length == 0)
|
|
return NTLSTATUS_SUCCESS;
|
|
if (s->Type != SYS_SOCK_STREAM || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->Listening) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
if (s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
|
|
size_t written = 0;
|
|
err_t err = netconn_write_partly(s->Conn, buffer, length, NETCONN_COPY, &written);
|
|
if (err == ERR_OK && sentOut)
|
|
*sentOut = written;
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
static KSTATUS LwipReceiveFromBuffered(LWIP_PROVIDER_SOCKET* s, uint8_t* out,
|
|
size_t capacity, size_t* copiedOut) {
|
|
size_t copied = copiedOut ? *copiedOut : 0;
|
|
|
|
while (s->ReceiveBuffer && copied < capacity) {
|
|
struct netbuf* nb = s->ReceiveBuffer;
|
|
void* data = NULL;
|
|
u16_t segLen = 0;
|
|
err_t err = netbuf_data(nb, &data, &segLen);
|
|
if (err != ERR_OK || !data) {
|
|
netbuf_delete(nb);
|
|
s->ReceiveBuffer = NULL;
|
|
s->ReceiveOffset = 0;
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
if (s->ReceiveOffset >= segLen) {
|
|
s->ReceiveOffset = 0;
|
|
if (netbuf_next(nb) < 0) {
|
|
netbuf_delete(nb);
|
|
s->ReceiveBuffer = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
size_t available = (size_t)segLen - s->ReceiveOffset;
|
|
size_t want = capacity - copied;
|
|
size_t take = (available < want) ? available : want;
|
|
memcpy(out + copied, (const uint8_t*)data + s->ReceiveOffset, take);
|
|
copied += take;
|
|
s->ReceiveOffset += take;
|
|
|
|
if (s->ReceiveOffset >= segLen) {
|
|
s->ReceiveOffset = 0;
|
|
if (netbuf_next(nb) < 0) {
|
|
netbuf_delete(nb);
|
|
s->ReceiveBuffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (copiedOut)
|
|
*copiedOut = copied;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Receive operation.
|
|
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (receivedOut)
|
|
*receivedOut = 0;
|
|
if (!s || (!buffer && length))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (flags != 0)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (length > LWIP_PROVIDER_SOCKET_IO_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (length == 0)
|
|
return NTLSTATUS_SUCCESS;
|
|
if (s->Type != SYS_SOCK_STREAM || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->Listening) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
if (s->ShutdownRead) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
|
|
size_t copied = 0;
|
|
st = LwipReceiveFromBuffered(s, (uint8_t*)buffer, length, &copied);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
|
|
if (copied == 0 && !s->ReceiveBuffer) {
|
|
struct netbuf* nb = NULL;
|
|
err_t err = ERR_OK;
|
|
st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
if (err == ERR_CLSD) {
|
|
s->ShutdownRead = 1;
|
|
if (receivedOut)
|
|
*receivedOut = 0;
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
if (err != ERR_OK) {
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
s->ReceiveBuffer = nb;
|
|
s->ReceiveOffset = 0;
|
|
st = LwipReceiveFromBuffered(s, (uint8_t*)buffer, length, &copied);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
}
|
|
|
|
if (receivedOut)
|
|
*receivedOut = copied;
|
|
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags,
|
|
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (sentOut)
|
|
*sentOut = 0;
|
|
if (!s || (!buffer && length) || !remote)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketAddressValid(remote, remoteSize))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if (flags != 0)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (s->Type == SYS_SOCK_DGRAM && length > LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!LwipSocketFamilyMatches(s, remote))
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (length == 0)
|
|
return NTLSTATUS_SUCCESS;
|
|
if ((s->Type != SYS_SOCK_DGRAM && s->Type != SYS_SOCK_RAW) || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->ShutdownWrite) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
|
|
struct netbuf* nb = netbuf_new();
|
|
if (!nb) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
|
|
err_t err = netbuf_ref(nb, buffer, (u16_t)length);
|
|
if (err == ERR_OK) {
|
|
ip_addr_t ip;
|
|
KSTATUS makeStatus = LwipSocketMakeIp(remote, &ip);
|
|
if (KSUCCESS(makeStatus))
|
|
err = netconn_sendto(s->Conn, nb, &ip, remote->Port);
|
|
else
|
|
err = ERR_ARG;
|
|
}
|
|
netbuf_delete(nb);
|
|
|
|
if (err == ERR_OK && sentOut)
|
|
*sentOut = length;
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags,
|
|
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (receivedOut)
|
|
*receivedOut = 0;
|
|
if (!s || (!buffer && length))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (flags != 0)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (s->Type == SYS_SOCK_DGRAM && length > LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (length == 0)
|
|
return NTLSTATUS_SUCCESS;
|
|
if ((s->Type != SYS_SOCK_DGRAM && s->Type != SYS_SOCK_RAW) || !s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (s->ShutdownRead) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
|
|
struct netbuf* nb = NULL;
|
|
err_t err = ERR_OK;
|
|
st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err);
|
|
if (!KSUCCESS(st)) {
|
|
LwipSocketUnlock(s);
|
|
return st;
|
|
}
|
|
if (err != ERR_OK) {
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
if (!nb) {
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
size_t copied = 0;
|
|
do {
|
|
void* data = NULL;
|
|
u16_t segLen = 0;
|
|
err = netbuf_data(nb, &data, &segLen);
|
|
if (err != ERR_OK || !data)
|
|
break;
|
|
|
|
size_t remaining = length - copied;
|
|
size_t take = ((size_t)segLen < remaining) ? (size_t)segLen : remaining;
|
|
if (take) {
|
|
memcpy((uint8_t*)buffer + copied, data, take);
|
|
copied += take;
|
|
}
|
|
} while (copied < length && netbuf_next(nb) >= 0);
|
|
|
|
if (remoteOut) {
|
|
const ip_addr_t* from = netbuf_fromaddr(nb);
|
|
if (from) {
|
|
LwipSocketFillAddress(remoteOut, from, netbuf_fromport(nb));
|
|
} else {
|
|
memset(remoteOut, 0, sizeof(*remoteOut));
|
|
remoteOut->Size = sizeof(*remoteOut);
|
|
remoteOut->Family = s->Domain;
|
|
}
|
|
}
|
|
|
|
netbuf_delete(nb);
|
|
if (receivedOut)
|
|
*receivedOut = copied;
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
// Implements the provider Set Option operation.
|
|
static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s || !value)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (valueSize != sizeof(uint32_t))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if (!s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t v = 0;
|
|
memcpy(&v, value, sizeof(v));
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
switch (option) {
|
|
case SYS_SOCKET_OPT_RECV_TIMEOUT_MS:
|
|
s->ReceiveTimeoutMs = v;
|
|
netconn_set_recvtimeout(s->Conn, v);
|
|
break;
|
|
case SYS_SOCKET_OPT_SEND_TIMEOUT_MS:
|
|
s->SendTimeoutMs = v;
|
|
netconn_set_sendtimeout(s->Conn, v);
|
|
break;
|
|
case SYS_SOCKET_OPT_NONBLOCKING:
|
|
s->NonBlocking = v ? 1 : 0;
|
|
netconn_set_nonblocking(s->Conn, s->NonBlocking);
|
|
break;
|
|
default:
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Get Option operation.
|
|
static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (actualSizeOut)
|
|
*actualSizeOut = sizeof(uint32_t);
|
|
if (!s || !value)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (valueSize < sizeof(uint32_t))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if (!s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
uint32_t v = 0;
|
|
switch (option) {
|
|
case SYS_SOCKET_OPT_RECV_TIMEOUT_MS:
|
|
v = s->ReceiveTimeoutMs;
|
|
break;
|
|
case SYS_SOCKET_OPT_SEND_TIMEOUT_MS:
|
|
v = s->SendTimeoutMs;
|
|
break;
|
|
case SYS_SOCKET_OPT_NONBLOCKING:
|
|
v = s->NonBlocking ? 1u : 0u;
|
|
break;
|
|
default:
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
memcpy(value, &v, sizeof(v));
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Poll operation.
|
|
static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s || !returnedEventsOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t revents = 0;
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
if (!s->Conn) {
|
|
*returnedEventsOut = SYS_POLL_INVALID;
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
err_t lastErr = netconn_err(s->Conn);
|
|
if (s->PollErrorEvents > 0 || (lastErr != ERR_OK && lastErr != ERR_INPROGRESS))
|
|
revents |= SYS_POLL_ERROR;
|
|
|
|
if ((events & SYS_POLL_READ) != 0) {
|
|
if (s->ReceiveBuffer || s->PollReadEvents > 0 || s->Conn->recv_avail > 0 || s->ShutdownRead)
|
|
revents |= SYS_POLL_READ;
|
|
}
|
|
|
|
if ((events & SYS_POLL_WRITE) != 0) {
|
|
if (!s->Listening && !s->ShutdownWrite && !s->PollErrorEvents)
|
|
revents |= SYS_POLL_WRITE;
|
|
}
|
|
|
|
if (s->ShutdownRead || s->ShutdownWrite)
|
|
revents |= SYS_POLL_HANGUP;
|
|
|
|
*returnedEventsOut = revents;
|
|
LwipSocketUnlock(s);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the provider Shutdown operation.
|
|
static KSTATUS LwipProviderShutdown(void* socket, uint32_t how) {
|
|
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
|
if (!s)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (how != SYS_SHUTDOWN_READ && how != SYS_SHUTDOWN_WRITE && how != SYS_SHUTDOWN_BOTH)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!s->Conn)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS st = LwipSocketLock(s);
|
|
if (!KSUCCESS(st))
|
|
return st;
|
|
|
|
u8_t shutRx = (how == SYS_SHUTDOWN_READ || how == SYS_SHUTDOWN_BOTH) ? 1 : 0;
|
|
u8_t shutTx = (how == SYS_SHUTDOWN_WRITE || how == SYS_SHUTDOWN_BOTH) ? 1 : 0;
|
|
err_t err = netconn_shutdown(s->Conn, shutRx, shutTx);
|
|
if (err == ERR_OK) {
|
|
if (shutRx) s->ShutdownRead = 1;
|
|
if (shutTx) s->ShutdownWrite = 1;
|
|
}
|
|
|
|
LwipSocketUnlock(s);
|
|
return LwipErrToStatus(err);
|
|
}
|
|
|
|
// Implements the provider Resolve Host operation.
|
|
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
|
|
if (!name || !addressOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!gLwipInitialized)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
u8_t dnsType;
|
|
if (family == SYS_AF_INET)
|
|
dnsType = NETCONN_DNS_IPV4;
|
|
else if (family == SYS_AF_INET6)
|
|
dnsType = NETCONN_DNS_IPV6;
|
|
else
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
ip_addr_t resolved;
|
|
err_t err = netconn_gethostbyname_addrtype(name, &resolved, dnsType);
|
|
if (err != ERR_OK)
|
|
return LwipErrToStatus(err);
|
|
if ((family == SYS_AF_INET && !IP_IS_V4(&resolved)) ||
|
|
(family == SYS_AF_INET6 && !IP_IS_V6(&resolved)))
|
|
return NTLSTATUS_NOT_FOUND;
|
|
|
|
LwipSocketFillAddress(addressOut, &resolved, 0);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Dispatches a provider-private socket request carried by an IRP.
|
|
static KSTATUS LwipDispatchSocketRequest(NET_SOCKET_IRP_REQUEST* request) {
|
|
if (!request || request->Size != sizeof(*request))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
switch (request->Operation) {
|
|
case NetSocketIrpCreate:
|
|
return LwipProviderSocketCreate(request->u.Create.Domain,
|
|
request->u.Create.Type,
|
|
request->u.Create.Protocol,
|
|
request->u.Create.ProviderSocketOut);
|
|
case NetSocketIrpClose:
|
|
LwipProviderSocketClose(request->u.Close.Socket);
|
|
return NTLSTATUS_SUCCESS;
|
|
case NetSocketIrpBind:
|
|
return LwipProviderBind(request->u.Bind.Socket,
|
|
request->u.Bind.Address,
|
|
request->u.Bind.AddressSize);
|
|
case NetSocketIrpListen:
|
|
return LwipProviderListen(request->u.Listen.Socket,
|
|
request->u.Listen.Backlog);
|
|
case NetSocketIrpAccept:
|
|
return LwipProviderAccept(request->u.Accept.Socket,
|
|
request->u.Accept.AcceptedOut,
|
|
request->u.Accept.RemoteOut,
|
|
request->CancelRequested);
|
|
case NetSocketIrpConnect:
|
|
return LwipProviderConnect(request->u.Connect.Socket,
|
|
request->u.Connect.Address,
|
|
request->u.Connect.AddressSize,
|
|
request->CancelRequested);
|
|
case NetSocketIrpSend:
|
|
return LwipProviderSend(request->u.Send.Socket,
|
|
request->u.Send.Buffer,
|
|
request->u.Send.Length,
|
|
request->u.Send.Flags,
|
|
request->u.Send.SentOut);
|
|
case NetSocketIrpReceive:
|
|
return LwipProviderReceive(request->u.Receive.Socket,
|
|
request->u.Receive.Buffer,
|
|
request->u.Receive.Length,
|
|
request->u.Receive.Flags,
|
|
request->u.Receive.ReceivedOut,
|
|
request->CancelRequested);
|
|
case NetSocketIrpSendTo:
|
|
return LwipProviderSendTo(request->u.SendTo.Socket,
|
|
request->u.SendTo.Buffer,
|
|
request->u.SendTo.Length,
|
|
request->u.SendTo.Flags,
|
|
request->u.SendTo.Remote,
|
|
request->u.SendTo.RemoteSize,
|
|
request->u.SendTo.SentOut);
|
|
case NetSocketIrpReceiveFrom:
|
|
return LwipProviderReceiveFrom(request->u.ReceiveFrom.Socket,
|
|
request->u.ReceiveFrom.Buffer,
|
|
request->u.ReceiveFrom.Length,
|
|
request->u.ReceiveFrom.Flags,
|
|
request->u.ReceiveFrom.RemoteOut,
|
|
request->u.ReceiveFrom.ReceivedOut,
|
|
request->CancelRequested);
|
|
case NetSocketIrpShutdown:
|
|
return LwipProviderShutdown(request->u.Shutdown.Socket,
|
|
request->u.Shutdown.How);
|
|
case NetSocketIrpSetOption:
|
|
return LwipProviderSetOption(request->u.SetOption.Socket,
|
|
request->u.SetOption.Option,
|
|
request->u.SetOption.Value,
|
|
request->u.SetOption.ValueSize);
|
|
case NetSocketIrpGetOption:
|
|
return LwipProviderGetOption(request->u.GetOption.Socket,
|
|
request->u.GetOption.Option,
|
|
request->u.GetOption.Value,
|
|
request->u.GetOption.ValueSize,
|
|
request->u.GetOption.ActualSizeOut);
|
|
case NetSocketIrpPoll:
|
|
return LwipProviderPoll(request->u.Poll.Socket,
|
|
request->u.Poll.Events,
|
|
request->u.Poll.ReturnedEventsOut);
|
|
case NetSocketIrpResolveHost:
|
|
return LwipProviderResolveHost(request->u.ResolveHost.Name,
|
|
request->u.ResolveHost.Family,
|
|
request->u.ResolveHost.AddressOut);
|
|
default:
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// Dispatches socket internal device controls for the lwIP stack device.
|
|
static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp) {
|
|
(void)device;
|
|
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
|
|
if (!ios)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (ios->Parameters.DeviceIoControl.IoControlCode != NET_IOCTL_SOCKET)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
if (ios->Parameters.DeviceIoControl.InputLength < sizeof(NET_SOCKET_IRP_REQUEST))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
|
|
NET_SOCKET_IRP_REQUEST* request = (NET_SOCKET_IRP_REQUEST*)ios->Parameters.DeviceIoControl.InputBuffer;
|
|
return LwipDispatchSocketRequest(request);
|
|
}
|
|
|
|
// Implements the driver Entry operation.
|
|
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
|
(void)init;
|
|
DbgPrint("lwIP: driver loading...\n");
|
|
|
|
LWIP_NETIF_CONFIG defaultConfig;
|
|
KSTATUS st = LwipLoadConfigFromSection(LWIP_SETTINGS_DEFAULT_SECTION, &defaultConfig);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: required settings [%s] missing or invalid in %s (0x%x)\n",
|
|
LWIP_SETTINGS_DEFAULT_SECTION, KE_SETTINGS_PATH, st);
|
|
return st;
|
|
}
|
|
|
|
st = LwipStackInitialize(ddk);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: initialization failed: 0x%x\n", st);
|
|
return st;
|
|
}
|
|
|
|
IoInitializeDriverObject(&gLwipDriver, SYS_SV_LITERAL("lwip-stack"));
|
|
gLwipDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = LwipDispatchInternalDeviceControl;
|
|
|
|
st = IoRegisterDriverObject(&gLwipDriver);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: driver object registration failed: 0x%x\n", st);
|
|
return st;
|
|
}
|
|
|
|
st = IoCreateDevice(&gLwipDriver, 0, &gLwipDevice);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: stack device creation failed: 0x%x\n", st);
|
|
return st;
|
|
}
|
|
(void)IoSetDeviceName(gLwipDevice, SYS_SV_LITERAL("lwip-stack"));
|
|
gLwipProvider.StackDevice = gLwipDevice;
|
|
|
|
st = NetRegisterStackProvider(&gLwipProvider);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("lwIP: provider registration failed: 0x%x\n", st);
|
|
return st;
|
|
}
|
|
|
|
DbgPrint("lwIP: driver initialized\n");
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|