aboutsummaryrefslogtreecommitdiff
path: root/src/ifaddr
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2020-02-19 22:14:08 +0200
committerOskari Timperi <oskari.timperi@iki.fi>2020-02-19 22:14:08 +0200
commitb5665310e5c1e5d60893248ff146b1a60032dd87 (patch)
tree57e7eb9aea927f08c57879b452e82c3e6da4af14 /src/ifaddr
downloadifaddr-nim-b5665310e5c1e5d60893248ff146b1a60032dd87.tar.gz
ifaddr-nim-b5665310e5c1e5d60893248ff146b1a60032dd87.zip
initial commitHEADmaster
Diffstat (limited to 'src/ifaddr')
-rw-r--r--src/ifaddr/ifaddrposix.nim77
-rw-r--r--src/ifaddr/private/utils.nim26
-rw-r--r--src/ifaddr/windows.nim81
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