Skip to content

Commit

Permalink
Added Unbound Host Override
Browse files Browse the repository at this point in the history
  • Loading branch information
elacy committed Oct 4, 2024
1 parent 83ff360 commit ac07898
Show file tree
Hide file tree
Showing 6 changed files with 419 additions and 1 deletion.
5 changes: 4 additions & 1 deletion pfsenseapi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"encoding/json"
"errors"
"fmt"
"golang.org/x/exp/slices"
"io"
"net/http"
"sync"
"time"

"golang.org/x/exp/slices"
)

var (
Expand Down Expand Up @@ -39,6 +40,7 @@ type Client struct {
System *SystemService
Token *TokenService
DHCP *DHCPService
Unbound *UnboundService
Status *StatusService
Interface *InterfaceService
Routing *RoutingService
Expand Down Expand Up @@ -96,6 +98,7 @@ func NewClient(config Config) *Client {
newClient.Routing = &RoutingService{client: newClient}
newClient.Firewall = &FirewallService{client: newClient}
newClient.User = &UserService{client: newClient}
newClient.Unbound = &UnboundService{client: newClient}
return newClient
}

Expand Down
22 changes: 22 additions & 0 deletions pfsenseapi/stringarray.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package pfsenseapi

import "strings"

// StringArray is designed to unmarshal PFSense string arrays which look like
// "192.168.0.1,192.168.1.1"
type StringArray []string

// UnmarshalJSON implements the json.Unmarshaler interface.
func (sa *StringArray) UnmarshalJSON(data []byte) error {
// Empty string is ""
if len(data) <= 2 {
*sa = make(StringArray, 0)
return nil

Check warning on line 14 in pfsenseapi/stringarray.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/stringarray.go#L13-L14

Added lines #L13 - L14 were not covered by tests
}

// Remove quotes from string
data = data[1 : len(data)-1]

*sa = strings.Split(string(data), ",")
return nil
}
22 changes: 22 additions & 0 deletions pfsenseapi/testdata/listhostoverrides.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"status": "ok",
"code": 200,
"return": 0,
"message": "Success",
"data": [
{
"host": "one",
"domain": "test.com",
"ip": "192.168.1.1",
"descr": "1",
"aliases": ""
},
{
"host": "two",
"domain": "test.com",
"ip": "192.168.1.1",
"descr": "two",
"aliases": ""
}
]
}
14 changes: 14 additions & 0 deletions pfsenseapi/testdata/updatehostoverride.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"status": "ok",
"code": 200,
"return": 0,
"message": "Success",
"data":
{
"host": "two",
"domain": "test.com",
"ip": "192.168.1.1",
"descr": "two",
"aliases": ""
}
}
184 changes: 184 additions & 0 deletions pfsenseapi/unbound.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package pfsenseapi

import (
"context"
"encoding/json"
"fmt"
"strconv"
)

const (
hostOverrideEndpoint = "api/v1/services/unbound/host_override"
)

// Unbound provides Unbound API methods
type UnboundService service

// Gateway represents a single routing gateway
type UnboundHostOverride struct {
Aliases *UnboundAliasesList `json:"aliases,omitempty"`
Description string `json:"descr"`
Domain string `json:"domain"`
Host string `json:"host"`
IP StringArray `json:"ip"`
}

type UnboundAliasesList struct {
Items []*UnboundHostOverrideAlias `json:"item"`
}

func (ual *UnboundAliasesList) UnmarshalJSON(data []byte) error {
if len(data) == 0 || (len(data) == 2 && string(data) == "\"\"") {
*ual = UnboundAliasesList{}
return nil
}

type unboundAliasesList UnboundAliasesList

Check warning on line 36 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L36

Added line #L36 was not covered by tests

aux := &struct {
*unboundAliasesList
}{
unboundAliasesList: (*unboundAliasesList)(ual),

Check warning on line 41 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L38-L41

Added lines #L38 - L41 were not covered by tests
}

return json.Unmarshal(data, &aux)

Check warning on line 44 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L44

Added line #L44 was not covered by tests
}

type UnboundHostOverrideAlias struct {
Host string `json:"host"`
Domain string `json:"domain"`
Description string `json:"description"`
}

type apiWriteResponse[ResponseType any] struct {
apiResponse
Data *ResponseType `json:"data"`
}

type apiListResponse[ResponseType any] struct {
apiResponse
Data []ResponseType `json:"data"`
}

type createHostOverride struct {
UnboundHostOverride
Apply bool `json:"apply"`
}

