187 lines
4.5 KiB
Go
187 lines
4.5 KiB
Go
package xgb
|
|
|
|
/*
|
|
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 : len(display)]
|
|
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 && c.host != "unix" {
|
|
if protocol == "" {
|
|
protocol = "tcp"
|
|
}
|
|
c.conn, err = net.Dial(protocol,
|
|
c.host+":"+strconv.Itoa(6000+c.DisplayNumber))
|
|
} else {
|
|
c.host = ""
|
|
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
|
|
}
|