mirror of
https://github.com/Drop-OSS/libtailscale.git
synced 2026-01-30 20:55:18 +01:00
libtailscale: add tailscale_loopback_api function
Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:
committed by
David Crawshaw
parent
998fdc4759
commit
72c10539e2
@@ -19,6 +19,7 @@ extern int TsnetSetLogFD(int sd, int fd);
|
||||
extern int TsnetListen(int sd, char* net, char* addr, int* listenerOut);
|
||||
extern int TsnetListenerClose(int ld);
|
||||
extern int TsnetAccept(int ld, int* connOut);
|
||||
extern int TsnetLoopbackAPI(int sd, char* addrOut, size_t addrLen, char* credOut);
|
||||
|
||||
tailscale tailscale_new() {
|
||||
return TsnetNewServer();
|
||||
@@ -71,6 +72,10 @@ int tailscale_set_logfd(tailscale sd, int fd) {
|
||||
return TsnetSetLogFD(sd, fd);
|
||||
}
|
||||
|
||||
int tailscale_loopback_api(tailscale sd, char* addr_out, size_t addrlen, char cred_out[static 33]) {
|
||||
return TsnetLoopbackAPI(sd, addr_out, addrlen, cred_out);
|
||||
}
|
||||
|
||||
int tailscale_errmsg(tailscale sd, char* buf, size_t buflen) {
|
||||
return TsnetErrmsg(sd, buf, buflen);
|
||||
}
|
||||
|
||||
40
tailscale.go
40
tailscale.go
@@ -362,3 +362,43 @@ func TsnetSetLogFD(sd C.int, fd int) C.int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//export TsnetLoopbackAPI
|
||||
func TsnetLoopbackAPI(sd C.int, addrOut *C.char, addrLen C.size_t, credOut *C.char) C.int {
|
||||
// Panic here to ensure we always leave the out values NUL-terminated.
|
||||
if addrOut == nil {
|
||||
panic("loopback_api passed nil addr_out")
|
||||
} else if addrLen == 0 {
|
||||
panic("loopback_api passed addrlen of 0")
|
||||
}
|
||||
|
||||
// Start out NUL-termianted to cover error conditions.
|
||||
*addrOut = '\x00'
|
||||
*credOut = '\x00'
|
||||
|
||||
s, err := getServer(sd)
|
||||
if err != nil {
|
||||
return s.recErr(err)
|
||||
}
|
||||
addr, cred, err := s.s.LoopbackLocalAPI()
|
||||
if err != nil {
|
||||
return s.recErr(err)
|
||||
}
|
||||
if len(cred) != 32 {
|
||||
return s.recErr(fmt.Errorf("libtailscale: len(cred)=%d, want 32", len(cred)))
|
||||
}
|
||||
if len(addr)+1 > int(addrLen) {
|
||||
return s.recErr(fmt.Errorf("libtailscale: loopback addr of %d bytes is too long for addrlen %d", len(addr), addrLen))
|
||||
}
|
||||
out := unsafe.Slice((*byte)(unsafe.Pointer(addrOut)), addrLen)
|
||||
n := copy(out, addr)
|
||||
out[n] = '\x00'
|
||||
|
||||
// credOut is non-nil and 33 bytes long because
|
||||
// it's defined in C as char cred_out[static 33].
|
||||
out = unsafe.Slice((*byte)(unsafe.Pointer(credOut)), 33)
|
||||
copy(out, cred)
|
||||
out[32] = '\x00'
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
11
tailscale.h
11
tailscale.h
@@ -118,6 +118,17 @@ extern int tailscale_listener_close(tailscale_listener listener);
|
||||
// -1 - call tailscale_errmsg for details
|
||||
extern int tailscale_accept(tailscale_listener listener, tailscale_conn* conn_out);
|
||||
|
||||
// tailscale_loopback_api starts a LocalAPI listener on a loopback address.
|
||||
//
|
||||
// To make http requests to the LocalAPI:
|
||||
// 1. cred must be provided as the basic auth password
|
||||
// 2. the header "Sec-Tailscale: localapi" must be set
|
||||
//
|
||||
// The NUL-terminated ip:port address of the LocalAPI is written to addr_out.
|
||||
// The 32-byte basic auth password + NUL-terminator is written to cred_out.
|
||||
//
|
||||
// Returns zero on success or -1 on error, call tailscale_errmsg for details.
|
||||
extern int tailscale_loopback_api(tailscale sd, char* addr_out, size_t addrlen, char cred_out[static 33]);
|
||||
|
||||
// tailscale_errmsg writes the details of the last error to buf.
|
||||
//
|
||||
|
||||
@@ -17,9 +17,15 @@ char* tmps2;
|
||||
|
||||
char* control_url = 0;
|
||||
|
||||
int addrlen = 128;
|
||||
char* addr = NULL;
|
||||
char* cred = NULL;
|
||||
|
||||
int errlen = 512;
|
||||
char* err = NULL;
|
||||
|
||||
tailscale s1, s2;
|
||||
|
||||
int set_err(tailscale sd, char tag) {
|
||||
err[0] = tag;
|
||||
err[1] = ':';
|
||||
@@ -30,9 +36,11 @@ int set_err(tailscale sd, char tag) {
|
||||
|
||||
int test_conn() {
|
||||
err = calloc(errlen, 1);
|
||||
addr = calloc(addrlen, 1);
|
||||
cred = calloc(33, 1);
|
||||
int ret;
|
||||
|
||||
tailscale s1 = tailscale_new();
|
||||
s1 = tailscale_new();
|
||||
if ((ret = tailscale_set_control_url(s1, control_url)) != 0) {
|
||||
return set_err(s1, '0');
|
||||
}
|
||||
@@ -46,7 +54,7 @@ int test_conn() {
|
||||
return set_err(s1, '3');
|
||||
}
|
||||
|
||||
tailscale s2 = tailscale_new();
|
||||
s2 = tailscale_new();
|
||||
if ((ret = tailscale_set_control_url(s2, control_url)) != 0) {
|
||||
return set_err(s2, '4');
|
||||
}
|
||||
@@ -102,11 +110,20 @@ int test_conn() {
|
||||
if ((ret = tailscale_listener_close(ln)) != 0) {
|
||||
return set_err(s1, 'a');
|
||||
}
|
||||
if ((ret = tailscale_close(s1)) != 0) {
|
||||
|
||||
if ((ret = tailscale_loopback_api(s1, addr, addrlen, cred)) != 0) {
|
||||
return set_err(s1, 'b');
|
||||
}
|
||||
if ((ret = tailscale_close(s2)) != 0) {
|
||||
return set_err(s2, 'c');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_conn() {
|
||||
if (tailscale_close(s1) != 0) {
|
||||
return set_err(s1, 'd');
|
||||
}
|
||||
if (tailscale_close(s2) != 0) {
|
||||
return set_err(s2, 'e');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -114,6 +131,8 @@ int test_conn() {
|
||||
import "C"
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -161,4 +180,27 @@ func RunTestConn(t *testing.T) {
|
||||
if C.test_conn() != 0 {
|
||||
t.Fatal(C.GoString(C.err))
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+C.GoString(C.addr)+"/localapi/v0/status", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Sec-Tailscale", "localapi")
|
||||
req.SetBasicAuth("", C.GoString(C.cred))
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := io.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
t.Errorf("/status: %d: %s", res.StatusCode, b)
|
||||
}
|
||||
|
||||
if C.close_conn() != 0 {
|
||||
t.Fatal(C.GoString(C.err))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user