Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation OOB and disOOB strategies from byeDPI #329

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

siriusfreak
Copy link

This PR contains the implementation OOB and disOOB strategies from https://github.com/hufrea/byedpi/blob/main/desync.c

Copy link

google-cla bot commented Nov 13, 2024

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/Jigsaw-Code/outline-sdk => /Users/sirius/repos/outline-sdk/
Copy link
Contributor

Choose a reason for hiding this comment

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

Revert. For development, you can use Go workspaces instead.

opts sockopt.TCPOptions
oobByte byte
oobPosition int64
disOOB bool
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be called just disorder?

Also, can we compose with the disorder from #323 instead of reproducing the logic here?

/cc @PeterZhizhin

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see a clear way how OOB and disorder can be piped together to achieve desired behavior.

That said, I believe we can extract the following API from disorder package:

func withDisorder(tcpOpts *scokopt.TCPOptions, targetHopLimit int, functionToCall func() error) error

The function will do the following:

  1. Get the current hop limit.
  2. Set the hop limit to target targetHopLimit.
  3. Calls function functionToCall
  4. Restores the hop limit.

After we have #324 merged, we will also have it wait for the socket send all bytes before resetting the hop limit.

The withDisorder function will be called in both OOB strategy and disorder strategy.

What do you think?

return syscall.Sendmsg(int(fd), data, nil, nil, flags)
}

func getSocketDescriptor(conn *net.TCPConn) (SocketDescriptor, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Unused, delete. Also, the docs say this file descriptor may not be reliable: https://pkg.go.dev/net#TCPConn.File

return syscall.WSASend(syscall.Handle(fd), &wsaBuf[0], 1, &bytesSent, uint32(flags), nil, nil)
}

func getSocketDescriptor(conn *net.TCPConn) (SocketDescriptor, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete

disOOB bool,
delay time.Duration,
) io.Writer {
return &oobWriter{conn: conn, opts: opts, oobPosition: oobPosition, oobByte: oobByte, disOOB: disOOB, delay: delay}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return &oobWriter{conn: conn, opts: opts, oobPosition: oobPosition, oobByte: oobByte, disOOB: disOOB, delay: delay}
// TODO: Implement io.ReaderFrom.
return &oobWriter{conn: conn, opts: opts, oobPosition: oobPosition, oobByte: oobByte, disOOB: disOOB, delay: delay}

written = int(w.oobPosition)
secondPart[0] = tmp

if w.disOOB {
Copy link
Contributor

Choose a reason for hiding this comment

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

Clean up with a defer from the other if-block instead. It makes it clearer what it's cleaning up and ensures it's always called. Keeps all the disorder logic in one place.

oobPosition int64
sd SocketDescriptor
oobByte byte // Byte to send as OOB
disOOB bool // Flag to enable disOOB mode
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename to disorder

Copy link
Contributor

Choose a reason for hiding this comment

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

The convention in go is to have the platform as a name suffix. Use ops_unix.go instead. Same for windows.

type SocketDescriptor int

func sendTo(fd SocketDescriptor, data []byte, flags int) (err error) {
return syscall.Sendmsg(int(fd), data, nil, nil, flags)
Copy link
Contributor

Choose a reason for hiding this comment

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

Use https://pkg.go.dev/golang.org/x/sys/unix#SendmsgN, so we can return the number of bytes written in case of error.

var written int
var err error

if w.oobPosition > 0 && w.oobPosition < int64(len(data))-1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a corner case here. Probably worth capturing in the tests.

If the oob position aligns with the end of a write, it will never happen.

The problem is that you are dependent on an extra byte from the following write to push the OOB byte.

There's a way to fix that without requiring memory allocation. The SendMsg call can take the OOB as a separate buffer!

On Windows, the WSASend call takes a list of buffers, so just append the OOB to that list.

var written int
var err error

if w.oobPosition > 0 && w.oobPosition < int64(len(data))-1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

This somewhat copies the logic from split. Consider changing oobPosition to runAtPacketN from disorder: https://github.com/Jigsaw-Code/outline-sdk/blob/main/x/disorder/writer.go#L32

We can then pipe split together with oob to achieve oob byte to be sent at a defined call number.

For example: disorder:1|split:123. This transport will send first 123 bytes normally. Then will send the URG byte. Then will send the rest of the packet.

Copy link
Contributor

@PeterZhizhin PeterZhizhin left a comment

Choose a reason for hiding this comment

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

Amazing, thank you!

return &oobWriter{conn: conn, opts: opts, oobPosition: oobPosition, oobByte: oobByte, disOOB: disOOB, delay: delay}
}

func (w *oobWriter) Write(data []byte) (int, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

func (w *oobWriter) Write(data []byte) (written int, err error) {

}
params := config.URL.Opaque

splitStr := strings.Split(params, ":")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe named arguments? https://github.com/Jigsaw-Code/outline-sdk/pull/324/files#diff-974d5fac785c258e952052dd1f6aa5b012bf2e4e43eb708de99420175a67e99b

But it's a matter of preference. @fortuna should know better.

Also, maybe you can make most arguments optional? I believe most users can use default timeout, default URG byte value, etc.


// Use Control to execute Sendto on the file descriptor
var sendErr error
err = rawConn.Control(func(fd uintptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: semantically it's a Write rather than Control. Feel free to keep as is, if you feel like it.

var oldTTL int
if w.disOOB {
w.setTTL.Do(func() {
oldTTL, err = w.opts.HopLimit()
Copy link
Contributor

Choose a reason for hiding this comment

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

Take a look at the suggestion in https://github.com/Jigsaw-Code/outline-sdk/pull/329/files#r1840884687 that could simplify this and reduce code duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants