github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/cmd/discosrv/main.go (about) 1 // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). 2 // All rights reserved. Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "encoding/binary" 9 "encoding/hex" 10 "flag" 11 "fmt" 12 "io" 13 "log" 14 "net" 15 "os" 16 "sync" 17 "time" 18 19 "github.com/calmh/syncthing/discover" 20 "github.com/calmh/syncthing/protocol" 21 "github.com/golang/groupcache/lru" 22 "github.com/juju/ratelimit" 23 ) 24 25 type node struct { 26 addresses []address 27 updated time.Time 28 } 29 30 type address struct { 31 ip []byte 32 port uint16 33 } 34 35 var ( 36 nodes = make(map[protocol.NodeID]node) 37 lock sync.Mutex 38 queries = 0 39 announces = 0 40 answered = 0 41 limited = 0 42 unknowns = 0 43 debug = false 44 lruSize = 1024 45 limitAvg = 1 46 limitBurst = 10 47 limiter *lru.Cache 48 ) 49 50 func main() { 51 var listen string 52 var timestamp bool 53 var statsIntv int 54 var statsFile string 55 56 flag.StringVar(&listen, "listen", ":22026", "Listen address") 57 flag.BoolVar(&debug, "debug", false, "Enable debug output") 58 flag.BoolVar(×tamp, "timestamp", true, "Timestamp the log output") 59 flag.IntVar(&statsIntv, "stats-intv", 0, "Statistics output interval (s)") 60 flag.StringVar(&statsFile, "stats-file", "/var/log/discosrv.stats", "Statistics file name") 61 flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries") 62 flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s") 63 flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets") 64 flag.Parse() 65 66 limiter = lru.New(lruSize) 67 68 log.SetOutput(os.Stdout) 69 if !timestamp { 70 log.SetFlags(0) 71 } 72 73 addr, _ := net.ResolveUDPAddr("udp", listen) 74 conn, err := net.ListenUDP("udp", addr) 75 if err != nil { 76 log.Fatal(err) 77 } 78 79 if statsIntv > 0 { 80 go logStats(statsFile, statsIntv) 81 } 82 83 var buf = make([]byte, 1024) 84 for { 85 buf = buf[:cap(buf)] 86 n, addr, err := conn.ReadFromUDP(buf) 87 88 if limit(addr) { 89 // Rate limit in effect for source 90 continue 91 } 92 93 if err != nil { 94 log.Fatal(err) 95 } 96 97 if n < 4 { 98 log.Printf("Received short packet (%d bytes)", n) 99 continue 100 } 101 102 buf = buf[:n] 103 magic := binary.BigEndian.Uint32(buf) 104 105 switch magic { 106 case discover.AnnouncementMagic: 107 handleAnnounceV2(addr, buf) 108 109 case discover.QueryMagic: 110 handleQueryV2(conn, addr, buf) 111 112 default: 113 lock.Lock() 114 unknowns++ 115 lock.Unlock() 116 } 117 } 118 } 119 120 func limit(addr *net.UDPAddr) bool { 121 key := addr.IP.String() 122 123 lock.Lock() 124 defer lock.Unlock() 125 126 bkt, ok := limiter.Get(key) 127 if ok { 128 bkt := bkt.(*ratelimit.Bucket) 129 if bkt.TakeAvailable(1) != 1 { 130 // Rate limit exceeded; ignore packet 131 if debug { 132 log.Println("Rate limit exceeded for", key) 133 } 134 limited++ 135 return true 136 } 137 } else { 138 if debug { 139 log.Println("New limiter for", key) 140 } 141 // One packet per ten seconds average rate, burst ten packets 142 limiter.Add(key, ratelimit.NewBucket(10*time.Second/time.Duration(limitAvg), int64(limitBurst))) 143 } 144 145 return false 146 } 147 148 func handleAnnounceV2(addr *net.UDPAddr, buf []byte) { 149 var pkt discover.Announce 150 err := pkt.UnmarshalXDR(buf) 151 if err != nil && err != io.EOF { 152 log.Println("AnnounceV2 Unmarshal:", err) 153 log.Println(hex.Dump(buf)) 154 return 155 } 156 if debug { 157 log.Printf("<- %v %#v", addr, pkt) 158 } 159 160 lock.Lock() 161 announces++ 162 lock.Unlock() 163 164 ip := addr.IP.To4() 165 if ip == nil { 166 ip = addr.IP.To16() 167 } 168 169 var addrs []address 170 for _, addr := range pkt.This.Addresses { 171 tip := addr.IP 172 if len(tip) == 0 { 173 tip = ip 174 } 175 addrs = append(addrs, address{ 176 ip: tip, 177 port: addr.Port, 178 }) 179 } 180 181 node := node{ 182 addresses: addrs, 183 updated: time.Now(), 184 } 185 186 var id protocol.NodeID 187 if len(pkt.This.ID) == 32 { 188 // Raw node ID 189 copy(id[:], pkt.This.ID) 190 } else { 191 id.UnmarshalText(pkt.This.ID) 192 } 193 194 lock.Lock() 195 nodes[id] = node 196 lock.Unlock() 197 } 198 199 func handleQueryV2(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) { 200 var pkt discover.Query 201 err := pkt.UnmarshalXDR(buf) 202 if err != nil { 203 log.Println("QueryV2 Unmarshal:", err) 204 log.Println(hex.Dump(buf)) 205 return 206 } 207 if debug { 208 log.Printf("<- %v %#v", addr, pkt) 209 } 210 211 var id protocol.NodeID 212 if len(pkt.NodeID) == 32 { 213 // Raw node ID 214 copy(id[:], pkt.NodeID) 215 } else { 216 id.UnmarshalText(pkt.NodeID) 217 } 218 219 lock.Lock() 220 node, ok := nodes[id] 221 queries++ 222 lock.Unlock() 223 224 if ok && len(node.addresses) > 0 { 225 ann := discover.Announce{ 226 Magic: discover.AnnouncementMagic, 227 This: discover.Node{ 228 ID: pkt.NodeID, 229 }, 230 } 231 for _, addr := range node.addresses { 232 ann.This.Addresses = append(ann.This.Addresses, discover.Address{IP: addr.ip, Port: addr.port}) 233 } 234 if debug { 235 log.Printf("-> %v %#v", addr, pkt) 236 } 237 238 tb := ann.MarshalXDR() 239 _, _, err = conn.WriteMsgUDP(tb, nil, addr) 240 if err != nil { 241 log.Println("QueryV2 response write:", err) 242 } 243 244 lock.Lock() 245 answered++ 246 lock.Unlock() 247 } 248 } 249 250 func next(intv int) time.Time { 251 d := time.Duration(intv) * time.Second 252 t0 := time.Now() 253 t1 := t0.Add(d).Truncate(d) 254 time.Sleep(t1.Sub(t0)) 255 return t1 256 } 257 258 func logStats(file string, intv int) { 259 f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 260 if err != nil { 261 log.Fatal(err) 262 } 263 for { 264 t := next(intv) 265 266 lock.Lock() 267 268 var deleted = 0 269 for id, node := range nodes { 270 if time.Since(node.updated) > 60*time.Minute { 271 delete(nodes, id) 272 deleted++ 273 } 274 } 275 276 fmt.Fprintf(f, "%d Nr:%d Ne:%d Qt:%d Qa:%d A:%d U:%d Lq:%d Lc:%d\n", 277 t.Unix(), len(nodes), deleted, queries, answered, announces, unknowns, limited, limiter.Len()) 278 f.Sync() 279 280 queries = 0 281 announces = 0 282 answered = 0 283 limited = 0 284 unknowns = 0 285 286 lock.Unlock() 287 } 288 }