From d40844181a2522adf98ccf6e7b39341ac2685bed Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Fri, 2 Jun 2017 14:14:24 -0700 Subject: [PATCH] client: add fuzzy timer For randomizing update check intervals and backoff delays to reduce chance of DoSing the server if lots of clients start together. --- omaha/client/fuzzytime.go | 53 ++++++++++++++++++++++++++++++++++ omaha/client/fuzzytime_test.go | 32 ++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 omaha/client/fuzzytime.go create mode 100644 omaha/client/fuzzytime_test.go diff --git a/omaha/client/fuzzytime.go b/omaha/client/fuzzytime.go new file mode 100644 index 0000000..ca9669f --- /dev/null +++ b/omaha/client/fuzzytime.go @@ -0,0 +1,53 @@ +// Copyright 2017 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package client + +import ( + "math/rand" + "time" +) + +func init() { + // Ensure seeding the prng is never forgotten, that would defeat + // the whole point of using fuzzy timers to guard against a DoS. + rand.Seed(time.Now().UnixNano()) +} + +// FuzzyDuration randomizes the duration d within the range specified +// by fuzz. Specifically the value range is: [d-(fuzz/2), d+(fuzz/2)] +// The result will never be negative. +func FuzzyDuration(d, fuzz time.Duration) time.Duration { + if fuzz < 0 { + return d + } + // apply range [-fuzz/2, fuzz/2] + d += time.Duration(rand.Int63n(int64(fuzz)+1) - (int64(fuzz) / 2)) + if d < 0 { + return 0 + } + return d +} + +// FuzzyAfter waits for the fuzzy duration to elapse and then sends the +// current time on the returned channel. See FuzzyDuration. +func FuzzyAfter(d, fuzz time.Duration) <-chan time.Time { + return time.After(FuzzyDuration(d, fuzz)) +} + +// FuzzySleep pauses the current goroutine for the fuzzy duration d. +// See FuzzyDuration. +func FuzzySleep(d, fuzz time.Duration) { + time.Sleep(FuzzyDuration(d, fuzz)) +} diff --git a/omaha/client/fuzzytime_test.go b/omaha/client/fuzzytime_test.go new file mode 100644 index 0000000..1a6631d --- /dev/null +++ b/omaha/client/fuzzytime_test.go @@ -0,0 +1,32 @@ +// Copyright 2017 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package client + +import ( + "testing" + "time" +) + +func TestFuzzyDuration(t *testing.T) { + const d = time.Minute + for i := 0; i < 1000; i++ { + f := FuzzyDuration(d, d) + if f < d/2 { + t.Errorf("%d < %d", f, d/2) + } else if f > d+d/2 { + t.Errorf("%d > %d", f, d+d/2) + } + } +}