1 /*
2 
3 Copyright (c) 2003-2018, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 #include "libtorrent/kademlia/msg.hpp"
34 #include "libtorrent/bdecode.hpp"
35 #include "libtorrent/entry.hpp"
36 
37 namespace libtorrent { namespace dht {
38 
verify_message_impl(bdecode_node const & message,span<key_desc_t const> desc,span<bdecode_node> ret,span<char> error)39 bool verify_message_impl(bdecode_node const& message, span<key_desc_t const> desc
40 	, span<bdecode_node> ret, span<char> error)
41 {
42 	TORRENT_ASSERT(desc.size() == ret.size());
43 
44 	auto const size = ret.size();
45 
46 	// get a non-root bdecode_node that still
47 	// points to the root. message should not be copied
48 	bdecode_node msg = message.non_owning();
49 
50 	// clear the return buffer
51 	for (int i = 0; i < size; ++i)
52 		ret[i].clear();
53 
54 	// when parsing child nodes, this is the stack
55 	// of bdecode_nodes to return to
56 	bdecode_node stack[5];
57 	int stack_ptr = -1;
58 
59 	if (msg.type() != bdecode_node::dict_t)
60 	{
61 		std::snprintf(error.data(), static_cast<std::size_t>(error.size()), "not a dictionary");
62 		return false;
63 	}
64 	++stack_ptr;
65 	stack[stack_ptr] = msg;
66 	for (int i = 0; i < size; ++i)
67 	{
68 		key_desc_t const& k = desc[i];
69 
70 		//		std::fprintf(stderr, "looking for %s in %s\n", k.name, print_entry(*msg).c_str());
71 
72 		ret[i] = msg.dict_find(k.name);
73 		// none_t means any type
74 		if (ret[i] && ret[i].type() != k.type && k.type != bdecode_node::none_t)
75 			ret[i].clear();
76 		if (!ret[i] && (k.flags & key_desc_t::optional) == 0)
77 		{
78 			// the key was not found, and it's not an optional key
79 			std::snprintf(error.data(), static_cast<std::size_t>(error.size()), "missing '%s' key", k.name);
80 			return false;
81 		}
82 
83 		if (k.size > 0
84 			&& ret[i]
85 			&& k.type == bdecode_node::string_t)
86 		{
87 			bool const invalid = (k.flags & key_desc_t::size_divisible)
88 				? (ret[i].string_length() % k.size) != 0
89 				: ret[i].string_length() != k.size;
90 
91 			if (invalid)
92 			{
93 				// the string was not of the required size
94 				ret[i].clear();
95 				if ((k.flags & key_desc_t::optional) == 0)
96 				{
97 					std::snprintf(error.data(), static_cast<std::size_t>(error.size())
98 						, "invalid value for '%s'", k.name);
99 					return false;
100 				}
101 			}
102 		}
103 		if (k.flags & key_desc_t::parse_children)
104 		{
105 			TORRENT_ASSERT(k.type == bdecode_node::dict_t);
106 
107 			if (ret[i])
108 			{
109 				++stack_ptr;
110 				TORRENT_ASSERT(stack_ptr < int(sizeof(stack) / sizeof(stack[0])));
111 				msg = ret[i];
112 				stack[stack_ptr] = msg;
113 			}
114 			else
115 			{
116 				// skip all children
117 				while (i < size && (desc[i].flags & key_desc_t::last_child) == 0) ++i;
118 				// if this assert is hit, desc is incorrect
119 				TORRENT_ASSERT(i < size);
120 			}
121 		}
122 		else if (k.flags & key_desc_t::last_child)
123 		{
124 			TORRENT_ASSERT(stack_ptr > 0);
125 			// this can happen if the specification passed
126 			// in is unbalanced. i.e. contain more last_child
127 			// nodes than parse_children
128 			if (stack_ptr == 0) return false;
129 			--stack_ptr;
130 			msg = stack[stack_ptr];
131 		}
132 	}
133 	return true;
134 }
135 
136 } }
137