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

connect to client with user provided meta info

parent 868f68b4
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
*.o *.o
.DS_Store .DS_Store
*.swp *.swp
.ccls-cache/
CC = gcc CC = gcc
CFLAGS = -g -Wall 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 # clang warn about unused argument, it requires -pthread when compiling but not when linking
nat_traversal: main.o nat_traversal.o nat_type.o nat_traversal: main.o nat_traversal.o nat_type.o
$(CC) $(CFLAGS) -o nat_traversal main.o nat_traversal.o nat_type.o -pthread $(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 main.o: main.c
$(CC) $(CFLAGS) -c main.c $(CC) $(CFLAGS) -c main.c
...@@ -17,4 +23,4 @@ nat_type.o: nat_type.c ...@@ -17,4 +23,4 @@ nat_type.o: nat_type.c
$(CC) $(CFLAGS) -c nat_type.c $(CC) $(CFLAGS) -c nat_type.c
clean: clean:
$(RM) nat_traversal *.o *~ $(RM) stun_host_test punch_server nat_traversal *.o *~
#include <unistd.h> #include <arpa/inet.h>
#include <string.h> #include <errno.h>
#include <stdlib.h> #include <netinet/in.h>
#include <stdio.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <unistd.h>
#include <arpa/inet.h>
#include "nat_traversal.h" #include "nat_traversal.h"
#include "utils.h"
#define DEFAULT_SERVER_PORT 9988 #define DEFAULT_SERVER_PORT 9988
#define MSG_BUF_SIZE 512 #define MSG_BUF_SIZE 512
#define STUN_SERVER_RETRIES 3
// use public stun servers to detect port allocation rule
static char *stun_servers[] = {
"stun.ideasip.com",
"stun.ekiga.net",
"203.183.172.196"
};
// definition checked against extern declaration // definition checked against extern declaration
int verbose = 0; int verbose = 0;
int main(int argc, char** argv) int main(int argc, char **argv) {
{ char *stun_server = NULL;
char* stun_server = stun_servers[0];
char local_ip[16] = "0.0.0.0"; char local_ip[16] = "0.0.0.0";
uint16_t stun_port = DEFAULT_STUN_SERVER_PORT; uint16_t stun_port = DEFAULT_STUN_SERVER_PORT;
uint16_t local_port = DEFAULT_LOCAL_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; uint32_t peer_id = 0;
int ttl = 10; 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; int opt;
while ((opt = getopt (argc, argv, "H:h:t:P:p:s:d:i:v")) != -1) while ((opt = getopt(argc, argv, "H:h:I:t:P:p:s:m:o:d:i:vzZ")) != -1) {
{ switch (opt) {
switch (opt)
{
case 'h': case 'h':
printf("%s", usage); printf("%s", usage);
break; break;
case 'z':
get_info = 1;
break;
case 'Z':
get_info_from_meta = 1;
break;
case 'H': case 'H':
stun_server = optarg; stun_server = optarg;
break; break;
...@@ -56,6 +61,12 @@ int main(int argc, char** argv) ...@@ -56,6 +61,12 @@ int main(int argc, char** argv)
case 's': case 's':
punch_server = optarg; punch_server = optarg;
break; break;
case 'm':
meta = optarg;
break;
case 'o':
peer_meta = optarg;
break;
case 'd': case 'd':
peer_id = atoi(optarg); peer_id = atoi(optarg);
break; break;
...@@ -74,47 +85,124 @@ int main(int argc, char** argv) ...@@ -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}; char ext_ip[16] = {0};
uint16_t ext_port = 0; uint16_t ext_port = 0;
// TODO we should try another STUN server if failed // 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); int i;
nat_type type;
printf("NAT type: %s\n", get_nat_desc(type)); for (i = 0; i < STUN_SERVER_RETRIES; i++) {
if (ext_port) { type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip,
printf("external address: %s:%d\n", ext_ip, ext_port); &ext_port);
} else { if (type != 0) {
return -1; break;
}
} }
if (!punch_server) { if (!ext_port) {
printf("please specify punch server\n");
return -1; return -1;
} }
verbose_log("nat detect got ip: %s, port %d\n", ext_ip, ext_port);
struct peer_info self; struct peer_info self;
strncpy(self.ip, ext_ip, 16); self.meta = malloc(32);
strcpy(self.ip, ext_ip);
self.port = ext_port; self.port = ext_port;
self.type = type; 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; /* strncpy(self.meta, meta, strlen(meta)); */
server_addr.sin_family = AF_INET; /* printf(""); */
server_addr.sin_addr.s_addr = inet_addr(punch_server);
server_addr.sin_port = htons(DEFAULT_SERVER_PORT); /* printf("second %s %ld\n", meta, strlen(meta)); */
client c; client c;
c.type = type; c.type = type;
c.ttl = ttl; c.ttl = ttl;
/* printf("third %s %ld\n", self.meta, strlen(self.meta)); */
if (enroll(self, server_addr, &c) < 0) { if (enroll(self, server_addr, &c) < 0) {
printf("failed to enroll\n"); printf("failed to enroll\n");
return -1; return -1;
} }
printf("enroll successfully, ID: %d\n", c.id); verbose_log("enroll successfully, ID: %d\n", c.id);
if (peer_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) { 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; 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.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 <sys/socket.h>
#include <netinet/in.h> #include <sys/time.h>
#include <netdb.h> #include <time.h>
#include <pthread.h> #include <unistd.h>
#include "nat_traversal.h" #include "nat_traversal.h"
...@@ -22,66 +23,116 @@ ...@@ -22,66 +23,116 @@
// file scope variables // file scope variables
static int ports[MAX_PORT - MIN_PORT]; 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); 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 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 = encode16(cli->msg_buf, GetPeerInfo);
cli->msg_buf = encode32(cli->msg_buf, peer_id); cli->msg_buf = encode32(cli->msg_buf, peer_id);
if (-1 == send_to_punch_server(cli)) { return send_get_peer_info_request(cli, peer);
return -1; }
}
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) { if (n_bytes <= 0) {
return -1; 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; return 1;
} else { }
peer->port = ntohs(peer->port); verbose_log(
peer->type = ntohs(peer->type); "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 0;
} }
return -1;
} }
static int send_dummy_udp_packet(int fd, struct sockaddr_in addr) { static int send_dummy_udp_packet(int fd, struct sockaddr_in addr) {
char dummy = 'c'; char dummy = 'c';
struct timeval tv = {5, 0}; 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)); return sendto(fd, &dummy, 1, 0, (struct sockaddr *)&addr, sizeof(addr));
} }
static int punch_hole(struct sockaddr_in peer_addr, int ttl) { static int punch_hole(struct sockaddr_in peer_addr, int ttl) {
int hole = socket(AF_INET, SOCK_DGRAM, 0); int hole = socket(AF_INET, SOCK_DGRAM, 0);
if (hole != -1) { if (hole != -1) {
//struct sockaddr_in local_addr; // struct sockaddr_in local_addr;
//local_addr.sin_family = AF_INET; // local_addr.sin_family = AF_INET;
//local_addr.sin_addr.s_addr = htonl(INADDR_ANY); // local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1); // local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1);
//int reuse_addr = 1; // int reuse_addr = 1;
//setsockopt(hole, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr)); // setsockopt(hole, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr,
//if (bind(hole, (struct sockaddr *)&local_addr, sizeof(local_addr))) { // sizeof(reuse_addr)); if (bind(hole, (struct sockaddr *)&local_addr,
// sizeof(local_addr))) {
// if (errno == EADDRINUSE) { // if (errno == EADDRINUSE) {
// printf("addr already in use, try another port\n"); // verbose_log("addr already in use, try another port\n");
// return -1; // return -1;
// } // }
//} //}
/* TODO we can use traceroute to get the number of hops to the peer /* 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)); 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) { if (send_dummy_udp_packet(hole, peer_addr) < 0) {
return -1; return -1;
} }
...@@ -90,7 +141,7 @@ static int punch_hole(struct sockaddr_in peer_addr, int ttl) { ...@@ -90,7 +141,7 @@ static int punch_hole(struct sockaddr_in peer_addr, int ttl) {
return hole; 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; fd_set fds;
int max_fd = 0; int max_fd = 0;
FD_ZERO(&fds); FD_ZERO(&fds);
...@@ -144,17 +195,14 @@ static void shuffle(int *num, int len) { ...@@ -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 // TODO choose port prediction strategy
/* /*
* according to birthday paradox, probability that port randomly chosen from [1024, 65535] * according to birthday paradox, probability that port randomly chosen from
* will collide with another one chosen by the same way is * [1024, 65535] will collide with another one chosen by the same way is p(n)
* p(n) = 1-(64511!/(64511^n*64511!)) * = 1-(64511!/(64511^n*64511!)) where '!' is the factorial operator, n is the
* where '!' is the factorial operator, n is the number of ports chosen. * number of ports chosen. P(100)=0.073898 P(200)=0.265667 P(300)=0.501578
* P(100)=0.073898
* P(200)=0.265667
* P(300)=0.501578
* P(400)=0.710488 * P(400)=0.710488
* P(500)=0.856122 * P(500)=0.856122
* P(600)=0.938839 * P(600)=0.938839
...@@ -177,7 +225,8 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -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); peer_addr.sin_port = htons(port);
if ((holes[i] = punch_hole(peer_addr, c->ttl)) < 0) { 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)); verbose_log("failed to punch hole, error: %s\n", strerror(errno));
break; break;
} }
...@@ -192,10 +241,10 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -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 // hole punched, notify remote peer via punch server
c->msg_buf = encode16(c->msg_buf, NotifyPeer); 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); send_to_punch_server(c);
struct timeval timeout={100, 0}; struct timeval timeout = {100, 0};
int fd = wait_for_peer(holes, i, &timeout); int fd = wait_for_peer(holes, i, &timeout);
if (fd > 0) { if (fd > 0) {
on_connected(fd); on_connected(fd);
...@@ -204,29 +253,15 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -204,29 +253,15 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
for (; j < i; ++j) { for (; j < i; ++j) {
close(holes[j]); close(holes[j]);
} }
printf("timout, not connected\n"); verbose_log("timout, not connected\n");
} }
return 0; return 0;
} }
// run in another thread void try_connect_to_peer(struct peer_info peer) {
static void* server_notify_handler(void* data) { verbose_log("recved command, ready to connect to %s:%d\n", peer.ip,
int server_sock = *(int*)data; peer.port);
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);
struct sockaddr_in peer_addr; struct sockaddr_in peer_addr;
...@@ -244,7 +279,7 @@ static void* server_notify_handler(void* data) { ...@@ -244,7 +279,7 @@ static void* server_notify_handler(void* data) {
continue; continue;
} }
if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 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; break;
} }
...@@ -252,7 +287,7 @@ static void* server_notify_handler(void* data) { ...@@ -252,7 +287,7 @@ static void* server_notify_handler(void* data) {
// let OS choose available ports // let OS choose available ports
if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) { 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; break;
} }
...@@ -264,11 +299,11 @@ static void* server_notify_handler(void* data) { ...@@ -264,11 +299,11 @@ static void* server_notify_handler(void* data) {
on_connected(fd); on_connected(fd);
// TODO // TODO
return NULL; return;
} }
} }
printf("holes punched, waiting for peer\n"); verbose_log("holes punched, waiting for peer\n");
struct timeval tv = {100, 0}; struct timeval tv = {100, 0};
int fd = wait_for_peer(sock_array, i, &tv); int fd = wait_for_peer(sock_array, i, &tv);
if (fd > 0) { if (fd > 0) {
...@@ -279,58 +314,138 @@ static void* server_notify_handler(void* data) { ...@@ -279,58 +314,138 @@ static void* server_notify_handler(void* data) {
close(sock_array[j]); 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; return NULL;
} }
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c) { void hexDump(char *desc, void *addr, int len) {
int i, temp; int i;
for (i = 0, temp = MIN_PORT; temp <= MAX_PORT; i++, temp++) { unsigned char buff[17];
ports[i] = temp; 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) { // And store a printable ASCII character for later.
printf("failed to connect to punch server\n"); 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; 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 = c->buf;
c->msg_buf = encode16(c->msg_buf, Enroll); c->msg_buf = encode16(c->msg_buf, Enroll);
c->msg_buf = encode(c->msg_buf, self.ip, 16); 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.port);
c->msg_buf = encode16(c->msg_buf, self.type); 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)) { if (-1 == send_to_punch_server(c)) {
verbose_log("sending to punch server failed\n");
return -1; return -1;
} }
// wait for server reply to get own ID // wait for server reply to get own ID
uint32_t peer_id = 0; uint32_t peer_id = 0;
struct timeval tv; struct timeval tv;
tv.tv_sec = 5; tv.tv_sec = 10;
tv.tv_usec = 0; tv.tv_usec = 0;
setsockopt(server_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); setsockopt(c->sfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
int n = recv(server_sock, &peer_id, sizeof(uint32_t), 0); int n = recv(c->sfd, &peer_id, sizeof(uint32_t), 0);
if (n != sizeof(uint32_t)) { if (n != sizeof(uint32_t)) {
return -1; return -1;
} }
c->id = ntohl(peer_id); c->id = ntohl(peer_id);
verbose_log("enrolled, id: %d\n", c->id);
return 0; 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 // wait for command from punch server in another thread
pthread_t thread_id; 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; return thread_id;
} }
...@@ -339,56 +454,75 @@ void on_connected(int sock) { ...@@ -339,56 +454,75 @@ void on_connected(int sock) {
char buf[MSG_BUF_SIZE] = {0}; char buf[MSG_BUF_SIZE] = {0};
struct sockaddr_in remote_addr; struct sockaddr_in remote_addr;
socklen_t fromlen = sizeof remote_addr; socklen_t fromlen = sizeof remote_addr;
recvfrom(sock, buf, MSG_BUF_SIZE, 0, (struct sockaddr *)&remote_addr, &fromlen); recvfrom(sock, buf, MSG_BUF_SIZE, 0, (struct sockaddr *)&remote_addr,
printf("recv %s\n", buf); &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 // restore the ttl
int ttl = 64; int ttl = 64;
setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); 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; struct peer_info peer;
int n = get_peer_info(cli, peer_id, &peer); int n = get_peer_info(cli, peer_id, &peer);
if (n) { if (n) {
verbose_log("get_peer_info() return %d\n", 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 -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)); int connect_to_peer_from_meta(client *cli, char *peer_meta) {
struct peer_info peer;
// choose less restricted peer as initiator int n = get_peer_info_from_meta(cli, peer_meta, &peer);
switch(peer.type) { if (n) {
case OpenInternet: verbose_log("get_peer_info() return %d\n", n);
// todo verbose_log("failed to get info of remote peer\n");
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");
return -1; return -1;
// log
} }
return real_connect_to_peer(cli, &peer);
return 0;
} }
...@@ -7,32 +7,51 @@ struct client { ...@@ -7,32 +7,51 @@ struct client {
int sfd; int sfd;
uint32_t id; uint32_t id;
char buf[128]; char buf[128];
//use a stack-based buffer to prevent memory allocation every time // use a stack-based buffer to prevent memory allocation every time
char* msg_buf; char *msg_buf;
nat_type type; nat_type type;
char ext_ip[16]; char ext_ip[16];
uint16_t ext_port; uint16_t ext_port;
// ttl of hole punching packets, // ttl of hole punching packets,
// it should be greater than the number of hops between host to NAT of own side // it should be greater than the number of hops between host to NAT of own
// and less than the number of hops between host to NAT of remote side, // 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 // so that the hole punching packets just die in the way
int ttl; 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 { struct peer_info {
uint32_t id;
char ip[16]; char ip[16];
uint16_t port; uint16_t port;
uint16_t type; uint16_t type;
char *meta;
}; };
enum msg_type { enum msg_type {
Enroll = 0x01, Enroll = 0x01,
GetPeerInfo = 0x02, GetPeerInfo = 0x02,
NotifyPeer = 0x03, NotifyPeer = 0x03,
}; GetPeerInfoFromMeta = 0x04,
NotifyPeerFromMeta = 0x05,
};
// public functions // public functions
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c); int enroll(struct peer_info self, struct sockaddr_in punch_server, client *c);
pthread_t wait_for_command(int* server_sock); pthread_t wait_for_command(int *server_sock);
int connect_to_peer(client* cli, uint32_t peer_id); 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); 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <sys/time.h>
#include <netdb.h> #include <time.h>
#include <unistd.h>
#include "nat_type.h" #include "nat_type.h"
#define MAX_RETRIES_NUM 3 #define MAX_RETRIES_NUM 3
static const char* nat_types[] = { // use public stun servers to detect port allocation rule
"blocked", static char *stun_servers[] = {"stun.avigora.com",
"open internet", "iphone-stun.strato-iphone.de",
"full cone", "numb.viagenie.ca",
"restricted NAT", "stun.12connect.com",
"port-restricted cone", "stun.12voip.com",
"symmetric NAT", "stun.1und1.de",
"error" "stun.3clogic.com",
}; "stun.3cx.com",
"stun.a-mm.tv",
char* encode16(char* buf, uint16_t data) "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); uint16_t ndata = htons(data);
memcpy(buf, (void*)(&ndata), sizeof(uint16_t)); memcpy(buf, (void *)(&ndata), sizeof(uint16_t));
return buf + 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); uint32_t ndata = htonl(data);
memcpy(buf, (void*)(&ndata), sizeof(uint32_t)); memcpy(buf, (void *)(&ndata), sizeof(uint32_t));
return buf + 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, type);
ptr = encode16(ptr, 4); ptr = encode16(ptr, 4);
ptr = encode32(ptr, value); ptr = encode32(ptr, value);
...@@ -47,15 +204,22 @@ char* encodeAtrUInt32(char* ptr, uint16_t type, uint32_t value) ...@@ -47,15 +204,22 @@ char* encodeAtrUInt32(char* ptr, uint16_t type, uint32_t value)
return ptr; 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); memcpy(buf, data, length);
return buf + length; return buf + length;
} }
static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress* result ) char *encodeWithLen(char *buf, const char *data) {
{ size_t s = sizeof(*data);
if (hdrLen != 8 /* ipv4 size */ && hdrLen != 20 /* ipv6 size */ ) { 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; return -1;
} }
body++; // Skip pad body++; // Skip pad
...@@ -63,12 +227,13 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress* ...@@ -63,12 +227,13 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress*
uint16_t nport; uint16_t nport;
memcpy(&nport, body, 2); memcpy(&nport, body, 2);
body+=2; body += 2;
result->port = ntohs(nport); result->port = ntohs(nport);
if (result->family == IPv4Family) { if (result->family == IPv4Family) {
uint32_t naddr; 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); result->addr.ipv4 = ntohl(naddr);
// Note: addr.ipv4 is stored in host byte order // Note: addr.ipv4 is stored in host byte order
return 0; return 0;
...@@ -79,9 +244,9 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress* ...@@ -79,9 +244,9 @@ static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress*
return -1; return -1;
} }
static void gen_random_string(char *s, const int len) { void gen_random_string(char *s, const int len) {
static const char alphanum[] = srand(time(NULL));
"0123456789" const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"; "abcdefghijklmnopqrstuvwxyz";
...@@ -89,21 +254,24 @@ static void gen_random_string(char *s, const int len) { ...@@ -89,21 +254,24 @@ static void gen_random_string(char *s, const int len) {
for (; i < len; ++i) { for (; i < len; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; 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) { static int send_bind_request(int sock, const char *remote_host,
char* buf = malloc(MAX_STUN_MESSAGE_LENGTH); uint16_t remote_port, uint32_t change_ip,
char* ptr = buf; uint32_t change_port, StunAtrAddress *addr_array) {
char *buf = malloc(MAX_STUN_MESSAGE_LENGTH);
char *ptr = buf;
StunHeader h; StunHeader h;
h.msgType = BindRequest; h.msgType = BindRequest;
gen_random_string((char*)&h.magicCookieAndTid, 16); gen_random_string((char *)&h.magicCookieAndTid, 16);
ptr = encode16(ptr, h.msgType); ptr = encode16(ptr, h.msgType);
char* lengthp = ptr; char *lengthp = ptr;
ptr = encode16(ptr, 0); 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) { if (change_ip || change_port) {
ptr = encodeAtrUInt32(ptr, ChangeRequest, 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_ ...@@ -122,12 +290,14 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
struct sockaddr_in remote_addr; struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET; 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); remote_addr.sin_port = htons(remote_port);
int retries; int retries;
for (retries = 0; retries < MAX_RETRIES_NUM; 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 // sendto() barely failed
free(buf); free(buf);
...@@ -139,15 +309,16 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -139,15 +309,16 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
struct timeval tv; struct timeval tv;
tv.tv_sec = 3; tv.tv_sec = 3;
tv.tv_usec = 0; 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) { if (errno != EAGAIN || errno != EWOULDBLOCK) {
free(buf); free(buf);
return -1; return -1;
} }
//timout, retry // timout, retry
} else { } else {
// got response // got response
break; break;
...@@ -163,23 +334,24 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -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); uint16_t msg_type = ntohs(reply_header.msgType);
if (msg_type == BindResponse) { if (msg_type == BindResponse) {
char* body = buf + sizeof(StunHeader); char *body = buf + sizeof(StunHeader);
uint16_t size = ntohs(reply_header.msgLength); uint16_t size = ntohs(reply_header.msgLength);
StunAtrHdr* attr; StunAtrHdr *attr;
unsigned int attrLen; unsigned int attrLen;
unsigned int attrLenPad; unsigned int attrLenPad;
int atrType; int atrType;
while (size > 0) { while (size > 0) {
attr = (StunAtrHdr*)(body); attr = (StunAtrHdr *)(body);
attrLen = ntohs(attr->length); 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); attrLenPad = attrLen % 4 == 0 ? 0 : 4 - (attrLen % 4);
atrType = ntohs(attr->type); atrType = ntohs(attr->type);
if ( attrLen + attrLenPad + 4 > size ) { if (attrLen + attrLenPad + 4 > size) {
free(buf); free(buf);
return -1; return -1;
...@@ -197,7 +369,7 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -197,7 +369,7 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
} }
break; break;
case ChangedAddress: case ChangedAddress:
if (stun_parse_atr_addr( body, attrLen, addr_array + 1)) { if (stun_parse_atr_addr(body, attrLen, addr_array + 1)) {
free(buf); free(buf);
return -1; return -1;
...@@ -217,11 +389,17 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -217,11 +389,17 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
return 0; return 0;
} }
const char* get_nat_desc(nat_type type) { const char *get_nat_desc(nat_type type) { return nat_types[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; uint32_t mapped_ip = 0;
uint16_t mapped_port = 0; uint16_t mapped_port = 0;
int s = socket(AF_INET, SOCK_DGRAM, 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* ...@@ -232,7 +410,8 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
nat_type nat_type; nat_type nat_type;
int reuse_addr = 1; 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; struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET; 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* ...@@ -281,9 +460,10 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
goto cleanup_sock; goto cleanup_sock;
} else { } else {
if (changed_ip != 0 && changed_port != 0) { 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)}; 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); 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* ...@@ -293,20 +473,21 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
goto cleanup_sock; 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; nat_type = SymmetricNAT;
goto cleanup_sock; 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; nat_type = RestricPortNAT;
goto cleanup_sock; goto cleanup_sock;
} }
nat_type = RestricNAT; nat_type = RestricNAT;
goto cleanup_sock; goto cleanup_sock;
} } else {
else {
nat_type = FullCone; nat_type = FullCone;
goto cleanup_sock; goto cleanup_sock;
} }
......
...@@ -73,6 +73,7 @@ typedef struct ...@@ -73,6 +73,7 @@ typedef struct
} addr; } addr;
} StunAtrAddress; } StunAtrAddress;
char* encode8(char* buf, size_t data);
char* encode16(char* buf, uint16_t data); char* encode16(char* buf, uint16_t data);
char* encode32(char* buf, uint32_t data); char* encode32(char* buf, uint32_t data);
char* encode(char* buf, const char* data, unsigned int length); char* encode(char* buf, const char* data, unsigned int length);
...@@ -83,6 +84,7 @@ extern int verbose; ...@@ -83,6 +84,7 @@ extern int verbose;
printf(format, ##__VA_ARGS__); \ printf(format, ##__VA_ARGS__); \
} while(0) } 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); 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 package main
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "errors"
"io"
"math/rand"
"net" "net"
"os"
"sync" "sync"
"time"
log "github.com/sirupsen/logrus"
) )
type nat_info struct { type PeerInfo struct {
Ip [16]byte IP [16]byte
Port uint16
NatType uint16
Meta string
ID uint32
}
type natInfo struct {
IP [16]byte
Port uint16 Port uint16
Nat_type uint16 NatType uint16
} }
const ( const (
Enroll = 1 _ = iota
GetPeerInfo = 2 Enroll
NotifyPeer = 3 GetPeerInfo
NotifyPeer
GetPeerInfoFromMeta
NotifyPeerFromMeta
PeerOffline = 1
PeerError = 2
ListeningPort = ":9988"
) )
var seq uint32 = 1 var (
var peers map[uint32]nat_info seq uint32 = 1
var peers_conn map[uint32]net.Conn peers map[uint32]PeerInfo
var m sync.Mutex 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() { func init() {
peers = make(map[uint32]nat_info) peers = make(map[uint32]PeerInfo)
peers_conn = make(map[uint32]net.Conn) 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() defer l.Close()
go dumpPeers()
for { for {
conn, err := l.Accept() conn, err := l.Accept()
if err != nil { if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("Accepting connection failed")
continue continue
} }
log.Info("New connection received")
go handleConn(conn) 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 // 2 bytes for message type
func handleConn(c net.Conn) { func handleConn(c net.Conn) {
defer c.Close() defer c.Close()
var peerID uint32 = 0 log.Info("new connection received!")
var myInfo PeerInfo
for { for {
// read message type first var myBuf bytes.Buffer
w := io.MultiWriter(&myBuf, c)
data := make([]byte, 2) data := make([]byte, 2)
_, err := c.Read(data) _, err := c.Read(data)
log.WithFields(log.Fields{
"header": data,
}).Info("new received header")
if err != nil { if err != nil {
m.Lock() mutex.Lock()
fmt.Printf("error: %v, peer %d disconnected\n", err, peerID) delete(peers, myInfo.ID)
delete(peers, peerID) delete(peerConn, myInfo.ID)
delete(peers_conn, peerID) delete(peersFromMeta, myInfo.Meta)
m.Unlock() delete(peerConnFromMeta, myInfo.Meta)
mutex.Unlock()
log.WithFields(log.Fields{
"err": err,
"myID": myInfo.ID,
}).Info("peer left")
return return
} }
t := binary.BigEndian.Uint16(data[:])
switch binary.BigEndian.Uint16(data[:]) { switch t {
case Enroll: case Enroll:
var peer nat_info var err error
err = binary.Read(c, binary.BigEndian, &peer) myInfo, err = readPeerInfo(c)
if err != nil { if err != nil {
continue log.WithFields(log.Fields{
"err": err,
}).Warn("Reading meta failed")
break
} }
mutex.Lock()
fmt.Println("peer enrolled, addr: ", string(peer.Ip[:]), peer.Port, peer.Nat_type)
m.Lock()
seq++ seq++
peerID = seq myInfo.ID = seq
peers[peerID] = peer peers[myInfo.ID] = myInfo
peers_conn[peerID] = c peerConn[myInfo.ID] = c
fmt.Println("new peer, id : ", peerID) if myInfo.Meta != "" {
m.Unlock() peersFromMeta[myInfo.Meta] = myInfo
err = binary.Write(c, binary.BigEndian, peerID) 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 { 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: case GetPeerInfo:
var peer_id uint32 var peerID uint32
binary.Read(c, binary.BigEndian, &peer_id) err = binary.Read(c, binary.BigEndian, &peerID)
if val, ok := peers[peer_id]; ok { if err != nil {
binary.Write(c, binary.BigEndian, val) log.WithFields(log.Fields{
} else { "err": err,
var offline uint8 = 0 "peerID": peerID,
binary.Write(c, binary.BigEndian, offline) "myID": myInfo.ID,
fmt.Printf("%d offline\n", peer_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: case NotifyPeer:
var peer_id uint32 var peerID uint32
binary.Read(c, binary.BigEndian, &peer_id) err = binary.Read(c, binary.BigEndian, &peerID)
fmt.Println("notify to peer", peer_id) if err != nil {
if val, ok := peers_conn[peer_id]; ok { log.WithFields(log.Fields{
if err = binary.Write(val, binary.BigEndian, peers[peerID]); err != nil { "err": err,
// unable to notify peer "peerID": peerID,
fmt.Println("offline") "myID": myInfo.ID,
}).Warn("Unable to get peer id")
break
} }
} else { conn, err := getConn(PeerInfo{ID: peerID})
fmt.Println("offline") 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: 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 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