File Coverage

_rhash.c
Criterion Covered Total %
statement 238 325 73.2
branch 116 194 59.7
condition n/a
subroutine n/a
pod n/a
total 354 519 68.2


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(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             /**
51             * Initialize static data of rhash algorithms
52             */
53 3           void rhash_library_init(void)
54             {
55 3           rhash_init_algorithms(RHASH_ALL_HASHES);
56             #ifdef USE_OPENSSL
57             rhash_plug_openssl();
58             #endif
59 3           }
60              
61             /**
62             * Returns the number of supported hash algorithms.
63             *
64             * @return the number of supported hash functions
65             */
66 1           int RHASH_API rhash_count(void)
67             {
68 1           return rhash_info_size;
69             }
70              
71             /* Lo-level rhash library functions */
72              
73             /**
74             * Allocate and initialize RHash context for calculating hash(es).
75             * After initializing rhash_update()/rhash_final() functions should be used.
76             * Then the context must be freed by calling rhash_free().
77             *
78             * @param hash_id union of bit flags, containing ids of hashes to calculate.
79             * @return initialized rhash context, NULL on error and errno is set
80             */
81 6           RHASH_API rhash rhash_init(unsigned hash_id)
82             {
83             unsigned tail_bit_index; /* index of hash_id trailing bit */
84 6           unsigned num = 0; /* number of hashes to compute */
85 6           rhash_context_ext *rctx = NULL; /* allocated rhash context */
86 6           size_t hash_size_sum = 0; /* size of hash contexts to store in rctx */
87              
88             unsigned i, bit_index, id;
89             struct rhash_hash_info* info;
90             size_t aligned_size;
91             char* phash_ctx;
92              
93 6           hash_id &= RHASH_ALL_HASHES;
94 6 50         if (hash_id == 0) {
95 0           errno = EINVAL;
96 0           return NULL;
97             }
98              
99 6           tail_bit_index = rhash_ctz(hash_id); /* get trailing bit index */
100 6 50         assert(tail_bit_index < RHASH_HASH_COUNT);
101              
102 6           id = 1 << tail_bit_index;
103              
104 6 100         if (hash_id == id) {
105             /* handle the most common case of only one hash */
106 2           num = 1;
107 2           info = &rhash_info_table[tail_bit_index];
108 2           hash_size_sum = info->context_size;
109             } else {
110             /* another case: hash_id contains several hashes */
111 62 100         for (bit_index = tail_bit_index; id <= hash_id; bit_index++, id = id << 1) {
112 58 50         assert(id != 0);
113 58 50         assert(bit_index < RHASH_HASH_COUNT);
114 58           info = &rhash_info_table[bit_index];
115 58 100         if (hash_id & id) {
116             /* align sizes by 8 bytes */
117 56           aligned_size = (info->context_size + 7) & ~7;
118 56           hash_size_sum += aligned_size;
119 56           num++;
120             }
121             }
122 4 50         assert(num > 1);
123             }
124              
125             /* align the size of the rhash context common part */
126 6           aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7;
127 6 50         assert(aligned_size >= sizeof(rhash_context_ext));
128              
129             /* allocate rhash context with enough memory to store contexts of all used hashes */
130 6           rctx = (rhash_context_ext*)malloc(aligned_size + hash_size_sum);
131 6 50         if (rctx == NULL) return NULL;
132              
133             /* initialize common fields of the rhash context */
134 6           memset(rctx, 0, sizeof(rhash_context_ext));
135 6           rctx->rc.hash_id = hash_id;
136 6           rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */
137 6           rctx->state = STATE_ACTIVE;
138 6           rctx->hash_vector_size = num;
139              
140             /* aligned hash contexts follows rctx->vector[num] in the same memory block */
141 6           phash_ctx = (char*)rctx + aligned_size;
142 6 50         assert(phash_ctx >= (char*)&rctx->vector[num]);
143              
144             /* initialize context for every hash in a loop */
145 66 100         for (bit_index = tail_bit_index, id = 1 << tail_bit_index, i = 0;
146 60           id <= hash_id; bit_index++, id = id << 1)
147             {
148             /* check if a hash function with given id shall be included into rctx */
149 60 100         if ((hash_id & id) != 0) {
150 58           info = &rhash_info_table[bit_index];
151 58 50         assert(info->context_size > 0);
152 58 50         assert(((phash_ctx - (char*)0) & 7) == 0); /* hash context is aligned */
153 58 50         assert(info->init != NULL);
154              
155 58           rctx->vector[i].hash_info = info;
156 58           rctx->vector[i].context = phash_ctx;
157              
158             /* BTIH initialization is complex, save pointer for later */
159 58 100         if ((id & RHASH_BTIH) != 0) rctx->bt_ctx = phash_ctx;
160 58           phash_ctx += (info->context_size + 7) & ~7;
161              
162             /* initialize the i-th hash context */
163 58           info->init(rctx->vector[i].context);
164 58           i++;
165             }
166             }
167              
168 6           return &rctx->rc; /* return allocated and initialized rhash context */
169             }
170              
171             /**
172             * Free RHash context memory.
173             *
174             * @param ctx the context to free.
175             */
176 4           void rhash_free(rhash ctx)
177             {
178 4           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
179             unsigned i;
180              
181 4 50         if (ctx == 0) return;
182 4 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
183 4           ectx->state = STATE_DELETED; /* mark memory block as being removed */
184              
185             /* clean the hash functions, which require additional clean up */
186 34 100         for (i = 0; i < ectx->hash_vector_size; i++) {
187 30           struct rhash_hash_info* info = ectx->vector[i].hash_info;
188 30 100         if (info->cleanup != 0) {
189 2           info->cleanup(ectx->vector[i].context);
190             }
191             }
192              
193 4           free(ectx);
194             }
195              
196             /**
197             * Re-initialize RHash context to reuse it.
198             * Useful to speed up processing of many small messages.
199             *
200             * @param ctx context to reinitialize
201             */
202 3           RHASH_API void rhash_reset(rhash ctx)
203             {
204 3           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
205             unsigned i;
206              
207 3 50         assert(ectx->hash_vector_size > 0);
208 3 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
209 3           ectx->state = STATE_ACTIVE; /* re-activate the structure */
210              
211             /* re-initialize every hash in a loop */
212 7 100         for (i = 0; i < ectx->hash_vector_size; i++) {
213 4           struct rhash_hash_info* info = ectx->vector[i].hash_info;
214 4 50         if (info->cleanup != 0) {
215 0           info->cleanup(ectx->vector[i].context);
216             }
217              
218 4 50         assert(info->init != NULL);
219 4           info->init(ectx->vector[i].context);
220             }
221 3           ectx->flags &= ~RCTX_FINALIZED; /* clear finalized state */
222 3           }
223              
224             /**
225             * Calculate hashes of message.
226             * Can be called repeatedly with chunks of the message to be hashed.
227             *
228             * @param ctx the rhash context
229             * @param message message chunk
230             * @param length length of the message chunk
231             * @return 0 on success; On fail return -1 and set errno
232             */
233 8           RHASH_API int rhash_update(rhash ctx, const void* message, size_t length)
234             {
235 8           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
236             unsigned i;
237              
238 8 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
239 8 50         if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
240              
241 8           ctx->msg_size += length;
242              
243             /* call update method for every algorithm */
244 68 100         for (i = 0; i < ectx->hash_vector_size; i++) {
245 60           struct rhash_hash_info* info = ectx->vector[i].hash_info;
246 60 50         assert(info->update != 0);
247 60           info->update(ectx->vector[i].context, message, length);
248             }
249 8           return 0; /* no error processing at the moment */
250             }
251              
252             /**
253             * Finalize hash calculation and optionally store the first hash.
254             *
255             * @param ctx the rhash context
256             * @param first_result optional buffer to store a calculated hash with the lowest available id
257             * @return 0 on success; On fail return -1 and set errno
258             */
259 9           RHASH_API int rhash_final(rhash ctx, unsigned char* first_result)
260             {
261 9           unsigned i = 0;
262             unsigned char buffer[130];
263 9 100         unsigned char* out = (first_result ? first_result : buffer);
264 9           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
265 9 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
266              
267             /* skip final call if already finalized and auto-final is on */
268 9 50         if ((ectx->flags & RCTX_FINALIZED_MASK) ==
269 0           (RCTX_AUTO_FINAL | RCTX_FINALIZED)) return 0;
270              
271             /* call final method for every algorithm */
272 71 100         for (i = 0; i < ectx->hash_vector_size; i++) {
273 62           struct rhash_hash_info* info = ectx->vector[i].hash_info;
274 62 50         assert(info->final != 0);
275 62 50         assert(info->info->digest_size < sizeof(buffer));
276 62           info->final(ectx->vector[i].context, out);
277 62           out = buffer;
278             }
279 9           ectx->flags |= RCTX_FINALIZED;
280 9           return 0; /* no error processing at the moment */
281             }
282              
283             /**
284             * Store digest for given hash_id.
285             * If hash_id is zero, function stores digest for a hash with the lowest id found in the context.
286             * For nonzero hash_id the context must contain it, otherwise function silently does nothing.
287             *
288             * @param ctx rhash context
289             * @param hash_id id of hash to retrieve or zero for hash with the lowest available id
290             * @param result buffer to put the hash into
291             */
292 44           static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result)
293             {
294 44           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
295             unsigned i;
296             rhash_vector_item *item;
297             struct rhash_hash_info* info;
298             unsigned char* digest;
299              
300 44 50         assert(ectx);
301 44 50         assert(ectx->hash_vector_size > 0 && ectx->hash_vector_size <= RHASH_HASH_COUNT);
    50          
302              
303             /* finalize context if not yet finalized and auto-final is on */
304 44 100         if ((ectx->flags & RCTX_FINALIZED_MASK) == RCTX_AUTO_FINAL) {
305 5           rhash_final(ctx, NULL);
306             }
307              
308 44 50         if (hash_id == 0) {
309 0           item = &ectx->vector[0]; /* get the first hash */
310 0           info = item->hash_info;
311             } else {
312 44           for (i = 0;; i++) {
313 397 50         if (i >= ectx->hash_vector_size) {
314 0           return; /* hash_id not found, do nothing */
315             }
316 397           item = &ectx->vector[i];
317 397           info = item->hash_info;
318 397 100         if (info->info->hash_id == hash_id) break;
319 353           }
320             }
321 44           digest = ((unsigned char*)item->context + info->digest_diff);
322 44 100         if (info->info->flags & F_SWAP32) {
323 8 50         assert((info->info->digest_size & 3) == 0);
324             /* NB: the next call is correct only for multiple of 4 byte size */
325 8           rhash_swap_copy_str_to_u32(result, 0, digest, info->info->digest_size);
326 36 100         } else if (info->info->flags & F_SWAP64) {
327 3           rhash_swap_copy_u64_to_str(result, digest, info->info->digest_size);
328             } else {
329 33           memcpy(result, digest, info->info->digest_size);
330             }
331             }
332              
333             /**
334             * Set the callback function to be called from the
335             * rhash_file() and rhash_file_update() functions
336             * on processing every file block. The file block
337             * size is set internally by rhash and now is 8 KiB.
338             *
339             * @param ctx rhash context
340             * @param callback pointer to the callback function
341             * @param callback_data pointer to data passed to the callback
342             */
343 0           RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data)
344             {
345 0           ((rhash_context_ext*)ctx)->callback = (void*)callback;
346 0           ((rhash_context_ext*)ctx)->callback_data = callback_data;
347 0           }
348              
349              
350             /* hi-level message hashing interface */
351              
352             /**
353             * Compute a hash of the given message.
354             *
355             * @param hash_id id of hash sum to compute
356             * @param message the message to process
357             * @param length message length
358             * @param result buffer to receive binary hash string
359             * @return 0 on success, -1 on error
360             */
361 1           RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result)
362             {
363             rhash ctx;
364 1           hash_id &= RHASH_ALL_HASHES;
365 1           ctx = rhash_init(hash_id);
366 1 50         if (ctx == NULL) return -1;
367 1           rhash_update(ctx, message, length);
368 1           rhash_final(ctx, result);
369 1           rhash_free(ctx);
370 1           return 0;
371             }
372              
373             /**
374             * Hash a file or stream. Multiple hashes can be computed.
375             * First, inintialize ctx parameter with rhash_init() before calling
376             * rhash_file_update(). Then use rhash_final() and rhash_print()
377             * to retrive hash values. Finaly call rhash_free() on ctx
378             * to free allocated memory or call rhash_reset() to reuse ctx.
379             *
380             * @param ctx rhash context
381             * @param fd descriptor of the file to hash
382             * @return 0 on success, -1 on error and errno is set
383             */
384 0           RHASH_API int rhash_file_update(rhash ctx, FILE* fd)
385             {
386 0           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
387 0           const size_t block_size = 8192;
388             unsigned char *buffer, *pmem;
389 0           size_t length = 0, align8;
390 0           int res = 0;
391 0 0         if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
392              
393 0 0         if (ctx == NULL) {
394 0           errno = EINVAL;
395 0           return -1;
396             }
397              
398 0           pmem = (unsigned char*)malloc(block_size + 8);
399 0 0         if (!pmem) return -1; /* errno is set to ENOMEM according to UNIX 98 */
400              
401 0           align8 = ((unsigned char*)0 - pmem) & 7;
402 0           buffer = pmem + align8;
403              
404 0 0         while (!feof(fd)) {
405             /* stop if canceled */
406 0 0         if (ectx->state != STATE_ACTIVE) break;
407              
408 0           length = fread(buffer, 1, block_size, fd);
409              
410 0 0         if (ferror(fd)) {
411 0           res = -1; /* note: errno contains error code */
412 0           break;
413 0 0         } else if (length) {
414 0           rhash_update(ctx, buffer, length);
415              
416 0 0         if (ectx->callback) {
417 0           ((rhash_callback_t)ectx->callback)(ectx->callback_data, ectx->rc.msg_size);
418             }
419             }
420             }
421              
422 0           free(buffer);
423 0           return res;
424             }
425              
426             /**
427             * Compute a single hash for given file.
428             *
429             * @param hash_id id of hash sum to compute
430             * @param filepath path to the file to hash
431             * @param result buffer to receive hash value with the lowest requested id
432             * @return 0 on success, -1 on error and errno is set
433             */
434 0           RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result)
435             {
436             FILE* fd;
437             rhash ctx;
438             int res;
439              
440 0           hash_id &= RHASH_ALL_HASHES;
441 0 0         if (hash_id == 0) {
442 0           errno = EINVAL;
443 0           return -1;
444             }
445              
446 0 0         if ((fd = fopen(filepath, "rb")) == NULL) return -1;
447              
448 0 0         if ((ctx = rhash_init(hash_id)) == NULL) return -1;
449              
450 0           res = rhash_file_update(ctx, fd); /* hash the file */
451 0           fclose(fd);
452              
453 0           rhash_final(ctx, result);
454 0           rhash_free(ctx);
455 0           return res;
456             }
457              
458             #ifdef _WIN32 /* windows only function */
459             #include
460              
461             /**
462             * Compute a single hash for given file.
463             *
464             * @param hash_id id of hash sum to compute
465             * @param filepath path to the file to hash
466             * @param result buffer to receive hash value with the lowest requested id
467             * @return 0 on success, -1 on error, -1 on error and errno is set
468             */
469             RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result)
470             {
471             FILE* fd;
472             rhash ctx;
473             int res;
474              
475             hash_id &= RHASH_ALL_HASHES;
476             if (hash_id == 0) {
477             errno = EINVAL;
478             return -1;
479             }
480              
481             if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1;
482              
483             if ((ctx = rhash_init(hash_id)) == NULL) return -1;
484              
485             res = rhash_file_update(ctx, fd); /* hash the file */
486             fclose(fd);
487              
488             rhash_final(ctx, result);
489             rhash_free(ctx);
490             return res;
491             }
492             #endif
493              
494             /* RHash information functions */
495              
496             /**
497             * Returns information about a hash function by its hash_id.
498             *
499             * @param hash_id the id of hash algorithm
500             * @return pointer to the rhash_info structure containing the information
501             */
502 62           const rhash_info* rhash_info_by_id(unsigned hash_id)
503             {
504 62           hash_id &= RHASH_ALL_HASHES;
505             /* check that only one bit is set */
506 62 50         if (hash_id != (hash_id & -(int)hash_id)) return NULL;
507             /* note: alternative condition is (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) */
508 62           return rhash_info_table[rhash_ctz(hash_id)].info;
509             }
510              
511             /**
512             * Detect default digest output format for given hash algorithm.
513             *
514             * @param hash_id the id of hash algorithm
515             * @return 1 for base32 format, 0 for hexadecimal
516             */
517 3           RHASH_API int rhash_is_base32(unsigned hash_id)
518             {
519             /* fast method is just to test a bit-mask */
520 3           return ((hash_id & (RHASH_TTH | RHASH_AICH)) != 0);
521             }
522              
523             /**
524             * Returns size of binary digest for given hash algorithm.
525             *
526             * @param hash_id the id of hash algorithm
527             * @return digest size in bytes
528             */
529 2           RHASH_API int rhash_get_digest_size(unsigned hash_id)
530             {
531 2           hash_id &= RHASH_ALL_HASHES;
532 2 50         if (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) return -1;
    50          
533 2           return (int)rhash_info_table[rhash_ctz(hash_id)].info->digest_size;
534             }
535              
536             /**
537             * Returns length of digest hash string in default output format.
538             *
539             * @param hash_id the id of hash algorithm
540             * @return the length of hash string
541             */
542 1           RHASH_API int rhash_get_hash_length(unsigned hash_id)
543             {
544 1           const rhash_info* info = rhash_info_by_id(hash_id);
545 1 50         return (int)(info ? (info->flags & F_BS32 ?
    50          
546 1           BASE32_LENGTH(info->digest_size) : info->digest_size * 2) : 0);
547             }
548              
549             /**
550             * Returns a name of given hash algorithm.
551             *
552             * @param hash_id the id of hash algorithm
553             * @return algorithm name
554             */
555 1           RHASH_API const char* rhash_get_name(unsigned hash_id)
556             {
557 1           const rhash_info* info = rhash_info_by_id(hash_id);
558 1 50         return (info ? info->name : 0);
559             }
560              
561             /**
562             * Returns a name part of magnet urn of the given hash algorithm.
563             * Such magnet_name is used to generate a magnet link of the form
564             * urn:<magnet_name>=<hash_value>.
565             *
566             * @param hash_id the id of hash algorithm
567             * @return name
568             */
569 14           RHASH_API const char* rhash_get_magnet_name(unsigned hash_id)
570             {
571 14           const rhash_info* info = rhash_info_by_id(hash_id);
572 14 50         return (info ? info->magnet_name : 0);
573             }
574              
575 3           static size_t rhash_get_magnet_url_size(const char* filepath,
576             rhash context, unsigned hash_mask, int flags)
577             {
578 3           size_t size = 0; /* count terminating '\0' */
579 3           unsigned bit, hash = context->hash_id & hash_mask;
580              
581             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
582 3 50         if ((flags & RHPR_NO_MAGNET) == 0) {
583 3           size += 8;
584             }
585              
586 3 50         if ((flags & RHPR_FILESIZE) != 0) {
587 3           uint64_t num = context->msg_size;
588              
589 3           size += 4;
590 3 50         if (num == 0) size++;
591             else {
592 6 100         for (; num; num /= 10, size++);
593             }
594             }
595              
596 3 100         if (filepath) {
597 1           size += 4 + rhash_urlencode(NULL, filepath);
598             }
599              
600             /* loop through hash values */
601 11 100         for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
602             const char* name;
603 8 100         if ((bit & hash) == 0) continue;
604 7 50         if ((name = rhash_get_magnet_name(bit)) == 0) continue;
605              
606 7           size += (7 + 2) + strlen(name);
607 7 100         size += rhash_print(NULL, context, bit,
608 7           (bit & (RHASH_SHA1 | RHASH_BTIH) ? RHPR_BASE32 : 0));
609             }
610              
611 3           return size;
612             }
613              
614             /**
615             * Print magnet link with given filepath and calculated hash sums into the
616             * output buffer. The hash_mask can limit which hash values will be printed.
617             * The function returns the size of the required buffer.
618             * If output is NULL the .
619             *
620             * @param output a string buffer to receive the magnet link or NULL
621             * @param filepath the file path to be printed or NULL
622             * @param context algorithms state
623             * @param hash_mask bit mask of the hash sums to add to the link
624             * @param flags can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET,
625             * RHPR_FILESIZE
626             * @return number of written characters, including terminating '\0' on success, 0 on fail
627             */
628 6           RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
629             rhash context, unsigned hash_mask, int flags)
630             {
631             int i;
632 6           const char* begin = output;
633              
634 6 100         if (output == NULL) return rhash_get_magnet_url_size(
635             filepath, context, hash_mask, flags);
636              
637             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
638 3 50         if ((flags & RHPR_NO_MAGNET) == 0) {
639 3           strcpy(output, "magnet:?");
640 3           output += 8;
641             }
642              
643 3 50         if ((flags & RHPR_FILESIZE) != 0) {
644 3           strcpy(output, "xl=");
645 3           output += 3;
646 3           output += rhash_sprintI64(output, context->msg_size);
647 3           *(output++) = '&';
648             }
649              
650 3 100         if (filepath) {
651 1           strcpy(output, "dn=");
652 1           output += 3;
653 1           output += rhash_urlencode(output, filepath);
654 1           *(output++) = '&';
655             }
656 3           flags &= RHPR_UPPERCASE;
657              
658 9 100         for (i = 0; i < 2; i++) {
659             unsigned bit;
660 6           unsigned hash = context->hash_id & hash_mask;
661 6           hash = (i == 0 ? hash & (RHASH_ED2K | RHASH_AICH)
662 6 100         : hash & ~(RHASH_ED2K | RHASH_AICH));
663 6 100         if (!hash) continue;
664              
665             /* loop through hash values */
666 11 100         for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
667             const char* name;
668 7 50         if ((bit & hash) == 0) continue;
669 7 50         if (!(name = rhash_get_magnet_name(bit))) continue;
670              
671 7           strcpy(output, "xt=urn:");
672 7           output += 7;
673 7           strcpy(output, name);
674 7           output += strlen(name);
675 7           *(output++) = ':';
676 7 100         output += rhash_print(output, context, bit,
677 7           (bit & (RHASH_SHA1 | RHASH_BTIH) ? flags | RHPR_BASE32 : flags));
678 7           *(output++) = '&';
679             }
680             }
681 3           output[-1] = '\0'; /* terminate the line */
682              
683 3           return (output - begin);
684             }
685              
686             /* hash sum output */
687              
688             /**
689             * Print a text presentation of a given hash sum to the specified buffer,
690             *
691             * @param output a buffer to print the hash to
692             * @param bytes a hash sum to print
693             * @param size a size of hash sum in bytes
694             * @param flags a bit-mask controlling how to format the hash sum,
695             * can be a mix of the flags: RHPR_RAW, RHPR_HEX, RHPR_BASE32,
696             * RHPR_BASE64, RHPR_UPPERCASE, RHPR_REVERSE
697             * @return the number of written characters
698             */
699 48           size_t rhash_print_bytes(char* output, const unsigned char* bytes,
700             size_t size, int flags)
701             {
702             size_t str_len;
703 48           int upper_case = (flags & RHPR_UPPERCASE);
704 48           int format = (flags & ~RHPR_MODIFIER);
705              
706 48           switch (format) {
707             case RHPR_HEX:
708 38           str_len = size * 2;
709 38           rhash_byte_to_hex(output, bytes, (unsigned)size, upper_case);
710 38           break;
711             case RHPR_BASE32:
712 8           str_len = BASE32_LENGTH(size);
713 8           rhash_byte_to_base32(output, bytes, (unsigned)size, upper_case);
714 8           break;
715             case RHPR_BASE64:
716 1           str_len = BASE64_LENGTH(size);
717 1           rhash_byte_to_base64(output, bytes, (unsigned)size);
718 1           break;
719             default:
720 1           str_len = size;
721 1           memcpy(output, bytes, size);
722 1           break;
723             }
724 48           return str_len;
725             }
726              
727             /**
728             * Print text presentation of a hash sum with given hash_id to the specified
729             * output buffer. If the hash_id is zero, then print the hash sum with
730             * the lowest id stored in the hash context.
731             * The function call fails if the context doesn't include a hash with the
732             * given hash_id.
733             *
734             * @param output a buffer to print the hash to
735             * @param context algorithms state
736             * @param hash_id id of the hash sum to print or 0 to print the first hash
737             * saved in the context.
738             * @param flags a bitmask controlling how to print the hash. Can contain flags
739             * RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc.
740             * @return the number of written characters on success or 0 on fail
741             */
742 51           size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags)
743             {
744             const rhash_info* info;
745             unsigned char digest[80];
746             size_t digest_size;
747              
748 51 100         info = (hash_id != 0 ? rhash_info_by_id(hash_id) :
749 5           ((rhash_context_ext*)context)->vector[0].hash_info->info);
750              
751 51 50         if (info == NULL) return 0;
752 51           digest_size = info->digest_size;
753 51 50         assert(digest_size <= 64);
754              
755 51           flags &= (RHPR_FORMAT | RHPR_MODIFIER);
756 51 100         if ((flags & RHPR_FORMAT) == 0) {
757             /* use default format if not specified by flags */
758 44 100         flags |= (info->flags & RHASH_INFO_BASE32 ? RHPR_BASE32 : RHPR_HEX);
759             }
760              
761 51 100         if (output == NULL) {
762 7           switch (flags & RHPR_FORMAT) {
763             case RHPR_HEX:
764 4           return (digest_size * 2);
765             case RHPR_BASE32:
766 3           return BASE32_LENGTH(digest_size);
767             case RHPR_BASE64:
768 0           return BASE64_LENGTH(digest_size);
769             default:
770 0           return digest_size;
771             }
772             }
773              
774             /* note: use info->hash_id, cause hash_id can be 0 */
775 44           rhash_put_digest(context, info->hash_id, digest);
776              
777 44 100         if ((flags & ~RHPR_UPPERCASE) == (RHPR_REVERSE | RHPR_HEX)) {
778             /* reverse the digest */
779 1           unsigned char *p = digest, *r = digest + digest_size - 1;
780             char tmp;
781 9 100         for (; p < r; p++, r--) {
782 8           tmp = *p;
783 8           *p = *r;
784 8           *r = tmp;
785             }
786             }
787              
788 51           return rhash_print_bytes(output, digest, digest_size, flags);
789             }
790              
791             #if defined(_WIN32) && defined(RHASH_EXPORTS)
792             #include
793             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved);
794             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved)
795             {
796             (void)hModule;
797             (void)reserved;
798             switch (reason) {
799             case DLL_PROCESS_ATTACH:
800             rhash_library_init();
801             break;
802             case DLL_PROCESS_DETACH:
803             /*rhash_library_free();*/
804             case DLL_THREAD_ATTACH:
805             case DLL_THREAD_DETACH:
806             break;
807             }
808             return TRUE;
809             }
810             #endif
811              
812             /**
813             * Process a BitTorrent-related rhash message.
814             *
815             * @param msg_id message identifier
816             * @param bt BitTorrent context
817             * @param ldata data depending on message
818             * @param rdata data depending on message
819             * @return message-specific data
820             */
821 0           static rhash_uptr_t process_bt_msg(unsigned msg_id, torrent_ctx* bt, rhash_uptr_t ldata, rhash_uptr_t rdata)
822             {
823 0 0         if (bt == NULL) return RHASH_ERROR;
824              
825 0           switch (msg_id) {
826             case RMSG_BT_ADD_FILE:
827 0           bt_add_file(bt, (const char*)ldata, *(unsigned long long*)rdata);
828 0           break;
829             case RMSG_BT_SET_OPTIONS:
830 0           bt_set_options(bt, (unsigned)ldata);
831 0           break;
832             case RMSG_BT_SET_ANNOUNCE:
833 0           bt_add_announce(bt, (const char*)ldata);
834 0           break;
835             case RMSG_BT_SET_PIECE_LENGTH:
836 0           bt_set_piece_length(bt, (size_t)ldata);
837 0           break;
838             case RMSG_BT_SET_BATCH_SIZE:
839 0           bt_set_piece_length(bt,
840 0           bt_default_piece_length(*(unsigned long long*)ldata));
841 0           break;
842             case RMSG_BT_SET_PROGRAM_NAME:
843 0           bt_set_program_name(bt, (const char*)ldata);
844 0           break;
845             case RMSG_BT_GET_TEXT:
846 0           return (rhash_uptr_t)bt_get_text(bt, (char**)ldata);
847             default:
848 0           return RHASH_ERROR; /* unknown message */
849             }
850 0           return 0;
851             }
852              
853             #define PVOID2UPTR(p) ((rhash_uptr_t)(((char*)(p)) + 0))
854              
855             /**
856             * Process a rhash message.
857             *
858             * @param msg_id message identifier
859             * @param dst message destination (can be NULL for generic messages)
860             * @param ldata data depending on message
861             * @param rdata data depending on message
862             * @return message-specific data
863             */
864 0           RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata)
865             {
866             /* for messages working with rhash context */
867 0           rhash_context_ext* const ctx = (rhash_context_ext*)dst;
868              
869 0           switch (msg_id) {
870             case RMSG_GET_CONTEXT:
871             {
872             unsigned i;
873 0 0         for (i = 0; i < ctx->hash_vector_size; i++) {
874 0           struct rhash_hash_info* info = ctx->vector[i].hash_info;
875 0 0         if (info->info->hash_id == (unsigned)ldata)
876 0           return PVOID2UPTR(ctx->vector[i].context);
877             }
878 0           return (rhash_uptr_t)0;
879             }
880              
881             case RMSG_CANCEL:
882             /* mark rhash context as canceled, in a multithreaded program */
883 0           atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPED);
884 0           return 0;
885              
886             case RMSG_IS_CANCELED:
887 0           return (ctx->state == STATE_STOPED);
888              
889             case RMSG_GET_FINALIZED:
890 0           return ((ctx->flags & RCTX_FINALIZED) != 0);
891             case RMSG_SET_AUTOFINAL:
892 0           ctx->flags &= ~RCTX_AUTO_FINAL;
893 0 0         if (ldata) ctx->flags |= RCTX_AUTO_FINAL;
894 0           break;
895              
896             /* OpenSSL related messages */
897             #ifdef USE_OPENSSL
898             case RMSG_SET_OPENSSL_MASK:
899             rhash_openssl_hash_mask = (unsigned)ldata;
900             break;
901             case RMSG_GET_OPENSSL_MASK:
902             return rhash_openssl_hash_mask;
903             #endif
904              
905             /* BitTorrent related messages */
906             case RMSG_BT_ADD_FILE:
907             case RMSG_BT_SET_OPTIONS:
908             case RMSG_BT_SET_ANNOUNCE:
909             case RMSG_BT_SET_PIECE_LENGTH:
910             case RMSG_BT_SET_PROGRAM_NAME:
911             case RMSG_BT_GET_TEXT:
912             case RMSG_BT_SET_BATCH_SIZE:
913 0           return process_bt_msg(msg_id, (torrent_ctx*)(((rhash_context_ext*)dst)->bt_ctx), ldata, rdata);
914              
915             default:
916 0           return RHASH_ERROR; /* unknown message */
917             }
918 0           return 0;
919             }