File Coverage

_rhash.c
Criterion Covered Total %
statement 236 337 70.0
branch 120 204 58.8
condition n/a
subroutine n/a
pod n/a
total 356 541 65.8


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