diff options
Diffstat (limited to 'src/trackerlogic.h')
| -rw-r--r-- | src/trackerlogic.h | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/trackerlogic.h b/src/trackerlogic.h new file mode 100644 index 0000000..7292d36 --- /dev/null +++ b/src/trackerlogic.h | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. */ | ||
| 3 | |||
| 4 | #ifndef OT_TRACKERLOGIC_H__ | ||
| 5 | #define OT_TRACKERLOGIC_H__ | ||
| 6 | |||
| 7 | #include <signal.h> | ||
| 8 | #include <stdatomic.h> | ||
| 9 | #include <stdint.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <sys/time.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | #include <time.h> | ||
| 14 | |||
| 15 | typedef uint8_t ot_hash[20]; | ||
| 16 | typedef time_t ot_time; | ||
| 17 | typedef char ot_ip6[16]; | ||
| 18 | typedef struct { | ||
| 19 | ot_ip6 address; | ||
| 20 | int bits; | ||
| 21 | } ot_net; | ||
| 22 | /* List of peers should fit in a single UDP packet (around 1200 bytes) */ | ||
| 23 | #define OT_MAX_PEERS_UDP6 66 | ||
| 24 | #define OT_MAX_PEERS_UDP4 200 | ||
| 25 | |||
| 26 | #define OT_IP_SIZE6 16 | ||
| 27 | #define OT_IP_SIZE4 4 | ||
| 28 | #define OT_PORT_SIZE 2 | ||
| 29 | #define OT_FLAG_SIZE 1 | ||
| 30 | #define OT_TIME_SIZE 1 | ||
| 31 | |||
| 32 | /* Some tracker behaviour tunable */ | ||
| 33 | #ifndef OT_CLIENT_TIMEOUT | ||
| 34 | #define OT_CLIENT_TIMEOUT 30 | ||
| 35 | #endif | ||
| 36 | #ifndef OT_CLIENT_TIMEOUT_CHECKINTERVAL | ||
| 37 | #define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10 | ||
| 38 | #endif | ||
| 39 | #define OT_CLIENT_TIMEOUT_SEND (60 * 15) | ||
| 40 | #define OT_CLIENT_REQUEST_INTERVAL (60 * 30) | ||
| 41 | #define OT_CLIENT_REQUEST_VARIATION (60 * 6) | ||
| 42 | |||
| 43 | #define OT_TORRENT_TIMEOUT_HOURS 24 | ||
| 44 | #define OT_TORRENT_TIMEOUT (60 * OT_TORRENT_TIMEOUT_HOURS) | ||
| 45 | |||
| 46 | #define OT_CLIENT_REQUEST_INTERVAL_RANDOM \ | ||
| 47 | (OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION / 2 + (int)(nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION)) | ||
| 48 | |||
| 49 | /* If WANT_MODEST_FULLSCRAPES is on, ip addresses may not | ||
| 50 | fullscrape more frequently than this amount in seconds */ | ||
| 51 | #define OT_MODEST_PEER_TIMEOUT (60 * 5) | ||
| 52 | |||
| 53 | /* If peers come back before 10 minutes, don't live sync them */ | ||
| 54 | #define OT_CLIENT_SYNC_RENEW_BOUNDARY 10 | ||
| 55 | |||
| 56 | /* Number of tracker admin ip addresses allowed */ | ||
| 57 | #define OT_ADMINIP_MAX 64 | ||
| 58 | #define OT_MAX_THREADS 64 | ||
| 59 | |||
| 60 | /* Number of minutes after announce before peer is removed */ | ||
| 61 | #define OT_PEER_TIMEOUT 45 | ||
| 62 | |||
| 63 | /* We maintain a list of 1024 pointers to sorted list of ot_torrent structs | ||
| 64 | Sort key is, of course, its hash */ | ||
| 65 | /* Set from the Makefile, 8 to 16. More buckets means shorter vectors and a | ||
| 66 | cheaper insert, which only pays for a tracker carrying millions of | ||
| 67 | torrents; see DESIGN. */ | ||
| 68 | #ifndef OT_BUCKET_COUNT_BITS | ||
| 69 | #define OT_BUCKET_COUNT_BITS 10 | ||
| 70 | #endif | ||
| 71 | |||
| 72 | #define OT_BUCKET_COUNT (1 << OT_BUCKET_COUNT_BITS) | ||
| 73 | #define OT_BUCKET_COUNT_SHIFT (32 - OT_BUCKET_COUNT_BITS) | ||
| 74 | |||
| 75 | /* if _DEBUG_RANDOMTORRENTS is set, this is the amount of torrents to create | ||
| 76 | on startup */ | ||
| 77 | #define RANDOMTORRENTS (1024 * 1024 * 1) | ||
| 78 | |||
| 79 | #if defined(CLOCK_REALTIME_COARSE) | ||
| 80 | #define OT_CLOCK_CHEAP CLOCK_REALTIME_COARSE /* Linux */ | ||
| 81 | #elif defined(CLOCK_REALTIME_FAST) | ||
| 82 | #define OT_CLOCK_CHEAP CLOCK_REALTIME_FAST /* the BSDs */ | ||
| 83 | #else | ||
| 84 | #define OT_CLOCK_CHEAP CLOCK_REALTIME /* macOS and whatever else */ | ||
| 85 | #endif | ||
| 86 | |||
| 87 | static inline ot_time time_now_seconds(void) { | ||
| 88 | struct timespec now; | ||
| 89 | clock_gettime(OT_CLOCK_CHEAP, &now); | ||
| 90 | return (ot_time)now.tv_sec; | ||
| 91 | } | ||
| 92 | |||
| 93 | static inline ot_time time_now_minutes(void) { return time_now_seconds() / 60; } | ||
| 94 | |||
| 95 | /* For the deadlines the tracker sets on itself, which read better as the | ||
| 96 | span they allow than as an addition to the current second */ | ||
| 97 | static inline ot_time time_future_seconds(int span) { return time_now_seconds() + span; } | ||
| 98 | /* Cleared from the interrupt handler and read by every worker */ | ||
| 99 | extern _Atomic int g_opentracker_running; | ||
| 100 | |||
| 101 | /* Sent to a thread parked in a blocking read */ | ||
| 102 | #define OT_SIGNAL_WAKE SIGUSR1 | ||
| 103 | |||
| 104 | |||
| 105 | extern uint32_t g_tracker_id; | ||
| 106 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; | ||
| 107 | |||
| 108 | #define OT_PEER_COMPARE_SIZE6 ((OT_IP_SIZE6) + (OT_PORT_SIZE)) | ||
| 109 | #define OT_PEER_COMPARE_SIZE4 ((OT_IP_SIZE4) + (OT_PORT_SIZE)) | ||
| 110 | #define OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(PEER_SIZE) ((PEER_SIZE) - (OT_TIME_SIZE) - (OT_FLAG_SIZE)) | ||
| 111 | |||
| 112 | #define OT_PEER_SIZE6 ((OT_TIME_SIZE) + (OT_FLAG_SIZE) + (OT_PEER_COMPARE_SIZE6)) | ||
| 113 | #define OT_PEER_SIZE4 ((OT_TIME_SIZE) + (OT_FLAG_SIZE) + (OT_PEER_COMPARE_SIZE4)) | ||
| 114 | |||
| 115 | typedef uint8_t ot_peer; /* Generic pointer to a v6 or v4 peer */ | ||
| 116 | typedef uint8_t ot_peer6[OT_PEER_SIZE6]; | ||
| 117 | typedef uint8_t ot_peer4[OT_PEER_SIZE4]; | ||
| 118 | static const uint8_t PEER_FLAG_SEEDING = 0x80; | ||
| 119 | static const uint8_t PEER_FLAG_COMPLETED = 0x40; | ||
| 120 | static const uint8_t PEER_FLAG_STOPPED = 0x20; | ||
| 121 | static const uint8_t PEER_FLAG_FROM_SYNC = 0x10; | ||
| 122 | static const uint8_t PEER_FLAG_LEECHING = 0x00; | ||
| 123 | |||
| 124 | /* Takes an ot_peer6 and returns the proper pointer to the peer and sets peer_size */ | ||
| 125 | ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size); | ||
| 126 | size_t peer_size_from_peer6(ot_peer6 *peer); | ||
| 127 | |||
| 128 | /* New style */ | ||
| 129 | #define OT_SETIP(peer, ip) memcpy((uint8_t *)(peer), (ip), OT_IP_SIZE6) | ||
| 130 | #define OT_SETPORT(peer, port) memcpy(((uint8_t *)(peer)) + (OT_IP_SIZE6), (port), 2) | ||
| 131 | #define OT_PEERFLAG(peer) (((uint8_t *)(peer))[(OT_IP_SIZE6) + 2]) | ||
| 132 | #define OT_PEERFLAG_D(peer, peersize) (((uint8_t *)(peer))[(peersize) - 2]) | ||
| 133 | #define OT_PEERTIME(peer, peersize) (((uint8_t *)(peer))[(peersize) - 1]) | ||
| 134 | |||
| 135 | #define PEERS_BENCODED6 "6:peers6" | ||
| 136 | #define PEERS_BENCODED4 "5:peers" | ||
| 137 | |||
| 138 | #define OT_HASH_COMPARE_SIZE (sizeof(ot_hash)) | ||
| 139 | |||
| 140 | struct ot_peerlist; | ||
| 141 | typedef struct ot_peerlist ot_peerlist; | ||
| 142 | typedef struct { | ||
| 143 | ot_hash hash; | ||
| 144 | ot_peerlist *peer_list6; | ||
| 145 | ot_peerlist *peer_list4; | ||
| 146 | } ot_torrent; | ||
| 147 | |||
| 148 | #include "ot_vector.h" | ||
| 149 | |||
| 150 | struct ot_peerlist { | ||
| 151 | ot_time base; | ||
| 152 | size_t seed_count; | ||
| 153 | size_t peer_count; | ||
| 154 | size_t down_count; | ||
| 155 | /* normal peers vector or | ||
| 156 | pointer to ot_vector[32] buckets if data != NULL and space == 0 | ||
| 157 | */ | ||
| 158 | ot_vector peers; | ||
| 159 | }; | ||
| 160 | #define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space) | ||
| 161 | |||
| 162 | /* The http replies the tracker counts when it has to refuse a request. */ | ||
| 163 | enum { | ||
| 164 | CODE_HTTPERROR_302, | ||
| 165 | CODE_HTTPERROR_400, | ||
| 166 | CODE_HTTPERROR_400_PARAM, | ||
| 167 | CODE_HTTPERROR_400_COMPACT, | ||
| 168 | CODE_HTTPERROR_402_NOTMODEST, | ||
| 169 | CODE_HTTPERROR_402_PAYMENT_REQUIRED, | ||
| 170 | CODE_HTTPERROR_403_IP, | ||
| 171 | CODE_HTTPERROR_404, | ||
| 172 | CODE_HTTPERROR_500, | ||
| 173 | |||
| 174 | CODE_HTTPERROR_COUNT | ||
| 175 | }; | ||
| 176 | |||
| 177 | /* One statistic. Used when workers report stats to their workstruct */ | ||
| 178 | typedef _Atomic unsigned long long ot_counter; | ||
| 179 | |||
| 180 | /* What each thread counts for itself. The two runs at the end are indexed by | ||
| 181 | the http error code and by the age in minutes of a renewed peer */ | ||
| 182 | typedef enum { | ||
| 183 | OT_COUNTER_TCP_CONNECTIONS, | ||
| 184 | OT_COUNTER_UDP_CONNECTIONS, | ||
| 185 | OT_COUNTER_TCP_ANNOUNCES, | ||
| 186 | OT_COUNTER_UDP_ANNOUNCES, | ||
| 187 | OT_COUNTER_TCP_SCRAPES, | ||
| 188 | OT_COUNTER_UDP_SCRAPES, | ||
| 189 | OT_COUNTER_TCP_CONNECTS, | ||
| 190 | OT_COUNTER_UDP_CONNECTS, | ||
| 191 | OT_COUNTER_UDP_CONNID_MISSMATCH, | ||
| 192 | OT_COUNTER_COMPLETED, | ||
| 193 | OT_COUNTER_FULLSCRAPES, | ||
| 194 | OT_COUNTER_FULLSCRAPE_REQUESTS, | ||
| 195 | OT_COUNTER_FULLSCRAPE_BYTES, | ||
| 196 | OT_COUNTER_SYNCS, | ||
| 197 | OT_COUNTER_STALLS, | ||
| 198 | |||
| 199 | OT_COUNTER_FAILED, /* CODE_HTTPERROR_COUNT of them */ | ||
| 200 | OT_COUNTER_RENEWED = OT_COUNTER_FAILED + CODE_HTTPERROR_COUNT, /* OT_PEER_TIMEOUT of them */ | ||
| 201 | |||
| 202 | OT_COUNTER_COUNT = OT_COUNTER_RENEWED + OT_PEER_TIMEOUT | ||
| 203 | } ot_counter_index; | ||
| 204 | |||
| 205 | struct ot_workstruct { | ||
| 206 | /* Thread specific, static */ | ||
| 207 | |||
| 208 | /* This thread's own statistics, and the link ot_stats keeps them on */ | ||
| 209 | ot_counter counters[OT_COUNTER_COUNT]; | ||
| 210 | struct ot_workstruct *counters_next; | ||
| 211 | |||
| 212 | char *inbuf; | ||
| 213 | #define G_INBUF_SIZE 8192 | ||
| 214 | char *outbuf; | ||
| 215 | #define G_OUTBUF_SIZE 8192 | ||
| 216 | #ifdef _DEBUG_HTTPERROR | ||
| 217 | char *debugbuf; | ||
| 218 | #define G_DEBUGBUF_SIZE 8192 | ||
| 219 | #endif | ||
| 220 | |||
| 221 | /* The peer currently in the working */ | ||
| 222 | ot_peer6 peer; /* Can fit v6 and v4 peers */ | ||
| 223 | |||
| 224 | /* Pointers into the request buffer */ | ||
| 225 | ot_hash *hash; | ||
| 226 | char *peer_id; | ||
| 227 | |||
| 228 | /* HTTP specific, non static */ | ||
| 229 | char *request; | ||
| 230 | ssize_t request_size; | ||
| 231 | ssize_t header_size; | ||
| 232 | char *reply; | ||
| 233 | ssize_t reply_size; | ||
| 234 | |||
| 235 | /* Entropy state for rand48 function so that threads don't need to acquire mutexes for | ||
| 236 | global random() or arc4random() state, which causes heavy load on linuxes */ | ||
| 237 | uint16_t rand48_state[3]; | ||
| 238 | |||
| 239 | int keep_alive; | ||
| 240 | }; | ||
| 241 | |||
| 242 | /* Everything a thread that serves requests needs for itself. Set up and torn | ||
| 243 | down in one place so that a new kind of worker cannot miss a piece. | ||
| 244 | Returns -1 if the buffers could not be had. */ | ||
| 245 | int workstruct_init(struct ot_workstruct *ws); | ||
| 246 | void workstruct_deinit(struct ot_workstruct *ws); | ||
| 247 | |||
| 248 | /* | ||
| 249 | Exported functions | ||
| 250 | */ | ||
| 251 | |||
| 252 | #ifdef WANT_SYNC_LIVE | ||
| 253 | #define WANT_SYNC | ||
| 254 | #endif | ||
| 255 | |||
| 256 | #ifdef WANT_SYNC | ||
| 257 | #define WANT_SYNC_PARAM(param) , param | ||
| 258 | #else | ||
| 259 | #define WANT_SYNC_PARAM(param) | ||
| 260 | #endif | ||
| 261 | |||
| 262 | void trackerlogic_init(void); | ||
| 263 | void trackerlogic_deinit(void); | ||
| 264 | void exerr(const char *message); | ||
| 265 | |||
| 266 | /* add_peer_to_torrent does only release the torrent bucket if from_sync is set, | ||
| 267 | otherwise it is released in return_peers_for_torrent */ | ||
| 268 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount); | ||
| 269 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws); | ||
| 270 | void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char **reply, const char *reply_end); | ||
| 271 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply); | ||
| 272 | void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count); | ||
| 273 | #ifdef _DEBUG_RANDOMTORRENTS | ||
| 274 | void trackerlogic_add_random_torrents(size_t amount); | ||
| 275 | #endif | ||
| 276 | |||
| 277 | /* torrent iterator */ | ||
| 278 | void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data); | ||
| 279 | |||
| 280 | /* Helper, before it moves to its own object */ | ||
| 281 | void free_peerlist(ot_peerlist *peer_list); | ||
| 282 | |||
| 283 | #endif | ||
