diff --git a/src/fsys/dir.go b/src/fsys/dir.go index 7e0d43d..f8820e9 100644 --- a/src/fsys/dir.go +++ b/src/fsys/dir.go @@ -14,15 +14,11 @@ type Directory struct { Size uint64 Files []*File Directories []*Directory + Checksum string // Set manually } var ErrorNotDirectory error = fmt.Errorf("not a directory") -// // gets a child directory -// func getDirChild(path string, parentDir *Directory, recursive bool) (*Directory, error) { - -// } - func GetDir(path string, recursive bool) (*Directory, error) { absPath, err := filepath.Abs(path) if err != nil { @@ -53,7 +49,6 @@ func GetDir(path string, recursive bool) (*Directory, error) { return nil, err } - // var totalSize uint64 = 0 var innerDirs []*Directory var innerFiles []*File for _, entry := range entries { @@ -94,7 +89,6 @@ func GetDir(path string, recursive bool) (*Directory, error) { directory.Directories = innerDirs directory.Files = innerFiles - // directory.Size return &directory, nil } diff --git a/src/node/errors.go b/src/node/errors.go deleted file mode 100644 index a39fc3a..0000000 --- a/src/node/errors.go +++ /dev/null @@ -1,8 +0,0 @@ -package node - -import "fmt" - -var ( - ErrorNotConnected error = fmt.Errorf("not connected") - ErrorSentAll error = fmt.Errorf("sent the whole file") -) diff --git a/src/node/node.go b/src/node/node.go index c306989..6f335fc 100644 --- a/src/node/node.go +++ b/src/node/node.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "strings" - "sync" "time" "fmt" @@ -19,46 +18,42 @@ import ( "github.com/Unbewohnte/ftu/protocol" ) +// node-controlling states type NodeInnerStates struct { - Stopped bool - Connected bool - AllowedToTransfer bool + 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 { - ConnAddr string - Conn net.Conn - Port uint - EncryptionKey []byte + 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 - ServingPath string // path to the thing that will be sent - Recursive bool - AcceptedFiles []*fsys.File - DownloadsPath string + Ready bool // is the other node ready to receive another piece + ServingPath string // path to the thing that will be sent + Recursive bool // recursively send directory + AcceptedFiles []*fsys.File // files that`ve been accepted to be received + DownloadsPath string // where to download } // Sender and receiver in one type ! type Node struct { - PacketPipe chan *protocol.Packet - ErrorPipe chan error - Mutex *sync.Mutex - IsSending bool + 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 } -// Creates a new node +// Creates a new either a sending or receiving node with specified options func NewNode(options *NodeOptions) (*Node, error) { - mutex := new(sync.Mutex) - node := Node{ PacketPipe: make(chan *protocol.Packet, 100), - ErrorPipe: make(chan error, 100), - Mutex: mutex, IsSending: options.IsSending, Net: &Net{ Port: options.WorkingPort, @@ -69,7 +64,6 @@ func NewNode(options *NodeOptions) (*Node, error) { State: &NodeInnerStates{ AllowedToTransfer: false, Stopped: false, - Connected: false, }, TransferInfo: &TransferInfo{ ServingPath: options.ServerSide.ServingPath, @@ -81,14 +75,15 @@ func NewNode(options *NodeOptions) (*Node, error) { return &node, nil } -func (node *Node) connect(addr string, port uint) error { - if port == 0 { - port = node.Net.Port +// 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 } - fmt.Printf("Connecting to %s:%d...\n", addr, port) + fmt.Printf("Connecting to %s:%d...\n", node.Net.ConnAddr, node.Net.Port) - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", addr, port), time.Second*5) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", node.Net.ConnAddr, node.Net.Port), time.Second*5) if err != nil { return err } @@ -96,13 +91,13 @@ func (node *Node) connect(addr string, port uint) error { fmt.Printf("Connected\n") node.Net.Conn = conn - node.State.Connected = true return nil } +// Notify the other node and close the connection func (node *Node) disconnect() error { - if node.State.Connected && node.Net.Conn != nil { + if node.Net.Conn != nil { // notify the other node and close the connection err := protocol.SendPacket(node.Net.Conn, protocol.Packet{ Header: protocol.HeaderDisconnecting, @@ -117,13 +112,12 @@ func (node *Node) disconnect() error { } node.State.Stopped = true - node.State.Connected = false } return nil } -// Waits for connection on a pre-defined port +// 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)) if err != nil { @@ -139,7 +133,6 @@ func (node *Node) waitForConnection() error { fmt.Printf("New connection from %s\n", connection.RemoteAddr().String()) node.Net.Conn = connection - node.State.Connected = true return nil } @@ -150,6 +143,7 @@ func (node *Node) Start() { case true: // SENDER + // retrieve necessary information, wait for connection localIP, err := addr.GetLocal() if err != nil { panic(err) @@ -180,7 +174,7 @@ func (node *Node) Start() { // listen for incoming packets go receivePackets(node.Net.Conn, node.PacketPipe) - // send fileoffer + // send info on file/directory go sendFilePacket(node.Net.Conn, file, node.Net.EncryptionKey) // mainloop @@ -190,6 +184,7 @@ func (node *Node) Start() { break } + // receive incoming packets and decrypt them if necessary incomingPacket, ok := <-node.PacketPipe if !ok { node.State.Stopped = true @@ -202,8 +197,10 @@ func (node *Node) Start() { } } + // react based on a header of a received packet switch incomingPacket.Header { case protocol.HeaderReady: + // the other node is ready to receive file data node.TransferInfo.Ready = true case protocol.HeaderAccept: @@ -211,18 +208,20 @@ func (node *Node) Start() { fmt.Printf("Transfer allowed. Sending...\n") - case protocol.HeaderDisconnecting: + case protocol.HeaderReject: node.State.Stopped = true - fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) + fmt.Printf("Transfer rejected. Disconnecting...") - case protocol.HeaderReject: + case protocol.HeaderDisconnecting: node.State.Stopped = true - fmt.Printf("Transfer rejected. Disconnecting...") + fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) } - if node.State.AllowedToTransfer || node.TransferInfo.Ready { + // 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 err != nil { if err == ErrorSentAll { @@ -264,7 +263,7 @@ func (node *Node) Start() { // RECEIVER // connect to the sending node - err := node.connect(node.Net.ConnAddr, node.Net.Port) + err := node.connect() if err != nil { fmt.Printf("Could not connect to %s:%d\n", node.Net.ConnAddr, node.Net.Port) os.Exit(-1) @@ -280,6 +279,7 @@ func (node *Node) Start() { break } + // receive incoming packets and decrypt them if necessary incomingPacket, ok := <-node.PacketPipe if !ok { break @@ -291,9 +291,11 @@ func (node *Node) Start() { } } + // react based on a header of a received packet switch incomingPacket.Header { case protocol.HeaderFile: + // process an information about a singe file. Accept or reject the transfer go func() { file, err := decodeFilePacket(incomingPacket) if err != nil { @@ -404,6 +406,7 @@ func (node *Node) Start() { } } + // notify the other one that this node is ready err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ Header: protocol.HeaderReady, }) @@ -412,6 +415,10 @@ func (node *Node) Start() { } case protocol.HeaderEndfile: + // one of the files has been received completely, + // compare checksums and check if it is the last + // file in the transfer + // (TODO) fileIDReader := bytes.NewReader(incomingPacket.Body) var fileID uint64 err := binary.Read(fileIDReader, binary.BigEndian, &fileID) diff --git a/src/node/packets.go b/src/node/packets.go index 1f00675..0cf722e 100644 --- a/src/node/packets.go +++ b/src/node/packets.go @@ -4,6 +4,7 @@ package node import ( "bytes" "encoding/binary" + "fmt" "net" "github.com/Unbewohnte/ftu/fsys" @@ -34,6 +35,8 @@ func sendEncryptionKey(connection net.Conn, encrKey []byte) error { return nil } +var ErrorNotConnected error = fmt.Errorf("not connected") + // Reads packets from connection in an endless loop, sends them to the channel, if encrKey is not nil - // decrypts packet`s body beforehand func receivePackets(connection net.Conn, packetPipe chan *protocol.Packet) error { diff --git a/src/node/transfer.go b/src/node/transfer.go index 8afdef3..0a07cc3 100644 --- a/src/node/transfer.go +++ b/src/node/transfer.go @@ -3,6 +3,7 @@ package node import ( "bytes" "encoding/binary" + "fmt" "io" "net" "os" @@ -76,10 +77,37 @@ func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error { if connection == nil { return ErrorNotConnected } + // DIRECTORY~(dirname size in binary)(dirname)(dirsize)(checksumLengthInBinary)(checksum) + + dirPacketBuffer := new(bytes.Buffer) + + // dirname + dirnameLength := uint64(len(dir.Name)) + err := binary.Write(dirPacketBuffer, binary.BigEndian, &dirnameLength) + if err != nil { + return err + } + dirPacketBuffer.Write([]byte(dir.Name)) + + // dirsize + err = binary.Write(dirPacketBuffer, binary.BigEndian, dir.Size) + if err != nil { + return err + } + + // checksum + checksumLength := uint64(len(dir.Checksum)) + err = binary.Write(dirPacketBuffer, binary.BigEndian, &checksumLength) + if err != nil { + return err + } + dirPacketBuffer.Write([]byte(dir.Checksum)) return nil } +var ErrorSentAll error = fmt.Errorf("sent the whole file") + // sends a piece of file to the connection; The next calls will send // another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with // this key