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