Skip to content

Commit c7be14f

Browse files
committed
lws_dht
Add support for generic DHT to lws based on Juliusz Chroboczek's dht.c This is already MIT same as lws; there was a comment not to modify the original code style to facilitate maintenance but since it's from 14 years ago and MIT grants me the right, I cleaned it (removing forward references) and integrated it properly into lws. Co-developed-by: Gemini Pro 3.0
1 parent 52ecc2d commit c7be14f

File tree

6 files changed

+4097
-0
lines changed

6 files changed

+4097
-0
lines changed

.sai.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@
100100
"default-noudp": {
101101
"cmake": "-DLWS_WITH_UDP=0"
102102
},
103+
"default-dht": {
104+
"cmake": "-DLWS_WITH_DHT=1"
105+
"platforms": "w11/x86_64-amd/msvc,netbsd/aarch64BE-bcm2837-a53/gcc,freebsd/aarch64/llvm"
106+
},
103107
"fault-injection": {
104108
"cmake": "-DLWS_WITH_SYS_FAULT_INJECTION=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_CBOR=1"
105109
},

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ else()
362362
endif()
363363
option(LWS_WITH_MCUFONT_ENCODER "Build the ttf to mcufont encoder" OFF)
364364
option(LWS_WITH_WAKE_LOGGING "Log each wake reason" OFF)
365+
option(LWS_WITH_DHT "Include DHT APIs" OFF)
365366

366367
#
367368
# Compressed backtraces

include/libwebsockets.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ lws_fx_string(const lws_fx_t *a, char *buf, size_t size);
857857
#include <libwebsockets/lws-settings.h>
858858
#if defined(LWS_WITH_NETWORK)
859859
#include <libwebsockets/lws-netdev.h>
860+
#include "libwebsockets/lws-dht.h"
860861
#endif
861862

862863
#include <libwebsockets/lws-html.h>

