Changes
3 changed files (+117/-110)
-
-
@@ -83,13 +83,13 @@ return 1;} sood_discovery_response resp; sood_discovery_response_parse_result result = sood_discovery_response_parse( sood_result result = sood_discovery_response_parse( &resp, received, received_size ); if (result != SOOD_DISCOVERY_RESPONSE_PARSE_OK) { printf("Parse error: %d\n", result); if (result != SOOD_OK) { printf("Parse error: %d (%s)\n", result, sood_get_result_string(result)); close(sockfd); return 2; }
-
-
-
@@ -30,6 +30,31 @@#include <stdint.h> typedef enum { SOOD_OK = 0, SOOD_ITERATOR_DONE = 1, SOOD_ERR_PARSE_HEADER_SIZE_MISMATCH = 2, SOOD_ERR_PARSE_INVALID_SIGNATURE = 3, SOOD_ERR_PARSE_EMPTY_KEY = 4, SOOD_ERR_PARSE_KEY_SIZE_MISMATCH = 5, SOOD_ERR_PARSE_NON_UTF8_KEY = 6, SOOD_ERR_PARSE_VALUE_SIZE_CORRUPTED = 7, SOOD_ERR_PARSE_VALUE_SIZE_MISMATCH = 8, SOOD_ERR_PARSE_NON_UTF8_VALUE = 9, SOOD_ERR_VALIDATE_MISSING_REQUIRED_PROPERTY = 10, SOOD_ERR_VALIDATE_UNEXPECTED_VALUE_TYPE = 11, SOOD_ERR_VALIDATE_UNEXPECTED_MESSAGE_KIND = 12, } sood_result; /** * Get text representation of "sood_result". Text does not have "SOOD_" prefix. * * Returns an address of a null-terminated string (C string). * * Returns a NULL (0) when "result" is not a valid "sood_result" value. */ const char* sood_get_result_string(sood_result result); typedef enum { SOOD_MESSAGE_UNKNOWN = 0, SOOD_MESSAGE_QUERY = 1, SOOD_MESSAGE_RESPONSE = 2,
-
@@ -41,27 +66,24 @@ size_t raw_len;sood_message_kind kind; } sood_message; typedef enum { SOOD_PARSE_OK = 0, SOOD_PARSE_ERROR_HEADER_SIZE_MISMATCH = 1, SOOD_PARSE_ERROR_INVALID_SIGNATURE = 2, } sood_parse_result; /** * Parse bytes as SOOD message. * * This function ONLY parses header part: remaining bytes (properties) are unchecked. * Validations are performed on "sood_message_iter_next". Getting "SOOD_PARSE_OK" * does not mean the message is valid. * * If you just want to perform service discovery, use "sood_discovery_response_parse" instead. * This function is for advanced use-cases. * * Returns "SOOD_OK" when message header is valid and body part is ready to read. * The body part is not checked thus user should not assume a message is valid until iterating * through body. * * Returns "SOOD_ERR_PARSE_HEADER_SIZE_MISMATCH" or "SOOD_ERR_PARSE_INVALID_SIGNATURE" * when receiving bytes is not a valid SOOD message header. * * @param dst - Pointer for message struct to set parsed data. * @param ptr - Pointer for the source bytes. * @param len - Length of the source bytes. */ sood_parse_result sood_parse(sood_message *dst, const char *ptr, size_t len); sood_result sood_parse(sood_message *dst, const char *ptr, size_t len); typedef struct { const char *key_ptr;
-
@@ -70,17 +92,6 @@ const char *value_ptr;size_t value_len; } sood_message_property; typedef enum { SOOD_READ_PROPERTY_OK = 0, SOOD_READ_PROPERTY_DONE = 1, SOOD_READ_PROPERTY_ERROR_EMPTY_KEY = 2, SOOD_READ_PROPERTY_ERROR_KEY_SIZE_MISMATCH = 3, SOOD_READ_PROPERTY_ERROR_NON_UTF8_KEY = 4, SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_CORRUPTED = 5, SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_MISMATCH = 6, SOOD_READ_PROPERTY_ERROR_NON_UTF8_VALUE = 7, } sood_message_read_property_result; typedef struct { const sood_message *msg; size_t i;
-
@@ -97,14 +108,24 @@/** * Moves iterator next and parse next property into "dst". * * If iterator is at the end of body, this function returns "SOOD_READ_PROPERTY_DONE". * This function returns "SOOD_READ_PROPERTY_ERROR_*" when encountered message fields * violating SOOD message properties schema. * Returns "SOOD_OK" when it reads property into "dst". * * Returns "SOOD_ITERATOR_DONE" when no more properties to read. * "dst" is intact. * * Returns below error results when a message is not valid SOOD message: * * - SOOD_ERR_PARSE_EMPTY_KEY * - SOOD_ERR_PARSE_KEY_SIZE_MISMATCH * - SOOD_ERR_PARSE_NON_UTF8_KEY * - SOOD_ERR_PARSE_VALUE_SIZE_CORRUPTED * - SOOD_ERR_PARSE_VALUE_SIZE_MISMATCH * - SOOD_ERR_PARSE_NON_UTF8_VALUE * * @param iter - Iterator to iterate on. * @param dst - Pointer for property struct to put read key/value in. */ sood_message_read_property_result sood_message_iter_next( sood_result sood_message_iter_next( sood_message_iter *iter, sood_message_property *dst );
-
@@ -133,21 +154,6 @@ * Destination UDP port for IP multicast and broadcast.*/ extern const uint16_t SOOD_DISCOVERY_SERVER_UDP_PORT; typedef enum { SOOD_DISCOVERY_RESPONSE_PARSE_OK = 0, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_HEADER_SIZE_MISMATCH = 1, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_INVALID_SIGNATURE = 2, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_EMPTY_KEY = 3, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_KEY_SIZE_MISMATCH = 4, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_KEY = 5, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_CORRUPTED = 6, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_MISMATCH = 7, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_VALUE = 8, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_KIND = 9, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_MISSING_PROPERTY = 10, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_VALUE = 11, } sood_discovery_response_parse_result; typedef struct { uint16_t http_port; const char *name_ptr;
-
@@ -161,14 +167,18 @@/** * Parse bytes as SOOD response message. * * This function completely scans properties field and returns SOOD_DISCOVERY_RESPONSE_PARSE_OK * only when the whole message is valid and every required field exists. * Returns "SOOD_OK" when the whole bytes consists valid SOOD message and read into "dst". * * Returns "SOOD_ERR_PARSE_*" when the received bytes is not a valid SOOD message. * * Returns "SOOD_ERR_VALIDATE_*" when the received bytes is valid but the parsed message * does not qualify as a response message sent by Roon Server. * * @param dst - Pointer for response struct to set parsed properties. * @param message_ptr - Pointer for source bytes. * @param message_len - Byte size of the source bytes. */ sood_discovery_response_parse_result sood_discovery_response_parse( sood_result sood_discovery_response_parse( sood_discovery_response *dst, const char *message_ptr, size_t message_len
-
-
-
@@ -26,6 +26,53 @@ const sood = @import("lib.zig");const property = @import("Message/property.zig"); const constants = @import("constants.zig"); const sood_result = enum(c_int) { SOOD_OK = 0, SOOD_ITERATOR_DONE = 1, SOOD_ERR_PARSE_HEADER_SIZE_MISMATCH = 2, SOOD_ERR_PARSE_INVALID_SIGNATURE = 3, SOOD_ERR_PARSE_EMPTY_KEY = 4, SOOD_ERR_PARSE_KEY_SIZE_MISMATCH = 5, SOOD_ERR_PARSE_NON_UTF8_KEY = 6, SOOD_ERR_PARSE_VALUE_SIZE_CORRUPTED = 7, SOOD_ERR_PARSE_VALUE_SIZE_MISMATCH = 8, SOOD_ERR_PARSE_NON_UTF8_VALUE = 9, SOOD_ERR_VALIDATE_MISSING_REQUIRED_PROPERTY = 10, SOOD_ERR_VALIDATE_UNEXPECTED_VALUE_TYPE = 11, SOOD_ERR_VALIDATE_UNEXPECTED_MESSAGE_KIND = 12, const CAPIError = sood.Message.HeaderParseError || sood.Message.PropertyParseError || sood.discovery.Response.SchemaError; pub fn fromError(err: CAPIError) sood_result { return switch (err) { sood.Message.HeaderParseError.InvalidHeaderSize => .SOOD_ERR_PARSE_HEADER_SIZE_MISMATCH, sood.Message.HeaderParseError.InvalidSignature => .SOOD_ERR_PARSE_INVALID_SIGNATURE, sood.Message.PropertyParseError.EmptyKey => .SOOD_ERR_PARSE_EMPTY_KEY, sood.Message.PropertyParseError.IncompleteKey => .SOOD_ERR_PARSE_KEY_SIZE_MISMATCH, sood.Message.PropertyParseError.NonUTF8Key => .SOOD_ERR_PARSE_NON_UTF8_KEY, sood.Message.PropertyParseError.InvalidValueSizeField => .SOOD_ERR_PARSE_VALUE_SIZE_CORRUPTED, sood.Message.PropertyParseError.NonUTF8Value => .SOOD_ERR_PARSE_NON_UTF8_VALUE, sood.Message.PropertyParseError.IncompleteValue => .SOOD_ERR_PARSE_VALUE_SIZE_MISMATCH, sood.discovery.Response.SchemaError.MissingRequiredProperty => .SOOD_ERR_VALIDATE_MISSING_REQUIRED_PROPERTY, sood.discovery.Response.SchemaError.UnexpectedPropertyValue => .SOOD_ERR_VALIDATE_UNEXPECTED_VALUE_TYPE, sood.discovery.Response.SchemaError.NonResponseKindMessage => .SOOD_ERR_VALIDATE_UNEXPECTED_MESSAGE_KIND, }; } }; export fn sood_get_result_string(result: sood_result) ?[*:0]const u8 { return inline for (@typeInfo(sood_result).@"enum".fields) |field| { if (@intFromEnum(result) == field.value) { // Although we can archive the same with pointer arithmetic, we can let // compiler check boundary by slicing. For example, if we change below // line to: // break field.name.ptr + 10; // out of bounds (SOOD_OK ... 7 bytes) // it compiles and reads unintended region. break field.name[5..].ptr; } } else null; } const sood_message_kind = enum(c_int) { SOOD_MESSAGE_UNKNOWN = 0, SOOD_MESSAGE_QUERY = 1,
-
@@ -38,18 +85,11 @@ raw_len: usize,sood_message_kind: sood_message_kind, }; const sood_parse_result = enum(c_int) { SOOD_PARSE_OK = 0, SOOD_PARSE_ERROR_HEADER_SIZE_MISMATCH = 1, SOOD_PARSE_ERROR_INVALID_SIGNATURE = 2, }; export fn sood_parse(dst: *sood_message, ptr: [*]const u8, len: usize) sood_parse_result { export fn sood_parse(dst: *sood_message, ptr: [*]const u8, len: usize) sood_result { const bytes = ptr[0..len]; const message = sood.Message.parse(bytes) catch |err| return switch (err) { sood.Message.HeaderParseError.InvalidHeaderSize => .SOOD_PARSE_ERROR_HEADER_SIZE_MISMATCH, sood.Message.HeaderParseError.InvalidSignature => .SOOD_PARSE_ERROR_INVALID_SIGNATURE, const message = sood.Message.parse(bytes) catch |err| { return sood_result.fromError(err); }; dst.raw_ptr = ptr;
-
@@ -60,7 +100,7 @@ .query => .SOOD_MESSAGE_QUERY,.response => .SOOD_MESSAGE_RESPONSE, }; return .SOOD_PARSE_OK; return .SOOD_OK; } const sood_message_property = extern struct {
-
@@ -70,17 +110,6 @@ value_ptr: [*]const u8,value_len: usize, }; const sood_message_read_property_result = enum(c_int) { SOOD_READ_PROPERTY_OK = 0, SOOD_READ_PROPERTY_DONE = 1, SOOD_READ_PROPERTY_ERROR_EMPTY_KEY = 2, SOOD_READ_PROPERTY_ERROR_KEY_SIZE_MISMATCH = 3, SOOD_READ_PROPERTY_ERROR_NON_UTF8_KEY = 4, SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_CORRUPTED = 5, SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_MISMATCH = 6, SOOD_READ_PROPERTY_ERROR_NON_UTF8_VALUE = 7, }; const sood_message_iter = extern struct { msg: *const sood_message, i: usize,
-
@@ -94,21 +123,16 @@export fn sood_message_iter_next( iter: *sood_message_iter, dst: *sood_message_property, ) sood_message_read_property_result { ) sood_result { const bytes = iter.msg.raw_ptr[iter.i..iter.msg.raw_len]; if (bytes.len == 0) { return .SOOD_READ_PROPERTY_DONE; return .SOOD_ITERATOR_DONE; } var p: sood.Message.Property = undefined; iter.i += property.parseInto(bytes, &p) catch |err| return switch (err) { sood.Message.PropertyParseError.EmptyKey => .SOOD_READ_PROPERTY_ERROR_EMPTY_KEY, sood.Message.PropertyParseError.IncompleteKey => .SOOD_READ_PROPERTY_ERROR_KEY_SIZE_MISMATCH, sood.Message.PropertyParseError.NonUTF8Key => .SOOD_READ_PROPERTY_ERROR_NON_UTF8_KEY, sood.Message.PropertyParseError.InvalidValueSizeField => .SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_CORRUPTED, sood.Message.PropertyParseError.NonUTF8Value => .SOOD_READ_PROPERTY_ERROR_NON_UTF8_VALUE, sood.Message.PropertyParseError.IncompleteValue => .SOOD_READ_PROPERTY_ERROR_VALUE_SIZE_MISMATCH, iter.i += property.parseInto(bytes, &p) catch |err| { return sood_result.fromError(err); }; dst.key_ptr = p.key.ptr;
-
@@ -116,7 +140,7 @@ dst.key_len = p.key.len;dst.value_ptr = p.value.ptr; dst.value_len = p.value.len; return .SOOD_READ_PROPERTY_OK; return .SOOD_OK; } export const SOOD_DISCOVERY_QUERY_PREBUILT = constants.prebuilt_query.*;
-
@@ -134,21 +158,6 @@ constants.prebuilt_query.len,); } const sood_discovery_response_parse_result = enum(c_int) { SOOD_DISCOVERY_RESPONSE_PARSE_OK = 0, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_HEADER_SIZE_MISMATCH = 1, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_INVALID_SIGNATURE = 2, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_EMPTY_KEY = 3, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_KEY_SIZE_MISMATCH = 4, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_KEY = 5, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_CORRUPTED = 6, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_MISMATCH = 7, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_VALUE = 8, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_KIND = 9, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_MISSING_PROPERTY = 10, SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_VALUE = 11, }; const sood_discovery_response = extern struct { http_port: u16, name_ptr: [*]const u8,
-
@@ -163,21 +172,9 @@ export fn sood_discovery_response_parse(dst: *sood_discovery_response, message_ptr: [*]const u8, message_len: usize, ) sood_discovery_response_parse_result { const ParseError = sood.discovery.Response.ParseError; const resp = sood.discovery.Response.parse(message_ptr[0..message_len]) catch |err| return switch (err) { ParseError.InvalidHeaderSize => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_HEADER_SIZE_MISMATCH, ParseError.InvalidSignature => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_INVALID_SIGNATURE, ParseError.EmptyKey => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_EMPTY_KEY, ParseError.IncompleteKey => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_KEY_SIZE_MISMATCH, ParseError.NonUTF8Key => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_KEY, ParseError.InvalidValueSizeField => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_CORRUPTED, ParseError.IncompleteValue => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_VALUE_SIZE_MISMATCH, ParseError.NonUTF8Value => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_NON_UTF8_VALUE, ParseError.NonResponseKindMessage => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_KIND, ParseError.MissingRequiredProperty => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_MISSING_PROPERTY, ParseError.UnexpectedPropertyValue => .SOOD_DISCOVERY_RESPONSE_PARSE_ERROR_UNEXPECTED_VALUE, ) sood_result { const resp = sood.discovery.Response.parse(message_ptr[0..message_len]) catch |err| { return sood_result.fromError(err); }; dst.http_port = resp.http_port;
-
@@ -188,5 +185,5 @@ dst.display_version_len = resp.display_version.len;dst.unique_id_ptr = resp.unique_id.ptr; dst.unique_id_len = resp.unique_id.len; return .SOOD_DISCOVERY_RESPONSE_PARSE_OK; return .SOOD_OK; }
-