Update Iot.js
[platform/upstream/iotjs.git] / src / module / iotjs_module_udp.c
1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include "iotjs_def.h"
17
18 #include "iotjs_module_udp.h"
19
20 #include "iotjs_handlewrap.h"
21 #include "iotjs_module_buffer.h"
22 #include "iotjs_module_tcp.h"
23 #include "iotjs_reqwrap.h"
24
25
26 static void iotjs_udpwrap_destroy(iotjs_udpwrap_t* udpwrap);
27
28
29 iotjs_udpwrap_t* iotjs_udpwrap_create(const iotjs_jval_t* judp) {
30   iotjs_udpwrap_t* udpwrap = IOTJS_ALLOC(iotjs_udpwrap_t);
31   IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_udpwrap_t, udpwrap);
32
33   iotjs_handlewrap_initialize(&_this->handlewrap, judp,
34                               (uv_handle_t*)(&_this->handle),
35                               (JFreeHandlerType)iotjs_udpwrap_destroy);
36
37   const iotjs_environment_t* env = iotjs_environment_get();
38   uv_udp_init(iotjs_environment_loop(env), &_this->handle);
39
40   return udpwrap;
41 }
42
43
44 static void iotjs_udpwrap_destroy(iotjs_udpwrap_t* udpwrap) {
45   IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_udpwrap_t, udpwrap);
46   iotjs_handlewrap_destroy(&_this->handlewrap);
47   IOTJS_RELEASE(udpwrap);
48 }
49
50
51 iotjs_udpwrap_t* iotjs_udpwrap_from_handle(uv_udp_t* udp_handle) {
52   uv_handle_t* handle = (uv_handle_t*)(udp_handle);
53   iotjs_handlewrap_t* handlewrap = iotjs_handlewrap_from_handle(handle);
54   iotjs_udpwrap_t* udpwrap = (iotjs_udpwrap_t*)handlewrap;
55   IOTJS_ASSERT(iotjs_udpwrap_udp_handle(udpwrap) == udp_handle);
56   return udpwrap;
57 }
58
59
60 iotjs_udpwrap_t* iotjs_udpwrap_from_jobject(const iotjs_jval_t* judp) {
61   iotjs_handlewrap_t* handlewrap = iotjs_handlewrap_from_jobject(judp);
62   return (iotjs_udpwrap_t*)handlewrap;
63 }
64
65
66 uv_udp_t* iotjs_udpwrap_udp_handle(iotjs_udpwrap_t* udpwrap) {
67   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_udpwrap_t, udpwrap);
68   uv_handle_t* handle = iotjs_handlewrap_get_uv_handle(&_this->handlewrap);
69   return (uv_udp_t*)handle;
70 }
71
72
73 iotjs_jval_t* iotjs_udpwrap_jobject(iotjs_udpwrap_t* udpwrap) {
74   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_udpwrap_t, udpwrap);
75   return iotjs_handlewrap_jobject(&_this->handlewrap);
76 }
77
78
79 #define THIS iotjs_send_reqwrap_t* send_reqwrap
80
81 iotjs_send_reqwrap_t* iotjs_send_reqwrap_create(const iotjs_jval_t* jcallback,
82                                                 const size_t msg_size) {
83   iotjs_send_reqwrap_t* send_reqwrap = IOTJS_ALLOC(iotjs_send_reqwrap_t);
84   IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_send_reqwrap_t, send_reqwrap);
85
86   iotjs_reqwrap_initialize(&_this->reqwrap, jcallback, (uv_req_t*)&_this->req);
87   _this->msg_size = msg_size;
88
89   return send_reqwrap;
90 }
91
92
93 static void iotjs_send_reqwrap_destroy(THIS) {
94   IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_send_reqwrap_t, send_reqwrap);
95   iotjs_reqwrap_destroy(&_this->reqwrap);
96   IOTJS_RELEASE(send_reqwrap);
97 }
98
99
100 void iotjs_send_reqwrap_dispatched(THIS) {
101   IOTJS_VALIDATABLE_STRUCT_METHOD_VALIDATE(iotjs_send_reqwrap_t, send_reqwrap);
102   iotjs_send_reqwrap_destroy(send_reqwrap);
103 }
104
105
106 uv_udp_send_t* iotjs_send_reqwrap_req(THIS) {
107   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_send_reqwrap_t, send_reqwrap);
108   return &_this->req;
109 }
110
111
112 const iotjs_jval_t* iotjs_send_reqwrap_jcallback(THIS) {
113   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_send_reqwrap_t, send_reqwrap);
114   return iotjs_reqwrap_jcallback(&_this->reqwrap);
115 }
116
117
118 size_t iotjs_send_reqwrap_msg_size(THIS) {
119   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_send_reqwrap_t, send_reqwrap);
120   return _this->msg_size;
121 }
122
123 #undef THIS
124
125
126 JHANDLER_FUNCTION(UDP) {
127   JHANDLER_CHECK_THIS(object);
128   JHANDLER_CHECK_ARGS(0);
129
130   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
131   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_create(judp);
132   IOTJS_UNUSED(udp_wrap);
133 }
134
135
136 JHANDLER_FUNCTION(Bind) {
137   JHANDLER_CHECK_THIS(object);
138   JHANDLER_CHECK_ARGS_2(string, number);
139
140   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
141   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp);
142
143   iotjs_string_t address = JHANDLER_GET_ARG(0, string);
144   const int port = JHANDLER_GET_ARG(1, number);
145   const iotjs_jval_t* this_obj = JHANDLER_GET_THIS(object);
146   iotjs_jval_t reuse_addr =
147       iotjs_jval_get_property(this_obj, IOTJS_MAGIC_STRING__REUSEADDR);
148   IOTJS_ASSERT(iotjs_jval_is_boolean(&reuse_addr) ||
149                iotjs_jval_is_undefined(&reuse_addr));
150
151   int flags = false;
152   if (!iotjs_jval_is_undefined(&reuse_addr)) {
153     flags = iotjs_jval_as_boolean(&reuse_addr) ? UV_UDP_REUSEADDR : 0;
154   }
155
156   char addr[sizeof(sockaddr_in6)];
157   int err =
158       uv_ip4_addr(iotjs_string_data(&address), port, (sockaddr_in*)(&addr));
159
160   if (err == 0) {
161     err = uv_udp_bind(iotjs_udpwrap_udp_handle(udp_wrap),
162                       (const sockaddr*)(&addr), flags);
163   }
164
165   iotjs_jhandler_return_number(jhandler, err);
166
167   iotjs_jval_destroy(&reuse_addr);
168   iotjs_string_destroy(&address);
169 }
170
171
172 static void OnAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
173   if (suggested_size > IOTJS_MAX_READ_BUFFER_SIZE) {
174     suggested_size = IOTJS_MAX_READ_BUFFER_SIZE;
175   }
176
177   buf->base = iotjs_buffer_allocate(suggested_size);
178   buf->len = suggested_size;
179 }
180
181
182 static void OnRecv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
183                    const struct sockaddr* addr, unsigned int flags) {
184   if (nread == 0 && addr == NULL) {
185     if (buf->base != NULL)
186       iotjs_buffer_release(buf->base);
187     return;
188   }
189
190   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_handle(handle);
191
192   // udp handle
193   const iotjs_jval_t* judp = iotjs_udpwrap_jobject(udp_wrap);
194   IOTJS_ASSERT(iotjs_jval_is_object(judp));
195
196   // onmessage callback
197   iotjs_jval_t jonmessage =
198       iotjs_jval_get_property(judp, IOTJS_MAGIC_STRING_ONMESSAGE);
199   IOTJS_ASSERT(iotjs_jval_is_function(&jonmessage));
200
201   iotjs_jargs_t jargs = iotjs_jargs_create(4);
202   iotjs_jargs_append_number(&jargs, nread);
203   iotjs_jargs_append_jval(&jargs, judp);
204
205   if (nread < 0) {
206     if (buf->base != NULL)
207       iotjs_buffer_release(buf->base);
208     iotjs_make_callback(&jonmessage, iotjs_jval_get_undefined(), &jargs);
209     iotjs_jval_destroy(&jonmessage);
210     iotjs_jargs_destroy(&jargs);
211     return;
212   }
213
214   iotjs_jval_t jbuffer = iotjs_bufferwrap_create_buffer(nread);
215   iotjs_bufferwrap_t* buffer_wrap = iotjs_bufferwrap_from_jbuffer(&jbuffer);
216
217   iotjs_bufferwrap_copy(buffer_wrap, buf->base, nread);
218
219   iotjs_jargs_append_jval(&jargs, &jbuffer);
220
221   iotjs_jval_t rinfo = iotjs_jval_create_object();
222   AddressToJS(&rinfo, addr);
223   iotjs_jargs_append_jval(&jargs, &rinfo);
224
225   iotjs_make_callback(&jonmessage, iotjs_jval_get_undefined(), &jargs);
226
227   iotjs_jval_destroy(&rinfo);
228   iotjs_jval_destroy(&jbuffer);
229   iotjs_jval_destroy(&jonmessage);
230   iotjs_buffer_release(buf->base);
231   iotjs_jargs_destroy(&jargs);
232 }
233
234
235 JHANDLER_FUNCTION(RecvStart) {
236   JHANDLER_CHECK_THIS(object);
237   JHANDLER_CHECK_ARGS(0);
238
239   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
240   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp);
241
242   int err =
243       uv_udp_recv_start(iotjs_udpwrap_udp_handle(udp_wrap), OnAlloc, OnRecv);
244
245   // UV_EALREADY means that the socket is already bound but that's okay
246   if (err == UV_EALREADY)
247     err = 0;
248
249   iotjs_jhandler_return_number(jhandler, err);
250 }
251
252
253 JHANDLER_FUNCTION(RecvStop) {
254   JHANDLER_CHECK_THIS(object);
255   JHANDLER_CHECK_ARGS(0);
256
257   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
258   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp);
259
260   int r = uv_udp_recv_stop(iotjs_udpwrap_udp_handle(udp_wrap));
261
262   iotjs_jhandler_return_number(jhandler, r);
263 }
264
265
266 static void OnSend(uv_udp_send_t* req, int status) {
267   iotjs_send_reqwrap_t* req_wrap = (iotjs_send_reqwrap_t*)(req->data);
268   IOTJS_ASSERT(req_wrap != NULL);
269
270   // Take callback function object.
271   const iotjs_jval_t* jcallback = iotjs_send_reqwrap_jcallback(req_wrap);
272
273   if (iotjs_jval_is_function(jcallback)) {
274     // Take callback function object.
275
276     iotjs_jargs_t jargs = iotjs_jargs_create(2);
277     iotjs_jargs_append_number(&jargs, status);
278     iotjs_jargs_append_number(&jargs, iotjs_send_reqwrap_msg_size(req_wrap));
279
280     iotjs_make_callback(jcallback, iotjs_jval_get_undefined(), &jargs);
281     iotjs_jargs_destroy(&jargs);
282   }
283
284   iotjs_send_reqwrap_dispatched(req_wrap);
285 }
286
287
288 // Send messages using the socket.
289 // [0] buffer
290 // [1] port
291 // [2] ip
292 // [3] callback function
293 JHANDLER_FUNCTION(Send) {
294   JHANDLER_CHECK_THIS(object);
295   JHANDLER_CHECK_ARGS_3(object, number, string);
296   IOTJS_ASSERT(iotjs_jval_is_function(iotjs_jhandler_get_arg(jhandler, 3)) ||
297                iotjs_jval_is_undefined(iotjs_jhandler_get_arg(jhandler, 3)));
298
299   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
300   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp);
301
302   const iotjs_jval_t* jbuffer = JHANDLER_GET_ARG(0, object);
303   const unsigned short port = JHANDLER_GET_ARG(1, number);
304   iotjs_string_t address = JHANDLER_GET_ARG(2, string);
305   const iotjs_jval_t* jcallback = JHANDLER_GET_ARG(3, object);
306
307   iotjs_bufferwrap_t* buffer_wrap = iotjs_bufferwrap_from_jbuffer(jbuffer);
308   char* buffer = iotjs_bufferwrap_buffer(buffer_wrap);
309   int len = iotjs_bufferwrap_length(buffer_wrap);
310
311   iotjs_send_reqwrap_t* req_wrap = iotjs_send_reqwrap_create(jcallback, len);
312
313   uv_buf_t buf;
314   buf.base = buffer;
315   buf.len = len;
316
317   char addr[sizeof(sockaddr_in6)];
318   int err =
319       uv_ip4_addr(iotjs_string_data(&address), port, (sockaddr_in*)(&addr));
320
321   if (err == 0) {
322     err = uv_udp_send(iotjs_send_reqwrap_req(req_wrap),
323                       iotjs_udpwrap_udp_handle(udp_wrap), &buf, 1,
324                       (const sockaddr*)(&addr), OnSend);
325   }
326
327   if (err) {
328     iotjs_send_reqwrap_dispatched(req_wrap);
329   }
330
331   iotjs_jhandler_return_number(jhandler, err);
332
333   iotjs_string_destroy(&address);
334 }
335
336
337 // Close socket
338 JHANDLER_FUNCTION(Close) {
339   JHANDLER_CHECK_THIS(object);
340   JHANDLER_CHECK_ARGS(0);
341
342   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
343   iotjs_handlewrap_t* wrap = iotjs_handlewrap_from_jobject(judp);
344
345   iotjs_handlewrap_close(wrap, NULL);
346 }
347
348
349 GetSockNameFunction(udpwrap, udp_handle, uv_udp_getsockname);
350
351
352 JHANDLER_FUNCTION(GetSockeName) {
353   DoGetSockName(jhandler);
354 }
355
356
357 #define IOTJS_UV_SET_SOCKOPT(fn)                                \
358   JHANDLER_CHECK_THIS(object);                                  \
359   JHANDLER_CHECK_ARGS_1(number);                                \
360                                                                 \
361   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);         \
362   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp); \
363                                                                 \
364   int flag = JHANDLER_GET_ARG(0, number);                       \
365   int err = fn(iotjs_udpwrap_udp_handle(udp_wrap), flag);       \
366                                                                 \
367   iotjs_jhandler_return_number(jhandler, err);
368
369
370 JHANDLER_FUNCTION(SetBroadcast) {
371 #if !defined(__NUTTX__) && !defined(__TIZENRT__)
372   IOTJS_UV_SET_SOCKOPT(uv_udp_set_broadcast);
373 #else
374   IOTJS_ASSERT(!"Not implemented");
375
376   iotjs_jhandler_return_null(jhandler);
377 #endif
378 }
379
380
381 JHANDLER_FUNCTION(SetTTL) {
382 #if !defined(__NUTTX__) && !defined(__TIZENRT__)
383   IOTJS_UV_SET_SOCKOPT(uv_udp_set_ttl);
384 #else
385   IOTJS_ASSERT(!"Not implemented");
386
387   iotjs_jhandler_return_null(jhandler);
388 #endif
389 }
390
391
392 JHANDLER_FUNCTION(SetMulticastTTL) {
393 #if !defined(__NUTTX__) && !defined(__TIZENRT__)
394   IOTJS_UV_SET_SOCKOPT(uv_udp_set_multicast_ttl);
395 #else
396   IOTJS_ASSERT(!"Not implemented");
397
398   iotjs_jhandler_return_null(jhandler);
399 #endif
400 }
401
402
403 JHANDLER_FUNCTION(SetMulticastLoopback) {
404 #if !defined(__NUTTX__) && !defined(__TIZENRT__)
405   IOTJS_UV_SET_SOCKOPT(uv_udp_set_multicast_loop);
406 #else
407   IOTJS_ASSERT(!"Not implemented");
408
409   iotjs_jhandler_return_null(jhandler);
410 #endif
411 }
412
413 #undef IOTJS_UV_SET_SOCKOPT
414
415
416 void SetMembership(iotjs_jhandler_t* jhandler, uv_membership membership) {
417 #if !defined(__NUTTX__) && !defined(__TIZENRT__)
418   JHANDLER_CHECK_THIS(object);
419   JHANDLER_CHECK_ARGS_1(string);
420
421   const iotjs_jval_t* judp = JHANDLER_GET_THIS(object);
422   iotjs_udpwrap_t* udp_wrap = iotjs_udpwrap_from_jobject(judp);
423
424   iotjs_string_t address = JHANDLER_GET_ARG(0, string);
425   const iotjs_jval_t* arg1 = iotjs_jhandler_get_arg(jhandler, 1);
426   bool isUndefinedOrNull =
427       iotjs_jval_is_undefined(arg1) || iotjs_jval_is_null(arg1);
428   iotjs_string_t iface;
429
430   const char* iface_cstr;
431   if (isUndefinedOrNull) {
432     iface_cstr = NULL;
433   } else {
434     iface = iotjs_jval_as_string(arg1);
435     iface_cstr = iotjs_string_data(&iface);
436   }
437
438   int err = uv_udp_set_membership(iotjs_udpwrap_udp_handle(udp_wrap),
439                                   iotjs_string_data(&address), iface_cstr,
440                                   membership);
441
442   iotjs_jhandler_return_number(jhandler, err);
443
444   iotjs_string_destroy(&address);
445   if (!isUndefinedOrNull)
446     iotjs_string_destroy(&iface);
447 #else
448   IOTJS_ASSERT(!"Not implemented");
449
450   iotjs_jhandler_return_null(jhandler);
451 #endif
452 }
453
454
455 JHANDLER_FUNCTION(AddMembership) {
456   SetMembership(jhandler, UV_JOIN_GROUP);
457 }
458
459
460 JHANDLER_FUNCTION(DropMembership) {
461   SetMembership(jhandler, UV_LEAVE_GROUP);
462 }
463
464
465 JHANDLER_FUNCTION(Ref) {
466   IOTJS_ASSERT(!"Not implemented");
467
468   iotjs_jhandler_return_null(jhandler);
469 }
470
471
472 JHANDLER_FUNCTION(Unref) {
473   IOTJS_ASSERT(!"Not implemented");
474
475   iotjs_jhandler_return_null(jhandler);
476 }
477
478
479 iotjs_jval_t InitUdp() {
480   iotjs_jval_t udp = iotjs_jval_create_function_with_dispatch(UDP);
481
482   iotjs_jval_t prototype = iotjs_jval_create_object();
483   iotjs_jval_set_property_jval(&udp, IOTJS_MAGIC_STRING_PROTOTYPE, &prototype);
484
485   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_BIND, Bind);
486   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_RECVSTART, RecvStart);
487   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_RECVSTOP, RecvStop);
488   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_SEND, Send);
489   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_CLOSE, Close);
490   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_GETSOCKNAME,
491                         GetSockeName);
492   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_SETBROADCAST,
493                         SetBroadcast);
494   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_SETTTL, SetTTL);
495   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_SETMULTICASTTTL,
496                         SetMulticastTTL);
497   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_SETMULTICASTLOOPBACK,
498                         SetMulticastLoopback);
499   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_ADDMEMBERSHIP,
500                         AddMembership);
501   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_DROPMEMBERSHIP,
502                         DropMembership);
503   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_REF, Ref);
504   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_UNREF, Unref);
505
506   iotjs_jval_destroy(&prototype);
507
508   return udp;
509 }