コード例 #1
0
ファイル: scan.go プロジェクト: XiliangSong/gscan
func (options *ScanOptions) SSLMatchHosts(conn *tls.Conn) []string {
	hosts := make([]string, 0)
	options.hostsMutex.Lock()
	for _, host := range options.inputHosts {
		testhost := host.Host
		if strings.Contains(testhost, ".appspot.com") {
			testhost = "appengine.google.com"
		} else if strings.Contains(testhost, "ggpht.com") {
			testhost = "googleusercontent.com"
		} else if strings.Contains(testhost, ".books.google.com") {
			testhost = "books.google.com"
		} else if strings.Contains(testhost, ".googleusercontent.com") {
			testhost = "googleusercontent.com"
		}
		if conn.VerifyHostname(testhost) == nil {
			hosts = append(hosts, host.Host)
		}
	}
	options.hostsMutex.Unlock()
	dest := make([]string, len(hosts))
	perm := rand.Perm(len(hosts))
	for i, v := range perm {
		dest[v] = hosts[i]
	}
	hosts = dest
	return hosts
}
コード例 #2
0
// dial dials the host specified by req, using TLS if appropriate.
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
	dialAddr := netutil.CanonicalAddr(req.URL)

	if req.URL.Scheme == "http" {
		if s.Dialer == nil {
			return net.Dial("tcp", dialAddr)
		} else {
			return s.Dialer.Dial("tcp", dialAddr)
		}
	}

	// TODO validate the TLSClientConfig is set up?
	var conn *tls.Conn
	var err error
	if s.Dialer == nil {
		conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig)
	} else {
		conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig)
	}
	if err != nil {
		return nil, err
	}

	host, _, err := net.SplitHostPort(dialAddr)
	if err != nil {
		return nil, err
	}
	err = conn.VerifyHostname(host)
	if err != nil {
		return nil, err
	}

	return conn, nil
}
コード例 #3
0
ファイル: xmpp.go プロジェクト: ikq/go-xmpp
// NewClient establishes a new Client connection based on a set of Options.
func (o Options) NewClient() (*Client, error) {
	host := o.Host
	c, err := connect(host, o.User, o.Password)
	if err != nil {
		return nil, err
	}

	if strings.LastIndex(o.Host, ":") > 0 {
		host = host[:strings.LastIndex(o.Host, ":")]
	}

	client := new(Client)
	if o.NoTLS {
		if o.Debug {
			client.conn = DebugConn{c}
		} else {
			client.conn = c
		}
	} else {
		var tlsconn *tls.Conn
		if o.TLSConfig != nil {
			tlsconn = tls.Client(c, o.TLSConfig)
		} else {
			DefaultConfig.ServerName = host
			tlsconn = tls.Client(c, &DefaultConfig)
		}
		if err = tlsconn.Handshake(); err != nil {
			return nil, err
		}
		insecureSkipVerify := DefaultConfig.InsecureSkipVerify
		if o.TLSConfig != nil {
			insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
		}
		if !insecureSkipVerify {
			if err = tlsconn.VerifyHostname(host); err != nil {
				return nil, err
			}
		}

		if o.Debug {
			client.conn = DebugConn{tlsconn}
		} else {
			client.conn = tlsconn
		}
	}

	if err := client.init(&o); err != nil {
		client.Close()
		return nil, err
	}

	return client, nil
}
コード例 #4
0
ファイル: httpproxy.go プロジェクト: yernsun/ProxyClient
func (p *httpProxyClient) DialTCPSAddr(network string, raddr string) (ProxyTCPConn, error) {
	var tlsConn *tls.Conn
	rawConn, err := p.upProxy.DialTCPSAddr(network, p.proxyAddr)
	if err != nil {
		return nil, fmt.Errorf("无法连接代理服务器 %v ,错误:%v", p.proxyAddr, err)
	}

	var c Conn = rawConn

	if p.proxyType == "https" {
		tlsConn = tls.Client(c, &tls.Config{ServerName: p.proxyDomain, InsecureSkipVerify: p.insecureSkipVerify})
		if err := tlsConn.Handshake(); err != nil {
			tlsConn.Close()
			return nil, fmt.Errorf("TLS 协议握手错误:%v", err)
		}
		if p.insecureSkipVerify == false && tlsConn.VerifyHostname(p.proxyDomain) != nil {
			tlsConn.Close()
			return nil, fmt.Errorf("TLS 协议域名验证失败:%v", err)
		}
		c = tlsConn
	}

	req, err := http.NewRequest("CONNECT", p.proxyAddr, nil)
	if err != nil {
		c.Close()
		return nil, fmt.Errorf("创建请求错误:%v", err)
	}
	req.URL.Path = raddr
	req.URL.Host = p.proxyAddr

	if err := req.Write(c); err != nil {
		c.Close()
		return nil, fmt.Errorf("写请求错误:%v", err)
	}

	br := bufio.NewReader(c)

	res, err := http.ReadResponse(br, req)
	if err != nil {
		c.Close()
		return nil, fmt.Errorf("响应格式错误:%v", err)
	}

	if res.StatusCode != 200 {
		c.Close()
		return nil, fmt.Errorf("响应错误:%v", res)
	}

	return &HttpTCPConn{c, rawConn, tlsConn, net.TCPAddr{}, net.TCPAddr{}, "", "", 0, 0, p, res.Body}, nil
}
コード例 #5
0
ファイル: xmpp.go プロジェクト: kacpersaw/go-xmpp
// NewClient establishes a new Client connection based on a set of Options.
func (o Options) NewClient() (*Client, error) {
	host := o.Host
	c, err := connect(host, o.User, o.Password)
	if err != nil {
		return nil, err
	}

	client := new(Client)
	if o.NoTLS {
		client.conn = c
	} else {
		var tlsconn *tls.Conn
		if o.TLSConfig != nil {
			tlsconn = tls.Client(c, o.TLSConfig)
		} else {
			//from https://github.com/dullgiulio/go-xmpp
			usrServ := strings.Split(o.User, "@")
			if len(usrServ) != 2 {
				return nil, errors.New("xmpp: invalid username (want user@domain): " + o.User)
			}
			DefaultConfig.ServerName = usrServ[1]
			tlsconn = tls.Client(c, &DefaultConfig)
		}
		if err = tlsconn.Handshake(); err != nil {
			return nil, err
		}
		if strings.LastIndex(o.Host, ":") > 0 {
			host = host[:strings.LastIndex(o.Host, ":")]
		}
		insecureSkipVerify := DefaultConfig.InsecureSkipVerify
		if o.TLSConfig != nil {
			insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
		}
		if !insecureSkipVerify {
			if err = tlsconn.VerifyHostname(host); err != nil {
				return nil, err
			}
		}
		client.conn = tlsconn
	}

	if err := client.init(&o); err != nil {
		client.Close()
		return nil, err
	}

	return client, nil
}
コード例 #6
0
// dialWithoutProxy dials the host specified by url, using TLS if appropriate.
func (s *SpdyRoundTripper) dialWithoutProxy(url *url.URL) (net.Conn, error) {
	dialAddr := netutil.CanonicalAddr(url)

	if url.Scheme == "http" {
		if s.Dialer == nil {
			return net.Dial("tcp", dialAddr)
		} else {
			return s.Dialer.Dial("tcp", dialAddr)
		}
	}

	// TODO validate the TLSClientConfig is set up?
	var conn *tls.Conn
	var err error
	if s.Dialer == nil {
		conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig)
	} else {
		conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig)
	}
	if err != nil {
		return nil, err
	}

	// Return if we were configured to skip validation
	if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
		return conn, nil
	}

	host, _, err := net.SplitHostPort(dialAddr)
	if err != nil {
		return nil, err
	}
	err = conn.VerifyHostname(host)
	if err != nil {
		return nil, err
	}

	return conn, nil
}
コード例 #7
0
ファイル: dial.go プロジェクト: CodeJuan/kubernetes
func DialURL(url *url.URL, transport http.RoundTripper) (net.Conn, error) {
	dialAddr := netutil.CanonicalAddr(url)

	dialer, _ := utilnet.Dialer(transport)

	switch url.Scheme {
	case "http":
		if dialer != nil {
			return dialer("tcp", dialAddr)
		}
		return net.Dial("tcp", dialAddr)
	case "https":
		// Get the tls config from the transport if we recognize it
		var tlsConfig *tls.Config
		var tlsConn *tls.Conn
		var err error
		tlsConfig, _ = utilnet.TLSClientConfig(transport)

		if dialer != nil {
			// We have a dialer; use it to open the connection, then
			// create a tls client using the connection.
			netConn, err := dialer("tcp", dialAddr)
			if err != nil {
				return nil, err
			}
			if tlsConfig == nil {
				// tls.Client requires non-nil config
				glog.Warningf("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
				// tls.Handshake() requires ServerName or InsecureSkipVerify
				tlsConfig = &tls.Config{
					InsecureSkipVerify: true,
				}
			} else if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
				// tls.Handshake() requires ServerName or InsecureSkipVerify
				// infer the ServerName from the hostname we're connecting to.
				inferredHost := dialAddr
				if host, _, err := net.SplitHostPort(dialAddr); err == nil {
					inferredHost = host
				}
				// Make a copy to avoid polluting the provided config
				tlsConfigCopy := *tlsConfig
				tlsConfigCopy.ServerName = inferredHost
				tlsConfig = &tlsConfigCopy
			}
			tlsConn = tls.Client(netConn, tlsConfig)
			if err := tlsConn.Handshake(); err != nil {
				netConn.Close()
				return nil, err
			}

		} else {
			// Dial
			tlsConn, err = tls.Dial("tcp", dialAddr, tlsConfig)
			if err != nil {
				return nil, err
			}
		}

		// Return if we were configured to skip validation
		if tlsConfig != nil && tlsConfig.InsecureSkipVerify {
			return tlsConn, nil
		}

		// Verify
		host, _, _ := net.SplitHostPort(dialAddr)
		if err := tlsConn.VerifyHostname(host); err != nil {
			tlsConn.Close()
			return nil, err
		}

		return tlsConn, nil
	default:
		return nil, fmt.Errorf("Unknown scheme: %s", url.Scheme)
	}
}
コード例 #8
0
ファイル: proxy.go プロジェクト: alena1108/kubernetes
func (h *UpgradeAwareProxyHandler) dialURL() (net.Conn, error) {
	dialAddr := netutil.CanonicalAddr(h.Location)

	var dialer func(network, addr string) (net.Conn, error)
	if httpTransport, ok := h.Transport.(*http.Transport); ok && httpTransport.Dial != nil {
		dialer = httpTransport.Dial
	}

	switch h.Location.Scheme {
	case "http":
		if dialer != nil {
			return dialer("tcp", dialAddr)
		}
		return net.Dial("tcp", dialAddr)
	case "https":
		// TODO: this TLS logic can probably be cleaned up; it's messy in an attempt
		// to preserve behavior that we don't know for sure is exercised.

		// Get the tls config from the transport if we recognize it
		var tlsConfig *tls.Config
		var tlsConn *tls.Conn
		var err error
		if h.Transport != nil {
			httpTransport, ok := h.Transport.(*http.Transport)
			if ok {
				tlsConfig = httpTransport.TLSClientConfig
			}
		}
		if dialer != nil {
			// We have a dialer; use it to open the connection, then
			// create a tls client using the connection.
			netConn, err := dialer("tcp", dialAddr)
			if err != nil {
				return nil, err
			}
			// tls.Client requires non-nil config
			if tlsConfig == nil {
				glog.Warningf("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
				tlsConfig = &tls.Config{
					InsecureSkipVerify: true,
				}
			}
			tlsConn = tls.Client(netConn, tlsConfig)
			if err := tlsConn.Handshake(); err != nil {
				return nil, err
			}

		} else {
			// Dial
			tlsConn, err = tls.Dial("tcp", dialAddr, tlsConfig)
			if err != nil {
				return nil, err
			}
		}

		// Return if we were configured to skip validation
		if tlsConfig != nil && tlsConfig.InsecureSkipVerify {
			return tlsConn, nil
		}

		// Verify
		host, _, _ := net.SplitHostPort(dialAddr)
		if err := tlsConn.VerifyHostname(host); err != nil {
			tlsConn.Close()
			return nil, err
		}

		return tlsConn, nil
	default:
		return nil, fmt.Errorf("unknown scheme: %s", h.Location.Scheme)
	}
}
コード例 #9
0
ファイル: httpproxy.go プロジェクト: jannson/ProxyClient
func (p *httpProxyClient) DialTCPSAddrTimeout(network string, raddr string, timeout time.Duration) (ProxyTCPConn, error) {
	var rconn ProxyTCPConn
	var rerr error
	rMutex := sync.Mutex{}

	// 截止时间
	finalDeadline := time.Time{}
	if timeout != 0 {
		finalDeadline = time.Now().Add(timeout)
	}

	var tlsConn *tls.Conn
	rawConn, err := p.upProxy.DialTCPSAddrTimeout(network, p.proxyAddr, timeout)
	if err != nil {
		return nil, fmt.Errorf("无法连接代理服务器 %v ,错误:%v", p.proxyAddr, err)
	}

	c := Conn(rawConn)

	ch := make(chan int)

	// 实际执行部分
	run := func() {
		closed := false
		// 当连接不被使用时,ch<-1会引发异常,这时将关闭连接。
		defer func() {
			e := recover()
			if e != nil && closed == false {
				c.Close()
			}
		}()

		if p.proxyType == "https" {
			tlsConn = tls.Client(c, &tls.Config{ServerName: p.proxyDomain, InsecureSkipVerify: p.insecureSkipVerify})
			if err := tlsConn.Handshake(); err != nil {
				closed = true
				c.Close()
				rMutex.Lock()
				rerr = fmt.Errorf("TLS 协议握手错误:%v", err)
				rMutex.Unlock()
				ch <- 0
				return
			}
			if p.insecureSkipVerify == false && tlsConn.VerifyHostname(p.proxyDomain) != nil {
				closed = true
				tlsConn.Close()
				rMutex.Lock()
				rerr = fmt.Errorf("TLS 协议域名验证失败:%v", err)
				rMutex.Unlock()
				ch <- 0
				return
			}

			c = tlsConn
		}

		req, err := http.NewRequest("CONNECT", raddr, nil)
		if err != nil {
			closed = true
			c.Close()
			rMutex.Lock()
			rerr = fmt.Errorf("创建请求错误:%v", err)
			rMutex.Unlock()
			ch <- 0
			return
		}
		//req.URL.Path = raddr
		req.URL.Host = raddr
		req.Host = raddr

		if p.standardHeader {
			xpath := "/"
			rInt, err := rand.Int(rand.Reader, big.NewInt(20))
			var rInt64 int64
			if err != nil {
				rInt64 = srand.Int63n(20)
			} else {
				rInt64 = rInt.Int64()
			}

			for i := int64(-10); i < rInt64; i++ {
				xpath += "X"
			}

			req.Header.Add("Accept", "text/html, application/xhtml+xml, image/jxr, */*")
			req.Header.Add("Accept-Encoding", "gzip, deflate")
			req.Header.Add("Accept-Language", "zh-CN")
			req.Header.Add("XXnnection", "Keep-Alive")
			req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/000.00 (KHTML, like Gecko) Chrome/00.0.0000.0 Safari/000.00 Edge/00.00000")
			req.Header.Add("Cookie+path", xpath)

		}

		auth := base64.StdEncoding.EncodeToString([]byte(p.auth))
		auth = fmt.Sprintf("Basic %v", auth)
		req.Header.Add("Proxy-Authorization", auth)

		if err := req.Write(c); err != nil {
			closed = true
			c.Close()
			rMutex.Lock()
			rerr = fmt.Errorf("写请求错误:%v", err)
			rMutex.Unlock()
			ch <- 0
			return
		}

		br := bufio.NewReader(c)

		res, err := http.ReadResponse(br, req)
		if err != nil {
			closed = true
			c.Close()
			rMutex.Lock()
			rerr = fmt.Errorf("响应格式错误:%v", err)
			rMutex.Unlock()
			ch <- 0
			return
		}

		if res.StatusCode != 200 {
			closed = true
			c.Close()
			rMutex.Lock()
			rerr = fmt.Errorf("响应错误:%v", res)
			rMutex.Unlock()
			ch <- 0
			return
		}

		rMutex.Lock()
		rconn = &httpTCPConn{c, rawConn, tlsConn, net.TCPAddr{}, net.TCPAddr{}, "", "", 0, 0, p, res.Body}
		rMutex.Unlock()
		ch <- 1
		return
	}

	if timeout == 0 {
		go run()

		select {
		case <-ch:
			rMutex.Lock()
			defer rMutex.Unlock()
			return rconn, rerr
		}
	} else {
		c.SetDeadline(finalDeadline)

		ntimeout := finalDeadline.Sub(time.Now())
		if ntimeout <= 0 {
			return nil, fmt.Errorf("timeout")
		}
		t := time.NewTimer(ntimeout)
		defer t.Stop()

		go run()

		select {
		case <-t.C:
			return nil, fmt.Errorf("连接超时。")
		case <-ch:
			rMutex.Lock()
			defer rMutex.Unlock()
			if rerr == nil {
				c.SetDeadline(time.Time{})
			}
			return rconn, rerr
		}
	}
}
コード例 #10
0
ファイル: transport.go プロジェクト: Colearo/fabric
func (t *Transport) newClientConn(host, key string, tconn *tls.Conn) (*clientConn, error) {
	if err := tconn.Handshake(); err != nil {
		return nil, err
	}
	if !t.InsecureTLSDial {
		if err := tconn.VerifyHostname(host); err != nil {
			return nil, err
		}
	}
	state := tconn.ConnectionState()
	if p := state.NegotiatedProtocol; p != NextProtoTLS {
		return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS)
	}
	if !state.NegotiatedProtocolIsMutual {
		return nil, errors.New("http2: could not negotiate protocol mutually")
	}
	if _, err := tconn.Write(clientPreface); err != nil {
		return nil, err
	}

	cc := &clientConn{
		t:                    t,
		tconn:                tconn,
		connKey:              []string{key}, // TODO: cert's validated hostnames too
		tlsState:             &state,
		readerDone:           make(chan struct{}),
		nextStreamID:         1,
		maxFrameSize:         16 << 10, // spec default
		initialWindowSize:    65535,    // spec default
		maxConcurrentStreams: 1000,     // "infinite", per spec. 1000 seems good enough.
		streams:              make(map[uint32]*clientStream),
	}
	cc.bw = bufio.NewWriter(stickyErrWriter{tconn, &cc.werr})
	cc.br = bufio.NewReader(tconn)
	cc.fr = NewFramer(cc.bw, cc.br)
	cc.henc = hpack.NewEncoder(&cc.hbuf)

	cc.fr.WriteSettings()
	// TODO: re-send more conn-level flow control tokens when server uses all these.
	cc.fr.WriteWindowUpdate(0, 1<<30) // um, 0x7fffffff doesn't work to Google? it hangs?
	cc.bw.Flush()
	if cc.werr != nil {
		return nil, cc.werr
	}

	// Read the obligatory SETTINGS frame
	f, err := cc.fr.ReadFrame()
	if err != nil {
		return nil, err
	}
	sf, ok := f.(*SettingsFrame)
	if !ok {
		return nil, fmt.Errorf("expected settings frame, got: %T", f)
	}
	cc.fr.WriteSettingsAck()
	cc.bw.Flush()

	sf.ForeachSetting(func(s Setting) error {
		switch s.ID {
		case SettingMaxFrameSize:
			cc.maxFrameSize = s.Val
		case SettingMaxConcurrentStreams:
			cc.maxConcurrentStreams = s.Val
		case SettingInitialWindowSize:
			cc.initialWindowSize = s.Val
		default:
			// TODO(bradfitz): handle more
			t.vlogf("Unhandled Setting: %v", s)
		}
		return nil
	})
	// TODO: figure out henc size
	cc.hdec = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField)

	go cc.readLoop()
	return cc, nil
}
コード例 #11
0
ファイル: xmpp.go プロジェクト: derlaft/xmpp
// Dial creates a new connection to an XMPP server and authenticates as the
// given user.
func Dial(address, user, domain, password, resource string, config *Config) (c *Conn, err error) {
	c = new(Conn)
	c.inflights = make(map[Cookie]chan<- Stanza)

	var log io.Writer
	if config != nil && config.Log != nil {
		log = config.Log
	}

	var conn net.Conn
	if config != nil && config.Conn != nil {
		conn = config.Conn
	} else {
		if log != nil {
			io.WriteString(log, "Making TCP connection to "+address+"\n")
		}

		if conn, err = net.Dial("tcp", address); err != nil {
			return nil, err
		}
	}
	c.Conn = conn
	c.in, c.out = makeInOut(conn, config)

	features, err := c.getFeatures(domain)
	if err != nil {
		return nil, err
	}

	if features.StartTLS.XMLName.Local == "" {
		return nil, errors.New("xmpp: server doesn't support TLS")
	}

	fmt.Fprintf(c.out, "<starttls xmlns='%s'/>", nsTLS)

	proceed, err := nextStart(c.in)
	if err != nil {
		return nil, err
	}
	if proceed.Name.Space != nsTLS || proceed.Name.Local != "proceed" {
		return nil, errors.New("xmpp: expected <proceed> after <starttls> but got <" + proceed.Name.Local + "> in " + proceed.Name.Space)
	}

	if log != nil {
		io.WriteString(log, "Starting TLS handshake\n")
	}

	var tlsConn *tls.Conn
	if config != nil && config.SkipTLS {
		tlsConn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
	} else {
		tlsConn = tls.Client(conn, &tls.Config{ServerName: domain})
	}
	if err := tlsConn.Handshake(); err != nil {
		return nil, err
	}

	tlsState := tlsConn.ConnectionState()
	if len(tlsState.VerifiedChains) == 0 && !config.SkipTLS {
		return nil, errors.New("xmpp: failed to verify TLS certificate")
	}

	if log != nil && !config.SkipTLS {
		for i, cert := range tlsState.VerifiedChains[0] {
			fmt.Fprintf(log, "  certificate %d: %s\n", i, certName(cert))
		}
	}

	if err := tlsConn.VerifyHostname(domain); err != nil {
		if config.TrustedAddress {
			if log != nil {
				fmt.Fprintf(log, "Certificate fails to verify against domain in username: %s\n", err)
			}
			host, _, err := net.SplitHostPort(address)
			if err != nil {
				return nil, errors.New("xmpp: failed to split address when checking whether TLS certificate is valid: " + err.Error())
			}
			if err = tlsConn.VerifyHostname(host); err != nil {
				return nil, errors.New("xmpp: failed to match TLS certificate to address after failing to match to username: "******"Certificate matches against trusted server hostname: %s\n", host)
			}
		} else {
			return nil, errors.New("xmpp: failed to match TLS certificate to name: " + err.Error())
		}
	}

	c.in, c.out = makeInOut(tlsConn, config)
	c.rawOut = tlsConn

	if features, err = c.getFeatures(domain); err != nil {
		return nil, err
	}

	if config != nil && config.Create {
		if log != nil {
			io.WriteString(log, "Attempting to create account\n")
		}
		fmt.Fprintf(c.rawOut, "<iq type='set' id='create_1'><query xmlns='jabber:iq:register'><username>%s</username><password>%s</password></query></iq>", user, password)
		var iq ClientIQ
		if err = c.in.DecodeElement(&iq, nil); err != nil {
			return nil, errors.New("unmarshal <iq>: " + err.Error())
		}
		if iq.Type == "error" {
			return nil, errors.New("xmpp: account creation failed")
		}
	}

	if log != nil {
		io.WriteString(log, "Authenticating as "+user+"\n")
	}
	if err := c.authenticate(features, user, password); err != nil {
		return nil, err
	}

	if log != nil {
		io.WriteString(log, "Authentication successful\n")
	}

	if features, err = c.getFeatures(domain); err != nil {
		return nil, err
	}

	// Send IQ message asking to bind to the local user name.
	fmt.Fprintf(c.out, "<iq type='set' id='bind_2'><bind xmlns='%s'><resource>%s</resource></bind></iq>", nsBind, resource)

	var iq ClientIQ
	if err = c.in.DecodeElement(&iq, nil); err != nil {
		return nil, errors.New("unmarshal <iq>: " + err.Error())
	}
	if &iq.Bind == nil {
		return nil, errors.New("<iq> result missing <bind>")
	}
	c.jid = iq.Bind.Jid // our local id

	if features.Session != nil {
		// The server needs a session to be established. See RFC 3921,
		// section 3.
		fmt.Fprintf(c.out, "<iq to='%s' type='set' id='sess_1'><session xmlns='%s'/></iq>", domain, nsSession)
		if err = c.in.DecodeElement(&iq, nil); err != nil {
			return nil, errors.New("xmpp: unmarshal <iq>: " + err.Error())
		}
		if iq.Type != "result" {
			return nil, errors.New("xmpp: session establishment failed")
		}
	}
	return c, nil
}