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

mobileproxy clib #226

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x/examples/mobileproxy-clib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
demo/bin
19 changes: 19 additions & 0 deletions x/examples/mobileproxy-clib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Mobileproxy CLib Wrapper

This is a C wrapper for the mobileproxy library. It is intended to be used downstream by native wrappers like Swift or Java.

## Demo

To run the demo, you first must build the library for your platform. You can build it by running:

```bash
cd x

CGO_ENABLED=1 go build -buildmode=c-shared -o=examples/mobileproxy-clib/demo/bin github.com/Jigsaw-Code/outline-sdk/x/examples/mobileproxy-clib
```

Then, you can run the demo by running:

```bash
TODO
```
44 changes: 44 additions & 0 deletions x/examples/mobileproxy-clib/demo/demo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// -- WORK IN PROGRESS --

#include <stdio.h>
#include <bin/mobileproxy-clib.h>

// Is there a way to import these? Or do we need to define them ourselves?
typedef unsigned int StreamDialerPtr;
typedef unsigned int ProxyPtr;

int main()
{
StreamDialerPtr *dialer;
ProxyPtr *proxy;

dialer = NewStreamDialerFromConfig("split:3");
Copy link
Contributor

Choose a reason for hiding this comment

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

We need error handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, converting this to draft as we figure out that piece.

proxy = RunProxy("127.0.0.1:1234", dialer); // Is this call blocking? Do we need to run it in another thread?

// TODO: Wait for terminate key, we may read from console using fgetc
// Signal (i.e. Ctrl+C) is a Unix/Linux only API, Windows doesn't use it
// So it also depends on whether we need the program to be cross-platform
printf("Running proxy on 127.0.0.1:1234. Press <Enter> to terminate: ");

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 no call to read the Enter here.

Consider sigaction: https://stackoverflow.com/a/19059462

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this file is in progress. Converting this PR back to draft!

Copy link
Contributor

Choose a reason for hiding this comment

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

sigaction is not cross-platform. I'd rather use a standard library function.

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean? It's in the POSIX standard: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html

Does Windows not implement it? I guess signal is probably fine too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sigaction, but signal is good.

// Stop the proxy and clean up
StopProxy(proxy, 1000);

DeleteProxy(proxy);
DeleteStreamDialer(dialer);

return 0;
}
87 changes: 87 additions & 0 deletions x/examples/mobileproxy-clib/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

// #include <stdlib.h>
import (
"C"
"runtime/cgo"
"unsafe"

"github.com/Jigsaw-Code/outline-sdk/x/mobileproxy"
)

type StreamDialerPtr = unsafe.Pointer
daniellacosse marked this conversation as resolved.
Show resolved Hide resolved
type ProxyPtr = unsafe.Pointer

//export NewStreamDialerFromConfig
func NewStreamDialerFromConfig(config *C.char) StreamDialerPtr {
streamDialerObject, err := mobileproxy.NewStreamDialerFromConfig(C.GoString(config))

if err != nil {
// TODO: print something?
return unsafe.Pointer(nil)
}

streamDialerHandle := cgo.NewHandle(streamDialerObject)

return unsafe.Pointer(&streamDialerHandle)
}

//export RunProxy
func RunProxy(address *C.char, dialer StreamDialerPtr) ProxyPtr {
dialerObject := (*cgo.Handle)(dialer).Value().(mobileproxy.StreamDialer)

proxyObject, err := mobileproxy.RunProxy(C.GoString(address), &dialerObject)

if err != nil {
// TODO: print something?
Copy link
Contributor

Choose a reason for hiding this comment

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

We must have a way to return errors. Go Mobile returns a struct instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jyyi1 you can return a tuple right?

Copy link
Contributor

@jyyi1 jyyi1 May 10, 2024

Choose a reason for hiding this comment

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

Yes, you can return a tuple and paste the header here.

@fortuna, do you mean a more C-friendly way of returning errors, like below?

Proxy *proxy;
int err = CreateProxy(&proxy);

Copy link
Contributor

Choose a reason for hiding this comment

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

We need a way to return an error meesage.

Also, which design should we use?

  • Return error (proxy reference)
  • Return proxy (error reference)
  • Return tuple
  • return nothing (error and proxy reference)

I think Go Mobile on ios returns the value and writes the error to an input reference.

Copy link
Contributor Author

@daniellacosse daniellacosse May 13, 2024

Choose a reason for hiding this comment

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

I lean towards tuple with two pointers in it. Since we're throwing around unsafe pointers, with the tuple approach the caller doesn't have to do any guesswork around what kind of pointer they've received. But maybe there's a C way to do it? @jyyi1 thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

errno is very Unix specific. But sure, we can introduce our own errno. That's actually my code above, we'll return an int, and put the proxy into a "output parameter".

Copy link
Contributor

Choose a reason for hiding this comment

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

We need a way to return an error message too. Errno is not enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm going with tuple w/ error message as second optional argument

return unsafe.Pointer(nil)
}

handle := cgo.NewHandle(proxyObject)

return unsafe.Pointer(&handle)
}

//export AddURLProxy
func AddURLProxy(proxy ProxyPtr, url *C.char, dialer StreamDialerPtr) {
proxyObject := (*cgo.Handle)(proxy).Value().(mobileproxy.Proxy)
dialerObject := (*cgo.Handle)(dialer).Value().(mobileproxy.StreamDialer)

proxyObject.AddURLProxy(C.GoString(url), &dialerObject)
}

//export StopProxy
func StopProxy(proxy ProxyPtr, timeoutSeconds C.uint) {
proxyObject := (*cgo.Handle)(proxy).Value().(mobileproxy.Proxy)

proxyObject.Stop(int(timeoutSeconds))
}

//export DeleteStreamDialer
func DeleteStreamDialer(dialer StreamDialerPtr) {
(*cgo.Handle)(dialer).Delete()
}

//export DeleteProxy
func DeleteProxy(proxy ProxyPtr) {
(*cgo.Handle)(proxy).Delete()
}

func main() {
// We need the main function to make possible
// CGO compiler to compile the package as C shared library
}
Loading