Skip to content

Commit

Permalink
Separation of concerns for protocol observations (#35)
Browse files Browse the repository at this point in the history
* feat: creating sample Go module

* feat: example error handling

* refactor: random example communication message

* feat: example map data type (slice) & test cases

* Template for suggested experimental expectations (#24)

* feat: draft variable packet size experiment

* docs: new section for project replicability

* docs: markdown diagram (system control flow) + blockquote highlight

* refactor: utility to insights + diode collection script

* fix: text cache source location

* Delete `message` directory

* Delete `example` directory

* bump: latest Go milestone (minor release)

* Topic levels for MQTT (#27)

* feat: diode telemetry channel + updated configuration settings

* refactor: diode config settings & remove republish code

* feat: read file contents from text file

* feat: MQTT index, topic, payload, & checksum (example)

* feat: create new message counter via mutex

* feat: send file contents via MQTT telemetry topic

* nit: remove unused broker message contents

* refactor: message content of diode diagnostics

* docs: finalize project directory structure

* refactor: variables for testing content publisher

* feat: placeholder client + server (I/O)

* refactor: data diode settings & connection constants

* refactor: application constants with minimal error handling

* deps: remove outdated string metrics library

* docs: create B4-0144-355112.json (via Fend_B4_4.0.2.fw)

Source: #30

* refactor: diode test variables

* docs: mock external API for schema validation

* Mock system connection & message crafting (#34)

* bump: project requirements via upgrading direct dependencies

* feat: baseline for UUID + timestamp (MQTT)

* refactor: diode test binary logic + echo message

* feat: improve overall test coverage with republish contents

* refactor: content compontents for subscription overhaul

* fix: run all tests in current directory + subdirectories

* feat: MQTT test subscription + application (client/server)

* refactor: diode test command & server continuous stream

* fix: broken build with undefined import

* refactor: draft goroutine for server connection(s)
  • Loading branch information
TechSolomon authored Mar 25, 2024
1 parent a89f7c9 commit 084f41b
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 45 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
all: build

build:
go build -o diode -ldflags="-X main.SemVer=0.0.8" diode.go
go build -o diode -ldflags="-X main.SemVer=0.0.9" diode.go

test:
go test -v
go test -v ./...

run:
go run diode.go
Expand Down
2 changes: 1 addition & 1 deletion config/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ output:
port: 13337
tls: false
broker:
server: "test.mosquitto.org"
server: "localhost"
port: 1883
topic: "diode/telemetry"
8 changes: 4 additions & 4 deletions diode.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"time"

analysis "github.com/acep-uaf/data-diode/insights"
utility "github.com/acep-uaf/data-diode/utility"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -136,7 +137,7 @@ func main() {
Usage: "Testing state synchronization via diode I/O",
Action: func(tCtx *cli.Context) error {
fmt.Println("----- TEST -----")
exampleContents(InputTextFile)
utility.RepublishContents(InputTextFile, mqttBrokerIP, mqttBrokerTopic, mqttBrokerPort)
return nil
},
},
Expand All @@ -156,8 +157,7 @@ func main() {
Usage: "System benchmark analysis + report performance metrics",
Action: func(bCtx *cli.Context) error {
fmt.Println("----- BENCHMARKS -----")
// TODO: Perform specific benchmarks here...
// e.g. ping test, network throughput, system performance, disk I/O, memory usage
analysis.Pong()
return nil
},
},
Expand All @@ -167,7 +167,7 @@ func main() {
Usage: "MQTT → TCP stream demo",
Action: func(mCtx *cli.Context) error {
fmt.Println("----- MQTT -----")
republishContents(InputTextFile, mqttBrokerIP, mqttBrokerTopic, mqttBrokerPort)
utility.Subscription(mqttBrokerIP, mqttBrokerPort, mqttBrokerTopic, targetServerIP, targetServerPort)
return nil
},
},
Expand Down
151 changes: 142 additions & 9 deletions diode_test.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,77 @@
package main

import (
"bytes"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"

insights "github.com/acep-uaf/data-diode/insights"
"github.com/acep-uaf/data-diode/utility"
)

var (
BackupConfiguration = "config/B4-0144-355112.json"
SystemSettings = "config/settings.yaml"
FileChecksum = "477076c6fd8cf48ff2d0159b22bada27588c6fa84918d1c4fc20cd9ddd291dbd"
BackupConfiguration = "config/B4-0144-355112.json"
SystemSettings = "config/settings.yaml"
ProjectDocumentation = "docs/SOP.md"
FileChecksum = "477076c6fd8cf48ff2d0159b22bada27588c6fa84918d1c4fc20cd9ddd291dbd"
SampleMessage = "Hello, world."
InterfaceSize = 1024
InterfaceProtocol = "tcp"
InterfaceAddress = "localhost:13337"
)

type TCP struct {
ClientTargetIP string
ClientTargetPort int
ServerTargetIP string
ServerPort int
ServerSocketTimeout int
}

func TestAPI(t *testing.T) {
jsonFile, err := os.Open(BackupConfiguration)

schema := "CAMIO.2024.1.0"
version := filepath.Base(jsonFile.Name())

// FIXME: Cross reference the JSON contents, schema version, & configuration file?
fmt.Println(version, schema)

if err != nil {
t.Errorf("[?] %s via %s", err, jsonFile.Name())
}
}

func TestCLI(t *testing.T) {
got := "diode"
want := "diode"
binary := exec.Command("go", "build", "-o", "diode")
buildErr := binary.Run()
if buildErr != nil {
t.Fatalf("[!] Failed to build CLI binary: %v", buildErr)
}

if got != want {
t.Errorf("got %q, want %q", got, want)
cmd := exec.Command("./diode")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
t.Fatalf("[!] Failed to execute CLI command: %v", err)
}

expectation := "diode: try 'diode --help' for more information"
reality := strings.TrimSpace(stdout.String())
if reality != expectation {
t.Errorf("[?] Expected output: %q, but got: %q", expectation, reality)
}

if stderr.Len() > 0 {
t.Errorf("[?] Unexpected error output: %q", stderr.String())
}
}

Expand All @@ -43,10 +87,99 @@ func TestFileContents(t *testing.T) {
want := FileChecksum

if got != want {
t.Errorf("got %q, want %q", got, want)
t.Errorf(">> got %q, want %q", got, want)
}
}

func TestBinaryContents(t *testing.T) {
// TODO: Implement the following:
// - Craft a text message containing binary data + checksum.
// - Ensure transmission across data diode without corrupted information.
// - Check for uuenconding and base64 encoding / delimiters.

sample := []byte(SampleMessage)

if len(sample) == 0 {
t.Errorf("[!] No binary contents...")
}
}

func TestEchoMessage(t *testing.T) {
go func() {
listener, err := net.Listen(InterfaceProtocol, InterfaceAddress)
if err != nil {
t.Errorf("[!] Failed to start TCP server: %v", err)
}
defer listener.Close()

conn, err := listener.Accept()
if err != nil {
t.Errorf("[!] Failed to accept connection: %v", err)
}
defer conn.Close()

buf := make([]byte, InterfaceSize)
n, err := conn.Read(buf)
if err != nil {
t.Errorf("[!] Failed to read message: %v", err)
}

_, err = conn.Write(buf[:n])
if err != nil {
t.Errorf("[!] Failed to write message: %v", err)
}
}()

// TODO: Mock the TCP client/server to simulate the transmission of data.

conn, err := net.Dial(InterfaceProtocol, InterfaceAddress)
if err != nil {
t.Fatalf("[!] Failed to connect to TCP server: %v", err)
}
defer conn.Close()

message := SampleMessage
_, err = conn.Write([]byte(message))
if err != nil {
t.Fatalf("[!] Failed to send message: %v", err)
}

buf := make([]byte, len(message))
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("[!] Failed to read echoed message: %v", err)
}

match := string(buf[:n])
if match != message {
t.Errorf("[!] Echoed message does not match original message: got %q, want %q", match, message)
}
}

func TestRepublishContents(t *testing.T) {
// TODO: Mock the MQTT client.
location := ProjectDocumentation
broker := "localhost"
topic := "test/message"
port := 1883

// TODO: Mock the MQTT connection.

utility.RepublishContents(location, broker, topic, port)

if len(location) == 0 {
t.Errorf("[!] No location specified...")
}

if len(broker) == 0 {
t.Errorf("[!] No broker specified...")
}

if len(topic) == 0 {
t.Errorf("[!] No topic specified...")
}

if port == 0 {
t.Errorf("[!] No port specified...")
}
}

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ go 1.22.1

require (
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/google/uuid v1.6.0
github.com/olekukonko/tablewriter v0.0.5
github.com/urfave/cli/v2 v2.27.1
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
Expand Down
15 changes: 15 additions & 0 deletions insights/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ import (
"time"
)

func Pong() bool {
location := "https://example.com/"

resp, err := http.Get(location)

if err != nil {
return false
}
defer resp.Body.Close()

println(">> Status: ", resp.Status)

return true
}

func Checksum() [32]byte {
location := "https://www.gutenberg.org/cache/epub/84/pg84.txt"

Expand Down
34 changes: 19 additions & 15 deletions utility/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ func StartPlaceholderClient(host string, port int) {

func StartPlaceholderServer(host string, port int) {
listener, err := net.Listen(CONN_TYPE, fmt.Sprintf("%s:%d", host, port))

if err != nil {
fmt.Println(">> [!] Error listening: ", err.Error())
return
Expand All @@ -100,30 +99,35 @@ func StartPlaceholderServer(host string, port int) {
fmt.Println(">> Server waiting for connection...")

conn, err := listener.Accept()

if err != nil {
fmt.Println(">> [!] Error accepting: ", err.Error())
return
}

fmt.Println(">> Server accepted connection from: ", conn.RemoteAddr())

for {
data := make([]byte, SAMPLE)
_, err := conn.Read(data)
if err != nil {
fmt.Println(">> [!] Error receiving data: ", err.Error())
break
}
go func(conn net.Conn) {
defer conn.Close()

fmt.Printf(">> Received data: %s\n", string(data))
for {
data := make([]byte, SAMPLE)
bytesRead, err := conn.Read(data)
if err != nil {
fmt.Println(">> [!] Error receiving data: ", err.Error())
if err.Error() == "EOF" {
fmt.Println(">> Connection closed by client.")
return
}
}

_, err = conn.Write([]byte(ACKNOWLEDGEMENT))
fmt.Printf(">> Received data: %s\n", string(data[:bytesRead]))

if err != nil {
fmt.Println(">> [!] Error sending ACK: ", err.Error())
return
_, err = conn.Write([]byte(ACKNOWLEDGEMENT))
if err != nil {
fmt.Println(">> [!] Error sending ACK: ", err.Error())
return
}
}
}
}(conn)
}
}
21 changes: 21 additions & 0 deletions utility/application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package utility

import "testing"

func TestClient(t *testing.T) {
got := "server"
want := "client"

if got != want {
t.Errorf("got %q, want %q", got, want)
}
}

func TestServer(t *testing.T) {
got := "client"
want := "server"

if got != want {
t.Errorf("got %q, want %q", got, want)
}
}
Loading

0 comments on commit 084f41b

Please sign in to comment.