Skip to content
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
2 changes: 1 addition & 1 deletion python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Tailscale Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause

cmake_minimum_required(VERSION 3.4...3.18)
cmake_minimum_required(VERSION 3.5...3.18)
project(tailscale)

add_subdirectory(pybind11)
Expand Down
1 change: 0 additions & 1 deletion python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

build:
@git clone https://github.com/pybind/pybind11 || true
cd pybind11 && git checkout 3cc7e4258c15a6a19ba5e0b62a220b1a6196d4eb
cd .. && go build -buildmode=c-archive -o python/libtailscale.a github.com/tailscale/libtailscale
pip install .

Expand Down
55 changes: 30 additions & 25 deletions python/examples/echo.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
# Copyright (c) Tailscale Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause
# TODO(shayne): proper select/poll/epoll + os.set_blocking(conn, False)

import os
import select
import signal
import sys
from tailscale import TSNet

def handler(conn):
while True:
r, _, _ = select.select([conn], [], [], 10)
if not conn in r:
os._exit(0)
data = os.read(conn, 2048)
print(data.decode(), end="")
"""Handle a single connection - echo all received data."""
try:
while True:
data = conn.read(2048)
if not data: # Connection closed
break
try:
print(data.decode('utf-8'), end="")
except UnicodeDecodeError:
print(data.decode('utf-8', errors='replace'), end="")
finally:
conn.close()


def main():
procs = []
def shutdown(signum, frame):
print("\nShutting down...")
sys.exit(0)

ts = TSNet(ephemeral=True)
ts.up()
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)

ln = ts.listen("tcp", ":1999")
while True:
while procs:
pid, exit_code = os.waitpid(-1, os.WNOHANG)
if pid == 0:
break
procs.remove(pid)
# Get auth key from environment
# If not provided, library outputs an auth URL
authkey = os.environ.get('TS_AUTHKEY')

conn = ln.accept()
pid = os.fork()
if pid == 0:
return handler(conn)
procs.append(pid)
with TSNet(ephemeral=True, authkey=authkey) as ts:
ts.up()

ln.close()
ts.close()
with ts.listen("tcp", ":1999") as ln:
print("Listening on :1999")
while True:
conn = ln.accept()
handler(conn)


if __name__ == "__main__":
Expand Down
47 changes: 37 additions & 10 deletions python/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,39 @@

#include <pybind11/pybind11.h>
#include "libtailscale.h"
#include <sys/socket.h>
#include <unistd.h>

#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)

namespace py = pybind11;

// tailscale_accept
static int accept_connection(int ld, int* 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;
}

PYBIND11_MODULE(_tailscale, m) {
m.doc() = R"pbdoc(
Embedded Tailscale
Expand Down Expand Up @@ -37,47 +64,47 @@ PYBIND11_MODULE(_tailscale, m) {
)pbdoc");

m.def("err_msg", &TsnetErrmsg, R"pbdoc(

Get error message for a server
)pbdoc");

m.def("listen", [](int sd, char* network, char* addr) { int listenerOut; int rv = TsnetListen(sd, network, addr, &listenerOut); return std::make_tuple(listenerOut, rv); }, R"pbdoc(
Listen on a given protocol and port
)pbdoc");

m.def("accept", [](int ld) { int connOut; int rv = TsnetAccept(ld, &connOut); return std::make_tuple(connOut, rv);}, R"pbdoc(
m.def("accept", [](int ld) { int connOut; int rv = accept_connection(ld, &connOut); return std::make_tuple(connOut, rv);}, R"pbdoc(
Accept a given listener and connection
)pbdoc");

m.def("dial", &TsnetDial, R"pbdoc(

m.def("dial", [](int sd, char* network, char* addr) { int connOut; int rv = TsnetDial(sd, network, addr, &connOut); return std::make_tuple(connOut, rv); }, R"pbdoc(
Dial a connection on the tailnet
)pbdoc");

m.def("set_dir", &TsnetSetDir, R"pbdoc(

Set the state directory
)pbdoc");

m.def("set_hostname", &TsnetSetHostname, R"pbdoc(

Set the hostname
)pbdoc");

m.def("set_authkey", &TsnetSetAuthKey, R"pbdoc(

Set the auth key
)pbdoc");

m.def("set_control_url", &TsnetSetControlURL, R"pbdoc(

Set the control URL
)pbdoc");

m.def("set_ephemeral", &TsnetSetEphemeral, R"pbdoc(
Set the given tsnet server to be an ephemeral node.
)pbdoc");

m.def("set_log_fd", &TsnetSetLogFD, R"pbdoc(

Set the log file descriptor
)pbdoc");

m.def("loopback", &TsnetLoopback, R"pbdoc(

Start a loopback server
)pbdoc");

#ifdef VERSION_INFO
Expand Down
Loading