Use cached indexes.
This commit is contained in:
parent
158d772727
commit
4923fb34f9
5 changed files with 56 additions and 36 deletions
src
|
@ -15,15 +15,14 @@ class DNSManager::Request
|
||||||
# "dnsmanager-last-connection" => JSON::Any.new Time.utc.to_s
|
# "dnsmanager-last-connection" => JSON::Any.new Time.utc.to_s
|
||||||
#}
|
#}
|
||||||
user_id = response.user.uid
|
user_id = response.user.uid
|
||||||
dnsmanagerd.storage.user_must_exist! user_id
|
|
||||||
|
|
||||||
accepted_domains = dnsmanagerd.configuration.accepted_domains.not_nil!
|
accepted_domains = dnsmanagerd.configuration.accepted_domains.not_nil!
|
||||||
|
|
||||||
# Limit the number of domains in this message.
|
# Limit the number of domains in this message.
|
||||||
# Pagination will be required beyond a hundred domains.
|
# Pagination will be required beyond a hundred domains.
|
||||||
user_domains = dnsmanagerd.storage.user_domains(user_id).[0..100]
|
user_domains = dnsmanagerd.storage.user_domains(user_id).[0..100]
|
||||||
|
|
||||||
perms = dnsmanagerd.check_permissions user_id, "*"
|
perms = dnsmanagerd.check_permissions user_id, "*"
|
||||||
|
|
||||||
Response::Logged.new (perms == AuthD::User::PermissionLevel::Admin), accepted_domains, user_domains
|
Response::Logged.new (perms == AuthD::User::PermissionLevel::Admin), accepted_domains, user_domains
|
||||||
else
|
else
|
||||||
Response::ErrorInvalidToken.new
|
Response::ErrorInvalidToken.new
|
||||||
|
|
|
@ -8,16 +8,16 @@ require "dodb"
|
||||||
|
|
||||||
class DNSManager::Storage
|
class DNSManager::Storage
|
||||||
getter domains : DODB::CachedDataBase(Domain)
|
getter domains : DODB::CachedDataBase(Domain)
|
||||||
getter domains_by_name : DODB::Index(Domain)
|
getter domains_by_name : DODB::CachedIndex(Domain)
|
||||||
getter domains_by_share_key : DODB::Index(Domain)
|
getter domains_by_share_key : DODB::CachedIndex(Domain)
|
||||||
getter domains_by_transfer_key : DODB::Index(Domain)
|
getter domains_by_transfer_key : DODB::CachedIndex(Domain)
|
||||||
getter domains_by_owners : DODB::Tags(Domain)
|
getter domains_by_owners : DODB::Tags(Domain)
|
||||||
|
|
||||||
getter zones : DODB::CachedDataBase(Zone)
|
getter zones : DODB::CachedDataBase(Zone)
|
||||||
getter zones_by_domain : DODB::Index(Zone)
|
getter zones_by_domain : DODB::CachedIndex(Zone)
|
||||||
|
|
||||||
getter tokens : DODB::CachedDataBase(Token)
|
getter tokens : DODB::CachedDataBase(Token)
|
||||||
getter tokens_by_uuid : DODB::Index(Token)
|
getter tokens_by_uuid : DODB::CachedIndex(Token)
|
||||||
getter tokens_by_domain : DODB::Partition(Token)
|
getter tokens_by_domain : DODB::Partition(Token)
|
||||||
|
|
||||||
getter root : String
|
getter root : String
|
||||||
|
@ -103,8 +103,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_generated_zonefile(user_id : UserDataID, domain : String) : IPC::JSON
|
def get_generated_zonefile(user_id : UserDataID, domain : String) : IPC::JSON
|
||||||
|
|
||||||
user_must_exist! user_id
|
|
||||||
zone = zone_must_exist! domain
|
zone = zone_must_exist! domain
|
||||||
user_should_own! user_id, zone.domain
|
user_should_own! user_id, zone.domain
|
||||||
|
|
||||||
|
@ -118,8 +116,6 @@ class DNSManager::Storage
|
||||||
user_id : UserDataID,
|
user_id : UserDataID,
|
||||||
domain : String) : IPC::JSON
|
domain : String) : IPC::JSON
|
||||||
|
|
||||||
user_must_exist! user_id
|
|
||||||
|
|
||||||
# Prevent future very confusing errors.
|
# Prevent future very confusing errors.
|
||||||
domain = domain.downcase
|
domain = domain.downcase
|
||||||
|
|
||||||
|
@ -164,7 +160,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def ask_share_token(user_id : UserDataID, domain_name : String)
|
def ask_share_token(user_id : UserDataID, domain_name : String)
|
||||||
user_must_exist! user_id
|
|
||||||
user_should_own! user_id, domain_name
|
user_should_own! user_id, domain_name
|
||||||
|
|
||||||
domain = @domains_by_name.get domain_name
|
domain = @domains_by_name.get domain_name
|
||||||
|
@ -178,7 +173,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def ask_transfer_token(user_id : UserDataID, domain_name : String)
|
def ask_transfer_token(user_id : UserDataID, domain_name : String)
|
||||||
user_must_exist! user_id
|
|
||||||
user_should_own! user_id, domain_name
|
user_should_own! user_id, domain_name
|
||||||
|
|
||||||
domain = @domains_by_name.get domain_name
|
domain = @domains_by_name.get domain_name
|
||||||
|
@ -194,13 +188,12 @@ class DNSManager::Storage
|
||||||
# Check the domain owners.
|
# Check the domain owners.
|
||||||
# In case there's only the requesting user, allow him to gain full control.
|
# In case there's only the requesting user, allow him to gain full control.
|
||||||
def ask_unshare_domain(user_id : UserDataID, domain_name : String)
|
def ask_unshare_domain(user_id : UserDataID, domain_name : String)
|
||||||
user_must_exist! user_id
|
|
||||||
user_should_own! user_id, domain_name
|
user_should_own! user_id, domain_name
|
||||||
|
|
||||||
domain = @domains_by_name.get domain_name
|
domain = @domains_by_name.get domain_name
|
||||||
if domain.owners.size == 1 && domain.owners[0] == user_id
|
if domain.owners.size == 1 && domain.owners[0] == user_id
|
||||||
domain.share_key = nil
|
domain.share_key = nil
|
||||||
@domains_by_name.update_or_create domain_name, domain
|
@domains_by_name.update domain
|
||||||
Response::DomainChanged.new domain
|
Response::DomainChanged.new domain
|
||||||
else
|
else
|
||||||
Response::Error.new "You are not the only owner."
|
Response::Error.new "You are not the only owner."
|
||||||
|
@ -208,14 +201,12 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def gain_ownership(user_id : UserDataID, uuid : String)
|
def gain_ownership(user_id : UserDataID, uuid : String)
|
||||||
user_must_exist! user_id
|
|
||||||
|
|
||||||
if domain = @domains_by_share_key.get? uuid
|
if domain = @domains_by_share_key.get? uuid
|
||||||
if domain.owners.includes? user_id
|
if domain.owners.includes? user_id
|
||||||
return Response::Error.new "You already own this domain."
|
return Response::Error.new "You already own this domain."
|
||||||
end
|
end
|
||||||
domain.owners << user_id
|
domain.owners << user_id
|
||||||
@domains_by_name.update_or_create domain.name, domain
|
@domains_by_name.update domain
|
||||||
Response::DomainChanged.new domain
|
Response::DomainChanged.new domain
|
||||||
elsif domain = @domains_by_transfer_key.get? uuid
|
elsif domain = @domains_by_transfer_key.get? uuid
|
||||||
if domain.owners.includes? user_id
|
if domain.owners.includes? user_id
|
||||||
|
@ -223,7 +214,7 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
domain.transfer_key = nil
|
domain.transfer_key = nil
|
||||||
domain.owners = [user_id]
|
domain.owners = [user_id]
|
||||||
@domains_by_name.update_or_create domain.name, domain
|
@domains_by_name.update domain
|
||||||
Response::DomainChanged.new domain
|
Response::DomainChanged.new domain
|
||||||
else
|
else
|
||||||
Response::Error.new "There is no key with this UUID."
|
Response::Error.new "There is no key with this UUID."
|
||||||
|
@ -231,8 +222,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_or_update_zone(user_id : UserDataID, zone : Zone) : IPC::JSON
|
def add_or_update_zone(user_id : UserDataID, zone : Zone) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
|
|
||||||
# Test zone validity.
|
# Test zone validity.
|
||||||
if errors = zone.get_errors?
|
if errors = zone.get_errors?
|
||||||
Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}"
|
Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}"
|
||||||
|
@ -254,7 +243,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
def add_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
zone = zone_must_exist! domain
|
zone = zone_must_exist! domain
|
||||||
user_should_own! user_id, domain
|
user_should_own! user_id, domain
|
||||||
|
|
||||||
|
@ -281,7 +269,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
def update_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
zone = zone_must_exist! domain
|
zone = zone_must_exist! domain
|
||||||
user_should_own! user_id, domain
|
user_should_own! user_id, domain
|
||||||
|
|
||||||
|
@ -303,8 +290,7 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_rr(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON
|
def delete_rr(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON
|
||||||
user_must_exist! user_id
|
zone = zone_must_exist! domain
|
||||||
zone = zone_must_exist! domain
|
|
||||||
user_should_own! user_id, domain
|
user_should_own! user_id, domain
|
||||||
|
|
||||||
rr = zone.rr_not_readonly! rrid
|
rr = zone.rr_not_readonly! rrid
|
||||||
|
@ -321,7 +307,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_domain(user_id : UserDataID, domain_name : String) : IPC::JSON
|
def delete_domain(user_id : UserDataID, domain_name : String) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
zone_must_exist! domain_name
|
zone_must_exist! domain_name
|
||||||
user_should_own! user_id, domain_name
|
user_should_own! user_id, domain_name
|
||||||
|
|
||||||
|
@ -370,7 +355,6 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_zone(user_id : UserDataID, domain : String) : IPC::JSON
|
def get_zone(user_id : UserDataID, domain : String) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
zone = zone_must_exist! domain
|
zone = zone_must_exist! domain
|
||||||
user_should_own! user_id, domain
|
user_should_own! user_id, domain
|
||||||
|
|
||||||
|
@ -395,11 +379,9 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_user_data(user_id : UserDataID, user_to_delete : UserDataID?) : IPC::JSON
|
def delete_user_data(user_id : UserDataID, user_to_delete : UserDataID?) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
user_id_to_delete = if u = user_to_delete
|
user_id_to_delete = if u = user_to_delete
|
||||||
user_must_be_admin! user_id
|
user_must_be_admin! user_id
|
||||||
Baguette::Log.info "Admin #{user_id} removes data of user #{u}."
|
Baguette::Log.info "Admin #{user_id} removes data of user #{u}."
|
||||||
user_must_exist! u
|
|
||||||
u
|
u
|
||||||
else
|
else
|
||||||
Baguette::Log.info "User #{user_id} terminates his account."
|
Baguette::Log.info "User #{user_id} terminates his account."
|
||||||
|
@ -412,8 +394,11 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_domains(user_id : UserDataID) : Array(Domain)
|
def user_domains(user_id : UserDataID) : Array(Domain)
|
||||||
user_must_exist! user_id
|
if doms = domains_by_owners.get? user_id.to_s
|
||||||
domains_by_owners.get(user_id.to_s)
|
doms
|
||||||
|
else
|
||||||
|
Array(Domain).new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: is the user known from authd?
|
# TODO: is the user known from authd?
|
||||||
|
@ -434,14 +419,16 @@ class DNSManager::Storage
|
||||||
# Owning a domain means to be in the owners' list of the domain.
|
# Owning a domain means to be in the owners' list of the domain.
|
||||||
# TODO: accept admin users to override this test.
|
# TODO: accept admin users to override this test.
|
||||||
def user_should_own!(user_id : UserDataID, domain : String) : Nil
|
def user_should_own!(user_id : UserDataID, domain : String) : Nil
|
||||||
d = domains_by_name.get domain
|
if d = domains_by_name.get? domain
|
||||||
unless d.owners.includes? user_id
|
unless d.owners.includes? user_id
|
||||||
raise NoOwnershipException.new
|
raise NoOwnershipException.new
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise DomainNotFoundException.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_token(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON
|
def new_token(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON
|
||||||
user_must_exist! user_id
|
|
||||||
zone = zone_must_exist! domain
|
zone = zone_must_exist! domain
|
||||||
user_should_own! user_id, domain
|
user_should_own! user_id, domain
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,6 @@ class DNSManager::Storage::Domain
|
||||||
|
|
||||||
def initialize(@name, share_key = nil, transfer_key = nil, owners = [] of UserDataID)
|
def initialize(@name, share_key = nil, transfer_key = nil, owners = [] of UserDataID)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def_clone
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,8 @@ class DNSManager::Storage::Token
|
||||||
property domain : String
|
property domain : String
|
||||||
property rrid : UInt32
|
property rrid : UInt32
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
def initialize(@domain, @rrid)
|
def initialize(@domain, @rrid)
|
||||||
@uuid = UUID.random.to_s
|
@uuid = UUID.random.to_s
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,6 +35,8 @@ class DNSManager::Storage::Zone
|
||||||
def initialize(@domain)
|
def initialize(@domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
alias Error = String
|
alias Error = String
|
||||||
|
|
||||||
# Store a Resource Record: A, AAAA, TXT, PTR, CNAME…
|
# Store a Resource Record: A, AAAA, TXT, PTR, CNAME…
|
||||||
|
@ -96,6 +98,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class SOA < ResourceRecord
|
class SOA < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
# Start of Authority
|
# Start of Authority
|
||||||
property mname : String # Master Name Server for the zone.
|
property mname : String # Master Name Server for the zone.
|
||||||
property rname : String # admin email address john.doe@example.com => john\.doe.example.com
|
property rname : String # admin email address john.doe@example.com => john\.doe.example.com
|
||||||
|
@ -159,6 +163,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class A < ResourceRecord
|
class A < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -179,6 +185,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class AAAA < ResourceRecord
|
class AAAA < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -199,6 +207,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class TXT < ResourceRecord
|
class TXT < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -224,6 +234,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class PTR < ResourceRecord
|
class PTR < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -252,6 +264,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class NS < ResourceRecord
|
class NS < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -268,6 +282,8 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class CNAME < ResourceRecord
|
class CNAME < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
def get_errors : Array(Error)
|
def get_errors : Array(Error)
|
||||||
errors = [] of Error
|
errors = [] of Error
|
||||||
|
|
||||||
|
@ -289,7 +305,7 @@ class DNSManager::Storage::Zone
|
||||||
# TODO: verifications + print.
|
# TODO: verifications + print.
|
||||||
# baguette.netlib.re. 3600 IN TXT "v=spf1 a mx ip4:<IP> a:mail.baguette.netlib.re ~all"
|
# baguette.netlib.re. 3600 IN TXT "v=spf1 a mx ip4:<IP> a:mail.baguette.netlib.re ~all"
|
||||||
class SPF < ResourceRecord
|
class SPF < ResourceRecord
|
||||||
|
def_clone
|
||||||
|
|
||||||
# SPF mechanisms are about policy, which comes with the form of a single character:
|
# SPF mechanisms are about policy, which comes with the form of a single character:
|
||||||
# - '?' means no policy (neutral),
|
# - '?' means no policy (neutral),
|
||||||
|
@ -332,6 +348,8 @@ class DNSManager::Storage::Zone
|
||||||
property t : Type # type of modifier
|
property t : Type # type of modifier
|
||||||
property v : String # value
|
property v : String # value
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
def initialize(@t, @v)
|
def initialize(@t, @v)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -362,6 +380,8 @@ class DNSManager::Storage::Zone
|
||||||
INCLUDE # include foreign SPF policy
|
INCLUDE # include foreign SPF policy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
property q : Qualifier = Qualifier::Pass
|
property q : Qualifier = Qualifier::Pass
|
||||||
property t : Type # type of mechanism
|
property t : Type # type of mechanism
|
||||||
property v : String # value
|
property v : String # value
|
||||||
|
@ -484,6 +504,7 @@ class DNSManager::Storage::Zone
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
class DKIM < ResourceRecord
|
class DKIM < ResourceRecord
|
||||||
|
def_clone
|
||||||
enum Version
|
enum Version
|
||||||
DKIM1
|
DKIM1
|
||||||
end
|
end
|
||||||
|
@ -508,6 +529,8 @@ class DNSManager::Storage::Zone
|
||||||
io << "v=#{v};h=#{h.to_s.downcase};k=#{k.to_s.downcase};p=#{p}"
|
io << "v=#{v};h=#{h.to_s.downcase};k=#{k.to_s.downcase};p=#{p}"
|
||||||
io << ";n=#{n}" unless n == ""
|
io << ";n=#{n}" unless n == ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def_clone
|
||||||
end
|
end
|
||||||
|
|
||||||
property dkim : DKIMProperties
|
property dkim : DKIMProperties
|
||||||
|
@ -542,6 +565,7 @@ class DNSManager::Storage::Zone
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
class DMARC < ResourceRecord
|
class DMARC < ResourceRecord
|
||||||
|
def_clone
|
||||||
enum Version
|
enum Version
|
||||||
DMARC1
|
DMARC1
|
||||||
end
|
end
|
||||||
|
@ -551,6 +575,8 @@ class DNSManager::Storage::Zone
|
||||||
property mail : String
|
property mail : String
|
||||||
property limit : UInt32? = nil
|
property limit : UInt32? = nil
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
def to_s(io : IO)
|
def to_s(io : IO)
|
||||||
io << "#{mail}"
|
io << "#{mail}"
|
||||||
if l = @limit
|
if l = @limit
|
||||||
|
@ -596,6 +622,8 @@ class DNSManager::Storage::Zone
|
||||||
property rf : Array(ReportFormat)? = nil
|
property rf : Array(ReportFormat)? = nil
|
||||||
property ri : UInt32
|
property ri : UInt32
|
||||||
|
|
||||||
|
def_clone
|
||||||
|
|
||||||
def initialize(@p, @sp, @v, @adkim, @aspf, @pct, @fo, @rua, @ruf, @rf, @ri)
|
def initialize(@p, @sp, @v, @adkim, @aspf, @pct, @fo, @rua, @ruf, @rf, @ri)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -694,6 +722,7 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class MX < ResourceRecord
|
class MX < ResourceRecord
|
||||||
|
def_clone
|
||||||
property priority : UInt32 = 10
|
property priority : UInt32 = 10
|
||||||
def initialize(@name, @ttl, @target, @priority = 10)
|
def initialize(@name, @ttl, @target, @priority = 10)
|
||||||
@rrtype = "MX"
|
@rrtype = "MX"
|
||||||
|
@ -729,6 +758,7 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
|
|
||||||
class SRV < ResourceRecord
|
class SRV < ResourceRecord
|
||||||
|
def_clone
|
||||||
property port : UInt16
|
property port : UInt16
|
||||||
property protocol : String = "tcp"
|
property protocol : String = "tcp"
|
||||||
property priority : UInt32 = 10
|
property priority : UInt32 = 10
|
||||||
|
|
Loading…
Add table
Reference in a new issue