github.com/la5nta/wl2k-go@v0.11.8/transport/ardop/listen.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  	"fmt"
     9  	"io"
    10  	"net"
    11  )
    12  
    13  type listener struct {
    14  	incoming <-chan net.Conn
    15  	quit     chan struct{}
    16  	errors   <-chan error
    17  	addr     Addr
    18  }
    19  
    20  func (l listener) Accept() (c net.Conn, err error) {
    21  	select {
    22  	case c, ok := <-l.incoming:
    23  		if !ok {
    24  			return nil, io.EOF
    25  		}
    26  		return c, nil
    27  	case err = <-l.errors:
    28  		return nil, err
    29  	}
    30  }
    31  
    32  func (l listener) Addr() net.Addr {
    33  	return l.addr
    34  }
    35  
    36  func (l listener) Close() error {
    37  	close(l.quit)
    38  	return nil
    39  }
    40  
    41  func (tnc *TNC) Listen() (ln net.Listener, err error) {
    42  	if tnc.closed {
    43  		return nil, ErrTNCClosed
    44  	}
    45  
    46  	if tnc.listenerActive {
    47  		return nil, ErrActiveListenerExists
    48  	}
    49  	tnc.listenerActive = true
    50  
    51  	incoming := make(chan net.Conn)
    52  	quit := make(chan struct{})
    53  	errors := make(chan error)
    54  
    55  	mycall, err := tnc.MyCall()
    56  	if err != nil {
    57  		return nil, fmt.Errorf("Unable to get mycall: %s", err)
    58  	}
    59  
    60  	if err := tnc.SetListenEnabled(true); err != nil {
    61  		return nil, fmt.Errorf("TNC failed to enable listening: %s", err)
    62  	}
    63  
    64  	go func() {
    65  		defer func() {
    66  			close(incoming) // Important to close this first!
    67  			close(errors)
    68  			tnc.listenerActive = false
    69  		}()
    70  
    71  		msgListener := tnc.in.Listen()
    72  		defer msgListener.Close()
    73  		msgs := msgListener.Msgs()
    74  
    75  		var targetcall string
    76  		for {
    77  			select {
    78  			case <-quit:
    79  				tnc.SetListenEnabled(false) // Should return this in listener.Close()
    80  				errors <- fmt.Errorf("Closed")
    81  				return
    82  			case msg, ok := <-msgs:
    83  				if !ok {
    84  					errors <- ErrTNCClosed
    85  					return
    86  				}
    87  				switch msg.cmd {
    88  				case cmdCancelPending, cmdDisconnected:
    89  					targetcall = "" // Reset
    90  				case cmdTarget:
    91  					targetcall = msg.String()
    92  				case cmdConnected:
    93  					if targetcall == "" {
    94  						// This can not be an incoming connection.
    95  						// Incoming connections always gets cmdTarget before cmdConnected according to the spec
    96  						continue
    97  					}
    98  					remotecall := msg.value.([]string)[0]
    99  					tnc.data = &tncConn{
   100  						remoteAddr: Addr{remotecall},
   101  						localAddr:  Addr{targetcall},
   102  						ctrlOut:    tnc.out,
   103  						dataOut:    tnc.dataOut,
   104  						ctrlIn:     tnc.in,
   105  						dataIn:     tnc.dataIn,
   106  						eofChan:    make(chan struct{}),
   107  						isTCP:      tnc.isTCP,
   108  					}
   109  					tnc.connected = true
   110  					incoming <- tnc.data
   111  					targetcall = ""
   112  				}
   113  			}
   114  		}
   115  	}()
   116  
   117  	return listener{incoming, quit, errors, Addr{mycall}}, nil
   118  }