1 module natop.network;
2 
3 import core.exception : onOutOfMemoryError;
4 import vibe.core.net;
5 import vibe.core.log;
6 
7 import std.exception;
8 import std.string : fromStringz, format;
9 import std.conv : to;
10 import std.array : Appender;
11 import core.stdc.stdlib : malloc, free;
12 
13 version(Windows) {
14 import windows.windows;
15 import windows.iptypes;
16 import windows.iphlpapi;
17 }
18 version(Posix)
19 	import core.sys.posix.arpa.inet;
20 
21 enum LOG = true;
22 
23 struct IPRoute {
24 	NetworkAddress destination;
25 	NetworkAddress gateway;
26 	NetworkAddress netmask;
27 	string name;
28 
29 	string toString() {
30 		return "['" ~ destination.toAddressString() ~ "', '" ~ gateway.toAddressString() ~ "', '" ~ netmask.toAddressString() ~ "']";
31 	}
32 }
33 
34 bool validate(IPRoute rt_info) {
35 	if (rt_info.gateway.family == AF_UNSPEC || rt_info.destination.family == AF_UNSPEC)
36 		return false;
37 	auto dest = rt_info.destination.toAddressString();
38 	auto gateway = rt_info.gateway.toAddressString();
39 	if (gateway == "0.0.0.0") return false;
40 	foreach (ip; ["127.0.0.1"])
41 		if (dest == ip || gateway == ip)
42 			return false;
43 	return true;
44 }
45 
46 version(Windows)
47 IPRoute[] getDeviceListing() {
48 
49 	Appender!(IPRoute[]) ret;
50 	ret.reserve(4);
51 
52 	PIP_ADAPTER_INFO adapter_info;
53 	ULONG out_buf_size;
54 
55 	if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW)
56 		throw new Exception("Operation not supported: " ~ GetAdaptersInfo(adapter_info, &out_buf_size).to!string);
57 	adapter_info = cast(IP_ADAPTER_INFO*)malloc(out_buf_size);
58 	
59 	if (!adapter_info)
60 		onOutOfMemoryError();
61 	
62 	scope(exit) 
63 		free(adapter_info);
64 	DWORD ec = GetAdaptersInfo(adapter_info, &out_buf_size);
65 	enforce(ec == NO_ERROR, format("Got error: %s", ec.to!string));
66 
67 	for (PIP_ADAPTER_INFO adapter = adapter_info; adapter !is null; adapter = adapter.Next)
68 	{
69 		auto gateway = cast(string)adapter.GatewayList.IpAddress.String.ptr.fromStringz();
70 		IPRoute r;
71 		r.destination = resolveHost(cast(string)adapter.IpAddressList.IpAddress.String.ptr.fromStringz(), AF_INET, false);
72 		r.gateway = resolveHost(gateway, AF_INET, false);
73 		r.netmask = resolveHost(cast(string)adapter.IpAddressList.IpMask.String.ptr.fromStringz(), AF_INET, false);
74 		r.name = adapter.AdapterName.ptr.fromStringz().idup;
75 		if (validate(r))
76 			ret ~= r;
77 	}
78 
79 	return ret.data;
80 }
81 
82 version(linux) {
83 	import natop.internals.netlink;
84 	import natop.internals.rtnetlink;
85 	import std.string;
86 	import core.stdc.stdlib;
87 	import core.stdc.string;
88 	import core.sys.linux.sys.socket;
89 	import core.sys.posix.sys.socket;
90 	import core.sys.posix.net.if_;
91 	import core.sys.posix.unistd;
92 	int readNetLink(size_t BUFSIZE)(int nl_sock, ref ubyte[BUFSIZE] buf)
93 	{
94 		nlmsghdr* nl_hdr;		
95 		int msg_len;
96 		ubyte* pbuf = buf.ptr;	
97 		do {
98 			int read_len = cast(int)recv(nl_sock, pbuf, BUFSIZE - msg_len, 0);
99 			if (read_len < 0) return -1;
100 
101 			nl_hdr = cast(nlmsghdr*)pbuf;
102 			
103 			if ((NLMSG_OK(nl_hdr, cast(uint) read_len) == 0) || (nl_hdr.nlmsg_type == NLMSG_ERROR))
104 				return -1;
105 			
106 			if (nl_hdr.nlmsg_type == NLMSG_DONE) break;
107 			
108 			pbuf += read_len;
109 			msg_len += read_len;
110 			
111 			if ((nl_hdr.nlmsg_flags & NLM_F_MULTI) == 0) break;
112 			
113 		} while(nl_hdr.nlmsg_seq < 1 || nl_hdr.nlmsg_pid != cast(uint)getpid());
114 
115 		return msg_len;
116 	}
117 
118 	void parseRoute(nlmsghdr* nl_hdr, ref IPRoute[string] ip_route_map)
119 	{
120 		rtmsg* rt_msg = cast(rtmsg*)NLMSG_DATA(nl_hdr);
121 		
122 		if ((rt_msg.rtm_family != AF_INET) || (rt_msg.rtm_table != RT_TABLE_MAIN))
123 			return;
124 		
125 		uint rt_len = RTM_PAYLOAD(nl_hdr);
126 		IPRoute rt_info;
127 		for (rtattr* rt_attr = cast(rtattr*)RTM_RTA(rt_msg);
128 			RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len))
129 		{
130 			switch(rt_attr.rta_type)
131 			{
132 				case RTA_OIF:
133 					char[64] name;
134 					if_indextoname(cast(uint)*cast(int*)RTA_DATA(rt_attr), name.ptr);
135 					rt_info.name = name.ptr.fromStringz.idup;
136 					logInfo("Got name: %s", rt_info.name);
137 					break;
138 				case RTA_GATEWAY:
139 					rt_info.gateway.family = AF_INET;
140 					rt_info.gateway.sockAddrInet4.sin_addr.s_addr = ((*cast(uint*)RTA_DATA(rt_attr)));
141 					logInfo("Got gateway: %s", rt_info.gateway.toAddressString());
142 					break;
143 				/* Mistakenly presumed to be the ethernet device address? Turned out to be a netmask on fedora I think.
144 				 * Needs more testing.
145 				case RTA_DST:
146 					rt_info.destination.family = AF_INET;
147 					rt_info.destination.sockAddrInet4.sin_addr.s_addr = ((*cast(uint*)RTA_DATA(rt_attr)));
148 					logInfo("Got destination: %s", rt_info.destination.toAddressString());
149 					break;
150 				*/
151 				case RTA_PREFSRC:
152 					rt_info.destination.family = AF_INET;
153                     rt_info.destination.sockAddrInet4.sin_addr.s_addr = ((*cast(uint*)RTA_DATA(rt_attr)));
154                     logInfo("Got prefsrc: %s", rt_info.destination.toAddressString());
155 					break;
156 				default:
157 					logInfo("Got other type: %d", rt_attr.rta_type);
158 					break;
159 			}
160 		}
161 		if (auto ptr = rt_info.name in ip_route_map) {
162 			if (ptr.destination.family == AF_INET)
163 				ptr.gateway = rt_info.gateway;
164 			else
165 				ptr.destination = rt_info.destination;
166 		} else ip_route_map[rt_info.name] = rt_info;
167 		
168 	}
169 
170 	IPRoute[] getDeviceListing() {
171 		
172 		Appender!(IPRoute[]) ret;
173 		enum PF_ROUTE = 16;
174 		int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE);
175 		errnoEnforce(sock >= 0);
176 
177 		scope(exit) close(sock);
178 
179 		ubyte[8192] msg;
180 		memset(msg.ptr, 0, msg.sizeof);
181 
182 		nlmsghdr* nl_msg = cast(nlmsghdr*) msg.ptr;
183 		
184 		nl_msg.nlmsg_len = NLMSG_LENGTH(rtmsg.sizeof);
185 		nl_msg.nlmsg_type = cast(ushort) RTM_GETROUTE;
186 		nl_msg.nlmsg_flags = cast(ushort) (NLM_F_DUMP | NLM_F_REQUEST);
187 		nl_msg.nlmsg_seq = 0;
188 		nl_msg.nlmsg_pid = cast(uint) getpid();
189 
190 		errnoEnforce(send(sock, nl_msg, nl_msg.nlmsg_len, 0) >= 0);
191 		
192 		int len = readNetLink(sock, msg);
193 		errnoEnforce(len >= 0);
194 		uint ulen = cast(uint) len;
195 		IPRoute[string] routes;
196 		while (NLMSG_OK(nl_msg, ulen))
197 		{
198 			parseRoute(nl_msg, routes);
199 			nl_msg = NLMSG_NEXT(nl_msg, ulen);
200 		}
201 
202 		foreach (string name, IPRoute route; routes)
203 			if (route.gateway.family == AF_INET && route.destination.family == AF_INET)
204 			{
205 				logInfo("Appending %s", name);
206 				logInfo("Gateway: %s", route.gateway.toAddressString());
207 				logInfo("Destination: %s", route.destination.toAddressString());
208 				if (validate(route))
209 					ret ~= route;
210 			}
211 		logInfo("Returning %d items", ret.data.length);
212 		return ret.data;
213 	}
214 
215 }
216 
217 version(OSX) {
218 	import core.stdc.stdlib;
219 	import core.stdc.string;
220 	import natop.internals.route;
221 	import core.sys.posix.net.if_;
222 	import memutils.vector;
223 
224 	bool parseRoute(rt_msghdr* rtm, ref IPRoute rt_info)
225 	{
226 		sockaddr*[RTAX_MAX] rti_info;
227 		sockaddr* sa = cast(sockaddr*)(rtm + 1);
228 		for (int i = 0; i < RTAX_MAX; ++i)
229 		{
230 			if ((rtm.rtm_addrs & (1 << i)) == 0)
231 			{
232 				rti_info[i] = null;
233 				continue;
234 			}
235 			rti_info[i] = sa;
236 			size_t sa_len = cast(size_t) (sa.sa_len > 0 ? 1 + ((sa.sa_len - 1) | (long.sizeof - 1)) : long.sizeof);
237 			sa = cast(sockaddr*) ((cast(char*)sa) + sa_len);
238 		}
239 		
240 		sa = rti_info[RTAX_GATEWAY];
241 		if (!sa || !rti_info[RTAX_DST] || !rti_info[RTAX_NETMASK] || (sa.sa_family != AF_INET && sa.sa_family != AF_INET6))
242 			return false;
243 		NetworkAddress na;
244 		
245 		foreach (i; 0 .. 7) {
246 			na = NetworkAddress.init;
247 			if (!rti_info[i]) continue;
248 			memcpy(na.sockAddr, rti_info[i], sockaddr.sizeof);
249 			if (na.family == AF_INET || na.family == AF_INET6) {
250 				logInfo("Got %d", na.family);
251 				logInfo("%d: %s", i, na.toString());
252 			}
253 			else
254 				logInfo("Unspec family: %d", na.family);
255 		}
256 		memcpy(rt_info.gateway.sockAddr, rti_info[RTAX_GATEWAY], sockaddr.sizeof);
257 		memcpy(rt_info.netmask.sockAddr, rti_info[RTAX_NETMASK], sockaddr.sizeof);
258 		memcpy(rt_info.destination.sockAddr, rti_info[RTAX_DST], sockaddr.sizeof);
259 		
260 		if (!validate(rt_info))
261 			return false;
262 
263 		char[64] name;
264 		if_indextoname(cast(uint)rtm.rtm_index, name.ptr);
265 		rt_info.name = name.ptr.fromStringz.idup;
266 		return true;
267 	}
268 
269 	IPRoute[] getDeviceListing() {
270 		
271 		Appender!(IPRoute[]) ret;
272 		ret.reserve(4);
273 		int[6] mib = [CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0];
274 		
275 		size_t needed;
276 		errnoEnforce(sysctl(mib.ptr, 6, null, &needed, null, 0) >= 0);
277 		
278 		if (needed == 0)
279 			return IPRoute[].init;
280 		
281 		auto ub = Vector!ubyte(needed);
282 		
283 		errnoEnforce(sysctl(mib.ptr, 6, ub.ptr, &needed, null, 0) >= 0);
284 		
285 		rt_msghdr* rtm;
286 		ubyte* next = ub.ptr;
287 		ubyte* end = ub.ptr + needed;
288 		while (next < end)
289 		{
290 			rtm = cast(rt_msghdr*) next;
291 			
292 			if (rtm.rtm_version != RTM_VERSION)
293 				continue;
294 			
295 			IPRoute r;
296 			if (parseRoute(rtm, r))
297 				ret ~= r;
298 			
299 			next += rtm.rtm_msglen;
300 		}
301 		return ret.data;
302 		
303 	}
304 }
305 
306 interface Router {
307 	@property string id();
308 
309 	@property bool hasDevice();
310 
311 	void discover();
312 
313 	void deleteMapping(ushort external_port, bool is_tcp = true);
314 
315 	void createMapping(ushort local_port, ushort external_port, bool is_tcp = true);
316 }