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 |