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
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
nat_traversal.o: nat_traversal.c
nat_traversal.o: nat_traversal.c
$(CC) $(CFLAGS) -c nat_traversal.c
nat_type.o: nat_type.c
$(CC) $(CFLAGS) -c nat_type.c
clean:
$(RM) nat_traversal *.o *~
clean:
$(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];
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;
uint32_t peer_id = 0;
int ttl = 10;
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)
{
case 'h':
printf("%s", usage);
break;
case 'H':
stun_server = optarg;
break;
case 't':
ttl = atoi(optarg);
break;
case 'P':
stun_port = atoi(optarg);
break;
case 'p':
local_port = atoi(optarg);
break;
case 's':
punch_server = optarg;
break;
case 'd':
peer_id = atoi(optarg);
break;
case 'i':
strncpy(local_ip, optarg, 16);
break;
case 'v':
verbose = 1;
break;
case '?':
default:
printf("invalid option: %c\n", opt);
printf("%s", usage);
return -1;
}
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 *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";
int 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;
case 't':
ttl = atoi(optarg);
break;
case 'P':
stun_port = atoi(optarg);
break;
case 'p':
local_port = atoi(optarg);
break;
case 's':
punch_server = optarg;
break;
case 'm':
meta = optarg;
break;
case 'o':
peer_meta = optarg;
break;
case 'd':
peer_id = atoi(optarg);
break;
case 'i':
strncpy(local_ip, optarg, 16);
break;
case 'v':
verbose = 1;
break;
case '?':
default:
printf("invalid option: %c\n", opt);
printf("%s", usage);
return -1;
}
}
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;
}
char ext_ip[16] = {0};
uint16_t ext_port = 0;
if (!peer_id) {
printf("failed to get peer_id\n");
return -1;
}
// 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 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;
}
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;
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 (!punch_server) {
printf("please specify punch server\n");
return -1;
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;
}
struct peer_info self;
strncpy(self.ip, ext_ip, 16);
self.port = ext_port;
self.type = type;
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);
client c;
c.type = type;
c.ttl = ttl;
if (enroll(self, server_addr, &c) < 0) {
printf("failed to enroll\n");
return -1;
return 0;
}
char ext_ip[16] = {0};
uint16_t ext_port = 0;
// TODO we should try another STUN server if failed
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;
}
printf("enroll successfully, ID: %d\n", c.id);
}
if (!ext_port) {
return -1;
}
verbose_log("nat detect got ip: %s, port %d\n", ext_ip, ext_port);
struct peer_info self;
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)); */
/* 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;
}
verbose_log("enroll successfully, ID: %d\n", c.id);
if (peer_id) {
verbose_log("connecting to peer %d\n", peer_id);
if (connect_to_peer(&c, peer_id) < 0) {
verbose_log("failed to connect to peer %d\n", peer_id);
return -1;
}
}
if (peer_id) {
printf("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);
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;
}
}
pthread_t tid = wait_for_command(&c.sfd);
pthread_t tid = wait_for_command(&c.sfd);
pthread_join(tid, NULL);
return 0;
pthread_join(tid, NULL);
return 0;
}
#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,373 +23,506 @@
// file scope variables
static int ports[MAX_PORT - MIN_PORT];
static int send_to_punch_server(client* c) {
int n = send(c->sfd, c->buf, c->msg_buf - c->buf, 0);
c->msg_buf= c->buf;
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;
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) {
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;
}
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);
return send_get_peer_info_request(cli, peer);
}
int n_bytes = recv(cli->sfd, (void*)peer, sizeof(struct peer_info), 0);
if (n_bytes <= 0) {
return -1;
} else if (n_bytes == 1) {
// offline
return 1;
} else {
peer->port = ntohs(peer->port);
peer->type = ntohs(peer->type);
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);
}
return 0;
}
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;
}
if (n_bytes == 1) {
// offline or error
verbose_log("It seems peer has gone offline\n");
return 1;
}
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';
char dummy = 'c';
struct timeval tv = {5, 0};
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
return sendto(fd, &dummy, 1, 0, (struct sockaddr *)&addr, sizeof(addr));
}
struct timeval tv = {5, 0};
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))) {
// if (errno == EADDRINUSE) {
// printf("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
*/
setsockopt(hole, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
// 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;
}
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))) {
// if (errno == EADDRINUSE) {
// 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
*/
setsockopt(hole, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
// 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;
}
}
return hole;
return hole;
}
static int wait_for_peer(int* socks, int sock_num, struct timeval *timeout) {
fd_set fds;
int max_fd = 0;
FD_ZERO(&fds);
static int wait_for_peer(int *socks, int sock_num, struct timeval *timeout) {
fd_set fds;
int max_fd = 0;
FD_ZERO(&fds);
int i;
for (i = 0; i < sock_num; ++i) {
FD_SET(socks[i], &fds);
if (socks[i] > max_fd) {
max_fd = socks[i];
}
}
int ret = select(max_fd + 1, &fds, NULL, NULL, timeout);
int index = -1;
if (ret > 0) {
for (i = 0; i < sock_num; ++i) {
if (FD_ISSET(socks[i], &fds)) {
index = i;
break;
}
}
} else {
// timeout or error
int i;
for (i = 0; i < sock_num; ++i) {
FD_SET(socks[i], &fds);
if (socks[i] > max_fd) {
max_fd = socks[i];
}
}
int ret = select(max_fd + 1, &fds, NULL, NULL, timeout);
// one of the fds is ready, close others
if (index != -1) {
for (i = 0; i < sock_num; ++i) {
if (index != i) {
close(socks[i]);
}
}
int index = -1;
if (ret > 0) {
for (i = 0; i < sock_num; ++i) {
if (FD_ISSET(socks[i], &fds)) {
index = i;
break;
}
}
} else {
// timeout or error
}
return socks[index];
// one of the fds is ready, close others
if (index != -1) {
for (i = 0; i < sock_num; ++i) {
if (index != i) {
close(socks[i]);
}
}
return -1;
return socks[index];
}
return -1;
}
static void shuffle(int *num, int len) {
srand(time(NULL));
srand(time(NULL));
// Fisher-Yates shuffle algorithm
int i, r, temp;
for (i = len - 1; i > 0; i--) {
r = rand() % i;
// Fisher-Yates shuffle algorithm
int i, r, temp;
for (i = len - 1; i > 0; i--) {
r = rand() % i;
temp = num[i];
num[i] = num[r];
num[r] = temp;
}
temp = num[i];
num[i] = num[r];
num[r] = temp;
}
}
static int connect_to_symmetric_nat(client* c, uint32_t peer_id, 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
* P(400)=0.710488
* P(500)=0.856122
* P(600)=0.938839
* but symmetric NAT has port sensitive filter for incoming packet
* which makes the probalility decline dramatically.
* Moreover, symmetric NATs don't really allocate ports randomly.
*/
struct sockaddr_in peer_addr;
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = inet_addr(remote_peer.ip);
int *holes = malloc(NUM_OF_PORTS * sizeof(int));
shuffle(ports, MAX_PORT - MIN_PORT + 1);
int i = 0;
for (; i < NUM_OF_PORTS;) {
uint16_t port = ports[i];
if (port != remote_peer.port) { // exclude the used one
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
verbose_log("failed to punch hole, error: %s\n", strerror(errno));
break;
}
// sleep for a while to avoid flooding protection
usleep(1000 * 100);
++i;
} else {
ports[i] = ports[1000];
continue;
}
}
// 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);
send_to_punch_server(c);
struct timeval timeout={100, 0};
int fd = wait_for_peer(holes, i, &timeout);
if (fd > 0) {
on_connected(fd);
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
* P(400)=0.710488
* P(500)=0.856122
* P(600)=0.938839
* but symmetric NAT has port sensitive filter for incoming packet
* which makes the probalility decline dramatically.
* Moreover, symmetric NATs don't really allocate ports randomly.
*/
struct sockaddr_in peer_addr;
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = inet_addr(remote_peer.ip);
int *holes = malloc(NUM_OF_PORTS * sizeof(int));
shuffle(ports, MAX_PORT - MIN_PORT + 1);
int i = 0;
for (; i < NUM_OF_PORTS;) {
uint16_t port = ports[i];
if (port != remote_peer.port) { // exclude the used one
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
verbose_log("failed to punch hole, error: %s\n", strerror(errno));
break;
}
// sleep for a while to avoid flooding protection
usleep(1000 * 100);
++i;
} else {
int j = 0;
for (; j < i; ++j) {
close(holes[j]);
}
printf("timout, not connected\n");
ports[i] = ports[1000];
continue;
}
}
// hole punched, notify remote peer via punch server
c->msg_buf = encode16(c->msg_buf, NotifyPeer);
c->msg_buf = encode32(c->msg_buf, remote_peer.id);
send_to_punch_server(c);
struct timeval timeout = {100, 0};
int fd = wait_for_peer(holes, i, &timeout);
if (fd > 0) {
on_connected(fd);
} else {
int j = 0;
for (; j < i; ++j) {
close(holes[j]);
}
verbose_log("timout, not connected\n");
}
return 0;
return 0;
}
// run in another thread
static void* server_notify_handler(void* data) {
int server_sock = *(int*)data;
struct peer_info peer;
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;
// wait for notification
printf("waiting for notification...\n");
for (; ;) {
if (recv(server_sock, &peer, sizeof peer, 0) > 0) {
break;
}
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = inet_addr(peer.ip);
int sock_array[NUM_OF_PORTS];
int i = 0;
shuffle(ports, MAX_PORT - MIN_PORT + 1);
// send probe packets, check if connected with peer, if yes, stop probing
for (; i < NUM_OF_PORTS;) {
if (ports[i] == peer.port) {
ports[i] = ports[1000]; // TODO
continue;
}
if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
verbose_log("failed to create socket, send %d probe packets\n", i);
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;
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = inet_addr(peer.ip);
int sock_array[NUM_OF_PORTS];
int i = 0;
shuffle(ports, MAX_PORT - MIN_PORT + 1);
// send probe packets, check if connected with peer, if yes, stop probing
for (; i < NUM_OF_PORTS;) {
if (ports[i] == peer.port) {
ports[i] = ports[1000]; // TODO
continue;
}
if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("failed to create socket, send %d probe packets\n", i);
break;
}
peer_addr.sin_port = htons(ports[i]);
// let OS choose available ports
if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) {
printf("may trigger flooding protection\n");
break;
}
// wait for a while
struct timeval tv = {0, 1000 * 100};
int fd = wait_for_peer(sock_array, ++i, &tv);
if (fd > 0) {
// connected
on_connected(fd);
// TODO
return NULL;
}
peer_addr.sin_port = htons(ports[i]);
// let OS choose available ports
if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) {
verbose_log("may trigger flooding protection\n");
break;
}
printf("holes punched, waiting for peer\n");
struct timeval tv = {100, 0};
int fd = wait_for_peer(sock_array, i, &tv);
// wait for a while
struct timeval tv = {0, 1000 * 100};
int fd = wait_for_peer(sock_array, ++i, &tv);
if (fd > 0) {
on_connected(fd);
} else {
int j = 0;
for (j = 0; j < i; ++j) {
close(sock_array[j]);
}
// connected
on_connected(fd);
// TODO
return;
}
}
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) {
on_connected(fd);
} else {
int j = 0;
for (j = 0; j < i; ++j) {
close(sock_array[j]);
}
}
}
// 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;
}
}
// TODO wait for next notification
verbose_log("recved command, ready to connect to %s:%d\n", peer.ip,
peer.port);
return NULL;
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);
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if (connect(server_sock, (struct sockaddr *)&punch_server, sizeof(punch_server)) < 0) {
printf("failed to connect to punch server\n");
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
verbose_log(" %s\n", buff);
return -1;
// Output the offset.
verbose_log(" %04x ", i);
}
c->sfd = server_sock;
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);
// Now the hex code for the specific character.
verbose_log(" %02x", pc[i]);
if (-1 == send_to_punch_server(c)) {
return -1;
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) {
buff[i % 16] = '.';
} else {
buff[i % 16] = pc[i];
}
// wait for server reply to get own ID
uint32_t peer_id = 0;
struct timeval tv;
tv.tv_sec = 5;
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);
if (n != sizeof(uint32_t)) {
return -1;
}
buff[(i % 16) + 1] = '\0';
}
c->id = ntohl(peer_id);
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
verbose_log(" ");
i++;
}
return 0;
// 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;
}
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);
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 = 10;
tv.tv_usec = 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 thread_id;
return 0;
}
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);
printf("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));
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);
return thread_id;
}
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");
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);
verbose_log("recv %s\n", buf);
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));
}
return -1;
}
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; */
}
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");
return -1;
// log
}
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);
verbose_log("failed to get info of remote peer\n");
return 0;
return -1;
}
return real_connect_to_peer(cli, &peer);
}
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;
}
return real_connect_to_peer(cli, &peer);
}
......@@ -4,35 +4,54 @@
typedef struct client client;
struct client {
int sfd;
uint32_t id;
char buf[128];
//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,
// so that the hole punching packets just die in the way
int ttl;
int sfd;
uint32_t id;
char buf[128];
// 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,
// 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 {
char ip[16];
uint16_t port;
uint16_t type;
uint32_t id;
char ip[16];
uint16_t port;
uint16_t type;
char *meta;
};
enum msg_type {
Enroll = 0x01,
GetPeerInfo = 0x02,
NotifyPeer = 0x03,
};
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)
{
uint16_t ndata = htons(data);
memcpy(buf, (void*)(&ndata), sizeof(uint16_t));
return buf + sizeof(uint16_t);
// 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* encode32(char* buf, uint32_t data)
{
uint32_t ndata = htonl(data);
memcpy(buf, (void*)(&ndata), sizeof(uint32_t));
char *encode16(char *buf, uint16_t data) {
uint16_t ndata = htons(data);
memcpy(buf, (void *)(&ndata), sizeof(uint16_t));
return buf + sizeof(uint16_t);
}
return buf + sizeof(uint32_t);
char *encode32(char *buf, uint32_t data) {
uint32_t ndata = htonl(data);
memcpy(buf, (void *)(&ndata), sizeof(uint32_t));
return buf + sizeof(uint32_t);
}
char* encodeAtrUInt32(char* ptr, uint16_t type, uint32_t value)
{
ptr = encode16(ptr, type);
ptr = encode16(ptr, 4);
ptr = encode32(ptr, value);
char *encodeAtrUInt32(char *ptr, uint16_t type, uint32_t value) {
ptr = encode16(ptr, type);
ptr = encode16(ptr, 4);
ptr = encode32(ptr, value);
return ptr;
return ptr;
}
char* encode(char* buf, const char* data, unsigned int length)
{
memcpy(buf, data, length);
return buf + 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 */ ) {
return -1;
}
body++; // Skip pad
result->family = *body++;
uint16_t nport;
memcpy(&nport, 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);
result->addr.ipv4 = ntohl(naddr);
// Note: addr.ipv4 is stored in host byte order
return 0;
} else if (result->family == IPv6Family) {
printf("ipv6 is not implemented yet");
}
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
result->family = *body++;
uint16_t nport;
memcpy(&nport, 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);
result->addr.ipv4 = ntohl(naddr);
// Note: addr.ipv4 is stored in host byte order
return 0;
} else if (result->family == IPv6Family) {
printf("ipv6 is not implemented yet");
}
static void gen_random_string(char *s, const int len) {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
return -1;
}
int i = 0;
for (; i < len; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
}
void gen_random_string(char *s, const int len) {
srand(time(NULL));
const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
int i = 0;
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;
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;
ptr = encode16(ptr, 0);
ptr = encode(ptr, (const char*)&h.id, sizeof(h.id));
ptr = encode16(ptr, h.msgType);
char *lengthp = ptr;
ptr = encode16(ptr, 0);
ptr = encode(ptr, (const char *)&h.id, sizeof(h.id));
if (change_ip || change_port) {
ptr = encodeAtrUInt32(ptr, ChangeRequest, change_ip | change_port);
if (change_ip || change_port) {
ptr = encodeAtrUInt32(ptr, ChangeRequest, change_ip | change_port);
// length of stun body
encode16(lengthp, ptr - buf - sizeof(StunHeader));
// length of stun body
encode16(lengthp, ptr - buf - sizeof(StunHeader));
}
struct hostent *server = gethostbyname(remote_host);
if (server == NULL) {
fprintf(stderr, "no such host, %s\n", remote_host);
free(buf);
return -1;
}
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);
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))) {
// sendto() barely failed
free(buf);
return -1;
}
struct hostent *server = gethostbyname(remote_host);
if (server == NULL) {
fprintf(stderr, "no such host, %s\n", remote_host);
socklen_t fromlen = sizeof remote_addr;
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
if (recvfrom(sock, buf, 512, 0, (struct sockaddr *)&remote_addr,
&fromlen) <= 0) {
if (errno != EAGAIN || errno != EWOULDBLOCK) {
free(buf);
return -1;
}
// timout, retry
} else {
// got response
break;
}
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);
remote_addr.sin_port = htons(remote_port);
if (retries == MAX_RETRIES_NUM)
return -1;
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))) {
// sendto() barely failed
free(buf);
StunHeader reply_header;
memcpy(&reply_header, buf, sizeof(StunHeader));
return -1;
}
uint16_t msg_type = ntohs(reply_header.msgType);
socklen_t fromlen = sizeof remote_addr;
if (msg_type == BindResponse) {
char *body = buf + sizeof(StunHeader);
uint16_t size = ntohs(reply_header.msgLength);
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
StunAtrHdr *attr;
unsigned int attrLen;
unsigned int attrLenPad;
int atrType;
if (recvfrom(sock, buf, 512, 0, (struct sockaddr *)&remote_addr, &fromlen) <= 0) {
if (errno != EAGAIN || errno != EWOULDBLOCK) {
free(buf);
while (size > 0) {
attr = (StunAtrHdr *)(body);
return -1;
}
//timout, retry
} else {
// got response
break;
}
}
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
attrLenPad = attrLen % 4 == 0 ? 0 : 4 - (attrLen % 4);
atrType = ntohs(attr->type);
if (attrLen + attrLenPad + 4 > size) {
free(buf);
if (retries == MAX_RETRIES_NUM)
return -1;
}
StunHeader reply_header;
memcpy(&reply_header, buf, sizeof(StunHeader));
uint16_t msg_type = ntohs(reply_header.msgType);
if (msg_type == BindResponse) {
char* body = buf + sizeof(StunHeader);
uint16_t size = ntohs(reply_header.msgLength);
StunAtrHdr* attr;
unsigned int attrLen;
unsigned int attrLenPad;
int atrType;
while (size > 0) {
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
attrLenPad = attrLen % 4 == 0 ? 0 : 4 - (attrLen % 4);
atrType = ntohs(attr->type);
if ( attrLen + attrLenPad + 4 > size ) {
free(buf);
return -1;
}
body += 4; // skip the length and type in attribute header
size -= 4;
switch (atrType) {
case MappedAddress:
if (stun_parse_atr_addr(body, attrLen, addr_array)) {
free(buf);
return -1;
}
break;
case ChangedAddress:
if (stun_parse_atr_addr( body, attrLen, addr_array + 1)) {
free(buf);
return -1;
}
break;
default:
// ignore other attributes
break;
}
body += attrLen + attrLenPad;
size -= attrLen + attrLenPad;
}
}
free(buf);
body += 4; // skip the length and type in attribute header
size -= 4;
return 0;
}
switch (atrType) {
case MappedAddress:
if (stun_parse_atr_addr(body, attrLen, addr_array)) {
free(buf);
const char* get_nat_desc(nat_type type) {
return nat_types[type];
}
return -1;
}
break;
case ChangedAddress:
if (stun_parse_atr_addr(body, attrLen, addr_array + 1)) {
free(buf);
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) {
uint32_t mapped_ip = 0;
uint16_t mapped_port = 0;
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s <= 0) {
return Error;
return -1;
}
break;
default:
// ignore other attributes
break;
}
body += attrLen + attrLenPad;
size -= attrLen + attrLenPad;
}
}
nat_type nat_type;
free(buf);
int reuse_addr = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr));
return 0;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(local_ip);
const char *get_nat_desc(nat_type type) { return nat_types[type]; }
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);
if (s <= 0) {
return Error;
}
nat_type nat_type;
int reuse_addr = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr,
sizeof(reuse_addr));
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(local_ip);
local_addr.sin_port = htons(local_port);
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
if (errno == EADDRINUSE) {
printf("addr in use, try another port\n");
nat_type = Error;
goto cleanup_sock;
}
}
// 0 for mapped addr, 1 for changed addr
StunAtrAddress bind_result[2];
memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
if (send_bind_request(s, stun_host, stun_port, 0, 0, bind_result)) {
nat_type = Blocked;
goto cleanup_sock;
}
mapped_ip = bind_result[0].addr.ipv4; // in host byte order
mapped_port = bind_result[0].port;
uint32_t changed_ip = bind_result[1].addr.ipv4;
uint16_t changed_port = bind_result[1].port;
struct in_addr mapped_addr;
mapped_addr.s_addr = htonl(mapped_ip);
/*
* it's complicated to get the RECEIVER address of UDP packet,
* For Linux/Windows, set IP_PKTINFO option to enable ancillary
* message that contains a pktinfo structure that supplies
* some information about the incoming packets
*/
/* TODO use getifaddrs() to get interface address,
* then compare it with mapped address to determine
* if it's open Internet
*/
if (!strcmp(local_ip, inet_ntoa(mapped_addr))) {
nat_type = OpenInternet;
goto cleanup_sock;
} else {
if (changed_ip != 0 && changed_port != 0) {
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);
memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
if (send_bind_request(s, alt_host, changed_port, 0, 0, bind_result)) {
printf("failed to send request to alterative server\n");
nat_type = Error;
goto cleanup_sock;
}
local_addr.sin_port = htons(local_port);
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
if (errno == EADDRINUSE) {
printf("addr in use, try another port\n");
nat_type = Error;
goto cleanup_sock;
if (mapped_ip != bind_result[0].addr.ipv4 ||
mapped_port != bind_result[0].port) {
nat_type = SymmetricNAT;
goto cleanup_sock;
}
}
// 0 for mapped addr, 1 for changed addr
StunAtrAddress bind_result[2];
if (send_bind_request(s, alt_host, changed_port, 0, ChangePortFlag,
bind_result)) {
nat_type = RestricPortNAT;
goto cleanup_sock;
}
memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
if (send_bind_request(s, stun_host, stun_port, 0, 0, bind_result)) {
nat_type = Blocked;
nat_type = RestricNAT;
goto cleanup_sock;
}
mapped_ip = bind_result[0].addr.ipv4; // in host byte order
mapped_port = bind_result[0].port;
uint32_t changed_ip = bind_result[1].addr.ipv4;
uint16_t changed_port = bind_result[1].port;
struct in_addr mapped_addr;
mapped_addr.s_addr = htonl(mapped_ip);
/*
* it's complicated to get the RECEIVER address of UDP packet,
* For Linux/Windows, set IP_PKTINFO option to enable ancillary
* message that contains a pktinfo structure that supplies
* some information about the incoming packets
*/
/* TODO use getifaddrs() to get interface address,
* then compare it with mapped address to determine
* if it's open Internet
*/
if (!strcmp(local_ip, inet_ntoa(mapped_addr))) {
nat_type = OpenInternet;
} else {
nat_type = FullCone;
goto cleanup_sock;
} else {
if (changed_ip != 0 && changed_port != 0) {
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);
memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
if (send_bind_request(s, alt_host, changed_port, 0, 0, bind_result)) {
printf("failed to send request to alterative server\n");
nat_type = Error;
goto cleanup_sock;
}
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)) {
nat_type = RestricPortNAT;
goto cleanup_sock;
}
nat_type = RestricNAT;
goto cleanup_sock;
}
else {
nat_type = FullCone;
goto cleanup_sock;
}
} else {
printf("no alterative server, can't detect nat type\n");
nat_type = Error;
goto cleanup_sock;
}
}
} else {
printf("no alterative server, can't detect nat type\n");
nat_type = Error;
goto cleanup_sock;
}
}
cleanup_sock:
close(s);
struct in_addr ext_addr;
ext_addr.s_addr = htonl(mapped_ip);
strcpy(ext_ip, inet_ntoa(ext_addr));
*ext_port = mapped_port;
close(s);
struct in_addr ext_addr;
ext_addr.s_addr = htonl(mapped_ip);
strcpy(ext_ip, inet_ntoa(ext_addr));
*ext_port = mapped_port;
return nat_type;
return nat_type;
}
......@@ -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
Port uint16
Nat_type uint16
type PeerInfo struct {
IP [16]byte
Port uint16
NatType uint16
Meta string
ID uint32
}
type natInfo struct {
IP [16]byte
Port 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")
}
} else {
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
}
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