aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorwinter Sparkles2026-04-27 21:55:21 +0100
committerwinter Sparkles2026-04-27 21:55:21 +0100
commitc38bcb25aa1805009e399cea40e66d6f31195744 (patch)
tree1ac302eb72966eaf106e8cb070a7a32def8370b0
parent05a9d192ee17c503172e785fd1383a62f2feb20b (diff)
add requests (but not responses yet)
-rw-r--r--Doxyfile4
-rw-r--r--Makefile4
-rw-r--r--docs/examples.md3
-rw-r--r--docs/write-request.example.c14
-rw-r--r--libsparklepaw/common.h13
-rw-r--r--libsparklepaw/request.c110
-rw-r--r--libsparklepaw/request.h87
7 files changed, 231 insertions, 4 deletions
diff --git a/Doxyfile b/Doxyfile
index 96d1964..47facd9 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1750,7 +1750,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
-DISABLE_INDEX = YES
+DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
@@ -1767,7 +1767,7 @@ DISABLE_INDEX = YES
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
-GENERATE_TREEVIEW = YES
+GENERATE_TREEVIEW = NO
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
diff --git a/Makefile b/Makefile
index 4d46ec2..07afb7e 100644
--- a/Makefile
+++ b/Makefile
@@ -12,10 +12,10 @@ CFLAGS := -Wall -Wextra -flto
### build mode
ifeq ($(mode),debug)
- CFLAGS += -g -Og -DDEBUG
+ CFLAGS += -g -Og
BUILD_DIR := $(BUILD_DIR)/debug
else
- CFLAGS += -O3
+ CFLAGS += -O3 -DNDEBUG
LDFLAGS += -s
endif
diff --git a/docs/examples.md b/docs/examples.md
index b517053..9d2a6d1 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -7,3 +7,6 @@ Example of constructing a service and writing it out:
Example of reading a service in and printing it for debugging:
\include read-service.example.c
+
+Example of making a request (to stdout instead of a transport):
+\include write-request.example.c
diff --git a/docs/write-request.example.c b/docs/write-request.example.c
new file mode 100644
index 0000000..3abe82e
--- /dev/null
+++ b/docs/write-request.example.c
@@ -0,0 +1,14 @@
+#include <libsparklepaw/request.h>
+
+void main() {
+ struct sprkl_request* request = sprkl_request_make_fetchservice(
+ SPRKL_SIGALGO_ED25519,
+ "aaaabbbbccccddddaaaabbbbccccddddaaaabbbbccccddddaaaabbbbccccdddd",
+ 0
+ );
+
+ int n = sprkl_request_write(request, stdout);
+ fprintf(stderr, "\nWrote %d bytes\n", n);
+
+ sprkl_request_freeall(request);
+}
diff --git a/libsparklepaw/common.h b/libsparklepaw/common.h
new file mode 100644
index 0000000..511e6de
--- /dev/null
+++ b/libsparklepaw/common.h
@@ -0,0 +1,13 @@
+/** \file
+ * Common data types. */
+
+#ifndef SPRKL_COMMON_H
+#define SPRKL_COMMON_H
+
+/** Available signature algorithms.
+ * At present the only one is Ed25519, but there may be more in future. */
+enum sprkl_sigalgo {
+ SPRKL_SIGALGO_ED25519 = 25519 /**< Ed25519. */
+};
+
+#endif
diff --git a/libsparklepaw/request.c b/libsparklepaw/request.c
new file mode 100644
index 0000000..440cdd9
--- /dev/null
+++ b/libsparklepaw/request.c
@@ -0,0 +1,110 @@
+#include "endian.h"
+#include "request.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int sprkl_request_write(struct sprkl_request* request, FILE* stream) {
+ int n = 0;
+ n += fwrite(SPRKL_REQUEST_MAGIC, 1, SPRKL_REQUEST_MAGIC_SZ, stream);
+ n += sprkl_writeuint16be(SPRKL_PROTO_VERSION, stream);
+ n += sprkl_writeuint16be(request->verb, stream);
+ switch (request->verb) {
+ case SPRKL_VERB_ECHO:
+ n += fwrite(
+ request->echo.data, 1,
+ sizeof request->echo.data, stream
+ );
+ break;
+ case SPRKL_VERB_FETCHSERVICE:
+ struct sprkl_request_fetchservice* inner = &request->fetchsvc;
+ n += sprkl_writeuint16be(inner->keyalgo, stream);
+ n += sprkl_writeuint16be(inner->keysz, stream);
+ n += fwrite(inner->key, 1, inner->keysz, stream);
+ n += sprkl_writeuint16be(inner->index, stream);
+ break;
+ }
+ return n;
+}
+
+int sprkl_request_read(struct sprkl_request* request, FILE* stream) {
+ int n = 0;
+ uint8_t buf[SPRKL_REQUEST_MAGIC_SZ];
+ n += fread(buf, 1, 8, stream);
+ if (memcmp(buf, SPRKL_REQUEST_MAGIC, sizeof buf) != 0) {
+ errno = EPROTO;
+ return n;
+ }
+ uint16_t version;
+ n += sprkl_readuint16be(&version, stream);
+ if (version != SPRKL_PROTO_VERSION) {
+ errno = EPROTO;
+ return n;
+ }
+ n += sprkl_readuint16be((uint16_t*) &request->verb, stream);
+ if (
+ request->verb != SPRKL_VERB_ECHO
+ && request->verb != SPRKL_VERB_FETCHSERVICE
+ ) {
+ errno = ENOTSUP;
+ return n;
+ }
+ switch (request->verb) {
+ case SPRKL_VERB_ECHO:
+ n += fread(
+ &request->echo.data, 1,
+ sizeof request->echo.data, stream
+ );
+ break;
+ case SPRKL_VERB_FETCHSERVICE:
+ struct sprkl_request_fetchservice* inner = &request->fetchsvc;
+ n += sprkl_readuint16be((uint16_t*) &inner->keyalgo, stream);
+ n += sprkl_readuint16be(&inner->keysz, stream);
+ inner->key = malloc(inner->keysz);
+ n += fread(inner->key, 1, inner->keysz, stream);
+ n += sprkl_readuint16be(&inner->index, stream);
+ break;
+ }
+ return n;
+}
+
+void sprkl_request_freeparts(struct sprkl_request* request) {
+ switch (request->verb) {
+ case SPRKL_VERB_ECHO:
+ // do nothing
+ break;
+ case SPRKL_VERB_FETCHSERVICE:
+ free(request->fetchsvc.key);
+ break;
+ }
+}
+
+void sprkl_request_freeall(struct sprkl_request* request) {
+ sprkl_request_freeparts(request);
+ free(request);
+}
+
+struct sprkl_request* sprkl_request_make_fetchservice(
+ enum sprkl_sigalgo keyalgo,
+ uint8_t* key,
+ uint16_t serviceidx
+ ) {
+ // FIXME: check this properly instead of using assert i think
+ assert(keyalgo == SPRKL_SIGALGO_ED25519 && "i only support Ed25519");
+ uint16_t keysz = 0x40; // based on the above assertion
+ struct sprkl_request* req = malloc(sizeof(struct sprkl_request));
+ uint8_t* copiedkey = malloc(keysz);
+ memcpy(copiedkey, key, keysz);
+ req->verb = SPRKL_VERB_FETCHSERVICE;
+ struct sprkl_request_fetchservice fetchsvc = {
+ .keyalgo = keyalgo,
+ .keysz = keysz,
+ .key = copiedkey,
+ .index = serviceidx
+ };
+ req->fetchsvc = fetchsvc;
+ return req;
+}
diff --git a/libsparklepaw/request.h b/libsparklepaw/request.h
new file mode 100644
index 0000000..bf568f6
--- /dev/null
+++ b/libsparklepaw/request.h
@@ -0,0 +1,87 @@
+/** \file
+ * Requests and responses. */
+
+#ifndef SPRKL_REQUEST_H
+#define SPRKL_REQUEST_H
+
+#include "common.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+/** Magic bytes for requests. */
+#define SPRKL_REQUEST_MAGIC "PawRqust"
+#define SPRKL_REQUEST_MAGIC_SZ ((sizeof SPRKL_REQUEST_MAGIC) - 1)
+/** Currently supported protocol version. */
+#define SPRKL_PROTO_VERSION 1
+
+/** Request verb, indicates what action the request is performing. */
+enum sprkl_request_verb {
+ SPRKL_VERB_ECHO = 0, /**< Return same data back in the response. */
+ SPRKL_VERB_FETCHSERVICE = 1, /**< Fetch a service by key and index. */
+ //SPRKL_VERB_QUERYSERVICES = 2
+};
+
+/** Request payload for \ref SPRKL_VERB_ECHO. */
+struct sprkl_request_echo {
+ uint8_t data[16]; /**< Data to be returned by the remote party. */
+};
+
+/** Request payload for \ref SPRKL_VERB_FETCHSERVICE. */
+struct sprkl_request_fetchservice {
+ enum sprkl_sigalgo keyalgo; /**< The zone's signature algorithm. */
+ uint16_t keysz; /**< Length (in bytes) of the public key. */
+ uint8_t* key; /**< The zone's public key (no terminator). */
+ uint16_t index; /**< Index of the service within the zone. */
+};
+
+/** Request tagged union. */
+struct sprkl_request {
+ enum sprkl_request_verb verb; /**< Union tag, request verb. */
+ union {
+ struct sprkl_request_echo echo;
+ struct sprkl_request_fetchservice fetchsvc;
+ //struct sprkl_request_queryservices querysvcs;
+ };
+};
+
+/** Encodes and writes a request in wire format to a stream.
+ * \param[in] request Request to write.
+ * \param[out] stream Stream to write the request to.
+ * \returns number of bytes written. */
+int sprkl_request_write(struct sprkl_request* request, FILE* stream);
+
+/** Reads and decodes a request in wire format from a stream. This could fail if
+ * the protocol magic constant is wrong or the version is unsupported, in which
+ * case it will set \c errno to \c EPROTO, or if the verb is unsupported, in
+ * which case it will set \c errno to \c ENOTSUP.
+ * When reading a fetch service request, it will allocate a buffer for the key.
+ * \param[out] request Empty request to read into.
+ * \param[in] stream Stream to read it from.
+ * \returns number of bytes read. */
+int sprkl_request_read(struct sprkl_request* request, FILE* stream);
+
+/** Frees any pointers contained within the request, but not the request itself.
+ * What exactly gets freed varies depending on the request verb.
+ * \param request Request to free. */
+void sprkl_request_freeparts(struct sprkl_request* request);
+
+/** Frees any pointers contained within the request, and then the request
+ * itself. Same as sprkl_request_freeparts() followed by free(request).
+ * \param request Request to free. */
+void sprkl_request_freeall(struct sprkl_request* request);
+
+/** Allocates and returns a new request to fetch a service given its key and
+ * index. Copies the key, so both the request and the key need to be freed.
+ * \param keyalgo Algorithm used for the zone's public key.
+ * \param key The zone's public key (will be copied). Does not require a length
+ * or terminator as the length is determined based on the algorithm.
+ * \param serviceidx The index of the desired service within the zone.
+ * \returns a pointer to the new request struct. */
+struct sprkl_request* sprkl_request_make_fetchservice(
+ enum sprkl_sigalgo keyalgo,
+ uint8_t* key,
+ uint16_t serviceidx
+ );
+
+#endif