Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
nat_traversal
Commits
b435fd9e
Commit
b435fd9e
authored
May 24, 2017
by
Fong
Browse files
fix stack overflow and indent code
parent
7ccd0b6c
Changes
4
Hide whitespace changes
Inline
Side-by-side
main.c
View file @
b435fd9e
...
...
@@ -25,7 +25,7 @@ int verbose = 0;
int
main
(
int
argc
,
char
**
argv
)
{
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
local_port
=
DEFAULT_LOCAL_PORT
;
char
*
punch_server
=
NULL
;
...
...
@@ -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
"
;
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
)
{
...
...
@@ -50,20 +50,21 @@ int main(int argc, char** argv)
case
'P'
:
stun_port
=
atoi
(
optarg
);
break
;
case
's'
:
punch_server
=
optarg
;
break
;
case
'p'
:
local_port
=
atoi
(
optarg
);
break
;
case
's'
:
punch_server
=
optarg
;
break
;
case
'd'
:
peer_id
=
atoi
(
optarg
);
break
;
case
'i'
:
local_host
=
optarg
;
strncpy
(
local_ip
,
optarg
,
16
)
;
break
;
case
'v'
:
verbose
=
1
;
break
;
case
'?'
:
default:
printf
(
"invalid option: %c
\n
"
,
opt
);
...
...
@@ -77,7 +78,7 @@ int main(int argc, char** argv)
uint16_t
ext_port
=
0
;
// TODO we should try another STUN server if failed
nat_type
type
=
detect_nat_type
(
stun_server
,
stun_port
,
local_
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
));
if
(
ext_port
)
{
...
...
nat_traversal.c
View file @
b435fd9e
...
...
@@ -148,20 +148,20 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
// 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
* 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
;
...
...
@@ -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
);
break
;
}
// sleep for a while to avoid flooding protection
usleep
(
1000
*
100
);
++
i
;
}
else
{
nums
[
i
]
=
nums
[
1000
];
...
...
@@ -255,11 +256,8 @@ static void* server_notify_handler(void* data) {
break
;
}
/* add socket to FD_SET and check if connected
* set both fields of the timeval structure to zero
* to make select() return immediately
*/
struct
timeval
tv
=
{
0
,
0
};
// wait for a while
struct
timeval
tv
=
{
0
,
1000
*
100
};
int
fd
=
wait_for_peer
(
sock_array
,
++
i
,
&
tv
);
if
(
fd
>
0
)
{
// connected
...
...
@@ -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
);
if
(
fd
>
0
)
{
on_connected
(
fd
);
...
...
nat_type.c
View file @
b435fd9e
...
...
@@ -14,123 +14,121 @@
#define MAX_RETRIES_NUM 3
static
const
char
*
nat_types
[]
=
{
"blocked"
,
"open internet"
,
"full cone"
,
"restricted NAT"
,
"port-restricted cone"
,
"symmetric NAT"
,
"error"
"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
);
uint16_t
ndata
=
htons
(
data
);
memcpy
(
buf
,
(
void
*
)(
&
ndata
),
sizeof
(
uint16_t
));
return
buf
+
sizeof
(
uint16_t
);
}
char
*
encode32
(
char
*
buf
,
uint32_t
data
)
{
uint32_t
ndata
=
htonl
(
data
);
memcpy
(
buf
,
(
void
*
)(
&
ndata
),
sizeof
(
uint32_t
));
uint32_t
ndata
=
htonl
(
data
);
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
)
{
ptr
=
encode16
(
ptr
,
type
);
ptr
=
encode16
(
ptr
,
4
);
ptr
=
encode32
(
ptr
,
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
;
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
);
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"
);
}
return
-
1
;
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"
);
}
return
-
1
;
}
static
void
gen_random_string
(
char
*
s
,
const
int
len
)
{
static
const
char
alphanum
[]
=
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
;
int
i
=
0
;
for
(;
i
<
len
;
++
i
)
{
s
[
i
]
=
alphanum
[
rand
()
%
(
sizeof
(
alphanum
)
-
1
)];
}
s
[
len
]
=
0
;
static
const
char
alphanum
[]
=
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
;
int
i
=
0
;
for
(;
i
<
len
;
++
i
)
{
s
[
i
]
=
alphanum
[
rand
()
%
(
sizeof
(
alphanum
)
-
1
)];
}
}
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
;
char
*
buf
=
malloc
(
MAX_STUN_MESSAGE_LENGTH
);
char
*
ptr
=
buf
;
StunHeader
h
;
h
.
msgType
=
BindRequest
;
gen_random_string
((
char
*
)
&
h
.
magicCookieAndTid
,
16
);
StunHeader
h
;
h
.
msgType
=
BindRequest
;
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
);
if
(
server
==
NULL
)
{
fprintf
(
stderr
,
"no such host, %s
\n
"
,
remote_host
);
free
(
buf
);
return
-
1
;
}
struct
sockaddr_in
remote_addr
;
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
);
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
)))
{
//
udp
sendto barely failed
// sendto
()
barely failed
free
(
buf
);
return
-
1
;
...
...
@@ -159,12 +157,12 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
if
(
retries
==
MAX_RETRIES_NUM
)
return
-
1
;
StunHeader
reply_header
;
memcpy
(
&
reply_header
,
buf
,
sizeof
(
StunHeader
));
StunHeader
reply_header
;
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
);
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_
return
-
1
;
}
break
;
case
SourceAddress
:
if
(
stun_parse_atr_addr
(
body
,
attrLen
,
addr_array
+
2
))
{
free
(
buf
);
return
-
1
;
}
break
;
default:
// ignore
break
;
...
...
@@ -220,36 +211,36 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
size
-=
attrLen
+
attrLenPad
;
}
}
free
(
buf
);
free
(
buf
);
return
0
;
return
0
;
}
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
;
uint16_t
mapped_port
=
0
;
int
s
;
if
((
s
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
))
<=
0
)
{
return
Error
;
}
int
s
;
if
((
s
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
))
<=
0
)
{
return
Error
;
}
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
;
local_addr
.
sin_family
=
AF_INET
;
local_addr
.
sin_addr
.
s_addr
=
inet_addr
(
local_
host
);
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
)))
{
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
;
...
...
@@ -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
StunAtrAddress
bind_result
[
2
];
// 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
;
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
;
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, use the setsockopt() called IP_PKTINFO
* which will get you a parameter over recvmsg() called
* IP_PKTINFO, which carries a struct in_pktinfo,
* which has a 4 byte IP address hiding in its ipi_addr field.
* 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
i
nternet
* if it's open
I
nternet
*/
if
(
!
strcmp
(
local_
host
,
inet_ntoa
(
mapped_addr
)))
{
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
);
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
);
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
"
);
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
;
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
;
if
(
send_bind_request
(
s
,
alt_host
,
changed_port
,
0
,
ChangePortFlag
,
bind_result
))
{
nat_type
=
RestricPortNAT
;
goto
cleanup_sock
;
}
}
nat_type
=
RestricNAT
;
nat_type
=
RestricNAT
;
goto
cleanup_sock
;
}
else
{
nat_type
=
FullCone
;
}
else
{
nat_type
=
FullCone
;
goto
cleanup_sock
;
}
}
else
{
printf
(
"no alterative server, can't detect nat type
\n
"
);
nat_type
=
Error
;
}
}
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
;
...
...
nat_type.h
View file @
b435fd9e
...
...
@@ -41,36 +41,36 @@ typedef struct { uint32_t longpart[3]; } UInt96;
typedef
struct
{
uint32_t
magicCookie
;
// rfc 5389
UInt96
tid
;
uint32_t
magicCookie
;
// rfc 5389
UInt96
tid
;
}
Id
;
typedef
struct
{
uint16_t
msgType
;
uint16_t
msgLength
;
// length of stun body
union
{
UInt128
magicCookieAndTid
;
Id
id
;
};
uint16_t
msgType
;
uint16_t
msgLength
;
// length of stun body
union
{
UInt128
magicCookieAndTid
;
Id
id
;
};
}
StunHeader
;
typedef
struct
{
uint16_t
type
;
uint16_t
length
;
uint16_t
type
;
uint16_t
length
;
}
StunAtrHdr
;
typedef
struct
{
uint8_t
family
;
uint16_t
port
;
union
{
uint32_t
ipv4
;
// in host byte order
UInt128
ipv6
;
// in network byte order
}
addr
;
uint8_t
family
;
uint16_t
port
;
union
{
uint32_t
ipv4
;
// in host byte order
UInt128
ipv6
;
// in network byte order
}
addr
;
}
StunAtrAddress
;
char
*
encode16
(
char
*
buf
,
uint16_t
data
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment