| 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/dns/dns_transaction.h" | 5 #include "net/dns/dns_transaction.h" |
| 6 | 6 |
| 7 #include <deque> | 7 #include <deque> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/big_endian.h" | 11 #include "base/big_endian.h" |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/macros.h" |
| 14 #include "base/memory/ref_counted.h" | 15 #include "base/memory/ref_counted.h" |
| 15 #include "base/memory/scoped_ptr.h" | 16 #include "base/memory/scoped_ptr.h" |
| 16 #include "base/memory/weak_ptr.h" | 17 #include "base/memory/weak_ptr.h" |
| 17 #include "base/metrics/histogram.h" | 18 #include "base/metrics/histogram.h" |
| 18 #include "base/profiler/scoped_tracker.h" | 19 #include "base/profiler/scoped_tracker.h" |
| 19 #include "base/rand_util.h" | 20 #include "base/rand_util.h" |
| 20 #include "base/single_thread_task_runner.h" | 21 #include "base/single_thread_task_runner.h" |
| 21 #include "base/stl_util.h" | 22 #include "base/stl_util.h" |
| 22 #include "base/strings/string_piece.h" | 23 #include "base/strings/string_piece.h" |
| 23 #include "base/thread_task_runner_handle.h" | 24 #include "base/thread_task_runner_handle.h" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 54 return count; | 55 return count; |
| 55 } | 56 } |
| 56 | 57 |
| 57 bool IsIPLiteral(const std::string& hostname) { | 58 bool IsIPLiteral(const std::string& hostname) { |
| 58 IPAddressNumber ip; | 59 IPAddressNumber ip; |
| 59 return ParseIPLiteralToNumber(hostname, &ip); | 60 return ParseIPLiteralToNumber(hostname, &ip); |
| 60 } | 61 } |
| 61 | 62 |
| 62 scoped_ptr<base::Value> NetLogStartCallback( | 63 scoped_ptr<base::Value> NetLogStartCallback( |
| 63 const std::string* hostname, | 64 const std::string* hostname, |
| 64 uint16 qtype, | 65 uint16_t qtype, |
| 65 NetLogCaptureMode /* capture_mode */) { | 66 NetLogCaptureMode /* capture_mode */) { |
| 66 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 67 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 67 dict->SetString("hostname", *hostname); | 68 dict->SetString("hostname", *hostname); |
| 68 dict->SetInteger("query_type", qtype); | 69 dict->SetInteger("query_type", qtype); |
| 69 return dict.Pass(); | 70 return dict.Pass(); |
| 70 }; | 71 }; |
| 71 | 72 |
| 72 // ---------------------------------------------------------------------------- | 73 // ---------------------------------------------------------------------------- |
| 73 | 74 |
| 74 // A single asynchronous DNS exchange, which consists of sending out a | 75 // A single asynchronous DNS exchange, which consists of sending out a |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 | 298 |
| 298 class DnsTCPAttempt : public DnsAttempt { | 299 class DnsTCPAttempt : public DnsAttempt { |
| 299 public: | 300 public: |
| 300 DnsTCPAttempt(unsigned server_index, | 301 DnsTCPAttempt(unsigned server_index, |
| 301 scoped_ptr<StreamSocket> socket, | 302 scoped_ptr<StreamSocket> socket, |
| 302 scoped_ptr<DnsQuery> query) | 303 scoped_ptr<DnsQuery> query) |
| 303 : DnsAttempt(server_index), | 304 : DnsAttempt(server_index), |
| 304 next_state_(STATE_NONE), | 305 next_state_(STATE_NONE), |
| 305 socket_(socket.Pass()), | 306 socket_(socket.Pass()), |
| 306 query_(query.Pass()), | 307 query_(query.Pass()), |
| 307 length_buffer_(new IOBufferWithSize(sizeof(uint16))), | 308 length_buffer_(new IOBufferWithSize(sizeof(uint16_t))), |
| 308 response_length_(0) {} | 309 response_length_(0) {} |
| 309 | 310 |
| 310 // DnsAttempt: | 311 // DnsAttempt: |
| 311 int Start(const CompletionCallback& callback) override { | 312 int Start(const CompletionCallback& callback) override { |
| 312 DCHECK_EQ(STATE_NONE, next_state_); | 313 DCHECK_EQ(STATE_NONE, next_state_); |
| 313 callback_ = callback; | 314 callback_ = callback; |
| 314 start_time_ = base::TimeTicks::Now(); | 315 start_time_ = base::TimeTicks::Now(); |
| 315 next_state_ = STATE_CONNECT_COMPLETE; | 316 next_state_ = STATE_CONNECT_COMPLETE; |
| 316 int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete, | 317 int rv = socket_->Connect(base::Bind(&DnsTCPAttempt::OnIOComplete, |
| 317 base::Unretained(this))); | 318 base::Unretained(this))); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 int DoConnectComplete(int rv) { | 395 int DoConnectComplete(int rv) { |
| 395 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed. | 396 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed. |
| 396 tracked_objects::ScopedTracker tracking_profile( | 397 tracked_objects::ScopedTracker tracking_profile( |
| 397 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 398 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 398 "462784 DnsTCPAttempt::DoConnectComplete")); | 399 "462784 DnsTCPAttempt::DoConnectComplete")); |
| 399 | 400 |
| 400 DCHECK_NE(ERR_IO_PENDING, rv); | 401 DCHECK_NE(ERR_IO_PENDING, rv); |
| 401 if (rv < 0) | 402 if (rv < 0) |
| 402 return rv; | 403 return rv; |
| 403 | 404 |
| 404 uint16 query_size = static_cast<uint16>(query_->io_buffer()->size()); | 405 uint16_t query_size = static_cast<uint16_t>(query_->io_buffer()->size()); |
| 405 if (static_cast<int>(query_size) != query_->io_buffer()->size()) | 406 if (static_cast<int>(query_size) != query_->io_buffer()->size()) |
| 406 return ERR_FAILED; | 407 return ERR_FAILED; |
| 407 base::WriteBigEndian<uint16>(length_buffer_->data(), query_size); | 408 base::WriteBigEndian<uint16_t>(length_buffer_->data(), query_size); |
| 408 buffer_ = | 409 buffer_ = |
| 409 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size()); | 410 new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size()); |
| 410 next_state_ = STATE_SEND_LENGTH; | 411 next_state_ = STATE_SEND_LENGTH; |
| 411 return OK; | 412 return OK; |
| 412 } | 413 } |
| 413 | 414 |
| 414 int DoSendLength(int rv) { | 415 int DoSendLength(int rv) { |
| 415 DCHECK_NE(ERR_IO_PENDING, rv); | 416 DCHECK_NE(ERR_IO_PENDING, rv); |
| 416 if (rv < 0) | 417 if (rv < 0) |
| 417 return rv; | 418 return rv; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 462 return rv; | 463 return rv; |
| 463 if (rv == 0) | 464 if (rv == 0) |
| 464 return ERR_CONNECTION_CLOSED; | 465 return ERR_CONNECTION_CLOSED; |
| 465 | 466 |
| 466 buffer_->DidConsume(rv); | 467 buffer_->DidConsume(rv); |
| 467 if (buffer_->BytesRemaining() > 0) { | 468 if (buffer_->BytesRemaining() > 0) { |
| 468 next_state_ = STATE_READ_LENGTH; | 469 next_state_ = STATE_READ_LENGTH; |
| 469 return OK; | 470 return OK; |
| 470 } | 471 } |
| 471 | 472 |
| 472 base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_); | 473 base::ReadBigEndian<uint16_t>(length_buffer_->data(), &response_length_); |
| 473 // Check if advertised response is too short. (Optimization only.) | 474 // Check if advertised response is too short. (Optimization only.) |
| 474 if (response_length_ < query_->io_buffer()->size()) | 475 if (response_length_ < query_->io_buffer()->size()) |
| 475 return ERR_DNS_MALFORMED_RESPONSE; | 476 return ERR_DNS_MALFORMED_RESPONSE; |
| 476 // Allocate more space so that DnsResponse::InitParse sanity check passes. | 477 // Allocate more space so that DnsResponse::InitParse sanity check passes. |
| 477 response_.reset(new DnsResponse(response_length_ + 1)); | 478 response_.reset(new DnsResponse(response_length_ + 1)); |
| 478 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_); | 479 buffer_ = new DrainableIOBuffer(response_->io_buffer(), response_length_); |
| 479 next_state_ = STATE_READ_RESPONSE; | 480 next_state_ = STATE_READ_RESPONSE; |
| 480 return OK; | 481 return OK; |
| 481 } | 482 } |
| 482 | 483 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 } | 528 } |
| 528 | 529 |
| 529 State next_state_; | 530 State next_state_; |
| 530 base::TimeTicks start_time_; | 531 base::TimeTicks start_time_; |
| 531 | 532 |
| 532 scoped_ptr<StreamSocket> socket_; | 533 scoped_ptr<StreamSocket> socket_; |
| 533 scoped_ptr<DnsQuery> query_; | 534 scoped_ptr<DnsQuery> query_; |
| 534 scoped_refptr<IOBufferWithSize> length_buffer_; | 535 scoped_refptr<IOBufferWithSize> length_buffer_; |
| 535 scoped_refptr<DrainableIOBuffer> buffer_; | 536 scoped_refptr<DrainableIOBuffer> buffer_; |
| 536 | 537 |
| 537 uint16 response_length_; | 538 uint16_t response_length_; |
| 538 scoped_ptr<DnsResponse> response_; | 539 scoped_ptr<DnsResponse> response_; |
| 539 | 540 |
| 540 CompletionCallback callback_; | 541 CompletionCallback callback_; |
| 541 | 542 |
| 542 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt); | 543 DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt); |
| 543 }; | 544 }; |
| 544 | 545 |
| 545 // ---------------------------------------------------------------------------- | 546 // ---------------------------------------------------------------------------- |
| 546 | 547 |
| 547 // Implements DnsTransaction. Configuration is supplied by DnsSession. | 548 // Implements DnsTransaction. Configuration is supplied by DnsSession. |
| 548 // The suffix list is built according to the DnsConfig from the session. | 549 // The suffix list is built according to the DnsConfig from the session. |
| 549 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. | 550 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. |
| 550 // The first server to attempt on each query is given by | 551 // The first server to attempt on each query is given by |
| 551 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. | 552 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. |
| 552 // Each server is attempted DnsConfig::attempts times. | 553 // Each server is attempted DnsConfig::attempts times. |
| 553 class DnsTransactionImpl : public DnsTransaction, | 554 class DnsTransactionImpl : public DnsTransaction, |
| 554 public base::NonThreadSafe, | 555 public base::NonThreadSafe, |
| 555 public base::SupportsWeakPtr<DnsTransactionImpl> { | 556 public base::SupportsWeakPtr<DnsTransactionImpl> { |
| 556 public: | 557 public: |
| 557 DnsTransactionImpl(DnsSession* session, | 558 DnsTransactionImpl(DnsSession* session, |
| 558 const std::string& hostname, | 559 const std::string& hostname, |
| 559 uint16 qtype, | 560 uint16_t qtype, |
| 560 const DnsTransactionFactory::CallbackType& callback, | 561 const DnsTransactionFactory::CallbackType& callback, |
| 561 const BoundNetLog& net_log) | 562 const BoundNetLog& net_log) |
| 562 : session_(session), | 563 : session_(session), |
| 563 hostname_(hostname), | 564 hostname_(hostname), |
| 564 qtype_(qtype), | 565 qtype_(qtype), |
| 565 callback_(callback), | 566 callback_(callback), |
| 566 net_log_(net_log), | 567 net_log_(net_log), |
| 567 qnames_initial_size_(0), | 568 qnames_initial_size_(0), |
| 568 attempts_count_(0), | 569 attempts_count_(0), |
| 569 had_tcp_attempt_(false), | 570 had_tcp_attempt_(false), |
| 570 first_server_index_(0) { | 571 first_server_index_(0) { |
| 571 DCHECK(session_.get()); | 572 DCHECK(session_.get()); |
| 572 DCHECK(!hostname_.empty()); | 573 DCHECK(!hostname_.empty()); |
| 573 DCHECK(!callback_.is_null()); | 574 DCHECK(!callback_.is_null()); |
| 574 DCHECK(!IsIPLiteral(hostname_)); | 575 DCHECK(!IsIPLiteral(hostname_)); |
| 575 } | 576 } |
| 576 | 577 |
| 577 ~DnsTransactionImpl() override { | 578 ~DnsTransactionImpl() override { |
| 578 if (!callback_.is_null()) { | 579 if (!callback_.is_null()) { |
| 579 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, | 580 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, |
| 580 ERR_ABORTED); | 581 ERR_ABORTED); |
| 581 } // otherwise logged in DoCallback or Start | 582 } // otherwise logged in DoCallback or Start |
| 582 } | 583 } |
| 583 | 584 |
| 584 const std::string& GetHostname() const override { | 585 const std::string& GetHostname() const override { |
| 585 DCHECK(CalledOnValidThread()); | 586 DCHECK(CalledOnValidThread()); |
| 586 return hostname_; | 587 return hostname_; |
| 587 } | 588 } |
| 588 | 589 |
| 589 uint16 GetType() const override { | 590 uint16_t GetType() const override { |
| 590 DCHECK(CalledOnValidThread()); | 591 DCHECK(CalledOnValidThread()); |
| 591 return qtype_; | 592 return qtype_; |
| 592 } | 593 } |
| 593 | 594 |
| 594 void Start() override { | 595 void Start() override { |
| 595 DCHECK(!callback_.is_null()); | 596 DCHECK(!callback_.is_null()); |
| 596 DCHECK(attempts_.empty()); | 597 DCHECK(attempts_.empty()); |
| 597 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, | 598 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, |
| 598 base::Bind(&NetLogStartCallback, &hostname_, qtype_)); | 599 base::Bind(&NetLogStartCallback, &hostname_, qtype_)); |
| 599 AttemptResult result(PrepareSearch(), NULL); | 600 AttemptResult result(PrepareSearch(), NULL); |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 695 | 696 |
| 696 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv); | 697 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, result.rv); |
| 697 callback.Run(this, result.rv, response); | 698 callback.Run(this, result.rv, response); |
| 698 } | 699 } |
| 699 | 700 |
| 700 // Makes another attempt at the current name, |qnames_.front()|, using the | 701 // Makes another attempt at the current name, |qnames_.front()|, using the |
| 701 // next nameserver. | 702 // next nameserver. |
| 702 AttemptResult MakeAttempt() { | 703 AttemptResult MakeAttempt() { |
| 703 unsigned attempt_number = attempts_.size(); | 704 unsigned attempt_number = attempts_.size(); |
| 704 | 705 |
| 705 uint16 id = session_->NextQueryId(); | 706 uint16_t id = session_->NextQueryId(); |
| 706 scoped_ptr<DnsQuery> query; | 707 scoped_ptr<DnsQuery> query; |
| 707 if (attempts_.empty()) { | 708 if (attempts_.empty()) { |
| 708 query.reset(new DnsQuery(id, qnames_.front(), qtype_)); | 709 query.reset(new DnsQuery(id, qnames_.front(), qtype_)); |
| 709 } else { | 710 } else { |
| 710 query = attempts_[0]->GetQuery()->CloneWithNewId(id); | 711 query = attempts_[0]->GetQuery()->CloneWithNewId(id); |
| 711 } | 712 } |
| 712 | 713 |
| 713 const DnsConfig& config = session_->config(); | 714 const DnsConfig& config = session_->config(); |
| 714 | 715 |
| 715 unsigned server_index = | 716 unsigned server_index = |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) { | 751 AttemptResult MakeTCPAttempt(const DnsAttempt* previous_attempt) { |
| 751 DCHECK(previous_attempt); | 752 DCHECK(previous_attempt); |
| 752 DCHECK(!had_tcp_attempt_); | 753 DCHECK(!had_tcp_attempt_); |
| 753 | 754 |
| 754 unsigned server_index = previous_attempt->server_index(); | 755 unsigned server_index = previous_attempt->server_index(); |
| 755 | 756 |
| 756 scoped_ptr<StreamSocket> socket( | 757 scoped_ptr<StreamSocket> socket( |
| 757 session_->CreateTCPSocket(server_index, net_log_.source())); | 758 session_->CreateTCPSocket(server_index, net_log_.source())); |
| 758 | 759 |
| 759 // TODO(szym): Reuse the same id to help the server? | 760 // TODO(szym): Reuse the same id to help the server? |
| 760 uint16 id = session_->NextQueryId(); | 761 uint16_t id = session_->NextQueryId(); |
| 761 scoped_ptr<DnsQuery> query = | 762 scoped_ptr<DnsQuery> query = |
| 762 previous_attempt->GetQuery()->CloneWithNewId(id); | 763 previous_attempt->GetQuery()->CloneWithNewId(id); |
| 763 | 764 |
| 764 RecordLostPacketsIfAny(); | 765 RecordLostPacketsIfAny(); |
| 765 // Cancel all other attempts, no point waiting on them. | 766 // Cancel all other attempts, no point waiting on them. |
| 766 attempts_.clear(); | 767 attempts_.clear(); |
| 767 | 768 |
| 768 unsigned attempt_number = attempts_.size(); | 769 unsigned attempt_number = attempts_.size(); |
| 769 | 770 |
| 770 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(), | 771 DnsTCPAttempt* attempt = new DnsTCPAttempt(server_index, socket.Pass(), |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 934 return; | 935 return; |
| 935 DCHECK(!attempts_.empty()); | 936 DCHECK(!attempts_.empty()); |
| 936 AttemptResult result = ProcessAttemptResult( | 937 AttemptResult result = ProcessAttemptResult( |
| 937 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back().get())); | 938 AttemptResult(ERR_DNS_TIMED_OUT, attempts_.back().get())); |
| 938 if (result.rv != ERR_IO_PENDING) | 939 if (result.rv != ERR_IO_PENDING) |
| 939 DoCallback(result); | 940 DoCallback(result); |
| 940 } | 941 } |
| 941 | 942 |
| 942 scoped_refptr<DnsSession> session_; | 943 scoped_refptr<DnsSession> session_; |
| 943 std::string hostname_; | 944 std::string hostname_; |
| 944 uint16 qtype_; | 945 uint16_t qtype_; |
| 945 // Cleared in DoCallback. | 946 // Cleared in DoCallback. |
| 946 DnsTransactionFactory::CallbackType callback_; | 947 DnsTransactionFactory::CallbackType callback_; |
| 947 | 948 |
| 948 BoundNetLog net_log_; | 949 BoundNetLog net_log_; |
| 949 | 950 |
| 950 // Search list of fully-qualified DNS names to query next (in DNS format). | 951 // Search list of fully-qualified DNS names to query next (in DNS format). |
| 951 std::deque<std::string> qnames_; | 952 std::deque<std::string> qnames_; |
| 952 size_t qnames_initial_size_; | 953 size_t qnames_initial_size_; |
| 953 | 954 |
| 954 // List of attempts for the current name. | 955 // List of attempts for the current name. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 970 // Implementation of DnsTransactionFactory that returns instances of | 971 // Implementation of DnsTransactionFactory that returns instances of |
| 971 // DnsTransactionImpl. | 972 // DnsTransactionImpl. |
| 972 class DnsTransactionFactoryImpl : public DnsTransactionFactory { | 973 class DnsTransactionFactoryImpl : public DnsTransactionFactory { |
| 973 public: | 974 public: |
| 974 explicit DnsTransactionFactoryImpl(DnsSession* session) { | 975 explicit DnsTransactionFactoryImpl(DnsSession* session) { |
| 975 session_ = session; | 976 session_ = session; |
| 976 } | 977 } |
| 977 | 978 |
| 978 scoped_ptr<DnsTransaction> CreateTransaction( | 979 scoped_ptr<DnsTransaction> CreateTransaction( |
| 979 const std::string& hostname, | 980 const std::string& hostname, |
| 980 uint16 qtype, | 981 uint16_t qtype, |
| 981 const CallbackType& callback, | 982 const CallbackType& callback, |
| 982 const BoundNetLog& net_log) override { | 983 const BoundNetLog& net_log) override { |
| 983 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl( | 984 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl( |
| 984 session_.get(), hostname, qtype, callback, net_log)); | 985 session_.get(), hostname, qtype, callback, net_log)); |
| 985 } | 986 } |
| 986 | 987 |
| 987 private: | 988 private: |
| 988 scoped_refptr<DnsSession> session_; | 989 scoped_refptr<DnsSession> session_; |
| 989 }; | 990 }; |
| 990 | 991 |
| 991 } // namespace | 992 } // namespace |
| 992 | 993 |
| 993 // static | 994 // static |
| 994 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( | 995 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( |
| 995 DnsSession* session) { | 996 DnsSession* session) { |
| 996 return scoped_ptr<DnsTransactionFactory>( | 997 return scoped_ptr<DnsTransactionFactory>( |
| 997 new DnsTransactionFactoryImpl(session)); | 998 new DnsTransactionFactoryImpl(session)); |
| 998 } | 999 } |
| 999 | 1000 |
| 1000 } // namespace net | 1001 } // namespace net |
| OLD | NEW |