summaryrefslogtreecommitdiff
path: root/stun.c
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
committerDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
commit1a9fbac03c684f29cff9ac44875bd9504a89f54e (patch)
tree1b2e40dbe5dc1899ef5b62c4325c9b94c9c450fc /stun.c
all: import netsniff-ng 0.5.8-rc0 source
We decided to get rid of the old Git history and start a new one for several reasons: *) Allow / enforce only high-quality commits (which was not the case for many commits in the history), have a policy that is more close to the one from the Linux kernel. With high quality commits, we mean code that is logically split into commits and commit messages that are signed-off and have a proper subject and message body. We do not allow automatic Github merges anymore, since they are total bullshit. However, we will either cherry-pick your patches or pull them manually. *) The old archive was about ~27MB for no particular good reason. This basically derived from the bad decision that also some PDF files where stored there. From this moment onwards, no binary objects are allowed to be stored in this repository anymore. The old archive is not wiped away from the Internet. You will still be able to find it, e.g. on git.cryptoism.org etc. Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Diffstat (limited to 'stun.c')
-rw-r--r--stun.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/stun.c b/stun.c
new file mode 100644
index 0000000..5892ad5
--- /dev/null
+++ b/stun.c
@@ -0,0 +1,190 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Copyright 2011 Daniel Borkmann.
+ * Subject to the GPL, version 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+
+#include "xmalloc.h"
+#include "xutils.h"
+#include "die.h"
+
+extern int print_stun_probe(char *server, int sport, int tport);
+
+#define BINDING_REQUEST 0x0001
+#define BINDING_RESPONSE 0x0101
+
+#define MAPPED_ADDRESS 0x0001
+
+#define TIMEOUT 5000
+#define REQUEST_LEN 20
+
+#define ID_COOKIE_FIELD htonl(((int) 'a' << 24) + \
+ ((int) 'c' << 16) + \
+ ((int) 'd' << 8) + \
+ (int) 'c')
+
+struct stun_header {
+ uint16_t type;
+ uint16_t len;
+ uint32_t magic_cookie;
+ uint32_t transid[3];
+};
+
+struct stun_attrib {
+ uint16_t type;
+ uint16_t len;
+ uint8_t *value;
+};
+
+struct stun_mapped_addr {
+ uint8_t none;
+ uint8_t family;
+ uint16_t port;
+ uint32_t ip;
+};
+
+static int stun_test(const char *server_ip, int server_port,
+ int tun_port)
+{
+ int ret, sock;
+ uint8_t pkt[256];
+ uint8_t rpkt[256];
+ size_t len, off, max;
+ struct in_addr in;
+ struct timeval timeout;
+ struct stun_header *hdr, *rhdr;
+ struct stun_attrib *attr;
+ struct stun_mapped_addr *addr;
+ struct sockaddr_in saddr, daddr;
+ fd_set fdset;
+
+ if (!server_ip)
+ return -EINVAL;
+
+ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ panic("Cannot obtain socket!\n");
+
+ set_reuseaddr(sock);
+
+ saddr.sin_family = PF_INET;
+ saddr.sin_port = htons(tun_port);
+ saddr.sin_addr.s_addr = INADDR_ANY;
+
+ ret = bind(sock, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (ret)
+ panic("Cannot bind udp socket!\n");
+
+ len = REQUEST_LEN;
+ hdr = (struct stun_header *) pkt;
+ hdr->type = htons(BINDING_REQUEST);
+ hdr->len = 0;
+ hdr->magic_cookie = ID_COOKIE_FIELD;
+ hdr->transid[0] = htonl(rand());
+ hdr->transid[1] = htonl(rand());
+ hdr->transid[2] = htonl(rand());
+
+ daddr.sin_family = PF_INET;
+ daddr.sin_port = htons(server_port);
+ daddr.sin_addr.s_addr = inet_addr(server_ip);
+
+ ret = sendto(sock, pkt, len, 0, (struct sockaddr *) &daddr,
+ sizeof(daddr));
+ if (ret != len) {
+ printf("Error sending request (%s)!\n", strerror(errno));
+ return -EIO;
+ }
+
+ timeout.tv_sec = TIMEOUT / 1000;
+ timeout.tv_usec = (TIMEOUT % 1000) * 1000;
+
+ FD_ZERO(&fdset);
+ FD_SET(sock, &fdset);
+
+ ret = select(sock + 1, &fdset, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ printf("STUN server timeout!\n");
+ return -EIO;
+ }
+
+ memset(rpkt, 0, sizeof(rpkt));
+ len = read(sock, rpkt, sizeof(rpkt));
+
+ close(sock);
+
+ if (len < REQUEST_LEN) {
+ printf("Bad STUN response (%s)!\n", strerror(errno));
+ return -EIO;
+ }
+
+ rhdr = (struct stun_header *) rpkt;
+ if (ntohs(rhdr->type) != BINDING_RESPONSE) {
+ printf("Wrong STUN response type!\n");
+ return -EIO;
+ }
+
+ if (rhdr->len == 0) {
+ printf("No attributes in STUN response!\n");
+ return -EIO;
+ }
+
+ if (rhdr->magic_cookie != hdr->magic_cookie ||
+ rhdr->transid[0] != hdr->transid[0] ||
+ rhdr->transid[1] != hdr->transid[1] ||
+ rhdr->transid[2] != hdr->transid[2]) {
+ printf("Got wrong STUN transaction id!\n");
+ return -EIO;
+ }
+
+ off = REQUEST_LEN;
+ max = ntohs(rhdr->len) + REQUEST_LEN;
+
+ while (off + 8 < max) {
+ attr = (struct stun_attrib *) (rpkt + off);
+ if (ntohs(attr->type) != MAPPED_ADDRESS)
+ goto next;
+
+ addr = (struct stun_mapped_addr *) (rpkt + off + 4);
+ if (addr->family != 0x1)
+ break;
+
+ in.s_addr = addr->ip;
+ printf("Public mapping %s:%u!\n",
+ inet_ntoa(in), ntohs(addr->port));
+ break;
+next:
+ off += 4;
+ off += ntohs(attr->len);
+ }
+
+ return 0;
+}
+
+int print_stun_probe(char *server, int sport, int tport)
+{
+ char *address;
+ struct hostent *hp;
+
+ printf("STUN on %s:%u\n", server, sport);
+
+ srand(time(NULL));
+ hp = gethostbyname(server);
+ if (!hp)
+ return -EIO;
+ address = inet_ntoa(*(struct in_addr *) hp->h_addr_list[0]);
+ return stun_test(address, sport, tport);
+}