| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- package p2p
- import (
- "fmt"
- "math/rand"
- "net"
- "strconv"
- "time"
- )
- const (
- DialerTimeout = 180 //seconds
- KeepAlivePeriod = 60 //minutes
- portMappingUpdateInterval = 900 // seconds = 15 mins
- upnpDiscoverAttempts = 3
- )
- // Dialer is not an interface in net, so we define one
- // *net.Dialer conforms to this
- type Dialer interface {
- Dial(network, address string) (net.Conn, error)
- }
- type Network interface {
- Start() error
- Listener(net.Addr) (net.Listener, error)
- Dialer(net.Addr) (Dialer, error)
- NewAddr(string, int) (addr net.Addr, err error)
- ParseAddr(string) (addr net.Addr, err error)
- }
- type NAT interface {
- GetExternalAddress() (addr net.IP, err error)
- AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
- DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
- }
- type TCPNetwork struct {
- nat NAT
- natType NATType
- quit chan chan bool
- ports chan string
- }
- type NATType int
- const (
- NONE = iota
- UPNP
- PMP
- )
- const (
- portMappingTimeout = 1200 // 20 mins
- )
- func NewTCPNetwork(natType NATType) (net *TCPNetwork) {
- return &TCPNetwork{
- natType: natType,
- ports: make(chan string),
- }
- }
- func (self *TCPNetwork) Dialer(addr net.Addr) (Dialer, error) {
- return &net.Dialer{
- Timeout: DialerTimeout * time.Second,
- // KeepAlive: KeepAlivePeriod * time.Minute,
- LocalAddr: addr,
- }, nil
- }
- func (self *TCPNetwork) Listener(addr net.Addr) (net.Listener, error) {
- if self.natType == UPNP {
- _, port, _ := net.SplitHostPort(addr.String())
- if self.quit == nil {
- self.quit = make(chan chan bool)
- go self.updatePortMappings()
- }
- self.ports <- port
- }
- return net.Listen(addr.Network(), addr.String())
- }
- func (self *TCPNetwork) Start() (err error) {
- switch self.natType {
- case NONE:
- case UPNP:
- nat, uerr := upnpDiscover(upnpDiscoverAttempts)
- if uerr != nil {
- err = fmt.Errorf("UPNP failed: ", uerr)
- } else {
- self.nat = nat
- }
- case PMP:
- err = fmt.Errorf("PMP not implemented")
- default:
- err = fmt.Errorf("Invalid NAT type: %v", self.natType)
- }
- return
- }
- func (self *TCPNetwork) Stop() {
- q := make(chan bool)
- self.quit <- q
- <-q
- }
- func (self *TCPNetwork) addPortMapping(lport int) (err error) {
- _, err = self.nat.AddPortMapping("TCP", lport, lport, "p2p listen port", portMappingTimeout)
- if err != nil {
- logger.Errorf("unable to add port mapping on %v: %v", lport, err)
- } else {
- logger.Debugf("succesfully added port mapping on %v", lport)
- }
- return
- }
- func (self *TCPNetwork) updatePortMappings() {
- timer := time.NewTimer(portMappingUpdateInterval * time.Second)
- lports := []int{}
- out:
- for {
- select {
- case port := <-self.ports:
- int64lport, _ := strconv.ParseInt(port, 10, 16)
- lport := int(int64lport)
- if err := self.addPortMapping(lport); err != nil {
- lports = append(lports, lport)
- }
- case <-timer.C:
- for lport := range lports {
- if err := self.addPortMapping(lport); err != nil {
- }
- }
- case errc := <-self.quit:
- errc <- true
- break out
- }
- }
- timer.Stop()
- for lport := range lports {
- if err := self.nat.DeletePortMapping("TCP", lport, lport); err != nil {
- logger.Debugf("unable to remove port mapping on %v: %v", lport, err)
- } else {
- logger.Debugf("succesfully removed port mapping on %v", lport)
- }
- }
- }
- func (self *TCPNetwork) NewAddr(host string, port int) (net.Addr, error) {
- ip, err := self.lookupIP(host)
- if err == nil {
- return &net.TCPAddr{
- IP: ip,
- Port: port,
- }, nil
- }
- return nil, err
- }
- func (self *TCPNetwork) ParseAddr(address string) (net.Addr, error) {
- host, port, err := net.SplitHostPort(address)
- if err == nil {
- iport, _ := strconv.Atoi(port)
- addr, e := self.NewAddr(host, iport)
- return addr, e
- }
- return nil, err
- }
- func (*TCPNetwork) lookupIP(host string) (ip net.IP, err error) {
- if ip = net.ParseIP(host); ip != nil {
- return
- }
- var ips []net.IP
- ips, err = net.LookupIP(host)
- if err != nil {
- logger.Warnln(err)
- return
- }
- if len(ips) == 0 {
- err = fmt.Errorf("No IP addresses available for %v", host)
- logger.Warnln(err)
- return
- }
- if len(ips) > 1 {
- // Pick a random IP address, simulating round-robin DNS.
- rand.Seed(time.Now().UTC().UnixNano())
- ip = ips[rand.Intn(len(ips))]
- } else {
- ip = ips[0]
- }
- return
- }
|