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"
"log"
"net"
"os"
"os/signal"
- "path"
"time"
"govpn"
)
var (
- bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
- proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
- peersPath = flag.String("peers", "peers", "Path to peers keys directory")
- stats = flag.String("stats", "", "Enable stats retrieving on host:port")
- proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port")
- mtu = flag.Int("mtu", 1452, "MTU for outgoing packets")
- egdPath = flag.String("egd", "", "Optional path to EGD socket")
+ 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.json", "Path to configuration JSON")
+ stats = flag.String("stats", "", "Enable stats retrieving on host:port")
+ proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port")
+ mtu = flag.Int("mtu", 1452, "MTU for outgoing packets")
+ egdPath = flag.String("egd", "", "Optional path to EGD socket")
)
-type Pkt struct {
- addr string
- conn govpn.RemoteConn
- data []byte
- ready chan struct{}
-}
-
-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, peer.Timeout, peer.CPR)
- 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(govpn.TimeoutDefault)
log.Println(govpn.VersionGet())
govpn.MTU = *mtu
- govpn.PeersInit(*peersPath)
+ confInit()
+ knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
if *egdPath != "" {
log.Println("Using", *egdPath, "EGD")
govpn.EGDInit(*egdPath)
}
- sink := make(chan Pkt)
switch *proto {
case "udp":
- startUDP(sink)
+ startUDP()
case "tcp":
- startTCP(sink)
+ startTCP()
case "all":
- startUDP(sink)
- startTCP(sink)
+ startUDP()
+ startTCP()
default:
log.Fatalln("Unknown protocol specified")
}
hsHeartbeat := time.Tick(timeout)
go func() { <-hsHeartbeat }()
- 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 pkt Pkt
- var ethEvent EthEvent
- var peerId *govpn.PeerId
- var peerConf *govpn.PeerConf
- var handshakeProcessForce bool
- ethSink := make(chan EthEvent)
-
log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
if *stats != "" {
log.Println("Stats are going to listen on", *stats)
go govpn.StatsProcessor(statsPort, &knownPeers)
}
if *proxy != "" {
- go proxyStart(sink)
+ go proxyStart()
}
log.Println("Server started")
+ var needsDeletion bool
MainCycle:
for {
select {
break MainCycle
case <-hsHeartbeat:
now := time.Now()
- for addr, hs := range states {
+ hsLock.Lock()
+ for addr, hs := range handshakes {
if hs.LastPing.Add(timeout).Before(now) {
log.Println("Deleting handshake state", addr)
hs.Zero()
- delete(states, addr)
+ delete(handshakes, addr)
}
}
- for addr, state := range peers {
- if state.peer.LastPing.Add(timeout).Before(now) {
- log.Println("Deleting peer", state.peer)
+ 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 {
+ log.Println("Deleting peer", ps.peer)
delete(peers, addr)
delete(knownPeers, addr)
- downPath := path.Join(
- govpn.PeersPath,
- state.peer.Id.String(),
- "down.sh",
+ delete(peersById, *ps.peer.Id)
+ go govpn.ScriptCall(
+ confs[*ps.peer.Id].Down,
+ ps.tap.Name,
)
- go govpn.ScriptCall(downPath, state.tap.Name)
- state.terminate <- struct{}{}
- state.peer.Zero()
+ ps.terminator <- struct{}{}
}
}
- 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
- }
- state := NewPeerState(peerReady.peer, peerReady.iface)
- if state == nil {
- continue
- }
- peers[peerReady.peer.Addr] = state
- knownPeers[peerReady.peer.Addr] = &peerReady.peer
- states[peerReady.peer.Addr].Zero()
- delete(states, peerReady.peer.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]; !exists || s.peer != ethEvent.peer {
- continue
- }
- ethEvent.peer.EthProcess(ethEvent.data, ethEvent.ready)
- case pkt = <-sink:
- if pkt.data == nil {
- pkt.ready <- struct{}{}
- continue
- }
- handshakeProcessForce = false
- HandshakeProcess:
- if _, exists = peers[pkt.addr]; handshakeProcessForce || !exists {
- peerId = govpn.IDsCache.Find(pkt.data)
- if peerId == nil {
- log.Println("Unknown identity from", pkt.addr)
- pkt.ready <- struct{}{}
- continue
- }
- peerConf = peerId.Conf()
- if peerConf == nil {
- log.Println("Can not get peer configuration", peerId.String())
- pkt.ready <- struct{}{}
- continue
- }
- state, exists = states[pkt.addr]
- if !exists {
- state = govpn.HandshakeNew(pkt.addr, pkt.conn, peerConf)
- states[pkt.addr] = state
- }
- peer = state.Server(pkt.data)
- if peer != nil {
- log.Println("Peer handshake finished", peer)
- if _, exists = peers[pkt.addr]; exists {
- go func() {
- peerReadySink <- PeerReadyEvent{
- peer, peers[pkt.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 {
- pkt.ready <- struct{}{}
- }
- continue
- }
- peerState, exists = peers[pkt.addr]
- if !exists {
- pkt.ready <- struct{}{}
- continue
- }
- // If it fails during processing, then try to work with it
- // as with handshake packet
- if !peerState.peer.PktProcess(pkt.data, peerState.tap, pkt.ready) {
- handshakeProcessForce = true
- goto HandshakeProcess
- }
+ hsLock.Unlock()
+ peersLock.Unlock()
+ peersByIdLock.Unlock()
+ kpLock.Unlock()
}
}
}