mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
Compare commits
12 commits
e3230817b6
...
84385717de
Author | SHA1 | Date | |
---|---|---|---|
|
84385717de | ||
|
63a5717bc7 | ||
|
c5afe58540 | ||
|
3bcd91b109 | ||
|
7d1291b9f0 | ||
|
6911c45bab | ||
|
879ae94183 | ||
|
7e20f4726f | ||
|
7f72c28e78 | ||
|
d704b61066 | ||
|
b93d8ef875 | ||
|
8a07131229 |
70 changed files with 2754 additions and 110 deletions
|
@ -9,10 +9,8 @@
|
|||
#include <AK/BitmapView.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef DNS_DEBUG
|
||||
# cmakedefine01 DNS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef EDITOR_DEBUG
|
||||
# cmakedefine01 EDITOR_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,15 @@ public:
|
|||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
constexpr IPv6Address(Array<u8, 16> const& data)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
template<SameAs<char const*> T>
|
||||
constexpr IPv6Address(T const&) = delete; // Disable implicit conversion of char const* -> ipv4 -> ipv6
|
||||
|
||||
constexpr IPv6Address(IPv4Address const& ipv4_address)
|
||||
{
|
||||
// IPv4 mapped IPv6 address
|
||||
|
|
|
@ -159,6 +159,7 @@ class TCPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(ByteString const& host, u16 port);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address, ByteString const&) { return connect(address); }
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd);
|
||||
|
||||
TCPSocket(TCPSocket&& other)
|
||||
|
@ -220,6 +221,7 @@ class UDPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(ByteString const& host, u16 port, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, ByteString const&, Optional<AK::Duration> timeout = {}) { return connect(address, timeout); }
|
||||
|
||||
UDPSocket(UDPSocket&& other)
|
||||
: Socket(static_cast<Socket&&>(other))
|
||||
|
|
6
Libraries/LibDNS/CMakeLists.txt
Normal file
6
Libraries/LibDNS/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
Message.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibDNS dns)
|
||||
target_link_libraries(LibDNS PRIVATE LibCore)
|
1177
Libraries/LibDNS/Message.cpp
Normal file
1177
Libraries/LibDNS/Message.cpp
Normal file
File diff suppressed because it is too large
Load diff
704
Libraries/LibDNS/Message.h
Normal file
704
Libraries/LibDNS/Message.h
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/MaybeOwned.h>
|
||||
#include <AK/RedBlackTree.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace DNS {
|
||||
namespace Messages {
|
||||
|
||||
struct DomainName;
|
||||
struct ParseContext {
|
||||
CountingStream& stream;
|
||||
NonnullOwnPtr<RedBlackTree<u16, DomainName>> pointers;
|
||||
};
|
||||
|
||||
enum class OpCode : u8;
|
||||
|
||||
struct Options {
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z |AD|CD| RCODE |
|
||||
constexpr inline static u16 QRMask = 0b1000000000000000;
|
||||
constexpr inline static u16 OpCodeMask = 0b0111100000000000;
|
||||
constexpr inline static u16 AuthoritativeAnswerMask = 0b0000010000000000;
|
||||
constexpr inline static u16 TruncatedMask = 0b0000001000000000;
|
||||
constexpr inline static u16 RecursionDesiredMask = 0b0000000100000000;
|
||||
constexpr inline static u16 RecursionAvailableMask = 0b0000000010000000;
|
||||
constexpr inline static u16 AuthenticatedDataMask = 0b0000000000100000;
|
||||
constexpr inline static u16 CheckingDisabledMask = 0b0000000000010000;
|
||||
constexpr inline static u16 ResponseCodeMask = 0b0000000000001111;
|
||||
|
||||
enum class ResponseCode : u16 {
|
||||
NoError = 0,
|
||||
FormatError = 1,
|
||||
ServerFailure = 2,
|
||||
NameError = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
};
|
||||
|
||||
void set_is_question(bool value) { raw = (raw & ~QRMask) | (value ? QRMask : 0); }
|
||||
void set_is_authoritative_answer(bool value) { raw = (raw & ~AuthoritativeAnswerMask) | (value ? AuthoritativeAnswerMask : 0); }
|
||||
void set_is_truncated(bool value) { raw = (raw & ~TruncatedMask) | (value ? TruncatedMask : 0); }
|
||||
void set_recursion_desired(bool value) { raw = (raw & ~RecursionDesiredMask) | (value ? RecursionDesiredMask : 0); }
|
||||
void set_recursion_available(bool value) { raw = (raw & ~RecursionAvailableMask) | (value ? RecursionAvailableMask : 0); }
|
||||
void set_response_code(ResponseCode code) { raw = (raw & ~ResponseCodeMask) | static_cast<u16>(code); }
|
||||
void set_checking_disabled(bool value) { raw = (raw & ~CheckingDisabledMask) | (value ? CheckingDisabledMask : 0); }
|
||||
void set_authenticated_data(bool value) { raw = (raw & ~AuthenticatedDataMask) | (value ? AuthenticatedDataMask : 0); }
|
||||
void set_op_code(OpCode code) { raw = (raw & ~OpCodeMask) | (static_cast<u16>(code) << 11); }
|
||||
|
||||
bool is_question() const { return (raw & QRMask) == 0; }
|
||||
bool is_authoritative_answer() const { return (raw & AuthoritativeAnswerMask) != 0; }
|
||||
bool is_truncated() const { return (raw & TruncatedMask) != 0; }
|
||||
bool recursion_desired() const { return (raw & RecursionDesiredMask) != 0; }
|
||||
bool recursion_available() const { return (raw & RecursionAvailableMask) != 0; }
|
||||
bool checking_disabled() const { return (raw & CheckingDisabledMask) != 0; }
|
||||
bool authenticated_data() const { return (raw & AuthenticatedDataMask) != 0; }
|
||||
ResponseCode response_code() const { return static_cast<ResponseCode>(raw & ResponseCodeMask); }
|
||||
OpCode op_code() const { return static_cast<OpCode>((raw & OpCodeMask) >> 11); }
|
||||
|
||||
String to_string() const;
|
||||
|
||||
NetworkOrdered<u16> raw { 0 };
|
||||
};
|
||||
StringView to_string(Options::ResponseCode);
|
||||
|
||||
struct Header {
|
||||
NetworkOrdered<u16> id;
|
||||
Options options;
|
||||
NetworkOrdered<u16> question_count;
|
||||
NetworkOrdered<u16> answer_count;
|
||||
NetworkOrdered<u16> authority_count;
|
||||
NetworkOrdered<u16> additional_count;
|
||||
};
|
||||
|
||||
struct DomainName {
|
||||
Vector<ByteString> labels;
|
||||
|
||||
static DomainName from_string(StringView);
|
||||
static ErrorOr<DomainName> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
String to_string() const;
|
||||
};
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4.
|
||||
enum class ResourceType : u16 {
|
||||
Reserved = 0, // [RFC6895]
|
||||
A = 1, // a host address [RFC1035]
|
||||
NS = 2, // an authoritative name server [RFC1035]
|
||||
MD = 3, // a mail destination (OBSOLETE - use MX) [RFC1035]
|
||||
MF = 4, // a mail forwarder (OBSOLETE - use MX) [RFC1035]
|
||||
CNAME = 5, // the canonical name for an alias [RFC1035]
|
||||
SOA = 6, // marks the start of a zone of authority [RFC1035]
|
||||
MB = 7, // a mailbox domain name (EXPERIMENTAL) [RFC1035]
|
||||
MG = 8, // a mail group member (EXPERIMENTAL) [RFC1035]
|
||||
MR = 9, // a mail rename domain name (EXPERIMENTAL) [RFC1035]
|
||||
NULL_ = 10, // a null RR (EXPERIMENTAL) [RFC1035]
|
||||
WKS = 11, // a well known service description [RFC1035]
|
||||
PTR = 12, // a domain name pointer [RFC1035]
|
||||
HINFO = 13, // host information [RFC1035]
|
||||
MINFO = 14, // mailbox or mail list information [RFC1035]
|
||||
MX = 15, // mail exchange [RFC1035]
|
||||
TXT = 16, // text strings [RFC1035]
|
||||
RP = 17, // for Responsible Person [RFC1183]
|
||||
AFSDB = 18, // for AFS Data Base location [RFC1183][RFC5864]
|
||||
X25 = 19, // for X.25 PSDN address [RFC1183]
|
||||
ISDN = 20, // for ISDN address [RFC1183]
|
||||
RT = 21, // for Route Through [RFC1183]
|
||||
NSAP = 22, // for NSAP address, NSAP style A record (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
NSAP_PTR = 23, // for domain name pointer, NSAP style (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
SIG = 24, // for security signature [RFC2536][RFC2931][RFC3110][RFC4034]
|
||||
KEY = 25, // for security key [RFC2536][RFC2539][RFC3110][RFC4034]
|
||||
PX = 26, // X.400 mail mapping information [RFC2163]
|
||||
GPOS = 27, // Geographical Position [RFC1712]
|
||||
AAAA = 28, // IP6 Address [RFC3596]
|
||||
LOC = 29, // Location Information [RFC1876]
|
||||
NXT = 30, // Next Domain (OBSOLETE) [RFC2535][RFC3755]
|
||||
EID = 31, // Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
NIMLOC = 32, // Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
SRV = 33, // Server Selection [1][RFC2782]
|
||||
ATMA = 34, // ATM Address "[ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]"
|
||||
NAPTR = 35, // Naming Authority Pointer [RFC3403]
|
||||
KX = 36, // Key Exchanger [RFC2230]
|
||||
CERT = 37, // CERT [RFC4398]
|
||||
A6 = 38, // A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563]
|
||||
DNAME = 39, // DNAME [RFC6672]
|
||||
SINK = 40, // SINK [Donald_E_Eastlake][draft-eastlake-kitchen-sink]
|
||||
OPT = 41, // OPT [RFC3225][RFC6891]
|
||||
APL = 42, // APL [RFC3123]
|
||||
DS = 43, // Delegation Signer [RFC4034]
|
||||
SSHFP = 44, // SSH Key Fingerprint [RFC4255]
|
||||
IPSECKEY = 45, // IPSECKEY [RFC4025]
|
||||
RRSIG = 46, // RRSIG [RFC4034]
|
||||
NSEC = 47, // NSEC [RFC4034][RFC9077]
|
||||
DNSKEY = 48, // DNSKEY [RFC4034]
|
||||
DHCID = 49, // DHCID [RFC4701]
|
||||
NSEC3 = 50, // NSEC3 [RFC5155][RFC9077]
|
||||
NSEC3PARAM = 51, // NSEC3PARAM [RFC5155]
|
||||
TLSA = 52, // TLSA [RFC6698]
|
||||
SMIMEA = 53, // S/MIME cert association [RFC8162]
|
||||
HIP = 55, // Host Identity Protocol [RFC8005]
|
||||
NINFO = 56, // NINFO [Jim_Reid]
|
||||
RKEY = 57, // RKEY [Jim_Reid]
|
||||
TALINK = 58, // Trust Anchor LINK [Wouter_Wijngaards]
|
||||
CDS = 59, // Child DS [RFC7344]
|
||||
CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS [RFC7344]
|
||||
OPENPGPKEY = 61, // OpenPGP Key [RFC7929]
|
||||
CSYNC = 62, // Child-To-Parent Synchronization [RFC7477]
|
||||
ZONEMD = 63, // Message Digest Over Zone Data [RFC8976]
|
||||
SVCB = 64, // General-purpose service binding [RFC9460]
|
||||
HTTPS = 65, // SVCB-compatible type for use with HTTP [RFC9460]
|
||||
SPF = 99, // [RFC7208]
|
||||
UINFO = 100, // [IANA-Reserved]
|
||||
UID = 101, // [IANA-Reserved]
|
||||
GID = 102, // [IANA-Reserved]
|
||||
UNSPEC = 103, // [IANA-Reserved]
|
||||
NID = 104, // [RFC6742]
|
||||
L32 = 105, // [RFC6742]
|
||||
L64 = 106, // [RFC6742]
|
||||
LP = 107, // [RFC6742]
|
||||
EUI48 = 108, // an EUI-48 address [RFC7043]
|
||||
EUI64 = 109, // an EUI-64 address [RFC7043]
|
||||
NXNAME = 128, // NXDOMAIN indicator for Compact Denial of Existence [draft-ietf-dnsop-compact-denial-of-existence-04]
|
||||
TKEY = 249, // Transaction Key [RFC2930]
|
||||
TSIG = 250, // Transaction Signature [RFC8945]
|
||||
IXFR = 251, // incremental transfer [RFC1995]
|
||||
AXFR = 252, // transfer of an entire zone [RFC1035][RFC5936]
|
||||
MAILB = 253, // mailbox-related RRs (MB, MG or MR) [RFC1035]
|
||||
MAILA = 254, // mail agent RRs (OBSOLETE - see MX) [RFC1035]
|
||||
ANY = 255, // A request for some or all records the server has available [RFC1035][RFC6895][RFC8482]
|
||||
URI = 256, // URI [RFC7553]
|
||||
CAA = 257, // Certification Authority Restriction [RFC8659]
|
||||
AVC = 258, // Application Visibility and Control [Wolfgang_Riedel]
|
||||
DOA = 259, // Digital Object Architecture [draft-durand-doa-over-dns]
|
||||
AMTRELAY = 260, // Automatic Multicast Tunneling Relay [RFC8777]
|
||||
RESINFO = 261, // Resolver Information as Key/Value Pairs [RFC9606]
|
||||
WALLET = 262, // Public wallet address [Paul_Hoffman]
|
||||
CLA = 263, // BP Convergence Layer Adapter [draft-johnson-dns-ipn-cla-07]
|
||||
IPN = 264, // BP Node Number [draft-johnson-dns-ipn-cla-07]
|
||||
TA = 32768, // DNSSEC Trust Authorities "[Sam_Weiler][Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]"
|
||||
DLV = 32769, // DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431]
|
||||
};
|
||||
StringView to_string(ResourceType);
|
||||
Optional<ResourceType> resource_type_from_string(StringView);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2.
|
||||
enum class Class : u16 {
|
||||
IN = 1, // the Internet [RFC1035]
|
||||
CH = 3, // the CHAOS class [Moon1981]
|
||||
HS = 4, // Hesiod [Dyer1987]
|
||||
};
|
||||
StringView to_string(Class);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-3.
|
||||
enum class OpCode : u8 {
|
||||
Query = 0, // a standard query (QUERY)
|
||||
IQuery = 1, // an inverse query (IQUERY)
|
||||
Status = 2, // a server status request (STATUS)
|
||||
Notify = 4, // NOTIFY
|
||||
Update = 5, // dynamic update (RFC 2136)
|
||||
DSO = 6, // DNS Stateful Operations (DSO) [RFC8490]
|
||||
Reserved = 7, // [RFC6895]
|
||||
ReservedMask = 15 // [RFC6895]
|
||||
};
|
||||
StringView to_string(OpCode);
|
||||
|
||||
namespace TLSA {
|
||||
|
||||
// Listings from IANA https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml.
|
||||
enum class CertUsage : u8 {
|
||||
CAConstraint = 0,
|
||||
ServiceCertificateConstraint = 1,
|
||||
TrustAnchorAssertion = 2,
|
||||
DomainIssuedCertificate = 3,
|
||||
Private = 255
|
||||
};
|
||||
enum class Selector : u8 {
|
||||
FullCertificate = 0,
|
||||
SubjectPublicKeyInfo = 1,
|
||||
Private = 255
|
||||
};
|
||||
enum class MatchingType : u8 {
|
||||
Full = 0,
|
||||
SHA256 = 1,
|
||||
SHA512 = 2,
|
||||
Private = 255
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DNSSEC {
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class Algorithm : u8 {
|
||||
RSAMD5 = 1, // RSA/MD5 [RFC4034][RFC3110]
|
||||
DSA = 3, // DSA/SHA-1 [RFC3755][RFC2536]
|
||||
RSASHA1 = 5, // RSA/SHA-1 [RFC3110]
|
||||
RSASHA1NSEC3SHA1 = 7, // [RFC5155]
|
||||
RSASHA256 = 8, // RSA/SHA-256 [RFC5702]
|
||||
RSASHA512 = 10, // RSA/SHA-512 [RFC5702]
|
||||
ECDSAP256SHA256 = 13, // ECDSA Curve P-256 with SHA-256 [RFC6605]
|
||||
ECDSAP384SHA384 = 14, // ECDSA Curve P-384 with SHA-384 [RFC6605]
|
||||
ED25519 = 15, // Ed25519 [RFC8080]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(Algorithm algorithm)
|
||||
{
|
||||
switch (algorithm) {
|
||||
case Algorithm::RSAMD5:
|
||||
return "RSAMD5"sv;
|
||||
case Algorithm::DSA:
|
||||
return "DSA"sv;
|
||||
case Algorithm::RSASHA1:
|
||||
return "RSASHA1"sv;
|
||||
case Algorithm::RSASHA1NSEC3SHA1:
|
||||
return "RSASHA1NSEC3SHA1"sv;
|
||||
case Algorithm::RSASHA256:
|
||||
return "RSASHA256"sv;
|
||||
case Algorithm::RSASHA512:
|
||||
return "RSASHA512"sv;
|
||||
case Algorithm::ECDSAP256SHA256:
|
||||
return "ECDSAP256SHA256"sv;
|
||||
case Algorithm::ECDSAP384SHA384:
|
||||
return "ECDSAP384SHA384"sv;
|
||||
case Algorithm::ED25519:
|
||||
return "ED25519"sv;
|
||||
case Algorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class DigestType : u8 {
|
||||
SHA1 = 1, // SHA-1 [RFC3658]
|
||||
SHA256 = 2, // SHA-256 [RFC4509]
|
||||
GOST3411 = 3, // GOST R 34.11-94 [RFC5933]
|
||||
SHA384 = 4, // SHA-384 [RFC6605]
|
||||
SHA512 = 5, // SHA-512 [RFC6605]
|
||||
SHA224 = 6, // SHA-224 [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(DigestType digest_type)
|
||||
{
|
||||
switch (digest_type) {
|
||||
case DigestType::SHA1:
|
||||
return "SHA1"sv;
|
||||
case DigestType::SHA256:
|
||||
return "SHA256"sv;
|
||||
case DigestType::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case DigestType::SHA384:
|
||||
return "SHA384"sv;
|
||||
case DigestType::SHA512:
|
||||
return "SHA512"sv;
|
||||
case DigestType::SHA224:
|
||||
return "SHA224"sv;
|
||||
case DigestType::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class NSEC3HashAlgorithm : u8 {
|
||||
SHA1 = 1, // [RFC5155]
|
||||
SHA256 = 2, // [RFC6605]
|
||||
GOST3411 = 3, // [RFC5933]
|
||||
SHA384 = 4, // [RFC6605]
|
||||
SHA512 = 5, // [RFC6605]
|
||||
SHA224 = 6, // [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(NSEC3HashAlgorithm hash_algorithm)
|
||||
{
|
||||
switch (hash_algorithm) {
|
||||
case NSEC3HashAlgorithm::SHA1:
|
||||
return "SHA1"sv;
|
||||
case NSEC3HashAlgorithm::SHA256:
|
||||
return "SHA256"sv;
|
||||
case NSEC3HashAlgorithm::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case NSEC3HashAlgorithm::SHA384:
|
||||
return "SHA384"sv;
|
||||
case NSEC3HashAlgorithm::SHA512:
|
||||
return "SHA512"sv;
|
||||
case NSEC3HashAlgorithm::SHA224:
|
||||
return "SHA224"sv;
|
||||
case NSEC3HashAlgorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Question {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
|
||||
static ErrorOr<Question> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
};
|
||||
|
||||
namespace Records {
|
||||
|
||||
struct A {
|
||||
IPv4Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::A;
|
||||
static ErrorOr<A> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct AAAA {
|
||||
IPv6Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::AAAA;
|
||||
static ErrorOr<AAAA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct TXT {
|
||||
ByteString content;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::TXT;
|
||||
static ErrorOr<TXT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("Text: '{}'", StringView { content }); }
|
||||
};
|
||||
struct CNAME {
|
||||
DomainName names;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CNAME;
|
||||
static ErrorOr<CNAME> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return names.to_string(); }
|
||||
};
|
||||
struct NS {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NS;
|
||||
static ErrorOr<NS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SOA {
|
||||
DomainName mname;
|
||||
DomainName rname;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SOA;
|
||||
static ErrorOr<SOA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("SOA MName: '{}', RName: '{}', Serial: {}, Refresh: {}, Retry: {}, Expire: {}, Minimum: {}", mname.to_string(), rname.to_string(), serial, refresh, retry, expire, minimum);
|
||||
}
|
||||
};
|
||||
struct MX {
|
||||
u16 preference;
|
||||
DomainName exchange;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::MX;
|
||||
static ErrorOr<MX> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("MX Preference: {}, Exchange: '{}'", preference, exchange.to_string()); }
|
||||
};
|
||||
struct PTR {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::PTR;
|
||||
static ErrorOr<PTR> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SRV {
|
||||
u16 priority;
|
||||
u16 weight;
|
||||
u16 port;
|
||||
DomainName target;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SRV;
|
||||
static ErrorOr<SRV> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("SRV Priority: {}, Weight: {}, Port: {}, Target: '{}'", priority, weight, port, target.to_string()); }
|
||||
};
|
||||
struct DNSKEY {
|
||||
u16 flags;
|
||||
u8 protocol;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
ByteBuffer public_key;
|
||||
|
||||
constexpr static inline u16 FlagSecureEntryPoint = 0b1000000000000000;
|
||||
constexpr static inline u16 FlagZoneKey = 0b0100000000000000;
|
||||
constexpr static inline u16 FlagRevoked = 0b0010000000000000;
|
||||
|
||||
constexpr bool is_secure_entry_point() const { return flags & FlagSecureEntryPoint; }
|
||||
constexpr bool is_zone_key() const { return flags & FlagZoneKey; }
|
||||
constexpr bool is_revoked() const { return flags & FlagRevoked; }
|
||||
constexpr bool is_key_signing_key() const { return is_secure_entry_point() && is_zone_key() && !is_revoked(); }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DNSKEY;
|
||||
static ErrorOr<DNSKEY> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("DNSKEY Flags: {}{}{}{}({}), Protocol: {}, Algorithm: {}, Public Key: {}",
|
||||
is_secure_entry_point() ? "sep "sv : ""sv,
|
||||
is_zone_key() ? "zone "sv : ""sv,
|
||||
is_revoked() ? "revoked "sv : ""sv,
|
||||
is_key_signing_key() ? "ksk "sv : ""sv,
|
||||
flags,
|
||||
protocol,
|
||||
DNSSEC::to_string(algorithm),
|
||||
TRY(encode_base64(public_key)));
|
||||
}
|
||||
};
|
||||
struct CDNSKEY : public DNSKEY {
|
||||
template<typename... Ts>
|
||||
CDNSKEY(Ts&&... args)
|
||||
: DNSKEY(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CDNSKEY;
|
||||
static ErrorOr<CDNSKEY> from_raw(ParseContext& raw) { return DNSKEY::from_raw(raw); }
|
||||
};
|
||||
struct DS {
|
||||
u16 key_tag;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
DNSSEC::DigestType digest_type;
|
||||
ByteBuffer digest;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DS;
|
||||
static ErrorOr<DS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "DS"_string; }
|
||||
};
|
||||
struct CDS : public DS {
|
||||
template<typename... Ts>
|
||||
CDS(Ts&&... args)
|
||||
: DS(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
static constexpr ResourceType type = ResourceType::CDS;
|
||||
static ErrorOr<CDS> from_raw(ParseContext& raw) { return DS::from_raw(raw); }
|
||||
};
|
||||
struct SIG {
|
||||
ResourceType type_covered;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
u8 label_count;
|
||||
u32 original_ttl;
|
||||
UnixDateTime expiration;
|
||||
UnixDateTime inception;
|
||||
u16 key_tag;
|
||||
DomainName signers_name;
|
||||
ByteBuffer signature;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SIG;
|
||||
static ErrorOr<SIG> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
struct RRSIG : public SIG {
|
||||
template<typename... Ts>
|
||||
RRSIG(Ts&&... args)
|
||||
: SIG(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::RRSIG;
|
||||
static ErrorOr<RRSIG> from_raw(ParseContext& raw) { return SIG::from_raw(raw); }
|
||||
};
|
||||
struct NSEC {
|
||||
DomainName next_domain_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC;
|
||||
static ErrorOr<NSEC> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC"_string; }
|
||||
};
|
||||
struct NSEC3 {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
DomainName next_hashed_owner_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3;
|
||||
static ErrorOr<NSEC3> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3"_string; }
|
||||
};
|
||||
struct NSEC3PARAM {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
|
||||
constexpr static inline u8 FlagOptOut = 0b10000000;
|
||||
|
||||
constexpr bool is_opt_out() const { return flags & FlagOptOut; }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3PARAM;
|
||||
static ErrorOr<NSEC3PARAM> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3PARAM"_string; }
|
||||
};
|
||||
struct TLSA {
|
||||
Messages::TLSA::CertUsage cert_usage;
|
||||
Messages::TLSA::Selector selector;
|
||||
Messages::TLSA::MatchingType matching_type;
|
||||
ByteBuffer certificate_association_data;
|
||||
|
||||
static ErrorOr<TLSA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "TLSA"_string; }
|
||||
};
|
||||
struct HINFO {
|
||||
ByteString cpu;
|
||||
ByteString os;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::HINFO;
|
||||
static ErrorOr<HINFO> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("HINFO CPU: '{}', OS: '{}'", StringView { cpu }, StringView { os }); }
|
||||
};
|
||||
struct OPT {
|
||||
struct Option {
|
||||
u16 code;
|
||||
ByteBuffer data;
|
||||
};
|
||||
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | UDP Payload Size |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | Extended RCode | VER | ZZ |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |DO| Z |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | OPT-LEN / OPT-DATA...
|
||||
|
||||
NetworkOrdered<u16> udp_payload_size { 0 };
|
||||
NetworkOrdered<u32> extended_rcode_and_flags { 0 };
|
||||
Vector<Option> options;
|
||||
static constexpr u32 MaskExtendedRCode = 0b11111111000000000000000000000000;
|
||||
static constexpr u32 MaskVersion = 0b00000000111100000000000000000000;
|
||||
static constexpr u32 MaskDO = 0b00000000000000001000000000000000;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::OPT;
|
||||
|
||||
constexpr u8 extended_rcode() const { return (extended_rcode_and_flags & MaskExtendedRCode) >> 24; }
|
||||
constexpr u8 version() const { return (extended_rcode_and_flags & MaskVersion) >> 20; }
|
||||
constexpr bool dnssec_ok() const { return extended_rcode_and_flags & MaskDO; }
|
||||
|
||||
void set_extended_rcode(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskExtendedRCode) | (value << 24); }
|
||||
void set_version(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskVersion) | (value << 20); }
|
||||
void set_dnssec_ok(bool value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskDO) | (value ? MaskDO : 0); }
|
||||
|
||||
static ErrorOr<OPT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.appendff("OPT UDP Payload Size: {}, Extended RCode: {}, Version: {}, DNSSEC OK: {}", udp_payload_size, extended_rcode(), version(), dnssec_ok());
|
||||
for (auto& option : options)
|
||||
builder.appendff(", opt[{} = '{:hex-dump}']", option.code, option.data.bytes());
|
||||
|
||||
return builder.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using Record = Variant<
|
||||
Records::A,
|
||||
Records::AAAA,
|
||||
Records::TXT,
|
||||
Records::CNAME,
|
||||
Records::NS,
|
||||
Records::SOA,
|
||||
Records::MX,
|
||||
Records::PTR,
|
||||
Records::SRV,
|
||||
Records::DNSKEY,
|
||||
Records::CDNSKEY,
|
||||
Records::DS,
|
||||
Records::CDS,
|
||||
Records::RRSIG,
|
||||
Records::NSEC,
|
||||
Records::NSEC3,
|
||||
Records::NSEC3PARAM,
|
||||
Records::TLSA,
|
||||
Records::HINFO,
|
||||
Records::OPT,
|
||||
// TODO: Add more records.
|
||||
ByteBuffer>; // Fallback for unknown records.
|
||||
|
||||
struct ResourceRecord {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
u32 ttl;
|
||||
Record record;
|
||||
Optional<ByteBuffer> raw;
|
||||
|
||||
static ErrorOr<ResourceRecord> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
|
||||
struct ZoneAuthority {
|
||||
DomainName name;
|
||||
ByteString admin_mailbox;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum_ttl;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Header header;
|
||||
Vector<Question> questions;
|
||||
Vector<ResourceRecord> answers;
|
||||
Vector<ResourceRecord> authorities;
|
||||
Vector<ResourceRecord> additional_records;
|
||||
|
||||
static ErrorOr<Message> from_raw(ParseContext&);
|
||||
static ErrorOr<Message> from_raw(Stream&);
|
||||
ErrorOr<size_t> to_raw(ByteBuffer&) const;
|
||||
|
||||
ErrorOr<String> format_for_log() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
488
Libraries/LibDNS/Resolver.h
Normal file
488
Libraries/LibDNS/Resolver.h
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/AtomicRefCounted.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibDNS/Message.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/RWLockProtected.h>
|
||||
|
||||
namespace DNS {
|
||||
class Resolver;
|
||||
|
||||
class LookupResult : public AtomicRefCounted<LookupResult>
|
||||
, public Weakable<LookupResult> {
|
||||
public:
|
||||
explicit LookupResult(Messages::DomainName name)
|
||||
: m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
Vector<Variant<IPv4Address, IPv6Address>> cached_addresses() const
|
||||
{
|
||||
Vector<Variant<IPv4Address, IPv6Address>> result;
|
||||
for (auto& re : m_cached_records) {
|
||||
re.record.record.visit(
|
||||
[&](Messages::Records::A const& a) { result.append(a.address); },
|
||||
[&](Messages::Records::AAAA const& aaaa) { result.append(aaaa.address); },
|
||||
[](auto&) {});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void check_expiration()
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
auto now = Core::DateTime::now();
|
||||
for (size_t i = 0; i < m_cached_records.size();) {
|
||||
auto& record = m_cached_records[i];
|
||||
if (record.expiration.has_value() && record.expiration.value() < now) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Removing expired record for {}", m_name.to_string());
|
||||
m_cached_records.remove(i);
|
||||
} else {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Keeping record for {} (expires in {})", m_name.to_string(), record.expiration.has_value() ? record.expiration.value().to_string() : "never"_string);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cached_records.is_empty())
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
void add_record(Messages::ResourceRecord record)
|
||||
{
|
||||
m_valid = true;
|
||||
auto expiration = record.ttl > 0 ? Optional<Core::DateTime>(Core::DateTime::from_timestamp(Core::DateTime::now().timestamp() + record.ttl)) : OptionalNone();
|
||||
m_cached_records.append({ move(record), move(expiration) });
|
||||
}
|
||||
|
||||
Vector<Messages::ResourceRecord> records() const
|
||||
{
|
||||
Vector<Messages::ResourceRecord> result;
|
||||
for (auto& re : m_cached_records)
|
||||
result.append(re.record);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_record_of_type(Messages::ResourceType type, bool later = false) const
|
||||
{
|
||||
if (later && m_desired_types.contains(type))
|
||||
return true;
|
||||
|
||||
for (auto const& re : m_cached_records) {
|
||||
if (re.record.type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void will_add_record_of_type(Messages::ResourceType type) { m_desired_types.set(type); }
|
||||
|
||||
void set_id(u16 id) { m_id = id; }
|
||||
u16 id() { return m_id; }
|
||||
|
||||
bool is_valid() const { return m_valid; }
|
||||
Messages::DomainName const& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
bool m_valid { false };
|
||||
Messages::DomainName m_name;
|
||||
struct RecordWithExpiration {
|
||||
Messages::ResourceRecord record;
|
||||
Optional<Core::DateTime> expiration;
|
||||
};
|
||||
Vector<RecordWithExpiration> m_cached_records;
|
||||
HashTable<Messages::ResourceType> m_desired_types;
|
||||
u16 m_id { 0 };
|
||||
};
|
||||
|
||||
class Resolver {
|
||||
public:
|
||||
enum class ConnectionMode {
|
||||
TCP,
|
||||
UDP,
|
||||
};
|
||||
|
||||
struct SocketResult {
|
||||
MaybeOwned<Core::Socket> socket;
|
||||
ConnectionMode mode;
|
||||
};
|
||||
|
||||
Resolver(Function<ErrorOr<SocketResult>()> create_socket)
|
||||
: m_pending_lookups(make<RedBlackTree<u16, PendingLookup>>())
|
||||
, m_create_socket(move(create_socket))
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
auto add_v4v6_entry = [&cache](StringView name_string, IPv4Address v4, IPv6Address v6) {
|
||||
auto name = Messages::DomainName::from_string(name_string);
|
||||
auto ptr = make_ref_counted<LookupResult>(name);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::A);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::AAAA);
|
||||
cache.set(name_string, ptr);
|
||||
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { v4 }, .raw = {} });
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { v6 }, .raw = {} });
|
||||
};
|
||||
|
||||
add_v4v6_entry("localhost"sv, { 127, 0, 0, 1 }, IPv6Address::loopback());
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<Empty>> when_socket_ready()
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
m_socket_ready_promises.append(promise);
|
||||
if (has_connection(false)) {
|
||||
promise->resolve({});
|
||||
return promise;
|
||||
}
|
||||
|
||||
if (!has_connection())
|
||||
promise->reject(Error::from_string_literal("Failed to create socket"));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void reset_connection()
|
||||
{
|
||||
m_socket.with_write_locked([&](auto& socket) { socket = {}; });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return expect_cached(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
auto result = lookup_in_cache(name, class_, desired_types);
|
||||
VERIFY(!result.is_null());
|
||||
dbgln_if(DNS_DEBUG, "DNS::expect({}) -> OK", name);
|
||||
return *result;
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup_in_cache(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
return m_cache.with_read_locked([&](auto& cache) -> RefPtr<LookupResult const> {
|
||||
auto it = cache.find(name);
|
||||
if (it == cache.end())
|
||||
return {};
|
||||
|
||||
auto& result = *it->value;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!result.has_record_of_type(type))
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup(move(name), class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
flush_cache();
|
||||
|
||||
auto promise = Core::Promise<NonnullRefPtr<LookupResult const>>::construct();
|
||||
|
||||
if (auto maybe_ipv4 = IPv4Address::from_string(name); maybe_ipv4.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::A)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { maybe_ipv4.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_ipv6 = IPv6Address::from_string(name); maybe_ipv6.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::AAAA)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { maybe_ipv6.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto result = lookup_in_cache(name, class_, desired_types)) {
|
||||
promise->resolve(result.release_nonnull());
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto domain_name = Messages::DomainName::from_string(name);
|
||||
|
||||
if (!has_connection()) {
|
||||
// Use system resolver
|
||||
// FIXME: Use an underlying resolver instead.
|
||||
dbgln_if(DNS_DEBUG, "Not ready to resolve, using system resolver and skipping cache for {}", name);
|
||||
auto record_or_error = Core::Socket::resolve_host(name, Core::Socket::SocketType::Stream);
|
||||
if (record_or_error.is_error()) {
|
||||
promise->reject(record_or_error.release_error());
|
||||
return promise;
|
||||
}
|
||||
auto result = make_ref_counted<LookupResult>(domain_name);
|
||||
auto record = record_or_error.release_value();
|
||||
record.visit(
|
||||
[&](IPv4Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { address }, .raw = {} });
|
||||
},
|
||||
[&](IPv6Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { address }, .raw = {} });
|
||||
});
|
||||
promise->resolve(result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto already_in_cache = false;
|
||||
auto result = m_cache.with_write_locked([&](auto& cache) -> NonnullRefPtr<LookupResult> {
|
||||
auto existing = [&] -> RefPtr<LookupResult> {
|
||||
if (cache.contains(name)) {
|
||||
auto ptr = *cache.get(name);
|
||||
|
||||
already_in_cache = true;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!ptr->has_record_of_type(type, true)) {
|
||||
already_in_cache = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (existing)
|
||||
return *existing;
|
||||
|
||||
auto ptr = make_ref_counted<LookupResult>(domain_name);
|
||||
for (auto const& type : desired_types)
|
||||
ptr->will_add_record_of_type(type);
|
||||
cache.set(name, ptr);
|
||||
return ptr;
|
||||
});
|
||||
|
||||
Optional<u16> cached_result_id;
|
||||
if (already_in_cache) {
|
||||
auto id = result->id();
|
||||
cached_result_id = id;
|
||||
auto existing_promise = m_pending_lookups.with_write_locked([&](auto& lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
if (auto* lookup = lookups->find(id))
|
||||
return lookup->promise;
|
||||
return nullptr;
|
||||
});
|
||||
if (existing_promise)
|
||||
return existing_promise.release_nonnull();
|
||||
|
||||
promise->resolve(*result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Messages::Message query;
|
||||
m_pending_lookups.with_read_locked([&](auto& lookups) {
|
||||
do
|
||||
fill_with_random({ &query.header.id, sizeof(query.header.id) });
|
||||
while (lookups->find(query.header.id) != nullptr);
|
||||
});
|
||||
query.header.question_count = max(1u, desired_types.size());
|
||||
query.header.options.set_response_code(Messages::Options::ResponseCode::NoError);
|
||||
query.header.options.set_recursion_desired(true);
|
||||
query.header.options.set_op_code(Messages::OpCode::Query);
|
||||
for (auto const& type : desired_types) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = domain_name,
|
||||
.type = type,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
if (query.questions.is_empty()) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = Messages::DomainName::from_string(name),
|
||||
.type = Messages::ResourceType::A,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
auto cached_entry = m_pending_lookups.with_write_locked([&](auto& pending_lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
// One more try to make sure we're not overwriting an existing lookup
|
||||
if (cached_result_id.has_value()) {
|
||||
if (auto* lookup = pending_lookups->find(*cached_result_id))
|
||||
return lookup->promise;
|
||||
}
|
||||
|
||||
pending_lookups->insert(query.header.id, { query.header.id, name, result->make_weak_ptr(), promise });
|
||||
return nullptr;
|
||||
});
|
||||
if (cached_entry) {
|
||||
dbgln_if(DNS_DEBUG, "DNS::lookup({}) -> Already in cache", name);
|
||||
return cached_entry.release_nonnull();
|
||||
}
|
||||
|
||||
ByteBuffer query_bytes;
|
||||
MUST(query.to_raw(query_bytes));
|
||||
|
||||
if (m_mode == ConnectionMode::TCP) {
|
||||
auto original_query_bytes = query_bytes;
|
||||
query_bytes = MUST(ByteBuffer::create_uninitialized(query_bytes.size() + sizeof(u16)));
|
||||
NetworkOrdered<u16> size = original_query_bytes.size();
|
||||
query_bytes.overwrite(0, &size, sizeof(size));
|
||||
query_bytes.overwrite(sizeof(size), original_query_bytes.data(), original_query_bytes.size());
|
||||
}
|
||||
|
||||
auto write_result = m_socket.with_write_locked([&](auto& socket) {
|
||||
return (*socket)->write_until_depleted(query_bytes.bytes());
|
||||
});
|
||||
if (write_result.is_error()) {
|
||||
promise->reject(write_result.release_error());
|
||||
return promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingLookup {
|
||||
u16 id { 0 };
|
||||
ByteString name;
|
||||
WeakPtr<LookupResult> result;
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> promise;
|
||||
};
|
||||
|
||||
ErrorOr<Messages::Message> parse_one_message()
|
||||
{
|
||||
if (m_mode == ConnectionMode::UDP)
|
||||
return m_socket.with_write_locked([&](auto& socket) { return Messages::Message::from_raw(**socket); });
|
||||
|
||||
return m_socket.with_write_locked([&](auto& socket) -> ErrorOr<Messages::Message> {
|
||||
if (!TRY((*socket)->can_read_without_blocking()))
|
||||
return Error::from_errno(EAGAIN);
|
||||
|
||||
auto size = TRY((*socket)->template read_value<NetworkOrdered<u16>>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(size));
|
||||
TRY((*socket)->read_until_filled(buffer));
|
||||
FixedMemoryStream stream { static_cast<ReadonlyBytes>(buffer) };
|
||||
return Messages::Message::from_raw(stream);
|
||||
});
|
||||
}
|
||||
|
||||
void process_incoming_messages()
|
||||
{
|
||||
while (true) {
|
||||
if (auto result = m_socket.with_read_locked([](auto& socket) { return (*socket)->can_read_without_blocking(); }); result.is_error() || !result.value())
|
||||
break;
|
||||
auto message_or_err = parse_one_message();
|
||||
if (message_or_err.is_error()) {
|
||||
if (!message_or_err.error().is_errno() || message_or_err.error().code() != EAGAIN)
|
||||
dbgln("DNS: Failed to receive message: {}", message_or_err.error());
|
||||
break;
|
||||
}
|
||||
|
||||
auto message = message_or_err.release_value();
|
||||
auto result = m_pending_lookups.with_write_locked([&](auto& lookups) -> ErrorOr<void> {
|
||||
auto* lookup = lookups->find(message.header.id);
|
||||
if (!lookup)
|
||||
return Error::from_string_literal("No pending lookup found for this message");
|
||||
|
||||
if (lookup->result.is_null())
|
||||
return {}; // Message is a response to a lookup that's been purged from the cache, ignore it
|
||||
|
||||
auto result = lookup->result.strong_ref();
|
||||
for (auto& record : message.answers)
|
||||
result->add_record(move(record));
|
||||
|
||||
lookup->promise->resolve(*result);
|
||||
lookups->remove(message.header.id);
|
||||
return {};
|
||||
});
|
||||
if (result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Received a message with no pending lookup: {}", result.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_connection(bool attempt_restart = true)
|
||||
{
|
||||
auto result = m_socket.with_read_locked(
|
||||
[&](auto& socket) { return socket.has_value() && (*socket)->is_open(); });
|
||||
|
||||
if (attempt_restart && !result && !m_attempting_restart) {
|
||||
TemporaryChange change(m_attempting_restart, true);
|
||||
auto create_result = m_create_socket();
|
||||
if (create_result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Failed to create socket: {}", create_result.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [socket, mode] = MUST(move(create_result));
|
||||
set_socket(move(socket), mode);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_socket(MaybeOwned<Core::Socket> socket, ConnectionMode mode = ConnectionMode::UDP)
|
||||
{
|
||||
m_mode = mode;
|
||||
m_socket.with_write_locked([&](auto& s) {
|
||||
s = move(socket);
|
||||
(*s)->on_ready_to_read = [this] {
|
||||
process_incoming_messages();
|
||||
};
|
||||
(*s)->set_notifications_enabled(true);
|
||||
});
|
||||
|
||||
for (auto& promise : m_socket_ready_promises)
|
||||
promise->resolve({});
|
||||
|
||||
m_socket_ready_promises.clear();
|
||||
}
|
||||
|
||||
void flush_cache()
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
HashTable<ByteString> to_remove;
|
||||
for (auto& entry : cache) {
|
||||
entry.value->check_expiration();
|
||||
if (!entry.value->is_valid())
|
||||
to_remove.set(entry.key);
|
||||
}
|
||||
for (auto const& key : to_remove)
|
||||
cache.remove(key);
|
||||
});
|
||||
}
|
||||
|
||||
Threading::RWLockProtected<HashMap<ByteString, NonnullRefPtr<LookupResult>>> m_cache;
|
||||
Threading::RWLockProtected<NonnullOwnPtr<RedBlackTree<u16, PendingLookup>>> m_pending_lookups;
|
||||
Threading::RWLockProtected<Optional<MaybeOwned<Core::Socket>>> m_socket;
|
||||
Function<ErrorOr<SocketResult>()> m_create_socket;
|
||||
bool m_attempting_restart { false };
|
||||
ConnectionMode m_mode { ConnectionMode::UDP };
|
||||
Vector<NonnullRefPtr<Core::Promise<Empty>>> m_socket_ready_promises;
|
||||
};
|
||||
|
||||
}
|
|
@ -10,11 +10,7 @@
|
|||
# pragma GCC optimize("O3")
|
||||
#endif
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/Line.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/BitmapSequence.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <AK/Swift.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <ctype.h>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <AK/Format.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SIMD.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Math.h>
|
||||
#include <LibGfx/DeltaE.h>
|
||||
#include <math.h>
|
||||
|
|
|
@ -12,18 +12,13 @@
|
|||
|
||||
#include "DeprecatedPainter.h"
|
||||
#include "Bitmap.h"
|
||||
#include "Font/Font.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Stack.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(AK_COMPILER_GCC)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
|
@ -16,8 +15,6 @@
|
|||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/BoundingBox.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/IntegralMath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
||||
struct hb_font_t;
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <core/SkTypeface.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontData.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
#include <AK/LsanSuppressions.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Font/TypefaceSkia.h>
|
||||
|
||||
#include <core/SkData.h>
|
||||
#include <core/SkFontMgr.h>
|
||||
#include <core/SkRefCnt.h>
|
||||
#include <core/SkTypeface.h>
|
||||
#ifndef AK_OS_ANDROID
|
||||
# include <ports/SkFontMgr_fontconfig.h>
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
class Profile;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <LibGfx/ICC/Profile.h>
|
||||
#include <LibGfx/ICC/Tags.h>
|
||||
#include <LibGfx/ICC/WellKnownProfiles.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <LibCompress/Lzw.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||
#include <LibGfx/ImageFormats/PNGLoader.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Error.h>
|
||||
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
|
||||
#include <jxl/decode.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <AK/LEB128.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/ImageFormats/TinyVGLoader.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCompress/DeflateTables.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/WebPSharedLossless.h>
|
||||
|
|
|
@ -13,20 +13,9 @@
|
|||
#include <LibGfx/PathSkia.h>
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkBlurTypes.h>
|
||||
#include <core/SkCanvas.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkMaskFilter.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathBuilder.h>
|
||||
#include <core/SkRRect.h>
|
||||
#include <core/SkSurface.h>
|
||||
#include <effects/SkGradientShader.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <gpu/GrDirectContext.h>
|
||||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <pathops/SkPathOps.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
|
||||
|
@ -16,9 +15,7 @@
|
|||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendContext.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendSurface.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlDirectContext.h>
|
||||
#endif
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/PathSkia.h>
|
||||
#include <core/SkContourMeasure.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <core/SkFont.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathMeasure.h>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "TextLayout.h"
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -7,16 +7,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Utf32View.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/FontCascadeList.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -15,6 +15,19 @@ public:
|
|||
HeaderMap() = default;
|
||||
~HeaderMap() = default;
|
||||
|
||||
HeaderMap(Vector<Header> headers)
|
||||
: m_headers(move(headers))
|
||||
{
|
||||
for (auto& header : m_headers)
|
||||
m_map.set(header.name, header.value);
|
||||
}
|
||||
|
||||
HeaderMap(HeaderMap const&) = default;
|
||||
HeaderMap(HeaderMap&&) = default;
|
||||
|
||||
HeaderMap& operator=(HeaderMap const&) = default;
|
||||
HeaderMap& operator=(HeaderMap&&) = default;
|
||||
|
||||
void set(ByteString name, ByteString value)
|
||||
{
|
||||
m_map.set(name, value);
|
||||
|
@ -56,10 +69,7 @@ template<>
|
|||
inline ErrorOr<HTTP::HeaderMap> decode(Decoder& decoder)
|
||||
{
|
||||
auto headers = TRY(decoder.decode<Vector<HTTP::Header>>());
|
||||
HTTP::HeaderMap header_map;
|
||||
for (auto& header : headers)
|
||||
header_map.set(move(header.name), move(header.value));
|
||||
return header_map;
|
||||
return HTTP::HeaderMap { move(headers) };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ void Request::set_up_internal_stream_data(DataReceived on_data_available)
|
|||
};
|
||||
|
||||
m_internal_stream_data->on_finish = [this, user_on_finish = move(user_on_finish)]() {
|
||||
if (!m_internal_stream_data->user_finish_called && m_internal_stream_data->read_stream->is_eof()) {
|
||||
if (!m_internal_stream_data->user_finish_called && (!m_internal_stream_data->read_stream || m_internal_stream_data->read_stream->is_eof())) {
|
||||
m_internal_stream_data->user_finish_called = true;
|
||||
user_on_finish(m_internal_stream_data->total_size, m_internal_stream_data->network_error);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,29 @@ ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, u16 port,
|
|||
return tls_socket;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(Core::SocketAddress address, ByteString const& host, Options options)
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
OwnPtr<Core::Socket> tcp_socket = TRY(Core::TCPSocket::connect(address));
|
||||
TRY(tcp_socket->set_blocking(false));
|
||||
auto tls_socket = make<TLSv12>(move(tcp_socket), move(options));
|
||||
tls_socket->set_sni(host);
|
||||
tls_socket->on_connected = [&] {
|
||||
promise->resolve({});
|
||||
};
|
||||
tls_socket->on_tls_error = [&](auto alert) {
|
||||
tls_socket->try_disambiguate_error();
|
||||
promise->reject(AK::Error::from_string_view(enum_to_string(alert)));
|
||||
};
|
||||
|
||||
TRY(promise->await());
|
||||
|
||||
tls_socket->on_tls_error = nullptr;
|
||||
tls_socket->on_connected = nullptr;
|
||||
tls_socket->m_context.should_expect_successful_read = true;
|
||||
return tls_socket;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, Core::Socket& underlying_stream, Options options)
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
|
@ -271,7 +294,8 @@ bool TLSv12::check_connection_state(bool read)
|
|||
|
||||
ErrorOr<bool> TLSv12::flush()
|
||||
{
|
||||
auto out_bytes = m_context.tls_buffer.bytes();
|
||||
ByteBuffer out = move(m_context.tls_buffer);
|
||||
auto out_bytes = out.bytes();
|
||||
|
||||
if (out_bytes.is_empty())
|
||||
return true;
|
||||
|
@ -298,17 +322,11 @@ ErrorOr<bool> TLSv12::flush()
|
|||
out_bytes = out_bytes.slice(written);
|
||||
} while (!out_bytes.is_empty());
|
||||
|
||||
if (out_bytes.is_empty() && !error.has_value()) {
|
||||
m_context.tls_buffer.clear();
|
||||
if (out_bytes.is_empty() && !error.has_value())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_context.send_retries++ == 10) {
|
||||
// drop the records, we can't send
|
||||
dbgln_if(TLS_DEBUG, "Dropping {} bytes worth of TLS records as max retries has been reached", m_context.tls_buffer.size());
|
||||
m_context.tls_buffer.clear();
|
||||
m_context.send_retries = 0;
|
||||
}
|
||||
if (!out_bytes.is_empty())
|
||||
dbgln("Dropping {} bytes worth of TLS records on the floor", out_bytes.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -357,6 +357,7 @@ public:
|
|||
|
||||
virtual void set_notifications_enabled(bool enabled) override { underlying_stream().set_notifications_enabled(enabled); }
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(Core::SocketAddress, ByteString const& host, Options = {});
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, u16 port, Options = {});
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, Core::Socket& underlying_stream, Options = {});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibWeb/Animations/Animation.h>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/GridFormattingContext.h>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Gradients.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibWeb/Painting/SVGPathPaintable.h>
|
||||
#include <LibWeb/Painting/SVGSVGPaintable.h>
|
||||
|
||||
|
|
|
@ -72,6 +72,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
Optional<StringView> profile_process;
|
||||
Optional<StringView> webdriver_content_ipc_path;
|
||||
Optional<StringView> user_agent_preset;
|
||||
Optional<StringView> dns_server_address;
|
||||
Optional<u16> dns_server_port;
|
||||
bool use_dns_over_tls = false;
|
||||
bool log_all_js_exceptions = false;
|
||||
bool enable_idl_tracing = false;
|
||||
bool enable_http_cache = false;
|
||||
|
@ -101,6 +104,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
|
||||
args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig");
|
||||
args_parser.add_option(collect_garbage_on_every_allocation, "Collect garbage after every JS heap allocation", "collect-garbage-on-every-allocation", 'g');
|
||||
args_parser.add_option(dns_server_address, "Set the DNS server address", "dns-server", 0, "host|address");
|
||||
args_parser.add_option(dns_server_port, "Set the DNS server port", "dns-port", 0, "port (default: 53 or 853 if --dot)");
|
||||
args_parser.add_option(use_dns_over_tls, "Use DNS over TLS", "dot");
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||
.help_string = "Name of the User-Agent preset to use in place of the default User-Agent",
|
||||
|
@ -120,6 +126,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
if (force_new_process)
|
||||
disable_sql_database = true;
|
||||
|
||||
if (!dns_server_port.has_value())
|
||||
dns_server_port = use_dns_over_tls ? 853 : 53;
|
||||
|
||||
Optional<ProcessType> debug_process_type;
|
||||
Optional<ProcessType> profile_process_type;
|
||||
|
||||
|
@ -140,6 +149,11 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
.disable_sql_database = disable_sql_database ? DisableSQLDatabase::Yes : DisableSQLDatabase::No,
|
||||
.debug_helper_process = move(debug_process_type),
|
||||
.profile_helper_process = move(profile_process_type),
|
||||
.dns_settings = (dns_server_address.has_value()
|
||||
? (use_dns_over_tls
|
||||
? DNSSettings(DNSOverTLS(dns_server_address.release_value(), *dns_server_port))
|
||||
: DNSSettings(DNSOverUDP(dns_server_address.release_value(), *dns_server_port)))
|
||||
: SystemDNS {}),
|
||||
};
|
||||
|
||||
if (webdriver_content_ipc_path.has_value())
|
||||
|
|
|
@ -163,7 +163,19 @@ ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process()
|
|||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<Requests::RequestClient>("RequestServer"sv, move(arguments));
|
||||
auto client = TRY(launch_server_process<Requests::RequestClient>("RequestServer"sv, move(arguments)));
|
||||
WebView::Application::chrome_options().dns_settings.visit(
|
||||
[](WebView::SystemDNS) {},
|
||||
[&](WebView::DNSOverTLS const& dns_over_tls) {
|
||||
dbgln("Setting DNS server to {}:{} with TLS", dns_over_tls.server_address, dns_over_tls.port);
|
||||
client->async_set_dns_server(dns_over_tls.server_address, dns_over_tls.port, true);
|
||||
},
|
||||
[&](WebView::DNSOverUDP const& dns_over_udp) {
|
||||
dbgln("Setting DNS server to {}:{}", dns_over_udp.server_address, dns_over_udp.port);
|
||||
client->async_set_dns_server(dns_over_udp.server_address, dns_over_udp.port, false);
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client()
|
||||
|
|
|
@ -45,6 +45,18 @@ enum class EnableAutoplay {
|
|||
Yes,
|
||||
};
|
||||
|
||||
struct SystemDNS { };
|
||||
struct DNSOverTLS {
|
||||
ByteString server_address;
|
||||
u16 port;
|
||||
};
|
||||
struct DNSOverUDP {
|
||||
ByteString server_address;
|
||||
u16 port;
|
||||
};
|
||||
|
||||
using DNSSettings = Variant<SystemDNS, DNSOverTLS, DNSOverUDP>;
|
||||
|
||||
struct ChromeOptions {
|
||||
Vector<URL::URL> urls;
|
||||
Vector<ByteString> raw_urls;
|
||||
|
@ -58,6 +70,7 @@ struct ChromeOptions {
|
|||
Optional<ProcessType> debug_helper_process {};
|
||||
Optional<ProcessType> profile_helper_process {};
|
||||
Optional<ByteString> webdriver_content_ipc_path {};
|
||||
DNSSettings dns_settings { SystemDNS {} };
|
||||
};
|
||||
|
||||
enum class IsLayoutTestMode {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/PathFontProvider.h>
|
||||
#include <LibWebView/Plugins/FontPlugin.h>
|
||||
|
|
|
@ -8,6 +8,7 @@ set(CSS_LOADER_DEBUG ON)
|
|||
set(CSS_PARSER_DEBUG ON)
|
||||
set(CSS_TOKENIZER_DEBUG ON)
|
||||
set(CSS_TRANSITIONS_DEBUG ON)
|
||||
set(DNS_DEBUG ON)
|
||||
set(EDITOR_DEBUG ON)
|
||||
set(EMOJI_DEBUG ON)
|
||||
set(FILE_WATCHER_DEBUG ON)
|
||||
|
|
|
@ -382,6 +382,7 @@ set(lagom_standard_libraries
|
|||
Compress
|
||||
Crypto
|
||||
Diff
|
||||
DNS
|
||||
GC
|
||||
HTTP
|
||||
IPC
|
||||
|
@ -448,6 +449,8 @@ endif()
|
|||
# Lagom Utilities
|
||||
lagom_utility(abench SOURCES ../../Utilities/abench.cpp LIBS LibMain LibFileSystem LibMedia)
|
||||
|
||||
lagom_utility(dns SOURCES ../../Utilities/dns.cpp LIBS LibDNS LibMain LibTLS LibCrypto)
|
||||
|
||||
if (ENABLE_GUI_TARGETS)
|
||||
lagom_utility(animation SOURCES ../../Utilities/animation.cpp LIBS LibGfx LibMain)
|
||||
lagom_utility(icc SOURCES ../../Utilities/icc.cpp LIBS LibGfx LibMain LibURL)
|
||||
|
|
|
@ -25,7 +25,7 @@ target_include_directories(requestserverservice PRIVATE ${CMAKE_CURRENT_BINARY_D
|
|||
target_include_directories(requestserverservice PRIVATE ${LADYBIRD_SOURCE_DIR}/Services/)
|
||||
|
||||
target_link_libraries(RequestServer PRIVATE requestserverservice)
|
||||
target_link_libraries(requestserverservice PUBLIC LibCore LibMain LibCrypto LibFileSystem LibIPC LibMain LibTLS LibWebView LibWebSocket LibURL LibTextCodec LibThreading CURL::libcurl)
|
||||
target_link_libraries(requestserverservice PUBLIC LibCore LibDNS LibMain LibCrypto LibFileSystem LibIPC LibMain LibTLS LibWebView LibWebSocket LibURL LibTextCodec LibThreading CURL::libcurl)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||
# Solaris has socket and networking related functions in two extra libraries
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <AK/Badge.h>
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
|
@ -24,6 +25,47 @@ ByteString g_default_certificate_path;
|
|||
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
|
||||
static IDAllocator s_client_ids;
|
||||
static long s_connect_timeout_seconds = 90L;
|
||||
static HashMap<ByteString, ByteString> g_dns_cache; // host -> curl "resolve" string
|
||||
static struct {
|
||||
Optional<Core::SocketAddress> server_address;
|
||||
Optional<ByteString> server_hostname;
|
||||
u16 port;
|
||||
bool use_dns_over_tls = true;
|
||||
} g_dns_info;
|
||||
|
||||
static WeakPtr<Resolver> s_resolver {};
|
||||
static NonnullRefPtr<Resolver> default_resolver()
|
||||
{
|
||||
if (auto resolver = s_resolver.strong_ref())
|
||||
return *resolver;
|
||||
auto resolver = make_ref_counted<Resolver>([] -> ErrorOr<DNS::Resolver::SocketResult> {
|
||||
if (!g_dns_info.server_address.has_value()) {
|
||||
if (!g_dns_info.server_hostname.has_value())
|
||||
return Error::from_string_literal("No DNS server configured");
|
||||
|
||||
auto resolved = TRY(default_resolver()->dns.lookup(*g_dns_info.server_hostname)->await());
|
||||
if (resolved->cached_addresses().is_empty())
|
||||
return Error::from_string_literal("Failed to resolve DNS server hostname");
|
||||
auto address = resolved->cached_addresses().first().visit([](auto& addr) -> Core::SocketAddress { return { addr, g_dns_info.port }; });
|
||||
g_dns_info.server_address = address;
|
||||
}
|
||||
|
||||
if (g_dns_info.use_dns_over_tls) {
|
||||
return DNS::Resolver::SocketResult {
|
||||
MaybeOwned<Core::Socket>(TRY(TLS::TLSv12::connect(*g_dns_info.server_address, *g_dns_info.server_hostname))),
|
||||
DNS::Resolver::ConnectionMode::TCP,
|
||||
};
|
||||
}
|
||||
|
||||
return DNS::Resolver::SocketResult {
|
||||
MaybeOwned<Core::Socket>(TRY(Core::BufferedSocket<Core::UDPSocket>::create(TRY(Core::UDPSocket::connect(*g_dns_info.server_address))))),
|
||||
DNS::Resolver::ConnectionMode::UDP,
|
||||
};
|
||||
});
|
||||
|
||||
s_resolver = resolver;
|
||||
return resolver;
|
||||
}
|
||||
|
||||
struct ConnectionFromClient::ActiveRequest {
|
||||
CURLM* multi { nullptr };
|
||||
|
@ -199,6 +241,7 @@ int ConnectionFromClient::on_timeout_callback(void*, long timeout_ms, void* user
|
|||
|
||||
ConnectionFromClient::ConnectionFromClient(IPC::Transport transport)
|
||||
: IPC::ConnectionFromClient<RequestClientEndpoint, RequestServerEndpoint>(*this, move(transport), s_client_ids.allocate())
|
||||
, m_resolver(default_resolver())
|
||||
{
|
||||
s_connections.set(client_id(), *this);
|
||||
|
||||
|
@ -264,6 +307,33 @@ Messages::RequestServer::IsSupportedProtocolResponse ConnectionFromClient::is_su
|
|||
return protocol == "http"sv || protocol == "https"sv;
|
||||
}
|
||||
|
||||
void ConnectionFromClient::set_dns_server(ByteString const& host_or_address, u16 port, bool use_tls)
|
||||
{
|
||||
if (host_or_address == g_dns_info.server_hostname && port == g_dns_info.port && use_tls == g_dns_info.use_dns_over_tls)
|
||||
return;
|
||||
|
||||
auto result = [&] -> ErrorOr<void> {
|
||||
Core::SocketAddress addr;
|
||||
if (auto v4 = IPv4Address::from_string(host_or_address); v4.has_value())
|
||||
addr = { v4.value(), port };
|
||||
else if (auto v6 = IPv6Address::from_string(host_or_address); v6.has_value())
|
||||
addr = { v6.value(), port };
|
||||
else
|
||||
TRY(default_resolver()->dns.lookup(host_or_address)->await())->cached_addresses().first().visit([&](auto& address) { addr = { address, port }; });
|
||||
|
||||
g_dns_info.server_address = addr;
|
||||
g_dns_info.server_hostname = host_or_address;
|
||||
g_dns_info.port = port;
|
||||
g_dns_info.use_dns_over_tls = use_tls;
|
||||
return {};
|
||||
}();
|
||||
|
||||
if (result.is_error())
|
||||
dbgln("Failed to set DNS server: {}", result.error());
|
||||
else
|
||||
default_resolver()->dns.reset_connection();
|
||||
}
|
||||
|
||||
void ConnectionFromClient::start_request(i32 request_id, ByteString const& method, URL::URL const& url, HTTP::HeaderMap const& request_headers, ByteBuffer const& request_body, Core::ProxyData const& proxy_data)
|
||||
{
|
||||
if (!url.is_valid()) {
|
||||
|
@ -272,6 +342,22 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString const& metho
|
|||
return;
|
||||
}
|
||||
|
||||
auto host = url.serialized_host().value().to_byte_string();
|
||||
auto dns_promise = m_resolver->dns.lookup(host, DNS::Messages::Class::IN, Array { DNS::Messages::ResourceType::A, DNS::Messages::ResourceType::AAAA }.span());
|
||||
auto resolve_result = dns_promise->await();
|
||||
if (resolve_result.is_error()) {
|
||||
dbgln("StartRequest: DNS lookup failed for '{}': {}", host, resolve_result.error());
|
||||
async_request_finished(request_id, 0, Requests::NetworkError::UnableToResolveHost);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dns_result = resolve_result.release_value();
|
||||
if (dns_result->records().is_empty()) {
|
||||
dbgln("StartRequest: DNS lookup failed for '{}'", host);
|
||||
async_request_finished(request_id, 0, Requests::NetworkError::UnableToResolveHost);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* easy = curl_easy_init();
|
||||
if (!easy) {
|
||||
dbgln("StartRequest: Failed to initialize curl easy handle");
|
||||
|
@ -349,6 +435,24 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString const& metho
|
|||
set_option(CURLOPT_HEADERFUNCTION, &on_header_received);
|
||||
set_option(CURLOPT_HEADERDATA, reinterpret_cast<void*>(request.ptr()));
|
||||
|
||||
StringBuilder resolve_opt_builder;
|
||||
resolve_opt_builder.appendff("{}:{}:", host, url.port_or_default());
|
||||
auto first = true;
|
||||
for (auto& addr : dns_result->cached_addresses()) {
|
||||
auto formatted_address = addr.visit(
|
||||
[&](IPv4Address const& ipv4) { return ipv4.to_byte_string(); },
|
||||
[&](IPv6Address const& ipv6) { return MUST(ipv6.to_string()).to_byte_string(); });
|
||||
if (!first)
|
||||
resolve_opt_builder.append(',');
|
||||
first = false;
|
||||
resolve_opt_builder.append(formatted_address);
|
||||
}
|
||||
|
||||
auto formatted_address = resolve_opt_builder.to_byte_string();
|
||||
g_dns_cache.set(host, formatted_address);
|
||||
curl_slist* resolve_list = curl_slist_append(nullptr, formatted_address.characters());
|
||||
curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve_list);
|
||||
|
||||
auto result = curl_multi_add_handle(m_curl_multi, easy);
|
||||
VERIFY(result == CURLM_OK);
|
||||
|
||||
|
@ -495,7 +599,16 @@ void ConnectionFromClient::ensure_connection(URL::URL const& url, ::RequestServe
|
|||
}
|
||||
|
||||
if (cache_level == CacheLevel::ResolveOnly) {
|
||||
dbgln("FIXME: EnsureConnection: Implement ResolveOnly cache level");
|
||||
[[maybe_unused]] auto promise = m_resolver->dns.lookup(url.serialized_host().value().to_byte_string(), DNS::Messages::Class::IN, Array { DNS::Messages::ResourceType::A, DNS::Messages::ResourceType::AAAA }.span());
|
||||
if constexpr (REQUESTSERVER_DEBUG) {
|
||||
Core::ElapsedTimer timer;
|
||||
timer.start();
|
||||
promise->when_resolved([url, timer](auto const& results) -> ErrorOr<void> {
|
||||
dbgln("ensure_connection::ResolveOnly({}) OK {} entrie(s) in {}ms", url, results->cached_addresses().size(), timer.elapsed());
|
||||
return {};
|
||||
});
|
||||
promise->when_rejected([url](auto const&) { dbgln("ensure_connection::ResolveOnly({}) rejected", url); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibDNS/Resolver.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibWebSocket/WebSocket.h>
|
||||
#include <RequestServer/Forward.h>
|
||||
|
@ -15,6 +16,16 @@
|
|||
|
||||
namespace RequestServer {
|
||||
|
||||
struct Resolver : public RefCounted<Resolver>
|
||||
, Weakable<Resolver> {
|
||||
Resolver(Function<ErrorOr<DNS::Resolver::SocketResult>()> create_socket)
|
||||
: dns(move(create_socket))
|
||||
{
|
||||
}
|
||||
|
||||
DNS::Resolver dns;
|
||||
};
|
||||
|
||||
class ConnectionFromClient final
|
||||
: public IPC::ConnectionFromClient<RequestClientEndpoint, RequestServerEndpoint> {
|
||||
C_OBJECT(ConnectionFromClient);
|
||||
|
@ -34,6 +45,7 @@ private:
|
|||
|
||||
virtual Messages::RequestServer::ConnectNewClientResponse connect_new_client() override;
|
||||
virtual Messages::RequestServer::IsSupportedProtocolResponse is_supported_protocol(ByteString const&) override;
|
||||
virtual void set_dns_server(ByteString const& host_or_address, u16 port, bool use_tls) override;
|
||||
virtual void start_request(i32 request_id, ByteString const&, URL::URL const&, HTTP::HeaderMap const&, ByteBuffer const&, Core::ProxyData const&) override;
|
||||
virtual Messages::RequestServer::StopRequestResponse stop_request(i32) override;
|
||||
virtual Messages::RequestServer::SetCertificateResponse set_certificate(i32, ByteString const&, ByteString const&) override;
|
||||
|
@ -61,6 +73,7 @@ private:
|
|||
RefPtr<Core::Timer> m_timer;
|
||||
HashMap<int, NonnullRefPtr<Core::Notifier>> m_read_notifiers;
|
||||
HashMap<int, NonnullRefPtr<Core::Notifier>> m_write_notifiers;
|
||||
NonnullRefPtr<Resolver> m_resolver;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ endpoint RequestServer
|
|||
{
|
||||
connect_new_client() => (IPC::File client_socket)
|
||||
|
||||
// use_tls: enable DNS over TLS
|
||||
set_dns_server(ByteString host_or_address, u16 port, bool use_tls) =|
|
||||
|
||||
// Test if a specific protocol is supported, e.g "http"
|
||||
is_supported_protocol(ByteString protocol) => (bool supported)
|
||||
|
||||
|
|
132
Utilities/dns.cpp
Normal file
132
Utilities/dns.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibDNS/Resolver.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibTLS/TLSv12.h>
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
struct Request {
|
||||
Vector<DNS::Messages::ResourceType> types;
|
||||
ByteString name;
|
||||
};
|
||||
Vector<Request> requests;
|
||||
Request current_request;
|
||||
StringView server_address;
|
||||
StringView cert_path;
|
||||
bool use_tls = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(cert_path, "Path to the CA certificate file", "ca-certs", 'C', "file");
|
||||
args_parser.add_option(server_address, "The address of the DNS server to query", "server", 's', "addr");
|
||||
args_parser.add_option(use_tls, "Use TLS to connect to the server", "tls", 0);
|
||||
args_parser.add_positional_argument(Core::ArgsParser::Arg {
|
||||
.help_string = "The resource types and name of the DNS record to query",
|
||||
.name = "rr,rr@name",
|
||||
.min_values = 1,
|
||||
.max_values = 99999,
|
||||
.accept_value = [&](StringView name) -> ErrorOr<bool> {
|
||||
auto parts = name.split_view('@');
|
||||
if (parts.size() > 2 || parts.is_empty())
|
||||
return Error::from_string_literal("Invalid record/name format");
|
||||
|
||||
if (parts.size() == 1) {
|
||||
current_request.types.append(DNS::Messages::ResourceType::ANY);
|
||||
current_request.name = parts[0];
|
||||
} else {
|
||||
auto rr_parts = parts[0].split_view(',');
|
||||
for (auto& rr : rr_parts) {
|
||||
ByteString rr_name = rr;
|
||||
auto type = DNS::Messages::resource_type_from_string(rr_name.to_uppercase());
|
||||
if (!type.has_value())
|
||||
return Error::from_string_literal("Invalid resource type");
|
||||
current_request.types.append(type.value());
|
||||
}
|
||||
current_request.name = parts[1];
|
||||
}
|
||||
requests.append(move(current_request));
|
||||
current_request = {};
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (server_address.is_empty()) {
|
||||
outln("You must specify a server address to query");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (current_request.name.is_empty() && !current_request.types.is_empty()) {
|
||||
outln("You must specify at least one DNS record to query");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::EventLoop loop;
|
||||
|
||||
DNS::Resolver resolver {
|
||||
[&] -> ErrorOr<DNS::Resolver::SocketResult> {
|
||||
Core::SocketAddress addr;
|
||||
if (auto v4 = IPv4Address::from_string(server_address); v4.has_value()) {
|
||||
addr = { v4.value(), static_cast<u16>(use_tls ? 853 : 53) };
|
||||
} else if (auto v6 = IPv6Address::from_string(server_address); v6.has_value()) {
|
||||
addr = { v6.value(), static_cast<u16>(use_tls ? 853 : 53) };
|
||||
} else {
|
||||
return MUST(resolver.lookup(server_address)->await())->cached_addresses().first().visit([&](auto& address) -> DNS::Resolver::SocketResult {
|
||||
if (use_tls) {
|
||||
auto tls = MUST(TLS::TLSv12::connect({ address, 853 }, server_address));
|
||||
return { move(tls), DNS::Resolver::ConnectionMode::TCP };
|
||||
}
|
||||
return { MUST(Core::BufferedSocket<Core::UDPSocket>::create(MUST(Core::UDPSocket::connect({ address, 53 })))), DNS::Resolver::ConnectionMode::UDP };
|
||||
});
|
||||
}
|
||||
|
||||
if (use_tls)
|
||||
return DNS::Resolver::SocketResult { MUST(TLS::TLSv12::connect(addr, server_address)), DNS::Resolver::ConnectionMode::TCP };
|
||||
return DNS::Resolver::SocketResult { MUST(Core::BufferedSocket<Core::UDPSocket>::create(MUST(Core::UDPSocket::connect(addr)))), DNS::Resolver::ConnectionMode::UDP };
|
||||
}
|
||||
};
|
||||
|
||||
DefaultRootCACertificates::set_default_certificate_paths(Array<ByteString, 1> { cert_path.is_empty() ? "/etc/ssl/cert.pem"sv : cert_path });
|
||||
|
||||
MUST(resolver.when_socket_ready()->await());
|
||||
|
||||
size_t pending_requests = requests.size();
|
||||
for (auto& request : requests) {
|
||||
resolver.lookup(request.name, DNS::Messages::Class::IN, request.types)
|
||||
->when_resolved([&](auto& result) {
|
||||
outln("Resolved {}:", request.name);
|
||||
HashTable<DNS::Messages::ResourceType> types;
|
||||
auto recs = result->records();
|
||||
for (auto& record : recs)
|
||||
types.set(record.type);
|
||||
|
||||
for (auto& type : types) {
|
||||
outln(" - {} IN {}:", request.name, DNS::Messages::to_string(type));
|
||||
for (auto& record : recs) {
|
||||
if (type != record.type)
|
||||
continue;
|
||||
|
||||
outln(" - {}", record.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if (--pending_requests == 0)
|
||||
loop.quit(0);
|
||||
})
|
||||
.when_rejected([&](auto& error) {
|
||||
outln("Failed to resolve {} IN {}: {}", request.name, DNS::Messages::to_string(request.types.first()), error);
|
||||
if (--pending_requests == 0)
|
||||
loop.quit(1);
|
||||
});
|
||||
}
|
||||
|
||||
return loop.exec();
|
||||
}
|
Loading…
Reference in a new issue