/src/node/src/udp_wrap.cc
Line | Count | Source |
1 | | // Copyright Joyent, Inc. and other Node contributors. |
2 | | // |
3 | | // Permission is hereby granted, free of charge, to any person obtaining a |
4 | | // copy of this software and associated documentation files (the |
5 | | // "Software"), to deal in the Software without restriction, including |
6 | | // without limitation the rights to use, copy, modify, merge, publish, |
7 | | // distribute, sublicense, and/or sell copies of the Software, and to permit |
8 | | // persons to whom the Software is furnished to do so, subject to the |
9 | | // following conditions: |
10 | | // |
11 | | // The above copyright notice and this permission notice shall be included |
12 | | // in all copies or substantial portions of the Software. |
13 | | // |
14 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 | | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 | | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 | | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 | | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 | | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 | | // USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | | |
22 | | #include "udp_wrap.h" |
23 | | #include "env-inl.h" |
24 | | #include "handle_wrap.h" |
25 | | #include "node_buffer.h" |
26 | | #include "node_errors.h" |
27 | | #include "node_external_reference.h" |
28 | | #include "node_sockaddr-inl.h" |
29 | | #include "permission/permission.h" |
30 | | #include "req_wrap-inl.h" |
31 | | #include "util-inl.h" |
32 | | |
33 | | namespace node { |
34 | | |
35 | | using errors::TryCatchScope; |
36 | | using v8::Array; |
37 | | using v8::ArrayBuffer; |
38 | | using v8::BackingStore; |
39 | | using v8::BackingStoreInitializationMode; |
40 | | using v8::Boolean; |
41 | | using v8::Context; |
42 | | using v8::DontDelete; |
43 | | using v8::FunctionCallbackInfo; |
44 | | using v8::FunctionTemplate; |
45 | | using v8::HandleScope; |
46 | | using v8::Integer; |
47 | | using v8::Isolate; |
48 | | using v8::Local; |
49 | | using v8::MaybeLocal; |
50 | | using v8::Object; |
51 | | using v8::PropertyAttribute; |
52 | | using v8::ReadOnly; |
53 | | using v8::Signature; |
54 | | using v8::Uint32; |
55 | | using v8::Undefined; |
56 | | using v8::Value; |
57 | | |
58 | | namespace { |
59 | | template <int (*fn)(uv_udp_t*, int)> |
60 | 0 | void SetLibuvInt32(const FunctionCallbackInfo<Value>& args) { |
61 | 0 | UDPWrap* wrap = BaseObject::Unwrap<UDPWrap>(args.This()); |
62 | 0 | if (wrap == nullptr) { |
63 | 0 | args.GetReturnValue().Set(UV_EBADF); |
64 | 0 | return; |
65 | 0 | } |
66 | 0 | Environment* env = wrap->env(); |
67 | 0 | CHECK_EQ(args.Length(), 1); |
68 | 0 | int flag; |
69 | 0 | if (!args[0]->Int32Value(env->context()).To(&flag)) { |
70 | 0 | return; |
71 | 0 | } |
72 | 0 | int err = fn(wrap->GetLibuvHandle(), flag); |
73 | 0 | args.GetReturnValue().Set(err); |
74 | 0 | } Unexecuted instantiation: udp_wrap.cc:void node::(anonymous namespace)::SetLibuvInt32<&uv_udp_set_multicast_ttl>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: udp_wrap.cc:void node::(anonymous namespace)::SetLibuvInt32<&uv_udp_set_multicast_loop>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: udp_wrap.cc:void node::(anonymous namespace)::SetLibuvInt32<&uv_udp_set_broadcast>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: udp_wrap.cc:void node::(anonymous namespace)::SetLibuvInt32<&uv_udp_set_ttl>(v8::FunctionCallbackInfo<v8::Value> const&) |
75 | | } // namespace |
76 | | |
77 | | class SendWrap : public ReqWrap<uv_udp_send_t> { |
78 | | public: |
79 | | SendWrap(Environment* env, Local<Object> req_wrap_obj, bool have_callback); |
80 | | inline bool have_callback() const; |
81 | | size_t msg_size; |
82 | | |
83 | | SET_NO_MEMORY_INFO() |
84 | | SET_MEMORY_INFO_NAME(SendWrap) |
85 | | SET_SELF_SIZE(SendWrap) |
86 | | |
87 | | private: |
88 | | const bool have_callback_; |
89 | | }; |
90 | | |
91 | | |
92 | | SendWrap::SendWrap(Environment* env, |
93 | | Local<Object> req_wrap_obj, |
94 | | bool have_callback) |
95 | 0 | : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_UDPSENDWRAP), |
96 | 0 | have_callback_(have_callback) { |
97 | 0 | } |
98 | | |
99 | | |
100 | 0 | bool SendWrap::have_callback() const { |
101 | 0 | return have_callback_; |
102 | 0 | } |
103 | | |
104 | 0 | UDPListener::~UDPListener() { |
105 | 0 | if (wrap_ != nullptr) |
106 | 0 | wrap_->set_listener(nullptr); |
107 | 0 | } |
108 | | |
109 | 0 | UDPWrapBase::~UDPWrapBase() { |
110 | 0 | set_listener(nullptr); |
111 | 0 | } |
112 | | |
113 | 0 | UDPListener* UDPWrapBase::listener() const { |
114 | 0 | CHECK_NOT_NULL(listener_); |
115 | 0 | return listener_; |
116 | 0 | } |
117 | | |
118 | 0 | void UDPWrapBase::set_listener(UDPListener* listener) { |
119 | 0 | if (listener_ != nullptr) |
120 | 0 | listener_->wrap_ = nullptr; |
121 | 0 | listener_ = listener; |
122 | 0 | if (listener_ != nullptr) { |
123 | 0 | CHECK_NULL(listener_->wrap_); |
124 | 0 | listener_->wrap_ = this; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | 0 | UDPWrapBase* UDPWrapBase::FromObject(Local<Object> obj) { |
129 | 0 | CHECK_GT(obj->InternalFieldCount(), UDPWrapBase::kUDPWrapBaseField); |
130 | 0 | return static_cast<UDPWrapBase*>(obj->GetAlignedPointerFromInternalField( |
131 | 0 | UDPWrapBase::kUDPWrapBaseField, EmbedderDataTag::kDefault)); |
132 | 0 | } |
133 | | |
134 | 0 | void UDPWrapBase::AddMethods(Environment* env, Local<FunctionTemplate> t) { |
135 | 0 | SetProtoMethod(env->isolate(), t, "recvStart", RecvStart); |
136 | 0 | SetProtoMethod(env->isolate(), t, "recvStop", RecvStop); |
137 | 0 | } |
138 | | |
139 | | void UDPWrapBase::RegisterExternalReferences( |
140 | 0 | ExternalReferenceRegistry* registry) { |
141 | 0 | registry->Register(RecvStart); |
142 | 0 | registry->Register(RecvStop); |
143 | 0 | } |
144 | | |
145 | | UDPWrap::UDPWrap(Environment* env, Local<Object> object) |
146 | 0 | : HandleWrap(env, |
147 | 0 | object, |
148 | 0 | reinterpret_cast<uv_handle_t*>(&handle_), |
149 | 0 | AsyncWrap::PROVIDER_UDPWRAP) { |
150 | 0 | object->SetAlignedPointerInInternalField(UDPWrapBase::kUDPWrapBaseField, |
151 | 0 | static_cast<UDPWrapBase*>(this), |
152 | 0 | EmbedderDataTag::kDefault); |
153 | |
|
154 | 0 | int r = uv_udp_init(env->event_loop(), &handle_); |
155 | 0 | CHECK_EQ(r, 0); // can't fail anyway |
156 | | |
157 | 0 | set_listener(this); |
158 | 0 | } |
159 | | |
160 | | |
161 | | void UDPWrap::Initialize(Local<Object> target, |
162 | | Local<Value> unused, |
163 | | Local<Context> context, |
164 | 0 | void* priv) { |
165 | 0 | Environment* env = Environment::GetCurrent(context); |
166 | 0 | Isolate* isolate = env->isolate(); |
167 | |
|
168 | 0 | Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New); |
169 | 0 | t->InstanceTemplate()->SetInternalFieldCount( |
170 | 0 | UDPWrapBase::kInternalFieldCount); |
171 | |
|
172 | 0 | enum PropertyAttribute attributes = |
173 | 0 | static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
174 | |
|
175 | 0 | Local<Signature> signature = Signature::New(isolate, t); |
176 | |
|
177 | 0 | Local<FunctionTemplate> get_fd_templ = |
178 | 0 | FunctionTemplate::New(isolate, UDPWrap::GetFD, Local<Value>(), signature); |
179 | |
|
180 | 0 | t->PrototypeTemplate()->SetAccessorProperty(env->fd_string(), |
181 | 0 | get_fd_templ, |
182 | 0 | Local<FunctionTemplate>(), |
183 | 0 | attributes); |
184 | |
|
185 | 0 | UDPWrapBase::AddMethods(env, t); |
186 | 0 | SetProtoMethod(isolate, t, "open", Open); |
187 | 0 | SetProtoMethod(isolate, t, "bind", Bind); |
188 | 0 | SetProtoMethod(isolate, t, "connect", Connect); |
189 | 0 | SetProtoMethod(isolate, t, "send", Send); |
190 | 0 | SetProtoMethod(isolate, t, "bind6", Bind6); |
191 | 0 | SetProtoMethod(isolate, t, "connect6", Connect6); |
192 | 0 | SetProtoMethod(isolate, t, "send6", Send6); |
193 | 0 | SetProtoMethod(isolate, t, "disconnect", Disconnect); |
194 | 0 | SetProtoMethod(isolate, |
195 | 0 | t, |
196 | 0 | "getpeername", |
197 | 0 | GetSockOrPeerName<UDPWrap, uv_udp_getpeername>); |
198 | 0 | SetProtoMethod(isolate, |
199 | 0 | t, |
200 | 0 | "getsockname", |
201 | 0 | GetSockOrPeerName<UDPWrap, uv_udp_getsockname>); |
202 | 0 | SetProtoMethod(isolate, t, "addMembership", AddMembership); |
203 | 0 | SetProtoMethod(isolate, t, "dropMembership", DropMembership); |
204 | 0 | SetProtoMethod( |
205 | 0 | isolate, t, "addSourceSpecificMembership", AddSourceSpecificMembership); |
206 | 0 | SetProtoMethod( |
207 | 0 | isolate, t, "dropSourceSpecificMembership", DropSourceSpecificMembership); |
208 | 0 | SetProtoMethod(isolate, t, "setMulticastInterface", SetMulticastInterface); |
209 | 0 | SetProtoMethod( |
210 | 0 | isolate, t, "setMulticastTTL", SetLibuvInt32<uv_udp_set_multicast_ttl>); |
211 | 0 | SetProtoMethod(isolate, |
212 | 0 | t, |
213 | 0 | "setMulticastLoopback", |
214 | 0 | SetLibuvInt32<uv_udp_set_multicast_loop>); |
215 | 0 | SetProtoMethod( |
216 | 0 | isolate, t, "setBroadcast", SetLibuvInt32<uv_udp_set_broadcast>); |
217 | 0 | SetProtoMethod(isolate, t, "setTTL", SetLibuvInt32<uv_udp_set_ttl>); |
218 | 0 | SetProtoMethod(isolate, t, "bufferSize", BufferSize); |
219 | 0 | SetProtoMethodNoSideEffect(isolate, t, "getSendQueueSize", GetSendQueueSize); |
220 | 0 | SetProtoMethodNoSideEffect( |
221 | 0 | isolate, t, "getSendQueueCount", GetSendQueueCount); |
222 | |
|
223 | 0 | t->Inherit(HandleWrap::GetConstructorTemplate(env)); |
224 | |
|
225 | 0 | SetConstructorFunction(context, target, "UDP", t); |
226 | 0 | env->set_udp_constructor_function(t->GetFunction(context).ToLocalChecked()); |
227 | | |
228 | | // Create FunctionTemplate for SendWrap |
229 | 0 | Local<FunctionTemplate> swt = |
230 | 0 | BaseObject::MakeLazilyInitializedJSTemplate(env); |
231 | 0 | swt->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
232 | 0 | SetConstructorFunction(context, target, "SendWrap", swt); |
233 | |
|
234 | 0 | Local<Object> constants = Object::New(isolate); |
235 | 0 | NODE_DEFINE_CONSTANT(constants, UV_UDP_IPV6ONLY); |
236 | 0 | NODE_DEFINE_CONSTANT(constants, UV_UDP_REUSEADDR); |
237 | 0 | NODE_DEFINE_CONSTANT(constants, UV_UDP_REUSEPORT); |
238 | 0 | target->Set(context, |
239 | 0 | env->constants_string(), |
240 | 0 | constants).Check(); |
241 | 0 | } |
242 | | |
243 | 0 | void UDPWrap::RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
244 | 0 | UDPWrapBase::RegisterExternalReferences(registry); |
245 | 0 | registry->Register(New); |
246 | 0 | registry->Register(GetFD); |
247 | |
|
248 | 0 | registry->Register(Open); |
249 | 0 | registry->Register(Bind); |
250 | 0 | registry->Register(Connect); |
251 | 0 | registry->Register(Send); |
252 | 0 | registry->Register(Bind6); |
253 | 0 | registry->Register(Connect6); |
254 | 0 | registry->Register(Send6); |
255 | 0 | registry->Register(Disconnect); |
256 | 0 | registry->Register(GetSockOrPeerName<UDPWrap, uv_udp_getpeername>); |
257 | 0 | registry->Register(GetSockOrPeerName<UDPWrap, uv_udp_getsockname>); |
258 | 0 | registry->Register(AddMembership); |
259 | 0 | registry->Register(DropMembership); |
260 | 0 | registry->Register(AddSourceSpecificMembership); |
261 | 0 | registry->Register(DropSourceSpecificMembership); |
262 | 0 | registry->Register(SetMulticastInterface); |
263 | 0 | registry->Register(SetLibuvInt32<uv_udp_set_multicast_ttl>); |
264 | 0 | registry->Register(SetLibuvInt32<uv_udp_set_multicast_loop>); |
265 | 0 | registry->Register(SetLibuvInt32<uv_udp_set_broadcast>); |
266 | 0 | registry->Register(SetLibuvInt32<uv_udp_set_ttl>); |
267 | 0 | registry->Register(BufferSize); |
268 | 0 | registry->Register(GetSendQueueSize); |
269 | 0 | registry->Register(GetSendQueueCount); |
270 | 0 | } |
271 | | |
272 | 0 | void UDPWrap::New(const FunctionCallbackInfo<Value>& args) { |
273 | 0 | CHECK(args.IsConstructCall()); |
274 | 0 | Environment* env = Environment::GetCurrent(args); |
275 | 0 | new UDPWrap(env, args.This()); |
276 | 0 | } |
277 | | |
278 | | |
279 | 0 | void UDPWrap::GetFD(const FunctionCallbackInfo<Value>& args) { |
280 | 0 | int fd = UV_EBADF; |
281 | 0 | #if !defined(_WIN32) |
282 | 0 | UDPWrap* wrap = Unwrap<UDPWrap>(args.This()); |
283 | 0 | if (wrap != nullptr) |
284 | 0 | uv_fileno(reinterpret_cast<uv_handle_t*>(&wrap->handle_), &fd); |
285 | 0 | #endif |
286 | 0 | args.GetReturnValue().Set(fd); |
287 | 0 | } |
288 | | |
289 | | int sockaddr_for_family(int address_family, |
290 | | const char* address, |
291 | | const unsigned short port, |
292 | 0 | struct sockaddr_storage* addr) { |
293 | 0 | switch (address_family) { |
294 | 0 | case AF_INET: |
295 | 0 | return uv_ip4_addr(address, port, reinterpret_cast<sockaddr_in*>(addr)); |
296 | 0 | case AF_INET6: |
297 | 0 | return uv_ip6_addr(address, port, reinterpret_cast<sockaddr_in6*>(addr)); |
298 | 0 | default: |
299 | 0 | UNREACHABLE("unexpected address family"); |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | 0 | void UDPWrap::DoBind(const FunctionCallbackInfo<Value>& args, int family) { |
304 | 0 | UDPWrap* wrap; |
305 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
306 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
307 | | |
308 | | // bind(ip, port, flags) |
309 | 0 | CHECK_EQ(args.Length(), 3); |
310 | | |
311 | 0 | node::Utf8Value address(args.GetIsolate(), args[0]); |
312 | | |
313 | | // Check for network permission |
314 | 0 | Environment* env = wrap->env(); |
315 | |
|
316 | 0 | ERR_ACCESS_DENIED_IF_INSUFFICIENT_PERMISSIONS( |
317 | 0 | env, permission::PermissionScope::kNet, address.ToStringView(), args); |
318 | | |
319 | 0 | Local<Context> ctx = args.GetIsolate()->GetCurrentContext(); |
320 | 0 | uint32_t port, flags; |
321 | 0 | if (!args[1]->Uint32Value(ctx).To(&port) || |
322 | 0 | !args[2]->Uint32Value(ctx).To(&flags)) |
323 | 0 | return; |
324 | 0 | struct sockaddr_storage addr_storage; |
325 | 0 | int err = sockaddr_for_family(family, address.out(), port, &addr_storage); |
326 | 0 | if (err == 0) { |
327 | 0 | err = uv_udp_bind(&wrap->handle_, |
328 | 0 | reinterpret_cast<const sockaddr*>(&addr_storage), |
329 | 0 | flags); |
330 | 0 | } |
331 | |
|
332 | 0 | if (err == 0) |
333 | 0 | wrap->listener()->OnAfterBind(); |
334 | |
|
335 | 0 | args.GetReturnValue().Set(err); |
336 | 0 | } |
337 | | |
338 | | |
339 | 0 | void UDPWrap::DoConnect(const FunctionCallbackInfo<Value>& args, int family) { |
340 | 0 | UDPWrap* wrap; |
341 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
342 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
343 | | |
344 | | // Check for network permission |
345 | 0 | Environment* env = wrap->env(); |
346 | 0 | THROW_IF_INSUFFICIENT_PERMISSIONS(env, |
347 | 0 | permission::PermissionScope::kNet, |
348 | 0 | "", |
349 | 0 | args.GetReturnValue().Set(UV_EACCES)); |
350 | | |
351 | 0 | CHECK_EQ(args.Length(), 2); |
352 | | |
353 | 0 | node::Utf8Value address(args.GetIsolate(), args[0]); |
354 | 0 | Local<Context> ctx = args.GetIsolate()->GetCurrentContext(); |
355 | 0 | uint32_t port; |
356 | 0 | if (!args[1]->Uint32Value(ctx).To(&port)) |
357 | 0 | return; |
358 | 0 | struct sockaddr_storage addr_storage; |
359 | 0 | int err = sockaddr_for_family(family, address.out(), port, &addr_storage); |
360 | 0 | if (err == 0) { |
361 | 0 | err = uv_udp_connect(&wrap->handle_, |
362 | 0 | reinterpret_cast<const sockaddr*>(&addr_storage)); |
363 | 0 | } |
364 | |
|
365 | 0 | args.GetReturnValue().Set(err); |
366 | 0 | } |
367 | | |
368 | | |
369 | 0 | void UDPWrap::Open(const FunctionCallbackInfo<Value>& args) { |
370 | 0 | UDPWrap* wrap; |
371 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
372 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
373 | 0 | CHECK(args[0]->IsNumber()); |
374 | 0 | int fd = FromV8Value<int>(args[0]); |
375 | 0 | int err = uv_udp_open(&wrap->handle_, fd); |
376 | |
|
377 | 0 | args.GetReturnValue().Set(err); |
378 | 0 | } |
379 | | |
380 | | |
381 | 0 | void UDPWrap::Bind(const FunctionCallbackInfo<Value>& args) { |
382 | 0 | DoBind(args, AF_INET); |
383 | 0 | } |
384 | | |
385 | | |
386 | 0 | void UDPWrap::Bind6(const FunctionCallbackInfo<Value>& args) { |
387 | 0 | DoBind(args, AF_INET6); |
388 | 0 | } |
389 | | |
390 | | |
391 | 0 | void UDPWrap::BufferSize(const FunctionCallbackInfo<Value>& args) { |
392 | 0 | Environment* env = Environment::GetCurrent(args); |
393 | 0 | UDPWrap* wrap; |
394 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
395 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
396 | | |
397 | 0 | CHECK(args[0]->IsUint32()); |
398 | 0 | CHECK(args[1]->IsBoolean()); |
399 | 0 | bool is_recv = args[1].As<Boolean>()->Value(); |
400 | 0 | const char* uv_func_name = is_recv ? "uv_recv_buffer_size" : |
401 | 0 | "uv_send_buffer_size"; |
402 | |
|
403 | 0 | if (!args[0]->IsInt32()) { |
404 | 0 | USE(env->CollectUVExceptionInfo(args[2], UV_EINVAL, uv_func_name)); |
405 | 0 | return; |
406 | 0 | } |
407 | | |
408 | 0 | uv_handle_t* handle = reinterpret_cast<uv_handle_t*>(&wrap->handle_); |
409 | 0 | int size = static_cast<int>(args[0].As<Uint32>()->Value()); |
410 | 0 | int err; |
411 | |
|
412 | 0 | if (is_recv) |
413 | 0 | err = uv_recv_buffer_size(handle, &size); |
414 | 0 | else |
415 | 0 | err = uv_send_buffer_size(handle, &size); |
416 | |
|
417 | 0 | if (err != 0) { |
418 | 0 | USE(env->CollectUVExceptionInfo(args[2], err, uv_func_name)); |
419 | 0 | return; |
420 | 0 | } |
421 | | |
422 | 0 | args.GetReturnValue().Set(size); |
423 | 0 | } |
424 | | |
425 | | |
426 | 0 | void UDPWrap::Connect(const FunctionCallbackInfo<Value>& args) { |
427 | 0 | DoConnect(args, AF_INET); |
428 | 0 | } |
429 | | |
430 | | |
431 | 0 | void UDPWrap::Connect6(const FunctionCallbackInfo<Value>& args) { |
432 | 0 | DoConnect(args, AF_INET6); |
433 | 0 | } |
434 | | |
435 | | |
436 | 0 | void UDPWrap::Disconnect(const FunctionCallbackInfo<Value>& args) { |
437 | 0 | UDPWrap* wrap; |
438 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
439 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
440 | | |
441 | 0 | CHECK_EQ(args.Length(), 0); |
442 | | |
443 | 0 | int err = uv_udp_connect(&wrap->handle_, nullptr); |
444 | |
|
445 | 0 | args.GetReturnValue().Set(err); |
446 | 0 | } |
447 | | |
448 | 0 | void UDPWrap::SetMulticastInterface(const FunctionCallbackInfo<Value>& args) { |
449 | 0 | UDPWrap* wrap; |
450 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
451 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
452 | | |
453 | 0 | CHECK_EQ(args.Length(), 1); |
454 | 0 | CHECK(args[0]->IsString()); |
455 | | |
456 | 0 | Utf8Value iface(args.GetIsolate(), args[0]); |
457 | |
|
458 | 0 | const char* iface_cstr = *iface; |
459 | |
|
460 | 0 | int err = uv_udp_set_multicast_interface(&wrap->handle_, iface_cstr); |
461 | 0 | args.GetReturnValue().Set(err); |
462 | 0 | } |
463 | | |
464 | | void UDPWrap::SetMembership(const FunctionCallbackInfo<Value>& args, |
465 | 0 | uv_membership membership) { |
466 | 0 | UDPWrap* wrap; |
467 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
468 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
469 | | |
470 | 0 | CHECK_EQ(args.Length(), 2); |
471 | | |
472 | 0 | node::Utf8Value address(args.GetIsolate(), args[0]); |
473 | 0 | node::Utf8Value iface(args.GetIsolate(), args[1]); |
474 | |
|
475 | 0 | const char* iface_cstr = *iface; |
476 | 0 | if (args[1]->IsUndefined() || args[1]->IsNull()) { |
477 | 0 | iface_cstr = nullptr; |
478 | 0 | } |
479 | |
|
480 | 0 | int err = uv_udp_set_membership(&wrap->handle_, |
481 | 0 | *address, |
482 | 0 | iface_cstr, |
483 | 0 | membership); |
484 | 0 | args.GetReturnValue().Set(err); |
485 | 0 | } |
486 | | |
487 | | |
488 | 0 | void UDPWrap::AddMembership(const FunctionCallbackInfo<Value>& args) { |
489 | 0 | SetMembership(args, UV_JOIN_GROUP); |
490 | 0 | } |
491 | | |
492 | | |
493 | 0 | void UDPWrap::DropMembership(const FunctionCallbackInfo<Value>& args) { |
494 | 0 | SetMembership(args, UV_LEAVE_GROUP); |
495 | 0 | } |
496 | | |
497 | | void UDPWrap::SetSourceMembership(const FunctionCallbackInfo<Value>& args, |
498 | 0 | uv_membership membership) { |
499 | 0 | UDPWrap* wrap; |
500 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
501 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
502 | | |
503 | 0 | CHECK_EQ(args.Length(), 3); |
504 | | |
505 | 0 | node::Utf8Value source_address(args.GetIsolate(), args[0]); |
506 | 0 | node::Utf8Value group_address(args.GetIsolate(), args[1]); |
507 | 0 | node::Utf8Value iface(args.GetIsolate(), args[2]); |
508 | |
|
509 | 0 | if (*iface == nullptr) return; |
510 | 0 | const char* iface_cstr = *iface; |
511 | 0 | if (args[2]->IsUndefined() || args[2]->IsNull()) { |
512 | 0 | iface_cstr = nullptr; |
513 | 0 | } |
514 | |
|
515 | 0 | int err = uv_udp_set_source_membership(&wrap->handle_, |
516 | 0 | *group_address, |
517 | 0 | iface_cstr, |
518 | 0 | *source_address, |
519 | 0 | membership); |
520 | 0 | args.GetReturnValue().Set(err); |
521 | 0 | } |
522 | | |
523 | | void UDPWrap::AddSourceSpecificMembership( |
524 | 0 | const FunctionCallbackInfo<Value>& args) { |
525 | 0 | SetSourceMembership(args, UV_JOIN_GROUP); |
526 | 0 | } |
527 | | |
528 | | |
529 | | void UDPWrap::DropSourceSpecificMembership( |
530 | 0 | const FunctionCallbackInfo<Value>& args) { |
531 | 0 | SetSourceMembership(args, UV_LEAVE_GROUP); |
532 | 0 | } |
533 | | |
534 | | |
535 | 0 | void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) { |
536 | 0 | Environment* env = Environment::GetCurrent(args); |
537 | |
|
538 | 0 | UDPWrap* wrap; |
539 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
540 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
541 | | |
542 | | // Check for network permission |
543 | 0 | THROW_IF_INSUFFICIENT_PERMISSIONS(env, |
544 | 0 | permission::PermissionScope::kNet, |
545 | 0 | "", |
546 | 0 | args.GetReturnValue().Set(UV_EACCES)); |
547 | | |
548 | 0 | CHECK(args.Length() == 4 || args.Length() == 6); |
549 | 0 | CHECK(args[0]->IsObject()); |
550 | 0 | CHECK(args[1]->IsArray()); |
551 | 0 | CHECK(args[2]->IsUint32()); |
552 | | |
553 | 0 | bool sendto = args.Length() == 6; |
554 | 0 | if (sendto) { |
555 | | // send(req, list, list.length, port, address, hasCallback) |
556 | 0 | CHECK(args[3]->IsUint32()); |
557 | 0 | CHECK(args[4]->IsString()); |
558 | 0 | CHECK(args[5]->IsBoolean()); |
559 | 0 | } else { |
560 | | // send(req, list, list.length, hasCallback) |
561 | 0 | CHECK(args[3]->IsBoolean()); |
562 | 0 | } |
563 | | |
564 | 0 | Local<Array> chunks = args[1].As<Array>(); |
565 | | // it is faster to fetch the length of the |
566 | | // array in js-land |
567 | 0 | size_t count = args[2].As<Uint32>()->Value(); |
568 | |
|
569 | 0 | MaybeStackBuffer<uv_buf_t, 16> bufs(count); |
570 | | |
571 | | // construct uv_buf_t array |
572 | 0 | for (size_t i = 0; i < count; i++) { |
573 | 0 | Local<Value> chunk; |
574 | 0 | if (!chunks->Get(env->context(), i).ToLocal(&chunk)) return; |
575 | | |
576 | 0 | size_t length = Buffer::Length(chunk); |
577 | |
|
578 | 0 | bufs[i] = uv_buf_init(Buffer::Data(chunk), length); |
579 | 0 | } |
580 | | |
581 | 0 | int err = 0; |
582 | 0 | struct sockaddr_storage addr_storage; |
583 | 0 | sockaddr* addr = nullptr; |
584 | 0 | if (sendto) { |
585 | 0 | const unsigned short port = args[3].As<Uint32>()->Value(); |
586 | 0 | node::Utf8Value address(env->isolate(), args[4]); |
587 | 0 | err = sockaddr_for_family(family, address.out(), port, &addr_storage); |
588 | 0 | if (err == 0) |
589 | 0 | addr = reinterpret_cast<sockaddr*>(&addr_storage); |
590 | 0 | } |
591 | |
|
592 | 0 | if (err == 0) { |
593 | 0 | wrap->current_send_req_wrap_ = args[0].As<Object>(); |
594 | 0 | wrap->current_send_has_callback_ = |
595 | 0 | sendto ? args[5]->IsTrue() : args[3]->IsTrue(); |
596 | |
|
597 | 0 | err = static_cast<int>(wrap->Send(*bufs, count, addr)); |
598 | |
|
599 | 0 | wrap->current_send_req_wrap_.Clear(); |
600 | 0 | wrap->current_send_has_callback_ = false; |
601 | 0 | } |
602 | |
|
603 | 0 | args.GetReturnValue().Set(err); |
604 | 0 | } |
605 | | |
606 | | ssize_t UDPWrap::Send(uv_buf_t* bufs_ptr, |
607 | | size_t count, |
608 | 0 | const sockaddr* addr) { |
609 | 0 | if (IsHandleClosing()) return UV_EBADF; |
610 | | |
611 | 0 | size_t msg_size = 0; |
612 | 0 | for (size_t i = 0; i < count; i++) |
613 | 0 | msg_size += bufs_ptr[i].len; |
614 | |
|
615 | 0 | int err = 0; |
616 | 0 | if (!env()->options()->test_udp_no_try_send) [[unlikely]] { |
617 | 0 | err = uv_udp_try_send(&handle_, bufs_ptr, count, addr); |
618 | 0 | if (err == UV_ENOSYS || err == UV_EAGAIN) { |
619 | 0 | err = 0; |
620 | 0 | } else if (err >= 0) { |
621 | 0 | size_t sent = err; |
622 | 0 | while (count > 0 && bufs_ptr->len <= sent) { |
623 | 0 | sent -= bufs_ptr->len; |
624 | 0 | bufs_ptr++; |
625 | 0 | count--; |
626 | 0 | } |
627 | 0 | if (count > 0) { |
628 | 0 | CHECK_LT(sent, bufs_ptr->len); |
629 | 0 | bufs_ptr->base += sent; |
630 | 0 | bufs_ptr->len -= sent; |
631 | 0 | } else { |
632 | 0 | CHECK_EQ(static_cast<size_t>(err), msg_size); |
633 | | // + 1 so that the JS side can distinguish 0-length async sends from |
634 | | // 0-length sync sends. |
635 | 0 | return msg_size + 1; |
636 | 0 | } |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | 0 | if (err == 0) { |
641 | 0 | AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(this); |
642 | 0 | ReqWrap<uv_udp_send_t>* req_wrap = listener()->CreateSendWrap(msg_size); |
643 | 0 | if (req_wrap == nullptr) return UV_ENOSYS; |
644 | | |
645 | 0 | err = req_wrap->Dispatch( |
646 | 0 | uv_udp_send, |
647 | 0 | &handle_, |
648 | 0 | bufs_ptr, |
649 | 0 | count, |
650 | 0 | addr, |
651 | 0 | uv_udp_send_cb{[](uv_udp_send_t* req, int status) { |
652 | 0 | UDPWrap* self = ContainerOf(&UDPWrap::handle_, req->handle); |
653 | 0 | self->listener()->OnSendDone( |
654 | 0 | ReqWrap<uv_udp_send_t>::from_req(req), status); |
655 | 0 | }}); |
656 | 0 | if (err) |
657 | 0 | delete req_wrap; |
658 | 0 | } |
659 | | |
660 | 0 | return err; |
661 | 0 | } |
662 | | |
663 | | |
664 | 0 | ReqWrap<uv_udp_send_t>* UDPWrap::CreateSendWrap(size_t msg_size) { |
665 | 0 | SendWrap* req_wrap = new SendWrap(env(), |
666 | 0 | current_send_req_wrap_, |
667 | 0 | current_send_has_callback_); |
668 | 0 | req_wrap->msg_size = msg_size; |
669 | 0 | return req_wrap; |
670 | 0 | } |
671 | | |
672 | | |
673 | 0 | void UDPWrap::Send(const FunctionCallbackInfo<Value>& args) { |
674 | 0 | DoSend(args, AF_INET); |
675 | 0 | } |
676 | | |
677 | | |
678 | 0 | void UDPWrap::Send6(const FunctionCallbackInfo<Value>& args) { |
679 | 0 | DoSend(args, AF_INET6); |
680 | 0 | } |
681 | | |
682 | | |
683 | 0 | AsyncWrap* UDPWrap::GetAsyncWrap() { |
684 | 0 | return this; |
685 | 0 | } |
686 | | |
687 | 0 | SocketAddress UDPWrap::GetPeerName() { |
688 | 0 | return SocketAddress::FromPeerName(handle_); |
689 | 0 | } |
690 | | |
691 | 0 | SocketAddress UDPWrap::GetSockName() { |
692 | 0 | return SocketAddress::FromSockName(handle_); |
693 | 0 | } |
694 | | |
695 | 0 | void UDPWrapBase::RecvStart(const FunctionCallbackInfo<Value>& args) { |
696 | 0 | UDPWrapBase* wrap = UDPWrapBase::FromObject(args.This()); |
697 | 0 | args.GetReturnValue().Set(wrap == nullptr ? UV_EBADF : wrap->RecvStart()); |
698 | 0 | } |
699 | | |
700 | 0 | int UDPWrap::RecvStart() { |
701 | 0 | if (IsHandleClosing()) return UV_EBADF; |
702 | 0 | int err = uv_udp_recv_start(&handle_, OnAlloc, OnRecv); |
703 | | // UV_EALREADY means that the socket is already bound but that's okay |
704 | 0 | if (err == UV_EALREADY) |
705 | 0 | err = 0; |
706 | 0 | return err; |
707 | 0 | } |
708 | | |
709 | | |
710 | 0 | void UDPWrapBase::RecvStop(const FunctionCallbackInfo<Value>& args) { |
711 | 0 | UDPWrapBase* wrap = UDPWrapBase::FromObject(args.This()); |
712 | 0 | args.GetReturnValue().Set(wrap == nullptr ? UV_EBADF : wrap->RecvStop()); |
713 | 0 | } |
714 | | |
715 | 0 | int UDPWrap::RecvStop() { |
716 | 0 | if (IsHandleClosing()) return UV_EBADF; |
717 | 0 | return uv_udp_recv_stop(&handle_); |
718 | 0 | } |
719 | | |
720 | | |
721 | 0 | void UDPWrap::OnSendDone(ReqWrap<uv_udp_send_t>* req, int status) { |
722 | 0 | BaseObjectPtr<SendWrap> req_wrap{static_cast<SendWrap*>(req)}; |
723 | 0 | if (req_wrap->have_callback()) { |
724 | 0 | Environment* env = req_wrap->env(); |
725 | 0 | HandleScope handle_scope(env->isolate()); |
726 | 0 | Context::Scope context_scope(env->context()); |
727 | 0 | Local<Value> arg[] = { |
728 | 0 | Integer::New(env->isolate(), status), |
729 | 0 | Integer::New(env->isolate(), req_wrap->msg_size), |
730 | 0 | }; |
731 | 0 | req_wrap->MakeCallback(env->oncomplete_string(), 2, arg); |
732 | 0 | } |
733 | 0 | } |
734 | | |
735 | | |
736 | | void UDPWrap::OnAlloc(uv_handle_t* handle, |
737 | | size_t suggested_size, |
738 | 0 | uv_buf_t* buf) { |
739 | 0 | UDPWrap* wrap = ContainerOf(&UDPWrap::handle_, |
740 | 0 | reinterpret_cast<uv_udp_t*>(handle)); |
741 | 0 | *buf = wrap->listener()->OnAlloc(suggested_size); |
742 | 0 | } |
743 | | |
744 | 0 | uv_buf_t UDPWrap::OnAlloc(size_t suggested_size) { |
745 | 0 | return env()->allocate_managed_buffer(suggested_size); |
746 | 0 | } |
747 | | |
748 | | void UDPWrap::OnRecv(uv_udp_t* handle, |
749 | | ssize_t nread, |
750 | | const uv_buf_t* buf, |
751 | | const sockaddr* addr, |
752 | 0 | unsigned int flags) { |
753 | 0 | UDPWrap* wrap = ContainerOf(&UDPWrap::handle_, handle); |
754 | 0 | wrap->listener()->OnRecv(nread, *buf, addr, flags); |
755 | 0 | } |
756 | | |
757 | | void UDPWrap::OnRecv(ssize_t nread, |
758 | | const uv_buf_t& buf_, |
759 | | const sockaddr* addr, |
760 | 0 | unsigned int flags) { |
761 | 0 | Environment* env = this->env(); |
762 | 0 | Isolate* isolate = env->isolate(); |
763 | 0 | std::unique_ptr<BackingStore> bs = env->release_managed_buffer(buf_); |
764 | 0 | if (nread == 0 && addr == nullptr) { |
765 | 0 | return; |
766 | 0 | } |
767 | | |
768 | 0 | HandleScope handle_scope(isolate); |
769 | 0 | Context::Scope context_scope(env->context()); |
770 | |
|
771 | 0 | Local<Value> argv[] = { |
772 | 0 | Integer::New(isolate, static_cast<int32_t>(nread)), |
773 | 0 | object(), |
774 | 0 | Undefined(isolate), |
775 | 0 | Undefined(isolate)}; |
776 | |
|
777 | 0 | if (nread < 0) { |
778 | 0 | MakeCallback(env->onmessage_string(), arraysize(argv), argv); |
779 | 0 | return; |
780 | 0 | } else if (nread == 0) { |
781 | 0 | bs = ArrayBuffer::NewBackingStore(isolate, 0); |
782 | 0 | } else if (static_cast<size_t>(nread) != bs->ByteLength()) { |
783 | 0 | CHECK_LE(static_cast<size_t>(nread), bs->ByteLength()); |
784 | 0 | std::unique_ptr<BackingStore> old_bs = std::move(bs); |
785 | 0 | bs = ArrayBuffer::NewBackingStore( |
786 | 0 | isolate, nread, BackingStoreInitializationMode::kUninitialized); |
787 | 0 | memcpy(bs->Data(), old_bs->Data(), nread); |
788 | 0 | } |
789 | | |
790 | 0 | Local<Object> address; |
791 | 0 | { |
792 | 0 | bool has_caught = false; |
793 | 0 | { |
794 | 0 | TryCatchScope try_catch(env); |
795 | 0 | if (!AddressToJS(env, addr).ToLocal(&address)) { |
796 | 0 | DCHECK(try_catch.HasCaught() && !try_catch.HasTerminated()); |
797 | 0 | argv[2] = try_catch.Exception(); |
798 | 0 | DCHECK(!argv[2].IsEmpty()); |
799 | 0 | has_caught = true; |
800 | 0 | } |
801 | 0 | } |
802 | 0 | if (has_caught) { |
803 | 0 | DCHECK(!argv[2].IsEmpty()); |
804 | 0 | MakeCallback(env->onerror_string(), arraysize(argv), argv); |
805 | 0 | return; |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, std::move(bs)); |
810 | 0 | { |
811 | 0 | bool has_caught = false; |
812 | 0 | { |
813 | 0 | TryCatchScope try_catch(env); |
814 | 0 | if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&argv[2])) { |
815 | 0 | DCHECK(try_catch.HasCaught() && !try_catch.HasTerminated()); |
816 | 0 | argv[2] = try_catch.Exception(); |
817 | 0 | DCHECK(!argv[2].IsEmpty()); |
818 | 0 | has_caught = true; |
819 | 0 | } |
820 | 0 | } |
821 | 0 | if (has_caught) { |
822 | 0 | DCHECK(!argv[2].IsEmpty()); |
823 | 0 | MakeCallback(env->onerror_string(), arraysize(argv), argv); |
824 | 0 | return; |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | 0 | argv[3] = address; |
829 | 0 | MakeCallback(env->onmessage_string(), arraysize(argv), argv); |
830 | 0 | } |
831 | | |
832 | | MaybeLocal<Object> UDPWrap::Instantiate(Environment* env, |
833 | | AsyncWrap* parent, |
834 | 0 | UDPWrap::SocketType type) { |
835 | 0 | AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(parent); |
836 | | |
837 | | // If this assert fires then Initialize hasn't been called yet. |
838 | 0 | CHECK_EQ(env->udp_constructor_function().IsEmpty(), false); |
839 | 0 | return env->udp_constructor_function()->NewInstance(env->context()); |
840 | 0 | } |
841 | | |
842 | 0 | void UDPWrap::GetSendQueueSize(const FunctionCallbackInfo<Value>& args) { |
843 | 0 | UDPWrap* wrap; |
844 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
845 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
846 | | |
847 | 0 | size_t size = uv_udp_get_send_queue_size(&wrap->handle_); |
848 | 0 | args.GetReturnValue().Set(static_cast<double>(size)); |
849 | 0 | } |
850 | | |
851 | 0 | void UDPWrap::GetSendQueueCount(const FunctionCallbackInfo<Value>& args) { |
852 | 0 | UDPWrap* wrap; |
853 | 0 | ASSIGN_OR_RETURN_UNWRAP( |
854 | 0 | &wrap, args.This(), args.GetReturnValue().Set(UV_EBADF)); |
855 | | |
856 | 0 | size_t count = uv_udp_get_send_queue_count(&wrap->handle_); |
857 | 0 | args.GetReturnValue().Set(static_cast<double>(count)); |
858 | 0 | } |
859 | | |
860 | | } // namespace node |
861 | | |
862 | | NODE_BINDING_CONTEXT_AWARE_INTERNAL(udp_wrap, node::UDPWrap::Initialize) |
863 | | NODE_BINDING_EXTERNAL_REFERENCE(udp_wrap, |
864 | | node::UDPWrap::RegisterExternalReferences) |