Skip to content
This repository has been archived by the owner on Apr 9, 2020. It is now read-only.

Commit

Permalink
Remove OTA support
Browse files Browse the repository at this point in the history
  • Loading branch information
lixin9311 committed Aug 27, 2017
1 parent 2c9262b commit b246139
Show file tree
Hide file tree
Showing 13 changed files with 38 additions and 250 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ AES is recommended for shadowsocks-go. [Intel AES Instruction Set](http://en.wik

### One Time Auth

Append `-auth` to the encryption method to enable [One Time Auth (OTA)](https://shadowsocks.org/en/spec/one-time-auth.html).

- For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect
- For client: the `-A` command line option can also enable OTA
Deprecated

### UDP relay

Expand Down
3 changes: 2 additions & 1 deletion cmd/shadowsocks-httpget/httpget.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"flag"
"fmt"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"io"
"math"
"net"
Expand All @@ -14,6 +13,8 @@ import (
"strconv"
"strings"
"time"

ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)

var config struct {
Expand Down
11 changes: 1 addition & 10 deletions cmd/shadowsocks-local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"os"
"path"
"strconv"
"strings"
"time"

ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
Expand Down Expand Up @@ -173,9 +172,7 @@ func parseServerConfig(config *ss.Config) {

if len(config.ServerPassword) == 0 {
method := config.Method
if config.Auth {
method += "-auth"
}

// only one encryption table
cipher, err := ss.NewCipher(method, config.Password)
if err != nil {
Expand Down Expand Up @@ -369,7 +366,6 @@ func main() {
flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port")
flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb")
flag.BoolVar((*bool)(&debug), "d", false, "print debug message")
flag.BoolVar(&cmdConfig.Auth, "A", false, "one time auth")

flag.Parse()

Expand All @@ -381,11 +377,6 @@ func main() {
cmdConfig.Server = cmdServer
ss.SetDebug(debug)

if strings.HasSuffix(cmdConfig.Method, "-auth") {
cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5]
cmdConfig.Auth = true
}

exists, err := ss.IsFileExists(configFile)
// If no config file in current directory, try search it in the binary directory
// Note there's no portable way to detect the binary directory.
Expand Down
65 changes: 20 additions & 45 deletions cmd/shadowsocks-server/server.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"encoding/binary"
"errors"
"flag"
Expand Down Expand Up @@ -30,16 +29,15 @@ const (
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address

lenIPv4 = net.IPv4len + 2 // ipv4 + 2port
lenIPv6 = net.IPv6len + 2 // ipv6 + 2port
lenDmBase = 2 // 1addrLen + 2port, plus addrLen
lenHmacSha1 = 10
lenIPv4 = net.IPv4len + 2 // ipv4 + 2port
lenIPv6 = net.IPv6len + 2 // ipv6 + 2port
lenDmBase = 2 // 1addrLen + 2port, plus addrLen
)

var debug ss.DebugLog
var udp bool

func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) {
func getRequest(conn *ss.Conn) (host string, err error) {
ss.SetReadTimeout(conn)

// buf size should at least have the same size with the largest possible
Expand Down Expand Up @@ -86,29 +84,15 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) {
// parse port
port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd])
host = net.JoinHostPort(host, strconv.Itoa(int(port)))
// if specified one time auth enabled, we should verify this
if auth || addrType&ss.OneTimeAuthMask > 0 {
ota = true
if _, err = io.ReadFull(conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil {
return
}
iv := conn.GetIv()
key := conn.GetKey()
actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd])
if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) {
err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd])
return
}
}
return
}

const logCntDelta = 100

var connCnt int
var nextLogConnCnt int = logCntDelta
var nextLogConnCnt = logCntDelta

func handleConnection(conn *ss.Conn, auth bool) {
func handleConnection(conn *ss.Conn) {
var host string

connCnt++ // this maybe not accurate, but should be enough
Expand Down Expand Up @@ -136,7 +120,7 @@ func handleConnection(conn *ss.Conn, auth bool) {
}
}()

host, ota, err := getRequest(conn, auth)
host, err := getRequest(conn)
if err != nil {
log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err)
closed = true
Expand Down Expand Up @@ -166,13 +150,9 @@ func handleConnection(conn *ss.Conn, auth bool) {
}
}()
if debug {
debug.Printf("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta())
}
if ota {
go ss.PipeThenCloseOta(conn, remote)
} else {
go ss.PipeThenClose(conn, remote)
debug.Printf("piping %s<->%s", conn.RemoteAddr(), host)
}
go ss.PipeThenClose(conn, remote)
ss.PipeThenClose(remote, conn)
closed = true
return
Expand Down Expand Up @@ -245,7 +225,7 @@ func (pm *PasswdManager) del(port string) {
// port. A different approach would be directly change the password used by
// that port, but that requires **sharing** password between the port listener
// and password manager.
func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) {
func (pm *PasswdManager) updatePortPasswd(port, password string) {
pl, ok := pm.get(port)
if !ok {
log.Printf("new port %s added\n", port)
Expand All @@ -258,11 +238,11 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) {
}
// run will add the new port listener to passwdManager.
// So there maybe concurrent access to passwdManager and we need lock to protect it.
go run(port, password, auth)
go run(port, password)
if udp {
pl, _ := pm.getUDP(port)
pl.listener.Close()
go runUDP(port, password, auth)
go runUDP(port, password)
}
}

Expand All @@ -282,13 +262,13 @@ func updatePasswd() {
return
}
for port, passwd := range config.PortPassword {
passwdManager.updatePortPasswd(port, passwd, config.Auth)
passwdManager.updatePortPasswd(port, passwd)
if oldconfig.PortPassword != nil {
delete(oldconfig.PortPassword, port)
}
}
// port password still left in the old config should be closed
for port, _ := range oldconfig.PortPassword {
for port := range oldconfig.PortPassword {
log.Printf("closing port %s as it's deleted\n", port)
passwdManager.del(port)
}
Expand All @@ -309,7 +289,7 @@ func waitSignal() {
}
}

func run(port, password string, auth bool) {
func run(port, password string) {
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Printf("error listening port %v: %v\n", port, err)
Expand All @@ -335,11 +315,11 @@ func run(port, password string, auth bool) {
continue
}
}
go handleConnection(ss.NewConn(conn, cipher.Copy()), auth)
go handleConnection(ss.NewConn(conn, cipher.Copy()))
}
}

func runUDP(port, password string, auth bool) {
func runUDP(port, password string) {
var cipher *ss.Cipher
port_i, _ := strconv.Atoi(port)
log.Printf("listening udp port %v\n", port)
Expand All @@ -358,7 +338,7 @@ func runUDP(port, password string, auth bool) {
log.Printf("Error generating cipher for udp port: %s %v\n", port, err)
conn.Close()
}
SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth)
SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy())
for {
if err := ss.ReadAndHandleUDPReq(SecurePacketConn); err != nil {
debug.Println(err)
Expand Down Expand Up @@ -414,11 +394,6 @@ func main() {

ss.SetDebug(debug)

if strings.HasSuffix(cmdConfig.Method, "-auth") {
cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5]
cmdConfig.Auth = true
}

var err error
config, err = ss.ParseConfig(configFile)
if err != nil {
Expand All @@ -445,9 +420,9 @@ func main() {
runtime.GOMAXPROCS(core)
}
for port, password := range config.PortPassword {
go run(port, password, config.Auth)
go run(port, password)
if udp {
go runUDP(port, password, config.Auth)
go runUDP(port, password)
}
}

Expand Down
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"server_port":8388,
"local_port":1080,
"password":"barfoo!",
"method": "aes-128-cfb-auth",
"method": "aes-128-cfb",
"timeout":600
}
2 changes: 1 addition & 1 deletion script/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ test_shadowsocks() {
server_pid=$!
wait_server $SERVER_PORT

$LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" -A &
$LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" &
local_pid=$!
wait_server $LOCAL_PORT

Expand Down
6 changes: 0 additions & 6 deletions shadowsocks/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
// "log"
"os"
"reflect"
"strings"
"time"
)

Expand All @@ -24,7 +23,6 @@ type Config struct {
LocalPort int `json:"local_port"`
Password string `json:"password"`
Method string `json:"method"` // encryption method
Auth bool `json:"auth"` // one time auth

// following options are only used by server
PortPassword map[string]string `json:"port_password"`
Expand Down Expand Up @@ -87,10 +85,6 @@ func ParseConfig(path string) (config *Config, err error) {
return nil, err
}
readTimeout = time.Duration(config.Timeout) * time.Second
if strings.HasSuffix(strings.ToLower(config.Method), "-auth") {
config.Method = config.Method[:len(config.Method)-5]
config.Auth = true
}
return
}

Expand Down
27 changes: 1 addition & 26 deletions shadowsocks/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ type Conn struct {
*Cipher
readBuf []byte
writeBuf []byte
chunkId uint32
}

func NewConn(c net.Conn, cipher *Cipher) *Conn {
Expand Down Expand Up @@ -64,17 +63,7 @@ func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, er
return
}
c = NewConn(conn, cipher)
if cipher.ota {
if c.enc == nil {
if _, err = c.initEncrypt(); err != nil {
return
}
}
// since we have initEncrypt, we must send iv manually
conn.Write(cipher.iv)
rawaddr[0] |= OneTimeAuthMask
rawaddr = otaConnectAuth(cipher.iv, cipher.key, rawaddr)
}

if _, err = c.write(rawaddr); err != nil {
c.Close()
return nil, err
Expand Down Expand Up @@ -103,16 +92,6 @@ func (c *Conn) GetKey() (key []byte) {
return
}

func (c *Conn) IsOta() bool {
return c.ota
}

func (c *Conn) GetAndIncrChunkId() (chunkId uint32) {
chunkId = c.chunkId
c.chunkId += 1
return
}

func (c *Conn) Read(b []byte) (n int, err error) {
if c.dec == nil {
iv := make([]byte, c.info.ivLen)
Expand Down Expand Up @@ -143,10 +122,6 @@ func (c *Conn) Read(b []byte) (n int, err error) {

func (c *Conn) Write(b []byte) (n int, err error) {
nn := len(b)
if c.ota {
chunkId := c.GetAndIncrChunkId()
b = otaReqChunkAuth(c.iv, chunkId, b)
}
headerLen := len(b) - nn

n, err = c.write(b)
Expand Down
11 changes: 0 additions & 11 deletions shadowsocks/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"encoding/binary"
"errors"
"io"
"strings"

"github.com/aead/chacha20"
"golang.org/x/crypto/blowfish"
Expand Down Expand Up @@ -188,7 +187,6 @@ type Cipher struct {
dec cipher.Stream
key []byte
info *cipherInfo
ota bool // one-time auth
iv []byte
}

Expand All @@ -199,13 +197,6 @@ func NewCipher(method, password string) (c *Cipher, err error) {
if password == "" {
return nil, errEmptyPassword
}
var ota bool
if strings.HasSuffix(strings.ToLower(method), "-auth") {
method = method[:len(method)-5] // len("-auth") = 5
ota = true
} else {
ota = false
}
mi, ok := cipherMethod[method]
if !ok {
return nil, errors.New("Unsupported encryption method: " + method)
Expand All @@ -218,7 +209,6 @@ func NewCipher(method, password string) (c *Cipher, err error) {
if err != nil {
return nil, err
}
c.ota = ota
return c, nil
}

Expand Down Expand Up @@ -268,6 +258,5 @@ func (c *Cipher) Copy() *Cipher {
nc := *c
nc.enc = nil
nc.dec = nil
nc.ota = c.ota
return &nc
}
Loading

4 comments on commit b246139

@dszhengyu
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several mistakes in ota, such as the place New a cipher and the place Write back to a client.
This page(https://shadowsocks.org/en/spec/one-time-auth.html) could not be open anymore.
It is specified in shadowsocks.org that ss does not design to support integrity,
so I am wondering that why OTA is included in ss in the first place?

@arthurkiller
Copy link
Collaborator

@arthurkiller arthurkiller commented on b246139 Sep 4, 2017 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arthurkiller
Copy link
Collaborator

@arthurkiller arthurkiller commented on b246139 Sep 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dszhengyu

I am wondering that why OTA is included in ss in the first place?

OTA is a feature in protocol which implement by ss-go before it has been depreciated.

Perhaps the designer did not find out OTA is not a good idea before.

@dszhengyu
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.