Compare commits
9 Commits
v0.0.6-deb
...
main
Author | SHA1 | Date | |
---|---|---|---|
37ae1e217d | |||
bdb2539eba | |||
4a9e8f758e | |||
659918983b | |||
62704fac46 | |||
6a1c07d3f5 | |||
7270f4c91c | |||
19b44c14d9 | |||
05e035925f |
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
### dnsexit-manager library
|
||||||
|
|
||||||
|
This is a part of my handmade DNS management project.
|
||||||
|
It's a driver that's create ability to manage your DNSexit subdomains via API.
|
||||||
|
|
||||||
|
#### How it works?
|
||||||
|
|
||||||
|
This driver relies on `dns-manager` library which describes all drivers API you may use.
|
||||||
|
The architecture is as simple as ABC: the drivers must implement all methods from `dns-manager`
|
||||||
|
library to be compatible with it.
|
||||||
|
|
||||||
|
It means anyone else can extend this project by creating own driver for their DNS providers.
|
||||||
|
|
||||||
|
#### To-Do list
|
||||||
|
- [ ] Better error handling
|
||||||
|
- [ ] Docs for the project
|
||||||
|
- [ ] Make an example of usage
|
@ -3,30 +3,30 @@ package dnsexit_manager
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
dns "git.uoc.run.place/OxFF/dns-manager"
|
dns "git.jolbec.icu/OxFF/dns-manager"
|
||||||
)
|
)
|
||||||
|
|
||||||
const API_BASE_URL = "https://api.dnsexit.com/dns/"
|
const API_BASE_URL = "https://api.dnsexit.com/dns/"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DOMAIN_NAME string
|
DomainName string
|
||||||
API_KEY string
|
API_Key string
|
||||||
API_URL string
|
API_URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
Locker sync.Mutex
|
Locker sync.Mutex
|
||||||
API_KEY string
|
API_Key string
|
||||||
API_URL string
|
API_URL string
|
||||||
Domain string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type dnsexitDTO struct {
|
type dnsexitRequestDTO struct {
|
||||||
ApiKey string `json:"apikey"`
|
ApiKey string `json:"apikey"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Add *dns.Record `json:"add,omitempty"`
|
Add *dns.Record `json:"add,omitempty"`
|
||||||
@ -34,63 +34,74 @@ type dnsexitDTO struct {
|
|||||||
Delete *dns.Record `json:"delete,omitempty"`
|
Delete *dns.Record `json:"delete,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertInterfaceSliceToStruct(slice []interface{}) *[]dns.Domain {
|
type dnsexitResponseDTO struct {
|
||||||
var domains []dns.Domain
|
Code int
|
||||||
|
Message string
|
||||||
for _, domain := range slice {
|
|
||||||
fmt.Println(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &domains
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseError(body *[]byte) error {
|
||||||
|
var response dnsexitResponseDTO
|
||||||
|
|
||||||
func (c *client) AddRecord(rec *dns.Record) (error, *dns.Response) {
|
if err := json.Unmarshal(*body, &response); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Code != 0 {
|
||||||
|
return errors.New(response.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) AddRecord(rec *dns.Record) (*dns.Response, error) {
|
||||||
c.Locker.Lock()
|
c.Locker.Lock()
|
||||||
defer c.Locker.Unlock()
|
defer c.Locker.Unlock()
|
||||||
|
|
||||||
request_body, err := json.Marshal(&dnsexitDTO{
|
request_body, err := json.Marshal(&dnsexitRequestDTO{
|
||||||
ApiKey: c.API_KEY,
|
ApiKey: c.API_Key,
|
||||||
Domain: c.Domain,
|
Domain: c.Domain,
|
||||||
Add: rec,
|
Add: rec,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(request_body))
|
response, err := http.Post(
|
||||||
|
|
||||||
resp, err := http.Post(
|
|
||||||
c.API_URL,
|
c.API_URL,
|
||||||
"application/json",
|
"application/json",
|
||||||
bytes.NewBuffer(request_body),
|
bytes.NewBuffer(request_body),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &dns.Response{Message: string(body)}
|
if err = parseError(&body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dns.Response{Message: string(body)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) DeleteRecord(rec *dns.Record) (error, *dns.Response) {
|
func (c *client) DeleteRecord(rec *dns.Record) (*dns.Response, error) {
|
||||||
c.Locker.Lock()
|
c.Locker.Lock()
|
||||||
defer c.Locker.Unlock()
|
defer c.Locker.Unlock()
|
||||||
|
|
||||||
request_body, err := json.Marshal(&dnsexitDTO{
|
request_body, err := json.Marshal(&dnsexitRequestDTO{
|
||||||
ApiKey: c.API_KEY,
|
ApiKey: c.API_Key,
|
||||||
Domain: c.Domain,
|
Domain: c.Domain,
|
||||||
Delete: rec,
|
Delete: rec,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Post(
|
resp, err := http.Post(
|
||||||
@ -98,30 +109,36 @@ func (c *client) DeleteRecord(rec *dns.Record) (error, *dns.Response) {
|
|||||||
"application/json",
|
"application/json",
|
||||||
bytes.NewBuffer(request_body),
|
bytes.NewBuffer(request_body),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &dns.Response{Message: string(body)}
|
if err = parseError(&body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dns.Response{Message: string(body)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) UpdateRecord(rec *dns.Record) (error, *dns.Response) {
|
func (c *client) UpdateRecord(rec *dns.Record) (*dns.Response, error) {
|
||||||
c.Locker.Lock()
|
c.Locker.Lock()
|
||||||
defer c.Locker.Unlock()
|
defer c.Locker.Unlock()
|
||||||
|
|
||||||
request_body, err := json.Marshal(&dnsexitDTO{
|
request_body, err := json.Marshal(&dnsexitRequestDTO{
|
||||||
ApiKey: c.API_KEY,
|
ApiKey: c.API_Key,
|
||||||
Domain: c.Domain,
|
Domain: c.Domain,
|
||||||
Update: rec,
|
Update: rec,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Post(
|
resp, err := http.Post(
|
||||||
@ -129,37 +146,42 @@ func (c *client) UpdateRecord(rec *dns.Record) (error, *dns.Response) {
|
|||||||
"application/json",
|
"application/json",
|
||||||
bytes.NewBuffer(request_body),
|
bytes.NewBuffer(request_body),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &dns.Response{Message: string(body)}
|
if err = parseError(&body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dns.Response{Message: string(body)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) GetRecords() (error, []*dns.Record) {
|
func (c *client) GetRecords() (*[]dns.Record, error) {
|
||||||
var subdomains = CreateSet()
|
var subdomains = NewRecordSet()
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
result_chan := make(chan []*dns.Record)
|
result_chan := make(chan []*dns.Record)
|
||||||
err_chan := make(chan error)
|
err_chan := make(chan error)
|
||||||
|
|
||||||
err, namesevers := c.getNSRecods()
|
nameservers, err := c.getNSRecods()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ns := range *namesevers {
|
for _, ns := range *nameservers {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func(ns string) {
|
go func(ns string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
err, records := find_dns_records(c.Domain, ns)
|
records, err := find_dns_records(c.Domain, ns)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err_chan <- err
|
err_chan <- err
|
||||||
@ -180,20 +202,20 @@ func (c *client) GetRecords() (error, []*dns.Record) {
|
|||||||
select {
|
select {
|
||||||
case records, ok := <-result_chan:
|
case records, ok := <-result_chan:
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, convertInterfaceSliceToStruct(subdomains.List())
|
return subdomains.List(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for record := range records {
|
for _, record := range records {
|
||||||
subdomains.Add(record)
|
subdomains.Add(record)
|
||||||
}
|
}
|
||||||
case _, _ = <-err_chan:
|
case _, _ = <-err_chan:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf Config) New() dns.Actions {
|
func (conf Config) New() (dns.Actions, error) {
|
||||||
var api_url string = API_BASE_URL
|
var api_url = API_BASE_URL
|
||||||
|
|
||||||
if len(conf.API_URL) > 5 {
|
if len(conf.API_URL) > 5 {
|
||||||
api_url = conf.API_URL
|
api_url = conf.API_URL
|
||||||
@ -201,8 +223,8 @@ func (conf Config) New() dns.Actions {
|
|||||||
|
|
||||||
return &client{
|
return &client{
|
||||||
Locker: sync.Mutex{},
|
Locker: sync.Mutex{},
|
||||||
API_KEY: conf.API_KEY,
|
API_Key: conf.API_Key,
|
||||||
API_URL: api_url,
|
API_URL: api_url,
|
||||||
Domain: conf.DOMAIN_NAME,
|
Domain: conf.DomainName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
21
go.mod
21
go.mod
@ -1,13 +1,16 @@
|
|||||||
module git.uoc.run.place/OxFF/dnsexit-manager
|
module git.jolbec.icu/OxFF/dnsexit-manager
|
||||||
|
|
||||||
go 1.23.4
|
go 1.23.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.uoc.run.place/OxFF/dns-manager v0.0.0-20250107205730-c4ab438a9fd5 // indirect
|
git.jolbec.icu/OxFF/dns-manager v0.0.5
|
||||||
github.com/miekg/dns v1.1.62 // indirect
|
github.com/miekg/dns v1.1.62
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
)
|
||||||
golang.org/x/net v0.27.0 // indirect
|
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
require (
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
)
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/tools v0.29.0 // indirect
|
||||||
|
)
|
||||||
|
44
set.go
44
set.go
@ -1,33 +1,35 @@
|
|||||||
package dnsexit_manager
|
package dnsexit_manager
|
||||||
|
|
||||||
|
import dns "git.jolbec.icu/OxFF/dns-manager"
|
||||||
|
|
||||||
type Set struct {
|
type RecordSet struct {
|
||||||
elements map[interface{}]struct{}
|
records map[dns.Record]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSet() *Set {
|
func NewRecordSet() *RecordSet {
|
||||||
return &Set{
|
return &RecordSet{
|
||||||
elements: make(map[interface{}]struct{}),
|
records: make(map[dns.Record]struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *Set) Add(value interface{}) {
|
func (s *RecordSet) Add(record *dns.Record) {
|
||||||
_, found := set.elements[value]
|
s.records[*record] = struct{}{}
|
||||||
|
|
||||||
if !found{
|
|
||||||
set.elements[value] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *Set) Delete(value interface{}) {
|
func (s *RecordSet) Remove(record dns.Record) {
|
||||||
delete(set.elements, value)
|
delete(s.records, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *Set) List() []interface{} {
|
func (s *RecordSet) Contains(record *dns.Record) bool {
|
||||||
keys := make([]interface{}, 0, len(set.elements))
|
_, exists := s.records[*record]
|
||||||
for key := range set.elements {
|
return exists
|
||||||
keys = append(keys, key)
|
}
|
||||||
}
|
|
||||||
|
func (s *RecordSet) List() *[]dns.Record {
|
||||||
return keys
|
records := make([]dns.Record, 0, len(s.records))
|
||||||
|
for record := range s.records {
|
||||||
|
records = append(records, record)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &records
|
||||||
}
|
}
|
||||||
|
38
utilities.go
38
utilities.go
@ -6,50 +6,50 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
dns "git.uoc.run.place/OxFF/dns-manager"
|
dns "git.jolbec.icu/OxFF/dns-manager"
|
||||||
dns_req "github.com/miekg/dns"
|
dns_req "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *client) getNSRecods() (error, *[]string) {
|
func (c *client) getNSRecods() (*[]string, error) {
|
||||||
var nameservers []string
|
var nameservers []string
|
||||||
|
|
||||||
ns, err := net.LookupNS(c.Domain)
|
ns, err := net.LookupNS(c.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range ns {
|
for _, v := range ns {
|
||||||
nameservers = append(nameservers, v.Host[:len(v.Host)-1]+":53")
|
nameservers = append(nameservers, v.Host[:len(v.Host)-1]+":53")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &nameservers
|
return &nameservers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_record(record dns_req.RR) (error, *dns.Record) {
|
func parse_record(record dns_req.RR) (*dns.Record, error) {
|
||||||
splitted := strings.SplitN(record.String(), "\t", -1)
|
split := strings.SplitN(record.String(), "\t", -1)
|
||||||
|
|
||||||
if len(splitted) == 5 {
|
if len(split) == 5 {
|
||||||
var parsed dns.Record
|
var parsed dns.Record
|
||||||
rec_type := dns.RecordType(splitted[3])
|
rec_type := dns.RecordType(split[3])
|
||||||
|
|
||||||
if !rec_type.Check() {
|
if !rec_type.Check() {
|
||||||
return errors.New("provided DNS type is not exist in the main library"), nil
|
return nil, errors.New("provided DNS type is not exist in the main library")
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed.Type = rec_type
|
parsed.Type = rec_type
|
||||||
ttl, _ := strconv.ParseUint(splitted[1], 10, 64)
|
ttl, _ := strconv.ParseUint(split[1], 10, 64)
|
||||||
|
|
||||||
parsed.Name = splitted[0]
|
parsed.Name = split[0]
|
||||||
parsed.Content = splitted[4]
|
parsed.Content = split[4]
|
||||||
parsed.TTL = uint(ttl)
|
parsed.TTL = uint(ttl)
|
||||||
|
|
||||||
return nil, &parsed
|
return &parsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("no meaningful record provided"), nil
|
return nil, errors.New("no meaningful record provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
func find_dns_records(domain string, nameserver string) (error, []*dns.Record) {
|
func find_dns_records(domain string, nameserver string) ([]*dns.Record, error) {
|
||||||
var subdomains []*dns.Record
|
var subdomains []*dns.Record
|
||||||
|
|
||||||
tr := new(dns_req.Transfer)
|
tr := new(dns_req.Transfer)
|
||||||
@ -58,21 +58,21 @@ func find_dns_records(domain string, nameserver string) (error, []*dns.Record) {
|
|||||||
|
|
||||||
client, err := tr.In(m, nameserver)
|
client, err := tr.In(m, nameserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to zone transfer in " + err.Error()), nil
|
return nil, errors.New("failed to zone transfer in " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for msg := range client {
|
for msg := range client {
|
||||||
if msg.Error != nil {
|
if msg.Error != nil {
|
||||||
return msg.Error, nil
|
return nil, msg.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range msg.RR {
|
for _, r := range msg.RR {
|
||||||
err, record := parse_record(r)
|
record, err := parse_record(r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
subdomains = append(subdomains, record)
|
subdomains = append(subdomains, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, subdomains
|
return subdomains, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user