How to do UDP hole punching

I will now show you how to do UDP hole punching, with code in C. Hole punching is an advanced networking concept, so you’re expected to at least know how to compile/run this code.

I have written lots of comments inside the code to explain what is happening.

To use this, run the server code in a computer with a public IP address, and then run the client code in two or more different computers, each behind a different NAT.

I compiled and tested this on Ubuntu Server 11.04 and CentOS 5, it should work easily in probably all other linuces and BSDs. It could run in Windows using the WSA code. This code is also not endianness-safe, so it would be best to run it on x86-64 or similar in all machines.

I release this code to the public domain, but you’re a complete lunatic if you plan to use this code in any real program.

server code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// UDP hole punching example, server code
// Base UDP code stolen from http://www.abc.se/~m6695/udp.html
// By Oscar Rodriguez
// This code is public domain, but you're a complete lunatic
// if you plan to use this code in any real program.
 
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
#define BUFLEN 512
#define NPACK 10
#define PORT 9930
 
// A small struct to hold a UDP endpoint. We'll use this to hold each client's endpoint.
struct client
{
    int host;
    short port;
};
 
// Just a function to kill the program when something goes wrong.
void diep(char *s)
{
    perror(s);
    exit(1);
}
 
int main(void)
{
    struct sockaddr_in si_me, si_other;
    int s, i, j, slen=sizeof(si_other);
    char buf[BUFLEN];
    struct client clients[10]; // 10 clients. Notice that we're not doing any bound checking.
    int n = 0;
 
    // Create a UDP socket
    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");
 
    // si_me stores our local endpoint. Remember that this program
    // has to be run in a network with UDP endpoint previously known
    // and directly accessible by all clients. In simpler terms, the
    // server cannot be behind a NAT.
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(PORT);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (struct sockaddr*)(&si_me), sizeof(si_me))==-1)
        diep("bind");
 
    while (1)
    {
        // When a new client sends a datagram...
        if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr*)(&si_other), &slen)==-1)
            diep("recvfrom");
        // The client's public UDP endpoint data is now in si_other.
        // Notice that we're completely ignoring the datagram payload.
        // If we want to support multiple clients inside the same NAT,
        // we'd have clients send their own private UDP endpoints
        // encoded in some way inside the payload, and store those as
        // well.
        printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
        // Now we add the client's UDP endpoint in our list.
        clients[n].host = si_other.sin_addr.s_addr;
        clients[n].port = si_other.sin_port;
        n++;
        // And then tell everybody about everybody's public UDP endpoints
        for (i = 0; i < n; i++)
        {
            si_other.sin_addr.s_addr = clients[i].host;
            si_other.sin_port = clients[i].port;
            // We send a datagram for each client in our list. Of course,
            // we could also assemble a single datagram and send that.
            for (j = 0; j < n; j++)
            {
                // The payload is the client's public UDP endpoint, clients[j]
                printf("Sending to %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
                // We're sending binary data here, using the server's byte order.
                // In your code, you should make sure every client agrees on the endianness.
                if (sendto(s, &clients[j], 6, 0, (struct sockaddr*)(&si_other), slen)==-1)
                    diep("sendto");
            }
        }
        printf("Now we have %d clients\n", n);
        // And we go back to listening. Notice that since UDP has no notion
        // of connections, we can use the same socket to listen for data
        // from different clients.
    }
 
    // Actually, we never reach this point...
    close(s);
    return 0;
}

client code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// UDP hole punching example, client code
// Base UDP code stolen from http://www.abc.se/~m6695/udp.html
// By Oscar Rodriguez
// This code is public domain, but you're a complete lunatic
// if you plan to use this code in any real program.
 
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
#define BUFLEN 512
#define NPACK 10
#define PORT 9930
 
// This is our server's IP address. In case you're wondering, this one is an RFC 5737 address.
#define SRV_IP "203.0.113.61"
 
// A small struct to hold a UDP endpoint. We'll use this to hold each peer's endpoint.
struct client
{
    int host;
    short port;
};
 
// Just a function to kill the program when something goes wrong.
void diep(char *s)
{
    perror(s);
    exit(1);
}
 
