Commit 70c0ff3d authored by YI's avatar YI
Browse files

connect to client with user provided meta info

parent 868f68b4
......@@ -2,3 +2,4 @@
*.o
.DS_Store
*.swp
.ccls-cache/
CC = gcc
CFLAGS = -g -Wall
all: nat_traversal
all: nat_traversal punch_server stun_server_test
# clang warn about unused argument, it requires -pthread when compiling but not when linking
nat_traversal: main.o nat_traversal.o nat_type.o
$(CC) $(CFLAGS) -o nat_traversal main.o nat_traversal.o nat_type.o -pthread
punch_server: punch_server.go
go build punch_server.go
stun_server_test: stun_server_test.c
gcc stun_host_test.c nat_type.c -o stun_host_test
main.o: main.c
$(CC) $(CFLAGS) -c main.c
......@@ -17,4 +23,4 @@ nat_type.o: nat_type.c
$(CC) $(CFLAGS) -c nat_type.c
clean:
$(RM) nat_traversal *.o *~
$(RM) stun_host_test punch_server nat_traversal *.o *~
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "nat_traversal.h"
#include "utils.h"
#define DEFAULT_SERVER_PORT 9988
#define MSG_BUF_SIZE 512
// use public stun servers to detect port allocation rule
static char *stun_servers[] = {
"stun.ideasip.com",
"stun.ekiga.net",
"203.183.172.196"
};
#define STUN_SERVER_RETRIES 3
// definition checked against extern declaration
int verbose = 0;
int main(int argc, char** argv)
{
char* stun_server = stun_servers[0];
int main(int argc, char **argv) {
char *stun_server = NULL;
char local_ip[16] = "0.0.0.0";
uint16_t stun_port = DEFAULT_STUN_SERVER_PORT;
uint16_t local_port = DEFAULT_LOCAL_PORT;
char* punch_server = NULL;
char *punch_server = NULL;
char *meta = NULL;
char *peer_meta = NULL;
uint32_t peer_id = 0;
int ttl = 10;
int get_info = 0;
int get_info_from_meta = 0;
static char usage[] = "usage: [-h] [-H STUN_HOST] [-t ttl] [-P STUN_PORT] [-s punch server] [-d id] [-i SOURCE_IP] [-p SOURCE_PORT] [-v verbose]\n";
static char usage[] =
"usage: [-h] [-H STUN_HOST] [-t ttl] [-P STUN_PORT] [-s punch server] "
"[-d id] [-i SOURCE_IP] [-p SOURCE_PORT] [-v verbose]\n";
int opt;
while ((opt = getopt (argc, argv, "H:h:t:P:p:s:d:i:v")) != -1)
{
switch (opt)
{
while ((opt = getopt(argc, argv, "H:h:I:t:P:p:s:m:o:d:i:vzZ")) != -1) {
switch (opt) {
case 'h':
printf("%s", usage);
break;
case 'z':
get_info = 1;
break;
case 'Z':
get_info_from_meta = 1;
break;
case 'H':
stun_server = optarg;
break;
......@@ -56,6 +61,12 @@ int main(int argc, char** argv)
case 's':
punch_server = optarg;
break;
case 'm':
meta = optarg;
break;
case 'o':
peer_meta = optarg;
break;
case 'd':
peer_id = atoi(optarg);
break;
......@@ -74,47 +85,124 @@ int main(int argc, char** argv)
}
}
if (punch_server == NULL) {
printf("please specify punch server\n");
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(punch_server);
server_addr.sin_port = htons(DEFAULT_SERVER_PORT);
if (get_info) {
client cli;
struct peer_info peer;
int res = init(server_addr, &cli);
if (res) {
printf("init punch server socket failed\n");
return res;
}
if (!peer_id) {
printf("failed to get peer_id\n");
return -1;
}
int n = get_peer_info(&cli, peer_id, &peer);
if (n) {
verbose_log("get_peer_info() returned %d\n", n);
printf("failed to get info of remote peer\n");
return -1;
}
return 0;
}
if (get_info_from_meta) {
client cli;
struct peer_info peer;
int res = init(server_addr, &cli);
if (res) {
printf("init punch server socket failed\n");
return res;
}
if (peer_meta == NULL) {
printf("failed to get peer_meta\n");
return -1;
}
int n = get_peer_info_from_meta(&cli, peer_meta, &peer);
if (n) {
printf("failed to get info of remote peer\n");
return -1;
}
return 0;
}
char ext_ip[16] = {0};
uint16_t ext_port = 0;
// TODO we should try another STUN server if failed
nat_type type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip, &ext_port);
printf("NAT type: %s\n", get_nat_desc(type));
if (ext_port) {
printf("external address: %s:%d\n", ext_ip, ext_port);
} else {
return -1;
int i;
nat_type type;
for (i = 0; i < STUN_SERVER_RETRIES; i++) {
type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip,
&ext_port);
if (type != 0) {
break;
}
}
if (!punch_server) {
printf("please specify punch server\n");
if (!ext_port) {
return -1;
}
verbose_log("nat detect got ip: %s, port %d\n", ext_ip, ext_port);
struct peer_info self;
strncpy(self.ip, ext_ip, 16);
self.meta = malloc(32);
strcpy(self.ip, ext_ip);
self.port = ext_port;
self.type = type;
/* printf("first %s %ld\n", meta, strlen(meta)); */
if (meta != NULL) {
strcpy(self.meta, meta);
} else {
gen_random_string(self.meta, 20);
}
/* printf("first %s %ld\n", self.meta, strlen(self.meta)); */
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(punch_server);
server_addr.sin_port = htons(DEFAULT_SERVER_PORT);
/* strncpy(self.meta, meta, strlen(meta)); */
/* printf(""); */
/* printf("second %s %ld\n", meta, strlen(meta)); */
client c;
c.type = type;
c.ttl = ttl;
/* printf("third %s %ld\n", self.meta, strlen(self.meta)); */
if (enroll(self, server_addr, &c) < 0) {
printf("failed to enroll\n");
return -1;
}
printf("enroll successfully, ID: %d\n", c.id);
verbose_log("enroll successfully, ID: %d\n", c.id);
if (peer_id) {
printf("connecting to peer %d\n", peer_id);
verbose_log("connecting to peer %d\n", peer_id);
if (connect_to_peer(&c, peer_id) < 0) {
printf("failed to connect to peer %d\n", peer_id);
verbose_log("failed to connect to peer %d\n", peer_id);
return -1;
}
}
if (peer_meta != NULL) {
verbose_log("connecting to peer %s\n", peer_meta);
if (connect_to_peer_from_meta(&c, peer_meta) < 0) {
verbose_log("failed to connect to peer %s\n", peer_meta);
return -1;
}
......
#include <arpa/inet.h>
#include <errno.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "nat_traversal.h"
......@@ -22,66 +23,116 @@
// file scope variables
static int ports[MAX_PORT - MIN_PORT];
static int send_to_punch_server(client* c) {
static int send_to_punch_server(client *c) {
verbose_log("sending %ld bytes of data to punch server\n",
c->msg_buf - c->buf);
hexDump(NULL, c->buf, c->msg_buf - c->buf);
int n = send(c->sfd, c->buf, c->msg_buf - c->buf, 0);
c->msg_buf= c->buf;
c->msg_buf = c->buf;
if (n == -1) {
verbose_log("send to punch server, error number: %d, error: %s\n", errno,
strerror(errno));
return n;
}
return 0;
}
static int get_peer_info(client* cli, uint32_t peer_id, struct peer_info *peer) {
int get_peer_info(client *cli, uint32_t peer_id, struct peer_info *peer) {
cli->msg_buf = cli->buf;
cli->msg_buf = encode16(cli->msg_buf, GetPeerInfo);
cli->msg_buf = encode32(cli->msg_buf, peer_id);
if (-1 == send_to_punch_server(cli)) {
return -1;
}
return send_get_peer_info_request(cli, peer);
}
int n_bytes = recv(cli->sfd, (void*)peer, sizeof(struct peer_info), 0);
int get_peer_info_from_meta(client *cli, char peer_meta[21],
struct peer_info *peer) {
cli->msg_buf = cli->buf;
cli->msg_buf = encode16(cli->msg_buf, GetPeerInfoFromMeta);
cli->msg_buf = encode8(cli->msg_buf, (uint8_t)strlen(peer_meta));
cli->msg_buf = encode(cli->msg_buf, peer_meta, strlen(peer_meta));
return send_get_peer_info_request(cli, peer);
}
int recv_peer_info(int fd, struct peer_info *peer) {
struct my_peer_info peer_i;
int n_bytes;
n_bytes = recv(fd, (void *)&peer_i, sizeof(struct my_peer_info), 0);
if (n_bytes <= 0) {
return -1;
} else if (n_bytes == 1) {
// offline
}
if (n_bytes == 1) {
// offline or error
verbose_log("It seems peer has gone offline\n");
return 1;
} else {
peer->port = ntohs(peer->port);
peer->type = ntohs(peer->type);
}
verbose_log(
"Dumping %d bytes of data received, should have dumped %ld bytes\n",
n_bytes, sizeof(struct my_peer_info));
hexDump(NULL, &peer_i, n_bytes);
peer->id = ntohl(peer_i.id);
verbose_log("The ip got is %s\n", peer_i.ip);
strcpy(peer->ip, peer_i.ip);
peer->port = ntohs(peer_i.port);
peer->type = ntohs(peer_i.type);
verbose_log("Peer info got, id: %d, ip: %s, port: %d, type: %s, len: %d\n",
peer->id, peer->ip, peer->port, get_nat_desc(peer->type),
peer_i.len);
peer->meta = malloc(peer_i.len + 1);
int m_bytes = recv(fd, (void *)peer->meta, peer_i.len, 0);
if (m_bytes <= 0) {
verbose_log("failed to get meta, error: %s\n", strerror(errno));
return -1;
}
verbose_log("meta is %s\n", peer->meta);
n_bytes += m_bytes;
hexDump(NULL, peer, n_bytes);
return n_bytes;
}
int send_get_peer_info_request(client *cli, struct peer_info *peer) {
if (-1 == send_to_punch_server(cli)) {
return -1;
}
if (recv_peer_info(cli->sfd, peer) > 0) {
return 0;
}
return -1;
}
static int send_dummy_udp_packet(int fd, struct sockaddr_in addr) {
char dummy = 'c';
struct timeval tv = {5, 0};
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
return sendto(fd, &dummy, 1, 0, (struct sockaddr *)&addr, sizeof(addr));
}
static int punch_hole(struct sockaddr_in peer_addr, int ttl) {
int hole = socket(AF_INET, SOCK_DGRAM, 0);
if (hole != -1) {
//struct sockaddr_in local_addr;
//local_addr.sin_family = AF_INET;
//local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1);
//int reuse_addr = 1;
//setsockopt(hole, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr));
//if (bind(hole, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
// struct sockaddr_in local_addr;
// local_addr.sin_family = AF_INET;
// local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1);
// int reuse_addr = 1;
// setsockopt(hole, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr,
// sizeof(reuse_addr)); if (bind(hole, (struct sockaddr *)&local_addr,
// sizeof(local_addr))) {
// if (errno == EADDRINUSE) {
// printf("addr already in use, try another port\n");
// verbose_log("addr already in use, try another port\n");
// return -1;
// }
//}
/* TODO we can use traceroute to get the number of hops to the peer
* to make sure this packet woudn't reach the peer but get through the NAT in front of itself
* to make sure this packet woudn't reach the peer but get through the NAT
* in front of itself
*/
setsockopt(hole, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
// send short ttl packets to avoid triggering flooding protection of NAT in front of peer
// send short ttl packets to avoid triggering flooding protection of NAT in
// front of peer
if (send_dummy_udp_packet(hole, peer_addr) < 0) {
return -1;
}
......@@ -90,7 +141,7 @@ static int punch_hole(struct sockaddr_in peer_addr, int ttl) {
return hole;
}
static int wait_for_peer(int* socks, int sock_num, struct timeval *timeout) {
static int wait_for_peer(int *socks, int sock_num, struct timeval *timeout) {
fd_set fds;
int max_fd = 0;
FD_ZERO(&fds);
......@@ -144,17 +195,14 @@ static void shuffle(int *num, int len) {
}
}
static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_info remote_peer) {
static int connect_to_symmetric_nat(client *c, struct peer_info remote_peer) {
// TODO choose port prediction strategy
/*
* according to birthday paradox, probability that port randomly chosen from [1024, 65535]
* will collide with another one chosen by the same way is
* p(n) = 1-(64511!/(64511^n*64511!))
* where '!' is the factorial operator, n is the number of ports chosen.
* P(100)=0.073898
* P(200)=0.265667
* P(300)=0.501578
* according to birthday paradox, probability that port randomly chosen from
* [1024, 65535] will collide with another one chosen by the same way is p(n)
* = 1-(64511!/(64511^n*64511!)) where '!' is the factorial operator, n is the
* number of ports chosen. P(100)=0.073898 P(200)=0.265667 P(300)=0.501578
* P(400)=0.710488
* P(500)=0.856122
* P(600)=0.938839
......@@ -177,7 +225,8 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
peer_addr.sin_port = htons(port);
if ((holes[i] = punch_hole(peer_addr, c->ttl)) < 0) {
// NAT in front of us wound't tolerate too many ports used by one application
// NAT in front of us wound't tolerate too many ports used by one
// application
verbose_log("failed to punch hole, error: %s\n", strerror(errno));
break;
}
......@@ -192,10 +241,10 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
// hole punched, notify remote peer via punch server
c->msg_buf = encode16(c->msg_buf, NotifyPeer);
c->msg_buf = encode32(c->msg_buf, peer_id);
c->msg_buf = encode32(c->msg_buf, remote_peer.id);
send_to_punch_server(c);
struct timeval timeout={100, 0};
struct timeval timeout = {100, 0};
int fd = wait_for_peer(holes, i, &timeout);
if (fd > 0) {
on_connected(fd);
......@@ -204,29 +253,15 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
for (; j < i; ++j) {
close(holes[j]);
}
printf("timout, not connected\n");
verbose_log("timout, not connected\n");
}
return 0;
}
// run in another thread
static void* server_notify_handler(void* data) {
int server_sock = *(int*)data;
struct peer_info peer;
// wait for notification
printf("waiting for notification...\n");
for (; ;) {
if (recv(server_sock, &peer, sizeof peer, 0) > 0) {
break;
}
}
peer.port = ntohs(peer.port);
peer.type = ntohs(peer.type);
printf("recved command, ready to connect to %s:%d\n", peer.ip, peer.port);
void try_connect_to_peer(struct peer_info peer) {
verbose_log("recved command, ready to connect to %s:%d\n", peer.ip,
peer.port);
struct sockaddr_in peer_addr;
......@@ -244,7 +279,7 @@ static void* server_notify_handler(void* data) {
continue;
}
if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("failed to create socket, send %d probe packets\n", i);
verbose_log("failed to create socket, send %d probe packets\n", i);
break;
}
......@@ -252,7 +287,7 @@ static void* server_notify_handler(void* data) {
// let OS choose available ports
if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) {
printf("may trigger flooding protection\n");
verbose_log("may trigger flooding protection\n");
break;
}
......@@ -264,11 +299,11 @@ static void* server_notify_handler(void* data) {
on_connected(fd);
// TODO
return NULL;
return;
}
}
printf("holes punched, waiting for peer\n");
verbose_log("holes punched, waiting for peer\n");
struct timeval tv = {100, 0};
int fd = wait_for_peer(sock_array, i, &tv);
if (fd > 0) {
......@@ -279,58 +314,138 @@ static void* server_notify_handler(void* data) {
close(sock_array[j]);
}
}
}
// TODO wait for next notification
// run in another thread
static void *server_notify_handler(void *data) {
int server_sock = *(int *)data;
struct peer_info peer;
// wait for notification
verbose_log("waiting for notification...\n");
for (;;) {
struct peer_info peer;
if (recv_peer_info(server_sock, &peer) > 0) {
break;
}
}
verbose_log("recved command, ready to connect to %s:%d\n", peer.ip,
peer.port);
try_connect_to_peer(peer);
return NULL;
}
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c) {
int i, temp;
for (i = 0, temp = MIN_PORT; temp <= MAX_PORT; i++, temp++) {
ports[i] = temp;
void hexDump(char *desc, void *addr, int len) {
int i;
unsigned char buff[17];
unsigned char *pc = (unsigned char *)addr;
// Output description if given.
if (desc != NULL)
verbose_log("%s:\n", desc);
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
verbose_log(" %s\n", buff);
// Output the offset.
verbose_log(" %04x ", i);
}
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
// Now the hex code for the specific character.
verbose_log(" %02x", pc[i]);
if (connect(server_sock, (struct sockaddr *)&punch_server, sizeof(punch_server)) < 0) {
printf("failed to connect to punch server\n");
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) {
buff[i % 16] = '.';
} else {
buff[i % 16] = pc[i];
}
return -1;
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
verbose_log(" ");
i++;
}
// And print the final ASCII bit.
verbose_log(" %s\n", buff);
}
int init(struct sockaddr_in punch_server, client *c) {
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
int res = connect(server_sock, (struct sockaddr *)&punch_server,
sizeof(punch_server));
if (res < 0) {
verbose_log("failed to connect to punch server: %d, port: %d\n",
punch_server.sin_addr.s_addr, punch_server.sin_port);
verbose_log("code: %d, error: %s\n", res, strerror(errno));
return res;
}
c->sfd = server_sock;
return 0;
}
int enroll(struct peer_info self, struct sockaddr_in punch_server, client *c) {
verbose_log("enrolling, meta: %s, ip: %s, port: %d, nat type: %s\n",
self.meta, self.ip, self.port, get_nat_desc(self.type));
int i, temp;
for (i = 0, temp = MIN_PORT; temp <= MAX_PORT; i++, temp++) {
ports[i] = temp;
}
int res = init(punch_server, c);
if (res != 0) {
verbose_log("failed to connect to punch server\n");
return res;
}
c->msg_buf = c->buf;
c->msg_buf = encode16(c->msg_buf, Enroll);
c->msg_buf = encode(c->msg_buf, self.ip, 16);
c->msg_buf = encode16(c->msg_buf, self.port);
c->msg_buf = encode16(c->msg_buf, self.type);
c->msg_buf = encode8(c->msg_buf, (uint8_t)strlen(self.meta));
c->msg_buf = encode(c->msg_buf, self.meta, strlen(self.meta));
if (-1 == send_to_punch_server(c)) {
verbose_log("sending to punch server failed\n");
return -1;
}
// wait for server reply to get own ID
uint32_t peer_id = 0;
struct timeval tv;
tv.tv_sec = 5;
tv.tv_sec = 10;
tv.tv_usec = 0;
setsockopt(server_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
int n = recv(server_sock, &peer_id, sizeof(uint32_t), 0);
setsockopt(c->sfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
int n = recv(c->sfd, &peer_id, sizeof(uint32_t), 0);
if (n != sizeof(uint32_t)) {
return -1;
}
c->id = ntohl(peer_id);
verbose_log("enrolled, id: %d\n", c->id);
return 0;
}
pthread_t wait_for_command(int* server_sock)
{
pthread_t wait_for_command(int *server_sock) {
// wait for command from punch server in another thread
pthread_t thread_id;
pthread_create(&thread_id, NULL, server_notify_handler, (void*)server_sock);
pthread_create(&thread_id, NULL, server_notify_handler, (void *)server_sock);
return thread_id;
}
......@@ -339,56 +454,75 @@ void on_connected(int sock) {
char buf[MSG_BUF_SIZE] = {0};
struct sockaddr_in remote_addr;
socklen_t fromlen = sizeof remote_addr;
recvfrom(sock, buf, MSG_BUF_SIZE, 0, (struct sockaddr *)&remote_addr, &fromlen);
printf("recv %s\n", buf);
recvfrom(sock, buf, MSG_BUF_SIZE, 0, (struct sockaddr *)&remote_addr,
&fromlen);
verbose_log("recv %s\n", buf);
printf("connected with peer from %s:%d\n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
verbose_log("connected with peer from %s:%d\n",
inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
// restore the ttl
int ttl = 64;
setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
sendto(sock, "hello, peer", strlen("hello, peer"), 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr));
sendto(sock, "hello, peer", strlen("hello, peer"), 0,
(struct sockaddr *)&remote_addr, sizeof(remote_addr));
}
int connect_to_peer(client* cli, uint32_t peer_id) {
int real_connect_to_peer(client *cli, struct peer_info *peer) {
verbose_log("connecting to id: %d, meta: %s, ip: %s, port %d, nat type: %s\n",
peer->id, peer->meta, peer->ip, peer->port,
get_nat_desc(peer->type));
return connect_to_symmetric_nat(cli, *peer);
// choose less restricted peer as initiator
/* switch(peer.type) { */
/* case OpenInternet: */
/* // todo */
/* break; */
/* case FullCone: */
/* break; */
/* case RestricNAT: */
/* // todo */
/* break; */
/* case RestricPortNAT: */
/* // todo */
/* break; */
/* case SymmetricNAT: */
/* if (cli->type == SymmetricNAT) { */
/* connect_to_symmetric_nat_from_meta(cli, peer_meta, peer); */
/* } */
/* else { */
/* // todo */
/* } */
/* break; */
/* default: */
/* verbose_log("unknown nat type\n"); */
/* return -1; */
/* // log */
/* } */
/* return 0; */
}
int connect_to_peer(client *cli, uint32_t peer_id) {
struct peer_info peer;
int n = get_peer_info(cli, peer_id, &peer);
if (n) {
verbose_log("get_peer_info() return %d\n", n);
printf("failed to get info of remote peer\n");
verbose_log("failed to get info of remote peer\n");
return -1;
}
return real_connect_to_peer(cli, &peer);
}
printf("peer %d: %s:%d, nat type: %s\n", peer_id, peer.ip, peer.port, get_nat_desc(peer.type));
// choose less restricted peer as initiator
switch(peer.type) {
case OpenInternet:
// todo
break;
case FullCone:
break;
case RestricNAT:
// todo
break;
case RestricPortNAT:
// todo
break;
case SymmetricNAT:
if (cli->type == SymmetricNAT) {
connect_to_symmetric_nat(cli, peer_id, peer);
}
else {
// todo
}
break;
default:
printf("unknown nat type\n");
int connect_to_peer_from_meta(client *cli, char *peer_meta) {
struct peer_info peer;
int n = get_peer_info_from_meta(cli, peer_meta, &peer);
if (n) {
verbose_log("get_peer_info() return %d\n", n);
verbose_log("failed to get info of remote peer\n");
return -1;
// log
}
return 0;
return real_connect_to_peer(cli, &peer);
}
......@@ -7,32 +7,51 @@ struct client {
int sfd;
uint32_t id;
char buf[128];
//use a stack-based buffer to prevent memory allocation every time
char* msg_buf;
// use a stack-based buffer to prevent memory allocation every time
char *msg_buf;
nat_type type;
char ext_ip[16];
uint16_t ext_port;
// ttl of hole punching packets,
// it should be greater than the number of hops between host to NAT of own side
// and less than the number of hops between host to NAT of remote side,
// it should be greater than the number of hops between host to NAT of own
// side and less than the number of hops between host to NAT of remote side,
// so that the hole punching packets just die in the way
int ttl;
};
struct my_peer_info {
uint32_t id;
char ip[16];
uint16_t port;
uint16_t type;
uint8_t len;
} __attribute__((packed));
struct peer_info {
uint32_t id;
char ip[16];
uint16_t port;
uint16_t type;
char *meta;
};
enum msg_type {
Enroll = 0x01,
GetPeerInfo = 0x02,
NotifyPeer = 0x03,
};
GetPeerInfoFromMeta = 0x04,
NotifyPeerFromMeta = 0x05,
};
// public functions
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c);
pthread_t wait_for_command(int* server_sock);
int connect_to_peer(client* cli, uint32_t peer_id);
int enroll(struct peer_info self, struct sockaddr_in punch_server, client *c);
pthread_t wait_for_command(int *server_sock);
int connect_to_peer(client *cli, uint32_t peer_id);
int connect_to_peer_from_meta(client *cli, char *peer_meta);
void on_connected(int sock);
int get_peer_info(client *cli, uint32_t peer_id, struct peer_info *peer);
int get_peer_info_from_meta(client *cli, char *peer_meta,
struct peer_info *peer);
int init(struct sockaddr_in punch_server, client *c);
void hexDump(char *desc, void *addr, int len);
int send_get_peer_info_request(client *cli, struct peer_info *peer);
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "nat_type.h"
#define MAX_RETRIES_NUM 3
static const char* nat_types[] = {
"blocked",
"open internet",
"full cone",
"restricted NAT",
"port-restricted cone",
"symmetric NAT",
"error"
};
char* encode16(char* buf, uint16_t data)
{
// use public stun servers to detect port allocation rule
static char *stun_servers[] = {"stun.avigora.com",
"iphone-stun.strato-iphone.de",
"numb.viagenie.ca",
"stun.12connect.com",
"stun.12voip.com",
"stun.1und1.de",
"stun.3clogic.com",
"stun.3cx.com",
"stun.a-mm.tv",
"stun.aa.net.uk",
"stun.acrobits.cz",
"stun.actionvoip.com",
"stun.advfn.com",
"stun.aeta-audio.com",
"stun.aeta.com",
"stun.altar.com.pl",
"stun.avigora.com",
"stun.avigora.fr",
"stun.b2b2c.ca",
"stun.bahnhof.net",
"stun.barracuda.com",
"stun.bluesip.net",
"stun.botonakis.com",
"stun.budgetsip.com",
"stun.cablenet-as.net",
"stun.callromania.ro",
"stun.callwithus.com",
"stun.cheapvoip.com",
"stun.ciktel.com",
"stun.cloopen.com",
"stun.commpeak.com",
"stun.comtube.com",
"stun.comtube.ru",
"stun.cope.es",
"stun.counterpath.com",
"stun.counterpath.net",
"stun.cryptonit.net",
"stun.demos.ru",
"stun.dus.net",
"stun.easycall.pl",
"stun.easyvoip.com",
"stun.ekiga.net",
"stun.epygi.com",
"stun.etoilediese.fr",
"stun.faktortel.com.au",
"stun.freecall.com",
"stun.freeswitch.org",
"stun.freevoipdeal.com",
"stun.gmx.de",
"stun.gmx.net",
"stun.gradwell.com",
"stun.halonet.pl",
"stun.hoiio.com",
"stun.hosteurope.de",
"stun.ideasip.com",
"stun.infra.net",
"stun.internetcalls.com",
"stun.intervoip.com",
"stun.ippi.fr",
"stun.ipshka.com",
"stun.irian.at",
"stun.it1.hr",
"stun.ivao.aero",
"stun.jumblo.com",
"stun.justvoip.com",
"stun.linphone.org",
"stun.liveo.fr",
"stun.lowratevoip.com",
"stun.lundimatin.fr",
"stun.mit.de",
"stun.miwifi.com",
"stun.modulus.gr",
"stun.myvoiptraffic.com",
"stun.mywatson.it",
"stun.netappel.com",
"stun.nfon.net",
"stun.noc.ams-ix.net",
"stun.node4.co.uk",
"stun.nonoh.net",
"stun.nottingham.ac.uk",
"stun.nova.is",
"stun.ooma.com",
"stun.ozekiphone.com",
"stun.personal-voip.de",
"stun.phone.com",
"stun.pjsip.org",
"stun.poivy.com",
"stun.powervoip.com",
"stun.ppdi.com",
"stun.rackco.com",
"stun.rockenstein.de",
"stun.rolmail.net",
"stun.rynga.com",
"stun.schlund.de",
"stun.services.mozilla.com",
"stun.sigmavoip.com",
"stun.sip.us",
"stun.sipdiscount.com",
"stun.sipgate.net",
"stun.siplogin.de",
"stun.sipnet.net",
"stun.sipnet.ru",
"stun.siportal.it",
"stun.sippeer.dk",
"stun.siptraffic.com",
"stun.sma.de",
"stun.smartvoip.com",
"stun.smsdiscount.com",
"stun.snafu.de",
"stun.solcon.nl",
"stun.solnet.ch",
"stun.sonetel.com",
"stun.sonetel.net",
"stun.speedy.com.ar",
"stun.srce.hr",
"stun.ssl7.net",
"stun.stunprotocol.org",
"stun.t-online.de",
"stun.tel.lu",
"stun.telbo.com",
"stun.tng.de",
"stun.twt.it",
"stun.uls.co.za",
"stun.usfamily.net",
"stun.vipgroup.net",
"stun.viva.gr",
"stun.vivox.com",
"stun.vo.lu",
"stun.voicetrading.com",
"stun.voip.aebc.com",
"stun.voip.blackberry.com",
"stun.voip.eutelia.it",
"stun.voiparound.com",
"stun.voipblast.com",
"stun.voipbuster.com",
"stun.voipbusterpro.com",
"stun.voipcheap.co.uk",
"stun.voipcheap.com",
"stun.voipfibre.com",
"stun.voipgain.com",
"stun.voipgate.com",
"stun.voipinfocenter.com",
"stun.voipplanet.nl",
"stun.voippro.com",
"stun.voipraider.com",
"stun.voipstunt.com",
"stun.voipwise.com",
"stun.voipzoom.com",
"stun.voxgratia.org",
"stun.voxox.com",
"stun.voztele.com",
"stun.webcalldirect.com",
"stun.xtratelecom.es",
"stun.zadarma.com",
"stun.zoiper.com",
"stun1.faktortel.com.au"};
static const char *nat_types[] = {
"blocked", "open internet", "full cone",
"restricted NAT", "port-restricted cone", "symmetric NAT",
"error"};
char *encode8(char *buf, size_t data) {
/* uint8_t ndata = htons(data); */
memcpy(buf, (void *)(&data), sizeof(uint8_t));
return buf + sizeof(uint8_t);
}
char *encode16(char *buf, uint16_t data) {
uint16_t ndata = htons(data);
memcpy(buf, (void*)(&ndata), sizeof(uint16_t));
memcpy(buf, (void *)(&ndata), sizeof(uint16_t));
return buf + sizeof(uint16_t);
}
char* encode32(char* buf, uint32_t data)
{
char *encode32(char *buf, uint32_t data) {
uint32_t ndata = htonl(data);
memcpy(buf, (void*)(&ndata), sizeof(uint32_t));
memcpy(buf, (void *)(&ndata), sizeof(uint32_t));
return buf + sizeof(uint32_t);
}
char* encodeAtrUInt32(char* ptr, uint16_t type, uint32_t value)
{
char *encodeAtrUInt32(char *ptr, uint16_t type, uint32_t value) {
ptr = encode16(ptr, type);
ptr = encode16(ptr, 4);
ptr = encode32(ptr, value);
......@@ -47,15 +204,22 @@ char* encodeAtrUInt32(char* ptr, uint16_t type, uint32_t value)
return ptr;
}
char* encode(char* buf, const char* data, unsigned int length)
{
char *encode(char *buf, const char *data, unsigned int length) {
memcpy(buf, data, length);
return buf + length;
}
static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress* result )
{
if (hdrLen != 8 /* ipv4 size */ && hdrLen != 20 /* ipv6 size */ ) {
char *encodeWithLen(char *buf, const char *data) {
size_t s = sizeof(*data);
memcpy(buf, &s, sizeof(s));
buf = buf + sizeof(s);
memcpy(buf, data, s);
return buf + s;
}
static int stun_parse_atr_addr(char *body, unsigned int hdrLen,
StunAtrAddress *result) {
if (hdrLen != 8 /* ipv4 size */ && hdrLen != 20 /* ipv6 size */) {
return -1;
}
body++; // Skip pad
......@@ -63,12 +227,13 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress*
uint16_t nport;
memcpy(&nport, body, 2);
body+=2;
body += 2;
result->port = ntohs(nport);
if (result->family == IPv4Family) {
uint32_t naddr;
memcpy(&naddr, body, sizeof(uint32_t)); body+=sizeof(uint32_t);
memcpy(&naddr, body, sizeof(uint32_t));
body += sizeof(uint32_t);
result->addr.ipv4 = ntohl(naddr);
// Note: addr.ipv4 is stored in host byte order
return 0;
......@@ -79,9 +244,9 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress*
return -1;
}
static void gen_random_string(char *s, const int len) {
static const char alphanum[] =
"0123456789"
void gen_random_string(char *s, const int len) {
srand(time(NULL));
const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
......@@ -89,21 +254,24 @@ static void gen_random_string(char *s, const int len) {
for (; i < len; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
}
s[len] = '\0';
}
static int send_bind_request(int sock, const char* remote_host, uint16_t remote_port, uint32_t change_ip, uint32_t change_port, StunAtrAddress* addr_array) {
char* buf = malloc(MAX_STUN_MESSAGE_LENGTH);
char* ptr = buf;
static int send_bind_request(int sock, const char *remote_host,
uint16_t remote_port, uint32_t change_ip,
uint32_t change_port, StunAtrAddress *addr_array) {
char *buf = malloc(MAX_STUN_MESSAGE_LENGTH);
char *ptr = buf;
StunHeader h;
h.msgType = BindRequest;
gen_random_string((char*)&h.magicCookieAndTid, 16);
gen_random_string((char *)&h.magicCookieAndTid, 16);
ptr = encode16(ptr, h.msgType);
char* lengthp = ptr;
char *lengthp = ptr;
ptr = encode16(ptr, 0);
ptr = encode(ptr, (const char*)&h.id, sizeof(h.id));
ptr = encode(ptr, (const char *)&h.id, sizeof(h.id));
if (change_ip || change_port) {
ptr = encodeAtrUInt32(ptr, ChangeRequest, change_ip | change_port);
......@@ -122,12 +290,14 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
memcpy(&remote_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
memcpy(&remote_addr.sin_addr.s_addr, server->h_addr_list[0],
server->h_length);
remote_addr.sin_port = htons(remote_port);
int retries;
for (retries = 0; retries < MAX_RETRIES_NUM; retries++) {
if (-1 == sendto(sock, buf, ptr - buf, 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) {
if (-1 == sendto(sock, buf, ptr - buf, 0, (struct sockaddr *)&remote_addr,
sizeof(remote_addr))) {
// sendto() barely failed
free(buf);
......@@ -139,15 +309,16 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
if (recvfrom(sock, buf, 512, 0, (struct sockaddr *)&remote_addr, &fromlen) <= 0) {
if (recvfrom(sock, buf, 512, 0, (struct sockaddr *)&remote_addr,
&fromlen) <= 0) {
if (errno != EAGAIN || errno != EWOULDBLOCK) {
free(buf);
return -1;
}
//timout, retry
// timout, retry
} else {
// got response
break;
......@@ -163,23 +334,24 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
uint16_t msg_type = ntohs(reply_header.msgType);
if (msg_type == BindResponse) {
char* body = buf + sizeof(StunHeader);
char *body = buf + sizeof(StunHeader);
uint16_t size = ntohs(reply_header.msgLength);
StunAtrHdr* attr;
StunAtrHdr *attr;
unsigned int attrLen;
unsigned int attrLenPad;
int atrType;
while (size > 0) {
attr = (StunAtrHdr*)(body);
attr = (StunAtrHdr *)(body);
attrLen = ntohs(attr->length);
// attrLen may not be on 4 byte boundary, in which case we need to pad to 4 bytes when advancing to next attribute
// attrLen may not be on 4 byte boundary, in which case we need to pad to
// 4 bytes when advancing to next attribute
attrLenPad = attrLen % 4 == 0 ? 0 : 4 - (attrLen % 4);
atrType = ntohs(attr->type);
if ( attrLen + attrLenPad + 4 > size ) {
if (attrLen + attrLenPad + 4 > size) {
free(buf);
return -1;
......@@ -197,7 +369,7 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
}
break;
case ChangedAddress:
if (stun_parse_atr_addr( body, attrLen, addr_array + 1)) {
if (stun_parse_atr_addr(body, attrLen, addr_array + 1)) {
free(buf);
return -1;
......@@ -217,11 +389,17 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
return 0;
}
const char* get_nat_desc(nat_type type) {
return nat_types[type];
}
const char *get_nat_desc(nat_type type) { return nat_types[type]; }
nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char* local_ip, uint16_t local_port, char* ext_ip, uint16_t* ext_port) {
nat_type detect_nat_type(char *stun_host, uint16_t stun_port,
const char *local_ip, uint16_t local_port,
char *ext_ip, uint16_t *ext_port) {
if (stun_host == NULL) {
srand(time(NULL));
int i = rand() % (sizeof(stun_servers) / sizeof(stun_servers[0]) - 1);
verbose_log("Using the %dth stun server %s\n", i, stun_servers[i]);
stun_host = stun_servers[i];
}
uint32_t mapped_ip = 0;
uint16_t mapped_port = 0;
int s = socket(AF_INET, SOCK_DGRAM, 0);
......@@ -232,7 +410,8 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
nat_type nat_type;
int reuse_addr = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr));
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr,
sizeof(reuse_addr));
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
......@@ -281,9 +460,10 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
goto cleanup_sock;
} else {
if (changed_ip != 0 && changed_port != 0) {
if (send_bind_request(s, stun_host, stun_port, ChangeIpFlag, ChangePortFlag, bind_result)) {
if (send_bind_request(s, stun_host, stun_port, ChangeIpFlag,
ChangePortFlag, bind_result)) {
struct in_addr addr = {htonl(changed_ip)};
char* alt_host = inet_ntoa(addr);
char *alt_host = inet_ntoa(addr);
memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
......@@ -293,20 +473,21 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
goto cleanup_sock;
}
if (mapped_ip != bind_result[0].addr.ipv4 || mapped_port != bind_result[0].port) {
if (mapped_ip != bind_result[0].addr.ipv4 ||
mapped_port != bind_result[0].port) {
nat_type = SymmetricNAT;
goto cleanup_sock;
}
if (send_bind_request(s, alt_host, changed_port, 0, ChangePortFlag, bind_result)) {
if (send_bind_request(s, alt_host, changed_port, 0, ChangePortFlag,
bind_result)) {
nat_type = RestricPortNAT;
goto cleanup_sock;
}
nat_type = RestricNAT;
goto cleanup_sock;
}
else {
} else {
nat_type = FullCone;
goto cleanup_sock;
}
......
......@@ -73,6 +73,7 @@ typedef struct
} addr;
} StunAtrAddress;
char* encode8(char* buf, size_t data);
char* encode16(char* buf, uint16_t data);
char* encode32(char* buf, uint32_t data);
char* encode(char* buf, const char* data, unsigned int length);
......@@ -83,6 +84,7 @@ extern int verbose;
printf(format, ##__VA_ARGS__); \
} while(0)
nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char* local_host, uint16_t local_port, char* ext_ip, uint16_t* ext_port);
nat_type detect_nat_type(char* stun_host, uint16_t stun_port, const char* local_host, uint16_t local_port, char* ext_ip, uint16_t* ext_port);
const char* get_nat_desc(nat_type type);
void gen_random_string(char *s, const int len);
23.21.150.121
iphone-stun.strato-iphone.de
numb.viagenie.ca
s1.taraba.net
s2.taraba.net
stun.12connect.com
stun.12voip.com
stun.1und1.de
stun.2talk.co.nz
stun.2talk.com
stun.3clogic.com
stun.3cx.com
stun.a-mm.tv
stun.aa.net.uk
stun.acrobits.cz
stun.actionvoip.com
stun.advfn.com
stun.aeta-audio.com
stun.aeta.com
stun.alltel.com.au
stun.altar.com.pl
stun.annatel.net
stun.antisip.com
stun.arbuz.ru
stun.avigora.com
stun.avigora.fr
stun.awa-shima.com
stun.awt.be
stun.b2b2c.ca
stun.bahnhof.net
stun.barracuda.com
stun.bluesip.net
stun.bmwgs.cz
stun.botonakis.com
stun.budgetphone.nl
stun.budgetsip.com
stun.cablenet-as.net
stun.callromania.ro
stun.callwithus.com
stun.cbsys.net
stun.chathelp.ru
stun.cheapvoip.com
stun.ciktel.com
stun.cloopen.com
stun.colouredlines.com.au
stun.comfi.com
stun.commpeak.com
stun.comtube.com
stun.comtube.ru
stun.cope.es
stun.counterpath.com
stun.counterpath.net
stun.cryptonit.net
stun.darioflaccovio.it
stun.datamanagement.it
stun.dcalling.de
stun.decanet.fr
stun.demos.ru
stun.develz.org
stun.dingaling.ca
stun.doublerobotics.com
stun.drogon.net
stun.duocom.es
stun.dus.net
stun.e-fon.ch
stun.easybell.de
stun.easycall.pl
stun.easyvoip.com
stun.efficace-factory.com
stun.einsundeins.com
stun.einsundeins.de
stun.ekiga.net
stun.epygi.com
stun.etoilediese.fr
stun.eyeball.com
stun.faktortel.com.au
stun.freecall.com
stun.freeswitch.org
stun.freevoipdeal.com
stun.fuzemeeting.com
stun.gmx.de
stun.gmx.net
stun.gradwell.com
stun.halonet.pl
stun.hellonanu.com
stun.hoiio.com
stun.hosteurope.de
stun.ideasip.com
stun.imesh.com
stun.infra.net
stun.internetcalls.com
stun.intervoip.com
stun.ipcomms.net
stun.ipfire.org
stun.ippi.fr
stun.ipshka.com
stun.iptel.org
stun.irian.at
stun.it1.hr
stun.ivao.aero
stun.jappix.com
stun.jumblo.com
stun.justvoip.com
stun.kanet.ru
stun.kiwilink.co.nz
stun.kundenserver.de
stun.linea7.net
stun.linphone.org
stun.liveo.fr
stun.lowratevoip.com
stun.lugosoft.com
stun.lundimatin.fr
stun.magnet.ie
stun.manle.com
stun.mgn.ru
stun.mit.de
stun.mitake.com.tw
stun.miwifi.com
stun.modulus.gr
stun.mozcom.com
stun.myvoiptraffic.com
stun.mywatson.it
stun.nas.net
stun.neotel.co.za
stun.netappel.com
stun.netappel.fr
stun.netgsm.com.tr
stun.nfon.net
stun.noblogs.org
stun.noc.ams-ix.net
stun.node4.co.uk
stun.nonoh.net
stun.nottingham.ac.uk
stun.nova.is
stun.nventure.com
stun.on.net.mk
stun.ooma.com
stun.ooonet.ru
stun.oriontelekom.rs
stun.outland-net.de
stun.ozekiphone.com
stun.patlive.com
stun.personal-voip.de
stun.petcube.com
stun.phone.com
stun.phoneserve.com
stun.pjsip.org
stun.poivy.com
stun.powerpbx.org
stun.powervoip.com
stun.ppdi.com
stun.prizee.com
stun.qq.com
stun.qvod.com
stun.rackco.com
stun.rapidnet.de
stun.rb-net.com
stun.refint.net
stun.remote-learner.net
stun.rixtelecom.se
stun.rockenstein.de
stun.rolmail.net
stun.rounds.com
stun.rynga.com
stun.samsungsmartcam.com
stun.schlund.de
stun.services.mozilla.com
stun.sigmavoip.com
stun.sip.us
stun.sipdiscount.com
stun.sipgate.net
stun.siplogin.de
stun.sipnet.net
stun.sipnet.ru
stun.siportal.it
stun.sippeer.dk
stun.siptraffic.com
stun.skylink.ru
stun.sma.de
stun.smartvoip.com
stun.smsdiscount.com
stun.snafu.de
stun.softjoys.com
stun.solcon.nl
stun.solnet.ch
stun.sonetel.com
stun.sonetel.net
stun.sovtest.ru
stun.speedy.com.ar
stun.spokn.com
stun.srce.hr
stun.ssl7.net
stun.stunprotocol.org
stun.symform.com
stun.symplicity.com
stun.sysadminman.net
stun.t-online.de
stun.tagan.ru
stun.tatneft.ru
stun.teachercreated.com
stun.tel.lu
stun.telbo.com
stun.telefacil.com
stun.tis-dialog.ru
stun.tng.de
stun.twt.it
stun.u-blox.com
stun.ucallweconn.net
stun.ucsb.edu
stun.ucw.cz
stun.uls.co.za
stun.unseen.is
stun.usfamily.net
stun.veoh.com
stun.vidyo.com
stun.vipgroup.net
stun.virtual-call.com
stun.viva.gr
stun.vivox.com
stun.vline.com
stun.vo.lu
stun.vodafone.ro
stun.voicetrading.com
stun.voip.aebc.com
stun.voip.blackberry.com
stun.voip.eutelia.it
stun.voiparound.com
stun.voipblast.com
stun.voipbuster.com
stun.voipbusterpro.com
stun.voipcheap.co.uk
stun.voipcheap.com
stun.voipfibre.com
stun.voipgain.com
stun.voipgate.com
stun.voipinfocenter.com
stun.voipplanet.nl
stun.voippro.com
stun.voipraider.com
stun.voipstunt.com
stun.voipwise.com
stun.voipzoom.com
stun.vopium.com
stun.voxgratia.org
stun.voxox.com
stun.voys.nl
stun.voztele.com
stun.vyke.com
stun.webcalldirect.com
stun.whoi.edu
stun.wifirst.net
stun.wwdl.net
stun.xs4all.nl
stun.xtratelecom.es
stun.yesss.at
stun.zadarma.com
stun.zadv.com
stun.zoiper.com
stun1.faktortel.com.au
stun1.voiceeclipse.net
stunserver.org
package main
import (
"bytes"
"encoding/binary"
"fmt"
"errors"
"io"
"math/rand"
"net"
"os"
"sync"
"time"
log "github.com/sirupsen/logrus"
)
type nat_info struct {
Ip [16]byte
type PeerInfo struct {
IP [16]byte
Port uint16
NatType uint16
Meta string
ID uint32
}
type natInfo struct {
IP [16]byte
Port uint16
Nat_type uint16
NatType uint16
}
const (
Enroll = 1
GetPeerInfo = 2
NotifyPeer = 3
_ = iota
Enroll
GetPeerInfo
NotifyPeer
GetPeerInfoFromMeta
NotifyPeerFromMeta
PeerOffline = 1
PeerError = 2
ListeningPort = ":9988"
)
var seq uint32 = 1
var peers map[uint32]nat_info
var peers_conn map[uint32]net.Conn
var m sync.Mutex
var (
seq uint32 = 1
peers map[uint32]PeerInfo
peersFromMeta map[string]PeerInfo
peerConn map[uint32]net.Conn
peerConnFromMeta map[string]net.Conn
mutex sync.RWMutex
letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
ErrPeerNotFound = errors.New("Peer not found")
ErrConnNotFound = errors.New("Connection not found, peer maybe is now offline")
)
func main() {
peers = make(map[uint32]nat_info)
peers_conn = make(map[uint32]net.Conn)
func init() {
peers = make(map[uint32]PeerInfo)
peersFromMeta = make(map[string]PeerInfo)
peerConn = make(map[uint32]net.Conn)
peerConnFromMeta = make(map[string]net.Conn)
rand.Seed(time.Now().UnixNano())
l, _ := net.Listen("tcp", ":9988")
log.SetOutput(os.Stdout)
log.SetLevel(log.DebugLevel)
}
func main() {
l, err := net.Listen("tcp", ListeningPort)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Fatal("Unable to start server")
}
defer l.Close()
go dumpPeers()
for {
conn, err := l.Accept()
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("Accepting connection failed")
continue
}
log.Info("New connection received")
go handleConn(conn)
}
}
func dumpPeers() {
for {
time.Sleep(10 * time.Second)
mutex.RLock()
for k, v := range peers {
log.WithFields(log.Fields{
"Key": k,
"ID": v.ID,
"Meta": v.Meta,
"IP": string(v.IP[:]),
"Port": v.Port,
"NatType": v.NatType,
}).Debug("peer info")
}
mutex.RUnlock()
}
}
func RandStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func readMeta(c io.Reader) (meta string, err error) {
var metaSize uint8
err = binary.Read(c, binary.BigEndian, &metaSize)
if err != nil || metaSize == 0 {
log.WithFields(log.Fields{
"err": err,
"metaSize": metaSize,
}).Info("reading meta failed")
return
}
data := make([]byte, metaSize)
if _, err = c.Read(data); err != nil {
log.WithFields(log.Fields{
"err": err,
}).Info("reading meta failed")
return
}
meta = string(data)
return
}
func writeMeta(c io.Writer, meta string) (err error) {
err = binary.Write(c, binary.BigEndian, uint8(len(meta)))
if err != nil {
return
}
return binary.Write(c, binary.BigEndian, []byte(meta))
}
func readPeerInfo(r io.Reader) (p PeerInfo, err error) {
var IP [16]byte
var Port uint16
var NatType uint16
if err = binary.Read(r, binary.BigEndian, &IP); err != nil {
return
}
if err = binary.Read(r, binary.BigEndian, &Port); err != nil {
return
}
if err = binary.Read(r, binary.BigEndian, &NatType); err != nil {
return
}
p = PeerInfo{
IP: IP,
Port: Port,
NatType: NatType,
}
meta, err := readMeta(r)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Warn("reading meta failed")
meta = RandStringRunes(18)
}
p.Meta = meta
log.WithFields(log.Fields{
"meta": meta,
}).Info("reading meta succeeded")
return
}
func writePeerInfo(w io.Writer, p PeerInfo) (err error) {
p1 := natInfo{
IP: p.IP,
Port: p.Port,
NatType: p.NatType,
}
var buf bytes.Buffer
if err = binary.Write(&buf, binary.BigEndian, p.ID); err != nil {
return
}
if err = binary.Write(&buf, binary.BigEndian, p1); err != nil {
return
}
if err = writeMeta(&buf, p.Meta); err != nil {
return
}
return binary.Write(w, binary.BigEndian, (&buf).Bytes())
}
func getPeerInfo(p PeerInfo) (p1 PeerInfo, err error) {
var ok bool
mutex.RLock()
defer mutex.RUnlock()
if p.ID != 0 {
if p1, ok = peers[p.ID]; ok {
return
}
}
if p.Meta != "" {
if p1, ok = peersFromMeta[p.Meta]; ok {
return
}
}
err = ErrPeerNotFound
return
}
func getConn(p PeerInfo) (c net.Conn, err error) {
var ok bool
mutex.RLock()
defer mutex.RUnlock()
if p.ID != 0 {
if c, ok = peerConn[p.ID]; ok {
return
}
}
if p.Meta != "" {
if c, ok = peerConnFromMeta[p.Meta]; ok {
return
}
}
err = ErrConnNotFound
return
}
// 2 bytes for message type
func handleConn(c net.Conn) {
defer c.Close()
var peerID uint32 = 0
log.Info("new connection received!")
var myInfo PeerInfo
for {
// read message type first
var myBuf bytes.Buffer
w := io.MultiWriter(&myBuf, c)
data := make([]byte, 2)
_, err := c.Read(data)
log.WithFields(log.Fields{
"header": data,
}).Info("new received header")
if err != nil {
m.Lock()
fmt.Printf("error: %v, peer %d disconnected\n", err, peerID)
delete(peers, peerID)
delete(peers_conn, peerID)
m.Unlock()
mutex.Lock()
delete(peers, myInfo.ID)
delete(peerConn, myInfo.ID)
delete(peersFromMeta, myInfo.Meta)
delete(peerConnFromMeta, myInfo.Meta)
mutex.Unlock()
log.WithFields(log.Fields{
"err": err,
"myID": myInfo.ID,
}).Info("peer left")
return
}
switch binary.BigEndian.Uint16(data[:]) {
t := binary.BigEndian.Uint16(data[:])
switch t {
case Enroll:
var peer nat_info
err = binary.Read(c, binary.BigEndian, &peer)
var err error
myInfo, err = readPeerInfo(c)
if err != nil {
continue
log.WithFields(log.Fields{
"err": err,
}).Warn("Reading meta failed")
break
}
fmt.Println("peer enrolled, addr: ", string(peer.Ip[:]), peer.Port, peer.Nat_type)
m.Lock()
mutex.Lock()
seq++
peerID = seq
peers[peerID] = peer
peers_conn[peerID] = c
fmt.Println("new peer, id : ", peerID)
m.Unlock()
err = binary.Write(c, binary.BigEndian, peerID)
myInfo.ID = seq
peers[myInfo.ID] = myInfo
peerConn[myInfo.ID] = c
if myInfo.Meta != "" {
peersFromMeta[myInfo.Meta] = myInfo
peerConnFromMeta[myInfo.Meta] = c
}
mutex.Unlock()
log.WithFields(log.Fields{
"ID": myInfo.ID,
"Meta": myInfo.Meta,
"IP": string(myInfo.IP[:]),
"Port": myInfo.Port,
"NatType": myInfo.NatType,
}).Debug("New peer enrolled")
err = binary.Write(w, binary.BigEndian, myInfo.ID)
if err != nil {
continue
log.WithFields(log.Fields{
"err": err,
"ID": myInfo.ID,
"Meta": myInfo.Meta,
"IP": string(myInfo.IP[:]),
"Port": myInfo.Port,
"NatType": myInfo.NatType,
}).Warn("Unable to return my peer info")
break
}
case GetPeerInfo:
var peer_id uint32
binary.Read(c, binary.BigEndian, &peer_id)
if val, ok := peers[peer_id]; ok {
binary.Write(c, binary.BigEndian, val)
} else {
var offline uint8 = 0
binary.Write(c, binary.BigEndian, offline)
fmt.Printf("%d offline\n", peer_id)
var peerID uint32
err = binary.Read(c, binary.BigEndian, &peerID)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to get peer id")
binary.Write(c, binary.BigEndian, PeerOffline)
break
}
peer, err := getPeerInfo(PeerInfo{ID: peerID})
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to get peer info")
break
}
err = writePeerInfo(w, peer)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to write peer info")
break
}
case NotifyPeer:
var peer_id uint32
binary.Read(c, binary.BigEndian, &peer_id)
fmt.Println("notify to peer", peer_id)
if val, ok := peers_conn[peer_id]; ok {
if err = binary.Write(val, binary.BigEndian, peers[peerID]); err != nil {
// unable to notify peer
fmt.Println("offline")
var peerID uint32
err = binary.Read(c, binary.BigEndian, &peerID)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to get peer id")
break
}
} else {
fmt.Println("offline")
conn, err := getConn(PeerInfo{ID: peerID})
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to get peer conn")
binary.Write(c, binary.BigEndian, PeerOffline)
break
}
err = writePeerInfo(conn, myInfo)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerID": peerID,
"myID": myInfo.ID,
}).Warn("Unable to write my peer info to peer connection")
break
}
case GetPeerInfoFromMeta:
peerMeta, err := readMeta(c)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"myMeta": myInfo.Meta,
}).Warn("Unable to get peer meta")
break
}
peer, err := getPeerInfo(PeerInfo{Meta: peerMeta})
if err != nil {
log.WithFields(log.Fields{
"err": err,
"meta": peerMeta,
"myMeta": myInfo.Meta,
}).Warn("Unable to get peer info")
break
}
err = writePeerInfo(w, peer)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerMeta": peer.Meta,
"myMeta": myInfo.Meta,
}).Warn("Unable to write peer info")
break
}
case NotifyPeerFromMeta:
peerMeta, err := readMeta(c)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"myMeta": myInfo.Meta,
}).Warn("Unable to get peer id")
break
}
conn, err := getConn(PeerInfo{Meta: peerMeta})
if err != nil {
log.WithFields(log.Fields{
"err": err,
"meta": peerMeta,
"myMeta": myInfo.Meta,
}).Warn("Unable to get peer conn")
binary.Write(c, binary.BigEndian, PeerOffline)
break
}
err = writePeerInfo(conn, myInfo)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"peerMeta": peerMeta,
"myMeta": myInfo.Meta,
}).Warn("Unable to write my peer to peer connection")
break
}
default:
fmt.Println("illegal message")
log.WithFields(log.Fields{
"type": t,
}).Warn("Illegal message")
}
log.WithFields(log.Fields{
"response": (&myBuf).Bytes(),
}).Debug("Response sent")
}
return
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nat_type.h"
#define DEFAULT_STUN_SERVER_PORT 3478
#define DEFAULT_LOCAL_PORT 34780
int appendToFile(char *c, nat_type type) {
FILE *fp;
int retval;
fp = fopen("public_stun_list-working.txt", "a");
if (fp == NULL)
return -1;
retval = fprintf(fp, "%s, %d\n", c, type);
fclose(fp);
return retval;
}
int main() {
uint16_t stun_port = DEFAULT_STUN_SERVER_PORT;
uint16_t local_port = DEFAULT_LOCAL_PORT;
char ext_ip[16] = {0};
uint16_t ext_port = 0;
char local_ip[16] = "0.0.0.0";
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
/* from https://gist.github.com/mondain/b0ec1cf5f60ae726202e */
fp = fopen("public-stun-list.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu:\n", read);
printf("%s\n", line);
line[strcspn(line, "\n")] = 0;
char *stun_server = line;
printf("Testing stun server %s\n", stun_server);
nat_type type = detect_nat_type(stun_server, stun_port, local_ip,
local_port, ext_ip, &ext_port);
printf("The return value is %d (%s)\n", type, get_nat_desc(type));
if (type != 0) {
appendToFile(stun_server, type);
};
;
}
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment