Changes
8 changed files (+619/-1)
-
-
@@ -15,7 +15,7 @@ // <https://ziglang.org/learn/build-system/> for more info.const std = @import("std"); pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const linkage = b.option(std.builtin.LinkMode, "linkage", "Link mode of the generated library file") orelse .static;
-
@@ -36,6 +36,25 @@ });lib.installHeader(b.path("src/c.h"), "sood.h"); b.installArtifact(lib); b.default_step.dependOn(&lib.step); // Man page const man_dir_path = try b.build_root.join(b.allocator, &.{"man"}); var man_dir = try std.fs.cwd().openDir(man_dir_path, .{ .iterate = true }); defer man_dir.close(); var it = man_dir.iterate(); while (try it.next()) |entry| { if (entry.kind != .file) { continue; } const section = entry.name[entry.name.len - 1]; b.installFile( b.fmt("man/{s}", .{entry.name}), b.fmt("share/man/man{u}/{s}", .{ section, entry.name }), ); } // Install WASM build. const wasm_step = b.step("wasm", "Build WebAssembly module");
-
-
man/sood.3 (new)
-
@@ -0,0 +1,142 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood 3 .SH NAME sood \- SOOD message used for Roon service discovery .SH SYNOPSIS .nf .B #include <sood.h> .fi .SH DESCRIPTION SOOD message is a UDP packet data format used for Roon Server discovery. This .B libsood library provides parser functions and constants for the service discovery process. .PP This library does NOT provide functions performing I/O. User is responsible for sending and/or receiving UDP packet. .SS Strings Otherwise noted, this library uses an address and length to represent strings, named "*_ptr" and "*_len" correspondingly. Characters are encoded as UTF\-8. .SS Allocation This library does not allocate memory. User is responsible for allocating a struct and pass an address of the struct via function parameter, usually named .B dst\fR. .SS Bit size Platforms having non-8bit byte (char) are not supported. .SH RETURN VALUE Functions provided by this library returns a shared enum as result codes. .SS SOOD_OK Procedure successfully completed and destination pointer is populated if any. .SS SOOD_ITERATOR_DONE Only .BR sood_message_iter_next (3) returns this code. Iterator reached at the end of a message and no more bytes to read. .SS SOOD_ERR_PARSE_HEADER_SIZE_MISMATCH Aborted header parsing due to received byte size being shorter than that of SOOD message header. .SS SOOD_ERR_PARSE_INVALID_SIGNATURE Aborted header parsing due to the received bytes does not starts with SOOD message signature (magic bytes). .SS SOOD_ERR_PARSE_EMPTY_KEY Aborted body parsing due to size of key is 0. libsood currently rejects empty keys. .SS SOOD_ERR_PARSE_KEY_SIZE_MISMATCH Aborted body parsing due to message bytes ends at middle of a key field. .SS SOOD_ERR_PARSE_NON_UTF8_KEY Aborted body parsing due to message has a key that is not a valid UTF-8 string. .SS SOOD_ERR_PARSE_VALUE_SIZE_CORRUPTED Aborted body parsing due to message bytes ends at middle of value size field. (A value size field is unsigned 16bit integer.) .SS SOOD_ERR_PARSE_VALUE_SIZE_MISMATCH Aborted body parsing due to message bytes ends at middle of a value field. .SS SOOD_ERR_PARSE_NON_UTF8_VALUE Aborted body parsing due to message has a value that is not a valid UTF-8 string. As SOOD message is schema\-less key\-value, every value has to be a valid UTF-8 string. .SS SOOD_ERR_VALIDATE_MISSING_REQUIRED_PROPERTY Aborted response message parsing due to message does not have expected key. .SS SOOD_ERR_VALIDATE_UNEXPECTED_VALUE_TYPE Aborted response message parsing due to fields have unexpected format, such as port number field having non\-decimal string. .SS SOOD_ERR_VALIDATE_UNEXPECTED_MESSAGE_KIND Aborted response message parsing due to message type field is not set to .BR R . .SH CONSTANTS .SS SOOD_DISCOVERY_QUERY_PREBUILT A SOOD message ready to send via IP multicast or broadcast. This bytes contains a NUL character: do not treat as C\-strings. .nf .RS #include <sys/socket.h> sendto (sockfd, SOOD_DISCOVERY_QUERY_PREBUILT, sizeof(SOOD_DISCOVERY_QUERY_PREBUILT), 0, &multicast_address, sizeof(multicast_address)); .RE .fi .SS SOOD_DISCOVERY_MULTICAST_IPV4_ADDRESS_BE IPv4 multicast address Roon Server listens to. This is in network byte order. .SS SOOD_DISCOVERY_MULTICAST_IPV4_ADDRESS Same as .BR SOOD_DISCOVERY_MULTICAST_IPV4_ADDRESS_BE , but in a byte array. .SS SOOD_DISCOVERY_SERVER_UDP_PORT UDP port for sending IPv4 multicast and broadcast to. .SH SEE ALSO .BR sood_parse (3), .BR sood_get_result_string (3), .BR sood_message_iterator (3), .BR sood_message_iter_next (3), .BR sood_discovery_response_parse (3)
-
-
man/sood.7 (new)
-
@@ -0,0 +1,107 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood 7 .SH NAME libsood \- Library for parsing and building SOOD message, binary format for Roon service discovery .SH DESCRIPTION .B libsood provides parser and builder functions for SOOD message, which is binary message format sent over UDP, to perform Roon Server discovery. Code is written in Zig and available as Zig library, static/dynamic library (for C and languages supporting C API), and WebAssembly module. .PP Currently, only Zig library expose a function to build SOOD message. .PP To perform Roon Server discovery, send a prebuilt discovery query message via IP multicast. You can send the query at every IPv4 network interfaces too, like the official Roon Node.js API[0]. You can also send the query via UDP broadcast, for a single interface or multiple interfaces, like the Roon Node.js API and Pyroon[1]. .B libsood exports the IPv4 multicast address and target UDP port number for this process. .nf [0] https://github.com/RoonLabs/node-roon-api [1] https://github.com/pavoni/pyroon .fi .PP When a Roon Server receives the discovery query message, it may then send back a discovery response message to the sender. Parse the discovery response message using a function from .B libsood and you'll get necessary information to communicate with the Roon Server. Note that the response message does NOT contain IP address of the Roon Server; read that from a UDP packet. .PP Due to the nature of UDP and probably Roon Server's throttle mechanism, sending of the discovery query message does not gurantee a discovery response message. You should set an appropriate timeout and resend the query until you receive a discovery response message. .SH DOCUMENTATION .SS Zig library Not available yet. However, API is simple and source code has okay\-ish amount of doc comments and unit tests. Consulting for the source code would be the best way to learn the API. .SS C API See .BR sood (3). .SS WebAssembly module Not available yet. .SH LIBRARY DESIGN .SS Zig library Zig API is designed to be simple and ease\-to\-use, rather than performance focused. Functions return a struct in general, hoping Zig compiler deciding good memory location (copy or pointer). .PP I/O is not included in the Zig API. Users are responsible for performing UDP send/recv operations. .SS C API Like Zig API, .BR sood (3) avoids any I/O including memory allocation, for portability. It runs everywhere Zig compiler supports and C99 is available. .PP Users are responsible for allocating struct and passing pointer for functions. .SS WebAssembly module WebAssembly module is designed to be bare\-minimum: only high\-level response parsing. .PP Considering various language can access the module through WebAssembly runtime, WebAssembly module API is designed to minimize the need for raw memory handling. Every struct fields has corresponding getter function that returns a field value, and every slice has functions to get an address and length of the slice. .SH SEE ALSO .BR sood (3)
-
-
-
@@ -0,0 +1,87 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood_discovery_response_parse 3 .SH NAME sood_discovery_response_parse \- Parse SOOD message sent by Roon Server .SH SYNOPSIS .nf .B #include <sood.h> \fBsood_result sood_discovery_response_parse( sood_discovery_response \fR*\fIdst\fB, const char \fR*\fImessage_ptr\fB, size_t \fImessage_len\fB ); .fi .SH DESCRIPTION Parses an array of bytes as a SOOD message, then populate .I *dst with parsed data if the bytes is a valid SOOD message. .PP .BR sood_discovery_response_parse () does, unlike .BR sood_parse (3) and .BR sood_message_iter_next (3), parse the entire message bytes in one-go, validate known properties and cast numerical properties into corresponding data type. Unless you need an access to properties not defined in the .BR sood_discovery_response , use .BR sood_discovery_response_parse (). .SH RETURN VALUE See .BR sood (3) for a list of return values. .SH EXAMPLES .nf #include <sys/socket.h> char received[512]; ssize_t received_size; sood_discovery_response resp; sood_result result; received_size = recv(sockfd, received, sizeof(received), 0); close(sockfd); if (received_size > 0) { result = sood_discovery_response_parse( &resp, received, received_size ); if (result != SOOD_OK) { printf("%s\\n", sood_get_result_string(result)); return 1; } printf("Name: %.*s\\n", resp.name_len, resp.name_ptr); printf("Version: %.*s\\n", resp.display_version_len, resp.display_version_ptr); printf("Unique ID: %.*s\\n", resp.unique_id_len, resp.unique_id_ptr); printf("HTTP port: %d\\n", resp.http_port); return 0; } .fi .SH SEE ALSO .BR sood (3), .BR sood_get_result_string (3)
-
-
-
@@ -0,0 +1,51 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood_get_result_string 3 .SH NAME sood_get_result_string \- Get a textual representation of code returned by libsood functions .SH SYNOPSIS .nf .B #include <sood.h> \fBconst char* sood_get_result_string(sood_result \fIresult\fB); .fi .SH DESCRIPTION Get a human\-readable textual representation of result code in C\-string (NUL\-terminated string) form. .SH RETURN VALUE Pointer for the text, terminated by NUL character. .SH EXAMPLES .nf #include <sood.h> sood_result result; result = sood_parse(&msg, bytes_ptr, bytes_len); printf("%s\\n", sood_get_result_string(result)); printf("%s\\n", SOOD_OK); /* "OK" */ printf("%s\\n", SOOD_ERR_PARSE_EMPTY_KEY); /* "ERR_PARSE_EMPTY_KEY" */ .fi .SH SEE ALSO .BR sood (3), .BR sood_discovery_response_parse (3), .BR sood_parse (3), .BR sood_message_iter_next (3)
-
-
-
@@ -0,0 +1,102 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood_message_iter_next 3 .SH NAME sood_message_iter_next \- Get a next property from SOOD message body .SH SYNOPSIS .nf .B #include <sood.h> \fBsood_result sood_message_iter_next(sood_message_iter \fR*\fIiter\fB, sood_message_property \fR*\fIdst\fB); .fi .SH DESCRIPTION Advances an iterator and reads a next property from a .B sood_message_iter into .I *dst\fR. Make sure to call .BR sood_message_iterator (3) before calling .BR sood_message_iter_next () - operating upon uninitialized .B sood_message_iter will lead to reading from uninitialized memory. .PP If an iterator is at the end of a SOOD message body, .BR sood_message_iter_next () returns .B SOOD_ITERATOR_DONE result code. .SH RETURN VALUE See .BR sood (3) for a list of return values. .SH EXAMPLES .nf #include <sys/socket.h> #include <string.h> #include <sood.h> char received[512]; ssize_t received_size; sood_result result; sood_message msg; sood_message_iter iter; sood_message_property prop; received_size = recv(sockfd, received, sizeof(received), 0); close(sockfd); if (received_size > 0) { result = sood_parse(&msg, received, received_size); if (result != SOOD_OK) { printf("%s\\n", sood_get_result_string(result)); return 1; } sood_message_iterator(&iter, &msg); while (1) { memset(&prop, 0, sizeof(prop)); result = sood_message_iter_next(&iter, &prop); switch (result) { case SOOD_OK: printf("%.*s=%.*s\\n", prop.key_ptr, prop.key_len, prop.value_ptr, prop.value_len); break; case SOOD_ITERATOR_DONE: return 0; case SOOD_ITERATOR_DONE: printf("%s\\n", sood_get_result_string(result)); return 1; } } } .fi .SH SEE ALSO .BR sood (3), .BR sood_discovery_response_parse (3), .BR sood_get_result_string (3), .BR sood_parse (3), .BR sood_message_iterator (3)
-
-
-
@@ -0,0 +1,44 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood_message_iterator 3 .SH NAME sood_message_iterator \- Initialize SOOD message body iterator .SH SYNOPSIS .nf .B #include <sood.h> \fBvoid sood_message_iterator(sood_message_iter \fR*\fIdst\fB, const sood_message \fR*\fImsg\fB); .fi .SH DESCRIPTION Initializes an iterator for parsing SOOD message body (properties) from .B sood_message\fR, which is populated by .BR sood_parse (3)\fR. .SH EXAMPLES An example of the use of .BR sood_message_iterator () is shown in .BR sood_message_iter_next (3)\fR. .SH SEE ALSO .BR sood (3), .BR sood_parse (3), .BR sood_message_iter_next (3)
-
-
man/sood_parse.3 (new)
-
@@ -0,0 +1,66 @@.\" Copyright 2025 Shota FUJI .\" .\" Licensed under the Zero-Clause BSD License or the Apache License, Version 2.0, at your option. .\" You may not use, copy, modify, or distribute this file except according to those terms. You can .\" find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt, and a copy of the Apache License, .\" Version 2.0 at LICENSES/Apache-2.0.txt. You may also obtain a copy of the Apache License, Version .\" 2.0 at <https://www.apache.org/licenses/LICENSE-2.0> .\" .\" SPDX-License-Identifier: 0BSD OR Apache-2.0 .TH sood_parse 3 .SH NAME sood_parse \- Parse SOOD message header used for Roon service discovery .SH SYNOPSIS .nf .B #include <sood.h> \fBsood_result sood_parse(sood_message \fR*\fIdst\fB, const char \fR*\fIptr\fB, size_t \fIlen\fB); .fi .SH DESCRIPTION Parses an array of bytes as SOOD message header, then populate .I *dst with parsed data if a start of .I *msg is valid SOOD message header bytes. .PP This is low-level utility function. To simply parse Roon Server info, use .BR sood_discovery_response_parse (3) instead. .SH RETURN VALUE See .BR sood (3) for a list of return values. .SH CAVEATS User should not assume an entire bytes is valid SOOD message based sorely on the result of .BR sood_parse () - this function returns .B SOOD_OK even if a message contains malformed body after the header part. .SH EXAMPLES An example of the use of .BR sood_parse () is shown in .BR sood_message_iter_next (3)\fR. .SH SEE ALSO .BR sood (3), .BR sood_discovery_response_parse (3), .BR sood_message_iterator (3)
-