• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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