func (s UnboundService) CreateHostOverride(
ctx context.Context,
hostOverride *UnboundHostOverride,
apply bool,
) (*UnboundHostOverride, error) {
jsonData, err := json.Marshal(&createHostOverride{
UnboundHostOverride: *hostOverride,
Apply: apply,
})

if err != nil {
return nil, err

Check warning on line 79 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L79

Added line #L79 was not covered by tests
}

response, err := s.client.post(ctx, hostOverrideEndpoint, nil, jsonData)
if err != nil {
return nil, err

Check warning on line 84 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L84

Added line #L84 was not covered by tests
}

return s.parseWriteResponse(response)
}

type updateHostOverride struct {
UnboundHostOverride
Apply bool `json:"apply"`
Id string `json:"id"`
}

func (s UnboundService) parseWriteResponse(
response []byte,
) (*UnboundHostOverride, error) {
resp := new(apiWriteResponse[UnboundHostOverride])
if err := json.Unmarshal(response, resp); err != nil {
return nil, err

Check warning on line 101 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L101

Added line #L101 was not covered by tests
}

return resp.Data, nil
}

func (s UnboundService) UpdateHostOverride(
ctx context.Context,
hostOverride *UnboundHostOverride,
apply bool,
) (*UnboundHostOverride, error) {
id, err := s.getHostOverridesObjectId(ctx, hostOverride.Host, hostOverride.Domain)
if err != nil {
return nil, fmt.Errorf("error finding override: %v", err)

Check warning on line 114 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L114

Added line #L114 was not covered by tests
}

jsonData, err := json.Marshal(&updateHostOverride{
UnboundHostOverride: *hostOverride,
Apply: apply,
Id: fmt.Sprint(id),
})

if err != nil {
return nil, fmt.Errorf("error marshaling request: %v", err)

Check warning on line 124 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L124

Added line #L124 was not covered by tests
}

response, err := s.client.put(ctx, hostOverrideEndpoint, nil, jsonData)
if err != nil {
return nil, fmt.Errorf("error sending request: %v", err)

Check warning on line 129 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L129

Added line #L129 was not covered by tests
}

return s.parseWriteResponse(response)
}

// Gets the index of the Unbound Host Override, you can only have one host override with the same host, name and ip type
func (s UnboundService) getHostOverridesObjectId(ctx context.Context, host string, domain string) (int, error) {
list, err := s.ListHostOverrides(ctx)

if err != nil {
return 0, err

Check warning on line 140 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L140

Added line #L140 was not covered by tests
}

for i, item := range list {
if item.Host == host && item.Domain == domain {
return i, nil
}
}

return 0, fmt.Errorf("Unable to find host override with host %s, domain %s", host, domain)

Check warning on line 149 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L149

Added line #L149 was not covered by tests
}

func (s UnboundService) ListHostOverrides(ctx context.Context) ([]*UnboundHostOverride, error) {
response, err := s.client.get(ctx, hostOverrideEndpoint, nil)
if err != nil {
return nil, err
}

resp := new(apiListResponse[*UnboundHostOverride])
if err = json.Unmarshal(response, &resp); err != nil {
return nil, err
}
return resp.Data, nil
}

func (s UnboundService) DeleteHostOverride(ctx context.Context, host string, domain string, apply bool) error {
id, err := s.getHostOverridesObjectId(ctx, host, domain)

if err != nil {
return err

Check warning on line 169 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L169

Added line #L169 was not covered by tests
}

queryMap := map[string]string{
"id": fmt.Sprint(id),

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2ConnectionError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2ErrCode.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2FrameHeader.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2FrameType.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2GoAwayError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2Setting.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2SettingID.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2StreamError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2connError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2duplicatePseudoHeaderError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2headerFieldNameError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2headerFieldValueError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2pseudoHeaderError.Error

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.http2writeData.String

Check warning

Code scanning / govulncheck

GO-2024-2687 Warning

pfsenseapi/unbound.go:173:22: github.com/sjafferali/pfsense-api-goclient/pfsenseapi.DeleteHostOverride calls fmt.Sprint, which eventually calls net/http.transportReadFromServerError.Error
"apply": strconv.FormatBool(apply),
}

_, err = s.client.delete(ctx, hostOverrideEndpoint, queryMap)

if err != nil {
return err

Check warning on line 180 in pfsenseapi/unbound.go

View check run for this annotation

Codecov / codecov/patch

pfsenseapi/unbound.go#L180

Added line #L180 was not covered by tests
}

return nil
}
Loading

0 comments on commit ac07898

Please sign in to comment.