nixio: More sockopts
[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 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <net/if.h>
25 #include <sys/time.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include "nixio.h"
30
31
32 /**
33 * setblocking()
34 */
35 static int nixio_sock_setblocking(lua_State *L) {
36 int fd = nixio__checkfd(L, 1);
37 luaL_checkany(L, 2);
38 int set = lua_toboolean(L, 2);
39 int flags = fcntl(fd, F_GETFL);
40
41 if (flags == -1) {
42 return nixio__perror(L);
43 }
44
45 if (!set) {
46 flags |= O_NONBLOCK;
47 } else {
48 flags &= ~O_NONBLOCK;
49 }
50
51 return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
52 }
53
54 static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
55 int value;
56 socklen_t optlen = sizeof(value);
57 if (!set) {
58 if (!getsockopt(fd, level, opt, &value, &optlen)) {
59 lua_pushinteger(L, value);
60 return 1;
61 }
62 } else {
63 value = luaL_checkinteger(L, set);
64 if (!setsockopt(fd, level, opt, &value, optlen)) {
65 lua_pushboolean(L, 1);
66 return 1;
67 }
68 }
69 return nixio__perror(L);
70 }
71
72 static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
73 struct linger value;
74 socklen_t optlen = sizeof(value);
75 if (!set) {
76 if (!getsockopt(fd, level, opt, &value, &optlen)) {
77 lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
78 return 1;
79 }
80 } else {
81 value.l_linger = luaL_checkinteger(L, set);
82 value.l_onoff = value.l_linger ? 1 : 0;
83 if (!setsockopt(fd, level, opt, &value, optlen)) {
84 lua_pushboolean(L, 1);
85 return 1;
86 }
87 }
88 return nixio__perror(L);
89 }
90
91 static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
92 struct timeval value;
93 socklen_t optlen = sizeof(value);
94 if (!set) {
95 if (!getsockopt(fd, level, opt, &value, &optlen)) {
96 lua_pushinteger(L, value.tv_sec);
97 lua_pushinteger(L, value.tv_usec);
98 return 2;
99 }
100 } else {
101 value.tv_sec = luaL_checkinteger(L, set);
102 value.tv_usec = luaL_optinteger(L, set + 1, 0);
103 if (!setsockopt(fd, level, opt, &value, optlen)) {
104 lua_pushboolean(L, 1);
105 return 1;
106 }
107 }
108 return nixio__perror(L);
109 }
110
111 #ifdef SO_BINDTODEVICE
112
113 static int nixio__gso_b(lua_State *L, int fd, int level, int opt, int set) {
114 if (!set) {
115 socklen_t optlen = IFNAMSIZ;
116 char ifname[IFNAMSIZ];
117 if (!getsockopt(fd, level, opt, ifname, &optlen)) {
118 lua_pushlstring(L, ifname, optlen);
119 return 1;
120 }
121 } else {
122 size_t valuelen;
123 const char *value = luaL_checklstring(L, set, &valuelen);
124 luaL_argcheck(L, valuelen <= IFNAMSIZ, set, "invalid interface name");
125 if (!setsockopt(fd, level, opt, value, valuelen)) {
126 lua_pushboolean(L, 1);
127 return 1;
128 }
129 }
130 return nixio__perror(L);
131 }
132
133 #endif /* SO_BINDTODEVICE */
134
135 /**
136 * get/setsockopt() helper
137 */
138 static int nixio__getsetsockopt(lua_State *L, int set) {
139 nixio_sock *sock = nixio__checksock(L);
140 const char *level = luaL_optlstring(L, 2, "", NULL);
141 const char *option = luaL_optlstring(L, 3, "", NULL);
142 set = (set) ? 4 : 0;
143
144 if (!strcmp(level, "socket")) {
145 if (!strcmp(option, "keepalive")) {
146 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
147 } else if (!strcmp(option, "reuseaddr")) {
148 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
149 } else if (!strcmp(option, "rcvbuf")) {
150 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
151 } else if (!strcmp(option, "sndbuf")) {
152 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
153 } else if (!strcmp(option, "priority")) {
154 #ifdef SO_PRIORITY
155 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
156 #else
157 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
158 #endif
159 } else if (!strcmp(option, "broadcast")) {
160 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
161 } else if (!strcmp(option, "dontroute")) {
162 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_DONTROUTE, set);
163 } else if (!strcmp(option, "error")) {
164 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_ERROR, set);
165 } else if (!strcmp(option, "oobinline")) {
166 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_OOBINLINE, set);
167 } else if (!strcmp(option, "linger")) {
168 return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
169 } else if (!strcmp(option, "sndtimeo")) {
170 return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
171 } else if (!strcmp(option, "rcvtimeo")) {
172 return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
173 } else if (!strcmp(option, "bindtodevice")) {
174 #ifdef SO_BINDTODEVICE
175 return nixio__gso_b(L, sock->fd, SOL_SOCKET, SO_BINDTODEVICE, set);
176 #else
177 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
178 #endif
179 } else {
180 return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
181 " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
182 " dontroute, bindtodevice, error, oobinline"
183 );
184 }
185 } else if (!strcmp(level, "tcp")) {
186 if (sock->type != SOCK_STREAM) {
187 return luaL_error(L, "not a TCP socket");
188 }
189 if (!strcmp(option, "cork")) {
190 #ifdef TCP_CORK
191 return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
192 #else
193 return nixio__pstatus(L, !(errno = ENOPROTOOPT));
194 #endif
195 } else if (!strcmp(option, "nodelay")) {
196 return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
197 } else {
198 return luaL_argerror(L, 3, "supported values: cork, nodelay");
199 }
200 } else {
201 return luaL_argerror(L, 2, "supported values: socket, tcp");
202 }
203 }
204
205 /**
206 * getsockopt()
207 */
208 static int nixio_sock_getsockopt(lua_State *L) {
209 return nixio__getsetsockopt(L, 0);
210 }
211
212 /**
213 * setsockopt()
214 */
215 static int nixio_sock_setsockopt(lua_State *L) {
216 return nixio__getsetsockopt(L, 1);
217 }
218
219 /* module table */
220 static const luaL_reg M[] = {
221 {"setblocking", nixio_sock_setblocking},
222 {"getsockopt", nixio_sock_getsockopt},
223 {"setsockopt", nixio_sock_setsockopt},
224 {NULL, NULL}
225 };
226
227 void nixio_open_sockopt(lua_State *L) {
228 lua_pushvalue(L, -2);
229 luaL_register(L, NULL, M);
230 lua_pop(L, 1);
231
232 luaL_getmetatable(L, NIXIO_FILE_META);
233 lua_pushcfunction(L, nixio_sock_setblocking);
234 lua_setfield(L, -2, "setblocking");
235 lua_pop(L, 1);
236 }