Commit 70c0ff3d authored by YI's avatar YI
Browse files

connect to client with user provided meta info

parent 868f68b4
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
*.o *.o
.DS_Store .DS_Store
*.swp *.swp
.ccls-cache/
CC = gcc CC = gcc
CFLAGS = -g -Wall 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 # 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 $(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 main.o: main.c
$(CC) $(CFLAGS) -c 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 $(CC) $(CFLAGS) -c nat_traversal.c
nat_type.o: nat_type.c nat_type.o: nat_type.c
$(CC) $(CFLAGS) -c nat_type.c $(CC) $(CFLAGS) -c nat_type.c
clean: clean:
$(RM) nat_traversal *.o *~ $(RM) stun_host_test punch_server nat_traversal *.o *~
#include <unistd.h> #include <arpa/inet.h>
#include <string.h> #include <errno.h>
#include <stdlib.h> #include <netinet/in.h>
#include <stdio.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <unistd.h>
#include <arpa/inet.h>
#include "nat_traversal.h" #include "nat_traversal.h"
#include "utils.h"
#define DEFAULT_SERVER_PORT 9988 #define DEFAULT_SERVER_PORT 9988
#define MSG_BUF_SIZE 512 #define MSG_BUF_SIZE 512
#define STUN_SERVER_RETRIES 3
// use public stun servers to detect port allocation rule
static char *stun_servers[] = {
"stun.ideasip.com",
"stun.ekiga.net",
"203.183.172.196"
};
// definition checked against extern declaration // definition checked against extern declaration
int verbose = 0; int verbose = 0;
int main(int argc, char** argv) int main(int argc, char **argv) {
{ char *stun_server = NULL;
char* stun_server = stun_servers[0]; char local_ip[16] = "0.0.0.0";
char local_ip[16] = "0.0.0.0"; uint16_t stun_port = DEFAULT_STUN_SERVER_PORT;
uint16_t stun_port = DEFAULT_STUN_SERVER_PORT; uint16_t local_port = DEFAULT_LOCAL_PORT;
uint16_t local_port = DEFAULT_LOCAL_PORT; char *punch_server = NULL;
char* punch_server = NULL; char *meta = NULL;
uint32_t peer_id = 0; char *peer_meta = NULL;
int ttl = 10; 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 get_info = 0;
int opt; int get_info_from_meta = 0;
while ((opt = getopt (argc, argv, "H:h:t:P:p:s:d:i:v")) != -1)
{ static char usage[] =
switch (opt) "usage: [-h] [-H STUN_HOST] [-t ttl] [-P STUN_PORT] [-s punch server] "
{ "[-d id] [-i SOURCE_IP] [-p SOURCE_PORT] [-v verbose]\n";
case 'h': int opt;
printf("%s", usage); while ((opt = getopt(argc, argv, "H:h:I:t:P:p:s:m:o:d:i:vzZ")) != -1) {
break; switch (opt) {
case 'H': case 'h':
stun_server = optarg; printf("%s", usage);
break; break;
case 't': case 'z':
ttl = atoi(optarg); get_info = 1;
break; break;
case 'P': case 'Z':
stun_port = atoi(optarg); get_info_from_meta = 1;
break; break;
case 'p': case 'H':
local_port = atoi(optarg); stun_server = optarg;
break; break;
case 's': case 't':
punch_server = optarg; ttl = atoi(optarg);
break; break;
case 'd': case 'P':
peer_id = atoi(optarg); stun_port = atoi(optarg);
break; break;
case 'i': case 'p':
strncpy(local_ip, optarg, 16); local_port = atoi(optarg);
break; break;
case 'v': case 's':
verbose = 1; punch_server = optarg;
break; break;
case '?': case 'm':
default: meta = optarg;
printf("invalid option: %c\n", opt); break;
printf("%s", usage); case 'o':
peer_meta = optarg;
return -1; 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}; if (!peer_id) {
uint16_t ext_port = 0; printf("failed to get peer_id\n");
return -1;
}
// TODO we should try another STUN server if failed int n = get_peer_info(&cli, peer_id, &peer);
nat_type type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip, &ext_port); 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 (get_info_from_meta) {
if (ext_port) { client cli;
printf("external address: %s:%d\n", ext_ip, ext_port); struct peer_info peer;
} else { int res = init(server_addr, &cli);
return -1;
if (res) {
printf("init punch server socket failed\n");
return res;
} }
if (!punch_server) { if (peer_meta == NULL) {
printf("please specify punch server\n"); printf("failed to get peer_meta\n");
return -1; 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; return 0;
strncpy(self.ip, ext_ip, 16); }
self.port = ext_port;
self.type = type; char ext_ip[16] = {0};
uint16_t ext_port = 0;
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET; // TODO we should try another STUN server if failed
server_addr.sin_addr.s_addr = inet_addr(punch_server); int i;
server_addr.sin_port = htons(DEFAULT_SERVER_PORT); nat_type type;
for (i = 0; i < STUN_SERVER_RETRIES; i++) {
client c; type = detect_nat_type(stun_server, stun_port, local_ip, local_port, ext_ip,
c.type = type; &ext_port);
c.ttl = ttl; if (type != 0) {
if (enroll(self, server_addr, &c) < 0) { break;
printf("failed to enroll\n");
return -1;
} }
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) { if (peer_meta != NULL) {
printf("connecting to peer %d\n", peer_id); verbose_log("connecting to peer %s\n", peer_meta);
if (connect_to_peer(&c, peer_id) < 0) { if (connect_to_peer_from_meta(&c, peer_meta) < 0) {
printf("failed to connect to peer %d\n", peer_id); 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); pthread_join(tid, NULL);
return 0; return 0;
} }
This diff is collapsed.
...@@ -4,35 +4,54 @@ ...@@ -4,35 +4,54 @@
typedef struct client client; typedef struct client client;
struct client { struct client {
int sfd; int sfd;
uint32_t id; uint32_t id;
char buf[128]; char buf[128];
//use a stack-based buffer to prevent memory allocation every time // use a stack-based buffer to prevent memory allocation every time
char* msg_buf; char *msg_buf;
nat_type type; nat_type type;
char ext_ip[16]; char ext_ip[16];
uint16_t ext_port; uint16_t ext_port;
// ttl of hole punching packets, // ttl of hole punching packets,
// it should be greater than the number of hops between host to NAT of own side // it should be greater than the number of hops between host to NAT of own
// and less than the number of hops between host to NAT of remote side, // 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 // so that the hole punching packets just die in the way
int ttl; 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 { struct peer_info {
char ip[16]; uint32_t id;
uint16_t port; char ip[16];
uint16_t type; uint16_t port;
uint16_t type;
char *meta;
}; };
enum msg_type { enum msg_type {
Enroll = 0x01, Enroll = 0x01,
GetPeerInfo = 0x02, GetPeerInfo = 0x02,
NotifyPeer = 0x03, NotifyPeer = 0x03,
}; GetPeerInfoFromMeta = 0x04,
NotifyPeerFromMeta = 0x05,
};
// public functions // public functions
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c); int enroll(struct peer_info self, struct sockaddr_in punch_server, client *c);
pthread_t wait_for_command(int* server_sock); pthread_t wait_for_command(int *server_sock);
int connect_to_peer(client* cli, uint32_t peer_id); 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); 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);
This diff is collapsed.
...@@ -73,6 +73,7 @@ typedef struct ...@@ -73,6 +73,7 @@ typedef struct
} addr; } addr;
} StunAtrAddress; } StunAtrAddress;
char* encode8(char* buf, size_t data);
char* encode16(char* buf, uint16_t data); char* encode16(char* buf, uint16_t data);
char* encode32(char* buf, uint32_t data); char* encode32(char* buf, uint32_t data);
char* encode(char* buf, const char* data, unsigned int length); char* encode(char* buf, const char* data, unsigned int length);
...@@ -83,6 +84,7 @@ extern int verbose; ...@@ -83,6 +84,7 @@ extern int verbose;
printf(format, ##__VA_ARGS__); \ printf(format, ##__VA_ARGS__); \
} while(0) } 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); const char* get_nat_desc(nat_type type);
void gen_random_string(char *s, const int len);
23.21.150.121
iphone-stun.strato-iphone.de
numb.viagenie.ca
s1.taraba.net
s2.taraba.net
stun.12connect.com
stun.12voip.com
stun.1und1.de
stun.2talk.co.nz
stun.2talk.com
stun.3clogic.com
stun.3cx.com
stun.a-mm.tv
stun.aa.net.uk
stun.acrobits.cz
stun.actionvoip.com
stun.advfn.com
stun.aeta-audio.com
stun.aeta.com
stun.alltel.com.au
stun.altar.com.pl
stun.annatel.net
stun.antisip.com
stun.arbuz.ru
stun.avigora.com
stun.avigora.fr
stun.awa-shima.com
stun.awt.be
stun.b2b2c.ca
stun.bahnhof.net
stun.barracuda.com
stun.bluesip.net
stun.bmwgs.cz
stun.botonakis.com
stun.budgetphone.nl
stun.budgetsip.com
stun.cablenet-as.net
stun.callromania.ro
stun.callwithus.com
stun.cbsys.net
stun.chathelp.ru
stun.cheapvoip.com
stun.ciktel.com
stun.cloopen.com
stun.colouredlines.com.au
stun.comfi.com
stun.commpeak.com
stun.comtube.com
stun.comtube.ru
stun.cope.es
stun.counterpath.com
stun.counterpath.net
stun.cryptonit.net
stun.darioflaccovio.it
stun.datamanagement.it
stun.dcalling.de
stun.decanet.fr
stun.demos.ru
stun.develz.org
stun.dingaling.ca
stun.doublerobotics.com
stun.drogon.net
stun.duocom.es
stun.dus.net
stun.e-fon.ch
stun.easybell.de
stun.easycall.pl
stun.easyvoip.com
stun.efficace-factory.com
stun.einsundeins.com
stun.einsundeins.de
stun.ekiga.net
stun.epygi.com
stun.etoilediese.fr
stun.eyeball.com
stun.faktortel.com.au
stun.freecall.com
stun.freeswitch.org
stun.freevoipdeal.com
stun.fuzemeeting.com
stun.gmx.de
stun.gmx.net
stun.gradwell.com
stun.halonet.pl
stun.hellonanu.com
stun.hoiio.com
stun.hosteurope.de
stun.ideasip.com
stun.imesh.com
stun.infra.net
stun.internetcalls.com
stun.intervoip.com
stun.ipcomms.net
stun.ipfire.org
stun.ippi.fr
stun.ipshka.com
stun.iptel.org
stun.irian.at
stun.it1.hr
stun.ivao.aero
stun.jappix.com
stun.jumblo.com
stun.justvoip.com
stun.kanet.ru
stun.kiwilink.co.nz
stun.kundenserver.de
stun.linea7.net
stun.linphone.org
stun.liveo.fr
stun.lowratevoip.com
stun.lugosoft.com
stun.lundimatin.fr
stun.magnet.ie
stun.manle.com
stun.mgn.ru
stun.mit.de
stun.mitake.com.tw
stun.miwifi.com
stun.modulus.gr
stun.mozcom.com
stun.myvoiptraffic.com
stun.mywatson.it
stun.nas.net
stun.neotel.co.za
stun.netappel.com
stun.netappel.fr
stun.netgsm.com.tr
stun.nfon.net
stun.noblogs.org
stun.noc.ams-ix.net
stun.node4.co.uk
stun.nonoh.net
stun.nottingham.ac.uk
stun.nova.is
stun.nventure.com
stun.on.net.mk
stun.ooma.com
stun.ooonet.ru
stun.oriontelekom.rs
stun.outland-net.de
stun.ozekiphone.com
stun.patlive.com
stun.personal-voip.de
stun.petcube.com
stun.phone.com
stun.phoneserve.com
stun.pjsip.org
stun.poivy.com
stun.powerpbx.org
stun.powervoip.com
stun.ppdi.com
stun.prizee.com
stun.qq.com
stun.qvod.com
stun.rackco.com
stun.rapidnet.de
stun.rb-net.com
stun.refint.net
stun.remote-learner.net
stun.rixtelecom.se
stun.rockenstein.de
stun.rolmail.net
stun.rounds.com
stun.rynga.com
stun.samsungsmartcam.com
stun.schlund.de
stun.services.mozilla.com
stun.sigmavoip.com
stun.sip.us
stun.sipdiscount.com
stun.sipgate.net
stun.siplogin.de
stun.sipnet.net
stun.sipnet.ru
stun.siportal.it
stun.sippeer.dk
stun.siptraffic.com
stun.skylink.ru
stun.sma.de
stun.smartvoip.com
stun.smsdiscount.com
stun.snafu.de
stun.softjoys.com
stun.solcon.nl
stun.solnet.ch
stun.sonetel.com
stun.sonetel.net
stun.sovtest.ru
stun.speedy.com.ar
stun.spokn.com
stun.srce.hr
stun.ssl7.net
stun.stunprotocol.org
stun.symform.com
stun.symplicity.com
stun.sysadminman.net
stun.t-online.de
stun.tagan.ru
stun.tatneft.ru
stun.teachercreated.com
stun.tel.lu
stun.telbo.com
stun.telefacil.com
stun.tis-dialog.ru
stun.tng.de
stun.twt.it
stun.u-blox.com
stun.ucallweconn.net
stun.ucsb.edu
stun.ucw.cz
stun.uls.co.za
stun.unseen.is
stun.usfamily.net
stun.veoh.com
stun.vidyo.com
stun.vipgroup.net
stun.virtual-call.com
stun.viva.gr
stun.vivox.com
stun.vline.com
stun.vo.lu
stun.vodafone.ro
stun.voicetrading.com
stun.voip.aebc.com
stun.voip.blackberry.com
stun.voip.eutelia.it
stun.voiparound.com
stun.voipblast.com
stun.voipbuster.com
stun.voipbusterpro.com
stun.voipcheap.co.uk
stun.voipcheap.com
stun.voipfibre.com
stun.voipgain.com
stun.voipgate.com
stun.voipinfocenter.com
stun.voipplanet.nl
stun.voippro.com
stun.voipraider.com
stun.voipstunt.com
stun.voipwise.com
stun.voipzoom.com
stun.vopium.com
stun.voxgratia.org
stun.voxox.com
stun.voys.nl
stun.voztele.com
stun.vyke.com
stun.webcalldirect.com
stun.whoi.edu
stun.wifirst.net
stun.wwdl.net
stun.xs4all.nl
stun.xtratelecom.es
stun.yesss.at
stun.zadarma.com
stun.zadv.com
stun.zoiper.com
stun1.faktortel.com.au
stun1.voiceeclipse.net
stunserver.org
package main package main
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "errors"
"io"
"math/rand"
"net" "net"
"os"
"sync" "sync"
"time"
log "github.com/sirupsen/logrus"
) )
type nat_info struct { type PeerInfo struct {
Ip [16]byte IP [16]byte
Port uint16 Port uint16
Nat_type uint16 NatType uint16
Meta string
ID uint32
}
type natInfo struct {
IP [16]byte
Port uint16
NatType uint16
} }
const ( const (
Enroll = 1 _ = iota
GetPeerInfo = 2 Enroll
NotifyPeer = 3 GetPeerInfo
NotifyPeer
GetPeerInfoFromMeta
NotifyPeerFromMeta
PeerOffline = 1
PeerError = 2
ListeningPort = ":9988"
) )
var seq uint32 = 1 var (
var peers map[uint32]nat_info seq uint32 = 1
var peers_conn map[uint32]net.Conn peers map[uint32]PeerInfo
var m sync.Mutex 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() { func init() {
peers = make(map[uint32]nat_info) peers = make(map[uint32]PeerInfo)
peers_conn = make(map[uint32]net.Conn) 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() defer l.Close()
go dumpPeers()
for { for {
conn, err := l.Accept() conn, err := l.Accept()
if err != nil { if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("Accepting connection failed")
continue continue
} }
log.Info("New connection received")
go handleConn(conn) 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 // 2 bytes for message type
func handleConn(c net.Conn) { func handleConn(c net.Conn) {
defer c.Close() defer c.Close()
var peerID uint32 = 0 log.Info("new connection received!")
var myInfo PeerInfo
for { for {
// read message type first var myBuf bytes.Buffer
w := io.MultiWriter(&myBuf, c)
data := make([]byte, 2) data := make([]byte, 2)
_, err := c.Read(data) _, err := c.Read(data)
log.WithFields(log.Fields{
"header": data,
}).Info("new received header")
if err != nil { if err != nil {
m.Lock() mutex.Lock()
fmt.Printf("error: %v, peer %d disconnected\n", err, peerID) delete(peers, myInfo.ID)
delete(peers, peerID) delete(peerConn, myInfo.ID)
delete(peers_conn, peerID) delete(peersFromMeta, myInfo.Meta)
m.Unlock() delete(peerConnFromMeta, myInfo.Meta)
mutex.Unlock()
log.WithFields(log.Fields{
"err": err,
"myID": myInfo.ID,
}).Info("peer left")
return return
} }
t := binary.BigEndian.Uint16(data[:])
switch binary.BigEndian.Uint16(data[:]) { switch t {
case Enroll: case Enroll:
var peer nat_info var err error
err = binary.Read(c, binary.BigEndian, &peer) myInfo, err = readPeerInfo(c)
if err != nil { if err != nil {
continue log.WithFields(log.Fields{
"err": err,
}).Warn("Reading meta failed")
break
} }
mutex.Lock()
fmt.Println("peer enrolled, addr: ", string(peer.Ip[:]), peer.Port, peer.Nat_type)
m.Lock()
seq++ seq++
peerID = seq myInfo.ID = seq
peers[peerID] = peer peers[myInfo.ID] = myInfo
peers_conn[peerID] = c peerConn[myInfo.ID] = c
fmt.Println("new peer, id : ", peerID) if myInfo.Meta != "" {
m.Unlock() peersFromMeta[myInfo.Meta] = myInfo
err = binary.Write(c, binary.BigEndian, peerID) 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 { 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: case GetPeerInfo:
var peer_id uint32 var peerID uint32
binary.Read(c, binary.BigEndian, &peer_id) err = binary.Read(c, binary.BigEndian, &peerID)
if val, ok := peers[peer_id]; ok { if err != nil {
binary.Write(c, binary.BigEndian, val) log.WithFields(log.Fields{
} else { "err": err,
var offline uint8 = 0 "peerID": peerID,
binary.Write(c, binary.BigEndian, offline) "myID": myInfo.ID,
fmt.Printf("%d offline\n", peer_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: case NotifyPeer:
var peer_id uint32 var peerID uint32
binary.Read(c, binary.BigEndian, &peer_id) err = binary.Read(c, binary.BigEndian, &peerID)
fmt.Println("notify to peer", peer_id) if err != nil {
if val, ok := peers_conn[peer_id]; ok { log.WithFields(log.Fields{
if err = binary.Write(val, binary.BigEndian, peers[peerID]); err != nil { "err": err,
// unable to notify peer "peerID": peerID,
fmt.Println("offline") "myID": myInfo.ID,
} }).Warn("Unable to get peer id")
} else { break
fmt.Println("offline") }
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: 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 return
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nat_type.h"
#define DEFAULT_STUN_SERVER_PORT 3478
#define DEFAULT_LOCAL_PORT 34780
int appendToFile(char *c, nat_type type) {
FILE *fp;
int retval;
fp = fopen("public_stun_list-working.txt", "a");
if (fp == NULL)
return -1;
retval = fprintf(fp, "%s, %d\n", c, type);
fclose(fp);
return retval;
}
int main() {
uint16_t stun_port = DEFAULT_STUN_SERVER_PORT;
uint16_t local_port = DEFAULT_LOCAL_PORT;
char ext_ip[16] = {0};
uint16_t ext_port = 0;
char local_ip[16] = "0.0.0.0";
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
/* from https://gist.github.com/mondain/b0ec1cf5f60ae726202e */
fp = fopen("public-stun-list.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu:\n", read);
printf("%s\n", line);
line[strcspn(line, "\n")] = 0;
char *stun_server = line;
printf("Testing stun server %s\n", stun_server);
nat_type type = detect_nat_type(stun_server, stun_port, local_ip,
local_port, ext_ip, &ext_port);
printf("The return value is %d (%s)\n", type, get_nat_desc(type));
if (type != 0) {
appendToFile(stun_server, type);
};
;
}
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment