1/*=========================================================================*\
2* Unix domain socket tcp sub module
3* LuaSocket toolkit
4\*=========================================================================*/
5#include <string.h>
6
7#include "lua.h"
8#include "lauxlib.h"
9#include "compat.h"
10
11#include "auxiliar.h"
12#include "socket.h"
13#include "options.h"
14#include "unixtcp.h"
15#include <sys/un.h>
16
17/*=========================================================================*\
18* Internal function prototypes
19\*=========================================================================*/
20static int global_create(lua_State *L);
21static int meth_connect(lua_State *L);
22static int meth_listen(lua_State *L);
23static int meth_bind(lua_State *L);
24static int meth_send(lua_State *L);
25static int meth_shutdown(lua_State *L);
26static int meth_receive(lua_State *L);
27static int meth_accept(lua_State *L);
28static int meth_close(lua_State *L);
29static int meth_setoption(lua_State *L);
30static int meth_settimeout(lua_State *L);
31static int meth_getfd(lua_State *L);
32static int meth_setfd(lua_State *L);
33static int meth_dirty(lua_State *L);
34static int meth_getstats(lua_State *L);
35static int meth_setstats(lua_State *L);
36static int meth_getsockname(lua_State *L);
37
38static const char *unixtcp_tryconnect(p_unix un, const char *path);
39static const char *unixtcp_trybind(p_unix un, const char *path);
40
41/* unixtcp object methods */
42static luaL_Reg unixtcp_methods[] = {
43 {"__gc", meth_close},
44 {"__tostring", auxiliar_tostring},
45 {"accept", meth_accept},
46 {"bind", meth_bind},
47 {"close", meth_close},
48 {"connect", meth_connect},
49 {"dirty", meth_dirty},
50 {"getfd", meth_getfd},
51 {"getstats", meth_getstats},
52 {"setstats", meth_setstats},
53 {"listen", meth_listen},
54 {"receive", meth_receive},
55 {"send", meth_send},
56 {"setfd", meth_setfd},
57 {"setoption", meth_setoption},
58 {"setpeername", meth_connect},
59 {"setsockname", meth_bind},
60 {"getsockname", meth_getsockname},
61 {"settimeout", meth_settimeout},
62 {"shutdown", meth_shutdown},
63 {NULL, NULL}
64};
65
66/* socket option handlers */
67static t_opt optset[] = {
68 {"keepalive", opt_set_keepalive},
69 {"reuseaddr", opt_set_reuseaddr},
70 {"linger", opt_set_linger},
71 {NULL, NULL}
72};
73
74/* functions in library namespace */
75static luaL_Reg func[] = {
76 {"tcp", global_create},
77 {NULL, NULL}
78};
79
80/*-------------------------------------------------------------------------*\
81* Initializes module
82\*-------------------------------------------------------------------------*/
83int unixtcp_open(lua_State *L)
84{
85 /* create classes */
86 auxiliar_newclass(L, "unixtcp{master}", unixtcp_methods);
87 auxiliar_newclass(L, "unixtcp{client}", unixtcp_methods);
88 auxiliar_newclass(L, "unixtcp{server}", unixtcp_methods);
89
90 /* create class groups */
91 auxiliar_add2group(L, "unixtcp{master}", "unixtcp{any}");
92 auxiliar_add2group(L, "unixtcp{client}", "unixtcp{any}");
93 auxiliar_add2group(L, "unixtcp{server}", "unixtcp{any}");
94
95 luaL_setfuncs(L, func, 0);
96 return 0;
97}
98
99/*=========================================================================*\
100* Lua methods
101\*=========================================================================*/
102/*-------------------------------------------------------------------------*\
103* Just call buffered IO methods
104\*-------------------------------------------------------------------------*/
105static int meth_send(lua_State *L) {
106 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1);
107 return buffer_meth_send(L, &un->buf);
108}
109
110static int meth_receive(lua_State *L) {
111 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1);
112 return buffer_meth_receive(L, &un->buf);
113}
114
115static int meth_getstats(lua_State *L) {
116 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1);
117 return buffer_meth_getstats(L, &un->buf);
118}
119
120static int meth_setstats(lua_State *L) {
121 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1);
122 return buffer_meth_setstats(L, &un->buf);
123}
124
125/*-------------------------------------------------------------------------*\
126* Just call option handler
127\*-------------------------------------------------------------------------*/
128static int meth_setoption(lua_State *L) {
129 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
130 return opt_meth_setoption(L, optset, &un->sock);
131}
132
133/*-------------------------------------------------------------------------*\
134* Select support methods
135\*-------------------------------------------------------------------------*/
136static int meth_getfd(lua_State *L) {
137 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
138 lua_pushnumber(L, (int) un->sock);
139 return 1;
140}
141
142/* this is very dangerous, but can be handy for those that are brave enough */
143static int meth_setfd(lua_State *L) {
144 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
145 un->sock = (t_socket) luaL_checknumber(L, 2);
146 return 0;
147}
148
149static int meth_dirty(lua_State *L) {
150 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
151 lua_pushboolean(L, !buffer_isempty(&un->buf));
152 return 1;
153}
154
155/*-------------------------------------------------------------------------*\
156* Waits for and returns a client object attempting connection to the
157* server object
158\*-------------------------------------------------------------------------*/
159static int meth_accept(lua_State *L) {
160 p_unix server = (p_unix) auxiliar_checkclass(L, "unixtcp{server}", 1);
161 p_timeout tm = timeout_markstart(&server->tm);
162 t_socket sock;
163 int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);
164 /* if successful, push client socket */
165 if (err == IO_DONE) {
166 p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix));
167 auxiliar_setclass(L, "unixtcp{client}", -1);
168 /* initialize structure fields */
169 socket_setnonblocking(&sock);
170 clnt->sock = sock;
171 io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv,
172 (p_error) socket_ioerror, &clnt->sock);
173 timeout_init(&clnt->tm, -1, -1);
174 buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
175 return 1;
176 } else {
177 lua_pushnil(L);
178 lua_pushstring(L, socket_strerror(err));
179 return 2;
180 }
181}
182
183/*-------------------------------------------------------------------------*\
184* Binds an object to an address
185\*-------------------------------------------------------------------------*/
186static const char *unixtcp_trybind(p_unix un, const char *path) {
187 struct sockaddr_un local;
188 size_t len = strlen(path);
189 int err;
190 if (len >= sizeof(local.sun_path)) return "path too long";
191 memset(&local, 0, sizeof(local));
192 strcpy(local.sun_path, path);
193 local.sun_family = AF_UNIX;
194#ifdef UNIX_HAS_SUN_LEN
195 local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len)
196 + len + 1;
197 err = socket_bind(&un->sock, (SA *) &local, local.sun_len);
198
199#else
200 err = socket_bind(&un->sock, (SA *) &local,
201 sizeof(local.sun_family) + len);
202#endif
203 if (err != IO_DONE) socket_destroy(&un->sock);
204 return socket_strerror(err);
205}
206
207static int meth_bind(lua_State *L) {
208 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1);
209 const char *path = luaL_checkstring(L, 2);
210 const char *err = unixtcp_trybind(un, path);
211 if (err) {
212 lua_pushnil(L);
213 lua_pushstring(L, err);
214 return 2;
215 }
216 lua_pushnumber(L, 1);
217 return 1;
218}
219
220static int meth_getsockname(lua_State *L)
221{
222 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
223 struct sockaddr_un peer = {0};
224 socklen_t peer_len = sizeof(peer);
225
226 if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {
227 lua_pushnil(L);
228 lua_pushstring(L, socket_strerror(errno));
229 return 2;
230 }
231
232 lua_pushstring(L, peer.sun_path);
233 return 1;
234}
235
236/*-------------------------------------------------------------------------*\
237* Turns a master unixtcp object into a client object.
238\*-------------------------------------------------------------------------*/
239static const char *unixtcp_tryconnect(p_unix un, const char *path)
240{
241 struct sockaddr_un remote;
242 int err;
243 size_t len = strlen(path);
244 if (len >= sizeof(remote.sun_path)) return "path too long";
245 memset(&remote, 0, sizeof(remote));
246 strcpy(remote.sun_path, path);
247 remote.sun_family = AF_UNIX;
248 timeout_markstart(&un->tm);
249#ifdef UNIX_HAS_SUN_LEN
250 remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)
251 + len + 1;
252 err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm);
253#else
254 err = socket_connect(&un->sock, (SA *) &remote,
255 sizeof(remote.sun_family) + len, &un->tm);
256#endif
257 if (err != IO_DONE) socket_destroy(&un->sock);
258 return socket_strerror(err);
259}
260
261static int meth_connect(lua_State *L)
262{
263 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1);
264 const char *path = luaL_checkstring(L, 2);
265 const char *err = unixtcp_tryconnect(un, path);
266 if (err) {
267 lua_pushnil(L);
268 lua_pushstring(L, err);
269 return 2;
270 }
271 /* turn master object into a client object */
272 auxiliar_setclass(L, "unixtcp{client}", 1);
273 lua_pushnumber(L, 1);
274 return 1;
275}
276
277/*-------------------------------------------------------------------------*\
278* Closes socket used by object
279\*-------------------------------------------------------------------------*/
280static int meth_close(lua_State *L)
281{
282 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
283 socket_destroy(&un->sock);
284 lua_pushnumber(L, 1);
285 return 1;
286}
287
288/*-------------------------------------------------------------------------*\
289* Puts the sockt in listen mode
290\*-------------------------------------------------------------------------*/
291static int meth_listen(lua_State *L)
292{
293 p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1);
294 int backlog = (int) luaL_optnumber(L, 2, 32);
295 int err = socket_listen(&un->sock, backlog);
296 if (err != IO_DONE) {
297 lua_pushnil(L);
298 lua_pushstring(L, socket_strerror(err));
299 return 2;
300 }
301 /* turn master object into a server object */
302 auxiliar_setclass(L, "unixtcp{server}", 1);
303 lua_pushnumber(L, 1);
304 return 1;
305}
306
307/*-------------------------------------------------------------------------*\
308* Shuts the connection down partially
309\*-------------------------------------------------------------------------*/
310static int meth_shutdown(lua_State *L)
311{
312 /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */
313 static const char* methods[] = { "receive", "send", "both", NULL };
314 p_unix tcp = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1);
315 int how = luaL_checkoption(L, 2, "both", methods);
316 socket_shutdown(&tcp->sock, how);
317 lua_pushnumber(L, 1);
318 return 1;
319}
320
321/*-------------------------------------------------------------------------*\
322* Just call tm methods
323\*-------------------------------------------------------------------------*/
324static int meth_settimeout(lua_State *L) {
325 p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1);
326 return timeout_meth_settimeout(L, &un->tm);
327}
328
329/*=========================================================================*\
330* Library functions
331\*=========================================================================*/
332/*-------------------------------------------------------------------------*\
333* Creates a master unixtcp object
334\*-------------------------------------------------------------------------*/
335static int global_create(lua_State *L) {
336 t_socket sock;
337 int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0);
338 /* try to allocate a system socket */
339 if (err == IO_DONE) {
340 /* allocate unixtcp object */
341 p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
342 /* set its type as master object */
343 auxiliar_setclass(L, "unixtcp{master}", -1);
344 /* initialize remaining structure fields */
345 socket_setnonblocking(&sock);
346 un->sock = sock;
347 io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,
348 (p_error) socket_ioerror, &un->sock);
349 timeout_init(&un->tm, -1, -1);
350 buffer_init(&un->buf, &un->io, &un->tm);
351 return 1;
352 } else {
353 lua_pushnil(L);
354 lua_pushstring(L, socket_strerror(err));
355 return 2;
356 }
357}
358