Is there a juce class able to acquire LAN IP?


#1

Hi all,

Did anyone find a handy juce class that can get the running machine’s IP in its LAN? ie 192.168.1.x

Or we have to do it in our own way… like using a NSHost on a mac, by which it may cause a lot of additional work because of mixing cpp and objc.

Thanks for any suggestion.


#2

If you search on datagram sockets you’ll find some extensions/fixes for the sockets classes I posted. I added a helper class that let’s you create an array of all the IP addresses currently in use by a given machine, it works on iOS, Android, Mac, Windows, and Linux. It’s probably a bit out of date. I think I fixed a few more streaming socket bugs since, and I think that the header files got refactored in the Juce master branch, but the helper class should be pretty self contained.


#3

FWIW, I merged with the tip just the other day. Here is my mucked up juce_socket.cpp and juce_socket.h. I think all my changes are backwards compatible, but I never really checked. You could just yank IpAddress. You’d use it something like this:

    Array<IpAddress> ips;
    IpAddress::findAllIpAddresses (ips);

    for (int n=0; n < ips.size(); ++n)
        Logger::outputDebugString (ips[n].toString());

#4

Thanks jfitzpat, I’ve imported your class and it works after a few adjustments in header includes, I only have run it on a mac, i’ll try win later.

//
//  J_IPAddress.h
//  AGP
//
//  Created by Someone on 7/13/12.
//  Copyright (c) 2012 yourcompanyname. All rights reserved.
//

#ifndef AGP_J_IPAddress_h
#define AGP_J_IPAddress_h


#include "../JuceLibraryCode/JuceHeader.h"

class JUCE_API IpAddress
{
public:
    //==============================================================================
    /** Populates a list of the IPv4 addresses of all the available network cards. */
    static void findAllIpAddresses (Array<IpAddress>& results);
    
    //==============================================================================
    /** Creates a null address (0.0.0.0). */
    IpAddress();
    
    /** Creates from a host order uint32. */
    IpAddress (uint32 addr);
    
    /** Creates from another address. */
    IpAddress (const IpAddress& other);
    
    /** Creates a copy of another address. */
    IpAddress& operator= (const IpAddress& other);
    
    /** Creates an address from a string ("1.2.3.4"). */
    explicit IpAddress (const String& addr);
    
    /** Returns a dot-separated string in the form "1.2.3.4". */
    String toString() const;
    
    /** Return as host order uint32. */
    uint32 toUint32() const noexcept;
    
    /** Return as network order uint32. */
    uint32 toNetworkUint32() const noexcept;
    
    /** Returns true if this address is ANY (0.0.0.0). */
    bool isAny() const noexcept;
    
    /** Returns true if this address is BROADCAST (255.255.255.255). */
    bool isBroadcast() const noexcept;
    
    /** Returns true if this address is LOOPBACK (127.0.0.1). */
    bool isLocal() const noexcept;
    
    bool operator== (const IpAddress& other) const noexcept;
    bool operator!= (const IpAddress& other) const noexcept;
    
    //==============================================================================
    /** IPv4 Any Address. */
    static const IpAddress any;
    static const IpAddress broadcast;
    static const IpAddress localhost;
    
    //==============================================================================
private:
    uint32 ipAddress;
};

#endif
//
//  J_IPAdress.cpp
//  AGP
//
//  Created by someone on 7/13/12.
//  Copyright (c) 2012 yourcompanyname. All rights reserved.
//

#if JUCE_MSVC
#pragma warning (push)
#pragma warning (disable : 4127 4389 4018)
#endif

#ifndef AI_NUMERICSERV  // (missing in older Mac SDKs)
#define AI_NUMERICSERV 0x1000
#endif

#if JUCE_WINDOWS

#else

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//#include <sys/errno.h>
//#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>

#endif

#include "J_IPAddress.h"


//==============================================================================
//==============================================================================
IpAddress::IpAddress()
: ipAddress (0)
{
}

IpAddress::IpAddress (uint32 addr)
: ipAddress (addr)
{
}

IpAddress::IpAddress (const IpAddress& other)
: ipAddress (other.ipAddress)
{
}

IpAddress& IpAddress::operator= (const IpAddress& other)
{
    ipAddress = other.ipAddress;
    return *this;
}

IpAddress::IpAddress (const String& addr)
{
    uint32 temp = inet_addr (addr.toUTF8());
    ipAddress = ntohl (temp);
}

