<commit_before>-module(erldns_pgsql).

-include("dns.hrl").
-include("erldns.hrl").

-export([init/0, lookup_records/0, domain_names/1]).
-export([db_to_record/2]).

-define(MAX_TXT_SIZE, 255).

init() -> ok.

lookup_records() ->
  case squery("select * from domains") of
    {ok, _, Rows} ->
      lists:map(
        fun({Id,Name,_,_,_,_,_}) ->
            {Name, lookup_records(Name, list_to_integer(binary_to_list(Id)))}
        end, Rows);
    Result ->
      lager:error("~p:~p", [?MODULE, Result]), []
  end.

lookup_records(Name, DomainId) ->
  case equery("select * from records where domain_id = $1", [DomainId]) of
    {ok, _, Rows} ->
      lists:map(fun(Row) -> row_to_record(Name, Row) end, Rows);
    Result ->
      lager:error("~p:~p", [?MODULE, Result]), []
  end.

%% Convert a name to a list of possible domain names by working
%% back through the labels to construct each possible domain.
domain_names(Qname) -> domain_names(dns:dname_to_labels(Qname), []).
domain_names([], Names) -> Names;
domain_names([Label|Rest], Names) -> domain_names(Rest, Names ++ [dns:labels_to_dname([Label] ++ Rest)]).

%% Take a row and turn it into a DNS resource record.
row_to_record(_, {_, _Id, Name, Type, Content, TTL, Priority, _ChangeDate}) ->
  #db_rr{name=Name, type=Type, content=Content, ttl=TTL, priority=Priority};
row_to_record(_, {_, _Id, Name, Type, Content, TTL, Priority, _ChangeDate, _Auth}) ->
  #db_rr{name=Name, type=Type, content=Content, ttl=TTL, priority=Priority}.

%% Convert an internal DB representation to a dns RR.
db_to_record(Qname, Record) when is_record(Record, db_rr) ->
  lager:debug("Parsing content for ~p", [Qname]),
  case parse_content(Record#db_rr.content, Record#db_rr.priority, Record#db_rr.type) of
    unsupported -> unsupported;
    Data ->
      #dns_rr{
        name = Record#db_rr.name,
        type = erldns_records:name_type(Record#db_rr.type),
        data = Data,
        ttl  = erldns_records:default_ttl(Record#db_rr.ttl)
      }
  end;
db_to_record(Qname, Value) ->
  lager:debug("~p:failed to convert DB record to DNS record for ~p with ~p (wildcard? ~p)", [?MODULE, Qname, Value]),
  [].

%% All of these functions are used to parse the content field
%% stored in the DB into a correct dns_rrdata in-memory record.
parse_content(Content, _, ?DNS_TYPE_SOA_BSTR) ->
  [MnameStr, RnameStr, SerialStr, RefreshStr, RetryStr, ExpireStr, MinimumStr] = string:tokens(binary_to_list(Content), " "),
  [Mname, Rname, Serial, Refresh, Retry, Expire, Minimum] = [MnameStr, re:replace(RnameStr, "@", ".", [{return, list}]), to_i(SerialStr), to_i(RefreshStr), to_i(RetryStr), to_i(ExpireStr), to_i(MinimumStr)],
  #dns_rrdata_soa{mname=Mname, rname=Rname, serial=Serial, refresh=Refresh, retry=Retry, expire=Expire, minimum=Minimum};

parse_content(Content, _, ?DNS_TYPE_NS_BSTR) ->
  #dns_rrdata_ns{dname=Content};
parse_content(Content, _, ?DNS_TYPE_CNAME_BSTR) ->
  #dns_rrdata_cname{dname=Content};<commit_msg>Handle invalid SOA data.<commit_after>-module(erldns_pgsql).

-include("dns.hrl").
-include("erldns.hrl").

-export([init/0, lookup_records/0, domain_names/1]).
-export([db_to_record/2]).

-define(MAX_TXT_SIZE, 255).

init() -> ok.

lookup_records() ->
  case squery("select * from domains") of
    {ok, _, Rows} ->
      lists:map(
        fun({Id,Name,_,_,_,_,_}) ->
            {Name, lookup_records(Name, list_to_integer(binary_to_list(Id)))}
        end, Rows);
    Result ->
      lager:error("~p:~p", [?MODULE, Result]), []
  end.

lookup_records(Name, DomainId) ->
  case equery("select * from records where domain_id = $1", [DomainId]) of
    {ok, _, Rows} ->
      lists:map(fun(Row) -> row_to_record(Name, Row) end, Rows);
    Result ->
      lager:error("~p:~p", [?MODULE, Result]), []
  end.

%% Convert a name to a list of possible domain names by working
%% back through the labels to construct each possible domain.
domain_names(Qname) -> domain_names(dns:dname_to_labels(Qname), []).
domain_names([], Names) -> Names;
domain_names([Label|Rest], Names) -> domain_names(Rest, Names ++ [dns:labels_to_dname([Label] ++ Rest)]).

%% Take a row and turn it into a DNS resource record.
row_to_record(_, {_, _Id, Name, Type, Content, TTL, Priority, _ChangeDate}) ->
  #db_rr{name=Name, type=Type, content=Content, ttl=TTL, priority=Priority};
row_to_record(_, {_, _Id, Name, Type, Content, TTL, Priority, _ChangeDate, _Auth}) ->
  #db_rr{name=Name, type=Type, content=Content, ttl=TTL, priority=Priority}.

%% Convert an internal DB representation to a dns RR.
db_to_record(Qname, Record) when is_record(Record, db_rr) ->
  lager:debug("Parsing content for ~p", [Qname]),
  case parse_content(Record#db_rr.content, Record#db_rr.priority, Record#db_rr.type) of
    unsupported -> unsupported;
    Data ->
      #dns_rr{
        name = Record#db_rr.name,
        type = erldns_records:name_type(Record#db_rr.type),
        data = Data,
        ttl  = erldns_records:default_ttl(Record#db_rr.ttl)
      }
  end;
db_to_record(Qname, Value) ->
  lager:debug("~p:failed to convert DB record to DNS record for ~p with ~p (wildcard? ~p)", [?MODULE, Qname, Value]),
  [].

%% All of these functions are used to parse the content field
%% stored in the DB into a correct dns_rrdata in-memory record.
parse_content(Content, _, ?DNS_TYPE_SOA_BSTR) ->
  case string:tokens(binary_to_list(Content), " ") of
    [MnameStr, RnameStr, SerialStr, RefreshStr, RetryStr, ExpireStr, MinimumStr] ->
      [Mname, Rname, Serial, Refresh, Retry, Expire, Minimum] = [MnameStr, re:replace(RnameStr, "@", ".", [{return, list}]), to_i(SerialStr), to_i(RefreshStr), to_i(RetryStr), to_i(ExpireStr), to_i(MinimumStr)],
      #dns_rrdata_soa{mname=Mname, rname=Rname, serial=Serial, refresh=Refresh, retry=Retry, expire=Expire, minimum=Minimum};
    _ ->
      lager:error("~p:SOA record with invalid content: ~p", [?MODULE, Content]),
      unsupported
  end;

parse_content(Content, _, ?DNS_TYPE_NS_BSTR) ->
  #dns_rrdata_ns{dname=Content};
parse_content(Content, _, ?DNS_TYPE_CNAME_BSTR) ->
  #dns_rrdata_cname{dname=Content};
{'fraction_non_alphanumeric': 0.1590309822017139, 'fraction_numerical': 0.0026367831245880024, 'mean_word_length': 4.6142460684551345, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1961097', 'n_tokens_mistral': 2238, 'n_tokens_neox': 2207, 'n_words': 494}