diff options
| author | Oskari Timperi <oskari.timperi@iki.fi> | 2020-02-19 22:14:08 +0200 |
|---|---|---|
| committer | Oskari Timperi <oskari.timperi@iki.fi> | 2020-02-19 22:14:08 +0200 |
| commit | b5665310e5c1e5d60893248ff146b1a60032dd87 (patch) | |
| tree | 57e7eb9aea927f08c57879b452e82c3e6da4af14 /src/ifaddr | |
| download | ifaddr-nim-b5665310e5c1e5d60893248ff146b1a60032dd87.tar.gz ifaddr-nim-b5665310e5c1e5d60893248ff146b1a60032dd87.zip | |
Diffstat (limited to 'src/ifaddr')
| -rw-r--r-- | src/ifaddr/ifaddrposix.nim | 77 | ||||
| -rw-r--r-- | src/ifaddr/private/utils.nim | 26 | ||||
| -rw-r--r-- | src/ifaddr/windows.nim | 81 |
3 files changed, 184 insertions, 0 deletions
diff --git a/src/ifaddr/ifaddrposix.nim b/src/ifaddr/ifaddrposix.nim new file mode 100644 index 0000000..1fca199 --- /dev/null +++ b/src/ifaddr/ifaddrposix.nim @@ -0,0 +1,77 @@ +import bitops +from nativesockets import getAddrString, ntohl, Port +import os +from posix import SockAddr, AF_INET, AF_INET6, Sockaddr_in, Sockaddr_in6, SockLen +import tables + +import private/utils + +type + ifaddrs {.importc: "struct ifaddrs", header: "<ifaddrs.h>".} = object + ifa_next: ptr ifaddrs + ifa_name: cstring + ifa_flags: cuint + ifa_addr: ptr SockAddr + ifa_netmask: ptr SockAddr + # Skip fields we don't care about + +proc getifaddrs(ifap: ptr ptr ifaddrs): cint {.importc, header: "<ifaddrs.h>".} + +proc freeifaddrs(ifa: ptr ifaddrs) {.importc, header: "<ifaddrs.h>".} + +proc getPrefixLen(sa: ptr SockAddr): int = + if sa.sa_family.uint32 == AF_INET.uint32: + let sa4 = cast[ptr Sockaddr_in](sa) + result = countLeadingZeroBits(bitnot(ntohl(sa4.sin_addr.s_addr))) + elif sa.sa_family.uint32 == AF_INET6.uint32: + # TODO: not sure if this is exactly the right thing to do ... + let sa6 = cast[ptr Sockaddr_in6](sa) + for i in 0..15: + inc(result, countSetBits(sa6.sin6_addr.s6_addr[i].uint8)) + else: + result = 0 + +proc getAdapters*(): seq[Adapter] = + var addrs: ptr ifaddrs + + if getifaddrs(addr addrs) != 0: + raiseOSError(osLastError(), "getifaddrs") + + defer: freeifaddrs(addrs) + + var ips = initOrderedTable[string, Adapter]() + + var curr = addrs + while curr != nil: + let name = $curr.ifa_name + if curr.ifa_addr != nil: + var + ip: IpAddress + flowInfo: uint32 + scopeId: uint32 + try: + fromSockAddrPtr(curr.ifa_addr, ip, flowInfo, scopeId) + except: + curr = curr.ifa_next + continue + if name notin ips: + ips[name] = Adapter(name: name, niceName: name, ips: @[]) + let prefix = getPrefixLen(curr.ifa_netmask) + case ip.family + of IpAddressFamily.IPv6: + ips[name].ips.add(IP(family: IpAddressFamily.IPv6, + flowInfo: flowInfo, + scopeId: scopeId, + ip: ip, + networkPrefix: prefix, + niceName: name)) + of IpAddressFamily.IPv4: + ips[name].ips.add(IP(family: IpAddressFamily.IPv4, + ip: ip, + networkPrefix: prefix, + niceName: name)) + curr = curr.ifa_next + + result = @[] + for v in ips.values: + result.add(v) diff --git a/src/ifaddr/private/utils.nim b/src/ifaddr/private/utils.nim new file mode 100644 index 0000000..ee84b70 --- /dev/null +++ b/src/ifaddr/private/utils.nim @@ -0,0 +1,26 @@ +from net import IpAddress + +when defined(windows) or defined(nimdoc): + from winlean import SockAddr, Sockaddr_in, Sockaddr_in6, AF_INET, AF_INET6, SockLen +elif defined(linux) or defined(maxosx): + from posix import SockAddr, Sockaddr_in, Sockaddr_in6, AF_INET, AF_INET6, SockLen + +from nativesockets import Port +from net import fromSockAddr + + +proc fromSockAddrPtr*(sa: ptr SockAddr, address: var IpAddress, flowInfo: var uint32, + scopeId: var uint32) = + var port: Port + if sa.sa_family.uint32 == AF_INET.uint32: + let sa4 = cast[ptr Sockaddr_in](sa)[] + let size = sizeof(Sockaddr_in).SockLen + fromSockAddr(sa4, size, address, port) + elif sa.sa_family.uint32 == AF_INET6.uint32: + let sa6 = cast[ptr Sockaddr_in6](sa)[] + let size = sizeof(Sockaddr_in6).SockLen + fromSockAddr(sa6, size, address, port) + flowInfo = sa6.sin6_flowinfo.uint32 + scopeId = sa6.sin6_scope_id.uint32 + else: + raise newException(Exception, "unknown sa_family") diff --git a/src/ifaddr/windows.nim b/src/ifaddr/windows.nim new file mode 100644 index 0000000..0d0e10d --- /dev/null +++ b/src/ifaddr/windows.nim @@ -0,0 +1,81 @@ +# Required for IP_ADAPTER_ADDRESSES_LH +{.emit:"""/*INCLUDESECTION*/ +#include <winsock2.h> +""".} + +import winlean +import os + +import private/utils + + +type + IP_ADAPTER_ADDRESSES_LH {.importc, header: "<iphlpapi.h>".} = object + Next: ptr IP_ADAPTER_ADDRESSES_LH + AdapterName: cstring + FirstUnicastAddress: ptr IP_ADAPTER_UNICAST_ADDRESS_LH + Description: WideCString + FriendlyName: WideCString + + IP_ADAPTER_UNICAST_ADDRESS_LH {.importc, header: "<iphlpapi.h>".} = object + Next: ptr IP_ADAPTER_UNICAST_ADDRESS_LH + Address: SOCKET_ADDRESS + OnLinkPrefixLength: uint8 + + SOCKET_ADDRESS {.importc, header: "<winsock2.h>"} = object + lpSockaddr: ptr SockAddr + iSockaddrLength: cint + +const + ERROR_BUFFER_OVERFLOW = 0x6f + +proc GetAdaptersAddresses(family: ULONG, flags: ULONG, reserved: pointer, adapterAddresses: ptr IP_ADAPTER_ADDRESSES_LH, size: ptr ULONG): ULONG {.stdcall, dynlib: "iphlpapi.dll", importc.} + +proc getAdapters*(): seq[Adapter] = + result = @[] + + var + bufsz: ULONG = 15*1024 + rc: DWORD = ERROR_BUFFER_OVERFLOW + buffer: seq[byte] + + while rc == ERROR_BUFFER_OVERFLOW: + buffer.setLen(bufsz) + rc = GetAdaptersAddresses(AF_UNSPEC, 0, nil, + cast[ptr IP_ADAPTER_ADDRESSES_LH](addr buffer[0]), addr bufsz) + + if rc != 0: + raiseOSError(rc.OSErrorCode, "GetAdaptersAddresses") + + let first = cast[ptr IP_ADAPTER_ADDRESSES_LH](addr buffer[0]) + var curr = first + while curr != nil: + let name = $curr.AdapterName + let niceName = $curr.Description + var ips: seq[IP] = @[] + + var currAddr = curr.FirstUnicastAddress + while currAddr != nil: + var + ip: IpAddress + scopeId: uint32 + flowInfo: uint32 + fromSockAddrPtr(currAddr.Address.lpSockaddr, ip, flowInfo, scopeId) + case ip.family + of IpAddressFamily.IPv6: + ips.add(IP(family: IpAddressFamily.IPv6, + flowInfo: flowInfo, + scopeId: scopeId, + ip: ip, + networkPrefix: currAddr.OnLinkPrefixLength.int, + niceName: $curr.FriendlyName)) + of IpAddressFamily.IPv4: + ips.add(IP(family: IpAddressFamily.IPv4, + ip: ip, + networkPrefix: currAddr.OnLinkPrefixLength.int, + niceName: $curr.FriendlyName)) + currAddr = currAddr.Next + + result.add(Adapter(ips: ips, name: name, niceName: niceName)) + + curr = curr.Next |
