2
0
mirror of https://github.com/namecoin/ncdns synced 2024-10-31 09:20:31 +00:00
ncdns/server/web.go
2014-12-06 09:30:14 +00:00

223 lines
5.4 KiB
Go

package server
import "net/http"
import "html/template"
import "github.com/hlandau/degoutils/log"
import "github.com/hlandau/ncdns/util"
import "github.com/hlandau/ncdns/ncdomain"
import "github.com/miekg/dns"
import "github.com/kr/pretty"
import "time"
import "strings"
import "fmt"
import "flag"
var layoutTpl *template.Template
var mainPageTpl *template.Template
var lookupPageTpl *template.Template
var tplSetFlag = flag.String("tplset", "std", "Subdirectory of tpl/ to look for templates in (default: std)")
func initTemplates() error {
if lookupPageTpl != nil {
return nil
}
if *tplSetFlag == "" {
*tplSetFlag = "std"
}
var err error
layoutTpl, err = template.ParseFiles(tplFilename("layout"))
if err != nil {
return err
}
mainPageTpl, err = deriveTemplate(tplFilename("main"))
if err != nil {
return err
}
lookupPageTpl, err = deriveTemplate(tplFilename("lookup"))
if err != nil {
return err
}
return nil
}
func deriveTemplate(filename string) (*template.Template, error) {
cl, err := layoutTpl.Clone()
if err != nil {
return nil, err
}
return cl.ParseFiles(filename)
}
func tplFilename(filename string) string {
return "tpl/" + *tplSetFlag + "/" + 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 := `<span id="logo1">` + csparts[0] + `</span>`
if len(csparts) > 1 {
cshtml = `<span id="logo1">` + csparts[0] + `</span><span id="logo2">.</span><span id="logo3">` + csparts[1] + `</span>`
}
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: "." + csparts[1],
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.Query(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.Query(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 {
err := initTemplates()
if 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 s.ListenAndServe()
// TODO: error handling
return nil
}