1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <mdns/mdns.h>
6
7#include <errno.h>
8#include <unittest/unittest.h>
9
10// Test values.
11const uint8_t kRdata[4] = {0xA, 0xB, 0xC};
12const char kRrName[] = "test_rr";
13
14// Test state manages sample values for testing. It is always initialized the
15// same way.
16//
17// TODO(kjharland): Rector existing tests to use this and reduce boilerplate.
18struct test_data {
19    bool reset() {
20        // Init message.
21        mdns_init_message(&message);
22
23        // Init rr.
24        strcpy(rr.name, kRrName);
25        rr.type = RR_TYPE_AAAA;
26        rr.clazz = RR_CLASS_IN;
27        rr.rdata = const_cast<uint8_t*>(kRdata);
28        rr.rdlength = sizeof(kRdata);
29        rr.ttl = 42;
30        return true;
31    }
32
33    mdns_message message;
34    mdns_rr rr;
35};
36
37// Returns true iff the given question has the given expected properties.
38static bool verify_question(mdns_question* q, char* domain, uint16_t qtype, uint16_t qclass) {
39    ASSERT_STR_EQ(q->domain, domain, "question has incorrect domain");
40    ASSERT_EQ(q->qtype, qtype, "question has incorrect type");
41    ASSERT_EQ(q->qclass, qclass, "question has incorrect class");
42    return true;
43}
44
45// Returns true iff the given resource record has the given expected properties.
46static bool verify_rr(mdns_rr* rr,
47                      char* name,
48                      uint16_t type,
49                      uint16_t clazz,
50                      uint8_t* rdata,
51                      uint16_t rdlength,
52                      uint32_t ttl) {
53    ASSERT_STR_EQ(rr->name, name, "rr has incorrect name");
54    ASSERT_EQ(rr->type, type, "rr has incorrect type");
55    ASSERT_EQ(rr->clazz, clazz, "rr has incorrect class");
56    ASSERT_EQ(rr->rdlength, rdlength, "rr has incorrect rdlength");
57    ASSERT_EQ(rr->ttl, ttl, "rr has incorrect ttl");
58    return true;
59}
60
61// Returns true iff the given message is zeroed out.
62static bool verify_message_is_zeroed(mdns_message* message) {
63    ASSERT_EQ(message->header.id, 0, "id should be zero");
64    ASSERT_EQ(message->header.flags, 0, "flags should be zero");
65    ASSERT_EQ(message->header.qd_count, 0, "question count should be zero");
66    ASSERT_EQ(message->header.an_count, 0, "answer count should be zero");
67    ASSERT_EQ(message->header.ns_count, 0, "name server count should be zero");
68    ASSERT_EQ(message->header.ar_count, 0, "addition resource count should be zero");
69    ASSERT_NULL(message->questions, "questions should be null");
70    ASSERT_NULL(message->answers, "answers should be null");
71    ASSERT_NULL(message->authorities, "authorities should be null");
72    return true;
73}
74
75static bool test_mdns_init_message(void) {
76    BEGIN_TEST;
77
78    mdns_message message;
79    mdns_init_message(&message);
80    EXPECT_TRUE(verify_message_is_zeroed(&message), "message was not zeroed");
81    mdns_free_message(&message);
82
83    END_TEST;
84}
85
86static bool test_mdns_add_first_question(void) {
87    BEGIN_TEST;
88
89    mdns_message message;
90    mdns_init_message(&message);
91    EXPECT_EQ(message.header.qd_count, 0, "question count should be zero");
92    EXPECT_NULL(message.questions, "questions should be null");
93
94    char domain[20] = "https://fuchsia.com";
95    uint16_t qtype = 0x1234;
96    uint16_t qclass = 0xABCD;
97
98    int retval = mdns_add_question(&message, domain, qtype, qclass);
99    EXPECT_EQ(retval, 0, "should return zero if no error");
100    EXPECT_EQ(message.header.qd_count, 1, "question count should be one");
101    EXPECT_TRUE(verify_question(message.questions, domain, qtype, qclass));
102    EXPECT_NULL(message.questions->next, "last question next ptr should be NULL");
103
104    mdns_free_message(&message);
105
106    END_TEST;
107}
108
109static bool test_mdns_add_nth_question(void) {
110    BEGIN_TEST;
111
112    mdns_message message;
113    mdns_init_message(&message);
114    EXPECT_EQ(message.header.qd_count, 0, "question count should be zero");
115    EXPECT_NULL(message.questions, "questions should be null");
116
117    char domain[20] = "https://fuchsia.com";
118    uint16_t qtypeA = 0x1234;
119    uint16_t qclassA = 0xABCD;
120
121    int retval = mdns_add_question(&message, domain, qtypeA, qclassA);
122    EXPECT_EQ(retval, 0, "should return zero if no error");
123
124    message.header.qd_count = 4; // Fiddle with header to ensure it's reset.
125    uint16_t qtypeB = 0x1235;
126    uint16_t qclassB = 0xABCE;
127    retval = mdns_add_question(&message, domain, qtypeB, qclassB);
128    EXPECT_EQ(retval, 0, "should return zero if no error");
129
130    EXPECT_EQ(message.header.qd_count, 2, "question count should be two");
131    EXPECT_TRUE(verify_question(message.questions, domain, qtypeA, qclassA));
132    EXPECT_NONNULL(message.questions->next, "non-last question next ptr should not be NULL");
133    EXPECT_TRUE(verify_question(message.questions->next, domain, qtypeB, qclassB));
134    EXPECT_NULL(message.questions->next->next, "last question next ptr should be NULL");
135
136    mdns_free_message(&message);
137
138    END_TEST;
139}
140
141static bool test_mdns_add_domain_too_long(void) {
142    BEGIN_TEST;
143
144    mdns_message message;
145    mdns_init_message(&message);
146
147    uint16_t qtype = 0x1234;
148    uint16_t qclass = 0xABCD;
149
150    char domain[MAX_DOMAIN_LENGTH + 1];
151    memset(domain, 1, MAX_DOMAIN_LENGTH + 1);
152
153    int retval = mdns_add_question(&message, domain, qtype, qclass);
154    EXPECT_EQ(retval, -1, "should return -1 on error");
155    EXPECT_EQ(errno, ENAMETOOLONG, "errno should be ENAMETOOLONG");
156    EXPECT_NULL(message.questions, "question should not have been added on error");
157
158    mdns_free_message(&message);
159
160    END_TEST;
161}
162
163static bool test_mdns_add_first_answer(void) {
164    BEGIN_TEST;
165
166    test_data t;
167    t.reset();
168
169    int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
170                                 t.rr.rdata, t.rr.rdlength, t.rr.ttl);
171    EXPECT_EQ(retval, 0, "should return zero if no error");
172    EXPECT_NONNULL(t.message.answers, "answer was not added");
173    EXPECT_EQ(t.message.header.an_count, 1, "answer count should be one");
174    EXPECT_TRUE(verify_rr(t.message.answers, t.rr.name, t.rr.type, t.rr.clazz,
175                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
176
177    END_TEST;
178}
179
180static bool test_mdns_add_nth_answer(void) {
181    BEGIN_TEST;
182
183    test_data t;
184    t.reset();
185
186    int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
187                                 t.rr.rdata, t.rr.rdlength, t.rr.ttl);
188    EXPECT_EQ(retval, 0, "should return zero if no error");
189
190    char other_name[] = "other name";
191    uint16_t other_type = RR_TYPE_A;
192    uint16_t other_clazz = RR_CLASS_IN;
193    uint8_t other_rdata[] = {t.rr.rdata[0]};
194    uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t);
195    uint32_t other_ttl = t.rr.ttl + 1;
196    retval = mdns_add_answer(&t.message, other_name, other_type, other_clazz,
197                             other_rdata, other_rdlength, other_ttl);
198    EXPECT_NONNULL(t.message.answers, "answer was not added");
199    EXPECT_EQ(t.message.header.an_count, 2, "answer count should be two");
200
201    EXPECT_TRUE(verify_rr(t.message.answers, t.rr.name, t.rr.type, t.rr.clazz,
202                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
203
204    EXPECT_NONNULL(t.message.answers->next, "second answer was not added");
205    EXPECT_TRUE(verify_rr(t.message.answers->next, other_name, other_type,
206                          other_clazz, other_rdata, other_rdlength, other_ttl));
207    EXPECT_NULL(t.message.answers->next->next,
208                "second answer nextptr should be null");
209
210    END_TEST;
211}
212
213static bool test_mdns_add_answer_bad_rr_type(void) {
214    BEGIN_TEST;
215
216    test_data t;
217    t.reset();
218    t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type.
219    int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
220                                 t.rr.rdata, t.rr.rdlength, t.rr.ttl);
221    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type");
222    EXPECT_EQ(retval, -1, "should return value < zero on error");
223    EXPECT_NULL(t.message.answers, "should not have added answer to message");
224    EXPECT_EQ(t.message.header.an_count, 0, "answer count should be zero");
225
226    END_TEST;
227}
228
229static bool test_mdns_add_answer_bad_rr_class(void) {
230    BEGIN_TEST;
231
232    test_data t;
233    t.reset();
234    t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class.
235    int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
236                                 t.rr.rdata, t.rr.rdlength, t.rr.ttl);
237    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class");
238    EXPECT_EQ(retval, -1, "should return value < zero on error");
239    EXPECT_NULL(t.message.answers, "should not have added answer to message");
240    EXPECT_EQ(t.message.header.an_count, 0, "answer count should be zero");
241
242    END_TEST;
243}
244
245static bool test_mdns_add_first_authority(void) {
246    BEGIN_TEST;
247
248    test_data t;
249    t.reset();
250
251    int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
252                                    t.rr.rdata, t.rr.rdlength, t.rr.ttl);
253    EXPECT_EQ(retval, 0, "should return zero if no error");
254    EXPECT_NONNULL(t.message.authorities, "authority was not added");
255    EXPECT_EQ(t.message.header.ns_count, 1, "authority count should be one");
256    EXPECT_TRUE(verify_rr(t.message.authorities, t.rr.name, t.rr.type, t.rr.clazz,
257                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
258
259    END_TEST;
260}
261
262static bool test_mdns_add_nth_authority(void) {
263    BEGIN_TEST;
264
265    test_data t;
266    t.reset();
267
268    int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
269                                    t.rr.rdata, t.rr.rdlength, t.rr.ttl);
270    EXPECT_EQ(retval, 0, "should return zero if no error");
271
272    char other_name[] = "other name";
273    uint16_t other_type = RR_TYPE_A;
274    uint16_t other_clazz = RR_CLASS_IN;
275    uint8_t other_rdata[] = {t.rr.rdata[0]};
276    uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t);
277    uint32_t other_ttl = t.rr.ttl + 1;
278    retval = mdns_add_authority(&t.message, other_name, other_type, other_clazz,
279                                other_rdata, other_rdlength, other_ttl);
280    EXPECT_NONNULL(t.message.authorities, "authority was not added");
281    EXPECT_EQ(t.message.header.ns_count, 2, "authority count should be two");
282
283    EXPECT_TRUE(verify_rr(t.message.authorities, t.rr.name, t.rr.type, t.rr.clazz,
284                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
285
286    EXPECT_NONNULL(t.message.authorities->next, "second authority was not added");
287    EXPECT_TRUE(verify_rr(t.message.authorities->next, other_name, other_type,
288                          other_clazz, other_rdata, other_rdlength, other_ttl));
289    EXPECT_NULL(t.message.authorities->next->next,
290                "second authority nextptr should be null");
291
292    END_TEST;
293}
294
295static bool test_mdns_add_authority_bad_rr_type(void) {
296    BEGIN_TEST;
297
298    test_data t;
299    t.reset();
300    t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type.
301    int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
302                                    t.rr.rdata, t.rr.rdlength, t.rr.ttl);
303    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type");
304    EXPECT_EQ(retval, -1, "should return value < zero on error");
305    EXPECT_NULL(t.message.authorities, "should not have added authority to message");
306    EXPECT_EQ(t.message.header.ns_count, 0, "authority count should be zero");
307
308    END_TEST;
309}
310
311static bool test_mdns_add_authority_bad_rr_class(void) {
312    BEGIN_TEST;
313
314    test_data t;
315    t.reset();
316    t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class.
317    int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
318                                    t.rr.rdata, t.rr.rdlength, t.rr.ttl);
319    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class");
320    EXPECT_EQ(retval, -1, "should return value < zero on error");
321    EXPECT_NULL(t.message.authorities, "should not have added authority to message");
322    EXPECT_EQ(t.message.header.ns_count, 0, "authority count should be zero");
323
324    END_TEST;
325}
326
327static bool test_mdns_add_first_additional(void) {
328    BEGIN_TEST;
329
330    test_data t;
331    t.reset();
332
333    int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
334                                     t.rr.rdata, t.rr.rdlength, t.rr.ttl);
335    EXPECT_EQ(retval, 0, "should return zero if no error");
336    EXPECT_NONNULL(t.message.additionals, "additional was not added");
337    EXPECT_EQ(t.message.header.ar_count, 1, "additional count should be one");
338    EXPECT_TRUE(verify_rr(t.message.additionals, t.rr.name, t.rr.type, t.rr.clazz,
339                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
340
341    END_TEST;
342}
343
344static bool test_mdns_add_nth_additional(void) {
345    BEGIN_TEST;
346
347    test_data t;
348    t.reset();
349
350    int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
351                                     t.rr.rdata, t.rr.rdlength, t.rr.ttl);
352    EXPECT_EQ(retval, 0, "should return zero if no error");
353
354    char other_name[] = "other name";
355    uint16_t other_type = RR_TYPE_A;
356    uint16_t other_clazz = RR_CLASS_IN;
357    uint8_t other_rdata[] = {t.rr.rdata[0]};
358    uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t);
359    uint32_t other_ttl = t.rr.ttl + 1;
360    retval = mdns_add_additional(&t.message, other_name, other_type, other_clazz,
361                                 other_rdata, other_rdlength, other_ttl);
362    EXPECT_NONNULL(t.message.additionals, "additional was not added");
363    EXPECT_EQ(t.message.header.ar_count, 2, "additional count should be two");
364
365    EXPECT_TRUE(verify_rr(t.message.additionals, t.rr.name, t.rr.type, t.rr.clazz,
366                          t.rr.rdata, t.rr.rdlength, t.rr.ttl));
367
368    EXPECT_NONNULL(t.message.additionals->next, "second additional was not added");
369    EXPECT_TRUE(verify_rr(t.message.additionals->next, other_name, other_type,
370                          other_clazz, other_rdata, other_rdlength, other_ttl));
371    EXPECT_NULL(t.message.additionals->next->next,
372                "second additional nextptr should be null");
373
374    END_TEST;
375}
376
377static bool test_mdns_add_additional_bad_rr_type(void) {
378    BEGIN_TEST;
379
380    test_data t;
381    t.reset();
382    t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type.
383    int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
384                                     t.rr.rdata, t.rr.rdlength, t.rr.ttl);
385    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type");
386    EXPECT_EQ(retval, -1, "should return value < zero on error");
387    EXPECT_NULL(t.message.additionals, "should not have added additional to message");
388    EXPECT_EQ(t.message.header.ar_count, 0, "additional count should be zero");
389
390    END_TEST;
391}
392
393static bool test_mdns_add_additional_bad_rr_class(void) {
394    BEGIN_TEST;
395
396    test_data t;
397    t.reset();
398    t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class.
399    int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz,
400                                     t.rr.rdata, t.rr.rdlength, t.rr.ttl);
401    EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class");
402    EXPECT_EQ(retval, -1, "should return value < zero on error");
403    EXPECT_NULL(t.message.additionals, "should not have added additional to message");
404    EXPECT_EQ(t.message.header.ar_count, 0, "additional count should be zero");
405
406    END_TEST;
407}
408
409static bool test_mdns_free_message(void) {
410    BEGIN_TEST;
411
412    mdns_message message;
413    mdns_init_message(&message);
414
415    char domain[20] = "https://fuchsia.com";
416    int retval = mdns_add_question(&message, domain, 0, 0);
417    EXPECT_EQ(retval, 0, "should return zero if no error");
418    retval = mdns_add_question(&message, domain, 0, 0);
419    EXPECT_EQ(retval, 0, "should return zero if no error");
420
421    // Double check questions were successfully added.
422    EXPECT_NONNULL(message.questions, "first question was not added");
423    EXPECT_NONNULL(message.questions->next, "second question was not added");
424
425    mdns_free_message(&message);
426    EXPECT_TRUE(verify_message_is_zeroed(&message), "message was not zeroed");
427
428    END_TEST;
429}
430
431static bool test_mdns_unmarshal_incomplete_header(void) {
432    BEGIN_TEST;
433
434    mdns_message message;
435    uint16_t encoded_message[] = {0};
436
437    // pass buf_len value smaller than MDNS_HEADER_SIZE to indicate the full
438    // message did not fit into the provided buffer.
439    int retval = mdns_unmarshal(encoded_message, MDNS_HEADER_SIZE - 1, &message);
440    EXPECT_LT(retval, 0, "should have returned a negative value on error");
441    EXPECT_TRUE(verify_message_is_zeroed(&message),
442                "message was mutated even though decoding failed");
443
444    retval = mdns_unmarshal(encoded_message, MDNS_HEADER_SIZE - 5, &message);
445    EXPECT_LT(retval, 0, "should have returned a negative value on error");
446    EXPECT_TRUE(verify_message_is_zeroed(&message),
447                "message was mutated even though decoding failed");
448
449    retval = mdns_unmarshal(encoded_message, 0, &message);
450    EXPECT_LT(retval, 0, "should have returned a negative value on error");
451    EXPECT_TRUE(verify_message_is_zeroed(&message),
452                "message was mutated even though decoding failed");
453
454    END_TEST;
455}
456
457static bool test_mdns_unmarshal_empty_message(void) {
458    BEGIN_TEST;
459
460    mdns_message message;
461
462    // Completely empty message.
463    uint16_t encoded_message_1[] = {
464        // Header section
465        0, // ID
466        0, // Flags section
467        0, // Question count
468        0, // Answer count
469        0, // Authority count
470        0, // Additionals count
471        // No message content.
472    };
473
474    int retval = mdns_unmarshal(encoded_message_1, MDNS_HEADER_SIZE, &message);
475    EXPECT_EQ(retval, MDNS_HEADER_SIZE, "should have read 12 bytes");
476    EXPECT_TRUE(verify_message_is_zeroed(&message),
477                "message is not zeroed even though input data was empty");
478
479    // Message with ID and flags but still "empty" because no questions or
480    // records are inside.
481    uint16_t encoded_message_2[] = {
482        // Header section
483        0xABCD, // ID
484        0xCDEF, // Flags section
485        0,      // Question count
486        0,      // Answer count
487        0,      // Authority count
488        0,      // Additionals count
489        // No message content.
490    };
491
492    retval = mdns_unmarshal(encoded_message_2, MDNS_HEADER_SIZE, &message);
493    EXPECT_EQ(retval, MDNS_HEADER_SIZE, "should have read 12 bytes");
494    EXPECT_EQ(message.header.id, 0xABCD, "ID should be 0xABCD (171)");
495    EXPECT_EQ(message.header.flags, 0xCDEF, "flags should be 0xCDEF (205)");
496    EXPECT_EQ(message.header.qd_count, 0, "question count should be 0");
497    EXPECT_EQ(message.header.qd_count, 0, "answer count should be 0");
498    EXPECT_EQ(message.header.qd_count, 0, "authority count should be 0");
499    EXPECT_EQ(message.header.qd_count, 0, "additionals count should be 0");
500    EXPECT_NULL(message.questions, "questions should be null");
501    EXPECT_NULL(message.answers, "answers should be null");
502    EXPECT_NULL(message.authorities, "authorities should be null");
503    EXPECT_NULL(message.additionals, "additionals should be null");
504    END_TEST;
505}
506
507BEGIN_TEST_CASE(mdns_free_message)
508RUN_TEST(test_mdns_free_message)
509END_TEST_CASE(mdns_free_message)
510
511BEGIN_TEST_CASE(mdns_init_message)
512RUN_TEST(test_mdns_init_message)
513END_TEST_CASE(mdns_init_message)
514
515BEGIN_TEST_CASE(mdns_add_question)
516RUN_TEST(test_mdns_add_first_question)
517RUN_TEST(test_mdns_add_nth_question)
518RUN_TEST(test_mdns_add_domain_too_long)
519END_TEST_CASE(mdns_add_question)
520
521BEGIN_TEST_CASE(mdns_add_answer)
522RUN_TEST(test_mdns_add_first_answer)
523RUN_TEST(test_mdns_add_nth_answer)
524RUN_TEST(test_mdns_add_answer_bad_rr_type)
525RUN_TEST(test_mdns_add_answer_bad_rr_class)
526END_TEST_CASE(mdns_add_answer)
527
528BEGIN_TEST_CASE(mdns_add_authority)
529RUN_TEST(test_mdns_add_first_authority)
530RUN_TEST(test_mdns_add_nth_authority)
531RUN_TEST(test_mdns_add_authority_bad_rr_type)
532RUN_TEST(test_mdns_add_authority_bad_rr_class)
533END_TEST_CASE(mdns_add_authority)
534
535BEGIN_TEST_CASE(mdns_add_additional)
536RUN_TEST(test_mdns_add_first_additional)
537RUN_TEST(test_mdns_add_nth_additional)
538RUN_TEST(test_mdns_add_additional_bad_rr_type)
539RUN_TEST(test_mdns_add_additional_bad_rr_class)
540END_TEST_CASE(mdns_add_additional)
541
542BEGIN_TEST_CASE(test_mdns_unmarshal)
543RUN_TEST(test_mdns_unmarshal_incomplete_header)
544RUN_TEST(test_mdns_unmarshal_empty_message)
545END_TEST_CASE(test_mdns_unmarshal)
546
547int main(int argc, char* argv[]) {
548    return unittest_run_all_tests(argc, argv) ? 0 : -1;
549}
550