]> Cypherpunks.ru repositories - govpn.git/commitdiff
Refactor server
authorBruno Clermont <bruno@robotinfra.com>
Wed, 8 Feb 2017 10:52:55 +0000 (18:52 +0800)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 25 Feb 2017 09:22:36 +0000 (12:22 +0300)
- server can be used as a Go library
- unexposed type and values that aren't consumed outside package
- golint fixes
- switch logging to logrus
- add more logging messages
- improve usage as a library: no more `panic` or `os.Exit`, return `error` instead
- evalute/raise nearly all possible `error` values
- use interface to allow library consumer to use custom peer configuration backend

12 files changed:
src/cypherpunks.ru/govpn/cmd/govpn-server/action.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/cmd/govpn-server/common.go [deleted file]
src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go
src/cypherpunks.ru/govpn/cmd/govpn-server/main.go
src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go [deleted file]
src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go [deleted file]
src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go [deleted file]
src/cypherpunks.ru/govpn/server/common.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/server/proxy.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/server/server.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/server/tcp.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/server/udp.go [new file with mode: 0644]

diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/action.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/action.go
new file mode 100644 (file)
index 0000000..9c25426
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+       "bytes"
+
+       "github.com/pkg/errors"
+
+       "cypherpunks.ru/govpn"
+)
+
+func preUpAction(path string) govpn.TunnelPreUpAction {
+       if len(path) == 0 {
+               return nil
+       }
+
+       return func(ctx govpn.PeerContext) (*govpn.TAP, error) {
+               result, err := govpn.ScriptCall(path, ctx.Config.Iface, ctx.RemoteAddress)
+               if err != nil {
+                       return nil, errors.Wrap(err, "govpn.ScriptCall")
+               }
+               if len(ctx.Config.Iface) == 0 {
+                       sepIndex := bytes.Index(result, []byte{'\n'})
+                       if sepIndex < 0 {
+                               sepIndex = len(result)
+                       }
+                       ctx.Config.Iface = string(result[:sepIndex])
+               }
+
+               if len(ctx.Config.Iface) == 0 {
+                       return nil, errors.Errorf("Script %q didn't returned an interface name", path)
+               }
+
+               tap, err := govpn.TAPListen(ctx.Config.Iface, ctx.Config.MTU)
+               if err != nil {
+                       return nil, errors.Wrap(err, "govpn.TAPListen")
+               }
+               return tap, nil
+       }
+}
diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go
deleted file mode 100644 (file)
index ff108ad..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
-       "bytes"
-       "sync"
-
-       "cypherpunks.ru/govpn"
-)
-
-// PeerState holds server side state of a single connecting/connected peer
-type PeerState struct {
-       peer       *govpn.Peer
-       terminator chan struct{}
-       tap        *govpn.TAP
-}
-
-var (
-       handshakes map[string]*govpn.Handshake = make(map[string]*govpn.Handshake)
-       hsLock     sync.RWMutex
-
-       peers     = make(map[string]*PeerState)
-       peersLock sync.RWMutex
-
-       peersByID     = make(map[govpn.PeerID]string)
-       peersByIDLock sync.RWMutex
-
-       knownPeers govpn.KnownPeers
-       kpLock     sync.RWMutex
-)
-
-func callUp(peerID *govpn.PeerID, remoteAddr string) (string, error) {
-       ifaceName := confs[*peerID].Iface
-       if confs[*peerID].Up != "" {
-               result, err := govpn.ScriptCall(confs[*peerID].Up, ifaceName, remoteAddr)
-               if err != nil {
-                       govpn.Printf(
-                               `[script-failed bind="%s" path="%s" err="%s"]`,
-                               *bindAddr,
-                               confs[*peerID].Up,
-                               err,
-                       )
-                       return "", err
-               }
-               if ifaceName == "" {
-                       sepIndex := bytes.Index(result, []byte{'\n'})
-                       if sepIndex < 0 {
-                               sepIndex = len(result)
-                       }
-                       ifaceName = string(result[:sepIndex])
-               }
-       }
-       if ifaceName == "" {
-               govpn.Printf(`[tap-failed bind="%s" peer="%s"]`, *bindAddr, *peerID)
-       }
-       return ifaceName, nil
-}
index 62eca02437fa6135046345d0aa92c974a39c7e30..80581ab4418db9ec8474aeab8ef24664525fdcc3 100644 (file)
@@ -19,41 +19,64 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
-       "errors"
        "io/ioutil"
-       "log"
        "time"
 
+       "github.com/Sirupsen/logrus"
+       "github.com/pkg/errors"
        "gopkg.in/yaml.v2"
 
        "cypherpunks.ru/govpn"
 )
 
-const (
-       RefreshRate = time.Minute
-)
+const refreshRate = time.Minute
 
 var (
-       confs    map[govpn.PeerID]*govpn.PeerConf
+       confs    peerConfigurations
        idsCache *govpn.MACCache
+       logger   *logrus.Logger
 )
 
