github.com/la5nta/wl2k-go@v0.11.8/transport/ax25/agwpe/agwpe.go (about) 1 package agwpe 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "log" 10 "net" 11 "os" 12 "strconv" 13 "time" 14 ) 15 16 var ( 17 ErrTNCClosed = errors.New("TNC closed") 18 ErrPortClosed = errors.New("port closed") 19 ) 20 21 type TNC struct { 22 conn net.Conn 23 demux *demux 24 } 25 26 func newTNC(conn net.Conn) *TNC { 27 t := &TNC{ 28 conn: conn, 29 demux: newDemux(), 30 } 31 go t.run() 32 return t 33 } 34 35 func (t *TNC) run() { 36 defer debugf("TNC run() exited") 37 defer t.Close() 38 for { 39 var f frame 40 if err := t.read(&f); err != nil { 41 debugf("read failed: %v", err) 42 return 43 } 44 if !t.demux.Enqueue(f) { 45 return 46 } 47 } 48 } 49 50 func OpenTCP(addr string) (*TNC, error) { 51 conn, err := net.Dial("tcp", addr) 52 if err != nil { 53 return nil, err 54 } 55 return newTNC(conn), nil 56 } 57 58 func (t *TNC) Ping() error { _, err := t.Version(); return err } 59 60 func (t *TNC) Version() (string, error) { 61 resp := t.demux.NextFrame(kindVersionNumber) 62 t.write(versionNumberFrame()) 63 select { 64 case <-time.After(3 * time.Second): 65 return "", fmt.Errorf("response timeout") 66 case f, ok := <-resp: 67 if !ok { 68 return "", ErrTNCClosed 69 } 70 if len(f.Data) != 8 { 71 return "", fmt.Errorf("'%c' frame with invalid data length", f.DataKind) 72 } 73 var v struct{ Major, _, Minor, _ uint16 } 74 binary.Read(bytes.NewReader(f.Data), binary.LittleEndian, &v) 75 return fmt.Sprintf("%d.%d", v.Major, v.Minor), nil 76 } 77 } 78 79 func (t *TNC) Close() error { 80 t.demux.Close() 81 return t.conn.Close() 82 } 83 84 func (t *TNC) RegisterPort(port int, mycall string) (*Port, error) { 85 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 86 defer cancel() 87 p := newPort(t, uint8(port), mycall) 88 if err := p.register(ctx); err != nil { 89 t.Close() 90 return nil, err 91 } 92 return p, nil 93 } 94 95 func (t *TNC) write(f frame) error { 96 _, err := f.WriteTo(t.conn) 97 if err == nil && f.DataKind != kindOutstandingFramesForConn { 98 debugf("-> %v", f) 99 } 100 return err 101 } 102 103 func (t *TNC) read(f *frame) error { 104 _, err := f.ReadFrom(t.conn) 105 if err == nil && f.DataKind != kindOutstandingFramesForConn { 106 debugf("<- %v", *f) 107 } 108 return err 109 } 110 111 func debugf(s string, v ...interface{}) { 112 if t, _ := strconv.ParseBool(os.Getenv("AGWPE_DEBUG")); !t { 113 return 114 } 115 log.Printf(s, v...) 116 }