Commit b435fd9e authored by Fong's avatar Fong
Browse files

fix stack overflow and indent code

parent 7ccd0b6c
...@@ -25,7 +25,7 @@ int verbose = 0; ...@@ -25,7 +25,7 @@ int verbose = 0;
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
char* stun_server = stun_servers[0]; char* stun_server = stun_servers[0];
char* local_host = "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;
...@@ -34,7 +34,7 @@ int main(int argc, char** argv) ...@@ -34,7 +34,7 @@ int main(int argc, char** argv)
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")) != -1) while ((opt = getopt (argc, argv, "H:h:t:P:p:s:d:i:v")) != -1)
{ {
switch (opt) switch (opt)
{ {
...@@ -50,20 +50,21 @@ int main(int argc, char** argv) ...@@ -50,20 +50,21 @@ int main(int argc, char** argv)
case 'P': case 'P':
stun_port = atoi(optarg); stun_port = atoi(optarg);
break; break;
case 's':
punch_server = optarg;
break;
case 'p': case 'p':
local_port = atoi(optarg); local_port = atoi(optarg);
break; break;
case 's':
punch_server = optarg;
break;
case 'd': case 'd':
peer_id = atoi(optarg); peer_id = atoi(optarg);
break; break;
case 'i': case 'i':
local_host = optarg; strncpy(local_ip, optarg, 16);
break; break;
case 'v': case 'v':
verbose = 1; verbose = 1;
break;
case '?': case '?':
default: default:
printf("invalid option: %c\n", opt); printf("invalid option: %c\n", opt);
...@@ -77,7 +78,7 @@ int main(int argc, char** argv) ...@@ -77,7 +78,7 @@ int main(int argc, char** argv)
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_host, local_port, ext_ip, &ext_port); nat_type type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip, &ext_port);
printf("NAT type: %s\n", get_nat_desc(type)); printf("NAT type: %s\n", get_nat_desc(type));
if (ext_port) { if (ext_port) {
......
...@@ -148,20 +148,20 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -148,20 +148,20 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
// 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 [1024, 65535]
* will collide with another one chosen by the same way is * will collide with another one chosen by the same way is
* p(n) = 1-(64511!/(64511^n*64511!)) * p(n) = 1-(64511!/(64511^n*64511!))
* where '!' is the factorial operator, n is the number of ports chosen. * where '!' is the factorial operator, n is the number of ports chosen.
* P(100)=0.073898 * P(100)=0.073898
* P(200)=0.265667 * P(200)=0.265667
* P(300)=0.501578 * 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
* but symmetric NAT has port sensitive filter for incoming packet * but symmetric NAT has port sensitive filter for incoming packet
* which makes the probalility decline dramatically. * which makes the probalility decline dramatically.
* Moreover, symmetric NATs don't really allocate ports randomly. * Moreover, symmetric NATs don't really allocate ports randomly.
*/ */
struct sockaddr_in peer_addr; struct sockaddr_in peer_addr;
peer_addr.sin_family = AF_INET; peer_addr.sin_family = AF_INET;
...@@ -181,7 +181,8 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -181,7 +181,8 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
verbose_log("%s, NAT flooding protection triggered, try %d times\n", strerror(errno), i); verbose_log("%s, NAT flooding protection triggered, try %d times\n", strerror(errno), i);
break; break;
} }
// sleep for a while to avoid flooding protection
usleep(1000 * 100);
++i; ++i;
} else { } else {
nums[i] = nums[1000]; nums[i] = nums[1000];
...@@ -255,11 +256,8 @@ static void* server_notify_handler(void* data) { ...@@ -255,11 +256,8 @@ static void* server_notify_handler(void* data) {
break; break;
} }
/* add socket to FD_SET and check if connected // wait for a while
* set both fields of the timeval structure to zero struct timeval tv = {0, 1000 * 100};
* to make select() return immediately
*/
struct timeval tv = {0, 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) {
// connected // connected
...@@ -270,7 +268,8 @@ static void* server_notify_handler(void* data) { ...@@ -270,7 +268,8 @@ static void* server_notify_handler(void* data) {
} }
} }
struct timeval tv = {10, 0}; printf("holes punched, waiting for peer\n");
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) {
on_connected(fd); on_connected(fd);
......
...@@ -14,123 +14,121 @@ ...@@ -14,123 +14,121 @@
#define MAX_RETRIES_NUM 3 #define MAX_RETRIES_NUM 3
static const char* nat_types[] = { static const char* nat_types[] = {
"blocked", "blocked",
"open internet", "open internet",
"full cone", "full cone",
"restricted NAT", "restricted NAT",
"port-restricted cone", "port-restricted cone",
"symmetric NAT", "symmetric NAT",
"error" "error"
}; };
char* encode16(char* buf, uint16_t data) 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);
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 ) static int stun_parse_atr_addr( char* body, unsigned int hdrLen, StunAtrAddress* result )
{ {
if (hdrLen != 8 /* ipv4 size */ && hdrLen != 20 /* ipv6 size */ ) { if (hdrLen != 8 /* ipv4 size */ && hdrLen != 20 /* ipv6 size */ ) {
return -1; return -1;
} }
body++; // Skip pad body++; // Skip pad
result->family = *body++; result->family = *body++;
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;
} else if (result->family == IPv6Family) { } else if (result->family == IPv6Family) {
printf("ipv6 is not implemented yet"); printf("ipv6 is not implemented yet");
} }
return -1; return -1;
} }
static void gen_random_string(char *s, const int len) { static void gen_random_string(char *s, const int len) {
static const char alphanum[] = static const char alphanum[] =
"0123456789" "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"; "abcdefghijklmnopqrstuvwxyz";
int i = 0; int i = 0;
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, uint16_t remote_port, uint32_t change_ip, uint32_t change_port, StunAtrAddress* addr_array) {
char* buf = malloc(MAX_STUN_MESSAGE_LENGTH); char* buf = malloc(MAX_STUN_MESSAGE_LENGTH);
char* ptr = buf; 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);
// length of stun body // length of stun body
encode16(lengthp, ptr - buf - sizeof(StunHeader)); encode16(lengthp, ptr - buf - sizeof(StunHeader));
} }
struct hostent *server = gethostbyname(remote_host); struct hostent *server = gethostbyname(remote_host);
if (server == NULL) { if (server == NULL) {
fprintf(stderr, "no such host, %s\n", remote_host); fprintf(stderr, "no such host, %s\n", remote_host);
free(buf); free(buf);
return -1; return -1;
} }
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))) {
// udp sendto barely failed // sendto() barely failed
free(buf); free(buf);
return -1; return -1;
...@@ -159,12 +157,12 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -159,12 +157,12 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
if (retries == MAX_RETRIES_NUM) if (retries == MAX_RETRIES_NUM)
return -1; return -1;
StunHeader reply_header; StunHeader reply_header;
memcpy(&reply_header, buf, sizeof(StunHeader)); memcpy(&reply_header, buf, sizeof(StunHeader));
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);
...@@ -205,13 +203,6 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -205,13 +203,6 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
return -1; return -1;
} }
break; break;
case SourceAddress:
if (stun_parse_atr_addr( body, attrLen, addr_array + 2)) {
free(buf);
return -1;
}
break;
default: default:
// ignore // ignore
break; break;
...@@ -220,36 +211,36 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -220,36 +211,36 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
size -= attrLen + attrLenPad; size -= attrLen + attrLenPad;
} }
} }
free(buf); free(buf);
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_host, uint16_t local_port, char* ext_ip, uint16_t* ext_port) { 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; uint32_t mapped_ip = 0;
uint16_t mapped_port = 0; uint16_t mapped_port = 0;
int s; int s;
if((s = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) { if((s = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) {
return Error; return Error;
} }
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;
local_addr.sin_addr.s_addr = inet_addr(local_host); local_addr.sin_addr.s_addr = inet_addr(local_ip);
local_addr.sin_port = htons(local_port); local_addr.sin_port = htons(local_port);
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr))) { if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
if (errno == EADDRINUSE) { if (errno == EADDRINUSE) {
printf("addr in use, try another port\n"); printf("addr in use, try another port\n");
nat_type = Error; nat_type = Error;
...@@ -257,76 +248,75 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char* ...@@ -257,76 +248,75 @@ nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char*
} }
} }
// 0 for mapped addr, 1 for changed addr // 0 for mapped addr, 1 for changed addr
StunAtrAddress bind_result[2]; StunAtrAddress bind_result[2];
memset(bind_result, 0, sizeof(StunAtrAddress) * 2); memset(bind_result, 0, sizeof(StunAtrAddress) * 2);
if (send_bind_request(s, stun_host, stun_port, 0, 0, bind_result)) { if (send_bind_request(s, stun_host, stun_port, 0, 0, bind_result)) {
nat_type = Blocked; nat_type = Blocked;
goto cleanup_sock; goto cleanup_sock;
} }
mapped_ip = bind_result[0].addr.ipv4; // in host byte order mapped_ip = bind_result[0].addr.ipv4; // in host byte order
mapped_port = bind_result[0].port; mapped_port = bind_result[0].port;
uint32_t changed_ip = bind_result[1].addr.ipv4; uint32_t changed_ip = bind_result[1].addr.ipv4;
uint16_t changed_port = bind_result[1].port; uint16_t changed_port = bind_result[1].port;
struct in_addr mapped_addr; struct in_addr mapped_addr;
mapped_addr.s_addr = htonl(mapped_ip); mapped_addr.s_addr = htonl(mapped_ip);
/* /*
* it's complicated to get the RECEIVER address of UDP packet, * it's complicated to get the RECEIVER address of UDP packet,
* For Linux, use the setsockopt() called IP_PKTINFO * For Linux/Windows, set IP_PKTINFO option to enable ancillary
* which will get you a parameter over recvmsg() called * message that contains a pktinfo structure that supplies
* IP_PKTINFO, which carries a struct in_pktinfo, * some information about the incoming packets
* which has a 4 byte IP address hiding in its ipi_addr field.
*/ */
/* TODO use getifaddrs() to get interface address, /* TODO use getifaddrs() to get interface address,
* then compare it with mapped address to determine * then compare it with mapped address to determine
* if it's open internet * if it's open Internet
*/ */
if (!strcmp(local_host, inet_ntoa(mapped_addr))) { if (!strcmp(local_ip, inet_ntoa(mapped_addr))) {
nat_type = OpenInternet; nat_type = OpenInternet;
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);
if (send_bind_request(s, alt_host, changed_port, 0, 0, bind_result)) { if (send_bind_request(s, alt_host, changed_port, 0, 0, bind_result)) {
printf("failed to send request to alterative server\n"); printf("failed to send request to alterative server\n");
nat_type = Error; nat_type = Error;
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;
} }
} else { } else {
printf("no alterative server, can't detect nat type\n"); printf("no alterative server, can't detect nat type\n");
nat_type = Error; nat_type = Error;
goto cleanup_sock; goto cleanup_sock;
} }
} }
cleanup_sock: cleanup_sock:
close(s); close(s);
struct in_addr ext_addr; struct in_addr ext_addr;
......
...@@ -41,36 +41,36 @@ typedef struct { uint32_t longpart[3]; } UInt96; ...@@ -41,36 +41,36 @@ typedef struct { uint32_t longpart[3]; } UInt96;
typedef struct typedef struct
{ {
uint32_t magicCookie; // rfc 5389 uint32_t magicCookie; // rfc 5389
UInt96 tid; UInt96 tid;
} Id; } Id;
typedef struct typedef struct
{ {
uint16_t msgType; uint16_t msgType;
uint16_t msgLength; // length of stun body uint16_t msgLength; // length of stun body
union union
{ {
UInt128 magicCookieAndTid; UInt128 magicCookieAndTid;
Id id; Id id;
}; };
} StunHeader; } StunHeader;
typedef struct typedef struct
{ {
uint16_t type; uint16_t type;
uint16_t length; uint16_t length;
} StunAtrHdr; } StunAtrHdr;
typedef struct typedef struct
{ {
uint8_t family; uint8_t family;
uint16_t port; uint16_t port;
union union
{ {
uint32_t ipv4; // in host byte order uint32_t ipv4; // in host byte order
UInt128 ipv6; // in network byte order UInt128 ipv6; // in network byte order
} addr; } addr;
} StunAtrAddress; } StunAtrAddress;
char* encode16(char* buf, uint16_t data); char* encode16(char* buf, uint16_t data);
......
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