| OLD | NEW |
| 1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
| 4 | 4 |
| 5 #if defined(FLETCH_TARGET_OS_MACOS) | 5 #if defined(FLETCH_TARGET_OS_MACOS) |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <stdio.h> | 8 #include <stdio.h> |
| 9 #include <stdlib.h> | 9 #include <stdlib.h> |
| 10 #include <string.h> | 10 #include <string.h> |
| 11 #include <arpa/inet.h> | 11 #include <arpa/inet.h> |
| 12 #include <pthread.h> | 12 #include <pthread.h> |
| 13 #include <sys/time.h> |
| 13 | 14 |
| 14 #include <dns_sd.h> | 15 #include <dns_sd.h> |
| 15 | 16 |
| 16 #include "include/dart_api.h" | 17 #include "include/dart_api.h" |
| 17 #include "include/dart_native_api.h" | 18 #include "include/dart_native_api.h" |
| 18 | 19 |
| 19 #include "mdns_extension.h" | 20 #include "mdns_extension.h" |
| 20 | 21 |
| 22 // Constants. |
| 23 static uint32_t kUsecsInSecs = 1000000; |
| 24 static uint32_t kMillisecsInUsecs = 1000; |
| 25 |
| 21 // Context passed to the callback receiving the mDNS information. | 26 // Context passed to the callback receiving the mDNS information. |
| 22 struct Context { | 27 struct Context { |
| 28 DNSServiceRef ref; |
| 29 // The timeout waiting for results. |
| 30 int timeout; |
| 23 // The Dart port where the result should be delivered. | 31 // The Dart port where the result should be delivered. |
| 24 Dart_Port port; | 32 Dart_Port port; |
| 25 }; | 33 }; |
| 26 | 34 |
| 27 // Print fatal error and exit. | 35 // Print fatal error and exit. |
| 28 static void Fatal(const char *message) { | 36 static void Fatal(const char *message) { |
| 29 fprintf(stderr, "%s (errno %d)\n", message, errno); | 37 fprintf(stderr, "%s (errno %d)\n", message, errno); |
| 30 exit(-1); | 38 exit(-1); |
| 31 } | 39 } |
| 32 | 40 |
| 41 int64_t timeval_to_usec(const struct timeval* tv) { |
| 42 return( (int64_t)tv->tv_sec * kUsecsInSecs + tv->tv_usec ) ; |
| 43 } |
| 44 |
| 45 struct timeval* usec_to_timeval(int64_t usec, struct timeval* tv) { |
| 46 tv->tv_sec = usec / kUsecsInSecs; |
| 47 tv->tv_usec = usec % kUsecsInSecs; |
| 48 return tv; |
| 49 } |
| 50 |
| 51 // Free allocated resources associated with a context. |
| 52 static void FreeContext(Context* ctx) { |
| 53 DNSServiceRefDeallocate(ctx->ref); |
| 54 free(ctx); |
| 55 } |
| 56 |
| 33 // Code running in the threads started for pumping the results. | 57 // Code running in the threads started for pumping the results. |
| 34 static void* ThreadFunction(void* data) { | 58 static void* ThreadFunction(void* data) { |
| 35 DNSServiceRef ref = reinterpret_cast<DNSServiceRef>(data); | 59 Context* ctx = reinterpret_cast<Context*>(data); |
| 36 | 60 |
| 37 // Preocess results until an error occurs. | 61 // Determint the end-time for responses to this request. Timeout from Dart |
| 38 DNSServiceErrorType result = kDNSServiceErr_NoError; | 62 // is in milliseconds. |
| 39 while (result == kDNSServiceErr_NoError) { | 63 struct timeval time; |
| 40 result = DNSServiceProcessResult(ref); | 64 if (gettimeofday(&time, NULL) == -1) { |
| 65 FreeContext(ctx); |
| 66 return NULL; |
| 41 } | 67 } |
| 42 // The expected error from deallocation the ref. | 68 int64_t end_time = timeval_to_usec(&time) + ctx->timeout * kMillisecsInUsecs; |
| 43 if (result != kDNSServiceErr_BadReference) { | 69 |
| 44 fprintf(stderr, "Error from DNSServiceProcessResult: %d\n", result); | 70 // Setup single file descriptor for select. |
| 71 int fd = DNSServiceRefSockFD(ctx->ref); |
| 72 fd_set readfds; |
| 73 FD_ZERO(&readfds); |
| 74 FD_SET(fd, &readfds); |
| 75 while (true) { |
| 76 struct timeval timeout; |
| 77 int64_t timeout_usec = end_time - timeval_to_usec(&time); |
| 78 if (timeout_usec <= 0) break; |
| 79 usec_to_timeval(timeout_usec, &timeout); |
| 80 int rc = select(fd + 1, &readfds, NULL, NULL, &timeout); |
| 81 if (rc == -1 && errno == EINTR) continue; |
| 82 |
| 83 // Terminate the loop if timeout or error. |
| 84 if (rc <= 0) break; |
| 85 |
| 86 // Process the result which is ready. |
| 87 DNSServiceErrorType result = DNSServiceProcessResult(ctx->ref); |
| 88 if (result != kDNSServiceErr_NoError) break; |
| 89 |
| 90 // Prepare new timeout. |
| 91 if (gettimeofday(&time, NULL) == -1) break; |
| 45 } | 92 } |
| 46 | 93 |
| 94 FreeContext(ctx); |
| 47 return NULL; | 95 return NULL; |
| 48 } | 96 } |
| 49 | 97 |
| 50 // Start a thread for receiving results from a specific DNSServiceRef. | 98 // Start a thread for receiving results from a specific DNSServiceRef. |
| 51 static int StartThread(DNSServiceRef ref) { | 99 static int StartThread(Context* ctx) { |
| 52 pthread_attr_t attr; | 100 pthread_attr_t attr; |
| 53 int result = pthread_attr_init(&attr); | 101 int result = pthread_attr_init(&attr); |
| 54 if (result != 0) Fatal("Error from pthread_attr_init"); | 102 if (result != 0) Fatal("Error from pthread_attr_init"); |
| 55 | 103 |
| 56 result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | 104 result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| 57 if (result != 0) Fatal("Error from pthread_attr_setdetachstate"); | 105 if (result != 0) Fatal("Error from pthread_attr_setdetachstate"); |
| 58 | 106 |
| 59 pthread_t tid; | 107 pthread_t tid; |
| 60 result = pthread_create(&tid, &attr, ThreadFunction, ref); | 108 result = pthread_create(&tid, &attr, ThreadFunction, ctx); |
| 61 if (result != 0) Fatal("Error from pthread_create"); | 109 if (result != 0) Fatal("Error from pthread_create"); |
| 62 | 110 |
| 63 result = pthread_attr_destroy(&attr); | 111 result = pthread_attr_destroy(&attr); |
| 64 if (result != 0) Fatal("Error from pthread_attr_destroy"); | 112 if (result != 0) Fatal("Error from pthread_attr_destroy"); |
| 65 | 113 |
| 66 return 0; | 114 return 0; |
| 67 } | 115 } |
| 68 | 116 |
| 69 // Callback for results initiated by calling DNSServiceGetAddrInfo. | 117 |
| 70 static void GetAddrInfoCallback(DNSServiceRef ref, | 118 // Callback for results initiated by calling DNSServiceQueryRecord. |
| 119 static void QueryRecordCallback(DNSServiceRef ref, |
| 71 DNSServiceFlags flags, | 120 DNSServiceFlags flags, |
| 72 uint32_t interfaceIndex, | 121 uint32_t interfaceIndex, |
| 73 DNSServiceErrorType errorCode, | 122 DNSServiceErrorType errorCode, |
| 74 const char *hostname, | 123 const char *fullname, |
| 75 const struct sockaddr *address, | 124 uint16_t rrtype, |
| 125 uint16_t rrclass, |
| 126 uint16_t rdlen, |
| 127 const void* rdata, |
| 76 uint32_t ttl, | 128 uint32_t ttl, |
| 77 void *context) { | 129 void *context) { |
| 78 if (address->sa_family == AF_INET) { | 130 if (rrclass != kDNSServiceClass_IN) return; |
| 79 const struct sockaddr_in* address_in = (const struct sockaddr_in*)address; | |
| 80 const uint8_t* addr = | |
| 81 reinterpret_cast<const uint8_t*>(&address_in->sin_addr.s_addr); | |
| 82 | 131 |
| 83 struct Context* ctx = reinterpret_cast<struct Context*>(context); | 132 struct Context* ctx = reinterpret_cast<struct Context*>(context); |
| 84 | 133 |
| 85 // Build the response message. | 134 if (rrtype != kDNSServiceType_A && |
| 86 Dart_CObject cobject_hostname; | 135 rrtype != kDNSServiceType_SRV && |
| 87 cobject_hostname.type = Dart_CObject_kString; | 136 rrtype != kDNSServiceType_PTR) { |
| 88 cobject_hostname.value.as_string = const_cast<char*>(hostname); | 137 // Ignore unsupported types. |
| 89 Dart_CObject cobject_address; | 138 return; |
| 90 cobject_address.type = Dart_CObject_kTypedData; | 139 } |
| 91 cobject_address.value.as_typed_data.length = 4; | |
| 92 cobject_address.value.as_typed_data.type = Dart_TypedData_kUint8; | |
| 93 cobject_address.value.as_typed_data.values = const_cast<uint8_t*>(addr); | |
| 94 Dart_CObject cobject_result; | |
| 95 cobject_result.type = Dart_CObject_kArray; | |
| 96 Dart_CObject* result_array[] = {&cobject_hostname, &cobject_address}; | |
| 97 cobject_result.value.as_array.length = 2; | |
| 98 cobject_result.value.as_array.values = result_array; | |
| 99 Dart_PostCObject(ctx->port, &cobject_result); | |
| 100 | 140 |
| 101 // Result received, free allocated data and stop lookup. | 141 // Build the response message. |
| 102 free(ctx); | 142 Dart_CObject cobject_fullname; |
| 103 DNSServiceRefDeallocate(ref); | 143 cobject_fullname.type = Dart_CObject_kString; |
| 104 } | 144 cobject_fullname.value.as_string = const_cast<char*>(fullname); |
| 145 Dart_CObject cobject_type; |
| 146 cobject_type.type = Dart_CObject_kInt32; |
| 147 cobject_type.value.as_int32 = rrtype; |
| 148 Dart_CObject cobject_ttl; |
| 149 cobject_ttl.type = Dart_CObject_kInt32; |
| 150 cobject_ttl.value.as_int32 = ttl; |
| 151 Dart_CObject cobject_data; |
| 152 cobject_data.type = Dart_CObject_kTypedData; |
| 153 cobject_data.value.as_typed_data.length = rdlen; |
| 154 cobject_data.value.as_typed_data.type = Dart_TypedData_kUint8; |
| 155 cobject_data.value.as_typed_data.values = |
| 156 const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(rdata)); |
| 157 Dart_CObject cobject_result; |
| 158 cobject_result.type = Dart_CObject_kArray; |
| 159 Dart_CObject* result_array[] = |
| 160 {&cobject_fullname, &cobject_type, &cobject_ttl, &cobject_data}; |
| 161 cobject_result.value.as_array.length = 4; |
| 162 cobject_result.value.as_array.values = result_array; |
| 163 Dart_PostCObject(ctx->port, &cobject_result); |
| 105 } | 164 } |
| 106 | 165 |
| 107 // Lookup request from Dart. | 166 // Lookup request from Dart. |
| 108 void HandleLookup(Dart_Port port_id, char* hostname) { | 167 void HandleLookup(Dart_Port port_id, int type, char* fullname, int timeout) { |
| 109 DNSServiceRef ref; | 168 DNSServiceRef ref; |
| 110 DNSServiceErrorType result; | 169 DNSServiceErrorType result; |
| 111 struct Context* context = | 170 struct Context* context = |
| 112 reinterpret_cast<struct Context*>(malloc(sizeof(struct Context))); | 171 reinterpret_cast<struct Context*>(malloc(sizeof(struct Context))); |
| 113 context->port = port_id; | 172 result = DNSServiceQueryRecord(&ref, |
| 114 result = DNSServiceGetAddrInfo(&ref, | |
| 115 0, | 173 0, |
| 116 0, | 174 0, |
| 117 kDNSServiceProtocol_IPv4, | 175 fullname, |
| 118 hostname, | 176 type, |
| 119 &GetAddrInfoCallback, | 177 kDNSServiceClass_IN, |
| 178 &QueryRecordCallback, |
| 120 context); | 179 context); |
| 121 if (result != kDNSServiceErr_NoError) { | 180 if (result != kDNSServiceErr_NoError) { |
| 122 fprintf(stderr, "Error from DNSServiceProcessResult: %d\n", result); | 181 fprintf(stderr, "Error from DNSServiceQueryRecord: %d\n", result); |
| 123 } else { | 182 } else { |
| 124 // Start a thread for retreiving the results. | 183 // Start a thread for pumping the results. |
| 125 // TODO(sgjesse): Add a timeout for killing the thread if there | 184 context->ref = ref; |
| 126 // are no responses. | 185 context->timeout = timeout; |
| 127 StartThread(ref); | 186 context->port = port_id; |
| 187 StartThread(context); |
| 128 } | 188 } |
| 129 } | 189 } |
| 130 | 190 |
| 131 #endif | 191 #endif |
| OLD | NEW |