int main(int argc, char* argv[])
{
    struct sockaddr_in si_me, si_other;
    int s, i, f, j, k, slen=sizeof(si_other);
    struct client buf;
    struct client server;
    struct client peers[10]; // 10 peers. Notice that we're not doing any bound checking.
    int n = 0;
 
    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");
 
    // Our own endpoint data
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(PORT); // This is not really necessary, we can also use 0 (any port)
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
 
    // The server's endpoint data
    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(PORT);
    if (inet_aton(SRV_IP, &si_other.sin_addr)==0)
        diep("aton");
 
    // Store the server's endpoint data so we can easily discriminate between server and peer datagrams.
    server.host = si_other.sin_addr.s_addr;
    server.port = si_other.sin_port;
 
    // Send a simple datagram to the server to let it know of our public UDP endpoint.
    // Not only the server, but other clients will send their data through this endpoint.
    // The datagram payload is irrelevant, but if we wanted to support multiple
    // clients behind the same NAT, we'd send our won private UDP endpoint information
    // as well.
    if (sendto(s, "hi", 2, 0, (struct sockaddr*)(&si_other), slen)==-1)
        diep("sendto");
 
    // Right here, our NAT should have a session entry between our host and the server.
    // We can only hope our NAT maps the same public endpoint (both host and port) when we
    // send datagrams to other clients using our same private endpoint.
    while (1)
    {
        // Receive data from the socket. Notice that we use the same socket for server and
        // peer communications. We discriminate by using the remote host endpoint data, but
        // remember that IP addresses are easily spoofed (actually, that's what the NAT is
        // doing), so remember to do some kind of validation in here.
        if (recvfrom(s, &buf, sizeof(buf), 0, (struct sockaddr*)(&si_other), &slen)==-1)
            diep("recvfrom");
        printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
        if (server.host == si_other.sin_addr.s_addr && server.port == (short)(si_other.sin_port))
        {
            // The datagram came from the server. The server code is set to send us a
            // datagram for each peer, in which the payload contains the peer's UDP
            // endpoint data. We're receiving binary data here, sent using the server's
            // byte ordering. We should make sure we agree on the endianness in any
            // serious code.
            f = 0;
            // Now we just have to add the reported peer into our peer list
            for (i = 0; i < n && f == 0; i++)
            {
                if (peers[i].host == buf.host && peers[i].port == buf.port)
                {
                    f = 1;
                }
            }
            // Only add it if we didn't have it before.
            if (f == 0)
            {
                peers[n].host = buf.host;
                peers[n].port = buf.port;
                n++;
            }
            si_other.sin_addr.s_addr = buf.host;
            si_other.sin_port = buf.port;
            printf("Added peer %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
            printf("Now we have %d peers\n", n);
            // And here is where the actual hole punching happens. We are going to send
            // a bunch of datagrams to each peer. Since we're using the same socket we
            // previously used to send data to the server, our local endpoint is the same
            // as before.
            // If the NAT maps our local endpoint to the same public endpoint
            // regardless of the remote endpoint, after the first datagram we send, we
            // have an open session (the hole punch) between our local endpoint and the
            // peer's public endpoint. The first datagram will probably not go through
            // the peer's NAT, but since UDP is stateless, there is no way for our NAT
            // to know that the datagram we sent got dropped by the peer's NAT (well,
            // our NAT may get an ICMP Destination Unreachable, but most NATs are
            // configured to simply discard them) but when the peer sends us a datagram,
            // it will pass through the hole punch into our local endpoint.
            for (k = 0; k < 10; k++)
            {
                // Send 10 datagrams.
                for (i = 0; i < n; i++)
                {
                    si_other.sin_addr.s_addr = peers[i].host;
                    si_other.sin_port = peers[i].port;
                    // Once again, the payload is irrelevant. Feel free to send your VoIP
                    // data in here.
                    if (sendto(s, "hi", 2, 0, (struct sockaddr*)(&si_other), slen)==-1)
                        diep("sendto()");
                }
            }
        }
        else
        {
            // The datagram came from a peer
            for (i = 0; i < n; i++)
            {
                // Identify which peer it came from
                if (peers[i].host == buf.host && peers[i].port == (short)(buf.port))
                {
                    // And do something useful with the received payload
                    printf("Received from peer %d!\n", i);
                    break;
                }
            }
 
            // It is possible to get data from an unregistered peer. These are some reasons
            // I quickly came up with, in which this can happen:
            // 1. The server's datagram notifying us with the peer's address got lost,
            //    or it hasn't arrived yet (likely)
            // 2. A malicious (or clueless) user is sending you data on this endpoint (maybe
            //    unlikely)
            // 3. The peer's public endpoint changed either because the session timed out,
            //    or because its NAT did not assign the same public endpoint when sending
            //    datagrams to different remote endpoints. If this happens, and we're able
            //    to detect this situation, we could change our peer's endpoint data to
            //    the correct one. If we manage to pull this off correctly, even if at most
            //    one client has a NAT that doesn't support hole punching, we can communicate
            //    directly between both peers.
        }
    }
 
    // Actually, we never reach this point...
    close(s);
    return 0;
}

Relaunch of Rapapaing

It’s now been almost 9 years since I first launched rapapaing.com. It seems like it was yesterday!

The Internet is changing, and writing content by directly writing HTML is so pre-2010. So I’ve decided I should give the page a makeup and relaunch it, this time as a blog. Fear not, as there was no downtime, no 404 pages during the migration,  I have kept most of the previous content, and every old link is still valid, though they now point to places inside this blog thanks to our friends ModRewrite and HTTP code 301. However, it is possible that I messed up somewhere, so please let me know if an old link is broken.

Additionally, I’ve kept the minimalist retro look and feel, but instead of a gray page, previously found on the main page, I am now using the red I used on my personal page. Thanks a lot to the guys at plaintxt for this awesome theme.

But also times have changed. I am now a professional game programmer, and I wish to write a little bit more about my thoughts on game programming, design, content creation, and the game industry itself.

So, if you were a regular visitor, welcome back. If you’re a new visitor, welcome.

How to draw Pixel Art clouds quickly

Well, if you’re into pixel art like me, you will eventually want to draw some kind of landscape. Regardless of whether it’s for a game or a simple illustration, you will end up having to draw a sky, and if you’re like me, you’ll start by drawing some blue, and then…

And then nothing happens. Skies are blue, yes, but your precious and detailed piece of pixel art seems like it is now in front of a blue screen of death, and your blue looks horrid. That’s where you think you need some clouds to break the monotony, but you have no idea how to draw clouds in pixel art.

So you boot your browser and look for how to draw pixel art clouds, but at least, at the time I wrote this, there are no single comprehensive articles on how to draw pixel art clouds. I suppose this is some kind of secret each pixel artist keeps to himself, because many pieces of art have some nice clouds, yet nobody even mentions how to draw them. This is where you wish you would have done an indoors scene.

Well, fear not. I have come to tell you of one way you can use to draw credible pixel art clouds very quickly. This speed thing is quite important, because you simply can’t waste hours and hours of time just to draw a single cloud, and then when you try it on your game, it’s too big, or too fat, or simply too useless. With this method you can draw clouds in 1-3 minutes each. This is enough to make lots of clouds, some of which you will keep and some of which you will erase without a single bit of remorse.

This method is meant to draw altocumulus and stratocumulus clouds, which are, in my opinion, the most cloud-like type of clouds. This is quite important in art, remembering the old adage:

“It doesn’t matter how real it is. What matters is how real it looks”

Also, this method is meant to draw clouds illuminated from above (that would be between 10 AM and 2 PM).

You don’t have to draw your clouds like this, but you can use this as a starting point for drawing your own custom clouds with all kinds of effects for all kinds of needs. So let’s get started!

Step 1

The first thing you want to do is choose your color palette. Most likely you already have your blue for your background, and that is going to be your first and darker color. From there, start making lighter and less saturated colors (some hue shifting wouldn’t hurt as well), until you have 6 shades of blue. The darkest shade is your sky color, and the lightest one should be white, or really close to it.

Make some rectangles with each of these colors as I have done on the top-left corner of my picture, So you can easily access them whenever you need them.

Step 2

Once you choose your colors, start by picking the lightest color and draw some pixels resembling downward arcs, or the letter M if you like. These are going to be the topmost pixels in your cloud. Don’t put too much thought into these pixels, but try and make sure they’re somewhat evenly spaced, and not too thick (especially at the borders).

As you can see, I’ve drawn some two groups of pixels, one on the left and one on the right, each one roughly going up and then down again. I know, you need a lot of imagination to visualize this >_<;;; These are the highlights of your clouds.

Step 3

Now with the next shade of blue draw some pixels below and to the sides of the pixels you’ve already drawn. Think of this as building a base for the pixels you’ve already drawn. Once again, don’t put too much thought into what you’re drawing.

Step 4

Now with the darkest shade of blue that is not the background shade, draw the bottom of the cloud. This should be drawn a little bit more straight, but as with the first pixels, they should somewhat resemble an upside pointing arc. Once again, don’t stress the precision of the pixels you’re drawing, as clouds are all about randomness.

This is the bottom base of your cloud, and the rest of the cloud will go in between these two slices.

Step 5

Now, with the next lighter shade of blue, do the same as you did on step 3, only that above the pixels you’ve just drawn. On the next step we’re going to do a floodfill so also try and adjust the pixels in your image so the interior of the cloud will get properly filled without bleeding to the rest of the sky.

Step 6

Now with the only remaining shade of blue, floodfill the interior of the cloud. This is quite straightforward, but now you can see most of how the cloud will be like.

Step 7

Now it’s time for details. Fix any pixels that don’t look very good, and add some other pixels where they need to be added. You should polish your work, but once more, don’t dedicate too much time to this. Before you finish, don’t forget to remove your palette.

Finished!

There, now you have a bunch of pixels that look like a cloud. Now make lots of them and now you have your sky!