Merge nixio 0.2
[project/luci.git] / libs / nixio / src / sockopt.c
1 /*
2  * nixio - Linux I/O library for lua
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "nixio.h"
20
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <errno.h>
26
27 #ifndef IPV6_ADD_MEMBERSHIP
28 #define IPV6_ADD_MEMBERSHIP             IPV6_JOIN_GROUP
29 #endif
30
31 #ifndef IPV6_DROP_MEMBERSHIP
32 #define IPV6_DROP_MEMBERSHIP    IPV6_LEAVE_GROUP
33 #endif
34
35 static int nixio_sock_fileno(lua_State *L) {
36         lua_pushinteger(L, nixio__checkfd(L, 1));
37         return 1;
38 }
39
40 /**
41  * setblocking()
42  */
43 static int nixio_sock_setblocking(lua_State *L) {
44         int fd = nixio__checkfd(L, 1);
45         luaL_checkany(L, 2);
46         int set = lua_toboolean(L, 2);
47
48 #ifndef __WINNT__
49
50         int flags = fcntl(fd, F_GETFL);
51
52         if (flags == -1) {
53                 return nixio__perror(L);
54         }
55
56         if (!set) {
57                 flags |= O_NONBLOCK;
58         } else {
59                 flags &= ~O_NONBLOCK;
60         }
61
62         return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
63
64 #else /* __WINNT__ */
65
66         lua_getmetatable(L, 1);
67         luaL_getmetatable(L, NIXIO_META);
68         if (lua_equal(L, -1, -2)) {     /* Socket */
69                 unsigned long val = !set;
70                 return nixio__pstatus_s(L, !ioctlsocket(fd, FIONBIO, &val));
71         } else {                                        /* File */
72                 WSASetLastError(WSAENOTSOCK);
73                 return nixio__perror_s(L);
74         }
75
76 #endif /* __WINNT__ */
77 }
78
79 static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
80         int value;
81         socklen_t optlen = sizeof(value);
82         if (!set) {
83                 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
84                         lua_pushinteger(L, value);
85                         return 1;
86                 }
87         } else {
88                 value = luaL_checkinteger(L, set);
89                 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
90                         lua_pushboolean(L, 1);
91                         return 1;
92                 }
93         }
94         return nixio__perror_s(L);
95 }
96
97 static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
98         struct linger value;
99         socklen_t optlen = sizeof(value);
100         if (!set) {
101                 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
102                         lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
103                         return 1;
104                 }
105         } else {
106                 value.l_linger = luaL_checkinteger(L, set);
107                 value.l_onoff = value.l_linger ? 1 : 0;
108                 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
109                         lua_pushboolean(L, 1);
110                         return 1;
111                 }
112         }
113         return nixio__perror_s(L);
114 }
115
116 static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
117         struct timeval value;
118         socklen_t optlen = sizeof(value);
119         if (!set) {
120                 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
121                         lua_pushinteger(L, value.tv_sec);
122                         lua_pushinteger(L, value.tv_usec);
123                         return 2;
124                 }
125         } else {
126                 value.tv_sec  = luaL_checkinteger(L, set);
127                 value.tv_usec = luaL_optinteger(L, set + 1, 0);
128                 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
129                         lua_pushboolean(L, 1);
130                         return 1;
131                 }
132         }
133         return nixio__perror_s(L);
134 }
135
136 #ifdef SO_BINDTODEVICE
137
138 static int nixio__gso_b(lua_State *L, int fd, int level, int opt, int set) {
139         if (!set) {
140                 socklen_t optlen = IFNAMSIZ;
141                 char ifname[IFNAMSIZ];
142                 if (!getsockopt(fd, level, opt, (char *)ifname, &optlen)) {
143                         lua_pushlstring(L, ifname, optlen);
144                         return 1;
145                 }
146         } else {
147                 size_t valuelen;
148                 const char *value = luaL_checklstring(L, set, &valuelen);
149                 luaL_argcheck(L, valuelen <= IFNAMSIZ, set, "invalid interface name");
150                 if (!setsockopt(fd, level, opt, (char *)value, valuelen)) {
151                         lua_pushboolean(L, 1);
152                         return 1;
153                 }
154         }
155         return nixio__perror_s(L);
156 }
157
158 #endif /* SO_BINDTODEVICE */
159
160 static int nixio__gso_mreq4(lua_State *L, int fd, int level, int opt, int set) {
161         struct ip_mreq value;
162         socklen_t optlen = sizeof(value);
163         if (!set) {
164                 char buf[INET_ADDRSTRLEN];
165                 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
166                         if (!inet_ntop(AF_INET, &value.imr_multiaddr, buf, sizeof(buf))) {
167                                 return nixio__perror_s(L);
168                         }
169                         lua_pushstring(L, buf);
170                         if (!inet_ntop(AF_INET, &value.imr_interface, buf, sizeof(buf))) {
171                                 return nixio__perror_s(L);
172                         }
173                         lua_pushstring(L, buf);
174                         return 2;
175                 }
176         } else {
177                 const char *maddr = luaL_checkstring(L, set);
178                 const char *iface = luaL_optstring(L, set + 1, "0.0.0.0");
179                 if (inet_pton(AF_INET, maddr, &value.imr_multiaddr) < 1) {
180                         return nixio__perror_s(L);
181                 }
182                 if (inet_pton(AF_INET, iface, &value.imr_interface) < 1) {
183                         return nixio__perror_s(L);
184                 }
185                 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
186                         lua_pushboolean(L, 1);
187                         return 1;
188                 }
189         }
190         return nixio__perror_s(L);
191 }
192
193 static int nixio__gso_mreq6(lua_State *L, int fd, int level, int opt, int set) {
194         struct ipv6_mreq val;
195         socklen_t optlen = sizeof(val);
196         if (!set) {
197                 char buf[INET_ADDRSTRLEN];
198                 if (!getsockopt(fd, level, opt, (char *)&val, &optlen)) {
199                         if (!inet_ntop(AF_INET6, &val.ipv6mr_multiaddr, buf, sizeof(buf))) {
200                                 return nixio__perror_s(L);
201                         }
202                         lua_pushstring(L, buf);
203                         lua_pushnumber(L, val.ipv6mr_interface);
204                         return 2;
205                 }
206         } else {
207                 const char *maddr = luaL_checkstring(L, set);
208                 if (inet_pton(AF_INET6, maddr, &val.ipv6mr_multiaddr) < 1) {
209                         return nixio__perror_s(L);
210                 }
211                 val.ipv6mr_interface = luaL_optlong(L, set + 1, 0);
212                 if (!setsockopt(fd, level, opt, (char *)&val, optlen)) {
213                         lua_pushboolean(L, 1);
214                         return 1;
215                 }
216         }
217         return nixio__perror_s(L);
218 }
219
220 /**
221  * get/setsockopt() helper
222  */
223 static int nixio__getsetsockopt(lua_State *L, int set) {
224         nixio_sock *sock = nixio__checksock(L);
225         const char *level = luaL_optlstring(L, 2, "", NULL);
226         const char *option = luaL_optlstring(L, 3, "", NULL);
227         set = (set) ? 4 : 0;
228
229         if (!strcmp(level, "socket")) {
230                 if (!strcmp(option, "keepalive")) {
231                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
232                 } else if (!strcmp(option, "reuseaddr")) {
233                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
234                 } else if (!strcmp(option, "rcvbuf")) {
235                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
236                 } else if (!strcmp(option, "sndbuf")) {
237                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
238                 } else if (!strcmp(option, "priority")) {
239 #ifdef SO_PRIORITY
240                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
241 #else
242                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
243 #endif
244                 } else if (!strcmp(option, "broadcast")) {
245                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
246                 } else if (!strcmp(option, "dontroute")) {
247                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_DONTROUTE, set);
248                 } else if (!strcmp(option, "error")) {
249                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_ERROR, set);
250                 } else if (!strcmp(option, "oobinline")) {
251                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_OOBINLINE, set);
252                 } else if (!strcmp(option, "linger")) {
253                         return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
254                 } else if (!strcmp(option, "sndtimeo")) {
255                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
256                 } else if (!strcmp(option, "rcvtimeo")) {
257                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
258                 } else if (!strcmp(option, "bindtodevice")) {
259 #ifdef SO_BINDTODEVICE
260                         return nixio__gso_b(L, sock->fd, SOL_SOCKET, SO_BINDTODEVICE, set);
261 #else
262                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
263 #endif
264                 } else {
265                         return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
266                          " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
267                          " dontroute, bindtodevice, error, oobinline"
268                         );
269                 }
270         } else if (!strcmp(level, "tcp")) {
271                 if (!strcmp(option, "cork")) {
272 #ifdef TCP_CORK
273                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
274 #else
275                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
276 #endif
277                 } else if (!strcmp(option, "nodelay")) {
278                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
279                 } else {
280                         return luaL_argerror(L, 3, "supported values: cork, nodelay");
281                 }
282         } else if (!strcmp(level, "ip")) {
283                 if (!strcmp(option, "mtu")) {
284 #ifdef IP_MTU
285                         return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MTU, set);
286 #else
287                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
288 #endif
289                 } else if (!strcmp(option, "hdrincl")) {
290                         return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_HDRINCL,
291                                         set);
292                 } else if (!strcmp(option, "multicast_loop")) {
293                         return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
294                                         set);
295                 } else if (!strcmp(option, "multicast_ttl")) {
296                         return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_TTL,
297                                         set);
298                 } else if (!strcmp(option, "multicast_if")) {
299                         return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_MULTICAST_IF,
300                                         set);
301                 } else if (!strcmp(option, "add_membership")) {
302                         return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
303                                         set);
304                 } else if (!strcmp(option, "drop_membership")) {
305                         return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
306                                         set);
307                 } else {
308                         return luaL_argerror(L, 3,
309                                 "supported values: hdrincl, mtu, multicast_loop, "
310                                 "multicast_ttl, multicast_if, add_membership, drop_membership");
311                 }
312         } else if (!strcmp(level, "ipv6")) {
313                 if (!strcmp(option, "mtu")) {
314 #ifdef IPV6_MTU
315                         return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_MTU, set);
316 #else
317                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
318 #endif
319                 } else if (!strcmp(option, "v6only")) {
320 #ifdef IPV6_V6ONLY
321                         return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, set);
322 #else
323                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
324 #endif
325                 } else if (!strcmp(option, "multicast_loop")) {
326                         return nixio__gso_int(L, sock->fd, IPPROTO_IPV6,
327                                         IPV6_MULTICAST_LOOP, set);
328                 } else if (!strcmp(option, "multicast_hops")) {
329                         return nixio__gso_int(L, sock->fd, IPPROTO_IPV6,
330                                         IPV6_MULTICAST_HOPS, set);
331                 } else if (!strcmp(option, "multicast_if")) {
332                         return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
333                                         IPV6_MULTICAST_IF, set);
334                 } else if (!strcmp(option, "add_membership")) {
335                         return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
336                                         IPV6_ADD_MEMBERSHIP, set);
337                 } else if (!strcmp(option, "drop_membership")) {
338                         return nixio__gso_mreq6(L, sock->fd, IPPROTO_IPV6,
339                                         IPV6_DROP_MEMBERSHIP, set);
340                 } else {
341                         return luaL_argerror(L, 3,
342                                 "supported values: v6only, mtu, multicast_loop, multicast_hops,"
343                                 " multicast_if, add_membership, drop_membership");
344                 }
345         } else {
346                 return luaL_argerror(L, 2, "supported values: socket, tcp, ip, ipv6");
347         }
348 }
349
350 /**
351  * getsockopt()
352  */
353 static int nixio_sock_getsockopt(lua_State *L) {
354         return nixio__getsetsockopt(L, 0);
355 }
356
357 /**
358  * setsockopt()
359  */
360 static int nixio_sock_setsockopt(lua_State *L) {
361         return nixio__getsetsockopt(L, 1);
362 }
363
364 /* module table */
365 static const luaL_reg M[] = {
366         {"setblocking", nixio_sock_setblocking},
367         {"getsockopt",  nixio_sock_getsockopt},
368         {"setsockopt",  nixio_sock_setsockopt},
369         {"getopt",              nixio_sock_getsockopt},
370         {"setopt",              nixio_sock_setsockopt},
371         {"fileno",              nixio_sock_fileno},
372         {NULL,                  NULL}
373 };
374
375 void nixio_open_sockopt(lua_State *L) {
376         lua_pushvalue(L, -2);
377         luaL_register(L, NULL, M);
378         lua_pop(L, 1);
379
380         luaL_getmetatable(L, NIXIO_FILE_META);
381         lua_pushcfunction(L, nixio_sock_setblocking);
382         lua_setfield(L, -2, "setblocking");
383         lua_pushcfunction(L, nixio_sock_fileno);
384         lua_setfield(L, -2, "fileno");
385         lua_pop(L, 1);
386 }