network-info-0.2.1/0000755000000000000000000000000007346545000012325 5ustar0000000000000000network-info-0.2.1/CHANGELOG.md0000644000000000000000000000050507346545000014136 0ustar0000000000000000## 0.2.1 (2022-01-31) * Support NetBSD (#21) * Increase upper bound on interfaces to 128 (#24) * Fix segmentation fault for more than 64 interfaces (#23) ## 0.2.0.10 (2018-03) * Fixed a link error that manifested when the C compiler uses C99 by default ([#17](https://github.com/jacobstanley/network-info/issues/17)). network-info-0.2.1/LICENSE0000644000000000000000000000276407346545000013343 0ustar0000000000000000Copyright (c) 2010, Jacob Stanley All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Jacob Stanley nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. network-info-0.2.1/README.mkd0000644000000000000000000000073107346545000013760 0ustar0000000000000000network-info ============= This library provides simple read-only access to the local computer's networking configuration. It is currently capable of getting a list of all the network interfaces and their respective IPv4, IPv6 and MAC addresses. Compatibility -------------- network-info has been tested and is known to work on Ubuntu 10.10, Mac OS X 10.6.4, FreeBSD 9.0 and Windows XP/7. It probably works on other flavours of Linux, OS X, FreeBSD and Windows as well. network-info-0.2.1/Setup.hs0000644000000000000000000000005607346545000013762 0ustar0000000000000000import Distribution.Simple main = defaultMain network-info-0.2.1/cbits/0000755000000000000000000000000007346545000013431 5ustar0000000000000000network-info-0.2.1/cbits/common.h0000644000000000000000000000115307346545000015072 0ustar0000000000000000inline void ipv4copy(ipv4 *dst, struct sockaddr *addr) { *dst = ((struct sockaddr_in *)addr)->sin_addr.s_addr; } inline void ipv6copy(ipv6 *dst, struct sockaddr *addr) { memcpy(dst, ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, sizeof(ipv6)); } inline int wcsempty(const wchar_t *str) { return wcslen(str) == 0; } inline void wszcopy(wchar_t *dst, const wchar_t *src, size_t dst_size) { wcsncpy(dst, src, dst_size - 1); dst[dst_size - 1] = '\0'; } inline void mbswszcopy(wchar_t *dst, const char *src, size_t dst_size) { mbstowcs(dst, src, dst_size - 1); dst[dst_size - 1] = '\0'; } network-info-0.2.1/cbits/common.inc0000644000000000000000000000061007346545000015411 0ustar0000000000000000/* See https://github.com/jystic/network-info/issues/17 */ extern inline void ipv4copy(ipv4 *dst, struct sockaddr *addr); extern inline void ipv6copy(ipv6 *dst, struct sockaddr *addr); extern inline int wcsempty(const wchar_t *str); extern inline void wszcopy(wchar_t *dst, const wchar_t *src, size_t dst_size); extern inline void mbswszcopy(wchar_t *dst, const char *src, size_t dst_size); network-info-0.2.1/cbits/network-unix.c0000644000000000000000000001041707346545000016252 0ustar0000000000000000#include #include #include #include #include #include #include #ifdef __linux__ # include #else # include # include # ifndef __GNU__ # include # ifndef __sun # define AF_PACKET AF_LINK # endif # endif # if defined(__sun) || defined(__GNU__) # include # include # endif # ifdef __sun # include # include # endif #endif #ifdef __FreeBSD__ # include #endif #ifdef __DragonFly__ # include #endif #ifdef __NetBSD__ # include #endif #include "network.h" #include "common.h" #if defined(__sun) || (!defined(AF_PACKET) && defined(SIOCGARP)) int maccopy_arp(unsigned char *dst, struct sockaddr *addr) { // SOURCE DERIVED FROM: http://www.pauliesworld.org/project/getmac.c int sock; if ((sock=socket(AF_INET,SOCK_DGRAM,0)) > -1) { struct arpreq arpreq; memset(&arpreq, 0, sizeof (struct arpreq)); arpreq.arp_pa = *addr; if (ioctl(sock,SIOCGARP,(char*)&arpreq) == 0) { close (sock); memcpy(dst, (unsigned char *)arpreq.arp_ha.sa_data, MAC_SIZE); return 0; } else { close (sock); return 1; } } else { return 1; } } #endif #ifdef AF_PACKET void maccopy(unsigned char *dst, struct sockaddr *addr) { #ifdef __linux__ /* TODO check that sll_halen is equal to 6 (MAC_SIZE) */ memcpy(dst, ((struct sockaddr_ll *)addr)->sll_addr, MAC_SIZE); #else /* TODO check that sdl_alen is equal to 6 (MAC_SIZE) */ struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; memcpy(dst, sdl->sdl_data + sdl->sdl_nlen, MAC_SIZE); #endif } #endif struct network_interface *add_interface(struct network_interface *ns, const wchar_t *name, int max_ns) { int i; for (i = 0; i < max_ns; i++) { if (wcsempty(ns[i].name)) { wszcopy(ns[i].name, name, NAME_SIZE); return &ns[i]; } else if (wcscmp(ns[i].name, name) == 0) { return &ns[i]; } } return NULL; } int count_interfaces(struct network_interface *ns, int max_ns) { int i; for (i = 0; i < max_ns; i++) { if (wcsempty(ns[i].name)) { break; } } return i; } int c_get_network_interfaces(struct network_interface *ns, int max_ns) { struct network_interface *n; struct ifaddrs *ifaddr, *ifa; struct sockaddr *addr; wchar_t name[NAME_SIZE]; int family, error; error = getifaddrs(&ifaddr); if (error != 0) { /* TODO printing the error to stderr is not a very nice thing for * TODO a library to do, but i've never seen this happen and its * TODO probably better than failing silently. */ perror("getifaddrs"); return 0; } memset(ns, 0, sizeof(struct network_interface) * max_ns); for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { /* check we actually have an address in this item */ addr = ifa->ifa_addr; if (addr == NULL) continue; /* convert the interface name to wide characters */ mbswszcopy(name, ifa->ifa_name, NAME_SIZE); /* lookup or add a new interface with the given name */ n = add_interface(ns, name, max_ns); if (n == NULL) break; /* extract the address from this item */ family = addr->sa_family; if (family == AF_INET) { ipv4copy(&n->ip_address, addr); #if defined(__sun) || (!defined(AF_PACKET) && defined(SIOCGARP)) if ((ifa->ifa_flags & IFF_LOOPBACK) == 0) { maccopy_arp(n->mac_address, addr); } #endif } else if (family == AF_INET6) { ipv6copy(&n->ip6_address, addr); #if defined(__sun) || (!defined(AF_PACKET) && defined(SIOCGARP)) if ((ifa->ifa_flags & IFF_LOOPBACK) == 0) { maccopy_arp(n->mac_address, addr); } #endif #ifdef AF_PACKET } else if (family == AF_PACKET) { maccopy(n->mac_address, addr); #endif } } freeifaddrs(ifaddr); return count_interfaces(ns, max_ns); } #include "common.inc" network-info-0.2.1/cbits/network-windows.c0000644000000000000000000000336607346545000016766 0ustar0000000000000000#define _WIN32_WINNT 0x0501 #include #include #include #include #include #include "network.h" #include "common.h" int get_adapters_addresses(IP_ADAPTER_ADDRESSES *adapters, ULONG *size) { return GetAdaptersAddresses(AF_UNSPEC, 0, 0, adapters, size); } int c_get_network_interfaces(struct network_interface *ns, int max_ns) { struct sockaddr *addr; IP_ADAPTER_ADDRESSES *adapters, *adapter; IP_ADAPTER_UNICAST_ADDRESS *unicast; ULONG buffer_size; DWORD error; int family, i; /* make an initial call to get the necessary * size into the buffer_size variable */ error = get_adapters_addresses(NULL, &buffer_size); if (error != ERROR_BUFFER_OVERFLOW) { /* if we didn't get ERROR_BUFFER_OVERFLOW * then buffer_size was not set */ return 0; } adapters = malloc(buffer_size); error = get_adapters_addresses(adapters, &buffer_size); i = 0; if (error == NO_ERROR) { adapter = adapters; while (i < max_ns && adapter) { wszcopy(ns[i].name, adapter->FriendlyName, NAME_SIZE); memcpy(ns[i].mac_address, adapter->PhysicalAddress, MAC_SIZE); for (unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) { addr = unicast->Address.lpSockaddr; family = addr->sa_family; if (family == AF_INET) { ipv4copy(&ns[i].ip_address, addr); } else if (family == AF_INET6) { ipv6copy(&ns[i].ip6_address, addr); } } i++; adapter = adapter->Next; } } free(adapters); return i; } #include "common.inc" network-info-0.2.1/cbits/network.h0000644000000000000000000000046007346545000015273 0ustar0000000000000000#define NAME_SIZE (128+4) #define MAC_SIZE 6 typedef long ipv4; typedef long ipv6[4]; struct network_interface { wchar_t name[NAME_SIZE]; ipv4 ip_address; ipv6 ip6_address; unsigned char mac_address[MAC_SIZE]; }; int c_get_network_interfaces(struct network_interface *ns, int max_ns); network-info-0.2.1/network-info.cabal0000644000000000000000000000303707346545000015736 0ustar0000000000000000name: network-info version: 0.2.1 synopsis: Access the local computer's basic network configuration description: This library provides simple read-only access to the local computer's networking configuration. It is currently capable of getting a list of all the network interfaces and their respective IPv4, IPv6 and MAC addresses. . network-info has been tested and is known to work on Ubuntu, FreeBSD, NetBSD, Mac OS and Windows. homepage: http://github.com/jacobstanley/network-info license: BSD3 license-file: LICENSE author: Jacob Stanley maintainer: Jacob Stanley category: Network build-type: Simple cabal-version: >= 1.10 tested-with: GHC == 8.8.4, GHC == 8.10.7, GHC == 9.0.1, GHC == 9.2.1 extra-source-files: cbits/common.h, cbits/common.inc, cbits/network.h, test/src/Main.hs, test/network-info-test.cabal, test/run-tests.bat, test/run-tests.sh, README.mkd, CHANGELOG.md source-repository head type: git location: git://github.com/jacobstanley/network-info.git library default-language: Haskell2010 hs-source-dirs: src include-dirs: cbits cc-options: -Wall -std=c99 exposed-modules: Network.Info build-depends: base == 4.* if os(windows) c-sources: cbits/network-windows.c extra-libraries: iphlpapi else c-sources: cbits/network-unix.c if os(solaris) extra-libraries: socket, nsl network-info-0.2.1/src/Network/0000755000000000000000000000000007346545000014545 5ustar0000000000000000network-info-0.2.1/src/Network/Info.hsc0000644000000000000000000001266107346545000016145 0ustar0000000000000000{-# LANGUAGE ForeignFunctionInterface #-} module Network.Info ( getNetworkInterfaces, NetworkInterface (..), IPv4 (..), IPv6 (..), MAC (..), ) where import Data.Bits ((.&.), shiftR, shiftL) import Data.List (intersperse) import Data.Word import Foreign.C.String import Foreign.C.Types import Foreign.Marshal.Array import Foreign.Ptr import Foreign.Storable import Numeric (showHex) import Text.Printf ---------------------------------------------------------------------- -- FFI ---------------------------------------------------------------------- #include "network.h" foreign import ccall unsafe "c_get_network_interfaces" c_get_network_interfaces :: Ptr NetworkInterface -> CInt -> IO CInt ---------------------------------------------------------------------- -- Network interfaces ---------------------------------------------------------------------- -- | Describes the basic configuration of a network interface. /This/ -- /definition is currently limited to just one address per family./ data NetworkInterface = NetworkInterface { name :: String -- ^ Interface name (e.g. \"eth0\", \"lo\", \"Local Area Connection\") , ipv4 :: IPv4 -- ^ IPv4 address , ipv6 :: IPv6 -- ^ IPv6 address , mac :: MAC -- ^ MAC address } deriving (Show) instance Storable NetworkInterface where alignment _ = #const offsetof(struct {char x__; struct network_interface (y__); }, y__) sizeOf _ = #size struct network_interface peek ptr = do name <- peekCWString $ (#ptr struct network_interface, name) ptr ipv4 <- (#peek struct network_interface, ip_address) ptr ipv6 <- (#peek struct network_interface, ip6_address) ptr mac <- (#peek struct network_interface, mac_address) ptr return $ NetworkInterface name ipv4 ipv6 mac -- | Gets the address information for each of the network interfaces on -- the local computer. getNetworkInterfaces :: IO [NetworkInterface] getNetworkInterfaces = allocaArray 128 $ \ptr -> do count <- c_get_network_interfaces ptr 128 peekArray (fromIntegral count) ptr ---------------------------------------------------------------------- -- IPv4 addresses ---------------------------------------------------------------------- -- | Represents an IPv4 address (e.g. @172.23.21.1@, @127.0.0.1@) data IPv4 = IPv4 {-# UNPACK #-} !Word32 deriving (Eq, Ord, Bounded) instance Show IPv4 where show = showIPv4 instance Storable IPv4 where alignment _ = 1 sizeOf _ = 4 peek p = do ip <- peek (castPtr p) return (IPv4 ip) poke p (IPv4 ip) = poke (castPtr p) ip ---------------------------------------------------------------------- -- IPv6 addresses ---------------------------------------------------------------------- -- | Represents an IPv6 address (e.g. @2001:db8:85a3::8a2e:370:7334@, @::1@) data IPv6 = IPv6 {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 deriving (Eq, Ord, Bounded) -- | Not yet capable of collapsing groups of zeros, will still -- generate valid addresses however. instance Show IPv6 where show = showIPv6 instance Storable IPv6 where alignment _ = 1 sizeOf _ = 16 peek p = do let ptr = castPtr p a <- peekElemOff ptr 0 b <- peekElemOff ptr 1 c <- peekElemOff ptr 2 d <- peekElemOff ptr 3 return $ IPv6 a b c d poke p (IPv6 a b c d) = do let ptr = castPtr p pokeElemOff ptr 0 a pokeElemOff ptr 1 b pokeElemOff ptr 2 c pokeElemOff ptr 3 d ---------------------------------------------------------------------- -- MAC addresses ---------------------------------------------------------------------- -- | Represents a MAC address (e.g. @01:23:45:67:89:ab@) data MAC = MAC {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 deriving (Eq, Ord, Bounded) instance Show MAC where show (MAC a b c d e f) = printf "%02x:%02x:%02x:%02x:%02x:%02x" a b c d e f instance Storable MAC where alignment _ = 1 sizeOf _ = 6 peek p = do a <- peek $ castPtr p b <- peekByteOff p 1 c <- peekByteOff p 2 d <- peekByteOff p 3 e <- peekByteOff p 4 f <- peekByteOff p 5 return $ MAC a b c d e f poke p (MAC a b c d e f) = do poke (castPtr p) a pokeByteOff p 1 b pokeByteOff p 2 c pokeByteOff p 3 d pokeByteOff p 4 e pokeByteOff p 5 f ---------------------------------------------------------------------- -- Helper functions ---------------------------------------------------------------------- showIPv4 :: IPv4 -> String showIPv4 (IPv4 ip) = concat . intersperse "." $ showOctets where showOctets = map show $ word8s ip -- TODO: drop out consecutive zeros showIPv6 :: IPv6 -> String showIPv6 (IPv6 a b c d) = (concat . intersperse ":") groups where groups = map (flip showHex "") $ concatMap (group . word8s) [a,b,c,d] word8s :: Word32 -> [Word8] word8s x = [ fromIntegral $ x , fromIntegral $ x `shiftR` 8 , fromIntegral $ x `shiftR` 16 , fromIntegral $ x `shiftR` 24 ] group :: [Word8] -> [Word16] group = map2 $ \x y -> (fromIntegral x) `shiftL` 8 + (fromIntegral y) map2 :: (a -> a -> b) -> [a] -> [b] map2 _ [] = [] map2 f (x:y:zs) = f x y : map2 f zs network-info-0.2.1/test/0000755000000000000000000000000007346545000013304 5ustar0000000000000000network-info-0.2.1/test/network-info-test.cabal0000644000000000000000000000103307346545000017664 0ustar0000000000000000name: network-info-test version: 0.1 build-type: Simple cabal-version: >= 1.6 tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1 Executable network-info-test hs-source-dirs: ../src src include-dirs: ../cbits cc-options: -Wall -Werror main-is: Main.hs other-modules: Network.Info build-depends: base == 4.* if os(windows) c-sources: ../cbits/network-windows.c extra-libraries: iphlpapi else c-sources: ../cbits/network-unix.c if os(solaris) extra-libraries: socket, nsl network-info-0.2.1/test/run-tests.bat0000644000000000000000000000014307346545000015736 0ustar0000000000000000@echo off cabal configure && cabal build && .\dist\build\network-info-test\network-info-test.exe network-info-0.2.1/test/run-tests.sh0000644000000000000000000000021407346545000015601 0ustar0000000000000000#!/bin/bash TESTDIR="$(dirname -- "${0}")" cd $TESTDIR cabal configure && cabal build && ./dist/build/network-info-test/network-info-test network-info-0.2.1/test/src/0000755000000000000000000000000007346545000014073 5ustar0000000000000000network-info-0.2.1/test/src/Main.hs0000644000000000000000000000061507346545000015315 0ustar0000000000000000module Main (main) where import Network.Info main = do ns <- getNetworkInterfaces mapM (putStrLn . showInterface) ns showInterface :: NetworkInterface -> String showInterface n = name n ++ "\n" ++ "IPv4 Address: " ++ show (ipv4 n) ++ "\n" ++ "IPv6 Address: " ++ show (ipv6 n) ++ "\n" ++ "MAC Address: " ++ show (mac n) ++ "\n"