| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/sync/notifier/base/async_network_alive.h" | |
| 6 | |
| 7 #include <winsock2.h> | |
| 8 | |
| 9 #include "base/scoped_handle_win.h" | |
| 10 #include "chrome/browser/sync/notifier/base/utils.h" | |
| 11 #include "talk/base/common.h" | |
| 12 #include "talk/base/criticalsection.h" | |
| 13 #include "talk/base/logging.h" | |
| 14 #include "talk/base/physicalsocketserver.h" | |
| 15 #include "talk/base/scoped_ptr.h" | |
| 16 | |
| 17 namespace notifier { | |
| 18 | |
| 19 class PlatformNetworkInfo { | |
| 20 public: | |
| 21 PlatformNetworkInfo() : ws_handle_(NULL), event_handle_(NULL) { | |
| 22 } | |
| 23 | |
| 24 ~PlatformNetworkInfo() { | |
| 25 Close(); | |
| 26 } | |
| 27 | |
| 28 void Close() { | |
| 29 talk_base::CritScope crit_scope(&crit_sect_); | |
| 30 if (ws_handle_) { | |
| 31 if (event_handle_.IsValid()) // Unblock any waiting for network changes. | |
| 32 SetEvent(event_handle_.Get()); | |
| 33 // finishes the iteration. | |
| 34 VERIFY(WSALookupServiceEnd(ws_handle_) == 0); | |
| 35 ws_handle_ = NULL; | |
| 36 LOG(INFO) << "WSACleanup 1"; | |
| 37 ::WSACleanup(); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 bool IsAlive(bool* error) { | |
| 42 ASSERT(error); | |
| 43 *error = false; | |
| 44 | |
| 45 // If IsAlive was previously called, we need a new handle. | |
| 46 // Why? If we use the same handle, we only get diffs on what changed | |
| 47 // which isn't what we want. | |
| 48 Close(); | |
| 49 int result = Initialize(); | |
| 50 if (result != 0) { | |
| 51 LOG(ERROR) << "failed:" << result; | |
| 52 // Default to alive on error. | |
| 53 *error = true; | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 bool alive = false; | |
| 58 | |
| 59 // Retrieve network info and move to next one. In this function, we only | |
| 60 // need to know whether or not there is network connection. | |
| 61 // allocate 256 bytes for name, it should be enough for most cases. | |
| 62 // If the name is longer, it is OK as we will check the code returned and | |
| 63 // set correct network status. | |
| 64 char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; | |
| 65 bool flush_previous_result = false; | |
| 66 do { | |
| 67 DWORD control_flags = LUP_RETURN_NAME; | |
| 68 if (flush_previous_result) { | |
| 69 control_flags |= LUP_FLUSHPREVIOUS; | |
| 70 } | |
| 71 DWORD length = sizeof(result_buffer); | |
| 72 reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])->dwSize = | |
| 73 sizeof(WSAQUERYSET); | |
| 74 // ws_handle_ may be NULL (if exiting), but the call will simply fail | |
| 75 int result = ::WSALookupServiceNext( | |
| 76 ws_handle_, | |
| 77 control_flags, | |
| 78 &length, | |
| 79 reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])); | |
| 80 | |
| 81 if (result == 0) { | |
| 82 // get at least one connection, return "connected". | |
| 83 alive = true; | |
| 84 } else { | |
| 85 ASSERT(result == SOCKET_ERROR); | |
| 86 result = ::WSAGetLastError(); | |
| 87 if (result == WSA_E_NO_MORE || result == WSAENOMORE) { | |
| 88 break; | |
| 89 } | |
| 90 | |
| 91 // Error code WSAEFAULT means there is a network connection but the | |
| 92 // result_buffer size is too small to contain the results. The | |
| 93 // variable "length" returned from WSALookupServiceNext is the minimum | |
| 94 // number of bytes required. We do not need to retrieve detail info. | |
| 95 // Return "alive" in this case. | |
| 96 if (result == WSAEFAULT) { | |
| 97 alive = true; | |
| 98 flush_previous_result = true; | |
| 99 } else { | |
| 100 LOG(WARNING) << "failed:" << result; | |
| 101 *error = true; | |
| 102 break; | |
| 103 } | |
| 104 } | |
| 105 } while (true); | |
| 106 | |
| 107 LOG(INFO) << "alive: " << alive; | |
| 108 return alive; | |
| 109 } | |
| 110 | |
| 111 bool WaitForChange() { | |
| 112 // IsAlive must be called first. | |
| 113 int junk1 = 0, junk2 = 0; | |
| 114 DWORD bytes_returned = 0; | |
| 115 int result = SOCKET_ERROR; | |
| 116 { | |
| 117 talk_base::CritScope crit_scope(&crit_sect_); | |
| 118 if (!ws_handle_) | |
| 119 return false; | |
| 120 ASSERT(!event_handle_.IsValid()); | |
| 121 event_handle_.Set(CreateEvent(NULL, FALSE, FALSE, NULL)); | |
| 122 if (!event_handle_.IsValid()) { | |
| 123 LOG(WARNING) << "failed to CreateEvent"; | |
| 124 return false; | |
| 125 } | |
| 126 WSAOVERLAPPED overlapped = {0}; | |
| 127 overlapped.hEvent = event_handle_.Get(); | |
| 128 WSACOMPLETION completion; | |
| 129 ::SetZero(completion); | |
| 130 completion.Type = NSP_NOTIFY_EVENT; | |
| 131 completion.Parameters.Event.lpOverlapped = &overlapped; | |
| 132 | |
| 133 LOG(INFO) << "calling WSANSPIoctl"; | |
| 134 // Do a non-blocking request for change notification. event_handle_ | |
| 135 // will get signaled when there is a change, so we wait on it later. | |
| 136 // It can also be signaled by Close() in order allow clean termination. | |
| 137 result = ::WSANSPIoctl(ws_handle_, | |
| 138 SIO_NSP_NOTIFY_CHANGE, | |
| 139 &junk1, | |
| 140 0, | |
| 141 &junk2, | |
| 142 0, | |
| 143 &bytes_returned, | |
| 144 &completion); | |
| 145 } | |
| 146 if (NO_ERROR != result) { | |
| 147 result = ::WSAGetLastError(); | |
| 148 if (WSA_IO_PENDING != result) { | |
| 149 LOG(WARNING) << "failed: " << result; | |
| 150 event_handle_.Close(); | |
| 151 return false; | |
| 152 } | |
| 153 } | |
| 154 LOG(INFO) << "waiting"; | |
| 155 WaitForSingleObject(event_handle_.Get(), INFINITE); | |
| 156 event_handle_.Close(); | |
| 157 LOG(INFO) << "changed"; | |
| 158 return true; | |
| 159 } | |
| 160 | |
| 161 private: | |
| 162 int Initialize() { | |
| 163 WSADATA wsa_data; | |
| 164 LOG(INFO) << "calling WSAStartup"; | |
| 165 int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); | |
| 166 if (result != ERROR_SUCCESS) { | |
| 167 LOG(ERROR) << "failed:" << result; | |
| 168 return result; | |
| 169 } | |
| 170 | |
| 171 WSAQUERYSET query_set = {0}; | |
| 172 query_set.dwSize = sizeof(WSAQUERYSET); | |
| 173 query_set.dwNameSpace = NS_NLA; | |
| 174 // Initiate a client query to iterate through the | |
| 175 // currently connected networks. | |
| 176 if (0 != ::WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, | |
| 177 &ws_handle_)) { | |
| 178 result = ::WSAGetLastError(); | |
| 179 LOG(INFO) << "WSACleanup 2"; | |
| 180 ::WSACleanup(); | |
| 181 ASSERT(ws_handle_ == NULL); | |
| 182 ws_handle_ = NULL; | |
| 183 return result; | |
| 184 } | |
| 185 return 0; | |
| 186 } | |
| 187 talk_base::CriticalSection crit_sect_; | |
| 188 HANDLE ws_handle_; | |
| 189 ScopedHandle event_handle_; | |
| 190 DISALLOW_COPY_AND_ASSIGN(PlatformNetworkInfo); | |
| 191 }; | |
| 192 | |
| 193 class AsyncNetworkAliveWin32 : public AsyncNetworkAlive { | |
| 194 public: | |
| 195 AsyncNetworkAliveWin32() { | |
| 196 } | |
| 197 | |
| 198 virtual ~AsyncNetworkAliveWin32() { | |
| 199 if (network_info_) { | |
| 200 delete network_info_; | |
| 201 network_info_ = NULL; | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 protected: | |
| 206 // SignalThread Interface | |
| 207 virtual void DoWork() { | |
| 208 if (!network_info_) { | |
| 209 network_info_ = new PlatformNetworkInfo(); | |
| 210 } else { | |
| 211 // Since network_info is set, it means that | |
| 212 // we are suppose to wait for network state changes. | |
| 213 if (!network_info_->WaitForChange()) { | |
| 214 // The wait was aborted so we must be shutting down. | |
| 215 alive_ = false; | |
| 216 error_ = true; | |
| 217 return; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 if (network_info_->IsAlive(&error_)) { | |
| 222 // If there is an active connection, check that www.google.com:80 | |
| 223 // is reachable. | |
| 224 talk_base::PhysicalSocketServer physical; | |
| 225 scoped_ptr<talk_base::Socket> socket(physical.CreateSocket(SOCK_STREAM)); | |
| 226 if (socket->Connect(talk_base::SocketAddress("talk.google.com", 5222))) { | |
| 227 alive_ = false; | |
| 228 } else { | |
| 229 alive_ = true; | |
| 230 } | |
| 231 } else { | |
| 232 // If there are no available connections, then we aren't alive. | |
| 233 alive_ = false; | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 virtual void OnWorkStop() { | |
| 238 if (network_info_) { | |
| 239 network_info_->Close(); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 private: | |
| 244 DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveWin32); | |
| 245 }; | |
| 246 | |
| 247 AsyncNetworkAlive* AsyncNetworkAlive::Create() { | |
| 248 return new AsyncNetworkAliveWin32(); | |
| 249 } | |
| 250 | |
| 251 } // namespace notifier | |
| OLD | NEW |