package server import "net/http" import "html/template" import "github.com/namecoin/ncdns/util" import "github.com/namecoin/ncdns/ncdomain" import "github.com/miekg/dns" import "github.com/kr/pretty" import "path/filepath" import "time" import "strings" import "fmt" var layoutTpl *template.Template var mainPageTpl *template.Template var lookupPageTpl *template.Template func (s *Server) initTemplates() error { if lookupPageTpl != nil { return nil } var err error layoutTpl, err = template.ParseFiles(s.tplFilename("layout")) if err != nil { return err } mainPageTpl, err = deriveTemplate(s.tplFilename("main")) if err != nil { return err } lookupPageTpl, err = deriveTemplate(s.tplFilename("lookup")) return err } func deriveTemplate(filename string) (*template.Template, error) { cl, err := layoutTpl.Clone() if err != nil { return nil, err } return cl.ParseFiles(filename) } func (s *Server) tplFilename(filename string) string { td := filepath.Join(s.cfg.ConfigDir, "..", "tpl") if s.cfg.TplPath != "" { td = s.cfg.TplPath } return filepath.Join(td, s.cfg.TplSet, filename+".tpl") } type webServer struct { s *Server sm *http.ServeMux } type layoutInfo struct { SelfName string Time string CanonicalSuffix string CanonicalNameservers []string Hostmaster string CanonicalSuffixHTML template.HTML TLD string HasDNSSEC bool } func (ws *webServer) layoutInfo() *layoutInfo { csparts := strings.SplitN(ws.s.cfg.CanonicalSuffix, ".", 2) cshtml := `` + csparts[0] + `` if len(csparts) > 1 { cshtml = `` + csparts[0] + `.` + csparts[1] + `` } var tld string if len(csparts) > 1 { tld = "." + csparts[1] } li := &layoutInfo{ SelfName: ws.s.ServerName(), Time: time.Now().Format("2006-01-02 15:04:05"), CanonicalSuffix: ws.s.cfg.CanonicalSuffix, CanonicalNameservers: ws.s.cfg.canonicalNameservers, Hostmaster: ws.s.cfg.Hostmaster, CanonicalSuffixHTML: template.HTML(cshtml), TLD: tld, HasDNSSEC: ws.s.cfg.ZonePublicKey != "", } return li } func (ws *webServer) handleRoot(rw http.ResponseWriter, req *http.Request) { err := mainPageTpl.Execute(rw, ws.layoutInfo()) log.Infoe(err, "tpl") } func (ws *webServer) handleLookup(rw http.ResponseWriter, req *http.Request) { info := struct { layoutInfo JSONMode bool JSONValue string Query string Advanced bool NamecoinName string DomainName string BareName string NameParseError error ExistenceError error Expired bool Value string NCValue *ncdomain.Value NCValueFmt fmt.Formatter ParseErrors []error ParseWarnings []error RRs []dns.RR RRError error Valid bool }{layoutInfo: *ws.layoutInfo()} defer func() { err := lookupPageTpl.Execute(rw, &info) log.Infoe(err, "lookup page tpl") }() q := req.FormValue("q") info.Query = q info.BareName, info.NamecoinName, info.NameParseError = util.ParseFuzzyDomainNameNC(q) if info.NameParseError != nil { return } info.Advanced = (req.FormValue("adv") != "") info.DomainName = info.BareName + ".bit." info.JSONValue = req.FormValue("value") info.Value = strings.Trim(info.JSONValue, " \t\r\n") if info.Value == "" { info.Value, info.ExistenceError = ws.s.namecoinConn.NameQuery(info.NamecoinName, "") if info.ExistenceError != nil { return } } else { info.JSONMode = true } errorFunc := func(e error, isWarning bool) { if isWarning { info.ParseWarnings = append(info.ParseWarnings, e) } else { info.ParseErrors = append(info.ParseErrors, e) } } info.NCValue = ncdomain.ParseValue(info.NamecoinName, info.Value, ws.resolveFunc, errorFunc) if info.NCValue == nil { return } info.NCValueFmt = pretty.Formatter(info.NCValue) info.RRs, info.RRError = info.NCValue.RRsRecursive(nil, info.DomainName, "bit.") if len(info.ParseErrors) == 0 && info.RRError == nil { info.Valid = true } } func (ws *webServer) resolveFunc(name string) (string, error) { return ws.s.namecoinConn.NameQuery(name, "") } func (ws *webServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline';") rw.Header().Set("X-Frame-Options", "DENY") rw.Header().Set("X-Content-Type-Options", "nosniff") rw.Header().Set("Server", "ncdns") //req.Header.Set("Strict-Transport-Security", "max-age=259200") //req.Header.Set("X-Download-Options", "noopen") //req.Header.Set("X-XSS-Protection", "0") //req.Header.Set("X-Permitted-Cross-Domain-Policies", "none") clearAllCookies(rw, req) ws.sm.ServeHTTP(rw, req) } func clearAllCookies(rw http.ResponseWriter, req *http.Request) { for _, ck := range req.Cookies() { ck2 := http.Cookie{ Name: ck.Name, MaxAge: -1, } rw.Header().Add("Set-Cookie", ck2.String()) } } func webStart(listenAddr string, server *Server) error { if err := server.initTemplates(); err != nil { return err } ws := &webServer{ s: server, sm: http.NewServeMux(), } ws.sm.HandleFunc("/", ws.handleRoot) ws.sm.HandleFunc("/lookup", ws.handleLookup) s := http.Server{ Addr: listenAddr, Handler: ws, } go func() { err := s.ListenAndServe() log.Errore(err, "HTTP server") }() return nil }