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