File Coverage

_rhash.c
Criterion Covered Total %
statement 234 325 72.0
branch 115 192 59.9
condition n/a
subroutine n/a
pod n/a
total 349 517 67.5


line stmt bran cond sub pod time code
1             /* rhash.c - implementation of LibRHash library calls
2             *
3             * Copyright: 2008-2012 Aleksey Kravchenko
4             *
5             * Permission is hereby granted, free of charge, to any person obtaining a
6             * copy of this software and associated documentation files (the "Software"),
7             * to deal in the Software without restriction, including without limitation
8             * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9             * and/or sell copies of the Software, and to permit persons to whom the
10             * Software is furnished to do so.
11             *
12             * This program is distributed in the hope that it will be useful, but
13             * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14             * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk!
15             */
16              
17             /* macros for large file support, must be defined before any include file */
18             #define _LARGEFILE64_SOURCE
19             #define _FILE_OFFSET_BITS 64
20              
21             #include /* memset() */
22             #include /* free() */
23             #include /* ptrdiff_t */
24             #include
25             #include
26             #include
27              
28             /* modifier for Windows DLL */
29             #if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS)
30             # define RHASH_API __declspec(dllexport)
31             #endif
32              
33             #include "byte_order.h"
34             #include "algorithms.h"
35             #include "torrent.h"
36             #include "plug_openssl.h"
37             #include "util.h"
38             #include "hex.h"
39             #include "rhash.h" /* RHash library interface */
40              
41             #define STATE_ACTIVE 0xb01dbabe
42             #define STATE_STOPED 0xdeadbeef
43             #define STATE_DELETED 0xdecea5ed
44             #define RCTX_AUTO_FINAL 0x1
45             #define RCTX_FINALIZED 0x2
46             #define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED)
47             #define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64)
48             #define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_REVERSE)
49              
50 3           void rhash_library_init(void)
51             {
52 3           rhash_init_algorithms(RHASH_ALL_HASHES);
53             #ifdef USE_OPENSSL
54             rhash_plug_openssl();
55             #endif
56 3           }
57              
58 1           int RHASH_API rhash_count(void)
59             {
60 1           return rhash_info_size;
61             }
62              
63             /* LOW-LEVEL LIBRHASH INTERFACE */
64              
65 6           RHASH_API rhash rhash_init(unsigned hash_id)
66             {
67             unsigned tail_bit_index; /* index of hash_id trailing bit */
68 6           unsigned num = 0; /* number of hashes to compute */
69 6           rhash_context_ext *rctx = NULL; /* allocated rhash context */
70 6           size_t hash_size_sum = 0; /* size of hash contexts to store in rctx */
71              
72             unsigned i, bit_index, id;
73             struct rhash_hash_info* info;
74             size_t aligned_size;
75             char* phash_ctx;
76              
77 6           hash_id &= RHASH_ALL_HASHES;
78 6 50         if (hash_id == 0) {
79 0           errno = EINVAL;
80 0           return NULL;
81             }
82              
83 6           tail_bit_index = rhash_ctz(hash_id); /* get trailing bit index */
84 6 50         assert(tail_bit_index < RHASH_HASH_COUNT);
85              
86 6           id = 1 << tail_bit_index;
87              
88 6 100         if (hash_id == id) {
89             /* handle the most common case of only one hash */
90 2           num = 1;
91 2           info = &rhash_info_table[tail_bit_index];
92 2           hash_size_sum = info->context_size;
93             } else {
94             /* another case: hash_id contains several hashes */
95 64 100         for (bit_index = tail_bit_index; id <= hash_id; bit_index++, id = id << 1) {
96 60 50         assert(id != 0);
97 60 50         assert(bit_index < RHASH_HASH_COUNT);
98 60           info = &rhash_info_table[bit_index];
99 60 100         if (hash_id & id) {
100             /* align sizes by 8 bytes */
101 58           aligned_size = (info->context_size + 7) & ~7;
102 58           hash_size_sum += aligned_size;
103 58           num++;
104             }
105             }
106 4 50         assert(num > 1);
107             }
108              
109             /* align the size of the rhash context common part */
110 6           aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7;
111 6 50         assert(aligned_size >= sizeof(rhash_context_ext));
112              
113             /* allocate rhash context with enough memory to store contexts of all used hashes */
114 6           rctx = (rhash_context_ext*)malloc(aligned_size + hash_size_sum);
115 6 50         if (rctx == NULL) return NULL;
116              
117             /* initialize common fields of the rhash context */
118 6           memset(rctx, 0, sizeof(rhash_context_ext));
119 6           rctx->rc.hash_id = hash_id;
120 6           rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */
121 6           rctx->state = STATE_ACTIVE;
122 6           rctx->hash_vector_size = num;
123              
124             /* aligned hash contexts follows rctx->vector[num] in the same memory block */
125 6           phash_ctx = (char*)rctx + aligned_size;
126 6 50         assert(phash_ctx >= (char*)&rctx->vector[num]);
127              
128             /* initialize context for every hash in a loop */
129 68 100         for (bit_index = tail_bit_index, id = 1 << tail_bit_index, i = 0;
130 62           id <= hash_id; bit_index++, id = id << 1)
131             {
132             /* check if a hash function with given id shall be included into rctx */
133 62 100         if ((hash_id & id) != 0) {
134 60           info = &rhash_info_table[bit_index];
135 60 50         assert(info->context_size > 0);
136 60 50         assert(((phash_ctx - (char*)0) & 7) == 0); /* hash context is aligned */
137 60 50         assert(info->init != NULL);
138              
139 60           rctx->vector[i].hash_info = info;
140 60           rctx->vector[i].context = phash_ctx;
141              
142             /* BTIH initialization is complex, save pointer for later */
143 60 100         if ((id & RHASH_BTIH) != 0) rctx->bt_ctx = phash_ctx;
144 60           phash_ctx += (info->context_size + 7) & ~7;
145              
146             /* initialize the i-th hash context */
147 60           info->init(rctx->vector[i].context);
148 60           i++;
149             }
150             }
151              
152 6           return &rctx->rc; /* return allocated and initialized rhash context */
153             }
154              
155 4           void rhash_free(rhash ctx)
156             {
157 4           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
158             unsigned i;
159              
160 4 50         if (ctx == 0) return;
161 4 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
162 4           ectx->state = STATE_DELETED; /* mark memory block as being removed */
163              
164             /* clean the hash functions, which require additional clean up */
165 35 100         for (i = 0; i < ectx->hash_vector_size; i++) {
166 31           struct rhash_hash_info* info = ectx->vector[i].hash_info;
167 31 100         if (info->cleanup != 0) {
168 2           info->cleanup(ectx->vector[i].context);
169             }
170             }
171              
172 4           free(ectx);
173             }
174              
175 3           RHASH_API void rhash_reset(rhash ctx)
176             {
177 3           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
178             unsigned i;
179              
180 3 50         assert(ectx->hash_vector_size > 0);
181 3 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
182 3           ectx->state = STATE_ACTIVE; /* re-activate the structure */
183              
184             /* re-initialize every hash in a loop */
185 7 100         for (i = 0; i < ectx->hash_vector_size; i++) {
186 4           struct rhash_hash_info* info = ectx->vector[i].hash_info;
187 4 50         if (info->cleanup != 0) {
188 0           info->cleanup(ectx->vector[i].context);
189             }
190              
191 4 50         assert(info->init != NULL);
192 4           info->init(ectx->vector[i].context);
193             }
194 3           ectx->flags &= ~RCTX_FINALIZED; /* clear finalized state */
195 3           }
196              
197 8           RHASH_API int rhash_update(rhash ctx, const void* message, size_t length)
198             {
199 8           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
200             unsigned i;
201              
202 8 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
203 8 50         if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
204              
205 8           ctx->msg_size += length;
206              
207             /* call update method for every algorithm */
208 70 100         for (i = 0; i < ectx->hash_vector_size; i++) {
209 62           struct rhash_hash_info* info = ectx->vector[i].hash_info;
210 62 50         assert(info->update != 0);
211 62           info->update(ectx->vector[i].context, message, length);
212             }
213 8           return 0; /* no error processing at the moment */
214             }
215              
216 9           RHASH_API int rhash_final(rhash ctx, unsigned char* first_result)
217             {
218 9           unsigned i = 0;
219             unsigned char buffer[130];
220 9 100         unsigned char* out = (first_result ? first_result : buffer);
221 9           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
222 9 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
223              
224             /* skip final call if already finalized and auto-final is on */
225 9 50         if ((ectx->flags & RCTX_FINALIZED_MASK) ==
226 0           (RCTX_AUTO_FINAL | RCTX_FINALIZED)) return 0;
227              
228             /* call final method for every algorithm */
229 73 100         for (i = 0; i < ectx->hash_vector_size; i++) {
230 64           struct rhash_hash_info* info = ectx->vector[i].hash_info;
231 64 50         assert(info->final != 0);
232 64 50         assert(info->info->digest_size < sizeof(buffer));
233 64           info->final(ectx->vector[i].context, out);
234 64           out = buffer;
235             }
236 9           ectx->flags |= RCTX_FINALIZED;
237 9           return 0; /* no error processing at the moment */
238             }
239              
240             /**
241             * Store digest for given hash_id.
242             * If hash_id is zero, function stores digest for a hash with the lowest id found in the context.
243             * For nonzero hash_id the context must contain it, otherwise function silently does nothing.
244             *
245             * @param ctx rhash context
246             * @param hash_id id of hash to retrieve or zero for hash with the lowest available id
247             * @param result buffer to put the hash into
248             */
249 45           static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result)
250             {
251 45           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
252             unsigned i;
253             rhash_vector_item *item;
254             struct rhash_hash_info* info;
255             unsigned char* digest;
256              
257 45 50         assert(ectx);
258 45 50         assert(ectx->hash_vector_size > 0 && ectx->hash_vector_size <= RHASH_HASH_COUNT);
    50          
259              
260             /* finalize context if not yet finalized and auto-final is on */
261 45 100         if ((ectx->flags & RCTX_FINALIZED_MASK) == RCTX_AUTO_FINAL) {
262 5           rhash_final(ctx, NULL);
263             }
264              
265 45 50         if (hash_id == 0) {
266 0           item = &ectx->vector[0]; /* get the first hash */
267 0           info = item->hash_info;
268             } else {
269 45           for (i = 0;; i++) {
270 424 50         if (i >= ectx->hash_vector_size) {
271 0           return; /* hash_id not found, do nothing */
272             }
273 424           item = &ectx->vector[i];
274 424           info = item->hash_info;
275 424 100         if (info->info->hash_id == hash_id) break;
276 379           }
277             }
278 45           digest = ((unsigned char*)item->context + info->digest_diff);
279 45 100         if (info->info->flags & F_SWAP32) {
280 9 50         assert((info->info->digest_size & 3) == 0);
281             /* NB: the next call is correct only for multiple of 4 byte size */
282 9           rhash_swap_copy_str_to_u32(result, 0, digest, info->info->digest_size);
283 36 100         } else if (info->info->flags & F_SWAP64) {
284 3           rhash_swap_copy_u64_to_str(result, digest, info->info->digest_size);
285             } else {
286 33           memcpy(result, digest, info->info->digest_size);
287             }
288             }
289              
290 0           RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data)
291             {
292 0           ((rhash_context_ext*)ctx)->callback = (void*)callback;
293 0           ((rhash_context_ext*)ctx)->callback_data = callback_data;
294 0           }
295              
296             /* HIGH-LEVEL LIBRHASH INTERFACE */
297              
298 1           RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result)
299             {
300             rhash ctx;
301 1           hash_id &= RHASH_ALL_HASHES;
302 1           ctx = rhash_init(hash_id);
303 1 50         if (ctx == NULL) return -1;
304 1           rhash_update(ctx, message, length);
305 1           rhash_final(ctx, result);
306 1           rhash_free(ctx);
307 1           return 0;
308             }
309              
310 0           RHASH_API int rhash_file_update(rhash ctx, FILE* fd)
311             {
312 0           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
313 0           const size_t block_size = 8192;
314             unsigned char *buffer, *pmem;
315 0           size_t length = 0, align8;
316 0           int res = 0;
317 0 0         if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
318              
319 0 0         if (ctx == NULL) {
320 0           errno = EINVAL;
321 0           return -1;
322             }
323              
324 0           pmem = (unsigned char*)malloc(block_size + 8);
325 0 0         if (!pmem) return -1; /* errno is set to ENOMEM according to UNIX 98 */
326              
327 0           align8 = ((unsigned char*)0 - pmem) & 7;
328 0           buffer = pmem + align8;
329              
330 0 0         while (!feof(fd)) {
331             /* stop if canceled */
332 0 0         if (ectx->state != STATE_ACTIVE) break;
333              
334 0           length = fread(buffer, 1, block_size, fd);
335              
336 0 0         if (ferror(fd)) {
337 0           res = -1; /* note: errno contains error code */
338 0           break;
339 0 0         } else if (length) {
340 0           rhash_update(ctx, buffer, length);
341              
342 0 0         if (ectx->callback) {
343 0           ((rhash_callback_t)ectx->callback)(ectx->callback_data, ectx->rc.msg_size);
344             }
345             }
346             }
347              
348 0           free(buffer);
349 0           return res;
350             }
351              
352 0           RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result)
353             {
354             FILE* fd;
355             rhash ctx;
356             int res;
357              
358 0           hash_id &= RHASH_ALL_HASHES;
359 0 0         if (hash_id == 0) {
360 0           errno = EINVAL;
361 0           return -1;
362             }
363              
364 0 0         if ((fd = fopen(filepath, "rb")) == NULL) return -1;
365              
366 0 0         if ((ctx = rhash_init(hash_id)) == NULL) {
367 0           fclose(fd);
368 0           return -1;
369             }
370              
371 0           res = rhash_file_update(ctx, fd); /* hash the file */
372 0           fclose(fd);
373              
374 0           rhash_final(ctx, result);
375 0           rhash_free(ctx);
376 0           return res;
377             }
378              
379             #ifdef _WIN32 /* windows only function */
380             #include
381              
382             RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result)
383             {
384             FILE* fd;
385             rhash ctx;
386             int res;
387              
388             hash_id &= RHASH_ALL_HASHES;
389             if (hash_id == 0) {
390             errno = EINVAL;
391             return -1;
392             }
393              
394             if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1;
395              
396             if ((ctx = rhash_init(hash_id)) == NULL) {
397             fclose(fd);
398             return -1;
399             }
400              
401             res = rhash_file_update(ctx, fd); /* hash the file */
402             fclose(fd);
403              
404             rhash_final(ctx, result);
405             rhash_free(ctx);
406             return res;
407             }
408             #endif
409              
410             /* RHash information functions */
411              
412 3           RHASH_API int rhash_is_base32(unsigned hash_id)
413             {
414             /* fast method is just to test a bit-mask */
415 3           return ((hash_id & (RHASH_TTH | RHASH_AICH)) != 0);
416             }
417              
418 2           RHASH_API int rhash_get_digest_size(unsigned hash_id)
419             {
420 2           hash_id &= RHASH_ALL_HASHES;
421 2 50         if (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) return -1;
    50          
422 2           return (int)rhash_info_table[rhash_ctz(hash_id)].info->digest_size;
423             }
424              
425 1           RHASH_API int rhash_get_hash_length(unsigned hash_id)
426             {
427 1           const rhash_info* info = rhash_info_by_id(hash_id);
428 1 50         return (int)(info ? (info->flags & F_BS32 ?
    50          
429 1           BASE32_LENGTH(info->digest_size) : info->digest_size * 2) : 0);
430             }
431              
432 1           RHASH_API const char* rhash_get_name(unsigned hash_id)
433             {
434 1           const rhash_info* info = rhash_info_by_id(hash_id);
435 1 50         return (info ? info->name : 0);
436             }
437              
438 14           RHASH_API const char* rhash_get_magnet_name(unsigned hash_id)
439             {
440 14           const rhash_info* info = rhash_info_by_id(hash_id);
441 14 50         return (info ? info->magnet_name : 0);
442             }
443              
444 3           static size_t rhash_get_magnet_url_size(const char* filepath,
445             rhash context, unsigned hash_mask, int flags)
446             {
447 3           size_t size = 0; /* count terminating '\0' */
448 3           unsigned bit, hash = context->hash_id & hash_mask;
449              
450             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
451 3 50         if ((flags & RHPR_NO_MAGNET) == 0) {
452 3           size += 8;
453             }
454              
455 3 50         if ((flags & RHPR_FILESIZE) != 0) {
456 3           uint64_t num = context->msg_size;
457              
458 3           size += 4;
459 3 50         if (num == 0) size++;
460             else {
461 6 100         for (; num; num /= 10, size++);
462             }
463             }
464              
465 3 100         if (filepath) {
466 1           size += 4 + rhash_urlencode(NULL, filepath);
467             }
468              
469             /* loop through hash values */
470 11 100         for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
471             const char* name;
472 8 100         if ((bit & hash) == 0) continue;
473 7 50         if ((name = rhash_get_magnet_name(bit)) == 0) continue;
474              
475 7           size += (7 + 2) + strlen(name);
476 7 100         size += rhash_print(NULL, context, bit,
477 7           (bit & (RHASH_SHA1 | RHASH_BTIH) ? RHPR_BASE32 : 0));
478             }
479              
480 3           return size;
481             }
482              
483 6           RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
484             rhash context, unsigned hash_mask, int flags)
485             {
486             int i;
487 6           const char* begin = output;
488              
489 6 100         if (output == NULL) return rhash_get_magnet_url_size(
490             filepath, context, hash_mask, flags);
491              
492             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
493 3 50         if ((flags & RHPR_NO_MAGNET) == 0) {
494 3           strcpy(output, "magnet:?");
495 3           output += 8;
496             }
497              
498 3 50         if ((flags & RHPR_FILESIZE) != 0) {
499 3           strcpy(output, "xl=");
500 3           output += 3;
501 3           output += rhash_sprintI64(output, context->msg_size);
502 3           *(output++) = '&';
503             }
504              
505 3 100         if (filepath) {
506 1           strcpy(output, "dn=");
507 1           output += 3;
508 1           output += rhash_urlencode(output, filepath);
509 1           *(output++) = '&';
510             }
511 3           flags &= RHPR_UPPERCASE;
512              
513 9 100         for (i = 0; i < 2; i++) {
514             unsigned bit;
515 6           unsigned hash = context->hash_id & hash_mask;
516 6           hash = (i == 0 ? hash & (RHASH_ED2K | RHASH_AICH)
517 6 100         : hash & ~(RHASH_ED2K | RHASH_AICH));
518 6 100         if (!hash) continue;
519              
520             /* loop through hash values */
521 11 100         for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
522             const char* name;
523 7 50         if ((bit & hash) == 0) continue;
524 7 50         if (!(name = rhash_get_magnet_name(bit))) continue;
525              
526 7           strcpy(output, "xt=urn:");
527 7           output += 7;
528 7           strcpy(output, name);
529 7           output += strlen(name);
530 7           *(output++) = ':';
531 7 100         output += rhash_print(output, context, bit,
532 7           (bit & (RHASH_SHA1 | RHASH_BTIH) ? flags | RHPR_BASE32 : flags));
533 7           *(output++) = '&';
534             }
535             }
536 3           output[-1] = '\0'; /* terminate the line */
537              
538 3           return (output - begin);
539             }
540              
541              
542             /* HASH SUM OUTPUT INTERFACE */
543              
544 49           size_t rhash_print_bytes(char* output, const unsigned char* bytes,
545             size_t size, int flags)
546             {
547             size_t str_len;
548 49           int upper_case = (flags & RHPR_UPPERCASE);
549 49           int format = (flags & ~RHPR_MODIFIER);
550              
551 49           switch (format) {
552             case RHPR_HEX:
553 39           str_len = size * 2;
554 39           rhash_byte_to_hex(output, bytes, (unsigned)size, upper_case);
555 39           break;
556             case RHPR_BASE32:
557 8           str_len = BASE32_LENGTH(size);
558 8           rhash_byte_to_base32(output, bytes, (unsigned)size, upper_case);
559 8           break;
560             case RHPR_BASE64:
561 1           str_len = BASE64_LENGTH(size);
562 1           rhash_byte_to_base64(output, bytes, (unsigned)size);
563 1           break;
564             default:
565 1           str_len = size;
566 1           memcpy(output, bytes, size);
567 1           break;
568             }
569 49           return str_len;
570             }
571              
572 52           size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags)
573             {
574             const rhash_info* info;
575             unsigned char digest[80];
576             size_t digest_size;
577              
578 52 100         info = (hash_id != 0 ? rhash_info_by_id(hash_id) :
579 5           ((rhash_context_ext*)context)->vector[0].hash_info->info);
580              
581 52 50         if (info == NULL) return 0;
582 52           digest_size = info->digest_size;
583 52 50         assert(digest_size <= 64);
584              
585 52           flags &= (RHPR_FORMAT | RHPR_MODIFIER);
586 52 100         if ((flags & RHPR_FORMAT) == 0) {
587             /* use default format if not specified by flags */
588 45 100         flags |= (info->flags & RHASH_INFO_BASE32 ? RHPR_BASE32 : RHPR_HEX);
589             }
590              
591 52 100         if (output == NULL) {
592 7           switch (flags & RHPR_FORMAT) {
593             case RHPR_HEX:
594 4           return (digest_size * 2);
595             case RHPR_BASE32:
596 3           return BASE32_LENGTH(digest_size);
597             case RHPR_BASE64:
598 0           return BASE64_LENGTH(digest_size);
599             default:
600 0           return digest_size;
601             }
602             }
603              
604             /* note: use info->hash_id, cause hash_id can be 0 */
605 45           rhash_put_digest(context, info->hash_id, digest);
606              
607 45 100         if ((flags & ~RHPR_UPPERCASE) == (RHPR_REVERSE | RHPR_HEX)) {
608             /* reverse the digest */
609 1           unsigned char *p = digest, *r = digest + digest_size - 1;
610             char tmp;
611 9 100         for (; p < r; p++, r--) {
612 8           tmp = *p;
613 8           *p = *r;
614 8           *r = tmp;
615             }
616             }
617              
618 52           return rhash_print_bytes(output, digest, digest_size, flags);
619             }
620              
621             #if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS)
622             #include
623             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved);
624             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved)
625             {
626             (void)hModule;
627             (void)reserved;
628             switch (reason) {
629             case DLL_PROCESS_ATTACH:
630             rhash_library_init();
631             break;
632             case DLL_PROCESS_DETACH:
633             /*rhash_library_free();*/
634             case DLL_THREAD_ATTACH:
635             case DLL_THREAD_DETACH:
636             break;
637             }
638             return TRUE;
639             }
640             #endif
641              
642             /**
643             * Process a BitTorrent-related rhash message.
644             *
645             * @param msg_id message identifier
646             * @param bt BitTorrent context
647             * @param ldata data depending on message
648             * @param rdata data depending on message
649             * @return message-specific data
650             */
651 0           static rhash_uptr_t process_bt_msg(unsigned msg_id, torrent_ctx* bt, rhash_uptr_t ldata, rhash_uptr_t rdata)
652             {
653 0 0         if (bt == NULL) return RHASH_ERROR;
654              
655 0           switch (msg_id) {
656             case RMSG_BT_ADD_FILE:
657 0           bt_add_file(bt, (const char*)ldata, *(unsigned long long*)rdata);
658 0           break;
659             case RMSG_BT_SET_OPTIONS:
660 0           bt_set_options(bt, (unsigned)ldata);
661 0           break;
662             case RMSG_BT_SET_ANNOUNCE:
663 0           bt_add_announce(bt, (const char*)ldata);
664 0           break;
665             case RMSG_BT_SET_PIECE_LENGTH:
666 0           bt_set_piece_length(bt, (size_t)ldata);
667 0           break;
668             case RMSG_BT_SET_BATCH_SIZE:
669 0           bt_set_piece_length(bt,
670 0           bt_default_piece_length(*(unsigned long long*)ldata));
671 0           break;
672             case RMSG_BT_SET_PROGRAM_NAME:
673 0           bt_set_program_name(bt, (const char*)ldata);
674 0           break;
675             case RMSG_BT_GET_TEXT:
676 0           return (rhash_uptr_t)bt_get_text(bt, (char**)ldata);
677             default:
678 0           return RHASH_ERROR; /* unknown message */
679             }
680 0           return 0;
681             }
682              
683             #define PVOID2UPTR(p) ((rhash_uptr_t)(((char*)(p)) + 0))
684              
685 0           RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata)
686             {
687             /* for messages working with rhash context */
688 0           rhash_context_ext* const ctx = (rhash_context_ext*)dst;
689              
690 0           switch (msg_id) {
691             case RMSG_GET_CONTEXT:
692             {
693             unsigned i;
694 0 0         for (i = 0; i < ctx->hash_vector_size; i++) {
695 0           struct rhash_hash_info* info = ctx->vector[i].hash_info;
696 0 0         if (info->info->hash_id == (unsigned)ldata)
697 0           return PVOID2UPTR(ctx->vector[i].context);
698             }
699 0           return (rhash_uptr_t)0;
700             }
701              
702             case RMSG_CANCEL:
703             /* mark rhash context as canceled, in a multithreaded program */
704 0           atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPED);
705 0           return 0;
706              
707             case RMSG_IS_CANCELED:
708 0           return (ctx->state == STATE_STOPED);
709              
710             case RMSG_GET_FINALIZED:
711 0           return ((ctx->flags & RCTX_FINALIZED) != 0);
712             case RMSG_SET_AUTOFINAL:
713 0           ctx->flags &= ~RCTX_AUTO_FINAL;
714 0 0         if (ldata) ctx->flags |= RCTX_AUTO_FINAL;
715 0           break;
716              
717             /* OpenSSL related messages */
718             #ifdef USE_OPENSSL
719             case RMSG_SET_OPENSSL_MASK:
720             rhash_openssl_hash_mask = (unsigned)ldata;
721             break;
722             case RMSG_GET_OPENSSL_MASK:
723             return rhash_openssl_hash_mask;
724             #endif
725             case RMSG_GET_OPENSSL_SUPPORTED_MASK:
726 0           return rhash_get_openssl_supported_hash_mask();
727             case RMSG_GET_OPENSSL_AVAILABLE_MASK:
728 0           return rhash_get_openssl_available_hash_mask();
729              
730             /* BitTorrent related messages */
731             case RMSG_BT_ADD_FILE:
732             case RMSG_BT_SET_OPTIONS:
733             case RMSG_BT_SET_ANNOUNCE:
734             case RMSG_BT_SET_PIECE_LENGTH:
735             case RMSG_BT_SET_PROGRAM_NAME:
736             case RMSG_BT_GET_TEXT:
737             case RMSG_BT_SET_BATCH_SIZE:
738 0           return process_bt_msg(msg_id, (torrent_ctx*)(((rhash_context_ext*)dst)->bt_ctx), ldata, rdata);
739              
740             default:
741 0           return RHASH_ERROR; /* unknown message */
742             }
743 0           return 0;
744             }