OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * libjingle |
| 3 * Copyright 2004--2005, Google Inc. |
| 4 * |
| 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: |
| 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 * this list of conditions and the following disclaimer in the documentation |
| 12 * and/or other materials provided with the distribution. |
| 13 * 3. The name of the author may not be used to endorse or promote products |
| 14 * derived from this software without specific prior written permission. |
| 15 * |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 */ |
| 27 |
| 28 #include "talk/base/win32.h" |
| 29 |
| 30 #include <winsock2.h> |
| 31 #include <ws2tcpip.h> |
| 32 #include <algorithm> |
| 33 |
| 34 #include "talk/base/basictypes.h" |
| 35 #include "talk/base/common.h" |
| 36 #include "talk/base/logging.h" |
| 37 |
| 38 namespace talk_base { |
| 39 |
| 40 // Helper function declarations for inet_ntop/inet_pton. |
| 41 static const char* inet_ntop_v4(const void* src, char* dst, socklen_t size); |
| 42 static const char* inet_ntop_v6(const void* src, char* dst, socklen_t size); |
| 43 static int inet_pton_v4(const char* src, void* dst); |
| 44 static int inet_pton_v6(const char* src, void* dst); |
| 45 |
| 46 // Implementation of inet_ntop (create a printable representation of an |
| 47 // ip address). XP doesn't have its own inet_ntop, and |
| 48 // WSAAddressToString requires both IPv6 to be installed and for Winsock |
| 49 // to be initialized. |
| 50 const char* win32_inet_ntop(int af, const void *src, |
| 51 char* dst, socklen_t size) { |
| 52 if (!src || !dst) { |
| 53 return NULL; |
| 54 } |
| 55 switch (af) { |
| 56 case AF_INET: { |
| 57 return inet_ntop_v4(src, dst, size); |
| 58 } |
| 59 case AF_INET6: { |
| 60 return inet_ntop_v6(src, dst, size); |
| 61 } |
| 62 } |
| 63 return NULL; |
| 64 } |
| 65 |
| 66 // As above, but for inet_pton. Wraps inet_addr for v4, and implements inet_pton |
| 67 // for v6. Slightly more permissive than the RFC specified inet_pton, as it uses |
| 68 // windows' inet_addr which permits octal and hexadecimal values in v4 |
| 69 // addresses, while inet_pton only allows decimal. |
| 70 // Note that our inet_ntop will output normal 'dotted' v4 addresses only. |
| 71 int win32_inet_pton(int af, const char* src, void* dst) { |
| 72 if (!src || !dst) { |
| 73 return 0; |
| 74 } |
| 75 if (af == AF_INET) { |
| 76 return inet_pton_v4(src, dst); |
| 77 } else if (af == AF_INET6) { |
| 78 return inet_pton_v6(src, dst); |
| 79 } |
| 80 return -1; |
| 81 } |
| 82 |
| 83 // Helper function for inet_ntop for IPv4 addresses. |
| 84 // Outputs "dotted-quad" decimal notation. |
| 85 const char* inet_ntop_v4(const void* src, char* dst, socklen_t size) { |
| 86 if (size < INET_ADDRSTRLEN) { |
| 87 return NULL; |
| 88 } |
| 89 const struct in_addr* as_in_addr = |
| 90 reinterpret_cast<const struct in_addr*>(src); |
| 91 talk_base::sprintfn(dst, size, "%d.%d.%d.%d", |
| 92 as_in_addr->S_un.S_un_b.s_b1, |
| 93 as_in_addr->S_un.S_un_b.s_b2, |
| 94 as_in_addr->S_un.S_un_b.s_b3, |
| 95 as_in_addr->S_un.S_un_b.s_b4); |
| 96 return dst; |
| 97 } |
| 98 |
| 99 // Helper function for inet_ntop for IPv6 addresses. |
| 100 const char* inet_ntop_v6(const void* src, char* dst, socklen_t size) { |
| 101 if (size < INET6_ADDRSTRLEN) { |
| 102 return NULL; |
| 103 } |
| 104 const uint16* as_shorts = |
| 105 reinterpret_cast<const uint16*>(src); |
| 106 int runpos[8]; |
| 107 int current = 1; |
| 108 int max = 1; |
| 109 int maxpos = -1; |
| 110 int run_array_size = ARRAY_SIZE(runpos); |
| 111 // Run over the address marking runs of 0s. |
| 112 for (int i = 0; i < run_array_size; ++i) { |
| 113 if (as_shorts[i] == 0) { |
| 114 runpos[i] = current; |
| 115 if (current > max) { |
| 116 maxpos = i; |
| 117 max = current; |
| 118 } |
| 119 ++current; |
| 120 } else { |
| 121 runpos[i] = -1; |
| 122 current =1; |
| 123 } |
| 124 } |
| 125 |
| 126 if (max > 1) { |
| 127 int tmpmax = maxpos; |
| 128 // Run back through, setting -1 for all but the longest run. |
| 129 for (int i = run_array_size - 1; i >= 0; i--) { |
| 130 if (i > tmpmax) { |
| 131 runpos[i] = -1; |
| 132 } else if (runpos[i] == -1) { |
| 133 // We're less than maxpos, we hit a -1, so the 'good' run is done. |
| 134 // Setting tmpmax -1 means all remaining positions get set to -1. |
| 135 tmpmax = -1; |
| 136 } |
| 137 } |
| 138 } |
| 139 |
| 140 char* cursor = dst; |
| 141 // Print IPv4 compatible and IPv4 mapped addresses using the IPv4 helper. |
| 142 // These addresses have an initial run of either eight zero-bytes followed |
| 143 // by 0xFFFF, or an initial run of ten zero-bytes. |
| 144 if (runpos[0] == 1 && (maxpos == 5 || |
| 145 (maxpos == 4 && as_shorts[5] == 0xFFFF))) { |
| 146 *cursor++ = ':'; |
| 147 *cursor++ = ':'; |
| 148 if (maxpos == 4) { |
| 149 cursor += talk_base::sprintfn(cursor, INET6_ADDRSTRLEN - 2, "ffff:"); |
| 150 } |
| 151 const struct in_addr* as_v4 = |
| 152 reinterpret_cast<const struct in_addr*>(&(as_shorts[6])); |
| 153 inet_ntop_v4(as_v4, cursor, (INET6_ADDRSTRLEN - (cursor - dst))); |
| 154 } else { |
| 155 for (int i = 0; i < run_array_size; ++i) { |
| 156 if (runpos[i] == -1) { |
| 157 cursor += talk_base::sprintfn(cursor, |
| 158 INET6_ADDRSTRLEN - (cursor - dst), |
| 159 "%x", ntohs(as_shorts[i])); |
| 160 if (i != 7 && runpos[i + 1] != 1) { |
| 161 *cursor++ = ':'; |
| 162 } |
| 163 } else if (runpos[i] == 1) { |
| 164 // Entered the run; print the colons and skip the run. |
| 165 *cursor++ = ':'; |
| 166 *cursor++ = ':'; |
| 167 i += (max - 1); |
| 168 } |
| 169 } |
| 170 } |
| 171 return dst; |
| 172 } |
| 173 |
| 174 // Helper function for inet_pton for IPv4 addresses. |
| 175 // |src| points to a character string containing an IPv4 network address in |
| 176 // dotted-decimal format, "ddd.ddd.ddd.ddd", where ddd is a decimal number |
| 177 // of up to three digits in the range 0 to 255. |
| 178 // The address is converted and copied to dst, |
| 179 // which must be sizeof(struct in_addr) (4) bytes (32 bits) long. |
| 180 int inet_pton_v4(const char* src, void* dst) { |
| 181 int num_dot = 0; |
| 182 const char* readcursor = src; |
| 183 unsigned char result[4]; |
| 184 memset(result, 0, sizeof(result)); |
| 185 |
| 186 while (*readcursor != 0) { |
| 187 char current = *readcursor; |
| 188 if (current == '.') { |
| 189 if (++num_dot > 3) |
| 190 return 0; |
| 191 } else if (current >= '0' && current <= '9') { |
| 192 int new_value = result[num_dot] * 10 + (current - '0'); |
| 193 if (new_value > 255) { |
| 194 return 0; |
| 195 } |
| 196 result[num_dot] = new_value; |
| 197 } else { |
| 198 return 0; |
| 199 } |
| 200 ++readcursor; |
| 201 } |
| 202 if (num_dot != 3) { |
| 203 return 0; |
| 204 } |
| 205 memcpy(dst, result, 4); |
| 206 return 1; |
| 207 } |
| 208 |
| 209 // Helper function for inet_pton for IPv6 addresses. |
| 210 int inet_pton_v6(const char* src, void* dst) { |
| 211 // sscanf will pick any other invalid chars up, but it parses 0xnnnn as hex. |
| 212 // Check for literal x in the input string. |
| 213 const char* readcursor = src; |
| 214 char c = *readcursor++; |
| 215 while (c) { |
| 216 if (c == 'x') { |
| 217 return 0; |
| 218 } |
| 219 c = *readcursor++; |
| 220 } |
| 221 readcursor = src; |
| 222 |
| 223 struct in6_addr an_addr; |
| 224 memset(&an_addr, 0, sizeof(an_addr)); |
| 225 |
| 226 uint16* addr_cursor = reinterpret_cast<uint16*>(&an_addr.s6_addr[0]); |
| 227 uint16* addr_end = reinterpret_cast<uint16*>(&an_addr.s6_addr[16]); |
| 228 bool seencompressed = false; |
| 229 |
| 230 // Addresses that start with "::" (i.e., a run of initial zeros) or |
| 231 // "::ffff:" can potentially be IPv4 mapped or compatibility addresses. |
| 232 // These have dotted-style IPv4 addresses on the end (e.g. "::192.168.7.1"). |
| 233 if (*readcursor == ':' && *(readcursor+1) == ':' && |
| 234 *(readcursor + 2) != 0) { |
| 235 // Check for periods, which we'll take as a sign of v4 addresses. |
| 236 const char* addrstart = readcursor + 2; |
| 237 if (talk_base::strchr(addrstart, ".")) { |
| 238 const char* colon = talk_base::strchr(addrstart, "::"); |
| 239 if (colon) { |
| 240 uint16 a_short; |
| 241 int bytesread = 0; |
| 242 if (sscanf(addrstart, "%hx%n", &a_short, &bytesread) != 1 || |
| 243 a_short != 0xFFFF || bytesread != 4) { |
| 244 // Colons + periods means has to be ::ffff:a.b.c.d. But it wasn't. |
| 245 return 0; |
| 246 } else { |
| 247 an_addr.s6_addr[10] = 0xFF; |
| 248 an_addr.s6_addr[11] = 0xFF; |
| 249 addrstart = colon + 1; |
| 250 } |
| 251 } |
| 252 struct in_addr v4; |
| 253 if (inet_pton_v4(addrstart, &v4.s_addr)) { |
| 254 memcpy(&an_addr.s6_addr[12], &v4, sizeof(v4)); |
| 255 memcpy(dst, &an_addr, sizeof(an_addr)); |
| 256 return 1; |
| 257 } else { |
| 258 // Invalid v4 address. |
| 259 return 0; |
| 260 } |
| 261 } |
| 262 } |
| 263 |
| 264 // For addresses without a trailing IPv4 component ('normal' IPv6 addresses). |
| 265 while (*readcursor != 0 && addr_cursor < addr_end) { |
| 266 if (*readcursor == ':') { |
| 267 if (*(readcursor + 1) == ':') { |
| 268 if (seencompressed) { |
| 269 // Can only have one compressed run of zeroes ("::") per address. |
| 270 return 0; |
| 271 } |
| 272 // Hit a compressed run. Count colons to figure out how much of the |
| 273 // address is skipped. |
| 274 readcursor += 2; |
| 275 const char* coloncounter = readcursor; |
| 276 int coloncount = 0; |
| 277 if (*coloncounter == 0) { |
| 278 // Special case - trailing ::. |
| 279 addr_cursor = addr_end; |
| 280 } else { |
| 281 while (*coloncounter) { |
| 282 if (*coloncounter == ':') { |
| 283 ++coloncount; |
| 284 } |
| 285 ++coloncounter; |
| 286 } |
| 287 // (coloncount + 1) is the number of shorts left in the address. |
| 288 addr_cursor = addr_end - (coloncount + 1); |
| 289 seencompressed = true; |
| 290 } |
| 291 } else { |
| 292 ++readcursor; |
| 293 } |
| 294 } else { |
| 295 uint16 word; |
| 296 int bytesread = 0; |
| 297 if (sscanf(readcursor, "%hx%n", &word, &bytesread) != 1) { |
| 298 return 0; |
| 299 } else { |
| 300 *addr_cursor = htons(word); |
| 301 ++addr_cursor; |
| 302 readcursor += bytesread; |
| 303 if (*readcursor != ':' && *readcursor != '\0') { |
| 304 return 0; |
| 305 } |
| 306 } |
| 307 } |
| 308 } |
| 309 |
| 310 if (*readcursor != '\0' || addr_cursor < addr_end) { |
| 311 // Catches addresses too short or too long. |
| 312 return 0; |
| 313 } |
| 314 memcpy(dst, &an_addr, sizeof(an_addr)); |
| 315 return 1; |
| 316 } |
| 317 |
| 318 // |
| 319 // Unix time is in seconds relative to 1/1/1970. So we compute the windows |
| 320 // FILETIME of that time/date, then we add/subtract in appropriate units to |
| 321 // convert to/from unix time. |
| 322 // The units of FILETIME are 100ns intervals, so by multiplying by or dividing |
| 323 // by 10000000, we can convert to/from seconds. |
| 324 // |
| 325 // FileTime = UnixTime*10000000 + FileTime(1970) |
| 326 // UnixTime = (FileTime-FileTime(1970))/10000000 |
| 327 // |
| 328 |
| 329 void FileTimeToUnixTime(const FILETIME& ft, time_t* ut) { |
| 330 ASSERT(NULL != ut); |
| 331 |
| 332 // FILETIME has an earlier date base than time_t (1/1/1970), so subtract off |
| 333 // the difference. |
| 334 SYSTEMTIME base_st; |
| 335 memset(&base_st, 0, sizeof(base_st)); |
| 336 base_st.wDay = 1; |
| 337 base_st.wMonth = 1; |
| 338 base_st.wYear = 1970; |
| 339 |
| 340 FILETIME base_ft; |
| 341 SystemTimeToFileTime(&base_st, &base_ft); |
| 342 |
| 343 ULARGE_INTEGER base_ul, current_ul; |
| 344 memcpy(&base_ul, &base_ft, sizeof(FILETIME)); |
| 345 memcpy(¤t_ul, &ft, sizeof(FILETIME)); |
| 346 |
| 347 // Divide by big number to convert to seconds, then subtract out the 1970 |
| 348 // base date value. |
| 349 const ULONGLONG RATIO = 10000000; |
| 350 *ut = static_cast<time_t>((current_ul.QuadPart - base_ul.QuadPart) / RATIO); |
| 351 } |
| 352 |
| 353 void UnixTimeToFileTime(const time_t& ut, FILETIME* ft) { |
| 354 ASSERT(NULL != ft); |
| 355 |
| 356 // FILETIME has an earlier date base than time_t (1/1/1970), so add in |
| 357 // the difference. |
| 358 SYSTEMTIME base_st; |
| 359 memset(&base_st, 0, sizeof(base_st)); |
| 360 base_st.wDay = 1; |
| 361 base_st.wMonth = 1; |
| 362 base_st.wYear = 1970; |
| 363 |
| 364 FILETIME base_ft; |
| 365 SystemTimeToFileTime(&base_st, &base_ft); |
| 366 |
| 367 ULARGE_INTEGER base_ul; |
| 368 memcpy(&base_ul, &base_ft, sizeof(FILETIME)); |
| 369 |
| 370 // Multiply by big number to convert to 100ns units, then add in the 1970 |
| 371 // base date value. |
| 372 const ULONGLONG RATIO = 10000000; |
| 373 ULARGE_INTEGER current_ul; |
| 374 current_ul.QuadPart = base_ul.QuadPart + static_cast<int64>(ut) * RATIO; |
| 375 memcpy(ft, ¤t_ul, sizeof(FILETIME)); |
| 376 } |
| 377 |
| 378 bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename) { |
| 379 // TODO: Integrate into fileutils.h |
| 380 // TODO: Handle wide and non-wide cases via TCHAR? |
| 381 // TODO: Skip \\?\ processing if the length is not > MAX_PATH? |
| 382 // TODO: Write unittests |
| 383 |
| 384 // Convert to Utf16 |
| 385 int wlen = ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.length() + 1, |
| 386 NULL, 0); |
| 387 if (0 == wlen) { |
| 388 return false; |
| 389 } |
| 390 wchar_t* wfilename = STACK_ARRAY(wchar_t, wlen); |
| 391 if (0 == ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.length() + 1, |
| 392 wfilename, wlen)) { |
| 393 return false; |
| 394 } |
| 395 // Replace forward slashes with backslashes |
| 396 std::replace(wfilename, wfilename + wlen, L'/', L'\\'); |
| 397 // Convert to complete filename |
| 398 DWORD full_len = ::GetFullPathName(wfilename, 0, NULL, NULL); |
| 399 if (0 == full_len) { |
| 400 return false; |
| 401 } |
| 402 wchar_t* filepart = NULL; |
| 403 wchar_t* full_filename = STACK_ARRAY(wchar_t, full_len + 6); |
| 404 wchar_t* start = full_filename + 6; |
| 405 if (0 == ::GetFullPathName(wfilename, full_len, start, &filepart)) { |
| 406 return false; |
| 407 } |
| 408 // Add long-path prefix |
| 409 const wchar_t kLongPathPrefix[] = L"\\\\?\\UNC"; |
| 410 if ((start[0] != L'\\') || (start[1] != L'\\')) { |
| 411 // Non-unc path: <pathname> |
| 412 // Becomes: \\?\<pathname> |
| 413 start -= 4; |
| 414 ASSERT(start >= full_filename); |
| 415 memcpy(start, kLongPathPrefix, 4 * sizeof(wchar_t)); |
| 416 } else if (start[2] != L'?') { |
| 417 // Unc path: \\<server>\<pathname> |
| 418 // Becomes: \\?\UNC\<server>\<pathname> |
| 419 start -= 6; |
| 420 ASSERT(start >= full_filename); |
| 421 memcpy(start, kLongPathPrefix, 7 * sizeof(wchar_t)); |
| 422 } else { |
| 423 // Already in long-path form. |
| 424 } |
| 425 filename->assign(start); |
| 426 return true; |
| 427 } |
| 428 |
| 429 bool GetOsVersion(int* major, int* minor, int* build) { |
| 430 OSVERSIONINFO info = {0}; |
| 431 info.dwOSVersionInfoSize = sizeof(info); |
| 432 if (GetVersionEx(&info)) { |
| 433 if (major) *major = info.dwMajorVersion; |
| 434 if (minor) *minor = info.dwMinorVersion; |
| 435 if (build) *build = info.dwBuildNumber; |
| 436 return true; |
| 437 } |
| 438 return false; |
| 439 } |
| 440 |
| 441 bool GetCurrentProcessIntegrityLevel(int* level) { |
| 442 bool ret = false; |
| 443 HANDLE process = ::GetCurrentProcess(), token; |
| 444 if (OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, &token)) { |
| 445 DWORD size; |
| 446 if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &size) && |
| 447 GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
| 448 |
| 449 char* buf = STACK_ARRAY(char, size); |
| 450 TOKEN_MANDATORY_LABEL* til = |
| 451 reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buf); |
| 452 if (GetTokenInformation(token, TokenIntegrityLevel, til, size, &size)) { |
| 453 |
| 454 DWORD count = *GetSidSubAuthorityCount(til->Label.Sid); |
| 455 *level = *GetSidSubAuthority(til->Label.Sid, count - 1); |
| 456 ret = true; |
| 457 } |
| 458 } |
| 459 CloseHandle(token); |
| 460 } |
| 461 return ret; |
| 462 } |
| 463 } // namespace talk_base |
OLD | NEW |