diff --git a/tools/echo_server/README.md b/tools/echo_server/README.md index 23a4b92..d7dcf4a 100644 --- a/tools/echo_server/README.md +++ b/tools/echo_server/README.md @@ -26,6 +26,8 @@ The JSON file contains the following options: 1. Relative or absolute path to the server certificate generated in the credential creation prerequisite. 1. server-key-location 1. Relative or absolute path to the server key generated in the credential creation prerequisite. +1. use-udp + 1. Enable this option to run the USP echo server. ## Example Configuration ```json { @@ -34,7 +36,8 @@ The JSON file contains the following options: "secure-connection": false, "server-port": "9000", "server-certificate-location": "./certs/server.pem", - "server-key-location": "./certs/server.key" + "server-key-location": "./certs/server.key", + "use-udp": false } ``` diff --git a/tools/echo_server/config.json b/tools/echo_server/config.json index 6d2759b..aee38bd 100644 --- a/tools/echo_server/config.json +++ b/tools/echo_server/config.json @@ -5,5 +5,6 @@ "server-port": "9000", "cert-verify": true, "server-certificate-location": "./certs/server.pem", - "server-key-location": "./certs/server.key" + "server-key-location": "./certs/server.key", + "use-udp": false } diff --git a/tools/echo_server/echo_server.go b/tools/echo_server/echo_server.go index d4d0cd7..5349c04 100644 --- a/tools/echo_server/echo_server.go +++ b/tools/echo_server/echo_server.go @@ -23,90 +23,137 @@ * http://www.FreeRTOS.org */ -package main - -import ( - "crypto/rand" - "crypto/tls" - "crypto/x509" - "encoding/json" - "encoding/hex" - "flag" - "io" - "io/ioutil" - "log" - "net" - "os" - "time" - "bytes" -) - -const readTimeoutSecond = 300 -const disconnectCmd = "DISCONNECT" - -// Argument struct for JSON configuration -type Argument struct { - Verbose bool `json:"verbose"` - Logging bool `json:"logging"` - Secure bool `json:"secure-connection"` - CertVerify bool `json:"cert-verify"` - ServerPort string `json:"server-port"` - ServerCert string `json:"server-certificate-location"` - ServerKey string `json:"server-key-location"` -} + package main -func secureEcho(certPath string, keyPath string, port string, certVerify bool, verbose bool) { + import ( + "crypto/rand" + "crypto/tls" + "crypto/x509" + "encoding/json" + "encoding/hex" + "flag" + "io" + "io/ioutil" + "log" + "net" + "os" + "time" + "bytes" + ) - // load certificates - servertCert, err := tls.LoadX509KeyPair(certPath, keyPath) - if err != nil { - log.Fatalf("Error %s while loading server certificates", err) - } + const readTimeoutSecond = 300 + const disconnectCmd = "DISCONNECT" - serverCA, err := ioutil.ReadFile(certPath) - if err != nil { - log.Fatalf("Error %s while reading server certificates", err) - } + // Argument struct for JSON configuration + type Argument struct { + Verbose bool `json:"verbose"` + Logging bool `json:"logging"` + Secure bool `json:"secure-connection"` + CertVerify bool `json:"cert-verify"` + UseUDP bool `json:"use-udp"` + ServerPort string `json:"server-port"` + ServerCert string `json:"server-certificate-location"` + ServerKey string `json:"server-key-location"` + } - serverCAPool := x509.NewCertPool() - serverCAPool.AppendCertsFromPEM(serverCA) - - var clientAuth tls.ClientAuthType - if certVerify { - clientAuth = tls.RequireAndVerifyClientCert - } else { - clientAuth = tls.RequireAnyClientCert - } - - //Configure TLS - tlsConfig := tls.Config{Certificates: []tls.Certificate{servertCert}, - MinVersion: tls.VersionTLS12, - RootCAs: serverCAPool, - ClientAuth: clientAuth, - ClientCAs: serverCAPool, - // Cipher suites supported by AWS IoT Core. Note this is the intersection of the set - // of cipher suites supported by GO and by AWS IoT Core. - // See the complete list of supported cipher suites at https://docs.aws.amazon.com/iot/latest/developerguide/transport-security.html. - CipherSuites: []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_AES_128_GCM_SHA256, - tls.TLS_AES_256_GCM_SHA384, - }, - } + func secureEcho(certPath string, keyPath string, port string, certVerify bool, verbose bool) { - tlsConfig.Rand = rand.Reader - echoServerThread(port, &tlsConfig, verbose) -} + // load certificates + servertCert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + log.Fatalf("Error %s while loading server certificates", err) + } + + serverCA, err := ioutil.ReadFile(certPath) + if err != nil { + log.Fatalf("Error %s while reading server certificates", err) + } + + serverCAPool := x509.NewCertPool() + serverCAPool.AppendCertsFromPEM(serverCA) + + var clientAuth tls.ClientAuthType + if certVerify { + clientAuth = tls.RequireAndVerifyClientCert + } else { + clientAuth = tls.RequireAnyClientCert + } + + //Configure TLS + tlsConfig := tls.Config{Certificates: []tls.Certificate{servertCert}, + MinVersion: tls.VersionTLS12, + RootCAs: serverCAPool, + ClientAuth: clientAuth, + ClientCAs: serverCAPool, + // Cipher suites supported by AWS IoT Core. Note this is the intersection of the set + // of cipher suites supported by GO and by AWS IoT Core. + // See the complete list of supported cipher suites at https://docs.aws.amazon.com/iot/latest/developerguide/transport-security.html. + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + }, + } + + tlsConfig.Rand = rand.Reader + echoServerThread(port, &tlsConfig, verbose) + } -func echoServerThread(port string, tlsConfig *tls.Config, verbose bool) { + func echoServerThread(port string, tlsConfig *tls.Config, verbose bool) { + // listen on all interfaces + var echoServer net.Listener + var err error + + echoServer, err = net.Listen("tcp", ":"+port) + + if err != nil { + log.Fatalf("While trying to listen for a connection an error occurred %s.", err) + } else { + defer echoServer.Close() + } + + for { + connection, err := echoServer.Accept() + + if tcpConn, ok := connection.(*net.TCPConn); ok { + err := tcpConn.SetLinger(0) + if err != nil { + log.Printf("Error when setting linger: %s", err) + } + } else { + log.Printf("Cast TCP connection failed") + } + + + if tlsConfig != nil { + connection = tls.Server( connection, tlsConfig ) + log.Println("Opening secure TCP server listening to port " + port) + } else { + log.Println("Opening unsecure TCP server listening to port " + port) + } + + if err != nil { + log.Printf("Error %s while trying to connect.", err) + } else { + go readWrite(connection, verbose) + } + } + } + + func udpEchoServerThread(port string, verbose bool) { // listen on all interfaces - var echoServer net.Listener + var echoServer *net.UDPConn var err error - echoServer, err = net.Listen("tcp", ":"+port) + serverAddr, err := net.ResolveUDPAddr("udp",":"+port) + if err != nil { + log.Fatalf("While trying to resolve UDP address an error occurred %s.", err) + } + + echoServer, err = net.ListenUDP("udp", serverAddr) if err != nil { log.Fatalf("While trying to listen for a connection an error occurred %s.", err) @@ -115,40 +162,16 @@ func echoServerThread(port string, tlsConfig *tls.Config, verbose bool) { } for { - connection, err := echoServer.Accept() - - if tcpConn, ok := connection.(*net.TCPConn); ok { - err := tcpConn.SetLinger(0) - if err != nil { - log.Printf("Error when setting linger: %s", err) - } - } else { - log.Printf("Cast TCP connection failed") - } - - - if tlsConfig != nil { - connection = tls.Server( connection, tlsConfig ) - log.Println("Opening secure TCP server listening to port " + port) - } else { - log.Println("Opening unsecure TCP server listening to port " + port) - } - - if err != nil { - log.Printf("Error %s while trying to connect.", err) - } else { - go readWrite(connection, verbose) - } + udpReadWrite(echoServer, verbose) } -} + } -func readWrite(connection net.Conn, verbose bool) { + func udpReadWrite(connection *net.UDPConn, verbose bool) { defer connection.Close() buffer := make([]byte, 4096) firstMessage := true for { - connection.SetReadDeadline(time.Now().Add(readTimeoutSecond * time.Second)) - readBytes, err := connection.Read(buffer) + readBytes, addr, err := connection.ReadFromUDP(buffer) if err != nil { if err != io.EOF { log.Printf("Error %s while reading data. Expected an EOF to signal end of connection", err) @@ -166,58 +189,97 @@ func readWrite(connection net.Conn, verbose bool) { } firstMessage = false; } - writeBytes, err := connection.Write(buffer[:readBytes]) + writeBytes, err := connection.WriteToUDP(buffer[:readBytes], addr) if err != nil { log.Printf("Failed to send data with error: %s ", err) break } if writeBytes != 0 { - log.Printf("Succesfully echoed back %d bytes.", writeBytes) + log.Printf("Successfully echoed back %d bytes.", writeBytes) } } } -func startup(config Argument) { - log.Println("Starting TCP Echo application...") - if config.Secure { - secureEcho(config.ServerCert, config.ServerKey, config.ServerPort, config.CertVerify, config.Verbose) - } - echoServerThread(config.ServerPort, nil, config.Verbose) -} + func readWrite(connection net.Conn, verbose bool) { + defer connection.Close() + buffer := make([]byte, 4096) + firstMessage := true + for { + connection.SetReadDeadline(time.Now().Add(readTimeoutSecond * time.Second)) + readBytes, err := connection.Read(buffer) + if err != nil { + if err != io.EOF { + log.Printf("Error %s while reading data. Expected an EOF to signal end of connection", err) + } + break + } else { + log.Printf("Read %d bytes.", readBytes) + if verbose { + hexStr := hex.EncodeToString( buffer[:readBytes] ) + log.Printf("Hex message:\n%s", hexStr) + } + if firstMessage && bytes.Contains(buffer, []byte(disconnectCmd)) { + log.Printf("Server disconnect command received.") + break + } + firstMessage = false; + } + writeBytes, err := connection.Write(buffer[:readBytes]) + if err != nil { + log.Printf("Failed to send data with error: %s ", err) + break + } -func logSetup() { - echoLogFile, e := os.OpenFile("echo_server.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if e != nil { - log.Fatal("Failed to open log file.") - } else { - multi := io.MultiWriter(echoLogFile, os.Stdout) - log.SetOutput(multi) - } -} + if writeBytes != 0 { + log.Printf("Successfully echoed back %d bytes.", writeBytes) + } + } + } -func main() { + func startup(config Argument) { + log.Println("Starting Echo application...") + if config.Secure { + secureEcho(config.ServerCert, config.ServerKey, config.ServerPort, config.CertVerify, config.Verbose) + } + if config.UseUDP { + udpEchoServerThread(config.ServerPort, config.Verbose) + } + echoServerThread(config.ServerPort, nil, config.Verbose) + } - configLocation := flag.String("config", "./config.json", "Path to a JSON configuration.") - flag.Parse() - jsonFile, err := os.Open(*configLocation) + func logSetup() { + echoLogFile, e := os.OpenFile("echo_server.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if e != nil { + log.Fatal("Failed to open log file.") + } else { + multi := io.MultiWriter(echoLogFile, os.Stdout) + log.SetOutput(multi) + } + } - if err != nil { - log.Fatalf("Failed to open file with error: %s", err) - } - defer jsonFile.Close() + func main() { - byteValue, _ := ioutil.ReadAll(jsonFile) + configLocation := flag.String("config", "./config.json", "Path to a JSON configuration.") + flag.Parse() + jsonFile, err := os.Open(*configLocation) - var config Argument - err = json.Unmarshal(byteValue, &config) - if err != nil { - log.Fatalf("Failed to unmarshal json with error: %s", err) - } + if err != nil { + log.Fatalf("Failed to open file with error: %s", err) + } + defer jsonFile.Close() - if config.Logging { - logSetup() - } + byteValue, _ := ioutil.ReadAll(jsonFile) - startup(config) -} + var config Argument + err = json.Unmarshal(byteValue, &config) + if err != nil { + log.Fatalf("Failed to unmarshal json with error: %s", err) + } + + if config.Logging { + logSetup() + } + + startup(config) + }