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