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