<commit_before>package tasker

import (
	"errors"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/sony/sonyflake"
)

// Core is the task module config table.
// `MasterOutOfDate` means the time master state can be save, instance can race to be master when the duration of `Updated` to now bigger than this.
// `InstanceHeartbeat` is the max interval Instance should be check Master, should be less than `MasterOutOfDate`
type Core struct {
	Id                int
	MasterInstanceID  uint16    `orm:"column(master_instance_id)"`
	MasterFQDN        string    `orm:"column(master_fqdn)"`
	Updated           time.Time `orm:"auto_now"`
	MasterOutOfDate   int64     // ms
	InstanceHeartbeat int64     // ms
}

const (
	defaultMasterOutOfDate   = 6000
	defaultInstanceHeartbeat = 3000
)

// UniqID use for generate worker id.
var UniqID *sonyflake.Sonyflake
var InstanceID uint16
var IsMaster bool

func Init(MachineID func() (uint16, error), CheckMachineID func(uint16) bool) (err error) {
	if MachineID == nil {
		MachineID = lower16BitPrivateIP
	}

	if InstanceID, err = MachineID(); err != nil {
		return
	}

	if CheckMachineID != nil {
		if !CheckMachineID(InstanceID) {
			return errors.New("not valid MachineID via CheckMachineID method validate")
		}
	}

	if err = InitIDGEN(MachineID, CheckMachineID); err != nil {
		// InitIDGEN failed
		return
	}

	go keepMasterRace()

	return nil
}

func InitIDGEN(MachineID func() (uint16, error), CheckMachineID func(uint16) bool) error {
	// init sonyflake.
	UniqID = sonyflake.NewSonyflake(sonyflake.Settings{
		StartTime:      time.Now(),
		MachineID:      MachineID,
		CheckMachineID: CheckMachineID,
	})
	if UniqID == nil {
		return errors.New("initialize unique id generate tool failed")
	}
	return nil
}

func RegisterModel() {
	orm.RegisterModel(new(Core), new(Task))
}

func getCore() (core *Core, err error) {
	o := orm.NewOrm()
	core = &Core{Id: 1} // alwary read id=1
	err = o.Read(core)
	return
}

func (self *Core) becomeMaster() {
	self.MasterInstanceID = InstanceID
	self.MasterFQDN = FQDN()

	o := orm.NewOrm()
	var err error
	if self.Id != 0 {
		// save
		_, err = o.Update(self, "MasterInstanceID", "MasterOutOfDate", "MasterFQDN", "Updated")
	} else {
		// insert
		_, err = o.Insert(self)
	}

	if err != nil {
		beego.BeeLogger.Error("[tasker] try to becomeMaster failed: %s", err.Error())
	}
}

func (self *Core) heartbeatMaster() {
	o := orm.NewOrm()
	_, err := o.Update(self, "Updated")
	if err != nil {
		beego.BeeLogger.Error("[tasker] heartbeatMaster failed: %s", err.Error())
	}
}

// keepMasterRace always race to be master
func keepMasterRace() {
	rand.Seed(time.Now().Unix())

	for {
		core, err := getCore()
		if err != nil {
			if err == orm.ErrNoRows {
				// no core object, register self
				core.Id = 0
				core.MasterOutOfDate = defaultMasterOutOfDate
				core.InstanceHeartbeat = defaultInstanceHeartbeat
				core.becomeMaster()
				continue
			}

			// output the error log and sleep 1s
			beego.BeeLogger.Error("[tasker] get core config from db failed: %s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}

		if core.MasterInstanceID == InstanceID {
			// I'm master
			IsMaster = true
			core.heartbeatMaster()
		} else {
			// I'm not master
			IsMaster = false
			if time.Since(core.Updated).Nanoseconds()/int64(time.Millisecond) > core.MasterOutOfDate {
				// the master outofdate, I will be the master
				core.becomeMaster()
			}
		}

		time.Sleep(time.Duration(core.InstanceHeartbeat-rand.Int63n(1000)) * time.Millisecond)
	}
}

func lower16BitPrivateIP() (uint16, error) {
	ip, err := privateIPv4()
	if err != nil {
		return 0, err
	}

	return uint16(ip[2])<<8 + uint16(ip[3]), nil
}

func privateIPv4() (net.IP, error) {
	as, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	for _, a := range as {
		ipnet, ok := a.(*net.IPNet)
		if !ok || ipnet.IP.IsLoopback() {
			continue
		}

		ip := ipnet.IP.To4()
		if ip != nil {
			return ip, nil
		}
	}
	return nil, errors.New("no private ip address")
}

// Get Fully Qualified Domain Name
// returns "unknown" or hostanme in case of error
func FQDN() string {
	hostname, err := os.Hostname()
	if err != nil {
		return "unknown"

	}

	addrs, err := net.LookupIP(hostname)
	if err != nil {
		return hostname
	}

	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			ip, err := ipv4.MarshalText()
			if err != nil {
				return hostname
			}
			hosts, err := net.LookupAddr(string(ip))
			if err != nil {
				return hostname
			}
			fqdn := hosts[0]
			return strings.TrimSuffix(fqdn, ".") // return fqdn without trailing dot
		}

	}

	return hostname
}
<commit_msg>updaet<commit_after>package tasker

import (
	"errors"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/sony/sonyflake"
)

// Core is the task module config table.
// `MasterOutOfDate` means the time master state can be save, instance can race to be master when the duration of `Updated` to now bigger than this.
// `InstanceHeartbeat` is the max interval Instance should be check Master, should be less than `MasterOutOfDate`
type Core struct {
	Id                int
	MasterInstanceID  uint16    `orm:"column(master_instance_id)"`
	MasterFQDN        string    `orm:"column(master_fqdn)"`
	Updated           time.Time `orm:"auto_now"`
	MasterOutOfDate   int64     // ms
	InstanceHeartbeat int64     // ms
}

const (
	defaultMasterOutOfDate   = 6000
	defaultInstanceHeartbeat = 3000
)

// UniqID use for generate worker id.
var UniqID *sonyflake.Sonyflake
var InstanceID uint16
var IsMaster bool

func Init(MachineID func() (uint16, error), CheckMachineID func(uint16) bool) (err error) {
	if MachineID == nil {
		MachineID = lower16BitPrivateIP
	}

	if InstanceID, err = MachineID(); err != nil {
		return
	}

	if CheckMachineID != nil {
		if !CheckMachineID(InstanceID) {
			return errors.New("not valid MachineID via CheckMachineID method validate")
		}
	}

	if err = InitIDGEN(MachineID, CheckMachineID); err != nil {
		// InitIDGEN failed
		return
	}

	go keepMasterRace()

	return nil
}

func InitIDGEN(MachineID func() (uint16, error), CheckMachineID func(uint16) bool) error {
	// init sonyflake.
	UniqID = sonyflake.NewSonyflake(sonyflake.Settings{
		StartTime:      time.Now(),
		MachineID:      MachineID,
		CheckMachineID: CheckMachineID,
	})
	if UniqID == nil {
		return errors.New("initialize unique id generate tool failed")
	}
	return nil
}

func RegisterModel() {
	orm.RegisterModel(new(Core), new(Task))
}

func getCore() (core *Core, err error) {
	o := orm.NewOrm()
	core = &Core{Id: 1} // alwary read id=1
	err = o.Read(core)
	return
}

func (self *Core) becomeMaster() {
	self.MasterInstanceID = InstanceID
	self.MasterFQDN = FQDN()

	o := orm.NewOrm()
	var err error
	if self.Id != 0 {
		// save
		_, err = o.Update(self, "MasterInstanceID", "MasterFQDN", "Updated")
	} else {
		// insert
		_, err = o.Insert(self)
	}

	if err != nil {
		beego.BeeLogger.Error("[tasker] try to becomeMaster failed: %s", err.Error())
	}
}

func (self *Core) heartbeatMaster() {
	o := orm.NewOrm()
	_, err := o.Update(self, "Updated")
	if err != nil {
		beego.BeeLogger.Error("[tasker] heartbeatMaster failed: %s", err.Error())
	}
}

// keepMasterRace always race to be master
func keepMasterRace() {
	rand.Seed(time.Now().Unix())

	for {
		core, err := getCore()
		if err != nil {
			if err == orm.ErrNoRows {
				// no core object, register self
				core.Id = 0
				core.MasterOutOfDate = defaultMasterOutOfDate
				core.InstanceHeartbeat = defaultInstanceHeartbeat
				core.becomeMaster()
				continue
			}

			// output the error log and sleep 1s
			beego.BeeLogger.Error("[tasker] get core config from db failed: %s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}

		if core.MasterInstanceID == InstanceID {
			// I'm master
			IsMaster = true
			core.heartbeatMaster()
		} else {
			// I'm not master
			IsMaster = false
			if time.Since(core.Updated).Nanoseconds()/int64(time.Millisecond) > core.MasterOutOfDate {
				// the master outofdate, I will be the master
				core.becomeMaster()
			}
		}

		time.Sleep(time.Duration(core.InstanceHeartbeat-rand.Int63n(1000)) * time.Millisecond)
	}
}

func lower16BitPrivateIP() (uint16, error) {
	ip, err := privateIPv4()
	if err != nil {
		return 0, err
	}

	return uint16(ip[2])<<8 + uint16(ip[3]), nil
}

func privateIPv4() (net.IP, error) {
	as, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	for _, a := range as {
		ipnet, ok := a.(*net.IPNet)
		if !ok || ipnet.IP.IsLoopback() {
			continue
		}

		ip := ipnet.IP.To4()
		if ip != nil {
			return ip, nil
		}
	}
	return nil, errors.New("no private ip address")
}

// Get Fully Qualified Domain Name
// returns "unknown" or hostanme in case of error
func FQDN() string {
	hostname, err := os.Hostname()
	if err != nil {
		return "unknown"

	}

	addrs, err := net.LookupIP(hostname)
	if err != nil {
		return hostname
	}

	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			ip, err := ipv4.MarshalText()
			if err != nil {
				return hostname
			}
			hosts, err := net.LookupAddr(string(ip))
			if err != nil {
				return hostname
			}
			fqdn := hosts[0]
			return strings.TrimSuffix(fqdn, ".") // return fqdn without trailing dot
		}

	}

	return hostname
}
<|endoftext|>