include/libwebsockets/lws-dht.h

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/*
2+
* Copyright (c) 2009-2011 by Juliusz Chroboczek
3+
* Copyright (c) 2026 Andy Green <andy@warmcat.com>
4+
* Adaptation for lws, cleaning, modernization
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
#if !defined(__LWS_DHT_H__)
26+
#define __LWS_DHT_H__
27+
28+
#include <stddef.h>
29+
#include <stdint.h>
30+
#include <stdio.h>
31+
32+
/*! \defgroup dht Distributed Hash Table
33+
* ## Distributed Hash Table (DHT) API
34+
*
35+
* Lws provides a Mainline DHT implementation that can be used to track
36+
* external IP addresses and find nodes/peers in a P2P network.
37+
*/
38+
///@{
39+
40+
struct lws_dht_ctx;
41+
42+
extern const struct lws_protocols lws_dht_protocol;
43+
44+
/**
45+
* struct lws_dht_hash - DHT hash/ID structure
46+
*
47+
* \param type: LWS_DHT_HASH_TYPE_...
48+
* \param len: length of the ID in bytes
49+
* \param id: the ID bytes
50+
*/
51+
typedef struct lws_dht_hash {
52+
uint8_t type; /* LWS_DHT_HASH_TYPE_... */
53+
uint8_t len;
54+
uint8_t id[];
55+
} lws_dht_hash_t;
56+
57+
enum {
58+
LWS_DHT_HASH_TYPE_UNKNOWN = 0,
59+
LWS_DHT_HASH_TYPE_SHA1 = 0x11, /* 20 bytes */
60+
LWS_DHT_HASH_TYPE_SHA256 = 0x12, /* 32 bytes */
61+
LWS_DHT_HASH_TYPE_SHA512 = 0x13, /* 64 bytes */
62+
LWS_DHT_HASH_TYPE_BLAKE3 = 0x1e, /* 32 bytes */
63+
};
64+
65+
/**
66+
* lws_dht_hash_create() - Create a DHT hash from data
67+
*
68+
* \param type: LWS_DHT_HASH_TYPE_...
69+
* \param len: length of data
70+
* \param data: the data to hash or use as ID
71+
*
72+
* This creates a new lws_dht_hash_t object on the heap.
73+
*
74+
* \return pointer to the new hash, or NULL on failure.
75+
*/
76+
LWS_VISIBLE LWS_EXTERN lws_dht_hash_t *
77+
lws_dht_hash_create(int type, int len, const uint8_t *data);
78+
79+
/**
80+
* lws_dht_hash_destroy() - Destroy a DHT hash
81+
*
82+
* \param p: pointer to the hash pointer to destroy
83+
*
84+
* Frees the hash and sets the pointer to NULL.
85+
*/
86+
LWS_VISIBLE LWS_EXTERN void
87+
lws_dht_hash_destroy(lws_dht_hash_t **p);
88+
89+
/**
90+
* lws_dht_callback_t() - DHT event callback
91+
*
92+
* \param closure: user-defined closure pointer
93+
* \param event: LWS_DHT_EVENT_...
94+
* \param info_hash: the hash related to the event
95+
* \param data: event-specific data
96+
* \param data_len: length of event-specific data
97+
*/
98+
typedef void
99+
lws_dht_callback_t(void *closure, int event, const lws_dht_hash_t *info_hash, const void *data, size_t data_len);
100+
101+
/**
102+
* lws_dht_blacklist_cb_t() - DHT blacklist check callback
103+
*
104+
* \param sa: sockaddr to check
105+
* \param salen: length of sockaddr
106+
*
107+
* \return 0 if the address is allowed, non-zero if it is blacklisted.
108+
*/
109+
typedef int
110+
lws_dht_blacklist_cb_t(const struct sockaddr *sa, size_t salen);
111+
112+
/**
113+
* lws_dht_hash_cb_t() - Custom hash function for DHT
114+
*
115+
* \param hash_return: where to store the hash result
116+
* \param hash_size: size of the hash result buffer
117+
* \param v1: first data chunk
118+
* \param len1: length of first chunk
119+
* \param v2: second data chunk (optional)
120+
* \param len2: length of second chunk
121+
* \param v3: third data chunk (optional)
122+
* \param len3: length of third chunk
123+
*/
124+
typedef void
125+
lws_dht_hash_cb_t(void *hash_return, int hash_size,
126+
const void *v1, int len1,
127+
const void *v2, int len2,
128+
const void *v3, int len3);
129+
130+
/**
131+
* lws_dht_capture_announce_cb_t() - Captured announce callback
132+
*
133+
* \param ctx: DHT context
134+
* \param hash: the announced hash
135+
* \param fromaddr: where the announce came from
136+
* \param prt: the port announced
137+
*/
138+
typedef void
139+
lws_dht_capture_announce_cb_t(struct lws_dht_ctx *ctx, lws_dht_hash_t *hash,
140+
const struct sockaddr *fromaddr,
141+
unsigned short prt);
142+
143+
/* The maximum number of peers we store for a given hash. */
144+
#define DHT_MAX_PEERS 2048
145+
146+
/* The maximum number of hashes we're willing to track. */
147+
#define DHT_MAX_HASHES 16384
148+
149+
/* The maximum number of searches we keep data about. */
150+
#define DHT_MAX_SEARCHES 1024
151+
152+
/* The time after which we consider a search to be expirable. */
153+
#define DHT_SEARCH_EXPIRE_TIME (62 * 60)
154+
155+
/**
156+
* enum lws_dht_event_t - DHT events reported via callback
157+
*/
158+
typedef enum {
159+
LWS_DHT_EVENT_NONE,
160+
LWS_DHT_EVENT_VALUES, /**< Peers for requested hash found */
161+
LWS_DHT_EVENT_VALUES6, /**< IPv6 peers for requested hash found */
162+
LWS_DHT_EVENT_SEARCH_DONE, /**< Search operation completed */
163+
LWS_DHT_EVENT_SEARCH_DONE6, /**< IPv6 search operation completed */
164+
LWS_DHT_EVENT_EXTERNAL_ADDR, /**< External IPv4 address determined */
165+
LWS_DHT_EVENT_EXTERNAL_ADDR6, /**< External IPv6 address determined */
166+
} lws_dht_event_t;
167+
168+
169+
/**
170+
* struct lws_dht_info - Initialization parameters for DHT
171+
*
172+
* \param vhost: vhost to attach UDP wsi to
173+
* \param cb: callback for DHT events
174+
* \param closure: user-defined closure for cb
175+
* \param id: DHT ID (optional, NULL = random)
176+
* \param v: version string (optional, NULL = default)
177+
* \param port: UDP port to listen on
178+
* \param ipv6: enable IPv6
179+
* \param legacy: if set, on wire: no multihash, 20-byte assumed
180+
* \param aux: 0 (sha1), or MULTIHASH_TYPE_...
181+
* \param iface: interface to bind to
182+
* \param blacklist_cb: (optional) user blacklist cb
183+
* \param hash_cb: (optional) user hash cb
184+
* \param capture_announce_cb: (optional) user capture announce cb
185+
*/
186+
typedef struct lws_dht_info {
187+
struct lws_vhost *vhost;
188+
lws_dht_callback_t *cb;
189+
void *closure;
190+
const lws_dht_hash_t *id;
191+
const char *v;
192+
int port;
193+
uint8_t ipv6:1;
194+
uint8_t legacy:1;
195+
uint8_t aux;
196+
const char *iface;
197+
lws_dht_blacklist_cb_t *blacklist_cb;
198+
lws_dht_hash_cb_t *hash_cb;
199+
lws_dht_capture_announce_cb_t *capture_announce_cb;
200+
} lws_dht_info_t;
201+
202+
/**
203+
* lws_dht_create() - Create a DHT context
204+
*
205+
* \param info: initialization parameters
206+
*
207+
* \return pointer to DHT context or NULL on failure.
208+
*/
209+
LWS_VISIBLE LWS_EXTERN struct lws_dht_ctx *
210+
lws_dht_create(const lws_dht_info_t *info);
211+
212+
/**
213+
* lws_dht_destroy() - Destroy a DHT context
214+
*
215+
* \param pctx: pointer to the DHT context pointer to destroy
216+
*/
217+
LWS_VISIBLE LWS_EXTERN void
218+
lws_dht_destroy(struct lws_dht_ctx **pctx);
219+
220+
/**
221+
* lws_dht_insert_node() - Manually insert a node into the DHT
222+
*
223+
* \param ctx: DHT context
224+
* \param id: ID of the node
225+
* \param sa: sockaddr of the node
226+
* \param salen: length of sockaddr
227+
*
228+
* \return 1 if successful, 0 if it already exists, or -1 on error.
229+
*/
230+
LWS_VISIBLE LWS_EXTERN int
231+
lws_dht_insert_node(struct lws_dht_ctx *ctx, const lws_dht_hash_t *id,
232+
struct sockaddr *sa, size_t salen);
233+
234+
/**
235+
* lws_dht_ping_node() - Ping a node to verify it is alive
236+
*
237+
* \param ctx: DHT context
238+
* \param sa: sockaddr of the node
239+
* \param salen: length of sockaddr
240+
*
241+
* \return number of bytes sent on success, or -1 on error.
242+
*/
243+
LWS_VISIBLE LWS_EXTERN int
244+
lws_dht_ping_node(struct lws_dht_ctx *ctx, struct sockaddr *sa, size_t salen);
245+
246+
/**
247+
* lws_dht_search() - Perform an asynchronous search for a hash
248+
*
249+
* \param ctx: DHT context
250+
* \param id: hash to search for
251+
* \param port: port to search on
252+
* \param af: address family (AF_INET, AF_INET6, or 0 for both)
253+
* \param callback: search completion/result callback
254+
* \param closure: closure for \p callback
255+
*
256+
* This performs an iterative, asynchronous search for the requested hash. If
257+
* \p port is non-zero, it also announces our availability for this hash.
258+
*
259+
* Results (peers/values) are delivered to the \p callback as they are found via
260+
* `LWS_DHT_EVENT_VALUES` or `LWS_DHT_EVENT_VALUES6` events.
261+
*
262+
* The \p callback is also called with event `LWS_DHT_EVENT_SEARCH_DONE` (or
263+
* `LWS_DHT_EVENT_SEARCH_DONE6`) when the search operation has exhausted all
264+
* potential nodes or reached a timeout.
265+
*
266+
* \return 1 if search started successfully, 0 if answered locally (callback still called), or -1 on error.
267+
*/
268+
LWS_VISIBLE LWS_EXTERN int
269+
lws_dht_search(struct lws_dht_ctx *ctx, const lws_dht_hash_t *id, int port, int af,
270+
lws_dht_callback_t *callback, void *closure);
271+
272+
/**
273+
* lws_dht_nodes() - Get statistics about DHT nodes
274+
*
275+
* \param ctx: DHT context
276+
* \param af: address family (AF_INET, AF_INET6)
277+
* \param good_return: count of known good nodes
278+
* \param dubious_return: count of dubious nodes
279+
* \param cached_return: count of cached nodes
280+
* \param incoming_return: count of incoming nodes
281+
*
282+
* \return total number of good + dubious nodes.
283+
*/
284+
LWS_VISIBLE LWS_EXTERN int
285+
lws_dht_nodes(struct lws_dht_ctx *ctx, int af, int *good_return, int *dubious_return,
286+
int *cached_return, int *incoming_return);
287+
288+
/**
289+
* lws_dht_dump_tables() - Log the state of DHT routing tables
290+
*
291+
* \param ctx: DHT context
292+
*/
293+
LWS_VISIBLE LWS_EXTERN void
294+
lws_dht_dump_tables(struct lws_dht_ctx *ctx);
295+
296+
/**
297+
* lws_dht_get_nodes() - Get a list of known nodes
298+
*
299+
* \param ctx: DHT context
300+
* \param sin: buffer for IPv4 nodes
301+
* \param num: in/out count for IPv4 nodes
302+
* \param sin6: buffer for IPv6 nodes
303+
* \param num6: in/out count for IPv6 nodes
304+
*
305+
* \return total number of nodes filled (v4 + v6).
306+
*/
307+
LWS_VISIBLE LWS_EXTERN int
308+
lws_dht_get_nodes(struct lws_dht_ctx *ctx, struct sockaddr_in *sin, int *num,
309+
struct sockaddr_in6 *sin6, int *num6);
310+
311+
/**
312+
* lws_dht_get_external_addr() - Get our external IP and port as determined by STUN
313+
*
314+
* \param ctx: DHT context
315+
* \param ss: buffer for external address and port
316+
* \param sslen: length of buffer/written address
317+
*
318+
* \return 0 on success, or -1 if the address and port are not yet determined.
319+
*/
320+
LWS_VISIBLE LWS_EXTERN int
321+
lws_dht_get_external_addr(struct lws_dht_ctx *ctx, struct sockaddr_storage *ss, size_t *sslen);
322+
323+
#endif /* __LWS_DHT_H__ */
324+
325+
///@}

lib/misc/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ if (LWS_WITH_JPEG)
7373
list(APPEND SOURCES
7474
misc/jpeg.c)
7575
endif()
76+
if (LWS_WITH_DHT)
77+
list(APPEND SOURCES
78+
misc/dht.c)
79+
endif()
7680

7781

7882
if (LWS_WITH_DLO)

0 commit comments

Comments
 (0)