github.com/la5nta/wl2k-go@v0.11.8/transport/ax25/ax25_linux.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  //go:build libax25 && cgo
     6  // +build libax25,cgo
     7  
     8  package ax25
     9  
    10  /*
    11  #include <sys/socket.h>
    12  #include <netax25/ax25.h>
    13  #include <netax25/axlib.h>
    14  #include <netax25/axconfig.h>
    15  #include <fcntl.h>
    16  */
    17  import "C"
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"os"
    26  	"syscall"
    27  	"time"
    28  	"unsafe"
    29  )
    30  
    31  type ax25Addr C.struct_full_sockaddr_ax25
    32  
    33  var numAXPorts int
    34  
    35  // bug(martinhpedersen): The AX.25 stack does not support SOCK_STREAM, so any write to the connection
    36  // that is larger than maximum packet length will fail. The b2f impl. requires 125 bytes long packets.
    37  var ErrMessageTooLong = errors.New("Write: Message too long. Consider increasing maximum packet length to >= 125.")
    38  var ErrPortNotExist = errors.New("No such AX port found")
    39  
    40  type fd uintptr
    41  
    42  type ax25Listener struct {
    43  	sock      fd
    44  	localAddr AX25Addr
    45  	close     chan struct{}
    46  }
    47  
    48  func portExists(port string) bool { return C.ax25_config_get_dev(C.CString(port)) != nil }
    49  
    50  func loadPorts() (int, error) {
    51  	if numAXPorts > 0 {
    52  		return numAXPorts, nil
    53  	}
    54  
    55  	n, err := C.ax25_config_load_ports()
    56  	if err != nil {
    57  		return int(n), err
    58  	} else if n == 0 {
    59  		return 0, fmt.Errorf("No AX.25 ports configured")
    60  	}
    61  
    62  	numAXPorts = int(n)
    63  	return numAXPorts, err
    64  }
    65  
    66  func checkPort(axPort string) error {
    67  	if axPort == "" {
    68  		return errors.New("Invalid empty axport")
    69  	}
    70  	if _, err := loadPorts(); err != nil {
    71  		return err
    72  	}
    73  	if !portExists(axPort) {
    74  		return ErrPortNotExist
    75  	}
    76  	return nil
    77  }
    78  
    79  // Addr returns the listener's network address, an AX25Addr.
    80  func (ln ax25Listener) Addr() net.Addr { return ln.localAddr }
    81  
    82  // Close stops listening on the AX.25 port. Already Accepted connections are not closed.
    83  func (ln ax25Listener) Close() error { close(ln.close); return ln.sock.close() }
    84  
    85  // Accept waits for the next call and returns a generic Conn.
    86  //
    87  // See net.Listener for more information.
    88  func (ln ax25Listener) Accept() (net.Conn, error) {
    89  	err := ln.sock.waitRead(ln.close)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	nfd, addr, err := ln.sock.accept()
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	conn := &Conn{
   100  		localAddr:       ln.localAddr,
   101  		remoteAddr:      AX25Addr{addr},
   102  		ReadWriteCloser: os.NewFile(uintptr(nfd), ""),
   103  	}
   104  
   105  	return conn, nil
   106  }
   107  
   108  // ListenAX25 announces on the local port axPort using mycall as the local address.
   109  //
   110  // An error will be returned if axPort is empty.
   111  func ListenAX25(axPort, mycall string) (net.Listener, error) {
   112  	if err := checkPort(axPort); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	// Setup local address (via callsign of supplied axPort)
   117  	localAddr := newAX25Addr(mycall)
   118  	if err := localAddr.setPort(axPort); err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// Create file descriptor
   123  	var socket fd
   124  	if f, err := syscall.Socket(syscall.AF_AX25, syscall.SOCK_SEQPACKET, 0); err != nil {
   125  		return nil, err
   126  	} else {
   127  		socket = fd(f)
   128  	}
   129  
   130  	if err := socket.bind(localAddr); err != nil {
   131  		return nil, err
   132  	}
   133  	if err := syscall.Listen(int(socket), syscall.SOMAXCONN); err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return ax25Listener{
   138  		sock:      fd(socket),
   139  		localAddr: AX25Addr{localAddr},
   140  		close:     make(chan struct{}),
   141  	}, nil
   142  }
   143  
   144  func DialAX25Context(ctx context.Context, axPort, mycall, targetcall string) (*Conn, error) {
   145  	if err := checkPort(axPort); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	// Setup local address (via callsign of supplied axPort)
   150  	localAddr := newAX25Addr(mycall)
   151  	if err := localAddr.setPort(axPort); err != nil {
   152  		return nil, err
   153  	}
   154  	remoteAddr := newAX25Addr(targetcall)
   155  
   156  	// Create file descriptor
   157  	var socket fd
   158  	if f, err := syscall.Socket(syscall.AF_AX25, syscall.SOCK_SEQPACKET, 0); err != nil {
   159  		return nil, err
   160  	} else {
   161  		socket = fd(f)
   162  	}
   163  
   164  	// Bind
   165  	if err := socket.bind(localAddr); err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	// Connect
   170  	err := socket.connectContext(ctx, remoteAddr)
   171  	if err != nil {
   172  		socket.close()
   173  		return nil, err
   174  	}
   175  
   176  	return &Conn{
   177  		ReadWriteCloser: os.NewFile(uintptr(socket), axPort),
   178  		localAddr:       AX25Addr{localAddr},
   179  		remoteAddr:      AX25Addr{remoteAddr},
   180  	}, nil
   181  }
   182  
   183  // DialAX25Timeout acts like DialAX25 but takes a timeout.
   184  func DialAX25Timeout(axPort, mycall, targetcall string, timeout time.Duration) (*Conn, error) {
   185  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   186  	defer cancel()
   187  	conn, err := DialAX25Context(ctx, axPort, mycall, targetcall)
   188  	if err != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {
   189  		// Local timeout reached.
   190  		err = fmt.Errorf("Dial timeout")
   191  	}
   192  	return conn, err
   193  }
   194  
   195  func (c *Conn) Close() error {
   196  	if !c.ok() {
   197  		return syscall.EINVAL
   198  	}
   199  
   200  	return c.ReadWriteCloser.Close()
   201  }
   202  
   203  func (c *Conn) Write(p []byte) (n int, err error) {
   204  	if !c.ok() {
   205  		return 0, syscall.EINVAL
   206  	}
   207  
   208  	n, err = c.ReadWriteCloser.Write(p)
   209  	perr, ok := err.(*os.PathError)
   210  	if !ok {
   211  		return
   212  	}
   213  
   214  	switch perr.Err.Error() {
   215  	case "message too long":
   216  		return n, ErrMessageTooLong
   217  	default:
   218  		return
   219  	}
   220  }
   221  
   222  func (c *Conn) Read(p []byte) (n int, err error) {
   223  	if !c.ok() {
   224  		return 0, syscall.EINVAL
   225  	}
   226  
   227  	n, err = c.ReadWriteCloser.Read(p)
   228  	perr, ok := err.(*os.PathError)
   229  	if !ok {
   230  		return
   231  	}
   232  
   233  	// TODO: These errors should not be checked using string comparison!
   234  	// The weird error handling here is needed because of how the *os.File treats
   235  	// the underlying fd. This should be fixed the same way as net.FileConn does.
   236  	switch perr.Err.Error() {
   237  	case "transport endpoint is not connected": // We get this error when the remote hangs up
   238  		return n, io.EOF
   239  	default:
   240  		return
   241  	}
   242  }
   243  
   244  // DialAX25 connects to the remote station targetcall using the named axport and mycall.
   245  //
   246  // An error will be returned if axPort is empty.
   247  func DialAX25(axPort, mycall, targetcall string) (*Conn, error) {
   248  	return DialAX25Context(context.Background(), axPort, mycall, targetcall)
   249  }
   250  
   251  func (sock fd) connectContext(ctx context.Context, addr ax25Addr) (err error) {
   252  	if err = syscall.SetNonblock(int(sock), true); err != nil {
   253  		return err
   254  	}
   255  	defer syscall.SetNonblock(int(sock), false)
   256  
   257  	err = sock.connect(addr)
   258  	if err == nil {
   259  		return nil // Connected
   260  	} else if err != syscall.EINPROGRESS {
   261  		return err
   262  	}
   263  
   264  	// Wait for response as long as the dial context is valid.
   265  	for {
   266  		if ctx.Err() != nil {
   267  			sock.close()
   268  			return ctx.Err()
   269  		}
   270  		fdset := new(syscall.FdSet)
   271  		maxFd := fdSet(fdset, int(sock))
   272  		tv := syscall.NsecToTimeval(int64(10 * time.Millisecond))
   273  		n, err := syscall.Select(maxFd+1, nil, fdset, nil, &tv)
   274  		switch {
   275  		case n < 0 && err != syscall.EINTR:
   276  			sock.close()
   277  			return err
   278  		case n > 0:
   279  			// Verify that connection is OK
   280  			nerr, err := syscall.GetsockoptInt(int(sock), syscall.SOL_SOCKET, syscall.SO_ERROR)
   281  			if err != nil {
   282  				sock.close()
   283  				return err
   284  			}
   285  			err = syscall.Errno(nerr)
   286  			if nerr != 0 && err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR {
   287  				sock.close()
   288  				return err
   289  			}
   290  			return nil // Connected
   291  		default:
   292  			// Nothing has changed yet. Keep looping.
   293  			continue
   294  		}
   295  	}
   296  }
   297  
   298  // waitRead blocks until the socket is ready for read or the call is canceled
   299  //
   300  // The error syscall.EINVAL is returned if the cancel channel is closed, indicating
   301  // that the socket is being closed by another thread.
   302  func (sock fd) waitRead(cancel <-chan struct{}) error {
   303  	pr, pw, err := os.Pipe()
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	done := make(chan struct{})
   309  	go func() {
   310  		select {
   311  		case <-cancel:
   312  			pw.Write([]byte("\n"))
   313  		case <-done:
   314  			return
   315  		}
   316  	}()
   317  	defer func() { close(done); pw.Close() }()
   318  
   319  	fdset := new(syscall.FdSet)
   320  	maxFd := fdSet(fdset, int(sock), int(pr.Fd()))
   321  
   322  	syscall.SetNonblock(int(sock), true)
   323  	defer func() { syscall.SetNonblock(int(sock), false) }()
   324  
   325  	var n int
   326  	for {
   327  		n, err = syscall.Select(maxFd+1, fdset, nil, nil, nil)
   328  		if n < 0 || err != nil {
   329  			return err
   330  		}
   331  
   332  		if fdIsSet(fdset, int(sock)) {
   333  			break // sock is ready for read
   334  		} else {
   335  			return syscall.EINVAL
   336  		}
   337  	}
   338  	return nil
   339  }
   340  
   341  func (sock fd) close() error {
   342  	return syscall.Close(int(sock))
   343  }
   344  
   345  func (sock fd) accept() (nfd fd, addr ax25Addr, err error) {
   346  	addrLen := C.socklen_t(unsafe.Sizeof(addr))
   347  	n, err := C.accept(
   348  		C.int(sock),
   349  		(*C.struct_sockaddr)(unsafe.Pointer(&addr)),
   350  		&addrLen)
   351  
   352  	if addrLen != C.socklen_t(unsafe.Sizeof(addr)) {
   353  		panic("unexpected socklet_t")
   354  	}
   355  
   356  	return fd(n), addr, err
   357  }
   358  
   359  func (sock fd) connect(addr ax25Addr) (err error) {
   360  	_, err = C.connect(
   361  		C.int(sock),
   362  		(*C.struct_sockaddr)(unsafe.Pointer(&addr)),
   363  		C.socklen_t(unsafe.Sizeof(addr)))
   364  
   365  	return
   366  }
   367  
   368  func (sock fd) bind(addr ax25Addr) (err error) {
   369  	_, err = C.bind(
   370  		C.int(sock),
   371  		(*C.struct_sockaddr)(unsafe.Pointer(&addr)),
   372  		C.socklen_t(unsafe.Sizeof(addr)))
   373  
   374  	return
   375  }
   376  
   377  type ax25_address *C.ax25_address
   378  
   379  func (a ax25Addr) Address() Address {
   380  	return AddressFromString(
   381  		C.GoString(C.ax25_ntoa(a.ax25_address())),
   382  	)
   383  }
   384  
   385  func (a ax25Addr) Digis() []Address {
   386  	digis := make([]Address, a.numDigis())
   387  	for i, digi := range a.digis() {
   388  		digis[i] = AddressFromString(C.GoString(C.ax25_ntoa(digi)))
   389  	}
   390  	return digis
   391  }
   392  
   393  func (a *ax25Addr) numDigis() int {
   394  	return int(a.fsa_ax25.sax25_ndigis)
   395  }
   396  
   397  func (a *ax25Addr) digis() []ax25_address {
   398  	digis := make([]ax25_address, a.numDigis())
   399  	for i := range digis {
   400  		digis[i] = (*C.ax25_address)(unsafe.Pointer(&a.fsa_digipeater[i]))
   401  	}
   402  	return digis
   403  }
   404  
   405  func (a *ax25Addr) ax25_address() ax25_address {
   406  	return (*C.ax25_address)(unsafe.Pointer(&a.fsa_ax25.sax25_call.ax25_call))
   407  }
   408  
   409  func (a *ax25Addr) setPort(port string) (err error) {
   410  	C.ax25_aton_entry(
   411  		C.ax25_config_get_addr(C.CString(port)),
   412  		&a.fsa_digipeater[0].ax25_call[0],
   413  	)
   414  	a.fsa_ax25.sax25_ndigis = 1
   415  	return
   416  }
   417  
   418  func newAX25Addr(address string) ax25Addr {
   419  	var addr C.struct_full_sockaddr_ax25
   420  
   421  	if C.ax25_aton(C.CString(address), &addr) < 0 {
   422  		panic("ax25_aton")
   423  	}
   424  	addr.fsa_ax25.sax25_family = syscall.AF_AX25
   425  
   426  	return ax25Addr(addr)
   427  }
   428  
   429  func fdSet(p *syscall.FdSet, fd ...int) (max int) {
   430  	// Shamelessly stolen from src/pkg/exp/inotify/inotify_linux.go:
   431  	//
   432  	// Create fdSet, taking into consideration that
   433  	// 64-bit OS uses Bits: [16]int64, while 32-bit OS uses Bits: [32]int32.
   434  	// This only support File Descriptors up to 1024
   435  	//
   436  	fElemSize := 32 * 32 / len(p.Bits)
   437  
   438  	for _, i := range fd {
   439  		if i > 1024 {
   440  			panic(fmt.Errorf("fdSet: File Descriptor >= 1024: %v", i))
   441  		}
   442  		if i > max {
   443  			max = i
   444  		}
   445  		p.Bits[i/fElemSize] |= 1 << uint(i%fElemSize)
   446  	}
   447  	return max
   448  }
   449  
   450  func fdIsSet(p *syscall.FdSet, i int) bool {
   451  	fElemSize := 32 * 32 / len(p.Bits)
   452  	return p.Bits[i/fElemSize]&(1<<uint(i%fElemSize)) != 0
   453  }