package nexgb /* conn.go contains a couple of functions that do some real dirty work related to the initial connection handshake with X. This code is largely unmodified from the original XGB package that I forked. */ import ( "errors" "fmt" "io" "net" "os" "strconv" "strings" ) // connect connects to the X server given in the 'display' string, // and does all the necessary setup handshaking. // If 'display' is empty it will be taken from os.Getenv("DISPLAY"). // Note that you should read and understand the "Connection Setup" of the // X Protocol Reference Manual before changing this function: // http://goo.gl/4zGQg func (c *Conn) connect(display string) error { err := c.dial(display) if err != nil { return err } return c.postConnect() } // connect init from to the net.Conn, func (c *Conn) connectNet(netConn net.Conn) error { c.conn = netConn return c.postConnect() } // 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 { Logger.Printf("Could not get authority info: %v", err) Logger.Println("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) } buf := make([]byte, 12+Pad(len(authName))+Pad(len(authData))) 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+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 } code := head[0] reasonLen := head[1] 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, int(dataLen)*4+8, int(dataLen)*4+8) copy(buf, head) if _, err = io.ReadFull(c.conn, buf[8:]); err != nil { return err } if code == 0 { reason := buf[8 : 8+reasonLen] return fmt.Errorf("x protocol authentication refused: %s", string(reason)) } // Unfortunately, it isn't really feasible to read the setup bytes here, // since the code to do so is in a different package. // Users must call 'xproto.Setup(X)' to get the setup info. c.SetupBytes = buf // But also read stuff that we *need* to get started. c.setupResourceIdBase = Get32(buf[12:]) c.setupResourceIdMask = Get32(buf[16:]) return nil } // 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 len(scr) != 0 { c.DefaultScreen, err = strconv.Atoi(scr) if err != nil { return errors.New("bad display string: " + display0) } } // 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" } c.conn, err = net.Dial(protocol, c.host+":"+strconv.Itoa(6000+c.DisplayNumber)) } else { 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 }