|
1 | 1 | #import "MTDNS.h"
|
2 | 2 |
|
| 3 | +#import <arpa/inet.h> |
| 4 | +#include <netinet/tcp.h> |
| 5 | +#import <fcntl.h> |
| 6 | +#import <ifaddrs.h> |
| 7 | +#import <netdb.h> |
| 8 | +#import <netinet/in.h> |
| 9 | +#import <net/if.h> |
| 10 | + |
3 | 11 | #if defined(MtProtoKitDynamicFramework)
|
| 12 | +# import <MTProtoKitDynamic/MTQueue.h> |
4 | 13 | # import <MTProtoKitDynamic/MTSignal.h>
|
| 14 | +# import <MTProtoKitDynamic/MTBag.h> |
5 | 15 | # import <MTProtoKitDynamic/MTAtomic.h>
|
6 | 16 | # import <MTProtoKitDynamic/MTHttpRequestOperation.h>
|
7 | 17 | # import <MTProtoKitDynamic/MTEncryption.h>
|
|
15 | 25 | # import <MTProtoKitDynamic/MTSerialization.h>
|
16 | 26 | # import <MTProtoKitDynamic/MTLogging.h>
|
17 | 27 | #elif defined(MtProtoKitMacFramework)
|
| 28 | +# import <MTProtoKitMac/MTQueue.h> |
18 | 29 | # import <MTProtoKitMac/MTSignal.h>
|
| 30 | +# import <MTProtoKitMac/MTBag.h> |
19 | 31 | # import <MTProtoKitMac/MTAtomic.h>
|
20 | 32 | # import <MTProtoKitMac/MTHttpRequestOperation.h>
|
21 | 33 | # import <MTProtoKitMac/MTEncryption.h>
|
|
29 | 41 | # import <MTProtoKitMac/MTSerialization.h>
|
30 | 42 | # import <MTProtoKitMac/MTLogging.h>
|
31 | 43 | #else
|
| 44 | +# import <MTProtoKit/MTQueue.h> |
32 | 45 | # import <MTProtoKit/MTSignal.h>
|
| 46 | +# import <MTProtoKit/MTBag.h> |
33 | 47 | # import <MTProtoKit/MTAtomic.h>
|
34 | 48 | # import <MTProtoKit/MTHttpRequestOperation.h>
|
35 | 49 | # import <MTProtoKit/MTEncryption.h>
|
|
47 | 61 | #import <netinet/in.h>
|
48 | 62 | #import <arpa/inet.h>
|
49 | 63 |
|
| 64 | +@interface MTDNSHostContext : NSObject { |
| 65 | + MTBag *_subscribers; |
| 66 | + id<MTDisposable> _disposable; |
| 67 | +} |
| 68 | + |
| 69 | +@end |
| 70 | + |
| 71 | +@implementation MTDNSHostContext |
| 72 | + |
| 73 | +- (instancetype)initWithHost:(NSString *)host disposable:(id<MTDisposable>)disposable { |
| 74 | + self = [super init]; |
| 75 | + if (self != nil) { |
| 76 | + _subscribers = [[MTBag alloc] init]; |
| 77 | + } |
| 78 | + return self; |
| 79 | +} |
| 80 | + |
| 81 | +- (void)dealloc { |
| 82 | + [_disposable dispose]; |
| 83 | +} |
| 84 | + |
| 85 | +- (NSInteger)addSubscriber:(void (^)(NSString *))completion { |
| 86 | + return [_subscribers addItem:[completion copy]]; |
| 87 | +} |
| 88 | + |
| 89 | +- (void)removeSubscriber:(NSInteger)index { |
| 90 | + [_subscribers removeItem:index]; |
| 91 | +} |
| 92 | + |
| 93 | +- (bool)isEmpty { |
| 94 | + return [_subscribers isEmpty]; |
| 95 | +} |
| 96 | + |
| 97 | +- (void)complete:(NSString *)result { |
| 98 | + for (void (^completion)(NSString *) in [_subscribers copyItems]) { |
| 99 | + completion(result); |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +@end |
| 104 | + |
| 105 | +@interface MTDNSContext : NSObject { |
| 106 | + NSMutableDictionary<NSString *, MTDNSHostContext *> *_contexts; |
| 107 | +} |
| 108 | + |
| 109 | +@end |
| 110 | + |
| 111 | +@implementation MTDNSContext |
| 112 | + |
| 113 | ++ (MTQueue *)sharedQueue { |
| 114 | + static MTQueue *queue = nil; |
| 115 | + static dispatch_once_t onceToken; |
| 116 | + dispatch_once(&onceToken, ^{ |
| 117 | + queue = [[MTQueue alloc] init]; |
| 118 | + }); |
| 119 | + return queue; |
| 120 | +} |
| 121 | + |
| 122 | ++ (MTSignal *)shared { |
| 123 | + return [[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) { |
| 124 | + static MTDNSContext *instance = nil; |
| 125 | + static dispatch_once_t onceToken; |
| 126 | + dispatch_once(&onceToken, ^{ |
| 127 | + instance = [[MTDNSContext alloc] init]; |
| 128 | + }); |
| 129 | + [subscriber putNext:instance]; |
| 130 | + [subscriber putCompletion]; |
| 131 | + return nil; |
| 132 | + }] startOn:[self sharedQueue]]; |
| 133 | +} |
| 134 | + |
| 135 | +- (instancetype)init { |
| 136 | + self = [super init]; |
| 137 | + if (self != nil) { |
| 138 | + _contexts = [[NSMutableDictionary alloc] init]; |
| 139 | + } |
| 140 | + return self; |
| 141 | +} |
| 142 | + |
| 143 | +- (id<MTDisposable>)subscribe:(NSString *)host port:(int32_t)port completion:(void (^)(NSString *))completion { |
| 144 | + NSString *key = [NSString stringWithFormat:@"%@:%d", host, port]; |
| 145 | + |
| 146 | + MTMetaDisposable *disposable = nil; |
| 147 | + if (_contexts[key] == nil) { |
| 148 | + disposable = [[MTMetaDisposable alloc] init]; |
| 149 | + _contexts[key] = [[MTDNSHostContext alloc] initWithHost:host disposable:disposable]; |
| 150 | + } |
| 151 | + MTDNSHostContext *context = _contexts[key]; |
| 152 | + |
| 153 | + NSInteger index = [context addSubscriber:^(NSString *result) { |
| 154 | + if (completion) { |
| 155 | + completion(result); |
| 156 | + } |
| 157 | + }]; |
| 158 | + |
| 159 | + if (disposable != nil) { |
| 160 | + __weak MTDNSContext *weakSelf = self; |
| 161 | + [disposable setDisposable:[[[self performLookup:host port:port] deliverOn:[MTDNSContext sharedQueue]] startWithNext:^(NSString *result) { |
| 162 | + __strong MTDNSContext *strongSelf = weakSelf; |
| 163 | + if (strongSelf == nil) { |
| 164 | + return; |
| 165 | + } |
| 166 | + if (strongSelf->_contexts[key] != nil) { |
| 167 | + [strongSelf->_contexts[key] complete:result]; |
| 168 | + [strongSelf->_contexts removeObjectForKey:key]; |
| 169 | + } |
| 170 | + }]]; |
| 171 | + } |
| 172 | + |
| 173 | + __weak MTDNSContext *weakSelf = self; |
| 174 | + __weak MTDNSHostContext *weakContext = context; |
| 175 | + return [[MTBlockDisposable alloc] initWithBlock:^{ |
| 176 | + [[MTDNSContext sharedQueue] dispatchOnQueue:^{ |
| 177 | + __strong MTDNSContext *strongSelf = weakSelf; |
| 178 | + __strong MTDNSHostContext *strongContext = weakContext; |
| 179 | + if (strongSelf == nil || strongContext == nil) { |
| 180 | + return; |
| 181 | + } |
| 182 | + if (strongSelf->_contexts[key] != nil && strongSelf->_contexts[key] == strongContext) { |
| 183 | + [strongSelf->_contexts[key] removeSubscriber:index]; |
| 184 | + if ([strongSelf->_contexts[key] isEmpty]) { |
| 185 | + [strongSelf->_contexts removeObjectForKey:key]; |
| 186 | + } |
| 187 | + } |
| 188 | + }]; |
| 189 | + }]; |
| 190 | +} |
| 191 | + |
| 192 | +- (MTSignal *)performLookup:(NSString *)host port:(int32_t)port { |
| 193 | + MTSignal *lookupOnce = [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) { |
| 194 | + MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init]; |
| 195 | + [[MTQueue concurrentDefaultQueue] dispatchOnQueue:^{ |
| 196 | + struct addrinfo hints, *res, *res0; |
| 197 | + |
| 198 | + memset(&hints, 0, sizeof(hints)); |
| 199 | + hints.ai_family = PF_UNSPEC; |
| 200 | + hints.ai_socktype = SOCK_STREAM; |
| 201 | + hints.ai_protocol = IPPROTO_TCP; |
| 202 | + |
| 203 | + NSString *portStr = [NSString stringWithFormat:@"%d", port]; |
| 204 | + int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); |
| 205 | + |
| 206 | + NSString *address4 = nil; |
| 207 | + NSString *address6 = nil; |
| 208 | + |
| 209 | + if (gai_error == 0) { |
| 210 | + for(res = res0; res; res = res->ai_next) { |
| 211 | + if ((address4 == nil) && (res->ai_family == AF_INET)) { |
| 212 | + struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr; |
| 213 | + char *s = malloc(INET_ADDRSTRLEN); |
| 214 | + inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN); |
| 215 | + address4 = [NSString stringWithUTF8String:s]; |
| 216 | + free(s); |
| 217 | + } else if ((address6 == nil) && (res->ai_family == AF_INET6)) { |
| 218 | + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr; |
| 219 | + char *s = malloc(INET6_ADDRSTRLEN); |
| 220 | + inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN); |
| 221 | + address6 = [NSString stringWithUTF8String:s]; |
| 222 | + free(s); |
| 223 | + } |
| 224 | + } |
| 225 | + freeaddrinfo(res0); |
| 226 | + } |
| 227 | + |
| 228 | + if (address4 != nil) { |
| 229 | + [subscriber putNext:address4]; |
| 230 | + [subscriber putCompletion]; |
| 231 | + } else if (address6 != nil) { |
| 232 | + [subscriber putNext:address6]; |
| 233 | + [subscriber putCompletion]; |
| 234 | + } else { |
| 235 | + [subscriber putError:nil]; |
| 236 | + } |
| 237 | + }]; |
| 238 | + return disposable; |
| 239 | + }]; |
| 240 | + return [[lookupOnce catch:^MTSignal *(__unused id error) { |
| 241 | + return [[MTSignal complete] delay:2.0 onQueue:[MTDNSContext sharedQueue]]; |
| 242 | + }] take:1]; |
| 243 | +} |
| 244 | + |
| 245 | +@end |
| 246 | + |
50 | 247 | @interface MTDNSCachedHostname : NSObject
|
51 | 248 |
|
52 | 249 | @property (nonatomic, strong) NSString *ip;
|
@@ -144,4 +341,15 @@ + (MTSignal *)resolveHostname:(NSString *)hostname {
|
144 | 341 | }];
|
145 | 342 | }
|
146 | 343 |
|
| 344 | ++ (MTSignal *)resolveHostnameNative:(NSString *)hostname port:(int32_t)port { |
| 345 | + return [[MTDNSContext shared] mapToSignal:^MTSignal *(MTDNSContext *context) { |
| 346 | + return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) { |
| 347 | + return [context subscribe:hostname port:port completion:^(NSString *result) { |
| 348 | + [subscriber putNext:result]; |
| 349 | + [subscriber putCompletion]; |
| 350 | + }]; |
| 351 | + }]; |
| 352 | + }]; |
| 353 | +} |
| 354 | + |
147 | 355 | @end
|
0 commit comments