2 * nixio - Linux I/O library for lua
4 * Copyright (C) 2009 Steven Barth <steven@midlink.org>
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include <sys/types.h>
31 #ifndef IPV6_ADD_MEMBERSHIP
32 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
35 #ifndef IPV6_DROP_MEMBERSHIP
36 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
39 static int nixio_sock_fileno(lua_State *L) {
40 lua_pushinteger(L, nixio__checkfd(L, 1));
47 static int nixio_sock_setblocking(lua_State *L) {
48 int fd = nixio__checkfd(L, 1);
50 int set = lua_toboolean(L, 2);
54 int flags = fcntl(fd, F_GETFL);
57 return nixio__perror(L);
66 return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
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));
76 WSASetLastError(WSAENOTSOCK);
77 return nixio__perror_s(L);
80 #endif /* __WINNT__ */
83 static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
85 socklen_t optlen = sizeof(value);
87 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
88 lua_pushinteger(L, value);
92 value = luaL_checkinteger(L, set);
93 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
94 lua_pushboolean(L, 1);
98 return nixio__perror_s(L);
101 static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
103 socklen_t optlen = sizeof(value);
105 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
106 lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
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);
117 return nixio__perror_s(L);
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);
124 if (!getsockopt(fd, level, opt, (char *)&value, &optlen)) {
125 lua_pushinteger(L, value.tv_sec);
126 lua_pushinteger(L, value.tv_usec);
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);
137 return nixio__perror_s(L);
140 #ifdef SO_BINDTODEVICE
142 static int nixio__gso_b(lua_State *L, int fd, int level, int opt, int 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);
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);
159 return nixio__perror_s(L);
162 #endif /* SO_BINDTODEVICE */
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);
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);
173 lua_pushstring(L, buf);
174 if (!inet_ntop(AF_INET, &value.imr_interface, buf, sizeof(buf))) {
175 return nixio__perror_s(L);
177 lua_pushstring(L, buf);
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);
186 if (inet_pton(AF_INET, iface, &value.imr_interface) < 1) {
187 return nixio__perror_s(L);
189 if (!setsockopt(fd, level, opt, (char *)&value, optlen)) {
190 lua_pushboolean(L, 1);
194 return nixio__perror_s(L);
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);
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);
206 lua_pushstring(L, buf);
207 lua_pushinteger(L, val.ipv6mr_interface);
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);
215 val.ipv6mr_interface = luaL_optlong(L, set + 1, 0);
216 if (!setsockopt(fd, level, opt, (char *)&val, optlen)) {
217 lua_pushboolean(L, 1);
221 return nixio__perror_s(L);
225 * get/setsockopt() helper
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);
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")) {
244 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
246 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
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);
266 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
269 return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
270 " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
271 " dontroute, bindtodevice, error, oobinline"
274 } else if (!strcmp(level, "tcp")) {
275 if (!strcmp(option, "cork")) {
277 return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
279 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
281 } else if (!strcmp(option, "nodelay")) {
282 return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
284 return luaL_argerror(L, 3, "supported values: cork, nodelay");
286 } else if (!strcmp(level, "ip")) {
287 if (!strcmp(option, "mtu")) {
289 return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MTU, set);
291 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
293 } else if (!strcmp(option, "hdrincl")) {
294 return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_HDRINCL,
296 } else if (!strcmp(option, "multicast_loop")) {
297 return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
299 } else if (!strcmp(option, "multicast_ttl")) {
300 return nixio__gso_int(L, sock->fd, IPPROTO_IP, IP_MULTICAST_TTL,
302 } else if (!strcmp(option, "multicast_if")) {
303 return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_MULTICAST_IF,
305 } else if (!strcmp(option, "add_membership")) {
306 return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
308 } else if (!strcmp(option, "drop_membership")) {
309 return nixio__gso_mreq4(L, sock->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
312 return luaL_argerror(L, 3,
313 "supported values: hdrincl, mtu, multicast_loop, "
314 "multicast_ttl, multicast_if, add_membership, drop_membership");
316 } else if (!strcmp(level, "ipv6")) {
317 if (!strcmp(option, "mtu")) {
319 return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_MTU, set);
321 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
323 } else if (!strcmp(option, "v6only")) {
325 return nixio__gso_int(L, sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, set);
327 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
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);
345 return luaL_argerror(L, 3,
346 "supported values: v6only, mtu, multicast_loop, multicast_hops,"
347 " multicast_if, add_membership, drop_membership");
350 return luaL_argerror(L, 2, "supported values: socket, tcp, ip, ipv6");
357 static int nixio_sock_getsockopt(lua_State *L) {
358 return nixio__getsetsockopt(L, 0);
364 static int nixio_sock_setsockopt(lua_State *L) {
365 return nixio__getsetsockopt(L, 1);
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},
379 void nixio_open_sockopt(lua_State *L) {
380 lua_pushvalue(L, -2);
381 luaL_register(L, NULL, M);
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");