using System; using System.Collections.Generic; sealed class Axfr : Task { private uint NextRun; private uint Stage; private uint Refresh = 600; private uint Retry = 600; private void Finish( bool Ok ) { Stage = 0; uint Wait = ( Ok ? Refresh : Retry ) + 1; Wait -= (uint)Random.Number( (int)Wait / 5 ); // Random 20% variation. NextRun = Cache.Clock + Wait; #if (Trace) Log( "Finished : Ok=" + Ok + " Wait=" + Wait + " seconds" ); #endif TaskFinished(); } public override void Go() { if ( Stage == 0 ) { TaskInit(); #if (Trace) LogX( "Running" ); #endif } lock ( Cache.Workers ) { if ( Stage == 0 ) { Worker.Recurse( QName, QT.SOA, this ); // Forces update of cached SOA Stage = 1; } else if ( Stage == 1 ) { RRset SOA = QName.Get( QT.ServerFail, DC.Wire ); // Make sure we detect ServerFail if ( SOA == null ) SOA = QName.Get( QT.SOA, DC.Wire ); if ( SOA == null ) Worker.Recurse( QName, QT.SOA, this ); else if ( SOA.Type == QT.SOA ) { Refresh = SOA_Help.GetRefresh(SOA); Retry = SOA_Help.GetRetry(SOA); RRset Old = QName.Get( QT.SOA, DC.Zone ); uint NewSerial = SOA_Help.GetSerial(SOA); bool Same = SOA_Help.GetSerial(Old) == NewSerial; #if (Trace) Log( "Got Serial " + NewSerial + " Same=" + Same ); #endif if ( Same ) Finish( true ); else { Worker.Recurse( QName, QT.AXFR, this ); Stage = 2; } } else { #if (Trace) Log( "Failed to fetch SOA for " + QName ); #endif Finish( false ); } } else // Stage == 2 { bool Ok = false; RRset X = QName.Get( QT.AXFR ); if ( X != null && X.Type == QT.AXFR ) { NamedRRset[] L = X.Extra.ToArray(); Loader.LoadZone( QName, L, ZoneSource.AXFR ); Loader.SaveToFile( QName, L ); Ok = true; } Finish( Ok ); } } } private static List TaskList = new List(); public Axfr( Domain Zone ) : base( Zone, QT.AXFR ) { Zone.RefCount += 1; Cache.SetClock(); NextRun = Cache.Clock + 5 + (uint)Random.Number(5); // Schedule to run in next 5-10 seconds TaskList.Add( this ); } public static void Run() { Cache.SetClock(); foreach ( Axfr A in TaskList ) { if ( !A.Running && Cache.Clock >= A.NextRun ) { A.Go(); return; } } } }