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  }