1 #include "udp_wrap.h"
2 #include "async_wrap-inl.h"
3 #include "node_errors.h"
4 #include "node_sockaddr-inl.h"
5 
6 #include <algorithm>
7 
8 // TODO(RaisinTen): Replace all uses with empty `v8::Maybe`s.
9 #define JS_EXCEPTION_PENDING UV_EPROTO
10 
11 namespace node {
12 
13 using errors::TryCatchScope;
14 using v8::Array;
15 using v8::Context;
16 using v8::FunctionCallbackInfo;
17 using v8::FunctionTemplate;
18 using v8::HandleScope;
19 using v8::Int32;
20 using v8::Isolate;
21 using v8::Local;
22 using v8::Object;
23 using v8::Value;
24 
25 // JSUDPWrap is a testing utility used by test/common/udppair.js
26 // to simulate UDP traffic deterministically in Node.js tests.
27 class JSUDPWrap final : public UDPWrapBase, public AsyncWrap {
28  public:
29   JSUDPWrap(Environment* env, Local<Object> obj);
30 
31   int RecvStart() override;
32   int RecvStop() override;
33   ssize_t Send(uv_buf_t* bufs,
34                size_t nbufs,
35                const sockaddr* addr) override;
36   SocketAddress GetPeerName() override;
37   SocketAddress GetSockName() override;
38   AsyncWrap* GetAsyncWrap() override { return this; }
39 
40   static void New(const FunctionCallbackInfo<Value>& args);
41   static void EmitReceived(const FunctionCallbackInfo<Value>& args);
42   static void OnSendDone(const FunctionCallbackInfo<Value>& args);
43   static void OnAfterBind(const FunctionCallbackInfo<Value>& args);
44 
45   static void Initialize(Local<Object> target,
46                          Local<Value> unused,
47                          Local<Context> context,
48                          void* priv);
49   SET_NO_MEMORY_INFO()
50   SET_MEMORY_INFO_NAME(JSUDPWrap)
51   SET_SELF_SIZE(JSUDPWrap)
52 };
53 
JSUDPWrap(Environment* env, Local<Object> obj)54 JSUDPWrap::JSUDPWrap(Environment* env, Local<Object> obj)
55   : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) {
56   MakeWeak();
57 
58   obj->SetAlignedPointerInInternalField(
59       kUDPWrapBaseField, static_cast<UDPWrapBase*>(this));
60 }
61 
RecvStart()62 int JSUDPWrap::RecvStart() {
63   HandleScope scope(env()->isolate());
64   Context::Scope context_scope(env()->context());
65   TryCatchScope try_catch(env());
66   Local<Value> value;
67   int32_t value_int = JS_EXCEPTION_PENDING;
68   if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) ||
69       !value->Int32Value(env()->context()).To(&value_int)) {
70     if (try_catch.HasCaught() && !try_catch.HasTerminated())
71       errors::TriggerUncaughtException(env()->isolate(), try_catch);
72   }
73   return value_int;
74 }
75 
RecvStop()76 int JSUDPWrap::RecvStop() {
77   HandleScope scope(env()->isolate());
78   Context::Scope context_scope(env()->context());
79   TryCatchScope try_catch(env());
80   Local<Value> value;
81   int32_t value_int = JS_EXCEPTION_PENDING;
82   if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) ||
83       !value->Int32Value(env()->context()).To(&value_int)) {
84     if (try_catch.HasCaught() && !try_catch.HasTerminated())
85       errors::TriggerUncaughtException(env()->isolate(), try_catch);
86   }
87   return value_int;
88 }
89 
Send(uv_buf_t* bufs, size_t nbufs, const sockaddr* addr)90 ssize_t JSUDPWrap::Send(uv_buf_t* bufs,
91                         size_t nbufs,
92                         const sockaddr* addr) {
93   HandleScope scope(env()->isolate());
94   Context::Scope context_scope(env()->context());
95   TryCatchScope try_catch(env());
96   Local<Value> value;
97   int64_t value_int = JS_EXCEPTION_PENDING;
98   size_t total_len = 0;
99 
100   MaybeStackBuffer<Local<Value>, 16> buffers(nbufs);
101   for (size_t i = 0; i < nbufs; i++) {
102     buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len)
103         .ToLocalChecked();
104     total_len += bufs[i].len;
105   }
106 
107   Local<Object> address;
108   if (!AddressToJS(env(), addr).ToLocal(&address)) return value_int;
109 
110   Local<Value> args[] = {
111     listener()->CreateSendWrap(total_len)->object(),
112     Array::New(env()->isolate(), buffers.out(), nbufs),
113     address,
114   };
115 
116   if (!MakeCallback(env()->onwrite_string(), arraysize(args), args)
117           .ToLocal(&value) ||
118       !value->IntegerValue(env()->context()).To(&value_int)) {
119     if (try_catch.HasCaught() && !try_catch.HasTerminated())
120       errors::TriggerUncaughtException(env()->isolate(), try_catch);
121   }
122   return value_int;
123 }
124 
GetPeerName()125 SocketAddress JSUDPWrap::GetPeerName() {
126   SocketAddress ret;
127   CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret));
128   return ret;
129 }
130 
GetSockName()131 SocketAddress JSUDPWrap::GetSockName() {
132   SocketAddress ret;
133   CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret));
134   return ret;
135 }
136 
New(const FunctionCallbackInfo<Value>& args)137 void JSUDPWrap::New(const FunctionCallbackInfo<Value>& args) {
138   Environment* env = Environment::GetCurrent(args);
139   CHECK(args.IsConstructCall());
140   new JSUDPWrap(env, args.Holder());
141 }
142 
EmitReceived(const FunctionCallbackInfo<Value>& args)143 void JSUDPWrap::EmitReceived(const FunctionCallbackInfo<Value>& args) {
144   JSUDPWrap* wrap;
145   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
146   Environment* env = wrap->env();
147 
148   ArrayBufferViewContents<char> buffer(args[0]);
149   const char* data = buffer.data();
150   int len = buffer.length();
151 
152   CHECK(args[1]->IsInt32());   // family
153   CHECK(args[2]->IsString());  // address
154   CHECK(args[3]->IsInt32());   // port
155   CHECK(args[4]->IsInt32());   // flags
156   int family = args[1].As<Int32>()->Value() == 4 ? AF_INET : AF_INET6;
157   Utf8Value address(env->isolate(), args[2]);
158   int port = args[3].As<Int32>()->Value();
159   int flags = args[3].As<Int32>()->Value();
160 
161   sockaddr_storage addr;
162   CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0);
163 
164   // Repeatedly ask the stream's owner for memory, copy the data that we
165   // just read from JS into those buffers and emit them as reads.
166   while (len != 0) {
167     uv_buf_t buf = wrap->listener()->OnAlloc(len);
168     ssize_t avail = std::min<size_t>(buf.len, len);
169     memcpy(buf.base, data, avail);
170     data += avail;
171     len -= static_cast<int>(avail);
172     wrap->listener()->OnRecv(
173         avail, buf, reinterpret_cast<sockaddr*>(&addr), flags);
174   }
175 }
176 
OnSendDone(const FunctionCallbackInfo<Value>& args)177 void JSUDPWrap::OnSendDone(const FunctionCallbackInfo<Value>& args) {
178   JSUDPWrap* wrap;
179   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
180 
181   CHECK(args[0]->IsObject());
182   CHECK(args[1]->IsInt32());
183   ReqWrap<uv_udp_send_t>* req_wrap;
184   ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As<Object>());
185   int status = args[1].As<Int32>()->Value();
186 
187   wrap->listener()->OnSendDone(req_wrap, status);
188 }
189 
OnAfterBind(const FunctionCallbackInfo<Value>& args)190 void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo<Value>& args) {
191   JSUDPWrap* wrap;
192   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
193 
194   wrap->listener()->OnAfterBind();
195 }
196 
Initialize(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv)197 void JSUDPWrap::Initialize(Local<Object> target,
198                            Local<Value> unused,
199                            Local<Context> context,
200                            void* priv) {
201   Environment* env = Environment::GetCurrent(context);
202   Isolate* isolate = env->isolate();
203 
204   Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
205   t->InstanceTemplate()
206     ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1);
207   t->Inherit(AsyncWrap::GetConstructorTemplate(env));
208 
209   UDPWrapBase::AddMethods(env, t);
210   SetProtoMethod(isolate, t, "emitReceived", EmitReceived);
211   SetProtoMethod(isolate, t, "onSendDone", OnSendDone);
212   SetProtoMethod(isolate, t, "onAfterBind", OnAfterBind);
213 
214   SetConstructorFunction(context, target, "JSUDPWrap", t);
215 }
216 
217 
218 }  // namespace node
219 
220 NODE_BINDING_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize)
221