561 lines
25 KiB
C
561 lines
25 KiB
C
// RSSRV resource protocol listeners, sessions, request validation, and capability transfer.
|
|
|
|
#include "rssrv_internal.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct RSSRV_SESSION {
|
|
RSSRV_RESOURCE_SERVICE* Service;
|
|
SYS_HANDLE Endpoint;
|
|
uint32_t Administrative;
|
|
} RSSRV_SESSION;
|
|
|
|
// Replies with one fixed resource-protocol reply and no handle transfer.
|
|
static KSTATUS RssrvReplyStatus(SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
uint32_t operation,
|
|
KSTATUS status,
|
|
uint32_t kind) {
|
|
NTL_RESOURCE_REPLY reply;
|
|
memset(&reply, 0, sizeof(reply));
|
|
reply.Size = sizeof(reply);
|
|
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
|
|
reply.Operation = operation;
|
|
reply.Flags = NTL_RESOURCE_FLAG_NONE;
|
|
reply.Status = status;
|
|
reply.Kind = kind;
|
|
|
|
SYS_REPLY_PORT_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.EndpointHandle = endpoint;
|
|
request.MessageId = messageId;
|
|
request.Flags = SYS_PORT_FLAG_NONE;
|
|
request.Payload = &reply;
|
|
request.PayloadLength = sizeof(reply);
|
|
return SysReplyPort(&request);
|
|
}
|
|
|
|
// Handles one exact capability lookup while retaining the namespace lock through transfer.
|
|
static KSTATUS RssrvHandleLookup(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
const NTL_RESOURCE_REQUEST* request,
|
|
const char* path) {
|
|
if (!request->Rights)
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
|
|
RSSRV_NAMESPACE_BINDING_VIEW view;
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
KSTATUS status = RssrvNamespaceBeginLookup(service->Namespace,
|
|
path,
|
|
request->PathLength,
|
|
&view,
|
|
&kind);
|
|
if (!KSUCCESS(status))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
|
|
|
|
kind = view.Kind;
|
|
if ((request->Rights & ~view.ExportRights) != 0) {
|
|
RssrvNamespaceEndBinding(&view);
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_ACCESS_DENIED, kind);
|
|
}
|
|
|
|
NTL_RESOURCE_REPLY reply;
|
|
memset(&reply, 0, sizeof(reply));
|
|
reply.Size = sizeof(reply);
|
|
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
|
|
reply.Operation = NTL_RESOURCE_OP_LOOKUP;
|
|
reply.Status = NTLSTATUS_SUCCESS;
|
|
reply.Kind = kind;
|
|
|
|
SYS_PORT_HANDLE_TRANSFER transfer;
|
|
transfer.Handle = view.Handle;
|
|
transfer.DesiredRights = request->Rights;
|
|
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
|
|
|
SYS_REPLY_PORT_REQUEST out;
|
|
memset(&out, 0, sizeof(out));
|
|
out.Size = sizeof(out);
|
|
out.EndpointHandle = endpoint;
|
|
out.MessageId = messageId;
|
|
out.Flags = SYS_PORT_FLAG_NONE;
|
|
out.Payload = &reply;
|
|
out.PayloadLength = sizeof(reply);
|
|
out.Handles = &transfer;
|
|
out.HandleCount = 1;
|
|
status = SysReplyPort(&out);
|
|
RssrvNamespaceEndBinding(&view);
|
|
|
|
// A transfer failure leaves the synchronous call pending, so complete it with the precise status.
|
|
if (!KSUCCESS(status))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Resolves the first bound namespace object and transfers its traversal authority.
|
|
static KSTATUS RssrvHandleResolve(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
const NTL_RESOURCE_REQUEST* request,
|
|
const char* path) {
|
|
if ((request->Flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0)
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
|
|
RSSRV_NAMESPACE_BINDING_VIEW view;
|
|
KSTATUS status = RssrvNamespaceBeginResolve(service->Namespace,
|
|
path,
|
|
request->PathLength,
|
|
&view);
|
|
if (!KSUCCESS(status))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
status, NTL_RESOURCE_KIND_INVALID);
|
|
|
|
SYS_RIGHTS desiredRights = 0;
|
|
if (view.Kind == NTL_RESOURCE_KIND_VOLUME) {
|
|
desiredRights = SYS_VOLUME_RIGHT_OPEN;
|
|
if (request->Flags & NTL_RESOURCE_RESOLVE_FLAG_MODIFY)
|
|
desiredRights |= SYS_VOLUME_RIGHT_MODIFY;
|
|
} else if (view.Kind == NTL_RESOURCE_KIND_DIRECTORY) {
|
|
desiredRights = SYS_DIR_RIGHT_OPEN;
|
|
if (request->Flags & NTL_RESOURCE_RESOLVE_FLAG_MODIFY)
|
|
desiredRights |= SYS_DIR_RIGHT_MODIFY;
|
|
} else {
|
|
uint32_t kind = view.Kind;
|
|
RssrvNamespaceEndBinding(&view);
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_NOT_SUPPORTED, kind);
|
|
}
|
|
|
|
uint32_t kind = view.Kind;
|
|
if ((desiredRights & ~view.ExportRights) != 0) {
|
|
RssrvNamespaceEndBinding(&view);
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_ACCESS_DENIED, kind);
|
|
}
|
|
|
|
NTL_RESOURCE_REPLY reply;
|
|
memset(&reply, 0, sizeof(reply));
|
|
reply.Size = sizeof(reply);
|
|
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
|
|
reply.Operation = NTL_RESOURCE_OP_RESOLVE;
|
|
reply.Flags = request->Flags;
|
|
reply.Status = NTLSTATUS_SUCCESS;
|
|
reply.Kind = kind;
|
|
reply.ResolvedLength = view.ResolvedLength;
|
|
|
|
SYS_PORT_HANDLE_TRANSFER transfer;
|
|
transfer.Handle = view.Handle;
|
|
transfer.DesiredRights = desiredRights;
|
|
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
|
|
|
SYS_REPLY_PORT_REQUEST out;
|
|
memset(&out, 0, sizeof(out));
|
|
out.Size = sizeof(out);
|
|
out.EndpointHandle = endpoint;
|
|
out.MessageId = messageId;
|
|
out.Flags = SYS_PORT_FLAG_NONE;
|
|
out.Payload = &reply;
|
|
out.PayloadLength = sizeof(reply);
|
|
out.Handles = &transfer;
|
|
out.HandleCount = 1;
|
|
status = SysReplyPort(&out);
|
|
RssrvNamespaceEndBinding(&view);
|
|
if (!KSUCCESS(status))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Handles one exact metadata query without following the final symbolic link.
|
|
static KSTATUS RssrvHandleQuery(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
const NTL_RESOURCE_REQUEST* request,
|
|
const char* path) {
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
KSTATUS status = RssrvNamespaceQuery(service->Namespace, path, request->PathLength, &kind);
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, kind);
|
|
}
|
|
|
|
// Returns one immediate namespace child after the supplied bytewise cursor name.
|
|
static KSTATUS RssrvHandleEnumerate(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
const NTL_RESOURCE_REQUEST* request,
|
|
const char* path,
|
|
const char* after) {
|
|
RSSRV_NAMESPACE_ENTRY entry;
|
|
KSTATUS status = RssrvNamespaceEnumerate(service->Namespace,
|
|
path,
|
|
request->PathLength,
|
|
after,
|
|
request->AuxLength,
|
|
&entry);
|
|
if (!KSUCCESS(status))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation, status, entry.Kind);
|
|
|
|
uint8_t payload[sizeof(NTL_RESOURCE_REPLY) + NTL_RESOURCE_NAME_MAX];
|
|
NTL_RESOURCE_REPLY reply;
|
|
memset(&reply, 0, sizeof(reply));
|
|
reply.Size = sizeof(reply);
|
|
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
|
|
reply.Operation = NTL_RESOURCE_OP_ENUMERATE;
|
|
reply.Status = NTLSTATUS_SUCCESS;
|
|
reply.Kind = entry.Kind;
|
|
reply.NameLength = entry.NameLength;
|
|
memcpy(payload, &reply, sizeof(reply));
|
|
memcpy(payload + sizeof(reply), entry.Name, entry.NameLength);
|
|
uint32_t payloadLength = (uint32_t)sizeof(reply) + entry.NameLength;
|
|
|
|
SYS_REPLY_PORT_REQUEST out;
|
|
memset(&out, 0, sizeof(out));
|
|
out.Size = sizeof(out);
|
|
out.EndpointHandle = endpoint;
|
|
out.MessageId = messageId;
|
|
out.Flags = SYS_PORT_FLAG_NONE;
|
|
out.Payload = payload;
|
|
out.PayloadLength = payloadLength;
|
|
return SysReplyPort(&out);
|
|
}
|
|
|
|
// Handles one administrative bind. On success the namespace takes ownership of receivedHandle.
|
|
static KSTATUS RssrvHandleBind(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t messageId,
|
|
const NTL_RESOURCE_REQUEST* request,
|
|
const char* path,
|
|
uint32_t administrative,
|
|
SYS_HANDLE receivedHandle,
|
|
uint32_t* consumedOut) {
|
|
if (!consumedOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*consumedOut = 0;
|
|
if (!administrative || RssrvVolumesOwnPath(path, request->PathLength))
|
|
return RssrvReplyStatus(endpoint, messageId, request->Operation,
|
|
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
|
|
KSTATUS bindStatus = RssrvNamespaceBindOwned(service->Namespace,
|
|
path,
|
|
request->PathLength,
|
|
request->Kind,
|
|
receivedHandle,
|
|
request->ExportRights);
|
|
if (KSUCCESS(bindStatus)) *consumedOut = 1;
|
|
KSTATUS replyStatus = RssrvReplyStatus(endpoint, messageId, request->Operation, bindStatus,
|
|
KSUCCESS(bindStatus) ? request->Kind : NTL_RESOURCE_KIND_INVALID);
|
|
return KSUCCESS(replyStatus) ? bindStatus : replyStatus;
|
|
}
|
|
|
|
// Dispatches one received resource request and returns whether a received handle was consumed.
|
|
static uint32_t RssrvDispatchRequest(RSSRV_RESOURCE_SERVICE* service,
|
|
SYS_HANDLE endpoint,
|
|
uint32_t administrative,
|
|
const uint8_t* payload,
|
|
uint32_t payloadLength,
|
|
const SYS_PORT_MESSAGE_INFO* info,
|
|
SYS_HANDLE receivedHandle) {
|
|
uint32_t operation = 0;
|
|
if (!service || payloadLength < sizeof(NTL_RESOURCE_REQUEST)) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
|
|
NTL_RESOURCE_REQUEST request;
|
|
memcpy(&request, payload, sizeof(request));
|
|
operation = request.Operation;
|
|
size_t expectedLength = sizeof(request) + (size_t)request.PathLength + (size_t)request.AuxLength;
|
|
if (request.Size != sizeof(request) || request.Version != NTL_RESOURCE_PROTOCOL_VERSION ||
|
|
request.PathLength == 0 || expectedLength != payloadLength || expectedLength > SYS_PORT_MAX_INLINE_BYTES) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
const char* path = (const char*)(payload + sizeof(request));
|
|
const char* aux = path + request.PathLength;
|
|
KSTATUS status = RssrvNamespaceValidatePath(path, request.PathLength);
|
|
if (!KSUCCESS(status)) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
|
|
switch (operation) {
|
|
case NTL_RESOURCE_OP_LOOKUP:
|
|
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
|
|
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
(void)RssrvHandleLookup(service, endpoint, info->MessageId, &request, path);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_QUERY:
|
|
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
|
|
request.Rights != 0 || request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
(void)RssrvHandleQuery(service, endpoint, info->MessageId, &request, path);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_RESOLVE:
|
|
if (request.AuxLength != 0 || info->HandleCount != 0 || request.Rights != 0 ||
|
|
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID ||
|
|
(request.Flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
(void)RssrvHandleResolve(service, endpoint, info->MessageId, &request, path);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_ENUMERATE:
|
|
if (request.Flags != NTL_RESOURCE_FLAG_NONE || info->HandleCount != 0 || request.Rights != 0 ||
|
|
request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID ||
|
|
request.AuxLength > NTL_RESOURCE_NAME_MAX) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
for (uint32_t i = 0; i < request.AuxLength; ++i) {
|
|
if (aux[i] == '/' || aux[i] == '\\' || aux[i] == '\0') {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
}
|
|
(void)RssrvHandleEnumerate(service, endpoint, info->MessageId, &request, path, aux);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_CREATE_DIRECTORY:
|
|
if (!administrative || request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 ||
|
|
info->HandleCount != 0 || request.Rights != 0 || request.ExportRights != 0 ||
|
|
request.Kind != NTL_RESOURCE_KIND_INVALID || RssrvVolumesOwnPath(path, request.PathLength)) {
|
|
status = administrative ? NTLSTATUS_INVALID_PARAMETER : NTLSTATUS_ACCESS_DENIED;
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
status = RssrvNamespaceCreateDirectory(service->Namespace, path, request.PathLength);
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status,
|
|
KSUCCESS(status) ? NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY : NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_CREATE_SYMBOLIC_LINK:
|
|
if (!administrative || request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength == 0 ||
|
|
info->HandleCount != 0 || request.Rights != 0 || request.ExportRights != 0 ||
|
|
request.Kind != NTL_RESOURCE_KIND_INVALID || RssrvVolumesOwnPath(path, request.PathLength)) {
|
|
status = administrative ? NTLSTATUS_INVALID_PARAMETER : NTLSTATUS_ACCESS_DENIED;
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
status = RssrvNamespaceValidatePath(aux, request.AuxLength);
|
|
if (KSUCCESS(status))
|
|
status = RssrvNamespaceCreateSymbolicLink(service->Namespace,
|
|
path,
|
|
request.PathLength,
|
|
aux,
|
|
request.AuxLength);
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status,
|
|
KSUCCESS(status) ? NTL_RESOURCE_KIND_SYMBOLIC_LINK : NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
case NTL_RESOURCE_OP_BIND:
|
|
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 1 ||
|
|
!(request.Rights & SYS_RIGHT_TRANSFER) || request.ExportRights == 0 ||
|
|
(request.ExportRights & ~request.Rights) != 0 || request.Kind == NTL_RESOURCE_KIND_INVALID ||
|
|
request.Kind == NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY || request.Kind == NTL_RESOURCE_KIND_SYMBOLIC_LINK ||
|
|
receivedHandle == SYS_HANDLE_INVALID) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
{
|
|
uint32_t consumed = 0;
|
|
(void)RssrvHandleBind(service, endpoint, info->MessageId, &request, path, administrative,
|
|
receivedHandle, &consumed);
|
|
return consumed;
|
|
}
|
|
case NTL_RESOURCE_OP_UNBIND:
|
|
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
|
|
request.Rights != 0 || request.ExportRights != 0 || request.Kind != NTL_RESOURCE_KIND_INVALID) {
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
status = administrative && !RssrvVolumesOwnPath(path, request.PathLength)
|
|
? RssrvNamespaceUnbind(service->Namespace, path, request.PathLength)
|
|
: NTLSTATUS_ACCESS_DENIED;
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation, status, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
default:
|
|
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
|
|
NTLSTATUS_NOT_SUPPORTED, NTL_RESOURCE_KIND_INVALID);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Serves one connected resource session until the peer disconnects.
|
|
static void RssrvSessionThread(uintptr_t argument) {
|
|
RSSRV_SESSION* session = (RSSRV_SESSION*)argument;
|
|
if (!session || !session->Service) {
|
|
if (session) free(session);
|
|
SysExitThread(1);
|
|
return;
|
|
}
|
|
RSSRV_RESOURCE_SERVICE* service = session->Service;
|
|
SYS_HANDLE endpoint = session->Endpoint;
|
|
uint32_t administrative = session->Administrative;
|
|
RssrvSetCurrentThreadName(administrative ? "rssrv.session.admin" : "rssrv.session.public");
|
|
free(session);
|
|
|
|
uint8_t payload[SYS_PORT_MAX_INLINE_BYTES];
|
|
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
|
|
for (;;) {
|
|
memset(handles, 0, sizeof(handles));
|
|
SYS_PORT_MESSAGE_INFO info;
|
|
memset(&info, 0, sizeof(info));
|
|
info.Size = sizeof(info);
|
|
SYS_RECEIVE_PORT_REQUEST receive;
|
|
memset(&receive, 0, sizeof(receive));
|
|
receive.Size = sizeof(receive);
|
|
receive.EndpointHandle = endpoint;
|
|
receive.Flags = SYS_PORT_FLAG_NONE;
|
|
receive.Payload = payload;
|
|
receive.PayloadCapacity = sizeof(payload);
|
|
receive.HandlesOut = handles;
|
|
receive.HandleCapacity = SYS_PORT_MAX_TRANSFER_HANDLES;
|
|
receive.InfoOut = &info;
|
|
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
|
|
KSTATUS status = SysReceivePort(&receive);
|
|
if (status == NTLSTATUS_PORT_DISCONNECTED)
|
|
break;
|
|
if (!KSUCCESS(status))
|
|
break;
|
|
|
|
uint32_t consumed = 0;
|
|
if (info.Type == SYS_PORT_MESSAGE_REQUEST)
|
|
consumed = RssrvDispatchRequest(service,
|
|
endpoint,
|
|
administrative,
|
|
payload,
|
|
info.PayloadLength,
|
|
&info,
|
|
info.HandleCount == 1 ? handles[0] : SYS_HANDLE_INVALID);
|
|
for (uint32_t i = consumed ? 1u : 0u; i < info.HandleCount; ++i) {
|
|
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
|
|
}
|
|
}
|
|
|
|
(void)SysCloseHandle(endpoint);
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// Accepts sessions from one listener and gives every connection its own worker thread.
|
|
static void RssrvListenerThread(uintptr_t argument) {
|
|
RSSRV_LISTENER_WORKER* worker = (RSSRV_LISTENER_WORKER*)argument;
|
|
if (!worker || !worker->Service) {
|
|
SysExitThread(1);
|
|
return;
|
|
}
|
|
RssrvSetCurrentThreadName(worker->Administrative ? "rssrv.listen.admin" : "rssrv.listen.public");
|
|
for (;;) {
|
|
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
|
|
SYS_ACCEPT_PORT_REQUEST accept;
|
|
memset(&accept, 0, sizeof(accept));
|
|
accept.Size = sizeof(accept);
|
|
accept.ListenerHandle = worker->Listener;
|
|
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
|
|
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
|
|
accept.EndpointHandleOut = &endpoint;
|
|
KSTATUS status = SysAcceptPort(&accept);
|
|
if (!KSUCCESS(status))
|
|
break;
|
|
|
|
RSSRV_SESSION* session = (RSSRV_SESSION*)malloc(sizeof(*session));
|
|
if (!session) {
|
|
(void)SysDisconnectPort(endpoint);
|
|
(void)SysCloseHandle(endpoint);
|
|
continue;
|
|
}
|
|
session->Service = worker->Service;
|
|
session->Endpoint = endpoint;
|
|
session->Administrative = worker->Administrative;
|
|
|
|
SYS_CREATE_THREAD_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)RssrvSessionThread;
|
|
create.Argument = (uintptr_t)session;
|
|
create.Priority = 1;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
|
|
status = SysCreateThread(&create);
|
|
if (!KSUCCESS(status)) {
|
|
free(session);
|
|
(void)SysDisconnectPort(endpoint);
|
|
(void)SysCloseHandle(endpoint);
|
|
}
|
|
}
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// Creates one listener using the service's bounded default backlog.
|
|
static KSTATUS RssrvCreateListener(SYS_HANDLE* listenerOut) {
|
|
SYS_CREATE_PORT_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Flags = SYS_PORT_FLAG_NONE;
|
|
create.Backlog = RSSRV_LISTENER_BACKLOG;
|
|
create.HandleOut = listenerOut;
|
|
return SysCreatePort(&create);
|
|
}
|
|
|
|
// Starts one resource listener worker thread.
|
|
static KSTATUS RssrvStartListenerWorker(RSSRV_RESOURCE_SERVICE* service,
|
|
RSSRV_LISTENER_WORKER* worker,
|
|
SYS_HANDLE listener,
|
|
uint32_t administrative) {
|
|
if (!service || !worker || listener == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
worker->Service = service;
|
|
worker->Listener = listener;
|
|
worker->Administrative = administrative;
|
|
SYS_CREATE_THREAD_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)RssrvListenerThread;
|
|
create.Argument = (uintptr_t)worker;
|
|
create.Priority = 1;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
|
|
return SysCreateThread(&create);
|
|
}
|
|
|
|
// Initializes the public/admin resource protocol component around existing namespace state.
|
|
KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
|
|
RSSRV_NAMESPACE* namespaceState) {
|
|
if (!service || !namespaceState)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
memset(service, 0, sizeof(*service));
|
|
service->Namespace = namespaceState;
|
|
service->PublicListener = SYS_HANDLE_INVALID;
|
|
service->AdminListener = SYS_HANDLE_INVALID;
|
|
service->PublicWorker.Listener = SYS_HANDLE_INVALID;
|
|
service->AdminWorker.Listener = SYS_HANDLE_INVALID;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Creates the resource listeners and starts their accept workers.
|
|
KSTATUS RssrvResourceStart(RSSRV_RESOURCE_SERVICE* service) {
|
|
if (!service || !service->Namespace)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS status = RssrvCreateListener(&service->PublicListener);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = RssrvCreateListener(&service->AdminListener);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysCloseHandle(service->PublicListener);
|
|
service->PublicListener = SYS_HANDLE_INVALID;
|
|
return status;
|
|
}
|
|
status = RssrvStartListenerWorker(service, &service->PublicWorker, service->PublicListener, 0);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = RssrvStartListenerWorker(service, &service->AdminWorker, service->AdminListener, 1);
|
|
return status;
|
|
}
|