<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	runtime_debug "runtime/debug"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
)

const (
	CommonTimeout     = 3 * time.Second
	HostsConfig       = "/etc/trivialdns/hosts"
	NameserversConfig = "/etc/trivialdns/nameservers"
)

var (
	debugMode     = flag.Bool("debug", false, "print debug messages")
	webListenAddr = flag.String("web", ":8053", "setup web interface on this port")
	listenAddr    = flag.String("addr", ":53", "UDP and TCP host:port to start DNS service on")
)

func debug(fmt string, args ...interface{}) {
	if *debugMode {
		log.Printf(fmt, args...)
	}
}

func panicCatcher() {
	if r := recover(); r != nil {
		runtime_debug.PrintStack()
		log.Printf("PANIC: %s", r)
	}
}

func looksLikeDomainName(s string) bool {
	// domain names should have alphabet characters in them
	return strings.ContainsAny(s, "abcdefghijklmnopqrstuvwxyz")
}

type TrivialDnsServer struct {
	Servers  []string
	Database map[string]string

	stats     map[string]int
	statsLock sync.Mutex
}

func (self *TrivialDnsServer) Count(statname string) {
	self.statsLock.Lock()
	self.stats[statname] += 1
	self.statsLock.Unlock()
}

type StatTuple struct {
	Key   string
	Value int
}

func (self *TrivialDnsServer) GetStats() []StatTuple {
	result := []StatTuple{}
	keys := []string{}
	self.statsLock.Lock()
	for k, _ := range self.stats {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		result = append(result, StatTuple{k, self.stats[k]})
	}
	self.statsLock.Unlock()
	return result
}

func (self *TrivialDnsServer) refuse(w dns.ResponseWriter, req *dns.Msg) {
	self.Count("refusals")
	self.refuseWithCode(w, req, dns.RcodeRefused)
}

func (self *TrivialDnsServer) refuseOnPanic(w dns.ResponseWriter, req *dns.Msg) {
	self.Count("panic_refusals")
	self.refuseWithCode(w, req, dns.RcodeServerFailure)
}

func (self *TrivialDnsServer) refuseWithCode(w dns.ResponseWriter, req *dns.Msg, code int) {
	m := new(dns.Msg)
	for _, r := range req.Extra {
		if r.Header().Rrtype == dns.TypeOPT {
			m.SetEdns0(4096, r.(*dns.OPT).Do())
		}
	}
	m.SetRcode(req, code)
	w.WriteMsg(m)
}

func (self *TrivialDnsServer) getSingleSimpleQuestion(m *dns.Msg) *dns.Question {
	if len(m.Question) != 1 {
		return nil
	}
	q := m.Question[0]
	if q.Qtype != dns.TypeA || q.Qclass != dns.ClassINET {
		return nil
	}
	return &q
}

func (self *TrivialDnsServer) getSingleSimpleAnswer(m *dns.Msg) *net.IP {
	if len(m.Answer) != 1 {
		return nil
	}
	a, ok := m.Answer[0].(*dns.A)
	if !ok {
		return nil
	}
	if a.Hdr.Rrtype != dns.TypeA || a.Hdr.Class != dns.ClassINET {
		return nil
	}
	return &a.A
}

func (self *TrivialDnsServer) respondSuccessively(w dns.ResponseWriter, r *dns.Msg, addr net.IP) {
	q := self.getSingleSimpleQuestion(r)
	a := &dns.A{
		Hdr: dns.RR_Header{
			Name:     q.Name,
			Rrtype:   dns.TypeA,
			Class:    dns.ClassINET,
			Ttl:      30,
			Rdlength: 4,
		},
		A: addr,
	}
	m := new(dns.Msg)
	m.SetReply(r)
	m.Answer = []dns.RR{a}
	m.Authoritative = false
	w.WriteMsg(m)
}

func (self *TrivialDnsServer) tryAnswer(w dns.ResponseWriter, r *dns.Msg) bool {
	q := self.getSingleSimpleQuestion(r)
	if q == nil {
		return false
	}
	name := strings.TrimSuffix(q.Name, ".")
	if name == "panic.com" {
		panic("panic on request for panic.com")
	}
	value, ok := self.Database[name]
	if !ok {
		debug("%s: %s not found in local database", w.RemoteAddr(), name)
		parts := strings.Split(name, ".")
		b_found := false
		for len(parts) >= 2 {
			parts[0] = "*"
			wildcard_name := strings.Join(parts, ".")
			value, ok = self.Database[wildcard_name]
			if !ok {
				parts = parts[1:]
				continue
			} else {
				b_found = true
				break
			}
		}
		if !b_found {
			return false
		}
	}
	if looksLikeDomainName(value) {
		debug("%s: %s found in database -> redirect to %s", w.RemoteAddr(), name, value)
		self.redirectQuery(w, r, value)
		return true
	}

	debug("%s: %s found in database -> %s", w.RemoteAddr(), name, value)
	addr := net.ParseIP(value)
	self.Count("local_responses")
	self.respondSuccessively(w, r, addr)
	return true
}

func (self *TrivialDnsServer) redirectQuery(w dns.ResponseWriter, r *dns.Msg, newName string) {
	self.Count("redirected_requests")
	if !strings.HasSuffix(newName, ".") {
		newName = newName + "."
	}

	newR := new(dns.Msg)
	newR.SetQuestion(dns.Fqdn(newName), dns.TypeA)

	if response, _, err := self.exchangeWithUpstream(newR, false); err == nil {
		ip := self.getSingleSimpleAnswer(response)
		if ip == nil {
			debug("%s redirect to %s yielded no answer", w.RemoteAddr(), newName)
			self.Count("redirected_nowhere")
			self.refuse(w, r)
			return
		}
		self.Count("redirected_successively")
		self.respondSuccessively(w, r, *ip)
	} else {
		self.Count("upstream_errors")
		self.refuse(w, r)
		log.Printf("%s: error: %s", w.RemoteAddr(), err)
	}
}

func CompressIfLarge(m *dns.Msg) {
	bytes, err := m.Pack()
	if err != nil {
		return
	}
	if len(bytes) > 512 {  // may not fit into UDP packet
		m.Compress = true // will be compressed in WriteMsg
	}
}

func (self *TrivialDnsServer) proxyToUpstream(w dns.ResponseWriter, r *dns.Msg) {
	self.Count("proxied_requests")
	if response, _, err := self.exchangeWithUpstream(r, false); err == nil {
		if len(response.Answer) == 0 {
			self.Count("proxied_refusals")
		}
		CompressIfLarge(response)
		w.WriteMsg(response)
	} else {
		self.Count("upstream_errors")
		self.refuse(w, r)
		log.Printf("%s: error: %s", w.RemoteAddr(), err)
	}
}

func (self *TrivialDnsServer) exchangeWithUpstream(r *dns.Msg, useTcp bool) (*dns.Msg, time.Duration, error) {
	self.Count("upstream_queries")
	c := new(dns.Client)
	if useTcp {
		c.Net = "tcp"
	}

	c.ReadTimeout = CommonTimeout
	c.WriteTimeout = CommonTimeout

	i := rand.Intn(len(self.Servers))
	upstreamServer := self.Servers[i]

	response, rtt, err := c.Exchange(r, upstreamServer)

	if response.Truncated && !useTcp {
		// try again over TCP
		return self.exchangeWithUpstream(r, true)
	}
	return response, rtt, err
}

func (self *TrivialDnsServer) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	defer func() {
		if err := recover(); err != nil {
			runtime_debug.PrintStack()
			log.Printf("PANIC: %s", err)
			self.refuseOnPanic(w, r)
		}
	}()

	self.Count("requests")
	if self.tryAnswer(w, r) {
		return
	}
	self.proxyToUpstream(w, r)
}

func readAllLinesFromFile(filename string) ([]string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	lines := []string{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		lines = append(lines, line)
	}
	return lines, scanner.Err()
}

func readUpstreamServersFromConfig() []string {
	config := "/etc/trivialdns/nameservers"
	lines, err := readAllLinesFromFile(config)
	if err != nil {
		log.Fatalf("Failed to read %s: %s; exiting", config, err)
	}
	servers := []string{}
	for _, line := range lines {
		if !strings.Contains(line, ":") {
			line = line + ":53"
		}
		servers = append(servers, line)
	}
	return servers
}

func readDatabaseFromConfig() map[string]string {
	db := make(map[string]string)
	lines, err := readAllLinesFromFile(HostsConfig)
	if err != nil {
		log.Printf("Failed to read %s: %s; starting with empty database", HostsConfig, err)
		ioutil.WriteFile(HostsConfig, []byte{}, 0644)
		return db
	}
	for _, sourceLine := range lines {
		// remove comments
		parts := strings.Split(sourceLine, "#")
		line := strings.TrimSpace(parts[0])
		if line == "" {
			continue
		}
		parts = strings.Fields(line)
		if len(parts) != 2 {
			log.Printf("Suspicious line in config, ignoring: %s", sourceLine)
			continue
		}
		db[parts[0]] = parts[1]
	}
	return db
}

