github.com/la5nta/wl2k-go@v0.11.8/transport/ardop/conn.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 ardop
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"net"
    15  	"sync"
    16  	"time"
    17  )
    18  
    19  type tncConn struct {
    20  	dataLock sync.Mutex
    21  	ctrlOut  chan<- string
    22  	dataOut  chan<- []byte
    23  	dataIn   <-chan []byte
    24  	eofChan  chan struct{}
    25  	ctrlIn   broadcaster
    26  	isTCP    bool
    27  	onClose  []func() error
    28  
    29  	remoteAddr Addr
    30  	localAddr  Addr
    31  
    32  	// The flushLock is used to keep track of the "out queued" buffer.
    33  	//
    34  	// It is locked on write, and Flush() will block until it's unlocked.
    35  	// It is the control loop's responsibility to unlock this lock when buffer reached zero.
    36  	flushLock lock
    37  
    38  	mu       sync.Mutex
    39  	buffer   int
    40  	nWritten int
    41  }
    42  
    43  // TODO: implement
    44  func (conn *tncConn) SetDeadline(t time.Time) error      { return nil }
    45  func (conn *tncConn) SetReadDeadline(t time.Time) error  { return nil }
    46  func (conn *tncConn) SetWriteDeadline(t time.Time) error { return nil }
    47  
    48  func (conn *tncConn) RemoteAddr() net.Addr { return conn.remoteAddr }
    49  func (conn *tncConn) LocalAddr() net.Addr  { return conn.localAddr }
    50  
    51  func (conn *tncConn) Read(p []byte) (int, error) {
    52  	if len(p) == 0 {
    53  		return 0, nil
    54  	}
    55  
    56  	data, ok := <-conn.dataIn
    57  	if !ok {
    58  		return 0, io.EOF
    59  	}
    60  
    61  	if len(data) > len(p) {
    62  		panic("too large") // TODO: Handle
    63  	}
    64  
    65  	for i, b := range data {
    66  		p[i] = b
    67  	}
    68  
    69  	return len(data), nil
    70  }
    71  
    72  func (conn *tncConn) Write(p []byte) (int, error) {
    73  	conn.dataLock.Lock()
    74  	defer conn.dataLock.Unlock()
    75  
    76  	// TODO: Consider implementing chunking
    77  	if len(p) > 65535 { // uint16 (length bytes) max
    78  		p = p[:65535]
    79  	}
    80  
    81  	var buf bytes.Buffer
    82  
    83  	//"D:" + 2 byte count big endian + binary data + 2 byte CRC
    84  
    85  	// D:
    86  	if !conn.isTCP {
    87  		fmt.Fprint(&buf, "D:")
    88  	}
    89  
    90  	// 2 byte length
    91  	binary.Write(&buf, binary.BigEndian, uint16(len(p)))
    92  
    93  	// Binary data
    94  	n, _ := buf.Write(p)
    95  
    96  	// 2 byte CRC
    97  	if !conn.isTCP {
    98  		sum := crc16Sum(buf.Bytes()[2:]) // [2:], don't include D: in CRC sum.
    99  		binary.Write(&buf, binary.BigEndian, sum)
   100  	}
   101  
   102  	r := conn.ctrlIn.Listen()
   103  	defer r.Close()
   104  
   105  L:
   106  	for i := 0; ; i++ {
   107  		if i == 3 {
   108  			return 0, fmt.Errorf("CRC failure")
   109  		}
   110  
   111  		conn.dataOut <- buf.Bytes()
   112  		conn.mu.Lock()
   113  		conn.nWritten += n
   114  		conn.mu.Unlock()
   115  		for {
   116  			select {
   117  			case msg := <-r.Msgs():
   118  				if msg.cmd == cmdBuffer {
   119  					conn.flushLock.Lock()
   120  					break L // Wait until we get a buffer update before returning
   121  				} else if msg.cmd == cmdCRCFault {
   122  					if debugEnabled() {
   123  						log.Printf("conn.Write: Got CRCFault. Retry %d", i)
   124  					}
   125  					continue L
   126  				}
   127  			case <-conn.eofChan:
   128  				return n, io.EOF
   129  			}
   130  		}
   131  	}
   132  
   133  	return n, nil
   134  }
   135  
   136  func (conn *tncConn) Flush() error {
   137  	select {
   138  	case <-conn.flushLock.WaitChan():
   139  		return nil
   140  	case <-conn.eofChan:
   141  		return io.EOF
   142  	}
   143  }
   144  
   145  func (conn *tncConn) signalClosed() { close(conn.eofChan) }
   146  
   147  const flushAndCloseTimeout = 30 * time.Second // TODO: Remove when time is right (see Close).
   148  
   149  // Close closes the current connection.
   150  //
   151  // Will abort ("dirty disconnect") after 30 seconds if normal "disconnect" have not succeeded yet.
   152  func (conn *tncConn) Close() error {
   153  	if conn == nil {
   154  		return nil
   155  	}
   156  
   157  	defer func() {
   158  		for _, fn := range conn.onClose {
   159  			err := fn()
   160  			if err != nil && debugEnabled() {
   161  				log.Printf("onClose func failed: %v", err)
   162  			}
   163  		}
   164  	}()
   165  
   166  	// Flush: (THIS WILL PROBABLY BE REMOVED WHEN ARDOP MATURES)
   167  	// We have to flush, because ardop will disconnect without waiting for the last
   168  	// data in buffer to be sent.
   169  	//
   170  	// We also need to timeout the flush, because ardop does not seem to switch from IRS to ISS
   171  	// if we only write one simple line (*** error line). (autobreak).
   172  
   173  	// if tnc.state == IRS {
   174  	//   tnc.Break() // Break not implemented by ARDOP_Win yet.
   175  	// }
   176  	select {
   177  	case <-conn.flushLock.WaitChan():
   178  	case <-time.After(flushAndCloseTimeout):
   179  	}
   180  
   181  	r := conn.ctrlIn.Listen()
   182  	defer r.Close()
   183  
   184  	conn.ctrlOut <- string(cmdDisconnect)
   185  	timeout := time.After(flushAndCloseTimeout)
   186  	for {
   187  		select {
   188  		case msg, ok := <-r.Msgs(): // Wait for TNC to disconnect
   189  			if !ok {
   190  				return errors.New("TNC hung up while waiting for requested disconnect")
   191  			}
   192  
   193  			if msg.cmd == cmdDisconnected || (msg.cmd == cmdNewState && msg.State() == Disconnected) {
   194  				// The control loop have already closed the data connection
   195  				return nil
   196  			}
   197  		case <-timeout:
   198  			conn.ctrlOut <- string(cmdAbort)
   199  			return ErrDisconnectTimeout
   200  		}
   201  	}
   202  }
   203  
   204  // TxBufferLen returns the number of bytes in the out buffer queue.
   205  func (conn *tncConn) TxBufferLen() int {
   206  	conn.mu.Lock()
   207  	defer conn.mu.Unlock()
   208  
   209  	return conn.buffer
   210  }
   211  
   212  func (conn *tncConn) updateBuffer(b int) {
   213  	if conn == nil {
   214  		return
   215  	}
   216  
   217  	conn.mu.Lock()
   218  	defer conn.mu.Unlock()
   219  	conn.buffer = b
   220  
   221  	if b == 0 {
   222  		conn.flushLock.Unlock()
   223  	}
   224  }