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\*=========================================================================*/
29static int global_create(lua_State *L);
30static int global_create4(lua_State *L);
31static int global_create6(lua_State *L);
32static int meth_send(lua_State *L);
33static int meth_sendto(lua_State *L);
34static int meth_receive(lua_State *L);
35static int meth_receivefrom(lua_State *L);
36static int meth_getfamily(lua_State *L);
37static int meth_getsockname(lua_State *L);
38static int meth_getpeername(lua_State *L);
39static int meth_gettimeout(lua_State *L);
40static int meth_setsockname(lua_State *L);
41static int meth_setpeername(lua_State *L);
42static int meth_close(lua_State *L);
43static int meth_setoption(lua_State *L);
44static int meth_getoption(lua_State *L);
45static int meth_settimeout(lua_State *L);
46static int meth_getfd(lua_State *L);
47static int meth_setfd(lua_State *L);
48static int meth_dirty(lua_State *L);
49
50/* udp object methods */
51static 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 */
75static 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 */
95static 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 */
111static 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\*-------------------------------------------------------------------------*/
121int 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\*=========================================================================*/
142static 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\*-------------------------------------------------------------------------*/
152static 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\*-------------------------------------------------------------------------*/
172static 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\*-------------------------------------------------------------------------*/
208static 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\*-------------------------------------------------------------------------*/
237static 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\*-------------------------------------------------------------------------*/
281static 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\*-------------------------------------------------------------------------*/
295static 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 */
302static 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
308static 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\*-------------------------------------------------------------------------*/
318static 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
323static 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\*-------------------------------------------------------------------------*/
331static 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\*-------------------------------------------------------------------------*/
339static 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\*-------------------------------------------------------------------------*/
347static 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
352static 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\*-------------------------------------------------------------------------*/
360static 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\*-------------------------------------------------------------------------*/
394static 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\*-------------------------------------------------------------------------*/
404static 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\*-------------------------------------------------------------------------*/
430static 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
452static int global_create(lua_State *L) {
453 return udp_create(L, AF_UNSPEC);
454}
455
456static int global_create4(lua_State *L) {
457 return udp_create(L, AF_INET);
458}
459
460static int global_create6(lua_State *L) {
461 return udp_create(L, AF_INET6);
462}
463