Chromium Code Reviews| 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 |