Archive

Archive for the ‘MiniUPnP’ Category

Setting Up MiniUPnPc With Qt

September 18, 2011 7 comments

Recently I decided to give a feature of Universal Plug ‘n’ Play (UPnP) a whirl: Internet Gateway Device protocol (IGD) [1]. This is a feature which has become commonplace in network devices, specifically routers. Previously one would have to manually set port forwardings in the router’s administration panel in order to allow programs outside the LAN to talk to programs inside it. With UPnP’s IGD functionality the LAN-based program can now take care of those port mappings/forwardings itself. Many games, chat and other programs already use it. One could say that it is now an essential skill for anyone doing serious network programming.

If you need more than just IGD, you can go with one of the more expansive SDKs [2], but otherwise you should be perfectly happy with the extremely light-weight and easy to use MiniUPnPc library [3] which implements the IGD client functionality in ANSI C. This allows it to be used with a variety of languages, in this particular case C++.

The popular C++-based framework Qt doesn’t have native support for UPnP, thus one has to use an external library to add such support. I am currently using MiniUPnPc 1.6 with the Qt 4.7.4 libraries using the MinGW compiler.

Compiling MiniUPnPC is a quick and painless process. On Windows it’s as easy as executing the provided BAT file with MinGW in the PATH and within ten seconds you are left with an .a and .lib library. There’s also MSVC project support if you want to use MSVC, although I haven’t tested it yet.

At this point we can add the first bits of IGD support to our application. Most crucial is adding these parts to the top of the source file:

#define STATICLIB
#include <miniupnpc.h>

Next we have to initialize the network socket library, in this case Winsock2:

WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR) {
    QMessageBox::critical(this, "Error", "WSAStartup() failed.");
    return;
}

With that out of the way, we can verify that all is working using the simple demo code below:

UPNPDev* devlist;
UPNPUrls urls;
IGDdatas data;
char lanaddr[64]; // IP address on the LAN
const char* multicastif = 0;
const char* minissdpdpath = 0;
int error;
devlist = upnpDiscover(1000, multicastif, minissdpdpath, 0, 0, &error);
if (error > 0) {
    QMessageBox::critical(this, "Error", "UPnP discovery failed: " + QString::number(error));
}
else {
    error = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
    freeUPNPDevlist(devlist);
    if (error == 0) {
        QMessageBox::critical(this, tr("Devices"), tr("No IGD found."));
    }
    else if (error == 1) {
        QMessageBox::critical(this, tr("Devices"), tr("A valid connected IGD has been found."));
    }
    else if (error == 2) {
        QMessageBox::critical(this, tr("Devices"), tr("A valid IGD has been found, but it reported as not connected."));
    }
    else if (error == 3) {
        QMessageBox::critical(this, tr("Devices"), tr("An UPnP device was found, but wasn't recognized as an IGD."));
    }
}

As defined in the source code, the error codes for the upnpDiscover() function are as follows:

#define UPNPDISCOVER_SUCCESS (0)
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
#define UPNPDISCOVER_SOCKET_ERROR (-101)
#define UPNPDISCOVER_MEMORY_ERROR (-102)

Now, to get it all to compile so that we can test it. In our Qt project we need to add a few items to the Pro file. First, depending on whether the library file we intend to use is already in a known location (MinGW lib folder or Qt SDK’s lib folder) we may have to add its location using

LIBS += D:/dev/miniupnpc/miniupnpc.a

Personally I prefer to put the library files I use a lot in projects directly into the compiler’s lib folder. This means I only have to add the following to the Pro file for it:

LIBS += -lminiupnpc -liphlpapi

Note hereby that the library itself is called ‘libminiupnpc.a’, but we prefix with -l and omit the ‘lib’ and extension. Finally we must include the libiphlpapi.a library as it contains a number of functions used by MiniUPnPc’s network functionality.

At this point we should be able to compile the project and have it run successfully. If there’s a device on the network (router) with IGD (UPnP) enabled, it will respond to the discovery broadcast and be identified. You can verify the output using the commandline test application provided with MiniUPnPc ‘upnpc-static.exe’. Detailed sample code can be found in upnpc.c, which is the source for the test application. There isn’t much available in terms of documentation, but with some puzzling and liberal use of said sample source code, it shouldn’t be too hard to figure out how to do something.

Until next time,

Maya

[1] http://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
[2] http://upnp.org/sdcps-and-certification/resources/sdks/
[3] http://miniupnp.free.fr/

Advertisements
Categories: MiniUPnP, programming, Qt, UPnP