aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/contrib/nat-hole-punching/nat-punch-server.c
blob: 425885e2d25044e79d1357ebf61053c433dc0fd0 (plain) (blame)
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
/* Copyright (C) 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */

/* Example only. Do not run in production. */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

struct entry {
	uint8_t pubkey[32];
	uint32_t ip;
	uint16_t port;
};

enum { MAX_ENTRIES = 65536, PORT = 49918 };

static struct entry entries[MAX_ENTRIES];
static unsigned int next_entry;

/* XX: this should use a hash table */
static struct entry *find_entry(uint8_t key[32])
{
	int i;
	for (i = 0; i < MAX_ENTRIES; ++i) {
		if (!memcmp(entries[i].pubkey, key, 32))
			return &entries[i];
	}
	return NULL;
}

/* XX: this is obviously vulnerable to DoS */
static struct entry *find_or_insert_entry(uint8_t key[32])
{
	struct entry *entry = find_entry(key);
	if (!entry) {
		entry = &entries[next_entry++ % MAX_ENTRIES];
		memcpy(entry->pubkey, key, 32);
	}
	return entry;
}

int main(int argc, char *argv[])
{
	struct sockaddr_in addr = {
		.sin_family = AF_INET,
		.sin_addr = { .s_addr = htonl(INADDR_ANY) },
		.sin_port = htons(PORT)
	};
	struct {
		uint8_t my_pubkey[32];
		uint8_t their_pubkey[32];
	} __attribute__((packed)) packet;
	struct {
		uint32_t ip;
		uint16_t port;
	} __attribute__((packed)) reply;
	struct entry *entry;
	socklen_t len;
	ssize_t retlen;
	int optval;
	int sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		perror("socket");
		return errno;
	}

	optval = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
		perror("setsockopt");
		return errno;
	}

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return errno;
	}

	for (;;) {
		len = sizeof(addr);
		if (recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, &len) != sizeof(packet)) {
			perror("recvfrom");
			continue;
		}
		entry = find_or_insert_entry(packet.my_pubkey);
		entry->ip = addr.sin_addr.s_addr;
		entry->port = addr.sin_port;
		entry = find_entry(packet.their_pubkey);
		if (entry) {
			reply.ip = entry->ip;
			reply.port = entry->port;
			if (sendto(sock, &reply, sizeof(reply), 0, (struct sockaddr *)&addr, len) < 0) {
				perror("sendto");
				continue;
			}
		} else {
			if (sendto(sock, NULL, 0, 0, (struct sockaddr *)&addr, len) < 0) {
				perror("sendto");
				continue;
			}
		}
	}

	close(sock);
	return 0;
}