]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/backends/bind/zoneparser2.cc
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2005 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 # pragma warning ( disable: 4786 )
37 #include "ahuexception.hh"
42 #include "zoneparser.hh"
44 extern const char * bind_directory
;
45 void ZoneParser :: setDirectory ( const string
& dir
)
51 void ZoneParser :: parse ( const string
& fname
, const string
& origin
, unsigned int domain_id
)
53 d_filename
= fname
. c_str ();
57 zonein
= gzopen ( fname
. c_str (), "r" );
59 zonein
= gzdopen ( STDIN_FILENO
, "r" );
62 throw AhuException ( "Unable to open zonefile '" + fname
+ "': " + stringerror ());
75 while ( gzgets ( fds
. top (), cline
, sizeof ( cline
)- 1 )) {
77 chomp ( line
, " \x1a\r\n " );
84 if ( line
[ 0 ]== '$' && (! line
. find ( "$INCLUDE " ) || ! line
. find ( "$include " ))) {
86 stringtok ( parts
, line
, " \t\n " );
88 throw AhuException ( "Invalid $INCLUDE statement in zonefile '" + fname
+ "'" );
90 string filename
= unquotify ( parts
[ 1 ]);
92 filename
= d_dir
+ "/" + filename
;
95 gzFile fp
= gzopen ( filename
. c_str (), "r" );
97 throw AhuException ( "Unable to open zonefile '" + filename
+ "' included from '" + fname
+ "': " + stringerror ());
101 if ( eatLine ( line
, rec
))
102 for ( vector
< Record
>:: const_iterator i
= rec
. begin (); i
!= rec
. end ();++ i
)
103 d_callback ( domain_id
, i
-> name
, i
-> qtype
, i
-> content
, i
-> ttl
, i
-> prio
);
106 // if(ferror(fds.top())) {
107 // fclose(fds.top());
109 // throw AhuException("Error reading from file '"+fname+"': "+stringerror());
116 void ZoneParser :: parse ( const string
& fname
, const string
& origin
, vector
< Record
>& records
)
118 d_filename
= fname
. c_str ();
120 gzFile zonein
= gzopen ( fname
. c_str (), "r" );
123 throw AhuException ( "Unable to open zonefile '" + fname
+ "': " + stringerror ());
134 while (! fds
. empty ()) {
135 while ( gzgets ( fds
. top (), cline
, sizeof ( cline
)- 1 )) {
137 chomp ( line
, " \x1a\r\n " );
144 if ( line
[ 0 ]== '$' && (! line
. find ( "$INCLUDE " ) || ! line
. find ( "$include " ))) {
145 vector
< string
> parts
;
146 stringtok ( parts
, line
, " \t\r\n " );
148 throw AhuException ( "Invalid $INCLUDE statement in zonefile '" + fname
+ "'" );
150 string filename
= unquotify ( parts
[ 1 ]);
151 gzFile fp
= gzopen ( filename
. c_str (), "r" );
153 throw AhuException ( "Unable to open zonefile '" + filename
+ "' included from '" + fname
+ "': " + stringerror ());
157 if ( eatLine ( line
, rec
))
158 for ( vector
< Record
>:: const_iterator i
= rec
. begin (); i
!= rec
. end ();++ i
)
159 records
. push_back (* i
);
162 // if(ferror(fds.top())) {
163 // fclose(fds.top());
165 // throw AhuException("Error reading from file '"+fname+"': "+stringerror());
173 void ZoneParser :: fillRec ( const string
& qname
, const string
& qtype
, const string
& content
, int ttl
, int prio
, vector
< Record
>& recs
)
179 if (! QType :: chartocode ( qtype
. c_str ()))
180 throw AhuException ( "Unknown qtype '" + qtype
+ "' on line " + itoa ( d_lineno
)+ " of file '" + d_filename
+ "'" );
185 // cerr<<"filled with type: "<<rec.qtype<<", "<<QType::chartocode(qtype.c_str())<<": "<<content<<endl;
190 void ZoneParser :: cutOff ( string
& line
, const string
& delim
)
192 string :: size_type pos
= line
. find_first_of ( delim
);
193 if ( pos
== string :: npos
)
198 bool ZoneParser :: eatLine ( const string
& line
, vector
< Record
> & rec
)
202 static string lastfirstword
;
203 string :: size_type pos
= string :: npos
;
206 pos
= line
. find_first_of ( "(" );
207 if ( pos
!= string :: npos
) { // this is a line that continues
208 tline
= line
. substr ( 0 , pos
);
212 tline
= line
; // complete & boring line
214 else { // continuation
216 if ( pos
== string :: npos
) { // middle part
221 tline
. append ( line
. substr ( 0 , pos
)); // end part, we have a complete line!
225 // full & unparenthesised line now in tline!
226 // cout<<"line: '"<<tline<<"'"<<endl;
227 if ( tline
. empty () || tline
. find_first_not_of ( " \t\n " )== string :: npos
) {
233 if ( isspace ( tline
[ 0 ]))
234 tline
= lastfirstword
+ " \t " + tline
;
236 vector
< string
> parts
;
237 stringtok ( parts
, tline
, " \t " ); // we used to strip " here
238 if ( parts
[ 0 ][ 0 ]!= '$' && ! isspace ( parts
[ 0 ][ 0 ]))
239 lastfirstword
= parts
[ 0 ];
241 // for_each(parts.begin(),parts.end(),print);
243 return parseLine ( parts
, rec
);
246 ZoneParser ::~ ZoneParser ()
251 void ZoneParser :: setCallback ( callback_t
* callback
)
256 bool ZoneParser :: isNumber ( const string
& s
)
258 for ( string :: const_iterator i
= s
. begin ();
262 if (* i
== 'M' || * i
== 'D' || * i
== 'H' || * i
== 'W' || * i
== 'm' || * i
== 'd' || * i
== 'h' || * i
== 'w' ) // last character
270 bool ZoneParser :: isType ( const string
& s
)
281 bool ZoneParser :: isClass ( const string
& s
)
283 return ( s
. size ()== 2 && ( s
== "IN" || s
== "CH" || s
== "HS" || s
== "in" || s
== "ch" || s
== "hs" ));
286 unsigned int ZoneParser :: zoneNumber ( const string
& str
)
288 unsigned int val
= atoi ( str
. c_str ());
289 char lc
= toupper ( str
[ str
. length ()- 1 ]);
308 throw AhuException ( "Unable to parse " + d_origin
+ " time specification '" + str
+ "' at line " + itoa ( d_lineno
));
314 /** this parser handles 10 cases (sigh)
315 1) qname TTL CLASS QTYPE *
316 2) qname CLASS TTL QTYPE *
317 3) qname CLASS QTYPE *
321 And then everything again with a space first character, which implies 'same as last name'
324 void ZoneParser :: soaCanonic ( string
& content
)
327 stringtok ( parts
, content
, " \t " );
330 // 'ns.naamserver.net. hostmaster.naamserver.net 2001102501 8H 2H 1W 1D'
331 // FIXME: what about 'ns hostmaster.naamserver.net 2001102501 8H 2H 1W 1D'?
334 for ( vector
< string
>:: const_iterator i
= parts
. begin (); i
!= parts
. end ();++ i
,++ pos
) {
337 newcontent
. append ( 1 , ' ' );
338 newcontent
. append ( canonic ( * i
) );
341 unsigned int val
= zoneNumber (* i
);
343 newcontent
. append ( 1 , ' ' );
344 newcontent
. append ( itoa ( val
));
350 string
ZoneParser :: expandWord ( const string
& line
, int value
)
354 for ( string :: const_iterator i
= line
. begin (); i
!= line
. end ();++ i
) {
358 if (! escape
&& * i
== '$' ) {
359 if ( i
+ 2 < line
. end () && *( i
+ 1 )== '{' ) { // shit
360 string :: const_iterator k
=( i
+= 2 );
361 while ( k
++!= line
. end () && * k
!= '}' )
364 throw AhuException ( "Malformed $GENERATE statement" );
368 //copy(i,k,back_inserter(spec));
369 for ( string :: const_iterator a
= i
; a
!= k
; ++ a
)
372 vector
< string
> partjes
;
373 stringtok ( partjes
, spec
, "," );
375 throw AhuException ( "Malformed $GENERATE statement: '" + spec
+ "'" );
377 value
+= atoi ( partjes
[ 0 ]. c_str ());
380 if ( partjes
. size ()>= 2 )
381 width
= atoi ( partjes
[ 1 ]. c_str ());
382 if ( partjes
. size ()>= 3 )
389 format
. append ( 1 , radix
);
391 snprintf ( tmp
, 19 , format
. c_str (), value
);
397 newline
. append ( itoa ( value
));
400 newline
. append ( 1 ,* i
);
407 string
ZoneParser :: canonic ( const string
& dom
)
409 if ( dom
[ dom
. size ()- 1 ]!= '.' )
412 return dom
. substr ( 0 , dom
. size ()- 1 );
417 bool ZoneParser :: parseLine ( const vector
< string
>& words
, vector
< Record
>& rec
)
425 if (! Utility :: strcasecmp ( words
[ 0 ]. c_str (), "$ORIGIN" ) && words
. size ()> 1 ) {
426 d_origin
= canonic ( words
[ 1 ]);
428 else if (! Utility :: strcasecmp ( words
[ 0 ]. c_str (), "$TTL" ) && words
. size ()> 1 ) {
429 d_ttl
= zoneNumber ( words
[ 1 ]);
431 else if (! Utility :: strcasecmp ( words
[ 0 ]. c_str (), "$GENERATE" ) && words
. size ()> 1 ) {
432 // $GENERATE 1-127 $ CNAME $.0
433 string range
= words
[ 1 ]; // 1-127 means 1...127 (including 127). 1-127/2 is 1..3..5..
435 stringtok ( parts
, range
, "-/" );
436 if ( parts
. size ()< 2 || parts
. size ()> 3 )
437 throw AhuException ( "Malformed $GENERATE on line " + itoa ( d_lineno
)+ " of " + d_filename
);
439 int start
, stop
, step
= 1 ;
440 start
= atoi ( parts
[ 0 ]. c_str ());
441 stop
= atoi ( parts
[ 1 ]. c_str ());
443 step
= atoi ( parts
[ 2 ]. c_str ());
444 vector
< string
> newwords
;
446 for ( int i
= start
; i
<= stop
;++ i
) {
448 for ( unsigned int j
= 2 ; j
< words
. size ();++ j
) {
449 newwords
. push_back ( expandWord ( words
[ j
], i
));
451 parseLine ( newwords
, rec
);
456 throw AhuException ( "Unhandled command '" + words
[ 0 ]+ "' on line " + itoa ( d_lineno
)+ " of '" + d_filename
+ "'" );
463 if ( words
. size ()== 1 && words
[ 0 ]== ";" )
465 throw AhuException ( "Short line " + itoa ( d_lineno
)+ " in '" + d_filename
+ "': " + itoa ( words
. size ())+ " words. Probably due to repeated record without domainname" );
468 string qname
= words
[ 0 ];
472 if ( isNumber ( words
[ 1 ])) // 1 || 4
474 ttl
= zoneNumber ( words
[ 1 ]);
475 if ( isClass ( words
[ 2 ]))
492 else /* 2 || 3 || 5 */
494 if (! isClass ( words
[ 1 ]))
505 if ( isNumber ( words
[ 2 ]))
507 ttl
= zoneNumber ( words
[ 2 ]);
513 else if ( isType ( words
[ 2 ]))
524 throw AhuException ( "Funky parse case on line " + itoa ( d_lineno
));
530 if ( qname
[ qname
. size ()- 1 ]!= '.' )
534 // cerr<<qname<<", "<<qclass<<", "<<qtype<<", "<<ttl<<", rest from field "<<cpos<<endl;
536 int left
= words
. size ()- cpos
;
539 if (( qtype
== "MX" && left
== 2 ) || ( qtype
== "SRV" && left
== 4 )){
540 int prio
= atoi ( words
[ cpos
++]. c_str ()); left
--;
541 content
= words
[ cpos
++]; left
--;
544 content
+= " " + words
[ cpos
++];
549 if ( content
[ content
. size ()- 1 ]!= '.' )
550 content
+= "." + d_origin
;
552 fillRec ( qname
, qtype
, content
, ttl
, prio
, rec
);
556 content
= words
[ cpos
++]; left
--;
559 content
+= " " + words
[ cpos
++];
561 if ( qtype
== "MX" || qtype
== "CNAME" || qtype
== "NS" ) {
565 if ( content
[ content
. size ()- 1 ]!= '.' )
566 content
+= "." + d_origin
;
570 if ( qtype
== "TXT" && content
. size () > 2 ) { // strip quotes from TXT
572 content
= content
. substr ( 1 );
573 if ( content
[ content
. size ()- 1 ]== '"' )
574 content
. resize ( content
. size ()- 1 );
577 fillRec ( qname
, qtype
, content
, ttl
, 0 , rec
);
581 throw AhuException ( "No content on line " + itoa ( d_lineno
));