nixio linux IO/socket api (incomplete / WIP)
[project/luci.git] / libs / nixio / src / address.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 <lua.h>
20 #include <lualib.h>
21 #include <lauxlib.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <string.h>
26 #include <netdb.h>
27 #include "nixio.h"
28
29 /**
30  * getaddrinfo(host, family, port)
31  */
32 static int nixio_getaddrinfo(lua_State *L) {
33         const char *host = NULL;
34         if (!lua_isnoneornil(L, 1)) {
35                 host = luaL_checklstring(L, 1, NULL);
36         }
37         const char *family = luaL_optlstring(L, 2, "any", NULL);
38         const char *port = lua_tolstring(L, 3, NULL);
39
40         struct addrinfo hints, *result, *rp;
41         memset(&hints, 0, sizeof(hints));
42
43         if (!strcmp(family, "any")) {
44                 hints.ai_family = AF_UNSPEC;
45         } else if (!strcmp(family, "inet")) {
46                 hints.ai_family = AF_INET;
47         } else if (!strcmp(family, "inet6")) {
48                 hints.ai_family = AF_INET6;
49         } else {
50                 return luaL_argerror(L, 2, "supported values: any, inet, inet6");
51         }
52
53         hints.ai_socktype = 0;
54         hints.ai_protocol = 0;
55
56         int aistat = getaddrinfo(host, port, &hints, &result);
57         if (aistat) {
58                 lua_pushnil(L);
59                 lua_pushinteger(L, aistat);
60                 lua_pushstring(L, gai_strerror(aistat));
61                 return 3;
62         }
63
64         /* create socket object */
65         lua_newtable(L);
66         int i = 1;
67
68         for (rp = result; rp != NULL; rp = rp->ai_next) {
69                 /* avoid duplicate results */
70                 if (!port && rp->ai_socktype != SOCK_STREAM) {
71                         continue;
72                 }
73
74                 if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
75                         lua_createtable(L, 0, port ? 4 : 2);
76                         if (rp->ai_family == AF_INET) {
77                                 lua_pushliteral(L, "inet");
78                         } else if (rp->ai_family == AF_INET6) {
79                                 lua_pushliteral(L, "inet6");
80                         }
81                         lua_setfield(L, -2, "family");
82
83                         if (port) {
84                                 switch (rp->ai_socktype) {
85                                         case SOCK_STREAM:
86                                                 lua_pushliteral(L, "stream");
87                                                 break;
88                                         case SOCK_DGRAM:
89                                                 lua_pushliteral(L, "dgram");
90                                                 break;
91                                         case SOCK_RAW:
92                                                 lua_pushliteral(L, "raw");
93                                                 break;
94                                         default:
95                                                 lua_pushnil(L);
96                                                 break;
97                                 }
98                                 lua_setfield(L, -2, "socktype");
99                         }
100
101                         char ip[INET6_ADDRSTRLEN];
102                         void *binaddr = NULL;
103                         uint16_t binport = 0;
104
105                         if (rp->ai_family == AF_INET) {
106                                 struct sockaddr_in *v4addr = (struct sockaddr_in*)rp->ai_addr;
107                                 binport = v4addr->sin_port;
108                                 binaddr = (void *)&v4addr->sin_addr;
109                         } else if (rp->ai_family == AF_INET6) {
110                                 struct sockaddr_in6 *v6addr = (struct sockaddr_in6*)rp->ai_addr;
111                                 binport = v6addr->sin6_port;
112                                 binaddr = (void *)&v6addr->sin6_addr;
113                         }
114
115                         if (!inet_ntop(rp->ai_family, binaddr, ip, sizeof(ip))) {
116                                 freeaddrinfo(result);
117                                 return nixio__perror(L);
118                         }
119
120                         if (port) {
121                                 lua_pushinteger(L, ntohs(binport));
122                                 lua_setfield(L, -2, "port");
123                         }
124
125                         lua_pushstring(L, ip);
126                         lua_setfield(L, -2, "address");
127                         lua_rawseti(L, -2, i++);
128                 }
129         }
130
131         freeaddrinfo(result);
132
133         return 1;
134 }
135
136 /**
137  * getnameinfo(address, family)
138  */
139 static int nixio_getnameinfo(lua_State *L) {
140         const char *ip = luaL_checklstring(L, 1, NULL);
141         const char *family = luaL_optlstring(L, 2, "inet", NULL);
142         char host[NI_MAXHOST];
143
144         struct sockaddr *addr = NULL;
145         socklen_t alen = 0;
146         int res;
147
148         if (!strcmp(family, "inet")) {
149                 struct sockaddr_in inetaddr;
150                 memset(&inetaddr, 0, sizeof(inetaddr));
151                 inetaddr.sin_family = AF_INET;
152                 if (inet_pton(AF_INET, ip, &inetaddr.sin_addr) < 1) {
153                         return luaL_argerror(L, 1, "invalid address");
154                 }
155                 alen = sizeof(inetaddr);
156                 addr = (struct sockaddr *)&inetaddr;
157         } else if (!strcmp(family, "inet6")) {
158                 struct sockaddr_in6 inet6addr;
159                 memset(&inet6addr, 0, sizeof(inet6addr));
160                 inet6addr.sin6_family = AF_INET6;
161                 if (inet_pton(AF_INET6, ip, &inet6addr.sin6_addr) < 1) {
162                         return luaL_argerror(L, 1, "invalid address");
163                 }
164                 alen = sizeof(inet6addr);
165                 addr = (struct sockaddr *)&inet6addr;
166         } else {
167                 return luaL_argerror(L, 2, "supported values: inet, inet6");
168         }
169
170         res = getnameinfo(addr, alen, host, sizeof(host), NULL, 0, NI_NAMEREQD);
171         if (res) {
172                 lua_pushnil(L);
173                 lua_pushinteger(L, res);
174                 lua_pushstring(L, gai_strerror(res));
175                 return 3;
176         } else {
177                 lua_pushstring(L, host);
178                 return 1;
179         }
180 }
181
182 /**
183  * getsockname() / getpeername() helper
184  */
185 static int nixio_sock__getname(lua_State *L, int sock) {
186         int sockfd = nixio__checksockfd(L);
187         struct sockaddr_storage addr;
188         socklen_t addrlen = sizeof(addr);
189         char ipaddr[INET6_ADDRSTRLEN];
190         void *binaddr;
191         uint16_t port;
192
193         if (sock) {
194                 if (getsockname(sockfd, (struct sockaddr*)&addr, &addrlen)) {
195                         return nixio__perror(L);
196                 }
197         } else {
198                 if (getpeername(sockfd, (struct sockaddr*)&addr, &addrlen)) {
199                         return nixio__perror(L);
200                 }
201         }
202
203         if (addr.ss_family == AF_INET) {
204                 struct sockaddr_in *inetaddr = (struct sockaddr_in*)&addr;
205                 port = inetaddr->sin_port;
206                 binaddr = &inetaddr->sin_addr;
207         } else if (addr.ss_family == AF_INET6) {
208                 struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)&addr;
209                 port = inet6addr->sin6_port;
210                 binaddr = &inet6addr->sin6_addr;
211         } else {
212                 return luaL_error(L, "unknown address family");
213         }
214
215         if (!inet_ntop(addr.ss_family, binaddr, ipaddr, sizeof(ipaddr))) {
216                 return nixio__perror(L);
217         }
218
219         lua_pushstring(L, ipaddr);
220         lua_pushinteger(L, ntohs(port));
221         return 2;
222 }
223
224 /**
225  * getsockname()
226  */
227 static int nixio_sock_getsockname(lua_State *L) {
228         return nixio_sock__getname(L, 1);
229 }
230
231 /**
232  * getpeername()
233  */
234 static int nixio_sock_getpeername(lua_State *L) {
235         return nixio_sock__getname(L, 0);
236 }
237
238
239 /* module table */
240 static const luaL_reg R[] = {
241         {"getaddrinfo", nixio_getaddrinfo},
242         {"getnameinfo", nixio_getnameinfo},
243         {NULL,                  NULL}
244 };
245
246 /* object table */
247 static const luaL_reg M[] = {
248         {"getsockname", nixio_sock_getsockname},
249         {"getpeername", nixio_sock_getpeername},
250         {NULL,                  NULL}
251 };
252
253 void nixio_open_address(lua_State *L) {
254         luaL_register(L, NULL, R);
255
256         lua_pushvalue(L, -2);
257         luaL_register(L, NULL, M);
258         lua_pop(L, 1);
259 }