Serenity Operating System
1/*
2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "Answer.h"
8#include <LibIPC/Decoder.h>
9#include <LibIPC/Encoder.h>
10#include <time.h>
11
12namespace DNS {
13
14Answer::Answer(Name const& name, RecordType type, RecordClass class_code, u32 ttl, DeprecatedString const& record_data, bool mdns_cache_flush)
15 : m_name(name)
16 , m_type(type)
17 , m_class_code(class_code)
18 , m_ttl(ttl)
19 , m_record_data(record_data)
20 , m_mdns_cache_flush(mdns_cache_flush)
21{
22 time(&m_received_time);
23}
24
25bool Answer::has_expired() const
26{
27 return time(nullptr) >= m_received_time + m_ttl;
28}
29
30unsigned Answer::hash() const
31{
32 auto hash = pair_int_hash(CaseInsensitiveStringTraits::hash(name().as_string()), (u32)type());
33 hash = pair_int_hash(hash, pair_int_hash((u32)class_code(), ttl()));
34 hash = pair_int_hash(hash, record_data().hash());
35 hash = pair_int_hash(hash, (u32)mdns_cache_flush());
36 return hash;
37}
38
39bool Answer::operator==(Answer const& other) const
40{
41 if (&other == this)
42 return true;
43 if (!Name::Traits::equals(name(), other.name()))
44 return false;
45 if (type() != other.type())
46 return false;
47 if (class_code() != other.class_code())
48 return false;
49 if (ttl() != other.ttl())
50 return false;
51 if (record_data() != other.record_data())
52 return false;
53 if (mdns_cache_flush() != other.mdns_cache_flush())
54 return false;
55 return true;
56}
57
58}
59
60ErrorOr<void> AK::Formatter<DNS::RecordType>::format(AK::FormatBuilder& builder, DNS::RecordType value)
61{
62 switch (value) {
63 case DNS::RecordType::A:
64 return builder.put_string("A"sv);
65 case DNS::RecordType::NS:
66 return builder.put_string("NS"sv);
67 case DNS::RecordType::CNAME:
68 return builder.put_string("CNAME"sv);
69 case DNS::RecordType::SOA:
70 return builder.put_string("SOA"sv);
71 case DNS::RecordType::PTR:
72 return builder.put_string("PTR"sv);
73 case DNS::RecordType::MX:
74 return builder.put_string("MX"sv);
75 case DNS::RecordType::TXT:
76 return builder.put_string("TXT"sv);
77 case DNS::RecordType::AAAA:
78 return builder.put_string("AAAA"sv);
79 case DNS::RecordType::SRV:
80 return builder.put_string("SRV"sv);
81 }
82
83 TRY(builder.put_string("DNS record type "sv));
84 TRY(builder.put_u64((u16)value));
85 return {};
86}
87
88ErrorOr<void> AK::Formatter<DNS::RecordClass>::format(AK::FormatBuilder& builder, DNS::RecordClass value)
89{
90 switch (value) {
91 case DNS::RecordClass::IN:
92 return builder.put_string("IN"sv);
93 }
94
95 TRY(builder.put_string("DNS record class "sv));
96 TRY(builder.put_u64((u16)value));
97 return {};
98}
99
100namespace IPC {
101
102template<>
103ErrorOr<void> encode(Encoder& encoder, DNS::Answer const& answer)
104{
105 TRY(encoder.encode(answer.name().as_string()));
106 TRY(encoder.encode(static_cast<u16>(answer.type())));
107 TRY(encoder.encode(static_cast<u16>(answer.class_code())));
108 TRY(encoder.encode(answer.ttl()));
109 TRY(encoder.encode(answer.record_data()));
110 TRY(encoder.encode(answer.mdns_cache_flush()));
111
112 return {};
113}
114
115template<>
116ErrorOr<DNS::Answer> decode(Decoder& decoder)
117{
118 auto name = TRY(decoder.decode<DeprecatedString>());
119 auto record_type = TRY(decoder.decode<DNS::RecordType>());
120 auto class_code = TRY(decoder.decode<DNS::RecordClass>());
121 auto ttl = TRY(decoder.decode<u32>());
122 auto record_data = TRY(decoder.decode<DeprecatedString>());
123 auto cache_flush = TRY(decoder.decode<bool>());
124
125 return DNS::Answer { name, record_type, class_code, ttl, record_data, cache_flush };
126}
127
128}