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 }