You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale-android/libtailscale/libtailscale.go

131 lines
2.8 KiB
Go

package libtailscale
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
"tailscale.com/net/tsdial"
"tailscale.com/tsd"
"tailscale.com/types/logid"
"tailscale.com/wgengine"
"tailscale.com/wgengine/router"
)
type Request struct {
Method string
Path string
Headers map[string][]string
Body []byte
}
type Response struct {
StatusCode int
Headers map[string][]string
bodyWriter *bytes.Buffer
}
func (resp *Response) Body() []byte {
return resp.bodyWriter.Bytes()
}
type Tailscale interface {
ProcessRequest(req *Request) *Response
}
type tailscale struct {
backend *ipnlocal.LocalBackend
localapi *localapi.Handler
}
func NewTailscale() (Tailscale, error) {
logf := log.Printf
var sys tsd.System
var logID logid.PrivateID
logID.UnmarshalText([]byte("dead0000dead0000dead0000dead0000dead0000dead0000dead0000dead0000"))
netMon, err := netmon.New(logf)
if err != nil {
log.Printf("netmon.New: %w", err)
}
dialer := new(tsdial.Dialer)
cb := &router.CallbackRouter{
SetBoth: func(rcfg *router.Config, dcfg *dns.OSConfig) error { return nil },
SplitDNS: false,
GetBaseConfigFunc: nil,
}
engine, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
Tun: nil,
Router: cb,
DNS: cb,
Dialer: dialer,
SetSubsystem: sys.Set,
NetMon: netMon,
})
if err != nil {
return nil, fmt.Errorf("runBackend: NewUserspaceEngine: %v", err)
}
sys.Set(engine)
backend, err := ipnlocal.NewLocalBackend(logf, logID.Public(), &sys, 0)
if err != nil {
return nil, err
}
api := localapi.NewHandler(backend, logf, netMon, logID.Public())
return &tailscale{
backend: backend,
localapi: api,
}, nil
}
func (t *tailscale) ProcessRequest(req *Request) (resp *Response) {
defer func() {
if p := recover(); p != nil {
resp = &Response{
StatusCode: http.StatusInternalServerError,
}
fmt.Fprintf(resp, "%s", p)
}
}()
resp = &Response{
StatusCode: http.StatusOK,
Headers: make(map[string][]string),
bodyWriter: &bytes.Buffer{},
}
t.localapi.ServeHTTP(resp, req.AsHTTPRequest())
return
}
func (req *Request) AsHTTPRequest() *http.Request {
result, _ := http.NewRequest(req.Method, fmt.Sprintf("http://server/%v", req.Path), nil)
if req.Body != nil {
result.Body = io.NopCloser(bytes.NewReader(req.Body))
}
if req.Headers != nil {
for name, values := range req.Headers {
for _, value := range values {
result.Header.Add(name, value)
}
}
}
return result
}
func (resp *Response) Header() http.Header {
return http.Header(resp.Headers)
}
func (resp *Response) WriteHeader(statusCode int) {
resp.StatusCode = statusCode
}
func (resp *Response) Write(p []byte) (int, error) {
return resp.bodyWriter.Write(p)
}