func (self *TrivialDnsServer) WebIndexPage(w http.ResponseWriter, r *http.Request) {
	defer panicCatcher()
	fmt.Fprintf(w, "<!DOCTYPE html>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "<title>TrivialDNS</title>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "
TrivialDNS
\n")
	fmt.Fprintf(w, "
Names
\n")
	fmt.Fprintf(w, "Line format: {domain_name} {ip_address|domain_name_to_redirect_to}\n")
	fmt.Fprintf(w, "<form method=\"POST\" action=\"/save_hosts\">\n")
	fmt.Fprintf(w, "<textarea name=\"hosts\" cols=\"80\" rows=\"25\">\n")
	lines, _ := readAllLinesFromFile(HostsConfig)
	for _, line := range lines {
		fmt.Fprintf(w, "%s\n", line)
	}
	fmt.Fprintf(w, "</textarea>\n")
	fmt.Fprintf(w, "
\n")
	fmt.Fprintf(w, "<input type=\"submit\" value=\"Update\">\n")
	fmt.Fprintf(w, "</form>\n")

	fmt.Fprintf(w, "
Stats
\n")
	fmt.Fprintf(w, "
\n")
	for _, st := range self.GetStats() {
		fmt.Fprintf(w, "
%s	%d
\n", st.Key, st.Value)
	}
	fmt.Fprintf(w, "
\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
}

func (self *TrivialDnsServer) WebSaveHosts(w http.ResponseWriter, r *http.Request) {
	defer panicCatcher()
	if r.Method != "POST" {
		http.Error(w, "Expected POST query", 400)
		return
	}
	hosts := r.FormValue("hosts")
	if hosts == "" {
		http.Error(w, "`hosts` parameter is required", 400)
		return
	}
	err := ioutil.WriteFile(HostsConfig, []byte(hosts), 0644)
	if err != nil {
		http.Error(w, fmt.Sprintf("could not save hosts database: %s", err), 400)
		return
	}

	self.Database = readDatabaseFromConfig()
	log.Printf("Database updated: %v", self.Database)
	http.Redirect(w, r, "/", 302)
}

func main() {
	flag.Parse()

	tdns := &TrivialDnsServer{
		Servers:  readUpstreamServersFromConfig(),
		Database: readDatabaseFromConfig(),
		stats:    make(map[string]int),
	}

	log.Printf("Starting with nameservers %v", tdns.Servers)
	log.Printf("Starting with database %v", tdns.Database)

	addr := *listenAddr

	go func() {
		if err := dns.ListenAndServe(addr, "udp", tdns); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		if err := dns.ListenAndServe(addr, "tcp", tdns); err != nil {
			log.Fatal(err)
		}
	}()

	log.Printf("DNS server started")
	log.Printf("Starting web interface on %s", *webListenAddr)
	http.HandleFunc("/", tdns.WebIndexPage)
	http.HandleFunc("/save_hosts", tdns.WebSaveHosts)
	http.ListenAndServe(*webListenAddr, nil)
}
<commit_msg>Fix handling truncated responses<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	runtime_debug "runtime/debug"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
)

const (
	CommonTimeout     = 3 * time.Second
	HostsConfig       = "/etc/trivialdns/hosts"
	NameserversConfig = "/etc/trivialdns/nameservers"
)

var (
	debugMode     = flag.Bool("debug", false, "print debug messages")
	webListenAddr = flag.String("web", ":8053", "setup web interface on this port")
	listenAddr    = flag.String("addr", ":53", "UDP and TCP host:port to start DNS service on")
)

func debug(fmt string, args ...interface{}) {
	if *debugMode {
		log.Printf(fmt, args...)
	}
}

func panicCatcher() {
	if r := recover(); r != nil {
		runtime_debug.PrintStack()
		log.Printf("PANIC: %s", r)
	}
}

func looksLikeDomainName(s string) bool {
	// domain names should have alphabet characters in them
	return strings.ContainsAny(s, "abcdefghijklmnopqrstuvwxyz")
}

type TrivialDnsServer struct {
	Servers  []string
	Database map[string]string

	stats     map[string]int
	statsLock sync.Mutex
}

func (self *TrivialDnsServer) Count(statname string) {
	self.statsLock.Lock()
	self.stats[statname] += 1
	self.statsLock.Unlock()
}

type StatTuple struct {
	Key   string
	Value int
}

func (self *TrivialDnsServer) GetStats() []StatTuple {
	result := []StatTuple{}
	keys := []string{}
	self.statsLock.Lock()
	for k, _ := range self.stats {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		result = append(result, StatTuple{k, self.stats[k]})
	}
	self.statsLock.Unlock()
	return result
}

func (self *TrivialDnsServer) refuse(w dns.ResponseWriter, req *dns.Msg) {
	self.Count("refusals")
	self.refuseWithCode(w, req, dns.RcodeRefused)
}

func (self *TrivialDnsServer) refuseOnPanic(w dns.ResponseWriter, req *dns.Msg) {
	self.Count("panic_refusals")
	self.refuseWithCode(w, req, dns.RcodeServerFailure)
}

func (self *TrivialDnsServer) refuseWithCode(w dns.ResponseWriter, req *dns.Msg, code int) {
	m := new(dns.Msg)
	for _, r := range req.Extra {
		if r.Header().Rrtype == dns.TypeOPT {
			m.SetEdns0(4096, r.(*dns.OPT).Do())
		}
	}
	m.SetRcode(req, code)
	w.WriteMsg(m)
}

func (self *TrivialDnsServer) getSingleSimpleQuestion(m *dns.Msg) *dns.Question {
	if len(m.Question) != 1 {
		return nil
	}
	q := m.Question[0]
	if q.Qtype != dns.TypeA || q.Qclass != dns.ClassINET {
		return nil
	}
	return &q
}

func (self *TrivialDnsServer) getSingleSimpleAnswer(m *dns.Msg) *net.IP {
	if len(m.Answer) != 1 {
		return nil
	}
	a, ok := m.Answer[0].(*dns.A)
	if !ok {
		return nil
	}
	if a.Hdr.Rrtype != dns.TypeA || a.Hdr.Class != dns.ClassINET {
		return nil
	}
	return &a.A
}

func (self *TrivialDnsServer) respondSuccessively(w dns.ResponseWriter, r *dns.Msg, addr net.IP) {
	q := self.getSingleSimpleQuestion(r)
	a := &dns.A{
		Hdr: dns.RR_Header{
			Name:     q.Name,
			Rrtype:   dns.TypeA,
			Class:    dns.ClassINET,
			Ttl:      30,
			Rdlength: 4,
		},
		A: addr,
	}
	m := new(dns.Msg)
	m.SetReply(r)
	m.Answer = []dns.RR{a}
	m.Authoritative = false
	w.WriteMsg(m)
}

func (self *TrivialDnsServer) tryAnswer(w dns.ResponseWriter, r *dns.Msg) bool {
	q := self.getSingleSimpleQuestion(r)
	if q == nil {
		return false
	}
	name := strings.TrimSuffix(q.Name, ".")
	if name == "panic.com" {
		panic("panic on request for panic.com")
	}
	value, ok := self.Database[name]
	if !ok {
		debug("%s: %s not found in local database", w.RemoteAddr(), name)
		parts := strings.Split(name, ".")
		b_found := false
		for len(parts) >= 2 {
			parts[0] = "*"
			wildcard_name := strings.Join(parts, ".")
			value, ok = self.Database[wildcard_name]
			if !ok {
				parts = parts[1:]
				continue
			} else {
				b_found = true
				break
			}
		}
		if !b_found {
			return false
		}
	}
	if looksLikeDomainName(value) {
		debug("%s: %s found in database -> redirect to %s", w.RemoteAddr(), name, value)
		self.redirectQuery(w, r, value)
		return true
	}

	debug("%s: %s found in database -> %s", w.RemoteAddr(), name, value)
	addr := net.ParseIP(value)
	self.Count("local_responses")
	self.respondSuccessively(w, r, addr)
	return true
}

func (self *TrivialDnsServer) redirectQuery(w dns.ResponseWriter, r *dns.Msg, newName string) {
	self.Count("redirected_requests")
	if !strings.HasSuffix(newName, ".") {
		newName = newName + "."
	}

	newR := new(dns.Msg)
	newR.SetQuestion(dns.Fqdn(newName), dns.TypeA)

	if response, _, err := self.exchangeWithUpstream(newR, false); err == nil {
		ip := self.getSingleSimpleAnswer(response)
		if ip == nil {
			debug("%s redirect to %s yielded no answer", w.RemoteAddr(), newName)
			self.Count("redirected_nowhere")
			self.refuse(w, r)
			return
		}
		self.Count("redirected_successively")
		self.respondSuccessively(w, r, *ip)
	} else {
		self.Count("upstream_errors")
		self.refuse(w, r)
		log.Printf("%s: error: %s", w.RemoteAddr(), err)
	}
}

func CompressIfLarge(m *dns.Msg) {
	bytes, err := m.Pack()
	if err != nil {
		return
	}
	if len(bytes) > 512 {  // may not fit into UDP packet
		m.Compress = true // will be compressed in WriteMsg
	}
}

func (self *TrivialDnsServer) proxyToUpstream(w dns.ResponseWriter, r *dns.Msg) {
	self.Count("proxied_requests")
	if response, _, err := self.exchangeWithUpstream(r, false); err == nil {
		if len(response.Answer) == 0 {
			self.Count("proxied_refusals")
		}
		CompressIfLarge(response)
		w.WriteMsg(response)
	} else {
		self.Count("upstream_errors")
		self.refuse(w, r)
		log.Printf("%s: error: %s", w.RemoteAddr(), err)
	}
}

func (self *TrivialDnsServer) exchangeWithUpstream(r *dns.Msg, useTcp bool) (*dns.Msg, time.Duration, error) {
	self.Count("upstream_queries")
	c := new(dns.Client)
	if useTcp {
		c.Net = "tcp"
	}

	c.ReadTimeout = CommonTimeout
	c.WriteTimeout = CommonTimeout

	i := rand.Intn(len(self.Servers))
	upstreamServer := self.Servers[i]

	response, rtt, err := c.Exchange(r, upstreamServer)

	if response != nil && response.Truncated && !useTcp {
		// try again over TCP
		return self.exchangeWithUpstream(r, true)
	}
	return response, rtt, err
}

func (self *TrivialDnsServer) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	defer func() {
		if err := recover(); err != nil {
			runtime_debug.PrintStack()
			log.Printf("PANIC: %s", err)
			self.refuseOnPanic(w, r)
		}
	}()

	self.Count("requests")
	if self.tryAnswer(w, r) {
		return
	}
	self.proxyToUpstream(w, r)
}

func readAllLinesFromFile(filename string) ([]string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	lines := []string{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		lines = append(lines, line)
	}
	return lines, scanner.Err()
}

func readUpstreamServersFromConfig() []string {
	config := "/etc/trivialdns/nameservers"
	lines, err := readAllLinesFromFile(config)
	if err != nil {
		log.Fatalf("Failed to read %s: %s; exiting", config, err)
	}
	servers := []string{}
	for _, line := range lines {
		if !strings.Contains(line, ":") {
			line = line + ":53"
		}
		servers = append(servers, line)
	}
	return servers
}

func readDatabaseFromConfig() map[string]string {
	db := make(map[string]string)
	lines, err := readAllLinesFromFile(HostsConfig)
	if err != nil {
		log.Printf("Failed to read %s: %s; starting with empty database", HostsConfig, err)
		ioutil.WriteFile(HostsConfig, []byte{}, 0644)
		return db
	}
	for _, sourceLine := range lines {
		// remove comments
		parts := strings.Split(sourceLine, "#")
		line := strings.TrimSpace(parts[0])
		if line == "" {
			continue
		}
		parts = strings.Fields(line)
		if len(parts) != 2 {
			log.Printf("Suspicious line in config, ignoring: %s", sourceLine)
			continue
		}
		db[parts[0]] = parts[1]
	}
	return db
}

func (self *TrivialDnsServer) WebIndexPage(w http.ResponseWriter, r *http.Request) {
	defer panicCatcher()
	fmt.Fprintf(w, "<!DOCTYPE html>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "<title>TrivialDNS</title>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "
TrivialDNS
\n")
	fmt.Fprintf(w, "
Names
\n")
	fmt.Fprintf(w, "Line format: {domain_name} {ip_address|domain_name_to_redirect_to}\n")
	fmt.Fprintf(w, "<form method=\"POST\" action=\"/save_hosts\">\n")
	fmt.Fprintf(w, "<textarea name=\"hosts\" cols=\"80\" rows=\"25\">\n")
	lines, _ := readAllLinesFromFile(HostsConfig)
	for _, line := range lines {
		fmt.Fprintf(w, "%s\n", line)
	}
	fmt.Fprintf(w, "</textarea>\n")
	fmt.Fprintf(w, "
\n")
	fmt.Fprintf(w, "<input type=\"submit\" value=\"Update\">\n")
	fmt.Fprintf(w, "</form>\n")

	fmt.Fprintf(w, "
Stats
\n")
	fmt.Fprintf(w, "
\n")
	for _, st := range self.GetStats() {
		fmt.Fprintf(w, "
%s	%d
\n", st.Key, st.Value)
	}
	fmt.Fprintf(w, "
\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "\n")
}

func (self *TrivialDnsServer) WebSaveHosts(w http.ResponseWriter, r *http.Request) {
	defer panicCatcher()
	if r.Method != "POST" {
		http.Error(w, "Expected POST query", 400)
		return
	}
	hosts := r.FormValue("hosts")
	if hosts == "" {
		http.Error(w, "`hosts` parameter is required", 400)
		return
	}
	err := ioutil.WriteFile(HostsConfig, []byte(hosts), 0644)
	if err != nil {
		http.Error(w, fmt.Sprintf("could not save hosts database: %s", err), 400)
		return
	}

	self.Database = readDatabaseFromConfig()
	log.Printf("Database updated: %v", self.Database)
	http.Redirect(w, r, "/", 302)
}

func main() {
	flag.Parse()

	tdns := &TrivialDnsServer{
		Servers:  readUpstreamServersFromConfig(),
		Database: readDatabaseFromConfig(),
		stats:    make(map[string]int),
	}

	log.Printf("Starting with nameservers %v", tdns.Servers)
	log.Printf("Starting with database %v", tdns.Database)

	addr := *listenAddr

	go func() {
		if err := dns.ListenAndServe(addr, "udp", tdns); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		if err := dns.ListenAndServe(addr, "tcp", tdns); err != nil {
			log.Fatal(err)
		}
	}()

	log.Printf("DNS server started")
	log.Printf("Starting web interface on %s", *webListenAddr)
	http.HandleFunc("/", tdns.WebIndexPage)
	http.HandleFunc("/save_hosts", tdns.WebSaveHosts)
	http.ListenAndServe(*webListenAddr, nil)
}
<|endoftext|>


<commit_before>package typed

import (
	"github.com/karlseguin/gspec"
	"testing"
)

func Test_Json(t *testing.T) {
	spec := gspec.New(t)
	typed, err := Json([]byte(`{"power": 9002}`))
	spec.Expect(typed.Int("power")).ToEqual(9002)
	spec.Expect(err).ToBeNil()
}

func Test_JsonFile(t *testing.T) {
	spec := gspec.New(t)
	typed, err := JsonFile("test.json")
	spec.Expect(typed.String("name")).ToEqual("leto")
	spec.Expect(err).ToBeNil()
}

func Test_Bool(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("log", true))
	spec.Expect(typed.Bool("log")).ToEqual(true)
	spec.Expect(typed.BoolOr("log", false)).ToEqual(true)
	spec.Expect(typed.Bool("other")).ToEqual(false)
	spec.Expect(typed.BoolOr("other", true)).ToEqual(true)
}

func Test_Int(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("port", 84))
	spec.Expect(typed.Int("port")).ToEqual(84)
	spec.Expect(typed.IntOr("port", 11)).ToEqual(84)
	spec.Expect(typed.Int("other")).ToEqual(0)
	spec.Expect(typed.IntOr("other", 33)).ToEqual(33)
}

func Test_Float(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("pi", 3.14))
	spec.Expect(typed.Float("pi")).ToEqual(3.14)
	spec.Expect(typed.FloatOr("pi", 11.3)).ToEqual(3.14)
	spec.Expect(typed.Float("other")).ToEqual(0.0)
	spec.Expect(typed.FloatOr("other", 11.3)).ToEqual(11.3)
}

func Test_String(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("host", "localhost"))
	spec.Expect(typed.String("host")).ToEqual("localhost")
	spec.Expect(typed.StringOr("host", "openmymind.net")).ToEqual("localhost")
	spec.Expect(typed.String("other")).ToEqual("")
	spec.Expect(typed.StringOr("other", "openmymind.net")).ToEqual("openmymind.net")

}

func Test_Object(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("server", build("port", 32)))
	spec.Expect(typed.Object("server").Int("port")).ToEqual(32)
	spec.Expect(typed.ObjectOr("server", build("a", "b")).Int("port")).ToEqual(32)
	spec.Expect(len(typed.Object("other"))).ToEqual(0)
	spec.Expect(typed.ObjectOr("other", build("x", "y")).String("x")).ToEqual("y")
}

func Test_Bools(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("boring", []interface{}{true, false}))
	spec.Expect(typed.Bools("boring")).ToEqual([]bool{true, false})
	spec.Expect(typed.Bools("other")).ToEqual([]bool{})
	spec.Expect(typed.BoolsOr("boring", []bool{false, true})).ToEqual([]bool{true, false})
	spec.Expect(typed.BoolsOr("other", []bool{false, true})).ToEqual([]bool{false, true})
}

func Test_Ints(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("scores", []interface{}{2, 1}))
	spec.Expect(typed.Ints("scores")).ToEqual([]int{2, 1})
	spec.Expect(typed.Ints("other")).ToEqual([]int{})
	spec.Expect(typed.IntsOr("scores", []int{3, 4})).ToEqual([]int{2, 1})
	spec.Expect(typed.IntsOr("other", []int{3, 4})).ToEqual([]int{3, 4})
}

func Test_Ints_WithFloats(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("scores", []interface{}{2.1, 7.39}))
	spec.Expect(typed.Ints("scores")).ToEqual([]int{2, 7})
}

func Test_Floats(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("ranks", []interface{}{2.1, 1.2}))
	spec.Expect(typed.Floats("ranks")).ToEqual([]float64{2.1, 1.2})
	spec.Expect(typed.Floats("other")).ToEqual([]float64{})
}

func Test_Strings(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{"a", "b"}))
	spec.Expect(typed.Strings("names")).ToEqual([]string{"a", "b"})
	spec.Expect(typed.Strings("other")).ToEqual([]string{})
	spec.Expect(typed.StringsOr("names", []string{"c", "d"})).ToEqual([]string{"a", "b"})
	spec.Expect(typed.StringsOr("other", []string{"c", "d"})).ToEqual([]string{"c", "d"})
}

func Test_Objects(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{build("first", 1), build("second", 2)}))
	spec.Expect(typed.Objects("names")[0].Int("first")).ToEqual(1)
}

func Test_Maps(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{build("first", 1), build("second", 2)}))
	spec.Expect(typed.Maps("names")[1]["second"]).ToEqual(2)
}

func Test_StringBool(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"blocked":{"a":true,"b":false}}`)
	m := typed.StringBool("blocked")
	spec.Expect(m["a"]).ToEqual(true)
	spec.Expect(m["b"]).ToEqual(false)
}

func Test_StringInt(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"count":{"a":123,"b":43}}`)
	m := typed.StringInt("count")
	spec.Expect(m["a"]).ToEqual(123)
	spec.Expect(m["b"]).ToEqual(43)
	spec.Expect(m["xxz"]).ToEqual(0)
}

func Test_StringFloat(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"rank":{"aa":3.4,"bz":4.2}}`)
	m := typed.StringFloat("rank")
	spec.Expect(m["aa"]).ToEqual(3.4)
	spec.Expect(m["bz"]).ToEqual(4.2)
	spec.Expect(m["xx"]).ToEqual(0.0)
}

func Test_StringString(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"atreides":{"leto":"ghanima","paul":"alia"}}`)
	m := typed.StringString("atreides")
	spec.Expect(m["leto"]).ToEqual("ghanima")
	spec.Expect(m["paul"]).ToEqual("alia")
	spec.Expect(m["vladimir"]).ToEqual("")
}

func Test_StringObject(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"atreides":{"leto":{"sister": "ghanima"}, "goku": {"power": 9001}}}`)
	m := typed.StringObject("atreides")
	spec.Expect(m["leto"].String("sister")).ToEqual("ghanima")
	spec.Expect(m["goku"].Int("power")).ToEqual(9001)
}

func build(values ...interface{}) map[string]interface{} {
	m := make(map[string]interface{}, len(values))
	for i := 0; i < len(values); i += 2 {
		m[values[i].(string)] = values[i+1]
	}
	return m
}
<commit_msg>Written tests for FloatsOr<commit_after>package typed

import (
	"github.com/karlseguin/gspec"
	"testing"
)

func Test_Json(t *testing.T) {
	spec := gspec.New(t)
	typed, err := Json([]byte(`{"power": 9002}`))
	spec.Expect(typed.Int("power")).ToEqual(9002)
	spec.Expect(err).ToBeNil()
}

func Test_JsonFile(t *testing.T) {
	spec := gspec.New(t)
	typed, err := JsonFile("test.json")
	spec.Expect(typed.String("name")).ToEqual("leto")
	spec.Expect(err).ToBeNil()
}

func Test_Bool(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("log", true))
	spec.Expect(typed.Bool("log")).ToEqual(true)
	spec.Expect(typed.BoolOr("log", false)).ToEqual(true)
	spec.Expect(typed.Bool("other")).ToEqual(false)
	spec.Expect(typed.BoolOr("other", true)).ToEqual(true)
}

func Test_Int(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("port", 84))
	spec.Expect(typed.Int("port")).ToEqual(84)
	spec.Expect(typed.IntOr("port", 11)).ToEqual(84)
	spec.Expect(typed.Int("other")).ToEqual(0)
	spec.Expect(typed.IntOr("other", 33)).ToEqual(33)
}

func Test_Float(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("pi", 3.14))
	spec.Expect(typed.Float("pi")).ToEqual(3.14)
	spec.Expect(typed.FloatOr("pi", 11.3)).ToEqual(3.14)
	spec.Expect(typed.Float("other")).ToEqual(0.0)
	spec.Expect(typed.FloatOr("other", 11.3)).ToEqual(11.3)
}

func Test_String(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("host", "localhost"))
	spec.Expect(typed.String("host")).ToEqual("localhost")
	spec.Expect(typed.StringOr("host", "openmymind.net")).ToEqual("localhost")
	spec.Expect(typed.String("other")).ToEqual("")
	spec.Expect(typed.StringOr("other", "openmymind.net")).ToEqual("openmymind.net")

}

func Test_Object(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("server", build("port", 32)))
	spec.Expect(typed.Object("server").Int("port")).ToEqual(32)
	spec.Expect(typed.ObjectOr("server", build("a", "b")).Int("port")).ToEqual(32)
	spec.Expect(len(typed.Object("other"))).ToEqual(0)
	spec.Expect(typed.ObjectOr("other", build("x", "y")).String("x")).ToEqual("y")
}

func Test_Bools(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("boring", []interface{}{true, false}))
	spec.Expect(typed.Bools("boring")).ToEqual([]bool{true, false})
	spec.Expect(typed.Bools("other")).ToEqual([]bool{})
	spec.Expect(typed.BoolsOr("boring", []bool{false, true})).ToEqual([]bool{true, false})
	spec.Expect(typed.BoolsOr("other", []bool{false, true})).ToEqual([]bool{false, true})
}

func Test_Ints(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("scores", []interface{}{2, 1}))
	spec.Expect(typed.Ints("scores")).ToEqual([]int{2, 1})
	spec.Expect(typed.Ints("other")).ToEqual([]int{})
	spec.Expect(typed.IntsOr("scores", []int{3, 4})).ToEqual([]int{2, 1})
	spec.Expect(typed.IntsOr("other", []int{3, 4})).ToEqual([]int{3, 4})
}

func Test_Ints_WithFloats(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("scores", []interface{}{2.1, 7.39}))
	spec.Expect(typed.Ints("scores")).ToEqual([]int{2, 7})
}

func Test_Floats(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("ranks", []interface{}{2.1, 1.2}))
	spec.Expect(typed.Floats("ranks")).ToEqual([]float64{2.1, 1.2})
	spec.Expect(typed.Floats("other")).ToEqual([]float64{})
	spec.Expect(typed.FloatsOr("ranks", []float64{3.1, 4.2})).ToEqual([]float64{2.1, 1.2})
	spec.Expect(typed.FloatsOr("other", []float64{3.1, 4.2})).ToEqual([]float64{3.1, 4.2})
}

func Test_Strings(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{"a", "b"}))
	spec.Expect(typed.Strings("names")).ToEqual([]string{"a", "b"})
	spec.Expect(typed.Strings("other")).ToEqual([]string{})
	spec.Expect(typed.StringsOr("names", []string{"c", "d"})).ToEqual([]string{"a", "b"})
	spec.Expect(typed.StringsOr("other", []string{"c", "d"})).ToEqual([]string{"c", "d"})
}

func Test_Objects(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{build("first", 1), build("second", 2)}))
	spec.Expect(typed.Objects("names")[0].Int("first")).ToEqual(1)
}

func Test_Maps(t *testing.T) {
	spec := gspec.New(t)
	typed := New(build("names", []interface{}{build("first", 1), build("second", 2)}))
	spec.Expect(typed.Maps("names")[1]["second"]).ToEqual(2)
}

func Test_StringBool(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"blocked":{"a":true,"b":false}}`)
	m := typed.StringBool("blocked")
	spec.Expect(m["a"]).ToEqual(true)
	spec.Expect(m["b"]).ToEqual(false)
}

func Test_StringInt(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"count":{"a":123,"b":43}}`)
	m := typed.StringInt("count")
	spec.Expect(m["a"]).ToEqual(123)
	spec.Expect(m["b"]).ToEqual(43)
	spec.Expect(m["xxz"]).ToEqual(0)
}

func Test_StringFloat(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"rank":{"aa":3.4,"bz":4.2}}`)
	m := typed.StringFloat("rank")
	spec.Expect(m["aa"]).ToEqual(3.4)
	spec.Expect(m["bz"]).ToEqual(4.2)
	spec.Expect(m["xx"]).ToEqual(0.0)
}

