package rimuhosting import ( "encoding/xml" "errors" "io/ioutil" "net/http" "net/url" "regexp" querystring "github.com/google/go-querystring/query" ) // Base URL for the RimuHosting DNS services. const ( DefaultZonomiBaseURL = "https://zonomi.com/app/dns/dyndns.jsp" DefaultRimuHostingBaseURL = "https://rimuhosting.com/app/dns/dyndns.jsp" ) // Action names. const ( SetAction = "SET" QueryAction = "QUERY" DeleteAction = "DELETE" ) // Client the RimuHosting/Zonomi client. type Client struct { apiKey string HTTPClient *http.Client BaseURL string } // NewClient Creates a RimuHosting/Zonomi client. func NewClient(apiKey string) *Client { return &Client{ HTTPClient: http.DefaultClient, BaseURL: DefaultZonomiBaseURL, apiKey: apiKey, } } // FindTXTRecords Finds TXT records. // ex: // - https://zonomi.com/app/dns/dyndns.jsp?action=QUERY&name=example.com&api_key=apikeyvaluehere // - https://zonomi.com/app/dns/dyndns.jsp?action=QUERY&name=**.example.com&api_key=apikeyvaluehere func (c Client) FindTXTRecords(domain string) ([]Record, error) { action := ActionParameter{ Action: QueryAction, Name: domain, Type: "TXT", } resp, err := c.DoActions(action) if err != nil { return nil, err } return resp.Actions.Action.Records, nil } // DoActions performs actions. func (c Client) DoActions(actions ...ActionParameter) (*DNSAPIResult, error) { if len(actions) == 0 { return nil, errors.New("no action") } resp := &DNSAPIResult{} if len(actions) == 1 { action := actionParameter{ ActionParameter: actions[0], APIKey: c.apiKey, } err := c.do(action, resp) if err != nil { return nil, err } return resp, nil } multi := c.toMultiParameters(actions) err := c.do(multi, resp) if err != nil { return nil, err } return resp, nil } func (c Client) toMultiParameters(params []ActionParameter) multiActionParameter { multi := multiActionParameter{ APIKey: c.apiKey, } for _, parameters := range params { multi.Action = append(multi.Action, parameters.Action) multi.Name = append(multi.Name, parameters.Name) multi.Type = append(multi.Type, parameters.Type) multi.Value = append(multi.Value, parameters.Value) multi.TTL = append(multi.TTL, parameters.TTL) } return multi } func (c Client) do(params interface{}, data interface{}) error { baseURL, err := url.Parse(c.BaseURL) if err != nil { return err } v, err := querystring.Values(params) if err != nil { return err } exp := regexp.MustCompile(`(%5B)(%5D)(\d+)=`) baseURL.RawQuery = exp.ReplaceAllString(v.Encode(), "${1}${3}${2}=") req, err := http.NewRequest(http.MethodGet, baseURL.String(), nil) if err != nil { return err } resp, err := c.HTTPClient.Do(req) if err != nil { return err } defer func() { _ = resp.Body.Close() }() all, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode/100 != 2 { r := APIError{} err = xml.Unmarshal(all, &r) if err != nil { return err } return r } if data != nil { err := xml.Unmarshal(all, data) if err != nil { return err } } return nil } // AddRecord helper to create an action to add a TXT record. func AddRecord(domain string, content string, ttl int) ActionParameter { return ActionParameter{ Action: SetAction, Name: domain, Type: "TXT", Value: content, TTL: ttl, } } // DeleteRecord helper to create an action to delete a TXT record. func DeleteRecord(domain string, content string) ActionParameter { return ActionParameter{ Action: DeleteAction, Name: domain, Type: "TXT", Value: content, } }