String IpAddress::toString() const
{
    String s;
    
    s = String ((ipAddress >> 24) & 0xFF);
    s << '.';
    s << String ((ipAddress >> 16) & 0xFF);
    s << '.';
    s << String ((ipAddress >> 8) & 0xFF);
    s << '.';
    s << String (ipAddress & 0xFF);
    
    return s;
}

uint32 IpAddress::toUint32() const noexcept
{
    return ipAddress;
}

uint32 IpAddress::toNetworkUint32() const noexcept
{
    return htonl (ipAddress);
}

bool IpAddress::isAny() const noexcept          { return ipAddress == 0; }
bool IpAddress::isBroadcast() const noexcept    { return ipAddress == 0xFFFFFFFF; }
bool IpAddress::isLocal() const noexcept        { return ipAddress == 0x7F000001; }

bool IpAddress::operator== (const IpAddress& other) const noexcept
{ 
    return ipAddress == other.ipAddress; 
}

bool IpAddress::operator!= (const IpAddress& other) const noexcept
{ 
    return ipAddress != other.ipAddress;
}

#if JUCE_WINDOWS
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    // For consistancy
    result.addIfNotAlreadyThere (IpAddress ("127.0.0.1"));
    
    DynamicLibrary dll ("iphlpapi.dll");
    JUCE_DLL_FUNCTION (GetAdaptersInfo, getAdaptersInfo, DWORD, dll, (PIP_ADAPTER_INFO, PULONG))
    
    if (getAdaptersInfo != nullptr)
    {
        ULONG len = sizeof (IP_ADAPTER_INFO);
        HeapBlock<IP_ADAPTER_INFO> adapterInfo (1);
        
        if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW)
            adapterInfo.malloc (len, 1);
        
        if (getAdaptersInfo (adapterInfo, &len) == NO_ERROR)
        {
            for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != nullptr; adapter = adapter->Next)
            {
                IpAddress ip (adapter->IpAddressList.IpAddress.String);
                if (! ip.isAny())
                    result.addIfNotAlreadyThere (ip);
            }
        }
    }
}
#endif

#if JUCE_MAC || JUCE_IOS
// Oh joy, two incompatible SIOCGIFCONF interfaces...
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    struct ifconf cfg;
    size_t buffer_capacity;
    HeapBlock<char> buffer;
    int sock = -1;
    
    // Compute the sizes of ifreq structures
    const size_t ifreq_size_in = IFNAMSIZ + sizeof (struct sockaddr_in);
    const size_t ifreq_size_in6 = IFNAMSIZ + sizeof (struct sockaddr_in6);
    
    // Poor man's try since we can be in an existing noexcept
    do
    {
        // Create a dummy socket to execute the IO control on
        sock = socket (AF_INET, SOCK_DGRAM, 0);
        if (sock < 0)
            break;
        
        // Repeatedly call the IO control with increasing buffer sizes until success
        // Ugly, old school...
        bool success = true;
        buffer_capacity = ifreq_size_in6;
        do 
        {
            buffer_capacity *= 2;
            buffer.realloc (buffer_capacity);
            
            cfg.ifc_len = buffer_capacity;
            cfg.ifc_buf = buffer;
            
            if ((ioctl (sock, SIOCGIFCONF, &cfg) < 0) && (errno != EINVAL))
            {
                success = false;
                break;
            }
            
        } while ((buffer_capacity - cfg.ifc_len) < 2 * ifreq_size_in6);
        
        // How did we do?
        if (success == false)
            break;
        
        // Copy the interface addresses into the result array
        while (cfg.ifc_len >= ifreq_size_in) 
        {
            // Skip entries for non-internet addresses
            if (cfg.ifc_req->ifr_addr.sa_family == AF_INET)
            {
                const struct sockaddr_in* addr_in = (const struct sockaddr_in*) &cfg.ifc_req->ifr_addr;
                in_addr_t addr = addr_in->sin_addr.s_addr;
                // Skip entries without an address
                if (addr != INADDR_NONE)
                    result.addIfNotAlreadyThere (IpAddress (ntohl(addr)));
            }
            
            // Move to the next structure in the buffer
            // CANNOT just use sizeof (ifreq) because entries vary in size
            cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
            cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
        }
    } while (0);
    
    // Free the buffer and close the socket if necessary
    buffer.free();
    
    if (sock >= 0)
        close(sock);
}
#endif

