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. 220
      src/node/node.go
  2. 32
      src/node/transfer.go

220
src/node/node.go

@ -19,99 +19,132 @@ import (
)
// 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
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
}
// Network specific settings
type Net struct {
// netInfowork specific settings
type netInfoInfo struct {
ConnAddr string // address to connect to. Does not include port
Conn net.Conn // the core TCP connection of the node. Self-explanatory
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
}
// Both sending-side and receiving-side information
type TransferInfo struct {
Ready bool // is the other node ready to receive another piece
// Sending-side node information
type sending struct {
ServingPath string // path to the thing that will be sent
IsDirectory bool // is ServingPath a 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
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 !
type Node struct {
PacketPipe chan *protocol.Packet // a way to receive incoming packets from another goroutine
IsSending bool // sending or a receiving node
Net *Net
State *NodeInnerStates
TransferInfo *TransferInfo
packetPipe chan *protocol.Packet // a way to receive incoming packets from another goroutine
isSending bool // sending or a receiving node
netInfo *netInfoInfo
state *nodeInnerstates
transferInfo *transferInfo
}
// Creates a new either a sending or receiving node with specified options
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{
PacketPipe: make(chan *protocol.Packet, 100),
IsSending: options.IsSending,
Net: &Net{
packetPipe: make(chan *protocol.Packet, 100),
isSending: options.IsSending,
netInfo: &netInfoInfo{
Port: options.WorkingPort,
ConnAddr: options.ClientSide.ConnectionAddr,
EncryptionKey: nil,
Conn: nil,
},
State: &NodeInnerStates{
state: &nodeInnerstates{
AllowedToTransfer: false,
Stopped: false,
},
TransferInfo: &TransferInfo{
transferInfo: &transferInfo{
Sending: &sending{
ServingPath: options.ServerSide.ServingPath,
Recursive: options.ServerSide.Recursive,
IsDirectory: isDir,
},
Receiving: &receiving{
AcceptedFiles: nil,
DownloadsPath: options.ClientSide.DownloadsFolderPath,
},
},
}
return &node, nil
}
// Connect node to another listening one with a pre-defined address&&port
func (node *Node) connect() error {
if node.Net.Port == 0 {
node.Net.Port = 7270
if node.netInfo.Port == 0 {
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 {
return err
}
fmt.Printf("Connected\n")
node.Net.Conn = conn
node.netInfo.Conn = conn
return nil
}
// Notify the other node and close the connection
func (node *Node) disconnect() error {
if node.Net.Conn != nil {
if node.netInfo.Conn != nil {
// 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,
})
if err != nil {
return err
}
err = node.Net.Conn.Close()
err = node.netInfo.Conn.Close()
if err != nil {
return err
}
node.State.Stopped = true
node.state.Stopped = true
}
return nil
@ -119,7 +152,7 @@ func (node *Node) disconnect() error {
// Wait for a connection on a pre-defined port
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 {
return err
}
@ -132,14 +165,14 @@ func (node *Node) waitForConnection() error {
fmt.Printf("New connection from %s\n", connection.RemoteAddr().String())
node.Net.Conn = connection
node.netInfo.Conn = connection
return nil
}
// Starts the node in either sending or receiving state and performs the transfer
func (node *Node) Start() {
switch node.IsSending {
switch node.isSending {
case true:
// SENDER
@ -149,11 +182,22 @@ func (node *Node) Start() {
panic(err)
}
file, err := fsys.GetFile(node.TransferInfo.ServingPath)
var file *fsys.File
var dir *fsys.Directory
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
err = node.waitForConnection()
@ -163,35 +207,39 @@ func (node *Node) Start() {
// generate and send encryption key
encrKey := encryption.Generate32AESkey()
node.Net.EncryptionKey = encrKey
node.netInfo.EncryptionKey = encrKey
fmt.Printf("Generated encryption key: %s\n", encrKey)
err = sendEncryptionKey(node.Net.Conn, encrKey)
err = sendEncryptionKey(node.netInfo.Conn, encrKey)
if err != nil {
panic(err)
}
// listen for incoming packets
go receivePackets(node.Net.Conn, node.PacketPipe)
go receivePackets(node.netInfo.Conn, node.packetPipe)
// 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
for {
if node.State.Stopped {
if node.state.Stopped {
node.disconnect()
break
}
// receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe
incomingPacket, ok := <-node.packetPipe
if !ok {
node.State.Stopped = true
node.state.Stopped = true
}
if node.Net.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.Net.EncryptionKey)
if node.netInfo.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.netInfo.EncryptionKey)
if err != nil {
panic(err)
}
@ -201,28 +249,40 @@ func (node *Node) Start() {
switch incomingPacket.Header {
case protocol.HeaderReady:
// the other node is ready to receive file data
node.TransferInfo.Ready = true
node.transferInfo.Sending.CanSendBytes = true
case protocol.HeaderAccept:
node.State.AllowedToTransfer = true
node.state.AllowedToTransfer = true
fmt.Printf("Transfer allowed. Sending...\n")
case protocol.HeaderReject:
node.State.Stopped = true
node.state.Stopped = true
fmt.Printf("Transfer rejected. Disconnecting...")
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
// and wait for it to be ready again
if node.State.AllowedToTransfer && node.TransferInfo.Ready {
err = sendPiece(file, node.Net.Conn, node.Net.EncryptionKey)
if node.state.AllowedToTransfer && node.transferInfo.Sending.CanSendBytes {
// handle a single file or a directory
switch node.transferInfo.Sending.IsDirectory {
case true:
// sending a file in a directory (possibly more than 1)
if !node.transferInfo.Sending.Recursive {
} else {
}
case false:
// sending a single file
err = sendPiece(file, node.netInfo.Conn, node.netInfo.EncryptionKey)
switch err {
case ErrorSentAll:
// the file has been sent fully
@ -237,33 +297,35 @@ func (node *Node) Start() {
Body: fileIDBuff.Bytes(),
}
if node.Net.EncryptionKey != nil {
err = endFilePacket.EncryptBody(node.Net.EncryptionKey)
if node.netInfo.EncryptionKey != nil {
err = endFilePacket.EncryptBody(node.netInfo.EncryptionKey)
if err != nil {
panic(err)
}
}
protocol.SendPacket(node.Net.Conn, endFilePacket)
protocol.SendPacket(node.netInfo.Conn, endFilePacket)
// because there`s still no handling for directories - send
// done packet
protocol.SendPacket(node.Net.Conn, protocol.Packet{
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDone,
})
node.State.Stopped = true
node.state.Stopped = true
case nil:
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)
panic(err)
}
node.TransferInfo.Ready = false
node.transferInfo.Sending.CanSendBytes = false
}
}
}
@ -273,27 +335,27 @@ func (node *Node) Start() {
// connect to the sending node
err := node.connect()
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)
}
// listen for incoming packets
go receivePackets(node.Net.Conn, node.PacketPipe)
go receivePackets(node.netInfo.Conn, node.packetPipe)
// mainloop
for {
if node.State.Stopped {
if node.state.Stopped {
node.disconnect()
break
}
// receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe
incomingPacket, ok := <-node.packetPipe
if !ok {
break
}
if node.Net.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.Net.EncryptionKey)
if node.netInfo.EncryptionKey != nil {
err = incomingPacket.DecryptBody(node.netInfo.EncryptionKey)
if err != nil {
panic(err)
}
@ -322,12 +384,12 @@ func (node *Node) Start() {
if strings.EqualFold(answer, "y") || answer == "" {
// yes
err = os.MkdirAll(node.TransferInfo.DownloadsPath, os.ModePerm)
err = os.MkdirAll(node.transferInfo.Receiving.DownloadsPath, os.ModePerm)
if err != nil {
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
_, err := os.Stat(fullFilePath)
@ -340,28 +402,28 @@ func (node *Node) Start() {
file.Path = fullFilePath
file.Open()
node.TransferInfo.AcceptedFiles = append(node.TransferInfo.AcceptedFiles, file)
node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles, file)
// send aceptance packet
acceptancePacket := protocol.Packet{
Header: protocol.HeaderAccept,
Body: responsePacketFileIDBuffer.Bytes(),
}
if node.Net.EncryptionKey != nil {
err = acceptancePacket.EncryptBody(node.Net.EncryptionKey)
if node.netInfo.EncryptionKey != nil {
err = acceptancePacket.EncryptBody(node.netInfo.EncryptionKey)
if err != nil {
panic(err)
}
}
err = protocol.SendPacket(node.Net.Conn, acceptancePacket)
err = protocol.SendPacket(node.netInfo.Conn, acceptancePacket)
if err != nil {
panic(err)
}
// notify the node that we`re ready to transportation. No need
// 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,
})
if err != nil {
@ -375,19 +437,19 @@ func (node *Node) Start() {
Body: responsePacketFileIDBuffer.Bytes(),
}
if node.Net.EncryptionKey != nil {
err = rejectionPacket.EncryptBody(node.Net.EncryptionKey)
if node.netInfo.EncryptionKey != nil {
err = rejectionPacket.EncryptBody(node.netInfo.EncryptionKey)
if err != nil {
panic(err)
}
}
err = protocol.SendPacket(node.Net.Conn, rejectionPacket)
err = protocol.SendPacket(node.netInfo.Conn, rejectionPacket)
if err != nil {
panic(err)
}
node.State.Stopped = true
node.state.Stopped = true
}
}()
@ -400,7 +462,7 @@ func (node *Node) Start() {
panic(err)
}
for _, acceptedFile := range node.TransferInfo.AcceptedFiles {
for _, acceptedFile := range node.transferInfo.Receiving.AcceptedFiles {
if acceptedFile.ID == fileID {
// accepted
@ -415,7 +477,7 @@ func (node *Node) Start() {
}
// 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,
})
if err != nil {
@ -434,7 +496,7 @@ func (node *Node) Start() {
panic(err)
}
for index, acceptedFile := range node.TransferInfo.AcceptedFiles {
for index, acceptedFile := range node.transferInfo.Receiving.AcceptedFiles {
if acceptedFile.ID == fileID {
// accepted
@ -442,7 +504,7 @@ func (node *Node) Start() {
defer acceptedFile.Handler.Close()
// 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
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:
// retrieve the key
@ -473,15 +535,15 @@ func (node *Node) Start() {
encrKey := make([]byte, keySize)
packetReader.Read(encrKey)
node.Net.EncryptionKey = encrKey
node.netInfo.EncryptionKey = encrKey
case protocol.HeaderDone:
node.State.Stopped = true
node.state.Stopped = true
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
}
// FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)checksum
// FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)(checksum)
// send file packet with file description
filePacket := protocol.Packet{
@ -69,14 +69,22 @@ func sendFilePacket(connection net.Conn, file *fsys.File, encrKey []byte) error
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
}
// sends a notification about the directory
func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error {
// sends a notification about the directory. If encrkey != nil - encrypts
// packet`s body
func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory, encrKey []byte) error {
if connection == nil {
return ErrorNotConnected
}
dirPacket := protocol.Packet{
Header: protocol.HeaderDirectory,
}
// DIRECTORY~(dirname size in binary)(dirname)(dirsize)(checksumLengthInBinary)(checksum)
dirPacketBuffer := new(bytes.Buffer)
@ -103,6 +111,24 @@ func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error {
}
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
}

Loading…
Cancel
Save