<commit_before>/*
Package cors is net/http handler to handle CORS related requests
as defined by http://www.w3.org/TR/cors/

You can configure it by passing an option struct to cors.New:

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"foo.com"},
        AllowedMethods: []string{"GET", "POST", "DELETE"},
        AllowCredentials: true,
    })

Then insert the handler in the chain:

    handler = c.Handler(handler)

See Options documentation for more options.

The resulting handler is a standard net/http handler.
*/
package cors

import (
	"net/http"
	"strconv"
	"strings"
)

// Options is a configuration container to setup the CORS middleware.
type Options struct {
	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is ["*"]
	AllowedOrigins []string
	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests.
	AllowedMethods []string
	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowedHeaders []string
	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string
	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool
	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int
}

type Cors struct {
	// The CORS Options
	options Options
}

// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
	// Normalize options
	// Note: for origins and methods matching, the spec requires a case-sensitive matching.
	// As it may error prone, we chose to ignore the spec here.
	normOptions := Options{
		AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower),
		AllowedMethods: convert(options.AllowedMethods, strings.ToUpper),
		// Origin is always appended as some browsers will always request
		// for this header at preflight
		AllowedHeaders:   convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey),
		ExposedHeaders:   convert(options.ExposedHeaders, http.CanonicalHeaderKey),
		AllowCredentials: options.AllowCredentials,
		MaxAge:           options.MaxAge,
	}
	if len(normOptions.AllowedOrigins) == 0 {
		// Default is all origins
		normOptions.AllowedOrigins = []string{"*"}
	}
	if len(normOptions.AllowedMethods) == 0 {
		// Default is simple methods
		normOptions.AllowedMethods = []string{"GET", "POST"}
	}
	return &Cors{
		options: normOptions,
	}
}

// Default creates a new Cors handler with default options
func Default() *Cors {
	return New(Options{})
}

// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (cors *Cors) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			cors.handlePreflight(w, r)
			// Preflight requests are standalone and should stop the chain as some other
			// middleware may not handle OPTIONS requests correctly. One typical example
			// is authentication middleware ; OPTIONS requests won't carry authentication
			// headers (see #1)
		} else {
			cors.handleActualRequest(w, r)
			h.ServeHTTP(w, r)
		}
	})
}

// Martini compatible handler
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		cors.handlePreflight(w, r)
	} else {
		cors.handleActualRequest(w, r)
	}
}

// Negroni compatible interface
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "OPTIONS" {
		cors.handlePreflight(w, r)
		// Preflight requests are standalone and should stop the chain as some other
		// middleware may not handle OPTIONS requests correctly. One typical example
		// is authentication middleware ; OPTIONS requests won't carry authentication
		// headers (see #1)
	} else {
		cors.handleActualRequest(w, r)
		next(w, r)
	}
}

// handlePreflight handles pre-flight CORS requests
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")
	if r.Method != "OPTIONS" || origin == "" || !cors.isOriginAllowed(origin) {
		return
	}
	reqMethod := r.Header.Get("Access-Control-Request-Method")
	if !cors.isMethodAllowed(reqMethod) {
		return
	}
	reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
	if !cors.areHeadersAllowed(reqHeaders) {
		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
	// by Access-Control-Request-Method (if supported) can be enough
	headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
	if len(reqHeaders) > 0 {

		// Spec says: Since the list of headers can be unbounded, simply returning supported headers
		// from Access-Control-Request-Headers can be enough
		headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}
	if options.MaxAge > 0 {
		headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
	}
}

// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")
	if r.Method == "OPTIONS" || origin == "" || !cors.isOriginAllowed(origin) {
		return
	}
	// Note that spec does define a way to specifically disallow a simple method like GET or
	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
	// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
	// We think it's a nice feature to be able to have control on those methods though.
	if !cors.isMethodAllowed(r.Method) {
		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	if len(options.ExposedHeaders) > 0 {
		headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}
}

// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (cors *Cors) isOriginAllowed(origin string) bool {
	allowedOrigins := cors.options.AllowedOrigins
	origin = strings.ToLower(origin)
	for _, allowedOrigin := range allowedOrigins {
		switch allowedOrigin {
		case "*":
			return true
		case origin:
			return true
		}
	}
	return false
}

// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (cors *Cors) isMethodAllowed(method string) bool {
	allowedMethods := cors.options.AllowedMethods
	if len(allowedMethods) == 0 {
		// If no method allowed, always return false, even for preflight request
		return false
	}
	method = strings.ToUpper(method)
	if method == "OPTIONS" {
		// Always allow preflight requests
		return true
	}
	for _, allowedMethod := range allowedMethods {
		if allowedMethod == method {
			return true
		}
	}
	return false
}

// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool {
	if len(requestedHeaders) == 0 {
		return true
	}
	for _, header := range requestedHeaders {
		found := false
		for _, allowedHeader := range cors.options.AllowedHeaders {
			if header == allowedHeader {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}
<commit_msg>Adds a debugging option<commit_after>/*
Package cors is net/http handler to handle CORS related requests
as defined by http://www.w3.org/TR/cors/

You can configure it by passing an option struct to cors.New:

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"foo.com"},
        AllowedMethods: []string{"GET", "POST", "DELETE"},
        AllowCredentials: true,
    })

Then insert the handler in the chain:

    handler = c.Handler(handler)

See Options documentation for more options.

The resulting handler is a standard net/http handler.
*/
package cors

import (
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// Options is a configuration container to setup the CORS middleware.
type Options struct {
	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is ["*"]
	AllowedOrigins []string
	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests.
	AllowedMethods []string
	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowedHeaders []string
	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string
	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool
	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int
	// Debugging flag adds additional output to debug server side CORS issues
	Debug bool
	// log object to use when debugging
	log *log.Logger
}

type Cors struct {
	// The CORS Options
	options Options
}

// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
	// Normalize options
	// Note: for origins and methods matching, the spec requires a case-sensitive matching.
	// As it may error prone, we chose to ignore the spec here.
	normOptions := Options{
		AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower),
		AllowedMethods: convert(options.AllowedMethods, strings.ToUpper),
		// Origin is always appended as some browsers will always request
		// for this header at preflight
		AllowedHeaders:   convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey),
		ExposedHeaders:   convert(options.ExposedHeaders, http.CanonicalHeaderKey),
		AllowCredentials: options.AllowCredentials,
		MaxAge:           options.MaxAge,
		Debug:            options.Debug,
		log:              log.New(os.Stdout, "[cors] ", log.LstdFlags),
	}
	if len(normOptions.AllowedOrigins) == 0 {
		// Default is all origins
		normOptions.AllowedOrigins = []string{"*"}
	}
	if len(normOptions.AllowedMethods) == 0 {
		// Default is simple methods
		normOptions.AllowedMethods = []string{"GET", "POST"}
	}

	if normOptions.Debug {
		normOptions.log.Printf("Options: %v", normOptions)
	}
	return &Cors{
		options: normOptions,
	}
}

// Default creates a new Cors handler with default options
func Default() *Cors {
	return New(Options{})
}

// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (cors *Cors) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Preflight request")
			}
			cors.handlePreflight(w, r)
			// Preflight requests are standalone and should stop the chain as some other
			// middleware may not handle OPTIONS requests correctly. One typical example
			// is authentication middleware ; OPTIONS requests won't carry authentication
			// headers (see #1)
		} else {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Actual request")
			}
			cors.handleActualRequest(w, r)
			h.ServeHTTP(w, r)
		}
	})
}

// Martini compatible handler
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Preflight request")
		}
		cors.handlePreflight(w, r)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Actual request")
		}
		cors.handleActualRequest(w, r)
	}
}

// Negroni compatible interface
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Preflight request")
		}
		cors.handlePreflight(w, r)
		// Preflight requests are standalone and should stop the chain as some other
		// middleware may not handle OPTIONS requests correctly. One typical example
		// is authentication middleware ; OPTIONS requests won't carry authentication
		// headers (see #1)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Actual request")
		}

		cors.handleActualRequest(w, r)
		next(w, r)
	}
}

// handlePreflight handles pre-flight CORS requests
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method != "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: %s!=OPTIONS", r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: empty origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: origin '%s' not allowed", origin)
		}
		return
	}

	reqMethod := r.Header.Get("Access-Control-Request-Method")
	if !cors.isMethodAllowed(reqMethod) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: method '%s' not allowed", reqMethod)
		}

		return
	}
	reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
	if !cors.areHeadersAllowed(reqHeaders) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: headers '%v' not allowed", reqHeaders)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
	// by Access-Control-Request-Method (if supported) can be enough
	headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
	if len(reqHeaders) > 0 {

		// Spec says: Since the list of headers can be unbounded, simply returning supported headers
		// from Access-Control-Request-Headers can be enough
		headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}
	if options.MaxAge > 0 {
		headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Preflight response headers: %v", headers)
	}
}

// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method == %s",
				r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: missing origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: origin '%s' not allowed",
				origin)
		}

		return
	}

	// Note that spec does define a way to specifically disallow a simple method like GET or
	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
	// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
	// We think it's a nice feature to be able to have control on those methods though.
	if !cors.isMethodAllowed(r.Method) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method '%s' not allowed",
				r.Method)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	if len(options.ExposedHeaders) > 0 {
		headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Actual response added headers: %v", headers)
	}
}

// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (cors *Cors) isOriginAllowed(origin string) bool {
	allowedOrigins := cors.options.AllowedOrigins
	origin = strings.ToLower(origin)
	for _, allowedOrigin := range allowedOrigins {
		switch allowedOrigin {
		case "*":
			return true
		case origin:
			return true
		}
	}
	return false
}

// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (cors *Cors) isMethodAllowed(method string) bool {
	allowedMethods := cors.options.AllowedMethods
	if len(allowedMethods) == 0 {
		// If no method allowed, always return false, even for preflight request
		return false
	}
	method = strings.ToUpper(method)
	if method == "OPTIONS" {
		// Always allow preflight requests
		return true
	}
	for _, allowedMethod := range allowedMethods {
		if allowedMethod == method {
			return true
		}
	}
	return false
}

// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool {
	if len(requestedHeaders) == 0 {
		return true
	}
	for _, header := range requestedHeaders {
		found := false
		for _, allowedHeader := range cors.options.AllowedHeaders {
			if header == allowedHeader {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	id int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) error {
	removeJob(c.entries, id)
	return nil
}

func removeJob(data []*Entry, id int) {
	w := 0 // write index
	for _, x := range data {
		if id == x.id {
			continue
		}
		data[w] = x
		w++
	}
	data = data[:w]
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.Schedule(schedule, cmd)
	c.count++
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)): {
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go e.Job.Run()
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue
		}

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
		c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
				Schedule: e.Schedule,
				Next:     e.Next,
				Prev:     e.Prev,
				Job:      e.Job,
			})
	}
	return entries
}
<commit_msg>Added remove<commit_after>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	id int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) error {
	c.entries = removeJob(c.entries, id)
	return nil
}

func removeJob(data []*Entry, id int) []*Entry {
	w := 0 // write index
	for _, x := range data {
		if id == x.id {
			continue
		}
		data[w] = x
		w++
	}
	return data[:w]
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.count++
	c.Schedule(schedule, cmd, c.count)
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, id int) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		id:       id,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)): {
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go e.Job.Run()
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue
		}

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
		c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
				Schedule: e.Schedule,
				Next:     e.Next,
				Prev:     e.Prev,
				Job:      e.Job,
			})
	}
	return entries
}
<|endoftext|>


<commit_before>package thermal

import (
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/poly1305"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"log"
)

// cs3a is an implementation of the NaCl based cipher set 3a
type cs3a struct {
	id             string
	fingerprintBin []byte
	fingerprintHex string
	publicKey      [32]byte
	privateKey     [32]byte
}

// initialize generates a key pair and sets up the cipher set
func (cs *cs3a) initialize() error {

	// generate the key pair
	publicKey, privateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair in cs3a initialization")
		return err
	}

	// generate the fingerprint hash
	hash256 := sha256.New()
	hash256.Write(publicKey[:])
	fingerprintBin := hash256.Sum(nil)
	fingerprintHex := fmt.Sprintf("%x", fingerprintBin)

	// initialize the struct
	cs.id = "cs3a"
	cs.fingerprintBin = fingerprintBin
	cs.fingerprintHex = fingerprintHex
	cs.publicKey = *publicKey
	cs.privateKey = *privateKey

	return nil

}

func (cs *cs3a) String() string {
	return fmt.Sprintf("%s: %x", cs.id, cs.fingerprint)
}

func (cs *cs3a) csid() string {
	return cs.id
}

// fingerprint returns the csid and fingerprint for use in a 'parts' set
func (cs *cs3a) fingerprint() (string, string) {
	return cs.id, cs.fingerprintHex
}

/*
--------------------------------------------------------------------------------
The CS3a Open Packet Handshake
---------------------------------

The cs3a encryption & decryption of an inner open packet, from the perspective
of the sender and receiver:

Sender:
box.Precompute(senderLineSecret, receiverPublicKey, senderLinePrivateKey)
secretbox.Seal(encInnerPacket, packet, &nonce, senderLineSecret)

Receiver:
box.Precompute(&senderLineSecret, &senderLinePublicKey, &receiverPrivateKey)
secretbox.Open(packet, encInnerPacket, &nonce, &senderLineSecret)

The sender/receiver context helps to highlight the public/private key pairings.

However, the following functions use the context of local switch instance
and remote switch instance instead.
--------------------------------------------------------------------------------
*/

// encryptOpenPacket returns an assembled open packet body and a local line shared secret
func (cs *cs3a) encryptOpenPacket(packet []byte, remotePublicKey *[32]byte) (openPacketBody []byte, localLineSecret [32]byte, err error) {

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// temporary line key pair
	linePublicKey, linePrivateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair for line")
		return openPacketBody, localLineSecret, err
	}

	// Encrypt the inner packet
	var nonce [24]byte
	var encInnerPacket []byte

	box.Precompute(&localLineSecret, remotePublicKey, linePrivateKey)
	encInnerPacket = secretbox.Seal(encInnerPacket, packet, &nonce, &localLineSecret)

	// Generate the mac and assemble the body for the outer packet
	// <mac><local-line-public-key><encrypted-inner-packet-data>
	var macKey [32]byte
	var mac [16]byte
	var openPacketData []byte

	box.Precompute(&macKey, remotePublicKey, &cs.privateKey)
	openPacketData = append(openPacketData, linePublicKey[:]...)
	openPacketData = append(openPacketData, encInnerPacket...)
	poly1305.Sum(&mac, openPacketData, &macKey)

	openPacketBody = append(openPacketBody, mac[:]...)
	openPacketBody = append(openPacketBody, openPacketData...)

	return openPacketBody, localLineSecret, nil

}

// decryptOpenPacket returns an unencrypted inner open packet and a remote line shared secret
func (cs *cs3a) decryptOpenPacket(openPacketBody []byte, remotePublicKey *[32]byte) (packet []byte, remoteLineSecret [32]byte, err error) {

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// Unpack the outer packet body
	// <mac><remote-line-public-key><encrypted-inner-packet-data>
	var mac [16]byte
	var remoteLinePublicKey [32]byte
	var encInnerPacket []byte
	var openPacketData []byte

	copy(mac[:], openPacketBody[:16])
	copy(remoteLinePublicKey[:], openPacketBody[16:48])
	encInnerPacket = append(encInnerPacket, openPacketBody[48:]...)
	openPacketData = append(openPacketData, remoteLinePublicKey[:]...)
	openPacketData = append(openPacketData, encInnerPacket...)

	// Verify the mac
	var authenticated bool
	var macKey [32]byte

	box.Precompute(&macKey, remotePublicKey, &cs.privateKey)
	authenticated = poly1305.Verify(&mac, openPacketData, &macKey)
	if !authenticated {
		msg := "Incoming open packet failed MAC authentication"
		log.Println(msg)
		err = fmt.Errorf(msg)
		return packet, remoteLineSecret, err
	}

	// Decrypt the inner packet
	var nonce [24]byte

	box.Precompute(&remoteLineSecret, &remoteLinePublicKey, &cs.privateKey)
	packet, success := secretbox.Open(packet, encInnerPacket, &nonce, &remoteLineSecret)
	if !success {
		err := fmt.Errorf("Error opening the secretbox")
		return packet, remoteLineSecret, err
	}

	//log.Printf("packet: %x\n", packet)
	return packet, remoteLineSecret, nil
}

// generateLineEncryptionKey returns a key suitable for outgoing line packet encryption,
// in the form of sha256(local-line-secret, local-line-id, local-line-id)
func (cs *cs3a) generateLineEncryptionKey(localLineSecret *[32]byte, localLineId, remoteLineId *[16]byte) (key [32]byte) {

	hash256 := sha256.New()
	hash256.Write(localLineSecret[:])
	hash256.Write(localLineId[:])
	hash256.Write(remoteLineId[:])
	keyHash := hash256.Sum(nil)

	copy(key[:], keyHash[:])

	return key
}

// generateLineDecryptionKey returns a key suitable for incoming line packet decryption,
// in the form of sha256(remote-line-secret, remote-line-id, local-line-id)
func (cs *cs3a) generateLineDecryptionKey(remoteLineSecret *[32]byte, localLineId, remoteLineId *[16]byte) (key [32]byte) {

	hash256 := sha256.New()
	hash256.Write(remoteLineSecret[:])
	hash256.Write(remoteLineId[:])
	hash256.Write(localLineId[:])
	keyBin := hash256.Sum(nil)

	copy(key[:], keyBin[:])

	return key
}

// encryptLinePacket returns an encrypted and assembled outer line packet body
// in the form <nonce><secretbox-ciphertext>
func (cs *cs3a) encryptLinePacket(packet []byte, lineEncryptionKey *[32]byte) (linePacketBody []byte, err error) {
	var nonce [24]byte
	var linePacketData []byte

	// encrypt the inner channel packet
	rand.Reader.Read(nonce[:])
	linePacketData = secretbox.Seal(linePacketData, packet, &nonce, lineEncryptionKey)

	// assemble: <nonce><secretbox-ciphertext>
	linePacketBody = append(nonce[:], linePacketData[:]...)

	return linePacketBody, nil
}

// decryptLinePacket returns a decrypted inner line packet
// by disassembling and decrypting an outer packet body in the form <nonce><secretbox-ciphertext>
func (cs *cs3a) decryptLinePacket(linePacketBody []byte, lineDecryptionKey *[32]byte) (packet []byte, err error) {
	var nonce [24]byte

	// disassemble: <nonce><secretbox-ciphertext>
	copy(nonce[:], linePacketBody[:24])
	linePacketData := linePacketBody[24:]

	// decrypt the inner channel packet
	packet, success := secretbox.Open(packet, linePacketData, &nonce, lineDecryptionKey)
	if !success {
		err := fmt.Errorf("Error opening the secretbox")
		return packet, err
	}

	return packet, nil
}
<commit_msg>adjust doc strings<commit_after>package thermal

import (
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/poly1305"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"log"
)

// cs3a is an implementation of the NaCl based cipher set 3a
type cs3a struct {
	id             string
	fingerprintBin []byte
	fingerprintHex string
	publicKey      [32]byte
	privateKey     [32]byte
}

// initialize generates a key pair and sets up the cipher set
func (cs *cs3a) initialize() error {

	// generate the key pair
	publicKey, privateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair in cs3a initialization")
		return err
	}

	// generate the fingerprint hash
	hash256 := sha256.New()
	hash256.Write(publicKey[:])
	fingerprintBin := hash256.Sum(nil)
	fingerprintHex := fmt.Sprintf("%x", fingerprintBin)

	// initialize the struct
	cs.id = "cs3a"
	cs.fingerprintBin = fingerprintBin
	cs.fingerprintHex = fingerprintHex
	cs.publicKey = *publicKey
	cs.privateKey = *privateKey

	return nil

}