#if JUCE_LINUX || JUCE_ANDROID
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    const int s = socket (AF_INET, SOCK_DGRAM, 0);
    if (s != -1)
    {
        char buf [1024];
        struct ifconf ifc;
        ifc.ifc_len = sizeof (buf);
        ifc.ifc_buf = buf;
        ioctl (s, SIOCGIFCONF, &ifc);
        
        struct ifreq *ifr = ifc.ifc_req;
        int nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
        for(int i = 0; i < nInterfaces; i++)
        {
            struct ifreq *item = &ifr[i];
            
            if (item->ifr_addr.sa_family == AF_INET)
            {
                const struct sockaddr_in* addr_in = (const struct sockaddr_in*) &item->ifr_addr;
                in_addr_t addr = addr_in->sin_addr.s_addr;
                // Skip entries without an address
                if (addr != INADDR_NONE)
                    result.addIfNotAlreadyThere (IpAddress (ntohl(addr)));
            }
        }
        close (s);
    }
}
#endif

const IpAddress IpAddress::any (0);
const IpAddress IpAddress::broadcast (0xFFFFFFFF);
const IpAddress IpAddress::localhost (0x7F000001);

#5

This is all excellent stuff! I should really add something along these lines to the library. Will try to find a spare hour to do that soon.


#6

I only bothered with IPv4 for the IpAddress class. Same with all the datagram and streaming fixes, really just enough for my needs of the moment. After hacking URL to do Amazon’s weird REST stuff, I’ve been wishing I had time to do a while new set of juce modules for network stuff. Alas, unlike you, I have to sleep 5-6 hours each day… :wink:

But at least some of the stuff, like enumerating all the adapters on the different platforms, might at least be a useful reference.


#7

Has anyone gotten J_IPAddress to build on Windows? Want to share your edits?


#8

Here ya go, this runs on Win 7.

//
//  J_IPAdress.cpp
//  AGP
//
//  Created by someone on 7/13/12.
//  Copyright (c) 2012 yourcompanyname. All rights reserved.
//

#if JUCE_MSVC
#pragma warning (push)
#pragma warning (disable : 4127 4389 4018)
#endif

#ifndef AI_NUMERICSERV  // (missing in older Mac SDKs)
#define AI_NUMERICSERV 0x1000
#endif

#include "../JuceLibraryCode/JuceHeader.h"

#if JUCE_WINDOWS

#include <Windows.h>
#include <iphlpapi.h>

#else

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//#include <sys/errno.h>
//#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>

#endif

#include "J_IPAddress.h"


//==============================================================================
//==============================================================================
IpAddress::IpAddress()
: ipAddress (0)
{
}

IpAddress::IpAddress (uint32 addr)
: ipAddress (addr)
{
}

IpAddress::IpAddress (const IpAddress& other)
: ipAddress (other.ipAddress)
{
}

IpAddress& IpAddress::operator= (const IpAddress& other)
{
    ipAddress = other.ipAddress;
    return *this;
}

IpAddress::IpAddress (const String& addr)
{
    uint32 temp = inet_addr (addr.toUTF8());
    ipAddress = ntohl (temp);
}

String IpAddress::toString() const
{
    String s;
    
    s = String ((ipAddress >> 24) & 0xFF);
    s << '.';
    s << String ((ipAddress >> 16) & 0xFF);
    s << '.';
    s << String ((ipAddress >> 8) & 0xFF);
    s << '.';
    s << String (ipAddress & 0xFF);
    
    return s;
}

uint32 IpAddress::toUint32() const noexcept
{
    return ipAddress;
}

uint32 IpAddress::toNetworkUint32() const noexcept
{
    return htonl (ipAddress);
}

bool IpAddress::isAny() const noexcept          { return ipAddress == 0; }
bool IpAddress::isBroadcast() const noexcept    { return ipAddress == 0xFFFFFFFF; }
bool IpAddress::isLocal() const noexcept        { return ipAddress == 0x7F000001; }

bool IpAddress::operator== (const IpAddress& other) const noexcept
{ 
    return ipAddress == other.ipAddress; 
}

bool IpAddress::operator!= (const IpAddress& other) const noexcept
{ 
    return ipAddress != other.ipAddress;
}

