bencoding.c
来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 1,000 行 · 第 1/2 页
C
1,000 行
/* BitTorrent bencoding scanner and parser */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <sys/types.h>#ifdef HAVE_NETINET_IN_H#include <netinet/in.h> /* OS/2 needs this after sys/types.h */#endif#include "elinks.h"#include "protocol/bittorrent/bencoding.h"#include "protocol/bittorrent/common.h"#include "util/error.h"#include "util/sha1.h"#include "util/scanner.h"#include "util/string.h"#include "util/time.h"/* The various token types and what they contain. */enum bencoding_token { /* Char tokens: */ /* Char tokens range from 1 to 255 and have their char value as type */ /* meaning non char tokens have values from 256 and up. */ BENCODING_TOKEN_INTEGER = 'i', /* 'i' <integer> 'e' */ BENCODING_TOKEN_LIST = 'l', /* 'l' ( <any> ) * 'e' */ BENCODING_TOKEN_DICTIONARY = 'd', /* 'd' ( <string> <any> ) * 'e' */ BENCODING_TOKEN_END = 'e', /* Low level tokens: */ BENCODING_TOKEN_STRING = 256, /* <integer> ':' <bytes> */ /* High level tokens: */ /* Common tokens. */ BENCODING_TOKEN_FILES, /* dictionary */ BENCODING_TOKEN_NAME, /* string */ /* Tokens related to the metainfo file. */ BENCODING_TOKEN_ANNOUNCE, /* string */ BENCODING_TOKEN_ANNOUNCE_LIST, /* list */ BENCODING_TOKEN_COMMENT, /* string */ BENCODING_TOKEN_CREATED_BY, /* string */ BENCODING_TOKEN_CREATION_DATE, /* integer */ BENCODING_TOKEN_INFO, /* dictionary */ BENCODING_TOKEN_LENGTH, /* integer */ BENCODING_TOKEN_MD5SUM, /* string */ BENCODING_TOKEN_PATH, /* string */ BENCODING_TOKEN_PIECES, /* string */ BENCODING_TOKEN_PIECE_LENGTH, /* integer */ /* Tokens related to the tracker protocol response. */ BENCODING_TOKEN_FAILURE_REASON, /* string */ BENCODING_TOKEN_INTERVAL, /* integer */ BENCODING_TOKEN_IP, /* string */ BENCODING_TOKEN_PEERS, /* dictionary */ BENCODING_TOKEN_PEER_ID, /* string */ BENCODING_TOKEN_PORT, /* integer */ /* Tokens related to the tracker `scrape' response. */ BENCODING_TOKEN_COMPLETE, /* integer */ BENCODING_TOKEN_DOWNLOADED, /* integer */ BENCODING_TOKEN_INCOMPLETE, /* integer */ /* Another internal token type used both to mark unused tokens in the * scanner table as invalid or when scanning to signal that the * scanning should end. */ BENCODING_TOKEN_NONE = 0, /* Special token to report syntax errors. */ BENCODING_TOKEN_ERROR = 1,};/* ************************************************************************** *//* The scanner: *//* ************************************************************************** */#define is_bencoding_integer(c) ((c) == BENCODING_TOKEN_INTEGER)#define is_bencoding_list(c) ((c) == BENCODING_TOKEN_LIST)#define is_bencoding_dictionary(c) ((c) == BENCODING_TOKEN_DICTIONARY)#define is_bencoding_end(c) ((c) == BENCODING_TOKEN_END)#define is_bencoding_string(c) (isdigit(c))#define scan_bencoding_integer(scanner, s) \ while ((s) < (scanner)->end && isdigit(*(s))) (s)++;static inline voidscan_bencoding_token(struct scanner *scanner, struct scanner_token *token){ unsigned char *string = scanner->position; unsigned char first_char = *string; enum bencoding_token type = BENCODING_TOKEN_NONE; int real_length = -1; token->string = string++; if (is_bencoding_string(first_char)) { unsigned long string_length; scan_bencoding_integer(scanner, string); /* Check the length delimiter. */ if (*string == ':') { errno = 0; string_length = strtoul(token->string, NULL, 10); if (!errno && string_length < INT_MAX && string + string_length < scanner->end) { /* Claim the string data. */ token->string = string + 1; real_length = string_length; string = token->string + string_length; type = BENCODING_TOKEN_STRING; } } } else if (is_bencoding_end(first_char)) { type = BENCODING_TOKEN_END; } else if (is_bencoding_integer(first_char)) { unsigned char *integer_start = string; /* Signedness. */ if (*string == '-') string++; /* Scan to the end marker */ scan_bencoding_integer(scanner, string); if (*string == BENCODING_TOKEN_END) { token->string = integer_start; real_length = string - integer_start; type = BENCODING_TOKEN_INTEGER; string++; } } else if (is_bencoding_dictionary(first_char)) { type = BENCODING_TOKEN_DICTIONARY; } else if (is_bencoding_list(first_char)) { type = BENCODING_TOKEN_LIST; } token->type = type; token->length = real_length > 0 ? real_length : string - token->string; scanner->position = string;}static voidskip_bencoding_tokens(struct scanner *scanner){ int nesting_level = 0; assert(scanner_has_tokens(scanner)); /* Skip while nesting_level is > 0 since the first token can be the end * token. */ do { struct scanner_token *token = get_scanner_token(scanner); if (!token) return; switch (token->type) { case BENCODING_TOKEN_INTEGER: case BENCODING_TOKEN_STRING: break; case BENCODING_TOKEN_DICTIONARY: case BENCODING_TOKEN_LIST: nesting_level++; break; case BENCODING_TOKEN_END: nesting_level--; break; default: INTERNAL("Scanner error detected"); } skip_scanner_token(scanner); } while (nesting_level > 0 && scanner_has_tokens(scanner));}static struct scanner_token *scan_bencoding_tokens(struct scanner *scanner){ struct scanner_token *table_end = scanner->table + SCANNER_TOKENS; struct scanner_token *current; if (!begin_token_scanning(scanner)) return get_scanner_token(scanner); /* Scan tokens until we fill the table */ for (current = scanner->table + scanner->tokens; current < table_end && scanner->position < scanner->end; current++) { if (scanner->position >= scanner->end) break; scan_bencoding_token(scanner, current); /* Did someone scream for us to end the madness? */ if (current->type == BENCODING_TOKEN_NONE) { scanner->position = NULL; current--; break; } } return end_token_scanning(scanner, current);}struct scanner_info bencoding_scanner_info = { NULL, NULL, scan_bencoding_tokens,};/* ************************************************************************** *//* BitTorrent specific dictionary value type checking: *//* ************************************************************************** */struct bencoding_dictionary_info { unsigned char *key; enum bencoding_token key_type; enum bencoding_token value_type;};#define DICT(key, keytype, valuetype) \ { key, BENCODING_TOKEN_##keytype, BENCODING_TOKEN_##valuetype }static const struct bencoding_dictionary_info bencoding_dictionary_entries[] = { /* <key> <key-type> <value-type> */ DICT("announce list", ANNOUNCE_LIST, LIST), DICT("announce", ANNOUNCE, STRING), DICT("comment", COMMENT, STRING), DICT("complete", COMPLETE, INTEGER), DICT("created by", CREATED_BY, STRING), DICT("creation date", CREATION_DATE, INTEGER), DICT("downloaded", DOWNLOADED, INTEGER), DICT("failure reason", FAILURE_REASON, STRING), DICT("files", FILES, LIST), DICT("incomplete", INCOMPLETE, INTEGER), DICT("info", INFO, DICTIONARY), DICT("interval", INTERVAL, INTEGER), DICT("ip", IP, STRING), DICT("length", LENGTH, INTEGER), DICT("md5sum", MD5SUM, STRING), DICT("name", NAME, STRING), DICT("path", PATH, LIST), DICT("peer id", PEER_ID, STRING), DICT("peers", PEERS, LIST), DICT("peers", PEERS, STRING), DICT("piece length", PIECE_LENGTH, INTEGER), DICT("pieces", PIECES, STRING), DICT("port", PORT, INTEGER), DICT(NULL, NONE, NONE),};#undef DICT/* Looks up the key type and validates that the value token is valid. */enum bencoding_tokencheck_bencoding_dictionary_entry(struct scanner *scanner, struct scanner_token **value_ptr){ const struct bencoding_dictionary_info *entry; struct scanner_token *key, *value, key_backup; key = get_scanner_token(scanner); if (!key) return BENCODING_TOKEN_ERROR; if (key->type == BENCODING_TOKEN_END) return BENCODING_TOKEN_END; if (key->type != BENCODING_TOKEN_STRING) return BENCODING_TOKEN_NONE; /* Backup the token since the it might disappear when requesting the * following value token. */ copy_struct(&key_backup, key); key = &key_backup; *value_ptr = value = get_next_scanner_token(scanner); if (!value) return BENCODING_TOKEN_ERROR; for (entry = bencoding_dictionary_entries; entry->key; entry++) { if (!scanner_token_strlcasecmp(key, entry->key, -1)) continue; /* Type-check the value. Some keys have multiple types. */ if (value->type != entry->value_type) continue; return entry->key_type; } return BENCODING_TOKEN_NONE;}/* ************************************************************************** *//* The .torrent metafile parsing: *//* ************************************************************************** */static off_tparse_bencoding_integer(struct scanner_token *token){ unsigned char *string = token->string; int pos = 0, length = token->length; off_t integer = 0; int sign = 1; assert(length); if (string[pos] == '-') { pos++; sign = -1; } for (; pos < length && isdigit(string[pos]); pos++) { if (integer > (off_t) integer * 10) return 0; integer = (off_t) integer * 10 + string[pos] - '0'; } if (sign == -1) integer *= sign; return integer;}static unsigned char *normalize_bencoding_path(unsigned char *path, int pathlen, int *malicious){ struct string string; /* Normalize and check for malicious paths in the the file list. */ if (!init_string(&string) || !add_to_string(&string, "file://./") || !add_bytes_to_string(&string, path, pathlen)) { done_string(&string); return NULL; } path = normalize_uri(NULL, string.source); /* This shouldn't happened but it makes sense to be a little paranoid * here. ;-) */ if (memcmp(path, "file://", 7)) { done_string(&string); return NULL; } /* The normalization will make the path start with './' if the path is * OK and '/' if the path contained directory elevators which moved * outside the current working directory (CWD). These potentially * malicous paths will be translated to just be nested in the CWD. */ *malicious = !!dir_sep(path[7]); path += 8 + !*malicious; memmove(string.source, path, strlen(path) + 1); return string.source;}/* Add file to the file list. The new file is based on info from the passed * template and will have the given path after it has been normalized and * checked for sanity. */static enum bittorrent_stateadd_bittorrent_file(struct bittorrent_meta *meta, unsigned char *path, struct bittorrent_file *template){ struct bittorrent_file *file; int malicious; int pathlen; /* Normalize and check for malicious paths in the the file list. */ path = normalize_bencoding_path(path, strlen(path), &malicious); if (!path) return BITTORRENT_STATE_OUT_OF_MEM; if (malicious) meta->malicious_paths = malicious; pathlen = strlen(path); file = mem_calloc(1, sizeof(*file) + pathlen); if (!file) { mem_free(path); return BITTORRENT_STATE_OUT_OF_MEM; } copy_struct(file, template); memcpy(file->name, path, pathlen); mem_free(path); file->selected = 1; add_to_list_end(meta->files, file); return BITTORRENT_STATE_OK;}/* Parses a list of path elements and adds them each to the path string * separated by the platform specific directory separater. */static enum bittorrent_stateparse_bencoding_file_path(struct scanner *scanner, struct string *path){ assert(get_scanner_token(scanner)->type == BENCODING_TOKEN_LIST); skip_scanner_token(scanner); while (scanner_has_tokens(scanner)) { struct scanner_token *token = get_scanner_token(scanner); if (!token) break; if (token->type == BENCODING_TOKEN_END) { return BITTORRENT_STATE_OK; } if (token->type != BENCODING_TOKEN_STRING) break; if (path->length > 0) { /* Somewhat platform independant. dir_sep() is either a * macro or an inline function so the compiler should * optimize away the unneded branch. */ unsigned char separator = dir_sep('\\') ? '\\' : '/'; add_char_to_string(path, separator); } add_bytes_to_string(path, token->string, token->length); skip_scanner_token(scanner); } return BITTORRENT_STATE_ERROR;}/* Parse a dictionary of file information used for multi-file torrents. */static enum bittorrent_stateparse_bencoding_file_dictionary(struct bittorrent_meta *meta, struct scanner *scanner, struct string *path){ struct bittorrent_file file; assert(get_scanner_token(scanner)->type == BENCODING_TOKEN_DICTIONARY); skip_scanner_token(scanner); memset(&file, 0, sizeof(file)); while (scanner_has_tokens(scanner)) { struct scanner_token *value; enum bittorrent_state state; switch (check_bencoding_dictionary_entry(scanner, &value)) { case BENCODING_TOKEN_PATH: state = parse_bencoding_file_path(scanner, path); if (state != BITTORRENT_STATE_OK) return state; skip_scanner_token(scanner); break; case BENCODING_TOKEN_LENGTH: file.length = parse_bencoding_integer(value); skip_scanner_token(scanner); break; case BENCODING_TOKEN_MD5SUM: if (value->length != sizeof(md5_digest_hex_T)) return BITTORRENT_STATE_ERROR; memcpy(file.md5sum, value->string, value->length); skip_scanner_token(scanner); break; case BENCODING_TOKEN_END: skip_scanner_token(scanner); return add_bittorrent_file(meta, path->source, &file);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?