luci-lib-nixio: pass exact sockaddr length to getnameinfo()
[project/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 <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,
335 (saddr.ss_family == AF_INET)
336 ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
337 host, sizeof(host), NULL, 0, NI_NAMEREQD);
338
339 #ifdef __linux__
340 if (timeout > 0 && timeout < 1000)
341 {
342 ualarm(0, 0);
343 sigaction(SIGALRM, &sa_old, NULL);
344 }
345 #endif
346
347 if (res) {
348 lua_pushnil(L);
349 lua_pushinteger(L, res);
350 lua_pushstring(L, gai_strerror(res));
351 return 3;
352 } else {
353 lua_pushstring(L, host);
354 return 1;
355 }
356 }
357
358 /**
359 * getsockname()
360 */
361 static int nixio_sock_getsockname(lua_State *L) {
362 int sockfd = nixio__checksockfd(L);
363 struct sockaddr_storage saddr;
364 socklen_t addrlen = sizeof(saddr);
365 nixio_addr addr;
366
367 if (getsockname(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
368 nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
369 return nixio__perror_s(L);
370 }
371
372 lua_pushstring(L, addr.host);
373 lua_pushinteger(L, addr.port);
374 return 2;
375 }
376
377 /**
378 * getpeername()
379 */
380 static int nixio_sock_getpeername(lua_State *L) {
381 int sockfd = nixio__checksockfd(L);
382 struct sockaddr_storage saddr;
383 socklen_t addrlen = sizeof(saddr);
384 nixio_addr addr;
385
386 if (getpeername(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
387 nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
388 return nixio__perror_s(L);
389 }
390
391 lua_pushstring(L, addr.host);
392 lua_pushinteger(L, addr.port);
393 return 2;
394 }
395
396 #if defined(__linux__) || defined(BSD)
397 #ifdef BSD
398 #include <net/if.h>
399 #endif
400 #include <ifaddrs.h>
401
402 static int nixio_getifaddrs(lua_State *L) {
403 nixio_addr addr;
404 struct ifaddrs *ifaddr, *c;
405 if (getifaddrs(&ifaddr) == -1) {
406 return nixio__perror(L);
407 }
408
409 lua_newtable(L);
410 unsigned int i = 1;
411
412 for (c = ifaddr; c; c = c->ifa_next) {
413 lua_newtable(L);
414
415 lua_pushstring(L, c->ifa_name);
416 lua_setfield(L, -2, "name");
417
418 lua_createtable(L, 0, 7);
419 lua_pushboolean(L, c->ifa_flags & IFF_UP);
420 lua_setfield(L, -2, "up");
421
422 lua_pushboolean(L, c->ifa_flags & IFF_BROADCAST);
423 lua_setfield(L, -2, "broadcast");
424
425 lua_pushboolean(L, c->ifa_flags & IFF_LOOPBACK);
426 lua_setfield(L, -2, "loopback");
427
428 lua_pushboolean(L, c->ifa_flags & IFF_POINTOPOINT);
429 lua_setfield(L, -2, "pointtopoint");
430
431 lua_pushboolean(L, c->ifa_flags & IFF_NOARP);
432 lua_setfield(L, -2, "noarp");
433
434 lua_pushboolean(L, c->ifa_flags & IFF_PROMISC);
435 lua_setfield(L, -2, "promisc");
436
437 lua_pushboolean(L, c->ifa_flags & IFF_MULTICAST);
438 lua_setfield(L, -2, "multicast");
439 lua_setfield(L, -2, "flags");
440
441 if (c->ifa_addr) {
442 if (!nixio__addr_parse(&addr, c->ifa_addr)) {
443 lua_pushstring(L, addr.host);
444 lua_setfield(L, -2, "addr");
445 }
446
447 if (c->ifa_addr->sa_family == AF_INET) {
448 lua_pushliteral(L, "inet");
449 } else if (c->ifa_addr->sa_family == AF_INET6) {
450 lua_pushliteral(L, "inet6");
451 #ifdef AF_PACKET
452 } else if (c->ifa_addr->sa_family == AF_PACKET) {
453 lua_pushliteral(L, "packet");
454 #endif
455 } else {
456 lua_pushliteral(L, "unknown");
457 }
458 lua_setfield(L, -2, "family");
459
460 #ifdef __linux__
461 if (c->ifa_addr->sa_family == AF_PACKET) {
462 lua_pushinteger(L, addr.port);
463 lua_setfield(L, -2, "ifindex");
464
465 lua_pushinteger(L, addr.prefix);
466 lua_setfield(L, -2, "hatype");
467 }
468 #endif
469 }
470
471 #ifdef __linux__
472 if (c->ifa_data && (!c->ifa_addr
473 || c->ifa_addr->sa_family == AF_PACKET)) {
474 if (!c->ifa_addr) {
475 lua_pushliteral(L, "packet");
476 lua_setfield(L, -2, "family");
477 }
478
479 lua_createtable(L, 0, 10);
480 struct nixio__nds *stats = c->ifa_data;
481
482 lua_pushnumber(L, stats->rx_packets);
483 lua_setfield(L, -2, "rx_packets");
484
485 lua_pushnumber(L, stats->tx_packets);
486 lua_setfield(L, -2, "tx_packets");
487
488 lua_pushnumber(L, stats->rx_bytes);
489 lua_setfield(L, -2, "rx_bytes");
490
491 lua_pushnumber(L, stats->tx_bytes);
492 lua_setfield(L, -2, "tx_bytes");
493
494 lua_pushnumber(L, stats->rx_errors);
495 lua_setfield(L, -2, "rx_errors");
496
497 lua_pushnumber(L, stats->tx_errors);
498 lua_setfield(L, -2, "tx_errors");
499
500 lua_pushnumber(L, stats->rx_dropped);
501 lua_setfield(L, -2, "rx_dropped");
502
503 lua_pushnumber(L, stats->tx_dropped);
504 lua_setfield(L, -2, "tx_dropped");
505
506 lua_pushnumber(L, stats->multicast);
507 lua_setfield(L, -2, "multicast");
508
509 lua_pushnumber(L, stats->collisions);
510 lua_setfield(L, -2, "collisions");
511 } else {
512 lua_newtable(L);
513 }
514 lua_setfield(L, -2, "data");
515 #endif
516
517 if (c->ifa_netmask && !nixio__addr_parse(&addr, c->ifa_netmask)) {
518 lua_pushstring(L, addr.host);
519 lua_setfield(L, -2, "netmask");
520
521 lua_pushinteger(L, nixio__addr_prefix(c->ifa_netmask));
522 lua_setfield(L, -2, "prefix");
523 }
524
525 if (c->ifa_broadaddr && !nixio__addr_parse(&addr, c->ifa_broadaddr)) {
526 lua_pushstring(L, addr.host);
527 lua_setfield(L, -2, "broadaddr");
528 }
529
530 if (c->ifa_dstaddr && !nixio__addr_parse(&addr, c->ifa_dstaddr)) {
531 lua_pushstring(L, addr.host);
532 lua_setfield(L, -2, "dstaddr");
533 }
534
535 lua_rawseti(L, -2, i++);
536 }
537
538 freeifaddrs(ifaddr);
539 return 1;
540 }
541 #endif
542
543
544 /* module table */
545 static const luaL_reg R[] = {
546 #if defined(__linux__) || defined(BSD)
547 {"getifaddrs", nixio_getifaddrs},
548 #endif
549 {"getaddrinfo", nixio_getaddrinfo},
550 {"getnameinfo", nixio_getnameinfo},
551 {NULL, NULL}
552 };
553
554 /* object table */
555 static const luaL_reg M[] = {
556 {"getsockname", nixio_sock_getsockname},
557 {"getpeername", nixio_sock_getpeername},
558 {NULL, NULL}
559 };
560
561 void nixio_open_address(lua_State *L) {
562 luaL_register(L, NULL, R);
563
564 lua_pushvalue(L, -2);
565 luaL_register(L, NULL, M);
566 lua_pop(L, 1);
567 }