]> Cypherpunks.ru repositories - govpn.git/commitdiff
Optional HTTP-server providing with known peers information in JSON
authorSergey Matveev <stargrave@stargrave.org>
Thu, 30 Apr 2015 14:17:02 +0000 (17:17 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 30 Apr 2015 14:18:29 +0000 (17:18 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
README
cmd/govpn-client/main.go
cmd/govpn-server/main.go
doc/overview.texi
doc/user.texi
stats.go [new file with mode: 0644]

diff --git a/README b/README
index e9b104e05b99f6caa47a6f198b0be78dcb6c332d..a47dd94a7222f6b938ea7593a3cb5e73cb3f26d1 100644 (file)
--- a/README
+++ b/README
@@ -3,8 +3,8 @@ written on Go programming language. It uses Diffie-Hellman Encrypted Key
 Exchange (DH-EKE) for mutual zero-knowledge peers authentication and
 authenticated encrypted data transport. Other features include:
 IPv4/IPv6, rehandshake, heartbeat, pre-shared authentication keys (PSK),
-perfect forward secrecy (PFS), replay attack protection.
-GNU/Linux and FreeBSD support.
+perfect forward secrecy (PFS), replay attack protection, JSON real-time
+statistics. GNU/Linux and FreeBSD support.
 
 Home page: http://www.cypherpunks.ru/govpn/
 also available as Tor hidden service: http://vabu56j2ep2rwv3b.onion/govpn/
index 774edd3b6b4c4dcb4a89074ce92e5684791ed58e..b5f86f9657a0fae84ff5b7336d2f5a8f630cf00a 100644 (file)
@@ -36,6 +36,7 @@ var (
        keyPath    = flag.String("key", "", "Path to authentication key file")
        upPath     = flag.String("up", "", "Path to up-script")
        downPath   = flag.String("down", "", "Path to down-script")
+       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")
@@ -83,11 +84,21 @@ func main() {
        var ethPkt []byte
        var udpPkt *govpn.UDPPkt
        var udpPktData []byte
+       knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{remote.String(): &peer})
+
+       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)
+               }
+               go govpn.StatsProcessor(statsPort, &knownPeers)
+       }
 
        termSignal := make(chan os.Signal, 1)
        signal.Notify(termSignal, os.Interrupt, os.Kill)
 
-       log.Println(govpn.VersionGet())
        log.Println("Starting handshake")
        handshake := govpn.HandshakeStart(conn, remote, id, key)
 
index 3d952ea9e0d4e5a62cdb69fad9dba3c115124b74..2761d340a1b85b032731c31f9982675d225771a8 100644 (file)
@@ -35,6 +35,7 @@ import (
 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")
@@ -110,6 +111,7 @@ func main() {
        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
@@ -119,6 +121,14 @@ func main() {
        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)
+               }
+               go govpn.StatsProcessor(statsPort, &knownPeers)
+       }
        log.Println("Server started")
 
 MainCycle:
@@ -139,6 +149,7 @@ MainCycle:
                                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(),
@@ -155,6 +166,7 @@ MainCycle:
                                        continue
                                }
                                delete(peers, addr)
+                               delete(knownPeers, addr)
                                state.terminate <- struct{}{}
                                state.peer.Zero()
                                break
@@ -165,6 +177,7 @@ MainCycle:
                                continue
                        }
                        peers[addr] = state
+                       knownPeers[addr] = &peerReady.peer
                        states[addr].Zero()
                        delete(states, addr)
                        log.Println("Registered interface", peerReady.iface, "with peer", peer)
index 0330d4c531cf8ab067f6a3002df36fb39aabc717..a96d653bfe6cf5029c81e657f28d326f87f10c1a 100644 (file)
@@ -62,4 +62,6 @@ authentication (pre-shared key is not transmitted in any form between
 the peers, not even it's hash value)
 @item Built-in rehandshake and heartbeat features
 @item Several simultaneous clients support
+@item Optional built-in HTTP-server for retrieving information about
+known connected peers in @url{http://json.org/, JSON} format
 @end itemize
index b177e754de40effaf88ac42ab9d6aa55d6fa0083..193e9456979fb238b2f584598fa21e0ec4827101 100644 (file)
@@ -39,6 +39,10 @@ script that has to print interface's name on the first output line.
 Optionally there can be @code{down.sh} that will be executed when client
 disconnects, and @code{name} file containing human readable client's name.
 
+Each of them have ability to show statistics about known connected
+peers. If you specify @emph{host:port} in @code{-stats} argument, then
+it will run HTTP server on it, responding with JSON documents.
+
 @menu
 * Example usage::
 @end menu
@@ -99,7 +103,8 @@ client% while :; do
 done
 @end example
 
-FreeBSD IPv6 client-server example:
+FreeBSD IPv6 client-server example, with stats enabled on the server
+(localhost's 5678 port):
 
 @example
 server% cat > peers/CLIENTID/up.sh <<EOF
@@ -109,7 +114,7 @@ ifconfig $tap inet6 fc00::1/96 mtu 1462 up
 echo $tap
 EOF
 server% ifconfig em0 inet6 fe80::1/64
-server% GOMAXPROC=4 govpn-server -bind fe80::1%em0
+server% GOMAXPROC=4 govpn-server -bind fe80::1%em0 -stats [::1]:5678
 @end example
 
 @example
diff --git a/stats.go b/stats.go
new file mode 100644 (file)
index 0000000..8d39e7a
--- /dev/null
+++ b/stats.go
@@ -0,0 +1,47 @@
+package govpn
+
+import (
+       "encoding/json"
+       "log"
+       "net"
+       "time"
+)
+
+const (
+       RWTimeout = 10 * time.Second
+)
+
+type KnownPeers map[string]**Peer
+
+// StatsProcessor is assumed to be run in background. It accepts
+// connection on statsPort, reads anything one send to them and show
+// information about known peers in serialized JSON format. peers
+// argument is a reference to the map with references to the peers as
+// values. Map is used here because of ease of adding and removing
+// elements in it.
+func StatsProcessor(statsPort net.Listener, peers *KnownPeers) {
+       var conn net.Conn
+       var err error
+       var data []byte
+       buf := make([]byte, 2<<8)
+       for {
+               conn, err = statsPort.Accept()
+               if err != nil {
+                       log.Println("Error during accepting connection", err.Error())
+                       continue
+               }
+               conn.SetDeadline(time.Now().Add(RWTimeout))
+               conn.Read(buf)
+               conn.Write([]byte("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n"))
+               var peersList []*Peer
+               for _, peer := range *peers {
+                       peersList = append(peersList, *peer)
+               }
+               data, err = json.Marshal(peersList)
+               if err != nil {
+                       panic(err)
+               }
+               conn.Write(data)
+               conn.Close()
+       }
+}