diff options
author | Ruben Pollan <meskio@sindominio.net> | 2019-06-19 10:26:19 +0200 |
---|---|---|
committer | Ruben Pollan <meskio@sindominio.net> | 2019-08-16 22:58:45 +0200 |
commit | 35aaba1e0da53aed44a5741ca9a3a1e2de21baf5 (patch) | |
tree | a019f70b8e76a8cd845be960fa7d9f8d21f16e64 /pkg/standalone/bonafide/eip_service.go | |
parent | 78ee21c4a87d2e13c4f682162f15aa6411bea592 (diff) |
[refactor] bonafide to parse eip-service.json v3
Diffstat (limited to 'pkg/standalone/bonafide/eip_service.go')
-rw-r--r-- | pkg/standalone/bonafide/eip_service.go | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/pkg/standalone/bonafide/eip_service.go b/pkg/standalone/bonafide/eip_service.go new file mode 100644 index 0000000..94e303d --- /dev/null +++ b/pkg/standalone/bonafide/eip_service.go @@ -0,0 +1,250 @@ +package bonafide + +import ( + "encoding/json" + "fmt" + "io" + "log" + "math/rand" + "sort" + "strconv" + "strings" + "time" + + "0xacab.org/leap/bitmask-vpn/pkg/config" +) + +const ( + eip1API = config.APIURL + "1/config/eip-service.json" + eip3API = config.APIURL + "3/config/eip-service.json" +) + +type eipService struct { + Gateways []gatewayV3 + Locations map[string]location + OpenvpnConfiguration openvpnConfig `json:"openvpn_configuration"` + defaultGateway string +} + +type eipServiceV1 struct { + Gateways []gatewayV1 + Locations map[string]location + OpenvpnConfiguration openvpnConfig `json:"openvpn_configuration"` +} + +type location struct { + CountryCode string + Hemisphere string + Name string + Timezone string +} + +type gatewayV1 struct { + Capabilities struct { + Ports []string + Protocols []string + } + Host string + IPAddress string `json:"ip_address"` + Location string +} + +type gatewayV3 struct { + Capabilities struct { + Transport []transportV3 + } + Host string + IPAddress string `json:"ip_address"` + Location string +} + +type transportV3 struct { + Type string + Protocols []string + Ports []string + Options map[string]string +} + +func (b *Bonafide) fetchEipJSON() error { + resp, err := b.client.Post(eip3API, "", nil) + for err != nil { + log.Printf("Error fetching eip v3 json: %v", err) + time.Sleep(retryFetchJSONSeconds * time.Second) + resp, err = b.client.Post(eip3API, "", nil) + } + defer resp.Body.Close() + + switch resp.StatusCode { + case 200: + b.eip, err = decodeEIP3(resp.Body) + case 404: + resp, err = b.client.Post(eip1API, "", nil) + if err != nil { + return err + } + defer resp.Body.Close() + + b.eip, err = decodeEIP1(resp.Body) + default: + return fmt.Errorf("get eip json has failed with status: %s", resp.Status) + } + if err != nil { + return err + } + + b.sortGateways() + return nil +} + +func decodeEIP3(body io.Reader) (*eipService, error) { + var eip eipService + decoder := json.NewDecoder(body) + err := decoder.Decode(&eip) + return &eip, err +} + +func decodeEIP1(body io.Reader) (*eipService, error) { + var eip1 eipServiceV1 + decoder := json.NewDecoder(body) + err := decoder.Decode(&eip1) + if err != nil { + return nil, err + } + + eip3 := eipService{ + Gateways: make([]gatewayV3, len(eip1.Gateways)), + Locations: eip1.Locations, + OpenvpnConfiguration: eip1.OpenvpnConfiguration, + } + for _, g := range eip1.Gateways { + gateway := gatewayV3{ + Host: g.Host, + IPAddress: g.IPAddress, + Location: g.Location, + } + gateway.Capabilities.Transport = []transportV3{ + transportV3{ + Type: "openvpn", + Ports: g.Capabilities.Ports, + Protocols: g.Capabilities.Protocols, + }, + } + eip3.Gateways = append(eip3.Gateways, gateway) + } + return &eip3, nil +} + +func (eip eipService) getGateways(transport string) []Gateway { + gws := []Gateway{} + for _, g := range eip.Gateways { + for _, t := range g.Capabilities.Transport { + if t.Type != transport { + continue + } + + gateway := Gateway{ + Host: g.Host, + IPAddress: g.IPAddress, + Location: g.Location, + Ports: t.Ports, + Protocols: t.Protocols, + Options: t.Options, + } + gws = append(gws, gateway) + } + } + return gws +} + +func (eip *eipService) setDefaultGateway(name string) { + eip.defaultGateway = name +} + +func (eip eipService) getOpenvpnArgs() []string { + args := []string{} + for arg, value := range eip.OpenvpnConfiguration { + switch v := value.(type) { + case string: + args = append(args, "--"+arg) + args = append(args, strings.Split(v, " ")...) + case bool: + if v { + args = append(args, "--"+arg) + } + default: + log.Printf("Unknown openvpn argument type: %s - %v", arg, value) + } + } + return args +} + +func (eip *eipService) sortGatewaysByGeolocation(geolocatedGateways []string) { + gws := make([]gatewayV3, len(eip.Gateways)) + + if eip.defaultGateway != "" { + for _, gw := range eip.Gateways { + if gw.Location == eip.defaultGateway { + gws = append(gws, gw) + break + } + } + } + for _, host := range geolocatedGateways { + for _, gw := range eip.Gateways { + if gw.Host == host { + gws = append(gws, gw) + } + } + } + eip.Gateways = gws +} + +type gatewayDistance struct { + gateway gatewayV3 + distance int +} + +func (eip *eipService) sortGatewaysByTimezone(tzOffsetHours int) { + gws := []gatewayDistance{} + + for _, gw := range eip.Gateways { + distance := 13 + if gw.Location == eip.defaultGateway { + distance = -1 + } else { + gwOffset, err := strconv.Atoi(eip.Locations[gw.Location].Timezone) + if err != nil { + log.Printf("Error sorting gateways: %v", err) + } else { + distance = tzDistance(tzOffsetHours, gwOffset) + } + } + gws = append(gws, gatewayDistance{gw, distance}) + } + rand.Seed(time.Now().UnixNano()) + cmp := func(i, j int) bool { + if gws[i].distance == gws[j].distance { + return rand.Intn(2) == 1 + } + return gws[i].distance < gws[j].distance + } + sort.Slice(gws, cmp) + + for i, gw := range gws { + eip.Gateways[i] = gw.gateway + } +} + +func tzDistance(offset1, offset2 int) int { + abs := func(x int) int { + if x < 0 { + return -x + } + return x + } + distance := abs(offset1 - offset2) + if distance > 12 { + distance = 24 - distance + } + return distance +} |