1/* 2 * Copyright (c) 2013 Apple Inc. 3 * All rights reserved. 4 */ 5 6#include <syslog.h> 7#include <stdio.h> 8#include <dns_sd.h> 9#include <sys/socket.h> 10#include <CoreFoundation/CoreFoundation.h> 11#import <Foundation/Foundation.h> 12#include <CFNetwork/CFSocketStreamPriv.h> 13 14#define dns_timeout 10 15#define probe_timeout 30 16#define HTTP_RESPONSE_CODE_OK 200 17 18static void probe_callback(CFReadStreamRef readStream, CFStreamEventType type, void * context); 19static void timer_callback( CFRunLoopTimerRef timerref, void *context); 20 21static void 22probe_callback(CFReadStreamRef readStream, CFStreamEventType type, void * context) 23{ 24 int readbytes = 0; 25 int probe_OK = 0; 26 CFErrorRef error; 27 CFStringRef errorstr = NULL; 28 CFRunLoopTimerRef timerref = context; 29 CFHTTPMessageRef resp = NULL; 30 CFIndex responseCode = 0, bufferLength = 0; 31 char* buffer; 32 33 CFRunLoopTimerInvalidate(timerref); 34 CFRelease(timerref); 35 36 resp = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 37 38 responseCode = CFHTTPMessageGetResponseStatusCode(resp); 39 syslog(LOG_DEBUG, "sbslauncher response code is %ld", responseCode); 40 41 if (responseCode != HTTP_RESPONSE_CODE_OK) 42 goto done; 43 44 switch (type) { 45 case kCFStreamEventOpenCompleted: 46 probe_OK = 1; 47 break; 48 case kCFStreamEventHasBytesAvailable: 49 probe_OK = 1; 50 break; 51 case kCFStreamEventErrorOccurred: 52 error = CFReadStreamCopyError(readStream); 53 errorstr = CFErrorCopyDescription(error); 54 bufferLength = CFStringGetLength(errorstr) + 1; 55 buffer = (char*)malloc(bufferLength*sizeof(char)); 56 CFStringGetCString(errorstr, buffer, (bufferLength), CFStringGetSystemEncoding()); 57 syslog(LOG_ERR, "sbslauncher probe_callback stream, errorstr: %s", buffer); 58 59 free(buffer); 60 CFRelease(errorstr); 61 break; 62 default: 63 break; 64 } 65 66done: 67 syslog(LOG_DEBUG, "sbslauncher: probe result: %d", probe_OK); 68 if(resp) 69 CFRelease(resp); 70 CFRelease(readStream); 71 exit(probe_OK); 72} 73 74static void 75timer_callback( CFRunLoopTimerRef timerref, void *context) 76{ 77 syslog(LOG_DEBUG, "timer_callback: timer expires"); 78 CFRunLoopTimerInvalidate(timerref); 79 CFRelease(timerref); 80 /* release read stream */ 81 if (context) 82 CFRelease(context); 83 /* timeout, no response from server */ 84 exit(0); 85} 86 87int launch_http_probe(int argc, const char * argv[]) { 88 CFStringRef cfprobe_server = NULL; 89 CFStringRef cfinterface = NULL; 90 CFURLRef url = NULL; 91 CFRunLoopTimerRef timerref = NULL; 92 CFRunLoopTimerContext timer_context = { 0, NULL, NULL, NULL, NULL }; 93 NSURLRequest *request = NULL; 94 NSError *error; 95 NSURLResponse *response; 96 NSData *result; 97 int proberesult = 0; 98 99 CFHTTPMessageRef httpRequest = NULL; 100 CFReadStreamRef httpStream = NULL; 101 CFStreamClientContext stream_context = { 0, NULL, NULL, NULL, NULL }; 102 103 /* set up URL */ 104 105 if ((cfprobe_server = CFStringCreateWithCString(NULL, (char*)argv[2], kCFStringEncodingMacRoman)) == NULL) 106 goto done; 107 108 if ((cfinterface = CFStringCreateWithCString(NULL, (char*)argv[3], kCFStringEncodingMacRoman)) == NULL) 109 goto done; 110 111 if ((url = CFURLCreateWithString(NULL, cfprobe_server, NULL))==NULL){ 112 CFRelease(cfprobe_server); 113 goto done; 114 } 115 116 httpRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), url, kCFHTTPVersion1_1); 117 if (httpRequest == NULL) 118 goto done; 119 120 httpStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, httpRequest); 121 if (httpStream == NULL) 122 goto done; 123 124 static const CFOptionFlags eventMask = kCFStreamEventHasBytesAvailable | 125 kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; 126 127 timer_context.info = (void*)httpStream; 128 129 CFReadStreamSetProperty(httpStream, kCFStreamPropertyBoundInterfaceIdentifier, cfinterface); 130 131 /* set timer */ 132 timerref = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + probe_timeout, 0, 0, 0, timer_callback, &timer_context); 133 if (timerref == NULL) 134 goto done; 135 136 stream_context.info = (void*)timerref; 137 138 if (!CFReadStreamSetClient(httpStream, eventMask, probe_callback, &stream_context)) 139 { 140 CFRelease(timerref); 141 goto done; 142 } 143 144 // Schedule with the run loop 145 CFReadStreamScheduleWithRunLoop(httpStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); 146 147 if ( !CFReadStreamOpen(httpStream)){ 148 syslog(LOG_ERR, "sbslauncher CFReadStreamOpen err, read stream status %ld", CFReadStreamGetStatus(httpStream)); 149 } 150 151 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerref, kCFRunLoopCommonModes); 152 CFRunLoopRun(); 153 154done: 155 if (cfprobe_server) 156 CFRelease(cfprobe_server); 157 if (cfinterface) 158 CFRelease(cfinterface); 159 if (url) 160 CFRelease(url); 161 if (httpRequest) 162 CFRelease(httpRequest); 163 164 return(proberesult); 165} 166 167static DNSServiceRef dnsRedirectQuery = NULL; 168static int dnsRedirectDetected = 0; 169static int dnsRedirectWriteFD = -1; 170 171static void 172dns_redirect_callback(DNSServiceRef sdRef, 173 DNSServiceFlags flags, 174 uint32_t interfaceIndex, 175 DNSServiceErrorType errorCode, 176 const char *hostname, 177 const struct sockaddr *address, 178 uint32_t ttl, 179 void *context) 180{ 181 /* If we got back an answer, we detected a liar! */ 182 if ((errorCode == kDNSServiceErr_NoError) && (address != NULL)) { 183 dnsRedirectDetected = 1; 184 185 if (dnsRedirectWriteFD != -1) { 186 if (write(dnsRedirectWriteFD, address, address->sa_len) < 0) { 187 syslog(LOG_ERR, "sbslauncher write address home to socket %d fails, error: %s (tried to write %d bytes)", dnsRedirectWriteFD, strerror(errno), address->sa_len); 188 dnsRedirectDetected = 0; 189 } 190 191 } 192 } 193 194 if (!(flags & kDNSServiceFlagsMoreComing)) { 195 /* Clear query ref */ 196 if (dnsRedirectQuery != NULL) { 197 DNSServiceRefDeallocate(dnsRedirectQuery); 198 dnsRedirectQuery = NULL; 199 } 200 201 exit(dnsRedirectDetected); 202 } 203} 204 205#define HOSTNAME_CHARSET "abcdefghijklmnopqrstuvwxyz0123456789-" 206#define HOSTNAME_CHARSET_LENGTH (sizeof(HOSTNAME_CHARSET) - 1) 207#define BASE_RANDOM_SEGMENT_LENGTH 8 208#define VARIABLE_RANDOM_SEGMENT_LENGTH 20 209#define RANDOM_SEGMENT_MAX_LENGTH (BASE_RANDOM_SEGMENT_LENGTH + VARIABLE_RANDOM_SEGMENT_LENGTH + 1) 210 211int detect_dns_redirect (int argc, const char * argv[]) 212{ 213 Boolean success = FALSE; 214 char random_host[256]; 215 int character_index = 0; 216 int firstSegmentLength = (arc4random() % VARIABLE_RANDOM_SEGMENT_LENGTH) + BASE_RANDOM_SEGMENT_LENGTH; 217 int secondSegmentLength = (arc4random() % VARIABLE_RANDOM_SEGMENT_LENGTH) + BASE_RANDOM_SEGMENT_LENGTH; 218 int i; 219 220 /* Need a socket to write back on */ 221 if (argc >= 3 && argv[2]) { 222 dnsRedirectWriteFD = atoi(argv[2]); 223 /*int buffersize = sizeof(struct sockaddr_storage); 224 if (setsockopt(dnsRedirectWriteFD, SOL_SOCKET, SO_SNDBUF, &buffersize, sizeof(buffersize)) < 0) { 225 syslog(LOG_ERR, "Cannot set SO_SNDBUF for socket %d, error: %s\n", dnsRedirectWriteFD, strerror(errno)); 226 }*/ 227 } 228 229 /* Create random host xxxxxxxxxxxx.xxxxxxxxxxx.com */ 230 for (i = 0; i < firstSegmentLength; i++) { 231 random_host[character_index++] = HOSTNAME_CHARSET[(arc4random() % HOSTNAME_CHARSET_LENGTH)]; 232 } 233 234 random_host[character_index++] = '.'; 235 236 for (i = 0; i < secondSegmentLength; i++) { 237 random_host[character_index++] = HOSTNAME_CHARSET[(arc4random() % HOSTNAME_CHARSET_LENGTH)]; 238 } 239 240 strncpy((char*)random_host + character_index, ".com", sizeof(random_host) - character_index); 241 242 DNSServiceErrorType error = DNSServiceGetAddrInfo(&dnsRedirectQuery, kDNSServiceFlagsReturnIntermediates, 0, kDNSServiceProtocol_IPv4, random_host, dns_redirect_callback, NULL); 243 if (error != kDNSServiceErr_NoError) { 244 goto done; 245 } 246 247 error = DNSServiceSetDispatchQueue(dnsRedirectQuery, dispatch_get_main_queue()); 248 if (error != kDNSServiceErr_NoError) { 249 goto done; 250 } 251 252 /* set timer */ 253 CFRunLoopTimerRef timerref = NULL; 254 CFRunLoopTimerContext timer_context = { 0, NULL, NULL, NULL, NULL }; 255 timerref = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + dns_timeout, 0, 0, 0, timer_callback, &timer_context); 256 if (timerref == NULL) 257 goto done; 258 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerref, kCFRunLoopCommonModes); 259 260 CFRunLoopRun(); 261 success = TRUE; 262 263done: 264 return success; 265} 266