diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 00000000..f1fe8d1e --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1 @@ +*.so \ No newline at end of file diff --git a/plugins/matrix/Makefile b/plugins/matrix/Makefile new file mode 100644 index 00000000..64c83f48 --- /dev/null +++ b/plugins/matrix/Makefile @@ -0,0 +1,7 @@ +PLUGIN_NAME=matrix +TARGET=../happydomain-plugin-test-$(PLUGIN_NAME).so + +all: $(TARGET) + +$(TARGET): *.go + go build -buildmode=plugin -o $@ git.happydns.org/happyDomain/plugins/$(PLUGIN_NAME) diff --git a/plugins/matrix/main.go b/plugins/matrix/main.go new file mode 100644 index 00000000..789ae424 --- /dev/null +++ b/plugins/matrix/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "git.happydns.org/happyDomain/model" +) + +func NewTestPlugin() (happydns.TestPlugin, error) { + return &MatrixTester{ + TesterURI: "https://federationtester.matrix.org/api/report?server_name=%s", + }, nil +} diff --git a/plugins/matrix/test.go b/plugins/matrix/test.go new file mode 100644 index 00000000..d218924e --- /dev/null +++ b/plugins/matrix/test.go @@ -0,0 +1,148 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "strings" + + "git.happydns.org/happyDomain/model" +) + +type MatrixTester struct { + TesterURI string +} + +func (p *MatrixTester) PluginEnvName() []string { + return []string{ + "matrixim", + } +} + +func (p *MatrixTester) Version() happydns.PluginVersionInfo { + return happydns.PluginVersionInfo{ + Name: "Matrix Federation Tester", + Version: "0.1", + AvailableOn: happydns.PluginAvailability{ + ApplyToService: true, + LimitToServices: []string{"abstract.MatrixIM"}, + }, + } +} + +func (p *MatrixTester) AvailableOptions() happydns.PluginOptionsDocumentation { + return happydns.PluginOptionsDocumentation{ + RunOpts: []happydns.PluginOptionDocumentation{ + { + Id: "serviceDomain", + Type: "string", + Label: "Matrix domain", + Placeholder: "matrix.org", + Default: "matrix.org", + Required: true, + }, + }, + AdminOpts: []happydns.PluginOptionDocumentation{ + { + Id: "federationTesterServer", + Type: "string", + Label: "Federation Tester Server", + Placeholder: "https://federationtester.matrix.org/", + Default: "https://federationtester.matrix.org/", + Required: true, + }, + }, + } +} + +type FederationTesterResponse struct { + WellKnownResult struct { + Server string `json:"m.server"` + Result string `json:"result"` + } + DNSResult struct { + SRVError *struct { + Message string + } + } + ConnectionReports map[string]struct { + Errors []string + } + ConnectionErrors map[string]struct { + Message string + } + Version struct { + Name string `json:"name"` + Version string `json:"version"` + } + FederationOK bool `json:"FederationOK"` +} + +func (p *MatrixTester) RunTest(options happydns.PluginOptions, meta map[string]string) (*happydns.PluginResult, error) { + var domain string + + if dn, ok := options["domain"]; ok { + domain, _ = dn.(string) + } else if origin, ok := options["origin"]; ok { + domain, _ = origin.(string) + } + + if domain == "" { + return nil, fmt.Errorf("domain not defined") + } + + domain = strings.TrimSuffix(domain, ".") + + resp, err := http.Get(fmt.Sprintf(p.TesterURI, domain)) + if err != nil { + return nil, fmt.Errorf("unable to perform the test: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode >= 300 { + return nil, fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, ".")) + } + + var status happydns.PluginResultStatus + var statusLine string + var federationTest FederationTesterResponse + + err = json.NewDecoder(resp.Body).Decode(&federationTest) + if err != nil { + log.Printf("Error in check_matrix_federation, when decoding json: %s", err.Error()) + return nil, fmt.Errorf("sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, ".")) + } + + if federationTest.FederationOK { + status = happydns.PluginResultStatusOK + statusLine = "Running " + federationTest.Version.Name + " " + federationTest.Version.Version + } else { + status = happydns.PluginResultStatusKO + + if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" { + statusLine = fmt.Sprintf("%s OR %s", federationTest.DNSResult.SRVError.Message, federationTest.WellKnownResult.Result) + } else if len(federationTest.ConnectionErrors) > 0 { + var msg strings.Builder + for srv, cerr := range federationTest.ConnectionErrors { + if msg.Len() > 0 { + msg.WriteString("; ") + } + msg.WriteString(srv) + msg.WriteString(": ") + msg.WriteString(cerr.Message) + } + statusLine = fmt.Sprintf("Connection errors: %s", msg.String()) + } else if federationTest.WellKnownResult.Server != strings.TrimSuffix(domain, ".") { + statusLine = fmt.Sprintf("Bad homeserver_name: got %s, expected %s.", federationTest.WellKnownResult.Server, strings.TrimSuffix(domain, ".")) + } else { + statusLine = fmt.Sprintf("An unimplemented error occurs. Please report this to happydomain team. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, ".")) + } + } + + return &happydns.PluginResult{ + Status: status, + StatusLine: statusLine, + Report: federationTest, + }, nil +}