summaryrefslogtreecommitdiff
path: root/src/discovery.go
blob: e88d74ec8988795ff2013572c4cde050d69c7f5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package eduvpn

import (
	"fmt"
)

type DiscoFileError struct {
	URL string
	Err error
}

func (e *DiscoFileError) Error() string {
	return fmt.Sprintf("failed obtaining disco file %s with error %v", e.URL, e.Err)
}

type DiscoSigFileError struct {
	URL string
	Err error
}

func (e *DiscoSigFileError) Error() string {
	return fmt.Sprintf("failed obtaining disco signature file %s with error %v", e.URL, e.Err)
}

type DiscoVerifyError struct {
	File    string
	Sigfile string
	Err     error
}

func (e *DiscoVerifyError) Error() string {
	return fmt.Sprintf("failed verifying file %s with signature %s due to error %v", e.File, e.Sigfile, e.Err)
}

type DiscoList struct {
	Organizations *string `json:"organizations"`
	Servers       *string `json:"servers"`
}

// Helper function that gets a disco json
func getDiscoFile(jsonFile string) (string, error) {
	// Get json data
	discoURL := "https://disco.eduvpn.org/v2/"
	fileURL := discoURL + jsonFile
	_, fileBody, fileErr := HTTPGet(fileURL)

	if fileErr != nil {
		return "", &DiscoFileError{fileURL, fileErr}
	}

	// Get signature
	sigFile := jsonFile + ".minisig"
	sigURL := discoURL + sigFile
	_, sigBody, sigFileErr := HTTPGet(sigURL)

	if sigFileErr != nil {
		return "", &DiscoSigFileError{URL: sigURL, Err: sigFileErr}
	}

	// Verify signature
	// TODO: Handle this by keeping track of the previous sign time
	// Wrappers must do this?
	var previousSigTime uint64 = 0
	forcePrehash := false
	verifySuccess, verifyErr := Verify(string(sigBody), fileBody, jsonFile, previousSigTime, forcePrehash)

	if !verifySuccess || verifyErr != nil {
		return "", &DiscoVerifyError{File: jsonFile, Sigfile: sigFile, Err: verifyErr}
	}

	return string(fileBody), nil
}

type GetListError struct {
	File string
	Err  error
}

func (e *GetListError) Error() string {
	return fmt.Sprintf("failed getting disco list file %s with error %v", e.File, e.Err)
}

// FIXME: Implement these properly based on version and time info
func (eduvpn *VPNState) DetermineOrganizationsUpdate() bool {
	return eduvpn.DiscoList == nil || eduvpn.DiscoList.Organizations == nil
}

func (eduvpn *VPNState) DetermineServersUpdate() bool {
	return eduvpn.DiscoList == nil || eduvpn.DiscoList.Servers == nil
}

func (eduvpn *VPNState) EnsureDisco() {
	if eduvpn.DiscoList == nil {
		eduvpn.DiscoList = &DiscoList{}
	}
}

// Get the organization list
func (eduvpn *VPNState) GetOrganizationsList() (string, error) {
	if !eduvpn.DetermineOrganizationsUpdate() {
		return *eduvpn.DiscoList.Organizations, nil
	}
	file := "organization_list.json"
	body, err := getDiscoFile(file)
	if err != nil {
		return "", &GetListError{File: file, Err: err}
	}
	eduvpn.EnsureDisco()
	eduvpn.DiscoList.Organizations = &body
	eduvpn.WriteConfig()
	return body, nil
}

// Get the server list
func (eduvpn *VPNState) GetServersList() (string, error) {
	if !eduvpn.DetermineServersUpdate() {
		return *eduvpn.DiscoList.Servers, nil
	}
	file := "server_list.json"
	body, err := getDiscoFile("server_list.json")
	if err != nil {
		return "", &GetListError{File: file, Err: err}
	}
	eduvpn.EnsureDisco()
	eduvpn.DiscoList.Servers = &body
	eduvpn.WriteConfig()
	return body, nil
}