Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -126,11 +126,12 @@ OFINICategory.m \ OFINIFile.m \ OFSettings.m \ OFString+PathAdditions.m SRCS_PLUGINS = OFPlugin.m -SRCS_SOCKETS = OFHTTPServer.m \ +SRCS_SOCKETS = OFDNSResolver.m \ + OFHTTPServer.m \ OFStreamSocket.m \ OFTCPSocket.m \ OFUDPSocket.m \ resolver.m \ socket.m ADDED src/OFDNSResolver.h Index: src/OFDNSResolver.h ================================================================== --- src/OFDNSResolver.h +++ src/OFDNSResolver.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFDictionary OF_GENERIC(KeyType, ObjectType); + +/*! + * @class OFDNSResolver OFDNSResolver.h ObjFW/OFDNSResolver.h + * + * @brief A class for resolving DNS names. + */ +@interface OFDNSResolver: OFObject +{ + OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(OFString *) *) + *_staticHosts; + OFArray OF_GENERIC(OFString *) *_nameServers; + OFString *_Nullable _localDomain; + OFArray OF_GENERIC(OFString *) *_searchDomains; + size_t _minNumberOfDotsInAbsoluteName; + bool _usesTCP; +} + +/*! + * @brief A dictionary of static hosts. + * + * This dictionary is checked before actually looking up a host. + */ +@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. + */ +@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. + */ +@property (copy, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains; + +/*! + * @brief The minimum number of dots for a name to be considered absolute. + */ +@property (nonatomic) size_t minNumberOfDotsInAbsoluteName; + +/*! + * @brief Whether the resolver uses TCP to talk to a name server. + */ +@property (nonatomic) bool usesTCP; + +/*! + * @brief Creates a new, autoreleased OFDNSResolver. + */ ++ (instancetype)resolver; + +/*! + * @brief Initializes an already allocated OFDNSResolver. + */ +- (instancetype)init; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFDNSResolver.m Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include "unistd_wrapper.h" + +#import "OFDNSResolver.h" +#import "OFArray.h" +#import "OFCharacterSet.h" +#import "OFDictionary.h" +#import "OFFile.h" +#import "OFLocalization.h" +#import "OFString.h" + +#import "OFOpenItemFailedException.h" + +@interface OFDNSResolver () +#ifdef OF_HAVE_FILES +- (void)of_parseHosts: (OFString *)path; +- (void)of_parseResolvConf: (OFString *)path; +- (void)of_parseResolvConfOption: (OFString *)option; +#endif +@end + +static OFString * +domainFromHostname(void) +{ + char hostname[256]; + char *domain; + + if (gethostname(hostname, 256) != 0) + return nil; + + if ((domain = strchr(hostname, '.')) == NULL) + return nil; + + return [OFString stringWithCString: domain + 1 + encoding: [OFLocalization encoding]]; +} + +@implementation OFDNSResolver +@synthesize staticHosts = _staticHosts, nameServers = _nameServers; +@synthesize localDomain = _localDomain, searchDomains = _searchDomains; +@synthesize minNumberOfDotsInAbsoluteName = _minNumberOfDotsInAbsoluteName; +@synthesize usesTCP = _usesTCP; + ++ (instancetype)resolver +{ + return [[[self alloc] init] autorelease]; +} + +- (instancetype)init +{ + self = [super init]; + + @try { + _minNumberOfDotsInAbsoluteName = 1; + +#ifdef OF_HAVE_FILES + [self of_parseHosts: @"/etc/hosts"]; + [self of_parseResolvConf: @"/etc/resolv.conf"]; + [self of_parseResolvConf: @"/etc/resolv.conf.tail"]; +#endif + + if (_staticHosts == nil) + _staticHosts = [[OFDictionary alloc] init]; + + if (_nameServers == nil) + _nameServers = [[OFArray alloc] initWithObjects: + @"127.0.0.1", @"::1", nil]; + + if (_localDomain == nil) + _localDomain = [domainFromHostname() copy]; + + if (_searchDomains == nil) { + if (_localDomain != nil) + _searchDomains = [[OFArray alloc] + initWithObject: _localDomain]; + else + _searchDomains = [[OFArray alloc] init]; + } + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_staticHosts release]; + [_nameServers release]; + [_localDomain release]; + [_searchDomains release]; + + [super dealloc]; +} + +#ifdef OF_HAVE_FILES +- (void)of_parseHosts: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + OFCharacterSet *whitespaceCharacterSet = + [OFCharacterSet whitespaceCharacterSet]; + OFMutableDictionary *staticHosts; + OFFile *file; + OFString *line; + OFEnumerator *enumerator; + OFMutableArray *addresses; + + @try { + file = [OFFile fileWithPath: path + mode: @"r"]; + } @catch (OFOpenItemFailedException *e) { + objc_autoreleasePoolPop(pool); + return; + } + + staticHosts = [OFMutableDictionary dictionary]; + + while ((line = [file readLine]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + OFArray *components, *hosts; + size_t pos; + OFString *address; + + pos = [line rangeOfString: @"#"].location; + if (pos != OF_NOT_FOUND) + line = [line substringWithRange: of_range(0, pos)]; + + components = [line + componentsSeparatedByCharactersInSet: whitespaceCharacterSet + options: OF_STRING_SKIP_EMPTY]; + + if ([components count] < 2) { + objc_autoreleasePoolPop(pool2); + continue; + } + + address = [components firstObject]; + hosts = [components objectsInRange: + of_range(1, [components count] - 1)]; + + for (OFString *host in hosts) { + addresses = [staticHosts objectForKey: host]; + + if (addresses == nil) { + addresses = [OFMutableArray array]; + [staticHosts setObject: addresses + forKey: host]; + } + + [addresses addObject: address]; + } + + objc_autoreleasePoolPop(pool2); + } + + enumerator = [staticHosts objectEnumerator]; + while ((addresses = [enumerator nextObject]) != nil) + [addresses makeImmutable]; + + [staticHosts makeImmutable]; + + [_staticHosts release]; + _staticHosts = [staticHosts copy]; + + objc_autoreleasePoolPop(pool); +} + +- (void)of_parseResolvConf: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + OFCharacterSet *whitespaceCharacterSet = + [OFCharacterSet whitespaceCharacterSet]; + OFCharacterSet *commentCharacters = [OFCharacterSet + characterSetWithCharactersInString: @"#;"]; + OFMutableArray *nameServers = [[_nameServers mutableCopy] autorelease]; + OFFile *file; + OFString *line; + + @try { + file = [OFFile fileWithPath: path + mode: @"r"]; + } @catch (OFOpenItemFailedException *e) { + objc_autoreleasePoolPop(pool); + return; + } + + if (nameServers == nil) + nameServers = [OFMutableArray array]; + + while ((line = [file readLine]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + size_t pos; + OFArray *components, *arguments; + OFString *option; + + pos = [line indexOfCharacterFromSet: commentCharacters]; + if (pos != OF_NOT_FOUND) + line = [line substringWithRange: of_range(0, pos)]; + + components = [line + componentsSeparatedByCharactersInSet: whitespaceCharacterSet + options: OF_STRING_SKIP_EMPTY]; + + if ([components count] < 2) { + objc_autoreleasePoolPop(pool2); + continue; + } + + option = [components firstObject]; + arguments = [components objectsInRange: + of_range(1, [components count] - 1)]; + + if ([option isEqual: @"nameserver"]) { + if ([arguments count] != 1) { + objc_autoreleasePoolPop(pool2); + continue; + } + + [nameServers addObject: [arguments firstObject]]; + } else if ([option isEqual: @"domain"]) { + if ([arguments count] != 1) { + objc_autoreleasePoolPop(pool2); + continue; + } + + [_localDomain release]; + _localDomain = [[arguments firstObject] copy]; + } else if ([option isEqual: @"search"]) { + [_searchDomains release]; + _searchDomains = [arguments copy]; + } else if ([option isEqual: @"options"]) + for (OFString *argument in arguments) + [self of_parseResolvConfOption: argument]; + + objc_autoreleasePoolPop(pool2); + } + + [nameServers makeImmutable]; + + [_nameServers release]; + _nameServers = [nameServers copy]; + + objc_autoreleasePoolPop(pool); +} + +- (void)of_parseResolvConfOption: (OFString *)option +{ + if ([option hasPrefix: @"ndots:"]) { + option = [option substringWithRange: + of_range(6, [option length] - 6)]; + + @try { + _minNumberOfDotsInAbsoluteName = + (size_t)[option decimalValue]; + } @catch (id e) { + return; + } + } else if ([option isEqual: @"tcp"]) + _usesTCP = true; +} +#endif +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -74,10 +74,11 @@ # import "OFStreamSocket.h" # import "OFTCPSocket.h" # import "OFUDPSocket.h" # import "OFTLSSocket.h" # import "OFKernelEventObserver.h" +# import "OFDNSResolver.h" #endif #import "OFHTTPCookie.h" #import "OFHTTPCookieManager.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h"