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 "net/socket/ssl_client_socket.h" | 5 #include "net/socket/ssl_client_socket.h" |
6 | 6 |
7 #include "base/callback_helpers.h" | 7 #include "base/callback_helpers.h" |
8 #include "base/memory/ref_counted.h" | 8 #include "base/memory/ref_counted.h" |
9 #include "base/run_loop.h" | 9 #include "base/run_loop.h" |
10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
11 #include "net/base/address_list.h" | 11 #include "net/base/address_list.h" |
12 #include "net/base/io_buffer.h" | 12 #include "net/base/io_buffer.h" |
13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
14 #include "net/base/net_log.h" | 14 #include "net/base/net_log.h" |
15 #include "net/base/net_log_unittest.h" | 15 #include "net/base/net_log_unittest.h" |
16 #include "net/base/test_completion_callback.h" | 16 #include "net/base/test_completion_callback.h" |
17 #include "net/base/test_data_directory.h" | 17 #include "net/base/test_data_directory.h" |
| 18 #include "net/cert/asn1_util.h" |
| 19 #include "net/cert/ct_verifier.h" |
18 #include "net/cert/mock_cert_verifier.h" | 20 #include "net/cert/mock_cert_verifier.h" |
19 #include "net/cert/test_root_certs.h" | 21 #include "net/cert/test_root_certs.h" |
20 #include "net/dns/host_resolver.h" | 22 #include "net/dns/host_resolver.h" |
21 #include "net/http/transport_security_state.h" | 23 #include "net/http/transport_security_state.h" |
22 #include "net/socket/client_socket_factory.h" | 24 #include "net/socket/client_socket_factory.h" |
23 #include "net/socket/client_socket_handle.h" | 25 #include "net/socket/client_socket_handle.h" |
24 #include "net/socket/socket_test_util.h" | 26 #include "net/socket/socket_test_util.h" |
25 #include "net/socket/tcp_client_socket.h" | 27 #include "net/socket/tcp_client_socket.h" |
26 #include "net/ssl/channel_id_service.h" | 28 #include "net/ssl/channel_id_service.h" |
27 #include "net/ssl/default_channel_id_store.h" | 29 #include "net/ssl/default_channel_id_store.h" |
28 #include "net/ssl/ssl_cert_request_info.h" | 30 #include "net/ssl/ssl_cert_request_info.h" |
29 #include "net/ssl/ssl_config_service.h" | 31 #include "net/ssl/ssl_config_service.h" |
30 #include "net/test/cert_test_util.h" | 32 #include "net/test/cert_test_util.h" |
31 #include "net/test/spawned_test_server/spawned_test_server.h" | 33 #include "net/test/spawned_test_server/spawned_test_server.h" |
| 34 #include "testing/gmock/include/gmock/gmock.h" |
32 #include "testing/gtest/include/gtest/gtest.h" | 35 #include "testing/gtest/include/gtest/gtest.h" |
33 #include "testing/platform_test.h" | 36 #include "testing/platform_test.h" |
34 | 37 |
35 //----------------------------------------------------------------------------- | 38 //----------------------------------------------------------------------------- |
36 | 39 |
| 40 using testing::_; |
| 41 using testing::Return; |
| 42 using testing::Truly; |
| 43 |
37 namespace net { | 44 namespace net { |
38 | 45 |
39 namespace { | 46 namespace { |
40 | 47 |
41 const SSLConfig kDefaultSSLConfig; | 48 const SSLConfig kDefaultSSLConfig; |
42 | 49 |
43 // WrappedStreamSocket is a base class that wraps an existing StreamSocket, | 50 // WrappedStreamSocket is a base class that wraps an existing StreamSocket, |
44 // forwarding the Socket and StreamSocket interfaces to the underlying | 51 // forwarding the Socket and StreamSocket interfaces to the underlying |
45 // transport. | 52 // transport. |
46 // This is to provide a common base class for subclasses to override specific | 53 // This is to provide a common base class for subclasses to override specific |
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
649 base::Time delete_end, | 656 base::Time delete_end, |
650 const base::Closure& completion_callback) | 657 const base::Closure& completion_callback) |
651 OVERRIDE {} | 658 OVERRIDE {} |
652 virtual void DeleteAll(const base::Closure& completion_callback) OVERRIDE {} | 659 virtual void DeleteAll(const base::Closure& completion_callback) OVERRIDE {} |
653 virtual void GetAllChannelIDs(const GetChannelIDListCallback& callback) | 660 virtual void GetAllChannelIDs(const GetChannelIDListCallback& callback) |
654 OVERRIDE {} | 661 OVERRIDE {} |
655 virtual int GetChannelIDCount() OVERRIDE { return 0; } | 662 virtual int GetChannelIDCount() OVERRIDE { return 0; } |
656 virtual void SetForceKeepSessionState() OVERRIDE {} | 663 virtual void SetForceKeepSessionState() OVERRIDE {} |
657 }; | 664 }; |
658 | 665 |
| 666 // A mock CTVerifier that records every call to Verify but doesn't verify |
| 667 // anything. |
| 668 class MockCTVerifier : public CTVerifier { |
| 669 public: |
| 670 MOCK_METHOD5(Verify, int(X509Certificate*, |
| 671 const std::string&, |
| 672 const std::string&, |
| 673 ct::CTVerifyResult*, |
| 674 const BoundNetLog&)); |
| 675 }; |
| 676 |
659 class SSLClientSocketTest : public PlatformTest { | 677 class SSLClientSocketTest : public PlatformTest { |
660 public: | 678 public: |
661 SSLClientSocketTest() | 679 SSLClientSocketTest() |
662 : socket_factory_(ClientSocketFactory::GetDefaultFactory()), | 680 : socket_factory_(ClientSocketFactory::GetDefaultFactory()), |
663 cert_verifier_(new MockCertVerifier), | 681 cert_verifier_(new MockCertVerifier), |
664 transport_security_state_(new TransportSecurityState), | 682 transport_security_state_(new TransportSecurityState), |
665 ran_handshake_completion_callback_(false) { | 683 ran_handshake_completion_callback_(false) { |
666 cert_verifier_->set_default_result(OK); | 684 cert_verifier_->set_default_result(OK); |
667 context_.cert_verifier = cert_verifier_.get(); | 685 context_.cert_verifier = cert_verifier_.get(); |
668 context_.transport_security_state = transport_security_state_.get(); | 686 context_.transport_security_state = transport_security_state_.get(); |
669 } | 687 } |
670 | 688 |
671 void RecordCompletedHandshake() { ran_handshake_completion_callback_ = true; } | 689 void RecordCompletedHandshake() { ran_handshake_completion_callback_ = true; } |
672 | 690 |
673 protected: | 691 protected: |
674 // The address of the spawned test server, after calling StartTestServer(). | 692 // The address of the spawned test server, after calling StartTestServer(). |
675 const AddressList& addr() const { return addr_; } | 693 const AddressList& addr() const { return addr_; } |
676 | 694 |
677 // The SpawnedTestServer object, after calling StartTestServer(). | 695 // The SpawnedTestServer object, after calling StartTestServer(). |
678 const SpawnedTestServer* test_server() const { return test_server_.get(); } | 696 const SpawnedTestServer* test_server() const { return test_server_.get(); } |
679 | 697 |
| 698 void SetCTVerifier(CTVerifier* ct_verifier) { |
| 699 context_.cert_transparency_verifier = ct_verifier; |
| 700 } |
| 701 |
680 // Starts the test server with SSL configuration |ssl_options|. Returns true | 702 // Starts the test server with SSL configuration |ssl_options|. Returns true |
681 // on success. | 703 // on success. |
682 bool StartTestServer(const SpawnedTestServer::SSLOptions& ssl_options) { | 704 bool StartTestServer(const SpawnedTestServer::SSLOptions& ssl_options) { |
683 test_server_.reset(new SpawnedTestServer( | 705 test_server_.reset(new SpawnedTestServer( |
684 SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath())); | 706 SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath())); |
685 if (!test_server_->Start()) { | 707 if (!test_server_->Start()) { |
686 LOG(ERROR) << "Could not start SpawnedTestServer"; | 708 LOG(ERROR) << "Could not start SpawnedTestServer"; |
687 return false; | 709 return false; |
688 } | 710 } |
689 | 711 |
(...skipping 1786 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2476 | 2498 |
2477 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, | 2499 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
2478 ssl_options, | 2500 ssl_options, |
2479 base::FilePath()); | 2501 base::FilePath()); |
2480 ASSERT_TRUE(test_server.Start()); | 2502 ASSERT_TRUE(test_server.Start()); |
2481 | 2503 |
2482 AddressList addr; | 2504 AddressList addr; |
2483 ASSERT_TRUE(test_server.GetAddressList(&addr)); | 2505 ASSERT_TRUE(test_server.GetAddressList(&addr)); |
2484 | 2506 |
2485 TestCompletionCallback callback; | 2507 TestCompletionCallback callback; |
2486 CapturingNetLog log; | |
2487 scoped_ptr<StreamSocket> transport( | 2508 scoped_ptr<StreamSocket> transport( |
2488 new TCPClientSocket(addr, &log, NetLog::Source())); | 2509 new TCPClientSocket(addr, &log_, NetLog::Source())); |
2489 int rv = transport->Connect(callback.callback()); | 2510 int rv = callback.GetResult(transport->Connect(callback.callback())); |
2490 if (rv == ERR_IO_PENDING) | |
2491 rv = callback.WaitForResult(); | |
2492 EXPECT_EQ(OK, rv); | 2511 EXPECT_EQ(OK, rv); |
2493 | 2512 |
2494 SSLConfig ssl_config; | 2513 SSLConfig ssl_config; |
2495 ssl_config.signed_cert_timestamps_enabled = true; | 2514 ssl_config.signed_cert_timestamps_enabled = true; |
2496 | 2515 |
| 2516 MockCTVerifier ct_verifier; |
| 2517 SetCTVerifier(&ct_verifier); |
| 2518 |
| 2519 // Check that the SCT list is extracted as expected. |
| 2520 EXPECT_CALL(ct_verifier, Verify(_, "", "test", _, _)).WillRepeatedly( |
| 2521 Return(ERR_CT_NO_SCTS_VERIFIED_OK)); |
| 2522 |
2497 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( | 2523 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
2498 transport.Pass(), test_server.host_port_pair(), ssl_config)); | 2524 transport.Pass(), test_server.host_port_pair(), ssl_config)); |
| 2525 rv = callback.GetResult(sock->Connect(callback.callback())); |
| 2526 EXPECT_EQ(OK, rv); |
2499 | 2527 |
2500 EXPECT_FALSE(sock->IsConnected()); | 2528 EXPECT_TRUE(sock->signed_cert_timestamps_received_); |
| 2529 } |
2501 | 2530 |
2502 rv = sock->Connect(callback.callback()); | 2531 namespace { |
2503 | 2532 |
2504 CapturingNetLog::CapturedEntryList entries; | 2533 bool IsValidOCSPResponse(const base::StringPiece& input) { |
2505 log.GetEntries(&entries); | 2534 base::StringPiece ocsp_response = input; |
2506 EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT)); | 2535 base::StringPiece sequence, response_status, response_bytes; |
2507 if (rv == ERR_IO_PENDING) | 2536 return asn1::GetElement(&ocsp_response, asn1::kSEQUENCE, &sequence) && |
2508 rv = callback.WaitForResult(); | 2537 ocsp_response.empty() && |
2509 EXPECT_EQ(OK, rv); | 2538 asn1::GetElement(&sequence, asn1::kENUMERATED, &response_status) && |
2510 EXPECT_TRUE(sock->IsConnected()); | 2539 asn1::GetElement(&sequence, |
2511 log.GetEntries(&entries); | 2540 asn1::kContextSpecific | asn1::kConstructed | 0, |
2512 EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); | 2541 &response_status) && |
| 2542 sequence.empty(); |
| 2543 } |
2513 | 2544 |
2514 #if !defined(USE_OPENSSL) | 2545 } // namespace |
2515 EXPECT_TRUE(sock->signed_cert_timestamps_received_); | |
2516 #else | |
2517 // Enabling CT for OpenSSL is currently a noop. | |
2518 EXPECT_FALSE(sock->signed_cert_timestamps_received_); | |
2519 #endif | |
2520 | |
2521 sock->Disconnect(); | |
2522 EXPECT_FALSE(sock->IsConnected()); | |
2523 } | |
2524 | 2546 |
2525 // Test that enabling Signed Certificate Timestamps enables OCSP stapling. | 2547 // Test that enabling Signed Certificate Timestamps enables OCSP stapling. |
2526 TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabledOCSP) { | 2548 TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabledOCSP) { |
2527 SpawnedTestServer::SSLOptions ssl_options; | 2549 SpawnedTestServer::SSLOptions ssl_options; |
2528 ssl_options.staple_ocsp_response = true; | 2550 ssl_options.staple_ocsp_response = true; |
2529 // The test server currently only knows how to generate OCSP responses | 2551 // The test server currently only knows how to generate OCSP responses |
2530 // for a freshly minted certificate. | 2552 // for a freshly minted certificate. |
2531 ssl_options.server_certificate = SpawnedTestServer::SSLOptions::CERT_AUTO; | 2553 ssl_options.server_certificate = SpawnedTestServer::SSLOptions::CERT_AUTO; |
2532 | 2554 |
2533 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, | 2555 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
2534 ssl_options, | 2556 ssl_options, |
2535 base::FilePath()); | 2557 base::FilePath()); |
2536 ASSERT_TRUE(test_server.Start()); | 2558 ASSERT_TRUE(test_server.Start()); |
2537 | 2559 |
2538 AddressList addr; | 2560 AddressList addr; |
2539 ASSERT_TRUE(test_server.GetAddressList(&addr)); | 2561 ASSERT_TRUE(test_server.GetAddressList(&addr)); |
2540 | 2562 |
2541 TestCompletionCallback callback; | 2563 TestCompletionCallback callback; |
2542 CapturingNetLog log; | |
2543 scoped_ptr<StreamSocket> transport( | 2564 scoped_ptr<StreamSocket> transport( |
2544 new TCPClientSocket(addr, &log, NetLog::Source())); | 2565 new TCPClientSocket(addr, &log_, NetLog::Source())); |
2545 int rv = transport->Connect(callback.callback()); | 2566 int rv = callback.GetResult(transport->Connect(callback.callback())); |
2546 if (rv == ERR_IO_PENDING) | |
2547 rv = callback.WaitForResult(); | |
2548 EXPECT_EQ(OK, rv); | 2567 EXPECT_EQ(OK, rv); |
2549 | 2568 |
2550 SSLConfig ssl_config; | 2569 SSLConfig ssl_config; |
2551 // Enabling Signed Cert Timestamps ensures we request OCSP stapling for | 2570 // Enabling Signed Cert Timestamps ensures we request OCSP stapling for |
2552 // Certificate Transparency verification regardless of whether the platform | 2571 // Certificate Transparency verification regardless of whether the platform |
2553 // is able to process the OCSP status itself. | 2572 // is able to process the OCSP status itself. |
2554 ssl_config.signed_cert_timestamps_enabled = true; | 2573 ssl_config.signed_cert_timestamps_enabled = true; |
2555 | 2574 |
| 2575 MockCTVerifier ct_verifier; |
| 2576 SetCTVerifier(&ct_verifier); |
| 2577 |
| 2578 // Check that the OCSP response is extracted and well-formed. It should be the |
| 2579 // DER encoding of an OCSPResponse (RFC 2560), so check that it consists of a |
| 2580 // SEQUENCE of an ENUMERATED type and an element tagged with [0] EXPLICIT. In |
| 2581 // particular, it should not include the overall two-byte length prefix from |
| 2582 // TLS. |
| 2583 EXPECT_CALL(ct_verifier, |
| 2584 Verify(_, Truly(IsValidOCSPResponse), "", _, _)).WillRepeatedly( |
| 2585 Return(ERR_CT_NO_SCTS_VERIFIED_OK)); |
| 2586 |
2556 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( | 2587 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
2557 transport.Pass(), test_server.host_port_pair(), ssl_config)); | 2588 transport.Pass(), test_server.host_port_pair(), ssl_config)); |
| 2589 rv = callback.GetResult(sock->Connect(callback.callback())); |
| 2590 EXPECT_EQ(OK, rv); |
2558 | 2591 |
2559 EXPECT_FALSE(sock->IsConnected()); | |
2560 | |
2561 rv = sock->Connect(callback.callback()); | |
2562 | |
2563 CapturingNetLog::CapturedEntryList entries; | |
2564 log.GetEntries(&entries); | |
2565 EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT)); | |
2566 if (rv == ERR_IO_PENDING) | |
2567 rv = callback.WaitForResult(); | |
2568 EXPECT_EQ(OK, rv); | |
2569 EXPECT_TRUE(sock->IsConnected()); | |
2570 log.GetEntries(&entries); | |
2571 EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); | |
2572 | |
2573 #if !defined(USE_OPENSSL) | |
2574 EXPECT_TRUE(sock->stapled_ocsp_response_received_); | 2592 EXPECT_TRUE(sock->stapled_ocsp_response_received_); |
2575 #else | |
2576 // OCSP stapling isn't currently supported in the OpenSSL socket. | |
2577 EXPECT_FALSE(sock->stapled_ocsp_response_received_); | |
2578 #endif | |
2579 | |
2580 sock->Disconnect(); | |
2581 EXPECT_FALSE(sock->IsConnected()); | |
2582 } | 2593 } |
2583 | 2594 |
2584 TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) { | 2595 TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) { |
2585 SpawnedTestServer::SSLOptions ssl_options; | 2596 SpawnedTestServer::SSLOptions ssl_options; |
2586 ssl_options.signed_cert_timestamps_tls_ext = "test"; | 2597 ssl_options.signed_cert_timestamps_tls_ext = "test"; |
2587 | 2598 |
2588 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, | 2599 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
2589 ssl_options, | 2600 ssl_options, |
2590 base::FilePath()); | 2601 base::FilePath()); |
2591 ASSERT_TRUE(test_server.Start()); | 2602 ASSERT_TRUE(test_server.Start()); |
2592 | 2603 |
2593 AddressList addr; | 2604 AddressList addr; |
2594 ASSERT_TRUE(test_server.GetAddressList(&addr)); | 2605 ASSERT_TRUE(test_server.GetAddressList(&addr)); |
2595 | 2606 |
2596 TestCompletionCallback callback; | 2607 TestCompletionCallback callback; |
2597 CapturingNetLog log; | |
2598 scoped_ptr<StreamSocket> transport( | 2608 scoped_ptr<StreamSocket> transport( |
2599 new TCPClientSocket(addr, &log, NetLog::Source())); | 2609 new TCPClientSocket(addr, &log_, NetLog::Source())); |
2600 int rv = transport->Connect(callback.callback()); | 2610 int rv = callback.GetResult(transport->Connect(callback.callback())); |
2601 if (rv == ERR_IO_PENDING) | |
2602 rv = callback.WaitForResult(); | |
2603 EXPECT_EQ(OK, rv); | 2611 EXPECT_EQ(OK, rv); |
2604 | 2612 |
2605 SSLConfig ssl_config; | 2613 SSLConfig ssl_config; |
2606 ssl_config.signed_cert_timestamps_enabled = false; | 2614 ssl_config.signed_cert_timestamps_enabled = false; |
2607 | 2615 |
2608 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( | 2616 scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( |
2609 transport.Pass(), test_server.host_port_pair(), ssl_config)); | 2617 transport.Pass(), test_server.host_port_pair(), ssl_config)); |
2610 | 2618 rv = callback.GetResult(sock->Connect(callback.callback())); |
2611 EXPECT_FALSE(sock->IsConnected()); | |
2612 | |
2613 rv = sock->Connect(callback.callback()); | |
2614 | |
2615 CapturingNetLog::CapturedEntryList entries; | |
2616 log.GetEntries(&entries); | |
2617 EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT)); | |
2618 if (rv == ERR_IO_PENDING) | |
2619 rv = callback.WaitForResult(); | |
2620 EXPECT_EQ(OK, rv); | 2619 EXPECT_EQ(OK, rv); |
2621 EXPECT_TRUE(sock->IsConnected()); | |
2622 log.GetEntries(&entries); | |
2623 EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); | |
2624 | 2620 |
2625 EXPECT_FALSE(sock->signed_cert_timestamps_received_); | 2621 EXPECT_FALSE(sock->signed_cert_timestamps_received_); |
2626 | |
2627 sock->Disconnect(); | |
2628 EXPECT_FALSE(sock->IsConnected()); | |
2629 } | 2622 } |
2630 | 2623 |
2631 // Tests that IsConnectedAndIdle and WasEverUsed behave as expected. | 2624 // Tests that IsConnectedAndIdle and WasEverUsed behave as expected. |
2632 TEST_F(SSLClientSocketTest, ReuseStates) { | 2625 TEST_F(SSLClientSocketTest, ReuseStates) { |
2633 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, | 2626 SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, |
2634 SpawnedTestServer::kLocalhost, | 2627 SpawnedTestServer::kLocalhost, |
2635 base::FilePath()); | 2628 base::FilePath()); |
2636 ASSERT_TRUE(test_server.Start()); | 2629 ASSERT_TRUE(test_server.Start()); |
2637 | 2630 |
2638 AddressList addr; | 2631 AddressList addr; |
(...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2990 ssl_config.channel_id_enabled = true; | 2983 ssl_config.channel_id_enabled = true; |
2991 | 2984 |
2992 int rv; | 2985 int rv; |
2993 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv)); | 2986 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv)); |
2994 | 2987 |
2995 EXPECT_EQ(ERR_UNEXPECTED, rv); | 2988 EXPECT_EQ(ERR_UNEXPECTED, rv); |
2996 EXPECT_FALSE(sock_->IsConnected()); | 2989 EXPECT_FALSE(sock_->IsConnected()); |
2997 } | 2990 } |
2998 | 2991 |
2999 } // namespace net | 2992 } // namespace net |
OLD | NEW |