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