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 * Internal functions prototypes
15 \*=========================================================================*/
16 static int opt_setmembership(lua_State *L, p_socket ps, int level, int name);
17 static int opt_setboolean(lua_State *L, p_socket ps, int level, int name);
18 static int opt_getboolean(lua_State *L, p_socket ps, int level, int name);
19 static int opt_set(lua_State *L, p_socket ps, int level, int name,
20         void *val, int len);
21 static int opt_get(lua_State *L, p_socket ps, int level, int name,
22         void *val, int* len);
23 
24 /*=========================================================================*\
25 * Exported functions
26 \*=========================================================================*/
27 /*-------------------------------------------------------------------------*\
28 * Calls appropriate option handler
29 \*-------------------------------------------------------------------------*/
opt_meth_setoption(lua_State * L,p_opt opt,p_socket ps)30 int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)
31 {
32     const char *name = luaL_checkstring(L, 2);      /* obj, name, ... */
33     while (opt->name && strcmp(name, opt->name))
34         opt++;
35     if (!opt->func) {
36         char msg[45];
37         sprintf(msg, "unsupported option `%.35s'", name);
38         luaL_argerror(L, 2, msg);
39     }
40     return opt->func(L, ps);
41 }
42 
opt_meth_getoption(lua_State * L,p_opt opt,p_socket ps)43 int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
44 {
45     const char *name = luaL_checkstring(L, 2);      /* obj, name, ... */
46     while (opt->name && strcmp(name, opt->name))
47         opt++;
48     if (!opt->func) {
49         char msg[45];
50         sprintf(msg, "unsupported option `%.35s'", name);
51         luaL_argerror(L, 2, msg);
52     }
53     return opt->func(L, ps);
54 }
55 
56 /* enables reuse of local address */
opt_set_reuseaddr(lua_State * L,p_socket ps)57 int opt_set_reuseaddr(lua_State *L, p_socket ps)
58 {
59     return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
60 }
61 
opt_get_reuseaddr(lua_State * L,p_socket ps)62 int opt_get_reuseaddr(lua_State *L, p_socket ps)
63 {
64     return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
65 }
66 
67 /* enables reuse of local port */
opt_set_reuseport(lua_State * L,p_socket ps)68 int opt_set_reuseport(lua_State *L, p_socket ps)
69 {
70     return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
71 }
72 
opt_get_reuseport(lua_State * L,p_socket ps)73 int opt_get_reuseport(lua_State *L, p_socket ps)
74 {
75     return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
76 }
77 
78 /* disables the Naggle algorithm */
opt_set_tcp_nodelay(lua_State * L,p_socket ps)79 int opt_set_tcp_nodelay(lua_State *L, p_socket ps)
80 {
81     return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
82 }
83 
opt_get_tcp_nodelay(lua_State * L,p_socket ps)84 int opt_get_tcp_nodelay(lua_State *L, p_socket ps)
85 {
86     return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
87 }
88 
opt_set_keepalive(lua_State * L,p_socket ps)89 int opt_set_keepalive(lua_State *L, p_socket ps)
90 {
91     return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
92 }
93 
opt_get_keepalive(lua_State * L,p_socket ps)94 int opt_get_keepalive(lua_State *L, p_socket ps)
95 {
96     return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
97 }
98 
opt_set_dontroute(lua_State * L,p_socket ps)99 int opt_set_dontroute(lua_State *L, p_socket ps)
100 {
101     return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
102 }
103 
opt_set_broadcast(lua_State * L,p_socket ps)104 int opt_set_broadcast(lua_State *L, p_socket ps)
105 {
106     return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
107 }
108 
opt_set_ip_multicast_loop(lua_State * L,p_socket ps)109 int opt_set_ip_multicast_loop(lua_State *L, p_socket ps)
110 {
111     return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
112 }
113 
opt_get_ip_multicast_loop(lua_State * L,p_socket ps)114 int opt_get_ip_multicast_loop(lua_State *L, p_socket ps)
115 {
116     return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
117 }
118 
opt_set_linger(lua_State * L,p_socket ps)119 int opt_set_linger(lua_State *L, p_socket ps)
120 {
121     struct linger li;                      /* obj, name, table */
122     if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
123     lua_pushstring(L, "on");
124     lua_gettable(L, 3);
125     if (!lua_isboolean(L, -1))
126         luaL_argerror(L, 3, "boolean 'on' field expected");
127     li.l_onoff = (u_short) lua_toboolean(L, -1);
128     lua_pushstring(L, "timeout");
129     lua_gettable(L, 3);
130     if (!lua_isnumber(L, -1))
131         luaL_argerror(L, 3, "number 'timeout' field expected");
132     li.l_linger = (u_short) lua_tonumber(L, -1);
133     return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
134 }
135 
opt_get_linger(lua_State * L,p_socket ps)136 int opt_get_linger(lua_State *L, p_socket ps)
137 {
138     struct linger li;                      /* obj, name */
139     int len = sizeof(li);
140     int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len);
141     if (err)
142         return err;
143     lua_newtable(L);
144     lua_pushboolean(L, li.l_onoff);
145     lua_setfield(L, -2, "on");
146     lua_pushinteger(L, li.l_linger);
147     lua_setfield(L, -2, "timeout");
148     return 1;
149 }
150 
opt_set_ip_multicast_ttl(lua_State * L,p_socket ps)151 int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps)
152 {
153     int val = (int) luaL_checknumber(L, 3);    /* obj, name, int */
154     return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_TTL,
155         (char *) &val, sizeof(val));
156 }
157 
opt_set_ip_multicast_if(lua_State * L,p_socket ps)158 int opt_set_ip_multicast_if(lua_State *L, p_socket ps)
159 {
160     const char *address = luaL_checkstring(L, 3);    /* obj, name, ip */
161     struct in_addr val;
162     val.s_addr = htonl(INADDR_ANY);
163     if (strcmp(address, "*") && !inet_aton(address, &val))
164         luaL_argerror(L, 3, "ip expected");
165     return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF,
166         (char *) &val, sizeof(val));
167 }
168 
opt_get_ip_multicast_if(lua_State * L,p_socket ps)169 int opt_get_ip_multicast_if(lua_State *L, p_socket ps)
170 {
171     struct in_addr val;
172     socklen_t len = sizeof(val);
173     if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) {
174         lua_pushnil(L);
175         lua_pushstring(L, "getsockopt failed");
176         return 2;
177     }
178     lua_pushstring(L, inet_ntoa(val));
179     return 1;
180 }
181 
opt_set_ip_add_membership(lua_State * L,p_socket ps)182 int opt_set_ip_add_membership(lua_State *L, p_socket ps)
183 {
184     return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP);
185 }
186 
opt_set_ip_drop_membersip(lua_State * L,p_socket ps)187 int opt_set_ip_drop_membersip(lua_State *L, p_socket ps)
188 {
189     return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP);
190 }
191 
opt_set_ip6_v6only(lua_State * L,p_socket ps)192 int opt_set_ip6_v6only(lua_State *L, p_socket ps)
193 {
194     return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
195 }
196 
197 /*=========================================================================*\
198 * Auxiliar functions
199 \*=========================================================================*/
opt_setmembership(lua_State * L,p_socket ps,int level,int name)200 static int opt_setmembership(lua_State *L, p_socket ps, int level, int name)
201 {
202     struct ip_mreq val;                   /* obj, name, table */
203     if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
204     lua_pushstring(L, "multiaddr");
205     lua_gettable(L, 3);
206     if (!lua_isstring(L, -1))
207         luaL_argerror(L, 3, "string 'multiaddr' field expected");
208     if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))
209         luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
210     lua_pushstring(L, "interface");
211     lua_gettable(L, 3);
212     if (!lua_isstring(L, -1))
213         luaL_argerror(L, 3, "string 'interface' field expected");
214     val.imr_interface.s_addr = htonl(INADDR_ANY);
215     if (strcmp(lua_tostring(L, -1), "*") &&
216             !inet_aton(lua_tostring(L, -1), &val.imr_interface))
217         luaL_argerror(L, 3, "invalid 'interface' ip address");
218     return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
219 }
220 
221 static
opt_get(lua_State * L,p_socket ps,int level,int name,void * val,int * len)222 int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len)
223 {
224     socklen_t socklen = *len;
225     if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) {
226         lua_pushnil(L);
227         lua_pushstring(L, "getsockopt failed");
228         return 2;
229     }
230     *len = socklen;
231     return 0;
232 }
233 
234 static
opt_set(lua_State * L,p_socket ps,int level,int name,void * val,int len)235 int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len)
236 {
237     if (setsockopt(*ps, level, name, (char *) val, len) < 0) {
238         lua_pushnil(L);
239         lua_pushstring(L, "setsockopt failed");
240         return 2;
241     }
242     lua_pushnumber(L, 1);
243     return 1;
244 }
245 
opt_getboolean(lua_State * L,p_socket ps,int level,int name)246 static int opt_getboolean(lua_State *L, p_socket ps, int level, int name)
247 {
248     int val = 0;
249     int len = sizeof(val);
250     int err = opt_get(L, ps, level, name, (char *) &val, &len);
251     if (err)
252         return err;
253     lua_pushboolean(L, val);
254     return 1;
255 }
256 
opt_setboolean(lua_State * L,p_socket ps,int level,int name)257 static int opt_setboolean(lua_State *L, p_socket ps, int level, int name)
258 {
259     int val = auxiliar_checkboolean(L, 3);             /* obj, name, bool */
260     return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
261 }
262 
263