Merge 0.10->trunk
[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 == 501)
30 #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R)
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 #ifndef _WIN32
44         /* Link-local IPv4 addresses; see RFC 3927 and RFC 5735 */
45         const long ip4_linklocal = htonl(0xa9fe0000); /* 169.254.0.0 */
46         const long ip4_mask      = htonl(0xffff0000);
47         struct ifaddrs* addr = NULL, *a;
48 #endif
49         int n = 1;
50         int type = luaL_checkoption(L, 1, "both", type_strings);
51         const char link_local = lua_toboolean(L, 2); /* defaults to 0 (false) */
52         const char ipv4 = (type == 0 || type == 1);
53         const char ipv6 = (type == 0 || type == 2);
54
55 #ifndef _WIN32
56
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
64 #endif
65         lua_newtable(L);
66
67 #ifndef _WIN32
68
69         for(a = addr; a; a = a->ifa_next) {
70                 int family;
71                 char ipaddr[INET6_ADDRSTRLEN];
72                 const char* tmp = NULL;
73
74                 if(a->ifa_addr == NULL || a->ifa_flags & IFF_LOOPBACK) {
75                         continue;
76                 }
77
78                 family = a->ifa_addr->sa_family;
79
80                 if(ipv4 && family == AF_INET) {
81                         struct sockaddr_in* sa = (struct sockaddr_in*)a->ifa_addr;
82
83                         if(!link_local && ((sa->sin_addr.s_addr & ip4_mask) == ip4_linklocal)) {
84                                 continue;
85                         }
86
87                         tmp = inet_ntop(family, &sa->sin_addr, ipaddr, sizeof(ipaddr));
88                 } else if(ipv6 && family == AF_INET6) {
89                         struct sockaddr_in6* sa = (struct sockaddr_in6*)a->ifa_addr;
90
91                         if(!link_local && IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
92                                 continue;
93                         }
94
95                         if(IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr) || IN6_IS_ADDR_V4COMPAT(&sa->sin6_addr)) {
96                                 continue;
97                         }
98
99                         tmp = inet_ntop(family, &sa->sin6_addr, ipaddr, sizeof(ipaddr));
100                 }
101
102                 if(tmp != NULL) {
103                         lua_pushstring(L, tmp);
104                         lua_rawseti(L, -2, n++);
105                 }
106
107                 /* TODO: Error reporting? */
108         }
109
110         freeifaddrs(addr);
111 #else
112
113         if(ipv4) {
114                 lua_pushstring(L, "0.0.0.0");
115                 lua_rawseti(L, -2, n++);
116         }
117
118         if(ipv6) {
119                 lua_pushstring(L, "::");
120                 lua_rawseti(L, -2, n++);
121         }
122
123 #endif
124         return 1;
125 }
126
127 int luaopen_util_net(lua_State* L) {
128         luaL_Reg exports[] = {
129                 { "local_addresses", lc_local_addresses },
130                 { NULL, NULL }
131         };
132
133         lua_newtable(L);
134         luaL_setfuncs(L, exports, 0);
135         return 1;
136 }