|
|
// Copyright 2017-2018 DERO Project. All rights reserved.
// Use of this source code in any form is governed by RESEARCH license.
// license can be found in the LICENSE file.
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package p2p
import "io" import "net" import "fmt" import "time" import "testing" import "container/list" import "encoding/binary" import "runtime/debug"
import "github.com/romana/rlog" import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/globals"
// all communications flow in little endian
const LEVIN_SIGNATURE = 0x0101010101012101 //Bender's nightmare
const LEVIN_SIGNATURE_DATA = 0x0102010101011101 const LEVIN_PROTOCOL_VER_0 = 0 const LEVIN_PROTOCOL_VER_1 = 1
const LEVIN_PACKET_REQUEST = 0x00000001 const LEVIN_PACKET_RESPONSE = 0x00000002
// the whole structure should be packed into 33 bytes
type Levin_Header struct { Signature uint64 CB uint64 // this contains data size appended to buffer
ReturnData bool Command uint32 ReturnCode int32 Flags uint32 Protocol_Version uint32 }
// all response will have the signature in big endian form
type Levin_Data_Header struct { Signature uint64 // LEVIN_SIGNATURE_DATA
//Boost_Header byte
Data []byte }
// sets timeout based on connection state, so as stale connections are cleared quickly
func set_timeout(connection *Connection) { if connection.State == HANDSHAKE_PENDING { connection.Conn.SetReadDeadline(time.Now().Add(20 * time.Second)) // new connections have 20 seconds to handshake
} else { connection.Conn.SetReadDeadline(time.Now().Add(300 * time.Second)) // good connections have 5 mins to communicate
} }
/* this is the entire connection handler, all incoming/outgoing connections end up here */ func Handle_Connection(conn net.Conn, remote_addr *net.TCPAddr, incoming bool) {
var connection Connection var levin_header Levin_Header connection.Incoming = incoming connection.Conn = conn var idle int
connection.Addr = remote_addr // since we may be connecting via socks, get target IP
connection.Command_queue = list.New() // init command queue
connection.State = HANDSHAKE_PENDING if incoming { connection.logger = logger.WithFields(log.Fields{"RIP": remote_addr.String(), "DIR": "INC"}) } else { connection.logger = logger.WithFields(log.Fields{"RIP": remote_addr.String(), "DIR": "OUT"}) }
defer func() { if r := recover(); r != nil { connection.logger.Warnf("Recovered while handling connection, Stack trace below", r) connection.logger.Warnf("Stack trace \n%s", debug.Stack())
} }()
Connection_Add(&connection) // add connection to pool
if !incoming { Send_Handshake(&connection) // send handshake
}
// goroutine to exit the connection if signalled
go func() { ticker := time.NewTicker(1 * time.Second) // 1 second ticker
for { select { case <-ticker.C: idle++ // if idle more than 13 secs, we should send a timed sync
if idle > 13 { if connection.State != HANDSHAKE_PENDING { connection.State = IDLE } Send_Timed_Sync(&connection) //connection.logger.Debugf("We should send a timed sync")
idle = 0 } case <-Exit_Event: // p2p is shutting down, close the connection
connection.Exit = true ticker.Stop() // release resources of timer
Connection_Delete(&connection) conn.Close() return // close the connection and close the routine
} if connection.Exit { // release resources of timer
ticker.Stop() Connection_Delete(&connection) return } } }()
for { if connection.Exit { connection.logger.Debugf("Connection exited") conn.Close() return }
// wait and read header
header_data := make([]byte, 33, 33) // size of levin header
idle = 0 rlog.Tracef(10, "waiting to read header bytes from network %s\n", globals.CTXString(connection.logger)) set_timeout(&connection) read_bytes, err := io.ReadFull(conn, header_data)
if err != nil { rlog.Tracef(4, "Error while reading levin header exiting err:%s\n", err) connection.Exit = true continue } rlog.Tracef(10, "Read %d bytes from network\n", read_bytes)
if connection.State != HANDSHAKE_PENDING { connection.State = ACTIVE }
err = levin_header.DeSerialize(header_data)
if err != nil { rlog.Tracef(4, "Error while DeSerializing levin header exiting err:%s\n", err) connection.Exit = true continue }
// read data as per requirement
data := make([]byte, levin_header.CB, levin_header.CB) set_timeout(&connection) read_bytes, err = io.ReadFull(conn, data)
rlog.Tracef(10, "Read %d bytes from network for data \n", read_bytes) if err != nil { rlog.Tracef(4, "Error while reading levin data exiting err:%s\n", err) connection.Exit = true continue }
name := COMMAND_NAME[levin_header.Command] if name == "" { connection.logger.Warnf("No Such command %d exiting\n", levin_header.Command) connection.Exit = true continue }
//connection.logger.WithFields(log.Fields{
// "command": name,
// "flags": levin_header.Flags}).Debugf("Incoming Command")
if levin_header.Flags == LEVIN_PACKET_RESPONSE { if connection.Command_queue.Len() < 1 { connection.logger.Warnf("Invalid Response ( we have not queued anything\n") connection.Exit = true continue }
front_command := connection.Command_queue.Front() if levin_header.Command != front_command.Value.(uint32) { connection.logger.Warnf("Invalid Response ( we queued some other command\n") connection.Exit = true continue }
connection.Lock() connection.Command_queue.Remove(front_command) connection.Unlock()
switch levin_header.Command { case P2P_COMMAND_HANDSHAKE: // Parse incoming handshake response
Handle_P2P_Handshake_Command_Response(&connection, &levin_header, data) // if response is OK, mark conncection as good and add it to list
case P2P_COMMAND_TIMED_SYNC: // we never send timed response
// connection.logger.Infof("Response for timed sync arrived")
Handle_P2P_Timed_Sync_Response(&connection, &levin_header, data)
case P2P_COMMAND_PING: // we never send ping packets
case P2P_COMMAND_REQUEST_SUPPORT_FLAGS: // we never send flags packet
}
}
if levin_header.Flags == LEVIN_PACKET_REQUEST {
switch levin_header.Command { case P2P_COMMAND_HANDSHAKE: // send response
connection.logger.Debugf("Incoming handshake command") Handle_P2P_Handshake_Command(&connection, &levin_header, data)
case P2P_COMMAND_REQUEST_SUPPORT_FLAGS: // send reponse
Handle_P2P_Support_Flags(&connection, &levin_header, data)
case P2P_COMMAND_TIMED_SYNC: Handle_P2P_Timed_Sync(&connection, &levin_header, data) // crypto note core protocols commands related to blockchain
// peer wants to syncronise his chain to ours
case BC_NOTIFY_REQUEST_CHAIN: Handle_BC_Notify_Chain(&connection, &levin_header, data) // we want to syncronise our chain to peers
case BC_NOTIFY_RESPONSE_CHAIN_ENTRY: Handle_BC_Notify_Response_Chain_Entry(&connection, &levin_header, data)
case BC_NOTIFY_REQUEST_GET_OBJECTS: // peer requested some object
Handle_BC_Notify_Request_GetObjects(&connection, &levin_header, data) case BC_NOTIFY_RESPONSE_GET_OBJECTS: // peer responded to our object requests
Handle_BC_Notify_Response_GetObjects(&connection, &levin_header, data) case BC_NOTIFY_NEW_TRANSACTIONS: Handle_BC_Notify_New_Transactions(&connection, &levin_header, data) case BC_NOTIFY_NEW_BLOCK: Handle_BC_Notify_New_Block(&connection, &levin_header, data)
} } } }
/* this operation can never fail */ func SerializeLevinHeader(header Levin_Header) []byte {
packed_buffer := make([]byte, 33, 33)
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE) // packed 8 bytes
binary.LittleEndian.PutUint64(packed_buffer[8:16], header.CB) // packed 8 + 8 bytes
if header.ReturnData { packed_buffer[16] = 1 // packed 8 + 8 + 1
}
binary.LittleEndian.PutUint32(packed_buffer[17:17+4], header.Command) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[21:21+4], uint32(header.ReturnCode)) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[25:25+4], header.Flags) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[29:29+4], LEVIN_PROTOCOL_VER_1) // packed 8+8+1+4 bytes
return packed_buffer }
func (header Levin_Header) Serialize() ([]byte, int) { packed_buffer := make([]byte, 33, 33)
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE) // packed 8 bytes
binary.LittleEndian.PutUint64(packed_buffer[8:16], header.CB) // packed 8 + 8 bytes
if header.ReturnData { packed_buffer[16] = 1 // packed 8 + 8 + 1
}
binary.LittleEndian.PutUint32(packed_buffer[17:17+4], header.Command) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[21:21+4], uint32(header.ReturnCode)) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[25:25+4], header.Flags) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[29:29+4], LEVIN_PROTOCOL_VER_1) // packed 8+8+1+4 bytes
return packed_buffer, len(packed_buffer)
}
// extract structure info from hardcoded node
func (header *Levin_Header) DeSerialize(packed_buffer []byte) (err error) {
if len(packed_buffer) != 33 { return fmt.Errorf("Insufficient header bytes") }
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
if header.Signature != LEVIN_SIGNATURE { return fmt.Errorf("Incorrect Levin Signature") } header.CB = binary.LittleEndian.Uint64(packed_buffer[8:16]) // packed 8 + 8 bytes
if packed_buffer[16] == 0 { header.ReturnData = false // packed 8 + 8 + 1
} else { header.ReturnData = true // packed 8 + 8 + 1
}
header.Command = binary.LittleEndian.Uint32(packed_buffer[17 : 17+4]) // packed 8+8+1+4 bytes
header.ReturnCode = (int32)(binary.LittleEndian.Uint32(packed_buffer[21 : 21+4])) // packed 8+8+1+4 bytes
header.Flags = binary.LittleEndian.Uint32(packed_buffer[25 : 25+4]) // packed 8+8+1+4 bytes
header.Protocol_Version = binary.LittleEndian.Uint32(packed_buffer[29 : 29+4]) // packed 8+8+1+4 bytes
return nil
}
func (header Levin_Data_Header) Serialize() ([]byte, int) {
var packed_buffer []byte // if nothing is to be placed
if len(header.Data) == 0 { packed_buffer = make([]byte, 8+2, 8+2) // 10 bytes minimum heade
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE_DATA) // packed 8 bytes
packed_buffer[8] = 1 packed_buffer[9] = 0
return packed_buffer, len(packed_buffer)
} packed_buffer = make([]byte, 8+2+len(header.Data), 8+2+len(header.Data))
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE_DATA) // packed 8 bytes
packed_buffer[8] = 1 packed_buffer[9] = 8 copy(packed_buffer[10:], header.Data)
return packed_buffer, len(packed_buffer)
}
// extract structure info from hardcoded node
func (header *Levin_Data_Header) DeSerialize(packed_buffer []byte) (err error) {
if len(packed_buffer) < 10 { return fmt.Errorf("Insufficient header bytes") }
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
if header.Signature != LEVIN_SIGNATURE_DATA { return fmt.Errorf("WRONG LEVIN_SIGNATURE_DATA") }
if len(packed_buffer)-8 == 2 { return nil } header.Data = make([]byte, len(packed_buffer)-8+2, len(packed_buffer)-8+2) // ignore 2 bytes
// packed_buffer[8]=1 // version
// packed_buffer[9]=8 // boost 8 , this can be anything as per boost level
copy(header.Data, packed_buffer[10:])
return nil
}
func DeSerializeLevinHeader(packed_buffer []byte, header *Levin_Header) error {
if len(packed_buffer) != 33 { return fmt.Errorf("Insufficient header bytes") }
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
header.CB = binary.LittleEndian.Uint64(packed_buffer[8:16]) // packed 8 + 8 bytes
if packed_buffer[16] == 0 { header.ReturnData = false // packed 8 + 8 + 1
} else { header.ReturnData = true // packed 8 + 8 + 1
}
header.Command = binary.LittleEndian.Uint32(packed_buffer[17 : 17+4]) // packed 8+8+1+4 bytes
header.ReturnCode = (int32)(binary.LittleEndian.Uint32(packed_buffer[21 : 21+4])) // packed 8+8+1+4 bytes
header.Flags = binary.LittleEndian.Uint32(packed_buffer[25 : 25+4]) // packed 8+8+1+4 bytes
header.Protocol_Version = binary.LittleEndian.Uint32(packed_buffer[29 : 29+4]) // packed 8+8+1+4 bytes
return nil }
func TestSerializeDeserialize(t *testing.T) {
}
|