github.com/la5nta/wl2k-go@v0.11.8/transport/ax25/ax25.go (about) 1 // Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved. 2 // Use of this source code is governed by the MIT-license that can be 3 // found in the LICENSE file. 4 5 // Package ax25 provides a net.Conn and net.Listener interfaces for AX.25. 6 // 7 // # Supported TNCs 8 // 9 // This package currently implements interfaces for Linux' AX.25 stack and Tasco-like TNCs (Kenwood transceivers). 10 // 11 // # Build tags 12 // 13 // The Linux AX.25 stack bindings are guarded by some custom build tags: 14 // 15 // libax25 // Include support for Linux' AX.25 stack by linking against libax25. 16 // static // Link against static libraries only. 17 package ax25 18 19 import ( 20 "bytes" 21 "context" 22 "errors" 23 "fmt" 24 "io" 25 "net" 26 "strconv" 27 "strings" 28 "time" 29 30 "github.com/la5nta/wl2k-go/transport" 31 ) 32 33 const ( 34 // DefaultSerialBaud is the default serial_baud value of the serial-tnc scheme. 35 DefaultSerialBaud = 9600 36 ) 37 38 const _NETWORK = "AX.25" 39 40 var DefaultDialer = &Dialer{Timeout: 45 * time.Second} 41 42 func init() { 43 transport.RegisterDialer("ax25", DefaultDialer) 44 transport.RegisterDialer("serial-tnc", DefaultDialer) 45 transport.RegisterDialer("ax25+linux", DefaultDialer) 46 transport.RegisterDialer("ax25+serial-tnc", DefaultDialer) 47 } 48 49 type addr interface { 50 Address() Address // Callsign 51 Digis() []Address // Digipeaters 52 } 53 54 type AX25Addr struct{ addr } 55 56 func (a AX25Addr) Network() string { return _NETWORK } 57 func (a AX25Addr) String() string { 58 var buf bytes.Buffer 59 60 fmt.Fprint(&buf, a.Address()) 61 if len(a.Digis()) > 0 { 62 fmt.Fprint(&buf, " via") 63 } 64 for _, digi := range a.Digis() { 65 fmt.Fprintf(&buf, " %s", digi) 66 } 67 68 return buf.String() 69 } 70 71 type Address struct { 72 Call string 73 SSID uint8 74 } 75 76 type Conn struct { 77 io.ReadWriteCloser 78 localAddr AX25Addr 79 remoteAddr AX25Addr 80 } 81 82 func (c *Conn) LocalAddr() net.Addr { 83 if !c.ok() { 84 return nil 85 } 86 return c.localAddr 87 } 88 89 func (c *Conn) RemoteAddr() net.Addr { 90 if !c.ok() { 91 return nil 92 } 93 return c.remoteAddr 94 } 95 96 func (c *Conn) ok() bool { return c != nil } 97 98 func (c *Conn) SetDeadline(t time.Time) error { 99 return errors.New(`SetDeadline not implemented`) 100 } 101 102 func (c *Conn) SetReadDeadline(t time.Time) error { 103 return errors.New(`SetReadDeadline not implemented`) 104 } 105 106 func (c *Conn) SetWriteDeadline(t time.Time) error { 107 return errors.New(`SetWriteDeadline not implemented`) 108 } 109 110 type Beacon interface { 111 Now() error 112 Every(d time.Duration) error 113 114 LocalAddr() net.Addr 115 RemoteAddr() net.Addr 116 117 Message() string 118 } 119 120 type Dialer struct { 121 Timeout time.Duration 122 } 123 124 // DialURL dials ax25://, ax25+linux://, serial-tnc:// and ax25+serial-tnc:// URLs. 125 // 126 // See DialURLContext. 127 func (d Dialer) DialURL(url *transport.URL) (net.Conn, error) { 128 return d.DialURLContext(context.Background(), url) 129 } 130 131 // DialURLContext dials ax25://, ax25+linux://, serial-tnc:// and ax25+serial-tnc:// URLs. 132 // 133 // If the context is cancelled while dialing, the connection may be closed gracefully before returning an error. 134 func (d Dialer) DialURLContext(ctx context.Context, url *transport.URL) (net.Conn, error) { 135 target := url.Target 136 if len(url.Digis) > 0 { 137 target = fmt.Sprintf("%s via %s", target, strings.Join(url.Digis, " ")) 138 } 139 140 switch url.Scheme { 141 case "ax25", "ax25+linux": 142 ctx, cancel := context.WithTimeout(ctx, d.Timeout) 143 defer cancel() 144 conn, err := DialAX25Context(ctx, url.Host, url.User.Username(), target) 145 if err != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) { 146 // Local timeout reached. 147 err = fmt.Errorf("Dial timeout") 148 } 149 return conn, err 150 case "serial-tnc", "ax25+serial-tnc": 151 // TODO: This is some badly designed legacy stuff. Need to re-think the whole 152 // serial-tnc scheme. See issue #34. 153 hbaud := HBaud(1200) 154 if i, _ := strconv.Atoi(url.Params.Get("hbaud")); i > 0 { 155 hbaud = HBaud(i) 156 } 157 serialBaud := DefaultSerialBaud 158 if i, _ := strconv.Atoi(url.Params.Get("serial_baud")); i > 0 { 159 serialBaud = i 160 } 161 162 return DialKenwood( 163 url.Host, 164 url.User.Username(), 165 target, 166 NewConfig(hbaud, serialBaud), 167 nil, 168 ) 169 default: 170 return nil, transport.ErrUnsupportedScheme 171 } 172 } 173 174 func AddressFromString(str string) Address { 175 parts := strings.Split(str, "-") 176 addr := Address{Call: parts[0]} 177 if len(parts) > 1 { 178 ssid, err := strconv.ParseInt(parts[1], 10, 32) 179 if err == nil && ssid >= 0 && ssid <= 255 { 180 addr.SSID = uint8(ssid) 181 } 182 } 183 return addr 184 } 185 186 func (a Address) String() string { 187 if a.SSID > 0 { 188 return fmt.Sprintf("%s-%d", a.Call, a.SSID) 189 } else { 190 return a.Call 191 } 192 }