1 /* $NetBSD: soaserial.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $ */
3 /*****************************************************************
5 ** @(#) soaserial.c -- helper function for the dnssec zone key tools
7 ** Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
9 ** This software is open source.
11 ** Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions
15 ** Redistributions of source code must retain the above copyright notice,
16 ** this list of conditions and the following disclaimer.
18 ** Redistributions in binary form must reproduce the above copyright notice,
19 ** this list of conditions and the following disclaimer in the documentation
20 ** and/or other materials provided with the distribution.
22 ** Neither the name of Holger Zuleger HZnet nor the names of its contributors may
23 ** be used to endorse or promote products derived from this software without
24 ** specific prior written permission.
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 ** POSSIBILITY OF SUCH DAMAGE.
38 *****************************************************************/
43 # include <sys/types.h>
44 # include <sys/stat.h>
51 # include "config_zkt.h"
56 # include "soaserial.h"
59 static int inc_soa_serial (FILE *fp
, int use_unixtime
);
60 static int is_soa_rr (const char *line
);
61 static const char *strfindstr (const char *str
, const char *search
);
64 /****************************************************************
66 ** int inc_serial (filename, use_unixtime)
68 ** This function depends on a special syntax formating the
69 ** SOA record in the zone file!!
71 ** To match the SOA record, the SOA RR must be formatted
73 ** @ [ttl] IN SOA <master.fq.dn.> <hostmaster.fq.dn.> (
74 ** <SPACEes or TABs> 1234567890; serial number
75 ** <SPACEes or TABs> 86400 ; other values
77 ** The space from the first digit of the serial number to
78 ** the first none white space char or to the end of the line
79 ** must be at least 10 characters!
80 ** So you have to left justify the serial number in a field
81 ** of at least 10 characters like this:
82 ** <SPACEes or TABs> 1 ; Serial
84 ** Since ZKT 1.1.0 single line SOA records are also supported
86 ****************************************************************/
87 int inc_serial (const char *fname
, int use_unixtime
)
95 since BIND 9.4, there is a dnssec-signzone option available for
96 serial number increment.
97 If the user requests "unixtime"; then use this mechanism.
102 if ( (fp
= fopen (fname
, "r+")) == NULL
)
105 /* read until the line matches the beginning of a soa record ... */
106 while ( fgets (buf
, sizeof buf
, fp
) )
108 dbg_val ("inc_serial() checking line for SOA RR \"%s\"\n", buf
);
109 serial_pos
= is_soa_rr (buf
);
110 if ( serial_pos
) /* SOA record found ? */
119 dbg_val ("serial_pos = %d\n", serial_pos
);
120 if (serial_pos
> 1 ) /* if we found a single line SOA RR */
121 fseek (fp
, -(long)serial_pos
, SEEK_CUR
); /* go back to the beginning of the line */
123 error
= inc_soa_serial (fp
, use_unixtime
); /* .. inc soa serial no ... */
124 dbg_val ("inc_soa_serial() returns %d\n", error
);
126 if ( fclose (fp
) != 0 ) /* close the zone file in any case */
132 /*****************************************************************
133 ** check if line is the beginning of a SOA RR record, thus
134 ** containing the string "IN .* SOA" and ends with a '('
136 *****************************************************************/
137 static int is_soa_rr (const char *line
)
141 assert ( line
!= NULL
);
143 /* line contains "IN" and "SOA" */
144 if ( (p
= strfindstr (line
, "IN")) && strfindstr (p
+2, "SOA") )
146 p
= line
+ strlen (line
) - 1;
147 while ( p
> line
&& isspace (*p
) )
149 if ( *p
== '(' ) /* last character must be a '(' to start a multi line record */
156 /*****************************************************************
158 ** check if line is the beginning of a SOA RR record, thus
159 ** containing the string "IN .* SOA" and ends with a '('
160 ** (multiline record) or is a single line record.
162 ** returns 1 if it is a multi line record (for compability to
163 ** the old function) or the position of the serial number
164 ** field counted from the end of the line
166 *****************************************************************/
167 static int is_soa_rr (const char *line
)
172 assert ( line
!= NULL
);
174 /* line contains "IN" and "SOA" ? */
175 if ( (p
= strfindstr (line
, "IN")) && (soa_p
= strfindstr (p
+2, "SOA")) )
177 int len
= strlen (line
);
179 /* check for multiline record */
181 while ( p
> line
&& isspace (*p
) )
183 if ( *p
== '(' ) /* last character must be a '(' to start a multi line record */
186 /* line is single line record */
187 p
= soa_p
+ 3; /* start just behind the SOA string */
188 dbg_val1 ("p = \"%s\"\n", p
);
189 p
+= strspn (p
, " \t"); /* skip white space */
190 p
+= strcspn (p
, " \t"); /* skip primary master */
191 p
+= strspn (p
, " \t"); /* skip white space */
192 p
+= strcspn (p
, " \t"); /* skip mail address */
193 dbg_val1 ("p = \"%s\"\n", p
);
195 dbg_val1 ("is_soa_rr returns = %d\n", (line
+len
) - p
);
196 return (line
+len
) - p
; /* position of serial nr from the end of the line */
203 /*****************************************************************
204 ** Find string 'search' in 'str' and ignore case in comparison.
205 ** returns the position of 'search' in 'str' or NULL if not found.
206 *****************************************************************/
207 static const char *strfindstr (const char *str
, const char *search
)
212 assert ( str
!= NULL
);
213 assert ( search
!= NULL
);
215 c
= tolower (*search
);
218 while ( *p
&& tolower (*p
) != c
)
220 if ( strncasecmp (p
, search
, strlen (search
)) == 0 )
228 /*****************************************************************
229 ** return the serial number of the given time in the form
230 ** of YYYYmmdd00 as ulong value
231 *****************************************************************/
232 static ulong
serialtime (time_t sec
)
238 serialtime
= (t
->tm_year
+ 1900) * 10000;
239 serialtime
+= (t
->tm_mon
+1) * 100;
240 serialtime
+= t
->tm_mday
;
246 /*****************************************************************
247 ** inc_soa_serial (fp, use_unixtime)
248 ** increment the soa serial number of the file 'fp'
249 ** 'fp' must be opened "r+"
250 ** returns 0 on success or a negative value in case of an error
251 *****************************************************************/
252 static int inc_soa_serial (FILE *fp
, int use_unixtime
)
260 /* move forward until any non ws is reached */
261 while ( (c
= getc (fp
)) != EOF
&& isspace (c
) )
263 ungetc (c
, fp
); /* push back the last char */
265 pos
= ftell (fp
); /* mark position */
267 serial
= 0L; /* read in the current serial number */
268 /* be aware of the trailing space in the format string !! */
269 if ( fscanf (fp
, "%lu ", &serial
) != 1 ) /* try to get serial no */
271 eos
= ftell (fp
); /* mark first non digit/ws character pos */
274 if ( digits
< 10 ) /* not enough space for serial no ? */
280 today
= serialtime (today
); /* YYYYmmdd00 */
281 if ( serial
> 1970010100L && serial
< today
)
282 serial
= today
; /* set to current time */
283 serial
++; /* increment anyway */
286 fseek (fp
, pos
, SEEK_SET
); /* go back to the beginning */
287 fprintf (fp
, "%-*lu", digits
, serial
); /* write as many chars as before */
292 /*****************************************************************
293 ** return the error text of the inc_serial return coode
294 *****************************************************************/
295 const char *inc_errstr (int err
)
299 case -1: return "couldn't open zone file for modifying";
300 case -2: return "unexpected end of file";
301 case -3: return "no serial number found in zone file";
302 case -4: return "not enough space left for serialno";
303 case -5: return "error on closing zone file";
309 const char *progname
;
310 main (int argc
, char *argv
[])
319 now
= serialtime (now
);
320 printf ("now = %lu\n", now
);
322 if ( (err
= inc_serial (argv
[1], 0)) < 0 )
324 fprintf (stderr
, "can't change serial no: errno=%d %s\n",
325 err
, inc_errstr (err
));
329 snprintf (cmd
, sizeof(cmd
), "head -15 %s", argv
[1]);