Files
nt-lite/code/user/system/rssrv/resource_service.c
T

678 lines
31 KiB
C

// RSSRV resource protocol listeners, sessions, request validation, and capability transfer.
#include <stdbool.h>
#include "rssrv_internal.h"
#include "publication_scope.h"
#include <ntlite/native_heap.h>
#include <string.h>
typedef struct RSSRV_SESSION {
RSSRV_RESOURCE_SERVICE* Service;
SYS_HANDLE Endpoint;
bool Administrative;
const char* ScopePath;
} 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;
}
// Allocates one durable, exact-path publication listener owned by RSSRV.
static KSTATUS RssrvCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, uint32_t length,
SYS_HANDLE* listenerOut);
// 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 char* scopePath,
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;
}
// A scoped listener can only publish or withdraw its exact delegated path.
if (!RssrvScopeAllows(scopePath, operation, path, request.PathLength)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
return 0;
}
if (operation == NTL_RESOURCE_OP_CREATE_PUBLISHER) {
if (!administrative || scopePath || request.Flags != NTL_RESOURCE_FLAG_NONE ||
request.AuxLength || info->HandleCount || request.Rights || request.ExportRights ||
request.Kind != NTL_RESOURCE_KIND_INVALID ||
RssrvVolumesOwnPath(path, request.PathLength) || request.PathLength > RSSRV_PUBLICATION_PATH_MAX) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
return 0;
}
SYS_HANDLE publisher = SYS_HANDLE_INVALID;
status = RssrvCreatePublisher(service, path, request.PathLength, &publisher);
if (!KSUCCESS(status)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
NTL_RESOURCE_REPLY reply = {0};
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Status = NTLSTATUS_SUCCESS;
SYS_PORT_HANDLE_TRANSFER transfer = {0};
transfer.Handle = publisher;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_DUPLICATE |
SYS_RIGHT_TRANSFER | SYS_PORT_RIGHT_CONNECT;
SYS_REPLY_PORT_REQUEST out = {0};
out.Size = sizeof(out);
out.EndpointHandle = endpoint;
out.MessageId = info->MessageId;
out.Payload = &reply;
out.PayloadLength = sizeof(reply);
out.Handles = &transfer;
out.HandleCount = 1u;
status = SysReplyPort(&out);
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;
}
if (!SysSvIsUtf8Text(SysSvFromParts(aux, request.AuxLength))) {
(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;
const char* scopePath = session->ScopePath;
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,
scopePath,
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]);
}
}
// A scoped publisher owns the binding for the lifetime of its connected
// session. Release it on abrupt disconnect as well as graceful unbind.
if (scopePath)
(void)RssrvNamespaceUnbind(service->Namespace, scopePath, (uint32_t)strlen(scopePath));
(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;
session->ScopePath = worker->ScopePath[0] ? worker->ScopePath : NULL;
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);
}
// Reuses a per-path authority across service generations and never broadens its scope.
static KSTATUS RssrvCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, uint32_t length,
SYS_HANDLE* listenerOut) {
if (!service || !path || !listenerOut || !length ||
length > RSSRV_PUBLICATION_PATH_MAX) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = SysWait(service->ScopeMutex, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) return status;
for (uint32_t i = 0; i < service->ScopeCount; ++i) {
RSSRV_LISTENER_WORKER* worker = &service->ScopeWorkers[i];
if (strlen(worker->ScopePath) == length &&
memcmp(worker->ScopePath, path, length) == 0) {
*listenerOut = worker->Listener;
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_SUCCESS;
}
}
if (service->ScopeCount >= RSSRV_PUBLICATION_SCOPE_MAX) {
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_LIMIT_REACHED;
}
RSSRV_LISTENER_WORKER* worker = &service->ScopeWorkers[service->ScopeCount];
memset(worker, 0, sizeof(*worker));
memcpy(worker->ScopePath, path, length);
worker->ScopePath[length] = '\0';
status = RssrvCreateListener(&worker->Listener);
if (KSUCCESS(status))
status = RssrvStartListenerWorker(service, worker, worker->Listener, 1u);
if (!KSUCCESS(status)) {
if (worker->Listener != SYS_HANDLE_INVALID) (void)SysCloseHandle(worker->Listener);
memset(worker, 0, sizeof(*worker));
(void)SysReleaseMutex(service->ScopeMutex);
return status;
}
++service->ScopeCount;
*listenerOut = worker->Listener;
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_SUCCESS;
}
// Issues a publisher restricted to one RSSRV-owned namespace path.
KSTATUS RssrvResourceCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, SYS_HANDLE* listenerOut) {
if (!path) return NTLSTATUS_INVALID_PARAMETER;
return RssrvCreatePublisher(service, path, (uint32_t)strlen(path), listenerOut);
}
// 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;
KSTATUS lockStatus = SysCreateMutex(&service->ScopeMutex);
if (!KSUCCESS(lockStatus)) return lockStatus;
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;
}