-
Notifications
You must be signed in to change notification settings - Fork 55
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
base: main
Are you sure you want to change the base?
Conversation
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/ |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:
- Get the current hop limit.
- Set the hop limit to target
targetHopLimit
. - Calls function
functionToCall
- 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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to disorder
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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, ":") |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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.
This PR contains the implementation OOB and disOOB strategies from https://github.com/hufrea/byedpi/blob/main/desync.c