]> Cypherpunks.ru repositories - govpn.git/blobdiff - cmd/govpn-server/main.go
Raise copyright years
[govpn.git] / cmd / govpn-server / main.go
index cfcc45f4dee61a628cc1578a9af5d0f356b55b42..1161efde64f0424a877dd6143e421a405ef366af 100644 (file)
@@ -1,11 +1,10 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2020 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.
+the Free Software Foundation, version 3 of the License.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,86 +15,69 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Simple secure free software virtual private network daemon.
+// Simple secure, DPI/censorship-resistant free software VPN daemon.
 package main
 
 import (
-       "bytes"
        "flag"
+       "fmt"
        "log"
        "net"
        "os"
        "os/signal"
-       "path"
        "time"
 
-       "govpn"
+       "go.cypherpunks.ru/govpn/v7"
 )
 
 var (
-       bindAddr  = flag.String("bind", "[::]:1194", "Bind to address")
-       peersPath = flag.String("peers", "peers", "Path to peers keys directory")
-       stats     = flag.String("stats", "", "Enable stats retrieving on host:port")
-       mtu       = flag.Int("mtu", 1500, "MTU")
-       nonceDiff = flag.Int("noncediff", 1, "Allow nonce difference")
-       timeoutP  = flag.Int("timeout", 60, "Timeout seconds")
+       bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
+       proto    = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
+       confPath = flag.String("conf", "peers.yaml", "Path to configuration YAML")
+       stats    = flag.String("stats", "", "Enable stats retrieving on host:port")
+       proxy    = flag.String("proxy", "", "Enable HTTP proxy on host:port")
+       egdPath  = flag.String("egd", "", "Optional path to EGD socket")
+       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")
 )
 
