1 | /*=========================================================================*\ |
2 | * UDP object |
3 | * LuaSocket toolkit |
4 | \*=========================================================================*/ |
5 | #include <string.h> |
6 | #include <stdlib.h> |
7 | |
8 | #include "lua.h" |
9 | #include "lauxlib.h" |
10 | #include "compat.h" |
11 | |
12 | #include "auxiliar.h" |
13 | #include "socket.h" |
14 | #include "inet.h" |
15 | #include "options.h" |
16 | #include "udp.h" |
17 | |
18 | /* min and max macros */ |
19 | #ifndef MIN |
20 | #define MIN(x, y) ((x) < (y) ? x : y) |
21 | #endif |
22 | #ifndef MAX |
23 | #define MAX(x, y) ((x) > (y) ? x : y) |
24 | #endif |
25 | |
26 | /*=========================================================================*\ |
27 | * Internal function prototypes |
28 | \*=========================================================================*/ |
29 | static int global_create(lua_State *L); |
30 | static int global_create4(lua_State *L); |
31 | static int global_create6(lua_State *L); |
32 | static int meth_send(lua_State *L); |
33 | static int meth_sendto(lua_State *L); |
34 | static int meth_receive(lua_State *L); |
35 | static int meth_receivefrom(lua_State *L); |
36 | static int meth_getfamily(lua_State *L); |
37 | static int meth_getsockname(lua_State *L); |
38 | static int meth_getpeername(lua_State *L); |
39 | static int meth_gettimeout(lua_State *L); |
40 | static int meth_setsockname(lua_State *L); |
41 | static int meth_setpeername(lua_State *L); |
42 | static int meth_close(lua_State *L); |
43 | static int meth_setoption(lua_State *L); |
44 | static int meth_getoption(lua_State *L); |
45 | static int meth_settimeout(lua_State *L); |
46 | static int meth_getfd(lua_State *L); |
47 | static int meth_setfd(lua_State *L); |
48 | static int meth_dirty(lua_State *L); |
49 | |
50 | /* udp object methods */ |
51 | static luaL_Reg udp_methods[] = { |
52 | {"__gc" , meth_close}, |
53 | {"__tostring" , auxiliar_tostring}, |
54 | {"close" , meth_close}, |
55 | {"dirty" , meth_dirty}, |
56 | {"getfamily" , meth_getfamily}, |
57 | {"getfd" , meth_getfd}, |
58 | {"getpeername" , meth_getpeername}, |
59 | {"getsockname" , meth_getsockname}, |
60 | {"receive" , meth_receive}, |
61 | {"receivefrom" , meth_receivefrom}, |
62 | {"send" , meth_send}, |
63 | {"sendto" , meth_sendto}, |
64 | {"setfd" , meth_setfd}, |
65 | {"setoption" , meth_setoption}, |
66 | {"getoption" , meth_getoption}, |
67 | {"setpeername" , meth_setpeername}, |
68 | {"setsockname" , meth_setsockname}, |
69 | {"settimeout" , meth_settimeout}, |
70 | {"gettimeout" , meth_gettimeout}, |
71 | {NULL, NULL} |
72 | }; |
73 | |
74 | /* socket options for setoption */ |
75 | static t_opt optset[] = { |
76 | {"dontroute" , opt_set_dontroute}, |
77 | {"broadcast" , opt_set_broadcast}, |
78 | {"reuseaddr" , opt_set_reuseaddr}, |
79 | {"reuseport" , opt_set_reuseport}, |
80 | {"ip-multicast-if" , opt_set_ip_multicast_if}, |
81 | {"ip-multicast-ttl" , opt_set_ip_multicast_ttl}, |
82 | {"ip-multicast-loop" , opt_set_ip_multicast_loop}, |
83 | {"ip-add-membership" , opt_set_ip_add_membership}, |
84 | {"ip-drop-membership" , opt_set_ip_drop_membersip}, |
85 | {"ipv6-unicast-hops" , opt_set_ip6_unicast_hops}, |
86 | {"ipv6-multicast-hops" , opt_set_ip6_unicast_hops}, |
87 | {"ipv6-multicast-loop" , opt_set_ip6_multicast_loop}, |
88 | {"ipv6-add-membership" , opt_set_ip6_add_membership}, |
89 | {"ipv6-drop-membership" , opt_set_ip6_drop_membersip}, |
90 | {"ipv6-v6only" , opt_set_ip6_v6only}, |
91 | {NULL, NULL} |
92 | }; |
93 | |
94 | /* socket options for getoption */ |
95 | static t_opt optget[] = { |
96 | {"dontroute" , opt_get_dontroute}, |
97 | {"broadcast" , opt_get_broadcast}, |
98 | {"reuseaddr" , opt_get_reuseaddr}, |
99 | {"reuseport" , opt_get_reuseport}, |
100 | {"ip-multicast-if" , opt_get_ip_multicast_if}, |
101 | {"ip-multicast-loop" , opt_get_ip_multicast_loop}, |
102 | {"error" , opt_get_error}, |
103 | {"ipv6-unicast-hops" , opt_get_ip6_unicast_hops}, |
104 | {"ipv6-multicast-hops" , opt_get_ip6_unicast_hops}, |
105 | {"ipv6-multicast-loop" , opt_get_ip6_multicast_loop}, |
106 | {"ipv6-v6only" , opt_get_ip6_v6only}, |
107 | {NULL, NULL} |
108 | }; |
109 | |
110 | /* functions in library namespace */ |
111 | static luaL_Reg func[] = { |
112 | {"udp" , global_create}, |
113 | {"udp4" , global_create4}, |
114 | {"udp6" , global_create6}, |
115 | {NULL, NULL} |
116 | }; |
117 | |
118 | /*-------------------------------------------------------------------------*\ |
119 | * Initializes module |
120 | \*-------------------------------------------------------------------------*/ |
121 | int udp_open(lua_State *L) { |
122 | /* create classes */ |
123 | auxiliar_newclass(L, "udp{connected}" , udp_methods); |
124 | auxiliar_newclass(L, "udp{unconnected}" , udp_methods); |
125 | /* create class groups */ |
126 | auxiliar_add2group(L, "udp{connected}" , "udp{any}" ); |
127 | auxiliar_add2group(L, "udp{unconnected}" , "udp{any}" ); |
128 | auxiliar_add2group(L, "udp{connected}" , "select{able}" ); |
129 | auxiliar_add2group(L, "udp{unconnected}" , "select{able}" ); |
130 | /* define library functions */ |
131 | luaL_setfuncs(L, func, 0); |
132 | /* export default UDP size */ |
133 | lua_pushliteral(L, "_DATAGRAMSIZE" ); |
134 | lua_pushinteger(L, UDP_DATAGRAMSIZE); |
135 | lua_rawset(L, -3); |
136 | return 0; |
137 | } |
138 | |
139 | /*=========================================================================*\ |
140 | * Lua methods |
141 | \*=========================================================================*/ |
142 | static const char *udp_strerror(int err) { |
143 | /* a 'closed' error on an unconnected means the target address was not |
144 | * accepted by the transport layer */ |
145 | if (err == IO_CLOSED) return "refused" ; |
146 | else return socket_strerror(err); |
147 | } |
148 | |
149 | /*-------------------------------------------------------------------------*\ |
150 | * Send data through connected udp socket |
151 | \*-------------------------------------------------------------------------*/ |
152 | static int meth_send(lua_State *L) { |
153 | p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}" , 1); |
154 | p_timeout tm = &udp->tm; |
155 | size_t count, sent = 0; |
156 | int err; |
157 | const char *data = luaL_checklstring(L, 2, &count); |
158 | timeout_markstart(tm); |
159 | err = socket_send(&udp->sock, data, count, &sent, tm); |
160 | if (err != IO_DONE) { |
161 | lua_pushnil(L); |
162 | lua_pushstring(L, udp_strerror(err)); |
163 | return 2; |
164 | } |
165 | lua_pushnumber(L, (lua_Number) sent); |
166 | return 1; |
167 | } |
168 | |
169 | /*-------------------------------------------------------------------------*\ |
170 | * Send data through unconnected udp socket |
171 | \*-------------------------------------------------------------------------*/ |
172 | static int meth_sendto(lua_State *L) { |
173 | p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}" , 1); |
174 | size_t count, sent = 0; |
175 | const char *data = luaL_checklstring(L, 2, &count); |
176 | const char *ip = luaL_checkstring(L, 3); |
177 | const char *port = luaL_checkstring(L, 4); |
178 | p_timeout tm = &udp->tm; |
179 | int err; |
180 | struct addrinfo aihint; |
181 | struct addrinfo *ai; |
182 | memset(&aihint, 0, sizeof(aihint)); |
183 | aihint.ai_family = udp->family; |
184 | aihint.ai_socktype = SOCK_DGRAM; |
185 | aihint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; |
186 | err = getaddrinfo(ip, port, &aihint, &ai); |
187 | if (err) { |
188 | lua_pushnil(L); |
189 | lua_pushstring(L, gai_strerror(err)); |
190 | return 2; |
191 | } |
192 | timeout_markstart(tm); |
193 | err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, |
194 | (socklen_t) ai->ai_addrlen, tm); |
195 | freeaddrinfo(ai); |
196 | if (err != IO_DONE) { |
197 | lua_pushnil(L); |
198 | lua_pushstring(L, udp_strerror(err)); |
199 | return 2; |
200 | } |
201 | lua_pushnumber(L, (lua_Number) sent); |
202 | return 1; |
203 | } |
204 | |
205 | /*-------------------------------------------------------------------------*\ |
206 | * Receives data from a UDP socket |
207 | \*-------------------------------------------------------------------------*/ |
208 | static int meth_receive(lua_State *L) { |
209 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
210 | char buf[UDP_DATAGRAMSIZE]; |
211 | size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); |
212 | char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; |
213 | int err; |
214 | p_timeout tm = &udp->tm; |
215 | timeout_markstart(tm); |
216 | if (!dgram) { |
217 | lua_pushnil(L); |
218 | lua_pushliteral(L, "out of memory" ); |
219 | return 2; |
220 | } |
221 | err = socket_recv(&udp->sock, dgram, wanted, &got, tm); |
222 | /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ |
223 | if (err != IO_DONE && err != IO_CLOSED) { |
224 | lua_pushnil(L); |
225 | lua_pushstring(L, udp_strerror(err)); |
226 | if (wanted > sizeof(buf)) free(dgram); |
227 | return 2; |
228 | } |
229 | lua_pushlstring(L, dgram, got); |
230 | if (wanted > sizeof(buf)) free(dgram); |
231 | return 1; |
232 | } |
233 | |
234 | /*-------------------------------------------------------------------------*\ |
235 | * Receives data and sender from a UDP socket |
236 | \*-------------------------------------------------------------------------*/ |
237 | static int meth_receivefrom(lua_State *L) { |
238 | p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}" , 1); |
239 | char buf[UDP_DATAGRAMSIZE]; |
240 | size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); |
241 | char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; |
242 | struct sockaddr_storage addr; |
243 | socklen_t addr_len = sizeof(addr); |
244 | char addrstr[INET6_ADDRSTRLEN]; |
245 | char portstr[6]; |
246 | int err; |
247 | p_timeout tm = &udp->tm; |
248 | timeout_markstart(tm); |
249 | if (!dgram) { |
250 | lua_pushnil(L); |
251 | lua_pushliteral(L, "out of memory" ); |
252 | return 2; |
253 | } |
254 | err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, |
255 | &addr_len, tm); |
256 | /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ |
257 | if (err != IO_DONE && err != IO_CLOSED) { |
258 | lua_pushnil(L); |
259 | lua_pushstring(L, udp_strerror(err)); |
260 | if (wanted > sizeof(buf)) free(dgram); |
261 | return 2; |
262 | } |
263 | err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, |
264 | INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); |
265 | if (err) { |
266 | lua_pushnil(L); |
267 | lua_pushstring(L, gai_strerror(err)); |
268 | if (wanted > sizeof(buf)) free(dgram); |
269 | return 2; |
270 | } |
271 | lua_pushlstring(L, dgram, got); |
272 | lua_pushstring(L, addrstr); |
273 | lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); |
274 | if (wanted > sizeof(buf)) free(dgram); |
275 | return 3; |
276 | } |
277 | |
278 | /*-------------------------------------------------------------------------*\ |
279 | * Returns family as string |
280 | \*-------------------------------------------------------------------------*/ |
281 | static int meth_getfamily(lua_State *L) { |
282 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
283 | if (udp->family == AF_INET6) { |
284 | lua_pushliteral(L, "inet6" ); |
285 | return 1; |
286 | } else { |
287 | lua_pushliteral(L, "inet4" ); |
288 | return 1; |
289 | } |
290 | } |
291 | |
292 | /*-------------------------------------------------------------------------*\ |
293 | * Select support methods |
294 | \*-------------------------------------------------------------------------*/ |
295 | static int meth_getfd(lua_State *L) { |
296 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
297 | lua_pushnumber(L, (int) udp->sock); |
298 | return 1; |
299 | } |
300 | |
301 | /* this is very dangerous, but can be handy for those that are brave enough */ |
302 | static int meth_setfd(lua_State *L) { |
303 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
304 | udp->sock = (t_socket) luaL_checknumber(L, 2); |
305 | return 0; |
306 | } |
307 | |
308 | static int meth_dirty(lua_State *L) { |
309 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
310 | (void) udp; |
311 | lua_pushboolean(L, 0); |
312 | return 1; |
313 | } |
314 | |
315 | /*-------------------------------------------------------------------------*\ |
316 | * Just call inet methods |
317 | \*-------------------------------------------------------------------------*/ |
318 | static int meth_getpeername(lua_State *L) { |
319 | p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}" , 1); |
320 | return inet_meth_getpeername(L, &udp->sock, udp->family); |
321 | } |
322 | |
323 | static int meth_getsockname(lua_State *L) { |
324 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
325 | return inet_meth_getsockname(L, &udp->sock, udp->family); |
326 | } |
327 | |
328 | /*-------------------------------------------------------------------------*\ |
329 | * Just call option handler |
330 | \*-------------------------------------------------------------------------*/ |
331 | static int meth_setoption(lua_State *L) { |
332 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
333 | return opt_meth_setoption(L, optset, &udp->sock); |
334 | } |
335 | |
336 | /*-------------------------------------------------------------------------*\ |
337 | * Just call option handler |
338 | \*-------------------------------------------------------------------------*/ |
339 | static int meth_getoption(lua_State *L) { |
340 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
341 | return opt_meth_getoption(L, optget, &udp->sock); |
342 | } |
343 | |
344 | /*-------------------------------------------------------------------------*\ |
345 | * Just call tm methods |
346 | \*-------------------------------------------------------------------------*/ |
347 | static int meth_settimeout(lua_State *L) { |
348 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
349 | return timeout_meth_settimeout(L, &udp->tm); |
350 | } |
351 | |
352 | static int meth_gettimeout(lua_State *L) { |
353 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
354 | return timeout_meth_gettimeout(L, &udp->tm); |
355 | } |
356 | |
357 | /*-------------------------------------------------------------------------*\ |
358 | * Turns a master udp object into a client object. |
359 | \*-------------------------------------------------------------------------*/ |
360 | static int meth_setpeername(lua_State *L) { |
361 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
362 | p_timeout tm = &udp->tm; |
363 | const char *address = luaL_checkstring(L, 2); |
364 | int connecting = strcmp(address, "*" ); |
365 | const char *port = connecting? luaL_checkstring(L, 3): "0" ; |
366 | struct addrinfo connecthints; |
367 | const char *err; |
368 | memset(&connecthints, 0, sizeof(connecthints)); |
369 | connecthints.ai_socktype = SOCK_DGRAM; |
370 | /* make sure we try to connect only to the same family */ |
371 | connecthints.ai_family = udp->family; |
372 | if (connecting) { |
373 | err = inet_tryconnect(&udp->sock, &udp->family, address, |
374 | port, tm, &connecthints); |
375 | if (err) { |
376 | lua_pushnil(L); |
377 | lua_pushstring(L, err); |
378 | return 2; |
379 | } |
380 | auxiliar_setclass(L, "udp{connected}" , 1); |
381 | } else { |
382 | /* we ignore possible errors because Mac OS X always |
383 | * returns EAFNOSUPPORT */ |
384 | inet_trydisconnect(&udp->sock, udp->family, tm); |
385 | auxiliar_setclass(L, "udp{unconnected}" , 1); |
386 | } |
387 | lua_pushnumber(L, 1); |
388 | return 1; |
389 | } |
390 | |
391 | /*-------------------------------------------------------------------------*\ |
392 | * Closes socket used by object |
393 | \*-------------------------------------------------------------------------*/ |
394 | static int meth_close(lua_State *L) { |
395 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}" , 1); |
396 | socket_destroy(&udp->sock); |
397 | lua_pushnumber(L, 1); |
398 | return 1; |
399 | } |
400 | |
401 | /*-------------------------------------------------------------------------*\ |
402 | * Turns a master object into a server object |
403 | \*-------------------------------------------------------------------------*/ |
404 | static int meth_setsockname(lua_State *L) { |
405 | p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}" , 1); |
406 | const char *address = luaL_checkstring(L, 2); |
407 | const char *port = luaL_checkstring(L, 3); |
408 | const char *err; |
409 | struct addrinfo bindhints; |
410 | memset(&bindhints, 0, sizeof(bindhints)); |
411 | bindhints.ai_socktype = SOCK_DGRAM; |
412 | bindhints.ai_family = udp->family; |
413 | bindhints.ai_flags = AI_PASSIVE; |
414 | err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints); |
415 | if (err) { |
416 | lua_pushnil(L); |
417 | lua_pushstring(L, err); |
418 | return 2; |
419 | } |
420 | lua_pushnumber(L, 1); |
421 | return 1; |
422 | } |
423 | |
424 | /*=========================================================================*\ |
425 | * Library functions |
426 | \*=========================================================================*/ |
427 | /*-------------------------------------------------------------------------*\ |
428 | * Creates a master udp object |
429 | \*-------------------------------------------------------------------------*/ |
430 | static int udp_create(lua_State *L, int family) { |
431 | /* allocate udp object */ |
432 | p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); |
433 | auxiliar_setclass(L, "udp{unconnected}" , -1); |
434 | /* if family is AF_UNSPEC, we leave the socket invalid and |
435 | * store AF_UNSPEC into family. This will allow it to later be |
436 | * replaced with an AF_INET6 or AF_INET socket upon first use. */ |
437 | udp->sock = SOCKET_INVALID; |
438 | timeout_init(&udp->tm, -1, -1); |
439 | udp->family = family; |
440 | if (family != AF_UNSPEC) { |
441 | const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); |
442 | if (err != NULL) { |
443 | lua_pushnil(L); |
444 | lua_pushstring(L, err); |
445 | return 2; |
446 | } |
447 | socket_setnonblocking(&udp->sock); |
448 | } |
449 | return 1; |
450 | } |
451 | |
452 | static int global_create(lua_State *L) { |
453 | return udp_create(L, AF_UNSPEC); |
454 | } |
455 | |
456 | static int global_create4(lua_State *L) { |
457 | return udp_create(L, AF_INET); |
458 | } |
459 | |
460 | static int global_create6(lua_State *L) { |
461 | return udp_create(L, AF_INET6); |
462 | } |
463 | |