#include #include #include #include #include #include #include #include #include #include #include #include "benc.h" #include "metainfo.h" #include "subr.h" /* * d * announce = url * info = d * name = advisory file/dir save name * piece length = power of two length of each block * pieces = 20b of sha1-hash * num of pieces * length = length of file in bytes in single file download * files = l d * length = length of file in bytes * path = l path components * */ #ifndef PRId64 #define PRId64 "lld" #endif #ifndef PRIu32 #define PRIu32 "u" #endif void print_metainfo(struct metainfo *tp) { unsigned i; printf("Info hash: "); for (i = 0; i < 20; i++) printf("%.2x", tp->info_hash[i]); printf("\n"); printf("Tracker URL: %s\n", tp->announce); printf("Piece length: %" PRId64 "\n", (int64_t)tp->piece_length); printf("Number of pieces: %" PRIu32 "\n", tp->npieces); printf("Number of files: %u\n", tp->nfiles); printf("Advisory name: %s\n", tp->name); printf("Files:\n"); for (i = 0; i < tp->nfiles; i++) { printf("%s (%" PRId64 ")\n", tp->files[i].path, (int64_t)tp->files[i].length); } printf("Total length: %" PRId64 "\n\n", (int64_t)tp->total_length); } static int check_path(const char *path, size_t len) { if (len == 0) return 0; else if (len == 1 && path[0] == '.') return 0; else if (len == 2 && path[0] == '.' && path[1] == '.') return 0; else if (memchr(path, '/', len) != NULL) return 0; return 1; } int fill_fileinfo(const char *fdct, struct fileinfo *tfp) { int err; size_t npath, plen, len; const char *plst, *iter, *str; if ((err = benc_dget_off(fdct, "length", &tfp->length)) != 0) return err; if ((err = benc_dget_lst(fdct, "path", &plst)) != 0) return err; npath = plen = 0; iter = benc_first(plst); while (iter != NULL) { if (!benc_isstr(iter)) return EINVAL; benc_str(iter, &str, &len, &iter); if (!check_path(str, len)) return EINVAL; npath++; plen += len; } if (npath == 0) return EINVAL; if ((tfp->path = malloc(plen + (npath - 1) + 1)) == NULL) return ENOMEM; iter = benc_first(plst); benc_str(iter, &str, &len, &iter); memcpy(tfp->path, str, len); plen = len; npath--; while (npath > 0) { tfp->path[plen++] = '/'; benc_str(iter, &str, &len, &iter); memcpy(tfp->path + plen, str, len); plen += len; npath--; } tfp->path[plen] = '\0'; return 0; } void clear_metainfo(struct metainfo *mip) { int i; if (mip->piece_hash != NULL) free(mip->piece_hash); if (mip->announce != NULL) free(mip->announce); if (mip->files != NULL) { for (i = 0; i < mip->nfiles; i++) { if (mip->files[i].path != NULL) free(mip->files[i].path); } free(mip->files); } if (mip->name != NULL) free(mip->name); } int fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes) { size_t len; int err; const char *base_addr = bep; const char *hash_addr; if (!benc_isdct(bep)) return EINVAL; if ((err = benc_dget_strz(bep, "announce", &tp->announce, NULL)) != 0) goto out; if ((err = benc_dget_dct(bep, "info", &bep)) != 0) goto out; SHA1(bep, benc_length(bep), tp->info_hash); if ((err = benc_dget_off(bep, "piece length", &tp->piece_length)) != 0) goto out; if ((err = benc_dget_str(bep, "pieces", &hash_addr, &len)) != 0) goto out; if (len % 20 != 0) { err = EINVAL; goto out; } tp->npieces = len / 20; tp->pieces_off = hash_addr - base_addr; if (mem_hashes) { if ((tp->piece_hash = malloc(len)) == NULL) { err = ENOMEM; goto out; } bcopy(hash_addr, tp->piece_hash, len); } if ((err = benc_dget_strz(bep, "name", &tp->name, NULL)) != 0) goto out; err = benc_dget_off(bep, "length", &tp->total_length); if (err == 0) { tp->nfiles = 1; tp->files = calloc(1, sizeof(struct fileinfo)); if (tp->files != NULL) { tp->files[0].length = tp->total_length; tp->files[0].path = strdup(tp->name); if (tp->files[0].path == NULL) { err = ENOMEM; goto out; } } else { err = ENOMEM; goto out; } } else if (err == ENOENT) { int i; const char *flst, *fdct; if ((err = benc_dget_lst(bep, "files", &flst)) != 0) goto out; tp->nfiles = benc_nelems(flst); if (tp->nfiles < 1) { err = EINVAL; goto out; } tp->files = calloc(tp->nfiles, sizeof(struct fileinfo)); tp->total_length = 0; i = 0; for (fdct = benc_first(flst); fdct != NULL; fdct = benc_next(fdct)) { if (!benc_isdct(fdct)) { err = EINVAL; goto out; } if ((err = fill_fileinfo(fdct, &tp->files[i])) != 0) goto out; tp->total_length += tp->files[i].length; i++; } } else goto out; out: if (err != 0) clear_metainfo(tp); return err; } int load_metainfo(const char *path, off_t size, int mem_hashes, struct metainfo **res) { char *buf; int fd, err = 0; if ((fd = open(path, O_RDONLY)) == -1) return errno; if (size <= 0) { struct stat sb; if (fstat(fd, &sb) == -1) { close(fd); return errno; } else size = sb.st_size; } if ((buf = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) err = errno; close(fd); if (err == 0) err = benc_validate(buf, size); if (err == 0) if ((*res = calloc(1, sizeof(**res))) == NULL) err = ENOMEM; if (err == 0) if ((err = fill_metainfo(buf, *res, mem_hashes)) != 0) free(*res); munmap(buf, size); return err; }