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 }