1/***************************************************************** 2** 3** @(#) soaserial.c -- helper function for the dnssec zone key tools 4** 5** Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved. 6** 7** This software is open source. 8** 9** Redistribution and use in source and binary forms, with or without 10** modification, are permitted provided that the following conditions 11** are met: 12** 13** Redistributions of source code must retain the above copyright notice, 14** this list of conditions and the following disclaimer. 15** 16** Redistributions in binary form must reproduce the above copyright notice, 17** this list of conditions and the following disclaimer in the documentation 18** and/or other materials provided with the distribution. 19** 20** Neither the name of Holger Zuleger HZnet nor the names of its contributors may 21** be used to endorse or promote products derived from this software without 22** specific prior written permission. 23** 24** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 28** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34** POSSIBILITY OF SUCH DAMAGE. 35** 36*****************************************************************/ 37# include <stdio.h> 38# include <string.h> 39# include <stdlib.h> 40# include <ctype.h> 41# include <sys/types.h> 42# include <sys/stat.h> 43# include <time.h> 44# include <utime.h> 45# include <assert.h> 46#ifdef HAVE_CONFIG_H 47# include <config.h> 48#endif 49# include "config_zkt.h" 50# include "zconf.h" 51# include "log.h" 52# include "debug.h" 53#define extern 54# include "soaserial.h" 55#undef extern 56 57static int inc_soa_serial (FILE *fp, int use_unixtime); 58static int is_soa_rr (const char *line); 59static const char *strfindstr (const char *str, const char *search); 60 61 62/**************************************************************** 63** 64** int inc_serial (filename, use_unixtime) 65** 66** This function depends on a special syntax formating the 67** SOA record in the zone file!! 68** 69** To match the SOA record, the SOA RR must be formatted 70** like this: 71** @ [ttl] IN SOA <master.fq.dn.> <hostmaster.fq.dn.> ( 72** <SPACEes or TABs> 1234567890; serial number 73** <SPACEes or TABs> 86400 ; other values 74** ... 75** The space from the first digit of the serial number to 76** the first none white space char or to the end of the line 77** must be at least 10 characters! 78** So you have to left justify the serial number in a field 79** of at least 10 characters like this: 80** <SPACEes or TABs> 1 ; Serial 81** 82****************************************************************/ 83int inc_serial (const char *fname, int use_unixtime) 84{ 85 FILE *fp; 86 char buf[4095+1]; 87 int error; 88 89 /** 90 since BIND 9.4, there is a dnssec-signzone option available for 91 serial number increment. 92 If the user requests "unixtime"; then use this mechanism. 93 **/ 94#if defined(BIND_VERSION) && BIND_VERSION >= 940 95 if ( use_unixtime ) 96 return 0; 97#endif 98 if ( (fp = fopen (fname, "r+")) == NULL ) 99 return -1; 100 101 /* read until the line matches the beginning of a soa record ... */ 102 while ( fgets (buf, sizeof buf, fp) && !is_soa_rr (buf) ) 103 ; 104 105 if ( feof (fp) ) 106 { 107 fclose (fp); 108 return -2; 109 } 110 111 error = inc_soa_serial (fp, use_unixtime); /* .. inc soa serial no ... */ 112 113 if ( fclose (fp) != 0 ) 114 return -5; 115 return error; 116} 117 118/***************************************************************** 119** check if line is the beginning of a SOA RR record, thus 120** containing the string "IN .* SOA" and ends with a '(' 121** returns 1 if true 122*****************************************************************/ 123static int is_soa_rr (const char *line) 124{ 125 const char *p; 126 127 assert ( line != NULL ); 128 129 if ( (p = strfindstr (line, "IN")) && strfindstr (p+2, "SOA") ) /* line contains "IN" and "SOA" */ 130 { 131 p = line + strlen (line) - 1; 132 while ( p > line && isspace (*p) ) 133 p--; 134 if ( *p == '(' ) /* last character have to be a '(' to start a multi line record */ 135 return 1; 136 } 137 138 return 0; 139} 140 141/***************************************************************** 142** Find string 'search' in 'str' and ignore case in comparison. 143** returns the position of 'search' in 'str' or NULL if not found. 144*****************************************************************/ 145static const char *strfindstr (const char *str, const char *search) 146{ 147 const char *p; 148 int c; 149 150 assert ( str != NULL ); 151 assert ( search != NULL ); 152 153 c = tolower (*search); 154 p = str; 155 do { 156 while ( *p && tolower (*p) != c ) 157 p++; 158 if ( strncasecmp (p, search, strlen (search)) == 0 ) 159 return p; 160 p++; 161 } while ( *p ); 162 163 return NULL; 164} 165 166/***************************************************************** 167** return the serial number of the given time in the form 168** of YYYYmmdd00 as ulong value 169*****************************************************************/ 170static ulong serialtime (time_t sec) 171{ 172 struct tm *t; 173 ulong serialtime; 174 175 t = gmtime (&sec); 176 serialtime = (t->tm_year + 1900) * 10000; 177 serialtime += (t->tm_mon+1) * 100; 178 serialtime += t->tm_mday; 179 serialtime *= 100; 180 181 return serialtime; 182} 183 184/***************************************************************** 185** inc_soa_serial (fp, use_unixtime) 186** increment the soa serial number of the file 'fp' 187** 'fp' must be opened "r+" 188*****************************************************************/ 189static int inc_soa_serial (FILE *fp, int use_unixtime) 190{ 191 int c; 192 long pos, eos; 193 ulong serial; 194 int digits; 195 ulong today; 196 197 /* move forward until any non ws reached */ 198 while ( (c = getc (fp)) != EOF && isspace (c) ) 199 ; 200 ungetc (c, fp); /* push back the last char */ 201 202 pos = ftell (fp); /* mark position */ 203 204 serial = 0L; /* read in the current serial number */ 205 /* be aware of the trailing space in the format string !! */ 206 if ( fscanf (fp, "%lu ", &serial) != 1 ) /* try to get serial no */ 207 return -3; 208 eos = ftell (fp); /* mark first non digit/ws character pos */ 209 210 digits = eos - pos; 211 if ( digits < 10 ) /* not enough space for serial no ? */ 212 return -4; 213 214 today = time (NULL); 215 if ( !use_unixtime ) 216 { 217 today = serialtime (today); /* YYYYmmdd00 */ 218 if ( serial > 1970010100L && serial < today ) 219 serial = today; /* set to current time */ 220 serial++; /* increment anyway */ 221 } 222 223 fseek (fp, pos, SEEK_SET); /* go back to the beginning */ 224 fprintf (fp, "%-*lu", digits, serial); /* write as many chars as before */ 225 226 return 1; /* yep! */ 227} 228 229/***************************************************************** 230** return the error text of the inc_serial return coode 231*****************************************************************/ 232const char *inc_errstr (int err) 233{ 234 switch ( err ) 235 { 236 case -1: return "couldn't open zone file for modifying"; 237 case -2: return "unexpected end of file"; 238 case -3: return "no serial number found in zone file"; 239 case -4: return "not enough space left for serialno"; 240 case -5: return "error on closing zone file"; 241 } 242 return ""; 243} 244 245#ifdef SOA_TEST 246const char *progname; 247main (int argc, char *argv[]) 248{ 249 ulong now; 250 int err; 251 char cmd[255]; 252 253 progname = *argv; 254 255 now = time (NULL); 256 now = serialtime (now); 257 printf ("now = %lu\n", now); 258 259 if ( (err = inc_serial (argv[1], 0)) <= 0 ) 260 { 261 error ("can't change serial errno=%d\n", err); 262 exit (1); 263 } 264 265 snprintf (cmd, sizeof(cmd), "head -15 %s", argv[1]); 266 system (cmd); 267} 268#endif 269 270