+type peerConfigurations map[govpn.PeerID]*govpn.PeerConf
+
+func (peerConfs peerConfigurations) Get(peerID govpn.PeerID) *govpn.PeerConf {
+       pc, exists := peerConfs[peerID]
+       if !exists {
+               return nil
+       }
+       return pc
+}
+
+type peerConf struct {
+       Name        string `yaml:"name"`
+       Iface       string `yaml:"iface"`
+       MTU         int    `yaml:"mtu"`
+       Up          string `yaml:"up"`
+       Down        string `yaml:"down"`
+       TimeoutInt  int    `yaml:"timeout"`
+       Noise       bool   `yaml:"noise"`
+       CPR         int    `yaml:"cpr"`
+       Encless     bool   `yaml:"encless"`
+       TimeSync    int    `yaml:"timesync"`
+       VerifierRaw string `yaml:"verifier"`
+}
+
 func confRead() (*map[govpn.PeerID]*govpn.PeerConf, error) {
        data, err := ioutil.ReadFile(*confPath)
        if err != nil {
-               return nil, err
+               return nil, errors.Wrap(err, "ioutil.ReadFile")
        }
-       confsRaw := new(map[string]govpn.PeerConf)
+       confsRaw := new(map[string]peerConf)
        err = yaml.Unmarshal(data, confsRaw)
        if err != nil {
-               return nil, err
+               return nil, errors.Wrap(err, "yaml.Unmarshal")
        }
 
        confs := make(map[govpn.PeerID]*govpn.PeerConf, len(*confsRaw))
        for name, pc := range *confsRaw {
                verifier, err := govpn.VerifierFromString(pc.VerifierRaw)
                if err != nil {
-                       return nil, errors.New("Unable to decode verifier: " + err.Error())
+                       return nil, errors.Wrap(err, "govpn.VerifierFromString")
                }
                if pc.Encless {
                        pc.Noise = true
@@ -62,7 +85,11 @@ func confRead() (*map[govpn.PeerID]*govpn.PeerConf, error) {
                        pc.MTU = govpn.MTUDefault
                }
                if pc.MTU > govpn.MTUMax {
-                       govpn.Printf(`[mtu-high bind="%s" value="%d" overriden="%d"]`, *bindAddr, pc.MTU, govpn.MTUMax)
+                       logger.WithFields(logrus.Fields{
+                               "bind":         *bindAddr,
+                               "previous_mtu": pc.MTU,
+                               "new_mtu":      govpn.MTUMax,
+                       }).Warning("Overriden MTU")
                        pc.MTU = govpn.MTUMax
                }
                conf := govpn.PeerConf{
@@ -71,42 +98,54 @@ func confRead() (*map[govpn.PeerID]*govpn.PeerConf, error) {
                        Name:     name,
                        Iface:    pc.Iface,
                        MTU:      pc.MTU,
-                       Up:       pc.Up,
-                       Down:     pc.Down,
+                       PreUp:    preUpAction(pc.Up),
+                       Down:     govpn.RunScriptAction(&pc.Down),
                        Noise:    pc.Noise,
                        CPR:      pc.CPR,
                        Encless:  pc.Encless,
                        TimeSync: pc.TimeSync,
                }
                if pc.TimeoutInt <= 0 {
-                       pc.TimeoutInt = govpn.TimeoutDefault
+                       conf.Timeout = govpn.TimeoutDefault
+               } else {
+                       conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
                }
-               conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
                confs[*verifier.ID] = &conf
        }
        return &confs, nil
 }
 
 func confRefresh() error {
+       fields := logrus.Fields{
+               "func": "confRefresh",
+       }
+       logger.WithFields(fields).Debug("Check configuration file")
        newConfs, err := confRead()
        if err != nil {
-               govpn.Printf(`[conf-parse-failed bind="%s" err="%s"]`, *bindAddr, err)
-               return err
+               return errors.Wrap(err, "confRead")
        }
        confs = *newConfs
-       idsCache.Update(newConfs)
+       logger.WithFields(fields).WithField("newConfs", len(confs)).Debug("idsCache.Update")
+       if err = idsCache.Update(newConfs); err != nil {
+               return errors.Wrap(err, "idsCache.Update")
+       }
+       logger.WithFields(fields).Debug("Done")
        return nil
 }
 
 func confInit() {
        idsCache = govpn.NewMACCache()
-       if err := confRefresh(); err != nil {
-               log.Fatalln(err)
+       err := confRefresh()
+       fields := logrus.Fields{"func": "confInit"}
+       if err != nil {
+               logger.WithError(err).WithFields(fields).Fatal("Couldn't perform initial configuration read")
        }
        go func() {
                for {
-                       time.Sleep(RefreshRate)
-                       confRefresh()
+                       time.Sleep(refreshRate)
+                       if err = confRefresh(); err != nil {
+                               logger.WithError(err).WithFields(fields).Error("Couldn't refresh configuration")
+                       }
                }
        }()
 }
index 2beb396f6958409a2cf0f27f9e938e6a76558d47..789acdcaa51dbfc9a655be394780bf66b018ff5a 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -22,13 +22,11 @@ package main
 import (
        "flag"
        "fmt"
-       "log"
-       "net"
-       "os"
-       "os/signal"
-       "time"
+
+       "github.com/Sirupsen/logrus"
 
        "cypherpunks.ru/govpn"
+       "cypherpunks.ru/govpn/server"
 )
 
 var (
@@ -41,9 +39,13 @@ var (
        syslog   = flag.Bool("syslog", false, "Enable logging to syslog")
        version  = flag.Bool("version", false, "Print version information")
        warranty = flag.Bool("warranty", false, "Print warranty information")
+       logLevel = flag.String("log_level", "warning", "Log level")
 )
 
 func main() {
+       var err error
+       fields := logrus.Fields{"func": "main"}
+
        flag.Parse()
        if *warranty {
                fmt.Println(govpn.Warranty)
@@ -53,101 +55,40 @@ func main() {
                fmt.Println(govpn.VersionGet())
                return
        }
-       timeout := time.Second * time.Duration(govpn.TimeoutDefault)
-       log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
-       log.Println(govpn.VersionGet())
 
-       confInit()
-       knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
+       logger, err = govpn.NewLogger(*logLevel, *syslog)
+       if err != nil {
+               logrus.WithFields(fields).WithError(err).Fatal("Couldn't initialize logging")
+       }
+       govpn.SetLogger(logger)
 
        if *egdPath != "" {
-               log.Println("Using", *egdPath, "EGD")
+               logger.WithField("egd_path", *egdPath).WithFields(fields).Debug("Init EGD")
                govpn.EGDInit(*egdPath)
        }
 
-       if *syslog {
-               govpn.SyslogEnable()
-       }
+       confInit()
 
-       switch *proto {
-       case "udp":
-               startUDP()
-       case "tcp":
-               startTCP()
-       case "all":
-               startUDP()
-               startTCP()
-       default:
-               log.Fatalln("Unknown protocol specified")
+       serverConfig := server.Configuration{
+               BindAddress:  *bindAddr,
+               ProxyAddress: *proxy,
+               Timeout:      govpn.TimeoutDefault,
+       }
+       if serverConfig.Protocol, err = govpn.NewProtocolFromString(*proto); err != nil {
+               logger.WithError(err).WithFields(fields).WithField("proto", *proto).Fatal("Invalid protocol")
+       }
+       if err = serverConfig.Validate(); err != nil {
+               logger.WithError(err).WithFields(fields).Fatal("Invalid configuration")
        }
 
-       termSignal := make(chan os.Signal, 1)
-       signal.Notify(termSignal, os.Interrupt, os.Kill)
-
-       hsHeartbeat := time.Tick(timeout)
-       go func() { <-hsHeartbeat }()
+       srv := server.NewServer(serverConfig, confs, idsCache, logger, govpn.CatchSignalShutdown())
 
        if *stats != "" {
-               log.Println("Stats are going to listen on", *stats)
-               statsPort, err := net.Listen("tcp", *stats)
-               if err != nil {
-                       log.Fatalln("Can not listen on stats port:", err)
-               }
-               go govpn.StatsProcessor(statsPort, &knownPeers)
+               go govpn.StatsProcessor(*stats, srv.KnownPeers())
        }
-       if *proxy != "" {
-               go proxyStart()
-       }
-       govpn.BothPrintf(`[started bind="%s"]`, *bindAddr)
-
-       var needsDeletion bool
-MainCycle:
-       for {
-               select {
-               case <-termSignal:
-                       govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr)
-                       for _, ps := range peers {
-                               govpn.ScriptCall(
-                                       confs[*ps.peer.ID].Down,
-                                       ps.tap.Name,
-                                       ps.peer.Addr,
-                               )
-                       }
-                       break MainCycle
-               case <-hsHeartbeat:
-                       now := time.Now()
-                       hsLock.Lock()
-                       for addr, hs := range handshakes {
-                               if hs.LastPing.Add(timeout).Before(now) {
-                                       govpn.Printf(`[handshake-delete bind="%s" addr="%s"]`, *bindAddr, addr)
-                                       hs.Zero()
-                                       delete(handshakes, addr)
-                               }
-                       }
-                       peersLock.Lock()
-                       peersByIDLock.Lock()
-                       kpLock.Lock()
-                       for addr, ps := range peers {
-                               ps.peer.BusyR.Lock()
-                               needsDeletion = ps.peer.LastPing.Add(timeout).Before(now)
-                               ps.peer.BusyR.Unlock()
-                               if needsDeletion {
-                                       govpn.Printf(`[peer-delete bind="%s" peer="%s"]`, *bindAddr, ps.peer)
-                                       delete(peers, addr)
-                                       delete(knownPeers, addr)
-                                       delete(peersByID, *ps.peer.ID)
-                                       go govpn.ScriptCall(
-                                               confs[*ps.peer.ID].Down,
-                                               ps.tap.Name,
-                                               ps.peer.Addr,
-                                       )
-                                       ps.terminator <- struct{}{}
-                               }
-                       }
-                       hsLock.Unlock()
-                       peersLock.Unlock()
-                       peersByIDLock.Unlock()
-                       kpLock.Unlock()
-               }
+
+       go srv.MainCycle()
+       if err = <-srv.Error; err != nil {
+               logger.WithError(err).Fatal("Fatal error")
        }
 }
diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go
deleted file mode 100644 (file)
index 87e3455..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
-       "net/http"
-
-       "cypherpunks.ru/govpn"
-)
-
-type proxyHandler struct{}
-
-func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-       conn, _, err := w.(http.Hijacker).Hijack()
-       if err != nil {
-               govpn.Printf(`[proxy-hijack-failed bind="%s" err="%s"]`, *bindAddr, err)
-               return
-       }
-       conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
-       go handleTCP(conn)
-}
-
-func proxyStart() {
-       govpn.BothPrintf(`[proxy-listen bind="%s" addr="%s"]`, *bindAddr, *proxy)
-       s := &http.Server{
-               Addr:    *proxy,
-               Handler: proxyHandler{},
-       }
-       govpn.BothPrintf(`[proxy-finished bind="%s" result="%s"]`, *bindAddr, s.ListenAndServe())
-}
diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go
deleted file mode 100644 (file)
index c36da3f..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
-       "bytes"
-       "log"
-       "net"
-       "time"
-
-       "cypherpunks.ru/govpn"
-)
-
-func startTCP() {
-       bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
-       if err != nil {
-               log.Fatalln("Can not resolve bind address:", err)
-       }
-       listener, err := net.ListenTCP("tcp", bind)
-       if err != nil {
-               log.Fatalln("Can not listen on TCP:", err)
-       }
-       govpn.BothPrintf(`[tcp-listen bind="%s"]`, *bindAddr)
-       go func() {
-               for {
-                       conn, err := listener.AcceptTCP()
-                       if err != nil {
-                               govpn.Printf(`[tcp-accept-failed bind="%s" err="%s"]`, *bindAddr, err)
-                               continue
-                       }
-                       go handleTCP(conn)
-               }
-       }()
-}
-
-func handleTCP(conn net.Conn) {
-       addr := conn.RemoteAddr().String()
-       buf := make([]byte, govpn.EnclessEnlargeSize+2*govpn.MTUMax)
-       var n int
-       var err error
-       var prev int
-       var hs *govpn.Handshake
-       var ps *PeerState
-       var peer *govpn.Peer
-       var tap *govpn.TAP
-       var conf *govpn.PeerConf
-       for {
-               if prev == len(buf) {
-                       break
-               }
-               conn.SetReadDeadline(time.Now().Add(time.Duration(govpn.TimeoutDefault) * time.Second))
-               n, err = conn.Read(buf[prev:])
-               if err != nil {
-                       // Either EOFed or timeouted
-                       break
-               }
-               prev += n
-               peerID := idsCache.Find(buf[:prev])
-               if peerID == nil {
-                       continue
-               }
-               if hs == nil {
-                       conf = confs[*peerID]
-                       if conf == nil {
-                               govpn.Printf(
-                                       `[conf-get-failed bind="%s" peer="%s"]`,
-                                       *bindAddr, peerID.String(),
-                               )
-                               break
-                       }
-                       hs = govpn.NewHandshake(addr, conn, conf)
-               }
-               peer = hs.Server(buf[:prev])
-               prev = 0
-               if peer == nil {
-                       continue
-               }
-               hs.Zero()
-               govpn.Printf(
-                       `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
-                       *bindAddr, addr, peerID.String(),
-               )
-               peersByIDLock.RLock()
-               addrPrev, exists := peersByID[*peer.ID]
-               peersByIDLock.RUnlock()
-               if exists {
-                       peersLock.Lock()
-                       peers[addrPrev].terminator <- struct{}{}
-                       tap = peers[addrPrev].tap
-                       ps = &PeerState{
-                               peer:       peer,
-                               tap:        tap,
-                               terminator: make(chan struct{}),
-                       }
-                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
-                       peersByIDLock.Lock()
-                       kpLock.Lock()
-                       delete(peers, addrPrev)
-                       delete(knownPeers, addrPrev)
-                       peers[addr] = ps
-                       knownPeers[addr] = &peer
-                       peersByID[*peer.ID] = addr
-                       peersLock.Unlock()
-                       peersByIDLock.Unlock()
-                       kpLock.Unlock()
-                       govpn.Printf(
-                               `[rehandshake-completed bind="%s" peer="%s"]`,
-                               *bindAddr, peerID.String(),
-                       )
-               } else {
-                       ifaceName, err := callUp(peer.ID, peer.Addr)
-                       if err != nil {
-                               peer = nil
-                               break
-                       }
-                       tap, err = govpn.TAPListen(ifaceName, peer.MTU)
-                       if err != nil {
-                               govpn.Printf(
-                                       `[tap-failed bind="%s" peer="%s" err="%s"]`,
-                                       *bindAddr, peerID.String(), err,
-                               )
-                               peer = nil
-                               break
-                       }
-                       ps = &PeerState{
-                               peer:       peer,
-                               tap:        tap,
-                               terminator: make(chan struct{}, 1),
-                       }
-                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
-                       peersLock.Lock()
-                       peersByIDLock.Lock()
-                       kpLock.Lock()
-                       peers[addr] = ps
-                       peersByID[*peer.ID] = addr
-                       knownPeers[addr] = &peer
-                       peersLock.Unlock()
-                       peersByIDLock.Unlock()
-                       kpLock.Unlock()
-                       govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peerID.String())
-               }
-               break
-       }
-       if hs != nil {
-               hs.Zero()
-       }
-       if peer == nil {
-               return
-       }
-
-       prev = 0
-       var i int
-       for {
-               if prev == len(buf) {
-                       break
-               }
-               conn.SetReadDeadline(time.Now().Add(conf.Timeout))
-               n, err = conn.Read(buf[prev:])
-               if err != nil {
-                       // Either EOFed or timeouted
-                       break
-               }
-               prev += n
-       CheckMore:
-               if prev < govpn.MinPktLength {
-                       continue
-               }
-               i = bytes.Index(buf[:prev], peer.NonceExpect)
-               if i == -1 {
-                       continue
-               }
-               if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
-                       govpn.Printf(
-                               `[packet-unauthenticated bind="%s" addr="%s" peer="%s"]`,
-                               *bindAddr, addr, peer.ID.String(),
-                       )
-                       break
-               }
-               copy(buf, buf[i+govpn.NonceSize:prev])
-               prev = prev - i - govpn.NonceSize
-               goto CheckMore
-       }
-       peer.Zero()
-}
diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go
deleted file mode 100644 (file)
index cdcfb56..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
-       "log"
-       "net"
-
-       "cypherpunks.ru/govpn"
-)
-
-type UDPSender struct {
-       conn *net.UDPConn
-       addr *net.UDPAddr
-}
-
-func (c UDPSender) Write(data []byte) (int, error) {
-       return c.conn.WriteToUDP(data, c.addr)
-}
-
-var (
-       // Buffers for UDP parallel processing
-       udpBufs = make(chan []byte, 1<<8)
-)
-
-func startUDP() {
-       bind, err := net.ResolveUDPAddr("udp", *bindAddr)
-       if err != nil {
-               log.Fatalln("Can not resolve bind address:", err)
-       }
-       conn, err := net.ListenUDP("udp", bind)
-       if err != nil {
-               log.Fatalln("Can not listen on UDP:", err)
-       }
-       govpn.BothPrintf(`[udp-listen bind="%s"]`, *bindAddr)
-
-       udpBufs <- make([]byte, govpn.MTUMax)
-       go func() {
-               var buf []byte
-               var raddr *net.UDPAddr
-               var addr string
-               var n int
-               var err error
-               var ps *PeerState
-               var hs *govpn.Handshake
-               var addrPrev string
-               var exists bool
-               var peerID *govpn.PeerID
-               var conf *govpn.PeerConf
-               for {
-                       buf = <-udpBufs
-                       n, raddr, err = conn.ReadFromUDP(buf)
-                       if err != nil {
-                               govpn.Printf(`[receive-failed bind="%s" err="%s"]`, *bindAddr, err)
-                               break
-                       }
-                       addr = raddr.String()
-
-                       peersLock.RLock()
-                       ps, exists = peers[addr]
-                       peersLock.RUnlock()
-                       if exists {
-                               go func(peer *govpn.Peer, tap *govpn.TAP, buf []byte, n int) {
-                                       peer.PktProcess(buf[:n], tap, true)
-                                       udpBufs <- buf
-                               }(ps.peer, ps.tap, buf, n)
-                               continue
-                       }
-
-                       hsLock.RLock()
-                       hs, exists = handshakes[addr]
-                       hsLock.RUnlock()
-                       if !exists {
-                               peerID = idsCache.Find(buf[:n])
-                               if peerID == nil {
-                                       govpn.Printf(`[identity-unknown bind="%s" addr="%s"]`, *bindAddr, addr)
-                                       udpBufs <- buf
-                                       continue
-                               }
-                               conf = confs[*peerID]
-                               if conf == nil {
-                                       govpn.Printf(
-                                               `[conf-get-failed bind="%s" peer="%s"]`,
-                                               *bindAddr, peerID.String(),
-                                       )
-                                       udpBufs <- buf
-                                       continue
-                               }
-                               hs := govpn.NewHandshake(
-                                       addr,
-                                       UDPSender{conn: conn, addr: raddr},
-                                       conf,
-                               )
-                               hs.Server(buf[:n])
-                               udpBufs <- buf
-                               hsLock.Lock()
-                               handshakes[addr] = hs
-                               hsLock.Unlock()
-                               continue
-                       }
-
-                       peer := hs.Server(buf[:n])
-                       if peer == nil {
-                               udpBufs <- buf
-                               continue
-                       }
-                       govpn.Printf(
-                               `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
-                               *bindAddr, addr, peerID.String(),
-                       )
-                       hs.Zero()
-                       hsLock.Lock()
-                       delete(handshakes, addr)
-                       hsLock.Unlock()
-
-                       go func() {
-                               udpBufs <- make([]byte, govpn.MTUMax)
-                               udpBufs <- make([]byte, govpn.MTUMax)
-                       }()
-                       peersByIDLock.RLock()
-                       addrPrev, exists = peersByID[*peer.ID]
-                       peersByIDLock.RUnlock()
-                       if exists {
-                               peersLock.Lock()
-                               peers[addrPrev].terminator <- struct{}{}
-                               psNew := &PeerState{
-                                       peer:       peer,
-                                       tap:        peers[addrPrev].tap,
-                                       terminator: make(chan struct{}),
-                               }
-                               go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
-                                       govpn.PeerTapProcessor(peer, tap, terminator)
-                                       <-udpBufs
-                                       <-udpBufs
-                               }(psNew.peer, psNew.tap, psNew.terminator)
-                               peersByIDLock.Lock()
-                               kpLock.Lock()
-                               delete(peers, addrPrev)
-                               delete(knownPeers, addrPrev)
-                               peers[addr] = psNew
-                               knownPeers[addr] = &peer
-                               peersByID[*peer.ID] = addr
-                               peersLock.Unlock()
-                               peersByIDLock.Unlock()
-                               kpLock.Unlock()
-                               govpn.Printf(
-                                       `[rehandshake-completed bind="%s" peer="%s"]`,
-                                       *bindAddr, peer.ID.String(),
-                               )
-                       } else {
-                               go func(addr string, peer *govpn.Peer) {
-                                       ifaceName, err := callUp(peer.ID, peer.Addr)
-                                       if err != nil {
-                                               return
-                                       }
-                                       tap, err := govpn.TAPListen(ifaceName, peer.MTU)
-                                       if err != nil {
-                                               govpn.Printf(
-                                                       `[tap-failed bind="%s" peer="%s" err="%s"]`,
-                                                       *bindAddr, peer.ID.String(), err,
-                                               )
-                                               return
-                                       }
-                                       psNew := &PeerState{
-                                               peer:       peer,
-                                               tap:        tap,
-                                               terminator: make(chan struct{}),
-                                       }
-                                       go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
-                                               govpn.PeerTapProcessor(peer, tap, terminator)
-                                               <-udpBufs
-                                               <-udpBufs
-                                       }(psNew.peer, psNew.tap, psNew.terminator)
-                                       peersLock.Lock()
-                                       peersByIDLock.Lock()
-                                       kpLock.Lock()
-                                       peers[addr] = psNew
-                                       knownPeers[addr] = &peer
-                                       peersByID[*peer.ID] = addr
-                                       peersLock.Unlock()
-                                       peersByIDLock.Unlock()
-                                       kpLock.Unlock()
-                                       govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peer.ID.String())
-                               }(addr, peer)
-                       }
-                       udpBufs <- buf
-               }
-       }()
-}
diff --git a/src/cypherpunks.ru/govpn/server/common.go b/src/cypherpunks.ru/govpn/server/common.go
new file mode 100644 (file)
index 0000000..cb2d679
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+       "github.com/pkg/errors"
+
+       "cypherpunks.ru/govpn"
+)
+
+const logFuncPrefix = "govpn/server."
+
+var (
+       errMisconfiguredTap = errors.New("No PreUp and no Iface, can't create interface")
+       errPreUpNoTap       = errors.New("PreUp didn't returned an interface, and Iface is unset")
+)
+
+func (s *Server) callUp(peer *govpn.Peer, proto govpn.Protocol) (*govpn.TAP, error) {
+       var (
+               tap           *govpn.TAP
+               conf          = s.confs.Get(*peer.ID)
+               err           error
+               isConfigIface = len(conf.Iface) != 0
+               fields        = s.LogFields()
+       )
+       fields["func"] = logFuncPrefix + "Server.callUp"
+
+       if !isConfigIface && conf.PreUp == nil {
+               return nil, errors.Wrapf(errMisconfiguredTap, "interface:%q, PreUp:%q", conf.Iface, conf.PreUp)
+       }
+
+       if conf.PreUp != nil {
+               s.logger.WithFields(fields).Debug("PreUp defined, execute it")
+               tap, err = conf.PreUp(govpn.PeerContext{
+                       RemoteAddress: peer.Addr,
+                       Protocol:      proto,
+                       Config:        *conf,
+               })
+               if err != nil {
+                       return nil, errors.Wrap(err, "conf.PreUp")
+               }
+               s.logger.WithFields(fields).WithField("tap", tap).Debug("PreUp finished")
+       } else {
+               s.logger.WithFields(fields).Debug("No PreUp defined, skip")
+       }
+
+       if tap == nil {
+               s.logger.WithFields(fields).Debug("PreUp didn't returned an interface, create one")
+               if !isConfigIface {
+                       return nil, errors.Wrapf(errPreUpNoTap, "interface:%q tap:%q", conf.Iface, tap)
+               }
+
+               if tap, err = govpn.TAPListen(conf.Iface, peer.MTU); err != nil {
+                       return nil, errors.Wrap(err, "govpn.TAPListen")
+               }
+       }
+
+       conf.Iface = tap.Name
+
+       if conf.Up == nil {
+               s.logger.WithFields(fields).Debug("Got interface, no Up")
+               return tap, nil
+       }
+       s.logger.WithFields(fields).Debug("Got interface, execute Up")
+
+       err = conf.Up(govpn.PeerContext{
+               RemoteAddress: peer.Addr,
+               Protocol:      proto,
+               Config:        *conf,
+       })
+       if err != nil {
+               return nil, errors.Wrap(err, "conf.Up")
+       }
+       s.logger.WithFields(fields).Debug("Got interface, Up executed")
+       return tap, nil
+}
+
+func (s *Server) callDown(ps *PeerState) error {
+       fields := s.LogFields()
+       fields["func"] = logFuncPrefix + "Server.callDown"
+
+       conf := s.confs.Get(*ps.peer.ID)
+       if conf == nil {
+               s.logger.WithFields(fields).Error("Couldn't get configuration")
+               return nil
+       }
+       if conf.Down == nil {
+               s.logger.WithFields(fields).Debug("No Down, skip")
+               return nil
+       }
+       s.logger.WithFields(fields).Debug("Execute Down")
+       err := conf.Down(govpn.PeerContext{
+               RemoteAddress: ps.peer.Addr,
+               Config:        *conf,
+               Protocol:      ps.peer.Protocol,
+       })
+       s.logger.WithFields(fields).Debug("Down executed")
+       return errors.Wrap(err, "peer.Down")
+}
diff --git a/src/cypherpunks.ru/govpn/server/proxy.go b/src/cypherpunks.ru/govpn/server/proxy.go
new file mode 100644 (file)
index 0000000..ad0f75d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+       "net/http"
+
+       "github.com/Sirupsen/logrus"
+)
+
+type proxyHandler struct {
+       goVpnServer *Server
+}
+
+func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       conn, _, err := w.(http.Hijacker).Hijack()
+       if err != nil {
+               p.goVpnServer.logger.WithError(err).WithFields(
+                       logrus.Fields{
+                               "func":    logFuncPrefix + "proxyHandler.ServeHTTP",
+                               "address": p.goVpnServer.configuration.BindAddress,
+                       },
+               ).Error("Proxy hijack failed")
+               return
+       }
+       conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
+       go p.goVpnServer.handleTCP(conn)
+}
+
+func (s *Server) proxyStart() {
+       fields := logrus.Fields{
+               "func":    logFuncPrefix + "Server.proxyStart",
+               "address": s.configuration.BindAddress,
+               "proxy":   s.configuration.ProxyAddress,
+       }
+       s.logger.WithFields(fields).Info("Proxy Listen")
+       httpServer := &http.Server{
+               Addr: s.configuration.ProxyAddress,
+               Handler: proxyHandler{
+                       goVpnServer: s,
+               },
+       }
+       if err := httpServer.ListenAndServe(); err != nil {
+               s.logger.WithFields(fields).WithError(err).Error("Proxy failed")
+               return
+       }
+       s.logger.WithFields(fields).Info("Proxy finished")
+}
diff --git a/src/cypherpunks.ru/govpn/server/server.go b/src/cypherpunks.ru/govpn/server/server.go
new file mode 100644 (file)
index 0000000..9dfa032
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+       "sync"
+       "time"
+
+       "github.com/Sirupsen/logrus"
+       "github.com/pkg/errors"
+
+       "cypherpunks.ru/govpn"
+)
+
+// PeerConfigurer is used by a GoVPN server to figure the configuration of a single peer
+type PeerConfigurer interface {
+       Get(govpn.PeerID) *govpn.PeerConf
+}
+
+// MACPeerFinder is used by GoVPN server to figure the PeerID from handshake data
+type MACPeerFinder interface {
+       Find([]byte) (*govpn.PeerID, error)
+}
+
+// PeerState hold server side state of a single connecting/connected peer
+type PeerState struct {
+       peer       *govpn.Peer
+       terminator chan struct{}
+       tap        *govpn.TAP
+}
+
+// Configuration hold GoVPN server configuration
+type Configuration struct {
+       BindAddress  string
+       ProxyAddress string
+       Protocol     govpn.Protocol
+       Timeout      time.Duration
+}
+
+// Validate return an error if a configuration is invalid
+func (c *Configuration) Validate() error {
+       if len(c.BindAddress) == 0 {
+               return errors.New("Missing BindAddress")
+       }
+       return nil
+}
+
+// LogFields return a logrus compatible logging context
+func (c *Configuration) LogFields() logrus.Fields {
+       const prefix = "srv_conf_"
+       f := logrus.Fields{
+               prefix + "bind":     c.BindAddress,
+               prefix + "protocol": c.Protocol.String(),
+               prefix + "timeout":  c.Timeout.String(),
+       }
+       if len(c.ProxyAddress) > 0 {
+               f[prefix+"proxy"] = c.ProxyAddress
+       }
+       return f
+}
+
+// Server is a GoVPN server instance
+type Server struct {
+       configuration Configuration
+       termSignal    chan interface{}
+
+       idsCache MACPeerFinder
+       confs    PeerConfigurer
+
+       handshakes map[string]*govpn.Handshake
+       hsLock     sync.RWMutex
+
+       peers     map[string]*PeerState
+       peersLock sync.RWMutex
+
+       peersByID     map[govpn.PeerID]string
+       peersByIDLock sync.RWMutex
+
+       knownPeers govpn.KnownPeers
+       kpLock     sync.RWMutex
+
+       logger *logrus.Logger
+
+       // Error channel receives any kind of routine errors
+       Error chan error
+}
+
+// LogFields return a logrus compatible logging context
+func (s *Server) LogFields() logrus.Fields {
+       const prefix = "srv_"
+       return logrus.Fields{
+               prefix + "hs":    len(s.handshakes),
+               prefix + "peers": len(s.peers),
+               prefix + "known": len(s.knownPeers),
+       }
+}
+
+// KnownPeers return GoVPN peers.
+// used to get client statistics.
+func (s *Server) KnownPeers() *govpn.KnownPeers {
+       return &s.knownPeers
+}
+
+// NewServer return a configured GoVPN server, to listen network connection MainCycle must be executed
+func NewServer(serverConf Configuration, peerConfs PeerConfigurer, idsCache MACPeerFinder, logger *logrus.Logger, termSignal chan interface{}) *Server {
+       govpn.SetLogger(logger)
+       return &Server{
+               configuration: serverConf,
+               confs:         peerConfs,
+               termSignal:    termSignal,
+               idsCache:      idsCache,
+               handshakes:    make(map[string]*govpn.Handshake),
+               peers:         make(map[string]*PeerState),
+               peersByID:     make(map[govpn.PeerID]string),
+               knownPeers:    govpn.KnownPeers(make(map[string]**govpn.Peer)),
+               Error:         make(chan error, 1),
+               logger:        logger,
+       }
+}
+
+// MainCycle main loop that handle connecting/connected client
+func (s *Server) MainCycle() {
+       switch s.configuration.Protocol {
+       case govpn.ProtocolUDP:
+               s.startUDP()
+       case govpn.ProtocolTCP:
+               s.startTCP()
+       case govpn.ProtocolALL:
+               s.startUDP()
+               s.startTCP()
+       default:
+               s.Error <- errors.New("Unknown protocol specified")
+               return
+       }
+
+       if len(s.configuration.ProxyAddress) > 0 {
+               go s.proxyStart()
+       }
+       fields := logrus.Fields{"func": logFuncPrefix + "Server.MainCycle"}
+
+       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Starting...")
+
+       var needsDeletion bool
+       var err error
+       hsHeartbeat := time.Tick(s.configuration.Timeout)
+       go func() { <-hsHeartbeat }()
+
+MainCycle:
+       for {
+               select {
+               case <-s.termSignal:
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Terminating")
+                       for _, ps := range s.peers {
+                               if err = s.callDown(ps); err != nil {
+                                       s.logger.WithFields(fields).WithError(err).WithFields(ps.peer.LogFields()).Error("Failed to run callDown")
+                               }
+                               if err = ps.tap.Close(); err != nil {
+                                       logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't close TAP")
+                               }
+                       }
+                       // empty value signals that everything is fine
+                       s.Error <- nil
+                       break MainCycle
+               case <-hsHeartbeat:
+                       logrus.WithFields(fields).Debug("Heartbeat")
+                       now := time.Now()
+                       s.hsLock.Lock()
+                       for addr, hs := range s.handshakes {
+                               if hs.LastPing.Add(s.configuration.Timeout).Before(now) {
+                                       logrus.WithFields(fields).WithFields(hs.LogFields()).Debug("handshake is expired, delete")
+                                       hs.Zero()
+                                       delete(s.handshakes, addr)
+                               }
+                       }
+                       s.peersLock.Lock()
+                       s.peersByIDLock.Lock()
+                       s.kpLock.Lock()
+                       for addr, ps := range s.peers {
+                               ps.peer.BusyR.Lock()
+                               needsDeletion = ps.peer.LastPing.Add(s.configuration.Timeout).Before(now)
+                               ps.peer.BusyR.Unlock()
+                               if needsDeletion {
+                                       logrus.WithFields(fields).WithFields(ps.peer.LogFields()).Info("Delete peer")
+                                       delete(s.peers, addr)
+                                       delete(s.knownPeers, addr)
+                                       delete(s.peersByID, *ps.peer.ID)
+                                       if err = s.callDown(ps); err != nil {
+                                               logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't execute callDown")
+                                       }
+                                       if err = ps.tap.Close(); err != nil {
+                                               logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't close TAP")
+                                       }
+                                       ps.terminator <- struct{}{}
+                               }
+                       }
+                       s.hsLock.Unlock()
+                       s.peersLock.Unlock()
+                       s.peersByIDLock.Unlock()
+                       s.kpLock.Unlock()
+               }
+       }
+}
diff --git a/src/cypherpunks.ru/govpn/server/tcp.go b/src/cypherpunks.ru/govpn/server/tcp.go
new file mode 100644 (file)
index 0000000..34a78fc
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+       "bytes"
+       "net"
+       "time"
+
+       "github.com/Sirupsen/logrus"
+       "github.com/pkg/errors"
+
+       "cypherpunks.ru/govpn"
+)
+
+func (s *Server) startTCP() {
+       bind, err := net.ResolveTCPAddr("tcp", s.configuration.BindAddress)
+       if err != nil {
+               s.Error <- errors.Wrap(err, "net.ResolveTCPAddr")
+               return
+       }
+       listener, err := net.ListenTCP("tcp", bind)
+       if err != nil {
+               s.Error <- errors.Wrapf(err, "net.ListenTCP %q", bind.String())
+               return
+       }
+       fields := logrus.Fields{
+               "func": logFuncPrefix + "Server.startTCP",
+               "bind": bind.String(),
+       }
+       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Listen")
+       go func() {
+               for {
+                       conn, err := listener.AcceptTCP()
+                       if err != nil {
+                               s.logger.WithError(err).WithFields(fields).WithFields(s.LogFields()).Error("Failed to accept TCP connection")
+                               continue
+                       }
+                       go s.handleTCP(conn)
+               }
+       }()
+}
+
+func (s *Server) handleTCP(conn net.Conn) {
+       addr := conn.RemoteAddr().String()
+       buf := make([]byte, govpn.EnclessEnlargeSize+2*govpn.MTUMax)
+       var n int
+       var err error
+       var prev int
+       var hs *govpn.Handshake
+       var ps *PeerState
+       var peer *govpn.Peer
+       var deadLine time.Time
+       var tap *govpn.TAP
+       var conf *govpn.PeerConf
+       fields := logrus.Fields{
+               "func":   logFuncPrefix + "Server.handleTCP",
+               "remote": addr,
+       }
+       for {
+               if prev == len(buf) {
+                       // TODO log why
+                       break
+               }
+
+               deadLine = time.Now().Add(govpn.TimeoutDefault)
+               if err = conn.SetReadDeadline(deadLine); err != nil {
+                       s.Error <- errors.Wrapf(err, "conn.SetReadDeadline %s", deadLine.String())
+                       return
+               }
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Can't read connection: either EOFed or timeouted")
+                       break
+               }
+               prev += n
+               peerID, err := s.idsCache.Find(buf[:prev])
+               if err != nil {
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Couldn't lookup for peer in ids")
+                       continue
+               }
+               if peerID == nil {
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).Debug("Couldn't find peer")
+                       continue
+               }
+               if hs == nil {
+                       conf = s.confs.Get(*peerID)
+                       if conf == nil {
+                               s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Error("Configuration get failed")
+                               break
+                       }
+                       hs = govpn.NewHandshake(addr, conn, conf)
+               }
+               peer, err = hs.Server(buf[:prev])
+               if err != nil {
+                       s.logger.WithFields(fields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer")
+                       continue
+               }
+               prev = 0
+               if peer == nil {
+                       continue
+               }
+
+               s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Handshake completed")
+
+               hs.Zero()
+               s.peersByIDLock.RLock()
+               addrPrev, exists := s.peersByID[*peer.ID]
+               s.peersByIDLock.RUnlock()
+
+               if exists {
+                       s.peersLock.Lock()
+                       s.peers[addrPrev].terminator <- struct{}{}
+                       tap = s.peers[addrPrev].tap
+                       ps = &PeerState{
+                               peer:       peer,
+                               tap:        tap,
+                               terminator: make(chan struct{}),
+                       }
+                       peer.Protocol = govpn.ProtocolTCP
+                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
+                       s.peersByIDLock.Lock()
+                       s.kpLock.Lock()
+                       delete(s.peers, addrPrev)
+                       delete(s.knownPeers, addrPrev)
+                       s.peers[addr] = ps
+                       s.knownPeers[addr] = &peer
+                       s.peersByID[*peer.ID] = addr
+                       s.peersLock.Unlock()
+                       s.peersByIDLock.Unlock()
+                       s.kpLock.Unlock()
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Debug("Rehandshake completed")
+               } else {
+                       tap, err = s.callUp(peer, govpn.ProtocolTCP)
+                       if err != nil {
+                               s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).WithError(err).Error("TAP failed")
+                               peer = nil
+                               break
+                       }
+                       ps = &PeerState{
+                               peer:       peer,
+                               tap:        tap,
+                               terminator: make(chan struct{}, 1),
+                       }
+                       peer.Protocol = govpn.ProtocolTCP
+                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
+                       s.peersLock.Lock()
+                       s.peersByIDLock.Lock()
+                       s.kpLock.Lock()
+                       s.peers[addr] = ps
+                       s.peersByID[*peer.ID] = addr
+                       s.knownPeers[addr] = &peer
+                       s.peersLock.Unlock()
+                       s.peersByIDLock.Unlock()
+                       s.kpLock.Unlock()
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Peer created")
+               }
+               break
+       }
+       if hs != nil {
+               hs.Zero()
+       }
+       if peer == nil {
+               return
+       }
+
+       prev = 0
+       var i int
+       for {
+               if prev == len(buf) {
+                       break
+               }
+               deadLine = time.Now().Add(conf.Timeout)
+               if err = conn.SetReadDeadline(deadLine); err != nil {
+                       s.Error <- errors.Wrapf(err, "conn.SetReadDeadline %s", deadLine.String())
+                       return
+               }
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Can't read connection: either EOFed or timeouted")
+                       break
+               }
+               prev += n
+       CheckMore:
+               if prev < govpn.MinPktLength {
+                       continue
+               }
+               i = bytes.Index(buf[:prev], peer.NonceExpect)
+               if i == -1 {
+                       continue
+               }
+               if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Warn("Packet unauthenticated")
+                       break
+               }
+               copy(buf, buf[i+govpn.NonceSize:prev])
+               prev = prev - i - govpn.NonceSize
+               goto CheckMore
+       }
+       peer.Zero()
+}
diff --git a/src/cypherpunks.ru/govpn/server/udp.go b/src/cypherpunks.ru/govpn/server/udp.go
new file mode 100644 (file)
index 0000000..cd8c2fc
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+       "net"
+
+       "github.com/Sirupsen/logrus"
+       "github.com/pkg/errors"
+
+       "cypherpunks.ru/govpn"
+)
+
+type udpSender struct {
+       conn *net.UDPConn
+       addr *net.UDPAddr
+}
+
+func (c udpSender) Write(data []byte) (int, error) {
+       return c.conn.WriteToUDP(data, c.addr)
+}
+
+// TODO move to udpSender (?)
+// buffers for UDP parallel processing
+var udpBufs = make(chan []byte, 1<<8)
+
+func (s *Server) startUDP() {
+       bind, err := net.ResolveUDPAddr("udp", s.configuration.BindAddress)
+       if err != nil {
+               s.Error <- errors.Wrap(err, "net.ResolveUDPAddr")
+               return
+       }
+       conn, err := net.ListenUDP("udp", bind)
+       if err != nil {
+               s.Error <- errors.Wrapf(err, "net.ListenUDP %q", bind.String())
+               return
+       }
+
+       fields := logrus.Fields{
+               "func": logFuncPrefix + "Server.startUDP",
+               "bind": bind.String(),
+       }
+       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Listen")
+       udpBufs <- make([]byte, govpn.MTUMax)
+       go func() {
+               var buf []byte
+               var raddr *net.UDPAddr
+               var addr string
+               var n int
+               var err error
+               var ps *PeerState
+               var hs *govpn.Handshake
+               var addrPrev string
+               var exists bool
+               var peerID *govpn.PeerID
+               var conf *govpn.PeerConf
+               for {
+                       s.logger.WithFields(fields).Debug("Wait for UDP buffer")
+                       buf = <-udpBufs
+                       n, raddr, err = conn.ReadFromUDP(buf)
+                       if err != nil {
+                               s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Receive failure")
+                               break
+                       }
+                       addr = raddr.String()
+                       loopFields := logrus.Fields{"addr": addr}
+
+                       s.logger.WithFields(fields).WithFields(loopFields).Debug("Got UDP buffer, check if peer exists")
+                       s.peersLock.RLock()
+                       ps, exists = s.peers[addr]
+                       s.peersLock.RUnlock()
+                       if exists {
+                               s.logger.WithFields(fields).WithFields(loopFields).Debug("Already known peer, PktProcess")
+                               go func(peer *govpn.Peer, tap *govpn.TAP, buf []byte, n int) {
+                                       peer.PktProcess(buf[:n], tap, true)
+                                       udpBufs <- buf
+                               }(ps.peer, ps.tap, buf, n)
+                               continue
+                       }
+
+                       logrus.WithFields(fields).WithFields(loopFields).Debug("New peer")
+                       s.hsLock.RLock()
+                       hs, exists = s.handshakes[addr]
+                       s.hsLock.RUnlock()
+                       if !exists {
+                               logrus.WithFields(fields).WithFields(loopFields).Debug("No handshake yet, try to figure peer ID")
+                               peerID, err = s.idsCache.Find(buf[:n])
+                               if err != nil {
+                                       s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).WithError(err).Debug("Couldn't lookup for peer in ids")
+                                       udpBufs <- buf
+                                       continue
+                               }
+                               if peerID == nil {
+                                       s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).Debug("Identity unknown")
+                                       udpBufs <- buf
+                                       continue
+                               }
+
+                               loopFields["peer_id"] = peerID.String()
+                               s.logger.WithFields(fields).WithFields(loopFields).Debug("Found peer ID")
+                               conf = s.confs.Get(*peerID)
+                               if conf == nil {
+                                       s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Error("Peer try to connect, but not configured")
+                                       udpBufs <- buf
+                                       continue
+                               }
+
+                               s.logger.WithFields(loopFields).WithFields(fields).Debug("Got configuration, perform handshake")
+                               hs = govpn.NewHandshake(
+                                       addr,
+                                       udpSender{conn: conn, addr: raddr},
+                                       conf,
+                               )
+                               _, err := hs.Server(buf[:n])
+                               udpBufs <- buf
+                               if err != nil {
+                                       s.logger.WithFields(loopFields).WithFields(fields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer: handshake failed")
+                                       continue
+                               }
+                               s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).Info("Hashshake started, continue next packet")
+
+                               s.hsLock.Lock()
+                               s.handshakes[addr] = hs
+                               s.hsLock.Unlock()
+                               continue
+                       }
+
+                       logrus.WithFields(fields).WithFields(loopFields).Debug("Already go handshake, finish it")
+                       peer, err := hs.Server(buf[:n])
+                       if err != nil {
+                               s.logger.WithFields(fields).WithFields(loopFields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer: handshake failed")
+                               udpBufs <- buf
+                               continue
+                       }
+                       if peer == nil {
+                               s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).Error("Couldn't continue handshake")
+                               udpBufs <- buf
+                               continue
+                       }
+
+                       s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(loopFields).WithFields(peer.LogFields()).Info("Handshake completed")
+
+                       hs.Zero()
+                       s.hsLock.Lock()
+                       delete(s.handshakes, addr)
+                       s.hsLock.Unlock()
+
+                       go func() {
+                               udpBufs <- make([]byte, govpn.MTUMax)
+                               udpBufs <- make([]byte, govpn.MTUMax)
+                       }()
+                       s.peersByIDLock.RLock()
+                       addrPrev, exists = s.peersByID[*peer.ID]
+                       s.peersByIDLock.RUnlock()
+
+                       if exists {
+                               s.logger.WithFields(fields).WithFields(loopFields).Debug("Peer already exists")
+                               s.peersLock.Lock()
+                               s.peers[addrPrev].terminator <- struct{}{}
+                               psNew := &PeerState{
+                                       peer:       peer,
+                                       tap:        s.peers[addrPrev].tap,
+                                       terminator: make(chan struct{}),
+                               }
+                               peer.Protocol = govpn.ProtocolUDP
+
+                               go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
+                                       govpn.PeerTapProcessor(peer, tap, terminator)
+                                       <-udpBufs
+                                       <-udpBufs
+                               }(psNew.peer, psNew.tap, psNew.terminator)
+
+                               s.peersByIDLock.Lock()
+                               s.kpLock.Lock()
+                               delete(s.peers, addrPrev)
+                               delete(s.knownPeers, addrPrev)
+                               s.peers[addr] = psNew
+                               s.knownPeers[addr] = &peer
+                               s.peersByID[*peer.ID] = addr
+                               s.peersLock.Unlock()
+                               s.peersByIDLock.Unlock()
+                               s.kpLock.Unlock()
+
+                               s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Debug("Rehandshake completed")
+                       } else {
+                               go func(addr string, peer *govpn.Peer) {
+                                       s.logger.WithFields(fields).WithFields(loopFields).Debug("Peer do not already exists")
+                                       tap, err := s.callUp(peer, govpn.ProtocolUDP)
+                                       if err != nil {
+                                               s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).WithError(err).Error("TAP failed")
+                                               return
+                                       }
+                                       psNew := &PeerState{
+                                               peer:       peer,
+                                               tap:        tap,
+                                               terminator: make(chan struct{}),
+                                       }
+                                       peer.Protocol = govpn.ProtocolUDP
+                                       go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
+                                               govpn.PeerTapProcessor(peer, tap, terminator)
+                                               <-udpBufs
+                                               <-udpBufs
+                                       }(psNew.peer, psNew.tap, psNew.terminator)
+                                       s.peersLock.Lock()
+                                       s.peersByIDLock.Lock()
+                                       s.kpLock.Lock()
+                                       s.peers[addr] = psNew
+                                       s.knownPeers[addr] = &peer
+                                       s.peersByID[*peer.ID] = addr
+                                       s.peersLock.Unlock()
+                                       s.peersByIDLock.Unlock()
+                                       s.kpLock.Unlock()
+                                       s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Peer initialized")
+                               }(addr, peer)
+                       }
+                       udpBufs <- buf
+               }
+       }()
+}