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