This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
adlin/checker/checker.go
2022-04-29 23:08:54 +02:00

910 lines
28 KiB
Go

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"
)
const (
DEFAULT_RESOLVER = "2a01:e0a:2b:2250::1"
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. Taken from miekg/dns
)
var (
verbose = false
verbose2 = false
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
func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) {
tunnels, err := std.GetTunnelTokens()
if err != nil {
return 0, err
}
var minversion int = 2147483647
for _, tunnel := range tunnels {
if tunnel.Version == 0 {
continue
}
if tunnel.Dump != nil && tunnel.Version < minversion && suffixip == tunnel.SuffixIP {
minversion = tunnel.Version
}
}
return minversion, nil
}
func studentChecker(std *adlin.Student, also_check_matrix bool, offline bool) {
tuns, err := std.GetActivesTunnels()
if err != nil {
if offline {
tuns, err = std.GetDefaultTunnels()
if err != nil {
if verbose {
log.Printf("SKip %s as I'm unable to generate default tunnels: %s", std.Login, err.Error())
}
return
}
} else {
if verbose2 {
log.Printf("SKip %s due to error when getting active tunnels: %s", std.Login, err.Error())
}
return
}
}
if verbose2 && len(tuns) == 0 {
log.Printf("%s has no active tunnels: %s", std.Login, err.Error())
}
for _, tun := range tuns {
stdIP := tun.GetStudentIP()
if verbose2 {
log.Printf("Tests %s on %s...", std.Login, stdIP)
}
// Check ping
err = check_ping(stdIP, func(pkt *ping.Packet) {
tunnel_version, err := minTunnelVersion(std, tun.SuffixIP)
if verbose {
log.Printf("%s PONG (on %x); version=%d (%v)\n", std.Login, tun.SuffixIP, tunnel_version, err)
}
std.OnPong(true)
if !offline && (tunnel_version == 2147483647 || tunnel_version == 0) {
log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err)
return
}
// PingResolver
if tunnel_version == 3 {
tmp := strings.Split(stdIP, ":")
tmp[len(tmp)-1] = "2"
stdResolverIP := strings.Join(tmp, ":")
go check_ping(stdResolverIP, func(_ *ping.Packet) {
if verbose {
log.Printf("%s resolver PONG", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][PingResolver], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
})
}
dnsIP := stdIP
var glueErr error
// Is GLUE defined?
if glueIP, err := get_GLUE(std); glueIP != nil {
dnsIP = glueIP.String()
if verbose {
log.Printf("%s has defined GLUE: %s\n", std.Login, dnsIP)
}
} else if err != nil {
log.Printf("%s and GLUE: %s\n", std.Login, err)
glueErr = err
}
snicheck1 := false
snicheck1_tested := false
// Check DNS
if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil {
if addr == nil {
dnsAt := " at " + dnsIP
if glueErr != nil {
dnsAt = " + there is a problem with the GLUE record: " + glueErr.Error()
}
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], fmt.Errorf("%s: empty response from the server%s", std.MyDelegatedDomain(), dnsAt)); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
} else if tunnel_version == 3 && dnsIP == stdIP {
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], fmt.Errorf("%s: you shouldn't use DNAT on IPv6 (NAT on IPv6 is bad); instead, point your GLUE record to nsauth directly", std.MyDelegatedDomain())); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
} else {
if verbose {
log.Printf("%s just unlocked DNS challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][DNSDelegation], addr.String()); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
// Check HTTP with DNS
if glueErr != nil {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonDelegatedDomain], fmt.Errorf("Unable to perform the test due to GLUE problem: %w", glueErr))
} else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil {
if tunnel_version == 3 {
// Try port 80 on miniflux => should not respond if SNAT is correctly configured
minifluxIP := tun.GetServerIP(6)
if err := check_http(minifluxIP, std.MyDelegatedDomain()); err == nil {
if verbose {
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), "Bad SNAT config")
}
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonDelegatedDomain], fmt.Errorf("Your SNAT on IPv6 is badly configured. See HTTP IP result.")); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
} else {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonDelegatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonDelegatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonDelegatedDomain], err)
if verbose {
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
}
// Check HTTPs with DNS
if glueErr != nil {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonDelegatedDomain], fmt.Errorf("Unable to perform the test due to GLUE problem: %w", glueErr))
} else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil {
if tunnel_version == 3 {
// Try port 443 on miniflux => should not respond if SNAT is correctly configured
minifluxIP := tun.GetServerIP(6)
if err := check_https(std.MyDelegatedDomain(), minifluxIP); err == nil {
if verbose {
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), "Bad SNAT config")
}
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonDelegatedDomain], fmt.Errorf("Your SNAT on IPv6 is badly configured. You should not have SNAT on 443 port.")); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
} else {
snicheck1 = true
snicheck1_tested = true
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSonDelegatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
snicheck1 = true
snicheck1_tested = true
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSonDelegatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
snicheck1_tested = true
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonDelegatedDomain], err)
if verbose {
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
}
// Check Matrix (only if GLUE Ok and defer contraint)
if glueErr == nil && also_check_matrix {
// Check Matrix Federation first
if v, err := check_matrix_federation(std.MyDelegatedDomain()); err == nil {
if verbose {
log.Printf("%s just unlocked Matrix federation challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][MatrixSrv], v); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][MatrixSrv], err)
if verbose {
log.Printf("%s and Matrix federation: %s\n", std.Login, err)
}
}
// Check Matrix Client
if v, err := check_matrix_client(std.MyDelegatedDomain()); err == nil {
if verbose {
log.Printf("%s just unlocked Matrix client challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][MatrixClt], v); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][MatrixClt], err)
if verbose {
log.Printf("%s and Matrix client: %s\n", std.Login, err)
}
}
}
// Check DNSSEC (only if GLUE Ok)
if glueErr == nil {
if err := check_dnssec(std.MyDelegatedDomain(), dnsIP); err == nil {
if verbose {
log.Printf("%s just unlocked DNSSEC challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][DNSSEC], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][DNSSEC], err)
if verbose {
log.Printf("%s and DNSSEC: %s\n", std.Login, err)
}
}
}
}
} else {
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], err); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
if verbose {
log.Printf("%s and DNS: %s\n", std.Login, err)
}
}
// Check HTTP without DNS
if err := check_http(stdIP, ""); err == nil {
if tunnel_version == 3 {
// Try port 80 on miniflux => should not respond if SNAT is correctly configured
minifluxIP := tun.GetServerIP(6)
if err := check_http(minifluxIP, ""); err == nil {
if verbose {
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, "Bad SNAT config")
}
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonIP], fmt.Errorf("Your SNAT on IPv6 is too large: it seems that all requests to port 80 behind the router are redirected to web host. Eg. [news]:80 should not respond, however it responds with contents.")); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
} else {
if verbose {
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonIP], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
if verbose {
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonIP], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonIP], err)
if verbose {
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, err)
}
}
// Check DNS for association
if addr, err := check_dns(std.MyAssociatedDomain(), DEFAULT_RESOLVER); err == nil {
// Check HTTP on delegated domain
if err := check_http(addr.String(), std.MyAssociatedDomain()); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP (without DNS) challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonAssociatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonAssociatedDomain], err)
if verbose {
log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err)
}
}
// Check HTTPs without DNS
if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil {
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSonAssociatedDomain], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
// SNI check: validate if this check + HTTPS on delegation is validated
if snicheck1 {
if std.MyAssociatedDomain() == std.MyDelegatedDomain() {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSSNI], fmt.Errorf("associated and delegated domains have to be different. Please use eg. adlin.example.com as associated domain and wonderfulwebsite.example.com as delegation. Feel free to choose whatever you want that doesn't already exists in your zone!"))
if verbose {
log.Printf("%s and HTTPS-SNI: %s\n", std.Login, "associated and delegated domains not accessible at the same time through HTTPS")
}
} else {
if verbose {
log.Printf("%s just unlocked HTTPS-SNI challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSSNI], ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
}
} else {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonAssociatedDomain], err)
if verbose {
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
}
}
}
if snicheck1_tested && !snicheck1 {
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSSNI], fmt.Errorf("associated and delegated domain are not accessible through HTTPS at the same time, see errors for thoses checks"))
if verbose {
log.Printf("%s and HTTPS-SNI: %s\n", std.Login, "associated and delegated domains not accessible at the same time through HTTPS")
}
}
return
})
if err != nil && verbose {
log.Printf("%s: Unable to perform ping to %s: %s", std.Login, stdIP, err.Error())
}
}
}
func studentsChecker(offline bool) {
students, err := adlin.GetStudents()
if err != nil {
log.Println("Unable to check students:", err)
return
}
check_matrix_for := (time.Now().Second()/30)*5 + time.Now().Minute()%5
log.Printf("Checking students... (std_matrix%%10=%d)\n", check_matrix_for)
for istd, s := range students {
time.Sleep(250 * time.Millisecond)
go studentChecker(s, istd%10 == check_matrix_for, offline)
}
}