firehol/netdata

View on GitHub
src/go/plugin/go.d/modules/freeradius/api/client.go

Summary

Maintainability
A
40 mins
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package api

import (
    "context"
    "crypto/hmac"
    "crypto/md5"
    "fmt"
    "net"
    "strconv"
    "time"

    "layeh.com/radius"
    "layeh.com/radius/rfc2869"
)

type Status struct {
    AccessRequests        int64 `stm:"access-requests"`
    AccessAccepts         int64 `stm:"access-accepts"`
    AccessRejects         int64 `stm:"access-rejects"`
    AccessChallenges      int64 `stm:"access-challenges"`
    AuthResponses         int64 `stm:"auth-responses"`
    AuthDuplicateRequests int64 `stm:"auth-duplicate-requests"`
    AuthMalformedRequests int64 `stm:"auth-malformed-requests"`
    AuthInvalidRequests   int64 `stm:"auth-invalid-requests"`
    AuthDroppedRequests   int64 `stm:"auth-dropped-requests"`
    AuthUnknownTypes      int64 `stm:"auth-unknown-types"`

    AccountingRequests    int64 `stm:"accounting-requests"`
    AccountingResponses   int64 `stm:"accounting-responses"`
    AcctDuplicateRequests int64 `stm:"acct-duplicate-requests"`
    AcctMalformedRequests int64 `stm:"acct-malformed-requests"`
    AcctInvalidRequests   int64 `stm:"acct-invalid-requests"`
    AcctDroppedRequests   int64 `stm:"acct-dropped-requests"`
    AcctUnknownTypes      int64 `stm:"acct-unknown-types"`

    ProxyAccessRequests        int64 `stm:"proxy-access-requests"`
    ProxyAccessAccepts         int64 `stm:"proxy-access-accepts"`
    ProxyAccessRejects         int64 `stm:"proxy-access-rejects"`
    ProxyAccessChallenges      int64 `stm:"proxy-access-challenges"`
    ProxyAuthResponses         int64 `stm:"proxy-auth-responses"`
    ProxyAuthDuplicateRequests int64 `stm:"proxy-auth-duplicate-requests"`
    ProxyAuthMalformedRequests int64 `stm:"proxy-auth-malformed-requests"`
    ProxyAuthInvalidRequests   int64 `stm:"proxy-auth-invalid-requests"`
    ProxyAuthDroppedRequests   int64 `stm:"proxy-auth-dropped-requests"`
    ProxyAuthUnknownTypes      int64 `stm:"proxy-auth-unknown-types"`

    ProxyAccountingRequests    int64 `stm:"proxy-accounting-requests"`
    ProxyAccountingResponses   int64 `stm:"proxy-accounting-responses"`
    ProxyAcctDuplicateRequests int64 `stm:"proxy-acct-duplicate-requests"`
    ProxyAcctMalformedRequests int64 `stm:"proxy-acct-malformed-requests"`
    ProxyAcctInvalidRequests   int64 `stm:"proxy-acct-invalid-requests"`
    ProxyAcctDroppedRequests   int64 `stm:"proxy-acct-dropped-requests"`
    ProxyAcctUnknownTypes      int64 `stm:"proxy-acct-unknown-types"`
}

type (
    radiusClient interface {
        Exchange(ctx context.Context, packet *radius.Packet, address string) (*radius.Packet, error)
    }
    Config struct {
        Address string
        Port    int
        Secret  string
        Timeout time.Duration
    }
    Client struct {
        address string
        secret  string
        timeout time.Duration
        radiusClient
    }
)

func New(conf Config) *Client {
    return &Client{
        address:      net.JoinHostPort(conf.Address, strconv.Itoa(conf.Port)),
        secret:       conf.Secret,
        timeout:      conf.Timeout,
        radiusClient: &radius.Client{Retry: time.Second, MaxPacketErrors: 10},
    }
}

func (c Client) Status() (*Status, error) {
    packet, err := newStatusServerPacket(c.secret)
    if err != nil {
        return nil, fmt.Errorf("error on creating StatusServer packet: %v", err)
    }

    resp, err := c.queryServer(packet)
    if err != nil {
        return nil, fmt.Errorf("error on request to '%s': %v", c.address, err)
    }

    return decodeResponse(resp), nil
}

func (c Client) queryServer(packet *radius.Packet) (*radius.Packet, error) {
    ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
    defer cancel()

    resp, err := c.Exchange(ctx, packet, c.address)
    if err != nil {
        return nil, err
    }

    if resp.Code != radius.CodeAccessAccept {
        return nil, fmt.Errorf("'%s' returned response code %d", c.address, resp.Code)
    }
    return resp, nil
}