func (cs *cs3a) String() string {
	return fmt.Sprintf("%s: %x", cs.id, cs.fingerprint)
}

func (cs *cs3a) csid() string {
	return cs.id
}

// fingerprint returns the csid and fingerprint for use in a 'parts' set
func (cs *cs3a) fingerprint() (string, string) {
	return cs.id, cs.fingerprintHex
}

/*
--------------------------------------------------------------------------------
The CS3a Open Packet Handshake
---------------------------------

The cs3a encryption & decryption of an inner open packet, from the perspective
of the sender and receiver:

Sender:
box.Precompute(senderLineSecret, receiverPublicKey, senderLinePrivateKey)
secretbox.Seal(encInnerPacket, packet, &nonce, senderLineSecret)

Receiver:
box.Precompute(&senderLineSecret, &senderLinePublicKey, &receiverPrivateKey)
secretbox.Open(packet, encInnerPacket, &nonce, &senderLineSecret)

The sender/receiver context helps to highlight the public/private key pairings.

However, the following functions use the context of local switch instance
and remote switch instance instead.
--------------------------------------------------------------------------------
*/

// encryptOpenPacket returns an assembled open packet body and a local line shared secret
func (cs *cs3a) encryptOpenPacket(packet []byte, remotePublicKey *[32]byte) (openPacketBody []byte, localLineSecret [32]byte, err error) {

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// temporary line key pair
	linePublicKey, linePrivateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair for line")
		return openPacketBody, localLineSecret, err
	}

	// Encrypt the inner packet
	var nonce [24]byte
	var encInnerPacket []byte

	box.Precompute(&localLineSecret, remotePublicKey, linePrivateKey)
	encInnerPacket = secretbox.Seal(encInnerPacket, packet, &nonce, &localLineSecret)

	// Generate the mac and assemble the body for the outer packet
	// <mac><local-line-public-key><encrypted-inner-packet-data>
	var macKey [32]byte
	var mac [16]byte
	var openPacketData []byte

	box.Precompute(&macKey, remotePublicKey, &cs.privateKey)
	openPacketData = append(openPacketData, linePublicKey[:]...)
	openPacketData = append(openPacketData, encInnerPacket...)
	poly1305.Sum(&mac, openPacketData, &macKey)

	openPacketBody = append(openPacketBody, mac[:]...)
	openPacketBody = append(openPacketBody, openPacketData...)

	return openPacketBody, localLineSecret, nil

}

// decryptOpenPacket returns an unencrypted inner open packet and a remote line shared secret
func (cs *cs3a) decryptOpenPacket(openPacketBody []byte, remotePublicKey *[32]byte) (packet []byte, remoteLineSecret [32]byte, err error) {

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// Unpack the outer packet body
	// <mac><remote-line-public-key><encrypted-inner-packet-data>
	var mac [16]byte
	var remoteLinePublicKey [32]byte
	var encInnerPacket []byte
	var openPacketData []byte

	copy(mac[:], openPacketBody[:16])
	copy(remoteLinePublicKey[:], openPacketBody[16:48])
	encInnerPacket = append(encInnerPacket, openPacketBody[48:]...)
	openPacketData = append(openPacketData, remoteLinePublicKey[:]...)
	openPacketData = append(openPacketData, encInnerPacket...)

	// Verify the mac
	var authenticated bool
	var macKey [32]byte

	box.Precompute(&macKey, remotePublicKey, &cs.privateKey)
	authenticated = poly1305.Verify(&mac, openPacketData, &macKey)
	if !authenticated {
		msg := "Incoming open packet failed MAC authentication"
		log.Println(msg)
		err = fmt.Errorf(msg)
		return packet, remoteLineSecret, err
	}

	// Decrypt the inner packet
	var nonce [24]byte

	box.Precompute(&remoteLineSecret, &remoteLinePublicKey, &cs.privateKey)
	packet, success := secretbox.Open(packet, encInnerPacket, &nonce, &remoteLineSecret)
	if !success {
		err := fmt.Errorf("Error opening the secretbox")
		return packet, remoteLineSecret, err
	}

	//log.Printf("packet: %x\n", packet)
	return packet, remoteLineSecret, nil
}

// generateLineEncryptionKey returns a key suitable for outgoing line packet encryption
func (cs *cs3a) generateLineEncryptionKey(localLineSecret *[32]byte, localLineId, remoteLineId *[16]byte) (key [32]byte) {

	// sha256(local-line-secret, local-line-id, local-line-id)
	hash256 := sha256.New()
	hash256.Write(localLineSecret[:])
	hash256.Write(localLineId[:])
	hash256.Write(remoteLineId[:])
	keyHash := hash256.Sum(nil)

	copy(key[:], keyHash[:])

	return key
}

// generateLineDecryptionKey returns a key suitable for incoming line packet decryption
func (cs *cs3a) generateLineDecryptionKey(remoteLineSecret *[32]byte, localLineId, remoteLineId *[16]byte) (key [32]byte) {

	// sha256(remote-line-secret, remote-line-id, local-line-id)
	hash256 := sha256.New()
	hash256.Write(remoteLineSecret[:])
	hash256.Write(remoteLineId[:])
	hash256.Write(localLineId[:])
	keyBin := hash256.Sum(nil)

	copy(key[:], keyBin[:])

	return key
}

// encryptLinePacket encrypts a channel packet and builds a line packet body
func (cs *cs3a) encryptLinePacket(packet []byte, lineEncryptionKey *[32]byte) (linePacketBody []byte, err error) {
	var nonce [24]byte
	var linePacketData []byte

	// encrypt the inner channel packet
	rand.Reader.Read(nonce[:])
	linePacketData = secretbox.Seal(linePacketData, packet, &nonce, lineEncryptionKey)

	// assemble: <nonce><secretbox-ciphertext>
	linePacketBody = append(nonce[:], linePacketData[:]...)

	return linePacketBody, nil
}

// decryptLinePacket returns a decrypted channel packet from a line packet
func (cs *cs3a) decryptLinePacket(linePacketBody []byte, lineDecryptionKey *[32]byte) (packet []byte, err error) {
	var nonce [24]byte

	// disassemble: <nonce><secretbox-ciphertext>
	copy(nonce[:], linePacketBody[:24])
	linePacketData := linePacketBody[24:]

	// decrypt the inner channel packet
	packet, success := secretbox.Open(packet, linePacketData, &nonce, lineDecryptionKey)
	if !success {
		err := fmt.Errorf("Error opening the secretbox")
		return packet, err
	}

	return packet, nil
}
<|endoftext|>


<commit_before>package csrf

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"

	"github.com/gorilla/context"
	"github.com/gorilla/securecookie"
)

// CSRF token length in bytes.
const tokenLength = 32

// Context/session keys & prefixes
const (
	tokenKey    string = "gorilla.csrf.Token"
	errorKey    string = "gorilla.csrf.Error"
	cookieName  string = "_gorilla_csrf"
	errorPrefix string = "gorilla/csrf: "
)

var (
	// The name value used in form fields.
	fieldName = tokenKey
	// The default HTTP request header to inspect
	headerName = "X-CSRF-Token"
	// Idempotent (safe) methods as defined by RFC7231 section 4.2.2.
	safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"}
)

// TemplateTag provides a default template tag - e.g. {{ .csrfField }} - for use
// with the TemplateField function.
var TemplateTag = "csrfField"

var (
	// ErrNoReferer is returned when a HTTPS request provides an empty Referer
	// header.
	ErrNoReferer = errors.New("referer not supplied")
	// ErrBadReferer is returned when the scheme & host in the URL do not match
	// the supplied Referer header.
	ErrBadReferer = errors.New("referer invalid")
	// ErrNoToken is returned if no CSRF token is supplied in the request.
	ErrNoToken = errors.New("CSRF token not found in request")
	// ErrBadToken is returned if the CSRF token in the request does not match
	// the token in the session, or is otherwise malformed.
	ErrBadToken = errors.New("CSRF token invalid")
)

type csrf struct {
	h     http.Handler
	sc    *securecookie.SecureCookie
	store Store
	opts  options
}

// options contains the optional settings for the CSRF middleware.
type options struct {
	MaxAge int
	Domain string
	Path   string
	// Note that the function and field names match the case of the associated
	// http.Cookie field instead of the "correct" HTTPOnly name that golint suggests.
	HttpOnly      bool
	Secure        bool
	RequestHeader string
	FieldName     string
	ErrorHandler  http.Handler
}

