1 /*
2 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3 */
4
5 #include "amp_platform.h"
6 #include "amp_task.h"
7 #include "amp_utils.h"
8 #include "aos_system.h"
9 #include "aos_udp.h"
10 #include "be_inl.h"
11 #include "board_config.h"
12 #include "py_defines.h"
13
14 #ifndef INET_ADDRSTRLEN
15 #define INET_ADDRSTRLEN 16
16 #endif
17
18 #define MOD_STR "UDP"
19 #define MAX_UDP_RECV_LEN 256
20 #define MAX_UDP_RECV_TIMEOUT 200
21
22 typedef struct {
23 char ip[INET_ADDRSTRLEN];
24 int port;
25 char *msg;
26 } udp_options_t;
27
28 typedef struct {
29 int sock_id;
30 char *msg;
31 int msg_len;
32 udp_options_t options;
33 int js_cb_ref;
34 } udp_send_param_t;
35
36 typedef struct {
37 int ret;
38 int js_cb_ref;
39 } udp_send_notify_param_t;
40
41 typedef struct {
42 int sock_id;
43 udp_options_t options;
44 int js_cb_ref;
45 } udp_recv_param_t;
46
47 typedef struct {
48 char buf[MAX_UDP_RECV_LEN];
49 int recv_len;
50 char src_ip[INET_ADDRSTRLEN];
51 unsigned short src_port;
52 int js_cb_ref;
53 } udp_recv_notify_param_t;
54
55 static char g_udp_close_flag = 0;
56 static char g_udp_recv_flag = 0;
57 static aos_sem_t g_udp_close_sem = NULL;
58
59 /*************************************************************************************
60 * Function: native_udp_create_socket
61 * Description: js native addon for UDP.createSocket();
62 * Called by: js api
63 * Input: none
64 * Output: return socket fd when create socket success,
65 * return error number when create socket fail
66 **************************************************************************************/
native_udp_create_socket(duk_context * ctx)67 static duk_ret_t native_udp_create_socket(duk_context *ctx)
68 {
69 int sock_id = 0;
70
71 sock_id = aos_udp_socket_create();
72 if (sock_id < 0) {
73 amp_warn(MOD_STR, "create socket error!");
74 goto out;
75 }
76 amp_debug(MOD_STR, "sock_id = %d", sock_id);
77
78 duk_push_int(ctx, sock_id);
79
80 return 1;
81
82 out:
83 duk_push_string(ctx, "create socket error!");
84 return duk_throw(ctx);
85 }
86
87 /*************************************************************************************
88 * Function: native_udp_bind
89 * Description: js native addon for UDP.bind(sock_id,"ip",port)
90 * Called by: js api
91 * Input: UDP.bind(sock_id,"ip",port):
92 * sock_id: is a iterger
93 * ip: is a ip stirng like "192.168.1.1"
94 * port: is a iterger port
95 * Output: return 0 when bind successed
96 * return error number when bind fail
97 **************************************************************************************/
native_udp_bind(duk_context * ctx)98 static duk_ret_t native_udp_bind(duk_context *ctx)
99 {
100 int ret = -1;
101 int port = 0;
102 int sock_id;
103
104 if (!duk_is_number(ctx, 0) || !duk_is_number(ctx, 1)) {
105 amp_warn(MOD_STR, "parameter must be (number, number)");
106 goto out;
107 }
108
109 sock_id = duk_get_int(ctx, 0);
110 if (sock_id < 0) {
111 amp_error(MOD_STR, "socket id[%d] error", sock_id);
112 goto out;
113 }
114
115 port = duk_get_int(ctx, 1);
116 if (port < 0) {
117 amp_error(MOD_STR, "port[%d] error", port);
118 goto out;
119 }
120
121 amp_debug(MOD_STR, "udp bind socket id=%d, port=%d", sock_id, port);
122 ret = aos_udp_socket_bind(sock_id, port);
123 if (ret < 0) {
124 amp_error(MOD_STR, "udp bind error");
125 goto out;
126 }
127
128 // return ret;
129 out:
130 duk_push_int(ctx, ret);
131 return 1;
132 }
133
udp_send_notify(void * pdata)134 static void udp_send_notify(void *pdata)
135 {
136 udp_send_notify_param_t *p = (udp_send_notify_param_t *)pdata;
137 duk_context *ctx = be_get_context();
138 be_push_ref(ctx, p->js_cb_ref);
139 duk_push_int(ctx, p->ret);
140 if (duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS) {
141 amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
142 }
143 duk_pop(ctx);
144 be_unref(ctx, p->js_cb_ref);
145 aos_free(p);
146 duk_gc(ctx, 0);
147 }
148
149 /*************************************************************************************
150 * Function: udp_send_routin
151 * Description: create a task for blocking sendto call
152 * Called by:
153 **************************************************************************************/
udp_send_routine(udp_send_param_t * send_param)154 static int udp_send_routine(udp_send_param_t *send_param)
155 {
156 int ret = -1;
157 int sock_id;
158 udp_options_t udp_options;
159 udp_send_notify_param_t *p;
160 duk_context *ctx = be_get_context();
161 sock_id = send_param->sock_id;
162 memcpy(&udp_options, &(send_param->options), sizeof(udp_options_t));
163
164 ret = aos_udp_sendto(sock_id, &udp_options, send_param->msg,
165 send_param->msg_len, 0);
166 p = aos_calloc(1, sizeof(udp_send_notify_param_t));
167 if (!p) {
168 amp_warn(MOD_STR, "allocate memory failed");
169 be_unref(ctx, send_param->js_cb_ref);
170 goto out;
171 }
172
173 p->js_cb_ref = send_param->js_cb_ref;
174 p->ret = ret;
175 py_task_schedule_call(udp_send_notify, p);
176 ret = 0;
177
178 out:
179 aos_free(send_param->msg);
180 aos_free(send_param);
181 return ret;
182 }
183
184 /*************************************************************************************
185 * Function: native_udp_sendto
186 * Description: js native addon for
187 *UDP.send(sock_id,option,buffer_array,function(ret){}) Called by: js api
188 * Input: sock_id: interger
189 * options: is a object include options.ip and options.port
190 * buffer_array: is a array which include message to send
191 * function(ret): is the callback function which has a ret input
192 *param Output: return send msg length when send success return error
193 *number when send fail
194 **************************************************************************************/
195
native_udp_sendto(duk_context * ctx)196 static duk_ret_t native_udp_sendto(duk_context *ctx)
197 {
198 int ret = -1;
199 udp_options_t options;
200 int sock_id;
201 int i;
202 int msg_len;
203 char *msg;
204
205 if (!duk_is_number(ctx, 0) || !duk_is_object(ctx, 1) ||
206 !duk_is_function(ctx, 2)) {
207 amp_warn(MOD_STR,
208 "parameter must be (number, object, array, function)");
209 goto out;
210 }
211
212 sock_id = duk_get_int(ctx, 0);
213 if (sock_id <= 0) {
214 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
215 goto out;
216 }
217 memset(&options, 0, sizeof(udp_options_t));
218 /* options.port */
219 duk_get_prop_string(ctx, 1, "port");
220 if (!duk_is_number(ctx, -1)) {
221 amp_warn(MOD_STR, "port not specify");
222 duk_pop(ctx);
223 goto out;
224 }
225 options.port = duk_get_int(ctx, -1);
226 duk_pop(ctx);
227
228 /* options.address */
229 duk_get_prop_string(ctx, 1, "address");
230 if (!duk_is_string(ctx, -1)) {
231 amp_warn(MOD_STR, "ip not specify");
232 duk_pop(ctx);
233 goto out;
234 }
235 strncpy(options.ip, duk_get_string(ctx, -1), sizeof(options.ip) - 1);
236 duk_pop(ctx);
237
238 /* options.message */
239 duk_get_prop_string(ctx, 1, "message");
240 if (!duk_is_array(ctx, -1)) {
241 amp_warn(MOD_STR, "message not specify");
242 duk_pop(ctx);
243 goto out;
244 }
245
246 msg_len = duk_get_length(ctx, -1);
247 msg = (char *)aos_calloc(1, msg_len + 1);
248 if (!msg) {
249 amp_warn(MOD_STR, "allocate memory failed");
250 goto out;
251 }
252 for (i = 0; i < msg_len; i++) {
253 duk_get_prop_index(ctx, -1, i);
254 msg[i] = duk_get_int(ctx, -1);
255 duk_pop(ctx);
256 }
257 msg[msg_len] = 0;
258
259 // options.msg = (char *)duk_get_string(ctx, -1);
260 // msg_len = strlen(options.msg);
261 // duk_pop(ctx);
262
263 // msg = (char *)aos_calloc(1, msg_len + 1);
264 // if (!msg) {
265 // amp_warn(MOD_STR, "allocate memory failed");
266 // goto out;
267 // }
268 // strncpy(msg, options.msg, msg_len);
269 amp_debug(MOD_STR, "msg is:%s, length is: %d", msg, msg_len);
270 udp_send_param_t *send_param =
271 (udp_send_param_t *)aos_malloc(sizeof(*send_param));
272 if (!send_param) {
273 amp_error(MOD_STR, "allocate memory failed");
274 aos_free(msg);
275 goto out;
276 }
277 send_param->sock_id = sock_id;
278 duk_dup(ctx, 2);
279 send_param->js_cb_ref = be_ref(ctx);
280 send_param->msg = msg;
281 send_param->msg_len = msg_len;
282 memcpy(&(send_param->options), &options, sizeof(udp_options_t));
283
284 amp_debug(MOD_STR, "sockid:%d ip:%s port:%d msg:%s msg_len:%d", sock_id,
285 options.ip, options.port, msg, msg_len);
286
287 udp_send_routine(send_param);
288
289 out:
290 duk_push_int(ctx, ret);
291 return 1;
292 }
293
udp_recv_notify(void * pdata)294 static void udp_recv_notify(void *pdata)
295 {
296 int i = 0;
297 udp_recv_notify_param_t *p = (udp_recv_notify_param_t *)pdata;
298 duk_context *ctx = be_get_context();
299 be_push_ref(ctx, p->js_cb_ref);
300 int arr_idx = duk_push_array(ctx);
301 if (p->recv_len > 0) {
302 for (i = 0; i < p->recv_len; i++) {
303 duk_push_int(ctx, p->buf[i]);
304 duk_put_prop_index(ctx, arr_idx, i);
305 }
306 }
307 duk_push_object(ctx);
308 duk_push_string(ctx, p->src_ip);
309 duk_put_prop_string(ctx, -2, "host");
310 duk_push_uint(ctx, p->src_port);
311 duk_put_prop_string(ctx, -2, "port");
312 duk_push_int(ctx, p->recv_len);
313 if (duk_pcall(ctx, 3) != DUK_EXEC_SUCCESS) {
314 amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
315 }
316 duk_pop(ctx);
317 duk_gc(ctx, 0);
318 if (p->recv_len < 0) {
319 be_unref(ctx, p->js_cb_ref);
320 aos_free(p);
321 }
322 }
323
324 /*************************************************************************************
325 * Function: udp_recv_routine
326 * Description: create a task for blocking recvfrom call
327 * Called by:
328 **************************************************************************************/
udp_recv_routine(void * arg)329 static void udp_recv_routine(void *arg)
330 {
331 udp_recv_param_t *recv_param = (udp_recv_param_t *)arg;
332 int sock_id;
333 udp_options_t udp_options;
334 aos_networkAddr addr_info;
335 udp_recv_notify_param_t *p;
336 duk_context *ctx = be_get_context();
337 sock_id = recv_param->sock_id;
338 memcpy(&udp_options, &(recv_param->options), sizeof(udp_options_t));
339 p = aos_calloc(1, sizeof(udp_recv_notify_param_t));
340 if (!p) {
341 amp_warn(MOD_STR, "allocate memory failed");
342 duk_context *ctx = be_get_context();
343 be_unref(ctx, recv_param->js_cb_ref);
344 goto out;
345 }
346
347 g_udp_recv_flag = 1;
348 while (1) {
349 p->recv_len = aos_udp_recvfrom(sock_id, &addr_info, p->buf,
350 sizeof(p->buf), MAX_UDP_RECV_TIMEOUT);
351 strcpy(p->src_ip, addr_info.addr);
352 p->src_port = addr_info.port;
353 p->js_cb_ref = recv_param->js_cb_ref;
354
355 if (p->recv_len > 0) {
356 py_task_schedule_call(udp_recv_notify, p);
357 }
358
359 if (p->recv_len < 0) {
360 // connection closed
361 amp_error(MOD_STR, "connection closed:%d", p->recv_len);
362 break;
363 }
364
365 if (g_udp_close_flag) {
366 duk_context *ctx = be_get_context();
367 be_unref(ctx, recv_param->js_cb_ref);
368 if (p) {
369 aos_free(p);
370 }
371 break;
372 }
373 }
374 aos_udp_close_without_connect(sock_id);
375
376 out:
377 aos_free(recv_param);
378 g_udp_recv_flag = 0;
379 aos_sem_signal(&g_udp_close_sem);
380 aos_task_exit(0);
381
382 return;
383 }
384
385 /*************************************************************************************
386 * Function: native_udp_recvfrom
387 * Description: js native addon for
388 * UDP.recv(sock_id,function(length, buffer_array, src_ip,
389 *src_port){}) Called by: js api Input: sock_id: interger
390 * function(length, buffer_array, src_ip, src_port): the callback
391 *function length: the recv msg length buffer_array: the recv msg buffer src_ip:
392 *the peer ip string src_port: the peer port which is a interger
393 *
394 * Output: return 0 when UDP.recv call ok
395 * return error number UDP.recv call fail
396 **************************************************************************************/
native_udp_recvfrom(duk_context * ctx)397 static duk_ret_t native_udp_recvfrom(duk_context *ctx)
398 {
399 int ret = -1;
400 int sock_id = 0;
401 aos_task_t udp_recv_task;
402 udp_recv_param_t *recv_param;
403
404 if (!duk_is_number(ctx, 0) || !duk_is_function(ctx, 1)) {
405 amp_warn(MOD_STR, "parameter must be number and function");
406 goto out;
407 }
408
409 sock_id = duk_get_int(ctx, 0);
410 if (sock_id < 0) {
411 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
412 goto out;
413 }
414
415 amp_debug(MOD_STR, "sock_id: %d", sock_id);
416 recv_param = (udp_recv_param_t *)aos_calloc(1, sizeof(*recv_param));
417 if (!recv_param) {
418 amp_warn(MOD_STR, "allocate memory failed");
419 goto out;
420 }
421
422 recv_param->sock_id = sock_id;
423 duk_dup(ctx, 1);
424 recv_param->js_cb_ref = be_ref(ctx);
425
426 ret =
427 aos_task_new_ext(&udp_recv_task, "amp udp recv task", udp_recv_routine,
428 recv_param, 1024 * 4, ADDON_TSK_PRIORRITY);
429 if (ret != 0) {
430 amp_debug(MOD_STR, "udp recv task create faild");
431 goto out;
432 }
433
434 out:
435 duk_push_int(ctx, ret);
436 return 1;
437 }
438
439 /*************************************************************************************
440 * Function: native_udp_close_socket
441 * Description: js native addon for
442 * UDP.close(sock_id)
443 * Called by: js api
444 * Input: sock_id: interger
445 *
446 * Output: return 0 when UDP.close call ok
447 * return error number UDP.close call fail
448 **************************************************************************************/
native_udp_close_socket(duk_context * ctx)449 static duk_ret_t native_udp_close_socket(duk_context *ctx)
450 {
451 int ret = -1;
452 int sock_id = 0;
453
454 if (!duk_is_number(ctx, 0)) {
455 amp_warn(MOD_STR, "parameter must be number");
456 goto out;
457 }
458
459 sock_id = duk_get_int(ctx, 0);
460 if (sock_id <= 0) {
461 amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
462 goto out;
463 }
464 g_udp_close_flag = 1;
465 aos_sem_wait(&g_udp_close_sem, MAX_UDP_RECV_TIMEOUT + 50);
466 g_udp_close_flag = 0;
467 ret = 0;
468
469 out:
470 duk_push_int(ctx, ret);
471 return 1;
472 }
473
module_udp_source_clean(void)474 static void module_udp_source_clean(void)
475 {
476 if (g_udp_recv_flag) {
477 g_udp_close_flag = 1;
478 aos_sem_wait(&g_udp_close_sem, MAX_UDP_RECV_TIMEOUT + 50);
479 g_udp_close_flag = 0;
480 }
481 }
482
module_udp_register(void)483 void module_udp_register(void)
484 {
485 duk_context *ctx = be_get_context();
486
487 if (!g_udp_close_sem) {
488 if (aos_sem_new(&g_udp_close_sem, 0) != 0) {
489 amp_error(MOD_STR, "create udp sem fail");
490 return;
491 }
492 }
493
494 amp_module_free_register(module_udp_source_clean);
495
496 duk_push_object(ctx);
497
498 AMP_ADD_FUNCTION("createSocket", native_udp_create_socket, 0);
499 AMP_ADD_FUNCTION("bind", native_udp_bind, 2);
500 AMP_ADD_FUNCTION("sendto", native_udp_sendto, 3);
501 AMP_ADD_FUNCTION("recvfrom", native_udp_recvfrom, 2);
502 AMP_ADD_FUNCTION("close", native_udp_close_socket, 1);
503
504 duk_put_prop_string(ctx, -2, "UDP");
505 }
506