1
0

defender_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package common
  15. import (
  16. "encoding/hex"
  17. "fmt"
  18. "net"
  19. "testing"
  20. "time"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/stretchr/testify/require"
  23. "github.com/yl2chen/cidranger"
  24. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  25. )
  26. func TestBasicDefender(t *testing.T) {
  27. entries := []dataprovider.IPListEntry{
  28. {
  29. IPOrNet: "172.16.1.1/32",
  30. Type: dataprovider.IPListTypeDefender,
  31. Mode: dataprovider.ListModeDeny,
  32. },
  33. {
  34. IPOrNet: "172.16.1.2/32",
  35. Type: dataprovider.IPListTypeDefender,
  36. Mode: dataprovider.ListModeDeny,
  37. },
  38. {
  39. IPOrNet: "10.8.0.0/24",
  40. Type: dataprovider.IPListTypeDefender,
  41. Mode: dataprovider.ListModeDeny,
  42. },
  43. {
  44. IPOrNet: "192.168.1.1/32",
  45. Type: dataprovider.IPListTypeDefender,
  46. Mode: dataprovider.ListModeDeny,
  47. },
  48. {
  49. IPOrNet: "192.168.1.2/32",
  50. Type: dataprovider.IPListTypeDefender,
  51. Mode: dataprovider.ListModeDeny,
  52. },
  53. {
  54. IPOrNet: "10.8.9.0/24",
  55. Type: dataprovider.IPListTypeDefender,
  56. Mode: dataprovider.ListModeDeny,
  57. },
  58. {
  59. IPOrNet: "172.16.1.3/32",
  60. Type: dataprovider.IPListTypeDefender,
  61. Mode: dataprovider.ListModeAllow,
  62. },
  63. {
  64. IPOrNet: "172.16.1.4/32",
  65. Type: dataprovider.IPListTypeDefender,
  66. Mode: dataprovider.ListModeAllow,
  67. },
  68. {
  69. IPOrNet: "192.168.8.0/24",
  70. Type: dataprovider.IPListTypeDefender,
  71. Mode: dataprovider.ListModeAllow,
  72. },
  73. {
  74. IPOrNet: "192.168.1.3/32",
  75. Type: dataprovider.IPListTypeDefender,
  76. Mode: dataprovider.ListModeAllow,
  77. },
  78. {
  79. IPOrNet: "192.168.1.4/32",
  80. Type: dataprovider.IPListTypeDefender,
  81. Mode: dataprovider.ListModeAllow,
  82. },
  83. {
  84. IPOrNet: "192.168.9.0/24",
  85. Type: dataprovider.IPListTypeDefender,
  86. Mode: dataprovider.ListModeAllow,
  87. },
  88. }
  89. for idx := range entries {
  90. e := entries[idx]
  91. err := dataprovider.AddIPListEntry(&e, "", "", "")
  92. assert.NoError(t, err)
  93. }
  94. config := &DefenderConfig{
  95. Enabled: true,
  96. BanTime: 10,
  97. BanTimeIncrement: 2,
  98. Threshold: 5,
  99. ScoreInvalid: 2,
  100. ScoreValid: 1,
  101. ScoreNoAuth: 2,
  102. ScoreLimitExceeded: 3,
  103. ObservationTime: 15,
  104. EntriesSoftLimit: 1,
  105. EntriesHardLimit: 2,
  106. }
  107. d, err := newInMemoryDefender(config)
  108. assert.NoError(t, err)
  109. defender := d.(*memoryDefender)
  110. assert.True(t, defender.IsBanned("172.16.1.1", ProtocolSSH))
  111. assert.True(t, defender.IsBanned("192.168.1.1", ProtocolFTP))
  112. assert.False(t, defender.IsBanned("172.16.1.10", ProtocolSSH))
  113. assert.False(t, defender.IsBanned("192.168.1.10", ProtocolSSH))
  114. assert.False(t, defender.IsBanned("10.8.2.3", ProtocolSSH))
  115. assert.False(t, defender.IsBanned("10.9.2.3", ProtocolSSH))
  116. assert.True(t, defender.IsBanned("10.8.0.3", ProtocolSSH))
  117. assert.True(t, defender.IsBanned("10.8.9.3", ProtocolSSH))
  118. assert.False(t, defender.IsBanned("invalid ip", ProtocolSSH))
  119. assert.Equal(t, 0, defender.countBanned())
  120. assert.Equal(t, 0, defender.countHosts())
  121. hosts, err := defender.GetHosts()
  122. assert.NoError(t, err)
  123. assert.Len(t, hosts, 0)
  124. _, err = defender.GetHost("10.8.0.4")
  125. assert.Error(t, err)
  126. defender.AddEvent("172.16.1.4", ProtocolSSH, HostEventLoginFailed)
  127. defender.AddEvent("192.168.1.4", ProtocolSSH, HostEventLoginFailed)
  128. defender.AddEvent("192.168.8.4", ProtocolSSH, HostEventUserNotFound)
  129. defender.AddEvent("172.16.1.3", ProtocolSSH, HostEventLimitExceeded)
  130. defender.AddEvent("192.168.1.3", ProtocolSSH, HostEventLimitExceeded)
  131. assert.Equal(t, 0, defender.countHosts())
  132. testIP := "12.34.56.78"
  133. defender.AddEvent(testIP, ProtocolSSH, HostEventLoginFailed)
  134. assert.Equal(t, 1, defender.countHosts())
  135. assert.Equal(t, 0, defender.countBanned())
  136. score, err := defender.GetScore(testIP)
  137. assert.NoError(t, err)
  138. assert.Equal(t, 1, score)
  139. hosts, err = defender.GetHosts()
  140. assert.NoError(t, err)
  141. if assert.Len(t, hosts, 1) {
  142. assert.Equal(t, 1, hosts[0].Score)
  143. assert.True(t, hosts[0].BanTime.IsZero())
  144. assert.Empty(t, hosts[0].GetBanTime())
  145. }
  146. host, err := defender.GetHost(testIP)
  147. assert.NoError(t, err)
  148. assert.Equal(t, 1, host.Score)
  149. assert.Empty(t, host.GetBanTime())
  150. banTime, err := defender.GetBanTime(testIP)
  151. assert.NoError(t, err)
  152. assert.Nil(t, banTime)
  153. defender.AddEvent(testIP, ProtocolSSH, HostEventLimitExceeded)
  154. assert.Equal(t, 1, defender.countHosts())
  155. assert.Equal(t, 0, defender.countBanned())
  156. score, err = defender.GetScore(testIP)
  157. assert.NoError(t, err)
  158. assert.Equal(t, 4, score)
  159. hosts, err = defender.GetHosts()
  160. assert.NoError(t, err)
  161. if assert.Len(t, hosts, 1) {
  162. assert.Equal(t, 4, hosts[0].Score)
  163. assert.True(t, hosts[0].BanTime.IsZero())
  164. assert.Empty(t, hosts[0].GetBanTime())
  165. }
  166. defender.AddEvent(testIP, ProtocolSSH, HostEventUserNotFound)
  167. defender.AddEvent(testIP, ProtocolSSH, HostEventNoLoginTried)
  168. assert.Equal(t, 0, defender.countHosts())
  169. assert.Equal(t, 1, defender.countBanned())
  170. score, err = defender.GetScore(testIP)
  171. assert.NoError(t, err)
  172. assert.Equal(t, 0, score)
  173. banTime, err = defender.GetBanTime(testIP)
  174. assert.NoError(t, err)
  175. assert.NotNil(t, banTime)
  176. hosts, err = defender.GetHosts()
  177. assert.NoError(t, err)
  178. if assert.Len(t, hosts, 1) {
  179. assert.Equal(t, 0, hosts[0].Score)
  180. assert.False(t, hosts[0].BanTime.IsZero())
  181. assert.NotEmpty(t, hosts[0].GetBanTime())
  182. assert.Equal(t, hex.EncodeToString([]byte(testIP)), hosts[0].GetID())
  183. }
  184. host, err = defender.GetHost(testIP)
  185. assert.NoError(t, err)
  186. assert.Equal(t, 0, host.Score)
  187. assert.NotEmpty(t, host.GetBanTime())
  188. // now test cleanup, testIP is already banned
  189. testIP1 := "12.34.56.79"
  190. testIP2 := "12.34.56.80"
  191. testIP3 := "12.34.56.81"
  192. defender.AddEvent(testIP1, ProtocolSSH, HostEventNoLoginTried)
  193. defender.AddEvent(testIP2, ProtocolSSH, HostEventNoLoginTried)
  194. assert.Equal(t, 2, defender.countHosts())
  195. time.Sleep(20 * time.Millisecond)
  196. defender.AddEvent(testIP3, ProtocolSSH, HostEventNoLoginTried)
  197. assert.Equal(t, defender.config.EntriesSoftLimit, defender.countHosts())
  198. // testIP1 and testIP2 should be removed
  199. assert.Equal(t, defender.config.EntriesSoftLimit, defender.countHosts())
  200. score, err = defender.GetScore(testIP1)
  201. assert.NoError(t, err)
  202. assert.Equal(t, 0, score)
  203. score, err = defender.GetScore(testIP2)
  204. assert.NoError(t, err)
  205. assert.Equal(t, 0, score)
  206. score, err = defender.GetScore(testIP3)
  207. assert.NoError(t, err)
  208. assert.Equal(t, 2, score)
  209. defender.AddEvent(testIP3, ProtocolSSH, HostEventNoLoginTried)
  210. defender.AddEvent(testIP3, ProtocolSSH, HostEventNoLoginTried)
  211. // IP3 is now banned
  212. banTime, err = defender.GetBanTime(testIP3)
  213. assert.NoError(t, err)
  214. assert.NotNil(t, banTime)
  215. assert.Equal(t, 0, defender.countHosts())
  216. time.Sleep(20 * time.Millisecond)
  217. for i := 0; i < 3; i++ {
  218. defender.AddEvent(testIP1, ProtocolSSH, HostEventNoLoginTried)
  219. }
  220. assert.Equal(t, 0, defender.countHosts())
  221. assert.Equal(t, config.EntriesSoftLimit, defender.countBanned())
  222. banTime, err = defender.GetBanTime(testIP)
  223. assert.NoError(t, err)
  224. assert.Nil(t, banTime)
  225. banTime, err = defender.GetBanTime(testIP3)
  226. assert.NoError(t, err)
  227. assert.Nil(t, banTime)
  228. banTime, err = defender.GetBanTime(testIP1)
  229. assert.NoError(t, err)
  230. assert.NotNil(t, banTime)
  231. for i := 0; i < 3; i++ {
  232. defender.AddEvent(testIP, ProtocolSSH, HostEventNoLoginTried)
  233. time.Sleep(10 * time.Millisecond)
  234. defender.AddEvent(testIP3, ProtocolSSH, HostEventNoLoginTried)
  235. }
  236. assert.Equal(t, 0, defender.countHosts())
  237. assert.Equal(t, defender.config.EntriesSoftLimit, defender.countBanned())
  238. banTime, err = defender.GetBanTime(testIP3)
  239. assert.NoError(t, err)
  240. if assert.NotNil(t, banTime) {
  241. assert.True(t, defender.IsBanned(testIP3, ProtocolFTP))
  242. // ban time should increase
  243. newBanTime, err := defender.GetBanTime(testIP3)
  244. assert.NoError(t, err)
  245. assert.True(t, newBanTime.After(*banTime))
  246. }
  247. assert.True(t, defender.DeleteHost(testIP3))
  248. assert.False(t, defender.DeleteHost(testIP3))
  249. for _, e := range entries {
  250. err := dataprovider.DeleteIPListEntry(e.IPOrNet, e.Type, "", "", "")
  251. assert.NoError(t, err)
  252. }
  253. }
  254. func TestExpiredHostBans(t *testing.T) {
  255. config := &DefenderConfig{
  256. Enabled: true,
  257. BanTime: 10,
  258. BanTimeIncrement: 2,
  259. Threshold: 5,
  260. ScoreInvalid: 2,
  261. ScoreValid: 1,
  262. ScoreLimitExceeded: 3,
  263. ObservationTime: 15,
  264. EntriesSoftLimit: 1,
  265. EntriesHardLimit: 2,
  266. }
  267. d, err := newInMemoryDefender(config)
  268. assert.NoError(t, err)
  269. defender := d.(*memoryDefender)
  270. testIP := "1.2.3.4"
  271. defender.banned[testIP] = time.Now().Add(-24 * time.Hour)
  272. // the ban is expired testIP should not be listed
  273. res, err := defender.GetHosts()
  274. assert.NoError(t, err)
  275. assert.Len(t, res, 0)
  276. assert.False(t, defender.IsBanned(testIP, ProtocolFTP))
  277. _, err = defender.GetHost(testIP)
  278. assert.Error(t, err)
  279. _, ok := defender.banned[testIP]
  280. assert.True(t, ok)
  281. // now add an event for an expired banned ip, it should be removed
  282. defender.AddEvent(testIP, ProtocolFTP, HostEventLoginFailed)
  283. assert.False(t, defender.IsBanned(testIP, ProtocolFTP))
  284. entry, err := defender.GetHost(testIP)
  285. assert.NoError(t, err)
  286. assert.Equal(t, testIP, entry.IP)
  287. assert.Empty(t, entry.GetBanTime())
  288. assert.Equal(t, 1, entry.Score)
  289. res, err = defender.GetHosts()
  290. assert.NoError(t, err)
  291. if assert.Len(t, res, 1) {
  292. assert.Equal(t, testIP, res[0].IP)
  293. assert.Empty(t, res[0].GetBanTime())
  294. assert.Equal(t, 1, res[0].Score)
  295. }
  296. events := []hostEvent{
  297. {
  298. dateTime: time.Now().Add(-24 * time.Hour),
  299. score: 2,
  300. },
  301. {
  302. dateTime: time.Now().Add(-24 * time.Hour),
  303. score: 3,
  304. },
  305. }
  306. hs := hostScore{
  307. Events: events,
  308. TotalScore: 5,
  309. }
  310. defender.hosts[testIP] = hs
  311. // the recorded scored are too old
  312. res, err = defender.GetHosts()
  313. assert.NoError(t, err)
  314. assert.Len(t, res, 0)
  315. _, err = defender.GetHost(testIP)
  316. assert.Error(t, err)
  317. _, ok = defender.hosts[testIP]
  318. assert.True(t, ok)
  319. }
  320. func TestDefenderCleanup(t *testing.T) {
  321. d := memoryDefender{
  322. baseDefender: baseDefender{
  323. config: &DefenderConfig{
  324. ObservationTime: 1,
  325. EntriesSoftLimit: 2,
  326. EntriesHardLimit: 3,
  327. },
  328. },
  329. banned: make(map[string]time.Time),
  330. hosts: make(map[string]hostScore),
  331. }
  332. d.banned["1.1.1.1"] = time.Now().Add(-24 * time.Hour)
  333. d.banned["1.1.1.2"] = time.Now().Add(-24 * time.Hour)
  334. d.banned["1.1.1.3"] = time.Now().Add(-24 * time.Hour)
  335. d.banned["1.1.1.4"] = time.Now().Add(-24 * time.Hour)
  336. d.cleanupBanned()
  337. assert.Equal(t, 0, d.countBanned())
  338. d.banned["2.2.2.2"] = time.Now().Add(2 * time.Minute)
  339. d.banned["2.2.2.3"] = time.Now().Add(1 * time.Minute)
  340. d.banned["2.2.2.4"] = time.Now().Add(3 * time.Minute)
  341. d.banned["2.2.2.5"] = time.Now().Add(4 * time.Minute)
  342. d.cleanupBanned()
  343. assert.Equal(t, d.config.EntriesSoftLimit, d.countBanned())
  344. banTime, err := d.GetBanTime("2.2.2.3")
  345. assert.NoError(t, err)
  346. assert.Nil(t, banTime)
  347. d.hosts["3.3.3.3"] = hostScore{
  348. TotalScore: 0,
  349. Events: []hostEvent{
  350. {
  351. dateTime: time.Now().Add(-5 * time.Minute),
  352. score: 1,
  353. },
  354. {
  355. dateTime: time.Now().Add(-3 * time.Minute),
  356. score: 1,
  357. },
  358. {
  359. dateTime: time.Now(),
  360. score: 1,
  361. },
  362. },
  363. }
  364. d.hosts["3.3.3.4"] = hostScore{
  365. TotalScore: 1,
  366. Events: []hostEvent{
  367. {
  368. dateTime: time.Now().Add(-3 * time.Minute),
  369. score: 1,
  370. },
  371. },
  372. }
  373. d.hosts["3.3.3.5"] = hostScore{
  374. TotalScore: 1,
  375. Events: []hostEvent{
  376. {
  377. dateTime: time.Now().Add(-2 * time.Minute),
  378. score: 1,
  379. },
  380. },
  381. }
  382. d.hosts["3.3.3.6"] = hostScore{
  383. TotalScore: 1,
  384. Events: []hostEvent{
  385. {
  386. dateTime: time.Now().Add(-1 * time.Minute),
  387. score: 1,
  388. },
  389. },
  390. }
  391. score, err := d.GetScore("3.3.3.3")
  392. assert.NoError(t, err)
  393. assert.Equal(t, 1, score)
  394. d.cleanupHosts()
  395. assert.Equal(t, d.config.EntriesSoftLimit, d.countHosts())
  396. score, err = d.GetScore("3.3.3.4")
  397. assert.NoError(t, err)
  398. assert.Equal(t, 0, score)
  399. }
  400. func TestDefenderConfig(t *testing.T) {
  401. c := DefenderConfig{}
  402. err := c.validate()
  403. require.NoError(t, err)
  404. c.Enabled = true
  405. c.Threshold = 10
  406. c.ScoreInvalid = 10
  407. err = c.validate()
  408. require.Error(t, err)
  409. c.ScoreInvalid = 2
  410. c.ScoreLimitExceeded = 10
  411. err = c.validate()
  412. require.Error(t, err)
  413. c.ScoreLimitExceeded = 2
  414. c.ScoreValid = 10
  415. err = c.validate()
  416. require.Error(t, err)
  417. c.ScoreValid = 1
  418. c.ScoreNoAuth = 10
  419. err = c.validate()
  420. require.Error(t, err)
  421. c.ScoreNoAuth = 2
  422. c.BanTime = 0
  423. err = c.validate()
  424. require.Error(t, err)
  425. c.BanTime = 30
  426. c.BanTimeIncrement = 0
  427. err = c.validate()
  428. require.Error(t, err)
  429. c.BanTimeIncrement = 50
  430. c.ObservationTime = 0
  431. err = c.validate()
  432. require.Error(t, err)
  433. c.ObservationTime = 30
  434. err = c.validate()
  435. require.Error(t, err)
  436. c.EntriesSoftLimit = 10
  437. err = c.validate()
  438. require.Error(t, err)
  439. c.EntriesHardLimit = 10
  440. err = c.validate()
  441. require.Error(t, err)
  442. c.EntriesHardLimit = 20
  443. err = c.validate()
  444. require.NoError(t, err)
  445. c = DefenderConfig{
  446. Enabled: true,
  447. ScoreInvalid: -1,
  448. ScoreLimitExceeded: -1,
  449. ScoreNoAuth: -1,
  450. ScoreValid: -1,
  451. }
  452. err = c.validate()
  453. require.Error(t, err)
  454. assert.Equal(t, 0, c.ScoreInvalid)
  455. assert.Equal(t, 0, c.ScoreValid)
  456. assert.Equal(t, 0, c.ScoreLimitExceeded)
  457. assert.Equal(t, 0, c.ScoreNoAuth)
  458. }
  459. func BenchmarkDefenderBannedSearch(b *testing.B) {
  460. d := getDefenderForBench()
  461. ip, ipnet, err := net.ParseCIDR("10.8.0.0/12") // 1048574 ip addresses
  462. if err != nil {
  463. panic(err)
  464. }
  465. for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
  466. d.banned[ip.String()] = time.Now().Add(10 * time.Minute)
  467. }
  468. b.ResetTimer()
  469. for i := 0; i < b.N; i++ {
  470. d.IsBanned("192.168.1.1", ProtocolSSH)
  471. }
  472. }
  473. func BenchmarkCleanup(b *testing.B) {
  474. d := getDefenderForBench()
  475. ip, ipnet, err := net.ParseCIDR("192.168.4.0/24")
  476. if err != nil {
  477. panic(err)
  478. }
  479. b.ResetTimer()
  480. for i := 0; i < b.N; i++ {
  481. for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
  482. d.AddEvent(ip.String(), ProtocolSSH, HostEventLoginFailed)
  483. if d.countHosts() > d.config.EntriesHardLimit {
  484. panic("too many hosts")
  485. }
  486. if d.countBanned() > d.config.EntriesSoftLimit {
  487. panic("too many ip banned")
  488. }
  489. }
  490. }
  491. }
  492. func BenchmarkCIDRanger(b *testing.B) {
  493. ranger := cidranger.NewPCTrieRanger()
  494. for i := 0; i < 255; i++ {
  495. cidr := fmt.Sprintf("192.168.%d.1/24", i)
  496. _, network, _ := net.ParseCIDR(cidr)
  497. if err := ranger.Insert(cidranger.NewBasicRangerEntry(*network)); err != nil {
  498. panic(err)
  499. }
  500. }
  501. ipToMatch := net.ParseIP("192.167.1.2")
  502. b.ResetTimer()
  503. for i := 0; i < b.N; i++ {
  504. if _, err := ranger.Contains(ipToMatch); err != nil {
  505. panic(err)
  506. }
  507. }
  508. }
  509. func BenchmarkNetContains(b *testing.B) {
  510. var nets []*net.IPNet
  511. for i := 0; i < 255; i++ {
  512. cidr := fmt.Sprintf("192.168.%d.1/24", i)
  513. _, network, _ := net.ParseCIDR(cidr)
  514. nets = append(nets, network)
  515. }
  516. ipToMatch := net.ParseIP("192.167.1.1")
  517. b.ResetTimer()
  518. for i := 0; i < b.N; i++ {
  519. for _, n := range nets {
  520. n.Contains(ipToMatch)
  521. }
  522. }
  523. }
  524. func getDefenderForBench() *memoryDefender {
  525. config := &DefenderConfig{
  526. Enabled: true,
  527. BanTime: 30,
  528. BanTimeIncrement: 50,
  529. Threshold: 10,
  530. ScoreInvalid: 2,
  531. ScoreValid: 2,
  532. ObservationTime: 30,
  533. EntriesSoftLimit: 50,
  534. EntriesHardLimit: 100,
  535. }
  536. return &memoryDefender{
  537. baseDefender: baseDefender{
  538. config: config,
  539. },
  540. hosts: make(map[string]hostScore),
  541. banned: make(map[string]time.Time),
  542. }
  543. }
  544. func inc(ip net.IP) {
  545. for j := len(ip) - 1; j >= 0; j-- {
  546. ip[j]++
  547. if ip[j] > 0 {
  548. break
  549. }
  550. }
  551. }