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

.,ruby: enable accept to be called as TsnetAccept & fix gem #24

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 ruby/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GEM
rake

PLATFORMS
arm64-darwin-22
x86_64-linux-gnu

DEPENDENCIES
Expand Down
14 changes: 8 additions & 6 deletions ruby/lib/tailscale.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
require 'tailscale/version'
require 'ffi'
require 'rbconfig'
require 'base64'
require 'net/http'
require 'json'

# Tailscale provides an embedded tailscale network interface for ruby programs.
class Tailscale
Expand Down Expand Up @@ -36,8 +39,7 @@ module Libtailscale
attach_function :TsnetSetLogFD, [:int, :int], :int
attach_function :TsnetDial, [:int, :string, :string, :pointer], :int, blocking: true
attach_function :TsnetListen, [:int, :string, :string, :pointer], :int
attach_function :close, [:int], :int
attach_function :tailscale_accept, [:int, :pointer], :int, blocking: true
attach_function :TsnetAccept, [:int, :pointer], :int, blocking: true
attach_function :TsnetErrmsg, [:int, :pointer, :size_t], :int
attach_function :TsnetLoopback, [:int, :pointer, :size_t, :pointer, :pointer], :int
end
Expand Down Expand Up @@ -82,6 +84,7 @@ def initialize(ts, listener)
# write.
def accept
@ts.assert_open
IO.select [IO.for_fd(@listener)]
conn = FFI::MemoryPointer.new(:int)
Error.check @ts, Libtailscale::TsnetAccept(@listener, conn)
IO::new conn.read_int
Expand All @@ -90,7 +93,7 @@ def accept
# Close the listener.
def close
@ts.assert_open
Error.check @ts, Libtailscale::close(@listener)
IO.for_fd(@listener).close
end
end

Expand Down Expand Up @@ -225,9 +228,8 @@ def set_log_fd(log_fd)
end

# Dial a network address. +network+ is one of "tcp" or "udp". +addr+ is the
# remote address to connect to, and +local_addr+ is the local address to
# bind to. This method blocks until the connection is established.
def dial(network, addr, local_addr)
# remote address to connect to. This method blocks until the connection is established.
def dial(network, addr)
assert_open
conn = FFI::MemoryPointer.new(:int)
Error.check self, Libtailscale::TsnetDial(@t, network, addr, conn)
Expand Down
19 changes: 18 additions & 1 deletion ruby/test/tailscale/test_tailscale.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,28 @@ def test_dial_sorta_works
# TODO: make a more useful test when we can make a server to connect to.
ts = newts
ts.start
c = ts.dial "udp", "100.100.100.100:53", ""
c = ts.dial "udp", "100.100.100.100:53"
c.close
ts.close
end

# Requires a solution to be logged in:
# def test_listen_accept_dial_close
# ts = newts
# ts.up
# hn = ts.local_api.status["Self"]["HostName"]
# s = ts.listen "tcp", ":1999"
# c = ts.dial "tcp", "#{hn}:1999"
# ss = s.accept
# c.write "hello"
# assert_equal "hello", ss.read(5)
# ss.write "world"
# assert_equal "world", c.read(5)
# ss.close
# c.close
# ts.close
# end

def newts
t = Tailscale::new
unless ENV['VERBOSE']
Expand Down
23 changes: 2 additions & 21 deletions tailscale.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extern int TsnetSetControlURL(int sd, char* str);
extern int TsnetSetEphemeral(int sd, int ephemeral);
extern int TsnetSetLogFD(int sd, int fd);
extern int TsnetListen(int sd, char* net, char* addr, int* listenerOut);
extern int TsnetAccept(int ld, int* connOut);
extern int TsnetLoopback(int sd, char* addrOut, size_t addrLen, char* proxyOut, char* localOut);

tailscale tailscale_new() {
Expand Down Expand Up @@ -47,27 +48,7 @@ int tailscale_listen(tailscale sd, const char* network, const char* addr, tailsc
}

int tailscale_accept(tailscale_listener ld, tailscale_conn* conn_out) {
struct msghdr msg = {0};

char mbuf[256];
struct iovec io = { .iov_base = mbuf, .iov_len = sizeof(mbuf) };
msg.msg_iov = &io;
msg.msg_iovlen = 1;

char cbuf[256];
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);

if (recvmsg(ld, &msg, 0) == -1) {
return -1;
}

struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
unsigned char* data = CMSG_DATA(cmsg);

int fd = *(int*)data;
*conn_out = fd;
return 0;
return TsnetAccept(ld, (int*)conn_out);
}

int tailscale_set_dir(tailscale sd, const char* dir) {
Expand Down
36 changes: 36 additions & 0 deletions tailscale.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"syscall"
"unsafe"

"golang.org/x/sys/unix"
"tailscale.com/hostinfo"
"tailscale.com/tsnet"
"tailscale.com/types/logger"
Expand Down Expand Up @@ -254,6 +255,41 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int {
return 0
}

//export TsnetAccept
func TsnetAccept(listenerFd C.int, connOut *C.int) C.int {
listeners.mu.Lock()
ln := listeners.m[listenerFd]
listeners.mu.Unlock()

if ln == nil {
return C.EBADF
}

buf := make([]byte, unix.CmsgLen(int(unsafe.Sizeof((C.int)(0)))))
_, oobn, _, _, err := syscall.Recvmsg(int(listenerFd), nil, buf, 0)
if err != nil {
return ln.s.recErr(err)
}

scms, err := syscall.ParseSocketControlMessage(buf[:oobn])
if err != nil {
return ln.s.recErr(err)
}
if len(scms) != 1 {
return ln.s.recErr(fmt.Errorf("libtailscale: got %d control messages, want 1", len(scms)))
}
fds, err := syscall.ParseUnixRights(&scms[0])
if err != nil {
return ln.s.recErr(err)
}
if len(fds) != 1 {
return ln.s.recErr(fmt.Errorf("libtailscale: got %d FDs, want 1", len(fds)))
}
*connOut = (C.int)(fds[0])

return 0
}

func newConn(s *server, netConn net.Conn, connOut *C.int) error {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
if err != nil {
Expand Down