OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/local_discovery/service_discovery_client_mac.h" | 5 #include "chrome/browser/local_discovery/service_discovery_client_mac.h" |
6 | 6 |
7 #import <arpa/inet.h> | 7 #import <arpa/inet.h> |
8 #import <Foundation/Foundation.h> | 8 #import <Foundation/Foundation.h> |
9 #import <net/if_dl.h> | 9 #import <net/if_dl.h> |
10 #include <stddef.h> | 10 #include <stddef.h> |
11 #include <stdint.h> | 11 #include <stdint.h> |
12 | 12 |
13 #include "base/debug/dump_without_crashing.h" | 13 #include "base/debug/dump_without_crashing.h" |
14 #include "base/memory/singleton.h" | 14 #include "base/memory/singleton.h" |
15 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
16 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
17 #include "base/strings/sys_string_conversions.h" | |
17 #include "base/threading/thread.h" | 18 #include "base/threading/thread.h" |
18 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
19 #include "net/base/ip_address.h" | 20 #include "net/base/ip_address.h" |
20 #include "net/base/ip_endpoint.h" | 21 #include "net/base/ip_endpoint.h" |
21 | 22 |
22 using local_discovery::ServiceWatcherImplMac; | 23 using local_discovery::ServiceWatcherImplMac; |
23 using local_discovery::ServiceResolverImplMac; | 24 using local_discovery::ServiceResolverImplMac; |
24 | 25 |
25 @interface NetServiceBrowserDelegate | 26 @interface NetServiceBrowserDelegate |
26 : NSObject<NSNetServiceBrowserDelegate, NSNetServiceDelegate> { | 27 : NSObject<NSNetServiceBrowserDelegate, NSNetServiceDelegate> { |
(...skipping 26 matching lines...) Expand all Loading... | |
53 const NSTimeInterval kResolveTimeout = 10.0; | 54 const NSTimeInterval kResolveTimeout = 10.0; |
54 | 55 |
55 // Extracts the instance name, name type and domain from a full service name or | 56 // Extracts the instance name, name type and domain from a full service name or |
56 // the service type and domain from a service type. Returns true if successful. | 57 // the service type and domain from a service type. Returns true if successful. |
57 // TODO(justinlin): This current only handles service names with format | 58 // TODO(justinlin): This current only handles service names with format |
58 // <name>._<protocol2>._<protocol1>.<domain>. Service names with | 59 // <name>._<protocol2>._<protocol1>.<domain>. Service names with |
59 // subtypes will not parse correctly: | 60 // subtypes will not parse correctly: |
60 // <name>._<type>._<sub>._<protocol2>._<protocol1>.<domain>. | 61 // <name>._<type>._<sub>._<protocol2>._<protocol1>.<domain>. |
61 bool ExtractServiceInfo(const std::string& service, | 62 bool ExtractServiceInfo(const std::string& service, |
62 bool is_service_name, | 63 bool is_service_name, |
63 std::string* instance, | 64 base::scoped_nsobject<NSString>* instance, |
64 std::string* type, | 65 base::scoped_nsobject<NSString>* type, |
65 std::string* domain) { | 66 base::scoped_nsobject<NSString>* domain) { |
66 if (service.empty()) | 67 if (service.empty()) |
67 return false; | 68 return false; |
68 | 69 |
69 const size_t last_period = service.find_last_of('.'); | 70 const size_t last_period = service.find_last_of('.'); |
70 if (last_period == std::string::npos || service.length() <= last_period) | 71 if (last_period == std::string::npos || service.length() <= last_period) |
71 return false; | 72 return false; |
72 | 73 |
73 if (!is_service_name) { | 74 if (!is_service_name) { |
74 *instance = std::string(); | 75 type->reset(base::SysUTF8ToNSString(service.substr(0, last_period) + "."), |
Vitaly Buka (NO REVIEWS)
2016/07/11 20:30:25
Maybe safer to do all string transformations after
Robert Sesek
2016/07/11 20:35:18
I don't think it's any less safe to do it this way
Vitaly Buka (NO REVIEWS)
2016/07/11 20:48:24
'safer' -> 'cleaner'
My concerns is that find_las
Robert Sesek
2016/07/11 20:56:08
find_last_of isn't going to be searching code poin
| |
75 *type = service.substr(0, last_period) + "."; | 76 base::scoped_policy::RETAIN); |
76 } else { | 77 } else { |
77 // Find third last period that delimits type and instance name. | 78 // Find third last period that delimits type and instance name. |
78 size_t type_period = last_period; | 79 size_t type_period = last_period; |
79 for (int i = 0; i < 2; ++i) { | 80 for (int i = 0; i < 2; ++i) { |
80 type_period = service.find_last_of('.', type_period - 1); | 81 type_period = service.find_last_of('.', type_period - 1); |
81 if (type_period == std::string::npos) | 82 if (type_period == std::string::npos) |
82 return false; | 83 return false; |
83 } | 84 } |
84 | 85 |
85 *instance = service.substr(0, type_period); | 86 instance->reset(base::SysUTF8ToNSString(service.substr(0, type_period)), |
86 *type = service.substr(type_period + 1, last_period - type_period); | 87 base::scoped_policy::RETAIN); |
88 type->reset( | |
89 base::SysUTF8ToNSString( | |
90 service.substr(type_period + 1, last_period - type_period)), | |
91 base::scoped_policy::RETAIN); | |
87 } | 92 } |
88 *domain = service.substr(last_period + 1) + "."; | 93 domain->reset(base::SysUTF8ToNSString(service.substr(last_period + 1) + "."), |
94 base::scoped_policy::RETAIN); | |
89 | 95 |
90 return !domain->empty() && | 96 return [*domain length] > 0 && |
91 !type->empty() && | 97 [*type length] > 0 && |
92 (!is_service_name || !instance->empty()); | 98 (!is_service_name || [*instance length] > 0); |
93 } | 99 } |
94 | 100 |
95 void ParseTxtRecord(NSData* record, std::vector<std::string>* output) { | 101 void ParseTxtRecord(NSData* record, std::vector<std::string>* output) { |
96 if (record == nil || [record length] <= 1) | 102 if ([record length] <= 1) |
97 return; | 103 return; |
98 | 104 |
99 VLOG(1) << "ParseTxtRecord: " << [record length]; | 105 VLOG(1) << "ParseTxtRecord: " << [record length]; |
100 | 106 |
101 const char* record_bytes = reinterpret_cast<const char*>([record bytes]); | 107 const uint8_t* bytes = reinterpret_cast<const uint8_t*>([record bytes]); |
102 const char* const record_end = record_bytes + [record length]; | 108 size_t size = [record length]; |
103 // TODO(justinlin): More strict bounds checking. | 109 size_t offset = 0; |
104 while (record_bytes < record_end) { | 110 while (offset < size) { |
105 uint8_t size = *record_bytes++; | 111 uint8_t record_size = bytes[offset++]; |
106 if (size <= 0) | 112 if (offset > size - record_size) |
107 continue; | 113 break; |
108 | 114 |
109 if (record_bytes + size <= record_end) { | 115 base::scoped_nsobject<NSString> txt_record( |
110 VLOG(1) << "TxtRecord: " | 116 [[NSString alloc] initWithBytes:&bytes[offset] |
111 << std::string(record_bytes, static_cast<size_t>(size)); | 117 length:record_size |
112 output->push_back( | 118 encoding:NSUTF8StringEncoding]); |
113 [[[NSString alloc] initWithBytes:record_bytes | 119 if (txt_record) { |
114 length:size | 120 std::string txt_record_string = base::SysNSStringToUTF8(txt_record); |
115 encoding:NSUTF8StringEncoding] UTF8String]); | 121 VLOG(1) << "TxtRecord: " << txt_record_string; |
122 output->push_back(std::move(txt_record_string)); | |
123 } else { | |
124 VLOG(1) << "TxtRecord corrupted at offset " << offset; | |
116 } | 125 } |
117 record_bytes += size; | 126 |
127 offset += record_size; | |
118 } | 128 } |
119 } | 129 } |
120 | 130 |
121 } // namespace | 131 } // namespace |
122 | 132 |
123 ServiceDiscoveryClientMac::ServiceDiscoveryClientMac() {} | 133 ServiceDiscoveryClientMac::ServiceDiscoveryClientMac() {} |
124 ServiceDiscoveryClientMac::~ServiceDiscoveryClientMac() {} | 134 ServiceDiscoveryClientMac::~ServiceDiscoveryClientMac() {} |
125 | 135 |
126 std::unique_ptr<ServiceWatcher> ServiceDiscoveryClientMac::CreateServiceWatcher( | 136 std::unique_ptr<ServiceWatcher> ServiceDiscoveryClientMac::CreateServiceWatcher( |
127 const std::string& service_type, | 137 const std::string& service_type, |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
197 DCHECK(IsOnServiceDiscoveryThread()); | 207 DCHECK(IsOnServiceDiscoveryThread()); |
198 | 208 |
199 delegate_.reset([[NetServiceBrowserDelegate alloc] initWithContainer:this]); | 209 delegate_.reset([[NetServiceBrowserDelegate alloc] initWithContainer:this]); |
200 browser_.reset([[NSNetServiceBrowser alloc] init]); | 210 browser_.reset([[NSNetServiceBrowser alloc] init]); |
201 [browser_ setDelegate:delegate_]; | 211 [browser_ setDelegate:delegate_]; |
202 } | 212 } |
203 | 213 |
204 void | 214 void |
205 ServiceWatcherImplMac::NetServiceBrowserContainer::DiscoverOnDiscoveryThread() { | 215 ServiceWatcherImplMac::NetServiceBrowserContainer::DiscoverOnDiscoveryThread() { |
206 DCHECK(IsOnServiceDiscoveryThread()); | 216 DCHECK(IsOnServiceDiscoveryThread()); |
207 std::string instance; | 217 base::scoped_nsobject<NSString> instance, type, domain; |
208 std::string type; | |
209 std::string domain; | |
210 | 218 |
211 if (!ExtractServiceInfo(service_type_, false, &instance, &type, &domain)) | 219 if (!ExtractServiceInfo(service_type_, false, &instance, &type, &domain)) |
212 return; | 220 return; |
213 | 221 |
214 DCHECK(instance.empty()); | 222 DCHECK(![instance length]); |
215 DVLOG(1) << "Listening for service type '" << type | 223 DVLOG(1) << "Listening for service type '" << [type UTF8String] |
216 << "' on domain '" << domain << "'"; | 224 << "' on domain '" << [domain UTF8String] << "'"; |
217 | 225 |
218 base::Time start_time = base::Time::Now(); | 226 base::Time start_time = base::Time::Now(); |
219 [browser_ searchForServicesOfType:[NSString stringWithUTF8String:type.c_str()] | 227 [browser_ searchForServicesOfType:type inDomain:domain]; |
220 inDomain:[NSString stringWithUTF8String:domain.c_str()]]; | |
221 UMA_HISTOGRAM_TIMES("LocalDiscovery.MacBrowseCallTimes", | 228 UMA_HISTOGRAM_TIMES("LocalDiscovery.MacBrowseCallTimes", |
222 base::Time::Now() - start_time); | 229 base::Time::Now() - start_time); |
223 } | 230 } |
224 | 231 |
225 void ServiceWatcherImplMac::NetServiceBrowserContainer::OnServicesUpdate( | 232 void ServiceWatcherImplMac::NetServiceBrowserContainer::OnServicesUpdate( |
226 ServiceWatcher::UpdateType update, | 233 ServiceWatcher::UpdateType update, |
227 const std::string& service) { | 234 const std::string& service) { |
228 callback_runner_->PostTask(FROM_HERE, base::Bind(callback_, update, service)); | 235 callback_runner_->PostTask(FROM_HERE, base::Bind(callback_, update, service)); |
229 } | 236 } |
230 | 237 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
301 weak_factory_.GetWeakPtr())); | 308 weak_factory_.GetWeakPtr())); |
302 } | 309 } |
303 | 310 |
304 void ServiceResolverImplMac::NetServiceContainer::DeleteSoon() { | 311 void ServiceResolverImplMac::NetServiceContainer::DeleteSoon() { |
305 service_discovery_runner_->DeleteSoon(FROM_HERE, this); | 312 service_discovery_runner_->DeleteSoon(FROM_HERE, this); |
306 } | 313 } |
307 | 314 |
308 void | 315 void |
309 ServiceResolverImplMac::NetServiceContainer::StartResolvingOnDiscoveryThread() { | 316 ServiceResolverImplMac::NetServiceContainer::StartResolvingOnDiscoveryThread() { |
310 DCHECK(IsOnServiceDiscoveryThread()); | 317 DCHECK(IsOnServiceDiscoveryThread()); |
311 std::string instance; | 318 base::scoped_nsobject<NSString> instance, type, domain; |
312 std::string type; | |
313 std::string domain; | |
314 | 319 |
315 // The service object is set ahead of time by tests. | 320 // The service object is set ahead of time by tests. |
316 if (service_) | 321 if (service_) |
317 return; | 322 return; |
318 | 323 |
319 if (!ExtractServiceInfo(service_name_, true, &instance, &type, &domain)) | 324 if (!ExtractServiceInfo(service_name_, true, &instance, &type, &domain)) |
320 return OnResolveUpdate(ServiceResolver::STATUS_KNOWN_NONEXISTENT); | 325 return OnResolveUpdate(ServiceResolver::STATUS_KNOWN_NONEXISTENT); |
321 | 326 |
322 VLOG(1) << "ServiceResolverImplMac::ServiceResolverImplMac::" | 327 VLOG(1) << "ServiceResolverImplMac::ServiceResolverImplMac::" |
323 << "StartResolvingOnDiscoveryThread: Success"; | 328 << "StartResolvingOnDiscoveryThread: Success"; |
324 delegate_.reset([[NetServiceDelegate alloc] initWithContainer:this]); | 329 delegate_.reset([[NetServiceDelegate alloc] initWithContainer:this]); |
325 service_.reset( | 330 service_.reset( |
326 [[NSNetService alloc] | 331 [[NSNetService alloc] initWithDomain:domain type:type name:instance]); |
327 initWithDomain:[[NSString alloc] initWithUTF8String:domain.c_str()] | |
328 type:[[NSString alloc] initWithUTF8String:type.c_str()] | |
329 name:[[NSString alloc] initWithUTF8String:instance.c_str()]]); | |
330 [service_ setDelegate:delegate_]; | 332 [service_ setDelegate:delegate_]; |
331 | 333 |
332 [service_ resolveWithTimeout:kResolveTimeout]; | 334 [service_ resolveWithTimeout:kResolveTimeout]; |
333 | 335 |
334 VLOG(1) << "ServiceResolverImplMac::NetServiceContainer::" | 336 VLOG(1) << "ServiceResolverImplMac::NetServiceContainer::" |
335 << "StartResolvingOnDiscoveryThread: " << service_name_ | 337 << "StartResolvingOnDiscoveryThread: " << service_name_ |
336 << ", instance: " << instance << ", type: " << type | 338 << ", instance: " << [instance UTF8String] |
337 << ", domain: " << domain; | 339 << ", type: " << [type UTF8String] |
340 << ", domain: " << [domain UTF8String]; | |
338 } | 341 } |
339 | 342 |
340 void ServiceResolverImplMac::NetServiceContainer::OnResolveUpdate( | 343 void ServiceResolverImplMac::NetServiceContainer::OnResolveUpdate( |
341 RequestStatus status) { | 344 RequestStatus status) { |
342 if (status != STATUS_SUCCESS) { | 345 if (status != STATUS_SUCCESS) { |
343 callback_runner_->PostTask( | 346 callback_runner_->PostTask( |
344 FROM_HERE, base::Bind(callback_, status, ServiceDescription())); | 347 FROM_HERE, base::Bind(callback_, status, ServiceDescription())); |
345 return; | 348 return; |
346 } | 349 } |
347 | 350 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
428 | 431 |
429 - (id)initWithContainer: | 432 - (id)initWithContainer: |
430 (ServiceWatcherImplMac::NetServiceBrowserContainer*)container { | 433 (ServiceWatcherImplMac::NetServiceBrowserContainer*)container { |
431 if ((self = [super init])) { | 434 if ((self = [super init])) { |
432 container_ = container; | 435 container_ = container; |
433 services_.reset([[NSMutableArray alloc] initWithCapacity:1]); | 436 services_.reset([[NSMutableArray alloc] initWithCapacity:1]); |
434 } | 437 } |
435 return self; | 438 return self; |
436 } | 439 } |
437 | 440 |
438 - (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser | 441 - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser |
439 didFindService:(NSNetService *)netService | 442 didFindService:(NSNetService*)netService |
440 moreComing:(BOOL)moreServicesComing { | 443 moreComing:(BOOL)moreServicesComing { |
441 // Start monitoring this service for updates. | 444 // Start monitoring this service for updates. |
442 [netService setDelegate:self]; | 445 [netService setDelegate:self]; |
443 [netService startMonitoring]; | 446 [netService startMonitoring]; |
444 [services_ addObject:netService]; | 447 [services_ addObject:netService]; |
445 | 448 |
446 container_->OnServicesUpdate(local_discovery::ServiceWatcher::UPDATE_ADDED, | 449 container_->OnServicesUpdate(local_discovery::ServiceWatcher::UPDATE_ADDED, |
447 [[netService name] UTF8String]); | 450 base::SysNSStringToUTF8([netService name])); |
448 } | 451 } |
449 | 452 |
450 - (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser | 453 - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser |
451 didRemoveService:(NSNetService *)netService | 454 didRemoveService:(NSNetService*)netService |
452 moreComing:(BOOL)moreServicesComing { | 455 moreComing:(BOOL)moreServicesComing { |
453 NSUInteger index = [services_ indexOfObject:netService]; | 456 NSUInteger index = [services_ indexOfObject:netService]; |
454 if (index != NSNotFound) { | 457 if (index != NSNotFound) { |
455 container_->OnServicesUpdate( | 458 container_->OnServicesUpdate( |
456 local_discovery::ServiceWatcher::UPDATE_REMOVED, | 459 local_discovery::ServiceWatcher::UPDATE_REMOVED, |
457 [[netService name] UTF8String]); | 460 base::SysNSStringToUTF8([netService name])); |
458 | 461 |
459 // Stop monitoring this service for updates. | 462 // Stop monitoring this service for updates. |
460 [[services_ objectAtIndex:index] stopMonitoring]; | 463 [[services_ objectAtIndex:index] stopMonitoring]; |
461 [services_ removeObjectAtIndex:index]; | 464 [services_ removeObjectAtIndex:index]; |
462 } | 465 } |
463 } | 466 } |
464 | 467 |
465 - (void)netService:(NSNetService *)sender | 468 - (void)netService:(NSNetService*)sender |
466 didUpdateTXTRecordData:(NSData *)data { | 469 didUpdateTXTRecordData:(NSData*)data { |
467 container_->OnServicesUpdate(local_discovery::ServiceWatcher::UPDATE_CHANGED, | 470 container_->OnServicesUpdate(local_discovery::ServiceWatcher::UPDATE_CHANGED, |
468 [[sender name] UTF8String]); | 471 base::SysNSStringToUTF8([sender name])); |
469 } | 472 } |
470 | 473 |
471 @end | 474 @end |
472 | 475 |
473 @implementation NetServiceDelegate | 476 @implementation NetServiceDelegate |
474 | 477 |
475 - (id)initWithContainer: | 478 - (id)initWithContainer: |
476 (ServiceResolverImplMac::NetServiceContainer*)container { | 479 (ServiceResolverImplMac::NetServiceContainer*)container { |
477 if ((self = [super init])) { | 480 if ((self = [super init])) { |
478 container_ = container; | 481 container_ = container; |
479 } | 482 } |
480 return self; | 483 return self; |
481 } | 484 } |
482 | 485 |
483 - (void)netServiceDidResolveAddress:(NSNetService *)sender { | 486 - (void)netServiceDidResolveAddress:(NSNetService*)sender { |
484 container_->OnResolveUpdate(local_discovery::ServiceResolver::STATUS_SUCCESS); | 487 container_->OnResolveUpdate(local_discovery::ServiceResolver::STATUS_SUCCESS); |
485 } | 488 } |
486 | 489 |
487 - (void)netService:(NSNetService *)sender | 490 - (void)netService:(NSNetService*)sender |
488 didNotResolve:(NSDictionary *)errorDict { | 491 didNotResolve:(NSDictionary*)errorDict { |
489 container_->OnResolveUpdate( | 492 container_->OnResolveUpdate( |
490 local_discovery::ServiceResolver::STATUS_REQUEST_TIMEOUT); | 493 local_discovery::ServiceResolver::STATUS_REQUEST_TIMEOUT); |
491 } | 494 } |
492 | 495 |
493 @end | 496 @end |
OLD | NEW |