mirror of
				https://github.com/onyx-and-iris/q3rcon.git
				synced 2025-11-04 07:21:50 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "b20bca0c7783949c080bd21cce227d7c82df6e21" and "dd1d530d4423504c72c798d5742d4c1ebda95159" have entirely different histories.
		
	
	
		
			b20bca0c77
			...
			dd1d530d44
		
	
		
@ -1,15 +1,19 @@
 | 
				
			|||||||
package conn
 | 
					package conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/onyx-and-iris/q3rcon/internal/packet"
 | 
				
			||||||
	log "github.com/sirupsen/logrus"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UDPConn struct {
 | 
					type UDPConn struct {
 | 
				
			||||||
	conn     *net.UDPConn
 | 
						conn     *net.UDPConn
 | 
				
			||||||
 | 
						response packet.Response
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(host string, port int) (UDPConn, error) {
 | 
					func New(host string, port int) (UDPConn, error) {
 | 
				
			||||||
@ -25,6 +29,7 @@ func New(host string, port int) (UDPConn, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return UDPConn{
 | 
						return UDPConn{
 | 
				
			||||||
		conn:     conn,
 | 
							conn:     conn,
 | 
				
			||||||
 | 
							response: packet.NewResponse(),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,13 +42,39 @@ func (c UDPConn) Write(buf []byte) (int, error) {
 | 
				
			|||||||
	return n, nil
 | 
						return n, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c UDPConn) ReadUntil(timeout time.Time, buf []byte) (int, error) {
 | 
					func (c UDPConn) Listen(timeout time.Duration, resp chan<- string, errChan chan<- error) {
 | 
				
			||||||
	c.conn.SetReadDeadline(timeout)
 | 
						c.conn.SetReadDeadline(time.Now().Add(timeout))
 | 
				
			||||||
 | 
						done := make(chan struct{})
 | 
				
			||||||
 | 
						var sb strings.Builder
 | 
				
			||||||
 | 
						buf := make([]byte, 2048)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-done:
 | 
				
			||||||
 | 
								resp <- sb.String()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
			rlen, _, err := c.conn.ReadFromUDP(buf)
 | 
								rlen, _, err := c.conn.ReadFromUDP(buf)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
		return 0, err
 | 
									e, ok := err.(net.Error)
 | 
				
			||||||
 | 
									if ok {
 | 
				
			||||||
 | 
										if e.Timeout() {
 | 
				
			||||||
 | 
											close(done)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											errChan <- e
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if rlen < len(c.response.Header()) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if bytes.HasPrefix(buf, c.response.Header()) {
 | 
				
			||||||
 | 
									sb.Write(buf[len(c.response.Header()):rlen])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return rlen, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c UDPConn) Close() error {
 | 
					func (c UDPConn) Close() error {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,16 @@
 | 
				
			|||||||
package packet
 | 
					package packet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "fmt"
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const bufSz = 512
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Request struct {
 | 
					type Request struct {
 | 
				
			||||||
	magic    []byte
 | 
						magic    []byte
 | 
				
			||||||
	password string
 | 
						password string
 | 
				
			||||||
	buf      *bytes.Buffer
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewRequest(password string) Request {
 | 
					func NewRequest(password string) Request {
 | 
				
			||||||
	return Request{
 | 
						return Request{
 | 
				
			||||||
		magic:    []byte{'\xff', '\xff', '\xff', '\xff'},
 | 
							magic:    []byte{'\xff', '\xff', '\xff', '\xff'},
 | 
				
			||||||
		password: password,
 | 
							password: password,
 | 
				
			||||||
		buf:      bytes.NewBuffer(make([]byte, bufSz)),
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,8 +19,7 @@ func (r Request) Header() []byte {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r Request) Encode(cmd string) []byte {
 | 
					func (r Request) Encode(cmd string) []byte {
 | 
				
			||||||
	r.buf.Reset()
 | 
						datagram := r.Header()
 | 
				
			||||||
	r.buf.Write(r.Header())
 | 
						datagram = append(datagram, fmt.Sprintf(" %s %s", r.password, cmd)...)
 | 
				
			||||||
	r.buf.WriteString(fmt.Sprintf(" %s %s", r.password, cmd))
 | 
						return datagram
 | 
				
			||||||
	return r.buf.Bytes()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								option.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								option.go
									
									
									
									
									
								
							@ -1,27 +0,0 @@
 | 
				
			|||||||
package q3rcon
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Option is a functional option type that allows us to configure the VbanTxt.
 | 
					 | 
				
			||||||
type Option func(*Rcon)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WithLoginTimeout is a functional option to set the login timeout
 | 
					 | 
				
			||||||
func WithLoginTimeout(timeout time.Duration) Option {
 | 
					 | 
				
			||||||
	return func(rcon *Rcon) {
 | 
					 | 
				
			||||||
		rcon.loginTimeout = timeout
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WithDefaultTimeout is a functional option to set the default response timeout
 | 
					 | 
				
			||||||
func WithDefaultTimeout(timeout time.Duration) Option {
 | 
					 | 
				
			||||||
	return func(rcon *Rcon) {
 | 
					 | 
				
			||||||
		rcon.defaultTimeout = timeout
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WithTimeouts is a functional option to set the timeouts for responses per command
 | 
					 | 
				
			||||||
func WithTimeouts(timeouts map[string]time.Duration) Option {
 | 
					 | 
				
			||||||
	return func(rcon *Rcon) {
 | 
					 | 
				
			||||||
		rcon.timeouts = timeouts
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										73
									
								
								q3rcon.go
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								q3rcon.go
									
									
									
									
									
								
							@ -1,9 +1,7 @@
 | 
				
			|||||||
package q3rcon
 | 
					package q3rcon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +9,29 @@ import (
 | 
				
			|||||||
	"github.com/onyx-and-iris/q3rcon/internal/packet"
 | 
						"github.com/onyx-and-iris/q3rcon/internal/packet"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const respBufSiz = 2048
 | 
					// Option is a functional option type that allows us to configure the VbanTxt.
 | 
				
			||||||
 | 
					type Option func(*Rcon)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithLoginTimeout is a functional option to set the login timeout
 | 
				
			||||||
 | 
					func WithLoginTimeout(timeout time.Duration) Option {
 | 
				
			||||||
 | 
						return func(rcon *Rcon) {
 | 
				
			||||||
 | 
							rcon.loginTimeout = timeout
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithDefaultTimeout is a functional option to set the default response timeout
 | 
				
			||||||
 | 
					func WithDefaultTimeout(timeout time.Duration) Option {
 | 
				
			||||||
 | 
						return func(rcon *Rcon) {
 | 
				
			||||||
 | 
							rcon.defaultTimeout = timeout
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithTimeouts is a functional option to set the timeouts for responses per command
 | 
				
			||||||
 | 
					func WithTimeouts(timeouts map[string]time.Duration) Option {
 | 
				
			||||||
 | 
						return func(rcon *Rcon) {
 | 
				
			||||||
 | 
							rcon.timeouts = timeouts
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Rcon struct {
 | 
					type Rcon struct {
 | 
				
			||||||
	conn     conn.UDPConn
 | 
						conn     conn.UDPConn
 | 
				
			||||||
@ -21,6 +41,8 @@ type Rcon struct {
 | 
				
			|||||||
	loginTimeout   time.Duration
 | 
						loginTimeout   time.Duration
 | 
				
			||||||
	defaultTimeout time.Duration
 | 
						defaultTimeout time.Duration
 | 
				
			||||||
	timeouts       map[string]time.Duration
 | 
						timeouts       map[string]time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp chan string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(host string, port int, password string, options ...Option) (*Rcon, error) {
 | 
					func New(host string, port int, password string, options ...Option) (*Rcon, error) {
 | 
				
			||||||
@ -36,8 +58,7 @@ func New(host string, port int, password string, options ...Option) (*Rcon, erro
 | 
				
			|||||||
	r := &Rcon{
 | 
						r := &Rcon{
 | 
				
			||||||
		conn:           conn,
 | 
							conn:           conn,
 | 
				
			||||||
		request:        packet.NewRequest(password),
 | 
							request:        packet.NewRequest(password),
 | 
				
			||||||
		response: packet.NewResponse(),
 | 
							resp:           make(chan string),
 | 
				
			||||||
 | 
					 | 
				
			||||||
		loginTimeout:   5 * time.Second,
 | 
							loginTimeout:   5 * time.Second,
 | 
				
			||||||
		defaultTimeout: 20 * time.Millisecond,
 | 
							defaultTimeout: 20 * time.Millisecond,
 | 
				
			||||||
		timeouts:       make(map[string]time.Duration),
 | 
							timeouts:       make(map[string]time.Duration),
 | 
				
			||||||
@ -84,55 +105,21 @@ func (r Rcon) Send(cmd string) (string, error) {
 | 
				
			|||||||
		timeout = r.defaultTimeout
 | 
							timeout = r.defaultTimeout
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	respChan := make(chan string)
 | 
						e := make(chan error)
 | 
				
			||||||
	errChan := make(chan error)
 | 
						go r.conn.Listen(timeout, r.resp, e)
 | 
				
			||||||
	go r.listen(timeout, respChan, errChan)
 | 
					 | 
				
			||||||
	_, err := r.conn.Write(r.request.Encode(cmd))
 | 
						_, err := r.conn.Write(r.request.Encode(cmd))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	select {
 | 
						select {
 | 
				
			||||||
	case err := <-errChan:
 | 
						case err := <-e:
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	case resp := <-respChan:
 | 
						case resp := <-r.resp:
 | 
				
			||||||
		return strings.TrimPrefix(resp, string(r.response.Header())), nil
 | 
							return strings.TrimPrefix(resp, string(r.response.Header())), nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r Rcon) listen(timeout time.Duration, respChan chan<- string, errChan chan<- error) {
 | 
					 | 
				
			||||||
	done := make(chan struct{})
 | 
					 | 
				
			||||||
	respBuf := make([]byte, respBufSiz)
 | 
					 | 
				
			||||||
	var sb strings.Builder
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		select {
 | 
					 | 
				
			||||||
		case <-done:
 | 
					 | 
				
			||||||
			respChan <- sb.String()
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			rlen, err := r.conn.ReadUntil(time.Now().Add(timeout), respBuf)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				e, ok := err.(net.Error)
 | 
					 | 
				
			||||||
				if ok {
 | 
					 | 
				
			||||||
					if e.Timeout() {
 | 
					 | 
				
			||||||
						close(done)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						errChan <- e
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if rlen > len(r.response.Header()) {
 | 
					 | 
				
			||||||
				if bytes.HasPrefix(respBuf, r.response.Header()) {
 | 
					 | 
				
			||||||
					sb.Write(respBuf[len(r.response.Header()):rlen])
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r Rcon) Close() {
 | 
					func (r Rcon) Close() {
 | 
				
			||||||
	r.conn.Close()
 | 
						r.conn.Close()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user