| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/http/http_auth_handler_negotiate.h" | |
| 6 | |
| 7 #include "base/strings/string_util.h" | |
| 8 #include "base/strings/utf_string_conversions.h" | |
| 9 #include "net/base/net_errors.h" | |
| 10 #include "net/base/test_completion_callback.h" | |
| 11 #include "net/dns/mock_host_resolver.h" | |
| 12 #include "net/http/http_request_info.h" | |
| 13 #include "net/http/mock_allow_url_security_manager.h" | |
| 14 #if defined(OS_WIN) | |
| 15 #include "net/http/mock_sspi_library_win.h" | |
| 16 #elif defined(OS_POSIX) | |
| 17 #include "net/http/mock_gssapi_library_posix.h" | |
| 18 #endif | |
| 19 #include "testing/gtest/include/gtest/gtest.h" | |
| 20 #include "testing/platform_test.h" | |
| 21 | |
| 22 #if defined(OS_WIN) | |
| 23 typedef net::MockSSPILibrary MockAuthLibrary; | |
| 24 #elif defined(OS_POSIX) | |
| 25 typedef net::test::MockGSSAPILibrary MockAuthLibrary; | |
| 26 #endif | |
| 27 | |
| 28 namespace net { | |
| 29 | |
| 30 class HttpAuthHandlerNegotiateTest : public PlatformTest { | |
| 31 public: | |
| 32 void SetUp() override { | |
| 33 auth_library_ = new MockAuthLibrary(); | |
| 34 resolver_.reset(new MockHostResolver()); | |
| 35 resolver_->rules()->AddIPLiteralRule("alias", "10.0.0.2", | |
| 36 "canonical.example.com"); | |
| 37 | |
| 38 url_security_manager_.reset(new MockAllowURLSecurityManager()); | |
| 39 factory_.reset(new HttpAuthHandlerNegotiate::Factory()); | |
| 40 factory_->set_url_security_manager(url_security_manager_.get()); | |
| 41 factory_->set_library(auth_library_); | |
| 42 factory_->set_host_resolver(resolver_.get()); | |
| 43 } | |
| 44 | |
| 45 void SetupMocks(MockAuthLibrary* mock_library) { | |
| 46 #if defined(OS_WIN) | |
| 47 security_package_.reset(new SecPkgInfoW); | |
| 48 memset(security_package_.get(), 0x0, sizeof(SecPkgInfoW)); | |
| 49 security_package_->cbMaxToken = 1337; | |
| 50 mock_library->ExpectQuerySecurityPackageInfo( | |
| 51 L"Negotiate", SEC_E_OK, security_package_.get()); | |
| 52 #elif defined(OS_POSIX) | |
| 53 // Copied from an actual transaction! | |
| 54 static const char kAuthResponse[] = | |
| 55 "\x60\x82\x02\xCA\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x01" | |
| 56 "\x00\x6E\x82\x02\xB9\x30\x82\x02\xB5\xA0\x03\x02\x01\x05\xA1\x03" | |
| 57 "\x02\x01\x0E\xA2\x07\x03\x05\x00\x00\x00\x00\x00\xA3\x82\x01\xC1" | |
| 58 "\x61\x82\x01\xBD\x30\x82\x01\xB9\xA0\x03\x02\x01\x05\xA1\x16\x1B" | |
| 59 "\x14\x55\x4E\x49\x58\x2E\x43\x4F\x52\x50\x2E\x47\x4F\x4F\x47\x4C" | |
| 60 "\x45\x2E\x43\x4F\x4D\xA2\x2C\x30\x2A\xA0\x03\x02\x01\x01\xA1\x23" | |
| 61 "\x30\x21\x1B\x04\x68\x6F\x73\x74\x1B\x19\x6E\x69\x6E\x6A\x61\x2E" | |
| 62 "\x63\x61\x6D\x2E\x63\x6F\x72\x70\x2E\x67\x6F\x6F\x67\x6C\x65\x2E" | |
| 63 "\x63\x6F\x6D\xA3\x82\x01\x6A\x30\x82\x01\x66\xA0\x03\x02\x01\x10" | |
| 64 "\xA1\x03\x02\x01\x01\xA2\x82\x01\x58\x04\x82\x01\x54\x2C\xB1\x2B" | |
| 65 "\x0A\xA5\xFF\x6F\xEC\xDE\xB0\x19\x6E\x15\x20\x18\x0C\x42\xB3\x2C" | |
| 66 "\x4B\xB0\x37\x02\xDE\xD3\x2F\xB4\xBF\xCA\xEC\x0E\xF9\xF3\x45\x6A" | |
| 67 "\x43\xF3\x8D\x79\xBD\xCB\xCD\xB2\x2B\xB8\xFC\xD6\xB4\x7F\x09\x48" | |
| 68 "\x14\xA7\x4F\xD2\xEE\xBC\x1B\x2F\x18\x3B\x81\x97\x7B\x28\xA4\xAF" | |
| 69 "\xA8\xA3\x7A\x31\x1B\xFC\x97\xB6\xBA\x8A\x50\x50\xD7\x44\xB8\x30" | |
| 70 "\xA4\x51\x4C\x3A\x95\x6C\xA1\xED\xE2\xEF\x17\xFE\xAB\xD2\xE4\x70" | |
| 71 "\xDE\xEB\x7E\x86\x48\xC5\x3E\x19\x5B\x83\x17\xBB\x52\x26\xC0\xF3" | |
| 72 "\x38\x0F\xB0\x8C\x72\xC9\xB0\x8B\x99\x96\x18\xE1\x9E\x67\x9D\xDC" | |
| 73 "\xF5\x39\x80\x70\x35\x3F\x98\x72\x16\x44\xA2\xC0\x10\xAA\x70\xBD" | |
| 74 "\x06\x6F\x83\xB1\xF4\x67\xA4\xBD\xDA\xF7\x79\x1D\x96\xB5\x7E\xF8" | |
| 75 "\xC6\xCF\xB4\xD9\x51\xC9\xBB\xB4\x20\x3C\xDD\xB9\x2C\x38\xEA\x40" | |
| 76 "\xFB\x02\x6C\xCB\x48\x71\xE8\xF4\x34\x5B\x63\x5D\x13\x57\xBD\xD1" | |
| 77 "\x3D\xDE\xE8\x4A\x51\x6E\xBE\x4C\xF5\xA3\x84\xF7\x4C\x4E\x58\x04" | |
| 78 "\xBE\xD1\xCC\x22\xA0\x43\xB0\x65\x99\x6A\xE0\x78\x0D\xFC\xE1\x42" | |
| 79 "\xA9\x18\xCF\x55\x4D\x23\xBD\x5C\x0D\xB5\x48\x25\x47\xCC\x01\x54" | |
| 80 "\x36\x4D\x0C\x6F\xAC\xCD\x33\x21\xC5\x63\x18\x91\x68\x96\xE9\xD1" | |
| 81 "\xD8\x23\x1F\x21\xAE\x96\xA3\xBD\x27\xF7\x4B\xEF\x4C\x43\xFF\xF8" | |
| 82 "\x22\x57\xCF\x68\x6C\x35\xD5\x21\x48\x5B\x5F\x8F\xA5\xB9\x6F\x99" | |
| 83 "\xA6\xE0\x6E\xF0\xC5\x7C\x91\xC8\x0B\x8A\x4B\x4E\x80\x59\x02\xE9" | |
| 84 "\xE8\x3F\x87\x04\xA6\xD1\xCA\x26\x3C\xF0\xDA\x57\xFA\xE6\xAF\x25" | |
| 85 "\x43\x34\xE1\xA4\x06\x1A\x1C\xF4\xF5\x21\x9C\x00\x98\xDD\xF0\xB4" | |
| 86 "\x8E\xA4\x81\xDA\x30\x81\xD7\xA0\x03\x02\x01\x10\xA2\x81\xCF\x04" | |
| 87 "\x81\xCC\x20\x39\x34\x60\x19\xF9\x4C\x26\x36\x46\x99\x7A\xFD\x2B" | |
| 88 "\x50\x8B\x2D\x47\x72\x38\x20\x43\x0E\x6E\x28\xB3\xA7\x4F\x26\xF1" | |
| 89 "\xF1\x7B\x02\x63\x58\x5A\x7F\xC8\xD0\x6E\xF5\xD1\xDA\x28\x43\x1B" | |
| 90 "\x6D\x9F\x59\x64\xDE\x90\xEA\x6C\x8C\xA9\x1B\x1E\x92\x29\x24\x23" | |
| 91 "\x2C\xE3\xEA\x64\xEF\x91\xA5\x4E\x94\xE1\xDC\x56\x3A\xAF\xD5\xBC" | |
| 92 "\xC9\xD3\x9B\x6B\x1F\xBE\x40\xE5\x40\xFF\x5E\x21\xEA\xCE\xFC\xD5" | |
| 93 "\xB0\xE5\xBA\x10\x94\xAE\x16\x54\xFC\xEB\xAB\xF1\xD4\x20\x31\xCC" | |
| 94 "\x26\xFE\xBE\xFE\x22\xB6\x9B\x1A\xE5\x55\x2C\x93\xB7\x3B\xD6\x4C" | |
| 95 "\x35\x35\xC1\x59\x61\xD4\x1F\x2E\x4C\xE1\x72\x8F\x71\x4B\x0C\x39" | |
| 96 "\x80\x79\xFA\xCD\xEA\x71\x1B\xAE\x35\x41\xED\xF9\x65\x0C\x59\xF8" | |
| 97 "\xE1\x27\xDA\xD6\xD1\x20\x32\xCD\xBF\xD1\xEF\xE2\xED\xAD\x5D\xA7" | |
| 98 "\x69\xE3\x55\xF9\x30\xD3\xD4\x08\xC8\xCA\x62\xF8\x64\xEC\x9B\x92" | |
| 99 "\x1A\xF1\x03\x2E\xCC\xDC\xEB\x17\xDE\x09\xAC\xA9\x58\x86"; | |
| 100 test::GssContextMockImpl context1( | |
| 101 "localhost", // Source name | |
| 102 "example.com", // Target name | |
| 103 23, // Lifetime | |
| 104 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism | |
| 105 0, // Context flags | |
| 106 1, // Locally initiated | |
| 107 0); // Open | |
| 108 test::GssContextMockImpl context2( | |
| 109 "localhost", // Source name | |
| 110 "example.com", // Target name | |
| 111 23, // Lifetime | |
| 112 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism | |
| 113 0, // Context flags | |
| 114 1, // Locally initiated | |
| 115 1); // Open | |
| 116 test::MockGSSAPILibrary::SecurityContextQuery queries[] = { | |
| 117 test::MockGSSAPILibrary::SecurityContextQuery( | |
| 118 "Negotiate", // Package name | |
| 119 GSS_S_CONTINUE_NEEDED, // Major response code | |
| 120 0, // Minor response code | |
| 121 context1, // Context | |
| 122 NULL, // Expected input token | |
| 123 kAuthResponse), // Output token | |
| 124 test::MockGSSAPILibrary::SecurityContextQuery( | |
| 125 "Negotiate", // Package name | |
| 126 GSS_S_COMPLETE, // Major response code | |
| 127 0, // Minor response code | |
| 128 context2, // Context | |
| 129 kAuthResponse, // Expected input token | |
| 130 kAuthResponse) // Output token | |
| 131 }; | |
| 132 | |
| 133 for (size_t i = 0; i < arraysize(queries); ++i) { | |
| 134 mock_library->ExpectSecurityContext(queries[i].expected_package, | |
| 135 queries[i].response_code, | |
| 136 queries[i].minor_response_code, | |
| 137 queries[i].context_info, | |
| 138 queries[i].expected_input_token, | |
| 139 queries[i].output_token); | |
| 140 } | |
| 141 #endif // defined(OS_POSIX) | |
| 142 } | |
| 143 | |
| 144 #if defined(OS_POSIX) | |
| 145 void SetupErrorMocks(MockAuthLibrary* mock_library, | |
| 146 int major_status, | |
| 147 int minor_status) { | |
| 148 const gss_OID_desc kDefaultMech = { 0, NULL }; | |
| 149 test::GssContextMockImpl context( | |
| 150 "localhost", // Source name | |
| 151 "example.com", // Target name | |
| 152 0, // Lifetime | |
| 153 kDefaultMech, // Mechanism | |
| 154 0, // Context flags | |
| 155 1, // Locally initiated | |
| 156 0); // Open | |
| 157 test::MockGSSAPILibrary::SecurityContextQuery query( | |
| 158 "Negotiate", // Package name | |
| 159 major_status, // Major response code | |
| 160 minor_status, // Minor response code | |
| 161 context, // Context | |
| 162 NULL, // Expected input token | |
| 163 NULL); // Output token | |
| 164 | |
| 165 mock_library->ExpectSecurityContext(query.expected_package, | |
| 166 query.response_code, | |
| 167 query.minor_response_code, | |
| 168 query.context_info, | |
| 169 query.expected_input_token, | |
| 170 query.output_token); | |
| 171 } | |
| 172 | |
| 173 #endif // defined(OS_POSIX) | |
| 174 | |
| 175 int CreateHandler(bool disable_cname_lookup, bool use_port, | |
| 176 bool synchronous_resolve_mode, | |
| 177 const std::string& url_string, | |
| 178 scoped_ptr<HttpAuthHandlerNegotiate>* handler) { | |
| 179 factory_->set_disable_cname_lookup(disable_cname_lookup); | |
| 180 factory_->set_use_port(use_port); | |
| 181 resolver_->set_synchronous_mode(synchronous_resolve_mode); | |
| 182 GURL gurl(url_string); | |
| 183 | |
| 184 // Note: This is a little tricky because CreateAuthHandlerFromString | |
| 185 // expects a scoped_ptr<HttpAuthHandler>* rather than a | |
| 186 // scoped_ptr<HttpAuthHandlerNegotiate>*. This needs to do the cast | |
| 187 // after creating the handler, and make sure that generic_handler | |
| 188 // no longer holds on to the HttpAuthHandlerNegotiate object. | |
| 189 scoped_ptr<HttpAuthHandler> generic_handler; | |
| 190 int rv = factory_->CreateAuthHandlerFromString("Negotiate", | |
| 191 HttpAuth::AUTH_SERVER, | |
| 192 gurl, | |
| 193 BoundNetLog(), | |
| 194 &generic_handler); | |
| 195 if (rv != OK) | |
| 196 return rv; | |
| 197 HttpAuthHandlerNegotiate* negotiate_handler = | |
| 198 static_cast<HttpAuthHandlerNegotiate*>(generic_handler.release()); | |
| 199 handler->reset(negotiate_handler); | |
| 200 return rv; | |
| 201 } | |
| 202 | |
| 203 MockAuthLibrary* AuthLibrary() { return auth_library_; } | |
| 204 | |
| 205 private: | |
| 206 #if defined(OS_WIN) | |
| 207 scoped_ptr<SecPkgInfoW> security_package_; | |
| 208 #endif | |
| 209 // |auth_library_| is passed to |factory_|, which assumes ownership of it. | |
| 210 MockAuthLibrary* auth_library_; | |
| 211 scoped_ptr<MockHostResolver> resolver_; | |
| 212 scoped_ptr<URLSecurityManager> url_security_manager_; | |
| 213 scoped_ptr<HttpAuthHandlerNegotiate::Factory> factory_; | |
| 214 }; | |
| 215 | |
| 216 TEST_F(HttpAuthHandlerNegotiateTest, DisableCname) { | |
| 217 SetupMocks(AuthLibrary()); | |
| 218 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 219 EXPECT_EQ(OK, CreateHandler( | |
| 220 true, false, true, "http://alias:500", &auth_handler)); | |
| 221 | |
| 222 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 223 TestCompletionCallback callback; | |
| 224 HttpRequestInfo request_info; | |
| 225 std::string token; | |
| 226 EXPECT_EQ(OK, auth_handler->GenerateAuthToken(NULL, &request_info, | |
| 227 callback.callback(), &token)); | |
| 228 #if defined(OS_WIN) | |
| 229 EXPECT_EQ("HTTP/alias", auth_handler->spn()); | |
| 230 #elif defined(OS_POSIX) | |
| 231 EXPECT_EQ("HTTP@alias", auth_handler->spn()); | |
| 232 #endif | |
| 233 } | |
| 234 | |
| 235 TEST_F(HttpAuthHandlerNegotiateTest, DisableCnameStandardPort) { | |
| 236 SetupMocks(AuthLibrary()); | |
| 237 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 238 EXPECT_EQ(OK, CreateHandler( | |
| 239 true, true, true, "http://alias:80", &auth_handler)); | |
| 240 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 241 TestCompletionCallback callback; | |
| 242 HttpRequestInfo request_info; | |
| 243 std::string token; | |
| 244 EXPECT_EQ(OK, auth_handler->GenerateAuthToken(NULL, &request_info, | |
| 245 callback.callback(), &token)); | |
| 246 #if defined(OS_WIN) | |
| 247 EXPECT_EQ("HTTP/alias", auth_handler->spn()); | |
| 248 #elif defined(OS_POSIX) | |
| 249 EXPECT_EQ("HTTP@alias", auth_handler->spn()); | |
| 250 #endif | |
| 251 } | |
| 252 | |
| 253 TEST_F(HttpAuthHandlerNegotiateTest, DisableCnameNonstandardPort) { | |
| 254 SetupMocks(AuthLibrary()); | |
| 255 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 256 EXPECT_EQ(OK, CreateHandler( | |
| 257 true, true, true, "http://alias:500", &auth_handler)); | |
| 258 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 259 TestCompletionCallback callback; | |
| 260 HttpRequestInfo request_info; | |
| 261 std::string token; | |
| 262 EXPECT_EQ(OK, auth_handler->GenerateAuthToken(NULL, &request_info, | |
| 263 callback.callback(), &token)); | |
| 264 #if defined(OS_WIN) | |
| 265 EXPECT_EQ("HTTP/alias:500", auth_handler->spn()); | |
| 266 #elif defined(OS_POSIX) | |
| 267 EXPECT_EQ("HTTP@alias:500", auth_handler->spn()); | |
| 268 #endif | |
| 269 } | |
| 270 | |
| 271 TEST_F(HttpAuthHandlerNegotiateTest, CnameSync) { | |
| 272 SetupMocks(AuthLibrary()); | |
| 273 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 274 EXPECT_EQ(OK, CreateHandler( | |
| 275 false, false, true, "http://alias:500", &auth_handler)); | |
| 276 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 277 TestCompletionCallback callback; | |
| 278 HttpRequestInfo request_info; | |
| 279 std::string token; | |
| 280 EXPECT_EQ(OK, auth_handler->GenerateAuthToken(NULL, &request_info, | |
| 281 callback.callback(), &token)); | |
| 282 #if defined(OS_WIN) | |
| 283 EXPECT_EQ("HTTP/canonical.example.com", auth_handler->spn()); | |
| 284 #elif defined(OS_POSIX) | |
| 285 EXPECT_EQ("HTTP@canonical.example.com", auth_handler->spn()); | |
| 286 #endif | |
| 287 } | |
| 288 | |
| 289 TEST_F(HttpAuthHandlerNegotiateTest, CnameAsync) { | |
| 290 SetupMocks(AuthLibrary()); | |
| 291 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 292 EXPECT_EQ(OK, CreateHandler( | |
| 293 false, false, false, "http://alias:500", &auth_handler)); | |
| 294 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 295 TestCompletionCallback callback; | |
| 296 HttpRequestInfo request_info; | |
| 297 std::string token; | |
| 298 EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken( | |
| 299 NULL, &request_info, callback.callback(), &token)); | |
| 300 EXPECT_EQ(OK, callback.WaitForResult()); | |
| 301 #if defined(OS_WIN) | |
| 302 EXPECT_EQ("HTTP/canonical.example.com", auth_handler->spn()); | |
| 303 #elif defined(OS_POSIX) | |
| 304 EXPECT_EQ("HTTP@canonical.example.com", auth_handler->spn()); | |
| 305 #endif | |
| 306 } | |
| 307 | |
| 308 #if defined(OS_POSIX) | |
| 309 | |
| 310 // This test is only for GSSAPI, as we can't use explicit credentials with | |
| 311 // that library. | |
| 312 TEST_F(HttpAuthHandlerNegotiateTest, ServerNotInKerberosDatabase) { | |
| 313 SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE, 0x96C73A07); // No server | |
| 314 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 315 EXPECT_EQ(OK, CreateHandler( | |
| 316 false, false, false, "http://alias:500", &auth_handler)); | |
| 317 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 318 TestCompletionCallback callback; | |
| 319 HttpRequestInfo request_info; | |
| 320 std::string token; | |
| 321 EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken( | |
| 322 NULL, &request_info, callback.callback(), &token)); | |
| 323 EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS, callback.WaitForResult()); | |
| 324 } | |
| 325 | |
| 326 // This test is only for GSSAPI, as we can't use explicit credentials with | |
| 327 // that library. | |
| 328 TEST_F(HttpAuthHandlerNegotiateTest, NoKerberosCredentials) { | |
| 329 SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE, 0x96C73AC3); // No credentials | |
| 330 scoped_ptr<HttpAuthHandlerNegotiate> auth_handler; | |
| 331 EXPECT_EQ(OK, CreateHandler( | |
| 332 false, false, false, "http://alias:500", &auth_handler)); | |
| 333 ASSERT_TRUE(auth_handler.get() != NULL); | |
| 334 TestCompletionCallback callback; | |
| 335 HttpRequestInfo request_info; | |
| 336 std::string token; | |
| 337 EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken( | |
| 338 NULL, &request_info, callback.callback(), &token)); | |
| 339 EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS, callback.WaitForResult()); | |
| 340 } | |
| 341 | |
| 342 #if defined(DLOPEN_KERBEROS) | |
| 343 TEST_F(HttpAuthHandlerNegotiateTest, MissingGSSAPI) { | |
| 344 scoped_ptr<HostResolver> host_resolver(new MockHostResolver()); | |
| 345 MockAllowURLSecurityManager url_security_manager; | |
| 346 scoped_ptr<HttpAuthHandlerNegotiate::Factory> negotiate_factory( | |
| 347 new HttpAuthHandlerNegotiate::Factory()); | |
| 348 negotiate_factory->set_host_resolver(host_resolver.get()); | |
| 349 negotiate_factory->set_url_security_manager(&url_security_manager); | |
| 350 negotiate_factory->set_library( | |
| 351 new GSSAPISharedLibrary("/this/library/does/not/exist")); | |
| 352 | |
| 353 GURL gurl("http://www.example.com"); | |
| 354 scoped_ptr<HttpAuthHandler> generic_handler; | |
| 355 int rv = negotiate_factory->CreateAuthHandlerFromString( | |
| 356 "Negotiate", | |
| 357 HttpAuth::AUTH_SERVER, | |
| 358 gurl, | |
| 359 BoundNetLog(), | |
| 360 &generic_handler); | |
| 361 EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv); | |
| 362 EXPECT_TRUE(generic_handler.get() == NULL); | |
| 363 } | |
| 364 #endif // defined(DLOPEN_KERBEROS) | |
| 365 | |
| 366 #endif // defined(OS_POSIX) | |
| 367 | |
| 368 } // namespace net | |
| OLD | NEW |