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 }