ffi-tox 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/ProjectTox-Core/AUTHORS +0 -0
  3. data/ProjectTox-Core/ChangeLog +0 -0
  4. data/ProjectTox-Core/INSTALL +370 -0
  5. data/ProjectTox-Core/INSTALL.md +455 -56
  6. data/ProjectTox-Core/Makefile.am +35 -0
  7. data/ProjectTox-Core/NEWS +0 -0
  8. data/ProjectTox-Core/README +43 -0
  9. data/ProjectTox-Core/README.md +34 -44
  10. data/ProjectTox-Core/auto_tests/Makefile.inc +110 -0
  11. data/ProjectTox-Core/auto_tests/TCP_test.c +519 -0
  12. data/ProjectTox-Core/auto_tests/assoc_test.c +160 -0
  13. data/ProjectTox-Core/auto_tests/crypto_test.c +302 -0
  14. data/ProjectTox-Core/auto_tests/dht_test.c +362 -0
  15. data/ProjectTox-Core/auto_tests/encryptsave_test.c +104 -0
  16. data/ProjectTox-Core/auto_tests/friends_test.c +238 -0
  17. data/ProjectTox-Core/auto_tests/helpers.h +15 -0
  18. data/ProjectTox-Core/auto_tests/messenger_test.c +365 -0
  19. data/ProjectTox-Core/auto_tests/network_test.c +171 -0
  20. data/ProjectTox-Core/auto_tests/onion_test.c +363 -0
  21. data/ProjectTox-Core/auto_tests/skeleton_test.c +49 -0
  22. data/ProjectTox-Core/auto_tests/tox_test.c +454 -0
  23. data/ProjectTox-Core/auto_tests/toxav_basic_test.c +597 -0
  24. data/ProjectTox-Core/auto_tests/toxav_many_test.c +402 -0
  25. data/ProjectTox-Core/autogen.sh +6 -0
  26. data/ProjectTox-Core/build/Makefile.am +14 -0
  27. data/ProjectTox-Core/configure.ac +694 -0
  28. data/ProjectTox-Core/dist-build/android-arm.sh +3 -0
  29. data/ProjectTox-Core/dist-build/android-armv7.sh +3 -0
  30. data/ProjectTox-Core/dist-build/android-build.sh +59 -0
  31. data/ProjectTox-Core/dist-build/android-mips.sh +3 -0
  32. data/ProjectTox-Core/dist-build/android-x86.sh +3 -0
  33. data/ProjectTox-Core/docs/Group-Chats.md +71 -0
  34. data/ProjectTox-Core/docs/Hardening.txt +60 -0
  35. data/ProjectTox-Core/docs/Hardening_docs.txt +30 -0
  36. data/ProjectTox-Core/docs/Prevent_Tracking.txt +160 -0
  37. data/ProjectTox-Core/docs/TCP_Network.txt +154 -0
  38. data/ProjectTox-Core/docs/TODO +62 -0
  39. data/ProjectTox-Core/docs/Tox_middle_level_network_protocol.txt +120 -0
  40. data/ProjectTox-Core/docs/av_api.md +194 -0
  41. data/ProjectTox-Core/libtoxav.pc.in +11 -0
  42. data/ProjectTox-Core/libtoxcore.pc.in +11 -0
  43. data/ProjectTox-Core/m4/ax_have_epoll.m4 +104 -0
  44. data/ProjectTox-Core/m4/ax_pthread.m4 +317 -0
  45. data/ProjectTox-Core/m4/pkg.m4 +199 -0
  46. data/ProjectTox-Core/other/DHT_bootstrap.c +121 -58
  47. data/ProjectTox-Core/other/DHTnodes +3 -0
  48. data/ProjectTox-Core/other/Makefile.inc +20 -0
  49. data/ProjectTox-Core/other/bootstrap_node_packets.c +65 -0
  50. data/ProjectTox-Core/other/tox.png +0 -0
  51. data/ProjectTox-Core/testing/DHT_test.c +170 -98
  52. data/ProjectTox-Core/testing/Makefile.inc +112 -0
  53. data/ProjectTox-Core/testing/Messenger_test.c +133 -69
  54. data/ProjectTox-Core/testing/dns3_test.c +115 -0
  55. data/ProjectTox-Core/testing/misc_tools.c +59 -13
  56. data/ProjectTox-Core/testing/nTox.c +1127 -264
  57. data/ProjectTox-Core/testing/nTox.h +10 -19
  58. data/ProjectTox-Core/testing/tox_shell.c +159 -0
  59. data/ProjectTox-Core/testing/tox_sync.c +299 -0
  60. data/ProjectTox-Core/tools/README +11 -0
  61. data/ProjectTox-Core/tools/astylerc +11 -0
  62. data/ProjectTox-Core/tools/pre-commit +17 -0
  63. data/ProjectTox-Core/toxav/Makefile.inc +36 -0
  64. data/ProjectTox-Core/toxav/codec.c +357 -0
  65. data/ProjectTox-Core/toxav/codec.h +116 -0
  66. data/ProjectTox-Core/toxav/msi.c +1949 -0
  67. data/ProjectTox-Core/toxav/msi.h +267 -0
  68. data/ProjectTox-Core/toxav/rtp.c +600 -0
  69. data/ProjectTox-Core/toxav/rtp.h +196 -0
  70. data/ProjectTox-Core/toxav/toxav.c +1148 -0
  71. data/ProjectTox-Core/toxav/toxav.h +389 -0
  72. data/ProjectTox-Core/toxcore/DHT.c +2521 -0
  73. data/ProjectTox-Core/toxcore/DHT.h +412 -0
  74. data/ProjectTox-Core/toxcore/LAN_discovery.c +322 -0
  75. data/ProjectTox-Core/{core → toxcore}/LAN_discovery.h +17 -12
  76. data/ProjectTox-Core/toxcore/Makefile.inc +67 -0
  77. data/ProjectTox-Core/toxcore/Messenger.c +3006 -0
  78. data/ProjectTox-Core/toxcore/Messenger.h +818 -0
  79. data/ProjectTox-Core/toxcore/TCP_client.c +858 -0
  80. data/ProjectTox-Core/toxcore/TCP_client.h +156 -0
  81. data/ProjectTox-Core/toxcore/TCP_server.c +1332 -0
  82. data/ProjectTox-Core/toxcore/TCP_server.h +181 -0
  83. data/ProjectTox-Core/toxcore/assoc.c +1033 -0
  84. data/ProjectTox-Core/toxcore/assoc.h +104 -0
  85. data/ProjectTox-Core/toxcore/crypto_core.c +278 -0
  86. data/ProjectTox-Core/toxcore/crypto_core.h +151 -0
  87. data/ProjectTox-Core/toxcore/friend_requests.c +175 -0
  88. data/ProjectTox-Core/toxcore/friend_requests.h +83 -0
  89. data/ProjectTox-Core/toxcore/group_chats.c +837 -0
  90. data/ProjectTox-Core/toxcore/group_chats.h +199 -0
  91. data/ProjectTox-Core/toxcore/list.c +256 -0
  92. data/ProjectTox-Core/toxcore/list.h +85 -0
  93. data/ProjectTox-Core/toxcore/logger.c +153 -0
  94. data/ProjectTox-Core/toxcore/logger.h +84 -0
  95. data/ProjectTox-Core/toxcore/misc_tools.h +70 -0
  96. data/ProjectTox-Core/toxcore/net_crypto.c +2753 -0
  97. data/ProjectTox-Core/toxcore/net_crypto.h +410 -0
  98. data/ProjectTox-Core/toxcore/network.c +979 -0
  99. data/ProjectTox-Core/toxcore/network.h +367 -0
  100. data/ProjectTox-Core/toxcore/onion.c +540 -0
  101. data/ProjectTox-Core/toxcore/onion.h +150 -0
  102. data/ProjectTox-Core/toxcore/onion_announce.c +433 -0
  103. data/ProjectTox-Core/toxcore/onion_announce.h +139 -0
  104. data/ProjectTox-Core/toxcore/onion_client.c +1347 -0
  105. data/ProjectTox-Core/toxcore/onion_client.h +253 -0
  106. data/ProjectTox-Core/toxcore/ping.c +346 -0
  107. data/ProjectTox-Core/toxcore/ping.h +47 -0
  108. data/ProjectTox-Core/toxcore/ping_array.c +162 -0
  109. data/ProjectTox-Core/toxcore/ping_array.h +75 -0
  110. data/ProjectTox-Core/toxcore/tox.c +940 -0
  111. data/ProjectTox-Core/toxcore/tox.h +734 -0
  112. data/ProjectTox-Core/toxcore/util.c +193 -0
  113. data/ProjectTox-Core/toxcore/util.h +63 -0
  114. data/ProjectTox-Core/toxdns/Makefile.inc +29 -0
  115. data/ProjectTox-Core/toxdns/toxdns.c +238 -0
  116. data/ProjectTox-Core/toxdns/toxdns.h +88 -0
  117. data/ProjectTox-Core/toxencryptsave/Makefile.inc +45 -0
  118. data/ProjectTox-Core/toxencryptsave/toxencryptsave.c +179 -0
  119. data/ProjectTox-Core/toxencryptsave/toxencryptsave.h +74 -0
  120. data/interfaces/libtox.i +2 -6
  121. data/lib/ffi-tox/libtox.rb +406 -28
  122. metadata +124 -46
  123. data/ProjectTox-Core/CMakeLists.txt +0 -50
  124. data/ProjectTox-Core/cmake/FindLIBCONFIG.cmake +0 -15
  125. data/ProjectTox-Core/cmake/FindNaCl.cmake +0 -17
  126. data/ProjectTox-Core/cmake/FindSODIUM.cmake +0 -15
  127. data/ProjectTox-Core/core/CMakeLists.txt +0 -19
  128. data/ProjectTox-Core/core/DHT.c +0 -1104
  129. data/ProjectTox-Core/core/DHT.h +0 -111
  130. data/ProjectTox-Core/core/LAN_discovery.c +0 -79
  131. data/ProjectTox-Core/core/Lossless_UDP.c +0 -755
  132. data/ProjectTox-Core/core/Lossless_UDP.h +0 -106
  133. data/ProjectTox-Core/core/Messenger.c +0 -596
  134. data/ProjectTox-Core/core/Messenger.h +0 -165
  135. data/ProjectTox-Core/core/friend_requests.c +0 -131
  136. data/ProjectTox-Core/core/friend_requests.h +0 -51
  137. data/ProjectTox-Core/core/net_crypto.c +0 -575
  138. data/ProjectTox-Core/core/net_crypto.h +0 -134
  139. data/ProjectTox-Core/core/network.c +0 -205
  140. data/ProjectTox-Core/core/network.h +0 -134
  141. data/ProjectTox-Core/docs/commands.md +0 -25
  142. data/ProjectTox-Core/docs/start_guide.de.md +0 -40
  143. data/ProjectTox-Core/docs/start_guide.md +0 -38
  144. data/ProjectTox-Core/other/CMakeLists.txt +0 -9
  145. data/ProjectTox-Core/testing/CMakeLists.txt +0 -18
  146. data/ProjectTox-Core/testing/DHT_cryptosendfiletest.c +0 -228
  147. data/ProjectTox-Core/testing/DHT_sendfiletest.c +0 -176
  148. data/ProjectTox-Core/testing/Lossless_UDP_testclient.c +0 -214
  149. data/ProjectTox-Core/testing/Lossless_UDP_testserver.c +0 -201
  150. data/ProjectTox-Core/testing/misc_tools.h +0 -29
  151. data/ProjectTox-Core/testing/nTox_win32.c +0 -387
  152. data/ProjectTox-Core/testing/nTox_win32.h +0 -40
  153. data/ProjectTox-Core/testing/rect.py +0 -45
