| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "device/bluetooth/bluetooth_socket_win.h" | 
 |  | 
 | #include <objbase.h> | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/sequenced_task_runner.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/strings/sys_string_conversions.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "device/bluetooth/bluetooth_device_win.h" | 
 | #include "device/bluetooth/bluetooth_init_win.h" | 
 | #include "device/bluetooth/bluetooth_service_record_win.h" | 
 | #include "device/bluetooth/bluetooth_socket_thread.h" | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/ip_endpoint.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/winsock_init.h" | 
 | #include "net/base/winsock_util.h" | 
 |  | 
 | namespace { | 
 |  | 
 | const char kL2CAPNotSupported[] = "Bluetooth L2CAP protocal is not supported"; | 
 | const char kSocketAlreadyConnected[] = "Socket is already connected."; | 
 | const char kInvalidRfcommPort[] = "Invalid RFCCOMM port."; | 
 | const char kFailedToCreateSocket[] = "Failed to create socket."; | 
 | const char kFailedToBindSocket[] = "Failed to bind socket."; | 
 | const char kFailedToListenOnSocket[] = "Failed to listen on socket."; | 
 | const char kFailedToGetSockNameForSocket[] = "Failed to getsockname."; | 
 | const char kFailedToAccept[] = "Failed to accept."; | 
 | const char kInvalidUUID[] = "Invalid UUID"; | 
 | const char kWsaSetServiceError[] = "WSASetService error."; | 
 |  | 
 | std::string IPEndPointToBluetoothAddress(const net::IPEndPoint& end_point) { | 
 |   if (end_point.address().size() != net::kBluetoothAddressSize) | 
 |     return std::string(); | 
 |   // The address is copied from BTH_ADDR field of SOCKADDR_BTH, which is a | 
 |   // 64-bit ULONGLONG that stores Bluetooth address in little-endian. Print in | 
 |   // reverse order to preserve the correct ordering. | 
 |   return base::StringPrintf( | 
 |       "%02X:%02X:%02X:%02X:%02X:%02X", end_point.address().bytes()[5], | 
 |       end_point.address().bytes()[4], end_point.address().bytes()[3], | 
 |       end_point.address().bytes()[2], end_point.address().bytes()[1], | 
 |       end_point.address().bytes()[0]); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace device { | 
 |  | 
 | struct BluetoothSocketWin::ServiceRegData { | 
 |   ServiceRegData() { | 
 |     ZeroMemory(&address, sizeof(address)); | 
 |     ZeroMemory(&address_info, sizeof(address_info)); | 
 |     ZeroMemory(&uuid, sizeof(uuid)); | 
 |     ZeroMemory(&service, sizeof(service)); | 
 |   } | 
 |  | 
 |   SOCKADDR_BTH address; | 
 |   CSADDR_INFO address_info; | 
 |   GUID uuid; | 
 |   base::string16 name; | 
 |   WSAQUERYSET service; | 
 | }; | 
 |  | 
 | // static | 
 | scoped_refptr<BluetoothSocketWin> | 
 | BluetoothSocketWin::CreateBluetoothSocket( | 
 |     scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | 
 |     scoped_refptr<device::BluetoothSocketThread> socket_thread) { | 
 |   DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); | 
 |  | 
 |   return make_scoped_refptr( | 
 |       new BluetoothSocketWin(ui_task_runner, socket_thread)); | 
 | } | 
 |  | 
 | BluetoothSocketWin::BluetoothSocketWin( | 
 |     scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | 
 |     scoped_refptr<BluetoothSocketThread> socket_thread) | 
 |     : BluetoothSocketNet(ui_task_runner, socket_thread), | 
 |       supports_rfcomm_(false), | 
 |       rfcomm_channel_(0xFF), | 
 |       bth_addr_(BTH_ADDR_NULL) { | 
 | } | 
 |  | 
 | BluetoothSocketWin::~BluetoothSocketWin() { | 
 | } | 
 |  | 
 | void BluetoothSocketWin::Connect( | 
 |     const BluetoothDeviceWin* device, | 
 |     const BluetoothUUID& uuid, | 
 |     const base::Closure& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | 
 |   DCHECK(device); | 
 |  | 
 |   if (!uuid.IsValid()) { | 
 |     error_callback.Run(kInvalidUUID); | 
 |     return; | 
 |   } | 
 |  | 
 |   const BluetoothServiceRecordWin* service_record_win = | 
 |       device->GetServiceRecord(uuid); | 
 |   if (!service_record_win) { | 
 |     error_callback.Run(kInvalidUUID); | 
 |     return; | 
 |   } | 
 |  | 
 |   device_address_ = service_record_win->device_address(); | 
 |   if (service_record_win->SupportsRfcomm()) { | 
 |     supports_rfcomm_ = true; | 
 |     rfcomm_channel_ = service_record_win->rfcomm_channel(); | 
 |     bth_addr_ = service_record_win->device_bth_addr(); | 
 |   } | 
 |  | 
 |   socket_thread()->task_runner()->PostTask( | 
 |       FROM_HERE, | 
 |       base::Bind( | 
 |           &BluetoothSocketWin::DoConnect, | 
 |           this, | 
 |           base::Bind(&BluetoothSocketWin::PostSuccess, this, success_callback), | 
 |           base::Bind( | 
 |               &BluetoothSocketWin::PostErrorCompletion, this, error_callback))); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::Listen(scoped_refptr<BluetoothAdapter> adapter, | 
 |                                 const BluetoothUUID& uuid, | 
 |                                 const BluetoothAdapter::ServiceOptions& options, | 
 |                                 const base::Closure& success_callback, | 
 |                                 const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | 
 |  | 
 |   adapter_ = adapter; | 
 |   int rfcomm_channel = options.channel ? *options.channel : 0; | 
 |  | 
 |   // TODO(xiyuan): Use |options.name|. | 
 |   socket_thread()->task_runner()->PostTask( | 
 |       FROM_HERE, | 
 |       base::Bind(&BluetoothSocketWin::DoListen, | 
 |                  this, | 
 |                  uuid, | 
 |                  rfcomm_channel, | 
 |                  success_callback, | 
 |                  error_callback)); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::ResetData() { | 
 |   if (service_reg_data_) { | 
 |     if (WSASetService(&service_reg_data_->service,RNRSERVICE_DELETE, 0) == | 
 |         SOCKET_ERROR) { | 
 |       LOG(WARNING) << "Failed to unregister service."; | 
 |     } | 
 |     service_reg_data_.reset(); | 
 |   } | 
 | } | 
 |  | 
 | void BluetoothSocketWin::Accept( | 
 |     const AcceptCompletionCallback& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | 
 |  | 
 |   socket_thread()->task_runner()->PostTask( | 
 |       FROM_HERE, | 
 |       base::Bind(&BluetoothSocketWin::DoAccept, | 
 |                  this, | 
 |                  success_callback, | 
 |                  error_callback)); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::DoConnect( | 
 |     const base::Closure& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | 
 |   base::ThreadRestrictions::AssertIOAllowed(); | 
 |  | 
 |   if (tcp_socket()) { | 
 |     error_callback.Run(kSocketAlreadyConnected); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!supports_rfcomm_) { | 
 |     // TODO(youngki) add support for L2CAP sockets as well. | 
 |     error_callback.Run(kL2CAPNotSupported); | 
 |     return; | 
 |   } | 
 |  | 
 |   scoped_ptr<net::TCPSocket> scoped_socket( | 
 |       new net::TCPSocket(NULL, net::NetLog::Source())); | 
 |   net::EnsureWinsockInit(); | 
 |   SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); | 
 |   SOCKADDR_BTH sa; | 
 |   ZeroMemory(&sa, sizeof(sa)); | 
 |   sa.addressFamily = AF_BTH; | 
 |   sa.port = rfcomm_channel_; | 
 |   sa.btAddr = bth_addr_; | 
 |  | 
 |   // TODO(rpaquay): Condider making this call non-blocking. | 
 |   int status = connect(socket_fd, reinterpret_cast<SOCKADDR*>(&sa), sizeof(sa)); | 
 |   DWORD error_code = WSAGetLastError(); | 
 |   if (!(status == 0 || error_code == WSAEINPROGRESS)) { | 
 |     LOG(ERROR) << "Failed to connect bluetooth socket " | 
 |                << "(" << device_address_ << "): " | 
 |                << logging::SystemErrorCodeToString(error_code); | 
 |     error_callback.Run("Error connecting to socket: " + | 
 |                        logging::SystemErrorCodeToString(error_code)); | 
 |     closesocket(socket_fd); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the | 
 |   // TCPSocket implementation does not actually require one. | 
 |   int net_result = | 
 |       scoped_socket->AdoptConnectedSocket(socket_fd, net::IPEndPoint()); | 
 |   if (net_result != net::OK) { | 
 |     error_callback.Run("Error connecting to socket: " + | 
 |                        net::ErrorToString(net_result)); | 
 |     closesocket(socket_fd); | 
 |     return; | 
 |   } | 
 |  | 
 |   SetTCPSocket(scoped_socket.Pass()); | 
 |   success_callback.Run(); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::DoListen( | 
 |     const BluetoothUUID& uuid, | 
 |     int rfcomm_channel, | 
 |     const base::Closure& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | 
 |   DCHECK(!tcp_socket() && !service_reg_data_); | 
 |  | 
 |   // The valid range is 0-30. 0 means BT_PORT_ANY and 1-30 are the | 
 |   // valid RFCOMM port numbers of SOCKADDR_BTH. | 
 |   if (rfcomm_channel < 0 || rfcomm_channel > 30) { | 
 |     LOG(WARNING) << "Failed to start service: " | 
 |                  << "Invalid RFCCOMM port " << rfcomm_channel | 
 |                  << ", uuid=" << uuid.value(); | 
 |     PostErrorCompletion(error_callback, kInvalidRfcommPort); | 
 |     return; | 
 |   } | 
 |  | 
 |   SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); | 
 |   if (socket_fd == INVALID_SOCKET) { | 
 |     LOG(WARNING) << "Failed to start service: create socket, " | 
 |                  << "winsock err=" << WSAGetLastError(); | 
 |     PostErrorCompletion(error_callback, kFailedToCreateSocket); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Note that |socket_fd| belongs to a non-TCP address family (i.e. AF_BTH), | 
 |   // TCPSocket methods that involve address could not be called. So bind() | 
 |   // is called on |socket_fd| directly. | 
 |   scoped_ptr<net::TCPSocket> scoped_socket( | 
 |       new net::TCPSocket(NULL, net::NetLog::Source())); | 
 |   scoped_socket->AdoptListenSocket(socket_fd); | 
 |  | 
 |   SOCKADDR_BTH sa; | 
 |   struct sockaddr* sock_addr = reinterpret_cast<struct sockaddr*>(&sa); | 
 |   int sock_addr_len = sizeof(sa); | 
 |   ZeroMemory(&sa, sock_addr_len); | 
 |   sa.addressFamily = AF_BTH; | 
 |   sa.port = rfcomm_channel ? rfcomm_channel : BT_PORT_ANY; | 
 |   if (bind(socket_fd, sock_addr, sock_addr_len) < 0) { | 
 |     LOG(WARNING) << "Failed to start service: create socket, " | 
 |                  << "winsock err=" << WSAGetLastError(); | 
 |     PostErrorCompletion(error_callback, kFailedToBindSocket); | 
 |     return; | 
 |   } | 
 |  | 
 |   const int kListenBacklog = 5; | 
 |   if (scoped_socket->Listen(kListenBacklog) < 0) { | 
 |     LOG(WARNING) << "Failed to start service: Listen" | 
 |                  << "winsock err=" << WSAGetLastError(); | 
 |     PostErrorCompletion(error_callback, kFailedToListenOnSocket); | 
 |     return; | 
 |   } | 
 |  | 
 |   scoped_ptr<ServiceRegData> reg_data(new ServiceRegData); | 
 |   reg_data->name = base::UTF8ToUTF16(uuid.canonical_value()); | 
 |  | 
 |   if (getsockname(socket_fd, sock_addr, &sock_addr_len)) { | 
 |     LOG(WARNING) << "Failed to start service: getsockname, " | 
 |                  << "winsock err=" << WSAGetLastError(); | 
 |     PostErrorCompletion(error_callback, kFailedToGetSockNameForSocket); | 
 |     return; | 
 |   } | 
 |   reg_data->address = sa; | 
 |  | 
 |   reg_data->address_info.LocalAddr.iSockaddrLength = sizeof(sa); | 
 |   reg_data->address_info.LocalAddr.lpSockaddr = | 
 |       reinterpret_cast<struct sockaddr*>(®_data->address); | 
 |   reg_data->address_info.iSocketType = SOCK_STREAM; | 
 |   reg_data->address_info.iProtocol = BTHPROTO_RFCOMM; | 
 |  | 
 |   base::string16 cannonical_uuid = L"{" + base::ASCIIToUTF16( | 
 |       uuid.canonical_value()) + L"}"; | 
 |   if (!SUCCEEDED(CLSIDFromString(cannonical_uuid.c_str(), ®_data->uuid))) { | 
 |     LOG(WARNING) << "Failed to start service: " | 
 |                  << ", invalid uuid=" << cannonical_uuid; | 
 |     PostErrorCompletion(error_callback, kInvalidUUID); | 
 |     return; | 
 |   } | 
 |  | 
 |   reg_data->service.dwSize = sizeof(WSAQUERYSET); | 
 |   reg_data->service.lpszServiceInstanceName = | 
 |       const_cast<LPWSTR>(reg_data->name.c_str()); | 
 |   reg_data->service.lpServiceClassId = ®_data->uuid; | 
 |   reg_data->service.dwNameSpace = NS_BTH; | 
 |   reg_data->service.dwNumberOfCsAddrs = 1; | 
 |   reg_data->service.lpcsaBuffer = ®_data->address_info; | 
 |  | 
 |   if (WSASetService(®_data->service, | 
 |                     RNRSERVICE_REGISTER, 0) == SOCKET_ERROR) { | 
 |     LOG(WARNING) << "Failed to register profile: WSASetService" | 
 |                  << "winsock err=" << WSAGetLastError(); | 
 |     PostErrorCompletion(error_callback, kWsaSetServiceError); | 
 |     return; | 
 |   } | 
 |  | 
 |   SetTCPSocket(scoped_socket.Pass()); | 
 |   service_reg_data_ = reg_data.Pass(); | 
 |  | 
 |   PostSuccess(success_callback); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::DoAccept( | 
 |     const AcceptCompletionCallback& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | 
 |   int result = tcp_socket()->Accept( | 
 |       &accept_socket_, | 
 |       &accept_address_, | 
 |       base::Bind(&BluetoothSocketWin::OnAcceptOnSocketThread, | 
 |                  this, | 
 |                  success_callback, | 
 |                  error_callback)); | 
 |   if (result != net::OK && result != net::ERR_IO_PENDING) { | 
 |     LOG(WARNING) << "Failed to accept, net err=" << result; | 
 |     PostErrorCompletion(error_callback, kFailedToAccept); | 
 |   } | 
 | } | 
 |  | 
 | void BluetoothSocketWin::OnAcceptOnSocketThread( | 
 |     const AcceptCompletionCallback& success_callback, | 
 |     const ErrorCompletionCallback& error_callback, | 
 |     int accept_result) { | 
 |   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | 
 |   if (accept_result != net::OK) { | 
 |     LOG(WARNING) << "OnAccept error, net err=" << accept_result; | 
 |     PostErrorCompletion(error_callback, kFailedToAccept); | 
 |     return; | 
 |   } | 
 |  | 
 |   ui_task_runner()->PostTask( | 
 |     FROM_HERE, | 
 |     base::Bind(&BluetoothSocketWin::OnAcceptOnUI, | 
 |                this, | 
 |                base::Passed(&accept_socket_), | 
 |                accept_address_, | 
 |                success_callback, | 
 |                error_callback)); | 
 | } | 
 |  | 
 | void BluetoothSocketWin::OnAcceptOnUI( | 
 |     scoped_ptr<net::TCPSocket> accept_socket, | 
 |     const net::IPEndPoint& peer_address, | 
 |     const AcceptCompletionCallback& success_callback, | 
 |     const ErrorCompletionCallback& error_callback) { | 
 |   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | 
 |  | 
 |   const std::string peer_device_address = | 
 |       IPEndPointToBluetoothAddress(peer_address); | 
 |   const BluetoothDevice* peer_device = adapter_->GetDevice(peer_device_address); | 
 |   if (!peer_device) { | 
 |     LOG(WARNING) << "OnAccept failed with unknown device, addr=" | 
 |                  << peer_device_address; | 
 |     error_callback.Run(kFailedToAccept); | 
 |     return; | 
 |   } | 
 |  | 
 |   scoped_refptr<BluetoothSocketWin> peer_socket = | 
 |       CreateBluetoothSocket(ui_task_runner(), socket_thread()); | 
 |   peer_socket->SetTCPSocket(accept_socket.Pass()); | 
 |   success_callback.Run(peer_device, peer_socket); | 
 | } | 
 |  | 
 | }  // namespace device |