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
70c0ff3d
Commit
70c0ff3d
authored
Nov 27, 2018
by
YI
Browse files
connect to client with user provided meta info
parent
868f68b4
Changes
10
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
70c0ff3d
...
...
@@ -2,3 +2,4 @@
*.o
.DS_Store
*.swp
.ccls-cache/
Makefile
View file @
70c0ff3d
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
*
~
main.c
View file @
70c0ff3d
#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
;
}
nat_traversal.c
View file @
70c0ff3d
#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 <
pthrea
d.h>
#include <
sys/time
.h>
#include <
time
.h>
#include <
unist
d.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
=
encode
32
(
cli
->
msg_buf
,
peer_id
);
if
(
-
1
=
=
s
en
d_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
=
encode
16
(
cli
->
msg_buf
,
GetPeerInfo
);
cli
->
msg_buf
=
en
code32
(
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,
wait
ing
for
peer
\n
"
);
struct
timeval
tv
=
{
10
0
,
0
};
int
fd
=
wait_for_peer
(
sock_array
,
i
,
&
tv
);
//
wait for
a while
struct
timeval
tv
=
{
0
,
1000
*
10
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
]);
}
// 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
);
}
nat_traversal.h
View file @
70c0ff3d
...
...
@@ -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
);
nat_type.c
View file @
70c0ff3d
#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
;
}
nat_type.h
View file @
70c0ff3d
...
...
@@ -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
);
public-stun-list.txt
0 → 100644
View file @
70c0ff3d
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
punch_server.go
View file @
70c0ff3d
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
...
...
stun_host_test.c
0 → 100644
View file @
70c0ff3d
#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
);
}
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