summaryrefslogtreecommitdiff
path: root/tinystun.c
diff options
context:
space:
mode:
authorChris Hiszpanski <chris@hiszpanski.name>2022-06-11 17:10:30 -0700
committerChris Hiszpanski <chris@hiszpanski.name>2022-06-11 17:10:30 -0700
commitf2c15c2af30820366fd4f52576dfbe38a430cf3e (patch)
tree984fbde20662e0db8644dbcf8192fabf86866e55 /tinystun.c
parent402e37321fbb1eea15ec5f1af0b1f4566dcffa7a (diff)
Toy STUN clienttinystun
Diffstat (limited to 'tinystun.c')
-rw-r--r--tinystun.c112
1 files changed, 112 insertions, 0 deletions
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 <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;
+}