github.com/netdata/go.d.plugin@v0.58.1/modules/freeradius/api/client.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package api
     4  
     5  import (
     6  	"context"
     7  	"crypto/hmac"
     8  	"crypto/md5"
     9  	"fmt"
    10  	"net"
    11  	"strconv"
    12  	"time"
    13  
    14  	"layeh.com/radius"
    15  	"layeh.com/radius/rfc2869"
    16  )
    17  
    18  type Status struct {
    19  	AccessRequests        int64 `stm:"access-requests"`
    20  	AccessAccepts         int64 `stm:"access-accepts"`
    21  	AccessRejects         int64 `stm:"access-rejects"`
    22  	AccessChallenges      int64 `stm:"access-challenges"`
    23  	AuthResponses         int64 `stm:"auth-responses"`
    24  	AuthDuplicateRequests int64 `stm:"auth-duplicate-requests"`
    25  	AuthMalformedRequests int64 `stm:"auth-malformed-requests"`
    26  	AuthInvalidRequests   int64 `stm:"auth-invalid-requests"`
    27  	AuthDroppedRequests   int64 `stm:"auth-dropped-requests"`
    28  	AuthUnknownTypes      int64 `stm:"auth-unknown-types"`
    29  
    30  	AccountingRequests    int64 `stm:"accounting-requests"`
    31  	AccountingResponses   int64 `stm:"accounting-responses"`
    32  	AcctDuplicateRequests int64 `stm:"acct-duplicate-requests"`
    33  	AcctMalformedRequests int64 `stm:"acct-malformed-requests"`
    34  	AcctInvalidRequests   int64 `stm:"acct-invalid-requests"`
    35  	AcctDroppedRequests   int64 `stm:"acct-dropped-requests"`
    36  	AcctUnknownTypes      int64 `stm:"acct-unknown-types"`
    37  
    38  	ProxyAccessRequests        int64 `stm:"proxy-access-requests"`
    39  	ProxyAccessAccepts         int64 `stm:"proxy-access-accepts"`
    40  	ProxyAccessRejects         int64 `stm:"proxy-access-rejects"`
    41  	ProxyAccessChallenges      int64 `stm:"proxy-access-challenges"`
    42  	ProxyAuthResponses         int64 `stm:"proxy-auth-responses"`
    43  	ProxyAuthDuplicateRequests int64 `stm:"proxy-auth-duplicate-requests"`
    44  	ProxyAuthMalformedRequests int64 `stm:"proxy-auth-malformed-requests"`
    45  	ProxyAuthInvalidRequests   int64 `stm:"proxy-auth-invalid-requests"`
    46  	ProxyAuthDroppedRequests   int64 `stm:"proxy-auth-dropped-requests"`
    47  	ProxyAuthUnknownTypes      int64 `stm:"proxy-auth-unknown-types"`
    48  
    49  	ProxyAccountingRequests    int64 `stm:"proxy-accounting-requests"`
    50  	ProxyAccountingResponses   int64 `stm:"proxy-accounting-responses"`
    51  	ProxyAcctDuplicateRequests int64 `stm:"proxy-acct-duplicate-requests"`
    52  	ProxyAcctMalformedRequests int64 `stm:"proxy-acct-malformed-requests"`
    53  	ProxyAcctInvalidRequests   int64 `stm:"proxy-acct-invalid-requests"`
    54  	ProxyAcctDroppedRequests   int64 `stm:"proxy-acct-dropped-requests"`
    55  	ProxyAcctUnknownTypes      int64 `stm:"proxy-acct-unknown-types"`
    56  }
    57  
    58  type (
    59  	radiusClient interface {
    60  		Exchange(ctx context.Context, packet *radius.Packet, address string) (*radius.Packet, error)
    61  	}
    62  	Config struct {
    63  		Address string
    64  		Port    int
    65  		Secret  string
    66  		Timeout time.Duration
    67  	}
    68  	Client struct {
    69  		address string
    70  		secret  string
    71  		timeout time.Duration
    72  		radiusClient
    73  	}
    74  )
    75  
    76  func New(conf Config) *Client {
    77  	return &Client{
    78  		address:      net.JoinHostPort(conf.Address, strconv.Itoa(conf.Port)),
    79  		secret:       conf.Secret,
    80  		timeout:      conf.Timeout,
    81  		radiusClient: &radius.Client{Retry: time.Second, MaxPacketErrors: 10},
    82  	}
    83  }
    84  
    85  func (c Client) Status() (*Status, error) {
    86  	packet, err := newStatusServerPacket(c.secret)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("error on creating StatusServer packet: %v", err)
    89  	}
    90  
    91  	resp, err := c.queryServer(packet)
    92  	if err != nil {
    93  		return nil, fmt.Errorf("error on request to '%s': %v", c.address, err)
    94  	}
    95  
    96  	return decodeResponse(resp), nil
    97  }
    98  
    99  func (c Client) queryServer(packet *radius.Packet) (*radius.Packet, error) {
   100  	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
   101  	defer cancel()
   102  
   103  	resp, err := c.Exchange(ctx, packet, c.address)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	if resp.Code != radius.CodeAccessAccept {
   109  		return nil, fmt.Errorf("'%s' returned response code %d", c.address, resp.Code)
   110  	}
   111  	return resp, nil
   112  }
   113  
   114  func newStatusServerPacket(secret string) (*radius.Packet, error) {
   115  	// https://wiki.freeradius.org/config/Status#status-of-freeradius-server
   116  	packet := radius.New(radius.CodeStatusServer, []byte(secret))
   117  	if err := FreeRADIUSStatisticsType_Set(packet, FreeRADIUSStatisticsType_Value_All); err != nil {
   118  		return nil, err
   119  	}
   120  	if err := rfc2869.MessageAuthenticator_Set(packet, make([]byte, 16)); err != nil {
   121  		return nil, err
   122  	}
   123  	hash := hmac.New(md5.New, packet.Secret)
   124  	encode, err := packet.Encode()
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	if _, err := hash.Write(encode); err != nil {
   129  		return nil, err
   130  	}
   131  	if err := rfc2869.MessageAuthenticator_Set(packet, hash.Sum(nil)); err != nil {
   132  		return nil, err
   133  	}
   134  	return packet, nil
   135  }
   136  
   137  func decodeResponse(resp *radius.Packet) *Status {
   138  	return &Status{
   139  		AccessRequests:             int64(FreeRADIUSTotalAccessRequests_Get(resp)),
   140  		AccessAccepts:              int64(FreeRADIUSTotalAccessAccepts_Get(resp)),
   141  		AccessRejects:              int64(FreeRADIUSTotalAccessRejects_Get(resp)),
   142  		AccessChallenges:           int64(FreeRADIUSTotalAccessChallenges_Get(resp)),
   143  		AuthResponses:              int64(FreeRADIUSTotalAuthResponses_Get(resp)),
   144  		AuthDuplicateRequests:      int64(FreeRADIUSTotalAuthDuplicateRequests_Get(resp)),
   145  		AuthMalformedRequests:      int64(FreeRADIUSTotalAuthMalformedRequests_Get(resp)),
   146  		AuthInvalidRequests:        int64(FreeRADIUSTotalAuthInvalidRequests_Get(resp)),
   147  		AuthDroppedRequests:        int64(FreeRADIUSTotalAuthDroppedRequests_Get(resp)),
   148  		AuthUnknownTypes:           int64(FreeRADIUSTotalAuthUnknownTypes_Get(resp)),
   149  		AccountingRequests:         int64(FreeRADIUSTotalAccountingRequests_Get(resp)),
   150  		AccountingResponses:        int64(FreeRADIUSTotalAccountingResponses_Get(resp)),
   151  		AcctDuplicateRequests:      int64(FreeRADIUSTotalAcctDuplicateRequests_Get(resp)),
   152  		AcctMalformedRequests:      int64(FreeRADIUSTotalAcctMalformedRequests_Get(resp)),
   153  		AcctInvalidRequests:        int64(FreeRADIUSTotalAcctInvalidRequests_Get(resp)),
   154  		AcctDroppedRequests:        int64(FreeRADIUSTotalAcctDroppedRequests_Get(resp)),
   155  		AcctUnknownTypes:           int64(FreeRADIUSTotalAcctUnknownTypes_Get(resp)),
   156  		ProxyAccessRequests:        int64(FreeRADIUSTotalProxyAccessRequests_Get(resp)),
   157  		ProxyAccessAccepts:         int64(FreeRADIUSTotalProxyAccessAccepts_Get(resp)),
   158  		ProxyAccessRejects:         int64(FreeRADIUSTotalProxyAccessRejects_Get(resp)),
   159  		ProxyAccessChallenges:      int64(FreeRADIUSTotalProxyAccessChallenges_Get(resp)),
   160  		ProxyAuthResponses:         int64(FreeRADIUSTotalProxyAuthResponses_Get(resp)),
   161  		ProxyAuthDuplicateRequests: int64(FreeRADIUSTotalProxyAuthDuplicateRequests_Get(resp)),
   162  		ProxyAuthMalformedRequests: int64(FreeRADIUSTotalProxyAuthMalformedRequests_Get(resp)),
   163  		ProxyAuthInvalidRequests:   int64(FreeRADIUSTotalProxyAuthInvalidRequests_Get(resp)),
   164  		ProxyAuthDroppedRequests:   int64(FreeRADIUSTotalProxyAuthDroppedRequests_Get(resp)),
   165  		ProxyAuthUnknownTypes:      int64(FreeRADIUSTotalProxyAuthUnknownTypes_Get(resp)),
   166  		ProxyAccountingRequests:    int64(FreeRADIUSTotalProxyAccountingRequests_Get(resp)),
   167  		ProxyAccountingResponses:   int64(FreeRADIUSTotalProxyAccountingResponses_Get(resp)),
   168  		ProxyAcctDuplicateRequests: int64(FreeRADIUSTotalProxyAcctDuplicateRequests_Get(resp)),
   169  		ProxyAcctMalformedRequests: int64(FreeRADIUSTotalProxyAcctMalformedRequests_Get(resp)),
   170  		ProxyAcctInvalidRequests:   int64(FreeRADIUSTotalProxyAcctInvalidRequests_Get(resp)),
   171  		ProxyAcctDroppedRequests:   int64(FreeRADIUSTotalProxyAcctDroppedRequests_Get(resp)),
   172  		ProxyAcctUnknownTypes:      int64(FreeRADIUSTotalProxyAcctUnknownTypes_Get(resp)),
   173  	}
   174  }