package client
import (
"encoding/binary"
"fmt"
"io"
"net"
"strings"
"time"
"github.com/jcmturner/gokrb5/v8/iana/errorcode"
"github.com/jcmturner/gokrb5/v8/messages"
)
func (cl *Client ) sendToKDC (b []byte , realm string ) ([]byte , error ) {
var rb []byte
if cl .Config .LibDefaults .UDPPreferenceLimit == 1 {
rb , errtcp := cl .sendKDCTCP (realm , b )
if errtcp != nil {
if e , ok := errtcp .(messages .KRBError ); ok {
return rb , e
}
return rb , fmt .Errorf ("communication error with KDC via TCP: %v" , errtcp )
}
return rb , nil
}
if len (b ) <= cl .Config .LibDefaults .UDPPreferenceLimit {
rb , errudp := cl .sendKDCUDP (realm , b )
if errudp != nil {
if e , ok := errudp .(messages .KRBError ); ok && e .ErrorCode != errorcode .KRB_ERR_RESPONSE_TOO_BIG {
return rb , e
}
r , errtcp := cl .sendKDCTCP (realm , b )
if errtcp != nil {
if e , ok := errtcp .(messages .KRBError ); ok {
return r , e
}
return r , fmt .Errorf ("failed to communicate with KDC. Attempts made with UDP (%v) and then TCP (%v)" , errudp , errtcp )
}
rb = r
}
return rb , nil
}
rb , errtcp := cl .sendKDCTCP (realm , b )
if errtcp != nil {
if e , ok := errtcp .(messages .KRBError ); ok {
return rb , e
}
rb , errudp := cl .sendKDCUDP (realm , b )
if errudp != nil {
if e , ok := errudp .(messages .KRBError ); ok {
return rb , e
}
return rb , fmt .Errorf ("failed to communicate with KDC. Attempts made with TCP (%v) and then UDP (%v)" , errtcp , errudp )
}
}
return rb , nil
}
func (cl *Client ) sendKDCUDP (realm string , b []byte ) ([]byte , error ) {
var r []byte
_ , kdcs , err := cl .Config .GetKDCs (realm , false )
if err != nil {
return r , err
}
r , err = dialSendUDP (kdcs , b )
if err != nil {
return r , err
}
return checkForKRBError (r )
}
func dialSendUDP(kdcs map [int ]string , b []byte ) ([]byte , error ) {
var errs []string
for i := 1 ; i <= len (kdcs ); i ++ {
udpAddr , err := net .ResolveUDPAddr ("udp" , kdcs [i ])
if err != nil {
errs = append (errs , fmt .Sprintf ("error resolving KDC address: %v" , err ))
continue
}
conn , err := net .DialTimeout ("udp" , udpAddr .String (), 5 *time .Second )
if err != nil {
errs = append (errs , fmt .Sprintf ("error setting dial timeout on connection to %s: %v" , kdcs [i ], err ))
continue
}
if err := conn .SetDeadline (time .Now ().Add (5 * time .Second )); err != nil {
errs = append (errs , fmt .Sprintf ("error setting deadline on connection to %s: %v" , kdcs [i ], err ))
continue
}
rb , err := sendUDP (conn .(*net .UDPConn ), b )
if err != nil {
errs = append (errs , fmt .Sprintf ("error sneding to %s: %v" , kdcs [i ], err ))
continue
}
return rb , nil
}
return nil , fmt .Errorf ("error sending to a KDC: %s" , strings .Join (errs , "; " ))
}
func sendUDP(conn *net .UDPConn , b []byte ) ([]byte , error ) {
var r []byte
defer conn .Close ()
_ , err := conn .Write (b )
if err != nil {
return r , fmt .Errorf ("error sending to (%s): %v" , conn .RemoteAddr ().String (), err )
}
udpbuf := make ([]byte , 4096 )
n , _ , err := conn .ReadFrom (udpbuf )
r = udpbuf [:n ]
if err != nil {
return r , fmt .Errorf ("sending over UDP failed to %s: %v" , conn .RemoteAddr ().String (), err )
}
if len (r ) < 1 {
return r , fmt .Errorf ("no response data from %s" , conn .RemoteAddr ().String ())
}
return r , nil
}
func (cl *Client ) sendKDCTCP (realm string , b []byte ) ([]byte , error ) {
var r []byte
_ , kdcs , err := cl .Config .GetKDCs (realm , true )
if err != nil {
return r , err
}
r , err = dialSendTCP (kdcs , b )
if err != nil {
return r , err
}
return checkForKRBError (r )
}
func dialSendTCP(kdcs map [int ]string , b []byte ) ([]byte , error ) {
var errs []string
for i := 1 ; i <= len (kdcs ); i ++ {
tcpAddr , err := net .ResolveTCPAddr ("tcp" , kdcs [i ])
if err != nil {
errs = append (errs , fmt .Sprintf ("error resolving KDC address: %v" , err ))
continue
}
conn , err := net .DialTimeout ("tcp" , tcpAddr .String (), 5 *time .Second )
if err != nil {
errs = append (errs , fmt .Sprintf ("error setting dial timeout on connection to %s: %v" , kdcs [i ], err ))
continue
}
if err := conn .SetDeadline (time .Now ().Add (5 * time .Second )); err != nil {
errs = append (errs , fmt .Sprintf ("error setting deadline on connection to %s: %v" , kdcs [i ], err ))
continue
}
rb , err := sendTCP (conn .(*net .TCPConn ), b )
if err != nil {
errs = append (errs , fmt .Sprintf ("error sneding to %s: %v" , kdcs [i ], err ))
continue
}
return rb , nil
}
return nil , fmt .Errorf ("error sending to a KDC: %s" , strings .Join (errs , "; " ))
}
func sendTCP(conn *net .TCPConn , b []byte ) ([]byte , error ) {
defer conn .Close ()
var r []byte
hb := make ([]byte , 4 , 4 )
binary .BigEndian .PutUint32 (hb , uint32 (len (b )))
b = append (hb , b ...)
_ , err := conn .Write (b )
if err != nil {
return r , fmt .Errorf ("error sending to KDC (%s): %v" , conn .RemoteAddr ().String (), err )
}
sh := make ([]byte , 4 , 4 )
_, err = conn .Read (sh )
if err != nil {
return r , fmt .Errorf ("error reading response size header: %v" , err )
}
s := binary .BigEndian .Uint32 (sh )
rb := make ([]byte , s , s )
_, err = io .ReadFull (conn , rb )
if err != nil {
return r , fmt .Errorf ("error reading response: %v" , err )
}
if len (rb ) < 1 {
return r , fmt .Errorf ("no response data from KDC %s" , conn .RemoteAddr ().String ())
}
return rb , nil
}
func checkForKRBError(b []byte ) ([]byte , error ) {
var KRBErr messages .KRBError
if err := KRBErr .Unmarshal (b ); err == nil {
return b , KRBErr
}
return b , nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .