// Native service-manager control protocol. Names determine exported handle identity. #pragma once #include #include #include #include #include #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; }