5965eaefe212d35ca66404a17e5ee633f1714985
[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 static int nixio__gso_b(lua_State *L, int fd, int level, int opt, int set) {
112         if (!set) {
113                 socklen_t optlen = IFNAMSIZ;
114                 char ifname[IFNAMSIZ];
115                 if (!getsockopt(fd, level, opt, ifname, &optlen)) {
116                         lua_pushlstring(L, ifname, optlen);
117                         return 1;
118                 }
119         } else {
120                 size_t valuelen;
121                 const char *value = luaL_checklstring(L, set, &valuelen);
122                 luaL_argcheck(L, valuelen <= IFNAMSIZ, set, "invalid interface name");
123                 if (!setsockopt(fd, level, opt, value, valuelen)) {
124                         lua_pushboolean(L, 1);
125                         return 1;
126                 }
127         }
128         return nixio__perror(L);
129 }
130
131 /**
132  * get/setsockopt() helper
133  */
134 static int nixio__getsetsockopt(lua_State *L, int set) {
135         nixio_sock *sock = nixio__checksock(L);
136         const char *level = luaL_optlstring(L, 2, "", NULL);
137         const char *option = luaL_optlstring(L, 3, "", NULL);
138         set = (set) ? 4 : 0;
139
140         if (!strcmp(level, "socket")) {
141                 if (!strcmp(option, "keepalive")) {
142                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
143                 } else if (!strcmp(option, "reuseaddr")) {
144                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
145                 } else if (!strcmp(option, "rcvbuf")) {
146                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
147                 } else if (!strcmp(option, "sndbuf")) {
148                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
149                 } else if (!strcmp(option, "priority")) {
150 #ifdef SO_PRIORITY
151                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
152 #else
153                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
154 #endif
155                 } else if (!strcmp(option, "broadcast")) {
156                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
157                 } else if (!strcmp(option, "dontroute")) {
158                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_DONTROUTE, set);
159                 } else if (!strcmp(option, "linger")) {
160                         return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
161                 } else if (!strcmp(option, "sndtimeo")) {
162                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
163                 } else if (!strcmp(option, "rcvtimeo")) {
164                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
165                 } else if (!strcmp(option, "bindtodevice")) {
166                         return nixio__gso_b(L, sock->fd, SOL_SOCKET, SO_BINDTODEVICE, set);
167                 } else {
168                         return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
169                          " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo,"
170                          " dontroute, bindtodevice"
171                         );
172                 }
173         } else if (!strcmp(level, "tcp")) {
174                 if (sock->type != SOCK_STREAM) {
175                         return luaL_error(L, "not a TCP socket");
176                 }
177                 if (!strcmp(option, "cork")) {
178 #ifdef TCP_CORK
179                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
180 #else
181                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
182 #endif
183                 } else if (!strcmp(option, "nodelay")) {
184                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
185                 } else {
186                         return luaL_argerror(L, 3, "supported values: cork, nodelay");
187                 }
188         } else {
189                 return luaL_argerror(L, 2, "supported values: socket, tcp");
190         }
191 }
192
193 /**
194  * getsockopt()
195  */
196 static int nixio_sock_getsockopt(lua_State *L) {
197         return nixio__getsetsockopt(L, 0);
198 }
199
200 /**
201  * setsockopt()
202  */
203 static int nixio_sock_setsockopt(lua_State *L) {
204         return nixio__getsetsockopt(L, 1);
205 }
206
207 /* module table */
208 static const luaL_reg M[] = {
209         {"setblocking", nixio_sock_setblocking},
210         {"getsockopt",  nixio_sock_getsockopt},
211         {"setsockopt",  nixio_sock_setsockopt},
212         {NULL,                  NULL}
213 };
214
215 void nixio_open_sockopt(lua_State *L) {
216         lua_pushvalue(L, -2);
217         luaL_register(L, NULL, M);
218         lua_pop(L, 1);
219
220         luaL_getmetatable(L, NIXIO_FILE_META);
221         lua_pushcfunction(L, nixio_sock_setblocking);
222         lua_setfield(L, -2, "setblocking");
223         lua_pop(L, 1);
224 }