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