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") } for _, answer := range r.Answer { if t, ok := answer.(*dns.AAAA); ok { aaaa = t.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() if 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/", domain)) if err != nil { return } defer resp.Body.Close() if resp.StatusCode >= 300 { return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status) } _, err = ioutil.ReadAll(resp.Body) return } // 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 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 tuns, err := std.GetActivesTunnels() if err != nil { continue } for _, tun := range tuns { stdIP := tun.GetStudentIP() go 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 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 }) } } }