1 // Copyright 2014 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 "extensions/browser/api/sockets_udp/sockets_udp_api.h"
6 
7 #include "base/bind.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/storage_partition.h"
10 #include "content/public/common/socket_permission_request.h"
11 #include "extensions/browser/api/socket/udp_socket.h"
12 #include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h"
13 #include "extensions/common/api/sockets/sockets_manifest_data.h"
14 #include "net/base/net_errors.h"
15 
16 namespace extensions {
17 namespace api {
18 
19 using content::SocketPermissionRequest;
20 
21 const char kSocketNotFoundError[] = "Socket not found";
22 const char kPermissionError[] = "App does not have permission";
23 const char kWildcardAddress[] = "*";
24 const int kWildcardPort = 0;
25 
~UDPSocketAsyncApiFunction()26 UDPSocketAsyncApiFunction::~UDPSocketAsyncApiFunction() {}
27 
28 std::unique_ptr<SocketResourceManagerInterface>
CreateSocketResourceManager()29 UDPSocketAsyncApiFunction::CreateSocketResourceManager() {
30   return std::unique_ptr<SocketResourceManagerInterface>(
31       new SocketResourceManager<ResumableUDPSocket>());
32 }
33 
GetUdpSocket(int socket_id)34 ResumableUDPSocket* UDPSocketAsyncApiFunction::GetUdpSocket(int socket_id) {
35   return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
36 }
37 
38 UDPSocketExtensionWithDnsLookupFunction::
~UDPSocketExtensionWithDnsLookupFunction()39     ~UDPSocketExtensionWithDnsLookupFunction() {}
40 
41 std::unique_ptr<SocketResourceManagerInterface>
CreateSocketResourceManager()42 UDPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() {
43   return std::unique_ptr<SocketResourceManagerInterface>(
44       new SocketResourceManager<ResumableUDPSocket>());
45 }
46 
GetUdpSocket(int socket_id)47 ResumableUDPSocket* UDPSocketExtensionWithDnsLookupFunction::GetUdpSocket(
48     int socket_id) {
49   return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
50 }
51 
CreateSocketInfo(int socket_id,ResumableUDPSocket * socket)52 sockets_udp::SocketInfo CreateSocketInfo(int socket_id,
53                                          ResumableUDPSocket* socket) {
54   sockets_udp::SocketInfo socket_info;
55   // This represents what we know about the socket, and does not call through
56   // to the system.
57   socket_info.socket_id = socket_id;
58   if (!socket->name().empty()) {
59     socket_info.name.reset(new std::string(socket->name()));
60   }
61   socket_info.persistent = socket->persistent();
62   if (socket->buffer_size() > 0) {
63     socket_info.buffer_size.reset(new int(socket->buffer_size()));
64   }
65   socket_info.paused = socket->paused();
66 
67   // Grab the local address as known by the OS.
68   net::IPEndPoint localAddress;
69   if (socket->GetLocalAddress(&localAddress)) {
70     socket_info.local_address.reset(
71         new std::string(localAddress.ToStringWithoutPort()));
72     socket_info.local_port.reset(new int(localAddress.port()));
73   }
74 
75   return socket_info;
76 }
77 
SetSocketProperties(ResumableUDPSocket * socket,sockets_udp::SocketProperties * properties)78 void SetSocketProperties(ResumableUDPSocket* socket,
79                          sockets_udp::SocketProperties* properties) {
80   if (properties->name.get()) {
81     socket->set_name(*properties->name);
82   }
83   if (properties->persistent.get()) {
84     socket->set_persistent(*properties->persistent);
85   }
86   if (properties->buffer_size.get()) {
87     socket->set_buffer_size(*properties->buffer_size);
88   }
89 }
90 
SocketsUdpCreateFunction()91 SocketsUdpCreateFunction::SocketsUdpCreateFunction() {}
92 
~SocketsUdpCreateFunction()93 SocketsUdpCreateFunction::~SocketsUdpCreateFunction() {}
94 
Prepare()95 bool SocketsUdpCreateFunction::Prepare() {
96   params_ = sockets_udp::Create::Params::Create(*args_);
97   EXTENSION_FUNCTION_VALIDATE(params_.get());
98 
99   mojo::PendingRemote<network::mojom::UDPSocketListener> listener_remote;
100   socket_listener_receiver_ = listener_remote.InitWithNewPipeAndPassReceiver();
101   content::BrowserContext::GetDefaultStoragePartition(browser_context())
102       ->GetNetworkContext()
103       ->CreateUDPSocket(socket_.InitWithNewPipeAndPassReceiver(),
104                         std::move(listener_remote));
105   return true;
106 }
107 
Work()108 void SocketsUdpCreateFunction::Work() {
109   ResumableUDPSocket* socket = new ResumableUDPSocket(
110       std::move(socket_), std::move(socket_listener_receiver_),
111       extension_->id());
112 
113   sockets_udp::SocketProperties* properties = params_->properties.get();
114   if (properties) {
115     SetSocketProperties(socket, properties);
116   }
117 
118   sockets_udp::CreateInfo create_info;
119   create_info.socket_id = AddSocket(socket);
120   results_ = sockets_udp::Create::Results::Create(create_info);
121 }
122 
SocketsUdpUpdateFunction()123 SocketsUdpUpdateFunction::SocketsUdpUpdateFunction() {}
124 
~SocketsUdpUpdateFunction()125 SocketsUdpUpdateFunction::~SocketsUdpUpdateFunction() {}
126 
Prepare()127 bool SocketsUdpUpdateFunction::Prepare() {
128   params_ = sockets_udp::Update::Params::Create(*args_);
129   EXTENSION_FUNCTION_VALIDATE(params_.get());
130   return true;
131 }
132 
Work()133 void SocketsUdpUpdateFunction::Work() {
134   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
135   if (!socket) {
136     error_ = kSocketNotFoundError;
137     return;
138   }
139 
140   SetSocketProperties(socket, &params_->properties);
141   results_ = sockets_udp::Update::Results::Create();
142 }
143 
SocketsUdpSetPausedFunction()144 SocketsUdpSetPausedFunction::SocketsUdpSetPausedFunction()
145     : socket_event_dispatcher_(NULL) {}
146 
~SocketsUdpSetPausedFunction()147 SocketsUdpSetPausedFunction::~SocketsUdpSetPausedFunction() {}
148 
Prepare()149 bool SocketsUdpSetPausedFunction::Prepare() {
150   params_ = api::sockets_udp::SetPaused::Params::Create(*args_);
151   EXTENSION_FUNCTION_VALIDATE(params_.get());
152 
153   socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
154   DCHECK(socket_event_dispatcher_)
155       << "There is no socket event dispatcher. "
156          "If this assertion is failing during a test, then it is likely that "
157          "TestExtensionSystem is failing to provide an instance of "
158          "UDPSocketEventDispatcher.";
159   return socket_event_dispatcher_ != NULL;
160 }
161 
Work()162 void SocketsUdpSetPausedFunction::Work() {
163   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
164   if (!socket) {
165     error_ = kSocketNotFoundError;
166     return;
167   }
168 
169   if (socket->paused() != params_->paused) {
170     socket->set_paused(params_->paused);
171     if (socket->IsConnected() && !params_->paused) {
172       socket_event_dispatcher_->OnSocketResume(extension_->id(),
173                                                params_->socket_id);
174     }
175   }
176 
177   results_ = sockets_udp::SetPaused::Results::Create();
178 }
179 
SocketsUdpBindFunction()180 SocketsUdpBindFunction::SocketsUdpBindFunction()
181     : socket_event_dispatcher_(NULL) {}
182 
~SocketsUdpBindFunction()183 SocketsUdpBindFunction::~SocketsUdpBindFunction() {}
184 
Prepare()185 bool SocketsUdpBindFunction::Prepare() {
186   params_ = sockets_udp::Bind::Params::Create(*args_);
187   EXTENSION_FUNCTION_VALIDATE(params_.get());
188 
189   socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
190   DCHECK(socket_event_dispatcher_)
191       << "There is no socket event dispatcher. "
192          "If this assertion is failing during a test, then it is likely that "
193          "TestExtensionSystem is failing to provide an instance of "
194          "UDPSocketEventDispatcher.";
195   return socket_event_dispatcher_ != NULL;
196 }
197 
AsyncWorkStart()198 void SocketsUdpBindFunction::AsyncWorkStart() {
199   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
200   if (!socket) {
201     error_ = kSocketNotFoundError;
202     AsyncWorkCompleted();
203     return;
204   }
205 
206   content::SocketPermissionRequest param(
207       SocketPermissionRequest::UDP_BIND, params_->address, params_->port);
208   if (!SocketsManifestData::CheckRequest(extension(), param)) {
209     error_ = kPermissionError;
210     AsyncWorkCompleted();
211     return;
212   }
213   socket->Bind(params_->address, params_->port,
214                base::BindOnce(&SocketsUdpBindFunction::OnCompleted, this));
215 }
216 
OnCompleted(int net_result)217 void SocketsUdpBindFunction::OnCompleted(int net_result) {
218   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
219   if (!socket) {
220     error_ = kSocketNotFoundError;
221     AsyncWorkCompleted();
222     return;
223   }
224   results_ = sockets_udp::Bind::Results::Create(net_result);
225   if (net_result == net::OK) {
226     socket_event_dispatcher_->OnSocketBind(extension_->id(),
227                                            params_->socket_id);
228   } else {
229     error_ = net::ErrorToString(net_result);
230     AsyncWorkCompleted();
231     return;
232   }
233 
234   OpenFirewallHole(params_->address, params_->socket_id, socket);
235 }
236 
SocketsUdpSendFunction()237 SocketsUdpSendFunction::SocketsUdpSendFunction() : io_buffer_size_(0) {}
238 
~SocketsUdpSendFunction()239 SocketsUdpSendFunction::~SocketsUdpSendFunction() {}
240 
Prepare()241 bool SocketsUdpSendFunction::Prepare() {
242   params_ = sockets_udp::Send::Params::Create(*args_);
243   EXTENSION_FUNCTION_VALIDATE(params_.get());
244   io_buffer_size_ = params_->data.size();
245   io_buffer_ = base::MakeRefCounted<net::WrappedIOBuffer>(
246       reinterpret_cast<const char*>(params_->data.data()));
247 
248   return true;
249 }
250 
AsyncWorkStart()251 void SocketsUdpSendFunction::AsyncWorkStart() {
252   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
253   if (!socket) {
254     error_ = kSocketNotFoundError;
255     AsyncWorkCompleted();
256     return;
257   }
258 
259   content::SocketPermissionRequest param(
260       SocketPermissionRequest::UDP_SEND_TO, params_->address, params_->port);
261   if (!SocketsManifestData::CheckRequest(extension(), param)) {
262     error_ = kPermissionError;
263     AsyncWorkCompleted();
264     return;
265   }
266 
267   StartDnsLookup(net::HostPortPair(params_->address, params_->port));
268 }
269 
AfterDnsLookup(int lookup_result)270 void SocketsUdpSendFunction::AfterDnsLookup(int lookup_result) {
271   if (lookup_result == net::OK) {
272     StartSendTo();
273   } else {
274     SetSendResult(lookup_result, -1);
275   }
276 }
277 
StartSendTo()278 void SocketsUdpSendFunction::StartSendTo() {
279   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
280   if (!socket) {
281     error_ = kSocketNotFoundError;
282     AsyncWorkCompleted();
283     return;
284   }
285 
286   socket->SendTo(
287       io_buffer_, io_buffer_size_, addresses_.front(),
288       base::BindRepeating(&SocketsUdpSendFunction::OnCompleted, this));
289 }
290 
OnCompleted(int net_result)291 void SocketsUdpSendFunction::OnCompleted(int net_result) {
292   if (net_result >= net::OK) {
293     SetSendResult(net::OK, net_result);
294   } else {
295     SetSendResult(net_result, -1);
296   }
297 }
298 
SetSendResult(int net_result,int bytes_sent)299 void SocketsUdpSendFunction::SetSendResult(int net_result, int bytes_sent) {
300   CHECK(net_result <= net::OK) << "Network status code must be < 0";
301 
302   sockets_udp::SendInfo send_info;
303   send_info.result_code = net_result;
304   if (net_result == net::OK) {
305     send_info.bytes_sent.reset(new int(bytes_sent));
306   }
307 
308   if (net_result != net::OK)
309     error_ = net::ErrorToString(net_result);
310   results_ = sockets_udp::Send::Results::Create(send_info);
311   AsyncWorkCompleted();
312 }
313 
SocketsUdpCloseFunction()314 SocketsUdpCloseFunction::SocketsUdpCloseFunction() {}
315 
~SocketsUdpCloseFunction()316 SocketsUdpCloseFunction::~SocketsUdpCloseFunction() {}
317 
Prepare()318 bool SocketsUdpCloseFunction::Prepare() {
319   params_ = sockets_udp::Close::Params::Create(*args_);
320   EXTENSION_FUNCTION_VALIDATE(params_.get());
321   return true;
322 }
323 
Work()324 void SocketsUdpCloseFunction::Work() {
325   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
326   if (!socket) {
327     error_ = kSocketNotFoundError;
328     return;
329   }
330 
331   socket->Disconnect(false /* socket_destroying */);
332   RemoveSocket(params_->socket_id);
333   results_ = sockets_udp::Close::Results::Create();
334 }
335 
SocketsUdpGetInfoFunction()336 SocketsUdpGetInfoFunction::SocketsUdpGetInfoFunction() {}
337 
~SocketsUdpGetInfoFunction()338 SocketsUdpGetInfoFunction::~SocketsUdpGetInfoFunction() {}
339 
Prepare()340 bool SocketsUdpGetInfoFunction::Prepare() {
341   params_ = sockets_udp::GetInfo::Params::Create(*args_);
342   EXTENSION_FUNCTION_VALIDATE(params_.get());
343   return true;
344 }
345 
Work()346 void SocketsUdpGetInfoFunction::Work() {
347   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
348   if (!socket) {
349     error_ = kSocketNotFoundError;
350     return;
351   }
352 
353   sockets_udp::SocketInfo socket_info =
354       CreateSocketInfo(params_->socket_id, socket);
355   results_ = sockets_udp::GetInfo::Results::Create(socket_info);
356 }
357 
SocketsUdpGetSocketsFunction()358 SocketsUdpGetSocketsFunction::SocketsUdpGetSocketsFunction() {}
359 
~SocketsUdpGetSocketsFunction()360 SocketsUdpGetSocketsFunction::~SocketsUdpGetSocketsFunction() {}
361 
Prepare()362 bool SocketsUdpGetSocketsFunction::Prepare() { return true; }
363 
Work()364 void SocketsUdpGetSocketsFunction::Work() {
365   std::vector<sockets_udp::SocketInfo> socket_infos;
366   std::unordered_set<int>* resource_ids = GetSocketIds();
367   if (resource_ids != NULL) {
368     for (int socket_id : *resource_ids) {
369       ResumableUDPSocket* socket = GetUdpSocket(socket_id);
370       if (socket) {
371         socket_infos.push_back(CreateSocketInfo(socket_id, socket));
372       }
373     }
374   }
375   results_ = sockets_udp::GetSockets::Results::Create(socket_infos);
376 }
377 
SocketsUdpJoinGroupFunction()378 SocketsUdpJoinGroupFunction::SocketsUdpJoinGroupFunction() {}
379 
~SocketsUdpJoinGroupFunction()380 SocketsUdpJoinGroupFunction::~SocketsUdpJoinGroupFunction() {}
381 
Prepare()382 bool SocketsUdpJoinGroupFunction::Prepare() {
383   params_ = sockets_udp::JoinGroup::Params::Create(*args_);
384   EXTENSION_FUNCTION_VALIDATE(params_.get());
385   return true;
386 }
387 
AsyncWorkStart()388 void SocketsUdpJoinGroupFunction::AsyncWorkStart() {
389   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
390   if (!socket) {
391     error_ = kSocketNotFoundError;
392     AsyncWorkCompleted();
393     return;
394   }
395 
396   content::SocketPermissionRequest param(
397       SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
398       kWildcardAddress,
399       kWildcardPort);
400   if (!SocketsManifestData::CheckRequest(extension(), param)) {
401     error_ = kPermissionError;
402     AsyncWorkCompleted();
403     return;
404   }
405 
406   socket->JoinGroup(
407       params_->address,
408       base::BindOnce(&SocketsUdpJoinGroupFunction::OnCompleted, this));
409 }
410 
OnCompleted(int net_result)411 void SocketsUdpJoinGroupFunction::OnCompleted(int net_result) {
412   if (net_result != net::OK)
413     error_ = net::ErrorToString(net_result);
414   results_ = sockets_udp::JoinGroup::Results::Create(net_result);
415   AsyncWorkCompleted();
416 }
417 
SocketsUdpLeaveGroupFunction()418 SocketsUdpLeaveGroupFunction::SocketsUdpLeaveGroupFunction() {}
419 
~SocketsUdpLeaveGroupFunction()420 SocketsUdpLeaveGroupFunction::~SocketsUdpLeaveGroupFunction() {}
421 
Prepare()422 bool SocketsUdpLeaveGroupFunction::Prepare() {
423   params_ = api::sockets_udp::LeaveGroup::Params::Create(*args_);
424   EXTENSION_FUNCTION_VALIDATE(params_.get());
425   return true;
426 }
427 
AsyncWorkStart()428 void SocketsUdpLeaveGroupFunction::AsyncWorkStart() {
429   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
430   if (!socket) {
431     error_ = kSocketNotFoundError;
432     AsyncWorkCompleted();
433     return;
434   }
435 
436   content::SocketPermissionRequest param(
437       SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
438       kWildcardAddress,
439       kWildcardPort);
440   if (!SocketsManifestData::CheckRequest(extension(), param)) {
441     error_ = kPermissionError;
442     AsyncWorkCompleted();
443     return;
444   }
445   socket->LeaveGroup(
446       params_->address,
447       base::BindOnce(&SocketsUdpLeaveGroupFunction::OnCompleted, this));
448 }
449 
OnCompleted(int result)450 void SocketsUdpLeaveGroupFunction::OnCompleted(int result) {
451   if (result != net::OK)
452     error_ = net::ErrorToString(result);
453   results_ = sockets_udp::LeaveGroup::Results::Create(result);
454   AsyncWorkCompleted();
455 }
456 
457 SocketsUdpSetMulticastTimeToLiveFunction::
SocketsUdpSetMulticastTimeToLiveFunction()458     SocketsUdpSetMulticastTimeToLiveFunction() {}
459 
460 SocketsUdpSetMulticastTimeToLiveFunction::
~SocketsUdpSetMulticastTimeToLiveFunction()461     ~SocketsUdpSetMulticastTimeToLiveFunction() {}
462 
Prepare()463 bool SocketsUdpSetMulticastTimeToLiveFunction::Prepare() {
464   params_ = api::sockets_udp::SetMulticastTimeToLive::Params::Create(*args_);
465   EXTENSION_FUNCTION_VALIDATE(params_.get());
466   return true;
467 }
468 
Work()469 void SocketsUdpSetMulticastTimeToLiveFunction::Work() {
470   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
471   if (!socket) {
472     error_ = kSocketNotFoundError;
473     return;
474   }
475 
476   int net_result = socket->SetMulticastTimeToLive(params_->ttl);
477   if (net_result != net::OK)
478     error_ = net::ErrorToString(net_result);
479   results_ = sockets_udp::SetMulticastTimeToLive::Results::Create(net_result);
480 }
481 
482 SocketsUdpSetMulticastLoopbackModeFunction::
SocketsUdpSetMulticastLoopbackModeFunction()483     SocketsUdpSetMulticastLoopbackModeFunction() {}
484 
485 SocketsUdpSetMulticastLoopbackModeFunction::
~SocketsUdpSetMulticastLoopbackModeFunction()486     ~SocketsUdpSetMulticastLoopbackModeFunction() {}
487 
Prepare()488 bool SocketsUdpSetMulticastLoopbackModeFunction::Prepare() {
489   params_ = api::sockets_udp::SetMulticastLoopbackMode::Params::Create(*args_);
490   EXTENSION_FUNCTION_VALIDATE(params_.get());
491   return true;
492 }
493 
Work()494 void SocketsUdpSetMulticastLoopbackModeFunction::Work() {
495   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
496   if (!socket) {
497     error_ = kSocketNotFoundError;
498     return;
499   }
500 
501   int net_result = socket->SetMulticastLoopbackMode(params_->enabled);
502   if (net_result != net::OK)
503     error_ = net::ErrorToString(net_result);
504   results_ = sockets_udp::SetMulticastLoopbackMode::Results::Create(net_result);
505 }
506 
SocketsUdpGetJoinedGroupsFunction()507 SocketsUdpGetJoinedGroupsFunction::SocketsUdpGetJoinedGroupsFunction() {}
508 
~SocketsUdpGetJoinedGroupsFunction()509 SocketsUdpGetJoinedGroupsFunction::~SocketsUdpGetJoinedGroupsFunction() {}
510 
Prepare()511 bool SocketsUdpGetJoinedGroupsFunction::Prepare() {
512   params_ = api::sockets_udp::GetJoinedGroups::Params::Create(*args_);
513   EXTENSION_FUNCTION_VALIDATE(params_.get());
514   return true;
515 }
516 
Work()517 void SocketsUdpGetJoinedGroupsFunction::Work() {
518   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
519   if (!socket) {
520     error_ = kSocketNotFoundError;
521     return;
522   }
523 
524   content::SocketPermissionRequest param(
525       SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
526       kWildcardAddress,
527       kWildcardPort);
528   if (!SocketsManifestData::CheckRequest(extension(), param)) {
529     error_ = kPermissionError;
530     return;
531   }
532 
533   const std::vector<std::string>& groups = socket->GetJoinedGroups();
534   results_ = sockets_udp::GetJoinedGroups::Results::Create(groups);
535 }
536 
SocketsUdpSetBroadcastFunction()537 SocketsUdpSetBroadcastFunction::SocketsUdpSetBroadcastFunction() {
538 }
539 
~SocketsUdpSetBroadcastFunction()540 SocketsUdpSetBroadcastFunction::~SocketsUdpSetBroadcastFunction() {
541 }
542 
Prepare()543 bool SocketsUdpSetBroadcastFunction::Prepare() {
544   params_ = api::sockets_udp::SetBroadcast::Params::Create(*args_);
545   EXTENSION_FUNCTION_VALIDATE(params_.get());
546   return true;
547 }
548 
AsyncWorkStart()549 void SocketsUdpSetBroadcastFunction::AsyncWorkStart() {
550   ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
551   if (!socket) {
552     error_ = kSocketNotFoundError;
553     AsyncWorkCompleted();
554     return;
555   }
556 
557   socket->SetBroadcast(
558       params_->enabled,
559       base::BindOnce(&SocketsUdpSetBroadcastFunction::OnCompleted, this));
560 }
561 
OnCompleted(int net_result)562 void SocketsUdpSetBroadcastFunction::OnCompleted(int net_result) {
563   if (net_result != net::OK)
564     error_ = net::ErrorToString(net_result);
565   results_ = sockets_udp::SetBroadcast::Results::Create(net_result);
566   AsyncWorkCompleted();
567 }
568 
569 }  // namespace api
570 }  // namespace extensions
571