From 564d9cbf9087b4f9dcf79d59b535954bfac50ab7 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Fri, 18 Sep 2020 15:52:28 -0400 Subject: [PATCH] Faster servers information updater (#248) * Asynchronous repeatResolve * Parallel cyberghost and PIA (v3) processing, with a 10 goroutines limit * Add missing vyprvpn cli flag to updater * Increase DNS repetitions to 5 in order to obtain more IP addresses * Update old PIA IP addresses * Add Surfshark servers by API (unused for now) --- internal/cli/cli.go | 1 + internal/constants/pia.go | 66 +++++++++++++------------- internal/constants/servers.go | 2 +- internal/constants/servers_test.go | 2 +- internal/updater/cyberghost.go | 43 +++++++++++++---- internal/updater/pia.go | 62 ++++++++++++++++++------ internal/updater/purevpn.go | 2 +- internal/updater/resolver.go | 51 +++++++++++++++++--- internal/updater/resolver_test.go | 75 ++++++++++++++++++++++++++++++ internal/updater/surfshark.go | 47 ++++++++++++++++++- internal/updater/vyprvpn.go | 3 +- internal/updater/windscribe.go | 3 +- 12 files changed, 286 insertions(+), 71 deletions(-) create mode 100644 internal/updater/resolver_test.go diff --git a/internal/cli/cli.go b/internal/cli/cli.go index fb88afcb..2896b2ab 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -103,6 +103,7 @@ func Update(args []string) error { flagSet.BoolVar(&options.PIAold, "piaold", false, "Update Private Internet Access pre-summer 2020 servers") flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers") flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers") + flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers") flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers") if err := flagSet.Parse(args); err != nil { return err diff --git a/internal/constants/pia.go b/internal/constants/pia.go index d02ba836..cabe96da 100644 --- a/internal/constants/pia.go +++ b/internal/constants/pia.go @@ -124,9 +124,9 @@ func PIAOldGeoChoices() (choices []string) { func PIAOldServers() []models.PIAServer { return []models.PIAServer{ - {Region: "AU Melbourne", IPs: []net.IP{{27, 50, 82, 131}, {27, 50, 82, 133}, {43, 250, 204, 85}, {43, 250, 204, 87}, {43, 250, 204, 89}, {43, 250, 204, 91}, {43, 250, 204, 93}, {43, 250, 204, 95}, {43, 250, 204, 97}, {43, 250, 204, 99}, {43, 250, 204, 105}, {43, 250, 204, 107}, {43, 250, 204, 109}, {43, 250, 204, 111}, {43, 250, 204, 113}, {43, 250, 204, 115}, {43, 250, 204, 117}, {43, 250, 204, 119}, {43, 250, 204, 123}, {43, 250, 204, 125}}}, - {Region: "AU Perth", IPs: []net.IP{{43, 250, 205, 59}, {43, 250, 205, 93}, {43, 250, 205, 95}}}, - {Region: "AU Sydney", IPs: []net.IP{{27, 50, 68, 23}, {27, 50, 77, 251}, {27, 50, 81, 117}, {103, 13, 102, 117}, {103, 13, 102, 121}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 51}, {221, 121, 145, 131}, {221, 121, 145, 135}, {221, 121, 145, 137}, {221, 121, 145, 143}, {221, 121, 145, 145}, {221, 121, 145, 147}, {221, 121, 145, 151}, {221, 121, 145, 159}, {221, 121, 146, 203}, {221, 121, 146, 217}, {221, 121, 148, 221}, {221, 121, 152, 215}}}, + {Region: "AU Melbourne", IPs: []net.IP{{27, 50, 82, 131}, {27, 50, 82, 133}, {43, 250, 204, 105}, {43, 250, 204, 107}, {43, 250, 204, 109}, {43, 250, 204, 111}, {43, 250, 204, 113}, {43, 250, 204, 115}, {43, 250, 204, 117}, {43, 250, 204, 119}, {43, 250, 204, 123}, {43, 250, 204, 125}}}, + {Region: "AU Perth", IPs: []net.IP{{43, 250, 205, 59}, {43, 250, 205, 91}, {43, 250, 205, 93}, {43, 250, 205, 95}}}, + {Region: "AU Sydney", IPs: []net.IP{{27, 50, 68, 23}, {27, 50, 70, 87}, {27, 50, 77, 251}, {27, 50, 81, 117}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 43}, {118, 127, 60, 51}, {221, 121, 145, 137}, {221, 121, 145, 145}, {221, 121, 145, 147}, {221, 121, 145, 159}, {221, 121, 146, 203}, {221, 121, 148, 221}, {221, 121, 152, 215}}}, {Region: "Albania", IPs: []net.IP{{31, 171, 154, 114}}}, {Region: "Argentina", IPs: []net.IP{{190, 106, 134, 100}}}, {Region: "Austria", IPs: []net.IP{{89, 187, 168, 6}, {156, 146, 60, 129}}}, @@ -134,61 +134,61 @@ func PIAOldServers() []models.PIAServer { {Region: "Bosnia and Herzegovina", IPs: []net.IP{{185, 164, 35, 54}}}, {Region: "Bulgaria", IPs: []net.IP{{217, 138, 221, 66}}}, {Region: "CA Montreal", IPs: []net.IP{{172, 98, 71, 194}, {199, 36, 223, 130}, {199, 36, 223, 194}}}, - {Region: "CA Ontario", IPs: []net.IP{{162, 219, 176, 194}, {162, 253, 128, 98}, {184, 75, 208, 18}, {184, 75, 208, 34}, {184, 75, 208, 66}, {184, 75, 208, 90}, {184, 75, 208, 114}, {184, 75, 208, 122}, {184, 75, 208, 130}, {184, 75, 208, 170}, {184, 75, 208, 202}, {184, 75, 210, 18}, {184, 75, 210, 66}, {184, 75, 210, 106}, {184, 75, 210, 194}, {184, 75, 214, 18}, {184, 75, 215, 18}, {184, 75, 215, 26}, {184, 75, 215, 66}, {184, 75, 215, 74}}}, - {Region: "CA Toronto", IPs: []net.IP{{66, 115, 142, 130}, {66, 115, 145, 199}, {172, 98, 92, 66}, {172, 98, 92, 130}, {172, 98, 92, 194}}}, + {Region: "CA Ontario", IPs: []net.IP{{162, 219, 176, 42}, {162, 219, 176, 130}, {162, 219, 176, 194}, {184, 75, 208, 2}, {184, 75, 208, 18}, {184, 75, 208, 34}, {184, 75, 208, 66}, {184, 75, 208, 90}, {184, 75, 208, 114}, {184, 75, 208, 122}, {184, 75, 208, 130}, {184, 75, 208, 146}, {184, 75, 208, 170}, {184, 75, 210, 18}, {184, 75, 210, 194}, {184, 75, 214, 18}, {184, 75, 215, 18}, {184, 75, 215, 26}, {184, 75, 215, 66}, {184, 75, 215, 74}}}, + {Region: "CA Toronto", IPs: []net.IP{{66, 115, 142, 130}, {172, 98, 92, 66}, {172, 98, 92, 130}}}, {Region: "CA Vancouver", IPs: []net.IP{{162, 216, 47, 66}, {162, 216, 47, 194}, {172, 98, 89, 130}, {172, 98, 89, 194}}}, - {Region: "Czech Republic", IPs: []net.IP{{212, 102, 39, 1}}}, - {Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 226}, {185, 230, 127, 227}, {185, 230, 127, 228}, {185, 230, 127, 229}, {185, 230, 127, 230}, {185, 230, 127, 231}, {185, 230, 127, 232}, {185, 230, 127, 236}, {185, 230, 127, 237}, {185, 230, 127, 239}, {185, 230, 127, 240}, {185, 230, 127, 241}, {185, 230, 127, 243}, {193, 176, 86, 125}, {193, 176, 86, 138}, {193, 176, 86, 146}, {193, 176, 86, 154}, {193, 176, 86, 158}, {193, 176, 86, 162}, {193, 176, 86, 174}}}, - {Region: "DE Frankfurt", IPs: []net.IP{{195, 181, 170, 225}, {195, 181, 170, 239}, {195, 181, 170, 240}, {195, 181, 170, 241}, {195, 181, 170, 242}, {195, 181, 170, 243}, {195, 181, 170, 244}, {212, 102, 57, 138}}}, + {Region: "Czech Republic", IPs: []net.IP{{185, 216, 35, 66}, {212, 102, 39, 1}}}, + {Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 230}, {185, 230, 127, 231}, {185, 230, 127, 235}, {185, 230, 127, 236}, {185, 230, 127, 237}, {185, 230, 127, 238}, {185, 230, 127, 239}, {185, 230, 127, 241}, {193, 176, 86, 122}, {193, 176, 86, 124}, {193, 176, 86, 130}, {193, 176, 86, 134}, {193, 176, 86, 142}, {193, 176, 86, 150}, {193, 176, 86, 154}, {193, 176, 86, 166}, {193, 176, 86, 170}, {193, 176, 86, 174}, {193, 176, 86, 178}, {194, 36, 108, 6}}}, + {Region: "DE Frankfurt", IPs: []net.IP{{195, 181, 170, 225}, {195, 181, 170, 239}, {195, 181, 170, 240}, {195, 181, 170, 241}, {195, 181, 170, 242}, {212, 102, 57, 138}}}, {Region: "Denmark", IPs: []net.IP{{188, 126, 94, 34}}}, - {Region: "Estonia", IPs: []net.IP{{77, 247, 111, 82}, {77, 247, 111, 98}, {77, 247, 111, 114}}}, - {Region: "Finland", IPs: []net.IP{{188, 126, 89, 4}}}, - {Region: "France", IPs: []net.IP{{156, 146, 63, 1}}}, + {Region: "Estonia", IPs: []net.IP{{77, 247, 111, 98}}}, + {Region: "Finland", IPs: []net.IP{{188, 126, 89, 194}}}, + {Region: "France", IPs: []net.IP{{156, 146, 63, 1}, {156, 146, 63, 65}}}, {Region: "Greece", IPs: []net.IP{{154, 57, 3, 91}, {154, 57, 3, 106}, {154, 57, 3, 145}}}, - {Region: "Hungary", IPs: []net.IP{{185, 128, 26, 18}, {185, 128, 26, 19}, {185, 128, 26, 20}, {185, 128, 26, 21}, {185, 128, 26, 22}, {185, 128, 26, 23}, {185, 128, 26, 24}}}, + {Region: "Hungary", IPs: []net.IP{{185, 128, 26, 24}}}, {Region: "Iceland", IPs: []net.IP{{45, 133, 193, 50}, {45, 133, 193, 66}}}, {Region: "India", IPs: []net.IP{{150, 242, 12, 155}, {150, 242, 12, 171}, {150, 242, 12, 187}}}, - {Region: "Ireland", IPs: []net.IP{{23, 92, 127, 2}, {23, 92, 127, 10}, {23, 92, 127, 18}, {23, 92, 127, 34}, {23, 92, 127, 42}, {23, 92, 127, 50}}}, - {Region: "Israel", IPs: []net.IP{{31, 168, 172, 142}, {31, 168, 172, 143}, {31, 168, 172, 145}, {31, 168, 172, 146}}}, + {Region: "Ireland", IPs: []net.IP{{193, 56, 252, 210}, {193, 56, 252, 226}, {193, 56, 252, 242}}}, + {Region: "Israel", IPs: []net.IP{{31, 168, 172, 145}}}, {Region: "Italy", IPs: []net.IP{{156, 146, 41, 129}, {156, 146, 41, 193}}}, {Region: "Japan", IPs: []net.IP{{156, 146, 34, 1}, {156, 146, 34, 65}}}, {Region: "Latvia", IPs: []net.IP{{46, 183, 217, 34}, {46, 183, 218, 130}, {46, 183, 218, 146}}}, {Region: "Lithuania", IPs: []net.IP{{85, 206, 165, 96}, {85, 206, 165, 112}, {85, 206, 165, 128}}}, {Region: "Luxembourg", IPs: []net.IP{{92, 223, 89, 134}, {92, 223, 89, 135}, {92, 223, 89, 136}, {92, 223, 89, 137}, {92, 223, 89, 138}, {92, 223, 89, 140}}}, {Region: "Moldova", IPs: []net.IP{{178, 17, 172, 242}, {178, 17, 173, 194}, {178, 175, 128, 34}}}, - {Region: "Netherlands", IPs: []net.IP{{212, 102, 35, 103}}}, + {Region: "Netherlands", IPs: []net.IP{{89, 187, 174, 198}, {212, 102, 35, 101}, {212, 102, 35, 102}, {212, 102, 35, 103}, {212, 102, 35, 104}}}, {Region: "New Zealand", IPs: []net.IP{{43, 250, 207, 1}, {43, 250, 207, 3}}}, {Region: "North Macedonia", IPs: []net.IP{{185, 225, 28, 130}}}, {Region: "Norway", IPs: []net.IP{{46, 246, 122, 34}, {46, 246, 122, 162}}}, - {Region: "Poland", IPs: []net.IP{{185, 244, 214, 195}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}}}, + {Region: "Poland", IPs: []net.IP{{185, 244, 214, 195}, {185, 244, 214, 196}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}, {185, 244, 214, 200}}}, {Region: "Portugal", IPs: []net.IP{{89, 26, 241, 86}, {89, 26, 241, 102}, {89, 26, 241, 130}}}, - {Region: "Romania", IPs: []net.IP{{86, 105, 25, 70}, {86, 105, 25, 75}, {86, 105, 25, 76}, {86, 105, 25, 77}, {94, 176, 148, 35}, {143, 244, 54, 1}, {185, 210, 218, 99}, {185, 210, 218, 101}, {185, 210, 218, 103}, {185, 210, 218, 104}}}, + {Region: "Romania", IPs: []net.IP{{86, 105, 25, 69}, {86, 105, 25, 70}, {86, 105, 25, 74}, {86, 105, 25, 75}, {86, 105, 25, 77}, {86, 105, 25, 78}, {89, 33, 8, 38}, {89, 33, 8, 42}, {93, 115, 7, 70}, {94, 176, 148, 34}, {143, 244, 54, 1}, {185, 45, 12, 126}, {185, 210, 218, 98}, {185, 210, 218, 99}, {185, 210, 218, 100}, {185, 210, 218, 101}, {185, 210, 218, 104}, {185, 210, 218, 105}, {185, 210, 218, 108}, {188, 240, 220, 26}}}, {Region: "Serbia", IPs: []net.IP{{37, 120, 193, 226}}}, {Region: "Singapore", IPs: []net.IP{{156, 146, 56, 193}, {156, 146, 57, 38}, {156, 146, 57, 235}, {156, 146, 57, 244}}}, {Region: "Slovakia", IPs: []net.IP{{37, 120, 221, 98}}}, {Region: "South Africa", IPs: []net.IP{{102, 165, 20, 133}}}, {Region: "Spain", IPs: []net.IP{{212, 102, 49, 185}, {212, 102, 49, 251}}}, {Region: "Sweden", IPs: []net.IP{{46, 246, 3, 253}, {46, 246, 3, 254}}}, - {Region: "Switzerland", IPs: []net.IP{{156, 146, 62, 129}, {156, 146, 62, 193}, {212, 102, 36, 1}, {212, 102, 36, 166}}}, + {Region: "Switzerland", IPs: []net.IP{{156, 146, 62, 193}, {212, 102, 36, 1}, {212, 102, 36, 166}, {212, 102, 37, 240}, {212, 102, 37, 241}, {212, 102, 37, 242}, {212, 102, 37, 243}}}, {Region: "Turkey", IPs: []net.IP{{185, 195, 79, 34}, {185, 195, 79, 82}}}, {Region: "UAE", IPs: []net.IP{{45, 9, 250, 46}}}, - {Region: "UK London", IPs: []net.IP{{212, 102, 52, 1}, {212, 102, 52, 134}, {212, 102, 53, 129}}}, + {Region: "UK London", IPs: []net.IP{{37, 235, 96, 198}, {37, 235, 97, 11}, {212, 102, 52, 1}, {212, 102, 52, 134}, {212, 102, 52, 199}, {212, 102, 53, 93}, {212, 102, 53, 129}}}, {Region: "UK Manchester", IPs: []net.IP{{89, 238, 137, 36}, {89, 238, 137, 37}, {89, 238, 137, 38}, {89, 238, 137, 39}, {89, 238, 139, 52}, {89, 238, 139, 53}, {89, 238, 139, 54}, {89, 238, 139, 55}, {89, 238, 139, 56}, {89, 238, 139, 57}, {89, 238, 139, 58}, {89, 249, 67, 220}}}, - {Region: "UK Southampton", IPs: []net.IP{{31, 24, 226, 141}, {31, 24, 226, 147}, {31, 24, 226, 188}, {31, 24, 226, 189}, {31, 24, 226, 203}, {31, 24, 226, 205}, {31, 24, 226, 206}, {31, 24, 226, 220}, {31, 24, 226, 222}, {31, 24, 226, 223}, {31, 24, 226, 225}, {31, 24, 226, 226}, {31, 24, 226, 228}, {31, 24, 226, 232}, {31, 24, 226, 235}, {31, 24, 226, 244}, {31, 24, 226, 245}, {31, 24, 226, 246}, {31, 24, 226, 252}, {31, 24, 226, 254}}}, - {Region: "US Atlanta", IPs: []net.IP{{66, 115, 169, 195}, {66, 115, 169, 197}, {66, 115, 169, 199}, {66, 115, 169, 203}, {66, 115, 169, 206}, {66, 115, 169, 207}, {66, 115, 169, 208}, {66, 115, 169, 211}, {66, 115, 169, 214}, {156, 146, 46, 1}, {156, 146, 46, 134}, {156, 146, 46, 198}, {156, 146, 47, 11}}}, - {Region: "US California", IPs: []net.IP{{37, 235, 108, 144}, {89, 187, 187, 129}, {89, 187, 187, 162}, {91, 207, 175, 194}, {91, 207, 175, 197}, {91, 207, 175, 198}, {91, 207, 175, 199}, {91, 207, 175, 200}, {91, 207, 175, 203}, {91, 207, 175, 206}, {91, 207, 175, 209}, {91, 207, 175, 210}, {91, 207, 175, 211}}}, + {Region: "UK Southampton", IPs: []net.IP{{143, 244, 36, 58}, {143, 244, 37, 1}, {143, 244, 38, 1}, {143, 244, 38, 60}, {143, 244, 38, 119}}}, + {Region: "US Atlanta", IPs: []net.IP{{66, 115, 169, 195}, {66, 115, 169, 196}, {66, 115, 169, 197}, {66, 115, 169, 201}, {66, 115, 169, 202}, {66, 115, 169, 204}, {66, 115, 169, 205}, {66, 115, 169, 206}, {66, 115, 169, 209}, {66, 115, 169, 211}, {66, 115, 169, 212}, {66, 115, 169, 213}, {66, 115, 169, 214}, {156, 146, 46, 1}, {156, 146, 46, 134}, {156, 146, 46, 198}, {156, 146, 47, 11}}}, + {Region: "US California", IPs: []net.IP{{37, 235, 108, 144}, {89, 187, 187, 129}, {91, 207, 175, 194}, {91, 207, 175, 195}, {91, 207, 175, 196}, {91, 207, 175, 197}, {91, 207, 175, 198}, {91, 207, 175, 200}, {91, 207, 175, 201}, {91, 207, 175, 202}, {91, 207, 175, 203}, {91, 207, 175, 204}, {91, 207, 175, 206}, {91, 207, 175, 207}, {91, 207, 175, 209}, {91, 207, 175, 211}, {91, 207, 175, 212}}}, {Region: "US Chicago", IPs: []net.IP{{156, 146, 50, 1}, {156, 146, 50, 65}, {156, 146, 50, 134}, {156, 146, 50, 198}, {156, 146, 51, 11}, {212, 102, 58, 113}, {212, 102, 59, 54}, {212, 102, 59, 129}}}, - {Region: "US Dallas", IPs: []net.IP{{156, 146, 38, 65}, {156, 146, 38, 161}, {156, 146, 39, 1}, {156, 146, 39, 6}, {156, 146, 52, 6}, {156, 146, 52, 70}, {156, 146, 52, 139}, {156, 146, 52, 203}, {174, 127, 114, 53}, {174, 127, 114, 54}, {174, 127, 114, 56}, {174, 127, 114, 65}, {174, 127, 114, 66}, {174, 127, 114, 67}, {174, 127, 114, 71}, {174, 127, 114, 74}, {174, 127, 114, 75}, {174, 127, 114, 76}, {174, 127, 114, 77}, {174, 127, 114, 80}}}, - {Region: "US Denver", IPs: []net.IP{{174, 128, 225, 2}, {174, 128, 225, 98}, {174, 128, 225, 106}, {174, 128, 225, 186}, {174, 128, 236, 98}, {174, 128, 242, 234}, {174, 128, 242, 242}, {174, 128, 242, 250}, {174, 128, 243, 98}, {174, 128, 244, 66}, {174, 128, 244, 74}, {174, 128, 245, 122}, {174, 128, 250, 18}, {174, 128, 250, 26}, {198, 148, 82, 82}, {199, 115, 97, 202}, {199, 115, 98, 234}, {199, 115, 101, 178}, {199, 115, 101, 186}, {199, 115, 102, 146}}}, - {Region: "US East", IPs: []net.IP{{156, 146, 58, 201}, {156, 146, 58, 202}, {156, 146, 58, 203}, {156, 146, 58, 204}, {156, 146, 58, 205}, {156, 146, 58, 206}, {156, 146, 58, 207}, {156, 146, 58, 208}, {156, 146, 58, 209}, {193, 37, 253, 109}, {193, 37, 253, 114}, {193, 37, 253, 117}, {193, 37, 253, 133}, {194, 59, 251, 12}, {194, 59, 251, 24}, {194, 59, 251, 49}, {194, 59, 251, 53}, {194, 59, 251, 80}, {194, 59, 251, 93}, {194, 59, 251, 104}}}, - {Region: "US Florida", IPs: []net.IP{{156, 146, 42, 1}, {156, 146, 42, 65}, {156, 146, 42, 134}, {156, 146, 42, 198}, {156, 146, 43, 11}, {156, 146, 43, 75}, {193, 37, 252, 14}, {193, 37, 252, 15}, {193, 37, 252, 18}, {193, 37, 252, 19}, {193, 37, 252, 20}, {193, 37, 252, 21}, {193, 37, 252, 22}, {193, 37, 252, 24}, {193, 37, 252, 25}, {193, 37, 252, 26}, {193, 37, 252, 27}, {212, 102, 61, 19}, {212, 102, 61, 83}}}, - {Region: "US Houston", IPs: []net.IP{{74, 81, 88, 26}, {74, 81, 88, 42}, {74, 81, 88, 66}, {74, 81, 88, 74}, {205, 251, 148, 66}}}, - {Region: "US Las Vegas", IPs: []net.IP{{162, 251, 236, 2}, {162, 251, 236, 3}, {162, 251, 236, 4}, {162, 251, 236, 5}, {162, 251, 236, 6}, {162, 251, 236, 8}, {162, 251, 236, 9}, {199, 127, 56, 82}, {199, 127, 56, 83}, {199, 127, 56, 84}, {199, 127, 56, 86}, {199, 127, 56, 87}, {199, 127, 56, 88}, {199, 127, 56, 89}, {199, 127, 56, 90}, {199, 127, 56, 91}}}, - {Region: "US New York City", IPs: []net.IP{{156, 146, 36, 225}, {156, 146, 36, 240}, {156, 146, 37, 129}, {156, 146, 55, 198}, {156, 146, 58, 1}, {156, 146, 58, 134}, {173, 244, 217, 37}, {209, 95, 50, 50}, {209, 95, 50, 58}, {209, 95, 50, 60}, {209, 95, 50, 62}, {209, 95, 50, 64}, {209, 95, 50, 65}, {209, 95, 50, 66}, {209, 95, 50, 67}, {209, 95, 50, 68}, {209, 95, 50, 69}, {209, 95, 50, 84}, {209, 95, 50, 85}, {209, 95, 50, 87}}}, - {Region: "US Seattle", IPs: []net.IP{{84, 17, 41, 7}, {84, 17, 41, 10}, {84, 17, 41, 20}, {84, 17, 41, 22}, {84, 17, 41, 25}, {84, 17, 41, 27}, {84, 17, 41, 30}, {84, 17, 41, 38}, {84, 17, 41, 40}, {84, 17, 41, 41}, {84, 17, 41, 50}, {84, 17, 41, 53}, {84, 17, 41, 56}, {84, 17, 41, 58}, {84, 17, 41, 63}, {84, 17, 41, 92}, {84, 17, 41, 93}, {84, 17, 41, 95}, {212, 102, 46, 193}, {212, 102, 47, 134}}}, - {Region: "US Silicon Valley", IPs: []net.IP{{199, 116, 118, 130}, {199, 116, 118, 133}, {199, 116, 118, 136}, {199, 116, 118, 140}, {199, 116, 118, 158}, {199, 116, 118, 170}, {199, 116, 118, 172}, {199, 116, 118, 174}, {199, 116, 118, 178}, {199, 116, 118, 180}, {199, 116, 118, 184}, {199, 116, 118, 202}, {199, 116, 118, 204}, {199, 116, 118, 212}, {199, 116, 118, 219}, {199, 116, 118, 233}, {199, 116, 118, 239}, {199, 116, 118, 240}, {199, 116, 118, 244}, {199, 116, 118, 246}}}, - {Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 46}, {70, 32, 0, 47}, {70, 32, 0, 52}, {70, 32, 0, 53}, {70, 32, 0, 65}, {70, 32, 0, 68}, {70, 32, 0, 69}, {70, 32, 0, 70}, {70, 32, 0, 71}, {70, 32, 0, 72}, {70, 32, 0, 73}, {70, 32, 0, 75}, {70, 32, 0, 76}, {70, 32, 0, 113}, {70, 32, 0, 114}, {70, 32, 0, 115}, {70, 32, 0, 118}, {70, 32, 0, 119}, {70, 32, 0, 139}, {70, 32, 0, 173}}}, - {Region: "US West", IPs: []net.IP{{104, 200, 151, 6}, {104, 200, 151, 7}, {104, 200, 151, 9}, {104, 200, 151, 10}, {104, 200, 151, 12}, {104, 200, 151, 16}, {104, 200, 151, 17}, {104, 200, 151, 21}, {104, 200, 151, 49}, {104, 200, 151, 51}, {104, 200, 151, 56}, {104, 200, 151, 74}, {104, 200, 151, 78}, {104, 200, 151, 79}, {104, 200, 151, 81}, {104, 200, 151, 82}, {104, 200, 151, 84}, {104, 200, 151, 85}, {104, 200, 151, 87}, {104, 200, 151, 89}}}, + {Region: "US Dallas", IPs: []net.IP{{156, 146, 38, 65}, {156, 146, 38, 161}, {156, 146, 39, 1}, {156, 146, 39, 6}, {156, 146, 52, 6}, {156, 146, 52, 70}, {156, 146, 52, 139}, {156, 146, 52, 203}, {174, 127, 114, 53}, {174, 127, 114, 60}, {174, 127, 114, 68}, {174, 127, 114, 72}, {174, 127, 114, 75}, {174, 127, 114, 77}}}, + {Region: "US Denver", IPs: []net.IP{{174, 128, 225, 2}, {174, 128, 225, 98}, {174, 128, 226, 18}, {174, 128, 227, 226}, {174, 128, 236, 98}, {174, 128, 236, 106}, {174, 128, 242, 234}, {174, 128, 242, 250}, {174, 128, 243, 106}, {174, 128, 244, 66}, {174, 128, 245, 98}, {174, 128, 246, 10}, {174, 128, 250, 26}, {199, 115, 97, 202}, {199, 115, 98, 146}, {199, 115, 98, 226}, {199, 115, 98, 234}, {199, 115, 101, 178}, {199, 115, 102, 146}, {199, 115, 103, 10}}}, + {Region: "US East", IPs: []net.IP{{156, 146, 58, 202}, {156, 146, 58, 203}, {156, 146, 58, 204}, {156, 146, 58, 205}, {156, 146, 58, 206}, {156, 146, 58, 207}, {156, 146, 58, 208}, {156, 146, 58, 209}, {194, 59, 251, 5}, {194, 59, 251, 8}, {194, 59, 251, 25}, {194, 59, 251, 30}, {194, 59, 251, 38}, {194, 59, 251, 48}, {194, 59, 251, 49}, {194, 59, 251, 53}, {194, 59, 251, 66}, {194, 59, 251, 78}, {194, 59, 251, 79}, {194, 59, 251, 84}}}, + {Region: "US Florida", IPs: []net.IP{{156, 146, 42, 65}, {156, 146, 42, 134}, {156, 146, 42, 198}, {156, 146, 43, 11}, {156, 146, 43, 75}, {193, 37, 252, 14}, {193, 37, 252, 15}, {193, 37, 252, 16}, {193, 37, 252, 17}, {193, 37, 252, 18}, {193, 37, 252, 19}, {193, 37, 252, 20}, {193, 37, 252, 21}, {193, 37, 252, 22}, {193, 37, 252, 23}, {193, 37, 252, 25}, {193, 37, 252, 26}, {193, 37, 252, 27}, {212, 102, 61, 19}, {212, 102, 61, 83}}}, + {Region: "US Houston", IPs: []net.IP{{74, 81, 88, 26}, {74, 81, 88, 42}, {74, 81, 88, 66}, {74, 81, 88, 74}, {205, 251, 148, 90}, {205, 251, 148, 138}, {205, 251, 148, 154}, {205, 251, 148, 178}, {205, 251, 150, 146}, {205, 251, 150, 170}}}, + {Region: "US Las Vegas", IPs: []net.IP{{79, 110, 53, 34}, {79, 110, 53, 50}, {79, 110, 53, 66}, {79, 110, 53, 82}, {79, 110, 53, 98}, {79, 110, 53, 114}, {79, 110, 53, 130}, {79, 110, 53, 146}, {79, 110, 53, 162}, {79, 110, 53, 178}, {79, 110, 53, 194}, {79, 110, 53, 210}}}, + {Region: "US New York City", IPs: []net.IP{{156, 146, 36, 225}, {156, 146, 55, 198}}}, + {Region: "US Seattle", IPs: []net.IP{{84, 17, 41, 96}, {156, 146, 48, 65}, {156, 146, 48, 135}, {156, 146, 48, 200}, {156, 146, 49, 13}, {212, 102, 46, 129}, {212, 102, 46, 193}, {212, 102, 47, 134}}}, + {Region: "US Silicon Valley", IPs: []net.IP{{199, 116, 118, 133}, {199, 116, 118, 148}, {199, 116, 118, 167}, {199, 116, 118, 172}, {199, 116, 118, 173}, {199, 116, 118, 174}, {199, 116, 118, 185}, {199, 116, 118, 198}, {199, 116, 118, 202}, {199, 116, 118, 210}, {199, 116, 118, 212}, {199, 116, 118, 215}, {199, 116, 118, 217}, {199, 116, 118, 219}, {199, 116, 118, 220}, {199, 116, 118, 223}, {199, 116, 118, 237}, {199, 116, 118, 239}, {199, 116, 118, 240}, {199, 116, 118, 249}}}, + {Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 52}, {70, 32, 0, 53}, {70, 32, 0, 59}, {70, 32, 0, 60}, {70, 32, 0, 61}, {70, 32, 0, 64}, {70, 32, 0, 67}, {70, 32, 0, 68}, {70, 32, 0, 69}, {70, 32, 0, 70}, {70, 32, 0, 103}, {70, 32, 0, 106}, {70, 32, 0, 107}, {70, 32, 0, 114}, {70, 32, 0, 116}, {70, 32, 0, 120}, {70, 32, 0, 122}, {70, 32, 0, 168}, {70, 32, 0, 172}, {70, 32, 0, 173}}}, + {Region: "US West", IPs: []net.IP{{104, 200, 151, 7}, {104, 200, 151, 8}, {104, 200, 151, 9}, {104, 200, 151, 11}, {104, 200, 151, 13}, {104, 200, 151, 16}, {104, 200, 151, 17}, {104, 200, 151, 20}, {104, 200, 151, 21}, {104, 200, 151, 46}, {104, 200, 151, 47}, {104, 200, 151, 50}, {104, 200, 151, 53}, {104, 200, 151, 56}, {104, 200, 151, 59}, {104, 200, 151, 61}, {104, 200, 151, 72}, {104, 200, 151, 74}, {104, 200, 151, 78}, {104, 200, 151, 81}}}, {Region: "Ukraine", IPs: []net.IP{{62, 149, 20, 10}, {62, 149, 20, 40}}}, } } diff --git a/internal/constants/servers.go b/internal/constants/servers.go index 55ab6930..ed6ceaf0 100644 --- a/internal/constants/servers.go +++ b/internal/constants/servers.go @@ -27,7 +27,7 @@ func GetAllServers() (allServers models.AllServers) { }, PiaOld: models.PiaServers{ Version: 1, - Timestamp: 1599786395, + Timestamp: 1600458645, Servers: PIAOldServers(), }, Purevpn: models.PurevpnServers{ diff --git a/internal/constants/servers_test.go b/internal/constants/servers_test.go index 5bcb92c5..217fe04c 100644 --- a/internal/constants/servers_test.go +++ b/internal/constants/servers_test.go @@ -51,7 +51,7 @@ func Test_timestamps(t *testing.T) { assert.Equal(t, "EU4fTzD7jWC9N5kmN5bOEg", digestServersTimestamp(t, allServers.Mullvad.Servers, allServers.Mullvad.Timestamp)) assert.Equal(t, "OLI62FoTf2wis25Nw4FLpg", digestServersTimestamp(t, allServers.Nordvpn.Servers, allServers.Nordvpn.Timestamp)) assert.Equal(t, "hAjEIo6FIrUsJuRmKOKPzA", digestServersTimestamp(t, allServers.Pia.Servers, allServers.Pia.Timestamp)) - assert.Equal(t, "uiMp4IqH7NmvCIQ7gvR05Q", digestServersTimestamp(t, allServers.PiaOld.Servers, allServers.PiaOld.Timestamp)) + assert.Equal(t, "CKszzgA7YX5zqxQGiiOL9g", digestServersTimestamp(t, allServers.PiaOld.Servers, allServers.PiaOld.Timestamp)) assert.Equal(t, "kwJdVWTiBOspfrRwZIA+Sg", digestServersTimestamp(t, allServers.Purevpn.Servers, allServers.Purevpn.Timestamp)) assert.Equal(t, "q28ju2KJqLhrggJTTjXSiw", digestServersTimestamp(t, allServers.Surfshark.Servers, allServers.Surfshark.Timestamp)) assert.Equal(t, "KdIQWi2tYUM4aMXvWfVBEg", digestServersTimestamp(t, allServers.Vyprvpn.Servers, allServers.Vyprvpn.Timestamp)) diff --git a/internal/updater/cyberghost.go b/internal/updater/cyberghost.go index c458ecdd..242483a1 100644 --- a/internal/updater/cyberghost.go +++ b/internal/updater/cyberghost.go @@ -27,29 +27,52 @@ func findCyberghostServers(ctx context.Context, lookupIP lookupIPFunc) (servers cyberghostCountryCodes := getCyberghostSubdomainToRegion() possibleCountryCodes := mergeCountryCodes(cyberghostCountryCodes, allCountryCodes) + results := make(chan models.CyberghostServer) + const maxGoroutines = 10 + guard := make(chan struct{}, maxGoroutines) for groupID, groupName := range groups { for countryCode, region := range possibleCountryCodes { if err := ctx.Err(); err != nil { return nil, err } - host := fmt.Sprintf("%s-%s.cg-dialup.net", groupID, countryCode) - IPs, err := resolveRepeat(ctx, lookupIP, host, 2) - if err != nil || len(IPs) == 0 { - continue - } - servers = append(servers, models.CyberghostServer{ - Region: region, - Group: groupName, - IPs: IPs, - }) + const domain = "cg-dialup.net" + host := fmt.Sprintf("%s-%s.%s", groupID, countryCode, domain) + guard <- struct{}{} + go tryCyberghostHostname(ctx, lookupIP, host, groupName, region, results) + <-guard } } + for i := 0; i < len(groups)*len(possibleCountryCodes); i++ { + server := <-results + if server.IPs == nil { + continue + } + servers = append(servers, server) + } + if err := ctx.Err(); err != nil { + return servers, err + } sort.Slice(servers, func(i, j int) bool { return servers[i].Region < servers[j].Region }) return servers, nil } +func tryCyberghostHostname(ctx context.Context, lookupIP lookupIPFunc, + host, groupName, region string, + results chan<- models.CyberghostServer) { + IPs, err := resolveRepeat(ctx, lookupIP, host, 2) + if err != nil || len(IPs) == 0 { + results <- models.CyberghostServer{} + return + } + results <- models.CyberghostServer{ + Region: region, + Group: groupName, + IPs: IPs, + } +} + //nolint:goconst func stringifyCyberghostServers(servers []models.CyberghostServer) (s string) { s = "func CyberghostServers() []models.CyberghostServer {\n" diff --git a/internal/updater/pia.go b/internal/updater/pia.go index b9d86421..d83ffdc0 100644 --- a/internal/updater/pia.go +++ b/internal/updater/pia.go @@ -6,6 +6,7 @@ import ( "net" "sort" "strings" + "sync" "github.com/qdm12/gluetun/internal/models" ) @@ -50,11 +51,21 @@ func (u *updater) updatePIAOld(ctx context.Context) (err error) { if err != nil { return err } + const maxGoroutines = 10 + guard := make(chan struct{}, maxGoroutines) + errors := make(chan error) + serversCh := make(chan models.PIAServer) servers := make([]models.PIAServer, 0, len(contents)) + ctx, cancel := context.WithCancel(ctx) + wg := &sync.WaitGroup{} + defer func() { + cancel() + wg.Wait() + defer close(guard) + defer close(errors) + defer close(serversCh) + }() for fileName, content := range contents { - if err := ctx.Err(); err != nil { - return err - } remoteLines := extractRemoteLinesFromOpenvpn(content) if len(remoteLines) == 0 { return fmt.Errorf("cannot find any remote lines in %s", fileName) @@ -63,20 +74,19 @@ func (u *updater) updatePIAOld(ctx context.Context) (err error) { if len(hosts) == 0 { return fmt.Errorf("cannot find any hosts in %s", fileName) } - var IPs []net.IP - for _, host := range hosts { - newIPs, err := resolveRepeat(ctx, u.lookupIP, host, 3) - if err != nil { - return err - } - IPs = append(IPs, newIPs...) - } region := strings.TrimSuffix(fileName, ".ovpn") - server := models.PIAServer{ - Region: region, - IPs: uniqueSortedIPs(IPs), + guard <- struct{}{} + wg.Add(1) + go resolvePIAHostname(ctx, wg, region, hosts, u.lookupIP, errors, serversCh) + <-guard + } + for range contents { + select { + case err := <-errors: + return err + case server := <-serversCh: + servers = append(servers, server) } - servers = append(servers, server) } sort.Slice(servers, func(i, j int) bool { return servers[i].Region < servers[j].Region @@ -89,6 +99,28 @@ func (u *updater) updatePIAOld(ctx context.Context) (err error) { return nil } +func resolvePIAHostname(ctx context.Context, wg *sync.WaitGroup, + region string, hosts []string, lookupIP lookupIPFunc, + errors chan<- error, serversCh chan<- models.PIAServer) { + defer wg.Done() + var IPs []net.IP //nolint:prealloc + // usually one single host in this case + // so no need to run in goroutines the for loop below + for _, host := range hosts { + const repetition = 5 + newIPs, err := resolveRepeat(ctx, lookupIP, host, repetition) + if err != nil { + errors <- err + return + } + IPs = append(IPs, newIPs...) + } + serversCh <- models.PIAServer{ + Region: region, + IPs: uniqueSortedIPs(IPs), + } +} + func stringifyPIAServers(servers []models.PIAServer) (s string) { s = "func PIAServers() []models.PIAServer {\n" s += " return []models.PIAServer{\n" diff --git a/internal/updater/purevpn.go b/internal/updater/purevpn.go index 611ffc2d..5f9ba17e 100644 --- a/internal/updater/purevpn.go +++ b/internal/updater/purevpn.go @@ -90,7 +90,7 @@ func findPurevpnServers(ctx context.Context, httpGet httpGetFunc, lookupIP looku continue } host := jsonServer.UDP - const repetition = 3 + const repetition = 5 IPs, err := resolveRepeat(ctx, lookupIP, host, repetition) if err != nil { warnings = append(warnings, err.Error()) diff --git a/internal/updater/resolver.go b/internal/updater/resolver.go index 9cb3ccf7..45bea5a8 100644 --- a/internal/updater/resolver.go +++ b/internal/updater/resolver.go @@ -1,8 +1,10 @@ package updater import ( + "bytes" "context" "net" + "sort" ) func newResolver(resolverAddress string) *net.Resolver { @@ -30,12 +32,49 @@ func newLookupIP(r *net.Resolver) lookupIPFunc { } func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string, n int) (ips []net.IP, err error) { + foundIPs := make(chan []net.IP) + errors := make(chan error) + ctx, cancel := context.WithCancel(ctx) + defer cancel() for i := 0; i < n; i++ { - newIPs, err := lookupIP(ctx, host) - if err != nil { - return nil, err - } - ips = append(ips, newIPs...) + go func() { + newIPs, err := lookupIP(ctx, host) + if err != nil { + errors <- err + } else { + foundIPs <- newIPs + } + }() } - return uniqueSortedIPs(ips), nil + + uniqueIPs := make(map[string]struct{}) + for i := 0; i < n; i++ { + select { + case newIPs := <-foundIPs: + for _, ip := range newIPs { + key := ip.String() + uniqueIPs[key] = struct{}{} + } + case newErr := <-errors: + if err == nil { + err = newErr + cancel() + } + } + } + + ips = make([]net.IP, 0, len(uniqueIPs)) + for key := range uniqueIPs { + ip := net.ParseIP(key) + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + ips = append(ips, ip) + } + + sort.Slice(ips, func(i, j int) bool { + return bytes.Compare(ips[i], ips[j]) < 1 + }) + + return ips, err } diff --git a/internal/updater/resolver_test.go b/internal/updater/resolver_test.go new file mode 100644 index 00000000..bd3d8524 --- /dev/null +++ b/internal/updater/resolver_test.go @@ -0,0 +1,75 @@ +package updater + +import ( + "context" + "fmt" + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_resolveRepeat(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + lookupIPResult [][]net.IP + lookupIPErr error + n int + ips []net.IP + err error + }{ + "failure": { + lookupIPResult: [][]net.IP{ + {{1, 1, 1, 1}, {1, 1, 1, 2}}, + }, + lookupIPErr: fmt.Errorf("feeling sick"), + n: 1, + ips: []net.IP{}, + err: fmt.Errorf("feeling sick"), + }, + "successful": { + lookupIPResult: [][]net.IP{ + {{1, 1, 1, 1}, {1, 1, 1, 2}}, + {{2, 1, 1, 1}, {2, 1, 1, 2}}, + {{2, 1, 1, 3}, {2, 1, 1, 2}}, + }, + n: 3, + ips: []net.IP{ + {1, 1, 1, 1}, + {1, 1, 1, 2}, + {2, 1, 1, 1}, + {2, 1, 1, 2}, + {2, 1, 1, 3}, + }, + }, + } + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + if testCase.lookupIPErr == nil { + require.Len(t, testCase.lookupIPResult, testCase.n) + } + const host = "blabla" + i := 0 + lookupIP := func(ctx context.Context, argHost string) ( + ips []net.IP, err error) { + assert.Equal(t, host, argHost) + result := testCase.lookupIPResult[i] + i++ + return result, testCase.err + } + + ips, err := resolveRepeat( + context.Background(), lookupIP, host, testCase.n) + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, testCase.ips, ips) + }) + } +} diff --git a/internal/updater/surfshark.go b/internal/updater/surfshark.go index d7d5e973..12315a53 100644 --- a/internal/updater/surfshark.go +++ b/internal/updater/surfshark.go @@ -2,7 +2,9 @@ package updater import ( "context" + "encoding/json" "fmt" + "io/ioutil" "net" "sort" "strings" @@ -11,7 +13,7 @@ import ( ) func (u *updater) updateSurfshark(ctx context.Context) (err error) { - servers, warnings, err := findSurfsharkServers(ctx, u.lookupIP) + servers, warnings, err := findSurfsharkServersFromZip(ctx, u.lookupIP) if u.options.CLI { for _, warning := range warnings { u.logger.Warn("Surfshark: %s", warning) @@ -28,7 +30,47 @@ func (u *updater) updateSurfshark(ctx context.Context) (err error) { return nil } -func findSurfsharkServers(ctx context.Context, lookupIP lookupIPFunc) (servers []models.SurfsharkServer, warnings []string, err error) { +//nolint:deadcode,unused +func findSurfsharkServersFromAPI(ctx context.Context, lookupIP lookupIPFunc, httpGet httpGetFunc) (servers []models.SurfsharkServer, warnings []string, err error) { + const url = "https://my.surfshark.com/vpn/api/v1/server/clusters" + response, err := httpGet(url) + if err != nil { + return nil, nil, err + } + defer response.Body.Close() + b, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, nil, err + } + var jsonServers []struct { + Host string `json:"connectionName"` + Country string `json:"country"` + Location string `json:"location"` + } + if err := json.Unmarshal(b, &jsonServers); err != nil { + return nil, nil, err + } + for _, jsonServer := range jsonServers { + host := jsonServer.Host + const repetition = 5 + IPs, err := resolveRepeat(ctx, lookupIP, host, repetition) + if err != nil { + return nil, warnings, err + } else if len(IPs) == 0 { + warning := fmt.Sprintf("no IP address found for host %q", host) + warnings = append(warnings, warning) + continue + } + server := models.SurfsharkServer{ + Region: jsonServer.Country + " " + jsonServer.Location, + IPs: uniqueSortedIPs(IPs), + } + servers = append(servers, server) + } + return servers, warnings, nil +} + +func findSurfsharkServersFromZip(ctx context.Context, lookupIP lookupIPFunc) (servers []models.SurfsharkServer, warnings []string, err error) { const zipURL = "https://my.surfshark.com/vpn/api/v1/server/configurations" contents, err := fetchAndExtractFiles(zipURL) if err != nil { @@ -97,6 +139,7 @@ func findSurfsharkServers(ctx context.Context, lookupIP lookupIPFunc) (servers [ }) return servers, warnings, nil } + func getRemainingServers(ctx context.Context, mapping map[string]string, lookupIP lookupIPFunc) ( servers []models.SurfsharkServer, warnings []string, err error) { for subdomain, region := range mapping { diff --git a/internal/updater/vyprvpn.go b/internal/updater/vyprvpn.go index f58a2e80..5231dfeb 100644 --- a/internal/updater/vyprvpn.go +++ b/internal/updater/vyprvpn.go @@ -43,7 +43,8 @@ func findVyprvpnServers(ctx context.Context, lookupIP lookupIPFunc) (servers []m } var IPs []net.IP for _, host := range hosts { - newIPs, err := lookupIP(ctx, host) + const repetitions = 1 + newIPs, err := resolveRepeat(ctx, lookupIP, host, repetitions) if err != nil { return nil, err } diff --git a/internal/updater/windscribe.go b/internal/updater/windscribe.go index 00198645..639ad3a1 100644 --- a/internal/updater/windscribe.go +++ b/internal/updater/windscribe.go @@ -31,7 +31,8 @@ func findWindscribeServers(ctx context.Context, lookupIP lookupIPFunc) (servers return nil, err } host := countryCode + "." + domain - ips, err := resolveRepeat(ctx, lookupIP, host, 2) + const repetitions = 5 + ips, err := resolveRepeat(ctx, lookupIP, host, repetitions) if err != nil || len(ips) == 0 { continue }