func NetworkTick() { if common.IsListenTCP() { if !TCPServerStarted { TCPServerStarted = true go tcp_server() } } Mutex_net.Lock() conn_cnt := OutConsActive Mutex_net.Unlock() if next_drop_slowest.IsZero() { next_drop_slowest = time.Now().Add(DropSlowestEvery) } else if conn_cnt >= atomic.LoadUint32(&common.CFG.Net.MaxOutCons) { // Having max number of outgoing connections, check to drop the slowest one if time.Now().After(next_drop_slowest) { drop_slowest_peer() next_drop_slowest = time.Now().Add(DropSlowestEvery) } } // hammering protection - expire recently disconnected if next_clean_hammers.IsZero() { next_clean_hammers = time.Now().Add(HammeringMinReconnect) } else if time.Now().After(next_clean_hammers) { HammeringMutex.Lock() for k, t := range RecentlyDisconencted { if time.Now().Sub(t) >= HammeringMinReconnect { delete(RecentlyDisconencted, k) } } HammeringMutex.Unlock() ExpireCachedBlocks() next_clean_hammers = time.Now().Add(HammeringMinReconnect) } for conn_cnt < atomic.LoadUint32(&common.CFG.Net.MaxOutCons) { adrs := peersdb.GetBestPeers(16, ConnectionActive) if len(adrs) == 0 { common.LockCfg() if common.CFG.ConnectOnly == "" && common.DebugLevel > 0 { println("no new peers", len(OpenCons), conn_cnt) } common.UnlockCfg() break } DoNetwork(adrs[rand.Int31n(int32(len(adrs)))]) Mutex_net.Lock() conn_cnt = OutConsActive Mutex_net.Unlock() } }
func p_net(w http.ResponseWriter, r *http.Request) { if !ipchecker(r) { return } net_page := load_template("net.html") network.Mutex_net.Lock() net_page = strings.Replace(net_page, "{LISTEN_TCP}", fmt.Sprint(common.IsListenTCP(), network.TCPServerStarted), 1) net_page = strings.Replace(net_page, "{EXTERNAL_ADDR}", btc.NewNetAddr(network.BestExternalAddr()).String(), 1) network.Mutex_net.Unlock() write_html_head(w, r) w.Write([]byte(net_page)) write_html_tail(w) }
func p_net(w http.ResponseWriter, r *http.Request) { if !ipchecker(r) { return } net_page := load_template("net.html") net_row := load_template("net_row.html") network.Mutex_net.Lock() srt := make(network.SortedKeys, len(network.OpenCons)) cnt := 0 for k, v := range network.OpenCons { if !v.IsBroken() { srt[cnt].Key = k srt[cnt].ConnID = v.ConnID cnt++ } } sort.Sort(srt) net_page = strings.Replace(net_page, "{OUT_CONNECTIONS}", fmt.Sprint(network.OutConsActive), 1) net_page = strings.Replace(net_page, "{IN_CONNECTIONS}", fmt.Sprint(network.InConsActive), 1) net_page = strings.Replace(net_page, "{LISTEN_TCP}", fmt.Sprint(common.IsListenTCP(), network.TCPServerStarted), 1) net_page = strings.Replace(net_page, "{EXTERNAL_ADDR}", btc.NewNetAddr(network.BestExternalAddr()).String(), 1) for idx := range srt { v := network.OpenCons[srt[idx].Key] s := net_row v.Mutex.Lock() s = strings.Replace(s, "{CONNID}", fmt.Sprint(v.ConnID), -1) if v.Incoming { s = strings.Replace(s, "{CONN_DIR_ICON}", "<img src=\"webui/incoming.png\">", 1) } else { s = strings.Replace(s, "{CONN_DIR_ICON}", "<img src=\"webui/outgoing.png\">", 1) } s = strings.Replace(s, "{PEER_ADDR}", v.PeerAddr.Ip(), 1) s = strings.Replace(s, "{PERR_PING}", fmt.Sprint(v.GetAveragePing()), 1) s = strings.Replace(s, "{LAST_RCVD_LEN}", fmt.Sprint(v.LastBtsRcvd), 1) s = strings.Replace(s, "{LAST_RCVD_CMD}", v.LastCmdRcvd, 1) s = strings.Replace(s, "{LAST_SENT_LEN}", fmt.Sprint(v.LastBtsSent), 1) s = strings.Replace(s, "{LAST_SENT_CNT}", v.LastCmdSent, 1) s = strings.Replace(s, "{TOTAL_RCVD}", common.BytesToString(v.BytesReceived), 1) s = strings.Replace(s, "{TOTAL_SENT}", common.BytesToString(v.BytesSent), 1) s = strings.Replace(s, "{NODE_VERSION}", fmt.Sprint(v.Node.Version), 1) s = strings.Replace(s, "{USER_AGENT}", v.Node.Agent, 1) if v.Send.Buf != nil { s = strings.Replace(s, "<!--SENDBUF-->", common.BytesToString(uint64(len(v.Send.Buf))), 1) } if len(v.GetBlockInProgress) > 0 { s = strings.Replace(s, "<!--BLKSINPROG-->", fmt.Sprint(len(v.GetBlockInProgress), "blks "), 1) } v.Mutex.Unlock() net_page = templ_add(net_page, "<!--PEER_ROW-->", s) } network.Mutex_net.Unlock() write_html_head(w, r) w.Write([]byte(net_page)) write_html_tail(w) }
// Process that handles communication with a single peer func (c *OneConnection) Run() { c.SendVersion() c.Mutex.Lock() now := time.Now() c.X.LastDataGot = now c.nextMaintanence = now.Add(time.Minute) c.LastPingSent = now.Add(5*time.Second - common.PingPeerEvery) // do first ping ~5 seconds from now c.Mutex.Unlock() for !c.IsBroken() { if c.IsBroken() { break } if c.SendPendingData() { continue // Do now read the socket if we have pending data to send } cmd := c.FetchMessage() if cmd == nil { c.Tick() continue } if c.X.VerackReceived { c.PeerAddr.Alive() } c.Mutex.Lock() c.counters["rcvd_"+cmd.cmd]++ c.counters["rbts_"+cmd.cmd] += uint64(len(cmd.pl)) c.X.LastDataGot = time.Now() c.X.LastCmdRcvd = cmd.cmd c.X.LastBtsRcvd = uint32(len(cmd.pl)) c.Mutex.Unlock() if common.DebugLevel < 0 { fmt.Println(c.PeerAddr.Ip(), "->", cmd.cmd, len(cmd.pl)) } common.CountSafe("rcvd_" + cmd.cmd) common.CountSafeAdd("rbts_"+cmd.cmd, uint64(len(cmd.pl))) switch cmd.cmd { case "version": er := c.HandleVersion(cmd.pl) if er != nil { println("version msg error:", er.Error()) c.Disconnect() break } if c.Node.DoNotRelayTxs { c.DoS("SPV") break } if c.Node.Version >= 70012 { c.SendRawMsg("sendheaders", nil) if c.Node.Version >= 70013 { if common.CFG.TXPool.FeePerByte != 0 { var pl [8]byte binary.LittleEndian.PutUint64(pl[:], 1000*common.CFG.TXPool.FeePerByte) c.SendRawMsg("feefilter", pl[:]) } if c.Node.Version >= 70014 { if (c.Node.Services & SERVICE_SEGWIT) == 0 { // if the node does not support segwit, request compact blocks // only if we have not achieved he segwit enforcement moment if common.BlockChain.Consensus.Enforce_SEGWIT == 0 || common.Last.BlockHeight() < common.BlockChain.Consensus.Enforce_SEGWIT { c.SendRawMsg("sendcmpct", []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } } else { c.SendRawMsg("sendcmpct", []byte{0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } } } } case "verack": c.X.VerackReceived = true c.PeerAddr.Services = c.Node.Services c.PeerAddr.Save() if common.IsListenTCP() { c.SendOwnAddr() } case "inv": c.ProcessInv(cmd.pl) case "tx": if common.CFG.TXPool.Enabled { c.ParseTxNet(cmd.pl) } case "addr": c.ParseAddr(cmd.pl) case "block": //block received netBlockReceived(c, cmd.pl) case "getblocks": c.GetBlocks(cmd.pl) case "getdata": c.ProcessGetData(cmd.pl) case "getaddr": if !c.X.GetAddrDone { c.SendAddr() c.X.GetAddrDone = true } else { c.Mutex.Lock() c.counters["SecondGetAddr"]++ c.Mutex.Unlock() if c.Misbehave("SecondGetAddr", 1000/20) { break } } case "ping": re := make([]byte, len(cmd.pl)) copy(re, cmd.pl) c.SendRawMsg("pong", re) case "pong": if c.PingInProgress == nil { common.CountSafe("PongUnexpected") } else if bytes.Equal(cmd.pl, c.PingInProgress) { c.HandlePong() } else { common.CountSafe("PongMismatch") } case "getheaders": c.GetHeaders(cmd.pl) case "notfound": common.CountSafe("NotFound") case "headers": if c.HandleHeaders(cmd.pl) > 0 { c.sendGetHeaders() } case "sendheaders": c.Node.SendHeaders = true case "feefilter": if len(cmd.pl) >= 8 { c.X.MinFeeSPKB = int64(binary.LittleEndian.Uint64(cmd.pl[:8])) //println(c.PeerAddr.Ip(), c.Node.Agent, "feefilter", c.X.MinFeeSPKB) } case "sendcmpct": if len(cmd.pl) >= 9 { version := binary.LittleEndian.Uint64(cmd.pl[1:9]) if version > c.Node.SendCmpctVer { //println(c.ConnID, "sendcmpct", cmd.pl[0]) c.Node.SendCmpctVer = version c.Node.HighBandwidth = cmd.pl[0] == 1 } else { c.Mutex.Lock() c.counters[fmt.Sprint("SendCmpctV", version)]++ c.Mutex.Unlock() } } else { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "sendcmpct", hex.EncodeToString(cmd.pl)) } case "cmpctblock": c.ProcessCmpctBlock(cmd.pl) case "getblocktxn": c.ProcessGetBlockTxn(cmd.pl) //println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "getblocktxn", hex.EncodeToString(cmd.pl)) case "blocktxn": c.ProcessBlockTxn(cmd.pl) //println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn", hex.EncodeToString(cmd.pl)) default: if common.DebugLevel > 0 { println(cmd.cmd, "from", c.PeerAddr.Ip()) } } } c.Mutex.Lock() MutexRcv.Lock() for k, _ := range c.GetBlockInProgress { if rec, ok := BlocksToGet[k]; ok { rec.InProgress-- } else { //println("ERROR! Block", bip.hash.String(), "in progress, but not in BlocksToGet") } } MutexRcv.Unlock() ban := c.banit c.Mutex.Unlock() if ban { c.PeerAddr.Ban() common.CountSafe("PeersBanned") } else if c.X.Incomming { HammeringMutex.Lock() RecentlyDisconencted[c.PeerAddr.NetAddr.Ip4] = time.Now() HammeringMutex.Unlock() } if common.DebugLevel != 0 { println("Disconnected from", c.PeerAddr.Ip()) } c.Conn.Close() }
func NetworkTick() { if common.IsListenTCP() { if !TCPServerStarted { TCPServerStarted = true go tcp_server() } } // Push GetHeaders if not in progress Mutex_net.Lock() var cnt_headers_in_progress int var max_headers_got_cnt int var _v *OneConnection for _, v := range OpenCons { v.Mutex.Lock() if !v.X.AllHeadersReceived || v.X.GetHeadersInProgress { cnt_headers_in_progress++ } else if !v.X.LastHeadersEmpty { if _v == nil || v.X.TotalNewHeadersCount > max_headers_got_cnt { max_headers_got_cnt = v.X.TotalNewHeadersCount _v = v } } v.Mutex.Unlock() } conn_cnt := OutConsActive Mutex_net.Unlock() if cnt_headers_in_progress == 0 { if _v != nil { common.CountSafe("GetHeadersPush") /*println("No headers_in_progress, so take it from", _v.ConnID, _v.X.TotalNewHeadersCount, _v.X.LastHeadersEmpty)*/ _v.Mutex.Lock() _v.X.AllHeadersReceived = false _v.Mutex.Unlock() } else { common.CountSafe("GetHeadersNone") } } if common.CFG.DropPeers.DropEachMinutes != 0 { if next_drop_peer.IsZero() { next_drop_peer = time.Now().Add(common.DropSlowestEvery) } else if time.Now().After(next_drop_peer) { drop_worst_peer() next_drop_peer = time.Now().Add(common.DropSlowestEvery) } } // hammering protection - expire recently disconnected if next_clean_hammers.IsZero() { next_clean_hammers = time.Now().Add(HammeringMinReconnect) } else if time.Now().After(next_clean_hammers) { HammeringMutex.Lock() for k, t := range RecentlyDisconencted { if time.Now().Sub(t) >= HammeringMinReconnect { delete(RecentlyDisconencted, k) } } HammeringMutex.Unlock() next_clean_hammers = time.Now().Add(HammeringMinReconnect) } for conn_cnt < atomic.LoadUint32(&common.CFG.Net.MaxOutCons) { var segwit_conns uint32 if common.CFG.Net.MinSegwitCons > 0 { Mutex_net.Lock() for _, cc := range OpenCons { if (cc.Node.Services & SERVICE_SEGWIT) != 0 { segwit_conns++ } } Mutex_net.Unlock() } adrs := peersdb.GetBestPeers(128, func(ad *peersdb.PeerAddr) bool { if segwit_conns < common.CFG.Net.MinSegwitCons && (ad.Services&SERVICE_SEGWIT) == 0 { return true } return ConnectionActive(ad) }) if len(adrs) == 0 { common.LockCfg() if common.CFG.ConnectOnly == "" && common.DebugLevel > 0 { println("no new peers", len(OpenCons), conn_cnt) } common.UnlockCfg() break } DoNetwork(adrs[rand.Int31n(int32(len(adrs)))]) Mutex_net.Lock() conn_cnt = OutConsActive Mutex_net.Unlock() } }
// TCP server func tcp_server() { ad, e := net.ResolveTCPAddr("tcp4", fmt.Sprint("0.0.0.0:", common.DefaultTcpPort)) if e != nil { println("ResolveTCPAddr", e.Error()) return } lis, e := net.ListenTCP("tcp4", ad) if e != nil { println("ListenTCP", e.Error()) return } defer lis.Close() //fmt.Println("TCP server started at", ad.String()) for common.IsListenTCP() { common.CountSafe("NetServerLoops") Mutex_net.Lock() ica := InConsActive Mutex_net.Unlock() if ica < atomic.LoadUint32(&common.CFG.Net.MaxInCons) { lis.SetDeadline(time.Now().Add(time.Second)) tc, e := lis.AcceptTCP() if e == nil { var terminate bool if common.DebugLevel > 0 { fmt.Println("Incoming connection from", tc.RemoteAddr().String()) } // set port to default, for incomming connections ad, e := peersdb.NewPeerFromString(tc.RemoteAddr().String(), true) if e == nil { // Hammering protection HammeringMutex.Lock() ti, ok := RecentlyDisconencted[ad.NetAddr.Ip4] HammeringMutex.Unlock() if ok && time.Now().Sub(ti) < HammeringMinReconnect { //println(ad.Ip(), "is hammering within", time.Now().Sub(ti).String()) common.CountSafe("BanHammerIn") ad.Ban() terminate = true } if !terminate { // Incoming IP passed all the initial checks - talk to it conn := NewConnection(ad) conn.X.ConnectedAt = time.Now() conn.X.Incomming = true conn.Conn = tc Mutex_net.Lock() if _, ok := OpenCons[ad.UniqID()]; ok { //fmt.Println(ad.Ip(), "already connected") common.CountSafe("SameIpReconnect") Mutex_net.Unlock() terminate = true } else { OpenCons[ad.UniqID()] = conn InConsActive++ Mutex_net.Unlock() go func() { conn.Run() Mutex_net.Lock() delete(OpenCons, ad.UniqID()) InConsActive-- Mutex_net.Unlock() }() } } } else { if common.DebugLevel > 0 { println("NewPeerFromString:", e.Error()) } common.CountSafe("InConnRefused") terminate = true } // had any error occured - close teh TCP connection if terminate { tc.Close() } } } else { time.Sleep(1e9) } } Mutex_net.Lock() for _, c := range OpenCons { if c.X.Incomming { c.Disconnect() } } TCPServerStarted = false Mutex_net.Unlock() //fmt.Println("TCP server stopped") }
// Process that handles communication with a single peer func (c *OneConnection) Run() { c.SendVersion() c.Mutex.Lock() c.LastDataGot = time.Now() c.NextBlocksAsk = time.Now() // ask for blocks ASAP c.NextGetAddr = time.Now() // do getaddr ~10 seconds from now c.NextPing = time.Now().Add(5 * time.Second) // do first ping ~5 seconds from now c.Mutex.Unlock() for !c.IsBroken() { c.Mutex.Lock() c.LoopCnt++ c.Mutex.Unlock() cmd := c.FetchMessage() if c.IsBroken() { break } // Timeout ping in progress if c.PingInProgress != nil && time.Now().After(c.LastPingSent.Add(PingTimeout)) { if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "ping timeout") } common.CountSafe("PingTimeout") c.HandlePong() // this will set LastPingSent to nil } if cmd == nil { c.Tick() continue } c.Mutex.Lock() c.LastDataGot = time.Now() c.LastCmdRcvd = cmd.cmd c.LastBtsRcvd = uint32(len(cmd.pl)) c.Mutex.Unlock() c.PeerAddr.Alive() if common.DebugLevel < 0 { fmt.Println(c.PeerAddr.Ip(), "->", cmd.cmd, len(cmd.pl)) } if c.Send.Buf != nil && len(c.Send.Buf) > SendBufSizeHoldOn { common.CountSafe("hold_" + cmd.cmd) common.CountSafeAdd("hbts_"+cmd.cmd, uint64(len(cmd.pl))) continue } common.CountSafe("rcvd_" + cmd.cmd) common.CountSafeAdd("rbts_"+cmd.cmd, uint64(len(cmd.pl))) switch cmd.cmd { case "version": er := c.HandleVersion(cmd.pl) if er != nil { println("version:", er.Error()) c.Disconnect() } case "verack": c.VerackReceived = true if common.IsListenTCP() { c.SendOwnAddr() } case "inv": c.ProcessInv(cmd.pl) case "tx": if common.CFG.TXPool.Enabled { c.ParseTxNet(cmd.pl) } case "addr": c.ParseAddr(cmd.pl) case "block": //block received netBlockReceived(c, cmd.pl) case "getblocks": c.GetBlocks(cmd.pl) case "getdata": c.ProcessGetData(cmd.pl) case "getaddr": c.SendAddr() case "alert": c.HandleAlert(cmd.pl) case "ping": re := make([]byte, len(cmd.pl)) copy(re, cmd.pl) c.SendRawMsg("pong", re) case "pong": if c.PingInProgress == nil { common.CountSafe("PongUnexpected") } else if bytes.Equal(cmd.pl, c.PingInProgress) { c.HandlePong() } else { common.CountSafe("PongMismatch") } case "getheaders": c.GetHeaders(cmd.pl) case "notfound": common.CountSafe("NotFound") default: if common.DebugLevel > 0 { println(cmd.cmd, "from", c.PeerAddr.Ip()) } } } c.Mutex.Lock() ban := c.banit c.Mutex.Unlock() if ban { c.PeerAddr.Ban() common.CountSafe("PeersBanned") } else if c.Incoming { HammeringMutex.Lock() RecentlyDisconencted[c.PeerAddr.NetAddr.Ip4] = time.Now() HammeringMutex.Unlock() } if common.DebugLevel > 0 { println("Disconnected from", c.PeerAddr.Ip()) } c.NetConn.Close() }
func p_cfg(w http.ResponseWriter, r *http.Request) { if !ipchecker(r) { return } common.LockCfg() defer common.UnlockCfg() if r.Method == "POST" { if len(r.Form["configjson"]) > 0 { e := json.Unmarshal([]byte(r.Form["configjson"][0]), &common.CFG) if e == nil { common.Reset() } if len(r.Form["save"]) > 0 { common.SaveConfig() } http.Redirect(w, r, "/", http.StatusFound) return } if len(r.Form["walletdata"]) > 0 && len(r.Form["walletfname"]) > 0 { fn := r.Form["walletfname"][0] if fn != "" { fn = common.CFG.Walletdir + string(os.PathSeparator) + fn ioutil.WriteFile(fn, []byte(r.Form["walletdata"][0]), 0660) wallet.LoadWallet(fn) } http.Redirect(w, r, "/wal", http.StatusFound) return } if len(r.Form["shutdown"]) > 0 { usif.Exit_now = true w.Write([]byte("Your node should shut down soon")) return } return } // for any other GET we need a matching session-id if !checksid(r) { new_session_id(w) return } if len(r.Form["txponoff"]) > 0 { common.CFG.TXPool.Enabled = !common.CFG.TXPool.Enabled http.Redirect(w, r, "txs", http.StatusFound) return } if len(r.Form["txronoff"]) > 0 { common.CFG.TXRoute.Enabled = !common.CFG.TXRoute.Enabled http.Redirect(w, r, "txs", http.StatusFound) return } if len(r.Form["lonoff"]) > 0 { common.SetListenTCP(!common.IsListenTCP(), true) http.Redirect(w, r, "net", http.StatusFound) return } if len(r.Form["drop"]) > 0 { network.DropPeer(r.Form["drop"][0]) http.Redirect(w, r, "net", http.StatusFound) return } if len(r.Form["savecfg"]) > 0 { dat, _ := json.Marshal(&common.CFG) if dat != nil { ioutil.WriteFile(common.ConfigFile, dat, 0660) } http.Redirect(w, r, "/", http.StatusFound) return } if len(r.Form["beepblock"]) > 0 { common.CFG.Beeps.NewBlock = !common.CFG.Beeps.NewBlock http.Redirect(w, r, "/", http.StatusFound) return } if len(r.Form["freemem"]) > 0 { sys.FreeMem() http.Redirect(w, r, "/", http.StatusFound) return } if len(r.Form["mid"]) > 0 { v, e := strconv.ParseUint(r.Form["mid"][0], 10, 32) if e == nil && v < uint64(len(common.MinerIds)) { common.CFG.Beeps.MinerID = string(common.MinerIds[v].Tag) } else { common.CFG.Beeps.MinerID = "" } http.Redirect(w, r, "miners", http.StatusFound) return } }