Skip to content
Commits on Source (2)
*.swp
*~
obfs4proxy/obfs4proxy
Changes in version 0.0.8 - 2019-01-20:
- Bug 24793: Send the correct authorization HTTP header for basic auth.
- (meek_lite) Explicitly set Content-Length to zero when there is no data
to send.
- Added optional support for building as a Go 1.11 module. Patch by mvdan.
- Change the canonical upstream repo location to gitlab.
Changes in version 0.0.7 - 2016-11-15:
- Support configuring the obfs4 IAT parameter as the sole
ServerTransportOption on bridges, and correctly checkpoint the argument
......
......@@ -29,24 +29,24 @@ handshake variants without being obscenely slow is non-trivial.
### Dependencies
Build time library dependencies are handled by go get automatically but are
listed for clarity.
Build time library dependencies are handled by the Go module automatically.
* Go 1.2.0 or later. Prior versions of Go (Eg: 1.0.2) are missing certain
important parts of the runtime library like a SHA256 implementation.
* go.crypto (https://golang.org/x/crypto)
* go.net (https://golang.org/x/net)
* ed25519/extra25519 (https://github.com/agl/ed25519/extra25519)
* SipHash-2-4 (https://github.com/dchest/siphash)
* goptlib (https://git.torproject.org/pluggable-transports/goptlib.git)
If you are on Go versions earlier than 1.11, you might need to run `go get -d
./...` to download all the dependencies. Note however, that modules always use
the same dependency versions, while `go get -d` always downloads master.
* Go 1.11.0 or later. Patches to support up to 2 prior major releases will
be accepted if they are not overly intrusive and well written.
* See `go.mod` for build time dependencies.
### Installation
To build:
`go get git.torproject.org/pluggable-transports/obfs4.git/obfs4proxy`
To install:
Copy `$GOPATH/bin/obfs4proxy` to a permanent location (Eg: `/usr/local/bin`)
`go build -o obfs4proxy/obfs4proxy ./obfs4proxy`
To install, copy `./obfs4proxy/obfsproxy` to a permanent location
(Eg: `/usr/local/bin`)
Client side torrc configuration:
```
......
......@@ -31,7 +31,7 @@
// Not all of the convinience routines are replicated, only those that are
// immediately useful. The Rand variable provides access to the full math/rand
// API.
package csrand
package csrand // import "gitlab.com/yawning/obfs4.git/common/csrand"
import (
cryptRand "crypto/rand"
......
......@@ -27,7 +27,7 @@
// Package drbg implements a minimalistic DRBG based off SipHash-2-4 in OFB
// mode.
package drbg
package drbg // import "gitlab.com/yawning/obfs4.git/common/drbg"
import (
"encoding/binary"
......@@ -36,8 +36,7 @@ import (
"hash"
"github.com/dchest/siphash"
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
"gitlab.com/yawning/obfs4.git/common/csrand"
)
// Size is the length of the HashDrbg output.
......@@ -140,7 +139,7 @@ func (drbg *HashDrbg) Seed(seed int64) {
// NextBlock returns the next 8 byte DRBG block.
func (drbg *HashDrbg) NextBlock() []byte {
drbg.sip.Write(drbg.ofb[:])
_, _ = drbg.sip.Write(drbg.ofb[:])
copy(drbg.ofb[:], drbg.sip.Sum(nil))
ret := make([]byte, Size)
......
......@@ -27,7 +27,7 @@
// Package log implements a simple set of leveled logging wrappers around the
// standard log package.
package log
package log // import "gitlab.com/yawning/obfs4.git/common/log"
import (
"fmt"
......
......@@ -32,7 +32,7 @@
//
// Before using this package, it is strongly recommended that the specification
// is read and understood.
package ntor
package ntor // import "gitlab.com/yawning/obfs4.git/common/ntor"
import (
"bytes"
......@@ -43,12 +43,10 @@ import (
"fmt"
"io"
"github.com/agl/ed25519/extra25519"
"gitlab.com/yawning/obfs4.git/common/csrand"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
"github.com/agl/ed25519/extra25519"
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
)
const (
......@@ -387,21 +385,21 @@ func ntorCommon(secretInput bytes.Buffer, id *NodeID, b *PublicKey, x *PublicKey
// KEY_SEED = H(secret_input, t_key)
h := hmac.New(sha256.New, tKey)
h.Write(secretInput.Bytes())
_, _ = h.Write(secretInput.Bytes())
tmp := h.Sum(nil)
copy(keySeed[:], tmp)
// verify = H(secret_input, t_verify)
h = hmac.New(sha256.New, tVerify)
h.Write(secretInput.Bytes())
_, _ = h.Write(secretInput.Bytes())
verify := h.Sum(nil)
// auth_input = verify | ID | B | Y | X | PROTOID | "Server"
authInput := bytes.NewBuffer(verify)
authInput.Write(suffix.Bytes())
authInput.Write([]byte("Server"))
_, _ = authInput.Write(suffix.Bytes())
_, _ = authInput.Write([]byte("Server"))
h = hmac.New(sha256.New, tMac)
h.Write(authInput.Bytes())
_, _ = h.Write(authInput.Bytes())
tmp = h.Sum(nil)
copy(auth[:], tmp)
......
......@@ -28,7 +28,7 @@
// Package probdist implements a weighted probability distribution suitable for
// protocol parameterization. To allow for easy reproduction of a given
// distribution, the drbg package is used as the random number source.
package probdist
package probdist // import "gitlab.com/yawning/obfs4.git/common/probdist"
import (
"bytes"
......@@ -37,8 +37,8 @@ import (
"math/rand"
"sync"
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
"gitlab.com/yawning/obfs4.git/common/csrand"
"gitlab.com/yawning/obfs4.git/common/drbg"
)
const (
......
......@@ -31,7 +31,7 @@ import (
"fmt"
"testing"
"git.torproject.org/pluggable-transports/obfs4.git/common/drbg"
"gitlab.com/yawning/obfs4.git/common/drbg"
)
const debug = false
......
......@@ -30,7 +30,7 @@
// has been seen before based on the SipHash-2-4 digest of the sequence.
// Collisions are treated as positive matches, though the probability of this
// happening is negligible.
package replayfilter
package replayfilter // import "gitlab.com/yawning/obfs4.git/common/replayfilter"
import (
"container/list"
......@@ -39,8 +39,7 @@ import (
"time"
"github.com/dchest/siphash"
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
"gitlab.com/yawning/obfs4.git/common/csrand"
)
// maxFilterSize is the maximum capacity of a replay filter. This value is
......
......@@ -39,8 +39,8 @@ func (req *Request) authRFC1929() (err error) {
sendErrResp := func() {
// Swallow write/flush errors, the auth failure is the relevant error.
resp := []byte{authRFC1929Ver, authRFC1929Fail}
req.rw.Write(resp[:])
req.flushBuffers()
_, _ = req.rw.Write(resp[:])
_ = req.flushBuffers()
}
// The client sends a Username/Password request.
......
......@@ -35,7 +35,7 @@
// * The authentication provided by the client is always accepted as it is
// used as a channel to pass information rather than for authentication for
// pluggable transports.
package socks5
package socks5 // import "gitlab.com/yawning/obfs4.git/common/socks5"
import (
"bufio"
......@@ -257,15 +257,15 @@ func (req *Request) readCommand() error {
var err error
if err = req.readByteVerify("version", version); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
if err = req.readByteVerify("command", cmdConnect); err != nil {
req.Reply(ReplyCommandNotSupported)
_ = req.Reply(ReplyCommandNotSupported)
return err
}
if err = req.readByteVerify("reserved", rsv); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
......@@ -273,49 +273,49 @@ func (req *Request) readCommand() error {
var atyp byte
var host string
if atyp, err = req.readByte(); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
switch atyp {
case atypIPv4:
var addr []byte
if addr, err = req.readBytes(net.IPv4len); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String()
case atypDomainName:
var alen byte
if alen, err = req.readByte(); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
if alen == 0 {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return fmt.Errorf("domain name with 0 length")
}
var addr []byte
if addr, err = req.readBytes(int(alen)); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
host = string(addr)
case atypIPv6:
var rawAddr []byte
if rawAddr, err = req.readBytes(net.IPv6len); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
addr := make(net.IP, net.IPv6len)
copy(addr[:], rawAddr[:])
host = fmt.Sprintf("[%s]", addr.String())
default:
req.Reply(ReplyAddressNotSupported)
_ = req.Reply(ReplyAddressNotSupported)
return fmt.Errorf("unsupported address type 0x%02x", atyp)
}
var rawPort []byte
if rawPort, err = req.readBytes(2); err != nil {
req.Reply(ReplyGeneralFailure)
_ = req.Reply(ReplyGeneralFailure)
return err
}
port := int(rawPort[0])<<8 | int(rawPort[1])
......
......@@ -56,12 +56,16 @@ func (c *testReadWriter) Write(buf []byte) (n int, err error) {
return c.writeBuf.Write(buf)
}
func (c *testReadWriter) writeHex(str string) (n int, err error) {
func (c *testReadWriter) writeHex(str string) {
var buf []byte
var err error
if buf, err = hex.DecodeString(str); err != nil {
return
panic("writeHex: malformed hex: " + err.Error())
}
if _, err = c.readBuf.Write(buf); err != nil {
panic("writeHex: buffered write failed: " + err.Error())
}
return c.readBuf.Write(buf)
}
func (c *testReadWriter) readHex() string {
......
......@@ -29,7 +29,7 @@
// mechanism as defined in the obfs3 protocol specification. This
// implementation is suitable for obfuscation but MUST NOT BE USED when strong
// security is required as it is not constant time.
package uniformdh
package uniformdh // import "gitlab.com/yawning/obfs4.git/common/uniformdh"
import (
"fmt"
......
......@@ -230,7 +230,7 @@
Bytes 064:071 - Server to Client 64 bit SipHash-2-4 OFB IV.
Bytes 072:103 - Client to Server 256 bit NaCl secretbox key.
Bytes 104:119 - Client to Server NaCl secretbox nonce prefix.
Bytes 104:119 - Client to Server 128 bit NaCl secretbox nonce prefix.
Bytes 120:135 - Client to Server 128 bit SipHash-2-4 key.
Bytes 136:143 - Client to Server 64 bit SipHash-2-4 OFB IV.
......
module gitlab.com/yawning/obfs4.git
require (
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412
github.com/dchest/siphash v1.2.0
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1
)
......@@ -41,17 +41,16 @@ import (
"sync"
"syscall"
"golang.org/x/net/proxy"
"git.torproject.org/pluggable-transports/goptlib.git"
"git.torproject.org/pluggable-transports/obfs4.git/common/log"
"git.torproject.org/pluggable-transports/obfs4.git/common/socks5"
"git.torproject.org/pluggable-transports/obfs4.git/transports"
"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
"gitlab.com/yawning/obfs4.git/common/log"
"gitlab.com/yawning/obfs4.git/common/socks5"
"gitlab.com/yawning/obfs4.git/transports"
"gitlab.com/yawning/obfs4.git/transports/base"
"golang.org/x/net/proxy"
)
const (
obfs4proxyVersion = "0.0.7"
obfs4proxyVersion = "0.0.8"
obfs4proxyLogFile = "obfs4proxy.log"
socksAddr = "127.0.0.1:0"
)
......@@ -76,23 +75,25 @@ func clientSetup() (launched bool, listeners []net.Listener) {
for _, name := range ptClientInfo.MethodNames {
t := transports.Get(name)
if t == nil {
pt.CmethodError(name, "no such transport is supported")
_ = pt.CmethodError(name, "no such transport is supported")
continue
}
f, err := t.ClientFactory(stateDir)
if err != nil {
pt.CmethodError(name, "failed to get ClientFactory")
_ = pt.CmethodError(name, "failed to get ClientFactory")
continue
}
ln, err := net.Listen("tcp", socksAddr)
if err != nil {
pt.CmethodError(name, err.Error())
_ = pt.CmethodError(name, err.Error())
continue
}
go clientAcceptLoop(f, ln, ptClientProxy)
go func() {
_ = clientAcceptLoop(f, ln, ptClientProxy)
}()
pt.Cmethod(name, socks5.Version(), ln.Addr())
log.Infof("%s - registered listener: %s", name, ln.Addr())
......@@ -138,7 +139,7 @@ func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) {
args, err := f.ParseArgs(&socksReq.Args)
if err != nil {
log.Errorf("%s(%s) - invalid arguments: %s", name, addrStr, err)
socksReq.Reply(socks5.ReplyGeneralFailure)
_ = socksReq.Reply(socks5.ReplyGeneralFailure)
return
}
......@@ -150,7 +151,7 @@ func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) {
// This should basically never happen, since config protocol
// verifies this.
log.Errorf("%s(%s) - failed to obtain proxy dialer: %s", name, addrStr, log.ElideError(err))
socksReq.Reply(socks5.ReplyGeneralFailure)
_ = socksReq.Reply(socks5.ReplyGeneralFailure)
return
}
dialFn = dialer.Dial
......@@ -158,7 +159,7 @@ func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) {
remote, err := f.Dial("tcp", socksReq.Target, dialFn, args)
if err != nil {
log.Errorf("%s(%s) - outgoing connection failed: %s", name, addrStr, log.ElideError(err))
socksReq.Reply(socks5.ErrorToReplyCode(err))
_ = socksReq.Reply(socks5.ErrorToReplyCode(err))
return
}
defer remote.Close()
......@@ -173,8 +174,6 @@ func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) {
} else {
log.Infof("%s(%s) - closed connection", name, addrStr)
}
return
}
func serverSetup() (launched bool, listeners []net.Listener) {
......@@ -187,23 +186,25 @@ func serverSetup() (launched bool, listeners []net.Listener) {
name := bindaddr.MethodName
t := transports.Get(name)
if t == nil {
pt.SmethodError(name, "no such transport is supported")
_ = pt.SmethodError(name, "no such transport is supported")
continue
}
f, err := t.ServerFactory(stateDir, &bindaddr.Options)
if err != nil {
pt.SmethodError(name, err.Error())
_ = pt.SmethodError(name, err.Error())
continue
}
ln, err := net.ListenTCP("tcp", bindaddr.Addr)
if err != nil {
pt.SmethodError(name, err.Error())
_ = pt.SmethodError(name, err.Error())
continue
}
go serverAcceptLoop(f, ln, &ptServerInfo)
go func() {
_ = serverAcceptLoop(f, ln, &ptServerInfo)
}()
if args := f.Args(); args != nil {
pt.SmethodArgs(name, ln.Addr(), *args)
} else {
......@@ -263,8 +264,6 @@ func serverHandler(f base.ServerFactory, conn net.Conn, info *pt.ServerInfo) {
} else {
log.Infof("%s(%s) - closed connection", name, addrStr)
}
return
}
func copyLoop(a net.Conn, b net.Conn) error {
......@@ -327,7 +326,7 @@ func main() {
// Determine if this is a client or server, initialize the common state.
var ptListeners []net.Listener
launched := false
var launched bool
isClient, err := ptIsClient()
if err != nil {
golog.Fatalf("[ERROR]: %s - must be run as a managed transport", execName)
......
......@@ -29,6 +29,7 @@ package main
import (
"bufio"
"encoding/base64"
"fmt"
"net"
"net/http"
......@@ -68,7 +69,7 @@ func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
return nil, err
}
conn := new(httpConn)
conn.httpConn = httputil.NewClientConn(c, nil)
conn.httpConn = httputil.NewClientConn(c, nil) // nolint: staticcheck
conn.remoteAddr, err = net.ResolveTCPAddr(network, addr)
if err != nil {
conn.httpConn.Close()
......@@ -90,12 +91,14 @@ func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
}
req.Close = false
if s.haveAuth {
req.SetBasicAuth(s.username, s.password)
// SetBasicAuth doesn't quite do what is appropriate, because
// the correct header is `Proxy-Authorization`.
req.Header.Set("Proxy-Authorization", base64.StdEncoding.EncodeToString([]byte(s.username+":"+s.password)))
}
req.Header.Set("User-Agent", "")
resp, err := conn.httpConn.Do(req)
if err != nil && err != httputil.ErrPersistEOF {
if err != nil && err != httputil.ErrPersistEOF { // nolint: staticcheck
conn.httpConn.Close()
return nil, err
}
......@@ -110,7 +113,7 @@ func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
type httpConn struct {
remoteAddr *net.TCPAddr
httpConn *httputil.ClientConn
httpConn *httputil.ClientConn // nolint: staticcheck
hijackedConn net.Conn
staleReader *bufio.Reader
}
......
......@@ -43,19 +43,19 @@ import (
func ptEnvError(msg string) error {
line := []byte(fmt.Sprintf("ENV-ERROR %s\n", msg))
pt.Stdout.Write(line)
_, _ = pt.Stdout.Write(line)
return errors.New(msg)
}
func ptProxyError(msg string) error {
line := []byte(fmt.Sprintf("PROXY-ERROR %s\n", msg))
pt.Stdout.Write(line)
_, _ = pt.Stdout.Write(line)
return errors.New(msg)
}
func ptProxyDone() {
line := []byte("PROXY DONE\n")
pt.Stdout.Write(line)
_, _ = pt.Stdout.Write(line)
}
func ptIsClient() (bool, error) {
......