| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | package file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"log" | 
					
						
							| 
									
										
										
										
											2017-08-06 02:22:50 -07:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 	"github.com/miekg/dns" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TransferIn retrieves the zone from the masters, parses it and sets it live. | 
					
						
							|  |  |  | func (z *Zone) TransferIn() error { | 
					
						
							|  |  |  | 	if len(z.TransferFrom) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	m := new(dns.Msg) | 
					
						
							| 
									
										
										
										
											2016-04-15 14:26:27 +01:00
										 |  |  | 	m.SetAxfr(z.origin) | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	z1 := z.Copy() | 
					
						
							| 
									
										
										
										
											2016-04-13 20:14:03 +01:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		Err error | 
					
						
							|  |  |  | 		tr  string | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | Transfer: | 
					
						
							| 
									
										
										
										
											2016-04-13 20:14:03 +01:00
										 |  |  | 	for _, tr = range z.TransferFrom { | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 		t := new(dns.Transfer) | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 		c, err := t.In(m, tr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-21 23:46:20 -07:00
										 |  |  | 			log.Printf("[ERROR] Failed to setup transfer `%s' with `%q': %v", z.origin, tr, err) | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 			Err = err | 
					
						
							|  |  |  | 			continue Transfer | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for env := range c { | 
					
						
							|  |  |  | 			if env.Error != nil { | 
					
						
							| 
									
										
										
										
											2017-06-21 23:46:20 -07:00
										 |  |  | 				log.Printf("[ERROR] Failed to transfer `%s' from %q: %v", z.origin, tr, env.Error) | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 				Err = env.Error | 
					
						
							|  |  |  | 				continue Transfer | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, rr := range env.RR { | 
					
						
							| 
									
										
										
										
											2016-04-15 14:26:27 +01:00
										 |  |  | 				if err := z1.Insert(rr); err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-21 23:46:20 -07:00
										 |  |  | 					log.Printf("[ERROR] Failed to parse transfer `%s' from: %q: %v", z.origin, tr, err) | 
					
						
							| 
									
										
										
										
											2016-04-15 17:08:31 +01:00
										 |  |  | 					Err = err | 
					
						
							|  |  |  | 					continue Transfer | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 		Err = nil | 
					
						
							|  |  |  | 		break | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-05 07:37:05 +01:00
										 |  |  | 	if Err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-13 20:14:03 +01:00
										 |  |  | 		return Err | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	z.Tree = z1.Tree | 
					
						
							| 
									
										
										
										
											2016-04-16 16:16:52 +01:00
										 |  |  | 	z.Apex = z1.Apex | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	*z.Expired = false | 
					
						
							| 
									
										
										
										
											2016-04-15 14:26:27 +01:00
										 |  |  | 	log.Printf("[INFO] Transferred: %s from %s", z.origin, tr) | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // shouldTransfer checks the primaries of zone, retrieves the SOA record, checks the current serial | 
					
						
							|  |  |  | // and the remote serial and will return true if the remote one is higher than the locally configured one. | 
					
						
							|  |  |  | func (z *Zone) shouldTransfer() (bool, error) { | 
					
						
							|  |  |  | 	c := new(dns.Client) | 
					
						
							|  |  |  | 	c.Net = "tcp" // do this query over TCP to minimize spoofing | 
					
						
							|  |  |  | 	m := new(dns.Msg) | 
					
						
							| 
									
										
										
										
											2016-04-15 14:26:27 +01:00
										 |  |  | 	m.SetQuestion(z.origin, dns.TypeSOA) | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var Err error | 
					
						
							|  |  |  | 	serial := -1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | Transfer: | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 	for _, tr := range z.TransferFrom { | 
					
						
							|  |  |  | 		Err = nil | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 		ret, _, err := c.Exchange(m, tr) | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 		if err != nil || ret.Rcode != dns.RcodeSuccess { | 
					
						
							|  |  |  | 			Err = err | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, a := range ret.Answer { | 
					
						
							|  |  |  | 			if a.Header().Rrtype == dns.TypeSOA { | 
					
						
							|  |  |  | 				serial = int(a.(*dns.SOA).Serial) | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 				break Transfer | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if serial == -1 { | 
					
						
							|  |  |  | 		return false, Err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-24 10:51:20 -04:00
										 |  |  | 	if z.Apex.SOA == nil { | 
					
						
							|  |  |  | 		return true, Err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-16 16:16:52 +01:00
										 |  |  | 	return less(z.Apex.SOA.Serial, uint32(serial)), Err | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // less return true of a is smaller than b when taking RFC 1982 serial arithmetic into account. | 
					
						
							|  |  |  | func less(a, b uint32) bool { | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 	if a < b { | 
					
						
							|  |  |  | 		return (b - a) <= MaxSerialIncrement | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return (a - b) > MaxSerialIncrement | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Update updates the secondary zone according to its SOA. It will run for the life time of the server | 
					
						
							|  |  |  | // and uses the SOA parameters. Every refresh it will check for a new SOA number. If that fails (for all | 
					
						
							|  |  |  | // server) it wil retry every retry interval. If the zone failed to transfer before the expire, the zone | 
					
						
							|  |  |  | // will be marked expired. | 
					
						
							|  |  |  | func (z *Zone) Update() error { | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 	// If we don't have a SOA, we don't have a zone, wait for it to appear. | 
					
						
							| 
									
										
										
										
											2016-04-16 16:16:52 +01:00
										 |  |  | 	for z.Apex.SOA == nil { | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 		time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 	retryActive := false | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | Restart: | 
					
						
							| 
									
										
										
										
											2016-04-16 16:16:52 +01:00
										 |  |  | 	refresh := time.Second * time.Duration(z.Apex.SOA.Refresh) | 
					
						
							|  |  |  | 	retry := time.Second * time.Duration(z.Apex.SOA.Retry) | 
					
						
							|  |  |  | 	expire := time.Second * time.Duration(z.Apex.SOA.Expire) | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if refresh < time.Hour { | 
					
						
							|  |  |  | 		refresh = time.Hour | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if retry < time.Hour { | 
					
						
							|  |  |  | 		retry = time.Hour | 
					
						
							| 
									
										
										
										
											2016-04-05 07:37:05 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 	if refresh > 24*time.Hour { | 
					
						
							|  |  |  | 		refresh = 24 * time.Hour | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if retry > 12*time.Hour { | 
					
						
							|  |  |  | 		retry = 12 * time.Hour | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	refreshTicker := time.NewTicker(refresh) | 
					
						
							|  |  |  | 	retryTicker := time.NewTicker(retry) | 
					
						
							|  |  |  | 	expireTicker := time.NewTicker(expire) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-expireTicker.C: | 
					
						
							|  |  |  | 			if !retryActive { | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			*z.Expired = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case <-retryTicker.C: | 
					
						
							|  |  |  | 			if !retryActive { | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-08-06 02:22:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			time.Sleep(jitter(2000)) // 2s randomize | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 			ok, err := z.shouldTransfer() | 
					
						
							|  |  |  | 			if err != nil && ok { | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 				if err := z.TransferIn(); err != nil { | 
					
						
							|  |  |  | 					// transfer failed, leave retryActive true | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 				retryActive = false | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 				// transfer OK, possible new SOA, stop timers and redo | 
					
						
							|  |  |  | 				refreshTicker.Stop() | 
					
						
							|  |  |  | 				retryTicker.Stop() | 
					
						
							|  |  |  | 				expireTicker.Stop() | 
					
						
							|  |  |  | 				goto Restart | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case <-refreshTicker.C: | 
					
						
							| 
									
										
										
										
											2017-08-06 02:22:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			time.Sleep(jitter(5000)) // 5s randomize | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 			ok, err := z.shouldTransfer() | 
					
						
							|  |  |  | 			retryActive = err != nil | 
					
						
							|  |  |  | 			if err != nil && ok { | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 				if err := z.TransferIn(); err != nil { | 
					
						
							|  |  |  | 					// transfer failed | 
					
						
							|  |  |  | 					retryActive = true | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				retryActive = false | 
					
						
							|  |  |  | 				// transfer OK, possible new SOA, stop timers and redo | 
					
						
							|  |  |  | 				refreshTicker.Stop() | 
					
						
							|  |  |  | 				retryTicker.Stop() | 
					
						
							|  |  |  | 				expireTicker.Stop() | 
					
						
							|  |  |  | 				goto Restart | 
					
						
							| 
									
										
										
										
											2016-04-05 10:53:23 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-03 09:02:34 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 02:22:50 -07:00
										 |  |  | // jitter returns a random duration between [0,n) * time.Millisecond | 
					
						
							|  |  |  | func jitter(n int) time.Duration { | 
					
						
							|  |  |  | 	r := rand.Intn(n) | 
					
						
							|  |  |  | 	return time.Duration(r) * time.Millisecond | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-23 09:14:12 +01:00
										 |  |  | // MaxSerialIncrement is the maximum difference between two serial numbers. If the difference between | 
					
						
							|  |  |  | // two serials is greater than this number, the smaller one is considered greater. | 
					
						
							| 
									
										
										
										
											2016-04-06 22:29:33 +01:00
										 |  |  | const MaxSerialIncrement uint32 = 2147483647 |