summaryrefslogtreecommitdiff
path: root/tinystun.c
blob: 3fa703a73403509998d26af700747c770188ff97 (plain)
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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <netinet/in.h>
#include <sys/socket.h>

/**
 * Parse reply to STUN binding request
 *
 * \param req[in] Original request. Transaction id must match reply.
 * \param rep[in] Reply. Invalid after this function is called.
 * \param sz[in] Size of reply (in bytes).
 *
 * \return String containing server-reflexive address. Address is in the
 *   form "ip:port". String overwrites reply. Reply must not be used after
 *   this function.
 */
char* stun_parse_reply(const unsigned char*req, unsigned char* rep, size_t sz)
{
	static char addr[23];

	// length requirement
	if (sz < 32) {
		return NULL;
	}

	// type must be binding reply
	if (rep[0] != 0x01 || rep[1] != 0x01) {
		return NULL;
	}

	// magic cookie must match
	if (rep[4] != 0x21 || rep[5] != 0x12 || rep[6] != 0xa4 || rep[7] != 0x42) {
		return NULL;
	}

	// transaction id must match
	for (int i = 8; i < 20; i++) {
		if (req[i] != rep[i]) {
			return NULL;
		}
	}

	// first attribute expected to be of type XOR-MAPPED-ADDRESS
	if (rep[20] != 0x00 || rep[21] != 0x20) {
		return NULL;
	}

	// length expected to be 8 bytes
	if (rep[22] != 0x00 || rep[23] != 0x08) {
		return NULL;
	}

	// family expected to be IPv4
	if (rep[24] != 0x00 || rep[25] != 0x01) {
		return NULL;
	}

	// get address and port
	sprintf(rep, "%d.%d.%d.%d:%d",
		rep[28] ^ 0x21, rep[29] ^ 0x12, rep[30] ^ 0xa4, rep[31] ^ 0x42,
		((rep[26] ^ 0x21) << 8) | (rep[27] ^ 0x12));

	return rep;
}

int rtc_stun_binding_request()
{
	int fd;
	unsigned char req[] = {
		0x00, 0x01,						// binding request
		0x00, 0x00,						// length
		0x21, 0x12, 0xa4, 0x42,			// magic cookie
		rand(), rand(), rand(), rand(),	// tx id
		rand(), rand(), rand(), rand(),	// tx id (cont.)
		rand(), rand(), rand(), rand(),	// tx id (cont.)
	};

	if (fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP), -1 == fd) {
		perror("socket");
	}

	struct sockaddr_in sa = {
		.sin_addr.s_addr = htonl(164 << 24 | 90 << 16 | 156 << 8 | 231),
		.sin_port = htons(3478),
		.sin_family = AF_INET
	};

	if (-1 == sendto(fd, req, sizeof(req), 0, (struct sockaddr*)&sa, sizeof(sa))) {
		perror("sendto");
	}

	unsigned char rep[4096];
	int n;
	socklen_t sl = sizeof(struct sockaddr_in);
	if (n = recvfrom(fd, rep, sizeof(rep), 0, (struct sockaddr*)&sa, &sl), -1 == n) {
		perror("recvfrom");
	}

	char* a;
	if (a = stun_parse_reply(req, rep, n), a) {
		printf("%s\n", a);
	}
}

int main()
{
	srand(time(0));
	rtc_stun_binding_request();
	return 0;
}