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