1----------------------------------------------------------------
2-- IRONSIDES - DNS SERVER
3--
4-- By: Martin C. Carlisle and Barry S. Fagin
5--     Department of Computer Science
6--     United States Air Force Academy
7--
8-- This is free software; you can redistribute it and/or
9-- modify without restriction.  We do ask that you please keep
10-- the original author information, and clearly indicate if the
11-- software has been modified.
12--
13-- This software is distributed in the hope that it will be useful,
14-- but WITHOUT ANY WARRANTY; without even the implied warranty
15-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16----------------------------------------------------------------
17
18WITH Dns_Table_Pkg, zone_file_parser, error_msgs;
19with parser_utilities, Rr_Type.A_Record_Type, Rr_Type.Aaaa_Record_Type, Rr_Type.Cname_Record_Type,
20   Rr_Type.Mx_Record_Type, RR_Type.srv_record_type, Rr_Type.Ns_Record_Type, Rr_Type.Nsec_Record_Type, Rr_Type.Ptr_Record_Type;
21
22--just in case debugging needed
23--WITH Ada.Text_IO, Ada.Integer_Text_IO;
24
25package body process_first_line_of_record is
26--had to encapsulate this in a separate procedure to help the examiner
27--lots of parameters because of line-oriented file processing, different
28--kinds of parameters and multiline records require large amount of state
29--to persist across procedure calls
30      procedure ProcessFirstLineOfRecord (CurrentRecordType : in Dns_Types.Query_Type;
31         --common to all record types
32         currentOrigin : in Rr_Type.DomainNameStringType;
33         currentOwner : in Rr_Type.DomainNameStringType;
34         currentTTL : in unsigned_types.Unsigned32;
35         currentClass : in Rr_Type.ClassType;
36         currentLine : in rr_type.LineFromFileType;
37         Lastpos : in Rr_Type.Linelengthindex;
38         LineCount : Unsigned_Types.Unsigned32;
39         --for multiline records
40         InMultilineRecord : out Boolean;
41         LineInRecordCtr : out Unsigned_Types.Unsigned32;
42         --SOA record fields
43         currentNameServer : out rr_Type.DomainNameStringType;
44         CurrentEmail : out Rr_Type.DomainNameStringType;
45         --DNSKEY record (if needed)
46         DNSKEY_Rec : out Rr_Type.Dnskey_Record_Type.DNSKeyRecordType;
47         --RRSIG record (if needed)
48         RRSIG_Rec : out Rr_Type.rrsig_record_type.RRSIGRecordType;
49         recordSuccessfullyInserted : out Boolean;
50         Success : in out boolean)
51      is
52         currentIpv4 : Unsigned_Types.Unsigned32;
53         currentIpv6 : rr_type.aaaa_record_type.IPV6AddrType;
54         currentDomainName : rr_type.DomainNameStringType;
55         CurrentPref : Unsigned_Types.Unsigned16;
56         CurrentWeight : Unsigned_Types.Unsigned16;
57         CurrentPort : Unsigned_Types.Unsigned16;
58         RRString : Rr_Type.LineFromFileType;
59         NumberOfBlocks: rr_type.nsec_record_type.blockNumberValue;
60         NumberOfRecordTypes: Rr_Type.Nsec_Record_Type.RecordTypeIndexValue;
61         recordTypes : rr_type.nsec_record_type.recordTypeArrayType;
62         BlockNumbers: Rr_Type.Nsec_Record_Type.BlockNumberArrayType;
63         BlockLengths: Rr_Type.Nsec_Record_Type.BlockLengthArrayType;
64         bitMaps: Rr_Type.Nsec_Record_Type.BitMapsArrayArrayType;
65      begin
66         --these assignments all make bogus flow errors go away
67         inMultilineRecord := false;
68         lineInRecordCtr := 0;
69         currentNameServer := rr_type.BlankDomainName;
70         CurrentEmail := Rr_Type.BlankDomainName;
71         DNSKEY_Rec := Rr_Type.Dnskey_Record_Type.BlankDNSKeyRecord;
72         RRSIG_Rec := rr_type.rrsig_record_type.blankRRSIGRecord;
73         RecordSuccessfullyInserted := True;
74
75         CASE CurrentRecordType IS
76            WHEN Dns_Types.A => --A records
77               --next data item must be an ipv4 addr
78               Zone_File_Parser.ParseIpv4(currentIpv4, CurrentLine, LastPos, Success);
79
80               --can now build and insert a complete A record
81               if Success then
82                  Dns_Table_Pkg.DNS_Table.InsertARecord(
83                     Rr_Type.ConvertDomainNameToWire(CurrentOwner),
84                     rr_type.a_record_type.ARecordType'(
85                     TtlInSeconds => CurrentTTL, Class => CurrentClass,
86                     Ipv4 => CurrentIpv4), RecordSuccessfullyInserted);
87               end if;
88
89            when Dns_Types.AAAA => --AAAA records
90               --next item must be an ipv6 address
91               Zone_File_Parser.ParseIpv6(currentIpv6, CurrentLine, LastPos, Success);
92
93               --can now build and insert a complete AAAA record
94               if Success then
95                  dns_Table_Pkg.DNS_Table.InsertAAAARecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
96                     Rr_Type.Aaaa_Record_Type.AaaaRecordType'(
97                  TtlInSeconds => CurrentTTL, Class => CurrentClass,
98                  ipv6 => currentIpv6), recordSuccessfullyInserted);
99               end if;
100
101            when Dns_Types.CNAME => --CNAME records
102               --next item must be a domain name
103               Zone_File_Parser.ParseDomainName(currentDomainName, CurrentLine, LastPos, Success);
104
105               if success then
106                  --if domain name does not end in '.', append value of $ORIGIN
107                  parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
108                     LastPos, LineCount, Success);
109               end if;
110
111               --can now build and insert a complete CNAME record
112               if success then
113                  dns_table_pkg.DNS_Table.insertCNAMERecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
114                     rr_type.cname_record_type.CNAMERecordType'(
115                     ttlInSeconds => currentTTL , class => currentClass,
116                     CanonicalDomainName  => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
117                     RecordSuccessfullyInserted);
118               end if;
119
120	        when dns_types.MX => --MX records
121               --next must come a preference value, then a domain name (mail exchanger)
122               zone_file_parser.parsePrefAndDomainName(currentPref, currentDomainName,
123                  CurrentLine, LastPos, Success);
124               if success then
125                  --if domain name does not end in '.', append value of $ORIGIN
126                  parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
127                     LastPos, LineCount, Success);
128               end if;
129
130               if success then
131                  --can now build and insert a complete MX record
132                  dns_table_pkg.DNS_Table.insertMXRecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
133                     rr_type.mx_record_type.MXRecordType'(
134                     ttlInSeconds => currentTTL , class => currentClass,
135                     Pref => CurrentPref,
136                     MailExchanger => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
137                     RecordSuccessfullyInserted);
138               end if;
139
140               when dns_types.SRV => --SRV records
141               --next must come preference value, weight, port, then a domain name (server name)
142               zone_file_parser.parsePrefWeightPortAndDomainName(currentPref, currentWeight, currentPort, currentDomainName,
143                  CurrentLine, LastPos, Success);
144               if success then
145                  --if domain name does not end in '.', append value of $ORIGIN
146                  parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
147                     LastPos, LineCount, Success);
148               end if;
149
150               if success then
151                  --can now build and insert a complete SRV record
152                  dns_table_pkg.DNS_Table.insertSRVRecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
153                     rr_type.srv_record_type.SRVRecordType'(
154                     ttlInSeconds => currentTTL ,
155                     class => currentClass,
156                     Pref => CurrentPref,
157                     Weight => CurrentWeight,
158                     PortNum => CurrentPort,
159                     ServerName => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
160                     RecordSuccessfullyInserted);
161               end if;
162
163             WHEN Dns_Types.NS => --NS records
164                --next item must be a valid host name
165                Zone_File_Parser.ParseDomainName(CurrentDomainName, CurrentLine, LastPos, Success);
166                parser_utilities.checkValidHostName(currentDomainName, Success);
167                if success then
168                   --if domain name does not end in '.', append value of $ORIGIN
169                   parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
170                      LastPos, LineCount, Success);
171                end if;
172
173                if success then
174                   --can now build and insert a complete NS record
175                   dns_table_pkg.DNS_Table.insertNSRecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
176                     rr_type.ns_record_type.NSRecordType'(
177                      ttlInSeconds => currentTTL , class => currentClass,
178                      NameServer   => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
179                      RecordSuccessfullyInserted);
180                end if;
181
182             when Dns_Types.PTR => --PTR records
183                Zone_File_Parser.ParseDomainName(CurrentDomainName, CurrentLine, LastPos, Success);
184                if success then
185                   --if domain name does not end in '.', append value of $ORIGIN
186                   parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
187                      lastPos, lineCount, Success);
188                end if;
189
190                if success then
191                   --can now build and insert a complete PTR record
192                   dns_table_pkg.DNS_Table.insertPTRRecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
193                     rr_type.ptr_record_type.PTRRecordType'(
194                      ttlInSeconds => currentTTL , class => currentClass,
195                      DomainName   => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
196                     RecordSuccessfullyInserted);
197                end if;
198
199            when Dns_Types.SOA => --SOA records
200               InMultilineRecord := True;
201               lineInRecordCtr := 0;
202               Zone_File_Parser.ParseNameServerAndEmail(CurrentNameServer, CurrentEmail,
203                  CurrentLine, LastPos, Success);
204               --complete SOA record is inserted later, after all the other fields parsed
205
206               --if name server or email do not end in '.', append value of $ORIGIN
207               if success then
208                  parser_utilities.CheckAndAppendOrigin(CurrentNameServer, CurrentOrigin, CurrentLine,
209                     LastPos, LineCount, Success);
210               end if;
211
212               --if name server or email do not end in '.', append value of $ORIGIN
213               if success then
214                  parser_utilities.CheckAndAppendOrigin(CurrentEmail, CurrentOrigin, CurrentLine,
215                     LastPos, LineCount, Success);
216               end if;
217
218               parser_utilities.CheckValidHostName(CurrentNameServer, Success);
219               --NOTE:  CurrentEmail should eventually be checked too, but under slightly
220               --different rules, see pg 77 of 4th edition O'Reilly BIND book
221
222            --DNSSEC records below
223            when Dns_Types.DNSKEY => --DNSKEY records
224               InMultilineRecord := True;
225               lineInRecordCtr := 0;
226               Zone_File_Parser.ParseDNSKeyHeader(DNSKEY_Rec, CurrentLine, LastPos, Success);
227               --complete DNSKEY record will be inserted later, after key field parsed
228
229            when Dns_Types.NSEC =>
230               Zone_File_Parser.ParseDomainNameAndRRString(
231                  CurrentDomainName, RRString, CurrentLine, LastPos, Success);
232               parser_utilities.checkValidHostName(currentDomainName, Success);
233               if success then
234                  --if domain name does not end in '.', append value of $ORIGIN
235                  parser_utilities.CheckAndAppendOrigin(CurrentDomainName, CurrentOrigin, CurrentLine,
236                     LastPos, LineCount, Success);
237               END IF;
238               Zone_File_Parser.FillBlockInfo(RRString, NumberOfRecordTypes, RecordTypes, NumberOfBlocks, BlockNumbers,
239                  blockLengths, bitMaps, LineCount, Success);
240               if success then
241                   --can now build and insert a complete NSEC record
242                   dns_table_pkg.DNS_Table.insertNSECRecord(Rr_Type.ConvertDomainNameToWire(CurrentOwner),
243                      rr_type.nsec_record_type.NSECRecordType'(
244                        TtlInSeconds => CurrentTTL, Class => CurrentClass, RecordList => RRString,
245                         numberOfRecordTypes => numberOfRecordTypes, recordTypes => recordTypes,
246                         numberOfBlocks => numberofBlocks, blockNumbers => blockNumbers, blockLengths => blockLengths, bitMaps => bitMaps,
247                         NextDomainName => Rr_Type.ConvertDomainNameToWire(CurrentDomainName)),
248                         RecordSuccessfullyInserted);
249               else  --must have been missing a record type
250                  error_msgs.printMissingRecordTypeErrorInfo(currentLine, lastPos, lineCount);
251               end if;
252
253            when Dns_Types.RRSIG =>
254               InMultilineRecord := True;
255               LineInRecordCtr := 0;
256               Zone_File_Parser.ParseRRSigHeader(RRSIG_Rec, CurrentLine, LastPos, Success);
257
258            when others => -- can add more supported types here if needed
259               error_msgs.printUnsupportedRecordWarning(currentLine, lastPos, lineCount);
260          END CASE;
261      end ProcessFirstLineOfRecord;
262end process_first_line_of_record;
263