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 | \*=========================================================================*/ |
17 | static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); |
18 | static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); |
19 | static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); |
20 | static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); |
21 | static int opt_setint(lua_State *L, p_socket ps, int level, int name); |
22 | static int opt_getint(lua_State *L, p_socket ps, int level, int name); |
23 | static int opt_set(lua_State *L, p_socket ps, int level, int name, |
24 | void *val, int len); |
25 | static 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 | \*-------------------------------------------------------------------------*/ |
34 | int 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 | |
47 | int 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 */ |
61 | int opt_set_reuseaddr(lua_State *L, p_socket ps) |
62 | { |
63 | return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); |
64 | } |
65 | |
66 | int 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 */ |
72 | int opt_set_reuseport(lua_State *L, p_socket ps) |
73 | { |
74 | return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); |
75 | } |
76 | |
77 | int 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 */ |
83 | int opt_set_tcp_nodelay(lua_State *L, p_socket ps) |
84 | { |
85 | return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); |
86 | } |
87 | |
88 | int opt_get_tcp_nodelay(lua_State *L, p_socket ps) |
89 | { |
90 | return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); |
91 | } |
92 | |
93 | int opt_set_keepalive(lua_State *L, p_socket ps) |
94 | { |
95 | return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); |
96 | } |
97 | |
98 | int opt_get_keepalive(lua_State *L, p_socket ps) |
99 | { |
100 | return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); |
101 | } |
102 | |
103 | int opt_set_dontroute(lua_State *L, p_socket ps) |
104 | { |
105 | return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); |
106 | } |
107 | |
108 | int opt_get_dontroute(lua_State *L, p_socket ps) |
109 | { |
110 | return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); |
111 | } |
112 | |
113 | int opt_set_broadcast(lua_State *L, p_socket ps) |
114 | { |
115 | return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); |
116 | } |
117 | |
118 | int opt_get_broadcast(lua_State *L, p_socket ps) |
119 | { |
120 | return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); |
121 | } |
122 | |
123 | int 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 | |
128 | int 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 | |
133 | int 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 | |
138 | int 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 | |
143 | int 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 | |
148 | int 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 | |
153 | int 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 | |
158 | int 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 | |
163 | int 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 | |
180 | int 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 | |
195 | int 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 | |
200 | int 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 | |
211 | int 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 | |
224 | int 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 | |
229 | int 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 | |
234 | int 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 | |
239 | int 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 | |
244 | int opt_get_ip6_v6only(lua_State *L, p_socket ps) |
245 | { |
246 | return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); |
247 | } |
248 | |
249 | int 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 | \*=========================================================================*/ |
257 | static 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 | |
278 | static 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 | |
304 | static |
305 | int 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 | |
317 | static |
318 | int 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 | |
329 | static 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 | |
340 | int 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 | |
353 | static 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 | |
359 | static 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 | |
370 | static 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 | |