1/*=========================================================================*\
2* Common option interface
3* LuaSocket toolkit
4\*=========================================================================*/
5#include <string.h>
6
7#include "lauxlib.h"
8
9#include "auxiliar.h"
10#include "options.h"
11#include "inet.h"
12
13
14/*=========================================================================*\
15* Internal functions prototypes
16\*=========================================================================*/
17static int opt_setmembership(lua_State *L, p_socket ps, int level, int name);
18static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name);
19static int opt_setboolean(lua_State *L, p_socket ps, int level, int name);
20static int opt_getboolean(lua_State *L, p_socket ps, int level, int name);
21static int opt_setint(lua_State *L, p_socket ps, int level, int name);
22static int opt_getint(lua_State *L, p_socket ps, int level, int name);
23static int opt_set(lua_State *L, p_socket ps, int level, int name,
24 void *val, int len);
25static int opt_get(lua_State *L, p_socket ps, int level, int name,
26 void *val, int* len);
27
28/*=========================================================================*\
29* Exported functions
30\*=========================================================================*/
31/*-------------------------------------------------------------------------*\
32* Calls appropriate option handler
33\*-------------------------------------------------------------------------*/
34int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)
35{
36 const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
37 while (opt->name && strcmp(name, opt->name))
38 opt++;
39 if (!opt->func) {
40 char msg[45];
41 sprintf(msg, "unsupported option `%.35s'", name);
42 luaL_argerror(L, 2, msg);
43 }
44 return opt->func(L, ps);
45}
46
47int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
48{
49 const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
50 while (opt->name && strcmp(name, opt->name))
51 opt++;
52 if (!opt->func) {
53 char msg[45];
54 sprintf(msg, "unsupported option `%.35s'", name);
55 luaL_argerror(L, 2, msg);
56 }
57 return opt->func(L, ps);
58}
59
60/* enables reuse of local address */
61int opt_set_reuseaddr(lua_State *L, p_socket ps)
62{
63 return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
64}
65
66int opt_get_reuseaddr(lua_State *L, p_socket ps)
67{
68 return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
69}
70
71/* enables reuse of local port */
72int opt_set_reuseport(lua_State *L, p_socket ps)
73{
74 return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
75}
76
77int opt_get_reuseport(lua_State *L, p_socket ps)
78{
79 return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
80}
81
82/* disables the Naggle algorithm */
83int opt_set_tcp_nodelay(lua_State *L, p_socket ps)
84{
85 return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
86}
87
88int opt_get_tcp_nodelay(lua_State *L, p_socket ps)
89{
90 return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
91}
92
93int opt_set_keepalive(lua_State *L, p_socket ps)
94{
95 return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
96}
97
98int opt_get_keepalive(lua_State *L, p_socket ps)
99{
100 return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
101}
102
103int opt_set_dontroute(lua_State *L, p_socket ps)
104{
105 return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
106}
107
108int opt_get_dontroute(lua_State *L, p_socket ps)
109{
110 return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
111}
112
113int opt_set_broadcast(lua_State *L, p_socket ps)
114{
115 return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
116}
117
118int opt_get_broadcast(lua_State *L, p_socket ps)
119{
120 return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
121}
122
123int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps)
124{
125 return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);
126}
127
128int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps)
129{
130 return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);
131}
132
133int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps)
134{
135 return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
136}
137
138int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps)
139{
140 return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
141}
142
143int opt_set_ip_multicast_loop(lua_State *L, p_socket ps)
144{
145 return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
146}
147
148int opt_get_ip_multicast_loop(lua_State *L, p_socket ps)
149{
150 return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
151}
152
153int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps)
154{
155 return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
156}
157
158int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps)
159{
160 return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
161}
162
163int opt_set_linger(lua_State *L, p_socket ps)
164{
165 struct linger li; /* obj, name, table */
166 if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
167 lua_pushstring(L, "on");
168 lua_gettable(L, 3);
169 if (!lua_isboolean(L, -1))
170 luaL_argerror(L, 3, "boolean 'on' field expected");
171 li.l_onoff = (u_short) lua_toboolean(L, -1);
172 lua_pushstring(L, "timeout");
173 lua_gettable(L, 3);
174 if (!lua_isnumber(L, -1))
175 luaL_argerror(L, 3, "number 'timeout' field expected");
176 li.l_linger = (u_short) lua_tonumber(L, -1);
177 return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
178}
179
180int opt_get_linger(lua_State *L, p_socket ps)
181{
182 struct linger li; /* obj, name */
183 int len = sizeof(li);
184 int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len);
185 if (err)
186 return err;
187 lua_newtable(L);
188 lua_pushboolean(L, li.l_onoff);
189 lua_setfield(L, -2, "on");
190 lua_pushinteger(L, li.l_linger);
191 lua_setfield(L, -2, "timeout");
192 return 1;
193}
194
195int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps)
196{
197 return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL);
198}
199
200int opt_set_ip_multicast_if(lua_State *L, p_socket ps)
201{
202 const char *address = luaL_checkstring(L, 3); /* obj, name, ip */
203 struct in_addr val;
204 val.s_addr = htonl(INADDR_ANY);
205 if (strcmp(address, "*") && !inet_aton(address, &val))
206 luaL_argerror(L, 3, "ip expected");
207 return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF,
208 (char *) &val, sizeof(val));
209}
210
211int opt_get_ip_multicast_if(lua_State *L, p_socket ps)
212{
213 struct in_addr val;
214 socklen_t len = sizeof(val);
215 if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) {
216 lua_pushnil(L);
217 lua_pushstring(L, "getsockopt failed");
218 return 2;
219 }
220 lua_pushstring(L, inet_ntoa(val));
221 return 1;
222}
223
224int opt_set_ip_add_membership(lua_State *L, p_socket ps)
225{
226 return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP);
227}
228
229int opt_set_ip_drop_membersip(lua_State *L, p_socket ps)
230{
231 return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP);
232}
233
234int opt_set_ip6_add_membership(lua_State *L, p_socket ps)
235{
236 return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP);
237}
238
239int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps)
240{
241 return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP);
242}
243
244int opt_get_ip6_v6only(lua_State *L, p_socket ps)
245{
246 return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
247}
248
249int opt_set_ip6_v6only(lua_State *L, p_socket ps)
250{
251 return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
252}
253
254/*=========================================================================*\
255* Auxiliar functions
256\*=========================================================================*/
257static int opt_setmembership(lua_State *L, p_socket ps, int level, int name)
258{
259 struct ip_mreq val; /* obj, name, table */
260 if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
261 lua_pushstring(L, "multiaddr");
262 lua_gettable(L, 3);
263 if (!lua_isstring(L, -1))
264 luaL_argerror(L, 3, "string 'multiaddr' field expected");
265 if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))
266 luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
267 lua_pushstring(L, "interface");
268 lua_gettable(L, 3);
269 if (!lua_isstring(L, -1))
270 luaL_argerror(L, 3, "string 'interface' field expected");
271 val.imr_interface.s_addr = htonl(INADDR_ANY);
272 if (strcmp(lua_tostring(L, -1), "*") &&
273 !inet_aton(lua_tostring(L, -1), &val.imr_interface))
274 luaL_argerror(L, 3, "invalid 'interface' ip address");
275 return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
276}
277
278static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name)
279{
280 struct ipv6_mreq val; /* obj, opt-name, table */
281 memset(&val, 0, sizeof(val));
282 if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
283 lua_pushstring(L, "multiaddr");
284 lua_gettable(L, 3);
285 if (!lua_isstring(L, -1))
286 luaL_argerror(L, 3, "string 'multiaddr' field expected");
287 if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr))
288 luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
289 lua_pushstring(L, "interface");
290 lua_gettable(L, 3);
291 /* By default we listen to interface on default route
292 * (sigh). However, interface= can override it. We should
293 * support either number, or name for it. Waiting for
294 * windows port of if_nametoindex */
295 if (!lua_isnil(L, -1)) {
296 if (lua_isnumber(L, -1)) {
297 val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1);
298 } else
299 luaL_argerror(L, -1, "number 'interface' field expected");
300 }
301 return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
302}
303
304static
305int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len)
306{
307 socklen_t socklen = *len;
308 if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) {
309 lua_pushnil(L);
310 lua_pushstring(L, "getsockopt failed");
311 return 2;
312 }
313 *len = socklen;
314 return 0;
315}
316
317static
318int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len)
319{
320 if (setsockopt(*ps, level, name, (char *) val, len) < 0) {
321 lua_pushnil(L);
322 lua_pushstring(L, "setsockopt failed");
323 return 2;
324 }
325 lua_pushnumber(L, 1);
326 return 1;
327}
328
329static int opt_getboolean(lua_State *L, p_socket ps, int level, int name)
330{
331 int val = 0;
332 int len = sizeof(val);
333 int err = opt_get(L, ps, level, name, (char *) &val, &len);
334 if (err)
335 return err;
336 lua_pushboolean(L, val);
337 return 1;
338}
339
340int opt_get_error(lua_State *L, p_socket ps)
341{
342 int val = 0;
343 socklen_t len = sizeof(val);
344 if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) {
345 lua_pushnil(L);
346 lua_pushstring(L, "getsockopt failed");
347 return 2;
348 }
349 lua_pushstring(L, socket_strerror(val));
350 return 1;
351}
352
353static int opt_setboolean(lua_State *L, p_socket ps, int level, int name)
354{
355 int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */
356 return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
357}
358
359static int opt_getint(lua_State *L, p_socket ps, int level, int name)
360{
361 int val = 0;
362 int len = sizeof(val);
363 int err = opt_get(L, ps, level, name, (char *) &val, &len);
364 if (err)
365 return err;
366 lua_pushnumber(L, val);
367 return 1;
368}
369
370static int opt_setint(lua_State *L, p_socket ps, int level, int name)
371{
372 int val = (int) lua_tonumber(L, 3); /* obj, name, int */
373 return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
374}
375