package x import ( "bufio" "errors" "fmt" "io" "net" "os" "strconv" "strings" ) func NewConn() (*Conn, error) { return NewConnDisplay("") } func NewConnDisplay(display string) (*Conn, error) { c := new(Conn) err := c.connectDisplay(display) if err != nil { return nil, err } c.bufReader = bufio.NewReader(c.conn) c.in.init(&c.ioMu) c.out.bw = bufio.NewWriter(c.conn) c.ridAllocator.init(c.setup.ResourceIdBase, c.setup.ResourceIdMask) go c.readLoop() go c.eventSendLoop() return c, nil } func (c *Conn) connectDisplay(display string) error { err := c.dial(display) if err != nil { return err } return c.postConnect() } // dial initializes the actual net connection with X. func (c *Conn) dial(display string) error { if len(display) == 0 { display = os.Getenv("DISPLAY") } display0 := display if len(display) == 0 { return errors.New("empty display string") } colonIdx := strings.LastIndex(display, ":") if colonIdx < 0 { return errors.New("bad display string: " + display0) } var protocol, socket string if display[0] == '/' { socket = display[0:colonIdx] } else { slashIdx := strings.LastIndex(display, "/") if slashIdx >= 0 { protocol = display[0:slashIdx] c.host = display[slashIdx+1 : colonIdx] } else { c.host = display[0:colonIdx] } } display = display[colonIdx+1:] if len(display) == 0 { return errors.New("bad display string: " + display0) } var scr string dotIdx := strings.LastIndex(display, ".") if dotIdx < 0 { c.display = display[0:] } else { c.display = display[0:dotIdx] scr = display[dotIdx+1:] } var err error c.DisplayNumber, err = strconv.Atoi(c.display) if err != nil || c.DisplayNumber < 0 { return errors.New("bad display string: " + display0) } if scr != "" { c.ScreenNumber, err = strconv.Atoi(scr) if err != nil { return errors.New("bad display string: " + display0) } } logPrintf("socket: %q, protocol: %q\n", socket, protocol) // Connect to server if len(socket) != 0 { c.conn, err = net.Dial("unix", socket+":"+c.display) } else if len(c.host) != 0 { if protocol == "" { protocol = "tcp" } logPrintln("dial tcp") c.conn, err = net.Dial(protocol, c.host+":"+strconv.Itoa(6000+c.DisplayNumber)) } else { logPrintln("dial unix") c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display) } if err != nil { return errors.New("cannot connect to " + display0 + ": " + err.Error()) } return nil } // do the postConnect action after Conn get it's underly net.Conn func (c *Conn) postConnect() error { // Get authentication data authName, authData, err := readAuthority(c.host, c.display) noAuth := false if err != nil { logPrintf("Could not get authority info: %v", err) logPrintln("Trying connection without authority info...") authName = "" authData = []byte{} noAuth = true } // Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1". if !noAuth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) { return errors.New("unsupported auth protocol " + authName) } bufLen := 12 + len(authName) + Pad(len(authName)) + len(authData) + Pad(len(authData)) buf := make([]byte, bufLen) buf[0] = 0x6c buf[1] = 0 Put16(buf[2:], 11) Put16(buf[4:], 0) Put16(buf[6:], uint16(len(authName))) Put16(buf[8:], uint16(len(authData))) Put16(buf[10:], 0) copy(buf[12:], []byte(authName)) copy(buf[12+len(authName)+Pad(len(authName)):], authData) if _, err = c.conn.Write(buf); err != nil { return err } head := make([]byte, 8) if _, err = io.ReadFull(c.conn, head[0:8]); err != nil { return err } respType := head[0] major := Get16(head[2:]) minor := Get16(head[4:]) dataLen := Get16(head[6:]) if major != 11 || minor != 0 { return fmt.Errorf("x protocol version mismatch: %d.%d", major, minor) } buf = make([]byte, 8+dataLen*4) copy(buf, head) if _, err = io.ReadFull(c.conn, buf[8:]); err != nil { return err } if respType == ResponseTypeError { reasonLen := head[1] reason := buf[8 : 8+reasonLen] return fmt.Errorf("x protocol authentication refused: %s", string(reason)) } var setup Setup r := NewReaderFromData(buf) err = readSetup(r, &setup) if err != nil { return err } c.setup = &setup /* Make sure requested screen number is in bounds for this server */ if c.ScreenNumber >= len(setup.Roots) { return errors.New("invalid screen") } return nil }