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

263 lines
6.5 KiB
Go

package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"time"
"github.com/miekg/dns"
"github.com/sparrc/go-ping"
"git.nemunai.re/lectures/adlin/libadlin"
)
var verbose = false
// 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 {
return
}
pinger.Timeout = time.Second * 5
pinger.Count = 1
pinger.OnRecv = cb
pinger.SetPrivileged(true)
pinger.Run()
return
}
// PORT 53
func get_GLUE(domain string) (aaaa net.IP, err error) {
client := dns.Client{Net: "tcp", Timeout: time.Second * 5}
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, "[2a01:e0a:25a:9160::2]:53")
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")
}
if r.Rcode != dns.RcodeSuccess {
err = errors.New("failed to get a valid answer")
}
if len(r.Answer) > 0 {
aaaa = r.Answer[0].(*dns.AAAA).AAAA
}
return
}
// PORT 80
func check_http(ip string) (err error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
var resp *http.Response
resp, err = client.Get(fmt.Sprintf("http://[%s]/", ip))
if err != nil {
return
}
defer resp.Body.Close()
_, 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/", domain))
if err != nil {
return
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
return
}
// Main
func minTunnelVersion(std adlin.Student) (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 {
minversion = tunnel.Version
}
}
return minversion, nil
}
func studentsChecker() {
students, err := adlin.GetStudents()
if err != nil {
log.Println("Unable to check students:", err)
return
}
log.Println("Checking students...")
for _, s := range students {
time.Sleep(250 * time.Millisecond)
// Check ping
std := s
stdIP := adlin.StudentIP(std.Id).String() + "1"
go check_ping(stdIP, func(pkt *ping.Packet) {
tunnel_version, err := minTunnelVersion(std)
if verbose {
log.Printf("%s PONG; version=%d (%v)\n", std.Login, tunnel_version, err)
}
std.OnPong(true)
if tunnel_version == 2147483647 || tunnel_version == 0 {
log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err)
return
}
dnsIP := stdIP
// Is GLUE defined?
if glueIP, err := get_GLUE(std.MyDelegatedDomain()); 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)
}
// Check DNS
if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil {
if verbose {
log.Printf("%s just unlocked DNS challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
// Check HTTP with DNS
if addr == nil {
log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String())
} else if err := check_http(addr.String()); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else if verbose {
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
// Check HTTPs with DNS
if addr == nil {
log.Printf("%s and HTTPS (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String())
} else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil {
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else if verbose {
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
} else {
// Check HTTP without DNS
if err := check_http(stdIP); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else 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.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
}
} else if verbose {
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
}
}
return
})
}
}