1 | /*=========================================================================*\ |
2 | * Unix domain socket udp submodule |
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 "options.h" |
15 | #include "unix.h" |
16 | #include <sys/un.h> |
17 | |
18 | #define UNIXUDP_DATAGRAMSIZE 8192 |
19 | |
20 | /*=========================================================================*\ |
21 | * Internal function prototypes |
22 | \*=========================================================================*/ |
23 | static int global_create(lua_State *L); |
24 | static int meth_connect(lua_State *L); |
25 | static int meth_bind(lua_State *L); |
26 | static int meth_send(lua_State *L); |
27 | static int meth_receive(lua_State *L); |
28 | static int meth_close(lua_State *L); |
29 | static int meth_setoption(lua_State *L); |
30 | static int meth_settimeout(lua_State *L); |
31 | static int meth_gettimeout(lua_State *L); |
32 | static int meth_getfd(lua_State *L); |
33 | static int meth_setfd(lua_State *L); |
34 | static int meth_dirty(lua_State *L); |
35 | static int meth_receivefrom(lua_State *L); |
36 | static int meth_sendto(lua_State *L); |
37 | static int meth_getsockname(lua_State *L); |
38 | |
39 | static const char *unixudp_tryconnect(p_unix un, const char *path); |
40 | static const char *unixudp_trybind(p_unix un, const char *path); |
41 | |
42 | /* unixudp object methods */ |
43 | static luaL_Reg unixudp_methods[] = { |
44 | {"__gc" , meth_close}, |
45 | {"__tostring" , auxiliar_tostring}, |
46 | {"bind" , meth_bind}, |
47 | {"close" , meth_close}, |
48 | {"connect" , meth_connect}, |
49 | {"dirty" , meth_dirty}, |
50 | {"getfd" , meth_getfd}, |
51 | {"send" , meth_send}, |
52 | {"sendto" , meth_sendto}, |
53 | {"receive" , meth_receive}, |
54 | {"receivefrom" , meth_receivefrom}, |
55 | {"setfd" , meth_setfd}, |
56 | {"setoption" , meth_setoption}, |
57 | {"setpeername" , meth_connect}, |
58 | {"setsockname" , meth_bind}, |
59 | {"getsockname" , meth_getsockname}, |
60 | {"settimeout" , meth_settimeout}, |
61 | {"gettimeout" , meth_gettimeout}, |
62 | {NULL, NULL} |
63 | }; |
64 | |
65 | /* socket option handlers */ |
66 | static t_opt optset[] = { |
67 | {"reuseaddr" , opt_set_reuseaddr}, |
68 | {NULL, NULL} |
69 | }; |
70 | |
71 | /* functions in library namespace */ |
72 | static luaL_Reg func[] = { |
73 | {"udp" , global_create}, |
74 | {NULL, NULL} |
75 | }; |
76 | |
77 | /*-------------------------------------------------------------------------*\ |
78 | * Initializes module |
79 | \*-------------------------------------------------------------------------*/ |
80 | int unixudp_open(lua_State *L) |
81 | { |
82 | /* create classes */ |
83 | auxiliar_newclass(L, "unixudp{connected}" , unixudp_methods); |
84 | auxiliar_newclass(L, "unixudp{unconnected}" , unixudp_methods); |
85 | /* create class groups */ |
86 | auxiliar_add2group(L, "unixudp{connected}" , "unixudp{any}" ); |
87 | auxiliar_add2group(L, "unixudp{unconnected}" , "unixudp{any}" ); |
88 | auxiliar_add2group(L, "unixudp{connected}" , "select{able}" ); |
89 | auxiliar_add2group(L, "unixudp{unconnected}" , "select{able}" ); |
90 | |
91 | luaL_setfuncs(L, func, 0); |
92 | return 0; |
93 | } |
94 | |
95 | /*=========================================================================*\ |
96 | * Lua methods |
97 | \*=========================================================================*/ |
98 | static const char *unixudp_strerror(int err) |
99 | { |
100 | /* a 'closed' error on an unconnected means the target address was not |
101 | * accepted by the transport layer */ |
102 | if (err == IO_CLOSED) return "refused" ; |
103 | else return socket_strerror(err); |
104 | } |
105 | |
106 | static int meth_send(lua_State *L) |
107 | { |
108 | p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{connected}" , 1); |
109 | p_timeout tm = &un->tm; |
110 | size_t count, sent = 0; |
111 | int err; |
112 | const char *data = luaL_checklstring(L, 2, &count); |
113 | timeout_markstart(tm); |
114 | err = socket_send(&un->sock, data, count, &sent, tm); |
115 | if (err != IO_DONE) { |
116 | lua_pushnil(L); |
117 | lua_pushstring(L, unixudp_strerror(err)); |
118 | return 2; |
119 | } |
120 | lua_pushnumber(L, (lua_Number) sent); |
121 | return 1; |
122 | } |
123 | |
124 | /*-------------------------------------------------------------------------*\ |
125 | * Send data through unconnected unixudp socket |
126 | \*-------------------------------------------------------------------------*/ |
127 | static int meth_sendto(lua_State *L) |
128 | { |
129 | p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}" , 1); |
130 | size_t count, sent = 0; |
131 | const char *data = luaL_checklstring(L, 2, &count); |
132 | const char *path = luaL_checkstring(L, 3); |
133 | p_timeout tm = &un->tm; |
134 | int err; |
135 | struct sockaddr_un remote; |
136 | size_t len = strlen(path); |
137 | |
138 | if (len >= sizeof(remote.sun_path)) { |
139 | lua_pushnil(L); |
140 | lua_pushstring(L, "path too long" ); |
141 | return 2; |
142 | } |
143 | |
144 | memset(&remote, 0, sizeof(remote)); |
145 | strcpy(remote.sun_path, path); |
146 | remote.sun_family = AF_UNIX; |
147 | timeout_markstart(tm); |
148 | #ifdef UNIX_HAS_SUN_LEN |
149 | remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) |
150 | + len + 1; |
151 | err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); |
152 | #else |
153 | err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, |
154 | sizeof(remote.sun_family) + len, tm); |
155 | #endif |
156 | if (err != IO_DONE) { |
157 | lua_pushnil(L); |
158 | lua_pushstring(L, unixudp_strerror(err)); |
159 | return 2; |
160 | } |
161 | lua_pushnumber(L, (lua_Number) sent); |
162 | return 1; |
163 | } |
164 | |
165 | static int meth_receive(lua_State *L) { |
166 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
167 | char buf[UNIXUDP_DATAGRAMSIZE]; |
168 | size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); |
169 | char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; |
170 | int err; |
171 | p_timeout tm = &un->tm; |
172 | timeout_markstart(tm); |
173 | if (!dgram) { |
174 | lua_pushnil(L); |
175 | lua_pushliteral(L, "out of memory" ); |
176 | return 2; |
177 | } |
178 | err = socket_recv(&un->sock, dgram, wanted, &got, tm); |
179 | /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ |
180 | if (err != IO_DONE && err != IO_CLOSED) { |
181 | lua_pushnil(L); |
182 | lua_pushstring(L, unixudp_strerror(err)); |
183 | if (wanted > sizeof(buf)) free(dgram); |
184 | return 2; |
185 | } |
186 | lua_pushlstring(L, dgram, got); |
187 | if (wanted > sizeof(buf)) free(dgram); |
188 | return 1; |
189 | } |
190 | |
191 | /*-------------------------------------------------------------------------*\ |
192 | * Receives data and sender from a UDP socket |
193 | \*-------------------------------------------------------------------------*/ |
194 | static int meth_receivefrom(lua_State *L) { |
195 | p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}" , 1); |
196 | char buf[UNIXUDP_DATAGRAMSIZE]; |
197 | size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); |
198 | char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; |
199 | struct sockaddr_un addr; |
200 | socklen_t addr_len = sizeof(addr); |
201 | int err; |
202 | p_timeout tm = &un->tm; |
203 | timeout_markstart(tm); |
204 | if (!dgram) { |
205 | lua_pushnil(L); |
206 | lua_pushliteral(L, "out of memory" ); |
207 | return 2; |
208 | } |
209 | err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, |
210 | &addr_len, tm); |
211 | /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ |
212 | if (err != IO_DONE && err != IO_CLOSED) { |
213 | lua_pushnil(L); |
214 | lua_pushstring(L, unixudp_strerror(err)); |
215 | if (wanted > sizeof(buf)) free(dgram); |
216 | return 2; |
217 | } |
218 | |
219 | lua_pushlstring(L, dgram, got); |
220 | /* the path may be empty, when client send without bind */ |
221 | lua_pushstring(L, addr.sun_path); |
222 | if (wanted > sizeof(buf)) free(dgram); |
223 | return 2; |
224 | } |
225 | |
226 | /*-------------------------------------------------------------------------*\ |
227 | * Just call option handler |
228 | \*-------------------------------------------------------------------------*/ |
229 | static int meth_setoption(lua_State *L) { |
230 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
231 | return opt_meth_setoption(L, optset, &un->sock); |
232 | } |
233 | |
234 | /*-------------------------------------------------------------------------*\ |
235 | * Select support methods |
236 | \*-------------------------------------------------------------------------*/ |
237 | static int meth_getfd(lua_State *L) { |
238 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
239 | lua_pushnumber(L, (int) un->sock); |
240 | return 1; |
241 | } |
242 | |
243 | /* this is very dangerous, but can be handy for those that are brave enough */ |
244 | static int meth_setfd(lua_State *L) { |
245 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
246 | un->sock = (t_socket) luaL_checknumber(L, 2); |
247 | return 0; |
248 | } |
249 | |
250 | static int meth_dirty(lua_State *L) { |
251 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
252 | (void) un; |
253 | lua_pushboolean(L, 0); |
254 | return 1; |
255 | } |
256 | |
257 | /*-------------------------------------------------------------------------*\ |
258 | * Binds an object to an address |
259 | \*-------------------------------------------------------------------------*/ |
260 | static const char *unixudp_trybind(p_unix un, const char *path) { |
261 | struct sockaddr_un local; |
262 | size_t len = strlen(path); |
263 | int err; |
264 | if (len >= sizeof(local.sun_path)) return "path too long" ; |
265 | memset(&local, 0, sizeof(local)); |
266 | strcpy(local.sun_path, path); |
267 | local.sun_family = AF_UNIX; |
268 | #ifdef UNIX_HAS_SUN_LEN |
269 | local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) |
270 | + len + 1; |
271 | err = socket_bind(&un->sock, (SA *) &local, local.sun_len); |
272 | |
273 | #else |
274 | err = socket_bind(&un->sock, (SA *) &local, |
275 | sizeof(local.sun_family) + len); |
276 | #endif |
277 | if (err != IO_DONE) socket_destroy(&un->sock); |
278 | return socket_strerror(err); |
279 | } |
280 | |
281 | static int meth_bind(lua_State *L) |
282 | { |
283 | p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}" , 1); |
284 | const char *path = luaL_checkstring(L, 2); |
285 | const char *err = unixudp_trybind(un, path); |
286 | if (err) { |
287 | lua_pushnil(L); |
288 | lua_pushstring(L, err); |
289 | return 2; |
290 | } |
291 | lua_pushnumber(L, 1); |
292 | return 1; |
293 | } |
294 | |
295 | static int meth_getsockname(lua_State *L) |
296 | { |
297 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
298 | struct sockaddr_un peer = {0}; |
299 | socklen_t peer_len = sizeof(peer); |
300 | |
301 | if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { |
302 | lua_pushnil(L); |
303 | lua_pushstring(L, socket_strerror(errno)); |
304 | return 2; |
305 | } |
306 | |
307 | lua_pushstring(L, peer.sun_path); |
308 | return 1; |
309 | } |
310 | |
311 | /*-------------------------------------------------------------------------*\ |
312 | * Turns a master unixudp object into a client object. |
313 | \*-------------------------------------------------------------------------*/ |
314 | static const char *unixudp_tryconnect(p_unix un, const char *path) |
315 | { |
316 | struct sockaddr_un remote; |
317 | int err; |
318 | size_t len = strlen(path); |
319 | if (len >= sizeof(remote.sun_path)) return "path too long" ; |
320 | memset(&remote, 0, sizeof(remote)); |
321 | strcpy(remote.sun_path, path); |
322 | remote.sun_family = AF_UNIX; |
323 | timeout_markstart(&un->tm); |
324 | #ifdef UNIX_HAS_SUN_LEN |
325 | remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) |
326 | + len + 1; |
327 | err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); |
328 | #else |
329 | err = socket_connect(&un->sock, (SA *) &remote, |
330 | sizeof(remote.sun_family) + len, &un->tm); |
331 | #endif |
332 | if (err != IO_DONE) socket_destroy(&un->sock); |
333 | return socket_strerror(err); |
334 | } |
335 | |
336 | static int meth_connect(lua_State *L) |
337 | { |
338 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
339 | const char *path = luaL_checkstring(L, 2); |
340 | const char *err = unixudp_tryconnect(un, path); |
341 | if (err) { |
342 | lua_pushnil(L); |
343 | lua_pushstring(L, err); |
344 | return 2; |
345 | } |
346 | /* turn unconnected object into a connected object */ |
347 | auxiliar_setclass(L, "unixudp{connected}" , 1); |
348 | lua_pushnumber(L, 1); |
349 | return 1; |
350 | } |
351 | |
352 | /*-------------------------------------------------------------------------*\ |
353 | * Closes socket used by object |
354 | \*-------------------------------------------------------------------------*/ |
355 | static int meth_close(lua_State *L) |
356 | { |
357 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
358 | socket_destroy(&un->sock); |
359 | lua_pushnumber(L, 1); |
360 | return 1; |
361 | } |
362 | |
363 | /*-------------------------------------------------------------------------*\ |
364 | * Just call tm methods |
365 | \*-------------------------------------------------------------------------*/ |
366 | static int meth_settimeout(lua_State *L) |
367 | { |
368 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
369 | return timeout_meth_settimeout(L, &un->tm); |
370 | } |
371 | |
372 | static int meth_gettimeout(lua_State *L) |
373 | { |
374 | p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}" , 1); |
375 | return timeout_meth_gettimeout(L, &un->tm); |
376 | } |
377 | |
378 | /*=========================================================================*\ |
379 | * Library functions |
380 | \*=========================================================================*/ |
381 | /*-------------------------------------------------------------------------*\ |
382 | * Creates a master unixudp object |
383 | \*-------------------------------------------------------------------------*/ |
384 | static int global_create(lua_State *L) |
385 | { |
386 | t_socket sock; |
387 | int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); |
388 | /* try to allocate a system socket */ |
389 | if (err == IO_DONE) { |
390 | /* allocate unixudp object */ |
391 | p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); |
392 | /* set its type as master object */ |
393 | auxiliar_setclass(L, "unixudp{unconnected}" , -1); |
394 | /* initialize remaining structure fields */ |
395 | socket_setnonblocking(&sock); |
396 | un->sock = sock; |
397 | io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, |
398 | (p_error) socket_ioerror, &un->sock); |
399 | timeout_init(&un->tm, -1, -1); |
400 | buffer_init(&un->buf, &un->io, &un->tm); |
401 | return 1; |
402 | } else { |
403 | lua_pushnil(L); |
404 | lua_pushstring(L, socket_strerror(err)); |
405 | return 2; |
406 | } |
407 | } |
408 | |