| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/extensions/api/dial/dial_service.h" | 5 #include "chrome/browser/extensions/api/dial/dial_service.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 using net::IPEndPoint; | 35 using net::IPEndPoint; |
| 36 using net::NetworkInterface; | 36 using net::NetworkInterface; |
| 37 using net::NetworkInterfaceList; | 37 using net::NetworkInterfaceList; |
| 38 using net::StringIOBuffer; | 38 using net::StringIOBuffer; |
| 39 using net::UDPSocket; | 39 using net::UDPSocket; |
| 40 | 40 |
| 41 namespace extensions { | 41 namespace extensions { |
| 42 | 42 |
| 43 namespace { | 43 namespace { |
| 44 | 44 |
| 45 // The total number of requests to make. | 45 // The total number of requests to make per discovery cycle. |
| 46 const int kDialNumRequests = 1; | 46 const int kDialMaxRequests = 4; |
| 47 | 47 |
| 48 // The interval to wait between successive requests. | 48 // The interval to wait between successive requests. |
| 49 const int kDialRequestIntervalMillis = 1000; | 49 const int kDialRequestIntervalMillis = 1000; |
| 50 | 50 |
| 51 // The timeout (MX) set for responses. | 51 // The timeout (MX) set for responses. |
| 52 const int kDialResponseTimeoutSecs = 2; | 52 const int kDialResponseTimeoutSecs = 2; |
| 53 | 53 |
| 54 // The multicast IP address for discovery. | 54 // The multicast IP address for discovery. |
| 55 const char kDialRequestAddress[] = "239.255.255.250"; | 55 const char kDialRequestAddress[] = "239.255.255.250"; |
| 56 | 56 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 bool success = net::GetNetworkList(&list); | 106 bool success = net::GetNetworkList(&list); |
| 107 if (!success) | 107 if (!success) |
| 108 DVLOG(1) << "Could not retrieve network list!"; | 108 DVLOG(1) << "Could not retrieve network list!"; |
| 109 | 109 |
| 110 loop->PostTask(FROM_HERE, base::Bind(cb, list)); | 110 loop->PostTask(FROM_HERE, base::Bind(cb, list)); |
| 111 } | 111 } |
| 112 | 112 |
| 113 } // namespace | 113 } // namespace |
| 114 | 114 |
| 115 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) | 115 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) |
| 116 : is_writing_(false), is_reading_(false), discovery_active_(false), | 116 : is_writing_(false), |
| 117 num_requests_sent_(0) { | 117 is_reading_(false), |
| 118 discovery_active_(false), |
| 119 num_requests_sent_(0), |
| 120 max_requests_(kDialMaxRequests), |
| 121 finish_delay_(TimeDelta::FromMilliseconds((kDialMaxRequests - 1) * |
| 122 kDialRequestIntervalMillis) + |
| 123 TimeDelta::FromSeconds(kDialResponseTimeoutSecs)), |
| 124 request_interval_(TimeDelta::FromMilliseconds(kDialRequestIntervalMillis)) |
| 125 { |
| 118 IPAddressNumber address; | 126 IPAddressNumber address; |
| 119 bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address); | 127 bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address); |
| 120 DCHECK(result); | 128 DCHECK(result); |
| 121 send_address_ = IPEndPoint(address, kDialRequestPort); | 129 send_address_ = IPEndPoint(address, kDialRequestPort); |
| 122 send_buffer_ = new StringIOBuffer(BuildRequest()); | 130 send_buffer_ = new StringIOBuffer(BuildRequest()); |
| 123 net_log_ = net_log; | 131 net_log_ = net_log; |
| 124 net_log_source_.type = net::NetLog::SOURCE_UDP_SOCKET; | 132 net_log_source_.type = net::NetLog::SOURCE_UDP_SOCKET; |
| 125 net_log_source_.id = net_log_->NextID(); | 133 net_log_source_.id = net_log_->NextID(); |
| 126 finish_delay_ = TimeDelta::FromMilliseconds((kDialNumRequests - 1) * | |
| 127 kDialRequestIntervalMillis) + | |
| 128 TimeDelta::FromSeconds(kDialResponseTimeoutSecs); | |
| 129 } | 134 } |
| 130 | 135 |
| 131 DialServiceImpl::~DialServiceImpl() { | 136 DialServiceImpl::~DialServiceImpl() { |
| 132 DCHECK(thread_checker_.CalledOnValidThread()); | 137 DCHECK(thread_checker_.CalledOnValidThread()); |
| 133 } | 138 } |
| 134 | 139 |
| 135 void DialServiceImpl::AddObserver(Observer* observer) { | 140 void DialServiceImpl::AddObserver(Observer* observer) { |
| 136 DCHECK(thread_checker_.CalledOnValidThread()); | 141 DCHECK(thread_checker_.CalledOnValidThread()); |
| 137 observer_list_.AddObserver(observer); | 142 observer_list_.AddObserver(observer); |
| 138 } | 143 } |
| 139 | 144 |
| 140 void DialServiceImpl::RemoveObserver(Observer* observer) { | 145 void DialServiceImpl::RemoveObserver(Observer* observer) { |
| 141 DCHECK(thread_checker_.CalledOnValidThread()); | 146 DCHECK(thread_checker_.CalledOnValidThread()); |
| 142 observer_list_.RemoveObserver(observer); | 147 observer_list_.RemoveObserver(observer); |
| 143 } | 148 } |
| 144 | 149 |
| 145 bool DialServiceImpl::HasObserver(Observer* observer) { | 150 bool DialServiceImpl::HasObserver(Observer* observer) { |
| 146 DCHECK(thread_checker_.CalledOnValidThread()); | 151 DCHECK(thread_checker_.CalledOnValidThread()); |
| 147 return observer_list_.HasObserver(observer); | 152 return observer_list_.HasObserver(observer); |
| 148 } | 153 } |
| 149 | 154 |
| 150 bool DialServiceImpl::Discover() { | 155 bool DialServiceImpl::Discover() { |
| 151 DCHECK(thread_checker_.CalledOnValidThread()); | 156 DCHECK(thread_checker_.CalledOnValidThread()); |
| 152 if (discovery_active_) | 157 if (discovery_active_) |
| 153 return false; | 158 return false; |
| 154 discovery_active_ = true; | 159 discovery_active_ = true; |
| 155 | 160 |
| 156 DVLOG(1) << "Discovery started."; | 161 DVLOG(1) << "Discovery started."; |
| 157 | 162 |
| 158 // TODO(mfoltz): Send multiple requests. | 163 StartDiscovery(); |
| 159 StartRequest(); | |
| 160 return true; | 164 return true; |
| 161 } | 165 } |
| 162 | 166 |
| 163 void DialServiceImpl::FinishDiscovery() { | 167 void DialServiceImpl::StartDiscovery() { |
| 164 DCHECK(thread_checker_.CalledOnValidThread()); | 168 DCHECK(thread_checker_.CalledOnValidThread()); |
| 165 DCHECK(discovery_active_); | 169 DCHECK(discovery_active_); |
| 166 DVLOG(1) << "Discovery finished."; | 170 if (socket_.get()) |
| 167 CloseSocket(); | 171 return; |
| 168 finish_timer_.Stop(); | 172 |
| 169 discovery_active_ = false; | 173 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
| 170 num_requests_sent_ = 0; | 174 &GetNetworkListOnFileThread, |
| 171 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); | 175 base::MessageLoopProxy::current(), base::Bind( |
| 176 &DialServiceImpl::SendNetworkList, AsWeakPtr()))); |
| 172 } | 177 } |
| 173 | 178 |
| 174 bool DialServiceImpl::BindAndWriteSocket( | 179 bool DialServiceImpl::BindSocketAndSendRequest( |
| 175 const NetworkInterface& bind_interface) { | 180 const IPAddressNumber& bind_ip_address) { |
| 176 DCHECK(thread_checker_.CalledOnValidThread()); | 181 DCHECK(thread_checker_.CalledOnValidThread()); |
| 177 DCHECK(!socket_.get()); | 182 DCHECK(!socket_.get()); |
| 178 | 183 |
| 179 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); | 184 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); |
| 180 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, | 185 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, |
| 181 rand_cb, | 186 rand_cb, |
| 182 net_log_, | 187 net_log_, |
| 183 net_log_source_)); | 188 net_log_source_)); |
| 184 socket_->AllowBroadcast(); | 189 socket_->AllowBroadcast(); |
| 185 | 190 |
| 186 // Schedule a timer to finish the discovery process (and close the socket). | 191 // Schedule a timer to finish the discovery process (and close the socket). |
| 187 finish_timer_.Start(FROM_HERE, | 192 if (finish_delay_ > TimeDelta::FromSeconds(0)) { |
| 188 finish_delay_, | 193 finish_timer_.Start(FROM_HERE, |
| 189 this, | 194 finish_delay_, |
| 190 &DialServiceImpl::FinishDiscovery); | 195 this, |
| 196 &DialServiceImpl::FinishDiscovery); |
| 197 } |
| 191 | 198 |
| 192 // 0 means bind a random port | 199 // 0 means bind a random port |
| 193 IPEndPoint address(bind_interface.address, 0); | 200 IPEndPoint address(bind_ip_address, 0); |
| 194 | 201 |
| 195 if (!CheckResult("Bind", socket_->Bind(address))) | 202 if (!CheckResult("Bind", socket_->Bind(address))) |
| 196 return false; | 203 return false; |
| 197 | 204 |
| 198 DCHECK(socket_.get()); | 205 DCHECK(socket_.get()); |
| 199 | 206 |
| 200 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | 207 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); |
| 201 ReadSocket(); | 208 if (!ReadSocket()) |
| 209 return false; |
| 210 SendOneRequest(); |
| 211 return true; |
| 212 } |
| 213 |
| 214 void DialServiceImpl::SendOneRequest() { |
| 215 if (num_requests_sent_ == max_requests_) { |
| 216 request_timer_.Stop(); |
| 217 return; |
| 218 } |
| 219 num_requests_sent_++; |
| 220 if (!socket_.get()) { |
| 221 DLOG(WARNING) << "Socket not connected."; |
| 222 return; |
| 223 } |
| 202 | 224 |
| 203 if (is_writing_) { | 225 if (is_writing_) { |
| 204 DVLOG(1) << "Already writing."; | 226 DVLOG(1) << "Already writing."; |
| 205 return false; | 227 return; |
| 206 } | 228 } |
| 229 DVLOG(1) << "Sending request " << num_requests_sent_ << "/" |
| 230 << max_requests_; |
| 207 is_writing_ = true; | 231 is_writing_ = true; |
| 208 int result = socket_->SendTo( | 232 int result = socket_->SendTo( |
| 209 send_buffer_.get(), send_buffer_->size(), send_address_, | 233 send_buffer_.get(), send_buffer_->size(), send_address_, |
| 210 base::Bind(&DialServiceImpl::OnSocketWrite, AsWeakPtr())); | 234 base::Bind(&DialServiceImpl::OnSocketWrite, AsWeakPtr())); |
| 211 bool result_ok = CheckResult("SendTo", result); | 235 bool result_ok = CheckResult("SendTo", result); |
| 212 if (result_ok && result > 0) { | 236 if (result_ok && result > 0) { |
| 213 // Synchronous write. | 237 // Synchronous write. |
| 214 OnSocketWrite(result); | 238 OnSocketWrite(result); |
| 215 } | 239 } |
| 216 return result_ok; | |
| 217 } | |
| 218 | |
| 219 void DialServiceImpl::StartRequest() { | |
| 220 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 221 DCHECK(discovery_active_); | |
| 222 if (socket_.get()) | |
| 223 return; | |
| 224 | |
| 225 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | |
| 226 &GetNetworkListOnFileThread, | |
| 227 base::MessageLoopProxy::current(), base::Bind( | |
| 228 &DialServiceImpl::SendNetworkList, AsWeakPtr()))); | |
| 229 } | |
| 230 | |
| 231 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { | |
| 232 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 233 if (!networks.size()) { | |
| 234 DVLOG(1) << "No network interfaces found!"; | |
| 235 FOR_EACH_OBSERVER( | |
| 236 Observer, observer_list_, OnError(this, DIAL_SERVICE_NO_INTERFACES)); | |
| 237 return; | |
| 238 } | |
| 239 | |
| 240 const NetworkInterface* interface = NULL; | |
| 241 // Returns the first IPv4 address found. If there is a need for discovery | |
| 242 // across multiple networks, we could manage multiple sockets. | |
| 243 | |
| 244 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 | |
| 245 for (NetworkInterfaceList::const_iterator iter = networks.begin(); | |
| 246 iter != networks.end(); ++iter) { | |
| 247 DVLOG(1) << "Found " << iter->name << ", " | |
| 248 << net::IPAddressToString(iter->address); | |
| 249 if (iter->address.size() == net::kIPv4AddressSize) { | |
| 250 interface = &*iter; | |
| 251 break; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 if (interface == NULL) { | |
| 256 DVLOG(1) << "Could not find a valid interface to bind."; | |
| 257 } else { | |
| 258 BindAndWriteSocket(*interface); | |
| 259 } | |
| 260 } | 240 } |
| 261 | 241 |
| 262 void DialServiceImpl::OnSocketWrite(int result) { | 242 void DialServiceImpl::OnSocketWrite(int result) { |
| 263 DCHECK(thread_checker_.CalledOnValidThread()); | 243 DCHECK(thread_checker_.CalledOnValidThread()); |
| 264 is_writing_ = false; | 244 is_writing_ = false; |
| 265 if (!CheckResult("OnSocketWrite", result)) | 245 if (!CheckResult("OnSocketWrite", result)) |
| 266 return; | 246 return; |
| 267 | 247 |
| 268 if (result != send_buffer_->size()) { | 248 if (result != send_buffer_->size()) { |
| 269 DLOG(ERROR) << "Sent " << result << " chars, expected " | 249 DLOG(ERROR) << "Sent " << result << " chars, expected " |
| 270 << send_buffer_->size() << " chars"; | 250 << send_buffer_->size() << " chars"; |
| 271 } | 251 } |
| 272 // If discovery is inactive, no reason to notify observers. | 252 // If discovery is inactive, no reason to notify observers. |
| 273 if (!discovery_active_) { | 253 if (!discovery_active_) { |
| 274 DVLOG(1) << "Request sent after discovery finished. Ignoring."; | 254 DVLOG(1) << "Request sent after discovery finished. Ignoring."; |
| 275 return; | 255 return; |
| 276 } | 256 } |
| 277 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); | 257 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); |
| 278 num_requests_sent_++; | 258 // If we need to send additional requests, schedule a timer to do so. |
| 259 if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) { |
| 260 request_timer_.Start(FROM_HERE, |
| 261 request_interval_, |
| 262 this, |
| 263 &DialServiceImpl::SendOneRequest); |
| 264 } |
| 279 } | 265 } |
| 280 | 266 |
| 281 bool DialServiceImpl::ReadSocket() { | 267 bool DialServiceImpl::ReadSocket() { |
| 282 DCHECK(thread_checker_.CalledOnValidThread()); | 268 DCHECK(thread_checker_.CalledOnValidThread()); |
| 283 if (!socket_.get()) { | 269 if (!socket_.get()) { |
| 284 DLOG(WARNING) << "Socket not connected."; | 270 DLOG(WARNING) << "Socket not connected."; |
| 285 return false; | 271 return false; |
| 286 } | 272 } |
| 287 | 273 |
| 288 if (is_reading_) { | 274 if (is_reading_) { |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 base::StringToInt(config_id, &config_id_int)) { | 383 base::StringToInt(config_id, &config_id_int)) { |
| 398 device->set_config_id(config_id_int); | 384 device->set_config_id(config_id_int); |
| 399 } else { | 385 } else { |
| 400 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " | 386 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " |
| 401 << config_id; | 387 << config_id; |
| 402 } | 388 } |
| 403 | 389 |
| 404 return true; | 390 return true; |
| 405 } | 391 } |
| 406 | 392 |
| 393 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { |
| 394 DCHECK(thread_checker_.CalledOnValidThread()); |
| 395 const NetworkInterface* interface = NULL; |
| 396 // Returns the first IPv4 address found. If there is a need for discovery |
| 397 // across multiple networks, we could manage multiple sockets. |
| 398 |
| 399 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 |
| 400 for (NetworkInterfaceList::const_iterator iter = networks.begin(); |
| 401 iter != networks.end(); ++iter) { |
| 402 DVLOG(1) << "Found " << iter->name << ", " |
| 403 << net::IPAddressToString(iter->address); |
| 404 if (iter->address.size() == net::kIPv4AddressSize) { |
| 405 interface = &*iter; |
| 406 break; |
| 407 } |
| 408 } |
| 409 |
| 410 if (interface == NULL) { |
| 411 DVLOG(1) << "Could not find a valid interface to bind."; |
| 412 FinishDiscovery(); |
| 413 } else { |
| 414 BindSocketAndSendRequest(interface->address); |
| 415 } |
| 416 } |
| 417 |
| 418 void DialServiceImpl::FinishDiscovery() { |
| 419 DCHECK(thread_checker_.CalledOnValidThread()); |
| 420 DCHECK(discovery_active_); |
| 421 DVLOG(1) << "Discovery finished."; |
| 422 CloseSocket(); |
| 423 finish_timer_.Stop(); |
| 424 request_timer_.Stop(); |
| 425 discovery_active_ = false; |
| 426 num_requests_sent_ = 0; |
| 427 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); |
| 428 } |
| 429 |
| 407 void DialServiceImpl::CloseSocket() { | 430 void DialServiceImpl::CloseSocket() { |
| 408 DCHECK(thread_checker_.CalledOnValidThread()); | 431 DCHECK(thread_checker_.CalledOnValidThread()); |
| 409 is_reading_ = false; | 432 is_reading_ = false; |
| 410 is_writing_ = false; | 433 is_writing_ = false; |
| 411 socket_.reset(); | 434 socket_.reset(); |
| 412 } | 435 } |
| 413 | 436 |
| 414 bool DialServiceImpl::CheckResult(const char* operation, int result) { | 437 bool DialServiceImpl::CheckResult(const char* operation, int result) { |
| 415 DCHECK(thread_checker_.CalledOnValidThread()); | 438 DCHECK(thread_checker_.CalledOnValidThread()); |
| 416 DVLOG(1) << "Operation " << operation << " result " << result; | 439 DVLOG(1) << "Operation " << operation << " result " << result; |
| 417 if (result < net::OK && result != net::ERR_IO_PENDING) { | 440 if (result < net::OK && result != net::ERR_IO_PENDING) { |
| 418 CloseSocket(); | 441 CloseSocket(); |
| 419 std::string error_str(net::ErrorToString(result)); | 442 std::string error_str(net::ErrorToString(result)); |
| 420 DVLOG(0) << "dial socket error: " << error_str; | 443 DVLOG(0) << "dial socket error: " << error_str; |
| 421 // TODO(justinlin): More granular socket errors. | 444 // TODO(justinlin): More granular socket errors. |
| 422 FOR_EACH_OBSERVER( | 445 FOR_EACH_OBSERVER( |
| 423 Observer, observer_list_, OnError(this, DIAL_SERVICE_SOCKET_ERROR)); | 446 Observer, observer_list_, OnError(this, DIAL_SERVICE_SOCKET_ERROR)); |
| 424 return false; | 447 return false; |
| 425 } | 448 } |
| 426 return true; | 449 return true; |
| 427 } | 450 } |
| 428 | 451 |
| 429 } // namespace extensions | 452 } // namespace extensions |
| OLD | NEW |