Chromium Code Reviews| 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 "chrome/browser/extensions/api/socket/socket_api.h" | 5 #include "chrome/browser/extensions/api/socket/socket_api.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/containers/hash_tables.h" | 10 #include "base/containers/hash_tables.h" |
| 11 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
| 12 #include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" | 12 #include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" |
| 13 #include "chrome/browser/extensions/api/socket/socket.h" | 13 #include "chrome/browser/extensions/api/socket/socket.h" |
| 14 #include "chrome/browser/extensions/api/socket/tcp_socket.h" | 14 #include "chrome/browser/extensions/api/socket/tcp_socket.h" |
| 15 #include "chrome/browser/extensions/api/socket/tls_socket.h" | |
| 15 #include "chrome/browser/extensions/api/socket/udp_socket.h" | 16 #include "chrome/browser/extensions/api/socket/udp_socket.h" |
| 16 #include "chrome/browser/extensions/extension_system.h" | 17 #include "chrome/browser/extensions/extension_system.h" |
| 17 #include "chrome/browser/io_thread.h" | 18 #include "chrome/browser/io_thread.h" |
| 18 #include "chrome/common/extensions/permissions/socket_permission.h" | 19 #include "chrome/common/extensions/permissions/socket_permission.h" |
| 19 #include "extensions/common/extension.h" | 20 #include "extensions/common/extension.h" |
| 20 #include "extensions/common/permissions/permissions_data.h" | 21 #include "extensions/common/permissions/permissions_data.h" |
| 21 #include "net/base/host_port_pair.h" | 22 #include "net/base/host_port_pair.h" |
| 22 #include "net/base/io_buffer.h" | 23 #include "net/base/io_buffer.h" |
| 23 #include "net/base/ip_endpoint.h" | 24 #include "net/base/ip_endpoint.h" |
| 24 #include "net/base/net_errors.h" | 25 #include "net/base/net_errors.h" |
| 25 #include "net/base/net_log.h" | 26 #include "net/base/net_log.h" |
| 26 #include "net/base/net_util.h" | 27 #include "net/base/net_util.h" |
| 28 #include "net/socket/client_socket_handle.h" | |
| 29 #include "net/socket/client_socket_factory.h" | |
| 30 #include "net/url_request/url_request_context.h" | |
| 31 #include "net/url_request/url_request_context_getter.h" | |
| 27 | 32 |
| 28 namespace extensions { | 33 namespace extensions { |
| 29 | 34 |
| 30 using content::SocketPermissionRequest; | 35 using content::SocketPermissionRequest; |
| 31 | 36 |
| 32 const char kAddressKey[] = "address"; | 37 const char kAddressKey[] = "address"; |
| 33 const char kPortKey[] = "port"; | 38 const char kPortKey[] = "port"; |
| 34 const char kBytesWrittenKey[] = "bytesWritten"; | 39 const char kBytesWrittenKey[] = "bytesWritten"; |
| 35 const char kDataKey[] = "data"; | 40 const char kDataKey[] = "data"; |
| 36 const char kResultCodeKey[] = "resultCode"; | 41 const char kResultCodeKey[] = "resultCode"; |
| 37 const char kSocketIdKey[] = "socketId"; | 42 const char kSocketIdKey[] = "socketId"; |
| 38 | 43 |
| 39 const char kSocketNotFoundError[] = "Socket not found"; | 44 const char kSocketNotFoundError[] = "Socket not found"; |
| 40 const char kDnsLookupFailedError[] = "DNS resolution failed"; | 45 const char kDnsLookupFailedError[] = "DNS resolution failed"; |
| 41 const char kPermissionError[] = "App does not have permission"; | 46 const char kPermissionError[] = "App does not have permission"; |
| 42 const char kNetworkListError[] = "Network lookup failed or unsupported"; | 47 const char kNetworkListError[] = "Network lookup failed or unsupported"; |
| 43 const char kTCPSocketBindError[] = | 48 const char kTCPSocketBindError[] = |
| 44 "TCP socket does not support bind. For TCP server please use listen."; | 49 "TCP socket does not support bind. For TCP server please use listen."; |
| 45 const char kMulticastSocketTypeError[] = | 50 const char kMulticastSocketTypeError[] = |
| 46 "Only UDP socket supports multicast."; | 51 "Only UDP socket supports multicast."; |
| 52 const char kSecureSocketTypeError[] = | |
| 53 "Only TCP sockets are supported for TLS."; | |
| 54 const char kSocketNotConnectedError[] = "Socket not connected"; | |
| 47 const char kWildcardAddress[] = "*"; | 55 const char kWildcardAddress[] = "*"; |
| 48 const int kWildcardPort = 0; | 56 const int kWildcardPort = 0; |
| 49 | 57 |
| 58 namespace { | |
| 59 // Returns the SSL protocol version (as a uint16) represented by a string. | |
| 60 // Returns 0 if the string is invalid. Pulled from | |
| 61 // chrome/browser/net/ssl_config_service_manager_pref.cc. | |
| 62 uint16 SSLProtocolVersionFromString(const std::string& version_str) { | |
| 63 uint16 version = 0; // Invalid. | |
| 64 if (version_str == "ssl3") { | |
| 65 version = net::SSL_PROTOCOL_VERSION_SSL3; | |
| 66 } else if (version_str == "tls1") { | |
| 67 version = net::SSL_PROTOCOL_VERSION_TLS1; | |
| 68 } else if (version_str == "tls1.1") { | |
| 69 version = net::SSL_PROTOCOL_VERSION_TLS1_1; | |
| 70 } else if (version_str == "tls1.2") { | |
| 71 version = net::SSL_PROTOCOL_VERSION_TLS1_2; | |
| 72 } | |
| 73 return version; | |
| 74 } | |
| 75 } // namespace | |
| 76 | |
| 77 | |
| 50 SocketAsyncApiFunction::SocketAsyncApiFunction() { | 78 SocketAsyncApiFunction::SocketAsyncApiFunction() { |
| 51 } | 79 } |
| 52 | 80 |
| 53 SocketAsyncApiFunction::~SocketAsyncApiFunction() { | 81 SocketAsyncApiFunction::~SocketAsyncApiFunction() { |
| 54 } | 82 } |
| 55 | 83 |
| 56 bool SocketAsyncApiFunction::PrePrepare() { | 84 bool SocketAsyncApiFunction::PrePrepare() { |
| 57 manager_ = CreateSocketResourceManager(); | 85 manager_ = CreateSocketResourceManager(); |
| 58 return manager_->SetProfile(GetProfile()); | 86 return manager_->SetProfile(GetProfile()); |
| 59 } | 87 } |
| 60 | 88 |
| 61 bool SocketAsyncApiFunction::Respond() { | 89 bool SocketAsyncApiFunction::Respond() { |
| 62 return error_.empty(); | 90 return error_.empty(); |
| 63 } | 91 } |
| 64 | 92 |
| 65 scoped_ptr<SocketResourceManagerInterface> | 93 scoped_ptr<SocketResourceManagerInterface> |
| 66 SocketAsyncApiFunction::CreateSocketResourceManager() { | 94 SocketAsyncApiFunction::CreateSocketResourceManager() { |
| 67 return scoped_ptr<SocketResourceManagerInterface>( | 95 return scoped_ptr<SocketResourceManagerInterface>( |
| 68 new SocketResourceManager<Socket>()).Pass(); | 96 new SocketResourceManager<Socket>()).Pass(); |
| 69 } | 97 } |
| 70 | 98 |
| 71 int SocketAsyncApiFunction::AddSocket(Socket* socket) { | 99 int SocketAsyncApiFunction::AddSocket(Socket* socket) { |
| 72 return manager_->Add(socket); | 100 return manager_->Add(socket); |
| 73 } | 101 } |
| 74 | 102 |
| 75 Socket* SocketAsyncApiFunction::GetSocket(int api_resource_id) { | 103 Socket* SocketAsyncApiFunction::GetSocket(int api_resource_id) { |
| 76 return manager_->Get(extension_->id(), api_resource_id); | 104 return manager_->Get(extension_->id(), api_resource_id); |
| 77 } | 105 } |
| 78 | 106 |
| 107 void SocketAsyncApiFunction::SetSocket(int api_resource_id, | |
| 108 Socket* socket) { | |
| 109 manager_->Set(extension_->id(), api_resource_id, socket); | |
| 110 } | |
| 111 | |
| 79 base::hash_set<int>* SocketAsyncApiFunction::GetSocketIds() { | 112 base::hash_set<int>* SocketAsyncApiFunction::GetSocketIds() { |
| 80 return manager_->GetResourceIds(extension_->id()); | 113 return manager_->GetResourceIds(extension_->id()); |
| 81 } | 114 } |
| 82 | 115 |
| 83 void SocketAsyncApiFunction::RemoveSocket(int api_resource_id) { | 116 void SocketAsyncApiFunction::RemoveSocket(int api_resource_id) { |
| 84 manager_->Remove(extension_->id(), api_resource_id); | 117 manager_->Remove(extension_->id(), api_resource_id); |
| 85 } | 118 } |
| 86 | 119 |
| 87 SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() | 120 SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() |
| 88 : io_thread_(g_browser_process->io_thread()), | 121 : io_thread_(g_browser_process->io_thread()), |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 232 void SocketConnectFunction::AfterDnsLookup(int lookup_result) { | 265 void SocketConnectFunction::AfterDnsLookup(int lookup_result) { |
| 233 if (lookup_result == net::OK) { | 266 if (lookup_result == net::OK) { |
| 234 StartConnect(); | 267 StartConnect(); |
| 235 } else { | 268 } else { |
| 236 SetResult(new base::FundamentalValue(lookup_result)); | 269 SetResult(new base::FundamentalValue(lookup_result)); |
| 237 AsyncWorkCompleted(); | 270 AsyncWorkCompleted(); |
| 238 } | 271 } |
| 239 } | 272 } |
| 240 | 273 |
| 241 void SocketConnectFunction::StartConnect() { | 274 void SocketConnectFunction::StartConnect() { |
| 275 socket_->SetOriginalHostname(hostname_); | |
| 242 socket_->Connect(resolved_address_, port_, | 276 socket_->Connect(resolved_address_, port_, |
| 243 base::Bind(&SocketConnectFunction::OnConnect, this)); | 277 base::Bind(&SocketConnectFunction::OnConnect, this)); |
| 244 } | 278 } |
| 245 | 279 |
| 246 void SocketConnectFunction::OnConnect(int result) { | 280 void SocketConnectFunction::OnConnect(int result) { |
| 247 SetResult(new base::FundamentalValue(result)); | 281 SetResult(new base::FundamentalValue(result)); |
| 248 AsyncWorkCompleted(); | 282 AsyncWorkCompleted(); |
| 249 } | 283 } |
| 250 | 284 |
| 251 bool SocketDisconnectFunction::Prepare() { | 285 bool SocketDisconnectFunction::Prepare() { |
| (...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 889 SetResult(new base::FundamentalValue(result)); | 923 SetResult(new base::FundamentalValue(result)); |
| 890 return; | 924 return; |
| 891 } | 925 } |
| 892 | 926 |
| 893 base::ListValue* values = new base::ListValue(); | 927 base::ListValue* values = new base::ListValue(); |
| 894 values->AppendStrings((std::vector<std::string>&) | 928 values->AppendStrings((std::vector<std::string>&) |
| 895 static_cast<UDPSocket*>(socket)->GetJoinedGroups()); | 929 static_cast<UDPSocket*>(socket)->GetJoinedGroups()); |
| 896 SetResult(values); | 930 SetResult(values); |
| 897 } | 931 } |
| 898 | 932 |
| 933 SocketSecureFunction::SocketSecureFunction() {} | |
| 934 SocketSecureFunction::~SocketSecureFunction() {} | |
| 935 | |
| 936 bool SocketSecureFunction::Prepare() { | |
| 937 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 938 VLOG(1) << "chrome.socket.secure: Prepare()"; | |
| 939 params_ = api::socket::Secure::Params::Create(*args_); | |
| 940 EXTENSION_FUNCTION_VALIDATE(params_.get()); | |
| 941 url_request_getter_ = GetProfile()->GetRequestContext(); | |
| 942 underlying_socket_ = NULL; | |
| 943 return true; | |
| 944 } | |
| 945 | |
| 946 void SocketSecureFunction::Work() { | |
| 947 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 948 VLOG(1) << "chrome.socket.secure: Work()"; | |
| 949 Socket* socket = GetSocket(params_->socket_id); | |
| 950 int result = -1; | |
| 951 if (!socket) { | |
| 952 error_ = kSocketNotFoundError; | |
| 953 SetResult(new base::FundamentalValue(result)); | |
| 954 VLOG(1) << "chrome.socket.secure: Work(): no socket, bolting."; | |
|
Ryan Sleevi
2013/11/25 17:30:13
These VLOGs are all included in release/official b
Lally Singh
2013/12/05 17:07:12
All but 1 removed. The remainder moved to DVLOG (
| |
| 955 return; | |
| 956 } | |
| 957 | |
| 958 if (socket->GetSocketType() != Socket::TYPE_TCP) { | |
| 959 error_ = kSecureSocketTypeError; | |
| 960 SetResult(new base::FundamentalValue(result)); | |
| 961 VLOG(1) << "chrome.socket.secure: Work(): not a TCP socket, bolting."; | |
| 962 return; | |
| 963 } | |
| 964 | |
| 965 if (!socket->IsConnected()) { | |
| 966 error_ = kSocketNotConnectedError; | |
| 967 SetResult(new base::FundamentalValue(result)); | |
| 968 VLOG(1) << "chrome.socket.secure: Work(): unconnected socket, bolting."; | |
| 969 return; | |
| 970 } | |
| 971 | |
| 972 VLOG(1) << "SocketSecureFunction::Work: Starting up"; | |
| 973 | |
| 974 net::IPEndPoint dest_host_port_pair; | |
| 975 socket->GetPeerAddress(&dest_host_port_pair); | |
| 976 net::HostPortPair host_port( | |
| 977 socket->OriginalHostname(), | |
| 978 dest_host_port_pair.port()); | |
| 979 | |
| 980 // Modeled after content/browser/renderer_host/p2p/socket_host_tcp.cc:154 | |
| 981 // P2PSocketHostTcpBase::StartTls() | |
|
Ryan Sleevi
2013/11/25 17:30:13
Don't do comments like this. It will be out of dat
Lally Singh
2013/12/05 17:07:12
Acknowledged. The file/line# were removed, but th
| |
| 982 scoped_ptr<net::ClientSocketHandle> socket_handle( | |
| 983 new net::ClientSocketHandle()); | |
| 984 // We already know from GetSocketType() above that it's TCP. | |
|
Ryan Sleevi
2013/11/25 17:30:13
nit: Please rework the comments here to avoid the
Lally Singh
2013/12/05 17:07:12
This comment was removed, but instances of "we" we
| |
| 985 TCPSocket *tcp_socket = static_cast<TCPSocket*>(socket); | |
| 986 DCHECK(tcp_socket->ClientStream()); | |
| 987 | |
| 988 // We keep a pointer (not a lifetime-preserving reference) to the original | |
| 989 // TCPClientSocket, to give to TLSSocket upon successful TLS negotiation. | |
| 990 // Its properly owned by ssl_socket_, which doesn't pass through certain | |
| 991 // APIs that we'd like it to (SetKeepAlive, SetNoDelay). | |
|
Ryan Sleevi
2013/11/25 17:30:13
This is a bug. You shouldn't be doing this. Set yo
Lally Singh
2013/12/05 17:07:12
I've disabled the KeepAlive/NoDelay methods. The
| |
| 992 underlying_socket_ = tcp_socket->ClientStream(); | |
| 993 socket_handle->SetSocket(scoped_ptr<net::StreamSocket>(underlying_socket_)); | |
| 994 | |
| 995 // Have the old socket release its holds on the client stream we just gave | |
| 996 // away. | |
| 997 tcp_socket->Release(); | |
| 998 | |
| 999 net::SSLClientSocketContext context; | |
| 1000 Profile* profile = GetProfile(); | |
| 1001 DCHECK(profile); | |
| 1002 net::URLRequestContext* url_context = | |
| 1003 url_request_getter_->GetURLRequestContext(); | |
| 1004 | |
| 1005 DCHECK(url_context); | |
| 1006 context.cert_verifier = url_context->cert_verifier(); | |
| 1007 context.transport_security_state = url_context->transport_security_state(); | |
| 1008 DCHECK(context.transport_security_state); | |
| 1009 | |
| 1010 // Fill in the SSL socket params. | |
| 1011 net::SSLConfig ssl_config; | |
| 1012 profile->GetSSLConfigService()->GetSSLConfig(&ssl_config); | |
| 1013 if (params_->options.get() && params_->options->tls_version.get()) { | |
| 1014 uint16 v_min = 0, v_max = 0; | |
|
Ryan Sleevi
2013/11/25 17:30:13
As per the Google C++ style guide, eschew abbrevia
Lally Singh
2013/12/05 17:07:12
Done.
| |
| 1015 api::socket::TLSVersionConstraints *versions = | |
|
Ryan Sleevi
2013/11/25 17:30:13
As per http://www.chromium.org/developers/coding-s
Lally Singh
2013/12/05 17:07:12
Done.
| |
| 1016 params_->options->tls_version.get(); | |
| 1017 if (versions->min.get()) { | |
| 1018 v_min = SSLProtocolVersionFromString(*versions->min.get()); | |
| 1019 } | |
| 1020 if (versions->max.get()) { | |
| 1021 v_max = SSLProtocolVersionFromString(*versions->max.get()); | |
| 1022 } | |
| 1023 if (v_min) { | |
| 1024 ssl_config.version_min = v_min; | |
| 1025 } | |
| 1026 if (v_max) { | |
| 1027 ssl_config.version_max = v_max; | |
| 1028 } | |
| 1029 } | |
| 1030 net::ClientSocketFactory* socket_factory = | |
| 1031 net::ClientSocketFactory::GetDefaultFactory(); | |
|
Ryan Sleevi
2013/11/25 17:30:13
The "ideal" form is to pass this in as a variable
| |
| 1032 | |
| 1033 VLOG(1) << "SocketSecureFunction::Work: Creating TLS socket"; | |
| 1034 | |
| 1035 // Create the socket. | |
| 1036 ssl_socket_ = socket_factory->CreateSSLClientSocket( | |
| 1037 socket_handle.Pass(), host_port, ssl_config, context); | |
|
Ryan Sleevi
2013/11/25 17:30:13
BUG: This is broken, because you never connected |
Lally Singh
2013/12/05 17:07:12
I set it to underlying_socket above. That socket
| |
| 1038 | |
| 1039 VLOG(1) << "SocketSecureFunction::Work: Trying to connect"; | |
| 1040 // And try to connect. | |
| 1041 int status = ssl_socket_->Connect( | |
| 1042 base::Bind(&SocketSecureFunction::TlsConnectDone, this)); | |
| 1043 if (status != net::ERR_IO_PENDING) { | |
| 1044 TlsConnectDone(status); | |
|
Ryan Sleevi
2013/11/25 17:30:13
From an API point, this sort of async-or-recursion
Lally Singh
2013/12/05 17:07:12
I've added a comment on why this can't recurse int
| |
| 1045 VLOG(1) << "SocketSecureFunction::Work: Done. "; | |
| 1046 } else { | |
| 1047 VLOG(1) << "SocketSecureFunction::Work: Returning, " | |
| 1048 << "but expecting a call to TlsConnectDone. "; | |
| 1049 } | |
| 1050 } | |
| 1051 | |
| 1052 // Override the regular implementation, which would call AsyncWorkCompleted | |
| 1053 // immediately after Work(). | |
| 1054 void SocketSecureFunction::AsyncWorkStart() { | |
| 1055 Work(); | |
| 1056 } | |
| 1057 | |
| 1058 void SocketSecureFunction::TlsConnectDone(int result) { | |
| 1059 SetResult(new base::FundamentalValue(result)); | |
| 1060 VLOG(1) << "chrome.socket.secure: TlsConnectDone(): got back result " | |
| 1061 << result; | |
| 1062 | |
| 1063 // No matter how the TLS connection attempt went, the underlying socket's | |
| 1064 // no longer bound to the original TCPSocket. It belongs to ssl_socket_, | |
| 1065 // which we either promote to a new API-accessible socket (via a TLSSocket | |
| 1066 // wrapper) or delete. | |
| 1067 if (result == net::OK) { | |
| 1068 // Wrap the StreamSocket in a TLSSocket (which matches our API). Set the | |
| 1069 // handle of the socket to the new value, so that we can use the newly | |
| 1070 // connected socket for close/SetKeepAlive/etc. | |
| 1071 TLSSocket* wrapper = new TLSSocket( | |
| 1072 ssl_socket_.release(), underlying_socket_, extension_->id()); | |
| 1073 | |
| 1074 // This deletes the old TCPSocket for us. | |
| 1075 SetSocket(params_->socket_id, wrapper); | |
| 1076 } else { | |
| 1077 // Failed TLS connection. This'll delete the underlying TCPClientSocket. | |
| 1078 ssl_socket_.reset(); | |
| 1079 // This will delete the TCPSocket, which has already released its hold on | |
| 1080 // the TCPClientSocket. | |
| 1081 RemoveSocket(params_->socket_id); | |
| 1082 } | |
| 1083 AsyncWorkCompleted(); | |
| 1084 } | |
| 1085 | |
| 899 } // namespace extensions | 1086 } // namespace extensions |
| OLD | NEW |