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 <errno.h>
22 #include <string.h>
23
24 #ifdef __linux__
25 #include <linux/netdevice.h>
26
27 /* struct net_device_stats is buggy on amd64, redefine it */
28 struct nixio__nds {
29      uint32_t rx_packets;
30      uint32_t tx_packets;
31      uint32_t rx_bytes;
32      uint32_t tx_bytes;
33      uint32_t rx_errors;
34      uint32_t tx_errors;
35      uint32_t rx_dropped;
36      uint32_t tx_dropped;
37      uint32_t multicast;
38      uint32_t collisions;
39
40      uint32_t rx_length_errors;
41      uint32_t rx_over_errors;
42      uint32_t rx_crc_errors;
43      uint32_t rx_frame_errors;
44      uint32_t rx_fifo_errors;
45      uint32_t rx_missed_errors;
46
47      uint32_t tx_aborted_errors;
48      uint32_t tx_carrier_errors;
49      uint32_t tx_fifo_errors;
50      uint32_t tx_heartbeat_errors;
51      uint32_t tx_window_errors;
52
53      uint32_t rx_compressed;
54      uint32_t tx_compressed;
55 };
56 #endif
57
58 #ifndef NI_MAXHOST
59 #define NI_MAXHOST 1025
60 #endif
61
62 /**
63  * address pushing helper
64  */
65 int nixio__addr_parse(nixio_addr *addr, struct sockaddr *saddr) {
66         void *baddr;
67
68         addr->family = saddr->sa_family;
69         if (saddr->sa_family == AF_INET) {
70                 struct sockaddr_in *inetaddr = (struct sockaddr_in*)saddr;
71                 addr->port = ntohs(inetaddr->sin_port);
72                 baddr = &inetaddr->sin_addr;
73         } else if (saddr->sa_family == AF_INET6) {
74                 struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)saddr;
75                 addr->port = ntohs(inet6addr->sin6_port);
76                 baddr = &inet6addr->sin6_addr;
77 #ifdef AF_PACKET
78         } else if (saddr->sa_family == AF_PACKET) {
79                 struct sockaddr_ll *etheradddr = (struct sockaddr_ll*)saddr;
80                 addr->prefix = etheradddr->sll_hatype;
81                 addr->port = etheradddr->sll_ifindex;
82                 char *c = addr->host;
83                 for (size_t i = 0; i < etheradddr->sll_halen; i++) {
84                         *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0xf0) >> 4];
85                         *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0x0f)];
86                         *c++ = ':';
87                 }
88                 *(c-1) = 0;
89                 return 0;
90 #endif
91         } else {
92                 errno = EAFNOSUPPORT;
93                 return -1;
94         }
95
96         if (!inet_ntop(saddr->sa_family, baddr, addr->host, sizeof(addr->host))) {
97                 return -1;
98         }
99
100         return 0;
101 }
102
103 /**
104  * address pulling helper
105  */
106 int nixio__addr_write(nixio_addr *addr, struct sockaddr *saddr) {
107         if (addr->family == AF_UNSPEC) {
108                 if (strchr(addr->host, ':')) {
109                         addr->family = AF_INET6;
110                 } else {
111                         addr->family = AF_INET;
112                 }
113         }
114         if (addr->family == AF_INET) {
115                 struct sockaddr_in *inetaddr = (struct sockaddr_in *)saddr;
116                 memset(inetaddr, 0, sizeof(struct sockaddr_in));
117
118                 if (inet_pton(AF_INET, addr->host, &inetaddr->sin_addr) < 1) {
119                         return -1;
120                 }
121
122                 inetaddr->sin_family = AF_INET;
123                 inetaddr->sin_port = htons((uint16_t)addr->port);
124                 return 0;
125         } else if (addr->family == AF_INET6) {
126                 struct sockaddr_in6 *inet6addr = (struct sockaddr_in6 *)saddr;
127                 memset(inet6addr, 0, sizeof(struct sockaddr_in6));
128
129                 if (inet_pton(AF_INET6, addr->host, &inet6addr->sin6_addr) < 1) {
130                         return -1;
131                 }
132
133                 inet6addr->sin6_family = AF_INET6;
134                 inet6addr->sin6_port = htons((uint16_t)addr->port);
135                 return 0;
136         } else {
137                 errno = EAFNOSUPPORT;
138                 return -1;
139         }
140 }
141
142 /**
143  * netmask to prefix helper
144  */
145 int nixio__addr_prefix(struct sockaddr *saddr) {
146         int prefix = 0;
147         size_t len;
148         uint8_t *addr;
149
150         if (saddr->sa_family == AF_INET) {
151                 addr = (uint8_t*)(&((struct sockaddr_in*)saddr)->sin_addr);
152                 len = 4;
153         } else if (saddr->sa_family == AF_INET6) {
154                 addr = (uint8_t*)(&((struct sockaddr_in6*)saddr)->sin6_addr);
155                 len = 16;
156         } else {
157                 errno = EAFNOSUPPORT;
158                 return -1;
159         }
160
161         for (size_t i = 0; i < len; i++) {
162                 if (addr[i] == 0xff) {
163                         prefix += 8;
164                 } else if (addr[i] == 0x00) {
165                         break;
166                 } else {
167                         for (uint8_t c = addr[i]; c; c <<= 1) {
168                                 prefix++;
169                         }
170                 }
171         }
172
173         return prefix;
174 }
175
176 /**
177  * getaddrinfo(host, family, port)
178  */
179 static int nixio_getaddrinfo(lua_State *L) {
180         const char *host = NULL;
181         if (!lua_isnoneornil(L, 1)) {
182                 host = luaL_checklstring(L, 1, NULL);
183         }
184         const char *family = luaL_optlstring(L, 2, "any", NULL);
185         const char *port = lua_tolstring(L, 3, NULL);
186
187         struct addrinfo hints, *result, *rp;
188         memset(&hints, 0, sizeof(hints));
189
190         if (!strcmp(family, "any")) {
191                 hints.ai_family = AF_UNSPEC;
192         } else if (!strcmp(family, "inet")) {
193                 hints.ai_family = AF_INET;
194         } else if (!strcmp(family, "inet6")) {
195                 hints.ai_family = AF_INET6;
196         } else {
197                 return luaL_argerror(L, 2, "supported values: any, inet, inet6");
198         }
199
200         hints.ai_socktype = 0;
201         hints.ai_protocol = 0;
202
203         int aistat = getaddrinfo(host, port, &hints, &result);
204         if (aistat) {
205                 lua_pushnil(L);
206                 lua_pushinteger(L, aistat);
207                 lua_pushstring(L, gai_strerror(aistat));
208                 return 3;
209         }
210
211         /* create socket object */
212         lua_newtable(L);
213         int i = 1;
214
215         for (rp = result; rp != NULL; rp = rp->ai_next) {
216                 /* avoid duplicate results */
217 #ifndef __WINNT__
218                 if (!port && rp->ai_socktype != SOCK_STREAM) {
219                         continue;
220                 }
221 #endif
222
223                 if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
224                         lua_createtable(L, 0, port ? 4 : 2);
225                         if (rp->ai_family == AF_INET) {
226                                 lua_pushliteral(L, "inet");
227                         } else if (rp->ai_family == AF_INET6) {
228                                 lua_pushliteral(L, "inet6");
229                         }
230                         lua_setfield(L, -2, "family");
231
232                         if (port) {
233                                 switch (rp->ai_socktype) {
234                                         case SOCK_STREAM:
235                                                 lua_pushliteral(L, "stream");
236                                                 break;
237                                         case SOCK_DGRAM:
238                                                 lua_pushliteral(L, "dgram");
239                                                 break;
240                                         case SOCK_RAW:
241                                                 lua_pushliteral(L, "raw");
242                                                 break;
243                                         default:
244                                                 lua_pushnil(L);
245                                                 break;
246                                 }
247                                 lua_setfield(L, -2, "socktype");
248                         }
249
250                         nixio_addr addr;
251                         if (nixio__addr_parse(&addr, rp->ai_addr)) {
252                                 freeaddrinfo(result);
253                                 return nixio__perror_s(L);
254                         }
255
256                         if (port) {
257                                 lua_pushinteger(L, addr.port);
258                                 lua_setfield(L, -2, "port");
259                         }
260
261                         lua_pushstring(L, addr.host);
262                         lua_setfield(L, -2, "address");
263                         lua_rawseti(L, -2, i++);
264                 }
265         }
266
267         freeaddrinfo(result);
268
269         return 1;
270 }
271
272 /**
273  * getnameinfo(address, family)
274  */
275 static int nixio_getnameinfo(lua_State *L) {
276         const char *ip = luaL_checkstring(L, 1);
277         const char *family = luaL_optstring(L, 2, NULL);
278         char host[NI_MAXHOST];
279
280         struct sockaddr_storage saddr;
281         nixio_addr addr;
282         memset(&addr, 0, sizeof(addr));
283         strncpy(addr.host, ip, sizeof(addr.host) - 1);
284
285         if (!family) {
286                 addr.family = AF_UNSPEC;
287         } else if (!strcmp(family, "inet")) {
288                 addr.family = AF_INET;
289         } else if (!strcmp(family, "inet6")) {
290                 addr.family = AF_INET6;
291         } else {
292                 return luaL_argerror(L, 2, "supported values: inet, inet6");
293         }
294
295         nixio__addr_write(&addr, (struct sockaddr *)&saddr);
296
297         int res = getnameinfo((struct sockaddr *)&saddr, sizeof(saddr),
298          host, sizeof(host), NULL, 0, NI_NAMEREQD);
299         if (res) {
300                 lua_pushnil(L);
301                 lua_pushinteger(L, res);
302                 lua_pushstring(L, gai_strerror(res));
303                 return 3;
304         } else {
305                 lua_pushstring(L, host);
306                 return 1;
307         }
308 }
309
310 /**
311  * getsockname()
312  */
313 static int nixio_sock_getsockname(lua_State *L) {
314         int sockfd = nixio__checksockfd(L);
315         struct sockaddr_storage saddr;
316         socklen_t addrlen = sizeof(saddr);
317         nixio_addr addr;
318
319         if (getsockname(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
320          nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
321                 return nixio__perror_s(L);
322         }
323
324         lua_pushstring(L, addr.host);
325         lua_pushnumber(L, addr.port);
326         return 2;
327 }
328
329 /**
330  * getpeername()
331  */
332 static int nixio_sock_getpeername(lua_State *L) {
333         int sockfd = nixio__checksockfd(L);
334         struct sockaddr_storage saddr;
335         socklen_t addrlen = sizeof(saddr);
336         nixio_addr addr;
337
338         if (getpeername(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
339          nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
340                 return nixio__perror_s(L);
341         }
342
343         lua_pushstring(L, addr.host);
344         lua_pushnumber(L, addr.port);
345         return 2;
346 }
347
348 #if defined(__linux__) || defined(BSD)
349 #include <ifaddrs.h>
350
351 static int nixio_getifaddrs(lua_State *L) {
352         nixio_addr addr;
353         struct ifaddrs *ifaddr, *c;
354         if (getifaddrs(&ifaddr) == -1) {
355                 return nixio__perror(L);
356         }
357
358         lua_newtable(L);
359         unsigned int i = 1;
360
361         for (c = ifaddr; c; c = c->ifa_next) {
362                 lua_newtable(L);
363
364                 lua_pushstring(L, c->ifa_name);
365                 lua_setfield(L, -2, "name");
366
367                 lua_createtable(L, 0, 7);
368                         lua_pushboolean(L, c->ifa_flags & IFF_UP);
369                         lua_setfield(L, -2, "up");
370
371                         lua_pushboolean(L, c->ifa_flags & IFF_BROADCAST);
372                         lua_setfield(L, -2, "broadcast");
373
374                         lua_pushboolean(L, c->ifa_flags & IFF_LOOPBACK);
375                         lua_setfield(L, -2, "loopback");
376
377                         lua_pushboolean(L, c->ifa_flags & IFF_POINTOPOINT);
378                         lua_setfield(L, -2, "pointtopoint");
379
380                         lua_pushboolean(L, c->ifa_flags & IFF_NOARP);
381                         lua_setfield(L, -2, "noarp");
382
383                         lua_pushboolean(L, c->ifa_flags & IFF_PROMISC);
384                         lua_setfield(L, -2, "promisc");
385
386                         lua_pushboolean(L, c->ifa_flags & IFF_MULTICAST);
387                         lua_setfield(L, -2, "multicast");
388                 lua_setfield(L, -2, "flags");
389
390                 if (c->ifa_addr && !nixio__addr_parse(&addr, c->ifa_addr)) {
391                         lua_pushstring(L, addr.host);
392                         lua_setfield(L, -2, "addr");
393
394                         if (c->ifa_addr->sa_family == AF_INET) {
395                                 lua_pushliteral(L, "inet");
396                         } else if (c->ifa_addr->sa_family == AF_INET6) {
397                                 lua_pushliteral(L, "inet6");
398 #ifdef AF_PACKET
399                         } else if (c->ifa_addr->sa_family == AF_PACKET) {
400                                 lua_pushliteral(L, "packet");
401 #endif
402                         } else {
403                                 lua_pushliteral(L, "unknown");
404                         }
405                         lua_setfield(L, -2, "family");
406
407 #ifdef __linux__
408                         if (c->ifa_addr->sa_family == AF_PACKET) {
409                                 lua_pushinteger(L, addr.port);
410                                 lua_setfield(L, -2, "ifindex");
411
412                                 lua_pushinteger(L, addr.prefix);
413                                 lua_setfield(L, -2, "hatype");
414
415                                 if (c->ifa_data) {
416                                         lua_createtable(L, 0, 10);
417                                         struct nixio__nds *stats = c->ifa_data;
418
419                                         lua_pushnumber(L, stats->rx_packets);
420                                         lua_setfield(L, -2, "rx_packets");
421
422                                         lua_pushnumber(L, stats->tx_packets);
423                                         lua_setfield(L, -2, "tx_packets");
424
425                                         lua_pushnumber(L, stats->rx_bytes);
426                                         lua_setfield(L, -2, "rx_bytes");
427
428                                         lua_pushnumber(L, stats->tx_bytes);
429                                         lua_setfield(L, -2, "tx_bytes");
430
431                                         lua_pushnumber(L, stats->rx_errors);
432                                         lua_setfield(L, -2, "rx_errors");
433
434                                         lua_pushnumber(L, stats->tx_errors);
435                                         lua_setfield(L, -2, "tx_errors");
436
437                                         lua_pushnumber(L, stats->rx_dropped);
438                                         lua_setfield(L, -2, "rx_dropped");
439
440                                         lua_pushnumber(L, stats->tx_dropped);
441                                         lua_setfield(L, -2, "tx_dropped");
442
443                                         lua_pushnumber(L, stats->multicast);
444                                         lua_setfield(L, -2, "multicast");
445
446                                         lua_pushnumber(L, stats->collisions);
447                                         lua_setfield(L, -2, "collisions");
448                                 } else {
449                                         lua_newtable(L);
450                                 }
451                                 lua_setfield(L, -2, "data");
452                         }
453 #endif
454                 }
455
456                 if (c->ifa_netmask && !nixio__addr_parse(&addr, c->ifa_netmask)) {
457                         lua_pushstring(L, addr.host);
458                         lua_setfield(L, -2, "netmask");
459
460                         lua_pushinteger(L, nixio__addr_prefix(c->ifa_netmask));
461                         lua_setfield(L, -2, "prefix");
462                 }
463
464                 if (c->ifa_broadaddr && !nixio__addr_parse(&addr, c->ifa_broadaddr)) {
465                         lua_pushstring(L, addr.host);
466                         lua_setfield(L, -2, "broadaddr");
467                 }
468
469                 if (c->ifa_dstaddr && !nixio__addr_parse(&addr, c->ifa_dstaddr)) {
470                         lua_pushstring(L, addr.host);
471                         lua_setfield(L, -2, "dstaddr");
472                 }
473
474                 lua_rawseti(L, -2, i++);
475         }
476
477         freeifaddrs(ifaddr);
478         return 1;
479 }
480 #endif
481
482
483 /* module table */
484 static const luaL_reg R[] = {
485 #if defined(__linux__) || defined(BSD)
486         {"getifaddrs",  nixio_getifaddrs},
487 #endif
488         {"getaddrinfo", nixio_getaddrinfo},
489         {"getnameinfo", nixio_getnameinfo},
490         {NULL,                  NULL}
491 };
492
493 /* object table */
494 static const luaL_reg M[] = {
495         {"getsockname", nixio_sock_getsockname},
496         {"getpeername", nixio_sock_getpeername},
497         {NULL,                  NULL}
498 };
499
500 void nixio_open_address(lua_State *L) {
501         luaL_register(L, NULL, R);
502
503         lua_pushvalue(L, -2);
504         luaL_register(L, NULL, M);
505         lua_pop(L, 1);
506 }