func newStatusServerPacket(secret string) (*radius.Packet, error) {
    // https://wiki.freeradius.org/config/Status#status-of-freeradius-server
    packet := radius.New(radius.CodeStatusServer, []byte(secret))
    if err := FreeRADIUSStatisticsType_Set(packet, FreeRADIUSStatisticsType_Value_All); err != nil {
        return nil, err
    }
    if err := rfc2869.MessageAuthenticator_Set(packet, make([]byte, 16)); err != nil {
        return nil, err
    }
    hash := hmac.New(md5.New, packet.Secret)
    encode, err := packet.Encode()
    if err != nil {
        return nil, err
    }
    if _, err := hash.Write(encode); err != nil {
        return nil, err
    }
    if err := rfc2869.MessageAuthenticator_Set(packet, hash.Sum(nil)); err != nil {
        return nil, err
    }
    return packet, nil
}

func decodeResponse(resp *radius.Packet) *Status {
    return &Status{
        AccessRequests:             int64(FreeRADIUSTotalAccessRequests_Get(resp)),
        AccessAccepts:              int64(FreeRADIUSTotalAccessAccepts_Get(resp)),
        AccessRejects:              int64(FreeRADIUSTotalAccessRejects_Get(resp)),
        AccessChallenges:           int64(FreeRADIUSTotalAccessChallenges_Get(resp)),
        AuthResponses:              int64(FreeRADIUSTotalAuthResponses_Get(resp)),
        AuthDuplicateRequests:      int64(FreeRADIUSTotalAuthDuplicateRequests_Get(resp)),
        AuthMalformedRequests:      int64(FreeRADIUSTotalAuthMalformedRequests_Get(resp)),
        AuthInvalidRequests:        int64(FreeRADIUSTotalAuthInvalidRequests_Get(resp)),
        AuthDroppedRequests:        int64(FreeRADIUSTotalAuthDroppedRequests_Get(resp)),
        AuthUnknownTypes:           int64(FreeRADIUSTotalAuthUnknownTypes_Get(resp)),
        AccountingRequests:         int64(FreeRADIUSTotalAccountingRequests_Get(resp)),
        AccountingResponses:        int64(FreeRADIUSTotalAccountingResponses_Get(resp)),
        AcctDuplicateRequests:      int64(FreeRADIUSTotalAcctDuplicateRequests_Get(resp)),
        AcctMalformedRequests:      int64(FreeRADIUSTotalAcctMalformedRequests_Get(resp)),
        AcctInvalidRequests:        int64(FreeRADIUSTotalAcctInvalidRequests_Get(resp)),
        AcctDroppedRequests:        int64(FreeRADIUSTotalAcctDroppedRequests_Get(resp)),
        AcctUnknownTypes:           int64(FreeRADIUSTotalAcctUnknownTypes_Get(resp)),
        ProxyAccessRequests:        int64(FreeRADIUSTotalProxyAccessRequests_Get(resp)),
        ProxyAccessAccepts:         int64(FreeRADIUSTotalProxyAccessAccepts_Get(resp)),
        ProxyAccessRejects:         int64(FreeRADIUSTotalProxyAccessRejects_Get(resp)),
        ProxyAccessChallenges:      int64(FreeRADIUSTotalProxyAccessChallenges_Get(resp)),
        ProxyAuthResponses:         int64(FreeRADIUSTotalProxyAuthResponses_Get(resp)),
        ProxyAuthDuplicateRequests: int64(FreeRADIUSTotalProxyAuthDuplicateRequests_Get(resp)),
        ProxyAuthMalformedRequests: int64(FreeRADIUSTotalProxyAuthMalformedRequests_Get(resp)),
        ProxyAuthInvalidRequests:   int64(FreeRADIUSTotalProxyAuthInvalidRequests_Get(resp)),
        ProxyAuthDroppedRequests:   int64(FreeRADIUSTotalProxyAuthDroppedRequests_Get(resp)),
        ProxyAuthUnknownTypes:      int64(FreeRADIUSTotalProxyAuthUnknownTypes_Get(resp)),
        ProxyAccountingRequests:    int64(FreeRADIUSTotalProxyAccountingRequests_Get(resp)),
        ProxyAccountingResponses:   int64(FreeRADIUSTotalProxyAccountingResponses_Get(resp)),
        ProxyAcctDuplicateRequests: int64(FreeRADIUSTotalProxyAcctDuplicateRequests_Get(resp)),
        ProxyAcctMalformedRequests: int64(FreeRADIUSTotalProxyAcctMalformedRequests_Get(resp)),
        ProxyAcctInvalidRequests:   int64(FreeRADIUSTotalProxyAcctInvalidRequests_Get(resp)),
        ProxyAcctDroppedRequests:   int64(FreeRADIUSTotalProxyAcctDroppedRequests_Get(resp)),
        ProxyAcctUnknownTypes:      int64(FreeRADIUSTotalProxyAcctUnknownTypes_Get(resp)),
    }
}