/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "xstd/xstd.h" #include "xstd/h/net/if.h" #include "xstd/h/netinet.h" #include "xstd/h/iostream.h" #include "xstd/Assert.h" #include "xstd/getIfAddrs.h" #include "xstd/gadgets.h" #include "xstd/InetIfReq.h" #if HAVE_IFADDRS_H #include #endif #if WIN32 /* XXX: merge (declare our WIN32 specific ifconf) */ bool GetIfAddrs(Array &addrs, const String &ifname) { if (!ifname) ifname = "any"; Socket s; if (!s.create(PF_INET, SOCK_DGRAM, 0)) return false; INTERFACE_INFO InterfaceList[20]; unsigned long nBytesReturned; if (WSAIoctl(s.fd(), SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) return false; const int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); for (int i = 0; i < nNumInterfaces; ++i) { ifreq req; strncpy(req.ifr_name, ifname, sizeof(req.ifr_name)); memcpy(&req.ifr_addr, &InterfaceList[i].iiAddress, sizeof(req.ifr_addr)); addrs.append(req); } s.close(); return true; } #else struct ifreq; class IfConf: public ifconf { public: IfConf(int len = 0) { ifc_buf = 0; setLen(len); } ~IfConf() { setLen(0); } void setLen(int len); int reqSize(const struct ifreq *req) const; const ifreq *req(int off) const { return (const ifreq *)(ifc_buf + off); } operator void*() const { return (void*)ifc_buf; } }; void IfConf::setLen(int len) { delete[] ifc_buf; ifc_buf = len > 0 ? new char[len] : 0; ifc_len = len; } int IfConf::reqSize(const struct ifreq *req) const { #ifndef WIN32 // note: there are many broken cases not covered here; // see what /usr/src/contrib/bind/bin/named/ns_main.c has to do # if defined(HAVE_SA_LEN) const int varLen = Max((int)sizeof(sockaddr), (int)req->ifr_addr.sa_len); # else const int varLen = (int)sizeof(sockaddr); # endif return sizeof(req->ifr_name) + varLen; #else /* WIN32 */ return sizeof(*req); #endif } static bool ifNameMatch(const String &ifname, const String &aname) { if (const char *p = aname.chr(':')) return ifname.cmp(aname.data(), p - aname.data()) == 0; else return aname == ifname; } #if HAVE_GETIFADDRS bool GetIfAddrs(Array &addrs, const String &ifname) { struct ifaddrs *ifap = 0; struct ifaddrs *ifp = 0; if (getifaddrs(&ifap)) { // Error() ? return false; } for (ifp = ifap; ifp; ifp = ifp->ifa_next) { if (!ifp->ifa_addr) continue; if (ifname && !ifNameMatch(ifname, ifp->ifa_name)) continue; if (AF_INET == ifp->ifa_addr->sa_family || AF_INET6 == ifp->ifa_addr->sa_family) { InetIfReq req(ifp->ifa_name, InAddress(*ifp->ifa_addr)); addrs.push(req); } } freeifaddrs(ifap); return true; } #else static int ifreqSize(const ifreq *req); // guess how big the cfg buffer should be // should this be a Socket method? static bool GuessIfConfSize(Socket s, IfConf &cfg) { int last_returned_len = -1; // grow the buffer until we get errors or the answer fits // the asnwer fits if returned len does not change for (int buf_len = 4*1024; s; buf_len *= 2) { cfg.setLen(buf_len); if (!s.getIfConf(cfg)) return false; if (cfg.ifc_len == last_returned_len) return true; last_returned_len = cfg.ifc_len; } return false; } // Get a list of all interface addresses using Socket::getIfConf. // WARNING: this is IPv4 only! // bool GetIfAddrs(Array &addrs, const String &ifname) { Socket s; if (!s.create(PF_INET, SOCK_DGRAM, 0)) return false; IfConf cfg; if (GuessIfConfSize(s, cfg)) { const ifreq *req; for (int off = 0; off < cfg.ifc_len; off += ifreqSize(req)) { req = cfg.req(off); if (req->ifr_addr.sa_family != AF_INET) continue; if (ifname && !ifNameMatch(ifname, req->ifr_name)) continue; addrs.append(*req); } } s.close(); return true; } // calculate the actual size of ifreq structure static int ifreqSize(const ifreq *req) { // note: there are many broken cases not covered here; // see what /usr/src/contrib/bind/bin/named/ns_main.c has to do #if defined HAVE_SA_LEN return Max(sizeof(*req), req->ifr_addr.sa_len + sizeof(req->ifr_name)); #else return sizeof(*req); #endif } #endif /* else HAVE_GETIFADDRS */ #endif /* WIN32 */