From 265a621543836d4464d2eb636d4d52dac0549d49 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Sat, 1 Apr 2023 20:39:49 +0200 Subject: [PATCH 01/19] [M, B] fixed wrong package naming --- network/frame.go | 18 +++++++++--------- network/listener.go | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/network/frame.go b/network/frame.go index db4ba2f..a4da25a 100644 --- a/network/frame.go +++ b/network/frame.go @@ -1,10 +1,10 @@ -// Package network +// Fusion +// +// Created by Vinzenz Weist on 17.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// -// Copyright 2021 Vinzenz Weist. All rights reserved. -// Use of this source code is risked by yourself. -// license that can be found in the LICENSE file. - -package main +package network import ( "crypto/sha256" @@ -20,10 +20,10 @@ type frame struct { } const ( - opcodeByteCount uint32 = 0x1 - controlByteCount uint32 = 0x5 + opcodeByteCount uint32 = 0x1 + controlByteCount uint32 = 0x5 overheadByteCount uint32 = 0x25 - frameByteCount uint32 = 0xFFFFFFFF + frameByteCount uint32 = 0xFFFFFFFF ) const ( diff --git a/network/listener.go b/network/listener.go index 33d3379..a078ee4 100644 --- a/network/listener.go +++ b/network/listener.go @@ -1,10 +1,10 @@ -// Package main +// Fusion +// +// Created by Vinzenz Weist on 17.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// -// Copyright 2021 Vinzenz Weist. All rights reserved. -// Use of this source code is risked by yourself. -// license that can be found in the LICENSE file. - -package main +package network import ( "crypto/tls" From c46621f0c3ee502e1c3c881772790f2954016859 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Sat, 1 Apr 2023 23:16:31 +0200 Subject: [PATCH 02/19] [M] cleanup + readme update --- .gitignore | 1 + .idea/.gitignore | 8 -------- .idea/modules.xml | 8 -------- .idea/network.iml | 9 --------- .idea/vcs.xml | 6 ------ README.md | 2 +- 6 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/modules.xml delete mode 100644 .idea/network.iml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 50a823f..88dbbc7 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ fabric.properties # .idea/misc.xml # *.ipr +.idea/ # Sonarlint plugin # https://plugins.jetbrains.com/plugin/7973-sonarlint .idea/**/sonarlint/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a82482f..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/network.iml b/.idea/network.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/network.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 8e8bbac..13ce595 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ `Fusion` is a library which implements the `NWC-Protocol`. The `NWC-Protocol` is proprietary networking protocol which uses a small and lightweight header with a performance as fast as raw tcp performance. Built directly on top of Go's `net.Listener` with support for plain tcp and tls encrypted connections. -The implementation for the Client is [NetworKit](https://github.com/Vinz1911/networkit) written in swift on top of `Network.framework` to ensure maximum performance. +The implementation for the Client is [FusionKit](https://github.com/Vinz1911/fusionkit) written in swift on top of `Network.framework` to ensure maximum performance. ## License: [![License](https://img.shields.io/badge/license-GPLv3-blue.svg?longCache=true&style=flat)](https://github.com/Vinz1911/network-go/blob/main/LICENSE) From fdf7e398150417bf091539f409f256da1a0266f9 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Thu, 26 Oct 2023 00:16:41 +0200 Subject: [PATCH 03/19] [M] removed hash func --- network/frame.go | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/network/frame.go b/network/frame.go index a4da25a..cca287a 100644 --- a/network/frame.go +++ b/network/frame.go @@ -7,9 +7,7 @@ package network import ( - "crypto/sha256" "encoding/binary" - "encoding/hex" "errors" ) @@ -22,7 +20,6 @@ type frame struct { const ( opcodeByteCount uint32 = 0x1 controlByteCount uint32 = 0x5 - overheadByteCount uint32 = 0x25 frameByteCount uint32 = 0xFFFFFFFF ) @@ -36,12 +33,11 @@ const ( // create is for creating compliant message frames // returns message as data frame func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { - if uint32(len(data)) > frameByteCount - overheadByteCount { return nil, errors.New(writeBufferOverflow) } + if uint32(len(data)) > frameByteCount - controlByteCount { return nil, errors.New(writeBufferOverflow) } var frame []byte - var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + overheadByteCount) + var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + controlByteCount) frame = append(frame, opcode) - frame = append(frame, length...); var hash = sha256.Sum256(frame[:controlByteCount]) - frame = append(frame, hash[:]...) + frame = append(frame, length...) frame = append(frame, data...) return frame, nil } @@ -53,11 +49,9 @@ func (frame *frame) parse(data []byte, completion func(text *string, data []byte var length = frame.extractSize() if length == nil { return nil } if uint32(len(frame.buffer)) > frameByteCount { return errors.New(readBufferOverflow) } - if uint32(len(frame.buffer)) < overheadByteCount { return nil } + if uint32(len(frame.buffer)) < controlByteCount { return nil } if len(frame.buffer) < *length { return nil } for len(frame.buffer) >= *length && *length != 0 { - var digest = sha256.Sum256(frame.buffer[:controlByteCount]) - if hex.EncodeToString(digest[:]) != *frame.extractHash() { return errors.New(hashMismatch) } var bytes, err = frame.extractMessage(frame.buffer) if err != nil { return err } switch frame.buffer[0] { @@ -72,19 +66,10 @@ func (frame *frame) parse(data []byte, completion func(text *string, data []byte // MARK: - Private API - -// extract the message hash from the data -// if not possible it returns nil -func (frame *frame) extractHash() *string { - if uint32(len(frame.buffer)) < overheadByteCount { return nil } - var hash = frame.buffer[controlByteCount:overheadByteCount] - var digest = hex.EncodeToString(hash[:]) - return &digest -} - // extract the message frame size from the data // if not possible it returns zero func (frame *frame) extractSize() *int { - if uint32(len(frame.buffer)) < overheadByteCount { return nil } + if uint32(len(frame.buffer)) < controlByteCount { return nil } var size = frame.buffer[opcodeByteCount:controlByteCount] var length = int(binary.BigEndian.Uint32(size)) return &length @@ -93,9 +78,9 @@ func (frame *frame) extractSize() *int { // extract the message and remove the overhead // if not possible it returns nil func (frame *frame) extractMessage(data []byte) (message []byte, err error) { - if uint32(len(data)) < overheadByteCount { return nil, errors.New(parsingFailed) } + if uint32(len(data)) < controlByteCount { return nil, errors.New(parsingFailed) } var length = frame.extractSize() if length == nil { return nil, errors.New(parsingFailed) } - if uint32(*length) < overheadByteCount { return nil, errors.New(parsingFailed) } - return data[overheadByteCount:*length], nil -} + if uint32(*length) < controlByteCount { return nil, errors.New(parsingFailed) } + return data[controlByteCount:*length], nil +} \ No newline at end of file From 449007f452c8726739f66bd373f22a563a39a121 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Sun, 5 Nov 2023 18:42:31 +0100 Subject: [PATCH 04/19] [M] updated internals --- network/frame.go | 1 - network/listener.go | 21 ++++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/network/frame.go b/network/frame.go index cca287a..49169f4 100644 --- a/network/frame.go +++ b/network/frame.go @@ -24,7 +24,6 @@ const ( ) const ( - hashMismatch string = "message hash does not match" parsingFailed string = "message parsing failed" readBufferOverflow string = "read buffer overflow" writeBufferOverflow string = "write buffer overflow" diff --git a/network/listener.go b/network/listener.go index a078ee4..46754b2 100644 --- a/network/listener.go +++ b/network/listener.go @@ -23,6 +23,10 @@ const ( PingMessage uint8 = 0x3 ) +const ( + maximum uint32 = 0x8000 +) + // Listener is a tcp based connection listener // this is for handling incoming pure tcp connections type Listener struct { @@ -70,14 +74,9 @@ func (listener *Listener) Cancel() { listener.listener = nil } -// SendTextMessage is for sending a text based message -func (listener *Listener) SendTextMessage(conn net.Conn, str string) { - listener.processingSend(conn, []byte(str), TextMessage) -} - -// SendBinaryMessage is for sending a text based message -func (listener *Listener) SendBinaryMessage(conn net.Conn, data []byte) { - listener.processingSend(conn, data, BinaryMessage) +// SendMessage is for sending messages +func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []byte) { + listener.processingSend(conn, data, messageType) } /// MARK: - Private API @@ -92,7 +91,7 @@ func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint } // parse a message frame -func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { +func (listener *Listener) processingParse(conn net.Conn, frame frame, data []byte) error { if listener.listener == nil { return errors.New(parsingFailed) } var err = frame.parse(data, func(text *string, data []byte, ping []byte) { listener.Message(conn, text, data) @@ -118,11 +117,11 @@ func (listener *Listener) sendPong(conn net.Conn, data []byte) { func (listener *Listener) receiveMessage(conn net.Conn) { var frame = frame{} listener.Ready(conn) - var buffer = make([]byte, 0x2000) + var buffer = make([]byte, maximum) for { var size, err = conn.Read(buffer) if err != nil { if err == io.EOF { listener.Cancelled(conn) } else { listener.Failed(conn, err) }; break } - err = listener.processingParse(conn, &frame, buffer[:size]) + err = listener.processingParse(conn, frame, buffer[:size]) if err != nil { listener.Failed(conn, err); listener.remove(conn); break } } } \ No newline at end of file From 56de89dbeac8bde1fed5f8a07a5e1ecd4432f720 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Sun, 5 Nov 2023 18:53:52 +0100 Subject: [PATCH 05/19] [M] fix pointer issue --- network/listener.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/listener.go b/network/listener.go index 46754b2..9adf0d5 100644 --- a/network/listener.go +++ b/network/listener.go @@ -91,7 +91,7 @@ func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint } // parse a message frame -func (listener *Listener) processingParse(conn net.Conn, frame frame, data []byte) error { +func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { if listener.listener == nil { return errors.New(parsingFailed) } var err = frame.parse(data, func(text *string, data []byte, ping []byte) { listener.Message(conn, text, data) @@ -121,7 +121,7 @@ func (listener *Listener) receiveMessage(conn net.Conn) { for { var size, err = conn.Read(buffer) if err != nil { if err == io.EOF { listener.Cancelled(conn) } else { listener.Failed(conn, err) }; break } - err = listener.processingParse(conn, frame, buffer[:size]) + err = listener.processingParse(conn, &frame, buffer[:size]) if err != nil { listener.Failed(conn, err); listener.remove(conn); break } } } \ No newline at end of file From 48690ef4317b9e0a49d6002fbc3dd430efb1a2f8 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Mon, 6 Nov 2023 14:59:17 +0100 Subject: [PATCH 06/19] [M] updated framer --- network/frame.go | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/network/frame.go b/network/frame.go index 49169f4..12dde9a 100644 --- a/network/frame.go +++ b/network/frame.go @@ -24,9 +24,10 @@ const ( ) const ( - parsingFailed string = "message parsing failed" - readBufferOverflow string = "read buffer overflow" - writeBufferOverflow string = "write buffer overflow" + parsingFailed string = "message parsing failed" + readBufferOverflow string = "read buffer overflow" + writeBufferOverflow string = "write buffer overflow" + sizeExtractionFailed string = "size extraction failed" ) // create is for creating compliant message frames @@ -45,41 +46,32 @@ func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { // returns parsed data as 'Data' or 'String' func (frame *frame) parse(data []byte, completion func(text *string, data []byte, ping []byte)) error { frame.buffer = append(frame.buffer, data...) - var length = frame.extractSize() - if length == nil { return nil } - if uint32(len(frame.buffer)) > frameByteCount { return errors.New(readBufferOverflow) } - if uint32(len(frame.buffer)) < controlByteCount { return nil } - if len(frame.buffer) < *length { return nil } - for len(frame.buffer) >= *length && *length != 0 { - var bytes, err = frame.extractMessage(frame.buffer) - if err != nil { return err } + var length, err = frame.extractSize(); if err != nil { return nil } + if len(frame.buffer) > int(frameByteCount) { return errors.New(readBufferOverflow) } + if len(frame.buffer) < int(controlByteCount) { return nil }; if len(frame.buffer) < length { return nil } + for len(frame.buffer) >= length && length != 0 { + var bytes, err = frame.extractMessage(length); if err != nil { return err } switch frame.buffer[0] { case TextMessage: var result = string(bytes); completion(&result, nil, nil) case BinaryMessage: completion(nil, bytes, nil) case PingMessage: completion(nil, nil, bytes) default: return errors.New(parsingFailed) } - if len(frame.buffer) <= *length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[*length:] } - } - return nil + if len(frame.buffer) <= length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[length:] } + }; return nil } -// MARK: - Private API - - // extract the message frame size from the data // if not possible it returns zero -func (frame *frame) extractSize() *int { - if uint32(len(frame.buffer)) < controlByteCount { return nil } +func (frame *frame) extractSize() (length int, err error) { + if len(frame.buffer) < int(controlByteCount) { return 0x0, errors.New(sizeExtractionFailed) } var size = frame.buffer[opcodeByteCount:controlByteCount] - var length = int(binary.BigEndian.Uint32(size)) - return &length + return int(binary.BigEndian.Uint32(size)), nil } // extract the message and remove the overhead // if not possible it returns nil -func (frame *frame) extractMessage(data []byte) (message []byte, err error) { - if uint32(len(data)) < controlByteCount { return nil, errors.New(parsingFailed) } - var length = frame.extractSize() - if length == nil { return nil, errors.New(parsingFailed) } - if uint32(*length) < controlByteCount { return nil, errors.New(parsingFailed) } - return data[controlByteCount:*length], nil +func (frame *frame) extractMessage(length int) (message []byte, err error) { + if len(frame.buffer) < int(controlByteCount) { return nil, errors.New(parsingFailed) } + if length < int(controlByteCount) { return nil, errors.New(parsingFailed) } + return frame.buffer[controlByteCount:length], nil } \ No newline at end of file From e688a5598e90dbc9c2c8005040eb4ba47bcd47c6 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Tue, 7 Nov 2023 15:31:34 +0100 Subject: [PATCH 07/19] [M] added testing --- network/frame_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 network/frame_test.go diff --git a/network/frame_test.go b/network/frame_test.go new file mode 100644 index 0000000..6c58c11 --- /dev/null +++ b/network/frame_test.go @@ -0,0 +1,36 @@ +package network + +import ( + "bytes" + "testing" +) + +// TestFramerText is for testing the frame creator and parser +// includes testing for multiple message extraction from single byte buffer +func TestFramerText(test *testing.T) { + frame := frame{} + message := "Hello World!" + var bytesBlock []byte + data, err := frame.create([]byte(message), TextMessage) + if err != nil { test.Errorf("parsing failed") } + for i := 0; i < 1000; i++ { bytesBlock = append(bytesBlock, data...) } + err = frame.parse(bytesBlock, func(text *string, data []byte, ping []byte) { + if text != nil { if *text != message { test.Errorf("parsing failed") } } + }) + if err != nil { test.Errorf("parsing failed") } +} + +// TestFramerBinary is for testing the frame creator and parser +// includes testing for multiple message extraction from single byte buffer +func TestFramerBinary(test *testing.T) { + frame := frame{} + message := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + var bytesBlock []byte + data, err := frame.create(message, BinaryMessage) + if err != nil { test.Errorf("parsing failed") } + for i := 0; i < 1000; i++ { bytesBlock = append(bytesBlock, data...) } + err = frame.parse(bytesBlock, func(text *string, binary []byte, ping []byte) { + if binary != nil { if !bytes.Equal(binary, message) { test.Errorf("parsing failed") } } + }) + if err != nil { test.Errorf("parsing failed") } +} \ No newline at end of file From 7b6f8080f17f0568831b3f2fed829c8b869b6882 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Tue, 7 Nov 2023 16:37:50 +0100 Subject: [PATCH 08/19] [M, F] rework interface --- network/frame.go | 10 +++++----- network/frame_test.go | 12 ++++++------ network/listener.go | 19 ++++++++----------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/network/frame.go b/network/frame.go index 12dde9a..0f7b597 100644 --- a/network/frame.go +++ b/network/frame.go @@ -43,8 +43,8 @@ func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { } // parse is for parsing data back into compliant messages -// returns parsed data as 'Data' or 'String' -func (frame *frame) parse(data []byte, completion func(text *string, data []byte, ping []byte)) error { +// returns parsed data as '[]Byte' or 'String' +func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8)) error { frame.buffer = append(frame.buffer, data...) var length, err = frame.extractSize(); if err != nil { return nil } if len(frame.buffer) > int(frameByteCount) { return errors.New(readBufferOverflow) } @@ -52,9 +52,9 @@ func (frame *frame) parse(data []byte, completion func(text *string, data []byte for len(frame.buffer) >= length && length != 0 { var bytes, err = frame.extractMessage(length); if err != nil { return err } switch frame.buffer[0] { - case TextMessage: var result = string(bytes); completion(&result, nil, nil) - case BinaryMessage: completion(nil, bytes, nil) - case PingMessage: completion(nil, nil, bytes) + case TextMessage: completion(bytes, TextMessage) + case BinaryMessage: completion(bytes, BinaryMessage) + case PingMessage: completion(bytes, PingMessage) default: return errors.New(parsingFailed) } if len(frame.buffer) <= length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[length:] } }; return nil diff --git a/network/frame_test.go b/network/frame_test.go index 6c58c11..d3413e0 100644 --- a/network/frame_test.go +++ b/network/frame_test.go @@ -13,9 +13,9 @@ func TestFramerText(test *testing.T) { var bytesBlock []byte data, err := frame.create([]byte(message), TextMessage) if err != nil { test.Errorf("parsing failed") } - for i := 0; i < 1000; i++ { bytesBlock = append(bytesBlock, data...) } - err = frame.parse(bytesBlock, func(text *string, data []byte, ping []byte) { - if text != nil { if *text != message { test.Errorf("parsing failed") } } + for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } + err = frame.parse(bytesBlock, func(data []byte, opcode uint8) { + if opcode == TextMessage { if string(data) != message { test.Errorf("parsing failed") } } }) if err != nil { test.Errorf("parsing failed") } } @@ -28,9 +28,9 @@ func TestFramerBinary(test *testing.T) { var bytesBlock []byte data, err := frame.create(message, BinaryMessage) if err != nil { test.Errorf("parsing failed") } - for i := 0; i < 1000; i++ { bytesBlock = append(bytesBlock, data...) } - err = frame.parse(bytesBlock, func(text *string, binary []byte, ping []byte) { - if binary != nil { if !bytes.Equal(binary, message) { test.Errorf("parsing failed") } } + for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } + err = frame.parse(bytesBlock, func(data []byte, opcode uint8) { + if opcode != BinaryMessage { if !bytes.Equal(data, message) { test.Errorf("parsing failed") } } }) if err != nil { test.Errorf("parsing failed") } } \ No newline at end of file diff --git a/network/listener.go b/network/listener.go index 9adf0d5..5612fc0 100644 --- a/network/listener.go +++ b/network/listener.go @@ -37,29 +37,28 @@ type Listener struct { Key string Ready func(conn net.Conn) - Message func(conn net.Conn, text *string, binary []byte) + Message func(conn net.Conn, data []byte, opcode uint8) Failed func(conn net.Conn, err error) Cancelled func(conn net.Conn) } // Start the NetworkGO connection listener // waits for incoming connections -func (listener *Listener) Start(parameter uint8, port uint16) error { +func (listener *Listener) Start(parameter uint8, port uint16) (err error) { switch parameter { case TCPConnection: - var err error listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) if err != nil { return err } case TLSConnection: - var cer, err = tls.LoadX509KeyPair(listener.Cert, listener.Key) + var cer tls.Certificate; cer, err = tls.LoadX509KeyPair(listener.Cert, listener.Key) if err != nil { return err } var config = &tls.Config{Certificates: []tls.Certificate{cer}} listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), config) if err != nil { return err } } - defer listener.listener.Close() + defer func() { if closed := listener.listener.Close(); closed != nil && err == nil { err = closed } }() for { - var conn, err = listener.listener.Accept() + var conn net.Conn; conn, err = listener.listener.Accept() if err != nil { return err } go listener.receiveMessage(conn) } @@ -79,8 +78,6 @@ func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []b listener.processingSend(conn, data, messageType) } -/// MARK: - Private API - // create and send message frame func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } @@ -93,9 +90,9 @@ func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint // parse a message frame func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { if listener.listener == nil { return errors.New(parsingFailed) } - var err = frame.parse(data, func(text *string, data []byte, ping []byte) { - listener.Message(conn, text, data) - if ping != nil { listener.sendPong(conn, ping) } + var err = frame.parse(data, func(data []byte, opcode uint8) { + listener.Message(conn, data, opcode) + if opcode == PingMessage { listener.sendPong(conn, data) } }) return err } From 2049e2d09d57cf743278086f743183c58049ffe0 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Tue, 7 Nov 2023 17:12:56 +0100 Subject: [PATCH 09/19] [M] cleanup --- network/frame.go | 2 +- network/listener.go | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/network/frame.go b/network/frame.go index 0f7b597..8b8722a 100644 --- a/network/frame.go +++ b/network/frame.go @@ -54,7 +54,7 @@ func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8 switch frame.buffer[0] { case TextMessage: completion(bytes, TextMessage) case BinaryMessage: completion(bytes, BinaryMessage) - case PingMessage: completion(bytes, PingMessage) + case pingMessage: completion(bytes, pingMessage) default: return errors.New(parsingFailed) } if len(frame.buffer) <= length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[length:] } }; return nil diff --git a/network/listener.go b/network/listener.go index 5612fc0..2ae0cf6 100644 --- a/network/listener.go +++ b/network/listener.go @@ -20,7 +20,7 @@ const ( TextMessage uint8 = 0x1 BinaryMessage uint8 = 0x2 - PingMessage uint8 = 0x3 + pingMessage uint8 = 0x3 ) const ( @@ -92,7 +92,7 @@ func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []by if listener.listener == nil { return errors.New(parsingFailed) } var err = frame.parse(data, func(data []byte, opcode uint8) { listener.Message(conn, data, opcode) - if opcode == PingMessage { listener.sendPong(conn, data) } + if opcode == pingMessage { listener.processingSend(conn, data, pingMessage) } }) return err } @@ -104,11 +104,6 @@ func (listener *Listener) remove(conn net.Conn) { listener.Cancelled(conn) } -// sendPong is for sending a pong based message -func (listener *Listener) sendPong(conn net.Conn, data []byte) { - listener.processingSend(conn, data, PingMessage) -} - // receiveMessage is handling all incoming input // keeps track broken connections func (listener *Listener) receiveMessage(conn net.Conn) { From bd19eb01c07cb49848a5dac30725e7c3b0542303 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 8 Nov 2023 01:12:00 +0100 Subject: [PATCH 10/19] [M, F] added some documentation --- network/frame.go | 28 ++++++++++++++++++---------- network/listener.go | 32 ++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/network/frame.go b/network/frame.go index 8b8722a..717650a 100644 --- a/network/frame.go +++ b/network/frame.go @@ -4,6 +4,9 @@ // Copyright © 2021 Vinzenz Weist. All rights reserved. // +// Package network encapsulates functionalities related to network communication. +// It provides a structure and associated methods to create and parse data frames +// for messaging across the fusion network protocol ensuring reliability and structure. package network import ( @@ -11,18 +14,21 @@ import ( "errors" ) -// frame is message creator & parser -// fast and reliable +// frame is a type that encapsulates operations related to the creation and parsing +// of message frames over the network. It aims to ensure fast and reliable communication +// by adhering to a specified frame structure. type frame struct { buffer []byte } +// Predefined constants to maintain consistency in frame size and errors. const ( opcodeByteCount uint32 = 0x1 controlByteCount uint32 = 0x5 frameByteCount uint32 = 0xFFFFFFFF ) +// Predefined error messages for common frame parsing issues. const ( parsingFailed string = "message parsing failed" readBufferOverflow string = "read buffer overflow" @@ -30,8 +36,9 @@ const ( sizeExtractionFailed string = "size extraction failed" ) -// create is for creating compliant message frames -// returns message as data frame +// create is a method to construct a compliant frame for sending a message. +// It takes in the message data and an opcode indicating the type of message. +// It returns a slice of bytes representing the framed message or an error if the frame could not be created. func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { if uint32(len(data)) > frameByteCount - controlByteCount { return nil, errors.New(writeBufferOverflow) } var frame []byte @@ -42,8 +49,9 @@ func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { return frame, nil } -// parse is for parsing data back into compliant messages -// returns parsed data as '[]Byte' or 'String' +// parse is a method for converting a sequence of bytes received over the network back into a structured message. +// It appends the incoming data to the frame's buffer and attempts to extract complete messages based on the frame structure. +// The completion callback is called with the parsed data and opcode when a message is successfully parsed. func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8)) error { frame.buffer = append(frame.buffer, data...) var length, err = frame.extractSize(); if err != nil { return nil } @@ -60,16 +68,16 @@ func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8 }; return nil } -// extract the message frame size from the data -// if not possible it returns zero +// extractSize attempts to determine the size of the next message in the buffer. +// It returns the size as an integer and an error if the size cannot be determined. func (frame *frame) extractSize() (length int, err error) { if len(frame.buffer) < int(controlByteCount) { return 0x0, errors.New(sizeExtractionFailed) } var size = frame.buffer[opcodeByteCount:controlByteCount] return int(binary.BigEndian.Uint32(size)), nil } -// extract the message and remove the overhead -// if not possible it returns nil +// extractMessage attempts to extract the message from the frame buffer based on the input length. +// It returns the extracted message as byte array and an error if the message cannot be extracted. func (frame *frame) extractMessage(length int) (message []byte, err error) { if len(frame.buffer) < int(controlByteCount) { return nil, errors.New(parsingFailed) } if length < int(controlByteCount) { return nil, errors.New(parsingFailed) } diff --git a/network/listener.go b/network/listener.go index 2ae0cf6..8ed3497 100644 --- a/network/listener.go +++ b/network/listener.go @@ -4,6 +4,10 @@ // Copyright © 2021 Vinzenz Weist. All rights reserved. // +// Package network encapsulates the logic required to set up network connections +// and communication, including TCP and TLS encrypted connections, along with handling +// different types of messages such as text and binary. +// It uses the fusion network protocol to ensuring reliability and structure. package network import ( @@ -14,21 +18,23 @@ import ( "strconv" ) +// Predefined constants for identifying connection and message types. const ( TCPConnection uint8 = 0x0 TLSConnection uint8 = 0x1 TextMessage uint8 = 0x1 BinaryMessage uint8 = 0x2 - pingMessage uint8 = 0x3 ) +// Private predefined constant for the maximum buffer size and pingMessage. const ( - maximum uint32 = 0x8000 + maximum uint32 = 0x8000 + pingMessage uint8 = 0x3 ) -// Listener is a tcp based connection listener -// this is for handling incoming pure tcp connections +// Listener struct represents a TCP based connection listener that handles incoming +// pure TCP connections or TLS encrypted connections. type Listener struct { frame frame listener net.Listener @@ -42,8 +48,8 @@ type Listener struct { Cancelled func(conn net.Conn) } -// Start the NetworkGO connection listener -// waits for incoming connections +// Start initiates the listener to start accepting incoming connections on the specified port. +// The parameter decides whether it's a TCP or TLS connection based on predefined constants. func (listener *Listener) Start(parameter uint8, port uint16) (err error) { switch parameter { case TCPConnection: @@ -64,8 +70,7 @@ func (listener *Listener) Start(parameter uint8, port uint16) (err error) { } } -// Cancel closes all connections and stops -// the listener from accepting new connections +// Cancel stops the listener from accepting new connections and closes any existing ones. func (listener *Listener) Cancel() { if listener.listener == nil { return } var err = listener.listener.Close() @@ -73,12 +78,12 @@ func (listener *Listener) Cancel() { listener.listener = nil } -// SendMessage is for sending messages +// SendMessage sends a message through the specified connection. func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []byte) { listener.processingSend(conn, data, messageType) } -// create and send message frame +// processingSend is a helper function to create and send a message frame over a connection. func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } var message, err = listener.frame.create(data, opcode) @@ -87,7 +92,7 @@ func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint if err != nil { listener.Failed(conn, err) } } -// parse a message frame +// processingParse is a helper function to parse a message frame from the connection data. func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { if listener.listener == nil { return errors.New(parsingFailed) } var err = frame.parse(data, func(data []byte, opcode uint8) { @@ -97,15 +102,14 @@ func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []by return err } -// remove is for terminating a specific connection +// remove terminates a specific connection and triggers the Cancelled callback. func (listener *Listener) remove(conn net.Conn) { var err = conn.Close() if err != nil { listener.Failed(conn, err) } listener.Cancelled(conn) } -// receiveMessage is handling all incoming input -// keeps track broken connections +// receiveMessage handles all incoming data for a connection and tracks broken connections. func (listener *Listener) receiveMessage(conn net.Conn) { var frame = frame{} listener.Ready(conn) From 55c344e7d5598d4f846c0dfa9752d78aebdd21e2 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 8 Nov 2023 01:14:11 +0100 Subject: [PATCH 11/19] [M, F] added some documentation --- network/frame_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/frame_test.go b/network/frame_test.go index d3413e0..f2eedaa 100644 --- a/network/frame_test.go +++ b/network/frame_test.go @@ -5,8 +5,8 @@ import ( "testing" ) -// TestFramerText is for testing the frame creator and parser -// includes testing for multiple message extraction from single byte buffer +// TestFramerText is for testing the frame creator and parser. +// Includes testing for multiple message extractions from single byte buffer. func TestFramerText(test *testing.T) { frame := frame{} message := "Hello World!" @@ -20,8 +20,8 @@ func TestFramerText(test *testing.T) { if err != nil { test.Errorf("parsing failed") } } -// TestFramerBinary is for testing the frame creator and parser -// includes testing for multiple message extraction from single byte buffer +// TestFramerBinary is for testing the frame creator and parser. +// Includes testing for multiple message extractions from single byte buffer. func TestFramerBinary(test *testing.T) { frame := frame{} message := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} From 15afc189bca91e1d07f0917849befcc6c4962a41 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 8 Nov 2023 01:20:23 +0100 Subject: [PATCH 12/19] [M, F] added some documentation --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 13ce595..0179882 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Fusion -`Fusion` is a library which implements the `NWC-Protocol`. The `NWC-Protocol` is proprietary networking protocol which uses a small and lightweight header with a performance as fast as raw tcp performance. +`Fusion` is a library which implements the `Fusion Framing Protocol (FFP)`. +The `Fusion Framing Protocol (FFP)` is proprietary networking protocol which uses a small and lightweight header with a performance as fast as raw tcp performance. Built directly on top of Go's `net.Listener` with support for plain tcp and tls encrypted connections. The implementation for the Client is [FusionKit](https://github.com/Vinz1911/fusionkit) written in swift on top of `Network.framework` to ensure maximum performance. @@ -34,11 +35,11 @@ listener := network.Listener{} listener.Ready = func(socket net.Conn) { } // listener received message from a connection -listener.Message = func(conn net.Conn, text *string, binary []byte) { +listener.Message = func(conn net.Conn, binary []byte, opcode uint8) { // message is text based - if text != nil { } + if opcode == network.TextMessage { println(string(binary)) } // message is binary based - if binary != nil { } + if binary == network.BinaryMessage { println(len(binary)) } } // listener connection failed @@ -48,7 +49,7 @@ listener.Failed = func(conn net.Conn, err error) { } listener.Cancelled = func(conn net.Conn) { } // start listener -err := listener.Start(network.TCPConnection, uint16(8080)) +err := listener.Start(network.TCPConnection, uint16(7878)) ``` ## Author: From ec079081ed7a05b8256a4f191cb223f9152459bd Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 8 Nov 2023 22:46:51 +0100 Subject: [PATCH 13/19] [M] set up IPv6 --- go.sum | 41 +++++++++++++++++++++++++++++++++++++++++ network/listener.go | 4 ++-- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4ab156a --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/network/listener.go b/network/listener.go index 8ed3497..2ba47a7 100644 --- a/network/listener.go +++ b/network/listener.go @@ -53,13 +53,13 @@ type Listener struct { func (listener *Listener) Start(parameter uint8, port uint16) (err error) { switch parameter { case TCPConnection: - listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) + listener.listener, err = net.Listen("tcp6", ":" + strconv.Itoa(int(port))) if err != nil { return err } case TLSConnection: var cer tls.Certificate; cer, err = tls.LoadX509KeyPair(listener.Cert, listener.Key) if err != nil { return err } var config = &tls.Config{Certificates: []tls.Certificate{cer}} - listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), config) + listener.listener, err = tls.Listen("tcp6", ":" + strconv.Itoa(int(port)), config) if err != nil { return err } } defer func() { if closed := listener.listener.Close(); closed != nil && err == nil { err = closed } }() From 7b331d1af9e9ffd04775d4481244967433b8b66b Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 22 Nov 2023 01:25:21 +0100 Subject: [PATCH 14/19] [M] updated tls config loading --- network/listener.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/network/listener.go b/network/listener.go index 2ba47a7..e922f57 100644 --- a/network/listener.go +++ b/network/listener.go @@ -38,9 +38,7 @@ const ( type Listener struct { frame frame listener net.Listener - - Cert string - Key string + TLSConfig *tls.Config Ready func(conn net.Conn) Message func(conn net.Conn, data []byte, opcode uint8) @@ -53,13 +51,10 @@ type Listener struct { func (listener *Listener) Start(parameter uint8, port uint16) (err error) { switch parameter { case TCPConnection: - listener.listener, err = net.Listen("tcp6", ":" + strconv.Itoa(int(port))) + listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) if err != nil { return err } case TLSConnection: - var cer tls.Certificate; cer, err = tls.LoadX509KeyPair(listener.Cert, listener.Key) - if err != nil { return err } - var config = &tls.Config{Certificates: []tls.Certificate{cer}} - listener.listener, err = tls.Listen("tcp6", ":" + strconv.Itoa(int(port)), config) + listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), listener.TLSConfig) if err != nil { return err } } defer func() { if closed := listener.listener.Close(); closed != nil && err == nil { err = closed } }() From da324ca9f2cd8bf2b3c9f89e460123cc1167d69f Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 7 Feb 2024 01:03:07 +0100 Subject: [PATCH 15/19] [M] prevent nil pointer dereference --- network/listener.go | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/network/listener.go b/network/listener.go index e922f57..dfe0cc7 100644 --- a/network/listener.go +++ b/network/listener.go @@ -13,7 +13,6 @@ package network import ( "crypto/tls" "errors" - "io" "net" "strconv" ) @@ -42,8 +41,7 @@ type Listener struct { Ready func(conn net.Conn) Message func(conn net.Conn, data []byte, opcode uint8) - Failed func(conn net.Conn, err error) - Cancelled func(conn net.Conn) + Failed func(err error) } // Start initiates the listener to start accepting incoming connections on the specified port. @@ -54,14 +52,14 @@ func (listener *Listener) Start(parameter uint8, port uint16) (err error) { listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) if err != nil { return err } case TLSConnection: + if listener.TLSConfig == nil { return errors.New("empty tls config") } listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), listener.TLSConfig) if err != nil { return err } } defer func() { if closed := listener.listener.Close(); closed != nil && err == nil { err = closed } }() for { var conn net.Conn; conn, err = listener.listener.Accept() - if err != nil { return err } - go listener.receiveMessage(conn) + if err != nil { return err }; if conn != nil { go listener.receiveMessage(conn) } } } @@ -69,7 +67,7 @@ func (listener *Listener) Start(parameter uint8, port uint16) (err error) { func (listener *Listener) Cancel() { if listener.listener == nil { return } var err = listener.listener.Close() - if err != nil { listener.Failed(nil, err) } + if err != nil { if listener.Failed != nil { listener.Failed(err) } } listener.listener = nil } @@ -82,37 +80,28 @@ func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []b func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } var message, err = listener.frame.create(data, opcode) - if err != nil { listener.Failed(conn, err); listener.remove(conn) } - _, err = conn.Write(message) - if err != nil { listener.Failed(conn, err) } + if err != nil { if listener.Failed != nil { listener.Failed(err) }; if conn != nil { err = conn.Close() }; return } + _, err = conn.Write(message); if err != nil { if listener.Failed != nil { listener.Failed(err) } } } // processingParse is a helper function to parse a message frame from the connection data. func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { if listener.listener == nil { return errors.New(parsingFailed) } var err = frame.parse(data, func(data []byte, opcode uint8) { - listener.Message(conn, data, opcode) + if conn != nil { if listener.Message != nil { listener.Message(conn, data, opcode) } } if opcode == pingMessage { listener.processingSend(conn, data, pingMessage) } }) return err } -// remove terminates a specific connection and triggers the Cancelled callback. -func (listener *Listener) remove(conn net.Conn) { - var err = conn.Close() - if err != nil { listener.Failed(conn, err) } - listener.Cancelled(conn) -} - // receiveMessage handles all incoming data for a connection and tracks broken connections. func (listener *Listener) receiveMessage(conn net.Conn) { - var frame = frame{} - listener.Ready(conn) + var frame = frame{}; if listener.Ready != nil { if conn != nil { listener.Ready(conn) } } var buffer = make([]byte, maximum) for { - var size, err = conn.Read(buffer) - if err != nil { if err == io.EOF { listener.Cancelled(conn) } else { listener.Failed(conn, err) }; break } + if conn == nil { break }; var size, err = conn.Read(buffer) + if err != nil { if listener.Failed != nil { listener.Failed(err) }; break } err = listener.processingParse(conn, &frame, buffer[:size]) - if err != nil { listener.Failed(conn, err); listener.remove(conn); break } + if err != nil { if listener.Failed != nil { listener.Failed(err) }; if conn != nil { err = conn.Close() }; break } } } \ No newline at end of file From 0c7f9d546bcce0708ef1d26b69859ee78e52fbae Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 7 Feb 2024 01:13:35 +0100 Subject: [PATCH 16/19] [M] prevent nil pointer dereference --- network/listener.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/listener.go b/network/listener.go index dfe0cc7..d917e45 100644 --- a/network/listener.go +++ b/network/listener.go @@ -13,6 +13,7 @@ package network import ( "crypto/tls" "errors" + "io" "net" "strconv" ) @@ -100,7 +101,7 @@ func (listener *Listener) receiveMessage(conn net.Conn) { var buffer = make([]byte, maximum) for { if conn == nil { break }; var size, err = conn.Read(buffer) - if err != nil { if listener.Failed != nil { listener.Failed(err) }; break } + if err != nil { if listener.Failed != nil { if err != io.EOF { listener.Failed(err) } }; break } err = listener.processingParse(conn, &frame, buffer[:size]) if err != nil { if listener.Failed != nil { listener.Failed(err) }; if conn != nil { err = conn.Close() }; break } } From bada9f013283c0ccb7e373805aa5bdf5b16a73b3 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Wed, 7 Feb 2024 23:25:19 +0100 Subject: [PATCH 17/19] [M] refactor --- network/listener.go | 69 ++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/network/listener.go b/network/listener.go index d917e45..5fc85e8 100644 --- a/network/listener.go +++ b/network/listener.go @@ -29,47 +29,42 @@ const ( // Private predefined constant for the maximum buffer size and pingMessage. const ( - maximum uint32 = 0x8000 - pingMessage uint8 = 0x3 + maximum uint32 = 0x8000 + pingMessage uint8 = 0x3 ) // Listener struct represents a TCP based connection listener that handles incoming // pure TCP connections or TLS encrypted connections. type Listener struct { - frame frame - listener net.Listener + frame frame + listener net.Listener TLSConfig *tls.Config - Ready func(conn net.Conn) - Message func(conn net.Conn, data []byte, opcode uint8) - Failed func(err error) + Ready func(conn net.Conn) + Message func(conn net.Conn, data []byte, opcode uint8) + Failed func(err error) } // Start initiates the listener to start accepting incoming connections on the specified port. // The parameter decides whether it's a TCP or TLS connection based on predefined constants. func (listener *Listener) Start(parameter uint8, port uint16) (err error) { switch parameter { - case TCPConnection: - listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) - if err != nil { return err } + case TCPConnection: listener.listener, err = net.Listen("tcp", ":"+strconv.Itoa(int(port))) case TLSConnection: if listener.TLSConfig == nil { return errors.New("empty tls config") } - listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), listener.TLSConfig) - if err != nil { return err } - } - defer func() { if closed := listener.listener.Close(); closed != nil && err == nil { err = closed } }() - for { - var conn net.Conn; conn, err = listener.listener.Accept() - if err != nil { return err }; if conn != nil { go listener.receiveMessage(conn) } + listener.listener, err = tls.Listen("tcp", ":"+strconv.Itoa(int(port)), listener.TLSConfig) } + if err != nil { return err } + defer listener.listener.Close() + for { conn, err := listener.listener.Accept(); if err != nil { return err }; go listener.receiveMessage(conn) } } // Cancel stops the listener from accepting new connections and closes any existing ones. func (listener *Listener) Cancel() { - if listener.listener == nil { return } - var err = listener.listener.Close() - if err != nil { if listener.Failed != nil { listener.Failed(err) } } - listener.listener = nil + if listener.listener != nil { + err := listener.listener.Close() + if err != nil && listener.Failed != nil { listener.Failed(err) } + }; listener.listener = nil } // SendMessage sends a message through the specified connection. @@ -80,29 +75,33 @@ func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []b // processingSend is a helper function to create and send a message frame over a connection. func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } - var message, err = listener.frame.create(data, opcode) - if err != nil { if listener.Failed != nil { listener.Failed(err) }; if conn != nil { err = conn.Close() }; return } - _, err = conn.Write(message); if err != nil { if listener.Failed != nil { listener.Failed(err) } } + message, err := listener.frame.create(data, opcode) + if err != nil { + if listener.Failed != nil { listener.Failed(err) } + if conn != nil { err = conn.Close() }; return + } + _, err = conn.Write(message) + if err != nil && listener.Failed != nil { listener.Failed(err) } } // processingParse is a helper function to parse a message frame from the connection data. func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { - if listener.listener == nil { return errors.New(parsingFailed) } - var err = frame.parse(data, func(data []byte, opcode uint8) { - if conn != nil { if listener.Message != nil { listener.Message(conn, data, opcode) } } + if listener.listener == nil { return errors.New("parsing failed") } + err := frame.parse(data, func(data []byte, opcode uint8) { + if listener.Message != nil { listener.Message(conn, data, opcode) } if opcode == pingMessage { listener.processingSend(conn, data, pingMessage) } - }) - return err + }); return err } // receiveMessage handles all incoming data for a connection and tracks broken connections. func (listener *Listener) receiveMessage(conn net.Conn) { - var frame = frame{}; if listener.Ready != nil { if conn != nil { listener.Ready(conn) } } - var buffer = make([]byte, maximum) + defer func() { if conn != nil { conn.Close() } }() + if listener.Ready != nil && conn != nil { listener.Ready(conn) } + var frame frame; buffer := make([]byte, maximum) for { - if conn == nil { break }; var size, err = conn.Read(buffer) - if err != nil { if listener.Failed != nil { if err != io.EOF { listener.Failed(err) } }; break } + size, err := conn.Read(buffer) + if err != nil { if err != io.EOF && listener.Failed != nil { listener.Failed(err) }; break } err = listener.processingParse(conn, &frame, buffer[:size]) - if err != nil { if listener.Failed != nil { listener.Failed(err) }; if conn != nil { err = conn.Close() }; break } + if err != nil { if listener.Failed != nil { listener.Failed(err) }; break } } -} \ No newline at end of file +} From c19f6d41b605a6e12149237af75c036e6aa68072 Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Thu, 1 Aug 2024 03:18:40 +0200 Subject: [PATCH 18/19] [M, F] added timeout invalidation --- network/listener.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/network/listener.go b/network/listener.go index 5fc85e8..30a181c 100644 --- a/network/listener.go +++ b/network/listener.go @@ -16,6 +16,7 @@ import ( "io" "net" "strconv" + "time" ) // Predefined constants for identifying connection and message types. @@ -47,16 +48,21 @@ type Listener struct { // Start initiates the listener to start accepting incoming connections on the specified port. // The parameter decides whether it's a TCP or TLS connection based on predefined constants. -func (listener *Listener) Start(parameter uint8, port uint16) (err error) { +// The interrupt defines the duration after a connection gets kicked, zero means infinity. +func (listener *Listener) Start(parameter uint8, port uint16, interrupt time.Duration) (err error) { switch parameter { - case TCPConnection: listener.listener, err = net.Listen("tcp", ":"+strconv.Itoa(int(port))) + case TCPConnection: listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) case TLSConnection: if listener.TLSConfig == nil { return errors.New("empty tls config") } - listener.listener, err = tls.Listen("tcp", ":"+strconv.Itoa(int(port)), listener.TLSConfig) + listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), listener.TLSConfig) } if err != nil { return err } defer listener.listener.Close() - for { conn, err := listener.listener.Accept(); if err != nil { return err }; go listener.receiveMessage(conn) } + for { + conn, err := listener.listener.Accept() + if err != nil { return err } + go listener.receiveMessage(conn, interrupt) + } } // Cancel stops the listener from accepting new connections and closes any existing ones. @@ -94,9 +100,15 @@ func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []by } // receiveMessage handles all incoming data for a connection and tracks broken connections. -func (listener *Listener) receiveMessage(conn net.Conn) { +func (listener *Listener) receiveMessage(conn net.Conn, interrupt time.Duration) { defer func() { if conn != nil { conn.Close() } }() if listener.Ready != nil && conn != nil { listener.Ready(conn) } + + if interrupt > 0 { + deadline := time.Now().Add(interrupt * time.Second); err := conn.SetDeadline(deadline) + if err != nil { if listener.Failed != nil { listener.Failed(err) }; return } + } + var frame frame; buffer := make([]byte, maximum) for { size, err := conn.Read(buffer) From 635d9b005030c75a8a685803412e2ca6471252cc Mon Sep 17 00:00:00 2001 From: Vinzenz Weist Date: Mon, 19 Aug 2024 13:06:58 +0200 Subject: [PATCH 19/19] [M] refactoring --- network/{frame.go => framer.go} | 34 +++++++++++------------ network/{frame_test.go => framer_test.go} | 12 ++++---- network/listener.go | 12 ++++---- 3 files changed, 29 insertions(+), 29 deletions(-) rename network/{frame.go => framer.go} (71%) rename network/{frame_test.go => framer_test.go} (79%) diff --git a/network/frame.go b/network/framer.go similarity index 71% rename from network/frame.go rename to network/framer.go index 717650a..5e12efa 100644 --- a/network/frame.go +++ b/network/framer.go @@ -17,15 +17,15 @@ import ( // frame is a type that encapsulates operations related to the creation and parsing // of message frames over the network. It aims to ensure fast and reliable communication // by adhering to a specified frame structure. -type frame struct { +type framer struct { buffer []byte } // Predefined constants to maintain consistency in frame size and errors. const ( - opcodeByteCount uint32 = 0x1 - controlByteCount uint32 = 0x5 - frameByteCount uint32 = 0xFFFFFFFF + opcodeConstant uint32 = 0x1 + controlConstant uint32 = 0x5 + frameConstant uint32 = 0xFFFFFFFF ) // Predefined error messages for common frame parsing issues. @@ -39,10 +39,10 @@ const ( // create is a method to construct a compliant frame for sending a message. // It takes in the message data and an opcode indicating the type of message. // It returns a slice of bytes representing the framed message or an error if the frame could not be created. -func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { - if uint32(len(data)) > frameByteCount - controlByteCount { return nil, errors.New(writeBufferOverflow) } +func (*framer) create(data []byte, opcode uint8) (message []byte, err error) { + if uint32(len(data)) > frameConstant - controlConstant { return nil, errors.New(writeBufferOverflow) } var frame []byte - var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + controlByteCount) + var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + controlConstant) frame = append(frame, opcode) frame = append(frame, length...) frame = append(frame, data...) @@ -52,11 +52,11 @@ func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { // parse is a method for converting a sequence of bytes received over the network back into a structured message. // It appends the incoming data to the frame's buffer and attempts to extract complete messages based on the frame structure. // The completion callback is called with the parsed data and opcode when a message is successfully parsed. -func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8)) error { +func (frame *framer) parse(data []byte, completion func(data []byte, opcode uint8)) error { frame.buffer = append(frame.buffer, data...) var length, err = frame.extractSize(); if err != nil { return nil } - if len(frame.buffer) > int(frameByteCount) { return errors.New(readBufferOverflow) } - if len(frame.buffer) < int(controlByteCount) { return nil }; if len(frame.buffer) < length { return nil } + if len(frame.buffer) > int(frameConstant) { return errors.New(readBufferOverflow) } + if len(frame.buffer) < int(controlConstant) { return nil }; if len(frame.buffer) < length { return nil } for len(frame.buffer) >= length && length != 0 { var bytes, err = frame.extractMessage(length); if err != nil { return err } switch frame.buffer[0] { @@ -70,16 +70,16 @@ func (frame *frame) parse(data []byte, completion func(data []byte, opcode uint8 // extractSize attempts to determine the size of the next message in the buffer. // It returns the size as an integer and an error if the size cannot be determined. -func (frame *frame) extractSize() (length int, err error) { - if len(frame.buffer) < int(controlByteCount) { return 0x0, errors.New(sizeExtractionFailed) } - var size = frame.buffer[opcodeByteCount:controlByteCount] +func (frame *framer) extractSize() (length int, err error) { + if len(frame.buffer) < int(controlConstant) { return 0x0, errors.New(sizeExtractionFailed) } + var size = frame.buffer[opcodeConstant:controlConstant] return int(binary.BigEndian.Uint32(size)), nil } // extractMessage attempts to extract the message from the frame buffer based on the input length. // It returns the extracted message as byte array and an error if the message cannot be extracted. -func (frame *frame) extractMessage(length int) (message []byte, err error) { - if len(frame.buffer) < int(controlByteCount) { return nil, errors.New(parsingFailed) } - if length < int(controlByteCount) { return nil, errors.New(parsingFailed) } - return frame.buffer[controlByteCount:length], nil +func (frame *framer) extractMessage(length int) (message []byte, err error) { + if len(frame.buffer) < int(controlConstant) { return nil, errors.New(parsingFailed) } + if length < int(controlConstant) { return nil, errors.New(parsingFailed) } + return frame.buffer[controlConstant:length], nil } \ No newline at end of file diff --git a/network/frame_test.go b/network/framer_test.go similarity index 79% rename from network/frame_test.go rename to network/framer_test.go index f2eedaa..062d50a 100644 --- a/network/frame_test.go +++ b/network/framer_test.go @@ -8,13 +8,13 @@ import ( // TestFramerText is for testing the frame creator and parser. // Includes testing for multiple message extractions from single byte buffer. func TestFramerText(test *testing.T) { - frame := frame{} + framer := framer{} message := "Hello World!" var bytesBlock []byte - data, err := frame.create([]byte(message), TextMessage) + data, err := framer.create([]byte(message), TextMessage) if err != nil { test.Errorf("parsing failed") } for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } - err = frame.parse(bytesBlock, func(data []byte, opcode uint8) { + err = framer.parse(bytesBlock, func(data []byte, opcode uint8) { if opcode == TextMessage { if string(data) != message { test.Errorf("parsing failed") } } }) if err != nil { test.Errorf("parsing failed") } @@ -23,13 +23,13 @@ func TestFramerText(test *testing.T) { // TestFramerBinary is for testing the frame creator and parser. // Includes testing for multiple message extractions from single byte buffer. func TestFramerBinary(test *testing.T) { - frame := frame{} + framer := framer{} message := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} var bytesBlock []byte - data, err := frame.create(message, BinaryMessage) + data, err := framer.create(message, BinaryMessage) if err != nil { test.Errorf("parsing failed") } for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } - err = frame.parse(bytesBlock, func(data []byte, opcode uint8) { + err = framer.parse(bytesBlock, func(data []byte, opcode uint8) { if opcode != BinaryMessage { if !bytes.Equal(data, message) { test.Errorf("parsing failed") } } }) if err != nil { test.Errorf("parsing failed") } diff --git a/network/listener.go b/network/listener.go index 30a181c..51677c4 100644 --- a/network/listener.go +++ b/network/listener.go @@ -37,7 +37,7 @@ const ( // Listener struct represents a TCP based connection listener that handles incoming // pure TCP connections or TLS encrypted connections. type Listener struct { - frame frame + framer framer listener net.Listener TLSConfig *tls.Config @@ -81,7 +81,7 @@ func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []b // processingSend is a helper function to create and send a message frame over a connection. func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } - message, err := listener.frame.create(data, opcode) + message, err := listener.framer.create(data, opcode) if err != nil { if listener.Failed != nil { listener.Failed(err) } if conn != nil { err = conn.Close() }; return @@ -91,9 +91,9 @@ func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint } // processingParse is a helper function to parse a message frame from the connection data. -func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { +func (listener *Listener) processingParse(conn net.Conn, framer *framer, data []byte) error { if listener.listener == nil { return errors.New("parsing failed") } - err := frame.parse(data, func(data []byte, opcode uint8) { + err := framer.parse(data, func(data []byte, opcode uint8) { if listener.Message != nil { listener.Message(conn, data, opcode) } if opcode == pingMessage { listener.processingSend(conn, data, pingMessage) } }); return err @@ -109,11 +109,11 @@ func (listener *Listener) receiveMessage(conn net.Conn, interrupt time.Duration) if err != nil { if listener.Failed != nil { listener.Failed(err) }; return } } - var frame frame; buffer := make([]byte, maximum) + var framer framer; buffer := make([]byte, maximum) for { size, err := conn.Read(buffer) if err != nil { if err != io.EOF && listener.Failed != nil { listener.Failed(err) }; break } - err = listener.processingParse(conn, &frame, buffer[:size]) + err = listener.processingParse(conn, &framer, buffer[:size]) if err != nil { if listener.Failed != nil { listener.Failed(err) }; break } } }