Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Features:
* [node-endpoint-tcp-server](examples/node-endpoint-tcp-server/main.go)
* [node-endpoint-tcp-client](examples/node-endpoint-tcp-client/main.go)
* [node-endpoint-custom](examples/node-endpoint-custom/main.go)
* [node-endpoint-custom-client](examples/node-endpoint-custom-client/main.go)
* [node-endpoint-custom-server](examples/node-endpoint-custom-server/main.go)
* [node-message-read](examples/node-message-read/main.go)
* [node-message-write](examples/node-message-write/main.go)
* [node-signature](examples/node-signature/main.go)
Expand Down
68 changes: 54 additions & 14 deletions endpoint_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gomavlib

import (
"context"
"errors"
"fmt"
"io"
"net"
Expand All @@ -13,7 +14,6 @@ import (
var reconnectPeriod = 2 * time.Second

type endpointClientConf interface {
isUDP() bool
getAddress() string
init(*Node) (Endpoint, error)
}
Expand All @@ -27,10 +27,6 @@ type EndpointTCPClient struct {
Address string
}

func (EndpointTCPClient) isUDP() bool {
return false
}

func (conf EndpointTCPClient) getAddress() string {
return conf.Address
}
Expand All @@ -50,10 +46,6 @@ type EndpointUDPClient struct {
Address string
}

func (EndpointUDPClient) isUDP() bool {
return true
}

func (conf EndpointUDPClient) getAddress() string {
return conf.Address
}
Expand All @@ -67,6 +59,30 @@ func (conf EndpointUDPClient) init(node *Node) (Endpoint, error) {
return e, err
}

// EndpointCustomClient sets up a endpoint that works with a custom implementation
// by providing a Connect func that returns a net.Conn.
type EndpointCustomClient struct {
// domain name or IP of the server to connect to, example: 1.2.3.4:5600
Address string
// custom connect function that connects to the provided address
Connect func(address string) (net.Conn, error)
// the label of the protocol
Label string
}

func (conf EndpointCustomClient) getAddress() string {
return conf.Address
}

func (conf EndpointCustomClient) init(node *Node) (Endpoint, error) {
e := &endpointClient{
node: node,
conf: conf,
}
err := e.initialize()
return e, err
}

type endpointClient struct {
node *Node
conf endpointClientConf
Expand Down Expand Up @@ -103,16 +119,31 @@ func (e *endpointClient) oneChannelAtAtime() bool {

func (e *endpointClient) connect() (io.ReadWriteCloser, error) {
network := func() string {
if e.conf.isUDP() {
switch e.conf.(type) {
case EndpointTCPClient:
return "tcp4"
case EndpointUDPClient:
return "udp4"
case EndpointCustomClient:
return "cust"
default:
return ""
}
return "tcp4"
}()

// in UDP, the only possible error is a DNS failure
// in TCP, the handshake must be completed
timedContext, timedContextClose := context.WithTimeout(e.ctx, e.node.ReadTimeout)
nconn, err := (&net.Dialer{}).DialContext(timedContext, network, e.conf.getAddress())
nconn, err := func() (net.Conn, error) {
if network == "cust" {
customConf := e.conf.(EndpointCustomClient)
if customConf.Connect == nil {
return nil, errors.New("no connect function provided on custom endpoint")
}
return customConf.Connect(customConf.Address)
}
return (&net.Dialer{}).DialContext(timedContext, network, e.conf.getAddress())
}()
timedContextClose()

if err != nil {
Expand Down Expand Up @@ -154,9 +185,18 @@ func (e *endpointClient) provide() (string, io.ReadWriteCloser, error) {

func (e *endpointClient) label() string {
return fmt.Sprintf("%s:%s", func() string {
if e.conf.isUDP() {
switch conf := e.conf.(type) {
case EndpointTCPClient:
return "tcp"
case EndpointUDPClient:
return "udp"
case EndpointCustomClient:
if conf.Label != "" {
return conf.Label
}
return "custom"
default:
return "unknown"
}
return "tcp"
}(), e.conf.getAddress())
}
67 changes: 52 additions & 15 deletions endpoint_server.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package gomavlib

import (
"errors"
"fmt"
"io"
"net"

"github.com/pion/transport/v2/udp"

"github.com/bluenviron/gomavlib/v3/pkg/timednetconn"
"github.com/pion/transport/v2/udp"
)

type endpointServerConf interface {
isUDP() bool
getAddress() string
init(*Node) (Endpoint, error)
}
Expand All @@ -25,10 +24,6 @@ type EndpointTCPServer struct {
Address string
}

func (EndpointTCPServer) isUDP() bool {
return false
}

func (conf EndpointTCPServer) getAddress() string {
return conf.Address
}
Expand All @@ -50,10 +45,6 @@ type EndpointUDPServer struct {
Address string
}

func (EndpointUDPServer) isUDP() bool {
return true
}

func (conf EndpointUDPServer) getAddress() string {
return conf.Address
}
Expand All @@ -67,6 +58,32 @@ func (conf EndpointUDPServer) init(node *Node) (Endpoint, error) {
return e, err
}

// EndpointCustomServer sets up a endpoint that works with custom implementations
// by providing a custom Listen func that returns a net.Listener.
// This allows you to use custom protocols that conform to the net.listner.
// A use case could be to add encrypted protocol implementations like DTLS or TCP with TLS.
type EndpointCustomServer struct {
// listen address, example: 0.0.0.0:5600
Address string
// function to invoke when server should start listening
Listen func(address string) (net.Listener, error)
// the label of the protocol
Label string
}

func (conf EndpointCustomServer) getAddress() string {
return conf.Address
}

func (conf EndpointCustomServer) init(node *Node) (Endpoint, error) {
e := &endpointServer{
node: node,
conf: conf,
}
err := e.initialize()
return e, err
}

type endpointServer struct {
node *Node
conf endpointServerConf
Expand All @@ -83,7 +100,8 @@ func (e *endpointServer) initialize() error {
return fmt.Errorf("invalid address")
}

if e.conf.isUDP() {
switch conf := e.conf.(type) {
case EndpointUDPServer:
var addr *net.UDPAddr
addr, err = net.ResolveUDPAddr("udp4", e.conf.getAddress())
if err != nil {
Expand All @@ -94,11 +112,21 @@ func (e *endpointServer) initialize() error {
if err != nil {
return err
}
} else {

case EndpointTCPServer:
e.listener, err = net.Listen("tcp4", e.conf.getAddress())
if err != nil {
return err
}

case EndpointCustomServer:
e.listener, err = conf.Listen(e.conf.getAddress())
if err != nil {
return err
}

default:
return errors.New("unsupported server-type")
}

e.terminate = make(chan struct{})
Expand Down Expand Up @@ -130,10 +158,19 @@ func (e *endpointServer) provide() (string, io.ReadWriteCloser, error) {
}

label := fmt.Sprintf("%s:%s", func() string {
if e.conf.isUDP() {
switch conf := e.conf.(type) {
case EndpointTCPServer:
return "tcp"
case EndpointUDPServer:
return "udp"
case EndpointCustomServer:
if conf.Label != "" {
return conf.Label
}
return "custom"
default:
return "unknown"
}
return "tcp"
}(), nconn.RemoteAddr())

conn := timednetconn.New(
Expand Down
50 changes: 50 additions & 0 deletions examples/node-endpoint-custom-client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Package main contains an example.
package main

import (
"crypto/tls"
"log"
"net"

"github.com/bluenviron/gomavlib/v3"
"github.com/bluenviron/gomavlib/v3/pkg/dialects/ardupilotmega"
)

// this example shows how to:
// 1) create a node which communicates with a custom TCP/TLS endpoint in client mode.
// 2) print incoming messages.

func main() {
// create a node which communicates with a TCP endpoint in client mode
node := &gomavlib.Node{
Endpoints: []gomavlib.EndpointConf{
gomavlib.EndpointCustomClient{
Address: "127.0.0.1:5600",
Connect: func(address string) (net.Conn, error) {
tlsConfig := &tls.Config{
// skip checking the certificate against a CA (just set to true for simplicity of this example)
InsecureSkipVerify: true,
}

return tls.Dial("tcp", address, tlsConfig)
},
Label: "TCP/TLS",
},
},
Dialect: ardupilotmega.Dialect,
OutVersion: gomavlib.V2, // change to V1 if you're unable to communicate with the target
OutSystemID: 10,
}
err := node.Initialize()
if err != nil {
panic(err)
}
defer node.Close()

// print incoming messages
for evt := range node.Events() {
if frm, ok := evt.(*gomavlib.EventFrame); ok {
log.Printf("received: id=%d, %+v\n", frm.Message().GetID(), frm.Message())
}
}
}
1 change: 1 addition & 0 deletions examples/node-endpoint-custom-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
certs
Loading