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 }