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 |