// Protect is HTTP middleware that provides Cross-Site Request Forgery
// protection.
//
// It securely generates a masked (unique-per-request) token that
// can be embedded in the HTTP response (e.g. form field or HTTP header).
// The original (unmasked) token is stored in the session, which is inaccessible
// by an attacker (provided you are using HTTPS). Subsequent requests are
// expected to include this token, which is compared against the session token.
// Requests that do not provide a matching token are served with a HTTP 403
// 'Forbidden' error response.
//
// Example:
//	package main
//
//	import (
//		"github.com/elithrar/protect"
//		"github.com/gorilla/mux"
//	)
//
//	func main() {
//	  r := mux.NewRouter()
//
//	  mux.HandlerFunc("/signup", GetSignupForm)
//	  // POST requests without a valid token will return a HTTP 403 Forbidden.
//	  mux.HandlerFunc("/signup/post", PostSignupForm)
//
//	  // Add the middleware to your router.
//	  http.ListenAndServe(":8000",
//			  csrf.Protect([]byte("32-byte-long-auth-key"))(r))
//	}
//
//	func GetSignupForm(w http.ResponseWriter, r *http.Request) {
//		// signup_form.tmpl just needs a {{ .csrfField }} template tag for
//		// csrf.TemplateField to inject the CSRF token into. Easy!
//		t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{
//			csrf.TemplateTag: csrf.TemplateField(r),
//		})
//		// We could also retrieve the token directly from csrf.Token(r) and
//		// set it in the request header - w.Header.Set("X-CSRF-Token", token)
//		// This is useful if your sending JSON to clients or a front-end JavaScript
//		// framework.
//	}
//
func Protect(authKey []byte, opts ...func(*csrf)) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		cs := parseOptions(h, opts...)

		// Set the defaults if no options have been specified
		if cs.opts.ErrorHandler == nil {
			cs.opts.ErrorHandler = http.HandlerFunc(unauthorizedHandler)
		}

		if cs.opts.MaxAge < 1 {
			// Default of 12 hours
			cs.opts.MaxAge = 3600 * 12
		}

		if cs.opts.FieldName == "" {
			cs.opts.FieldName = fieldName
		}

		if cs.opts.RequestHeader == "" {
			cs.opts.RequestHeader = headerName
		}

		// Create an authenticated securecookie instance.
		if cs.sc == nil {
			cs.sc = securecookie.New(authKey, nil)
			// Set the MaxAge of the underlying securecookie.
			cs.sc.MaxAge(cs.opts.MaxAge)
		}

		if cs.store == nil {
			// Default to the CookieStore
			cs.store = &CookieStore{
				name:   cookieName,
				maxAge: cs.opts.MaxAge,
				sc:     cs.sc,
			}
		}

		return *cs
	}
}

// Implements http.Handler for the csrf type.
func (cs csrf) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Retrieve the token from the session.
	// An error represents either a cookie that failed HMAC validation
	// or that doesn't exist.
	realToken, err := cs.store.Get(r)
	if err != nil || len(realToken) != tokenLength {
		// If there was an error retrieving the token, the token doesn't exist
		// yet, or it's the wrong length, generate a new token.
		// Note that the new token will (correctly) fail validation downstream
		// as it will no longer match the request token.
		realToken, err = generateRandomBytes(tokenLength)
		if err != nil {
			envError(r, err)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

		// Save the new (real) token in the session store.
		err = cs.store.Save(realToken, w)
		if err != nil {
			envError(r, err)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}
	}

	// Save the masked token to the request context
	context.Set(r, tokenKey, mask(realToken, r))

	// HTTP methods not defined as idempotent ("safe") under RFC7231 require
	// inspection.
	if !contains(safeMethods, r.Method) {
		// Enforce an origin check for HTTPS connections. As per the Django CSRF
		// implementation (https://goo.gl/vKA7GE) the Referer header is almost
		// always present for same-domain HTTP requests.
		if r.URL.Scheme == "https" {
			// Fetch the Referer value. Call the error handler if it's empty or
			// otherwise fails to parse.
			referer, err := url.Parse(r.Referer())
			if err != nil || referer.String() == "" {
				envError(r, ErrNoReferer)
				cs.opts.ErrorHandler.ServeHTTP(w, r)
				return
			}

			if sameOrigin(r.URL, referer) == false {
				envError(r, ErrBadReferer)
				cs.opts.ErrorHandler.ServeHTTP(w, r)
				return
			}
		}

		// If the token returned from the session store is nil for non-idempotent
		// ("unsafe") methods, call the error handler.
		if realToken == nil {
			envError(r, ErrNoToken)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

		// Retrieve the combined token (pad + masked) token and unmask it.
		requestToken := unmask(cs.requestToken(r))

		// Compare the request token against the real token
		if !compareTokens(requestToken, realToken) {
			envError(r, ErrBadToken)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

	}

	// Set the Vary: Cookie header to protect clients from caching the response.
	w.Header().Add("Vary", "Cookie")

	// Call the wrapped handler/router on success.
	cs.h.ServeHTTP(w, r)
	// Clear the request context after the handler has completed.
	context.Clear(r)
}

// unauthorizedhandler sets a HTTP 403 Forbidden status and writes the
// CSRF failure reason to the response.
func unauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("%s - %s",
		http.StatusText(http.StatusForbidden), FailureReason(r)),
		http.StatusForbidden)
	return
}
<commit_msg>Use pointer receiver for ServeHTTP method to avoid copies.<commit_after>package csrf

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"

	"github.com/gorilla/context"
	"github.com/gorilla/securecookie"
)

// CSRF token length in bytes.
const tokenLength = 32

// Context/session keys & prefixes
const (
	tokenKey    string = "gorilla.csrf.Token"
	errorKey    string = "gorilla.csrf.Error"
	cookieName  string = "_gorilla_csrf"
	errorPrefix string = "gorilla/csrf: "
)

var (
	// The name value used in form fields.
	fieldName = tokenKey
	// The default HTTP request header to inspect
	headerName = "X-CSRF-Token"
	// Idempotent (safe) methods as defined by RFC7231 section 4.2.2.
	safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"}
)

// TemplateTag provides a default template tag - e.g. {{ .csrfField }} - for use
// with the TemplateField function.
var TemplateTag = "csrfField"

var (
	// ErrNoReferer is returned when a HTTPS request provides an empty Referer
	// header.
	ErrNoReferer = errors.New("referer not supplied")
	// ErrBadReferer is returned when the scheme & host in the URL do not match
	// the supplied Referer header.
	ErrBadReferer = errors.New("referer invalid")
	// ErrNoToken is returned if no CSRF token is supplied in the request.
	ErrNoToken = errors.New("CSRF token not found in request")
	// ErrBadToken is returned if the CSRF token in the request does not match
	// the token in the session, or is otherwise malformed.
	ErrBadToken = errors.New("CSRF token invalid")
)

type csrf struct {
	h     http.Handler
	sc    *securecookie.SecureCookie
	store Store
	opts  options
}

// options contains the optional settings for the CSRF middleware.
type options struct {
	MaxAge int
	Domain string
	Path   string
	// Note that the function and field names match the case of the associated
	// http.Cookie field instead of the "correct" HTTPOnly name that golint suggests.
	HttpOnly      bool
	Secure        bool
	RequestHeader string
	FieldName     string
	ErrorHandler  http.Handler
}

// Protect is HTTP middleware that provides Cross-Site Request Forgery
// protection.
//
// It securely generates a masked (unique-per-request) token that
// can be embedded in the HTTP response (e.g. form field or HTTP header).
// The original (unmasked) token is stored in the session, which is inaccessible
// by an attacker (provided you are using HTTPS). Subsequent requests are
// expected to include this token, which is compared against the session token.
// Requests that do not provide a matching token are served with a HTTP 403
// 'Forbidden' error response.
//
// Example:
//	package main
//
//	import (
//		"github.com/elithrar/protect"
//		"github.com/gorilla/mux"
//	)
//
//	func main() {
//	  r := mux.NewRouter()
//
//	  mux.HandlerFunc("/signup", GetSignupForm)
//	  // POST requests without a valid token will return a HTTP 403 Forbidden.
//	  mux.HandlerFunc("/signup/post", PostSignupForm)
//
//	  // Add the middleware to your router.
//	  http.ListenAndServe(":8000",
//			  csrf.Protect([]byte("32-byte-long-auth-key"))(r))
//	}
//
//	func GetSignupForm(w http.ResponseWriter, r *http.Request) {
//		// signup_form.tmpl just needs a {{ .csrfField }} template tag for
//		// csrf.TemplateField to inject the CSRF token into. Easy!
//		t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{
//			csrf.TemplateTag: csrf.TemplateField(r),
//		})
//		// We could also retrieve the token directly from csrf.Token(r) and
//		// set it in the request header - w.Header.Set("X-CSRF-Token", token)
//		// This is useful if your sending JSON to clients or a front-end JavaScript
//		// framework.
//	}
//
func Protect(authKey []byte, opts ...func(*csrf)) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		cs := parseOptions(h, opts...)

		// Set the defaults if no options have been specified
		if cs.opts.ErrorHandler == nil {
			cs.opts.ErrorHandler = http.HandlerFunc(unauthorizedHandler)
		}

		if cs.opts.MaxAge < 1 {
			// Default of 12 hours
			cs.opts.MaxAge = 3600 * 12
		}

		if cs.opts.FieldName == "" {
			cs.opts.FieldName = fieldName
		}

		if cs.opts.RequestHeader == "" {
			cs.opts.RequestHeader = headerName
		}

		// Create an authenticated securecookie instance.
		if cs.sc == nil {
			cs.sc = securecookie.New(authKey, nil)
			// Set the MaxAge of the underlying securecookie.
			cs.sc.MaxAge(cs.opts.MaxAge)
		}

		if cs.store == nil {
			// Default to the CookieStore
			cs.store = &CookieStore{
				name:   cookieName,
				maxAge: cs.opts.MaxAge,
				sc:     cs.sc,
			}
		}

		return cs
	}
}

