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
Expand all
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
This diff is collapsed.
Click to expand it.
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
This diff is collapsed.
Click to expand it.
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