-type PeerReadyEvent struct {
-       peer  *govpn.Peer
-       iface string
-}
-
-type PeerState struct {
-       peer      *govpn.Peer
-       tap       *govpn.TAP
-       sink      chan []byte
-       ready     chan struct{}
-       terminate chan struct{}
-}
-
-func NewPeerState(peer *govpn.Peer, iface string) *PeerState {
-       tap, sink, ready, terminate, err := govpn.TAPListen(iface)
-       if err != nil {
-               log.Println("Unable to create Eth", err)
-               return nil
-       }
-       state := PeerState{
-               peer:      peer,
-               tap:       tap,
-               sink:      sink,
-               ready:     ready,
-               terminate: terminate,
-       }
-       return &state
-}
-
-type EthEvent struct {
-       peer  *govpn.Peer
-       data  []byte
-       ready chan struct{}
-}
-
 func main() {
        flag.Parse()
-       timeout := time.Second * time.Duration(*timeoutP)
-       var err error
+       if *warranty {
+               fmt.Println(govpn.Warranty)
+               return
+       }
+       if *version {
+               fmt.Println(govpn.VersionGet())
+               return
+       }
+       timeout := time.Second * time.Duration(govpn.TimeoutDefault)
        log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
+       log.Println(govpn.VersionGet())
 
-       govpn.MTU = *mtu
-       govpn.Timeout = *timeoutP
-       govpn.Noncediff = *nonceDiff
-       govpn.PeersInit(*peersPath)
+       confInit()
 
-       bind, err := net.ResolveUDPAddr("udp", *bindAddr)
-       if err != nil {
-               panic(err)
+       if *egdPath != "" {
+               log.Println("Using", *egdPath, "EGD")
+               govpn.EGDInit(*egdPath)
        }
-       conn, err := net.ListenUDP("udp", bind)
-       if err != nil {
-               panic(err)
+
+       if *syslog {
+               govpn.SyslogEnable()
+       }
+
+       switch *proto {
+       case "udp":
+               startUDP()
+       case "tcp":
+               startTCP()
+       case "all":
+               startUDP()
+               startTCP()
+       default:
+               log.Fatalln("Unknown protocol specified")
        }
-       udpSink, udpBuf, udpReady := govpn.ConnListen(conn)
 
        termSignal := make(chan os.Signal, 1)
        signal.Notify(termSignal, os.Interrupt, os.Kill)
@@ -103,158 +85,73 @@ func main() {
        hsHeartbeat := time.Tick(timeout)
        go func() { <-hsHeartbeat }()
 
-       var addr string
-       var state *govpn.Handshake
-       var peerState *PeerState
-       var peer *govpn.Peer
-       var exists bool
-       states := make(map[string]*govpn.Handshake)
-       peers := make(map[string]*PeerState)
-       peerReadySink := make(chan PeerReadyEvent)
-       knownPeers := govpn.KnownPeers(make(map[string]**govpn.Peer))
-       var peerReady PeerReadyEvent
-       var udpPkt govpn.UDPPkt
-       var udpPktData []byte
-       var ethEvent EthEvent
-       var peerId *govpn.PeerId
-       var handshakeProcessForce bool
-       ethSink := make(chan EthEvent)
-
-       log.Println(govpn.VersionGet())
        if *stats != "" {
                log.Println("Stats are going to listen on", *stats)
                statsPort, err := net.Listen("tcp", *stats)
                if err != nil {
-                       panic(err)
+                       log.Fatalln("Can not listen on stats port:", err)
                }
                go govpn.StatsProcessor(statsPort, &knownPeers)
        }
-       log.Println("Server started")
+       if *proxy != "" {
+               go proxyStart()
+       }
+       govpn.BothPrintf(`[started bind="%s"]`, *bindAddr)
 
+       var needsDeletion bool
 MainCycle:
        for {
                select {
                case <-termSignal:
+                       govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr)
+                       peers.Range(func(_, psI interface{}) bool {
+                               ps := psI.(*PeerState)
+                               govpn.ScriptCall(
+                                       confs[*ps.peer.ID].Down,
+                                       ps.tap.Name,
+                                       ps.peer.Addr,
+                               )
+                               return true
+                       })
                        break MainCycle
                case <-hsHeartbeat:
                        now := time.Now()
-                       for addr, hs := range states {
+
+                       handshakes.Range(func(addrI, hsI interface{}) bool {
+                               addr := addrI.(string)
+                               hs := hsI.(*govpn.Handshake)
                                if hs.LastPing.Add(timeout).Before(now) {
-                                       log.Println("Deleting handshake state", addr)
+                                       govpn.Printf(`[handshake-delete bind="%s" addr="%s"]`, *bindAddr, addr)
                                        hs.Zero()
-                                       delete(states, addr)
+                                       handshakes.Delete(addr)
                                }
-                       }
-                       for addr, state := range peers {
-                               if state.peer.LastPing.Add(timeout).Before(now) {
-                                       log.Println("Deleting peer", state.peer)
-                                       delete(peers, addr)
-                                       delete(knownPeers, addr)
-                                       downPath := path.Join(
-                                               govpn.PeersPath,
-                                               state.peer.Id.String(),
-                                               "down.sh",
+                               return true
+                       })
+
+                       peers.Range(func(addrI, psI interface{}) bool {
+                               addr := addrI.(string)
+                               ps := psI.(*PeerState)
+                               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.ID.String(),
                                        )
-                                       go govpn.ScriptCall(downPath, state.tap.Name)
-                                       state.terminate <- struct{}{}
-                                       state.peer.Zero()
-                               }
-                       }
-               case peerReady = <-peerReadySink:
-                       for addr, state := range peers {
-                               if state.tap.Name != peerReady.iface {
-                                       continue
-                               }
-                               delete(peers, addr)
-                               delete(knownPeers, addr)
-                               state.terminate <- struct{}{}
-                               state.peer.Zero()
-                               break
-                       }
-                       addr = peerReady.peer.Addr.String()
-                       state := NewPeerState(peerReady.peer, peerReady.iface)
-                       if state == nil {
-                               continue
-                       }
-                       peers[addr] = state
-                       knownPeers[addr] = &peerReady.peer
-                       states[addr].Zero()
-                       delete(states, addr)
-                       log.Println("Registered interface", peerReady.iface, "with peer", peer)
-                       go func(state *PeerState) {
-                               for data := range state.sink {
-                                       ethSink <- EthEvent{
-                                               peer:  state.peer,
-                                               data:  data,
-                                               ready: state.ready,
-                                       }
-                               }
-                       }(state)
-               case ethEvent = <-ethSink:
-                       if s, exists := peers[ethEvent.peer.Addr.String()]; !exists || s.peer != ethEvent.peer {
-                               continue
-                       }
-                       ethEvent.peer.EthProcess(ethEvent.data, conn, ethEvent.ready)
-               case udpPkt = <-udpSink:
-                       if udpPkt.Addr == nil {
-                               udpReady <- struct{}{}
-                               continue
-                       }
-                       udpPktData = udpBuf[:udpPkt.Size]
-                       addr = udpPkt.Addr.String()
-                       handshakeProcessForce = false
-               HandshakeProcess:
-                       if _, exists = peers[addr]; handshakeProcessForce || !exists {
-                               peerId = govpn.IDsCache.Find(udpPktData)
-                               if peerId == nil {
-                                       log.Println("Unknown identity from", addr)
-                                       udpReady <- struct{}{}
-                                       continue
-                               }
-                               state, exists = states[addr]
-                               if !exists {
-                                       state = govpn.HandshakeNew(udpPkt.Addr)
-                                       states[addr] = state
-                               }
-                               peer = state.Server(peerId, conn, udpPktData)
-                               if peer != nil {
-                                       log.Println("Peer handshake finished", peer)
-                                       if _, exists = peers[addr]; exists {
-                                               go func() {
-                                                       peerReadySink <- PeerReadyEvent{peer, peers[addr].tap.Name}
-                                               }()
-                                       } else {
-                                               go func() {
-                                                       upPath := path.Join(govpn.PeersPath, peer.Id.String(), "up.sh")
-                                                       result, err := govpn.ScriptCall(upPath, "")
-                                                       if err != nil {
-                                                               return
-                                                       }
-                                                       sepIndex := bytes.Index(result, []byte{'\n'})
-                                                       if sepIndex < 0 {
-                                                               sepIndex = len(result)
-                                                       }
-                                                       ifaceName := string(result[:sepIndex])
-                                                       peerReadySink <- PeerReadyEvent{peer, ifaceName}
-                                               }()
-                                       }
-                               }
-                               if !handshakeProcessForce {
-                                       udpReady <- struct{}{}
+                                       peers.Delete(addr)
+                                       knownPeers.Delete(addr)
+                                       peersByID.Delete(*ps.peer.ID)
+                                       go govpn.ScriptCall(
+                                               confs[*ps.peer.ID].Down,
+                                               ps.tap.Name,
+                                               ps.peer.Addr,
+                                       )
+                                       ps.terminator <- struct{}{}
                                }
-                               continue
-                       }
-                       peerState, exists = peers[addr]
-                       if !exists {
-                               udpReady <- struct{}{}
-                               continue
-                       }
-                       // If it fails during processing, then try to work with it
-                       // as with handshake packet
-                       if !peerState.peer.UDPProcess(udpPktData, peerState.tap, udpReady) {
-                               handshakeProcessForce = true
-                               goto HandshakeProcess
-                       }
+                               return true
+                       })
                }
        }
 }