|
| 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 | +///@} |
0 commit comments