/* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3.0 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3.0 along with this program. If not, see * <https://www.gnu.org/licenses/>. */ #import "OFObject.h" #import "OFDNSQuery.h" #import "OFDNSResourceRecord.h" #import "OFDNSResponse.h" #import "OFRunLoop.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN #define OFDNSResolverBufferLength 512 @class OFArray OF_GENERIC(ObjectType); @class OFDNSResolver; @class OFDNSResolverContext; @class OFDNSResolverSettings; @class OFDate; @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFMutableArray OF_GENERIC(ObjectType); @class OFMutableDictionary OF_GENERIC(KeyType, ObjectType); @class OFNumber; @class OFPair OF_GENERIC(FirstType, SecondType); @class OFTCPSocket; @class OFUDPSocket; /** * @enum OFDNSResolverErrorCode OFDNSResolver.h ObjFW/ObjFW.h * * @brief An enum describing why resolving a host failed. */ typedef enum { /** An unknown error */ OFDNSResolverErrorCodeUnknown, /** The query timed out */ OFDNSResolverErrorCodeTimeout, /** The query was canceled */ OFDNSResolverErrorCodeCanceled, /** * No result for the specified host with the specified type and class. * * This is only used in situations where this is an error, e.g. when * trying to connect to a host. */ OFDNSResolverErrorCodeNoResult, /** The server considered the query to be malformed */ OFDNSResolverErrorCodeServerInvalidFormat, /** The server was unable to process due to an internal error */ OFDNSResolverErrorCodeServerFailure, /** The server returned an error that the domain does not exist */ OFDNSResolverErrorCodeServerNameError, /** The server does not have support for the requested query */ OFDNSResolverErrorCodeServerNotImplemented, /** The server refused the query */ OFDNSResolverErrorCodeServerRefused, /** There was no name server to query */ OFDNSResolverErrorCodeNoNameServer } OFDNSResolverErrorCode; /** * @protocol OFDNSResolverQueryDelegate OFDNSResolver.h ObjFW/ObjFW.h * * @brief A delegate for performed DNS queries. */ @protocol OFDNSResolverQueryDelegate <OFObject> /** * @brief This method is called when a DNS resolver performed a query. * * @param resolver The acting resolver * @param query The query performed by the resolver * @param response The response from the DNS server, or nil on error * @param exception An exception that happened during resolving, or nil on * success */ - (void)resolver: (OFDNSResolver *)resolver didPerformQuery: (OFDNSQuery *)query response: (nullable OFDNSResponse *)response exception: (nullable id)exception; @end /** * @protocol OFDNSResolverQueryDelegate OFDNSResolver.h ObjFW/ObjFW.h * * @brief A delegate for resolved hosts. */ @protocol OFDNSResolverHostDelegate <OFObject> /** * @brief This method is called when a DNS resolver resolved a host to * addresses. * * @param resolver The acting resolver * @param host The host the resolver resolved * @param addresses OFData containing several OFSocketAddress * @param exception The exception that occurred during resolving, or nil on * success */ - (void)resolver: (OFDNSResolver *)resolver didResolveHost: (OFString *)host addresses: (nullable OFData *)addresses exception: (nullable id)exception; @end /** * @class OFDNSResolver OFDNSResolver.h ObjFW/ObjFW.h * * @brief A class for resolving DNS names. * * @note If you change any of the properties, make sure to set * @ref configReloadInterval to 0, as otherwise your changes will be * reverted back to the system configuration on the next periodic config * reload. */ OF_SUBCLASSING_RESTRICTED @interface OFDNSResolver: OFObject { OFDNSResolverSettings *_settings; OFUDPSocket *_IPv4Socket; #ifdef OF_HAVE_IPV6 OFUDPSocket *_IPv6Socket; #endif char _buffer[OFDNSResolverBufferLength]; OFMutableDictionary OF_GENERIC(OFNumber *, OFDNSResolverContext *) *_queries; OFMutableDictionary OF_GENERIC(OFTCPSocket *, OFDNSResolverContext *) *_TCPQueries; OFMutableDictionary OF_GENERIC(OFDNSQuery *, OFPair OF_GENERIC(OFDate *, OFDNSResponse *) *) *_cache; OFMutableArray OF_GENERIC(OFString *) *_lastNameServers; OFTimeInterval _lastCacheCleanup; } /** * @brief A dictionary of static hosts. * * This dictionary is checked before actually looking up a host. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (copy, nonatomic) OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(OFString *) *) *staticHosts; /** * @brief An array of name servers to use. * * The name servers are tried in order. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (copy, nonatomic) OFArray OF_GENERIC(OFString *) *nameServers; /** * @brief The local domain. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *localDomain; /** * @brief The domains to search for queries for short names. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (copy, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains; /** * @brief The timeout, in seconds, after which the next name server should be * tried. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (nonatomic) OFTimeInterval timeout; /** * @brief The number of attempts before giving up to resolve a host. * * Trying all name servers once is considered a single attempt. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (nonatomic) unsigned int maxAttempts; /** * @brief The minimum number of dots for a name to be considered absolute. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (nonatomic) unsigned int minNumberOfDotsInAbsoluteName; /** * @brief Whether the resolver forces TCP to talk to a name server. * * @warning If you change this, you need to set @ref configReloadInterval to 0 * to disable reloading the config after some time. If you don't, the * config will be reloaded and your change overridden. */ @property (nonatomic) bool forcesTCP; /** * @brief The interval in seconds in which the config should be reloaded. * * Setting this to 0 disables config reloading. * * @warning If you change this to anything other than 0, the config will be * reloaded eventually, which in turn can override the config * reloading interval itself again. */ @property (nonatomic) OFTimeInterval configReloadInterval; /** * @brief Creates a new, autoreleased OFDNSResolver. */ + (instancetype)resolver; /** * @brief Initializes an already allocated OFDNSResolver. */ - (instancetype)init; /** * @brief Asynchronously performs the specified query. * * @param query The query to perform * @param delegate The delegate to use for callbacks */ - (void)asyncPerformQuery: (OFDNSQuery *)query delegate: (id <OFDNSResolverQueryDelegate>)delegate; /** * @brief Asynchronously performs the specified query. * * @param query The query to perform * @param runLoopMode The run loop mode in which to resolve * @param delegate The delegate to use for callbacks */ - (void)asyncPerformQuery: (OFDNSQuery *)query runLoopMode: (OFRunLoopMode)runLoopMode delegate: (id <OFDNSResolverQueryDelegate>)delegate; /** * @brief Asynchronously resolves the specified host to socket addresses. * * @param host The host to resolve * @param delegate The delegate to use for callbacks */ - (void)asyncResolveAddressesForHost: (OFString *)host delegate: (id <OFDNSResolverHostDelegate>)delegate; /** * @brief Asynchronously resolves the specified host to socket addresses. * * @param host The host to resolve * @param addressFamily The desired socket address family * @param delegate The delegate to use for callbacks */ - (void)asyncResolveAddressesForHost: (OFString *)host addressFamily: (OFSocketAddressFamily)addressFamily delegate: (id <OFDNSResolverHostDelegate>)delegate; /** * @brief Asynchronously resolves the specified host to socket addresses. * * @param host The host to resolve * @param addressFamily The desired socket address family * @param runLoopMode The run loop mode in which to resolve * @param delegate The delegate to use for callbacks */ - (void)asyncResolveAddressesForHost: (OFString *)host addressFamily: (OFSocketAddressFamily)addressFamily runLoopMode: (OFRunLoopMode)runLoopMode delegate: (id <OFDNSResolverHostDelegate>)delegate; /** * @brief Synchronously resolves the specified host to socket addresses. * * @param host The host to resolve * @param addressFamily The desired socket address family * @return OFData containing several OFSocketAddress * @throw OFInvalidServerResponseException The received response was invalid * @throw OFTruncatedDataException The received response was truncated */ - (OFData *)resolveAddressesForHost: (OFString *)host addressFamily: (OFSocketAddressFamily)addressFamily; /** * @brief Closes all sockets and cancels all ongoing queries. */ - (void)close; @end OF_ASSUME_NONNULL_END