edgarluque.com/content/blog/intro-to-upnp.md
2021-12-24 12:02:07 +01:00

3.1 KiB

+++ title = "An intro to MiniUPNP" description = "An intro to the MiniUPNP C library." date = 2020-04-14 [taxonomies] categories = ["c"] +++

If you have a software that requires port forwarding, you may want to implement UPnP to make things easy for your end users.

When I wanted to implement it, the first library I found was MiniUPnP, sadly it doesn't have much documentation but a quick look at the header files and some examples on the internet I managed to make it work, here is how:

First the required include directives we need:

#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
#include <stdio.h>

The first thing we need to do is discover the UPnP devices on the network, this is done using upnpDiscover:

int main() {
  struct UPNPDev *upnp_dev = 0;

  int error = 0;
  upnp_dev = upnpDiscover(2000, NULL, NULL, 0, 0, 2, &error);

  if(error != 0) {
    printf("error discovering upnp devices: %s\n", strupnperror(error));
    return 1;
  }
}

Then we need to retrieve the Internet Gateway Device we discovered:

struct UPNPUrls upnp_urls;
struct IGDdatas upnp_data;
char aLanAddr[64];
const char *pPort = "8000";

// Retrieve a valid Internet Gateway Device
int status = UPNP_GetValidIGD(
    upnp_dev,
    &upnp_urls,
    &upnp_data,
    aLanAddr,
    sizeof(aLanAddr)
    );
printf("status=%d, lan_addr=%s\n", status, aLanAddr);

We check if we got the correct status and then map the port. We also declare the port we want to use, in this case I use the same number for the internal and external ports:

if (status == 1)
{
    printf("found valid IGD: %s\n", upnp_urls.controlURL);
    error =
        UPNP_AddPortMapping(
            upnp_urls.controlURL,
            upnp_data.first.servicetype,
            pPort, // external port
            pPort, // internal port
            aLanAddr, "My Application Name", "UDP",
            0,  // remote host
            "0" // lease duration, recommended 0 as some NAT
            // implementations may not support another value
        );

    if (error)
    {
        printf("failed to map port\n");
        printf("error: %s\n", strupnperror(error));
    }
    else
        printf("successfully mapped port\n");
}
else
    printf("no valid IGD found\n");

Now the port is mapped, at this point you can open a socket bound to the internal port and external clients will be able to connect using the external port.

Then we do some cleanup:

Note: The port here is the external one.

error = UPNP_DeletePortMapping(upnp_urls.controlURL,
                               upnp_data.first.servicetype,
                               pPort,
                               "UDP",
                               0);

if (error != 0) {
    printf("port map deletion error: %s\n", strupnperror(error));
}

FreeUPNPUrls(&upnp_urls);
freeUPNPDevlist(upnp_dev);
return 0;

Complete source code: gist

Compiled using: cc -lminiupnpc upnp.c