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 "tcp_wrap.h"
23
24 #include "connection_wrap.h"
25 #include "env-inl.h"
26 #include "handle_wrap.h"
27 #include "node_buffer.h"
28 #include "node_internals.h"
29 #include "connect_wrap.h"
30 #include "stream_base-inl.h"
31 #include "stream_wrap.h"
32 #include "util-inl.h"
33
34 #include <cstdlib>
35
36
37 namespace node {
38
39 using v8::Boolean;
40 using v8::Context;
41 using v8::EscapableHandleScope;
42 using v8::Function;
43 using v8::FunctionCallbackInfo;
44 using v8::FunctionTemplate;
45 using v8::Int32;
46 using v8::Integer;
47 using v8::Local;
48 using v8::MaybeLocal;
49 using v8::Object;
50 using v8::String;
51 using v8::Uint32;
52 using v8::Value;
53
Instantiate(Environment * env,AsyncWrap * parent,TCPWrap::SocketType type)54 MaybeLocal<Object> TCPWrap::Instantiate(Environment* env,
55 AsyncWrap* parent,
56 TCPWrap::SocketType type) {
57 EscapableHandleScope handle_scope(env->isolate());
58 AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(parent);
59 CHECK_EQ(env->tcp_constructor_template().IsEmpty(), false);
60 Local<Function> constructor = env->tcp_constructor_template()
61 ->GetFunction(env->context())
62 .ToLocalChecked();
63 CHECK_EQ(constructor.IsEmpty(), false);
64 Local<Value> type_value = Int32::New(env->isolate(), type);
65 return handle_scope.EscapeMaybe(
66 constructor->NewInstance(env->context(), 1, &type_value));
67 }
68
69
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)70 void TCPWrap::Initialize(Local<Object> target,
71 Local<Value> unused,
72 Local<Context> context,
73 void* priv) {
74 Environment* env = Environment::GetCurrent(context);
75
76 Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
77 Local<String> tcpString = FIXED_ONE_BYTE_STRING(env->isolate(), "TCP");
78 t->SetClassName(tcpString);
79 t->InstanceTemplate()->SetInternalFieldCount(StreamBase::kInternalFieldCount);
80
81 // Init properties
82 t->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "reading"),
83 Boolean::New(env->isolate(), false));
84 t->InstanceTemplate()->Set(env->owner_symbol(), Null(env->isolate()));
85 t->InstanceTemplate()->Set(env->onconnection_string(), Null(env->isolate()));
86
87 t->Inherit(LibuvStreamWrap::GetConstructorTemplate(env));
88
89 env->SetProtoMethod(t, "open", Open);
90 env->SetProtoMethod(t, "bind", Bind);
91 env->SetProtoMethod(t, "listen", Listen);
92 env->SetProtoMethod(t, "connect", Connect);
93 env->SetProtoMethod(t, "bind6", Bind6);
94 env->SetProtoMethod(t, "connect6", Connect6);
95 env->SetProtoMethod(t, "getsockname",
96 GetSockOrPeerName<TCPWrap, uv_tcp_getsockname>);
97 env->SetProtoMethod(t, "getpeername",
98 GetSockOrPeerName<TCPWrap, uv_tcp_getpeername>);
99 env->SetProtoMethod(t, "setNoDelay", SetNoDelay);
100 env->SetProtoMethod(t, "setKeepAlive", SetKeepAlive);
101
102 #ifdef _WIN32
103 env->SetProtoMethod(t, "setSimultaneousAccepts", SetSimultaneousAccepts);
104 #endif
105
106 target->Set(env->context(),
107 tcpString,
108 t->GetFunction(env->context()).ToLocalChecked()).Check();
109 env->set_tcp_constructor_template(t);
110
111 // Create FunctionTemplate for TCPConnectWrap.
112 Local<FunctionTemplate> cwt =
113 BaseObject::MakeLazilyInitializedJSTemplate(env);
114 cwt->Inherit(AsyncWrap::GetConstructorTemplate(env));
115 Local<String> wrapString =
116 FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap");
117 cwt->SetClassName(wrapString);
118 target->Set(env->context(),
119 wrapString,
120 cwt->GetFunction(env->context()).ToLocalChecked()).Check();
121
122 // Define constants
123 Local<Object> constants = Object::New(env->isolate());
124 NODE_DEFINE_CONSTANT(constants, SOCKET);
125 NODE_DEFINE_CONSTANT(constants, SERVER);
126 NODE_DEFINE_CONSTANT(constants, UV_TCP_IPV6ONLY);
127 target->Set(context,
128 env->constants_string(),
129 constants).Check();
130 }
131
132
New(const FunctionCallbackInfo<Value> & args)133 void TCPWrap::New(const FunctionCallbackInfo<Value>& args) {
134 // This constructor should not be exposed to public javascript.
135 // Therefore we assert that we are not trying to call this as a
136 // normal function.
137 CHECK(args.IsConstructCall());
138 CHECK(args[0]->IsInt32());
139 Environment* env = Environment::GetCurrent(args);
140
141 int type_value = args[0].As<Int32>()->Value();
142 TCPWrap::SocketType type = static_cast<TCPWrap::SocketType>(type_value);
143
144 ProviderType provider;
145 switch (type) {
146 case SOCKET:
147 provider = PROVIDER_TCPWRAP;
148 break;
149 case SERVER:
150 provider = PROVIDER_TCPSERVERWRAP;
151 break;
152 default:
153 UNREACHABLE();
154 }
155
156 new TCPWrap(env, args.This(), provider);
157 }
158
159
TCPWrap(Environment * env,Local<Object> object,ProviderType provider)160 TCPWrap::TCPWrap(Environment* env, Local<Object> object, ProviderType provider)
161 : ConnectionWrap(env, object, provider) {
162 int r = uv_tcp_init(env->event_loop(), &handle_);
163 CHECK_EQ(r, 0); // How do we proxy this error up to javascript?
164 // Suggestion: uv_tcp_init() returns void.
165 }
166
167
SetNoDelay(const FunctionCallbackInfo<Value> & args)168 void TCPWrap::SetNoDelay(const FunctionCallbackInfo<Value>& args) {
169 TCPWrap* wrap;
170 ASSIGN_OR_RETURN_UNWRAP(&wrap,
171 args.Holder(),
172 args.GetReturnValue().Set(UV_EBADF));
173 int enable = static_cast<int>(args[0]->IsTrue());
174 int err = uv_tcp_nodelay(&wrap->handle_, enable);
175 args.GetReturnValue().Set(err);
176 }
177
178
SetKeepAlive(const FunctionCallbackInfo<Value> & args)179 void TCPWrap::SetKeepAlive(const FunctionCallbackInfo<Value>& args) {
180 TCPWrap* wrap;
181 ASSIGN_OR_RETURN_UNWRAP(&wrap,
182 args.Holder(),
183 args.GetReturnValue().Set(UV_EBADF));
184 Environment* env = wrap->env();
185 int enable;
186 if (!args[0]->Int32Value(env->context()).To(&enable)) return;
187 unsigned int delay = static_cast<unsigned int>(args[1].As<Uint32>()->Value());
188 int err = uv_tcp_keepalive(&wrap->handle_, enable, delay);
189 args.GetReturnValue().Set(err);
190 }
191
192
193 #ifdef _WIN32
SetSimultaneousAccepts(const FunctionCallbackInfo<Value> & args)194 void TCPWrap::SetSimultaneousAccepts(const FunctionCallbackInfo<Value>& args) {
195 TCPWrap* wrap;
196 ASSIGN_OR_RETURN_UNWRAP(&wrap,
197 args.Holder(),
198 args.GetReturnValue().Set(UV_EBADF));
199 bool enable = args[0]->IsTrue();
200 int err = uv_tcp_simultaneous_accepts(&wrap->handle_, enable);
201 args.GetReturnValue().Set(err);
202 }
203 #endif
204
205
Open(const FunctionCallbackInfo<Value> & args)206 void TCPWrap::Open(const FunctionCallbackInfo<Value>& args) {
207 TCPWrap* wrap;
208 ASSIGN_OR_RETURN_UNWRAP(&wrap,
209 args.Holder(),
210 args.GetReturnValue().Set(UV_EBADF));
211 int64_t val;
212 if (!args[0]->IntegerValue(args.GetIsolate()->GetCurrentContext()).To(&val))
213 return;
214 int fd = static_cast<int>(val);
215 int err = uv_tcp_open(&wrap->handle_, fd);
216
217 if (err == 0)
218 wrap->set_fd(fd);
219
220 args.GetReturnValue().Set(err);
221 }
222
223 template <typename T>
Bind(const FunctionCallbackInfo<Value> & args,int family,std::function<int (const char * ip_address,int port,T * addr)> uv_ip_addr)224 void TCPWrap::Bind(
225 const FunctionCallbackInfo<Value>& args,
226 int family,
227 std::function<int(const char* ip_address, int port, T* addr)> uv_ip_addr) {
228 TCPWrap* wrap;
229 ASSIGN_OR_RETURN_UNWRAP(&wrap,
230 args.Holder(),
231 args.GetReturnValue().Set(UV_EBADF));
232 Environment* env = wrap->env();
233 node::Utf8Value ip_address(env->isolate(), args[0]);
234 int port;
235 unsigned int flags = 0;
236 if (!args[1]->Int32Value(env->context()).To(&port)) return;
237 if (family == AF_INET6 &&
238 !args[2]->Uint32Value(env->context()).To(&flags)) {
239 return;
240 }
241
242 T addr;
243 int err = uv_ip_addr(*ip_address, port, &addr);
244
245 if (err == 0) {
246 err = uv_tcp_bind(&wrap->handle_,
247 reinterpret_cast<const sockaddr*>(&addr),
248 flags);
249 }
250 args.GetReturnValue().Set(err);
251 }
252
Bind(const FunctionCallbackInfo<Value> & args)253 void TCPWrap::Bind(const FunctionCallbackInfo<Value>& args) {
254 Bind<sockaddr_in>(args, AF_INET, uv_ip4_addr);
255 }
256
257
Bind6(const FunctionCallbackInfo<Value> & args)258 void TCPWrap::Bind6(const FunctionCallbackInfo<Value>& args) {
259 Bind<sockaddr_in6>(args, AF_INET6, uv_ip6_addr);
260 }
261
262
Listen(const FunctionCallbackInfo<Value> & args)263 void TCPWrap::Listen(const FunctionCallbackInfo<Value>& args) {
264 TCPWrap* wrap;
265 ASSIGN_OR_RETURN_UNWRAP(&wrap,
266 args.Holder(),
267 args.GetReturnValue().Set(UV_EBADF));
268 Environment* env = wrap->env();
269 int backlog;
270 if (!args[0]->Int32Value(env->context()).To(&backlog)) return;
271 int err = uv_listen(reinterpret_cast<uv_stream_t*>(&wrap->handle_),
272 backlog,
273 OnConnection);
274 args.GetReturnValue().Set(err);
275 }
276
277
Connect(const FunctionCallbackInfo<Value> & args)278 void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) {
279 CHECK(args[2]->IsUint32());
280 // explicit cast to fit to libuv's type expectation
281 int port = static_cast<int>(args[2].As<Uint32>()->Value());
282 Connect<sockaddr_in>(args,
283 [port](const char* ip_address, sockaddr_in* addr) {
284 return uv_ip4_addr(ip_address, port, addr);
285 });
286 }
287
288
Connect6(const FunctionCallbackInfo<Value> & args)289 void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) {
290 Environment* env = Environment::GetCurrent(args);
291 CHECK(args[2]->IsUint32());
292 int port;
293 if (!args[2]->Int32Value(env->context()).To(&port)) return;
294 Connect<sockaddr_in6>(args,
295 [port](const char* ip_address, sockaddr_in6* addr) {
296 return uv_ip6_addr(ip_address, port, addr);
297 });
298 }
299
300 template <typename T>
Connect(const FunctionCallbackInfo<Value> & args,std::function<int (const char * ip_address,T * addr)> uv_ip_addr)301 void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args,
302 std::function<int(const char* ip_address, T* addr)> uv_ip_addr) {
303 Environment* env = Environment::GetCurrent(args);
304
305 TCPWrap* wrap;
306 ASSIGN_OR_RETURN_UNWRAP(&wrap,
307 args.Holder(),
308 args.GetReturnValue().Set(UV_EBADF));
309
310 CHECK(args[0]->IsObject());
311 CHECK(args[1]->IsString());
312
313 Local<Object> req_wrap_obj = args[0].As<Object>();
314 node::Utf8Value ip_address(env->isolate(), args[1]);
315
316 T addr;
317 int err = uv_ip_addr(*ip_address, &addr);
318
319 if (err == 0) {
320 AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(wrap);
321 ConnectWrap* req_wrap =
322 new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP);
323 err = req_wrap->Dispatch(uv_tcp_connect,
324 &wrap->handle_,
325 reinterpret_cast<const sockaddr*>(&addr),
326 AfterConnect);
327 if (err)
328 delete req_wrap;
329 }
330
331 args.GetReturnValue().Set(err);
332 }
333
334
335 // also used by udp_wrap.cc
AddressToJS(Environment * env,const sockaddr * addr,Local<Object> info)336 Local<Object> AddressToJS(Environment* env,
337 const sockaddr* addr,
338 Local<Object> info) {
339 EscapableHandleScope scope(env->isolate());
340 char ip[INET6_ADDRSTRLEN + UV_IF_NAMESIZE];
341 const sockaddr_in* a4;
342 const sockaddr_in6* a6;
343
344 int port;
345
346 if (info.IsEmpty())
347 info = Object::New(env->isolate());
348
349 switch (addr->sa_family) {
350 case AF_INET6:
351 a6 = reinterpret_cast<const sockaddr_in6*>(addr);
352 uv_inet_ntop(AF_INET6, &a6->sin6_addr, ip, sizeof ip);
353 // Add an interface identifier to a link local address.
354 if (IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr)) {
355 const size_t addrlen = strlen(ip);
356 CHECK_LT(addrlen, sizeof(ip));
357 ip[addrlen] = '%';
358 size_t scopeidlen = sizeof(ip) - addrlen - 1;
359 CHECK_GE(scopeidlen, UV_IF_NAMESIZE);
360 const int r = uv_if_indextoiid(a6->sin6_scope_id,
361 ip + addrlen + 1,
362 &scopeidlen);
363 CHECK_EQ(r, 0);
364 }
365 port = ntohs(a6->sin6_port);
366 info->Set(env->context(),
367 env->address_string(),
368 OneByteString(env->isolate(), ip)).Check();
369 info->Set(env->context(),
370 env->family_string(),
371 env->ipv6_string()).Check();
372 info->Set(env->context(),
373 env->port_string(),
374 Integer::New(env->isolate(), port)).Check();
375 break;
376
377 case AF_INET:
378 a4 = reinterpret_cast<const sockaddr_in*>(addr);
379 uv_inet_ntop(AF_INET, &a4->sin_addr, ip, sizeof ip);
380 port = ntohs(a4->sin_port);
381 info->Set(env->context(),
382 env->address_string(),
383 OneByteString(env->isolate(), ip)).Check();
384 info->Set(env->context(),
385 env->family_string(),
386 env->ipv4_string()).Check();
387 info->Set(env->context(),
388 env->port_string(),
389 Integer::New(env->isolate(), port)).Check();
390 break;
391
392 default:
393 info->Set(env->context(),
394 env->address_string(),
395 String::Empty(env->isolate())).Check();
396 }
397
398 return scope.Escape(info);
399 }
400
401
402 } // namespace node
403
404 NODE_MODULE_CONTEXT_AWARE_INTERNAL(tcp_wrap, node::TCPWrap::Initialize)
405