From ed0b9a46a6d6ddacc64c9f2c757a37809884d60a Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 22 May 2026 12:38:59 -0400
Subject: [PATCH] api: Add NET_GetAddressBytes().
As requested via tweet. :)
---
include/SDL3_net/SDL_net.h | 43 ++++++++++++++++++++++++++++++++++++++
src/SDL_net.c | 28 +++++++++++++++++++++++++
src/SDL_net.exports | 1 +
src/SDL_net.sym | 1 +
src/SDL_net_stub_only.c | 1 +
5 files changed, 74 insertions(+)
diff --git a/include/SDL3_net/SDL_net.h b/include/SDL3_net/SDL_net.h
index 5927befe..068a34ce 100644
--- a/include/SDL3_net/SDL_net.h
+++ b/include/SDL3_net/SDL_net.h
@@ -415,6 +415,49 @@ extern SDL_DECLSPEC NET_Status SDLCALL NET_GetAddressStatus(NET_Address *address
*/
extern SDL_DECLSPEC const char * SDLCALL NET_GetAddressString(NET_Address *address);
+/**
+ * Get the protocol-level bytes of a network address from a resolved address.
+ *
+ * This data is not human-readable, is protocol-specific, and might not even
+ * be in a specific byte order.
+ *
+ * This is only useful for possibly hashing, to map a address to a specific
+ * player in a game, or possibly for handing to a system-level networking API
+ * (which is _not_ recommended; an app does this at their own risk).
+ *
+ * Do not store these bytes for future runs of the program; there is no
+ * promise the format won't change.
+ *
+ * On return `*num_bytes` will hold the number of bytes provided with the
+ * address. Since the data is not NULL-terminated, this is the only way to
+ * determine its size; as such, this parameter must not be NULL.
+ *
+ * Do not free or modify the returned data; it belongs to the NET_Address
+ * that was queried, and is valid as long as the object lives. Either make
+ * sure the address has a reference as long as you need this or make a copy of
+ * the bytes.
+ *
+ * This will return NULL if resolution is still in progress, or if resolution
+ * failed. You can use NET_GetAddressStatus() or NET_WaitUntilResolved() to
+ * make sure resolution has successfully completed before calling this.
+ *
+ * A human-readable version is available in NET_GetAddressString() and isn't
+ * any less efficient to query than the raw bytes.
+ *
+ * \param address The NET_Address to query.
+ * \param num_bytes on return, will be set to the number of bytes returned.
+ * \returns a pointer to bytes, or NULL on error; call SDL_GetError() for details.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL_net 3.0.0.
+ *
+ * \sa NET_GetAddressString
+ * \sa NET_GetAddressStatus
+ * \sa NET_WaitUntilResolved
+ */
+extern SDL_DECLSPEC const void * SDLCALL NET_GetAddressBytes(NET_Address *address, int *num_bytes);
+
/**
* Add a reference to an NET_Address.
*
diff --git a/src/SDL_net.c b/src/SDL_net.c
index 6bc98628..1b55ff16 100644
--- a/src/SDL_net.c
+++ b/src/SDL_net.c
@@ -1253,6 +1253,34 @@ const char *NET_GetAddressString(NET_Address *addr)
return retval;
}
+const void * NET_GetAddressBytes(NET_Address *addr, int *num_bytes)
+{
+ if (!num_bytes) {
+ SDL_InvalidParamError("num_bytes");
+ return NULL;
+ }
+
+ *num_bytes = 0;
+
+ if (!addr) {
+ SDL_InvalidParamError("address");
+ return NULL;
+ }
+
+ const void *retval = NULL;
+ const NET_Status rc = NET_GetAddressStatus(addr);
+ if (rc == NET_SUCCESS) {
+ const struct addrinfo *ainfo = addr->ainfo;
+ SDL_assert(ainfo != NULL);
+ retval = (const void *) ainfo->ai_addr;
+ *num_bytes = (int) ainfo->ai_addrlen;
+ } else if (rc != NET_FAILURE) { // if NET_FAILURE, it'll set the error message.
+ SDL_SetError("Address not yet resolved");
+ }
+
+ return retval;
+}
+
int NET_CompareAddresses(const NET_Address *sdlneta, const NET_Address *sdlnetb)
{
if (sdlneta == sdlnetb) { // same pointer?
diff --git a/src/SDL_net.exports b/src/SDL_net.exports
index ef3bf338..d1c52d9b 100644
--- a/src/SDL_net.exports
+++ b/src/SDL_net.exports
@@ -32,4 +32,5 @@ _NET_WaitUntilInputAvailable
_NET_WaitUntilResolved
_NET_WaitUntilStreamSocketDrained
_NET_WriteToStreamSocket
+_NET_GetAddressBytes
# extra symbols go here (don't modify this line)
diff --git a/src/SDL_net.sym b/src/SDL_net.sym
index 53c9c9c1..07108897 100644
--- a/src/SDL_net.sym
+++ b/src/SDL_net.sym
@@ -33,6 +33,7 @@ SDL3_net_0.0.0 {
NET_WaitUntilResolved;
NET_WaitUntilStreamSocketDrained;
NET_WriteToStreamSocket;
+ NET_GetAddressBytes;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/SDL_net_stub_only.c b/src/SDL_net_stub_only.c
index c2350221..c2313264 100644
--- a/src/SDL_net_stub_only.c
+++ b/src/SDL_net_stub_only.c
@@ -29,6 +29,7 @@ NET_Address * NET_ResolveHostname(const char *host) { SDL_Unsupported(); return
NET_Status NET_WaitUntilResolved(NET_Address *address, Sint32 timeout) { SDL_Unsupported(); return NET_FAILURE; }
NET_Status NET_GetAddressStatus(NET_Address *address) { SDL_Unsupported(); return NET_FAILURE; }
const char * NET_GetAddressString(NET_Address *address) { SDL_Unsupported(); return NULL; }
+const void * NET_GetAddressBytes(NET_Address *address, int *num_bytes) { if (num_bytes) { *num_bytes = 0; } SDL_Unsupported(); return NULL; }
NET_Address *NET_RefAddress(NET_Address *address) { SDL_Unsupported(); return NULL; }
void NET_UnrefAddress(NET_Address *address) {}
void NET_SimulateAddressResolutionLoss(int percent_loss) {}