checker: Move checks in checks.go
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
3108f13073
commit
f48170e50b
@ -1,19 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-ping/ping"
|
"github.com/go-ping/ping"
|
||||||
"github.com/miekg/dns"
|
|
||||||
|
|
||||||
"git.nemunai.re/srs/adlin/libadlin"
|
"git.nemunai.re/srs/adlin/libadlin"
|
||||||
)
|
)
|
||||||
@ -29,515 +22,6 @@ var (
|
|||||||
domainsHostingMap = map[string]string{}
|
domainsHostingMap = map[string]string{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ICMP
|
|
||||||
|
|
||||||
func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) {
|
|
||||||
var pinger *ping.Pinger
|
|
||||||
pinger, err = ping.NewPinger(ip)
|
|
||||||
if err != nil {
|
|
||||||
if verbose {
|
|
||||||
log.Printf("check_ping: %s: %s", ip, err.Error())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer pinger.Stop()
|
|
||||||
|
|
||||||
pinger.Timeout = time.Second * 5
|
|
||||||
pinger.Count = 1
|
|
||||||
pinger.OnRecv = cb
|
|
||||||
pinger.SetPrivileged(true)
|
|
||||||
err = pinger.Run()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PORT 53
|
|
||||||
|
|
||||||
func get_GLUE(student *adlin.Student) (aaaa net.IP, err error) {
|
|
||||||
client := dns.Client{Net: "tcp", Timeout: time.Second * 5}
|
|
||||||
domain := student.MyDelegatedDomain()
|
|
||||||
|
|
||||||
dnssrv := "[2a01:e0a:2b:2250::b]:53"
|
|
||||||
if strings.HasSuffix(domain, student.MyDelegatedDomainSuffix()) {
|
|
||||||
dnssrv = "[2a01:e0a:2b:2250::b]:53"
|
|
||||||
} else if v, ok := domainsHostingMap[domain]; ok {
|
|
||||||
dnssrv = v
|
|
||||||
} else {
|
|
||||||
// Looking for root NS
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(".", dns.TypeNS)
|
|
||||||
m.RecursionDesired = false
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
|
|
||||||
var r *dns.Msg
|
|
||||||
r, _, err = client.Exchange(m, dnssrv)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return nil, errors.New("response is nil during initial recursion")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return nil, errors.New("failed to get a valid answer during initial recursion")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, answer := range r.Answer {
|
|
||||||
if t, ok := answer.(*dns.NS); ok {
|
|
||||||
dnssrv = t.Ns + ":53"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Do casual recursion
|
|
||||||
i := 0
|
|
||||||
recursion:
|
|
||||||
for i = 0; i < 10; i++ {
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(domain, dns.TypeNS)
|
|
||||||
m.RecursionDesired = false
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
|
|
||||||
var r *dns.Msg
|
|
||||||
r, _, err = client.Exchange(m, dnssrv)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return nil, errors.New("response is nil during recursion")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return nil, errors.New("failed to get a valid answer during recursion")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, answer := range r.Ns {
|
|
||||||
if t, ok := answer.(*dns.NS); ok {
|
|
||||||
dnssrv = t.Ns + ":53"
|
|
||||||
if t.Header().Name == domain {
|
|
||||||
break recursion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i >= 10 {
|
|
||||||
return nil, fmt.Errorf("too much name recursions")
|
|
||||||
} else {
|
|
||||||
domainsHostingMap[domain] = dnssrv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(domain, dns.TypeNS)
|
|
||||||
m.RecursionDesired = false
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
|
|
||||||
var r *dns.Msg
|
|
||||||
r, _, err = client.Exchange(m, dnssrv)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return nil, errors.New("response is nil")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return nil, errors.New("failed to get a valid answer")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, extra := range r.Extra {
|
|
||||||
if t, ok := extra.(*dns.AAAA); ok {
|
|
||||||
aaaa = t.AAAA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
|
||||||
client := dns.Client{Timeout: time.Second * 5}
|
|
||||||
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(domain, dns.TypeAAAA)
|
|
||||||
|
|
||||||
var r *dns.Msg
|
|
||||||
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
err = errors.New("response is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
err = errors.New("failed to get a valid answer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, answer := range r.Answer {
|
|
||||||
if t, ok := answer.(*dns.AAAA); ok {
|
|
||||||
aaaa = t.AAAA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func check_dnssec(domain, ip string) (err error) {
|
|
||||||
client := dns.Client{Net: "tcp", Timeout: time.Second * 10}
|
|
||||||
|
|
||||||
// Get DNSKEY
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
m.SetQuestion(domain, dns.TypeDNSKEY)
|
|
||||||
|
|
||||||
var r *dns.Msg
|
|
||||||
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return errors.New("response is nil")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return errors.New("failed to get a valid answer when getting DNSKEY")
|
|
||||||
}
|
|
||||||
|
|
||||||
var rrs []dns.RR
|
|
||||||
var dnskeys []*dns.DNSKEY
|
|
||||||
var dnskeysig *dns.RRSIG
|
|
||||||
for _, answer := range r.Answer {
|
|
||||||
if t, ok := answer.(*dns.DNSKEY); ok {
|
|
||||||
dnskeys = append(dnskeys, t)
|
|
||||||
rrs = append(rrs, dns.RR(t))
|
|
||||||
} else if t, ok := answer.(*dns.RRSIG); ok {
|
|
||||||
dnskeysig = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dnskeysig == nil {
|
|
||||||
return fmt.Errorf("Unable to verify DNSKEY record signature: No RRSIG found for DNSKEY record.")
|
|
||||||
}
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for _, dnskey := range dnskeys {
|
|
||||||
if err = dnskeysig.Verify(dnskey, rrs); err == nil {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf("Unable to verify DNSKEY record signature: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check AAAA validity
|
|
||||||
m = new(dns.Msg)
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
m.SetQuestion(domain, dns.TypeAAAA)
|
|
||||||
|
|
||||||
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return errors.New("response is nil")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return errors.New("failed to get a valid answer when getting AAAA records")
|
|
||||||
}
|
|
||||||
|
|
||||||
rrs = []dns.RR{}
|
|
||||||
var aaaas []*dns.AAAA
|
|
||||||
var aaaasig *dns.RRSIG
|
|
||||||
for _, answer := range r.Answer {
|
|
||||||
if t, ok := answer.(*dns.AAAA); ok {
|
|
||||||
aaaas = append(aaaas, t)
|
|
||||||
rrs = append(rrs, t)
|
|
||||||
} else if t, ok := answer.(*dns.RRSIG); ok {
|
|
||||||
aaaasig = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(aaaas) == 0 {
|
|
||||||
return errors.New("Something odd happen: no AAAA record found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if aaaasig == nil {
|
|
||||||
return fmt.Errorf("Unable to verify AAAA record signature: No RRSIG found for AAAA record.")
|
|
||||||
}
|
|
||||||
|
|
||||||
found = false
|
|
||||||
for _, dnskey := range dnskeys {
|
|
||||||
if err = aaaasig.Verify(dnskey, rrs); err == nil {
|
|
||||||
found = true
|
|
||||||
|
|
||||||
if !aaaasig.ValidityPeriod(time.Now()) {
|
|
||||||
utc := time.Now().UTC().Unix()
|
|
||||||
|
|
||||||
modi := (int64(aaaasig.Inception) - utc) / year68
|
|
||||||
ti := int64(aaaasig.Inception) + modi*year68
|
|
||||||
|
|
||||||
mode := (int64(aaaasig.Expiration) - utc) / year68
|
|
||||||
te := int64(aaaasig.Expiration) + mode*year68
|
|
||||||
|
|
||||||
if ti > utc {
|
|
||||||
return fmt.Errorf("Unable to verify AAAA record signature: signature not yet valid")
|
|
||||||
} else if utc > te {
|
|
||||||
return fmt.Errorf("Unable to verify AAAA record signature: signature expired")
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Unable to verify AAAA record signature: signature expired or not yet valid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf("Unable to verify AAAA record signature: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check DS
|
|
||||||
m = new(dns.Msg)
|
|
||||||
m.SetQuestion(domain, dns.TypeDS)
|
|
||||||
m.RecursionDesired = false
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
|
|
||||||
r, _, err = client.Exchange(m, "[2a01:e0a:2b:2250::b]:53")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return errors.New("response is nil")
|
|
||||||
}
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return errors.New("failed to get a valid answer when getting DS records in parent server")
|
|
||||||
}
|
|
||||||
|
|
||||||
found = false
|
|
||||||
for _, answer := range r.Answer {
|
|
||||||
if t, ok := answer.(*dns.DS); ok {
|
|
||||||
for _, dnskey := range dnskeys {
|
|
||||||
expectedDS := dnskey.ToDS(dns.SHA256)
|
|
||||||
if expectedDS.KeyTag == t.KeyTag && expectedDS.Algorithm == t.Algorithm && expectedDS.DigestType == t.DigestType && expectedDS.Digest == t.Digest {
|
|
||||||
found = true
|
|
||||||
err = nil
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("DS record found in parent zone differs from DNSKEY %v vs. %v.", expectedDS, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("Unable to find a valid DS record in parent zone (if you use your own domain (ie. not given by maatma), this can be due to a previously cached response, you should wait).")
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PORT 80
|
|
||||||
|
|
||||||
func check_http(ip, dn string) (err error) {
|
|
||||||
client := &http.Client{
|
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
req, errr := http.NewRequest("GET", fmt.Sprintf("http://[%s]/", ip), nil)
|
|
||||||
if errr != nil {
|
|
||||||
return errr
|
|
||||||
}
|
|
||||||
|
|
||||||
if dn != "" {
|
|
||||||
req.Header.Add("Host", strings.TrimSuffix(dn, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp *http.Response
|
|
||||||
resp, err = client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if dn != "" && resp.StatusCode >= 400 {
|
|
||||||
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ioutil.ReadAll(resp.Body)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PORT 443
|
|
||||||
|
|
||||||
func check_https(domain, ip string) (err error) {
|
|
||||||
var resp *http.Response
|
|
||||||
resp, err = http.Get(fmt.Sprintf("https://%s/", strings.TrimSuffix(domain, ".")))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
|
||||||
loc := resp.Header.Get("Location")
|
|
||||||
if loc != "" && strings.HasSuffix(dns.Fqdn(loc), domain) {
|
|
||||||
if dns.Fqdn(loc) == domain {
|
|
||||||
return fmt.Errorf("Redirection loop %s redirect to %s", domain, loc)
|
|
||||||
} else if err = check_https(dns.Fqdn(loc), ip); err != nil {
|
|
||||||
return fmt.Errorf("Error after following redirection to %s: %w", loc, err)
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
|
||||||
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ioutil.ReadAll(resp.Body)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MATRIX
|
|
||||||
|
|
||||||
type matrix_result 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 check_matrix_federation(domain string) (version string, err error) {
|
|
||||||
var resp *http.Response
|
|
||||||
resp, err = http.Get(fmt.Sprintf("https://federation-tester.adlin.nemunai.re/api/report?server_name=%s", strings.TrimSuffix(domain, ".")))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
|
||||||
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
var federationTest matrix_result
|
|
||||||
if err = json.NewDecoder(resp.Body).Decode(&federationTest); err != nil {
|
|
||||||
log.Printf("Error in check_matrix_federation, when decoding json: %s", err.Error())
|
|
||||||
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
|
||||||
} else if federationTest.FederationOK {
|
|
||||||
version = federationTest.Version.Name + " " + federationTest.Version.Version
|
|
||||||
return version, nil
|
|
||||||
} else if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" {
|
|
||||||
return "", fmt.Errorf("%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)
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("Connection errors: %s", msg.String())
|
|
||||||
} else if federationTest.WellKnownResult.Server != strings.TrimSuffix(domain, ".") {
|
|
||||||
return "", fmt.Errorf("Bad homeserver_name: got %s, expected %s.", federationTest.WellKnownResult.Server, strings.TrimSuffix(domain, "."))
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type matrix_wk_client struct {
|
|
||||||
Homeserver struct {
|
|
||||||
BaseURL string `json:"base_url"`
|
|
||||||
} `json:"m.homeserver"`
|
|
||||||
IdentityServer struct {
|
|
||||||
BaseURL string `json:"base_url"`
|
|
||||||
} `json:"m.identity_server"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type matrix_client_versions struct {
|
|
||||||
Versions []string `json:"versions"`
|
|
||||||
UnstableFeatures map[string]bool `json:"unstable_features"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func check_matrix_client(domain string) (version string, err error) {
|
|
||||||
var resp *http.Response
|
|
||||||
resp, err = http.Get(fmt.Sprintf("https://%s/.well-known/matrix/client", strings.TrimSuffix(domain, ".")))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var HomeserverBase = fmt.Sprintf("https://%s", strings.TrimSuffix(domain, "."))
|
|
||||||
|
|
||||||
if resp.StatusCode < 300 {
|
|
||||||
var wellknown matrix_wk_client
|
|
||||||
if err = json.NewDecoder(resp.Body).Decode(&wellknown); err != nil {
|
|
||||||
log.Printf("Error in check_matrix_client, when decoding json: %s", err.Error())
|
|
||||||
return "", fmt.Errorf("File at https://%s/.well-known/matrix/client is invalid: JSON parse error", strings.TrimSuffix(domain, "."))
|
|
||||||
} else if wellknown.Homeserver.BaseURL != "" {
|
|
||||||
if baseurl, err := url.Parse(wellknown.Homeserver.BaseURL); err != nil {
|
|
||||||
return "", fmt.Errorf("File at https://%s/.well-known/matrix/client is invalid: Bad homeserver URL: %s", strings.TrimSuffix(domain, "."), err.Error())
|
|
||||||
} else if !strings.HasSuffix(strings.TrimSuffix(baseurl.Host, "."), strings.TrimSuffix(domain, ".")) {
|
|
||||||
return "", fmt.Errorf("Your homeserver base_url is not under %s", strings.TrimSuffix(domain, "."))
|
|
||||||
} else if strings.TrimSuffix(baseurl.Host, ".") == strings.TrimSuffix(domain, ".") {
|
|
||||||
// This test can be optional
|
|
||||||
return "", fmt.Errorf("Your homeserver should be on its own subdomain")
|
|
||||||
} else {
|
|
||||||
HomeserverBase = wellknown.Homeserver.BaseURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp2 *http.Response
|
|
||||||
resp2, err = http.Get(fmt.Sprintf("%s/_matrix/client/versions", HomeserverBase))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp2.Body.Close()
|
|
||||||
|
|
||||||
if resp2.StatusCode != http.StatusOK {
|
|
||||||
return "", fmt.Errorf("Unable to fetch your homeserver versions at %s/_matrix/client/versions: %s", HomeserverBase, resp2.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientTest matrix_client_versions
|
|
||||||
if err = json.NewDecoder(resp2.Body).Decode(&clientTest); err != nil {
|
|
||||||
log.Printf("Error in check_matrix_client, when decoding versions json: %s", err.Error())
|
|
||||||
return "", fmt.Errorf("File at %s/_matrix/client/versions is invalid: JSON parse error: %s", HomeserverBase, err.Error())
|
|
||||||
} else if len(clientTest.Versions) == 0 {
|
|
||||||
return "", fmt.Errorf("File at %s/_matrix/client/versions is invalid: no protocol version supported", HomeserverBase)
|
|
||||||
} else {
|
|
||||||
return clientTest.Versions[len(clientTest.Versions)-1], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
|
|
||||||
func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) {
|
func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) {
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ping/ping"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
|
"git.nemunai.re/srs/adlin/libadlin"
|
||||||
|
)
|
||||||
|
|
||||||
type AdlinTest int
|
type AdlinTest int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -54,3 +72,512 @@ var CheckMap = map[int]map[AdlinTest]int{
|
|||||||
GuestNet: 214,
|
GuestNet: 214,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ICMP
|
||||||
|
|
||||||
|
func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) {
|
||||||
|
var pinger *ping.Pinger
|
||||||
|
pinger, err = ping.NewPinger(ip)
|
||||||
|
if err != nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("check_ping: %s: %s", ip, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer pinger.Stop()
|
||||||
|
|
||||||
|
pinger.Timeout = time.Second * 5
|
||||||
|
pinger.Count = 1
|
||||||
|
pinger.OnRecv = cb
|
||||||
|
pinger.SetPrivileged(true)
|
||||||
|
err = pinger.Run()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PORT 53
|
||||||
|
|
||||||
|
func get_GLUE(student *adlin.Student) (aaaa net.IP, err error) {
|
||||||
|
client := dns.Client{Net: "tcp", Timeout: time.Second * 5}
|
||||||
|
domain := student.MyDelegatedDomain()
|
||||||
|
|
||||||
|
dnssrv := "[2a01:e0a:2b:2250::b]:53"
|
||||||
|
if strings.HasSuffix(domain, student.MyDelegatedDomainSuffix()) {
|
||||||
|
dnssrv = "[2a01:e0a:2b:2250::b]:53"
|
||||||
|
} else if v, ok := domainsHostingMap[domain]; ok {
|
||||||
|
dnssrv = v
|
||||||
|
} else {
|
||||||
|
// Looking for root NS
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(".", dns.TypeNS)
|
||||||
|
m.RecursionDesired = false
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, dnssrv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return nil, errors.New("response is nil during initial recursion")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return nil, errors.New("failed to get a valid answer during initial recursion")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.NS); ok {
|
||||||
|
dnssrv = t.Ns + ":53"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do casual recursion
|
||||||
|
i := 0
|
||||||
|
recursion:
|
||||||
|
for i = 0; i < 10; i++ {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(domain, dns.TypeNS)
|
||||||
|
m.RecursionDesired = false
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, dnssrv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return nil, errors.New("response is nil during recursion")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return nil, errors.New("failed to get a valid answer during recursion")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, answer := range r.Ns {
|
||||||
|
if t, ok := answer.(*dns.NS); ok {
|
||||||
|
dnssrv = t.Ns + ":53"
|
||||||
|
if t.Header().Name == domain {
|
||||||
|
break recursion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= 10 {
|
||||||
|
return nil, fmt.Errorf("too much name recursions")
|
||||||
|
} else {
|
||||||
|
domainsHostingMap[domain] = dnssrv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(domain, dns.TypeNS)
|
||||||
|
m.RecursionDesired = false
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, dnssrv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return nil, errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return nil, errors.New("failed to get a valid answer")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extra := range r.Extra {
|
||||||
|
if t, ok := extra.(*dns.AAAA); ok {
|
||||||
|
aaaa = t.AAAA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
||||||
|
client := dns.Client{Timeout: time.Second * 5}
|
||||||
|
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(domain, dns.TypeAAAA)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
err = errors.New("response is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
err = errors.New("failed to get a valid answer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.AAAA); ok {
|
||||||
|
aaaa = t.AAAA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_dnssec(domain, ip string) (err error) {
|
||||||
|
client := dns.Client{Net: "tcp", Timeout: time.Second * 10}
|
||||||
|
|
||||||
|
// Get DNSKEY
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
m.SetQuestion(domain, dns.TypeDNSKEY)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting DNSKEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rrs []dns.RR
|
||||||
|
var dnskeys []*dns.DNSKEY
|
||||||
|
var dnskeysig *dns.RRSIG
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.DNSKEY); ok {
|
||||||
|
dnskeys = append(dnskeys, t)
|
||||||
|
rrs = append(rrs, dns.RR(t))
|
||||||
|
} else if t, ok := answer.(*dns.RRSIG); ok {
|
||||||
|
dnskeysig = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dnskeysig == nil {
|
||||||
|
return fmt.Errorf("Unable to verify DNSKEY record signature: No RRSIG found for DNSKEY record.")
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
if err = dnskeysig.Verify(dnskey, rrs); err == nil {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("Unable to verify DNSKEY record signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check AAAA validity
|
||||||
|
m = new(dns.Msg)
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
m.SetQuestion(domain, dns.TypeAAAA)
|
||||||
|
|
||||||
|
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting AAAA records")
|
||||||
|
}
|
||||||
|
|
||||||
|
rrs = []dns.RR{}
|
||||||
|
var aaaas []*dns.AAAA
|
||||||
|
var aaaasig *dns.RRSIG
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.AAAA); ok {
|
||||||
|
aaaas = append(aaaas, t)
|
||||||
|
rrs = append(rrs, t)
|
||||||
|
} else if t, ok := answer.(*dns.RRSIG); ok {
|
||||||
|
aaaasig = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(aaaas) == 0 {
|
||||||
|
return errors.New("Something odd happen: no AAAA record found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if aaaasig == nil {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: No RRSIG found for AAAA record.")
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
if err = aaaasig.Verify(dnskey, rrs); err == nil {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
if !aaaasig.ValidityPeriod(time.Now()) {
|
||||||
|
utc := time.Now().UTC().Unix()
|
||||||
|
|
||||||
|
modi := (int64(aaaasig.Inception) - utc) / year68
|
||||||
|
ti := int64(aaaasig.Inception) + modi*year68
|
||||||
|
|
||||||
|
mode := (int64(aaaasig.Expiration) - utc) / year68
|
||||||
|
te := int64(aaaasig.Expiration) + mode*year68
|
||||||
|
|
||||||
|
if ti > utc {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature not yet valid")
|
||||||
|
} else if utc > te {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature expired")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature expired or not yet valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check DS
|
||||||
|
m = new(dns.Msg)
|
||||||
|
m.SetQuestion(domain, dns.TypeDS)
|
||||||
|
m.RecursionDesired = false
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
|
r, _, err = client.Exchange(m, "[2a01:e0a:2b:2250::b]:53")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting DS records in parent server")
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.DS); ok {
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
expectedDS := dnskey.ToDS(dns.SHA256)
|
||||||
|
if expectedDS.KeyTag == t.KeyTag && expectedDS.Algorithm == t.Algorithm && expectedDS.DigestType == t.DigestType && expectedDS.Digest == t.Digest {
|
||||||
|
found = true
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("DS record found in parent zone differs from DNSKEY %v vs. %v.", expectedDS, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Unable to find a valid DS record in parent zone (if you use your own domain (ie. not given by maatma), this can be due to a previously cached response, you should wait).")
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PORT 80
|
||||||
|
|
||||||
|
func check_http(ip, dn string) (err error) {
|
||||||
|
client := &http.Client{
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req, errr := http.NewRequest("GET", fmt.Sprintf("http://[%s]/", ip), nil)
|
||||||
|
if errr != nil {
|
||||||
|
return errr
|
||||||
|
}
|
||||||
|
|
||||||
|
if dn != "" {
|
||||||
|
req.Header.Add("Host", strings.TrimSuffix(dn, "."))
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if dn != "" && resp.StatusCode >= 400 {
|
||||||
|
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ioutil.ReadAll(resp.Body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PORT 443
|
||||||
|
|
||||||
|
func check_https(domain, ip string) (err error) {
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = http.Get(fmt.Sprintf("https://%s/", strings.TrimSuffix(domain, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
||||||
|
loc := resp.Header.Get("Location")
|
||||||
|
if loc != "" && strings.HasSuffix(dns.Fqdn(loc), domain) {
|
||||||
|
if dns.Fqdn(loc) == domain {
|
||||||
|
return fmt.Errorf("Redirection loop %s redirect to %s", domain, loc)
|
||||||
|
} else if err = check_https(dns.Fqdn(loc), ip); err != nil {
|
||||||
|
return fmt.Errorf("Error after following redirection to %s: %w", loc, err)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ioutil.ReadAll(resp.Body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MATRIX
|
||||||
|
|
||||||
|
type matrix_result 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 check_matrix_federation(domain string) (version string, err error) {
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = http.Get(fmt.Sprintf("https://federation-tester.adlin.nemunai.re/api/report?server_name=%s", strings.TrimSuffix(domain, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
||||||
|
}
|
||||||
|
|
||||||
|
var federationTest matrix_result
|
||||||
|
if err = json.NewDecoder(resp.Body).Decode(&federationTest); err != nil {
|
||||||
|
log.Printf("Error in check_matrix_federation, when decoding json: %s", err.Error())
|
||||||
|
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
||||||
|
} else if federationTest.FederationOK {
|
||||||
|
version = federationTest.Version.Name + " " + federationTest.Version.Version
|
||||||
|
return version, nil
|
||||||
|
} else if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" {
|
||||||
|
return "", fmt.Errorf("%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)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("Connection errors: %s", msg.String())
|
||||||
|
} else if federationTest.WellKnownResult.Server != strings.TrimSuffix(domain, ".") {
|
||||||
|
return "", fmt.Errorf("Bad homeserver_name: got %s, expected %s.", federationTest.WellKnownResult.Server, strings.TrimSuffix(domain, "."))
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", strings.TrimSuffix(domain, "."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type matrix_wk_client struct {
|
||||||
|
Homeserver struct {
|
||||||
|
BaseURL string `json:"base_url"`
|
||||||
|
} `json:"m.homeserver"`
|
||||||
|
IdentityServer struct {
|
||||||
|
BaseURL string `json:"base_url"`
|
||||||
|
} `json:"m.identity_server"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type matrix_client_versions struct {
|
||||||
|
Versions []string `json:"versions"`
|
||||||
|
UnstableFeatures map[string]bool `json:"unstable_features"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_matrix_client(domain string) (version string, err error) {
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = http.Get(fmt.Sprintf("https://%s/.well-known/matrix/client", strings.TrimSuffix(domain, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var HomeserverBase = fmt.Sprintf("https://%s", strings.TrimSuffix(domain, "."))
|
||||||
|
|
||||||
|
if resp.StatusCode < 300 {
|
||||||
|
var wellknown matrix_wk_client
|
||||||
|
if err = json.NewDecoder(resp.Body).Decode(&wellknown); err != nil {
|
||||||
|
log.Printf("Error in check_matrix_client, when decoding json: %s", err.Error())
|
||||||
|
return "", fmt.Errorf("File at https://%s/.well-known/matrix/client is invalid: JSON parse error", strings.TrimSuffix(domain, "."))
|
||||||
|
} else if wellknown.Homeserver.BaseURL != "" {
|
||||||
|
if baseurl, err := url.Parse(wellknown.Homeserver.BaseURL); err != nil {
|
||||||
|
return "", fmt.Errorf("File at https://%s/.well-known/matrix/client is invalid: Bad homeserver URL: %s", strings.TrimSuffix(domain, "."), err.Error())
|
||||||
|
} else if !strings.HasSuffix(strings.TrimSuffix(baseurl.Host, "."), strings.TrimSuffix(domain, ".")) {
|
||||||
|
return "", fmt.Errorf("Your homeserver base_url is not under %s", strings.TrimSuffix(domain, "."))
|
||||||
|
} else if strings.TrimSuffix(baseurl.Host, ".") == strings.TrimSuffix(domain, ".") {
|
||||||
|
// This test can be optional
|
||||||
|
return "", fmt.Errorf("Your homeserver should be on its own subdomain")
|
||||||
|
} else {
|
||||||
|
HomeserverBase = wellknown.Homeserver.BaseURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp2 *http.Response
|
||||||
|
resp2, err = http.Get(fmt.Sprintf("%s/_matrix/client/versions", HomeserverBase))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp2.Body.Close()
|
||||||
|
|
||||||
|
if resp2.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("Unable to fetch your homeserver versions at %s/_matrix/client/versions: %s", HomeserverBase, resp2.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientTest matrix_client_versions
|
||||||
|
if err = json.NewDecoder(resp2.Body).Decode(&clientTest); err != nil {
|
||||||
|
log.Printf("Error in check_matrix_client, when decoding versions json: %s", err.Error())
|
||||||
|
return "", fmt.Errorf("File at %s/_matrix/client/versions is invalid: JSON parse error: %s", HomeserverBase, err.Error())
|
||||||
|
} else if len(clientTest.Versions) == 0 {
|
||||||
|
return "", fmt.Errorf("File at %s/_matrix/client/versions is invalid: no protocol version supported", HomeserverBase)
|
||||||
|
} else {
|
||||||
|
return clientTest.Versions[len(clientTest.Versions)-1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user