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