// Implements http.Handler for the csrf type.
func (cs *csrf) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Retrieve the token from the session.
	// An error represents either a cookie that failed HMAC validation
	// or that doesn't exist.
	realToken, err := cs.store.Get(r)
	if err != nil || len(realToken) != tokenLength {
		// If there was an error retrieving the token, the token doesn't exist
		// yet, or it's the wrong length, generate a new token.
		// Note that the new token will (correctly) fail validation downstream
		// as it will no longer match the request token.
		realToken, err = generateRandomBytes(tokenLength)
		if err != nil {
			envError(r, err)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

		// Save the new (real) token in the session store.
		err = cs.store.Save(realToken, w)
		if err != nil {
			envError(r, err)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}
	}

	// Save the masked token to the request context
	context.Set(r, tokenKey, mask(realToken, r))

	// HTTP methods not defined as idempotent ("safe") under RFC7231 require
	// inspection.
	if !contains(safeMethods, r.Method) {
		// Enforce an origin check for HTTPS connections. As per the Django CSRF
		// implementation (https://goo.gl/vKA7GE) the Referer header is almost
		// always present for same-domain HTTP requests.
		if r.URL.Scheme == "https" {
			// Fetch the Referer value. Call the error handler if it's empty or
			// otherwise fails to parse.
			referer, err := url.Parse(r.Referer())
			if err != nil || referer.String() == "" {
				envError(r, ErrNoReferer)
				cs.opts.ErrorHandler.ServeHTTP(w, r)
				return
			}

			if sameOrigin(r.URL, referer) == false {
				envError(r, ErrBadReferer)
				cs.opts.ErrorHandler.ServeHTTP(w, r)
				return
			}
		}

		// If the token returned from the session store is nil for non-idempotent
		// ("unsafe") methods, call the error handler.
		if realToken == nil {
			envError(r, ErrNoToken)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

		// Retrieve the combined token (pad + masked) token and unmask it.
		requestToken := unmask(cs.requestToken(r))

		// Compare the request token against the real token
		if !compareTokens(requestToken, realToken) {
			envError(r, ErrBadToken)
			cs.opts.ErrorHandler.ServeHTTP(w, r)
			return
		}

	}

	// Set the Vary: Cookie header to protect clients from caching the response.
	w.Header().Add("Vary", "Cookie")

	// Call the wrapped handler/router on success.
	cs.h.ServeHTTP(w, r)
	// Clear the request context after the handler has completed.
	context.Clear(r)
}

// unauthorizedhandler sets a HTTP 403 Forbidden status and writes the
// CSRF failure reason to the response.
func unauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("%s - %s",
		http.StatusText(http.StatusForbidden), FailureReason(r)),
		http.StatusForbidden)
	return
}
<|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import (
	"fmt"
)

// DataClient represents the RPC connection to the Data service.
type DataClient struct {
	*Client
}

// NewDataClient returns a new Data Client.
func NewDataClient() *DataClient {
	var service_ DataClient
	service_.Client = new(Client)
	return &service_
}

type GetNodeDataArgs struct {
	Site, Path, File string
}

// GetNodeData requests data from some node.
//
// If the data does not exist, return null length []byte.
func (s *DataClient) GetNodeData(site, path, file string) ([]byte, error) {
	args := &GetNodeDataArgs{site, path, file}
	var reply []byte
	err := s.RPCClient.Call("Data.GetNodeData", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("data: GetNodeData error:", err)
	}
	return reply, nil
}

type WriteNodeDataArgs struct {
	Site, Path, File, Content string
}

// WriteNodeData writes data for some node.
func (s *DataClient) WriteNodeData(site, path, file, content string) error {
	args := &WriteNodeDataArgs{site, path, file, content}
	if err := s.RPCClient.Call("Data.WriteNodeData", args, new(int)); err != nil {
		return fmt.Errorf("data: WriteNodeData error:", err)
	}
	return nil
}

type UpdateNodeArgs struct {
	Site string
	Node NodeInfo
}

// UpdateNode saves changes to given node.
func (s *DataClient) UpdateNode(site string, node_ NodeInfo) error {
	args := &UpdateNodeArgs{site, node_}
	if err := s.RPCClient.Call("Data.UpdateNode", args, new(int)); err != nil {
		return fmt.Errorf("data: WriteNodeData error:", err)
	}
	return nil
}
<commit_msg>Change function description.<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import (
	"fmt"
)

// DataClient represents the RPC connection to the Data service.
type DataClient struct {
	*Client
}

// NewDataClient returns a new Data Client.
func NewDataClient() *DataClient {
	var service_ DataClient
	service_.Client = new(Client)
	return &service_
}

type GetNodeDataArgs struct {
	Site, Path, File string
}

// GetNodeData requests data from some node.
//
// Returns an error if the data does not exist.
func (s *DataClient) GetNodeData(site, path, file string) ([]byte, error) {
	args := &GetNodeDataArgs{site, path, file}
	var reply []byte
	err := s.RPCClient.Call("Data.GetNodeData", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("data: GetNodeData error:", err)
	}
	return reply, nil
}

type WriteNodeDataArgs struct {
	Site, Path, File, Content string
}

// WriteNodeData writes data for some node.
func (s *DataClient) WriteNodeData(site, path, file, content string) error {
	args := &WriteNodeDataArgs{site, path, file, content}
	if err := s.RPCClient.Call("Data.WriteNodeData", args, new(int)); err != nil {
		return fmt.Errorf("data: WriteNodeData error:", err)
	}
	return nil
}

type UpdateNodeArgs struct {
	Site string
	Node NodeInfo
}

