#include "udp_wrap.h" #include "async_wrap-inl.h" #include "node_errors.h" #include "node_sockaddr-inl.h" #include namespace node { using errors::TryCatchScope; using v8::Array; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Local; using v8::Object; using v8::String; using v8::Value; // JSUDPWrap is a testing utility used by test/common/udppair.js // to simulate UDP traffic deterministically in Node.js tests. class JSUDPWrap final : public UDPWrapBase, public AsyncWrap { public: JSUDPWrap(Environment* env, Local obj); int RecvStart() override; int RecvStop() override; ssize_t Send(uv_buf_t* bufs, size_t nbufs, const sockaddr* addr) override; SocketAddress GetPeerName() override; SocketAddress GetSockName() override; AsyncWrap* GetAsyncWrap() override { return this; } static void New(const FunctionCallbackInfo& args); static void EmitReceived(const FunctionCallbackInfo& args); static void OnSendDone(const FunctionCallbackInfo& args); static void OnAfterBind(const FunctionCallbackInfo& args); static void Initialize(Local target, Local unused, Local context, void* priv); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(JSUDPWrap) SET_SELF_SIZE(JSUDPWrap) }; JSUDPWrap::JSUDPWrap(Environment* env, Local obj) : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) { MakeWeak(); obj->SetAlignedPointerInInternalField( kUDPWrapBaseField, static_cast(this)); } int JSUDPWrap::RecvStart() { HandleScope scope(env()->isolate()); Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; int32_t value_int = UV_EPROTO; if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || !value->Int32Value(env()->context()).To(&value_int)) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) errors::TriggerUncaughtException(env()->isolate(), try_catch); } return value_int; } int JSUDPWrap::RecvStop() { HandleScope scope(env()->isolate()); Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; int32_t value_int = UV_EPROTO; if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || !value->Int32Value(env()->context()).To(&value_int)) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) errors::TriggerUncaughtException(env()->isolate(), try_catch); } return value_int; } ssize_t JSUDPWrap::Send(uv_buf_t* bufs, size_t nbufs, const sockaddr* addr) { HandleScope scope(env()->isolate()); Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; int64_t value_int = UV_EPROTO; size_t total_len = 0; MaybeStackBuffer, 16> buffers(nbufs); for (size_t i = 0; i < nbufs; i++) { buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len) .ToLocalChecked(); total_len += bufs[i].len; } Local args[] = { listener()->CreateSendWrap(total_len)->object(), Array::New(env()->isolate(), buffers.out(), nbufs), AddressToJS(env(), addr) }; if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) .ToLocal(&value) || !value->IntegerValue(env()->context()).To(&value_int)) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) errors::TriggerUncaughtException(env()->isolate(), try_catch); } return value_int; } SocketAddress JSUDPWrap::GetPeerName() { SocketAddress ret; CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); return ret; } SocketAddress JSUDPWrap::GetSockName() { SocketAddress ret; CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); return ret; } void JSUDPWrap::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args.IsConstructCall()); new JSUDPWrap(env, args.Holder()); } void JSUDPWrap::EmitReceived(const FunctionCallbackInfo& args) { JSUDPWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); Environment* env = wrap->env(); ArrayBufferViewContents buffer(args[0]); const char* data = buffer.data(); int len = buffer.length(); CHECK(args[1]->IsInt32()); // family CHECK(args[2]->IsString()); // address CHECK(args[3]->IsInt32()); // port CHECK(args[4]->IsInt32()); // flags int family = args[1].As()->Value() == 4 ? AF_INET : AF_INET6; Utf8Value address(env->isolate(), args[2]); int port = args[3].As()->Value(); int flags = args[3].As()->Value(); sockaddr_storage addr; CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0); // Repeatedly ask the stream's owner for memory, copy the data that we // just read from JS into those buffers and emit them as reads. while (len != 0) { uv_buf_t buf = wrap->listener()->OnAlloc(len); ssize_t avail = std::min(buf.len, len); memcpy(buf.base, data, avail); data += avail; len -= avail; wrap->listener()->OnRecv( avail, buf, reinterpret_cast(&addr), flags); } } void JSUDPWrap::OnSendDone(const FunctionCallbackInfo& args) { JSUDPWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); CHECK(args[0]->IsObject()); CHECK(args[1]->IsInt32()); ReqWrap* req_wrap; ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As()); int status = args[1].As()->Value(); wrap->listener()->OnSendDone(req_wrap, status); } void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo& args) { JSUDPWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); wrap->listener()->OnAfterBind(); } void JSUDPWrap::Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); Local t = env->NewFunctionTemplate(New); Local js_udp_wrap_string = FIXED_ONE_BYTE_STRING(env->isolate(), "JSUDPWrap"); t->SetClassName(js_udp_wrap_string); t->InstanceTemplate() ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1); t->Inherit(AsyncWrap::GetConstructorTemplate(env)); UDPWrapBase::AddMethods(env, t); env->SetProtoMethod(t, "emitReceived", EmitReceived); env->SetProtoMethod(t, "onSendDone", OnSendDone); env->SetProtoMethod(t, "onAfterBind", OnAfterBind); target->Set(env->context(), js_udp_wrap_string, t->GetFunction(context).ToLocalChecked()).Check(); } } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize)