// RSSRV resource protocol listeners, sessions, request validation, and capability transfer. #include #include "rssrv_internal.h" #include "publication_scope.h" #include #include 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; }