shadowsocks-go原始碼分析

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

shadowsocks 協議

shadowsocks協議包格式為IV向量加Playload,這是加密後的結果.

+-------+----------+|  IV   | Payload  |+-------+----------+| Fixed | Variable |+-------+----------+

解密後為

+--------------+---------------------+------------------+----------+| Address Type | Destination Address | Destination Port |   Data   |+--------------+---------------------+------------------+----------+|      1       |       Variable      |         2        | Variable |+--------------+---------------------+------------------+----------+

服務端解密需要這個IV向量和原來就協商好的密鑰進行解密,握手完成後後續的所有TCP 資料包不會再帶上IV,而是使用握手時協商的那個IV。

源碼解析

這裡分析shadowsocks-server的原始碼,整體流程為:

  1. 監聽服務

  2. 初始化加密對象

  3. 解密用戶端資料

  4. 串連遠程主機

  5. 建立通訊串連

監聽服務

這雷根據設定檔,進行連接埠監聽,後面就是常規的Accept()等待串連.

    for port, password := range config.PortPassword {        go run(port, password, config.Auth)        if udp {            go runUDP(port, password, config.Auth)        }    }

初始化加密對象

當有連結來到後首先檢查是否已經初始化解密對象,然後封裝net.Conn.這裡的cipher根據不同的密碼編譯演算法實現了流加密解密XORKeyStream(dst, src []byte)介面,具體可以看golang內建標準庫cipher.Stream,當然這個介面要讀取iv向量後才能實現.

        //如果沒有初始化,解析初始化.        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)

解密用戶端資料

net.Conn被封裝後Read方法讀出的資料即為解密後的資料

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}

串連遠程主機

當讀取到解密的資料後就要開始解包了,這裡只複製主要代碼.

    //這裡判斷一個位元組的地址類型    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)

剩下可能還會有ota資料,根據ota已經被棄用了吧,這裡就不再說明了,有興趣的同學可以自己去查資料.

建立通訊串連

這裡建立通訊串連有點像使用了io.Copy(),分別串連兩個conn.

    //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            }        }    }

結語

這裡分析了shadowsocks-server的原始碼,還有shadowsocks-local的感覺大同小異就是多了一個加密的過程.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.