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.
21 #include <sys/types.h>
27 #ifndef IPV6_ADD_MEMBERSHIP
28 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
31 #ifndef IPV6_DROP_MEMBERSHIP
32 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
35 static int nixio_sock_fileno(lua_State
*L
) {
36 lua_pushinteger(L
, nixio__checkfd(L
, 1));
43 static int nixio_sock_setblocking(lua_State
*L
) {
44 int fd
= nixio__checkfd(L
, 1);
46 int set
= lua_toboolean(L
, 2);
50 int flags
= fcntl(fd
, F_GETFL
);
53 return nixio__perror(L
);
62 return nixio__pstatus(L
, !fcntl(fd
, F_SETFL
, flags
));
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
));
72 WSASetLastError(WSAENOTSOCK
);
73 return nixio__perror_s(L
);
76 #endif /* __WINNT__ */
79 static int nixio__gso_int(lua_State
*L
, int fd
, int level
, int opt
, int set
) {
81 socklen_t optlen
= sizeof(value
);
83 if (!getsockopt(fd
, level
, opt
, (char *)&value
, &optlen
)) {
84 lua_pushinteger(L
, value
);
88 value
= luaL_checkinteger(L
, set
);
89 if (!setsockopt(fd
, level
, opt
, (char *)&value
, optlen
)) {
90 lua_pushboolean(L
, 1);
94 return nixio__perror_s(L
);
97 static int nixio__gso_ling(lua_State
*L
, int fd
, int level
, int opt
, int set
) {
99 socklen_t optlen
= sizeof(value
);
101 if (!getsockopt(fd
, level
, opt
, (char *)&value
, &optlen
)) {
102 lua_pushinteger(L
, value
.l_onoff
? value
.l_linger
: 0);
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);
113 return nixio__perror_s(L
);
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
);
120 if (!getsockopt(fd
, level
, opt
, (char *)&value
, &optlen
)) {
121 lua_pushinteger(L
, value
.tv_sec
);
122 lua_pushinteger(L
, value
.tv_usec
);
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);
133 return nixio__perror_s(L
);
136 #ifdef SO_BINDTODEVICE
138 static int nixio__gso_b(lua_State
*L
, int fd
, int level
, int opt
, int 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
);
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);
155 return nixio__perror_s(L
);
158 #endif /* SO_BINDTODEVICE */
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
);
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
);
169 lua_pushstring(L
, buf
);
170 if (!inet_ntop(AF_INET
, &value
.imr_interface
, buf
, sizeof(buf
))) {
171 return nixio__perror_s(L
);
173 lua_pushstring(L
, buf
);
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
);
182 if (inet_pton(AF_INET
, iface
, &value
.imr_interface
) < 1) {
183 return nixio__perror_s(L
);
185 if (!setsockopt(fd
, level
, opt
, (char *)&value
, optlen
)) {
186 lua_pushboolean(L
, 1);
190 return nixio__perror_s(L
);
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
);
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
);
202 lua_pushstring(L
, buf
);
203 lua_pushnumber(L
, val
.ipv6mr_interface
);
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
);
211 val
.ipv6mr_interface
= luaL_optlong(L
, set
+ 1, 0);
212 if (!setsockopt(fd
, level
, opt
, (char *)&val
, optlen
)) {
213 lua_pushboolean(L
, 1);
217 return nixio__perror_s(L
);
221 * get/setsockopt() helper
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
);
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")) {
240 return nixio__gso_int(L
, sock
->fd
, SOL_SOCKET
, SO_PRIORITY
, set
);
242 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
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
);
262 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
265 return luaL_argerror(L
, 3, "supported values: keepalive, reuseaddr,"
266 " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
267 " dontroute, bindtodevice, error, oobinline"
270 } else if (!strcmp(level
, "tcp")) {
271 if (!strcmp(option
, "cork")) {
273 return nixio__gso_int(L
, sock
->fd
, IPPROTO_TCP
, TCP_CORK
, set
);
275 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
277 } else if (!strcmp(option
, "nodelay")) {
278 return nixio__gso_int(L
, sock
->fd
, IPPROTO_TCP
, TCP_NODELAY
, set
);
280 return luaL_argerror(L
, 3, "supported values: cork, nodelay");
282 } else if (!strcmp(level
, "ip")) {
283 if (!strcmp(option
, "mtu")) {
285 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IP
, IP_MTU
, set
);
287 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
289 } else if (!strcmp(option
, "hdrincl")) {
290 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IP
, IP_HDRINCL
,
292 } else if (!strcmp(option
, "multicast_loop")) {
293 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
,
295 } else if (!strcmp(option
, "multicast_ttl")) {
296 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
298 } else if (!strcmp(option
, "multicast_if")) {
299 return nixio__gso_mreq4(L
, sock
->fd
, IPPROTO_IP
, IP_MULTICAST_IF
,
301 } else if (!strcmp(option
, "add_membership")) {
302 return nixio__gso_mreq4(L
, sock
->fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
304 } else if (!strcmp(option
, "drop_membership")) {
305 return nixio__gso_mreq4(L
, sock
->fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
308 return luaL_argerror(L
, 3,
309 "supported values: hdrincl, mtu, multicast_loop, "
310 "multicast_ttl, multicast_if, add_membership, drop_membership");
312 } else if (!strcmp(level
, "ipv6")) {
313 if (!strcmp(option
, "mtu")) {
315 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IPV6
, IPV6_MTU
, set
);
317 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
319 } else if (!strcmp(option
, "v6only")) {
321 return nixio__gso_int(L
, sock
->fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, set
);
323 return nixio__pstatus(L
, !(errno
= ENOPROTOOPT
));
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
);
341 return luaL_argerror(L
, 3,
342 "supported values: v6only, mtu, multicast_loop, multicast_hops,"
343 " multicast_if, add_membership, drop_membership");
346 return luaL_argerror(L
, 2, "supported values: socket, tcp, ip, ipv6");
353 static int nixio_sock_getsockopt(lua_State
*L
) {
354 return nixio__getsetsockopt(L
, 0);
360 static int nixio_sock_setsockopt(lua_State
*L
) {
361 return nixio__getsetsockopt(L
, 1);
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
},
375 void nixio_open_sockopt(lua_State
*L
) {
376 lua_pushvalue(L
, -2);
377 luaL_register(L
, NULL
, M
);
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");