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