189 lines
4.1 KiB
Go
189 lines
4.1 KiB
Go
package aprsis
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.maze.io/go/ham/protocol"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
DefaultListenAddr = ":14580"
|
|
DefaultServerAddr = "rotate.aprs2.net:14580"
|
|
)
|
|
|
|
type Proxy struct {
|
|
Logger *logrus.Logger
|
|
Filter string
|
|
server string
|
|
listen net.Listener
|
|
packets chan *protocol.Packet
|
|
}
|
|
|
|
func NewProxy(listen, server string) (*Proxy, error) {
|
|
if _, err := net.ResolveTCPAddr("tcp", server); err != nil {
|
|
return nil, fmt.Errorf("aprsis: error resolving %q: %v", server, err)
|
|
}
|
|
|
|
listenAddr, err := net.ResolveTCPAddr("tcp", listen)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("aprsis: error listening on %s: %v", listen, err)
|
|
}
|
|
|
|
listener, err := net.ListenTCP("tcp", listenAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("aprsis: error listening on %s: %v", listen, err)
|
|
}
|
|
|
|
proxy := &Proxy{
|
|
Logger: logrus.New(),
|
|
server: server,
|
|
listen: listener,
|
|
}
|
|
go proxy.accept()
|
|
|
|
return proxy, nil
|
|
}
|
|
|
|
func (proxy *Proxy) Close() error {
|
|
if proxy.packets != nil {
|
|
close(proxy.packets)
|
|
proxy.packets = nil
|
|
}
|
|
return proxy.listen.Close()
|
|
}
|
|
|
|
func (proxy *Proxy) RawPackets() <-chan *protocol.Packet {
|
|
if proxy.packets == nil {
|
|
proxy.packets = make(chan *protocol.Packet, 16)
|
|
}
|
|
return proxy.packets
|
|
}
|
|
|
|
func (proxy *Proxy) accept() {
|
|
for {
|
|
client, err := proxy.listen.Accept()
|
|
if err != nil {
|
|
proxy.Logger.Errorf("aprs-is proxy: error accepting client: %v", err)
|
|
continue
|
|
}
|
|
|
|
go proxy.handle(client)
|
|
}
|
|
}
|
|
|
|
func (proxy *Proxy) handle(client net.Conn) {
|
|
defer func() { _ = client.Close() }()
|
|
|
|
host, _, _ := net.SplitHostPort(client.RemoteAddr().String())
|
|
proxy.Logger.Infof("aprs-is proxy[%s]: new connection", host)
|
|
|
|
server, err := net.Dial("tcp", proxy.server)
|
|
if err != nil {
|
|
proxy.Logger.Warnf("aprs-is proxy[%s]: can't connecto to APRS-IS server %s: %v", host, proxy.server, err)
|
|
return
|
|
}
|
|
defer func() { _ = server.Close() }()
|
|
|
|
var (
|
|
wait sync.WaitGroup
|
|
call string
|
|
)
|
|
wait.Go(func() { proxy.proxy(client, server, host, "->", &call) })
|
|
wait.Go(func() { proxy.proxy(server, client, host, "<-", nil) })
|
|
wait.Wait()
|
|
}
|
|
|
|
func (proxy *Proxy) proxy(dst, src net.Conn, host, dir string, call *string) {
|
|
defer func() {
|
|
if tcp, ok := dst.(*net.TCPConn); ok {
|
|
_ = tcp.CloseWrite()
|
|
} else {
|
|
_ = dst.Close()
|
|
}
|
|
}()
|
|
|
|
reader := bufio.NewReader(src)
|
|
for {
|
|
line, err := reader.ReadBytes('\n')
|
|
if err != nil {
|
|
proxy.Logger.Warnf("aprs-is proxy[%s]: %s read error: %v", host, src.RemoteAddr(), err)
|
|
return
|
|
}
|
|
|
|
// proxy to remote unaltered
|
|
if len(line) > 0 {
|
|
if _, err = dst.Write(line); err != nil {
|
|
proxy.Logger.Warnf("aprs-is proxy[%s]: %s write error: %v", host, dst.RemoteAddr(), err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// parse line
|
|
line = bytes.TrimRight(line, "\r\n")
|
|
if len(line) > 0 {
|
|
proxy.Logger.Tracef("aprs-is proxy[%s]: %s %s", host, dir, string(line))
|
|
|
|
if call != nil && strings.HasPrefix(string(line), "# logresp ") {
|
|
// server responds to client login
|
|
part := strings.SplitN(string(line), " ", 5)
|
|
if len(part) > 4 && part[3] == "verified," {
|
|
*call = part[2]
|
|
proxy.Logger.Infof("aprs-is proxy[%s]: logged in as %s", host, *call)
|
|
|
|
if proxy.Filter != "" {
|
|
proxy.Logger.Tracef("aprs-is proxy[%s]: %s filter %s", host, dir, proxy.Filter)
|
|
if _, err = fmt.Fprintf(src, "filter %s\r\n", proxy.Filter); err != nil {
|
|
proxy.Logger.Warnf("aprs-is proxy[%s]: %s write error: %v", host, src.RemoteAddr(), err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !isCommand(line) {
|
|
proxy.handleRawPacket(line)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (proxy *Proxy) handleRawPacket(data []byte) {
|
|
if proxy.packets == nil {
|
|
return
|
|
}
|
|
|
|
select {
|
|
case proxy.packets <- &protocol.Packet{
|
|
Protocol: "aprs",
|
|
Raw: data,
|
|
}:
|
|
default:
|
|
proxy.Logger.Warn("aprs-is proxy: raw packet channel full, dropping packet")
|
|
}
|
|
}
|
|
|
|
func isCommand(line []byte) bool {
|
|
if len(line) == 0 {
|
|
return true
|
|
}
|
|
|
|
if line[0] == '#' {
|
|
return true
|
|
}
|
|
|
|
if i := bytes.IndexByte(line, ' '); i > -1 {
|
|
switch strings.ToLower(string(line[:i])) {
|
|
case "user", "filter":
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|