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