#if JUCE_WINDOWS
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    // For consistancy
    result.addIfNotAlreadyThere (IpAddress ("127.0.0.1"));
    
    ULONG len = sizeof (IP_ADAPTER_INFO);
    HeapBlock<IP_ADAPTER_INFO> adapterInfo (1);
    
    if (GetAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW)
        adapterInfo.malloc (len, 1);
    
    if (GetAdaptersInfo (adapterInfo, &len) == NO_ERROR)
    {
        for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != nullptr; adapter = adapter->Next)
        {
            IpAddress ip (adapter->IpAddressList.IpAddress.String);
            if (! ip.isAny())
                result.addIfNotAlreadyThere (ip);
        }
    }
}
#endif

#if JUCE_MAC || JUCE_IOS
// Oh joy, two incompatible SIOCGIFCONF interfaces...
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    struct ifconf cfg;
    size_t buffer_capacity;
    HeapBlock<char> buffer;
    int sock = -1;
    
    // Compute the sizes of ifreq structures
    const size_t ifreq_size_in = IFNAMSIZ + sizeof (struct sockaddr_in);
    const size_t ifreq_size_in6 = IFNAMSIZ + sizeof (struct sockaddr_in6);
    
    // Poor man's try since we can be in an existing noexcept
    do
    {
        // Create a dummy socket to execute the IO control on
        sock = socket (AF_INET, SOCK_DGRAM, 0);
        if (sock < 0)
            break;
        
        // Repeatedly call the IO control with increasing buffer sizes until success
        // Ugly, old school...
        bool success = true;
        buffer_capacity = ifreq_size_in6;
        do 
        {
            buffer_capacity *= 2;
            buffer.realloc (buffer_capacity);
            
            cfg.ifc_len = buffer_capacity;
            cfg.ifc_buf = buffer;
            
            if ((ioctl (sock, SIOCGIFCONF, &cfg) < 0) && (errno != EINVAL))
            {
                success = false;
                break;
            }
            
        } while ((buffer_capacity - cfg.ifc_len) < 2 * ifreq_size_in6);
        
        // How did we do?
        if (success == false)
            break;
        
        // Copy the interface addresses into the result array
        while (cfg.ifc_len >= ifreq_size_in) 
        {
            // Skip entries for non-internet addresses
            if (cfg.ifc_req->ifr_addr.sa_family == AF_INET)
            {
                const struct sockaddr_in* addr_in = (const struct sockaddr_in*) &cfg.ifc_req->ifr_addr;
                in_addr_t addr = addr_in->sin_addr.s_addr;
                // Skip entries without an address
                if (addr != INADDR_NONE)
                    result.addIfNotAlreadyThere (IpAddress (ntohl(addr)));
            }
            
            // Move to the next structure in the buffer
            // CANNOT just use sizeof (ifreq) because entries vary in size
            cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
            cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
        }
    } while (0);
    
    // Free the buffer and close the socket if necessary
    buffer.free();
    
    if (sock >= 0)
        close(sock);
}
#endif

#if JUCE_LINUX || JUCE_ANDROID
void IpAddress::findAllIpAddresses (Array<IpAddress>& result)
{
    const int s = socket (AF_INET, SOCK_DGRAM, 0);
    if (s != -1)
    {
        char buf [1024];
        struct ifconf ifc;
        ifc.ifc_len = sizeof (buf);
        ifc.ifc_buf = buf;
        ioctl (s, SIOCGIFCONF, &ifc);
        
        struct ifreq *ifr = ifc.ifc_req;
        int nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
        for(int i = 0; i < nInterfaces; i++)
        {
            struct ifreq *item = &ifr[i];
            
            if (item->ifr_addr.sa_family == AF_INET)
            {
                const struct sockaddr_in* addr_in = (const struct sockaddr_in*) &item->ifr_addr;
                in_addr_t addr = addr_in->sin_addr.s_addr;
                // Skip entries without an address
                if (addr != INADDR_NONE)
                    result.addIfNotAlreadyThere (IpAddress (ntohl(addr)));
            }
        }
        close (s);
    }
}
#endif

const IpAddress IpAddress::any (0);
const IpAddress IpAddress::broadcast (0xFFFFFFFF);
const IpAddress IpAddress::localhost (0x7F000001);

#9

Was this incorporated into juce? I could not find it after a quick search in the api docs.


#10

Not yet - I meant to add something like this but never got chance to do so.


#11

You can not imagine the pain and suffering you will save me by implementing this into JUCE!

:wink:


#12

I’ve added this now - feedback welcome!


#13

I actually am just coming up for air from another project and need to do a little network/config tool. I’ll try the tip sometime over the next few days. Thanks!