Merge 0.9->0.10
[prosody.git] / util-src / net.c
1 /* Prosody IM
2 --
3 -- This project is MIT/X11 licensed. Please see the
4 -- COPYING file in the source package for more information.
5 --
6 -- Copyright (C) 2012 Paul Aurich
7 -- Copyright (C) 2013 Matthew Wild
8 -- Copyright (C) 2013 Florian Zeitz
9 --
10 */
11
12 #include <stddef.h>
13 #include <string.h>
14 #include <errno.h>
15
16 #ifndef _WIN32
17   #include <sys/ioctl.h>
18   #include <sys/types.h>
19   #include <sys/socket.h>
20   #include <net/if.h>
21   #include <ifaddrs.h>
22   #include <arpa/inet.h>
23   #include <netinet/in.h>
24 #endif
25
26 #include <lua.h>
27 #include <lauxlib.h>
28
29 #if (LUA_VERSION_NUM == 502)
30 #define luaL_register(L, N, R) luaL_setfuncs(L, R, 0)
31 #endif
32
33 /* Enumerate all locally configured IP addresses */
34
35 const char * const type_strings[] = {
36         "both",
37         "ipv4",
38         "ipv6",
39         NULL
40 };
41
42 static int lc_local_addresses(lua_State *L)
43 {
44 #ifndef _WIN32
45         /* Link-local IPv4 addresses; see RFC 3927 and RFC 5735 */
46         const long ip4_linklocal = htonl(0xa9fe0000); /* 169.254.0.0 */
47         const long ip4_mask      = htonl(0xffff0000);
48         struct ifaddrs *addr = NULL, *a;
49 #endif
50         int n = 1;
51         int type = luaL_checkoption(L, 1, "both", type_strings);
52         const char link_local = lua_toboolean(L, 2); /* defaults to 0 (false) */
53         const char ipv4 = (type == 0 || type == 1);
54         const char ipv6 = (type == 0 || type == 2);
55
56 #ifndef _WIN32
57         if (getifaddrs(&addr) < 0) {
58                 lua_pushnil(L);
59                 lua_pushfstring(L, "getifaddrs failed (%d): %s", errno,
60                 strerror(errno));
61                 return 2;
62         }
63 #endif
64         lua_newtable(L);
65
66 #ifndef _WIN32
67         for (a = addr; a; a = a->ifa_next) {
68                 int family;
69                 char ipaddr[INET6_ADDRSTRLEN];
70                 const char *tmp = NULL;
71
72                 if (a->ifa_addr == NULL || a->ifa_flags & IFF_LOOPBACK)
73                         continue;
74
75                 family = a->ifa_addr->sa_family;
76
77                 if (ipv4 && family == AF_INET) {
78                         struct sockaddr_in *sa = (struct sockaddr_in *)a->ifa_addr;
79                         if (!link_local &&((sa->sin_addr.s_addr & ip4_mask) == ip4_linklocal))
80                                 continue;
81                         tmp = inet_ntop(family, &sa->sin_addr, ipaddr, sizeof(ipaddr));
82                 } else if (ipv6 && family == AF_INET6) {
83                         struct sockaddr_in6 *sa = (struct sockaddr_in6 *)a->ifa_addr;
84                         if (!link_local && IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
85                                 continue;
86                         if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr) || IN6_IS_ADDR_V4COMPAT(&sa->sin6_addr))
87                                 continue;
88                         tmp = inet_ntop(family, &sa->sin6_addr, ipaddr, sizeof(ipaddr));
89                 }
90
91                 if (tmp != NULL) {
92                         lua_pushstring(L, tmp);
93                         lua_rawseti(L, -2, n++);
94                 }
95                 /* TODO: Error reporting? */
96         }
97
98         freeifaddrs(addr);
99 #else
100         if (ipv4) {
101                 lua_pushstring(L, "0.0.0.0");
102                 lua_rawseti(L, -2, n++);
103         }
104         if (ipv6) {
105                 lua_pushstring(L, "::");
106                 lua_rawseti(L, -2, n++);
107         }
108 #endif
109         return 1;
110 }
111
112 int luaopen_util_net(lua_State* L)
113 {
114         luaL_Reg exports[] = {
115                 { "local_addresses", lc_local_addresses },
116                 { NULL, NULL }
117         };
118
119         lua_newtable(L);
120         luaL_register(L, NULL,  exports);
121         return 1;
122 }