This is a creation in Article, where the information may have evolved or changed.
Shadowsocks protocol
The Shadowsocks protocol packet format is the IV vector plus playload, which is the result of the encryption.
+-------+----------+| IV | Payload |+-------+----------+| Fixed | Variable |+-------+----------+
After decryption for
+--------------+---------------------+------------------+----------+| Address Type | Destination Address | Destination Port | Data |+--------------+---------------------+------------------+----------+| 1 | Variable | 2 | Variable |+--------------+---------------------+------------------+----------+
The server-side decryption requires this IV vector and the original negotiated key to decrypt, after the handshake is complete, all subsequent TCP packets will not take the IV, but the one negotiated with the handshake when the IV.
SOURCE parsing
This analysis Shadowsocks-server source code, the overall process is:
Monitoring Services
Initializing cryptographic objects
Decrypting client data
Connecting to a remote host
Establish a communication connection
Monitoring Services
Here, according to the configuration file, port monitoring, followed by the regular accept () waiting for the connection.
for port, password := range config.PortPassword { go run(port, password, config.Auth) if udp { go runUDP(port, password, config.Auth) } }
Initializing cryptographic objects
When there is a link coming after first check whether the secret object has been initially resolved, then encapsulate net. Conn. Here the cipher based on different encryption algorithm to implement the stream encryption and decryption Xorkeystream (DST, src []byte) interface, in particular, can see Golang comes with standard library cipher. Stream, of course, this interface to read the IV vector before it can be implemented.
//如果没有初始化,解析初始化. if cipher == nil { log.Println("creating cipher for port:", port) cipher, err = ss.NewCipher(config.Method, password) if err != nil { log.Printf("Error generating cipher for port: %s %v\n", port, err) conn.Close() continue } } //这里要根据password产生key. key := evpBytesToKey(password, mi.keyLen)
Decrypting client data
Net. Conn is encapsulated after the Read method reads the data is the decrypted data
func (c *Conn) Read(b []byte) (n int, err error) { //如果解密接口没有实现,读取iv向量初始化解密接口. if c.dec == nil { iv := make([]byte, c.info.ivLen) if _, err = io.ReadFull(c.Conn, iv); err != nil { return } //读取到iv向量后就可以初始化解密接口 if err = c.initDecrypt(iv); err != nil { return } if len(c.iv) == 0 { c.iv = iv } } //如果读取buff大于内置buff则分配内存 cipherData := c.readBuf if len(b) > len(cipherData) { cipherData = make([]byte, len(b)) } else { cipherData = cipherData[:len(b)] } //读取数据后解密返回 n, err = c.Conn.Read(cipherData) if n > 0 { c.decrypt(b[0:n], cipherData[0:n]) } return}
Connecting to a remote host
When the decrypted data is read, the unpacking is started and only the main code is copied.
//这里判断一个字节的地址类型 addrType := buf[idType] //这里根据地址类型的不同读取地址 if _, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil { return } //这里读取2个字节的端口 port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) //当获取到地址和端口后调用dial连接远程主机 remote, err := net.Dial("tcp", host)
There may still be OTA data, depending on the OTA has been deprecated, it is no longer explained, interested students can check their own information.
Establish a communication connection
The communication connection here is a bit like using IO. Copy (), connecting two Conn respectively.
//PipeThenClose有点像io.Copy if ota { go ss.PipeThenCloseOta(conn, remote) } else { go ss.PipeThenClose(conn, remote) } ss.PipeThenClose(remote, conn) //PipeThenClose的原理就是不断读取然后写入,直到出错. for{ n, err := src.Read(buf) // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { // Note: avoid overwrite err returned by Read. if _, err := dst.Write(buf[0:n]); err != nil { Debug.Println("write:", err) break } } }
Conclusion
Here the analysis of the source code of Shadowsocks-server, as well as shadowsocks-local feel the same thing is more than a cryptographic process.