Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(777)

Side by Side Diff: chrome/browser/local_discovery/service_discovery_client_mac.mm

Issue 2132723003: [Mac] Make the local_discovery client more resilient to invalid UTF-8. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: '' Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/browser/local_discovery/service_discovery_client_mac_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/local_discovery/service_discovery_client_mac_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698