Files

97 lines
4.2 KiB
C

// Native service-manager control protocol. Names determine exported handle identity.
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/port.h>
#include <ntlite/status.h>
#define NTL_SERVICE_CONTROL_VERSION 3u
#define NTL_SERVICE_CONTROL_READY 1u
#define NTL_SERVICE_CONTROL_STOP 2u
#define NTL_SERVICE_EXPORT_NAME_MAX 31u
// READY transfers one handle for each named entry in ExportNames. The entry at
// index i names the handle at transfer index i; its position has no semantic
// meaning. STOP transfers zero handles and carries only zeroed export names.
// Each message is wholly self-contained so the Port cannot retain user pointers.
typedef struct NTL_SERVICE_CONTROL_MESSAGE {
uint32_t Size;
uint32_t Version;
uint32_t Operation;
uint32_t ExportCount;
KSTATUS Status;
uint32_t Reserved[3];
char ExportNames[SYS_PORT_MAX_TRANSFER_HANDLES][NTL_SERVICE_EXPORT_NAME_MAX + 1u];
} NTL_SERVICE_CONTROL_MESSAGE;
// Accepts the deliberately restricted, case-sensitive capability-name grammar.
static inline bool NtlServiceExportNameValid(const char* name) {
if (!name || !name[0]) return false;
for (uint32_t i = 0; i <= NTL_SERVICE_EXPORT_NAME_MAX; ++i) {
char c = name[i];
if (!c) return i != 0;
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.'))
return false;
}
return false;
}
// Labels an already counted READY transfer without assigning authority to its name.
static inline bool NtlServiceControlNameExport(NTL_SERVICE_CONTROL_MESSAGE* message,
uint32_t index, const char* name) {
if (!message || message->Operation != NTL_SERVICE_CONTROL_READY ||
index >= message->ExportCount || index >= SYS_PORT_MAX_TRANSFER_HANDLES ||
!NtlServiceExportNameValid(name))
return false;
memset(message->ExportNames[index], 0, sizeof(message->ExportNames[index]));
size_t length = strlen(name);
memcpy(message->ExportNames[index], name, length);
return true;
}
// Validates wire size, exact transfer count, export names, and zeroed unused entries.
static inline bool NtlServiceControlValid(const NTL_SERVICE_CONTROL_MESSAGE* message,
uint32_t operation, uint32_t handleCount) {
if (!message || message->Size != sizeof(*message) ||
message->Version != NTL_SERVICE_CONTROL_VERSION || message->Operation != operation ||
(operation != NTL_SERVICE_CONTROL_READY && operation != NTL_SERVICE_CONTROL_STOP) ||
message->ExportCount != handleCount || handleCount > SYS_PORT_MAX_TRANSFER_HANDLES ||
message->Reserved[0] || message->Reserved[1] || message->Reserved[2])
return false;
if (operation == NTL_SERVICE_CONTROL_STOP &&
(handleCount != 0u || message->Status != NTLSTATUS_SUCCESS))
return false;
for (uint32_t i = 0; i < SYS_PORT_MAX_TRANSFER_HANDLES; ++i) {
const char* name = message->ExportNames[i];
if (i >= handleCount) {
for (uint32_t j = 0; j <= NTL_SERVICE_EXPORT_NAME_MAX; ++j)
if (name[j]) return false;
continue;
}
if (!NtlServiceExportNameValid(name)) return false;
// The complete fixed-size wire representation has one canonical encoding.
uint32_t end = 0;
while (name[end]) ++end;
for (uint32_t j = end + 1u; j <= NTL_SERVICE_EXPORT_NAME_MAX; ++j)
if (name[j]) return false;
for (uint32_t earlier = 0; earlier < i; ++earlier)
if (strcmp(name, message->ExportNames[earlier]) == 0) return false;
}
return true;
}
// Finds the transfer index for a validated named READY export.
static inline int32_t NtlServiceControlFindExport(const NTL_SERVICE_CONTROL_MESSAGE* message,
const char* name) {
if (!message || !NtlServiceExportNameValid(name) ||
message->ExportCount > SYS_PORT_MAX_TRANSFER_HANDLES)
return -1;
for (uint32_t i = 0; i < message->ExportCount; ++i)
if (strcmp(message->ExportNames[i], name) == 0) return (int32_t)i;
return -1;
}