1package xmpp 2 3import ( 4 "log" 5 "net" 6 "time" 7 8 ourNet "github.com/coyim/coyim/net" 9 "github.com/coyim/coyim/xmpp/errors" 10 "golang.org/x/net/proxy" 11) 12 13const defaultDialTimeout = 60 * time.Second 14 15func (d *dialer) newTCPConn() (net.Conn, error) { 16 if d.proxy == nil { 17 d.proxy = proxy.Direct 18 } 19 20 //libpurple and xmpp-client are strict to section 3.2.3 and skip SRV lookup 21 //whenever the user has configured a custom server address. 22 //This is necessary to keep imported accounts from Adium/Pidgin/xmpp-client 23 //working as expected. 24 if d.hasCustomServer() { 25 d.config.SkipSRVLookup = true 26 } 27 28 //RFC 6120, Section 3.2.3 29 //See: https://xmpp.org/rfcs/rfc6120.html#tcp-resolution-srvnot 30 if d.config.SkipSRVLookup { 31 log.Println("Skipping SRV lookup") 32 return connectWithProxy(d.GetServer(), d.proxy) 33 } 34 35 return d.srvLookupAndFallback() 36} 37 38func (d *dialer) srvLookupAndFallback() (net.Conn, error) { 39 host := d.getJIDDomainpart() 40 log.Println("Make SRV lookup to:", host) 41 xmppAddrs, err := ResolveSRVWithProxy(d.proxy, host) 42 43 //Every other error means 44 //"the initiating entity [did] not receive a response to its SRV query" and 45 //we should use the fallback method 46 //See RFC 6120, Section 3.2.1, item 9 47 if err == ErrServiceNotAvailable { 48 return nil, err 49 } 50 51 //RFC 6120, Section 3.2.1, item 9 52 //If the SRV has no response, we fallback to use the origin domain 53 //at default port. 54 if len(xmppAddrs) == 0 { 55 err = errors.ErrTCPBindingFailed 56 57 //TODO: in this case, a failure to connect might be recovered using HTTP binding 58 //See: RFC 6120, Section 3.2.2 59 xmppAddrs = []string{d.getFallbackServer()} 60 } else { 61 //The SRV lookup succeeded but we failed to connect 62 err = errors.ErrConnectionFailed 63 } 64 65 conn, _, e := connectToFirstAvailable(xmppAddrs, d.proxy) 66 if e != nil { 67 return nil, err 68 } 69 70 return conn, nil 71} 72 73func connectToFirstAvailable(xmppAddrs []string, dialer proxy.Dialer) (net.Conn, string, error) { 74 for _, addr := range xmppAddrs { 75 conn, err := connectWithProxy(addr, dialer) 76 if err == nil { 77 return conn, addr, nil 78 } 79 } 80 81 return nil, "", errors.ErrConnectionFailed 82} 83 84func dialTimeout(network, addr string, dialer proxy.Dialer, t time.Duration) (c net.Conn, err error) { 85 result := make(chan bool, 1) 86 87 go func() { 88 c, err = dialer.Dial(network, addr) 89 result <- true 90 }() 91 92 select { 93 case <-time.After(t): 94 log.Println("tcp: dial timed out") 95 return nil, ourNet.ErrTimeout 96 case <-result: 97 return 98 } 99} 100 101func connectWithProxy(addr string, dialer proxy.Dialer) (conn net.Conn, err error) { 102 log.Printf("Connecting to %s\n", addr) 103 104 //TODO: It is not clear to me if this follows 105 //RFC 6120, Section 3.2.1, item 6 106 //See: https://xmpp.org/rfcs/rfc6120.html#tcp-resolution 107 conn, err = dialTimeout("tcp", addr, dialer, defaultDialTimeout) 108 if err != nil { 109 if err == ourNet.ErrTimeout { 110 return nil, err 111 } 112 113 return nil, errors.CreateErrFailedToConnect(addr, err) 114 } 115 116 return 117} 118