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