Skip to content

Commit

Permalink
Reworked set-listener message and API function to use netip.AddrPort
Browse files Browse the repository at this point in the history
  • Loading branch information
twystd committed May 27, 2024
1 parent f11a293 commit d5317b5
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ publish: release
gh release create "$(VERSION)" --draft --prerelease --title "$(VERSION)-beta" --notes-file release-notes.md

debug: build
go test ./... -run TestControllerAddrMarshalJSON
go test -v ./... -run TestSetListener

godoc:
godoc -http=:80 -index_interval=60s
Expand Down
5 changes: 2 additions & 3 deletions messages/set_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package messages

import (
"github.com/uhppoted/uhppote-core/types"
"net"
"net/netip"
)

type SetListenerRequest struct {
MsgType types.MsgType `uhppote:"value:0x90"`
SerialNumber types.SerialNumber `uhppote:"offset:4"`
Address net.IP `uhppote:"offset:8"`
Port uint16 `uhppote:"offset:12"`
AddrPort netip.AddrPort `uhppote:"offset:8"`
}

type SetListenerResponse struct {
Expand Down
15 changes: 5 additions & 10 deletions messages/set_listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package messages

import (
codec "github.com/uhppoted/uhppote-core/encoding/UTO311-L0x"
"net"
"net/netip"
"reflect"
"testing"
)
Expand All @@ -17,16 +17,12 @@ func TestMarshalSetListenerRequest(t *testing.T) {

request := SetListenerRequest{
SerialNumber: 423187757,
Address: net.IPv4(192, 168, 1, 100),
Port: 40000,
AddrPort: netip.MustParseAddrPort("192.168.1.100:40000"),
}

m, err := codec.Marshal(request)
if err != nil {
if m, err := codec.Marshal(request); err != nil {
t.Fatalf("Unexpected error: %v", err)
}

if !reflect.DeepEqual(m, expected) {
} else if !reflect.DeepEqual(m, expected) {
t.Errorf("Invalid byte array:\nExpected:\n%s\nReturned:\n%s", dump(expected, ""), dump(m, ""))
}
}
Expand All @@ -42,8 +38,7 @@ func TestFactoryUnmarshalSetListenerRequest(t *testing.T) {
expected := SetListenerRequest{
MsgType: 0x90,
SerialNumber: 423187757,
Address: net.IPv4(192, 168, 1, 100),
Port: 40000,
AddrPort: netip.MustParseAddrPort("192.168.1.100:40000"),
}

request, err := UnmarshalRequest(message)
Expand Down
26 changes: 13 additions & 13 deletions types/listener.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package types

import (
"fmt"
"net"
)

// import (
// "fmt"
// "net"
// )
//
// FIXME remove (unused)
type Listener struct {
SerialNumber SerialNumber
Address net.UDPAddr
}

func (l *Listener) String() string {
return fmt.Sprintf("%v %v", l.SerialNumber, l.Address)
}
// type Listener struct {
// SerialNumber SerialNumber
// Address net.UDPAddr
// }
//
// func (l *Listener) String() string {
// return fmt.Sprintf("%v %v", l.SerialNumber, l.Address)
// }
8 changes: 8 additions & 0 deletions uhppote/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uhppote

import (
"errors"
)

var ErrInvalidListenerAddress = errors.New("invalid listener address")
var ErrIncorrectController = errors.New("response from incorrect controller")
9 changes: 8 additions & 1 deletion uhppote/iuhppote.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ type IUHPPOTE interface {

SetAddress(deviceID uint32, address, mask, gateway net.IP) (*types.Result, error)
GetListener(deviceID uint32) (netip.AddrPort, error)
SetListener(deviceID uint32, address net.UDPAddr) (*types.Result, error)

// Sets the controller event listener address:port.
//
// The address must be either a valid IPv4 address and the port may not be 0 or
// 0.0.0.0:0.
// Returns true if the controller event listener address was set.
SetListener(deviceID uint32, address netip.AddrPort) (bool, error)

GetTime(deviceID uint32) (*types.Time, error)
SetTime(deviceID uint32, datetime time.Time) (*types.Time, error)
GetDoorControlState(deviceID uint32, door byte) (*types.DoorControlState, error)
Expand Down
32 changes: 17 additions & 15 deletions uhppote/set_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,35 @@ package uhppote

import (
"fmt"
"net"
"net/netip"

"github.com/uhppoted/uhppote-core/messages"
"github.com/uhppoted/uhppote-core/types"
)

func (u *uhppote) SetListener(serialNumber uint32, address net.UDPAddr) (*types.Result, error) {
if serialNumber == 0 {
return nil, fmt.Errorf("invalid device ID (%v)", serialNumber)
func (u *uhppote) SetListener(controller uint32, address netip.AddrPort) (bool, error) {
if controller == 0 {
return false, fmt.Errorf("invalid device ID (%v)", controller)
}

if address.IP.To4() == nil {
return nil, fmt.Errorf("invalid IP address: %v", address)
if !address.IsValid() {
return false, ErrInvalidListenerAddress
}

if (address != netip.MustParseAddrPort("0.0.0.0:0")) && (!address.Addr().Is4() || (address.Port() == 0)) {
return false, fmt.Errorf("invalid listener address: %v", address)
}

request := messages.SetListenerRequest{
SerialNumber: types.SerialNumber(serialNumber),
Address: address.IP,
Port: uint16(address.Port),
SerialNumber: types.SerialNumber(controller),
AddrPort: address,
}

if reply, err := sendto[messages.SetListenerResponse](u, serialNumber, request); err != nil {
return nil, err
if reply, err := sendto[messages.SetListenerResponse](u, controller, request); err != nil {
return false, err
} else if uint32(reply.SerialNumber) != controller {
return false, ErrIncorrectController
} else {
return &types.Result{
SerialNumber: reply.SerialNumber,
Succeeded: reply.Succeeded,
}, nil
return reply.Succeeded, nil
}
}
110 changes: 103 additions & 7 deletions uhppote/set_listener_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,116 @@
package uhppote

import (
"errors"
"fmt"
"net"
"net/netip"
"testing"
)

func TestSetListener(t *testing.T) {
message := []byte{
0x17, 0x90, 0x00, 0x00, 0x78, 0x37, 0x2a, 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

u := uhppote{
driver: &stub{
broadcastTo: func(addr *net.UDPAddr, request []byte, handler func([]byte) bool) ([]byte, error) {
return message, nil
},
},
}

if ok, err := u.SetListener(405419896, netip.MustParseAddrPort("192.168.1.100:60001")); err != nil {
t.Errorf("unexpected error (%v)", err)
} else if !ok {
t.Errorf("invalid response - expected:%v, got:%v", true, ok)
}
}

func TestSetListenerWithANYAddr(t *testing.T) {
message := []byte{
0x17, 0x90, 0x00, 0x00, 0x78, 0x37, 0x2a, 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

u := uhppote{
driver: &stub{
broadcastTo: func(addr *net.UDPAddr, request []byte, handler func([]byte) bool) ([]byte, error) {
return message, nil
},
},
}

if ok, err := u.SetListener(405419896, netip.MustParseAddrPort("0.0.0.0:0")); err != nil {
t.Errorf("unexpected error (%v)", err)
} else if !ok {
t.Errorf("invalid response - expected:%v, got:%v", true, ok)
}
}

func TestSetListenerWithResponseFromIncorrectController(t *testing.T) {
message := []byte{
0x17, 0x90, 0x00, 0x00, 0x2d, 0x55, 0x39, 0x19, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

u := uhppote{
driver: &stub{
broadcastTo: func(addr *net.UDPAddr, request []byte, handler func([]byte) bool) ([]byte, error) {
return message, nil
},
},
}

if ok, err := u.SetListener(405419896, netip.MustParseAddrPort("192.168.1.100:60001")); err == nil {
t.Errorf("expected 'invalid controller' error, got %v", err)
} else if fmt.Sprintf("%v", err) != "invalid controller ID - expected:405419896, got:423187757" {
t.Errorf("expected 'woot', got:%v", err)
} else if ok {
t.Errorf("expected 'not ok', got %v", ok)
}
}

func TestSetListenerWithInvalidDeviceID(t *testing.T) {
u := uhppote{}

_, err := u.SetListener(0, net.UDPAddr{
IP: net.IPv4(192, 168, 1, 100),
Port: 60001,
Zone: "",
})
if _, err := u.SetListener(0, netip.MustParseAddrPort("192.168.1.100:60001")); err == nil {
t.Errorf("Expected 'invalid device ID' error, got %v", err)
}
}

func TestSetListenerWithInvalidAddrPort(t *testing.T) {
u := uhppote{}

if _, err := u.SetListener(405419896, netip.AddrPort{}); !errors.Is(err, ErrInvalidListenerAddress) {
t.Errorf("Expected 'invalid listener address' error, got %v", err)
}
}

func TestSetListenerWithIPv6Address(t *testing.T) {
u := uhppote{}

if _, err := u.SetListener(405419896, netip.MustParseAddrPort("[2001:db8::68]:60001")); err == nil {
t.Errorf("Expected 'invalid listener address: [2001:db8::68]:60001', got %v", err)
} else if fmt.Sprintf("%v", err) != "invalid listener address: [2001:db8::68]:60001" {
t.Errorf("Expected 'invalid listener address: [2001:db8::68]:60001', got %v", err)
}
}

func TestSetListenerWithInvalidPort(t *testing.T) {
u := uhppote{}

if err == nil {
t.Fatalf("Expected 'Invalid device ID' error, got %v", err)
if _, err := u.SetListener(405419896, netip.MustParseAddrPort("192.168.1.100:0")); err == nil {
t.Errorf("Expected 'invalid listener address: 192.168.1.100:0', got %v", err)
} else if fmt.Sprintf("%v", err) != "invalid listener address: 192.168.1.100:0" {
t.Errorf("Expected 'invalid listener address: 192.168.1.100:0', got %v", err)
}
}

0 comments on commit d5317b5

Please sign in to comment.