func Test_StringString(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"atreides":{"leto":"ghanima","paul":"alia"}}`)
	m := typed.StringString("atreides")
	spec.Expect(m["leto"]).ToEqual("ghanima")
	spec.Expect(m["paul"]).ToEqual("alia")
	spec.Expect(m["vladimir"]).ToEqual("")
}

func Test_StringObject(t *testing.T) {
	spec := gspec.New(t)
	typed, _ := JsonString(`{"atreides":{"leto":{"sister": "ghanima"}, "goku": {"power": 9001}}}`)
	m := typed.StringObject("atreides")
	spec.Expect(m["leto"].String("sister")).ToEqual("ghanima")
	spec.Expect(m["goku"].Int("power")).ToEqual(9001)
}

func build(values ...interface{}) map[string]interface{} {
	m := make(map[string]interface{}, len(values))
	for i := 0; i < len(values); i += 2 {
		m[values[i].(string)] = values[i+1]
	}
	return m
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestMetadata(t *testing.T) {
	nameValue := "name"
	namespaceValue := "namespace"
	metadata := Metadata{nil, nameValue, namespaceValue} //set labels map[string]string to nil
	if metadata.Name != nameValue || metadata.Namespace != namespaceValue {
		t.Errorf("faulty struct metadata: %v", metadata)
	}
}

func TestResourceConstraint(t *testing.T) {
	validConstraint := &ResourceConstraint{"200m", "1000Mi"}
	invalidConstraint := &ResourceConstraint{"1000Mi", "200m"}
	incompleteConstraint := &ResourceConstraint{"1000Mi", ""}

	if validConstraint.Valid() == false {
		t.Errorf("must accept valid constraint: %v", validConstraint)
	}

	if invalidConstraint.Valid() == true {
		t.Errorf("must reject invalid constraint: %v", invalidConstraint)
	}

	if incompleteConstraint.Complete() == true {
		t.Errorf("must not accept incomplete constraint: %v", incompleteConstraint)
	}
}
<commit_msg>trimmed whitespace<commit_after>package main

import "testing"

func TestMetadata(t *testing.T) {
	nameValue := "name"
	namespaceValue := "namespace"
	metadata := Metadata{nil, nameValue, namespaceValue} //set labels map[string]string to nil
	if metadata.Name != nameValue || metadata.Namespace != namespaceValue {
		t.Errorf("faulty struct metadata: %v", metadata)
	}
}

func TestResourceConstraint(t *testing.T) {
	validConstraint := &ResourceConstraint{"200m", "1000Mi"}
	invalidConstraint := &ResourceConstraint{"1000Mi", "200m"}
	incompleteConstraint := &ResourceConstraint{"1000Mi", ""}

	if validConstraint.Valid() == false {
		t.Errorf("must accept valid constraint: %v", validConstraint)
	}

	if invalidConstraint.Valid() == true {
		t.Errorf("must reject invalid constraint: %v", invalidConstraint)
	}

	if incompleteConstraint.Complete() == true {
		t.Errorf("must not accept incomplete constraint: %v", incompleteConstraint)
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	"net/url"
)

// Redirect to the referer host if there is one
func RedirectLink(refer string) string {

	parsed, err := url.Parse(refer)
	if err != nil {
		return "/"
	}

	redir := &url.URL{
		Scheme: parsed.Scheme,
		Host:   parsed.Host,
	}

	return redir.String()

}
<commit_msg>fix redirect links?<commit_after>package utils

import (
	"net/url"
)

// Redirect to the referer host if there is one
func RedirectLink(refer string) string {

	parsed, err := url.Parse(refer)
	if err != nil {
		return "/"
	}

	redir := &url.URL{
		Scheme: parsed.Scheme,
		Host:   parsed.Host,
	}

	return redir.String()

}
<|endoftext|>


<commit_before>package lily

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

//
// Author João Nuno.
//
// joaonrb@gmail.com
//

func TestLineIterator(t *testing.T) {
	tmp := "/tmp/line_iterator.txt"
	content := "line1\n1\n"
	//defer os.Remove(tmp)
	err := ioutil.WriteFile(tmp, []byte(content), 0644)
	if err != nil {
		fmt.Printf("Tmp file couldn't be writen becauser error %s", err.Error())
		os.Exit(1)
	}
	iter, err := NewLineIterator(tmp)
	switch {
	case err != nil:
		t.Error(err.Error())
	case iter.Next() != "line1" && iter.HasNext():
		t.Error("Line 1 is not expected(line1).")
	case iter.Next() != "1" && iter.HasNext():
		t.Error("Line 2 is not expected(1).")
	case iter.Next() != "" && !iter.HasNext():
		t.Error("Line 3 is not expected().")
	case iter.Next() != "":
		t.Error("Line 4 is not expected at all.")
	}
}

func TestGenerateBase64StringSize(t *testing.T) {
	for i, n := range []int{0, 1, 2, 5, 10, 25, 30, 50, 75, 100} {
		hex := GenerateBase64String(n)
		if len(hex) != n {
			t.Errorf("%d - Size of GenerateBase64String(%d) sould be %d, got %d instead.", i, n, n, len(hex))
			return
		}
	}
}

func BGenerateBase64StringN(n int, b *testing.B) {
	for i := 0; i < b.N; i++ {
		GenerateBase64String(n)
	}
}

func BenchmarkGenerateBase64String1(b *testing.B) {
	BGenerateBase64StringN(1, b)
}

func BenchmarkGenerateBase64String5(b *testing.B) {
	BGenerateBase64StringN(5, b)
}

func BenchmarkGenerateBase64String10(b *testing.B) {
	BGenerateBase64StringN(10, b)
}

func BenchmarkGenerateBase64String25(b *testing.B) {
	BGenerateBase64StringN(25, b)
}

func BenchmarkGenerateBase64String50(b *testing.B) {
	BGenerateBase64StringN(50, b)
}

func BenchmarkGenerateBase64String75(b *testing.B) {
	BGenerateBase64StringN(75, b)
}

func BenchmarkGenerateBase64String100(b *testing.B) {
	BGenerateBase64StringN(100, b)
}
<commit_msg>Fix spell errors<commit_after>package lily

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

//
// Author João Nuno.
//
// joaonrb@gmail.com
//

func TestLineIterator(t *testing.T) {
	tmp := "/tmp/line_iterator.txt"
	content := "line1\n1\n"
	//defer os.Remove(tmp)
	err := ioutil.WriteFile(tmp, []byte(content), 0644)
	if err != nil {
		fmt.Printf("Tmp file couldn't be written becauser error %s", err.Error())
		os.Exit(1)
	}
	iter, err := NewLineIterator(tmp)
	switch {
	case err != nil:
		t.Error(err.Error())
	case iter.Next() != "line1" && iter.HasNext():
		t.Error("Line 1 is not expected(line1).")
	case iter.Next() != "1" && iter.HasNext():
		t.Error("Line 2 is not expected(1).")
	case iter.Next() != "" && !iter.HasNext():
		t.Error("Line 3 is not expected().")
	case iter.Next() != "":
		t.Error("Line 4 is not expected at all.")
	}
}

func TestGenerateBase64StringSize(t *testing.T) {
	for i, n := range []int{0, 1, 2, 5, 10, 25, 30, 50, 75, 100} {
		hex := GenerateBase64String(n)
		if len(hex) != n {
			t.Errorf("%d - Size of GenerateBase64String(%d) should be %d, got %d instead.", i, n, n, len(hex))
			return
		}
	}
}

func BGenerateBase64StringN(n int, b *testing.B) {
	for i := 0; i < b.N; i++ {
		GenerateBase64String(n)
	}
}

func BenchmarkGenerateBase64String1(b *testing.B) {
	BGenerateBase64StringN(1, b)
}

func BenchmarkGenerateBase64String5(b *testing.B) {
	BGenerateBase64StringN(5, b)
}

func BenchmarkGenerateBase64String10(b *testing.B) {
	BGenerateBase64StringN(10, b)
}

func BenchmarkGenerateBase64String25(b *testing.B) {
	BGenerateBase64StringN(25, b)
}

func BenchmarkGenerateBase64String50(b *testing.B) {
	BGenerateBase64StringN(50, b)
}

func BenchmarkGenerateBase64String75(b *testing.B) {
	BGenerateBase64StringN(75, b)
}

func BenchmarkGenerateBase64String100(b *testing.B) {
	BGenerateBase64StringN(100, b)
}
<|endoftext|>


<commit_before>package v8

import (
	"errors"
	"reflect"
	"time"
)

//
// Fast bind Go type or function to JS. Note, The function template and object template
// created in fast bind internal are never destroyed. The JS class map to Go type use a
// internal field to reference a Go object when it instanced. All of the internal field
// keep reference by engine. So, may be you don't like to create too many instance of them.
//
func (template *ObjectTemplate) Bind(typeName string, target interface{}) error {
	engine := template.engine

	typeInfo := reflect.TypeOf(target)

	if typeInfo.Kind() == reflect.Ptr {
		typeInfo = typeInfo.Elem()
	}

	if typeInfo.Kind() == reflect.Func {
		jsfunc := engine.GoFuncToJsFunc(reflect.ValueOf(target))
		template.SetAccessor(typeName, func(name string, info AccessorCallbackInfo) {
			info.ReturnValue().Set(jsfunc.NewFunction())
		}, nil, nil, PA_None)
		return nil
	}

	if typeInfo.Kind() == reflect.Struct {
		constructor := engine.NewFunctionTemplate(func(info FunctionCallbackInfo) {
			value := reflect.New(typeInfo)
			info.This().SetInternalField(0, &value)
		}, nil)
		constructor.SetClassName(typeName)

		objTemplate := constructor.InstanceTemplate()
		objTemplate.SetInternalFieldCount(1)
		objTemplate.SetNamedPropertyHandler(
			// get
			func(name string, info PropertyCallbackInfo) {
				value := info.This().GetInternalField(0).(*reflect.Value)

				field := value.Elem().FieldByName(name)

				if field.IsValid() {
					info.ReturnValue().Set(engine.GoValueToJsValue(field))
					return
				}

				method := value.MethodByName(name)

				if !method.IsValid() {
					info.CurrentScope().ThrowException("could't found property or method '" + typeName + "." + name + "'")
					return
				}

				info.ReturnValue().Set(engine.GoFuncToJsFunc(method).NewFunction())
			},
			// set
			func(name string, jsvalue *Value, info PropertyCallbackInfo) {
				value := info.This().GetInternalField(0).(*reflect.Value).Elem()

				field := value.FieldByName(name)

				if !field.IsValid() {
					info.CurrentScope().ThrowException("could't found property '" + typeName + "." + name + "'")
					return
				}

				engine.SetJsValueToGo(field, jsvalue)
			},
			// query
			func(name string, info PropertyCallbackInfo) {
				value := info.This().ToObject().GetInternalField(0).(*reflect.Value).Elem()
				info.ReturnValue().SetBoolean(value.FieldByName(name).IsValid() || value.MethodByName(name).IsValid())
			},
			// delete
			nil,
			// enum
			nil,
			nil,
		)

		template.SetAccessor(typeName, func(name string, info AccessorCallbackInfo) {
			info.ReturnValue().Set(constructor.NewFunction())
		}, nil, nil, PA_None)

		return nil
	}

	return errors.New("unsupported target type")
}

func (engine *Engine) GoValueToJsValue(value reflect.Value) *Value {
	switch value.Kind() {
	case reflect.Bool:
		return engine.NewBoolean(value.Bool())
	case reflect.String:
		return engine.NewString(value.String())
	case reflect.Int8, reflect.Int16, reflect.Int32:
		return engine.NewInteger(value.Int())
	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
		return engine.NewInteger(value.Int())
	case reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
		return engine.NewNumber(float64(value.Int()))
	case reflect.Float32, reflect.Float64:
		return engine.NewNumber(value.Float())
	case reflect.Array, reflect.Slice:
		arrayLen := value.Len()
		jsArrayVal := engine.NewArray(value.Len())
		jsArray := jsArrayVal.ToArray()
		for i := 0; i < arrayLen; i++ {
			jsArray.SetElement(i, engine.GoValueToJsValue(value.Index(i)))
		}
		return jsArrayVal
	case reflect.Map:
		jsObjectVal := engine.NewObject()
		jsObject := jsObjectVal.ToObject()
		for _, key := range value.MapKeys() {
			switch key.Kind() {
			case reflect.String:
				jsObject.SetProperty(key.String(), engine.GoValueToJsValue(value.MapIndex(key)), PA_None)
			case reflect.Int8, reflect.Int16, reflect.Int32,
				reflect.Uint8, reflect.Uint16, reflect.Uint32,
				reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
				jsObject.SetElement(int(key.Int()), engine.GoValueToJsValue(value.MapIndex(key)))
			}
		}
		return jsObjectVal
	case reflect.Func:
		return engine.GoFuncToJsFunc(value).NewFunction()
	case reflect.Struct:
		switch value.Interface().(type) {
		case time.Time:
			return engine.NewDate(value.Interface().(time.Time))
		}
	}
	return engine.Undefined()
}

func (engine *Engine) GoFuncToJsFunc(gofunc reflect.Value) *FunctionTemplate {
	funcType := gofunc.Type()
	return engine.NewFunctionTemplate(func(callbackInfo FunctionCallbackInfo) {
		numIn := funcType.NumIn()
		numArgs := callbackInfo.Length()

		var out []reflect.Value

		if funcType.IsVariadic() {
			in := make([]reflect.Value, 1)
			in[0] = reflect.MakeSlice(funcType.In(0), numArgs, numArgs)

			for i := 0; i < numArgs; i++ {
				jsvalue := callbackInfo.Get(i)
				engine.SetJsValueToGo(in[0].Index(i), jsvalue)
			}

			out = gofunc.CallSlice(in)
		} else {
			in := make([]reflect.Value, numIn)

			for i := 0; i < len(in); i++ {
				jsvalue := callbackInfo.Get(i)
				in[i] = reflect.Indirect(reflect.New(funcType.In(i)))
				engine.SetJsValueToGo(in[i], jsvalue)
			}

			out = gofunc.Call(in)
		}

		if out == nil {
			callbackInfo.CurrentScope().ThrowException("argument number not match")
			return
		}

		switch {
		// when Go function returns only one value
		case len(out) == 1:
			callbackInfo.ReturnValue().Set(engine.GoValueToJsValue(out[0]))
		// when Go function returns multi-value, put them in a JavaScript array
		case len(out) > 1:
			jsResults := engine.NewArray(len(out))
			jsResultsArray := jsResults.ToArray()

			for i := 0; i < len(out); i++ {
				jsResultsArray.SetElement(i, engine.GoValueToJsValue(out[i]))
			}

			callbackInfo.ReturnValue().Set(jsResults)
		}
	}, nil)
}

var (
	typeOfValue    = reflect.TypeOf(new(Value))
	typeOfObject   = reflect.TypeOf(new(Object))
	typeOfArray    = reflect.TypeOf(new(Array))
	typeOfRegExp   = reflect.TypeOf(new(RegExp))
	typeOfFunction = reflect.TypeOf(new(Function))
)

func (engine *Engine) SetJsValueToGo(field reflect.Value, jsvalue *Value) {
	goType := field.Type()
	switch goType.Kind() {
	case reflect.Bool:
		field.SetBool(jsvalue.ToBoolean())
	case reflect.String:
		field.SetString(jsvalue.ToString())
	case reflect.Int8, reflect.Int16, reflect.Int32:
		field.SetInt(int64(jsvalue.ToInt32()))
	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
		field.SetUint(uint64(jsvalue.ToUint32()))
	case reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
		field.SetInt(jsvalue.ToInteger())
	case reflect.Float32, reflect.Float64:
		field.SetFloat(jsvalue.ToNumber())
	case reflect.Slice:
		jsArray := jsvalue.ToArray()
		jsArrayLen := jsArray.Length()
		field.Set(reflect.MakeSlice(goType, jsArrayLen, jsArrayLen))
		fallthrough
	case reflect.Array:
		jsArray := jsvalue.ToArray()
		jsArrayLen := jsArray.Length()
		for i := 0; i < jsArrayLen; i++ {
			engine.SetJsValueToGo(field.Index(i), jsArray.GetElement(i))
		}
	case reflect.Map:
		jsObject := jsvalue.ToObject()
		jsObjectKeys := jsObject.GetPropertyNames()
		jsObjectKeysLen := jsObjectKeys.Length()
		field.Set(reflect.MakeMap(goType))
		itemType := goType.Elem()
		for i := 0; i < jsObjectKeysLen; i++ {
			mapKey := jsObjectKeys.GetElement(i).ToString()
			mapValue := reflect.Indirect(reflect.New(itemType))
			engine.SetJsValueToGo(mapValue, jsObject.GetProperty(mapKey))
			field.SetMapIndex(reflect.ValueOf(mapKey), mapValue)
		}
	case reflect.Interface:
		field.Set(reflect.ValueOf(jsvalue))
	case reflect.Func:
		function := jsvalue.ToFunction()
		field.Set(reflect.MakeFunc(goType, func(args []reflect.Value) []reflect.Value {
			jsargs := make([]*Value, len(args))
			for i := 0; i < len(args); i++ {
				jsargs[i] = engine.GoValueToJsValue(args[i])
			}
			jsresult := function.Call(jsargs...)

			outNum := goType.NumOut()

			if outNum == 1 {
				var result = reflect.Indirect(reflect.New(goType.Out(0)))
				engine.SetJsValueToGo(result, jsresult)
				return []reflect.Value{result}
			}

			results := make([]reflect.Value, outNum)
			jsresultArray := jsresult.ToArray()

			for i := 0; i < outNum; i++ {
				results[i] = reflect.Indirect(reflect.New(goType.Out(i)))
				engine.SetJsValueToGo(results[i], jsresultArray.GetElement(i))
			}

			return results
		}))
	default:
		switch goType {
		case typeOfValue:
			field.Set(reflect.ValueOf(jsvalue))
		case typeOfObject:
			field.Set(reflect.ValueOf(jsvalue.ToObject()))
		case typeOfArray:
			field.Set(reflect.ValueOf(jsvalue.ToArray()))
		case typeOfRegExp:
			field.Set(reflect.ValueOf(jsvalue.ToRegExp()))
		case typeOfFunction:
			field.Set(reflect.ValueOf(jsvalue.ToFunction()))
		}
	}
}
<commit_msg>Fix bug in Bind()<commit_after>package v8

import (
	"errors"
	"reflect"
	"time"
)

//
// Fast bind Go type or function to JS. Note, The function template and object template
// created in fast bind internal are never destroyed. The JS class map to Go type use a
// internal field to reference a Go object when it instanced. All of the internal field
// keep reference by engine. So, may be you don't like to create too many instance of them.
//
func (template *ObjectTemplate) Bind(typeName string, target interface{}) error {
	engine := template.engine

	typeInfo := reflect.TypeOf(target)

	if typeInfo.Kind() == reflect.Ptr {
		typeInfo = typeInfo.Elem()
	}

	if typeInfo.Kind() == reflect.Func {
		jsfunc := engine.GoFuncToJsFunc(reflect.ValueOf(target))
		template.SetAccessor(typeName, func(name string, info AccessorCallbackInfo) {
			info.ReturnValue().Set(jsfunc.NewFunction())
		}, nil, nil, PA_None)
		return nil
	}

	if typeInfo.Kind() == reflect.Struct {
		constructor := engine.NewFunctionTemplate(func(info FunctionCallbackInfo) {
			value := reflect.New(typeInfo)
			info.This().SetInternalField(0, &value)
		}, nil)
		constructor.SetClassName(typeName)

		objTemplate := constructor.InstanceTemplate()
		objTemplate.SetInternalFieldCount(1)
		objTemplate.SetNamedPropertyHandler(
			// get
			func(name string, info PropertyCallbackInfo) {
				value := info.This().GetInternalField(0).(*reflect.Value)

				field := value.Elem().FieldByName(name)

				if field.IsValid() {
					info.ReturnValue().Set(engine.GoValueToJsValue(field))
					return
				}

				method := value.MethodByName(name)

				if !method.IsValid() {
					info.CurrentScope().ThrowException("could't found property or method '" + typeName + "." + name + "'")
					return
				}

				info.ReturnValue().Set(engine.GoFuncToJsFunc(method).NewFunction())
			},
			// set
			func(name string, jsvalue *Value, info PropertyCallbackInfo) {
				value := info.This().GetInternalField(0).(*reflect.Value)

				field := value.Elem().FieldByName(name)

				if !field.IsValid() {
					info.CurrentScope().ThrowException("could't found property '" + typeName + "." + name + "'")
					return
				}

				engine.SetJsValueToGo(field, jsvalue)
			},
			// query
			func(name string, info PropertyCallbackInfo) {
				value := info.This().ToObject().GetInternalField(0).(*reflect.Value)

				info.ReturnValue().SetBoolean(
					value.Elem().FieldByName(name).IsValid() || value.MethodByName(name).IsValid(),
				)
			},
			// delete
			nil,
			// enum
			nil,
			nil,
		)

		template.SetAccessor(typeName, func(name string, info AccessorCallbackInfo) {
			info.ReturnValue().Set(constructor.NewFunction())
		}, nil, nil, PA_None)

		return nil
	}

	return errors.New("unsupported target type")
}

func (engine *Engine) GoValueToJsValue(value reflect.Value) *Value {
	switch value.Kind() {
	case reflect.Bool:
		return engine.NewBoolean(value.Bool())
	case reflect.String:
		return engine.NewString(value.String())
	case reflect.Int8, reflect.Int16, reflect.Int32:
		return engine.NewInteger(value.Int())
	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
		return engine.NewInteger(value.Int())
	case reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
		return engine.NewNumber(float64(value.Int()))
	case reflect.Float32, reflect.Float64:
		return engine.NewNumber(value.Float())
	case reflect.Array, reflect.Slice:
		arrayLen := value.Len()
		jsArrayVal := engine.NewArray(value.Len())
		jsArray := jsArrayVal.ToArray()
		for i := 0; i < arrayLen; i++ {
			jsArray.SetElement(i, engine.GoValueToJsValue(value.Index(i)))
		}
		return jsArrayVal
	case reflect.Map:
		jsObjectVal := engine.NewObject()
		jsObject := jsObjectVal.ToObject()
		for _, key := range value.MapKeys() {
			switch key.Kind() {
			case reflect.String:
				jsObject.SetProperty(key.String(), engine.GoValueToJsValue(value.MapIndex(key)), PA_None)
			case reflect.Int8, reflect.Int16, reflect.Int32,
				reflect.Uint8, reflect.Uint16, reflect.Uint32,
				reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
				jsObject.SetElement(int(key.Int()), engine.GoValueToJsValue(value.MapIndex(key)))
			}
		}
		return jsObjectVal
	case reflect.Func:
		return engine.GoFuncToJsFunc(value).NewFunction()
	case reflect.Struct:
		switch value.Interface().(type) {
		case time.Time:
			return engine.NewDate(value.Interface().(time.Time))
		}
	}
	return engine.Undefined()
}

func (engine *Engine) GoFuncToJsFunc(gofunc reflect.Value) *FunctionTemplate {
	funcType := gofunc.Type()
	return engine.NewFunctionTemplate(func(callbackInfo FunctionCallbackInfo) {
		numIn := funcType.NumIn()
		numArgs := callbackInfo.Length()

		var out []reflect.Value

		if funcType.IsVariadic() {
			in := make([]reflect.Value, 1)
			in[0] = reflect.MakeSlice(funcType.In(0), numArgs, numArgs)

			for i := 0; i < numArgs; i++ {
				jsvalue := callbackInfo.Get(i)
				engine.SetJsValueToGo(in[0].Index(i), jsvalue)
			}

			out = gofunc.CallSlice(in)
		} else {
			in := make([]reflect.Value, numIn)

			for i := 0; i < len(in); i++ {
				jsvalue := callbackInfo.Get(i)
				in[i] = reflect.Indirect(reflect.New(funcType.In(i)))
				engine.SetJsValueToGo(in[i], jsvalue)
			}

			out = gofunc.Call(in)
		}

		if out == nil {
			callbackInfo.CurrentScope().ThrowException("argument number not match")
			return
		}

		switch {
		// when Go function returns only one value
		case len(out) == 1:
			callbackInfo.ReturnValue().Set(engine.GoValueToJsValue(out[0]))
		// when Go function returns multi-value, put them in a JavaScript array
		case len(out) > 1:
			jsResults := engine.NewArray(len(out))
			jsResultsArray := jsResults.ToArray()

			for i := 0; i < len(out); i++ {
				jsResultsArray.SetElement(i, engine.GoValueToJsValue(out[i]))
			}

			callbackInfo.ReturnValue().Set(jsResults)
		}
	}, nil)
}

var (
	typeOfValue    = reflect.TypeOf(new(Value))
	typeOfObject   = reflect.TypeOf(new(Object))
	typeOfArray    = reflect.TypeOf(new(Array))
	typeOfRegExp   = reflect.TypeOf(new(RegExp))
	typeOfFunction = reflect.TypeOf(new(Function))
)

func (engine *Engine) SetJsValueToGo(field reflect.Value, jsvalue *Value) {
	goType := field.Type()
	switch goType.Kind() {
	case reflect.Bool:
		field.SetBool(jsvalue.ToBoolean())
	case reflect.String:
		field.SetString(jsvalue.ToString())
	case reflect.Int8, reflect.Int16, reflect.Int32:
		field.SetInt(int64(jsvalue.ToInt32()))
	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
		field.SetUint(uint64(jsvalue.ToUint32()))
	case reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64:
		field.SetInt(jsvalue.ToInteger())
	case reflect.Float32, reflect.Float64:
		field.SetFloat(jsvalue.ToNumber())
	case reflect.Slice:
		jsArray := jsvalue.ToArray()
		jsArrayLen := jsArray.Length()
		field.Set(reflect.MakeSlice(goType, jsArrayLen, jsArrayLen))
		fallthrough
	case reflect.Array:
		jsArray := jsvalue.ToArray()
		jsArrayLen := jsArray.Length()
		for i := 0; i < jsArrayLen; i++ {
			engine.SetJsValueToGo(field.Index(i), jsArray.GetElement(i))
		}
	case reflect.Map:
		jsObject := jsvalue.ToObject()
		jsObjectKeys := jsObject.GetPropertyNames()
		jsObjectKeysLen := jsObjectKeys.Length()
		field.Set(reflect.MakeMap(goType))
		itemType := goType.Elem()
		for i := 0; i < jsObjectKeysLen; i++ {
			mapKey := jsObjectKeys.GetElement(i).ToString()
			mapValue := reflect.Indirect(reflect.New(itemType))
			engine.SetJsValueToGo(mapValue, jsObject.GetProperty(mapKey))
			field.SetMapIndex(reflect.ValueOf(mapKey), mapValue)
		}
	case reflect.Interface:
		field.Set(reflect.ValueOf(jsvalue))
	case reflect.Func:
		function := jsvalue.ToFunction()
		field.Set(reflect.MakeFunc(goType, func(args []reflect.Value) []reflect.Value {
			jsargs := make([]*Value, len(args))
			for i := 0; i < len(args); i++ {
				jsargs[i] = engine.GoValueToJsValue(args[i])
			}
			jsresult := function.Call(jsargs...)

			outNum := goType.NumOut()

			if outNum == 1 {
				var result = reflect.Indirect(reflect.New(goType.Out(0)))
				engine.SetJsValueToGo(result, jsresult)
				return []reflect.Value{result}
			}

			results := make([]reflect.Value, outNum)
			jsresultArray := jsresult.ToArray()

			for i := 0; i < outNum; i++ {
				results[i] = reflect.Indirect(reflect.New(goType.Out(i)))
				engine.SetJsValueToGo(results[i], jsresultArray.GetElement(i))
			}

			return results
		}))
	default:
		switch goType {
		case typeOfValue:
			field.Set(reflect.ValueOf(jsvalue))
		case typeOfObject:
			field.Set(reflect.ValueOf(jsvalue.ToObject()))
		case typeOfArray:
			field.Set(reflect.ValueOf(jsvalue.ToArray()))
		case typeOfRegExp:
			field.Set(reflect.ValueOf(jsvalue.ToRegExp()))
		case typeOfFunction:
			field.Set(reflect.ValueOf(jsvalue.ToFunction()))
		}
	}
}
<|endoftext|>


<commit_before>package jsonschema

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"unicode/utf8"
)

type minimum struct {
	json.Number
	exclusive bool
}

func (m minimum) Validate(unnormalized interface{}) []ValidationError {
	v, err := normalizeNumber(unnormalized)
	if err != nil {
		return []ValidationError{ValidationError{err.Error()}}
	}
	var isLarger int
	switch n := v.(type) {
	case int64:
		isLarger = m.isLargerThanInt(n)
	case float64:
		isLarger = m.isLargerThanFloat(n)
	default:
		return nil
	}
	if isLarger > 0 || (m.exclusive && isLarger == 0) {
		minErr := ValidationError{fmt.Sprintf("Value must be larger than %s.", m)}
		return []ValidationError{minErr}
	}
	return nil
}

func (m *minimum) SetSchema(v map[string]json.RawMessage) {
	value, ok := v["exclusiveMinimum"]
	if ok {
		// Ignore errors from Unmarshal. If exclusiveMinimum is a non boolean JSON
		// value we leave it as false.
		json.Unmarshal(value, &m.exclusive)
	}
	return
}

func (m *minimum) UnmarshalJSON(b []byte) error {
	return json.Unmarshal(b, &m.Number)
}

func (m minimum) isLargerThanInt(n int64) int {
	if !strings.Contains(m.String(), ".") {
		intg, err := m.Int64()
		if err != nil {
			return 0
		}
		if intg > n {
			return 1
		} else if intg < n {
			return -1
		}
	} else {
		flt, err := m.Float64()
		if err != nil {
			return 0
		}
		if flt > float64(n) {
			return 1
		} else if flt < float64(n) {
			return -1
		}
	}
	return 0
}

func (m minimum) isLargerThanFloat(n float64) int {
	flt, err := m.Float64()
	if err != nil {
		return 0
	}
	if flt > n {
		return 1
	} else if flt < n {
		return -1
	}
	return 0
}

type maxLength int

func (m maxLength) Validate(v interface{}) []ValidationError {
	l, ok := v.(string)
	if !ok {
		return nil
	}
	if utf8.RuneCountInString(l) > int(m) {
		lenErr := ValidationError{fmt.Sprintf("String length must be shorter than %d characters.", m)}
		return []ValidationError{lenErr}
	}
	return nil
}

type minLength int

func (m minLength) Validate(v interface{}) []ValidationError {
	l, ok := v.(string)
	if !ok {
		return nil
	}
	if utf8.RuneCountInString(l) < int(m) {
		lenErr := ValidationError{fmt.Sprintf("String length must be shorter than %d characters.", m)}
		return []ValidationError{lenErr}
	}
	return nil
}

type pattern struct {
	regexp.Regexp
}

func (p pattern) Validate(v interface{}) []ValidationError {
	s, ok := v.(string)
	if !ok {
		return nil
	}
	if !p.MatchString(s) {
		patErr := ValidationError{fmt.Sprintf("String must match the pattern: \"%s\".", p.String())}
		return []ValidationError{patErr}
	}
	return nil
}

func (p *pattern) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	r, err := regexp.Compile(s)
	if err != nil {
		return err
	}
	p.Regexp = *r
	return nil
}

type maxItems int

func (m maxItems) Validate(v interface{}) []ValidationError {
	l, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if len(l) > int(m) {
		maxErr := ValidationError{fmt.Sprintf("Array must have fewer than %d items.", m)}
		return []ValidationError{maxErr}
	}
	return nil
}

type minItems int

func (m minItems) Validate(v interface{}) []ValidationError {
	l, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if len(l) < int(m) {
		minErr := ValidationError{fmt.Sprintf("Array must have more than %d items.", m)}
		return []ValidationError{minErr}
	}
	return nil
}

// The spec[0] is useless for this keyword. The implemention here is based on the tests and this[1] guide.
//
// [0] http://json-schema.org/latest/json-schema-validation.html#anchor37
// [1] http://spacetelescope.github.io/understanding-json-schema/reference/array.html
type items struct {
	schema            *Schema
	schemaSlice       *[]Schema
	additionalAllowed bool
	additionalItems   *Schema
}

func (i items) Validate(v interface{}) []ValidationError {
	var valErrs []ValidationError
	instances, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if i.schema != nil {
		for _, value := range instances {
			valErrs = append(valErrs, i.schema.Validate(value)...)
		}
	} else if i.schemaSlice != nil {
		for pos, value := range instances {
			if pos <= len(*i.schemaSlice)-1 {
				schema := (*i.schemaSlice)[pos]
				valErrs = append(valErrs, schema.Validate(value)...)
			} else if i.additionalAllowed {
				if i.additionalItems == nil {
					continue
				}
				valErrs = append(valErrs, i.additionalItems.Validate(value)...)
			} else if !i.additionalAllowed {
				return []ValidationError{ValidationError{"Additional items aren't allowed."}}
			}
		}
	}
	return valErrs
}

func (i *items) SetSchema(v map[string]json.RawMessage) {
	i.additionalAllowed = true
	value, ok := v["additionalItems"]
	if !ok {
		return
	}
	json.Unmarshal(value, &i.additionalAllowed)
	json.Unmarshal(value, &i.additionalItems)
	return
}

func (i *items) UnmarshalJSON(b []byte) error {
	if err1 := json.Unmarshal(b, &i.schema); err1 != nil {
		i.schema = nil
	}
	if err2 := json.Unmarshal(b, &i.schemaSlice); err2 != nil {
		i.schemaSlice = nil
		return err2
	}
	return nil
}

type properties map[string]json.RawMessage

func (p properties) Validate(v interface{}) []ValidationError {
	var valErrs []ValidationError
	for schemaKey, schemaValue := range p {
		dataMap, ok := v.(map[string]interface{})
		if !ok {
			continue
		}
		if dataValue, ok := dataMap[schemaKey]; ok {
			var schema Schema
			err := json.Unmarshal(schemaValue, &schema)
			if err != nil {
				break
			}
			valErrs = append(valErrs, schema.Validate(dataValue)...)
		}
	}
	return valErrs
}
<commit_msg>Remove unnecessary pointer to slice field.<commit_after>package jsonschema

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"unicode/utf8"
)

type minimum struct {
	json.Number
	exclusive bool
}

func (m minimum) Validate(unnormalized interface{}) []ValidationError {
	v, err := normalizeNumber(unnormalized)
	if err != nil {
		return []ValidationError{ValidationError{err.Error()}}
	}
	var isLarger int
	switch n := v.(type) {
	case int64:
		isLarger = m.isLargerThanInt(n)
	case float64:
		isLarger = m.isLargerThanFloat(n)
	default:
		return nil
	}
	if isLarger > 0 || (m.exclusive && isLarger == 0) {
		minErr := ValidationError{fmt.Sprintf("Value must be larger than %s.", m)}
		return []ValidationError{minErr}
	}
	return nil
}

func (m *minimum) SetSchema(v map[string]json.RawMessage) {
	value, ok := v["exclusiveMinimum"]
	if ok {
		// Ignore errors from Unmarshal. If exclusiveMinimum is a non boolean JSON
		// value we leave it as false.
		json.Unmarshal(value, &m.exclusive)
	}
	return
}

func (m *minimum) UnmarshalJSON(b []byte) error {
	return json.Unmarshal(b, &m.Number)
}

func (m minimum) isLargerThanInt(n int64) int {
	if !strings.Contains(m.String(), ".") {
		intg, err := m.Int64()
		if err != nil {
			return 0
		}
		if intg > n {
			return 1
		} else if intg < n {
			return -1
		}
	} else {
		flt, err := m.Float64()
		if err != nil {
			return 0
		}
		if flt > float64(n) {
			return 1
		} else if flt < float64(n) {
			return -1
		}
	}
	return 0
}

func (m minimum) isLargerThanFloat(n float64) int {
	flt, err := m.Float64()
	if err != nil {
		return 0
	}
	if flt > n {
		return 1
	} else if flt < n {
		return -1
	}
	return 0
}

type maxLength int

func (m maxLength) Validate(v interface{}) []ValidationError {
	l, ok := v.(string)
	if !ok {
		return nil
	}
	if utf8.RuneCountInString(l) > int(m) {
		lenErr := ValidationError{fmt.Sprintf("String length must be shorter than %d characters.", m)}
		return []ValidationError{lenErr}
	}
	return nil
}

type minLength int

func (m minLength) Validate(v interface{}) []ValidationError {
	l, ok := v.(string)
	if !ok {
		return nil
	}
	if utf8.RuneCountInString(l) < int(m) {
		lenErr := ValidationError{fmt.Sprintf("String length must be shorter than %d characters.", m)}
		return []ValidationError{lenErr}
	}
	return nil
}

type pattern struct {
	regexp.Regexp
}

func (p pattern) Validate(v interface{}) []ValidationError {
	s, ok := v.(string)
	if !ok {
		return nil
	}
	if !p.MatchString(s) {
		patErr := ValidationError{fmt.Sprintf("String must match the pattern: \"%s\".", p.String())}
		return []ValidationError{patErr}
	}
	return nil
}

func (p *pattern) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	r, err := regexp.Compile(s)
	if err != nil {
		return err
	}
	p.Regexp = *r
	return nil
}

type maxItems int

func (m maxItems) Validate(v interface{}) []ValidationError {
	l, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if len(l) > int(m) {
		maxErr := ValidationError{fmt.Sprintf("Array must have fewer than %d items.", m)}
		return []ValidationError{maxErr}
	}
	return nil
}

type minItems int

func (m minItems) Validate(v interface{}) []ValidationError {
	l, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if len(l) < int(m) {
		minErr := ValidationError{fmt.Sprintf("Array must have more than %d items.", m)}
		return []ValidationError{minErr}
	}
	return nil
}

// The spec[0] is useless for this keyword. The implemention here is based on the tests and this[1] guide.
//
// [0] http://json-schema.org/latest/json-schema-validation.html#anchor37
// [1] http://spacetelescope.github.io/understanding-json-schema/reference/array.html
type items struct {
	schema            *Schema
	schemaSlice       []Schema
	additionalAllowed bool
	additionalItems   *Schema
}

func (i items) Validate(v interface{}) []ValidationError {
	var valErrs []ValidationError
	instances, ok := v.([]interface{})
	if !ok {
		return nil
	}
	if i.schema != nil {
		for _, value := range instances {
			valErrs = append(valErrs, i.schema.Validate(value)...)
		}
	} else if i.schemaSlice != nil {
		for pos, value := range instances {
			if pos <= len(i.schemaSlice)-1 {
				schema := i.schemaSlice[pos]
				valErrs = append(valErrs, schema.Validate(value)...)
			} else if i.additionalAllowed {
				if i.additionalItems == nil {
					continue
				}
				valErrs = append(valErrs, i.additionalItems.Validate(value)...)
			} else if !i.additionalAllowed {
				return []ValidationError{ValidationError{"Additional items aren't allowed."}}
			}
		}
	}
	return valErrs
}

func (i *items) SetSchema(v map[string]json.RawMessage) {
	i.additionalAllowed = true
	value, ok := v["additionalItems"]
	if !ok {
		return
	}
	json.Unmarshal(value, &i.additionalAllowed)
	json.Unmarshal(value, &i.additionalItems)
	return
}

func (i *items) UnmarshalJSON(b []byte) error {
	if err1 := json.Unmarshal(b, &i.schema); err1 != nil {
		i.schema = nil
	}
	if err2 := json.Unmarshal(b, &i.schemaSlice); err2 != nil {
		i.schemaSlice = nil
		return err2
	}
	return nil
}

type properties map[string]json.RawMessage

func (p properties) Validate(v interface{}) []ValidationError {
	var valErrs []ValidationError
	for schemaKey, schemaValue := range p {
		dataMap, ok := v.(map[string]interface{})
		if !ok {
			continue
		}
		if dataValue, ok := dataMap[schemaKey]; ok {
			var schema Schema
			err := json.Unmarshal(schemaValue, &schema)
			if err != nil {
				break
			}
			valErrs = append(valErrs, schema.Validate(dataValue)...)
		}
	}
	return valErrs
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"crypto/tls"
	"expvar"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/google/uuid"
	lookup "github.com/vmware/govmomi/lookup/simulator"
	pbm "github.com/vmware/govmomi/pbm/simulator"
	"github.com/vmware/govmomi/simulator"
	"github.com/vmware/govmomi/simulator/esx"
	"github.com/vmware/govmomi/simulator/vpx"
	sts "github.com/vmware/govmomi/sts/simulator"
	vapi "github.com/vmware/govmomi/vapi/simulator"
	"github.com/vmware/govmomi/vim25/types"
)

func main() {
	model := simulator.VPX()

	flag.IntVar(&model.Datacenter, "dc", model.Datacenter, "Number of datacenters")
	flag.IntVar(&model.Cluster, "cluster", model.Cluster, "Number of clusters")
	flag.IntVar(&model.ClusterHost, "host", model.ClusterHost, "Number of hosts per cluster")
	flag.IntVar(&model.Host, "standalone-host", model.Host, "Number of standalone hosts")
	flag.IntVar(&model.Datastore, "ds", model.Datastore, "Number of local datastores")
	flag.IntVar(&model.Machine, "vm", model.Machine, "Number of virtual machines per resource pool")
	flag.IntVar(&model.Pool, "pool", model.Pool, "Number of resource pools per compute resource")
	flag.IntVar(&model.App, "app", model.App, "Number of virtual apps per compute resource")
	flag.IntVar(&model.Pod, "pod", model.Pod, "Number of storage pods per datacenter")
	flag.IntVar(&model.Portgroup, "pg", model.Portgroup, "Number of port groups")
	flag.IntVar(&model.Folder, "folder", model.Folder, "Number of folders")
	flag.BoolVar(&model.Autostart, "autostart", model.Autostart, "Autostart model created VMs")

	isESX := flag.Bool("esx", false, "Simulate standalone ESX")
	isTLS := flag.Bool("tls", true, "Enable TLS")
	cert := flag.String("tlscert", "", "Path to TLS certificate file")
	key := flag.String("tlskey", "", "Path to TLS key file")
	env := flag.String("E", "-", "Output vcsim variables to the given fifo or stdout")
	listen := flag.String("l", "127.0.0.1:8989", "Listen address for vcsim")
	tunnel := flag.Int("tunnel", -1, "SDK tunnel port")
	flag.BoolVar(&simulator.Trace, "trace", simulator.Trace, "Trace SOAP to stderr")

	flag.IntVar(&model.DelayConfig.Delay, "delay", model.DelayConfig.Delay, "Method response delay across all methods")
	methodDelayP := flag.String("method-delay", "", "Delay per method on the form 'method1:delay1,method2:delay2...'")
	flag.Float64Var(&model.DelayConfig.DelayJitter, "delay-jitter", model.DelayConfig.DelayJitter, "Delay jitter coefficient of variation (tip: 0.5 is a good starting value)")

	flag.Parse()

	methodDelay := *methodDelayP

	switch flag.Arg(0) {
	case "uuidgen": // util-linux not installed on Travis CI
		fmt.Println(uuid.New().String())
		return
	}

	if methodDelay != "" {
		m := make(map[string]int)
		for _, s := range strings.Split(methodDelay, ",") {
			s = strings.TrimSpace(s)
			tuples := strings.Split(s, ":")
			if len(tuples) == 2 {
				key := tuples[0]
				value, err := strconv.Atoi(tuples[1])
				if err != nil {
					log.Fatalf("Incorrect format of method-delay argument: %s", err)
				}
				m[key] = value
			} else {
				log.Fatal("Incorrect method delay format.")
			}
		}
		model.DelayConfig.MethodDelay = m
	}

	var err error
	out := os.Stdout

	if *env != "-" {
		out, err = os.OpenFile(*env, os.O_WRONLY, 0)
		if err != nil {
			log.Fatal(err)
		}
	}

	if err = updateHostTemplate(*listen); err != nil {
		log.Fatal(err)
	}

	if *isESX {
		opts := model
		model = simulator.ESX()
		// Preserve options that also apply to ESX
		model.Datastore = opts.Datastore
		model.Machine = opts.Machine
		model.Autostart = opts.Autostart
		model.DelayConfig.Delay = opts.DelayConfig.Delay
		model.DelayConfig.MethodDelay = opts.DelayConfig.MethodDelay
		model.DelayConfig.DelayJitter = opts.DelayConfig.DelayJitter
	}

	tag := " (govmomi simulator)"
	model.ServiceContent.About.Name += tag
	model.ServiceContent.About.OsType = runtime.GOOS + "-" + runtime.GOARCH

	esx.HostSystem.Summary.Hardware.Vendor += tag

	err = model.Create()
	if err != nil {
		log.Fatal(err)
	}

	model.Service.Listen = *listen
	if *isTLS {
		model.Service.TLS = new(tls.Config)
		if *cert != "" {
			c, err := tls.LoadX509KeyPair(*cert, *key)
			if err != nil {
				log.Fatal(err)
			}

			model.Service.TLS.Certificates = []tls.Certificate{c}
		}
	}

	expvar.Publish("vcsim", expvar.Func(func() interface{} {
		count := model.Count()

		return struct {
			Registry *simulator.Registry
			Model    *simulator.Model
		}{
			simulator.Map,
			&count,
		}
	}))

	model.Service.ServeMux = http.DefaultServeMux // expvar.init registers "/debug/vars" with the DefaultServeMux

	s := model.Service.NewServer()

	if *tunnel >= 0 {
		s.Tunnel = *tunnel
		if err := s.StartTunnel(); err != nil {
			log.Fatal(err)
		}
	}

	if !*isESX {
		// STS simulator
		path, handler := sts.New(s.URL, vpx.Setting)
		model.Service.Handle(path, handler)

		// vAPI simulator
		path, handler = vapi.New(s.URL, vpx.Setting)
		model.Service.Handle(path, handler)

		// Lookup Service simulator
		model.Service.RegisterSDK(lookup.New())

		// PBM simulator
		model.Service.RegisterSDK(pbm.New())
	}

	fmt.Fprintf(out, "export GOVC_URL=%s GOVC_SIM_PID=%d\n", s.URL, os.Getpid())
	if out != os.Stdout {
		err = out.Close()
		if err != nil {
			log.Fatal(err)
		}
	}

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

	<-sig

	model.Remove()
}

func updateHostTemplate(ip string) error {
	addr, port, err := net.SplitHostPort(ip)
	if err != nil {
		return err
	}
	if port != "0" { // server starts after the model is created, skipping auto-selected ports for now
		n, err := strconv.Atoi(port)
		if err != nil {
			return err
		}
		esx.HostSystem.Summary.Config.Port = int32(n)
	}

	nics := [][]types.HostVirtualNic{
		esx.HostConfigInfo.Network.Vnic,
		esx.HostConfigInfo.Vmotion.NetConfig.CandidateVnic,
	}

	for _, nic := range esx.HostConfigInfo.VirtualNicManagerInfo.NetConfig {
		nics = append(nics, nic.CandidateVnic)
	}

	for _, nic := range nics {
		for i := range nic {
			nic[i].Spec.Ip.IpAddress = addr // replace "127.0.0.1" with $addr
		}
	}

	return nil
}
<commit_msg>vcsim: Press any key to exit<commit_after>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"crypto/tls"
	"expvar"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/google/uuid"
	lookup "github.com/vmware/govmomi/lookup/simulator"
	pbm "github.com/vmware/govmomi/pbm/simulator"
	"github.com/vmware/govmomi/simulator"
	"github.com/vmware/govmomi/simulator/esx"
	"github.com/vmware/govmomi/simulator/vpx"
	sts "github.com/vmware/govmomi/sts/simulator"
	vapi "github.com/vmware/govmomi/vapi/simulator"
	"github.com/vmware/govmomi/vim25/types"
)

func main() {
	model := simulator.VPX()

	flag.IntVar(&model.Datacenter, "dc", model.Datacenter, "Number of datacenters")
	flag.IntVar(&model.Cluster, "cluster", model.Cluster, "Number of clusters")
	flag.IntVar(&model.ClusterHost, "host", model.ClusterHost, "Number of hosts per cluster")
	flag.IntVar(&model.Host, "standalone-host", model.Host, "Number of standalone hosts")
	flag.IntVar(&model.Datastore, "ds", model.Datastore, "Number of local datastores")
	flag.IntVar(&model.Machine, "vm", model.Machine, "Number of virtual machines per resource pool")
	flag.IntVar(&model.Pool, "pool", model.Pool, "Number of resource pools per compute resource")
	flag.IntVar(&model.App, "app", model.App, "Number of virtual apps per compute resource")
	flag.IntVar(&model.Pod, "pod", model.Pod, "Number of storage pods per datacenter")
	flag.IntVar(&model.Portgroup, "pg", model.Portgroup, "Number of port groups")
	flag.IntVar(&model.Folder, "folder", model.Folder, "Number of folders")
	flag.BoolVar(&model.Autostart, "autostart", model.Autostart, "Autostart model created VMs")

	isESX := flag.Bool("esx", false, "Simulate standalone ESX")
	isTLS := flag.Bool("tls", true, "Enable TLS")
	cert := flag.String("tlscert", "", "Path to TLS certificate file")
	key := flag.String("tlskey", "", "Path to TLS key file")
	env := flag.String("E", "-", "Output vcsim variables to the given fifo or stdout")
	listen := flag.String("l", "127.0.0.1:8989", "Listen address for vcsim")
	tunnel := flag.Int("tunnel", -1, "SDK tunnel port")
	flag.BoolVar(&simulator.Trace, "trace", simulator.Trace, "Trace SOAP to stderr")
	stdinExit := flag.Bool("stdinexit", false, "Press any key to exit")

	flag.IntVar(&model.DelayConfig.Delay, "delay", model.DelayConfig.Delay, "Method response delay across all methods")
	methodDelayP := flag.String("method-delay", "", "Delay per method on the form 'method1:delay1,method2:delay2...'")
	flag.Float64Var(&model.DelayConfig.DelayJitter, "delay-jitter", model.DelayConfig.DelayJitter, "Delay jitter coefficient of variation (tip: 0.5 is a good starting value)")

	flag.Parse()

	methodDelay := *methodDelayP

	switch flag.Arg(0) {
	case "uuidgen": // util-linux not installed on Travis CI
		fmt.Println(uuid.New().String())
		return
	}

	if methodDelay != "" {
		m := make(map[string]int)
		for _, s := range strings.Split(methodDelay, ",") {
			s = strings.TrimSpace(s)
			tuples := strings.Split(s, ":")
			if len(tuples) == 2 {
				key := tuples[0]
				value, err := strconv.Atoi(tuples[1])
				if err != nil {
					log.Fatalf("Incorrect format of method-delay argument: %s", err)
				}
				m[key] = value
			} else {
				log.Fatal("Incorrect method delay format.")
			}
		}
		model.DelayConfig.MethodDelay = m
	}

	var err error
	out := os.Stdout

	if *env != "-" {
		out, err = os.OpenFile(*env, os.O_WRONLY, 0)
		if err != nil {
			log.Fatal(err)
		}
	}

	if err = updateHostTemplate(*listen); err != nil {
		log.Fatal(err)
	}

	if *isESX {
		opts := model
		model = simulator.ESX()
		// Preserve options that also apply to ESX
		model.Datastore = opts.Datastore
		model.Machine = opts.Machine
		model.Autostart = opts.Autostart
		model.DelayConfig.Delay = opts.DelayConfig.Delay
		model.DelayConfig.MethodDelay = opts.DelayConfig.MethodDelay
		model.DelayConfig.DelayJitter = opts.DelayConfig.DelayJitter
	}

	tag := " (govmomi simulator)"
	model.ServiceContent.About.Name += tag
	model.ServiceContent.About.OsType = runtime.GOOS + "-" + runtime.GOARCH

	esx.HostSystem.Summary.Hardware.Vendor += tag

	err = model.Create()
	if err != nil {
		log.Fatal(err)
	}

	model.Service.Listen = *listen
	if *isTLS {
		model.Service.TLS = new(tls.Config)
		if *cert != "" {
			c, err := tls.LoadX509KeyPair(*cert, *key)
			if err != nil {
				log.Fatal(err)
			}

			model.Service.TLS.Certificates = []tls.Certificate{c}
		}
	}

	expvar.Publish("vcsim", expvar.Func(func() interface{} {
		count := model.Count()

		return struct {
			Registry *simulator.Registry
			Model    *simulator.Model
		}{
			simulator.Map,
			&count,
		}
	}))

	model.Service.ServeMux = http.DefaultServeMux // expvar.init registers "/debug/vars" with the DefaultServeMux

	s := model.Service.NewServer()

	if *tunnel >= 0 {
		s.Tunnel = *tunnel
		if err := s.StartTunnel(); err != nil {
			log.Fatal(err)
		}
	}

	if !*isESX {
		// STS simulator
		path, handler := sts.New(s.URL, vpx.Setting)
		model.Service.Handle(path, handler)

		// vAPI simulator
		path, handler = vapi.New(s.URL, vpx.Setting)
		model.Service.Handle(path, handler)

		// Lookup Service simulator
		model.Service.RegisterSDK(lookup.New())

		// PBM simulator
		model.Service.RegisterSDK(pbm.New())
	}

	fmt.Fprintf(out, "export GOVC_URL=%s GOVC_SIM_PID=%d\n", s.URL, os.Getpid())
	if out != os.Stdout {
		err = out.Close()
		if err != nil {
			log.Fatal(err)
		}
	}

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	if *stdinExit {
		fmt.Fprintf(out, "Press any key to exit")
		go func() {
			os.Stdin.Read(make([]byte, 1))
			sig <- syscall.SIGTERM
		}()
	}

	<-sig

	model.Remove()
}

func updateHostTemplate(ip string) error {
	addr, port, err := net.SplitHostPort(ip)
	if err != nil {
		return err
	}
	if port != "0" { // server starts after the model is created, skipping auto-selected ports for now
		n, err := strconv.Atoi(port)
		if err != nil {
			return err
		}
		esx.HostSystem.Summary.Config.Port = int32(n)
	}

	nics := [][]types.HostVirtualNic{
		esx.HostConfigInfo.Network.Vnic,
		esx.HostConfigInfo.Vmotion.NetConfig.CandidateVnic,
	}

	for _, nic := range esx.HostConfigInfo.VirtualNicManagerInfo.NetConfig {
		nics = append(nics, nic.CandidateVnic)
	}

	for _, nic := range nics {
		for i := range nic {
			nic[i].Spec.Ip.IpAddress = addr // replace "127.0.0.1" with $addr
		}
	}

	return nil
}
<|endoftext|>


<commit_before><commit_msg>Update mqtt/packet/packet.go<commit_after><|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sys

import (
	"os"
	"regexp"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System.
const SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"

// volumePath is a regular expression to check if a path is a Windows
// volume path (e.g., "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}"
// or "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\").
var volumePath = regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}\\?$`)

