File Coverage

_rhash.c
Criterion Covered Total %
statement 237 329 72.0
branch 117 196 59.6
condition n/a
subroutine n/a
pod n/a
total 354 525 67.4


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