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 "chrome/browser/extensions/native_message_process.h" | |
6 | |
7 #include <unistd.h> | |
8 | |
9 #include "base/command_line.h" | |
10 #include "base/file_path.h" | |
11 #include "base/json/json_reader.h" | |
12 #include "base/json/json_writer.h" | |
13 #include "base/logging.h" | |
14 #include "base/path_service.h" | |
15 #include "base/pickle.h" | |
16 #include "base/process_util.h" | |
17 #include "base/values.h" | |
18 #include "chrome/browser/extensions/message_service.h" | |
19 #include "chrome/common/chrome_paths.h" | |
20 #include "content/public/browser/browser_thread.h" | |
21 #include "content/public/common/result_codes.h" | |
22 | |
23 namespace { | |
24 | |
25 const int kExitTimeoutMS = 5000; | |
26 const uint32 kMaxMessageDataLength = 10 * 1024 * 1024; | |
27 const char kNativeClientDir[] = "Native Hosts"; | |
Aaron Boodman
2012/08/15 03:39:21
kNativeHostsDirectoryName. Yeah it's longer. Life'
eaugusti
2012/08/21 23:08:34
Ha, done.
| |
28 | |
29 } // namespace | |
30 | |
31 namespace extensions { | |
32 | |
33 NativeMessageProcess::MessageData::MessageData() { | |
34 } | |
35 | |
36 NativeMessageProcess::MessageData::MessageData(MessageType message_type, | |
37 std::string message_data) | |
38 : type(message_type), | |
39 data(message_data) { | |
40 } | |
41 | |
42 NativeMessageProcess::NativeMessageProcess( | |
43 base::WeakPtr<MessageService> weak_service, | |
44 int dest_port, | |
Aaron Boodman
2012/08/15 03:39:21
Nit: avoid abbreviations; I would prefer destinati
eaugusti
2012/08/21 23:08:34
Done.
| |
45 base::ProcessHandle handle, | |
Aaron Boodman
2012/08/15 03:39:21
Nit: native_process_handle
eaugusti
2012/08/21 23:08:34
Done.
| |
46 int read_fd, | |
47 int write_fd, | |
48 bool is_send_message) | |
49 : weak_service_(weak_service), | |
50 dest_port_(dest_port), | |
51 handle_(handle), | |
52 watching_write_(false), | |
53 read_fd_(read_fd), | |
54 write_fd_(write_fd), | |
55 is_send_message_(is_send_message) { | |
56 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
57 scoped_read_fd_.reset(&read_fd_); | |
58 scoped_write_fd_.reset(&write_fd_); | |
59 // Always watch the read end. | |
60 MessageLoopForIO::current()->WatchFileDescriptor(read_fd_, | |
61 true, /* persistent */ | |
62 MessageLoopForIO::WATCH_READ, | |
63 &read_watcher_, | |
64 this); | |
65 } | |
66 | |
67 NativeMessageProcess::~NativeMessageProcess() { | |
68 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
69 read_watcher_.StopWatchingFileDescriptor(); | |
Aaron Boodman
2012/08/15 03:39:21
You probably don't need to do this manually.
eaugusti
2012/08/21 23:08:34
Done.
| |
70 write_watcher_.StopWatchingFileDescriptor(); | |
71 | |
72 // Close the fds now. | |
73 scoped_read_fd_.reset(); | |
Aaron Boodman
2012/08/15 03:39:21
You probably don't need to do this manually.
eaugusti
2012/08/21 23:08:34
Done.
| |
74 scoped_write_fd_.reset(); | |
75 | |
76 // Give the process some time to shutdown, then try and kill it. | |
77 content::BrowserThread::PostDelayedTask( | |
78 content::BrowserThread::FILE, | |
79 FROM_HERE, | |
80 base::Bind(base::IgnoreResult(&base::KillProcess), | |
81 handle_, | |
82 content::RESULT_CODE_NORMAL_EXIT, | |
83 false /* don't wait for exit */), | |
84 base::TimeDelta::FromMilliseconds(kExitTimeoutMS)); | |
85 } | |
86 | |
87 // static | |
88 void NativeMessageProcess::Create( | |
89 base::WeakPtr<MessageService> weak_service, | |
90 const std::string& native_app_name, | |
91 const std::string& connection_message, | |
92 int dest_port, | |
93 MessageType type, | |
94 base::Callback<void(NativeMessageProcess*)> callback) { | |
95 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
96 DCHECK(type == TYPE_SEND_MESSAGE_REQUEST || type == TYPE_CONNECT); | |
97 | |
98 base::FileHandleMappingVector fd_map; | |
99 | |
100 int read_pipe_fds[2]; | |
Aaron Boodman
2012/08/15 03:39:21
zero init with = {0};
eaugusti
2012/08/21 23:08:34
Done.
| |
101 if (HANDLE_EINTR(pipe(read_pipe_fds)) != 0) { | |
102 LOG(ERROR) << "Bad read pipe"; | |
103 return; | |
104 } | |
105 file_util::ScopedFD read_pipe_read_fd(&read_pipe_fds[0]); | |
106 file_util::ScopedFD read_pipe_write_fd(&read_pipe_fds[1]); | |
107 fd_map.push_back(std::make_pair(*read_pipe_write_fd, STDOUT_FILENO)); | |
108 | |
109 int write_pipe_fds[2]; | |
Aaron Boodman
2012/08/15 03:39:21
initialize
eaugusti
2012/08/21 23:08:34
Done.
| |
110 if (HANDLE_EINTR(pipe(write_pipe_fds)) != 0) { | |
111 LOG(ERROR) << "Bad write pipe"; | |
112 return; | |
113 } | |
114 file_util::ScopedFD write_pipe_read_fd(&write_pipe_fds[0]); | |
115 file_util::ScopedFD write_pipe_write_fd(&write_pipe_fds[1]); | |
116 fd_map.push_back(std::make_pair(*write_pipe_read_fd, STDIN_FILENO)); | |
117 | |
118 FilePath client_path; | |
Aaron Boodman
2012/08/15 03:39:21
Suggestion: native_host_program
eaugusti
2012/08/21 23:08:34
Done.
| |
119 FilePath registered_client_dir; | |
Aaron Boodman
2012/08/15 03:39:21
Naming suggestion: native_host_registry
eaugusti
2012/08/21 23:08:34
Done.
| |
120 CHECK(PathService::Get(chrome::DIR_USER_DATA, ®istered_client_dir)); | |
121 registered_client_dir = registered_client_dir.Append(kNativeClientDir); | |
122 client_path = registered_client_dir.Append(native_app_name); | |
123 | |
124 // Make sure that the client is not trying to invoke something outside of the | |
125 // proper directory. Eg. '../../dangerous_something.exe'. | |
126 if (!file_util::ContainsPath(registered_client_dir, client_path)) { | |
127 LOG(ERROR) << "Paths are not accepted as native app names."; | |
Aaron Boodman
2012/08/15 03:39:21
LOG(ERROR) << "Could not find native host: " << na
eaugusti
2012/08/21 23:08:34
Done.
| |
128 return; | |
129 } | |
130 | |
131 CommandLine line(client_path); | |
132 base::ProcessHandle handle; | |
133 base::LaunchOptions options; | |
134 options.fds_to_remap = &fd_map; | |
135 if (!base::LaunchProcess(line, options, &handle)) { | |
136 LOG(ERROR) << "Error launching process"; | |
137 return; | |
138 } | |
139 | |
140 // We will not be reading from the write pipe, nor writing from the read pipe. | |
141 write_pipe_read_fd.reset(); | |
142 read_pipe_write_fd.reset(); | |
143 | |
144 NativeMessageProcess* process = new NativeMessageProcess( | |
145 weak_service, dest_port, handle, | |
146 *read_pipe_read_fd.release(), *write_pipe_write_fd.release(), | |
147 type == TYPE_SEND_MESSAGE_REQUEST); | |
148 process->SendImpl(type, connection_message); | |
149 | |
150 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
151 base::Bind(callback, process)); | |
Aaron Boodman
2012/08/15 03:39:21
bad indent. This line should line up with 'content
Matt Perry
2012/08/16 00:01:36
I don't think the style guide actually requires th
eaugusti
2012/08/21 23:08:35
Done.
| |
152 } | |
153 | |
154 void NativeMessageProcess::SendImpl(MessageType type, const std::string& json) { | |
155 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
156 pending_messages_.push_back(MessageData(type, json)); | |
157 if (!watching_write_) { | |
158 watching_write_ = true; | |
159 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
Aaron Boodman
2012/08/15 03:39:21
Do we even need the async stuff for writing? Writi
eaugusti
2012/08/21 23:08:35
I think we can get away without it. Looking at ipc
| |
160 write_fd_, true, /* persistent */ | |
161 MessageLoopForIO::WATCH_WRITE, &write_watcher_, this)) { | |
162 LOG(ERROR) << "Error watching for the FD."; | |
163 } | |
164 } | |
165 } | |
166 | |
167 void NativeMessageProcess::OnFileCanReadWithoutBlocking(int fd) { | |
168 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
169 | |
170 // Make sure that the fd given to us is the same one we started with. | |
171 CHECK_EQ(fd, read_fd_); | |
172 | |
173 // If this is a sendMessage request, stop trying to read after the first | |
174 // message. | |
175 if (is_send_message_) | |
176 read_watcher_.StopWatchingFileDescriptor(); | |
177 | |
178 MessageData data; | |
179 if (!ReadMessage(fd, &data)) { | |
180 LOG(ERROR) << "Bad Read"; | |
181 return; | |
182 } | |
183 | |
184 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
185 base::Bind(&MessageService::PostMessageFromRenderer, weak_service_, | |
186 dest_port_, data.data)); | |
187 } | |
188 | |
189 void NativeMessageProcess::OnFileCanWriteWithoutBlocking(int fd) { | |
190 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
191 | |
192 // Make sure that the fd given to us is the same one we started with. | |
193 CHECK_EQ(fd, write_fd_); | |
194 | |
195 write_watcher_.StopWatchingFileDescriptor(); | |
196 watching_write_ = false; | |
197 | |
198 while (!pending_messages_.empty()) { | |
199 WriteMessage(fd, pending_messages_.back()); | |
200 pending_messages_.pop_back(); | |
201 } | |
202 } | |
203 | |
204 bool NativeMessageProcess::WriteMessage(int fd, const MessageData& data) { | |
205 Pickle pickle; | |
206 | |
207 // Pickles will always pad bytes to 32-bit alignment, so just use a unit32. | |
208 pickle.WriteUInt32(data.type); | |
209 | |
210 // Pickles write the length of a string before it as a uint32. | |
211 pickle.WriteString(data.data); | |
212 | |
213 // Make sure that the pickle doesn't do any unexpected padding. | |
214 CHECK(8 + data.data.length() == pickle.payload_size()); | |
215 | |
216 if (!file_util::WriteFileDescriptor( | |
217 fd, | |
218 const_cast<const Pickle*>(&pickle)->payload(), | |
219 pickle.payload_size())) { | |
220 LOG(ERROR) << "Error writing message to the native client."; | |
221 return false; | |
222 } | |
223 | |
224 return true; | |
225 } | |
226 | |
227 bool NativeMessageProcess::ReadMessage(int fd, MessageData* message) { | |
228 // Read the type (uint32) and length (uint32). | |
229 char message_meta_data[8]; | |
230 if (!file_util::ReadFromFD(fd, message_meta_data, 8)) { | |
Aaron Boodman
2012/08/15 03:39:21
What about incomplete messages? It seems like ther
eaugusti
2012/08/21 23:08:35
ReadFromFD protects against partial reads by just
| |
231 LOG(ERROR) << "Error reading the message type and length."; | |
232 return false; | |
233 } | |
234 | |
235 Pickle pickle; | |
236 pickle.WriteBytes(message_meta_data, 8); | |
237 PickleIterator pickle_it(pickle); | |
238 uint32 type; | |
239 uint32 data_length; | |
240 if (!pickle_it.ReadUInt32(&type) || !pickle_it.ReadUInt32(&data_length)) { | |
241 LOG(ERROR) << "Error getting the message type and length from the pickle."; | |
242 return false; | |
243 } | |
244 | |
245 if (type < 0 || type >= NUM_MESSAGE_TYPES) { | |
246 LOG(ERROR) << type << " is not a valid message type."; | |
247 return false; | |
248 } | |
249 | |
250 if ((is_send_message_ && (type != TYPE_SEND_MESSAGE_RESPONSE)) || | |
251 (!is_send_message_ && (type != TYPE_CONNECT_MESSAGE))) { | |
252 LOG(ERROR) << "Recieved a message of type " << type << ". " | |
253 << "Expecting a message of type " | |
254 << (is_send_message_ ? TYPE_SEND_MESSAGE_RESPONSE : | |
255 TYPE_CONNECT_MESSAGE); | |
256 return false; | |
257 } | |
258 message->type = static_cast<MessageType>(type); | |
259 | |
260 if (data_length > kMaxMessageDataLength) { | |
261 LOG(ERROR) << data_length << " is too large for the length of a message. " | |
262 << "Max message size is " << kMaxMessageDataLength; | |
263 return false; | |
264 } | |
265 | |
266 std::string str_data(data_length, '\0'); | |
267 if (!file_util::ReadFromFD(fd, const_cast<char*>(str_data.data()), | |
Matt Perry
2012/08/16 00:01:36
use &str_data[0]
eaugusti
2012/08/21 23:08:35
Done.
| |
268 data_length)) { | |
269 LOG(ERROR) << "Error reading the json data."; | |
270 return false; | |
271 } | |
272 message->data.swap(str_data); | |
273 | |
274 return true; | |
275 } | |
276 | |
277 } // namespace extensions | |
OLD | NEW |