diff options
author | iphydf <iphydf@users.noreply.github.com> | 2016-10-01 00:26:52 +0100 |
---|---|---|
committer | iphydf <iphydf@users.noreply.github.com> | 2016-10-01 02:13:34 +0100 |
commit | c037100747a1a224160cb12defb600ddfe1ba927 (patch) | |
tree | 8332a7d288116b802866e1ed98d1b950425206f7 /testing | |
parent | 1977d56caaff40ea9bbf6754b69bec9539a5a969 (diff) |
Import the hstox SUT interface from hstox.
We'll maintain it in the c-toxcore repo, where it belongs.
Diffstat (limited to 'testing')
-rw-r--r-- | testing/hstox/.gitignore | 8 | ||||
-rw-r--r-- | testing/hstox/Makefile | 97 | ||||
-rw-r--r-- | testing/hstox/binary_decode.c | 224 | ||||
-rw-r--r-- | testing/hstox/binary_encode.c | 252 | ||||
-rw-r--r-- | testing/hstox/byteswap.h | 6 | ||||
-rw-r--r-- | testing/hstox/driver.c | 244 | ||||
-rw-r--r-- | testing/hstox/driver.h | 20 | ||||
-rw-r--r-- | testing/hstox/errors.h | 11 | ||||
-rw-r--r-- | testing/hstox/fuzz_main.c | 23 | ||||
-rw-r--r-- | testing/hstox/methods.c | 147 | ||||
-rw-r--r-- | testing/hstox/methods.h | 47 | ||||
-rw-r--r-- | testing/hstox/packet_kinds.c | 48 | ||||
-rw-r--r-- | testing/hstox/packet_kinds.h | 5 | ||||
-rw-r--r-- | testing/hstox/test-inputs/.gitignore | 1 | ||||
-rw-r--r-- | testing/hstox/test_main.c | 72 | ||||
-rw-r--r-- | testing/hstox/toxcore-sut.hs | 31 | ||||
-rw-r--r-- | testing/hstox/util.c | 74 | ||||
-rw-r--r-- | testing/hstox/util.h | 27 |
18 files changed, 1337 insertions, 0 deletions
diff --git a/testing/hstox/.gitignore b/testing/hstox/.gitignore new file mode 100644 index 00000000..6fe429cf --- /dev/null +++ b/testing/hstox/.gitignore | |||
@@ -0,0 +1,8 @@ | |||
1 | /*.dSYM | ||
2 | /*_main-* | ||
3 | /test-findings | ||
4 | !/Makefile | ||
5 | *.gcno | ||
6 | *.gcda | ||
7 | *.hi | ||
8 | *.o | ||
diff --git a/testing/hstox/Makefile b/testing/hstox/Makefile new file mode 100644 index 00000000..ec02719c --- /dev/null +++ b/testing/hstox/Makefile | |||
@@ -0,0 +1,97 @@ | |||
1 | # Don't bind to a CPU core. afl-fuzz doesn't release them again. | ||
2 | export AFL_NO_AFFINITY := 1 | ||
3 | |||
4 | CFLAGS := \ | ||
5 | -std=gnu99 \ | ||
6 | -Wall \ | ||
7 | -g3 \ | ||
8 | -DCONFIGURED=1 \ | ||
9 | -Imsgpack-c/include \ | ||
10 | -I../../toxcore \ | ||
11 | |||
12 | LDFLAGS := -pthread -lsodium | ||
13 | ifeq ($(shell echo 'int main(){}' | $(CC) -lrt -xc -o /dev/null - && echo true),true) | ||
14 | LDFLAGS += -lrt | ||
15 | endif | ||
16 | |||
17 | SOURCES := $(filter-out %_main.c,$(wildcard *.c msgpack-c/src/*.c ../../toxcore/*.c)) | ||
18 | HEADERS := $(shell find . -name "*.h") | ||
19 | |||
20 | ifeq ($(wildcard test-findings/master/fuzzer_stats),) | ||
21 | TEST_INPUTS := test-inputs | ||
22 | else | ||
23 | TEST_INPUTS := - | ||
24 | endif | ||
25 | |||
26 | # All the possible test flavours. | ||
27 | PROGRAMS := \ | ||
28 | test_main-cov \ | ||
29 | test_main-cov-asan \ | ||
30 | test_main-vanilla \ | ||
31 | test_main-vanilla-asan | ||
32 | |||
33 | ifneq ($(shell which afl-clang),) | ||
34 | AFL_CLANG := afl-clang | ||
35 | endif | ||
36 | |||
37 | ifneq ($(shell which afl-clang-fast),) | ||
38 | AFL_CLANG := afl-clang-fast | ||
39 | endif | ||
40 | |||
41 | ifneq ($(AFL_CLANG),) | ||
42 | PROGRAMS += \ | ||
43 | fuzz_main-afl \ | ||
44 | fuzz_main-afl-asan | ||
45 | endif | ||
46 | |||
47 | all: $(PROGRAMS) | ||
48 | |||
49 | check: test_main-sut | ||
50 | ./$< ./$< | ||
51 | |||
52 | master: fuzz_main-afl test-findings | ||
53 | afl-fuzz -i$(TEST_INPUTS) -o test-findings -M master ./$< | ||
54 | |||
55 | slave%: fuzz_main-afl test-findings | ||
56 | afl-fuzz -i test-inputs -o test-findings -S slave$* ./$< | ||
57 | |||
58 | %-afl: %.c $(SOURCES) $(HEADERS) | ||
59 | $(AFL_CLANG) $(CFLAGS) -O3 $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
60 | |||
61 | %-afl-asan: %.c $(SOURCES) $(HEADERS) | ||
62 | $(AFL_CLANG) $(CFLAGS) -O3 -fsanitize=address $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
63 | |||
64 | %-cov: %.c $(SOURCES) $(HEADERS) | ||
65 | $(CC) $(CFLAGS) -fprofile-arcs -ftest-coverage $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
66 | |||
67 | %-cov-asan: %.c $(SOURCES) $(HEADERS) | ||
68 | $(CC) $(CFLAGS) -fprofile-arcs -ftest-coverage -fsanitize=address $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
69 | |||
70 | %-vanilla: %.c $(SOURCES) $(HEADERS) | ||
71 | $(CC) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
72 | |||
73 | %-vanilla-asan: %.c $(SOURCES) $(HEADERS) | ||
74 | $(CC) $(CFLAGS) -fsanitize=address $(filter %.c,$^) $(LDFLAGS) -o $@ | ||
75 | |||
76 | %-sut: toxcore-sut.hs %.c $(SOURCES) $(HEADERS) | ||
77 | ghc -package hstox $< \ | ||
78 | $(filter %.c,$^) \ | ||
79 | $(foreach f,$(CFLAGS),-optc $f) \ | ||
80 | $(foreach f,$(LDFLAGS),-optl $f) \ | ||
81 | -o $@ | ||
82 | |||
83 | ifneq ($(wildcard /Volumes),) | ||
84 | test-findings: /Volumes/RAM\ Disk | ||
85 | mkdir -p "$</$@" | ||
86 | ln -sf "$</$@" $@ | ||
87 | |||
88 | /Volumes/RAM\ Disk: | ||
89 | diskutil erasevolume HFS+ 'RAM Disk' `hdiutil attach -nomount ram://204800` | ||
90 | else | ||
91 | test-findings: /dev/shm | ||
92 | mkdir -p "$</$@" | ||
93 | test -d $@ || ln -sf "$</$@" $@ | ||
94 | endif | ||
95 | |||
96 | clean: | ||
97 | rm -rf $(wildcard *_main-* *.dSYM *.gcno *.gcda *.o *.hi test-findings/*) | ||
diff --git a/testing/hstox/binary_decode.c b/testing/hstox/binary_decode.c new file mode 100644 index 00000000..aa4d7935 --- /dev/null +++ b/testing/hstox/binary_decode.c | |||
@@ -0,0 +1,224 @@ | |||
1 | #include "methods.h" | ||
2 | |||
3 | #include "byteswap.h" | ||
4 | #include "packet_kinds.h" | ||
5 | |||
6 | #include <DHT.h> | ||
7 | |||
8 | METHOD(bin, Binary_decode, CipherText) | ||
9 | { | ||
10 | uint64_t length; | ||
11 | uint64_t tmp; | ||
12 | |||
13 | SUCCESS { | ||
14 | memcpy(&tmp, args.ptr, sizeof(uint64_t)); | ||
15 | length = be64toh(tmp); | ||
16 | |||
17 | if (args.size >= sizeof(uint64_t) && args.size == length + sizeof(uint64_t)) | ||
18 | { | ||
19 | msgpack_pack_bin(res, args.size - sizeof(uint64_t)); | ||
20 | msgpack_pack_bin_body(res, args.ptr + sizeof(uint64_t), args.size - sizeof(uint64_t)); | ||
21 | } else { | ||
22 | msgpack_pack_nil(res); | ||
23 | } | ||
24 | } | ||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | METHOD(bin, Binary_decode, DhtPacket) | ||
29 | { | ||
30 | return pending; | ||
31 | } | ||
32 | |||
33 | METHOD(bin, Binary_decode, HostAddress) | ||
34 | { | ||
35 | return pending; | ||
36 | } | ||
37 | |||
38 | METHOD(bin, Binary_decode, Word64) | ||
39 | { | ||
40 | return pending; | ||
41 | } | ||
42 | |||
43 | METHOD(bin, Binary_decode, Key_PublicKey) | ||
44 | { | ||
45 | return pending; | ||
46 | } | ||
47 | |||
48 | METHOD(bin, Binary_decode, KeyPair) | ||
49 | { | ||
50 | SUCCESS { | ||
51 | if (args.size != 64) | ||
52 | { | ||
53 | msgpack_pack_nil(res); | ||
54 | } else { | ||
55 | msgpack_pack_array(res, 2); | ||
56 | msgpack_pack_bin(res, 32); | ||
57 | msgpack_pack_bin_body(res, args.ptr, 32); | ||
58 | msgpack_pack_bin(res, 32); | ||
59 | msgpack_pack_bin_body(res, args.ptr + 32, 32); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | METHOD(bin, Binary_decode, NodeInfo) | ||
67 | { | ||
68 | uint16_t data_processed; | ||
69 | Node_format node; | ||
70 | int len = unpack_nodes(&node, 1, &data_processed, (uint8_t const *)args.ptr, args.size, 1); | ||
71 | |||
72 | bool ip6_node = node.ip_port.ip.family == AF_INET6 || node.ip_port.ip.family == TCP_INET6; | ||
73 | bool tcp = node.ip_port.ip.family == TCP_INET || node.ip_port.ip.family == TCP_INET6; | ||
74 | |||
75 | uint16_t port = ntohs(node.ip_port.port); | ||
76 | uint32_t ip4 = ntohl(node.ip_port.ip.ip4.uint32); | ||
77 | uint32_t ip6_0 = ntohl(node.ip_port.ip.ip6.uint32[0]); | ||
78 | uint32_t ip6_1 = ntohl(node.ip_port.ip.ip6.uint32[1]); | ||
79 | uint32_t ip6_2 = ntohl(node.ip_port.ip.ip6.uint32[2]); | ||
80 | uint32_t ip6_3 = ntohl(node.ip_port.ip.ip6.uint32[3]); | ||
81 | |||
82 | SUCCESS { | ||
83 | if (len > 0 && data_processed > 0 && data_processed == args.size) | ||
84 | { | ||
85 | msgpack_pack_array(res, 3); | ||
86 | msgpack_pack_uint8(res, tcp); | ||
87 | msgpack_pack_array(res, 2); | ||
88 | msgpack_pack_array(res, 2); | ||
89 | msgpack_pack_uint8(res, ip6_node); | ||
90 | |||
91 | if (ip6_node) { | ||
92 | msgpack_pack_array(res, 4); | ||
93 | msgpack_pack_uint32(res, ip6_0); | ||
94 | msgpack_pack_uint32(res, ip6_1); | ||
95 | msgpack_pack_uint32(res, ip6_2); | ||
96 | msgpack_pack_uint32(res, ip6_3); | ||
97 | } else { | ||
98 | msgpack_pack_uint32(res, ip4); | ||
99 | } | ||
100 | |||
101 | msgpack_pack_uint16(res, port); | ||
102 | msgpack_pack_bin(res, crypto_box_PUBLICKEYBYTES); | ||
103 | msgpack_pack_bin_body(res, &node.public_key, crypto_box_PUBLICKEYBYTES); | ||
104 | } else { | ||
105 | msgpack_pack_nil(res); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | METHOD(bin, Binary_decode, NodesRequest) | ||
113 | { | ||
114 | return pending; | ||
115 | } | ||
116 | |||
117 | METHOD(bin, Binary_decode, NodesResponse) | ||
118 | { | ||
119 | return pending; | ||
120 | } | ||
121 | |||
122 | METHOD(bin, Binary_decode, Packet_Word64) | ||
123 | { | ||
124 | return pending; | ||
125 | } | ||
126 | |||
127 | METHOD(bin, Binary_decode, PacketKind) | ||
128 | { | ||
129 | SUCCESS { | ||
130 | if (args.size != 1) | ||
131 | { | ||
132 | msgpack_pack_nil(res); | ||
133 | } else { | ||
134 | uint8_t kind = args.ptr[0]; | ||
135 | size_t i; | ||
136 | |||
137 | for (i = 0; i < sizeof packet_kinds / sizeof *packet_kinds; i++) | ||
138 | { | ||
139 | if (packet_kinds[i] == kind) { | ||
140 | msgpack_pack_fix_uint8(res, i); | ||
141 | return 0; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // Packet kind not found => error. | ||
146 | msgpack_pack_nil(res); | ||
147 | } | ||
148 | } | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | METHOD(bin, Binary_decode, PingPacket) | ||
153 | { | ||
154 | return pending; | ||
155 | } | ||
156 | |||
157 | METHOD(bin, Binary_decode, PlainText) | ||
158 | { | ||
159 | return pending; | ||
160 | } | ||
161 | |||
162 | METHOD(bin, Binary_decode, PortNumber) | ||
163 | { | ||
164 | SUCCESS { | ||
165 | if (args.size == 2) | ||
166 | { | ||
167 | uint16_t tmp; | ||
168 | memcpy(&tmp, args.ptr, 2); | ||
169 | uint16_t port = ntohs(tmp); | ||
170 | msgpack_pack_uint16(res, port); | ||
171 | } else { | ||
172 | msgpack_pack_nil(res); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | METHOD(bin, Binary_decode, RpcPacket_Word64) | ||
180 | { | ||
181 | return pending; | ||
182 | } | ||
183 | |||
184 | METHOD(bin, Binary_decode, SocketAddress) | ||
185 | { | ||
186 | return pending; | ||
187 | } | ||
188 | |||
189 | METHOD(bin, Binary_decode, TransportProtocol) | ||
190 | { | ||
191 | return pending; | ||
192 | } | ||
193 | |||
194 | METHOD(array, Binary, decode) | ||
195 | { | ||
196 | CHECK_SIZE(args, 2); | ||
197 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_STR); | ||
198 | CHECK_TYPE(args.ptr[1], MSGPACK_OBJECT_BIN); | ||
199 | |||
200 | msgpack_object_str type = args.ptr[0].via.str; | ||
201 | #define DISPATCH(TYPE) \ | ||
202 | if (type.size == sizeof #TYPE - 1 && method_cmp(type.ptr, #TYPE, type.size) == 0) \ | ||
203 | return Binary_decode_##TYPE(args.ptr[1].via.bin, res) | ||
204 | DISPATCH(CipherText); | ||
205 | DISPATCH(DhtPacket); | ||
206 | DISPATCH(HostAddress); | ||
207 | DISPATCH(Word64); | ||
208 | DISPATCH(Key_PublicKey); | ||
209 | DISPATCH(KeyPair); | ||
210 | DISPATCH(NodeInfo); | ||
211 | DISPATCH(NodesRequest); | ||
212 | DISPATCH(NodesResponse); | ||
213 | DISPATCH(Packet_Word64); | ||
214 | DISPATCH(PacketKind); | ||
215 | DISPATCH(PingPacket); | ||
216 | DISPATCH(PlainText); | ||
217 | DISPATCH(PortNumber); | ||
218 | DISPATCH(RpcPacket_Word64); | ||
219 | DISPATCH(SocketAddress); | ||
220 | DISPATCH(TransportProtocol); | ||
221 | #undef DISPATCH | ||
222 | |||
223 | return unimplemented; | ||
224 | } | ||
diff --git a/testing/hstox/binary_encode.c b/testing/hstox/binary_encode.c new file mode 100644 index 00000000..a39533c0 --- /dev/null +++ b/testing/hstox/binary_encode.c | |||
@@ -0,0 +1,252 @@ | |||
1 | #include "methods.h" | ||
2 | |||
3 | #include "byteswap.h" | ||
4 | #include "packet_kinds.h" | ||
5 | |||
6 | #include <DHT.h> | ||
7 | |||
8 | METHOD(bin, Binary_encode, CipherText) | ||
9 | { | ||
10 | uint64_t length = be64toh(args.size); | ||
11 | |||
12 | SUCCESS { | ||
13 | msgpack_pack_bin(res, sizeof(uint64_t) + args.size); | ||
14 | msgpack_pack_bin_body(res, &length, sizeof(uint64_t)); | ||
15 | msgpack_pack_bin_body(res, args.ptr, args.size); | ||
16 | } | ||
17 | return 0; | ||
18 | } | ||
19 | |||
20 | METHOD(array, Binary_encode, DhtPacket) | ||
21 | { | ||
22 | return pending; | ||
23 | } | ||
24 | |||
25 | METHOD(array, Binary_encode, HostAddress) | ||
26 | { | ||
27 | return pending; | ||
28 | } | ||
29 | |||
30 | METHOD(u64, Binary_encode, Word64) | ||
31 | { | ||
32 | return pending; | ||
33 | } | ||
34 | |||
35 | METHOD(bin, Binary_encode, Key_PublicKey) | ||
36 | { | ||
37 | return pending; | ||
38 | } | ||
39 | |||
40 | METHOD(array, Binary_encode, KeyPair) | ||
41 | { | ||
42 | CHECK_SIZE(args, 2); | ||
43 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_BIN); | ||
44 | CHECK_TYPE(args.ptr[1], MSGPACK_OBJECT_BIN); | ||
45 | |||
46 | msgpack_object_bin secret_key = args.ptr[0].via.bin; | ||
47 | msgpack_object_bin public_key = args.ptr[1].via.bin; | ||
48 | |||
49 | CHECK_SIZE(secret_key, 32); | ||
50 | CHECK_SIZE(public_key, 32); | ||
51 | |||
52 | SUCCESS { | ||
53 | uint8_t data[64]; | ||
54 | memcpy(data, secret_key.ptr, 32); | ||
55 | memcpy(data + 32, public_key.ptr, 32); | ||
56 | msgpack_pack_bin(res, 64); | ||
57 | msgpack_pack_bin_body(res, data, 64); | ||
58 | } | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | #define PACKED_NODE_SIZE_IP6 (1 + SIZE_IP6 + sizeof(uint16_t) + crypto_box_PUBLICKEYBYTES) | ||
64 | METHOD(array, Binary_encode, NodeInfo) | ||
65 | { | ||
66 | CHECK_SIZE(args, 3); | ||
67 | |||
68 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_POSITIVE_INTEGER); | ||
69 | uint64_t protocol = args.ptr[0].via.u64; | ||
70 | |||
71 | CHECK_TYPE(args.ptr[1], MSGPACK_OBJECT_ARRAY); | ||
72 | msgpack_object_array address = args.ptr[1].via.array; | ||
73 | |||
74 | CHECK_SIZE(address, 2); | ||
75 | CHECK_TYPE(address.ptr[0], MSGPACK_OBJECT_ARRAY); | ||
76 | msgpack_object_array host_address = address.ptr[0].via.array; | ||
77 | |||
78 | CHECK_TYPE(address.ptr[1], MSGPACK_OBJECT_POSITIVE_INTEGER); | ||
79 | uint64_t port_number = address.ptr[1].via.u64; | ||
80 | |||
81 | CHECK_SIZE(host_address, 2); | ||
82 | CHECK_TYPE(host_address.ptr[0], MSGPACK_OBJECT_POSITIVE_INTEGER); | ||
83 | uint64_t address_family = host_address.ptr[0].via.u64; | ||
84 | |||
85 | CHECK_TYPE(args.ptr[2], MSGPACK_OBJECT_BIN); | ||
86 | msgpack_object_bin public_key = args.ptr[2].via.bin; | ||
87 | |||
88 | CHECK_SIZE(public_key, crypto_box_PUBLICKEYBYTES); | ||
89 | |||
90 | IP_Port ipp; | ||
91 | ipp.port = htons(port_number); | ||
92 | |||
93 | switch (address_family) { | ||
94 | case 0: { | ||
95 | /* IPv4*/ | ||
96 | if (protocol == 1) { | ||
97 | ipp.ip.family = TCP_INET; | ||
98 | } else { | ||
99 | ipp.ip.family = AF_INET; | ||
100 | } | ||
101 | |||
102 | CHECK_TYPE(host_address.ptr[1], MSGPACK_OBJECT_POSITIVE_INTEGER); | ||
103 | uint64_t addr = host_address.ptr[1].via.u64; | ||
104 | |||
105 | ipp.ip.ip4.uint32 = htonl(addr); | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | case 1: { | ||
110 | /* IPv6 */ | ||
111 | if (protocol == 1) { | ||
112 | ipp.ip.family = TCP_INET6; | ||
113 | } else { | ||
114 | ipp.ip.family = AF_INET6; | ||
115 | } | ||
116 | |||
117 | CHECK_TYPE(host_address.ptr[1], MSGPACK_OBJECT_ARRAY); | ||
118 | msgpack_object_array addr = host_address.ptr[1].via.array; | ||
119 | |||
120 | int i; | ||
121 | |||
122 | for (i = 0; i < 4; ++i) { | ||
123 | CHECK_TYPE(addr.ptr[i], MSGPACK_OBJECT_POSITIVE_INTEGER); | ||
124 | uint64_t component = addr.ptr[i].via.u64; | ||
125 | ipp.ip.ip6.uint32[i] = htonl(component); | ||
126 | } | ||
127 | |||
128 | break; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | Node_format node; | ||
133 | node.ip_port = ipp; | ||
134 | memcpy(&node.public_key, public_key.ptr, crypto_box_PUBLICKEYBYTES); | ||
135 | |||
136 | /* We assume IP6 because it's bigger */ | ||
137 | uint8_t packed_node[PACKED_NODE_SIZE_IP6]; | ||
138 | |||
139 | int len = pack_nodes(packed_node, sizeof packed_node, &node, 1); | ||
140 | |||
141 | if (len < 0) { | ||
142 | return failure; | ||
143 | } | ||
144 | |||
145 | SUCCESS { | ||
146 | msgpack_pack_bin(res, len); | ||
147 | msgpack_pack_bin_body(res, packed_node, len); | ||
148 | } | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | METHOD(bin, Binary_encode, NodesRequest) | ||
153 | { | ||
154 | return pending; | ||
155 | } | ||
156 | |||
157 | METHOD(array, Binary_encode, NodesResponse) | ||
158 | { | ||
159 | return pending; | ||
160 | } | ||
161 | |||
162 | METHOD(array, Binary_encode, Packet_Word64) | ||
163 | { | ||
164 | return pending; | ||
165 | } | ||
166 | |||
167 | METHOD(u64, Binary_encode, PacketKind) | ||
168 | { | ||
169 | CHECK(args < sizeof packet_kinds / sizeof * packet_kinds); | ||
170 | |||
171 | SUCCESS { | ||
172 | uint8_t data[] = {packet_kinds[args]}; | ||
173 | msgpack_pack_bin(res, sizeof data); | ||
174 | msgpack_pack_bin_body(res, data, sizeof data); | ||
175 | } | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | METHOD(u64, Binary_encode, PingPacket) | ||
180 | { | ||
181 | return pending; | ||
182 | } | ||
183 | |||
184 | METHOD(bin, Binary_encode, PlainText) | ||
185 | { | ||
186 | return pending; | ||
187 | } | ||
188 | |||
189 | METHOD(u64, Binary_encode, PortNumber) | ||
190 | { | ||
191 | SUCCESS { | ||
192 | if (args <= UINT16_MAX) | ||
193 | { | ||
194 | uint16_t port = ntohs(args); | ||
195 | msgpack_pack_bin(res, 2); | ||
196 | msgpack_pack_bin_body(res, &port, 2); | ||
197 | } else { | ||
198 | msgpack_pack_nil(res); | ||
199 | } | ||
200 | } | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | METHOD(array, Binary_encode, RpcPacket_Word64) | ||
205 | { | ||
206 | return pending; | ||
207 | } | ||
208 | |||
209 | METHOD(array, Binary_encode, SocketAddress) | ||
210 | { | ||
211 | return pending; | ||
212 | } | ||
213 | |||
214 | METHOD(u64, Binary_encode, TransportProtocol) | ||
215 | { | ||
216 | return pending; | ||
217 | } | ||
218 | |||
219 | METHOD(array, Binary, encode) | ||
220 | { | ||
221 | CHECK_SIZE(args, 2); | ||
222 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_STR); | ||
223 | |||
224 | msgpack_object_str type = args.ptr[0].via.str; | ||
225 | #define DISPATCH(TYPE, UTYPE, LTYPE) \ | ||
226 | do { \ | ||
227 | if (type.size == sizeof #TYPE - 1 && method_cmp(type.ptr, #TYPE, type.size) == 0) { \ | ||
228 | CHECK_TYPE(args.ptr[1], MSGPACK_OBJECT_##UTYPE); \ | ||
229 | return Binary_encode_##TYPE(args.ptr[1].via.LTYPE, res); \ | ||
230 | } \ | ||
231 | } while (0) | ||
232 | DISPATCH(CipherText, BIN, bin); | ||
233 | DISPATCH(DhtPacket, ARRAY, array); | ||
234 | DISPATCH(HostAddress, ARRAY, array); | ||
235 | DISPATCH(Word64, POSITIVE_INTEGER, u64); | ||
236 | DISPATCH(Key_PublicKey, BIN, bin); | ||
237 | DISPATCH(KeyPair, ARRAY, array); | ||
238 | DISPATCH(NodeInfo, ARRAY, array); | ||
239 | DISPATCH(NodesRequest, BIN, bin); | ||
240 | DISPATCH(NodesResponse, ARRAY, array); | ||
241 | DISPATCH(Packet_Word64, ARRAY, array); | ||
242 | DISPATCH(PacketKind, POSITIVE_INTEGER, u64); | ||
243 | DISPATCH(PingPacket, POSITIVE_INTEGER, u64); | ||
244 | DISPATCH(PlainText, BIN, bin); | ||
245 | DISPATCH(PortNumber, POSITIVE_INTEGER, u64); | ||
246 | DISPATCH(RpcPacket_Word64, ARRAY, array); | ||
247 | DISPATCH(SocketAddress, ARRAY, array); | ||
248 | DISPATCH(TransportProtocol, POSITIVE_INTEGER, u64); | ||
249 | #undef DISPATCH | ||
250 | |||
251 | return unimplemented; | ||
252 | } | ||
diff --git a/testing/hstox/byteswap.h b/testing/hstox/byteswap.h new file mode 100644 index 00000000..b5a58f66 --- /dev/null +++ b/testing/hstox/byteswap.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifdef __APPLE__ | ||
2 | #include <libkern/OSByteOrder.h> | ||
3 | |||
4 | #define htobe64(x) OSSwapHostToBigInt64(x) | ||
5 | #define be64toh(x) OSSwapBigToHostInt64(x) | ||
6 | #endif | ||
diff --git a/testing/hstox/driver.c b/testing/hstox/driver.c new file mode 100644 index 00000000..81773190 --- /dev/null +++ b/testing/hstox/driver.c | |||
@@ -0,0 +1,244 @@ | |||
1 | #include <errno.h> | ||
2 | #include <fcntl.h> | ||
3 | #include <netdb.h> | ||
4 | #include <signal.h> | ||
5 | #include <signal.h> | ||
6 | #include <stdarg.h> | ||
7 | #include <stdio.h> | ||
8 | #include <string.h> | ||
9 | #include <sys/socket.h> | ||
10 | #include <sys/types.h> | ||
11 | #include <unistd.h> | ||
12 | |||
13 | #include "driver.h" | ||
14 | #include "errors.h" | ||
15 | #include "methods.h" | ||
16 | #include "util.h" | ||
17 | |||
18 | #include <sodium.h> | ||
19 | |||
20 | static void handle_interrupt(int signum) | ||
21 | { | ||
22 | printf("Caught signal %d; exiting cleanly.\n", signum); | ||
23 | exit(0); | ||
24 | } | ||
25 | |||
26 | static int protocol_error(msgpack_packer *pk, char const *fmt, ...) | ||
27 | { | ||
28 | msgpack_pack_array(pk, 4); // 4 elements in the array | ||
29 | msgpack_pack_uint8(pk, 1); // 1. type = response | ||
30 | // 2. We don't know the msgid, because the packet we received is not a valid | ||
31 | // msgpack-rpc packet. | ||
32 | msgpack_pack_uint64(pk, 0); | ||
33 | |||
34 | // 3. Error message. | ||
35 | va_list ap; | ||
36 | va_start(ap, fmt); | ||
37 | int res = msgpack_pack_vstringf(pk, fmt, ap); | ||
38 | va_end(ap); | ||
39 | |||
40 | // 4. No success result. | ||
41 | msgpack_pack_array(pk, 0); | ||
42 | |||
43 | return res; | ||
44 | } | ||
45 | |||
46 | static bool type_check(msgpack_packer *pk, msgpack_object req, int index, | ||
47 | msgpack_object_type type) | ||
48 | { | ||
49 | if (req.via.array.ptr[index].type != type) { | ||
50 | protocol_error(pk, "element %d should be %s, but is %s", index, type_name(type), | ||
51 | type_name(req.via.array.ptr[index].type)); | ||
52 | return false; | ||
53 | } | ||
54 | |||
55 | return true; | ||
56 | } | ||
57 | |||
58 | static int write_sample_input(msgpack_object req) | ||
59 | { | ||
60 | static unsigned int n; | ||
61 | |||
62 | char filename[256]; | ||
63 | msgpack_object_str name = req.via.array.ptr[2].via.str; | ||
64 | snprintf(filename, sizeof filename - name.size, "test-inputs/%04u-", n++); | ||
65 | |||
66 | assert(sizeof filename - strlen(filename) > name.size + 4); | ||
67 | memcpy(filename + strlen(filename) + name.size, ".mp", 4); | ||
68 | memcpy(filename + strlen(filename), name.ptr, name.size); | ||
69 | |||
70 | int fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | ||
71 | |||
72 | if (fd < 0) | ||
73 | // If we can't open the sample file, we just don't write it. | ||
74 | { | ||
75 | return E_OK; | ||
76 | } | ||
77 | |||
78 | check_return(E_WRITE, ftruncate(fd, 0)); | ||
79 | |||
80 | msgpack_sbuffer sbuf __attribute__((__cleanup__(msgpack_sbuffer_destroy))); | ||
81 | msgpack_sbuffer_init(&sbuf); | ||
82 | |||
83 | msgpack_packer pk; | ||
84 | msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); | ||
85 | |||
86 | msgpack_pack_object(&pk, req); | ||
87 | |||
88 | check_return(E_WRITE, write(fd, sbuf.data, sbuf.size)); | ||
89 | |||
90 | return E_OK; | ||
91 | } | ||
92 | |||
93 | static int handle_request(struct settings cfg, int write_fd, msgpack_object req) | ||
94 | { | ||
95 | msgpack_sbuffer sbuf __attribute__((__cleanup__(msgpack_sbuffer_destroy))); /* buffer */ | ||
96 | msgpack_sbuffer_init(&sbuf); /* initialize buffer */ | ||
97 | |||
98 | msgpack_packer pk; /* packer */ | ||
99 | msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); /* initialize packer */ | ||
100 | |||
101 | if (req.type != MSGPACK_OBJECT_ARRAY) { | ||
102 | protocol_error(&pk, "expected array, but got %s", type_name(req.type)); | ||
103 | } else if (req.via.array.size != 4) { | ||
104 | protocol_error(&pk, "array length should be 4, but is %d", req.via.array.size); | ||
105 | } else if (type_check(&pk, req, 0, MSGPACK_OBJECT_POSITIVE_INTEGER) && | ||
106 | type_check(&pk, req, 1, MSGPACK_OBJECT_POSITIVE_INTEGER) && | ||
107 | type_check(&pk, req, 2, MSGPACK_OBJECT_STR) && | ||
108 | type_check(&pk, req, 3, MSGPACK_OBJECT_ARRAY)) { | ||
109 | if (cfg.collect_samples) { | ||
110 | propagate(write_sample_input(req)); | ||
111 | } | ||
112 | |||
113 | uint64_t msgid = req.via.array.ptr[1].via.u64; | ||
114 | msgpack_object_str name = req.via.array.ptr[2].via.str; | ||
115 | msgpack_object_array args = req.via.array.ptr[3].via.array; | ||
116 | |||
117 | msgpack_pack_array(&pk, 4); // 4 elements in the array | ||
118 | msgpack_pack_uint8(&pk, 1); // 1. type = response | ||
119 | msgpack_pack_uint64(&pk, msgid); // 2. msgid | ||
120 | |||
121 | if (name.size == (sizeof "rpc.capabilities") - 1 && | ||
122 | memcmp(name.ptr, "rpc.capabilities", name.size) == 0) { | ||
123 | // 3. Error. | ||
124 | msgpack_pack_string(&pk, "Capabilities negiotiation not implemented"); | ||
125 | // 4. No result. | ||
126 | msgpack_pack_nil(&pk); | ||
127 | } else { | ||
128 | // if error is null, this writes 3. no error, and 4. result | ||
129 | char const *error = | ||
130 | call_method(name, args, &pk); | ||
131 | |||
132 | if (error) { | ||
133 | if (cfg.debug) { | ||
134 | printf("Error '%s' in request: ", error); | ||
135 | msgpack_object_print(stdout, req); | ||
136 | printf("\n"); | ||
137 | } | ||
138 | |||
139 | msgpack_pack_string(&pk, error); | ||
140 | msgpack_pack_array(&pk, 0); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | check_return(E_WRITE, write(write_fd, sbuf.data, sbuf.size)); | ||
146 | |||
147 | return E_OK; | ||
148 | } | ||
149 | |||
150 | int communicate(struct settings cfg, int read_fd, int write_fd) | ||
151 | { | ||
152 | msgpack_unpacker unp __attribute__((__cleanup__(msgpack_unpacker_destroy))); | ||
153 | msgpack_unpacker_init(&unp, 128); | ||
154 | |||
155 | while (true) { | ||
156 | char buf[64]; | ||
157 | int size = check_return(E_READ, read(read_fd, buf, sizeof buf)); | ||
158 | |||
159 | if (size == 0) { | ||
160 | break; | ||
161 | } | ||
162 | |||
163 | if (msgpack_unpacker_buffer_capacity(&unp) < size && | ||
164 | !msgpack_unpacker_reserve_buffer(&unp, size)) { | ||
165 | return E_NOMEM; | ||
166 | } | ||
167 | |||
168 | memcpy(msgpack_unpacker_buffer(&unp), buf, size); | ||
169 | msgpack_unpacker_buffer_consumed(&unp, size); | ||
170 | |||
171 | msgpack_unpacked req __attribute__((__cleanup__(msgpack_unpacked_destroy))); | ||
172 | msgpack_unpacked_init(&req); | ||
173 | |||
174 | switch (msgpack_unpacker_next(&unp, &req)) { | ||
175 | case MSGPACK_UNPACK_SUCCESS: | ||
176 | propagate(handle_request(cfg, write_fd, req.data)); | ||
177 | break; | ||
178 | |||
179 | case MSGPACK_UNPACK_EXTRA_BYTES: | ||
180 | printf("EXTRA_BYTES\n"); | ||
181 | break; | ||
182 | |||
183 | case MSGPACK_UNPACK_CONTINUE: | ||
184 | break; | ||
185 | |||
186 | case MSGPACK_UNPACK_PARSE_ERROR: | ||
187 | return E_PARSE; | ||
188 | |||
189 | case MSGPACK_UNPACK_NOMEM_ERROR: | ||
190 | return E_NOMEM; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | return E_OK; | ||
195 | } | ||
196 | |||
197 | static int closep(int *fd) | ||
198 | { | ||
199 | return close(*fd); | ||
200 | } | ||
201 | |||
202 | static int run_tests(struct settings cfg, int port) | ||
203 | { | ||
204 | int listen_fd __attribute__((__cleanup__(closep))) = 0; | ||
205 | listen_fd = check_return(E_SOCKET, socket(AF_INET, SOCK_STREAM, 0)); | ||
206 | check_return(E_SOCKET, setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &(int) { | ||
207 | 1 | ||
208 | }, sizeof(int))); | ||
209 | |||
210 | struct sockaddr_in servaddr; | ||
211 | servaddr.sin_family = AF_INET; | ||
212 | servaddr.sin_addr.s_addr = htons(INADDR_ANY); | ||
213 | servaddr.sin_port = htons(port); | ||
214 | |||
215 | check_return(E_BIND, bind(listen_fd, (struct sockaddr *)&servaddr, sizeof servaddr)); | ||
216 | check_return(E_LISTEN, listen(listen_fd, 10)); | ||
217 | |||
218 | while (true) { | ||
219 | int comm_fd __attribute__((__cleanup__(closep))) = 0; | ||
220 | comm_fd = check_return(E_ACCEPT, accept(listen_fd, NULL, NULL)); | ||
221 | propagate(communicate(cfg, comm_fd, comm_fd)); | ||
222 | } | ||
223 | |||
224 | return E_OK; | ||
225 | } | ||
226 | |||
227 | uint32_t network_main(struct settings cfg, uint16_t port, unsigned int timeout) | ||
228 | { | ||
229 | signal(SIGALRM, handle_interrupt); | ||
230 | signal(SIGINT, handle_interrupt); | ||
231 | check_return(E_SODIUM, sodium_init()); | ||
232 | |||
233 | // Kill the process after `timeout` seconds so we don't get lingering | ||
234 | // processes bound to the test port when something goes wrong with a test run. | ||
235 | alarm(timeout); | ||
236 | |||
237 | int result = run_tests(cfg, port); | ||
238 | |||
239 | if (result == E_OK) { | ||
240 | return E_OK; | ||
241 | } | ||
242 | |||
243 | return result | (errno << 8); | ||
244 | } | ||
diff --git a/testing/hstox/driver.h b/testing/hstox/driver.h new file mode 100644 index 00000000..219df8a1 --- /dev/null +++ b/testing/hstox/driver.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stdbool.h> | ||
4 | #include <stdint.h> | ||
5 | |||
6 | // Settings for the test runner. | ||
7 | struct settings { | ||
8 | // Print the msgpack object on test failure. | ||
9 | bool debug; | ||
10 | // Write test sample files into test-inputs/. These files, one per test | ||
11 | // method, are used to seed the fuzzer. | ||
12 | bool collect_samples; | ||
13 | }; | ||
14 | |||
15 | // Main loop communicating via read/write file descriptors. The two fds can be | ||
16 | // the same in case of a network socket. | ||
17 | int communicate(struct settings cfg, int read_fd, int write_fd); | ||
18 | |||
19 | // Open a TCP socket on the given port and start communicate(). | ||
20 | uint32_t network_main(struct settings cfg, uint16_t port, unsigned int timeout); | ||
diff --git a/testing/hstox/errors.h b/testing/hstox/errors.h new file mode 100644 index 00000000..42bf864a --- /dev/null +++ b/testing/hstox/errors.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #define E_OK 0 | ||
2 | #define E_NOMEM 1 | ||
3 | #define E_SOCKET 2 | ||
4 | #define E_BIND 3 | ||
5 | #define E_LISTEN 4 | ||
6 | #define E_ACCEPT 5 | ||
7 | #define E_PARSE 6 | ||
8 | #define E_OPEN 7 | ||
9 | #define E_READ 8 | ||
10 | #define E_WRITE 9 | ||
11 | #define E_SODIUM 10 | ||
diff --git a/testing/hstox/fuzz_main.c b/testing/hstox/fuzz_main.c new file mode 100644 index 00000000..ff66a166 --- /dev/null +++ b/testing/hstox/fuzz_main.c | |||
@@ -0,0 +1,23 @@ | |||
1 | #include "driver.h" | ||
2 | |||
3 | #include <unistd.h> | ||
4 | |||
5 | // The number of afl iterations before terminating the process and starting a | ||
6 | // new one. | ||
7 | // See https://github.com/mcarpenter/afl/blob/master/llvm_mode/README.llvm. | ||
8 | #define ITERATIONS 1000 | ||
9 | |||
10 | #ifdef __GLASGOW_HASKELL__ | ||
11 | #define main fuzz_main | ||
12 | #endif | ||
13 | int main(int argc, char **argv) | ||
14 | { | ||
15 | struct settings cfg = {false, false}; | ||
16 | #ifdef __AFL_LOOP | ||
17 | |||
18 | while (__AFL_LOOP(ITERATIONS)) | ||
19 | #endif | ||
20 | communicate(cfg, STDIN_FILENO, STDOUT_FILENO); | ||
21 | |||
22 | return 0; | ||
23 | } | ||
diff --git a/testing/hstox/methods.c b/testing/hstox/methods.c new file mode 100644 index 00000000..08ad50fa --- /dev/null +++ b/testing/hstox/methods.c | |||
@@ -0,0 +1,147 @@ | |||
1 | #include "methods.h" | ||
2 | |||
3 | #include "util.h" | ||
4 | |||
5 | #include <crypto_core.h> | ||
6 | #include <net_crypto.h> | ||
7 | |||
8 | char const *const failure = "Failure"; | ||
9 | char const *const pending = "Pending"; | ||
10 | char const *const unimplemented = "Unimplemented"; | ||
11 | |||
12 | METHOD(array, Box, encrypt) | ||
13 | { | ||
14 | return pending; | ||
15 | } | ||
16 | |||
17 | METHOD(array, Box, decrypt) | ||
18 | { | ||
19 | return pending; | ||
20 | } | ||
21 | |||
22 | METHOD(array, CombinedKey, precompute) | ||
23 | { | ||
24 | return pending; | ||
25 | } | ||
26 | |||
27 | METHOD(array, KeyPair, newKeyPair) | ||
28 | { | ||
29 | uint8_t key1[crypto_box_PUBLICKEYBYTES]; | ||
30 | uint8_t key2[crypto_box_SECRETKEYBYTES]; | ||
31 | crypto_box_keypair(key1, key2); | ||
32 | |||
33 | SUCCESS { | ||
34 | // init array | ||
35 | msgpack_pack_array(res, 2); | ||
36 | msgpack_pack_bin(res, crypto_box_PUBLICKEYBYTES); | ||
37 | msgpack_pack_bin_body(res, key1, crypto_box_PUBLICKEYBYTES); | ||
38 | |||
39 | msgpack_pack_bin(res, crypto_box_SECRETKEYBYTES); | ||
40 | msgpack_pack_bin_body(res, key2, crypto_box_SECRETKEYBYTES); | ||
41 | } | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | METHOD(array, KeyPair, fromSecretKey) | ||
46 | { | ||
47 | CHECK_SIZE(args, 1); | ||
48 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_BIN); | ||
49 | CHECK_SIZE(args.ptr[0].via.bin, crypto_box_SECRETKEYBYTES); | ||
50 | |||
51 | Net_Crypto c; | ||
52 | uint8_t secret_key[crypto_box_SECRETKEYBYTES]; | ||
53 | memcpy(secret_key, args.ptr[0].via.bin.ptr, crypto_box_SECRETKEYBYTES); | ||
54 | load_secret_key(&c, secret_key); | ||
55 | |||
56 | SUCCESS { | ||
57 | msgpack_pack_array(res, 2); | ||
58 | |||
59 | msgpack_pack_bin(res, crypto_box_PUBLICKEYBYTES); | ||
60 | msgpack_pack_bin_body(res, c.self_secret_key, crypto_box_PUBLICKEYBYTES); | ||
61 | msgpack_pack_bin(res, crypto_box_SECRETKEYBYTES); | ||
62 | msgpack_pack_bin_body(res, c.self_public_key, crypto_box_SECRETKEYBYTES); | ||
63 | } | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | METHOD(array, Nonce, newNonce) | ||
68 | { | ||
69 | uint8_t nonce[24] = {0}; | ||
70 | new_nonce(nonce); | ||
71 | |||
72 | SUCCESS { | ||
73 | msgpack_pack_bin(res, sizeof nonce); | ||
74 | msgpack_pack_bin_body(res, nonce, sizeof nonce); | ||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | METHOD(array, Nonce, increment) | ||
81 | { | ||
82 | CHECK_SIZE(args, 1); | ||
83 | CHECK_TYPE(args.ptr[0], MSGPACK_OBJECT_BIN); | ||
84 | CHECK_SIZE(args.ptr[0].via.bin, 24); | ||
85 | |||
86 | uint8_t nonce[24]; | ||
87 | memcpy(nonce, args.ptr[0].via.bin.ptr, 24); | ||
88 | increment_nonce(nonce); | ||
89 | |||
90 | SUCCESS { | ||
91 | msgpack_pack_bin(res, sizeof nonce); | ||
92 | msgpack_pack_bin_body(res, nonce, sizeof nonce); | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | METHOD(array, rpc, capabilities) | ||
99 | { | ||
100 | return pending; | ||
101 | } | ||
102 | |||
103 | char const *call_method(msgpack_object_str name, msgpack_object_array args, msgpack_packer *res) | ||
104 | { | ||
105 | #define DISPATCH(SERVICE, NAME) \ | ||
106 | if (name.size == sizeof #SERVICE "." #NAME - 1 && \ | ||
107 | memcmp(name.ptr, #SERVICE "." #NAME, name.size) == 0) \ | ||
108 | return SERVICE##_##NAME(args, res) | ||
109 | DISPATCH(Binary, decode); | ||
110 | DISPATCH(Binary, encode); | ||
111 | DISPATCH(Box, decrypt); | ||
112 | DISPATCH(Box, encrypt); | ||
113 | DISPATCH(CombinedKey, precompute); | ||
114 | DISPATCH(KeyPair, fromSecretKey); | ||
115 | DISPATCH(KeyPair, newKeyPair); | ||
116 | DISPATCH(Nonce, increment); | ||
117 | DISPATCH(Nonce, newNonce); | ||
118 | #undef DISPATCH | ||
119 | |||
120 | // Default action: "Unimplemented" exception. New tests should be added here | ||
121 | // returning "Pending" until they are properly implemented. | ||
122 | return unimplemented; | ||
123 | } | ||
124 | |||
125 | int method_cmp(char const *ptr, char const *expected, size_t max_size) | ||
126 | { | ||
127 | char *transformed = malloc(max_size); | ||
128 | |||
129 | if (transformed == NULL) { | ||
130 | return memcmp(ptr, expected, max_size); | ||
131 | } | ||
132 | |||
133 | memcpy(transformed, ptr, max_size); | ||
134 | size_t i; | ||
135 | |||
136 | for (i = 0; i < max_size; i++) { | ||
137 | switch (transformed[i]) { | ||
138 | case '(': | ||
139 | case ')': | ||
140 | case ' ': | ||
141 | transformed[i] = '_'; | ||
142 | break; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return memcmp(transformed, expected, max_size); | ||
147 | } | ||
diff --git a/testing/hstox/methods.h b/testing/hstox/methods.h new file mode 100644 index 00000000..1a7f3059 --- /dev/null +++ b/testing/hstox/methods.h | |||
@@ -0,0 +1,47 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "util.h" | ||
4 | |||
5 | #include <msgpack.h> | ||
6 | |||
7 | #define CHECK(COND) \ | ||
8 | do { \ | ||
9 | if (!(COND)) \ | ||
10 | return #COND; \ | ||
11 | } while (0) | ||
12 | |||
13 | #define CHECK_SIZE(ARG, SIZE) \ | ||
14 | do { \ | ||
15 | if ((ARG).size != (SIZE)) \ | ||
16 | return ssprintf("%s:%d: Size of `" #ARG "' expected to be %zu, but was %zu", __FILE__, \ | ||
17 | __LINE__, SIZE, (ARG).size); \ | ||
18 | } while (0) | ||
19 | |||
20 | #define CHECK_TYPE(ARG, TYPE) \ | ||
21 | do { \ | ||
22 | if ((ARG).type != (TYPE)) \ | ||
23 | return ssprintf("%s:%d: Type of `" #ARG "' expected to be %s, but was %s", __FILE__, \ | ||
24 | __LINE__, type_name(TYPE), type_name((ARG).type)); \ | ||
25 | } while (0) | ||
26 | |||
27 | #define SUCCESS \ | ||
28 | msgpack_pack_array(res, 0); \ | ||
29 | if (true) | ||
30 | |||
31 | #define METHOD(TYPE, SERVICE, NAME) \ | ||
32 | char const *SERVICE##_##NAME(msgpack_object_##TYPE args, msgpack_packer *res) | ||
33 | |||
34 | // These are not defined by msgpack.h, but we need them for uniformity in the | ||
35 | // METHOD macro. | ||
36 | typedef int64_t msgpack_object_i64; | ||
37 | typedef uint64_t msgpack_object_u64; | ||
38 | |||
39 | METHOD(array, Binary, decode); | ||
40 | METHOD(array, Binary, encode); | ||
41 | |||
42 | char const *call_method(msgpack_object_str name, msgpack_object_array args, msgpack_packer *res); | ||
43 | int method_cmp(char const *ptr, char const *expected, size_t max_size); | ||
44 | |||
45 | extern char const *const failure; | ||
46 | extern char const *const pending; | ||
47 | extern char const *const unimplemented; | ||
diff --git a/testing/hstox/packet_kinds.c b/testing/hstox/packet_kinds.c new file mode 100644 index 00000000..314c814b --- /dev/null +++ b/testing/hstox/packet_kinds.c | |||
@@ -0,0 +1,48 @@ | |||
1 | #include "packet_kinds.h" | ||
2 | |||
3 | #include <network.h> | ||
4 | |||
5 | uint8_t const packet_kinds[21] = { | ||
6 | // = PingRequest -- 0x00: Ping request | ||
7 | NET_PACKET_PING_REQUEST, | ||
8 | // | PingResponse -- 0x01: Ping response | ||
9 | NET_PACKET_PING_RESPONSE, | ||
10 | // | NodesRequest -- 0x02: Nodes request | ||
11 | NET_PACKET_GET_NODES, | ||
12 | // | NodesResponse -- 0x04: Nodes response | ||
13 | NET_PACKET_SEND_NODES_IPV6, | ||
14 | // | CookieRequest -- 0x18: Cookie request | ||
15 | NET_PACKET_COOKIE_REQUEST, | ||
16 | // | CookieResponse -- 0x19: Cookie response | ||
17 | NET_PACKET_COOKIE_RESPONSE, | ||
18 | // | CryptoHandshake -- 0x1a: Crypto handshake | ||
19 | NET_PACKET_CRYPTO_HS, | ||
20 | // | CryptoData -- 0x1b: Crypto data | ||
21 | NET_PACKET_CRYPTO_DATA, | ||
22 | // | Crypto -- 0x20: Encrypted data | ||
23 | NET_PACKET_CRYPTO, | ||
24 | // | LanDiscovery -- 0x21: LAN discovery | ||
25 | NET_PACKET_LAN_DISCOVERY, | ||
26 | // | OnionRequest0 -- 0x80: Initial onion request | ||
27 | NET_PACKET_ONION_SEND_INITIAL, | ||
28 | // | OnionRequest1 -- 0x81: First level wrapped onion request | ||
29 | NET_PACKET_ONION_SEND_1, | ||
30 | // | OnionRequest2 -- 0x82: Second level wrapped onion request | ||
31 | NET_PACKET_ONION_SEND_2, | ||
32 | // | AnnounceRequest -- 0x83: Announce request | ||
33 | NET_PACKET_ANNOUNCE_REQUEST, | ||
34 | // | AnnounceResponse -- 0x84: Announce response | ||
35 | NET_PACKET_ANNOUNCE_RESPONSE, | ||
36 | // | OnionDataRequest -- 0x85: Onion data request | ||
37 | NET_PACKET_ONION_DATA_REQUEST, | ||
38 | // | OnionDataResponse -- 0x86: Onion data response | ||
39 | NET_PACKET_ONION_DATA_RESPONSE, | ||
40 | // | OnionResponse3 -- 0x8c: Third level wrapped onion response | ||
41 | NET_PACKET_ONION_RECV_3, | ||
42 | // | OnionResponse2 -- 0x8d: Second level wrapped onion response | ||
43 | NET_PACKET_ONION_RECV_2, | ||
44 | // | OnionResponse1 -- 0x8e: First level wrapped onion response | ||
45 | NET_PACKET_ONION_RECV_1, | ||
46 | // | BootstrapInfo -- 0xf0: Bootstrap node info request and response | ||
47 | BOOTSTRAP_INFO_PACKET_ID, | ||
48 | }; | ||
diff --git a/testing/hstox/packet_kinds.h b/testing/hstox/packet_kinds.h new file mode 100644 index 00000000..ab019ab1 --- /dev/null +++ b/testing/hstox/packet_kinds.h | |||
@@ -0,0 +1,5 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stdint.h> | ||
4 | |||
5 | extern uint8_t const packet_kinds[21]; | ||
diff --git a/testing/hstox/test-inputs/.gitignore b/testing/hstox/test-inputs/.gitignore new file mode 100644 index 00000000..5eb3d40d --- /dev/null +++ b/testing/hstox/test-inputs/.gitignore | |||
@@ -0,0 +1 @@ | |||
/?*.* | |||
diff --git a/testing/hstox/test_main.c b/testing/hstox/test_main.c new file mode 100644 index 00000000..7ac18da0 --- /dev/null +++ b/testing/hstox/test_main.c | |||
@@ -0,0 +1,72 @@ | |||
1 | #include "driver.h" | ||
2 | #include "errors.h" | ||
3 | |||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | |||
7 | // The msgpack-rpc test port expected by the test runner. | ||
8 | #define PORT 1234 | ||
9 | |||
10 | // Timeout in seconds after which the driver shuts down. Currently, a complete | ||
11 | // test run takes about 7 seconds. The timeout of 2 minutes is a guess so it | ||
12 | // keeps working for a while, even on a very slow computer. | ||
13 | #define TIMEOUT 120 | ||
14 | |||
15 | static char const *error_desc(int code) | ||
16 | { | ||
17 | switch (code) { | ||
18 | case E_OK: | ||
19 | return "Success"; | ||
20 | |||
21 | case E_NOMEM: | ||
22 | return "Error: Out of memory"; | ||
23 | |||
24 | case E_SOCKET: | ||
25 | return "Error: socket creation failed"; | ||
26 | |||
27 | case E_BIND: | ||
28 | return "Error: bind failed"; | ||
29 | |||
30 | case E_LISTEN: | ||
31 | return "Error: listen failed"; | ||
32 | |||
33 | case E_ACCEPT: | ||
34 | return "Error: accept failed"; | ||
35 | |||
36 | case E_PARSE: | ||
37 | return "Error: unable to parse msgpack input"; | ||
38 | |||
39 | case E_OPEN: | ||
40 | return "Error: open failed"; | ||
41 | |||
42 | case E_READ: | ||
43 | return "Error: read failed"; | ||
44 | |||
45 | case E_WRITE: | ||
46 | return "Error: write failed"; | ||
47 | |||
48 | case E_SODIUM: | ||
49 | return "Error: libsodium initialisation failed"; | ||
50 | } | ||
51 | |||
52 | return "Unknown error code"; | ||
53 | } | ||
54 | |||
55 | #ifdef __GLASGOW_HASKELL__ | ||
56 | #define main test_main | ||
57 | #endif | ||
58 | int main(void) | ||
59 | { | ||
60 | struct settings cfg = {true, true}; | ||
61 | uint32_t result = network_main(cfg, PORT, TIMEOUT); | ||
62 | int line = result >> 16; | ||
63 | int error = (result >> 8) & 0xff; | ||
64 | int code = result & 0xff; | ||
65 | |||
66 | if (code != E_OK) { | ||
67 | printf("%s, errno=%d, line=%d\n", error_desc(code), error, line); | ||
68 | return EXIT_FAILURE; | ||
69 | } | ||
70 | |||
71 | return EXIT_SUCCESS; | ||
72 | } | ||
diff --git a/testing/hstox/toxcore-sut.hs b/testing/hstox/toxcore-sut.hs new file mode 100644 index 00000000..73bdb702 --- /dev/null +++ b/testing/hstox/toxcore-sut.hs | |||
@@ -0,0 +1,31 @@ | |||
1 | module Main (main) where | ||
2 | |||
3 | import Control.Concurrent (threadDelay) | ||
4 | import System.Environment (getArgs, withArgs) | ||
5 | import System.Process (createProcess, proc, terminateProcess) | ||
6 | |||
7 | import Network.Tox.Testing (serve) | ||
8 | import qualified ToxTestSuite | ||
9 | |||
10 | |||
11 | foreign import ccall test_main :: IO () | ||
12 | |||
13 | |||
14 | main :: IO () | ||
15 | main = do | ||
16 | args <- getArgs | ||
17 | case args of | ||
18 | ["--sut"] -> test_main | ||
19 | self : testArgs -> do | ||
20 | -- Start a toxcore SUT (System Under Test) process that will listen on | ||
21 | -- port 1234. We call ourselves here, so the branch above is taken. | ||
22 | (_, _, _, sut) <- createProcess $ proc self ["--sut"] | ||
23 | -- 100ms delay to give the SUT time to set up its socket before we try to | ||
24 | -- build connections in the test runner. | ||
25 | threadDelay $ 100 * 1000 | ||
26 | -- ToxTestSuite (the test runner) makes connections to port 1234 to | ||
27 | -- communicate with the SUT. | ||
28 | withArgs (["--print-cpu-time", "--color"] ++ testArgs) ToxTestSuite.main | ||
29 | terminateProcess sut | ||
30 | _ -> | ||
31 | fail "Usage: toxcore-sut <path-to-toxcore-sut> [test-args...]" | ||
diff --git a/testing/hstox/util.c b/testing/hstox/util.c new file mode 100644 index 00000000..9461596c --- /dev/null +++ b/testing/hstox/util.c | |||
@@ -0,0 +1,74 @@ | |||
1 | #include "util.h" | ||
2 | |||
3 | #include <stdarg.h> | ||
4 | |||
5 | char const *type_name(msgpack_object_type type) | ||
6 | { | ||
7 | switch (type) { | ||
8 | case MSGPACK_OBJECT_NIL: | ||
9 | return "nil"; | ||
10 | |||
11 | case MSGPACK_OBJECT_BOOLEAN: | ||
12 | return "boolean"; | ||
13 | |||
14 | case MSGPACK_OBJECT_POSITIVE_INTEGER: | ||
15 | return "positive_integer"; | ||
16 | |||
17 | case MSGPACK_OBJECT_NEGATIVE_INTEGER: | ||
18 | return "negative_integer"; | ||
19 | |||
20 | case MSGPACK_OBJECT_FLOAT: | ||
21 | return "float"; | ||
22 | |||
23 | case MSGPACK_OBJECT_STR: | ||
24 | return "str"; | ||
25 | |||
26 | case MSGPACK_OBJECT_ARRAY: | ||
27 | return "array"; | ||
28 | |||
29 | case MSGPACK_OBJECT_MAP: | ||
30 | return "map"; | ||
31 | |||
32 | case MSGPACK_OBJECT_BIN: | ||
33 | return "bin"; | ||
34 | |||
35 | case MSGPACK_OBJECT_EXT: | ||
36 | return "ext"; | ||
37 | } | ||
38 | |||
39 | return "<unknown type>"; | ||
40 | } | ||
41 | |||
42 | int msgpack_pack_string(msgpack_packer *pk, char const *str) | ||
43 | { | ||
44 | size_t len = strlen(str); | ||
45 | msgpack_pack_str(pk, len); | ||
46 | return msgpack_pack_str_body(pk, str, len); | ||
47 | } | ||
48 | |||
49 | int msgpack_pack_vstringf(msgpack_packer *pk, char const *fmt, va_list ap) | ||
50 | { | ||
51 | char buf[1024]; | ||
52 | vsnprintf(buf, sizeof buf, fmt, ap); | ||
53 | return msgpack_pack_string(pk, buf); | ||
54 | } | ||
55 | |||
56 | int msgpack_pack_stringf(msgpack_packer *pk, char const *fmt, ...) | ||
57 | { | ||
58 | va_list ap; | ||
59 | va_start(ap, fmt); | ||
60 | int res = msgpack_pack_vstringf(pk, fmt, ap); | ||
61 | va_end(ap); | ||
62 | return res; | ||
63 | } | ||
64 | |||
65 | char const *ssprintf(char const *fmt, ...) | ||
66 | { | ||
67 | static char buf[1024]; | ||
68 | |||
69 | va_list ap; | ||
70 | va_start(ap, fmt); | ||
71 | vsnprintf(buf, sizeof buf, fmt, ap); | ||
72 | va_end(ap); | ||
73 | return buf; | ||
74 | } | ||
diff --git a/testing/hstox/util.h b/testing/hstox/util.h new file mode 100644 index 00000000..c00578a2 --- /dev/null +++ b/testing/hstox/util.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <msgpack.h> | ||
4 | |||
5 | #define check_return(err, expr) \ | ||
6 | __extension__({ \ | ||
7 | __typeof__(expr) _r = (expr); \ | ||
8 | if (_r < 0) \ | ||
9 | return err | (__LINE__ << 16); \ | ||
10 | _r; \ | ||
11 | }) | ||
12 | |||
13 | #define propagate(expr) \ | ||
14 | do { \ | ||
15 | __typeof__(expr) _r = (expr); \ | ||
16 | if (_r != E_OK) \ | ||
17 | return _r; \ | ||
18 | } while (0) | ||
19 | |||
20 | char const *type_name(msgpack_object_type type); | ||
21 | |||
22 | // Statically allocated "asprintf". | ||
23 | char const *ssprintf(char const *fmt, ...); | ||
24 | |||
25 | int msgpack_pack_string(msgpack_packer *pk, char const *str); | ||
26 | int msgpack_pack_stringf(msgpack_packer *pk, char const *fmt, ...); | ||
27 | int msgpack_pack_vstringf(msgpack_packer *pk, char const *fmt, va_list ap); | ||