// MkdirAllWithACL is a custom version of os.MkdirAll modified for use on Windows
// so that it is both volume path aware, and to create a directory
// an appropriate SDDL defined ACL for Builtin Administrators and Local System.
func MkdirAllWithACL(path string, _ os.FileMode) error {
	return mkdirall(path, true)
}

// MkdirAll is a custom version of os.MkdirAll that is volume path aware for
// Windows. It can be used as a drop-in replacement for os.MkdirAll.
func MkdirAll(path string, _ os.FileMode) error {
	return mkdirall(path, false)
}

// mkdirall is a custom version of os.MkdirAll modified for use on Windows
// so that it is both volume path aware, and can create a directory with
// a DACL.
func mkdirall(path string, adminAndLocalSystem bool) error {
	if volumePath.MatchString(path) {
		return nil
	}

	// The rest of this method is largely copied from os.MkdirAll and should be kept
	// as-is to ensure compatibility.

	// Fast path: if we can tell whether path is a directory or file, stop with success or error.
	dir, err := os.Stat(path)
	if err == nil {
		if dir.IsDir() {
			return nil
		}
		return &os.PathError{
			Op:   "mkdir",
			Path: path,
			Err:  syscall.ENOTDIR,
		}
	}

	// Slow path: make sure parent exists and then call Mkdir for path.
	i := len(path)
	for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
		i--
	}

	j := i
	for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
		j--
	}

	if j > 1 {
		// Create parent
		err = mkdirall(path[0:j-1], adminAndLocalSystem)
		if err != nil {
			return err
		}
	}

	// Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
	if adminAndLocalSystem {
		err = mkdirWithACL(path)
	} else {
		err = os.Mkdir(path, 0)
	}

	if err != nil {
		// Handle arguments like "foo/." by
		// double-checking that directory doesn't exist.
		dir, err1 := os.Lstat(path)
		if err1 == nil && dir.IsDir() {
			return nil
		}
		return err
	}
	return nil
}

