domains.go raw

   1  package desec
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"net/http"
   7  	"net/url"
   8  	"time"
   9  )
  10  
  11  // Domain a domain representation.
  12  type Domain struct {
  13  	Name       string      `json:"name,omitempty"`
  14  	MinimumTTL int         `json:"minimum_ttl,omitempty"`
  15  	Keys       []DomainKey `json:"keys,omitempty"`
  16  	Created    *time.Time  `json:"created,omitempty"`
  17  	Published  *time.Time  `json:"published,omitempty"`
  18  	Touched    *time.Time  `json:"touched,omitempty"`
  19  }
  20  
  21  // DomainKey a domain key representation.
  22  type DomainKey struct {
  23  	DNSKey  string   `json:"dnskey,omitempty"`
  24  	DS      []string `json:"ds,omitempty"`
  25  	Flags   int      `json:"flags,omitempty"`
  26  	KeyType string   `json:"keytype,omitempty"`
  27  }
  28  
  29  // DomainsService handles communication with the domain related methods of the deSEC API.
  30  //
  31  // https://desec.readthedocs.io/en/latest/dns/domains.html
  32  type DomainsService struct {
  33  	client *Client
  34  }
  35  
  36  // Create creating a domain.
  37  // https://desec.readthedocs.io/en/latest/dns/domains.html#creating-a-domain
  38  func (s *DomainsService) Create(ctx context.Context, domainName string) (*Domain, error) {
  39  	endpoint, err := s.client.createEndpoint("domains")
  40  	if err != nil {
  41  		return nil, fmt.Errorf("failed to create endpoint: %w", err)
  42  	}
  43  
  44  	req, err := s.client.newRequest(ctx, http.MethodPost, endpoint, Domain{Name: domainName})
  45  	if err != nil {
  46  		return nil, err
  47  	}
  48  
  49  	resp, err := s.client.httpClient.Do(req)
  50  	if err != nil {
  51  		return nil, fmt.Errorf("failed to call API: %w", err)
  52  	}
  53  
  54  	defer func() { _ = resp.Body.Close() }()
  55  
  56  	if resp.StatusCode != http.StatusCreated {
  57  		return nil, handleError(resp)
  58  	}
  59  
  60  	var domain Domain
  61  
  62  	err = handleResponse(resp, &domain)
  63  	if err != nil {
  64  		return nil, err
  65  	}
  66  
  67  	return &domain, nil
  68  }
  69  
  70  // GetAll listing domains.
  71  // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
  72  func (s *DomainsService) GetAll(ctx context.Context) ([]Domain, error) {
  73  	domains, _, err := s.GetAllPaginated(ctx, "")
  74  	if err != nil {
  75  		return nil, err
  76  	}
  77  
  78  	return domains, nil
  79  }
  80  
  81  // GetAllPaginated listing domains.
  82  // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
  83  func (s *DomainsService) GetAllPaginated(ctx context.Context, cursor string) ([]Domain, *Cursors, error) {
  84  	queryValues := url.Values{}
  85  	queryValues.Set("cursor", cursor)
  86  
  87  	return s.getAll(ctx, queryValues)
  88  }
  89  
  90  // GetResponsible returns the responsible domain for a given DNS query name.
  91  // https://desec.readthedocs.io/en/latest/dns/domains.html#identifying-the-responsible-domain-for-a-dns-name
  92  func (s *DomainsService) GetResponsible(ctx context.Context, domainName string) (*Domain, error) {
  93  	queryValues := url.Values{}
  94  	queryValues.Set("owns_qname", domainName)
  95  
  96  	domains, _, err := s.getAll(ctx, queryValues)
  97  	if err != nil {
  98  		return nil, err
  99  	}
 100  
 101  	if len(domains) == 0 {
 102  		return nil, &NotFoundError{Detail: "no responsible domain found"}
 103  	}
 104  
 105  	return &domains[0], nil
 106  }
 107  
 108  // getAll listing domains.
 109  // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
 110  func (s *DomainsService) getAll(ctx context.Context, query url.Values) ([]Domain, *Cursors, error) {
 111  	endpoint, err := s.client.createEndpoint("domains")
 112  	if err != nil {
 113  		return nil, nil, fmt.Errorf("failed to create endpoint: %w", err)
 114  	}
 115  
 116  	req, err := s.client.newRequest(ctx, http.MethodGet, endpoint, nil)
 117  	if err != nil {
 118  		return nil, nil, err
 119  	}
 120  
 121  	if len(query) > 0 {
 122  		req.URL.RawQuery = query.Encode()
 123  	}
 124  
 125  	resp, err := s.client.httpClient.Do(req)
 126  	if err != nil {
 127  		return nil, nil, fmt.Errorf("failed to call API: %w", err)
 128  	}
 129  
 130  	defer func() { _ = resp.Body.Close() }()
 131  
 132  	if resp.StatusCode != http.StatusOK {
 133  		return nil, nil, handleError(resp)
 134  	}
 135  
 136  	cursors, err := parseCursor(resp.Header)
 137  	if err != nil {
 138  		return nil, nil, err
 139  	}
 140  
 141  	var domains []Domain
 142  
 143  	err = handleResponse(resp, &domains)
 144  	if err != nil {
 145  		return nil, nil, err
 146  	}
 147  
 148  	return domains, cursors, nil
 149  }
 150  
 151  // Get retrieving a specific domain.
 152  // https://desec.readthedocs.io/en/latest/dns/domains.html#retrieving-a-specific-domain
 153  func (s *DomainsService) Get(ctx context.Context, domainName string) (*Domain, error) {
 154  	endpoint, err := s.client.createEndpoint("domains", domainName)
 155  	if err != nil {
 156  		return nil, fmt.Errorf("failed to create endpoint: %w", err)
 157  	}
 158  
 159  	req, err := s.client.newRequest(ctx, http.MethodGet, endpoint, nil)
 160  	if err != nil {
 161  		return nil, err
 162  	}
 163  
 164  	resp, err := s.client.httpClient.Do(req)
 165  	if err != nil {
 166  		return nil, fmt.Errorf("failed to call API: %w", err)
 167  	}
 168  
 169  	defer func() { _ = resp.Body.Close() }()
 170  
 171  	if resp.StatusCode != http.StatusOK {
 172  		return nil, handleError(resp)
 173  	}
 174  
 175  	var domains Domain
 176  
 177  	err = handleResponse(resp, &domains)
 178  	if err != nil {
 179  		return nil, err
 180  	}
 181  
 182  	return &domains, nil
 183  }
 184  
 185  // Delete deleting a domain.
 186  // https://desec.readthedocs.io/en/latest/dns/domains.html#deleting-a-domain
 187  func (s *DomainsService) Delete(ctx context.Context, domainName string) error {
 188  	endpoint, err := s.client.createEndpoint("domains", domainName)
 189  	if err != nil {
 190  		return fmt.Errorf("failed to create endpoint: %w", err)
 191  	}
 192  
 193  	req, err := s.client.newRequest(ctx, http.MethodDelete, endpoint, nil)
 194  	if err != nil {
 195  		return err
 196  	}
 197  
 198  	resp, err := s.client.httpClient.Do(req)
 199  	if err != nil {
 200  		return fmt.Errorf("failed to call API: %w", err)
 201  	}
 202  
 203  	defer func() { _ = resp.Body.Close() }()
 204  
 205  	if resp.StatusCode != http.StatusNoContent {
 206  		return handleError(resp)
 207  	}
 208  
 209  	return nil
 210  }
 211