go-tg-screenshot-bot
186 строк · 4.5 Кб
1package xgb
2
3/*
4conn.go contains a couple of functions that do some real dirty work related
5to the initial connection handshake with X.
6
7This code is largely unmodified from the original XGB package that I forked.
8*/
9
10import (
11"errors"
12"fmt"
13"io"
14"net"
15"os"
16"strconv"
17"strings"
18)
19
20// connect connects to the X server given in the 'display' string,
21// and does all the necessary setup handshaking.
22// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
23// Note that you should read and understand the "Connection Setup" of the
24// X Protocol Reference Manual before changing this function:
25// http://goo.gl/4zGQg
26func (c *Conn) connect(display string) error {
27err := c.dial(display)
28if err != nil {
29return err
30}
31
32return c.postConnect()
33}
34
35// connect init from to the net.Conn,
36func (c *Conn) connectNet(netConn net.Conn) error {
37c.conn = netConn
38return c.postConnect()
39}
40
41// do the postConnect action after Conn get it's underly net.Conn
42func (c *Conn) postConnect() error {
43// Get authentication data
44authName, authData, err := readAuthority(c.host, c.display)
45noauth := false
46if err != nil {
47Logger.Printf("Could not get authority info: %v", err)
48Logger.Println("Trying connection without authority info...")
49authName = ""
50authData = []byte{}
51noauth = true
52}
53
54// Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
55if !noauth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) {
56return errors.New("unsupported auth protocol " + authName)
57}
58
59buf := make([]byte, 12+Pad(len(authName))+Pad(len(authData)))
60buf[0] = 0x6c
61buf[1] = 0
62Put16(buf[2:], 11)
63Put16(buf[4:], 0)
64Put16(buf[6:], uint16(len(authName)))
65Put16(buf[8:], uint16(len(authData)))
66Put16(buf[10:], 0)
67copy(buf[12:], []byte(authName))
68copy(buf[12+Pad(len(authName)):], authData)
69if _, err = c.conn.Write(buf); err != nil {
70return err
71}
72
73head := make([]byte, 8)
74if _, err = io.ReadFull(c.conn, head[0:8]); err != nil {
75return err
76}
77code := head[0]
78reasonLen := head[1]
79major := Get16(head[2:])
80minor := Get16(head[4:])
81dataLen := Get16(head[6:])
82
83if major != 11 || minor != 0 {
84return fmt.Errorf("x protocol version mismatch: %d.%d", major, minor)
85}
86
87buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8)
88copy(buf, head)
89if _, err = io.ReadFull(c.conn, buf[8:]); err != nil {
90return err
91}
92
93if code == 0 {
94reason := buf[8 : 8+reasonLen]
95return fmt.Errorf("x protocol authentication refused: %s",
96string(reason))
97}
98
99// Unfortunately, it isn't really feasible to read the setup bytes here,
100// since the code to do so is in a different package.
101// Users must call 'xproto.Setup(X)' to get the setup info.
102c.SetupBytes = buf
103
104// But also read stuff that we *need* to get started.
105c.setupResourceIdBase = Get32(buf[12:])
106c.setupResourceIdMask = Get32(buf[16:])
107
108return nil
109}
110
111// dial initializes the actual net connection with X.
112func (c *Conn) dial(display string) error {
113if len(display) == 0 {
114display = os.Getenv("DISPLAY")
115}
116
117display0 := display
118if len(display) == 0 {
119return errors.New("empty display string")
120}
121
122colonIdx := strings.LastIndex(display, ":")
123if colonIdx < 0 {
124return errors.New("bad display string: " + display0)
125}
126
127var protocol, socket string
128
129if display[0] == '/' {
130socket = display[0:colonIdx]
131} else {
132slashIdx := strings.LastIndex(display, "/")
133if slashIdx >= 0 {
134protocol = display[0:slashIdx]
135c.host = display[slashIdx+1 : colonIdx]
136} else {
137c.host = display[0:colonIdx]
138}
139}
140
141display = display[colonIdx+1 : len(display)]
142if len(display) == 0 {
143return errors.New("bad display string: " + display0)
144}
145
146var scr string
147dotIdx := strings.LastIndex(display, ".")
148if dotIdx < 0 {
149c.display = display[0:]
150} else {
151c.display = display[0:dotIdx]
152scr = display[dotIdx+1:]
153}
154
155var err error
156c.DisplayNumber, err = strconv.Atoi(c.display)
157if err != nil || c.DisplayNumber < 0 {
158return errors.New("bad display string: " + display0)
159}
160
161if len(scr) != 0 {
162c.DefaultScreen, err = strconv.Atoi(scr)
163if err != nil {
164return errors.New("bad display string: " + display0)
165}
166}
167
168// Connect to server
169if len(socket) != 0 {
170c.conn, err = net.Dial("unix", socket+":"+c.display)
171} else if len(c.host) != 0 && c.host != "unix" {
172if protocol == "" {
173protocol = "tcp"
174}
175c.conn, err = net.Dial(protocol,
176c.host+":"+strconv.Itoa(6000+c.DisplayNumber))
177} else {
178c.host = ""
179c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display)
180}
181
182if err != nil {
183return errors.New("cannot connect to " + display0 + ": " + err.Error())
184}
185return nil
186}
187