mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-13 14:43:19 +00:00
ipn/{ipnlocal,localapi}, control/controlclient: add SetDNS localapi
Updates #1235 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
@@ -2577,6 +2577,42 @@ func (b *LocalBackend) FileTargets() ([]*apitype.FileTarget, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SetDNS adds a DNS record for the given domain name & TXT record
|
||||
// value.
|
||||
//
|
||||
// It's meant for use with dns-01 ACME (LetsEncrypt) challenges.
|
||||
//
|
||||
// This is the low-level interface. Other layers will provide more
|
||||
// friendly options to get HTTPS certs.
|
||||
func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error {
|
||||
req := &tailcfg.SetDNSRequest{
|
||||
Version: 1,
|
||||
Type: "TXT",
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
cc := b.cc
|
||||
if prefs := b.prefs; prefs != nil {
|
||||
req.NodeKey = tailcfg.NodeKey(prefs.Persist.PrivateNodeKey.Public())
|
||||
}
|
||||
b.mu.Unlock()
|
||||
if cc == nil {
|
||||
return errors.New("not connected")
|
||||
}
|
||||
if req.NodeKey.IsZero() {
|
||||
return errors.New("no nodekey")
|
||||
}
|
||||
if name == "" {
|
||||
return errors.New("missing 'name'")
|
||||
}
|
||||
if value == "" {
|
||||
return errors.New("missing 'value'")
|
||||
}
|
||||
return cc.SetDNS(ctx, req)
|
||||
}
|
||||
|
||||
func (b *LocalBackend) registerIncomingFile(inf *incomingFile, active bool) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
@@ -248,6 +248,10 @@ func (cc *mockControl) UpdateEndpoints(localPort uint16, endpoints []tailcfg.End
|
||||
cc.called("UpdateEndpoints")
|
||||
}
|
||||
|
||||
func (*mockControl) SetDNS(context.Context, *tailcfg.SetDNSRequest) error {
|
||||
panic("unexpected SetDNS call")
|
||||
}
|
||||
|
||||
// A very precise test of the sequence of function calls generated by
|
||||
// ipnlocal.Local into its controlclient instance, and the events it
|
||||
// produces upstream into the UI.
|
||||
|
@@ -100,6 +100,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.serveBugReport(w, r)
|
||||
case "/localapi/v0/file-targets":
|
||||
h.serveFileTargets(w, r)
|
||||
case "/localapi/v0/set-dns":
|
||||
h.serveSetDNS(w, r)
|
||||
case "/":
|
||||
io.WriteString(w, "tailscaled\n")
|
||||
default:
|
||||
@@ -382,6 +384,25 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
|
||||
rp.ServeHTTP(w, outReq)
|
||||
}
|
||||
|
||||
func (h *Handler) serveSetDNS(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitWrite {
|
||||
http.Error(w, "access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "want POST", 400)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
err := h.b.SetDNS(ctx, r.FormValue("name"), r.FormValue("value"))
|
||||
if err != nil {
|
||||
writeErrorJSON(w, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(struct{}{})
|
||||
}
|
||||
|
||||
var dialPeerTransportOnce struct {
|
||||
sync.Once
|
||||
v *http.Transport
|
||||
|
Reference in New Issue
Block a user