@@ -3,13 +3,17 @@ package caddydns01proxy
33import (
44 "fmt"
55 "net/http"
6+ "time"
67
78 "github.com/caddyserver/caddy/v2"
89 "github.com/caddyserver/caddy/v2/caddyconfig"
910 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
1011 "github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth"
12+ "github.com/caddyserver/certmagic"
13+ "github.com/libdns/libdns"
1114 "github.com/liujed/caddy-dns01proxy/jsonutil"
1215 "github.com/liujed/goutil/optionals"
16+ "go.uber.org/zap"
1317)
1418
1519func init () {
@@ -32,6 +36,8 @@ type Handler struct {
3236 // Identifies the domains at which each client is allowed to answer DNS-01
3337 // challenges. Derived from [AccountsRaw].
3438 ClientRegistry ClientRegistry `json:"-"`
39+
40+ logger * zap.Logger
3541}
3642
3743var _ caddy.Module = (* Handler )(nil )
@@ -53,6 +59,8 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
5359}
5460
5561func (h * Handler ) Provision (ctx caddy.Context ) error {
62+ h .logger = ctx .Logger ()
63+
5664 // Provision DNS.
5765 err := h .DNS .Provision (ctx )
5866 if err != nil {
@@ -152,7 +160,79 @@ func (h *Handler) handleDNSRequest(
152160 req * http.Request ,
153161 reqBody RequestBody ,
154162 ) (httpStatus int , respBody optionals.Optional [ResponseBody ], err error ) {
155- // TODO
156- return http .StatusInternalServerError , optionals .None [ResponseBody ](), nil
163+ // Check that the user gave a valid request body.
164+ if ! reqBody .IsValid () {
165+ return http .StatusBadRequest , optionals .None [ResponseBody ](), nil
166+ }
167+
168+ // Log the challenge domain that appears in the request.
169+ addLogField (req , zap .String ("domain" , reqBody .ChallengeFQDN ))
170+
171+ // Check that the user is authorized for the challenge domain in the
172+ // request.
173+ denyReasonOpt , err := h .ClientRegistry .AuthorizeUserChallengeDomain (
174+ req ,
175+ reqBody .ChallengeFQDN ,
176+ )
177+ if err != nil {
178+ return 0 , optionals .None [ResponseBody ](),
179+ fmt .Errorf ("unable to authorize user for requested domain: %w" , err )
180+ }
181+ if denyReason , denied := denyReasonOpt .Get (); denied {
182+ addLogField (req , zap .String (logAuthorizationFailure , string (denyReason )))
183+ return http .StatusForbidden , optionals .None [ResponseBody ](), nil
184+ }
185+
186+ // Figure out the challenge domain's DNS zone.
187+ zone , err := certmagic .FindZoneByFQDN (
188+ req .Context (),
189+ h .logger ,
190+ reqBody .ChallengeFQDN ,
191+ certmagic .RecursiveNameservers (h .DNS .Resolvers ),
192+ )
193+ if err != nil {
194+ return 0 , optionals .None [ResponseBody ](),
195+ fmt .Errorf (
196+ "unable to find DNS zone for %q: %w" ,
197+ reqBody .ChallengeFQDN ,
198+ err ,
199+ )
200+ }
201+
202+ // Build the DNS record to create/delete.
203+ ttl := time .Duration (h .DNS .TTL .GetOrDefault (0 ))
204+ if mode == hmCleanup {
205+ ttl = 0
206+ }
207+ records := []libdns.Record {
208+ libdns.TXT {
209+ Name : libdns .RelativeName (reqBody .ChallengeFQDN , zone ),
210+ TTL : ttl ,
211+ Text : `"` + reqBody .Value + `"` ,
212+ },
213+ }
214+
215+ switch mode {
216+ case hmPresent :
217+ // Create the DNS record.
218+ _ , err = h .DNS .Provider .AppendRecords (req .Context (), zone , records )
219+ if err != nil {
220+ return 0 , optionals .None [ResponseBody ](),
221+ fmt .Errorf ("error creating DNS record: %w" , err )
222+ }
223+ return http .StatusOK , optionals .Some (reqBody ), nil
224+
225+ case hmCleanup :
226+ // Delete the DNS record.
227+ _ , err = h .DNS .Provider .DeleteRecords (req .Context (), zone , records )
228+ if err != nil {
229+ return 0 , optionals .None [ResponseBody ](),
230+ fmt .Errorf ("error deleting DNS record: %w" , err )
231+ }
232+ return http .StatusOK , optionals .Some (reqBody ), nil
233+ }
234+
235+ return 0 , optionals .None [ResponseBody ](),
236+ fmt .Errorf ("unknown handler mode: %q" , mode )
157237 }
158238}
0 commit comments