// mkdirWithACL creates a new directory. If there is an error, it will be of
// type *PathError. .
//
// This is a modified and combined version of os.Mkdir and windows.Mkdir
// in golang to cater for creating a directory am ACL permitting full
// access, with inheritance, to any subfolder/file for Built-in Administrators
// and Local System.
func mkdirWithACL(name string) error {
	sa := windows.SecurityAttributes{Length: 0}
	sd, err := windows.SecurityDescriptorFromString(SddlAdministratorsLocalSystem)
	if err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}
	sa.Length = uint32(unsafe.Sizeof(sa))
	sa.InheritHandle = 1
	sa.SecurityDescriptor = sd

	namep, err := windows.UTF16PtrFromString(name)
	if err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}

	e := windows.CreateDirectory(namep, &sa)
	if e != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: e}
	}
	return nil
}
<commit_msg>sys: create SecurityAttribute only once (Windows)<commit_after>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sys

import (
	"os"
	"regexp"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

// SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System.
const SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"

// volumePath is a regular expression to check if a path is a Windows
// volume path (e.g., "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}"
// or "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\").
var volumePath = regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}\\?$`)

// MkdirAllWithACL is a custom version of os.MkdirAll modified for use on Windows
// so that it is both volume path aware, and to create a directory
// an appropriate SDDL defined ACL for Builtin Administrators and Local System.
func MkdirAllWithACL(path string, _ os.FileMode) error {
	sa, err := makeSecurityAttributes(SddlAdministratorsLocalSystem)
	if err != nil {
		return &os.PathError{Op: "mkdirall", Path: path, Err: err}
	}
	return mkdirall(path, sa)
}

// MkdirAll is a custom version of os.MkdirAll that is volume path aware for
// Windows. It can be used as a drop-in replacement for os.MkdirAll.
func MkdirAll(path string, _ os.FileMode) error {
	return mkdirall(path, nil)
}

// mkdirall is a custom version of os.MkdirAll modified for use on Windows
// so that it is both volume path aware, and can create a directory with
// a DACL.
func mkdirall(path string, perm *windows.SecurityAttributes) error {
	if volumePath.MatchString(path) {
		return nil
	}

	// The rest of this method is largely copied from os.MkdirAll and should be kept
	// as-is to ensure compatibility.

	// Fast path: if we can tell whether path is a directory or file, stop with success or error.
	dir, err := os.Stat(path)
	if err == nil {
		if dir.IsDir() {
			return nil
		}
		return &os.PathError{
			Op:   "mkdir",
			Path: path,
			Err:  syscall.ENOTDIR,
		}
	}

	// Slow path: make sure parent exists and then call Mkdir for path.
	i := len(path)
	for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
		i--
	}

	j := i
	for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
		j--
	}

	if j > 1 {
		// Create parent
		err = mkdirall(path[0:j-1], perm)
		if err != nil {
			return err
		}
	}

	// Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
	err = mkdirWithACL(path, perm)
	if err != nil {
		// Handle arguments like "foo/." by
		// double-checking that directory doesn't exist.
		dir, err1 := os.Lstat(path)
		if err1 == nil && dir.IsDir() {
			return nil
		}
		return err
	}
	return nil
}

// mkdirWithACL creates a new directory. If there is an error, it will be of
// type *PathError. .
//
// This is a modified and combined version of os.Mkdir and windows.Mkdir
// in golang to cater for creating a directory am ACL permitting full
// access, with inheritance, to any subfolder/file for Built-in Administrators
// and Local System.
func mkdirWithACL(name string, sa *windows.SecurityAttributes) error {
	if sa == nil {
		return os.Mkdir(name, 0)
	}

	namep, err := windows.UTF16PtrFromString(name)
	if err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}

	err = windows.CreateDirectory(namep, sa)
	if err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}
	return nil
}

func makeSecurityAttributes(sddl string) (*windows.SecurityAttributes, error) {
	var sa windows.SecurityAttributes
	sa.Length = uint32(unsafe.Sizeof(sa))
	sa.InheritHandle = 1
	var err error
	sa.SecurityDescriptor, err = windows.SecurityDescriptorFromString(sddl)
	if err != nil {
		return nil, err
	}
	return &sa, nil
}
<|endoftext|>


<commit_before>package kite

import (
	"errors"
	"fmt"
	"koding/newkite/dnode"
	"koding/newkite/dnode/rpc"
	"koding/newkite/kodingkey"
	"koding/newkite/token"
)

type kiteMethod struct {
	kite    *Kite
	method  string
	handler HandlerFunc
}

func (m kiteMethod) WrapArgs(args []interface{}, tr dnode.Transport) []interface{} {
	return []interface{}{&callOptionsOut{
		WithArgs: args,
		CallOptions: CallOptions{
			Kite: m.kite.Kite,
		},
	}}
}

func (m kiteMethod) Call(method string, args *dnode.Partial, tr dnode.Transport) {
	request, responseCallback, err := m.kite.parseRequest(method, args, tr)
	if err != nil {
		m.kite.Log.Notice("Did not understand request: %s", err)
		return
	}

	result, err := m.handler(request)
	if responseCallback == nil {
		return
	}

	if err != nil {
		err = responseCallback(err.Error(), result)
	} else {
		err = responseCallback(nil, result)
	}

	if err != nil {
		m.kite.Log.Error(err.Error())
	}
}

type HandlerFunc func(*Request) (response interface{}, err error)

func (k *Kite) HandleFunc(method string, handler HandlerFunc) {
	k.server.Handle(method, kiteMethod{k, method, handler})
}

type Request struct {
	Method         string
	Args           *dnode.Partial
	LocalKite      *Kite
	RemoteKite     *RemoteKite
	Username       string
	Authentication *callAuthentication
	RemoteAddr     string
}

type Callback func(r *Request)

func (c Callback) MarshalJSON() ([]byte, error) {
	return []byte(`"[Function]"`), nil
}

func (c Callback) WrapArgs(args []interface{}, tr dnode.Transport) []interface{} {
	return []interface{}{&callOptionsOut{
		WithArgs: args,
		CallOptions: CallOptions{
			Kite: tr.Properties()["localKite"].(*Kite).Kite,
		},
	}}
}

func (c Callback) Call(method string, args *dnode.Partial, tr dnode.Transport) {
	k := tr.Properties()["localKite"].(*Kite)
	req, _, err := k.parseRequest(method, args, tr)
	if err != nil {
		println("error 2", err.Error())
		return
	}

	c(req)
}

func (k *Kite) parseRequest(method string, arguments *dnode.Partial, tr dnode.Transport) (request *Request, response dnode.Function, err error) {
	// Parse dnode method arguments [options, response]
	args, err := arguments.Slice()
	if err != nil {
		return
	}
	if len(args) != 1 && len(args) != 2 {
		return nil, nil, errors.New("Invalid number of arguments")
	}

	// Parse options argument
	var options CallOptions
	if err = args[0].Unmarshal(&options); err != nil {
		return
	}

	// Parse response callback if present
	if len(args) > 1 && args[1] != nil {
		if err = args[1].Unmarshal(&response); err != nil {
			return
		}
	}

	// Trust the Kite if we have initiated the connection.
	// Otherwise try to authenticate the user.
	// RemoteAddr() returns "" if this is an outgoing connection.
	if tr.RemoteAddr() != "" {
		if err = k.authenticateUser(&options); err != nil {
			return
		}
	}

	// Properties about the client...
	properties := tr.Properties()

	// Create a new RemoteKite instance to pass it to the handler, so
	// the handler can call methods on the other site on the same connection.
	if properties["remoteKite"] == nil {
		// Do not create a new RemoteKite on every request,
		// cache it in Transport.Properties().
		client := tr.(*rpc.Client) // We only have a dnode/rpc.Client for now.
		remoteKite := k.newRemoteKiteWithClient(options.Kite, options.Authentication, client)
		properties["remoteKite"] = remoteKite

		// Notify Kite.OnConnect handlers.
		k.notifyRemoteKiteConnected(remoteKite)
	}

	request = &Request{
		Method:         method,
		Args:           options.WithArgs,
		LocalKite:      k,
		RemoteKite:     properties["remoteKite"].(*RemoteKite),
		RemoteAddr:     tr.RemoteAddr(),
		Username:       options.Kite.Username, // authenticateUser() sets it.
		Authentication: &options.Authentication,
	}

	// We need this information on disconnect
	properties["username"] = request.Username
	properties["kiteID"] = request.RemoteKite.Kite.ID

	return
}

// authenticateUser tries to authenticate the user by selecting appropriate
// authenticator function.
func (k *Kite) authenticateUser(options *CallOptions) error {
	f := k.Authenticators[options.Authentication.Type]
	if f == nil {
		return fmt.Errorf("Unknown authentication type: %s", options.Authentication.Type)
	}

	return f(options)
}

// AuthenticateFromToken is the default Authenticator for Kite.
func (k *Kite) AuthenticateFromToken(options *CallOptions) error {
	key, err := kodingkey.FromString(k.KodingKey)
	if err != nil {
		return fmt.Errorf("Invalid Koding Key: %s", k.KodingKey)
	}

	tkn, err := token.DecryptString(options.Authentication.Key, key)
	if err != nil {
		return fmt.Errorf("Invalid token: %s", options.Authentication.Key)
	}

	if !tkn.IsValid(k.ID) {
		return fmt.Errorf("Invalid token: %s", tkn)
	}

	options.Kite.Username = tkn.Username

	return nil
}

// AuthenticateFromToken authenticates user from Koding Key.
// Kontrol makes requests with a Koding Key.
func (k *Kite) AuthenticateFromKodingKey(options *CallOptions) error {
	if options.Authentication.Key != k.KodingKey {
		return fmt.Errorf("Invalid Koding Key")
	}

	// Set the username if missing.
	if options.Kite.Username == "" && k.Username != "" {
		options.Kite.Username = k.Username
	}

	return nil
}
<commit_msg>kite: do not check authentication on callback methods<commit_after>package kite

import (
	"errors"
	"fmt"
	"koding/newkite/dnode"
	"koding/newkite/dnode/rpc"
	"koding/newkite/kodingkey"
	"koding/newkite/token"
)

// kiteMethod implements dnode.Handler.
type kiteMethod struct {
	kite    *Kite
	method  string
	handler HandlerFunc
}

// WrapArgs is called before a Callback is sent to remote in order to wrap arguments.
func (m kiteMethod) WrapArgs(args []interface{}, tr dnode.Transport) []interface{} {
	return []interface{}{&callOptionsOut{
		WithArgs: args,
		CallOptions: CallOptions{
			Kite: m.kite.Kite,
		},
	}}
}

// Call is called when a method is received from remote.
func (m kiteMethod) Call(method string, args *dnode.Partial, tr dnode.Transport) {
	request, responseCallback, err := m.kite.parseRequest(method, args, tr, true)
	if err != nil {
		m.kite.Log.Notice("Did not understand request: %s", err)
		return
	}

	result, err := m.handler(request)
	if responseCallback == nil {
		return
	}

	if err != nil {
		err = responseCallback(err.Error(), result)
	} else {
		err = responseCallback(nil, result)
	}

	if err != nil {
		m.kite.Log.Error(err.Error())
	}
}

type HandlerFunc func(*Request) (response interface{}, err error)

// HandleFunc registers a handler to run when a method call is received from a Kite.
func (k *Kite) HandleFunc(method string, handler HandlerFunc) {
	k.server.Handle(method, kiteMethod{k, method, handler})
}

type Request struct {
	Method         string
	Args           *dnode.Partial
	LocalKite      *Kite
	RemoteKite     *RemoteKite
	Username       string
	Authentication *callAuthentication
	RemoteAddr     string
}

type Callback func(r *Request)

func (c Callback) MarshalJSON() ([]byte, error) {
	return []byte(`"[Function]"`), nil
}

func (c Callback) WrapArgs(args []interface{}, tr dnode.Transport) []interface{} {
	return []interface{}{&callOptionsOut{
		WithArgs: args,
		CallOptions: CallOptions{
			Kite: tr.Properties()["localKite"].(*Kite).Kite,
		},
	}}
}

// Call is called when a callback method call is received from remote.
func (c Callback) Call(method string, args *dnode.Partial, tr dnode.Transport) {
	k := tr.Properties()["localKite"].(*Kite)
	req, _, err := k.parseRequest(method, args, tr, false)
	if err != nil {
		k.Log.Notice("Did not understand callback message: %s", err)
		return
	}

	c(req)
}

// parseRequest is used to read a dnode message.
// It is called when a method or callback is received.
func (k *Kite) parseRequest(method string, arguments *dnode.Partial, tr dnode.Transport, authenticate bool) (
	request *Request, response dnode.Function, err error) {

	// Parse dnode method arguments [options, response]
	args, err := arguments.Slice()
	if err != nil {
		return
	}
	if len(args) != 1 && len(args) != 2 {
		return nil, nil, errors.New("Invalid number of arguments")
	}

	// Parse options argument
	var options CallOptions
	if err = args[0].Unmarshal(&options); err != nil {
		return
	}

	// Parse response callback if present
	if len(args) > 1 && args[1] != nil {
		if err = args[1].Unmarshal(&response); err != nil {
			return
		}
	}

	// Trust the Kite if we have initiated the connection.
	// Otherwise try to authenticate the user.
	// RemoteAddr() returns "" if this is an outgoing connection.
	// Also we do not need to authenticate requests when a callback method is received.
	if authenticate && tr.RemoteAddr() != "" {
		if err = k.authenticateUser(&options); err != nil {
			return
		}
	}

	// Properties about the client...
	properties := tr.Properties()

	// Create a new RemoteKite instance to pass it to the handler, so
	// the handler can call methods on the other site on the same connection.
	if properties["remoteKite"] == nil {
		// Do not create a new RemoteKite on every request,
		// cache it in Transport.Properties().
		client := tr.(*rpc.Client) // We only have a dnode/rpc.Client for now.
		remoteKite := k.newRemoteKiteWithClient(options.Kite, options.Authentication, client)
		properties["remoteKite"] = remoteKite

		// Notify Kite.OnConnect handlers.
		k.notifyRemoteKiteConnected(remoteKite)
	}

	request = &Request{
		Method:         method,
		Args:           options.WithArgs,
		LocalKite:      k,
		RemoteKite:     properties["remoteKite"].(*RemoteKite),
		RemoteAddr:     tr.RemoteAddr(),
		Username:       options.Kite.Username, // authenticateUser() sets it.
		Authentication: &options.Authentication,
	}

	return
}

// authenticateUser tries to authenticate the user by selecting appropriate
// authenticator function.
func (k *Kite) authenticateUser(options *CallOptions) error {
	f := k.Authenticators[options.Authentication.Type]
	if f == nil {
		return fmt.Errorf("Unknown authentication type: %s", options.Authentication.Type)
	}

	return f(options)
}

// AuthenticateFromToken is the default Authenticator for Kite.
func (k *Kite) AuthenticateFromToken(options *CallOptions) error {
	key, err := kodingkey.FromString(k.KodingKey)
	if err != nil {
		return fmt.Errorf("Invalid Koding Key: %s", k.KodingKey)
	}

	tkn, err := token.DecryptString(options.Authentication.Key, key)
	if err != nil {
		return fmt.Errorf("Invalid token: %s", options.Authentication.Key)
	}

	if !tkn.IsValid(k.ID) {
		return fmt.Errorf("Invalid token: %s", tkn)
	}

	options.Kite.Username = tkn.Username

	return nil
}

// AuthenticateFromToken authenticates user from Koding Key.
// Kontrol makes requests with a Koding Key.
func (k *Kite) AuthenticateFromKodingKey(options *CallOptions) error {
	if options.Authentication.Key != k.KodingKey {
		return fmt.Errorf("Invalid Koding Key")
	}

	// Set the username if missing.
	if options.Kite.Username == "" && k.Username != "" {
		options.Kite.Username = k.Username
	}

	return nil
}
<|endoftext|>