Merge pull request #939 from dibdot/master
[project/luci.git] / libs / luci-lib-nixio / src / bind.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 <string.h>
22 #include <unistd.h>
23 #include <errno.h>
24
25
26 /**
27  * connect()/bind() shortcut
28  */
29 static int nixio__bind_connect(lua_State *L, int do_bind) {
30         const char *host = NULL;
31         if (!lua_isnoneornil(L, 1)) {
32                 host = luaL_checklstring(L, 1, NULL);
33         }
34         const char *port = luaL_checklstring(L, 2, NULL);
35         const char *family = luaL_optlstring(L, 3, "any", NULL);
36         const char *socktype = luaL_optlstring(L, 4, "stream", NULL);
37
38         struct addrinfo hints, *result, *rp;
39         memset(&hints, 0, sizeof(hints));
40
41         if (!strcmp(family, "any")) {
42                 hints.ai_family = AF_UNSPEC;
43         } else if (!strcmp(family, "inet")) {
44                 hints.ai_family = AF_INET;
45         } else if (!strcmp(family, "inet6")) {
46                 hints.ai_family = AF_INET6;
47         } else {
48                 return luaL_argerror(L, 3, "supported values: any, inet, inet6");
49         }
50
51         if (!strcmp(socktype, "any")) {
52                 hints.ai_socktype = 0;
53         } else if (!strcmp(socktype, "stream")) {
54                 hints.ai_socktype = SOCK_STREAM;
55         } else if (!strcmp(socktype, "dgram")) {
56                 hints.ai_socktype = SOCK_DGRAM;
57         } else {
58                 return luaL_argerror(L, 4, "supported values: any, stream, dgram");
59         }
60
61         if (do_bind) {
62                 hints.ai_flags |= AI_PASSIVE;
63         }
64
65         hints.ai_protocol = 0;
66
67         int aistat = getaddrinfo(host, port, &hints, &result);
68         if (aistat) {
69                 lua_pushnil(L);
70                 lua_pushinteger(L, aistat);
71                 lua_pushstring(L, gai_strerror(aistat));
72                 return 3;
73         }
74
75         /* create socket object */
76         nixio_sock *sock = lua_newuserdata(L, sizeof(nixio_sock));
77         int status = -1, clstat;
78
79         for (rp = result; rp != NULL; rp = rp->ai_next) {
80                 sock->fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
81                 if (sock->fd == -1) {
82                         continue;
83                 }
84
85                 if (do_bind) {
86                         int one = 1;
87                         setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR,
88                          (char*)&one, sizeof(one));
89                         status = bind(sock->fd, rp->ai_addr, rp->ai_addrlen);
90                 } else {
91                         do {
92                                 status = connect(sock->fd, rp->ai_addr, rp->ai_addrlen);
93                         } while (status == -1 && errno == EINTR);
94                 }
95
96                 /* on success */
97                 if (!status) {
98                         sock->domain = rp->ai_family;
99                         sock->type = rp->ai_socktype;
100                         sock->protocol = rp->ai_protocol;
101                         break;
102                 }
103
104                 do {
105 #ifndef __WINNT__
106                         clstat = close(sock->fd);
107 #else
108                         clstat = closesocket(sock->fd);
109 #endif
110                 } while (clstat == -1 && errno == EINTR);
111         }
112
113         freeaddrinfo(result);
114
115         /* on failure */
116         if (status) {
117                 return nixio__perror_s(L);
118         }
119
120         luaL_getmetatable(L, NIXIO_META);
121         lua_setmetatable(L, -2);
122
123         return 1;
124 }
125
126 /**
127  * bind(host, port, [family=any], [type=any]) shortcut
128  */
129 static int nixio_bind(lua_State *L) {
130         return nixio__bind_connect(L, 1);
131 }
132
133 /**
134  * connect(host, port, [family=any], [type=any]) shortcut
135  */
136 static int nixio_connect(lua_State *L) {
137         return nixio__bind_connect(L, 0);
138 }
139
140 /**
141  * bind()/connect() helper
142  */
143 static int nixio_sock__bind_connect(lua_State *L, int do_bind) {
144         nixio_sock *sock = nixio__checksock(L);
145         int status = -1;
146
147         if (sock->domain == AF_INET || sock->domain == AF_INET6) {
148                 const char *host = NULL;
149                 if (!lua_isnoneornil(L, 2)) {
150                         host = luaL_checklstring(L, 2, NULL);
151                 }
152                 const char *port = luaL_checklstring(L, 3, NULL);
153
154                 struct addrinfo hints, *result, *rp;
155
156                 memset(&hints, 0, sizeof(hints));
157                 hints.ai_family = sock->domain;
158                 hints.ai_socktype = sock->type;
159                 hints.ai_protocol = sock->protocol;
160
161                 if (do_bind) {
162                         hints.ai_flags |= AI_PASSIVE;
163                 }
164
165                 int aistat = getaddrinfo(host, port, &hints, &result);
166                 if (aistat) {
167                         lua_pushnil(L);
168                         lua_pushinteger(L, aistat);
169                         lua_pushstring(L, gai_strerror(aistat));
170                         return 3;
171                 }
172
173                 for (rp = result; rp != NULL; rp = rp->ai_next) {
174                         if (do_bind) {
175                                 status = bind(sock->fd, rp->ai_addr, rp->ai_addrlen);
176                         } else {
177                                 do {
178                                         status = connect(sock->fd, rp->ai_addr, rp->ai_addrlen);
179                                 } while (status == -1 && errno == EINTR);
180                         }
181
182                         /* on success */
183                         if (!status || errno == EINPROGRESS) {
184                                 break;
185                         }
186                 }
187
188                 freeaddrinfo(result);
189 #ifndef __WINNT__
190         } else if (sock->domain == AF_UNIX) {
191                 size_t pathlen;
192                 const char *path = luaL_checklstring(L, 2, &pathlen);
193
194                 struct sockaddr_un addr;
195                 addr.sun_family = AF_UNIX;
196                 luaL_argcheck(L, pathlen <= sizeof(addr.sun_path), 2, "out of range");
197                 memcpy(addr.sun_path, path, pathlen);
198                 socklen_t alen = sizeof(sa_family_t) + pathlen;
199
200                 if (do_bind) {
201                         status = bind(sock->fd, (struct sockaddr*)&addr, alen);
202                 } else {
203                         do {
204                                 status = connect(sock->fd, (struct sockaddr*)&addr, alen);
205                         } while (status == -1 && errno == EINTR);
206                 }
207 #endif
208         } else {
209                 return luaL_error(L, "not supported");
210         }
211         return nixio__pstatus_s(L, !status);
212 }
213
214 /**
215  * bind()
216  */
217 static int nixio_sock_bind(lua_State *L) {
218         return nixio_sock__bind_connect(L, 1);
219 }
220
221 /**
222  * connect()
223  */
224 static int nixio_sock_connect(lua_State *L) {
225         return nixio_sock__bind_connect(L, 0);
226 }
227
228 /**
229  * listen()
230  */
231 static int nixio_sock_listen(lua_State *L) {
232         int sockfd = nixio__checksockfd(L);
233         int backlog = luaL_checkinteger(L, 2);
234         return nixio__pstatus_s(L, !listen(sockfd, backlog));
235 }
236
237 /**
238  * accept()
239  */
240 static int nixio_sock_accept(lua_State *L) {
241         nixio_sock *sock = nixio__checksock(L);
242         struct sockaddr_storage saddr;
243         nixio_addr addr;
244         socklen_t saddrlen = sizeof(saddr);
245         int newfd;
246
247         do {
248                 newfd = accept(sock->fd, (struct sockaddr *)&saddr, &saddrlen);
249         } while (newfd == -1 && errno == EINTR);
250         if (newfd < 0) {
251                 return nixio__perror_s(L);
252         }
253
254         /* create userdata */
255         nixio_sock *clsock = lua_newuserdata(L, sizeof(nixio_sock));
256         luaL_getmetatable(L, NIXIO_META);
257         lua_setmetatable(L, -2);
258
259         memcpy(clsock, sock, sizeof(clsock));
260         clsock->fd = newfd;
261
262         if (!nixio__addr_parse(&addr, (struct sockaddr *)&saddr)) {
263                 lua_pushstring(L, addr.host);
264                 lua_pushinteger(L, addr.port);
265                 return 3;
266         } else {
267                 return 1;
268         }
269 }
270
271 /* module table */
272 static const luaL_reg R[] = {
273         {"bind",                nixio_bind},
274         {"connect",             nixio_connect},
275         {NULL,                  NULL}
276 };
277
278 /* object table */
279 static const luaL_reg M[] = {
280         {"bind",                nixio_sock_bind},
281         {"connect",             nixio_sock_connect},
282         {"listen",              nixio_sock_listen},
283         {"accept",              nixio_sock_accept},
284         {NULL,                  NULL}
285 };
286
287 void nixio_open_bind(lua_State *L) {
288         luaL_register(L, NULL, R);
289
290         lua_pushvalue(L, -2);
291         luaL_register(L, NULL, M);
292         lua_pop(L, 1);
293 }