// UpdateNode saves changes to given node.
func (s *DataClient) UpdateNode(site string, node_ NodeInfo) error {
	args := &UpdateNodeArgs{site, node_}
	if err := s.RPCClient.Call("Data.UpdateNode", args, new(int)); err != nil {
		return fmt.Errorf("data: WriteNodeData error:", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package dmr

import (
	"bytes"
	"errors"
	"fmt"

	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/encoding/unicode"
)

const (
	// n_DFragMax, see DMR AI spec. page 163.
	MaxPacketFragmentSize = 1500
)

type DataBlock struct {
	Serial uint8
	CRC    uint16
	OK     bool
	Data   []byte
	Length uint8
}

func ParseDataBlock(data []byte, dataType uint8, confirmed bool) (*DataBlock, error) {
	var (
		crc uint16
		db  = &DataBlock{
			Length: dataBlockLength(dataType, confirmed),
		}
	)

	if confirmed {
		db.Serial = data[0] >> 1
		db.CRC = uint16(data[0]&B00000001)<<8 | uint16(data[1])
		db.Data = make([]byte, db.Length)
		copy(db.Data, data[2:2+db.Length])

		for _, block := range db.Data {
			crc9(&crc, block, 8)
		}
		crc9(&crc, db.Serial, 7)
		crc9end(&crc, 8)

		// Inverting according to the inversion polynomial.
		crc = ^crc
		crc &= 0x01ff
		// Applying CRC mask, see DMR AI spec. page 143
		crc ^= 0x01ff

		// FIXME(pd0mz): this is not working
		if crc != db.CRC {
			return nil, fmt.Errorf("dmr: block CRC error (%#04x != %#04x)", crc, db.CRC)
		}
	} else {
		db.Data = make([]byte, db.Length)
		copy(db.Data, data[:db.Length])
	}

	db.OK = true
	return db, nil
}

func (db *DataBlock) Bytes(dataType uint8, confirmed bool) []byte {
	var (
		size = dataBlockLength(dataType, confirmed)
		data = make([]byte, size)
	)

	if confirmed {
		for _, block := range db.Data {
			crc9(&db.CRC, block, 8)
		}
		crc9(&db.CRC, db.Serial, 7)
		crc9end(&db.CRC, 8)

		// Inverting according to the inversion polynomial.
		db.CRC = ^db.CRC
		db.CRC &= 0x01ff
		// Applying CRC mask, see DMR AI spec. page 143
		db.CRC ^= 0x01ff

		// Grow data slice to support the two byte prefix
		data = append(data, make([]byte, 2)...)

		data[0] = (db.Serial << 1) | (uint8(db.CRC>>8) & 0x01)
		data[1] = uint8(db.CRC)
		copy(data[2:], db.Data)
	} else {
		copy(data, db.Data)
	}

	return data
}

func dataBlockLength(dataType uint8, confirmed bool) uint8 {
	var size uint8

	switch dataType {
	case Data:
		size = 22
		break
	case Rate12Data:
		size = 10
		break
	case Rate34Data:
		size = 16
		break
	default:
		return 0
	}

	if !confirmed {
		return size + 2
	}
	return size
}

type DataFragment struct {
	Data   []byte
	Stored int
	Needed int
	CRC    uint32
}

func (df *DataFragment) DataBlocks(dataType uint8, confirm bool) ([]*DataBlock, error) {
	df.Stored = len(df.Data)
	if df.Stored > MaxPacketFragmentSize {
		df.Stored = MaxPacketFragmentSize
	}

	// See DMR AI spec. page. 73. for block sizes.
	var size = int(dataBlockLength(dataType, confirm))
	df.Needed = (df.Stored + size - 1) / size

	// Leave enough room for the 4 bytes CRC32
	if (df.Needed*size)-df.Stored < 4 {
		df.Needed++
	}

	// Calculate fragment CRC32
	for i := 0; i < (df.Needed*size)-4; i += 2 {
		if i+1 < df.Stored {
			crc32(&df.CRC, df.Data[i+1])
		} else {
			crc32(&df.CRC, 0)
		}
		if i < df.Stored {
			crc32(&df.CRC, df.Data[i])
		} else {
			crc32(&df.CRC, 0)
		}
	}
	crc32end(&df.CRC)

	var (
		blocks = make([]*DataBlock, df.Needed)
		stored int
	)
	for i := range blocks {
		block := &DataBlock{
			Serial: uint8(i % 128),
			Length: dataBlockLength(dataType, confirm),
		}
		block.Data = make([]byte, block.Length)

		store := int(block.Length)
		if df.Stored-stored < store {
			store = df.Stored - stored
		}
		copy(block.Data, df.Data[stored:stored+store])
		stored += store

		if i == (df.Needed - 1) {
			block.Data[block.Length-1] = uint8(df.CRC >> 24)
			block.Data[block.Length-2] = uint8(df.CRC >> 16)
			block.Data[block.Length-3] = uint8(df.CRC >> 8)
			block.Data[block.Length-4] = uint8(df.CRC)
		}

		// Calculate block CRC9
		block.CRC = 0
		for _, b := range block.Data {
			crc9(&block.CRC, b, 8)
		}
		crc9(&block.CRC, block.Serial, 7)
		crc9end(&block.CRC, 8)

		// Inverting according to the inversion polynomial
		block.CRC = ^block.CRC
		block.CRC &= 0x01ff
		// Applying CRC mask, see DMR AI spec. page 143
		block.CRC ^= 0x01ff

		blocks[i] = block
	}

	return blocks, nil
}

func CombineDataBlocks(blocks []*DataBlock) (*DataFragment, error) {
	if blocks == nil || len(blocks) == 0 {
		return nil, errors.New("dmr: no data blocks to combine")
	}

	f := &DataFragment{
		Data: make([]byte, MaxPacketFragmentSize),
	}
	for i, block := range blocks {
		if block.Length == 0 {
			continue
		}
		if i < (len(blocks) - 1) {
			if f.Stored+int(block.Length) < len(f.Data) {
				copy(f.Data[f.Stored:], block.Data[:block.Length])
				f.Stored += int(block.Length)
			}
		} else {
			if f.Stored+int(block.Length)-4 < len(f.Data) {
				copy(f.Data[f.Stored:], block.Data[:block.Length])
				f.Stored += int(block.Length)
			}
			f.CRC = 0
			f.CRC |= uint32(block.Data[block.Length-4])
			f.CRC |= uint32(block.Data[block.Length-3]) << 8
			f.CRC |= uint32(block.Data[block.Length-2]) << 16
			f.CRC |= uint32(block.Data[block.Length-1]) << 24
		}
	}

	var crc uint32
	for i := 0; i < f.Stored-4; i += 2 {
		crc32(&crc, f.Data[i+1])
		crc32(&crc, f.Data[i])
	}
	crc32end(&crc)

	if crc != f.CRC {
		return nil, fmt.Errorf("dmr: fragment CRC error (%#08x != %#08x)", crc, f.CRC)
	}
	return f, nil
}

var encodingMap map[uint8]encoding.Encoding

func BuildMessageData(msg string, ddFormat uint8, nullTerminated bool) ([]byte, error) {
	if e, ok := encodingMap[ddFormat]; ok {
		enc := e.NewEncoder()
		data, err := enc.Bytes([]byte(msg))
		if err != nil {
			return nil, err
		}
		if nullTerminated {
			data = append(data, []byte{0x00, 0x00}...)
		}
		return data, nil
	}
	return nil, fmt.Errorf("dmr: encoding %s text is not supported", DDFormatName[ddFormat])
}

func ParseMessageData(data []byte, ddFormat uint8, nullTerminated bool) (string, error) {
	if e, ok := encodingMap[ddFormat]; ok {
		dec := e.NewDecoder()
		str, err := dec.Bytes(data)
		if err != nil {
			return "", err
		}
		if nullTerminated {
			if idx := bytes.IndexByte(str, 0x00); idx >= 0 {
				str = str[:idx]
			}
		}
		return string(str), nil
	}
	return "", fmt.Errorf("dmr: decoding %s text is not supported", DDFormatName[ddFormat])
}

func init() {
	encodingMap = map[uint8]encoding.Encoding{
		DDFormatBinary:         binaryEncoding{},
		DDFormat8BitISO8859_2:  charmap.ISO8859_2,
		DDFormat8BitISO8859_3:  charmap.ISO8859_3,
		DDFormat8BitISO8859_4:  charmap.ISO8859_4,
		DDFormat8BitISO8859_5:  charmap.ISO8859_5,
		DDFormat8BitISO8859_6:  charmap.ISO8859_6,
		DDFormat8BitISO8859_7:  charmap.ISO8859_7,
		DDFormat8BitISO8859_8:  charmap.ISO8859_8,
		DDFormat8BitISO8859_10: charmap.ISO8859_10,
		DDFormat8BitISO8859_13: charmap.ISO8859_13,
		DDFormat8BitISO8859_14: charmap.ISO8859_14,
		DDFormat8BitISO8859_15: charmap.ISO8859_15,
		DDFormat8BitISO8859_16: charmap.ISO8859_16,
		DDFormatUTF8:           unicode.UTF8,
		DDFormatUTF16:          unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
		DDFormatUTF16BE:        unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM),
		DDFormatUTF16LE:        unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
	}
}
<commit_msg>Support Rate 1/2 Data CRC Mask<commit_after>package dmr

import (
	"bytes"
	"errors"
	"fmt"

	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/encoding/unicode"
)

const (
	// n_DFragMax, see DMR AI spec. page 163.
	MaxPacketFragmentSize = 1500
)

// CRC Masks for data block's CRC-9 calculation, see DMR AI spec. page 148 (Table B.21).
var crc9Masks = map[uint8]uint16{
	Rate12Data: 0x00f0,
	Rate34Data: 0x01ff,
	//Rate1Data: 0x010f,
}

func calculateCRC9(serial uint8, data []byte, dataType uint8) (crc uint16) {
	for _, block := range data {
		crc9(&crc, block, 8)
	}
	crc9(&crc, serial, 7)
	crc9end(&crc, 8)

	// Inverting according to the inversion polynomial.
	crc = ^crc
	crc &= 0x01ff

	// Applying Data Type CRC Mask
	crc ^= crc9Masks[dataType]

	return crc
}

type DataBlock struct {
	Serial uint8
	CRC    uint16
	OK     bool
	Data   []byte
	Length uint8
}

func ParseDataBlock(data []byte, dataType uint8, confirmed bool) (*DataBlock, error) {
	var (
		crc uint16
		db  = &DataBlock{
			Length: dataBlockLength(dataType, confirmed),
		}
	)

	if confirmed {
		db.Serial = data[0] >> 1
		db.CRC = uint16(data[0]&B00000001)<<8 | uint16(data[1])
		db.Data = make([]byte, db.Length)
		copy(db.Data, data[2:2+db.Length])

		crc = calculateCRC9(db.Serial, db.Data, dataType)

		// FIXME(pd0mz): this is not working
		if crc != db.CRC {
			return nil, fmt.Errorf("dmr: block CRC error (%#04x != %#04x)", crc, db.CRC)
		}
	} else {
		db.Data = make([]byte, db.Length)
		copy(db.Data, data[:db.Length])
	}

	db.OK = true
	return db, nil
}

func (db *DataBlock) Bytes(dataType uint8, confirmed bool) []byte {
	var (
		size = dataBlockLength(dataType, confirmed)
		data = make([]byte, size)
	)

	if confirmed {
		db.CRC = calculateCRC9(db.Serial, db.Data, dataType)

		// Grow data slice to support the two byte prefix
		data = append(data, make([]byte, 2)...)

		data[0] = (db.Serial << 1) | (uint8(db.CRC>>8) & 0x01)
		data[1] = uint8(db.CRC)
		copy(data[2:], db.Data)
	} else {
		copy(data, db.Data)
	}

	return data
}

func dataBlockLength(dataType uint8, confirmed bool) uint8 {
	var size uint8

	switch dataType {
	case Data:
		size = 22
		break
	case Rate12Data:
		size = 10
		break
	case Rate34Data:
		size = 16
		break
	default:
		return 0
	}

	if !confirmed {
		return size + 2
	}
	return size
}

type DataFragment struct {
	Data   []byte
	Stored int
	Needed int
	CRC    uint32
}

func (df *DataFragment) DataBlocks(dataType uint8, confirm bool) ([]*DataBlock, error) {
	df.Stored = len(df.Data)
	if df.Stored > MaxPacketFragmentSize {
		df.Stored = MaxPacketFragmentSize
	}

	// See DMR AI spec. page. 73. for block sizes.
	var size = int(dataBlockLength(dataType, confirm))
	df.Needed = (df.Stored + size - 1) / size

	// Leave enough room for the 4 bytes CRC32
	if (df.Needed*size)-df.Stored < 4 {
		df.Needed++
	}

	// Calculate fragment CRC32
	for i := 0; i < (df.Needed*size)-4; i += 2 {
		if i+1 < df.Stored {
			crc32(&df.CRC, df.Data[i+1])
		} else {
			crc32(&df.CRC, 0)
		}
		if i < df.Stored {
			crc32(&df.CRC, df.Data[i])
		} else {
			crc32(&df.CRC, 0)
		}
	}
	crc32end(&df.CRC)

	var (
		blocks = make([]*DataBlock, df.Needed)
		stored int
	)
	for i := range blocks {
		block := &DataBlock{
			Serial: uint8(i % 128),
			Length: dataBlockLength(dataType, confirm),
		}
		block.Data = make([]byte, block.Length)

		store := int(block.Length)
		if df.Stored-stored < store {
			store = df.Stored - stored
		}
		copy(block.Data, df.Data[stored:stored+store])
		stored += store

		if i == (df.Needed - 1) {
			block.Data[block.Length-1] = uint8(df.CRC >> 24)
			block.Data[block.Length-2] = uint8(df.CRC >> 16)
			block.Data[block.Length-3] = uint8(df.CRC >> 8)
			block.Data[block.Length-4] = uint8(df.CRC)
		}

		// Calculate block CRC9
		block.CRC = calculateCRC9(block.Serial, block.Data, dataType)

		blocks[i] = block
	}

	return blocks, nil
}

func CombineDataBlocks(blocks []*DataBlock) (*DataFragment, error) {
	if blocks == nil || len(blocks) == 0 {
		return nil, errors.New("dmr: no data blocks to combine")
	}

	f := &DataFragment{
		Data: make([]byte, MaxPacketFragmentSize),
	}
	for i, block := range blocks {
		if block.Length == 0 {
			continue
		}
		if i < (len(blocks) - 1) {
			if f.Stored+int(block.Length) < len(f.Data) {
				copy(f.Data[f.Stored:], block.Data[:block.Length])
				f.Stored += int(block.Length)
			}
		} else {
			if f.Stored+int(block.Length)-4 < len(f.Data) {
				copy(f.Data[f.Stored:], block.Data[:block.Length])
				f.Stored += int(block.Length)
			}
			f.CRC = 0
			f.CRC |= uint32(block.Data[block.Length-4])
			f.CRC |= uint32(block.Data[block.Length-3]) << 8
			f.CRC |= uint32(block.Data[block.Length-2]) << 16
			f.CRC |= uint32(block.Data[block.Length-1]) << 24
		}
	}

	var crc uint32
	for i := 0; i < f.Stored-4; i += 2 {
		crc32(&crc, f.Data[i+1])
		crc32(&crc, f.Data[i])
	}
	crc32end(&crc)

	if crc != f.CRC {
		return nil, fmt.Errorf("dmr: fragment CRC error (%#08x != %#08x)", crc, f.CRC)
	}
	return f, nil
}

var encodingMap map[uint8]encoding.Encoding

func BuildMessageData(msg string, ddFormat uint8, nullTerminated bool) ([]byte, error) {
	if e, ok := encodingMap[ddFormat]; ok {
		enc := e.NewEncoder()
		data, err := enc.Bytes([]byte(msg))
		if err != nil {
			return nil, err
		}
		if nullTerminated {
			data = append(data, []byte{0x00, 0x00}...)
		}
		return data, nil
	}
	return nil, fmt.Errorf("dmr: encoding %s text is not supported", DDFormatName[ddFormat])
}

func ParseMessageData(data []byte, ddFormat uint8, nullTerminated bool) (string, error) {
	if e, ok := encodingMap[ddFormat]; ok {
		dec := e.NewDecoder()
		str, err := dec.Bytes(data)
		if err != nil {
			return "", err
		}
		if nullTerminated {
			if idx := bytes.IndexByte(str, 0x00); idx >= 0 {
				str = str[:idx]
			}
		}
		return string(str), nil
	}
	return "", fmt.Errorf("dmr: decoding %s text is not supported", DDFormatName[ddFormat])
}

func init() {
	encodingMap = map[uint8]encoding.Encoding{
		DDFormatBinary:         binaryEncoding{},
		DDFormat8BitISO8859_2:  charmap.ISO8859_2,
		DDFormat8BitISO8859_3:  charmap.ISO8859_3,
		DDFormat8BitISO8859_4:  charmap.ISO8859_4,
		DDFormat8BitISO8859_5:  charmap.ISO8859_5,
		DDFormat8BitISO8859_6:  charmap.ISO8859_6,
		DDFormat8BitISO8859_7:  charmap.ISO8859_7,
		DDFormat8BitISO8859_8:  charmap.ISO8859_8,
		DDFormat8BitISO8859_10: charmap.ISO8859_10,
		DDFormat8BitISO8859_13: charmap.ISO8859_13,
		DDFormat8BitISO8859_14: charmap.ISO8859_14,
		DDFormat8BitISO8859_15: charmap.ISO8859_15,
		DDFormat8BitISO8859_16: charmap.ISO8859_16,
		DDFormatUTF8:           unicode.UTF8,
		DDFormatUTF16:          unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
		DDFormatUTF16BE:        unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM),
		DDFormatUTF16LE:        unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 Istio 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 galley

import (
	"fmt"
	"testing"

	kubeApiAdmission "k8s.io/api/admissionregistration/v1beta1"

	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/environment"
	"istio.io/istio/pkg/test/framework/components/environment/kube"
	"istio.io/istio/pkg/test/util/retry"
)

const (
	vwcName = "istiod-istio-system"
)

func TestWebhook(t *testing.T) {
	framework.NewTest(t).
		// Limit to Kube environment as we're testing integration of webhook with K8s.
		RequiresEnvironment(environment.Kube).
		Run(func(ctx framework.TestContext) {
			env := ctx.Environment().(*kube.Environment)

			// clear the updated fields and verify istiod updates them

			retry.UntilSuccessOrFail(t, func() error {
				got, err := env.GetValidatingWebhookConfiguration(vwcName)
				if err != nil {
					return fmt.Errorf("error getting initial webhook: %v", err)
				}
				if err := verifyValidatingWebhookConfiguration(got); err != nil {
					return err
				}

				updated := got.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration)
				updated.Webhooks[0].ClientConfig.CABundle = nil
				ignore := kubeApiAdmission.Ignore // can't take the address of a constant
				updated.Webhooks[0].FailurePolicy = &ignore

				return env.UpdateValidatingWebhookConfiguration(updated)
			})

			retry.UntilSuccessOrFail(t, func() error {
				got, err := env.GetValidatingWebhookConfiguration(vwcName)
				if err != nil {
					t.Fatalf("error getting initial webhook: %v", err)
				}
				if err := verifyValidatingWebhookConfiguration(got); err != nil {
					return fmt.Errorf("validatingwebhookconfiguration not updated yet: %v", err)
				}
				return nil
			})
		})
}

func verifyValidatingWebhookConfiguration(c *kubeApiAdmission.ValidatingWebhookConfiguration) error {
	if len(c.Webhooks) != 1 {
		return fmt.Errorf("only one webhook expected. Found %v", len(c.Webhooks))
	}
	wh := c.Webhooks[0]
	if *wh.FailurePolicy != kubeApiAdmission.Fail {
		return fmt.Errorf("wrong failure policy. c %v wanted %v", *wh.FailurePolicy, kubeApiAdmission.Fail)
	}
	if len(wh.ClientConfig.CABundle) == 0 {
		return fmt.Errorf("caBundle not matched")
	}
	return nil
}
<commit_msg>fix webhook_test.go in postsubmit (#21349)<commit_after>// Copyright 2019 Istio 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 galley

import (
	"errors"
	"fmt"
	"testing"

	kubeApiAdmission "k8s.io/api/admissionregistration/v1beta1"

	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/environment"
	"istio.io/istio/pkg/test/framework/components/environment/kube"
	"istio.io/istio/pkg/test/util/retry"
)

const (
	vwcName = "istiod-istio-system"
)

func TestWebhook(t *testing.T) {
	framework.NewTest(t).
		// Limit to Kube environment as we're testing integration of webhook with K8s.
		RequiresEnvironment(environment.Kube).
		Run(func(ctx framework.TestContext) {
			env := ctx.Environment().(*kube.Environment)

			// clear the updated fields and verify istiod updates them

			retry.UntilSuccessOrFail(t, func() error {
				got, err := env.GetValidatingWebhookConfiguration(vwcName)
				if err != nil {
					return fmt.Errorf("error getting initial webhook: %v", err)
				}
				if err := verifyValidatingWebhookConfiguration(got); err != nil {
					return err
				}

				updated := got.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration)
				updated.Webhooks[0].ClientConfig.CABundle = nil
				ignore := kubeApiAdmission.Ignore // can't take the address of a constant
				updated.Webhooks[0].FailurePolicy = &ignore

				return env.UpdateValidatingWebhookConfiguration(updated)
			})

			retry.UntilSuccessOrFail(t, func() error {
				got, err := env.GetValidatingWebhookConfiguration(vwcName)
				if err != nil {
					t.Fatalf("error getting initial webhook: %v", err)
				}
				if err := verifyValidatingWebhookConfiguration(got); err != nil {
					return fmt.Errorf("validatingwebhookconfiguration not updated yet: %v", err)
				}
				return nil
			})
		})
}

func verifyValidatingWebhookConfiguration(c *kubeApiAdmission.ValidatingWebhookConfiguration) error {
	if len(c.Webhooks) == 0 {
		return errors.New("no webhook entries found")
	}
	for i, wh := range c.Webhooks {
		if *wh.FailurePolicy != kubeApiAdmission.Fail {
			return fmt.Errorf("webhook #%v: wrong failure policy. c %v wanted %v",
				i, *wh.FailurePolicy, kubeApiAdmission.Fail)
		}
		if len(wh.ClientConfig.CABundle) == 0 {
			return fmt.Errorf("webhook #%v: caBundle not matched", i)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2019 The Kubernetes 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 preflight

import (
	"syscall"

	"github.com/pkg/errors"
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
	"k8s.io/utils/exec"
)

// Check validates if Docker is setup to use systemd as the cgroup driver.
func (idsc IsDockerSystemdCheck) Check() (warnings, errorList []error) {
	driver, err := kubeadmutil.GetCgroupDriverDocker(exec.New())
	if err != nil {
		return nil, []error{err}
	}
	if driver != kubeadmutil.CgroupDriverSystemd {
		err = errors.Errorf("detected %q as the Docker cgroup driver. "+
			"The recommended driver is %q. "+
			"Please follow the guide at https://kubernetes.io/docs/setup/cri/",
			driver,
			kubeadmutil.CgroupDriverSystemd)
		return []error{err}, nil
	}
	return nil, nil
}

// Check number of memory required by kubeadm
func (mc MemCheck) Check() (warnings, errorList []error) {
	info := syscall.Sysinfo_t{}
	err := syscall.Sysinfo(&info)
	if err != nil {
		errorList = append(errorList, errors.Wrapf(err, "failed to get system info"))
	}

	// Totalram returns bytes; convert to MB
	actual := uint64(info.Totalram) / 1024 / 1024
	if actual < mc.Mem {
		errorList = append(errorList, errors.Errorf("the system RAM (%d MB) is less than the minimum %d MB", actual, mc.Mem))
	}
	return warnings, errorList
}
<commit_msg>32bit > 4GB integer overflow edge case fix<commit_after>// +build linux

/*
Copyright 2019 The Kubernetes 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 preflight

import (
	"syscall"

	"github.com/pkg/errors"
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
	"k8s.io/utils/exec"
)

// Check validates if Docker is setup to use systemd as the cgroup driver.
func (idsc IsDockerSystemdCheck) Check() (warnings, errorList []error) {
	driver, err := kubeadmutil.GetCgroupDriverDocker(exec.New())
	if err != nil {
		return nil, []error{err}
	}
	if driver != kubeadmutil.CgroupDriverSystemd {
		err = errors.Errorf("detected %q as the Docker cgroup driver. "+
			"The recommended driver is %q. "+
			"Please follow the guide at https://kubernetes.io/docs/setup/cri/",
			driver,
			kubeadmutil.CgroupDriverSystemd)
		return []error{err}, nil
	}
	return nil, nil
}

// Check number of memory required by kubeadm
func (mc MemCheck) Check() (warnings, errorList []error) {
	info := syscall.Sysinfo_t{}
	err := syscall.Sysinfo(&info)
	if err != nil {
		errorList = append(errorList, errors.Wrapf(err, "failed to get system info"))
	}

	// Totalram holds the total usable memory. Unit holds the size of a memory unit in bytes. Multiply them and convert to MB
	actual := uint64(info.Totalram) * uint64(info.Unit) / 1024 / 1024
	if actual < mc.Mem {
		errorList = append(errorList, errors.Errorf("the system RAM (%d MB) is less than the minimum %d MB", actual, mc.Mem))
	}
	return warnings, errorList
}
<|endoftext|>