Browse Source

[node] unexported unnecessary struct fields, divided transferInfo into sending and receiving parts; implemented yet untested sendDirectoryPacket

main
Unbewohnte 3 years ago
parent
commit
205fbcc2d3
  1. 276
      src/node/node.go
  2. 32
      src/node/transfer.go

276
src/node/node.go

@ -19,57 +19,90 @@ import (
) )
// node-controlling states // node-controlling states
type NodeInnerStates struct { type nodeInnerstates struct {
Stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer Stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
} }
// Network specific settings // netInfowork specific settings
type Net struct { type netInfoInfo struct {
ConnAddr string // address to connect to. Does not include port ConnAddr string // address to connect to. Does not include port
Conn net.Conn // the core TCP connection of the node. Self-explanatory Conn net.Conn // the core TCP connection of the node. Self-explanatory
Port uint // a port to connect to/listen on Port uint // a port to connect to/listen on
EncryptionKey []byte // if != nil - incoming packets will be decrypted with it and outcoming packets will be encrypted EncryptionKey []byte // if != nil - incoming packets will be decrypted with it and outcoming packets will be encrypted
} }
// Both sending-side and receiving-side information // Sending-side node information
type TransferInfo struct { type sending struct {
Ready bool // is the other node ready to receive another piece ServingPath string // path to the thing that will be sent
ServingPath string // path to the thing that will be sent IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece
}
// Receiving-side node information
type receiving struct {
AcceptedFiles []*fsys.File // files that`ve been accepted to be received AcceptedFiles []*fsys.File // files that`ve been accepted to be received
DownloadsPath string // where to download DownloadsPath string // where to download
} }
// Both sending-side and receiving-side information
type transferInfo struct {
Receiving *receiving
Sending *sending
}
// Sender and receiver in one type ! // Sender and receiver in one type !
type Node struct { type Node struct {
PacketPipe chan *protocol.Packet // a way to receive incoming packets from another goroutine packetPipe chan *protocol.Packet // a way to receive incoming packets from another goroutine
IsSending bool // sending or a receiving node isSending bool // sending or a receiving node
Net *Net netInfo *netInfoInfo
State *NodeInnerStates state *nodeInnerstates
TransferInfo *TransferInfo transferInfo *transferInfo
} }
// Creates a new either a sending or receiving node with specified options // Creates a new either a sending or receiving node with specified options
func NewNode(options *NodeOptions) (*Node, error) { func NewNode(options *NodeOptions) (*Node, error) {
var isDir bool
if options.IsSending {
sendingPathStats, err := os.Stat(options.ServerSide.ServingPath)
if err != nil {
return nil, err
}
switch sendingPathStats.IsDir() {
case true:
isDir = true
case false:
isDir = false
}
}
node := Node{ node := Node{
PacketPipe: make(chan *protocol.Packet, 100), packetPipe: make(chan *protocol.Packet, 100),
IsSending: options.IsSending, isSending: options.IsSending,
Net: &Net{ netInfo: &netInfoInfo{
Port: options.WorkingPort, Port: options.WorkingPort,
ConnAddr: options.ClientSide.ConnectionAddr, ConnAddr: options.ClientSide.ConnectionAddr,
EncryptionKey: nil, EncryptionKey: nil,
Conn: nil, Conn: nil,
}, },
State: &NodeInnerStates{ state: &nodeInnerstates{
AllowedToTransfer: false, AllowedToTransfer: false,
Stopped: false, Stopped: false,
}, },
TransferInfo: &TransferInfo{ transferInfo: &transferInfo{
ServingPath: options.ServerSide.ServingPath, Sending: &sending{
Recursive: options.ServerSide.Recursive, ServingPath: options.ServerSide.ServingPath,
AcceptedFiles: nil, Recursive: options.ServerSide.Recursive,
DownloadsPath: options.ClientSide.DownloadsFolderPath, IsDirectory: isDir,
},
Receiving: &receiving{
AcceptedFiles: nil,
DownloadsPath: options.ClientSide.DownloadsFolderPath,
},
}, },
} }
return &node, nil return &node, nil
@ -77,41 +110,41 @@ func NewNode(options *NodeOptions) (*Node, error) {
// Connect node to another listening one with a pre-defined address&&port // Connect node to another listening one with a pre-defined address&&port
func (node *Node) connect() error { func (node *Node) connect() error {
if node.Net.Port == 0 { if node.netInfo.Port == 0 {
node.Net.Port = 7270 node.netInfo.Port = 7270
} }
fmt.Printf("Connecting to %s:%d...\n", node.Net.ConnAddr, node.Net.Port) fmt.Printf("Connecting to %s:%d...\n", node.netInfo.ConnAddr, node.netInfo.Port)
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", node.Net.ConnAddr, node.Net.Port), time.Second*5) conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", node.netInfo.ConnAddr, node.netInfo.Port), time.Second*5)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Connected\n") fmt.Printf("Connected\n")
node.Net.Conn = conn node.netInfo.Conn = conn
return nil return nil
} }
// Notify the other node and close the connection // Notify the other node and close the connection
func (node *Node) disconnect() error { func (node *Node) disconnect() error {
if node.Net.Conn != nil { if node.netInfo.Conn != nil {
// notify the other node and close the connection // notify the other node and close the connection
err := protocol.SendPacket(node.Net.Conn, protocol.Packet{ err := protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDisconnecting, Header: protocol.HeaderDisconnecting,
}) })
if err != nil { if err != nil {
return err return err
} }
err = node.Net.Conn.Close() err = node.netInfo.Conn.Close()
if err != nil { if err != nil {
return err return err
} }
node.State.Stopped = true node.state.Stopped = true
} }
return nil return nil
@ -119,7 +152,7 @@ func (node *Node) disconnect() error {
// Wait for a connection on a pre-defined port // Wait for a connection on a pre-defined port
func (node *Node) waitForConnection() error { func (node *Node) waitForConnection() error {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", node.Net.Port)) listener, err := net.Listen("tcp", fmt.Sprintf(":%d", node.netInfo.Port))
if err != nil { if err != nil {
return err return err
} }
@ -132,14 +165,14 @@ func (node *Node) waitForConnection() error {
fmt.Printf("New connection from %s\n", connection.RemoteAddr().String()) fmt.Printf("New connection from %s\n", connection.RemoteAddr().String())
node.Net.Conn = connection node.netInfo.Conn = connection
return nil return nil
} }
// Starts the node in either sending or receiving state and performs the transfer // Starts the node in either sending or receiving state and performs the transfer
func (node *Node) Start() { func (node *Node) Start() {
switch node.IsSending { switch node.isSending {
case true: case true:
// SENDER // SENDER
@ -149,11 +182,22 @@ func (node *Node) Start() {
panic(err) panic(err)
} }
file, err := fsys.GetFile(node.TransferInfo.ServingPath) var file *fsys.File
if err != nil { var dir *fsys.Directory
panic(err) switch node.transferInfo.Sending.IsDirectory {
case true:
dir, err = fsys.GetDir(node.transferInfo.Sending.ServingPath, node.transferInfo.Sending.Recursive)
if err != nil {
panic(err)
}
case false:
file, err = fsys.GetFile(node.transferInfo.Sending.ServingPath)
if err != nil {
panic(err)
}
} }
fmt.Printf("Sending \"%s\" (%.2f MB) locally on %s:%d\n", file.Name, float32(file.Size)/1024/1024, localIP, node.Net.Port)
fmt.Printf("Sending \"%s\" (%.2f MB) locally on %s:%d\n", file.Name, float32(file.Size)/1024/1024, localIP, node.netInfo.Port)
// wain for another node to connect // wain for another node to connect
err = node.waitForConnection() err = node.waitForConnection()
@ -163,35 +207,39 @@ func (node *Node) Start() {
// generate and send encryption key // generate and send encryption key
encrKey := encryption.Generate32AESkey() encrKey := encryption.Generate32AESkey()
node.Net.EncryptionKey = encrKey node.netInfo.EncryptionKey = encrKey
fmt.Printf("Generated encryption key: %s\n", encrKey) fmt.Printf("Generated encryption key: %s\n", encrKey)
err = sendEncryptionKey(node.Net.Conn, encrKey) err = sendEncryptionKey(node.netInfo.Conn, encrKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// listen for incoming packets // listen for incoming packets
go receivePackets(node.Net.Conn, node.PacketPipe) go receivePackets(node.netInfo.Conn, node.packetPipe)
// send info on file/directory // send info on file/directory
go sendFilePacket(node.Net.Conn, file, node.Net.EncryptionKey) if dir != nil {
go sendDirectoryPacket(node.netInfo.Conn, dir, node.netInfo.EncryptionKey)
} else {
go sendFilePacket(node.netInfo.Conn, file, node.netInfo.EncryptionKey)
}
// mainloop // mainloop
for { for {
if node.State.Stopped { if node.state.Stopped {
node.disconnect() node.disconnect()
break break
} }
// receive incoming packets and decrypt them if necessary // receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe incomingPacket, ok := <-node.packetPipe
if !ok { if !ok {
node.State.Stopped = true node.state.Stopped = true
} }
if node.Net.EncryptionKey != nil { if node.netInfo.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.Net.EncryptionKey) err = incomingPacket.DecryptBody(node.netInfo.EncryptionKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -201,69 +249,83 @@ func (node *Node) Start() {
switch incomingPacket.Header { switch incomingPacket.Header {
case protocol.HeaderReady: case protocol.HeaderReady:
// the other node is ready to receive file data // the other node is ready to receive file data
node.TransferInfo.Ready = true node.transferInfo.Sending.CanSendBytes = true
case protocol.HeaderAccept: case protocol.HeaderAccept:
node.State.AllowedToTransfer = true node.state.AllowedToTransfer = true
fmt.Printf("Transfer allowed. Sending...\n") fmt.Printf("Transfer allowed. Sending...\n")
case protocol.HeaderReject: case protocol.HeaderReject:
node.State.Stopped = true node.state.Stopped = true
fmt.Printf("Transfer rejected. Disconnecting...") fmt.Printf("Transfer rejected. Disconnecting...")
case protocol.HeaderDisconnecting: case protocol.HeaderDisconnecting:
node.State.Stopped = true node.state.Stopped = true
fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) fmt.Printf("%s disconnected\n", node.netInfo.Conn.RemoteAddr())
} }
// if allowed to transfer and the other node is ready to receive packets - send one piece // if allowed to transfer and the other node is ready to receive packets - send one piece
// and wait for it to be ready again // and wait for it to be ready again
if node.State.AllowedToTransfer && node.TransferInfo.Ready { if node.state.AllowedToTransfer && node.transferInfo.Sending.CanSendBytes {
err = sendPiece(file, node.Net.Conn, node.Net.EncryptionKey) // handle a single file or a directory
switch err { switch node.transferInfo.Sending.IsDirectory {
case ErrorSentAll: case true:
// the file has been sent fully // sending a file in a directory (possibly more than 1)
fileIDBuff := new(bytes.Buffer) if !node.transferInfo.Sending.Recursive {
err = binary.Write(fileIDBuff, binary.BigEndian, file.ID)
if err != nil { } else {
panic(err)
}
endFilePacket := protocol.Packet{
Header: protocol.HeaderEndfile,
Body: fileIDBuff.Bytes(),
} }
if node.Net.EncryptionKey != nil { case false:
err = endFilePacket.EncryptBody(node.Net.EncryptionKey) // sending a single file
err = sendPiece(file, node.netInfo.Conn, node.netInfo.EncryptionKey)
switch err {
case ErrorSentAll:
// the file has been sent fully
fileIDBuff := new(bytes.Buffer)
err = binary.Write(fileIDBuff, binary.BigEndian, file.ID)
if err != nil { if err != nil {
panic(err) panic(err)
} }
}
protocol.SendPacket(node.Net.Conn, endFilePacket) endFilePacket := protocol.Packet{
Header: protocol.HeaderEndfile,
Body: fileIDBuff.Bytes(),
}
// because there`s still no handling for directories - send if node.netInfo.EncryptionKey != nil {
// done packet err = endFilePacket.EncryptBody(node.netInfo.EncryptionKey)
protocol.SendPacket(node.Net.Conn, protocol.Packet{ if err != nil {
Header: protocol.HeaderDone, panic(err)
}) }
}
node.State.Stopped = true protocol.SendPacket(node.netInfo.Conn, endFilePacket)
case nil: // because there`s still no handling for directories - send
// done packet
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDone,
})
default: node.state.Stopped = true
node.State.Stopped = true
fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", file.Name, err) case nil:
panic(err)
default:
node.state.Stopped = true
fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", file.Name, err)
panic(err)
}
node.transferInfo.Sending.CanSendBytes = false
} }
node.TransferInfo.Ready = false
} }
} }
@ -273,27 +335,27 @@ func (node *Node) Start() {
// connect to the sending node // connect to the sending node
err := node.connect() err := node.connect()
if err != nil { if err != nil {
fmt.Printf("Could not connect to %s:%d\n", node.Net.ConnAddr, node.Net.Port) fmt.Printf("Could not connect to %s:%d\n", node.netInfo.ConnAddr, node.netInfo.Port)
os.Exit(-1) os.Exit(-1)
} }
// listen for incoming packets // listen for incoming packets
go receivePackets(node.Net.Conn, node.PacketPipe) go receivePackets(node.netInfo.Conn, node.packetPipe)
// mainloop // mainloop
for { for {
if node.State.Stopped { if node.state.Stopped {
node.disconnect() node.disconnect()
break break
} }
// receive incoming packets and decrypt them if necessary // receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe incomingPacket, ok := <-node.packetPipe
if !ok { if !ok {
break break
} }
if node.Net.EncryptionKey != nil { if node.netInfo.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.Net.EncryptionKey) err = incomingPacket.DecryptBody(node.netInfo.EncryptionKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -322,12 +384,12 @@ func (node *Node) Start() {
if strings.EqualFold(answer, "y") || answer == "" { if strings.EqualFold(answer, "y") || answer == "" {
// yes // yes
err = os.MkdirAll(node.TransferInfo.DownloadsPath, os.ModePerm) err = os.MkdirAll(node.transferInfo.Receiving.DownloadsPath, os.ModePerm)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fullFilePath := filepath.Join(node.TransferInfo.DownloadsPath, file.Name) fullFilePath := filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.Name)
// check if the file already exists; if yes - remove it and replace with a new one // check if the file already exists; if yes - remove it and replace with a new one
_, err := os.Stat(fullFilePath) _, err := os.Stat(fullFilePath)
@ -340,28 +402,28 @@ func (node *Node) Start() {
file.Path = fullFilePath file.Path = fullFilePath
file.Open() file.Open()
node.TransferInfo.AcceptedFiles = append(node.TransferInfo.AcceptedFiles, file) node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles, file)
// send aceptance packet // send aceptance packet
acceptancePacket := protocol.Packet{ acceptancePacket := protocol.Packet{
Header: protocol.HeaderAccept, Header: protocol.HeaderAccept,
Body: responsePacketFileIDBuffer.Bytes(), Body: responsePacketFileIDBuffer.Bytes(),
} }
if node.Net.EncryptionKey != nil { if node.netInfo.EncryptionKey != nil {
err = acceptancePacket.EncryptBody(node.Net.EncryptionKey) err = acceptancePacket.EncryptBody(node.netInfo.EncryptionKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
err = protocol.SendPacket(node.Net.Conn, acceptancePacket) err = protocol.SendPacket(node.netInfo.Conn, acceptancePacket)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// notify the node that we`re ready to transportation. No need // notify the node that we`re ready to transportation. No need
// for encryption because the body is nil // for encryption because the body is nil
err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ err = protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderReady, Header: protocol.HeaderReady,
}) })
if err != nil { if err != nil {
@ -375,19 +437,19 @@ func (node *Node) Start() {
Body: responsePacketFileIDBuffer.Bytes(), Body: responsePacketFileIDBuffer.Bytes(),
} }
if node.Net.EncryptionKey != nil { if node.netInfo.EncryptionKey != nil {
err = rejectionPacket.EncryptBody(node.Net.EncryptionKey) err = rejectionPacket.EncryptBody(node.netInfo.EncryptionKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
err = protocol.SendPacket(node.Net.Conn, rejectionPacket) err = protocol.SendPacket(node.netInfo.Conn, rejectionPacket)
if err != nil { if err != nil {
panic(err) panic(err)
} }
node.State.Stopped = true node.state.Stopped = true
} }
}() }()
@ -400,7 +462,7 @@ func (node *Node) Start() {
panic(err) panic(err)
} }
for _, acceptedFile := range node.TransferInfo.AcceptedFiles { for _, acceptedFile := range node.transferInfo.Receiving.AcceptedFiles {
if acceptedFile.ID == fileID { if acceptedFile.ID == fileID {
// accepted // accepted
@ -415,7 +477,7 @@ func (node *Node) Start() {
} }
// notify the other one that this node is ready // notify the other one that this node is ready
err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ err = protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderReady, Header: protocol.HeaderReady,
}) })
if err != nil { if err != nil {
@ -434,7 +496,7 @@ func (node *Node) Start() {
panic(err) panic(err)
} }
for index, acceptedFile := range node.TransferInfo.AcceptedFiles { for index, acceptedFile := range node.transferInfo.Receiving.AcceptedFiles {
if acceptedFile.ID == fileID { if acceptedFile.ID == fileID {
// accepted // accepted
@ -442,7 +504,7 @@ func (node *Node) Start() {
defer acceptedFile.Handler.Close() defer acceptedFile.Handler.Close()
// remove this file from the pool // remove this file from the pool
node.TransferInfo.AcceptedFiles = append(node.TransferInfo.AcceptedFiles[:index], node.TransferInfo.AcceptedFiles[index+1:]...) node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles[:index], node.transferInfo.Receiving.AcceptedFiles[index+1:]...)
// compare checksums // compare checksums
realChecksum, err := checksum.GetPartialCheckSum(acceptedFile.Handler) realChecksum, err := checksum.GetPartialCheckSum(acceptedFile.Handler)
@ -461,7 +523,7 @@ func (node *Node) Start() {
} }
} }
// node.State.Stopped = true // node.state.Stopped = true
case protocol.HeaderEncryptionKey: case protocol.HeaderEncryptionKey:
// retrieve the key // retrieve the key
@ -473,15 +535,15 @@ func (node *Node) Start() {
encrKey := make([]byte, keySize) encrKey := make([]byte, keySize)
packetReader.Read(encrKey) packetReader.Read(encrKey)
node.Net.EncryptionKey = encrKey node.netInfo.EncryptionKey = encrKey
case protocol.HeaderDone: case protocol.HeaderDone:
node.State.Stopped = true node.state.Stopped = true
case protocol.HeaderDisconnecting: case protocol.HeaderDisconnecting:
node.State.Stopped = true node.state.Stopped = true
fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) fmt.Printf("%s disconnected\n", node.netInfo.Conn.RemoteAddr())
} }
} }

32
src/node/transfer.go

@ -24,7 +24,7 @@ func sendFilePacket(connection net.Conn, file *fsys.File, encrKey []byte) error
return err return err
} }
// FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)checksum // FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)(checksum)
// send file packet with file description // send file packet with file description
filePacket := protocol.Packet{ filePacket := protocol.Packet{
@ -69,14 +69,22 @@ func sendFilePacket(connection net.Conn, file *fsys.File, encrKey []byte) error
return err return err
} }
// we do not check for packet size because there is no way that it`ll exceed current
// maximum of 128 KiB
return nil return nil
} }
// sends a notification about the directory // sends a notification about the directory. If encrkey != nil - encrypts
func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error { // packet`s body
func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory, encrKey []byte) error {
if connection == nil { if connection == nil {
return ErrorNotConnected return ErrorNotConnected
} }
dirPacket := protocol.Packet{
Header: protocol.HeaderDirectory,
}
// DIRECTORY~(dirname size in binary)(dirname)(dirsize)(checksumLengthInBinary)(checksum) // DIRECTORY~(dirname size in binary)(dirname)(dirsize)(checksumLengthInBinary)(checksum)
dirPacketBuffer := new(bytes.Buffer) dirPacketBuffer := new(bytes.Buffer)
@ -103,6 +111,24 @@ func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error {
} }
dirPacketBuffer.Write([]byte(dir.Checksum)) dirPacketBuffer.Write([]byte(dir.Checksum))
dirPacket.Body = dirPacketBuffer.Bytes()
if encrKey != nil {
// if the key is given - encrypt ready-to-go packet
err = dirPacket.EncryptBody(encrKey)
if err != nil {
return err
}
}
// and send it
err = protocol.SendPacket(connection, dirPacket)
if err != nil {
return err
}
// we do not check for packet size because there is no way that it`ll exceed current
// maximum of 128 KiB
return nil return nil
} }

Loading…
Cancel
Save