@@ -0,0 +1,181 @@
1
+ /*
2
+ * TCP_server.h -- Implementation of the TCP relay server part of Tox.
3
+ *
4
+ * Copyright (C) 2014 Tox project All Rights Reserved.
5
+ *
6
+ * This file is part of Tox.
7
+ *
8
+ * Tox is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * Tox is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with Tox. If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+ #ifndef TCP_SERVER_H
24
+ #define TCP_SERVER_H
25
+
26
+ #include "crypto_core.h"
27
+ #include "onion.h"
28
+ #include "list.h"
29
+
30
+ #ifdef TCP_SERVER_USE_EPOLL
31
+ #include "sys/epoll.h"
32
+ #endif
33
+
34
+ #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MACH__)
35
+ #define MSG_NOSIGNAL 0
36
+ #endif
37
+
38
+ #define MAX_INCOMMING_CONNECTIONS 256
39
+
40
+ #define TCP_MAX_BACKLOG MAX_INCOMMING_CONNECTIONS
41
+
42
+ #define MAX_PACKET_SIZE 2048
43
+
44
+ #define TCP_HANDSHAKE_PLAIN_SIZE (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)
45
+ #define TCP_SERVER_HANDSHAKE_SIZE (crypto_box_NONCEBYTES + TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES)
46
+ #define TCP_CLIENT_HANDSHAKE_SIZE (crypto_box_PUBLICKEYBYTES + TCP_SERVER_HANDSHAKE_SIZE)
47
+ #define TCP_MAX_OOB_DATA_LENGTH 1024
48
+
49
+ #define NUM_RESERVED_PORTS 16
50
+ #define NUM_CLIENT_CONNECTIONS (256 - NUM_RESERVED_PORTS)
51
+
52
+ #define TCP_PACKET_ROUTING_REQUEST 0
53
+ #define TCP_PACKET_ROUTING_RESPONSE 1
54
+ #define TCP_PACKET_CONNECTION_NOTIFICATION 2
55
+ #define TCP_PACKET_DISCONNECT_NOTIFICATION 3
56
+ #define TCP_PACKET_PING 4
57
+ #define TCP_PACKET_PONG 5
58
+ #define TCP_PACKET_OOB_SEND 6
59
+ #define TCP_PACKET_OOB_RECV 7
60
+ #define TCP_PACKET_ONION_REQUEST 8
61
+ #define TCP_PACKET_ONION_RESPONSE 9
62
+
63
+ #define ARRAY_ENTRY_SIZE 6
64
+
65
+ /* frequency to ping connected nodes and timeout in seconds */
66
+ #define TCP_PING_FREQUENCY 30
67
+ #define TCP_PING_TIMEOUT 10
68
+
69
+ #ifdef TCP_SERVER_USE_EPOLL
70
+ #define TCP_SOCKET_LISTENING 0
71
+ #define TCP_SOCKET_INCOMING 1
72
+ #define TCP_SOCKET_UNCONFIRMED 2
73
+ #define TCP_SOCKET_CONFIRMED 3
74
+ #endif
75
+
76
+ enum {
77
+ TCP_STATUS_NO_STATUS,
78
+ TCP_STATUS_CONNECTED,
79
+ TCP_STATUS_UNCONFIRMED,
80
+ TCP_STATUS_CONFIRMED,
81
+ };
82
+
83
+ typedef struct TCP_Priority_List TCP_Priority_List;
84
+
85
+ struct TCP_Priority_List {
86
+ TCP_Priority_List *next;
87
+ uint16_t size, sent;
88
+ uint8_t data[0];
89
+ };
90
+
91
+ typedef struct TCP_Secure_Connection {
92
+ uint8_t status;
93
+ sock_t sock;
94
+ uint8_t public_key[crypto_box_PUBLICKEYBYTES];
95
+ uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* Nonce of received packets. */
96
+ uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* Nonce of sent packets. */
97
+ uint8_t shared_key[crypto_box_BEFORENMBYTES];
98
+ uint16_t next_packet_length;
99
+ struct {
100
+ uint8_t status; /* 0 if not used, 1 if other is offline, 2 if other is online. */
101
+ uint8_t public_key[crypto_box_PUBLICKEYBYTES];
102
+ uint32_t index;
103
+ uint8_t other_id;
104
+ } connections[NUM_CLIENT_CONNECTIONS];
105
+ uint8_t last_packet[2 + MAX_PACKET_SIZE];
106
+ uint16_t last_packet_length;
107
+ uint16_t last_packet_sent;
108
+
109
+ TCP_Priority_List *priority_queue_start, *priority_queue_end;
110
+
111
+ uint64_t identifier;
112
+
113
+ uint64_t last_pinged;
114
+ uint64_t ping_id;
115
+ } TCP_Secure_Connection;
116
+
117
+
118
+ typedef struct {
119
+ Onion *onion;
120
+
121
+ #ifdef TCP_SERVER_USE_EPOLL
122
+ int efd;
123
+ uint64_t last_run_pinged;
124
+ #endif
125
+ sock_t *socks_listening;
126
+ unsigned int num_listening_socks;
127
+
128
+ uint8_t public_key[crypto_box_PUBLICKEYBYTES];
129
+ uint8_t secret_key[crypto_box_SECRETKEYBYTES];
130
+ TCP_Secure_Connection incomming_connection_queue[MAX_INCOMMING_CONNECTIONS];
131
+ uint16_t incomming_connection_queue_index;
132
+ TCP_Secure_Connection unconfirmed_connection_queue[MAX_INCOMMING_CONNECTIONS];
133
+ uint16_t unconfirmed_connection_queue_index;
134
+
135
+ TCP_Secure_Connection *accepted_connection_array;
136
+ uint32_t size_accepted_connections;
137
+ uint32_t num_accepted_connections;
138
+
139
+ uint64_t counter;
140
+
141
+ BS_LIST accepted_key_list;
142
+ } TCP_Server;
143
+
144
+ /* Create new TCP server instance.
145
+ */
146
+ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *public_key,
147
+ const uint8_t *secret_key, Onion *onion);
148
+
149
+ /* Run the TCP_server
150
+ */
151
+ void do_TCP_server(TCP_Server *TCP_server);
152
+
153
+ /* Kill the TCP server
154
+ */
155
+ void kill_TCP_server(TCP_Server *TCP_server);
156
+
157
+ /* Read the next two bytes in TCP stream then convert them to
158
+ * length (host byte order).
159
+ *
160
+ * return length on success
161
+ * return 0 if nothing has been read from socket.
162
+ * return ~0 on failure.
163
+ */
164
+ uint16_t read_TCP_length(sock_t sock);
165
+
166
+ /* Read length bytes from socket.
167
+ *
168
+ * return length on success
169
+ * return -1 on failure/no data in buffer.
170
+ */
171
+ int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length);
172
+
173
+ /* return length of received packet on success.
174
+ * return 0 if could not read any packet.
175
+ * return -1 on failure (connection must be killed).
176
+ */
177
+ int read_packet_TCP_secure_connection(sock_t sock, uint16_t *next_packet_length, const uint8_t *shared_key,
178
+ uint8_t *recv_nonce, uint8_t *data, uint16_t max_len);
179
+
180
+
181
+ #endif
@@ -0,0 +1,1033 @@
1
+
2
+ #ifdef HAVE_CONFIG_H
3
+ #include "config.h"
4
+ #endif
5
+
6
+ #include "logger.h"
7
+ #include "DHT.h"
8
+ #include "assoc.h"
9
+ #include "ping.h"
10
+
11
+ #include "LAN_discovery.h"
12
+
13
+ #include <assert.h>
14
+ #include "util.h"
15
+
16
+ /*
17
+ * BASIC OVERVIEW:
18
+ *
19
+ * Hash: The client_id is hashed with a local hash function.
20
+ * Hashes are used in multiple places for searching.
21
+ * Bucket: The first n bits of the client_id are used to
22
+ * select a bucket. This speeds up sorting, but the more
23
+ * important reason is to enforce a spread in the space of
24
+ * client_ids available.
25
+ *
26
+ *
27
+ * Candidates:
28
+ *
29
+ * Candidates are kept in buckets of hash tables. The hash
30
+ * function is calculated from the client_id. Up to
31
+ * HASH_COLLIDE_COUNT alternative positions are tried if
32
+ * the initial position is already used by a different entry.
33
+ * The collision function is multiplicative, not additive.
34
+ *
35
+ * A new candidate can bump an existing candidate, if it is
36
+ * more "desirable": Seen beats Heard.
37
+ */
38
+
39
+ /* candidates: alternative places for the same hash value */
40
+ #define HASH_COLLIDE_COUNT 5
41
+
42
+ /* bucket size shall be co-prime to this */
43
+ #define HASH_COLLIDE_PRIME 101
44
+
45
+ /* candidates: bump entries: timeout values for seen/heard to be considered of value */
46
+ #define CANDIDATES_SEEN_TIMEOUT 1800
47
+ #define CANDIDATES_HEARD_TIMEOUT 600
48
+
49
+ /* distance/index: index size & access mask */
50
+ #define DISTANCE_INDEX_INDEX_BITS (64 - DISTANCE_INDEX_DISTANCE_BITS)
51
+ #define DISTANCE_INDEX_INDEX_MASK ((1 << DISTANCE_INDEX_INDEX_BITS) - 1)
52
+
53
+ /* types to stay consistent */
54
+ typedef uint16_t bucket_t;
55
+ typedef uint32_t hash_t;
56
+ typedef uint16_t usecnt_t;
57
+
58
+ /* abbreviations ... */
59
+ typedef Assoc_distance_relative_callback dist_rel_cb;
60
+ typedef Assoc_distance_absolute_callback dist_abs_cb;
61
+
62
+ /*
63
+ * Client_data wrapped with additional data
64
+ */
65
+ typedef struct Client_entry {
66
+ hash_t hash;
67
+
68
+ /* shortcuts & rumors: timers and data */
69
+ uint64_t getnodes;
70
+ uint64_t used_at;
71
+
72
+ uint64_t seen_at;
73
+ uint64_t heard_at;
74
+
75
+ uint16_t seen_family;
76
+ uint16_t heard_family;
77
+
78
+ IP_Port assoc_heard4;
79
+ IP_Port assoc_heard6;
80
+
81
+ Client_data client;
82
+ } Client_entry;
83
+
84
+ typedef struct candidates_bucket {
85
+ Client_entry *list; /* hashed list */
86
+ } candidates_bucket;
87
+
88
+ struct Assoc {
89
+ hash_t self_hash; /* hash of self_client_id */
90
+ uint8_t self_client_id[CLIENT_ID_SIZE]; /* don't store entries for this */
91
+
92
+ /* association centralization: clients not in use */
93
+ size_t candidates_bucket_bits;
94
+ size_t candidates_bucket_count;
95
+ size_t candidates_bucket_size;
96
+ candidates_bucket *candidates;
97
+ uint64_t getnodes;
98
+ };
99
+
100
+ /*****************************************************************************/
101
+ /* HELPER FUNCTIONS */
102
+ /*****************************************************************************/
103
+
104
+ /* the complete distance would be CLIENT_ID_SIZE long...
105
+ * returns DISTANCE_INDEX_DISTANCE_BITS valid bits */
106
+ static uint64_t id_distance(const Assoc *assoc, void *callback_data, const uint8_t *id_ref, const uint8_t *id_test)
107
+ {
108
+ /* with BIG_ENDIAN, this would be a one-liner... */
109
+ uint64_t retval = 0;
110
+
111
+ uint8_t pos = 0, bits = DISTANCE_INDEX_DISTANCE_BITS;
112
+
113
+ while (bits > 8) {
114
+ uint8_t distance = abs((int8_t)id_ref[pos] ^ (int8_t)id_test[pos]);
115
+ retval = (retval << 8) | distance;
116
+ bits -= 8;
117
+ pos++;
118
+ }
119
+
120
+ return (retval << bits) | ((id_ref[pos] ^ id_test[pos]) >> (8 - bits));
121
+ }
122
+
123
+ /* qsort() callback for a sorting by id_distance() values */
124
+ static int dist_index_comp(const void *a, const void *b)
125
+ {
126
+ const uint64_t *_a = a;
127
+ const uint64_t *_b = b;
128
+
129
+ if (*_a < *_b)
130
+ return -1;
131
+
132
+ if (*_a > *_b)
133
+ return 1;
134
+
135
+ return 0;
136
+ }
137
+
138
+ /* get actual entry to a distance_index */
139
+ static Client_entry *dist_index_entry(Assoc *assoc, uint64_t dist_ind)
140
+ {
141
+ if ((dist_ind & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
142
+ return NULL;
143
+
144
+ size_t total = assoc->candidates_bucket_count * assoc->candidates_bucket_size;
145
+ uint32_t index = dist_ind & DISTANCE_INDEX_INDEX_MASK;
146
+
147
+ if (index < total) {
148
+ bucket_t b_id = index / assoc->candidates_bucket_size;
149
+ candidates_bucket *cnd_bckt = &assoc->candidates[b_id];
150
+ size_t b_ix = index % assoc->candidates_bucket_size;
151
+ Client_entry *entry = &cnd_bckt->list[b_ix];
152
+
153
+ if (entry->hash)
154
+ return entry;
155
+ }
156
+
157
+ return NULL;
158
+ }
159
+
160
+ /* get actual entry's client_id to a distance_index */
161
+ static uint8_t *dist_index_id(Assoc *assoc, uint64_t dist_ind)
162
+ {
163
+ Client_entry *entry = dist_index_entry(assoc, dist_ind);
164
+
165
+ if (entry)
166
+ return entry->client.client_id;
167
+
168
+ return NULL;
169
+ }
170
+
171
+ /* sorts first .. last, i.e. last is included */
172
+ static void dist_index_bubble(Assoc *assoc, uint64_t *dist_list, size_t first, size_t last, uint8_t *id,
173
+ void *custom_data, Assoc_distance_relative_callback dist_rel_func)
174
+ {
175
+ size_t i, k;
176
+
177
+ for (i = first; i <= last; i++) {
178
+ uint8_t *id1 = dist_index_id(assoc, dist_list[i]);
179
+
180
+ for (k = i + 1; k <= last; k++) {
181
+ uint8_t *id2 = dist_index_id(assoc, dist_list[k]);
182
+
183
+ if (id1 && id2)
184
+ if (dist_rel_func(assoc, custom_data, id, id1, id2) == 2) {
185
+ uint64_t swap = dist_list[i];
186
+ dist_list[i] = dist_list[k];
187
+ dist_list[k] = swap;
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ /* TODO: Check that there isn't a function like this elsewhere hidden.
194
+ * E.g. the one which creates a handshake_id isn't usable for this, it must
195
+ * always map the same ID to the same hash.
196
+ *
197
+ * Result is NOT MAPPED to CANDIDATES_TO_KEEP range, i.e. map before using
198
+ * it for list access. */
199
+ static hash_t id_hash(const Assoc *assoc, const uint8_t *id)
200
+ {
201
+ uint32_t i, res = 0x19a64e82;
202
+
203
+ for (i = 0; i < CLIENT_ID_SIZE; i++)
204
+ res = ((res << 1) ^ id[i]) + (res >> 31);
205
+
206
+ /* can't have zero as hash, a) marks an unused spot,
207
+ * b) collision function is multiplicative */
208
+ if (!(res % assoc->candidates_bucket_size))
209
+ res++;
210
+
211
+ return res;
212
+ }
213
+
214
+ /* up to HASH_COLLIDE_COUNT calls to different spots,
215
+ * result IS mapped to CANDIDATES_TO_KEEP range */
216
+ static hash_t hash_collide(const Assoc *assoc, hash_t hash)
217
+ {
218
+ uint64_t hash64 = hash % assoc->candidates_bucket_size;
219
+ hash64 = (hash64 * HASH_COLLIDE_PRIME) % assoc->candidates_bucket_size;
220
+
221
+ hash_t retval = hash64;
222
+
223
+ /* this should never happen when CANDIDATES_TO_KEEP is prime and hash not a multiple
224
+ * (id_hash() checks for a multiple and returns a different hash in that case)
225
+ *
226
+ * ( 1 .. (prime - 1) is a group over multiplication and every number has its inverse
227
+ * in the group, so no multiplication should ever end on zero as long neither
228
+ * of the two factors was zero-equivalent )
229
+ *
230
+ * BUT: because the usage of the word "never" invokes Murphy's law, catch it */
231
+ if (!retval) {
232
+ #ifdef DEBUG
233
+ fprintf(stderr, "assoc::hash_collide: hash %u, bucket size %u => %u!", hash, (uint)assoc->candidates_bucket_size,
234
+ retval);
235
+ assert(retval != 0);
236
+ #endif
237
+ retval = 1;
238
+ }
239
+
240
+ return retval;
241
+ }
242
+
243
+ /* returns the "seen" assoc related to the ipp */
244
+ static IPPTsPng *entry_assoc(Client_entry *cl_entry, const IP_Port *ipp)
245
+ {
246
+ if (!cl_entry)
247
+ return NULL;
248
+
249
+ if (ipp->ip.family == AF_INET)
250
+ return &cl_entry->client.assoc4;
251
+
252
+ if (ipp->ip.family == AF_INET6)
253
+ return &cl_entry->client.assoc6;
254
+
255
+ return NULL;
256
+ }
257
+
258
+ /* returns the "heard" assoc related to the ipp */
259
+ static IP_Port *entry_heard_get(Client_entry *entry, const IP_Port *ipp)
260
+ {
261
+ if (ipp->ip.family == AF_INET)
262
+ return &entry->assoc_heard4;
263
+ else if (ipp->ip.family == AF_INET6)
264
+ return &entry->assoc_heard6;
265
+ else
266
+ return NULL;
267
+ }
268
+
269
+ /* store a "heard" entry
270
+ * overwrites empty entry, does NOT overwrite non-LAN ip with
271
+ * LAN ip
272
+ *
273
+ * returns 1 if the entry did change */
274
+ static int entry_heard_store(Client_entry *entry, const IPPTs *ippts)
275
+ {
276
+ if (!entry || !ippts)
277
+ return 0;
278
+
279
+ if (!ipport_isset(&ippts->ip_port))
280
+ return 0;
281
+
282
+ IP_Port *heard;
283
+ const IP_Port *ipp = &ippts->ip_port;
284
+
285
+ if (ipp->ip.family == AF_INET)
286
+ heard = &entry->assoc_heard4;
287
+ else if (ipp->ip.family == AF_INET6)
288
+ heard = &entry->assoc_heard6;
289
+ else
290
+ return 0;
291
+
292
+ if (ipport_equal(ipp, heard))
293
+ return 0;
294
+
295
+ if (!ipport_isset(heard)) {
296
+ *heard = *ipp;
297
+ entry->heard_at = ippts->timestamp;
298
+ entry->heard_family = ipp->ip.family;
299
+ return 1;
300
+ }
301
+
302
+ /* don't destroy a good address with a crappy one
303
+ * (unless we're very timed out) */
304
+ uint8_t LAN_ipp = LAN_ip(ipp->ip) == 0;
305
+ uint8_t LAN_entry = LAN_ip(heard->ip) == 0;
306
+
307
+ if (LAN_ipp && !LAN_entry && !is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT))
308
+ return 0;
309
+
310
+ *heard = *ipp;
311
+ entry->heard_at = ippts->timestamp;
312
+ entry->heard_family = ipp->ip.family;
313
+
314
+ return 1;
315
+ }
316
+
317
+ /* maps Assoc callback signature to id_closest() */
318
+ static int assoc_id_closest(const Assoc *assoc, void *callback_data, const uint8_t *client_id,
319
+ const uint8_t *client_id1, const uint8_t *client_id2)
320
+ {
321
+ return id_closest(client_id, client_id1, client_id2);
322
+ }
323
+
324
+ static bucket_t id_bucket(const uint8_t *id, uint8_t bits)
325
+ {
326
+ /* return the first "bits" bits of id */
327
+ bucket_t retval = 0;
328
+
329
+ uint8_t pos = 0;
330
+
331
+ while (bits > 8) {
332
+ retval = (retval << 8) | id[pos++];
333
+ bits -= 8;
334
+ }
335
+
336
+ return (retval << bits) | (id[pos] >> (8 - bits));
337
+ }
338
+
339
+ /*****************************************************************************/
340
+ /* CANDIDATES FUNCTIONS */
341
+ /*****************************************************************************/
342
+
343
+
344
+ static bucket_t candidates_id_bucket(const Assoc *assoc, const uint8_t *id)
345
+ {
346
+ return id_bucket(id, assoc->candidates_bucket_bits);
347
+ }
348
+
349
+ static uint8_t candidates_search(const Assoc *assoc, const uint8_t *id, hash_t hash, Client_entry **entryptr)
350
+ {
351
+ bucket_t bucket = candidates_id_bucket(assoc, id);
352
+ candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
353
+ size_t coll, pos = hash % assoc->candidates_bucket_size;
354
+
355
+ for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) {
356
+ Client_entry *entry = &cnd_bckt->list[pos];
357
+
358
+ if (entry->hash == hash)
359
+ if (id_equal(entry->client.client_id, id)) {
360
+ *entryptr = entry;
361
+ return 1;
362
+ }
363
+ }
364
+
365
+ *entryptr = NULL;
366
+ return 0;
367
+ }
368
+
369
+ static void candidates_update_assoc(const Assoc *assoc, Client_entry *entry, uint8_t used, const IPPTs *ippts_send,
370
+ const IP_Port *ipp_recv)
371
+ {
372
+ if (!assoc || !entry || !ippts_send)
373
+ return;
374
+
375
+ IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port);
376
+
377
+ if (!ipptsp)
378
+ return;
379
+
380
+ if (used)
381
+ entry->used_at = unix_time();
382
+
383
+ /* do NOT do anything related to wanted, that's handled outside,
384
+ * just update the assoc (in the most sensible way)
385
+ */
386
+ if (ipp_recv) {
387
+ ipptsp->ip_port = ippts_send->ip_port;
388
+ ipptsp->timestamp = ippts_send->timestamp;
389
+ ipptsp->ret_ip_port = *ipp_recv;
390
+ ipptsp->ret_timestamp = unix_time();
391
+
392
+ entry->seen_at = unix_time();
393
+ entry->seen_family = ippts_send->ip_port.ip.family;
394
+
395
+ return;
396
+ }
397
+
398
+ entry_heard_store(entry, ippts_send);
399
+ }
400
+
401
+ static uint8_t candidates_create_internal(const Assoc *assoc, hash_t const hash, const uint8_t *id, uint8_t seen,
402
+ uint8_t used, bucket_t *bucketptr, size_t *posptr)
403
+ {
404
+ if (!assoc || !id || !bucketptr || !posptr)
405
+ return 0;
406
+
407
+ bucket_t bucket = candidates_id_bucket(assoc, id);
408
+ candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
409
+
410
+ size_t coll, pos = hash % assoc->candidates_bucket_size, check;
411
+ size_t pos_check[6];
412
+
413
+ memset(pos_check, 0, sizeof(pos_check));
414
+
415
+ for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) {
416
+ Client_entry *entry = &cnd_bckt->list[pos];
417
+
418
+ /* unset */
419
+ if (!entry->hash) {
420
+ *bucketptr = bucket;
421
+ *posptr = pos;
422
+
423
+ return 1;
424
+ }
425
+
426
+ /* 0. bad
427
+ * 1. seen bad, heard good
428
+ * 2. seen good
429
+ * 3. used */
430
+ // enumerated lists are superior to magic numbers
431
+ if (!is_timeout(entry->used_at, BAD_NODE_TIMEOUT))
432
+ check = USED;
433
+ else if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT))
434
+ check = SEENG;
435
+ else if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT))
436
+ check = SEENB_HEARDG;
437
+ else
438
+ check = BAD;
439
+
440
+ if (!pos_check[check])
441
+ pos_check[check] = pos + 1;
442
+ }
443
+
444
+ /* used > seen > heard > bad */
445
+ size_t i, pos_max = used ? USED : (seen ? SEENG : SEENB_HEARDG);
446
+
447
+ for (i = 0; i < pos_max; i++)
448
+ if (pos_check[i]) {
449
+ *bucketptr = bucket;
450
+ *posptr = pos_check[i] - 1;
451
+
452
+ return 1;
453
+ }
454
+
455
+ return 0;
456
+ }
457
+
458
+ static uint8_t candidates_create_new(const Assoc *assoc, hash_t hash, const uint8_t *id, uint8_t used,
459
+ const IPPTs *ippts_send, const IP_Port *ipp_recv)
460
+ {
461
+ if (!assoc || !id || !ippts_send)
462
+ return 0;
463
+
464
+ bucket_t bucket;
465
+ size_t pos;
466
+
467
+ if (!candidates_create_internal(assoc, hash, id, ipp_recv != NULL, used, &bucket, &pos))
468
+ return 0;
469
+
470
+ candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
471
+ Client_entry *entry = &cnd_bckt->list[pos];
472
+ memset(entry, 0, sizeof(*entry));
473
+ IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port);
474
+
475
+ if (!ipptsp)
476
+ return 0;
477
+
478
+ entry->hash = hash;
479
+ id_copy(entry->client.client_id, id);
480
+
481
+ if (used)
482
+ entry->used_at = unix_time();
483
+
484
+ if (ipp_recv && !ipport_isset(ipp_recv))
485
+ ipp_recv = NULL;
486
+
487
+ if (ipp_recv) {
488
+ entry->seen_at = ippts_send->timestamp;
489
+ entry->seen_family = ippts_send->ip_port.ip.family;
490
+
491
+ ipptsp->ip_port = ippts_send->ip_port;
492
+ ipptsp->timestamp = ippts_send->timestamp;
493
+ ipptsp->ret_ip_port = *ipp_recv;
494
+ ipptsp->ret_timestamp = unix_time();
495
+ } else {
496
+ IP_Port *heard = entry_heard_get(entry, &ippts_send->ip_port);
497
+
498
+ if (heard) {
499
+ entry->heard_at = ippts_send->timestamp;
500
+ entry->heard_family = ippts_send->ip_port.ip.family;
501
+
502
+ *heard = ippts_send->ip_port;
503
+ }
504
+ }
505
+
506
+ return 1;
507
+ }
508
+
509
+ /*****************************************************************************/
510
+
511
+ static void client_id_self_update(Assoc *assoc)
512
+ {
513
+ if (assoc->self_hash || !assoc->self_client_id)
514
+ return;
515
+
516
+ if (!assoc->self_hash) {
517
+ size_t i, sum = 0;
518
+
519
+ for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++)
520
+ sum |= assoc->self_client_id[i];
521
+
522
+ if (!sum)
523
+ return;
524
+
525
+ assoc->self_hash = id_hash(assoc, assoc->self_client_id);
526
+ }
527
+
528
+ LOGGER_DEBUG("id is now set, purging cache of self-references");
529
+
530
+ /* if we already added some (or loaded some) entries,
531
+ * look and remove if we find a match
532
+ */
533
+ bucket_t b_id = candidates_id_bucket(assoc, assoc->self_client_id);
534
+ candidates_bucket *cnd_bckt = &assoc->candidates[b_id];
535
+ size_t i, pos = assoc->self_hash % assoc->candidates_bucket_size;
536
+
537
+ for (i = 0; i < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos), i++) {
538
+ Client_entry *entry = &cnd_bckt->list[pos];
539
+
540
+ if (entry->hash == assoc->self_hash)
541
+ if (id_equal(entry->client.client_id, assoc->self_client_id))
542
+ entry->hash = 0;
543
+ }
544
+ }
545
+
546
+ /*****************************************************************************/
547
+ /* TRIGGER FUNCTIONS */
548
+ /*****************************************************************************/
549
+
550
+ /* Central entry point for new associations: add a new candidate to the cache
551
+ * seen should be 0 (zero), if the candidate was announced by someone else,
552
+ * seen should be 1 (one), if there is confirmed connectivity (a definite response)
553
+ */
554
+ uint8_t Assoc_add_entry(Assoc *assoc, const uint8_t *id, const IPPTs *ippts_send, const IP_Port *ipp_recv, uint8_t used)
555
+ {
556
+ if (!assoc || !id || !ippts_send)
557
+ return 0;
558
+
559
+ if (!assoc->self_hash) {
560
+ client_id_self_update(assoc);
561
+
562
+ if (!assoc->self_hash)
563
+ return 0;
564
+ }
565
+
566
+ if (!ipport_isset(&ippts_send->ip_port))
567
+ return 0;
568
+
569
+ if (ipp_recv && !ipport_isset(ipp_recv))
570
+ ipp_recv = NULL;
571
+
572
+ hash_t hash = id_hash(assoc, id);
573
+
574
+ if (hash == assoc->self_hash)
575
+ if (id_equal(id, assoc->self_client_id))
576
+ return 0;
577
+
578
+ /* if it's new:
579
+ * callback, if there's desire, add to clients, else to candidates
580
+ *
581
+ * if it's "old":
582
+ * if it's client: refresh
583
+ * if it's candidate:
584
+ * if !ipp_recv, refresh
585
+ * if ipp_recv: callback, if there's desire, move to candidates
586
+ */
587
+ Client_entry *cnd_entry;
588
+
589
+ if (!candidates_search(assoc, id, hash, &cnd_entry)) {
590
+ if (candidates_create_new(assoc, hash, id, used, ippts_send, ipp_recv))
591
+ return 1;
592
+ else
593
+ return 0;
594
+ } else {
595
+ candidates_update_assoc(assoc, cnd_entry, used, ippts_send, ipp_recv);
596
+ return 2;
597
+ }
598
+ }
599
+
600
+ /*****************************************************************************/
601
+ /* MAIN USE */
602
+ /*****************************************************************************/
603
+
604
+ uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *state)
605
+ {
606
+ if (!assoc || !state || !state->wanted_id || !state->result)
607
+ return 0;
608
+
609
+ if (!assoc->self_hash) {
610
+ client_id_self_update(assoc);
611
+
612
+ if (!assoc->self_hash)
613
+ return 0;
614
+ }
615
+
616
+ if (!state->distance_relative_func)
617
+ state->distance_relative_func = assoc_id_closest;
618
+
619
+ if (!state->distance_absolute_func)
620
+ state->distance_absolute_func = id_distance;
621
+
622
+ size_t dist_list_len = assoc->candidates_bucket_count * assoc->candidates_bucket_size;
623
+ uint64_t dist_list[dist_list_len];
624
+ memset(dist_list, ~0, dist_list_len * sizeof(dist_list[0]));
625
+ bucket_t b;
626
+ size_t i;
627
+
628
+ for (b = 0; b < assoc->candidates_bucket_count; b++) {
629
+ candidates_bucket *cnd_bckt = &assoc->candidates[b];
630
+
631
+ for (i = 0; i < assoc->candidates_bucket_size; i++) {
632
+ Client_entry *entry = &cnd_bckt->list[i];
633
+
634
+ if (entry->hash) {
635
+ if (state->flags & ProtoIPv4) {
636
+ if (!ipport_isset(&entry->client.assoc4.ip_port))
637
+ continue;
638
+
639
+ if (!(state->flags & LANOk))
640
+ if (!LAN_ip(entry->client.assoc4.ip_port.ip))
641
+ continue;
642
+ }
643
+
644
+ if (state->flags & ProtoIPv6) {
645
+ if (!ipport_isset(&entry->client.assoc6.ip_port))
646
+ continue;
647
+
648
+ if (!(state->flags & LANOk))
649
+ if (!LAN_ip(entry->client.assoc6.ip_port.ip))
650
+ continue;
651
+ }
652
+
653
+ uint64_t dist = state->distance_absolute_func(assoc, state->custom_data, state->wanted_id, entry->client.client_id);
654
+ uint32_t index = b * assoc->candidates_bucket_size + i;
655
+ dist_list[index] = (dist << DISTANCE_INDEX_INDEX_BITS) | index;
656
+ }
657
+ }
658
+ }
659
+
660
+ qsort(dist_list, dist_list_len, sizeof(dist_list[0]), dist_index_comp);
661
+
662
+ /* ok, ok, it's not *perfectly* sorted, because we used an absolute distance
663
+ * go over the result and see if we need to "smoothen things out"
664
+ * because those should be only very few and short streaks, the worst regularly
665
+ * used sorting function aka bubble sort is used */
666
+ uint64_t dist_prev = ~0;
667
+ size_t ind_prev = ~0, ind_curr;
668
+ size_t len = 1;
669
+
670
+ for (ind_curr = 0; ind_curr < dist_list_len; ind_curr++) {
671
+ /* sorted increasingly, so an invalid entry marks the end */
672
+ if ((dist_list[ind_curr] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
673
+ break;
674
+
675
+ uint64_t dist_curr = dist_list[ind_curr] >> DISTANCE_INDEX_INDEX_BITS;
676
+
677
+ if (dist_prev == dist_curr)
678
+ len++;
679
+ else {
680
+ if (len > 1)
681
+ dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data,
682
+ state->distance_relative_func);
683
+
684
+ dist_prev = dist_curr;
685
+ ind_prev = ind_curr;
686
+ len = 1;
687
+ }
688
+ }
689
+
690
+ if (len > 1)
691
+ dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data,
692
+ state->distance_relative_func);
693
+
694
+ /* ok, now dist_list is a strictly ascending sorted list of nodes
695
+ * a) extract CLOSE_QUOTA_USED clients, not timed out
696
+ * b) extract (1 - QUOTA) (better!) clients & candidates, not timed out
697
+ * c) save candidates which would be better, if contact can be established */
698
+ size_t client_quota_good = 0, pos = 0;
699
+ size_t client_quota_max = state->count_good;
700
+
701
+ ssize_t taken_last = - 1;
702
+
703
+ for (i = 0; (i < dist_list_len) && (pos < state->count); i++) {
704
+ /* sorted increasingly, so an invalid entry marks the end */
705
+ if ((dist_list[i] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
706
+ break;
707
+
708
+ Client_entry *entry = dist_index_entry(assoc, dist_list[i]);
709
+
710
+ if (entry && entry->hash) {
711
+ if (client_quota_good >= client_quota_max) {
712
+ state->result[pos++] = &entry->client;
713
+ taken_last = i;
714
+ } else {
715
+ if (state->flags & (ProtoIPv4 | ProtoIPv6)) {
716
+ if ((state->flags & ProtoIPv4) && is_timeout(entry->client.assoc4.timestamp, BAD_NODE_TIMEOUT))
717
+ continue;
718
+
719
+ if ((state->flags & ProtoIPv6) && is_timeout(entry->client.assoc6.timestamp, BAD_NODE_TIMEOUT))
720
+ continue;
721
+ } else if (is_timeout(entry->seen_at, BAD_NODE_TIMEOUT))
722
+ continue;
723
+
724
+ state->result[pos++] = &entry->client;
725
+ client_quota_good++;
726
+ taken_last = i;
727
+ }
728
+ }
729
+ }
730
+
731
+ /* if we had not enough valid entries the list might still not be filled.
732
+ *
733
+ * start again from last taken client, but leave out any requirement
734
+ */
735
+ if (pos < state->count) {
736
+ for (i = taken_last + 1; (i < dist_list_len) && (pos < state->count); i++) {
737
+ /* sorted increasingly, so an invalid entry marks the end */
738
+ if ((dist_list[i] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
739
+ break;
740
+
741
+ Client_entry *entry = dist_index_entry(assoc, dist_list[i]);
742
+
743
+ if (entry && entry->hash)
744
+ state->result[pos++] = &entry->client;
745
+ }
746
+ }
747
+
748
+ return pos;
749
+ }
750
+
751
+ /*****************************************************************************/
752
+ /* GLOBAL STRUCTURE FUNCTIONS */
753
+ /*****************************************************************************/
754
+
755
+ static uint8_t odd_min9_is_prime(size_t value)
756
+ {
757
+ size_t i = 3;
758
+
759
+ while (i * i <= value) {
760
+ if (!(value % i))
761
+ return 0;
762
+
763
+ i += 2;
764
+ }
765
+
766
+ return 1;
767
+ }
768
+
769
+ static size_t prime_upto_min9(size_t limit)
770
+ {
771
+ /* even => odd */
772
+ limit = limit - (1 - (limit % 2));
773
+
774
+ while (!odd_min9_is_prime(limit))
775
+ limit -= 2;
776
+
777
+ return limit;
778
+ }
779
+
780
+ /* create */
781
+ Assoc *new_Assoc(size_t bits, size_t entries, const uint8_t *public_id)
782
+ {
783
+ if (!public_id)
784
+ return NULL;
785
+
786
+ Assoc *assoc = calloc(1, sizeof(*assoc));
787
+
788
+ if (!assoc)
789
+ return NULL;
790
+
791
+ /*
792
+ * bits must be in [ 2 .. 15 ]
793
+ * entries must be a prime
794
+ */
795
+ if (bits < 2)
796
+ bits = 2;
797
+ else if (bits > 15)
798
+ bits = 15;
799
+
800
+ assoc->candidates_bucket_bits = bits;
801
+ assoc->candidates_bucket_count = 1U << bits;
802
+
803
+ if (entries < 25) {
804
+ if (entries <= 6)
805
+ entries = 5;
806
+ else {
807
+ entries = entries - (1 - (entries % 2)); /* even => odd */
808
+
809
+ /* 7..23: all odds but 9&15 are prime */
810
+ if (!(entries % 3)) /* 9, 15 */
811
+ entries -= 2; /* 7, 13 */
812
+ }
813
+ } else if (entries > ((1 << 17) - 1)) /* 130k+ */
814
+ entries = (1 << 17) - 1;
815
+ else {
816
+ /* 9+: test and find a prime less or equal */
817
+ size_t entries_test = prime_upto_min9(entries);
818
+
819
+ if (entries_test == HASH_COLLIDE_PRIME) /* disallowed */
820
+ entries_test = prime_upto_min9(entries_test - 1);
821
+
822
+ if (entries_test != entries) {
823
+
824
+ LOGGER_DEBUG("trimmed %i to %i.\n", (int)entries, (int)entries_test);
825
+ entries = (size_t)entries_test;
826
+ }
827
+ }
828
+
829
+ assoc->candidates_bucket_size = entries;
830
+
831
+ /* allocation: preferably few blobs */
832
+ size_t bckt, cix;
833
+ Client_entry *clients = malloc(sizeof(*clients) * assoc->candidates_bucket_count * assoc->candidates_bucket_size);
834
+
835
+ if (!clients) {
836
+ free(assoc);
837
+ return NULL;
838
+ }
839
+
840
+ candidates_bucket *lists = malloc(sizeof(*lists) * assoc->candidates_bucket_count);
841
+
842
+ if (!lists) {
843
+ free(assoc);
844
+ free(clients);
845
+ return NULL;
846
+ }
847
+
848
+ for (bckt = 0; bckt < assoc->candidates_bucket_count; bckt++) {
849
+ candidates_bucket *list = &lists[bckt];
850
+
851
+ list->list = &clients[bckt * assoc->candidates_bucket_size];
852
+
853
+ for (cix = 0; cix < assoc->candidates_bucket_size; cix++)
854
+ list->list[cix].hash = 0;
855
+ }
856
+
857
+ assoc->candidates = lists;
858
+ assoc->getnodes = unix_time();
859
+
860
+ id_copy(assoc->self_client_id, public_id);
861
+ client_id_self_update(assoc);
862
+
863
+ return assoc;
864
+ }
865
+
866
+ Assoc *new_Assoc_default(const uint8_t *public_id)
867
+ {
868
+ /* original 8, 251 averages to ~32k entries... probably the whole DHT :D
869
+ * 320 entries is fine, hopefully */
870
+ return new_Assoc(6, 15, public_id);
871
+ }
872
+
873
+ /* own client_id, assocs for this have to be ignored */
874
+ void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id)
875
+ {
876
+ if (assoc && id) {
877
+ assoc->self_hash = 0;
878
+ id_copy(assoc->self_client_id, id);
879
+ client_id_self_update(assoc);
880
+ }
881
+ }
882
+
883
+ #ifdef LOGGING
884
+ static char *idpart2str(uint8_t *id, size_t len);
885
+ #endif /* LOGGING */
886
+
887
+ /* refresh buckets */
888
+ void do_Assoc(Assoc *assoc, DHT *dht)
889
+ {
890
+ if (is_timeout(assoc->getnodes, ASSOC_BUCKET_REFRESH)) {
891
+ assoc->getnodes = unix_time();
892
+
893
+ size_t candidate = (rand() % assoc->candidates_bucket_count) + assoc->candidates_bucket_count;
894
+
895
+ /* in that bucket or the buckets closest to it:
896
+ * find the best heard candidate
897
+ * find the best seen candidate
898
+ * send getnode() requests to both */
899
+ uint8_t *target_id = NULL;
900
+ Client_entry *heard = NULL, *seen = NULL;
901
+ size_t i, k, m;
902
+
903
+ for (i = 1; i < assoc->candidates_bucket_count; i++) {
904
+ if (i % 2)
905
+ k = - (i >> 1);
906
+ else
907
+ k = i >> 1;
908
+
909
+ size_t bckt = (candidate + k) % assoc->candidates_bucket_count;
910
+
911
+ for (m = 0; m < assoc->candidates_bucket_size; m++)
912
+ if (assoc->candidates[bckt].list[m].hash) {
913
+ Client_entry *entry = &assoc->candidates[bckt].list[m];
914
+
915
+ if (!is_timeout(entry->getnodes, CANDIDATES_SEEN_TIMEOUT))
916
+ continue;
917
+
918
+ if (!target_id)
919
+ target_id = entry->client.client_id;
920
+
921
+ if (entry->seen_at) {
922
+ if (!seen)
923
+ if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT))
924
+ seen = entry;
925
+ }
926
+
927
+ if (entry->heard_at) {
928
+ if (!heard)
929
+ if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT))
930
+ heard = entry;
931
+ }
932
+
933
+ if (seen && heard)
934
+ break;
935
+ }
936
+
937
+ if (seen && heard)
938
+ break;
939
+ }
940
+
941
+ if (seen) {
942
+ IPPTsPng *ippts = seen->seen_family == AF_INET ? &seen->client.assoc4 : &seen->client.assoc6;
943
+
944
+ LOGGER_DEBUG("[%u] => S[%s...] %s:%u", (uint32_t)(candidate % assoc->candidates_bucket_count),
945
+ idpart2str(seen->client.client_id, 8), ip_ntoa(&ippts->ip_port.ip), htons(ippts->ip_port.port));
946
+
947
+ DHT_getnodes(dht, &ippts->ip_port, seen->client.client_id, target_id);
948
+ seen->getnodes = unix_time();
949
+ }
950
+
951
+ if (heard && (heard != seen)) {
952
+ IP_Port *ipp = heard->heard_family == AF_INET ? &heard->assoc_heard4 : &heard->assoc_heard6;
953
+
954
+ LOGGER_DEBUG("[%u] => H[%s...] %s:%u", (uint32_t)(candidate % assoc->candidates_bucket_count),
955
+ idpart2str(heard->client.client_id, 8), ip_ntoa(&ipp->ip), htons(ipp->port));
956
+
957
+ DHT_getnodes(dht, ipp, heard->client.client_id, target_id);
958
+ heard->getnodes = unix_time();
959
+ }
960
+
961
+ LOGGER_SCOPE (
962
+
963
+ if ( !heard && !seen )
964
+ LOGGER_DEBUG("[%u] => no nodes to talk to??", (uint32_t)(candidate % assoc->candidates_bucket_count));
965
+ );
966
+ }
967
+ }
968
+
969
+ /* destroy */
970
+ void kill_Assoc(Assoc *assoc)
971
+ {
972
+ if (assoc) {
973
+ free(assoc->candidates->list);
974
+ free(assoc->candidates);
975
+ free(assoc);
976
+ }
977
+ }
978
+
979
+ #ifdef LOGGING
980
+
981
+ static char buffer[CLIENT_ID_SIZE * 2 + 1];
982
+ static char *idpart2str(uint8_t *id, size_t len)
983
+ {
984
+ if (len > CLIENT_ID_SIZE)
985
+ len = CLIENT_ID_SIZE;
986
+
987
+ size_t i;
988
+
989
+ for (i = 0; i < len; i++)
990
+ sprintf(buffer + i * 2, "%02hhx", id[i]);
991
+
992
+ buffer[len * 2] = 0;
993
+ return buffer;
994
+ }
995
+
996
+ void Assoc_status(const Assoc *assoc)
997
+ {
998
+ if (!assoc) {
999
+ LOGGER_INFO("Assoc status: no assoc");
1000
+ return;
1001
+ }
1002
+
1003
+ LOGGER_INFO("[b:p] hash => [id...] used, seen, heard");
1004
+
1005
+ size_t bid, cid, total = 0;
1006
+
1007
+ for (bid = 0; bid < assoc->candidates_bucket_count; bid++) {
1008
+ candidates_bucket *bucket = &assoc->candidates[bid];
1009
+
1010
+ for (cid = 0; cid < assoc->candidates_bucket_size; cid++) {
1011
+ Client_entry *entry = &bucket->list[cid];
1012
+
1013
+ if (entry->hash) {
1014
+ total++;
1015
+
1016
+ LOGGER_INFO("[%3i:%3i] %08x => [%s...] %i, %i(%c), %i(%c)\n",
1017
+ (int)bid, (int)cid, entry->hash, idpart2str(entry->client.client_id, 8),
1018
+ entry->used_at ? (int)(unix_time() - entry->used_at) : 0,
1019
+ entry->seen_at ? (int)(unix_time() - entry->seen_at) : 0,
1020
+ entry->seen_at ? (entry->seen_family == AF_INET ? '4' : (entry->seen_family == AF_INET6 ? '6' : '?')) : '?',
1021
+ entry->heard_at ? (int)(unix_time() - entry->heard_at) : 0,
1022
+ entry->heard_at ? (entry->heard_family == AF_INET ? '4' : (entry->heard_family == AF_INET6 ? '6' : '?')) : '?');
1023
+ }
1024
+ }
1025
+ }
1026
+
1027
+ if (total) {
1028
+ LOGGER_INFO("Total: %i entries, table usage %i%%.\n", (int)total,
1029
+ (int)(total * 100 / (assoc->candidates_bucket_count * assoc->candidates_bucket_size)));
1030
+ }
1031
+ }
1032
+
1033
+ #endif /* LOGGING */