From f2c15c2af30820366fd4f52576dfbe38a430cf3e Mon Sep 17 00:00:00 2001 From: Chris Hiszpanski Date: Sat, 11 Jun 2022 17:10:30 -0700 Subject: Toy STUN client --- tinystun.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 tinystun.c (limited to 'tinystun.c') diff --git a/tinystun.c b/tinystun.c new file mode 100644 index 0000000..3fa703a --- /dev/null +++ b/tinystun.c @@ -0,0 +1,112 @@ +#include +#include +#include + +#include +#include + +/** + * 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; +} -- cgit v1.2.3