func Dial(conn net.Conn, pubkey *rsa.PublicKey, service, username, token string, timeout time.Duration) (c Conn, err error) {
	if strings.Contains(service, "\n") || strings.Contains(username, "\n") ||
		strings.Contains(service, ":") || strings.Contains(username, ":") ||
		strings.Contains(service, ",") || strings.Contains(username, ",") {
		err = ErrBadServiceOrUserName
		return
	}
	conn.SetDeadline(time.Now().Add(timeout))
	defer func() {
		conn.SetDeadline(time.Time{})
		if err != nil {
			conn.Close()
		}
	}()
	ks, err := proto.ClientKeyExchange(pubkey, conn)
	if err != nil {
		return
	}
	cmdio := ks.ClientCommandIO(conn)
	cmd := new(proto.Command)
	cmd.Type = proto.CMD_AUTH
	cmd.Params = make([]string, 3)
	cmd.Params[0] = service
	cmd.Params[1] = username
	cmd.Params[2] = token
	cmdio.WriteCommand(cmd, false)
	cmd, err = cmdio.ReadCommand()
	if err != nil {
		return
	}
	if cmd.Type == proto.CMD_REDIRECT {
		r := new(RedirectRequest)
		r.Addresses = cmd.Params
		return nil, r
	}
	if cmd.Type != proto.CMD_AUTHOK {
		return
	}
	c = NewConn(cmdio, service, username, conn)
	err = nil
	return
}
The conn will be closed if any error occur
package client
import (
	"crypto/rsa"
	"errors"
	"github.com/uniqush/uniqush-conn/proto"
	"net"
	"strings"
	"time"
)
var ErrBadServiceOrUserName = errors.New("service name or user name should not contain '\\n' or ':'")