You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
6.1 KiB
235 lines
6.1 KiB
9 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"log"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"github.com/btcsuite/btcd/wire"
|
||
|
)
|
||
|
|
||
|
// crawlTwistee runs in a goroutine, crawls the remote ip and updates the master
|
||
|
// list of currently active addresses
|
||
|
func crawlTwistee(tw *Twistee) {
|
||
|
|
||
|
if config.debug {
|
||
|
log.Printf("status - start crawl: twistee %s status: %v:%v lastcrawl: %s\n",
|
||
|
net.JoinHostPort(tw.na.IP.String(),
|
||
|
strconv.Itoa(int(tw.na.Port))),
|
||
|
tw.status,
|
||
|
tw.rating,
|
||
|
time.Since(tw.crawlStart).String())
|
||
|
}
|
||
|
|
||
|
tw.crawlActive = true
|
||
|
tw.crawlStart = time.Now()
|
||
|
|
||
|
defer crawlEnd(tw)
|
||
|
|
||
|
// connect to the remote ip and ask them for their addr list
|
||
|
ras, err := crawlIP(tw)
|
||
|
if err != nil {
|
||
|
// update the fact that we have not connected to this twistee
|
||
|
tw.lastTry = time.Now()
|
||
|
tw.connectFails++
|
||
|
// update the status of this failed twistee
|
||
|
switch tw.status {
|
||
|
case statusRG:
|
||
|
if tw.rating += 25; tw.rating > 30 {
|
||
|
tw.status = statusWG
|
||
|
}
|
||
|
case statusCG:
|
||
|
if tw.rating += 25; tw.rating >= 50 {
|
||
|
tw.status = statusWG
|
||
|
}
|
||
|
case statusWG:
|
||
|
if tw.rating += 30; tw.rating >= 100 {
|
||
|
tw.status = statusNG // not able to connect to this twistee so ignore
|
||
|
}
|
||
|
}
|
||
|
// no more to do so return which will shutdown the goroutine & call
|
||
|
// the deffered cleanup
|
||
|
if config.verbose {
|
||
|
log.Printf("debug - failed crawl: twistee %s newstatus: %v:%v\n",
|
||
|
net.JoinHostPort(tw.na.IP.String(),
|
||
|
strconv.Itoa(int(tw.na.Port))),
|
||
|
tw.status,
|
||
|
tw.rating)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// succesful connection and addresses received so mark status
|
||
|
if tw.status != statusCG {
|
||
|
tw.status = statusCG
|
||
|
}
|
||
|
tw.rating = 0
|
||
|
tw.connectFails = 0
|
||
|
tw.lastConnect = time.Now()
|
||
|
tw.lastTry = time.Now()
|
||
|
|
||
|
added := 0
|
||
|
|
||
|
// loop through all the received network addresses and add to thelist if not present
|
||
|
for _, na := range ras {
|
||
|
// a new network address so add to the system
|
||
|
if x := config.seeder.addNa(na); x == true {
|
||
|
added++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if config.verbose {
|
||
|
log.Printf("status - crawl done: twistee: %s newstatus: %v.%v received_addr: %v added_addr: %v CrawlTime: %s\n",
|
||
|
net.JoinHostPort(tw.na.IP.String(),
|
||
|
strconv.Itoa(int(tw.na.Port))),
|
||
|
tw.status,
|
||
|
tw.rating,
|
||
|
len(ras),
|
||
|
added,
|
||
|
time.Since(tw.crawlStart).String())
|
||
|
}
|
||
|
|
||
|
// goroutine ends. deffered cleanup runs
|
||
|
}
|
||
|
|
||
|
// crawlEnd is a deffered func to update theList after a crawl is all done
|
||
|
func crawlEnd(tw *Twistee) {
|
||
|
tw.crawlActive = false
|
||
|
// FIXME - scan for long term crawl active twistees. Dial timeout is 10 seconds
|
||
|
// so should be done in under 5 minutes
|
||
|
}
|
||
|
|
||
|
// crawlIP retrievs a slice of ip addresses from a client
|
||
|
func crawlIP(tw *Twistee) ([]*wire.NetAddress, error) {
|
||
|
|
||
|
ip := tw.na.IP.String()
|
||
|
port := strconv.Itoa(int(tw.na.Port))
|
||
|
// get correct formatting for ipv6 addresses
|
||
|
dialString := net.JoinHostPort(ip, port)
|
||
|
|
||
|
conn, err := net.DialTimeout("tcp", dialString, time.Second*10)
|
||
|
if err != nil {
|
||
|
if config.debug {
|
||
|
log.Printf("error - Could not connect to %s - %v\n", ip, err)
|
||
|
}
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
defer conn.Close()
|
||
|
if config.verbose {
|
||
|
log.Printf("%s - Connected to remote address. Last connect was %v ago\n", ip, time.Since(tw.lastConnect).String())
|
||
|
}
|
||
|
|
||
|
// First command to remote end needs to be a version command
|
||
|
// last parameter is lastblock
|
||
|
msgver, err := wire.NewMsgVersionFromConn(conn, NOUNCE, 0)
|
||
|
if err != nil {
|
||
|
log.Printf("error - NewMsgVer from conn: %v\n", err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = wire.WriteMessage(conn, msgver, PVER, TWISTNET)
|
||
|
if err != nil {
|
||
|
// Log and handle the error
|
||
|
log.Printf("error - %s:%s Write Message: %v\n", ip, port, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// first message received should be version
|
||
|
msg, _, err := wire.ReadMessage(conn, PVER, TWISTNET)
|
||
|
if err != nil {
|
||
|
// Log and handle the error
|
||
|
log.Printf("error - %s:%s Read Message after sending version: %v\n", ip, port, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
switch msg := msg.(type) {
|
||
|
case *wire.MsgVersion:
|
||
|
// The message is a pointer to a MsgVersion struct.
|
||
|
if config.debug {
|
||
|
log.Printf("%s - Remote version: %v\n", ip, msg.ProtocolVersion)
|
||
|
}
|
||
|
default:
|
||
|
if config.debug {
|
||
|
log.Printf("error: expected Version Message but received: %v\n", msg.Command())
|
||
|
}
|
||
|
return nil, errors.New("Error. Did not receive expected Version message from remote client")
|
||
|
}
|
||
|
|
||
|
// FIXME - update twistee client version with what they just said
|
||
|
|
||
|
// send verack command
|
||
|
msgverack := wire.NewMsgVerAck()
|
||
|
|
||
|
err = wire.WriteMessage(conn, msgverack, PVER, TWISTNET)
|
||
|
if err != nil {
|
||
|
// Log and handle the error
|
||
|
log.Printf("error - %s:%s Writing Message Ver Ack failed: %v\n", ip, port, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// second message received should be verack
|
||
|
msg, _, err = wire.ReadMessage(conn, PVER, TWISTNET)
|
||
|
if err != nil {
|
||
|
// Log and handle the error
|
||
|
log.Printf("error - %s:%s Reading Message expected Ver Ack: %v\n", ip, port, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
switch msg := msg.(type) {
|
||
|
case *wire.MsgVerAck:
|
||
|
if config.debug {
|
||
|
// The message is a pointer to a MsgVersion struct.
|
||
|
log.Printf("%s - received Version Ack\n", ip)
|
||
|
}
|
||
|
default:
|
||
|
if config.debug {
|
||
|
log.Printf("error: expected Version Ack Message but received: %v\n", msg.Command())
|
||
|
}
|
||
|
return nil, errors.New("Error. Did not receive expected Version Ack message from remote client")
|
||
|
}
|
||
|
|
||
|
// send getaddr command
|
||
|
msgGetAddr := wire.NewMsgGetAddr()
|
||
|
|
||
|
err = wire.WriteMessage(conn, msgGetAddr, PVER, TWISTNET)
|
||
|
if err != nil {
|
||
|
// Log and handle the error
|
||
|
log.Printf("error - %s:%s writing message Get Addr: %v\n", ip, port, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
dowhile := true
|
||
|
for dowhile == true {
|
||
|
|
||
|
// Using the Bitcoin lib for the Twister Net means it does not understand some
|
||
|
// of the commands and will error. We can ignore these as we are only
|
||
|
// interested in the addr message and its content.
|
||
|
msgaddr, _, _ := wire.ReadMessage(conn, PVER, TWISTNET)
|
||
|
if msgaddr != nil {
|
||
|
switch msg := msgaddr.(type) {
|
||
|
case *wire.MsgAddr:
|
||
|
// received the addrt message so return the result
|
||
|
if config.debug {
|
||
|
log.Printf("%s - received valid addr message\n", ip)
|
||
|
}
|
||
|
return msg.AddrList, nil
|
||
|
dowhile = false
|
||
|
default:
|
||
|
if config.debug {
|
||
|
log.Printf("%s - ignoring message - %v\n", ip, msg.Command())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// should never get here but need a return command
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
|
||
|
*/
|