bencoding.c

来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 1,000 行 · 第 1/2 页

C
1,000
字号
		case BENCODING_TOKEN_ERROR:			return BITTORRENT_STATE_ERROR;		case BENCODING_TOKEN_NONE:		default:			skip_bencoding_tokens(scanner);		}	}	return BITTORRENT_STATE_ERROR;}/* Parse a list of file dictionaries. */static enum bittorrent_stateparse_bencoding_files_list(struct bittorrent_meta *meta, struct scanner *scanner){	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);		struct string path;		enum bittorrent_state state;		if (!token) break;		if (token->type == BENCODING_TOKEN_END) {			skip_scanner_token(scanner);			return BITTORRENT_STATE_OK;		}		if (token->type != BENCODING_TOKEN_DICTIONARY)			return BITTORRENT_STATE_ERROR;		/* Allocating and freeing the path string here makes error		 * handling so much easier in parse_bencoding_file_dictionary()		 * because it can return right away. */		if (!init_string(&path))			return BITTORRENT_STATE_OUT_OF_MEM;		state = parse_bencoding_file_dictionary(meta, scanner, &path);		done_string(&path);		if (state != BITTORRENT_STATE_OK)			return state;	}	return BITTORRENT_STATE_ERROR;}/* Parse the info dictionary which contains file and piece infomation. */static enum bittorrent_stateparse_bencoding_info_dictionary(struct bittorrent_meta *meta,				struct scanner *scanner){	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;		int malicious;		off_t length;		switch (check_bencoding_dictionary_entry(scanner, &value)) {		case BENCODING_TOKEN_NAME:			meta->name = normalize_bencoding_path(value->string,							      value->length,							      &malicious);			if (!meta->name) return BITTORRENT_STATE_OUT_OF_MEM;			if (malicious)				meta->malicious_paths = malicious;			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_PIECES:			/* The piece hash must be a multiple of the SHA digest			 * length. */			if ((value->length % SHA_DIGEST_LENGTH) != 0)				return BITTORRENT_STATE_ERROR;			meta->pieces = value->length / SHA_DIGEST_LENGTH;			meta->piece_hash = memacpy(value->string, value->length);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_PIECE_LENGTH:			length = parse_bencoding_integer(value);			if (length < 0 || length >= INT_MAX)				return BITTORRENT_STATE_ERROR;			meta->piece_length = (uint32_t) length;			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_FILES:			meta->type = BITTORRENT_MULTI_FILE;			state = parse_bencoding_files_list(meta, scanner);			if (state != BITTORRENT_STATE_OK)				return state;			break;		case BENCODING_TOKEN_LENGTH:			file.length = parse_bencoding_integer(value);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_MD5SUM:			if (value->length != 32)				return BITTORRENT_STATE_ERROR;			memcpy(file.md5sum, value->string, value->length);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_END:			/* All file info was saved from the 'files' list. */			if (meta->type == BITTORRENT_MULTI_FILE)				return BITTORRENT_STATE_OK;			if (!meta->name)				return BITTORRENT_STATE_ERROR;			return add_bittorrent_file(meta, meta->name, &file);		case BENCODING_TOKEN_ERROR:			return BITTORRENT_STATE_ERROR;		case BENCODING_TOKEN_NONE:		default:			skip_bencoding_tokens(scanner);		}	}	/* Check if all requirements were met. */	return BITTORRENT_STATE_ERROR;}/* Validate that the bencoded metainfo file contained all the required fields * and that their values are sane. */static enum bittorrent_statecheck_bittorrent_metafile(struct bittorrent_meta *meta){	struct bittorrent_file *file;	off_t last_piece_length = 0;	off_t total_length = 0;	if (bittorrent_id_is_empty(meta->info_hash)	    || !meta->pieces	    || !meta->name	    || !meta->name[0]	    || !meta->piece_hash	    || !meta->piece_length	    || !meta->tracker_uris.size	    || list_empty(meta->files))		return BITTORRENT_STATE_ERROR;	/* FIXME: Should we also check if any two files have the same name? */	foreach (file, meta->files) {		if (file->length < 0 || !file->name)			return BITTORRENT_STATE_ERROR;		total_length += file->length;	}	last_piece_length = (off_t) total_length % meta->piece_length;	if (!last_piece_length)		last_piece_length = meta->piece_length;	meta->last_piece_length = (uint32_t) last_piece_length;	/* Check that the non-zero last_piece_length can be stored. */	if (meta->last_piece_length != last_piece_length)		return BITTORRENT_STATE_ERROR;	return BITTORRENT_STATE_OK;}enum bittorrent_stateparse_bittorrent_metafile(struct bittorrent_meta *meta, struct string *metafile){	struct scanner scanner;	memset(meta, 0, sizeof(*meta));	init_list(meta->files);	init_scanner(&scanner, &bencoding_scanner_info,		     metafile->source, metafile->source + metafile->length);	{		struct scanner_token *token = get_scanner_token(&scanner);		if (!token || token->type != BENCODING_TOKEN_DICTIONARY)			return BITTORRENT_STATE_ERROR;		skip_scanner_token(&scanner);	}	while (scanner_has_tokens(&scanner)) {		struct scanner_token *value;		switch (check_bencoding_dictionary_entry(&scanner, &value)) {		case BENCODING_TOKEN_ANNOUNCE:		{			unsigned char *value_string;			struct uri *uri;			value_string = memacpy(value->string, value->length);			skip_scanner_token(&scanner);			if (!value_string) break;			uri = get_uri(value_string, 0);			mem_free(value_string);			if (uri) {				add_to_uri_list(&meta->tracker_uris, uri);				done_uri(uri);			}			break;		}		case BENCODING_TOKEN_ANNOUNCE_LIST:			/* FIXME: Add to the tracker URI list, when/if multiple			 * trackers are/will be supported. */			skip_bencoding_tokens(&scanner);			break;		case BENCODING_TOKEN_INFO:		{			unsigned char *start = value->string;			struct scanner_token *token;			enum bittorrent_state state;			state = parse_bencoding_info_dictionary(meta, &scanner);			if (state != BITTORRENT_STATE_OK)				return BITTORRENT_STATE_ERROR;			token = get_scanner_token(&scanner);			assert(token && token->type == BENCODING_TOKEN_END);			/* Digest the dictionary to create the info hash. */			SHA1(start, token->string + token->length - start,			     meta->info_hash);			skip_scanner_token(&scanner);			break;		}		case BENCODING_TOKEN_COMMENT:			meta->comment = memacpy(value->string,						int_min(value->length, MAX_STR_LEN));			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_CREATION_DATE:			meta->creation_date = (time_t) parse_bencoding_integer(value);			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_CREATED_BY:			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_END:			/* Check if all requirements were met. */			return check_bittorrent_metafile(meta);		case BENCODING_TOKEN_ERROR:			return BITTORRENT_STATE_ERROR;		case BENCODING_TOKEN_NONE:		default:			skip_bencoding_tokens(&scanner);		}	}	return BITTORRENT_STATE_ERROR;}/* ************************************************************************** *//* Tracker response parsing: *//* ************************************************************************** */static enum bittorrent_stateparse_bencoding_peer_dictionary(struct bittorrent_connection *bittorrent,				struct scanner *scanner){	struct scanner_token ip;	bittorrent_id_T id;	/* Set to invalid value. */	int port = -1;	assert(get_scanner_token(scanner)->type == BENCODING_TOKEN_DICTIONARY);	skip_scanner_token(scanner);	memset(id, 0, sizeof(bittorrent_id_T));	memset(&ip, 0, sizeof(ip));	while (scanner_has_tokens(scanner)) {		struct scanner_token *value;		switch (check_bencoding_dictionary_entry(scanner, &value)) {		case BENCODING_TOKEN_IP:			copy_struct(&ip, value);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_PORT:			port = (int) parse_bencoding_integer(value);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_PEER_ID:			if (value->length != sizeof(bittorrent_id_T))				return BITTORRENT_STATE_ERROR;			memcpy(id, value->string, value->length);			skip_scanner_token(scanner);			break;		case BENCODING_TOKEN_END:			skip_scanner_token(scanner);			return add_peer_to_bittorrent_pool(bittorrent, id, port,							   ip.string, ip.length);		case BENCODING_TOKEN_ERROR:			return BITTORRENT_STATE_ERROR;		case BENCODING_TOKEN_NONE:		default:			skip_bencoding_tokens(scanner);		}	}	return BITTORRENT_STATE_ERROR;}static enum bittorrent_stateparse_bencoding_peers_list(struct bittorrent_connection *bittorrent,			   struct scanner *scanner){	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);		enum bittorrent_state state;		if (!token) break;		if (token->type == BENCODING_TOKEN_END)			return BITTORRENT_STATE_OK;		if (token->type != BENCODING_TOKEN_DICTIONARY)			return BITTORRENT_STATE_ERROR;		state = parse_bencoding_peer_dictionary(bittorrent, scanner);		if (state != BITTORRENT_STATE_OK)			return state;	}	return BITTORRENT_STATE_ERROR;}/* Parses the compact peer list format. It is a string made up of substrings of * length 6, where the first 4 bytes hold the IP address and the 2 last bytes * hold the port number. */static enum bittorrent_stateparse_bencoding_peers_string(struct bittorrent_connection *bittorrent,			     struct scanner *scanner){	struct scanner_token *token = get_scanner_token(scanner);	unsigned char *pos;	unsigned char *last_peer_info_start = token->string + token->length - 6;	enum bittorrent_state state = BITTORRENT_STATE_OK;	assert(get_scanner_token(scanner)->type == BENCODING_TOKEN_STRING);	for (pos = token->string; pos <= last_peer_info_start; pos += 6) {		/* Only IPv4 strings can occur in this format. */		unsigned char ip[INET_ADDRSTRLEN];		int iplen;		uint16_t port;		iplen = snprintf(ip, sizeof(ip), "%d.%d.%d.%d",				(int) pos[0], (int) pos[1],				(int) pos[2], (int) pos[3]);		memcpy(&port, pos + 4, sizeof(port));		port = ntohs(port);		state = add_peer_to_bittorrent_pool(bittorrent, NULL, port,						    ip, iplen);		if (state != BITTORRENT_STATE_OK)			break;	}	return state;}enum bittorrent_stateparse_bittorrent_tracker_response(struct bittorrent_connection *bittorrent,				  struct string *response){	struct scanner scanner;	init_scanner(&scanner, &bencoding_scanner_info,		     response->source, response->source + response->length);	{		struct scanner_token *token = get_scanner_token(&scanner);		if (!token || token->type != BENCODING_TOKEN_DICTIONARY)			return BITTORRENT_STATE_ERROR;		skip_scanner_token(&scanner);	}	while (scanner_has_tokens(&scanner)) {		struct scanner_token *value;		enum bittorrent_state state;		off_t integer;		switch (check_bencoding_dictionary_entry(&scanner, &value)) {		case BENCODING_TOKEN_FAILURE_REASON:			response->source = value->string;			response->length = value->length;			return BITTORRENT_STATE_REQUEST_FAILURE;		case BENCODING_TOKEN_INTERVAL:			bittorrent->tracker.interval = (int) parse_bencoding_integer(value);			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_COMPLETE:			integer = parse_bencoding_integer(value);			if (0 < integer && integer < INT_MAX)				bittorrent->complete = (uint32_t) integer;			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_INCOMPLETE:			integer = parse_bencoding_integer(value);			if (0 < integer && integer < INT_MAX)				bittorrent->incomplete = (uint32_t) integer;			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_PEERS:			/* There are two formats: the normal list and the more			 * compact string variant. */			switch (value->type) {			case BENCODING_TOKEN_LIST:				state = parse_bencoding_peers_list(bittorrent, &scanner);				if (state != BITTORRENT_STATE_OK)					return state;				assert(get_scanner_token(&scanner)				       && get_scanner_token(&scanner)->type						       == BENCODING_TOKEN_END);				break;			case BENCODING_TOKEN_STRING:				/* Parse peer list when using compact format. */				state = parse_bencoding_peers_string(bittorrent, &scanner);				if (state != BITTORRENT_STATE_OK)					return state;				assert(get_scanner_token(&scanner) == value);				break;			default:				return BITTORRENT_STATE_ERROR;			}			skip_scanner_token(&scanner);			break;		case BENCODING_TOKEN_END:			/* TODO: Check if all requirements were met. */			return BITTORRENT_STATE_OK;		case BENCODING_TOKEN_ERROR:			return BITTORRENT_STATE_ERROR;		case BENCODING_TOKEN_NONE:		default:			skip_bencoding_tokens(&scanner);		}	}	return BITTORRENT_STATE_ERROR;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?