<commit_before>package main

// This utility moves a network interface into a network namespace and
// configures it. The configuration is passed in as a JSON object
// (marshalled prot.NetworkAdapter).  It is necessary to implement
// this as a separate utility as in Go one does not have tight control
// over which OS thread a given Go thread/routing executes but as can
// only enter a namespace with a specific OS thread.
//
// Note, this logs to stdout so that the caller (gcs) can log the
// output itself.

import (
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/Microsoft/opengcs/service/gcs/prot"
	"github.com/Microsoft/opengcs/service/gcsutils/gcstools/commoncli"
	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

func netnsConfigMain() {
	if err := netnsConfig(); err != nil {
		log.Errorf("netnsConfig returned: %s", err)
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(-1)
	}
	log.Info("netnsConfig succeeded")
	os.Exit(0)
}

func netnsConfig() error {
	ifStr := flag.String("if", "", "Interface/Adapter to move/configure")
	nspid := flag.Int("nspid", -1, "Process ID (to locate netns")
	cfgStr := flag.String("cfg", "", "Adapter configuration (json)")
	logArgs := commoncli.SetFlagsForLogging()

	flag.Parse()
	if err := commoncli.SetupLogging(logArgs...); err != nil {
		return err
	}
	if *ifStr == "" || *nspid == -1 || *cfgStr == "" {
		return fmt.Errorf("All three arguments must be specified")
	}

	var a prot.NetworkAdapter
	if err := json.Unmarshal([]byte(*cfgStr), &a); err != nil {
		return err
	}

	if a.NatEnabled {
		log.Infof("Configure %s in %d with: %s/%d gw=%s", *ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
	} else {
		log.Infof("Configure %s in %d with DHCP", *ifStr, *nspid)
	}

	// Lock the OS Thread so we don't accidentally switch namespaces
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// Stash current network namespace away and make sure we enter it as we leave
	log.Infof("Obtaining current namespace")
	origNS, err := netns.Get()
	if err != nil {
		return fmt.Errorf("netns.Get() failed: %v", err)
	}
	defer origNS.Close()
	log.Infof("Original namespace %v", origNS)

	// Get a reference to the new network namespace
	ns, err := netns.GetFromPid(*nspid)
	if err != nil {
		return fmt.Errorf("netns.GetFromPid(%d) failed: %v", *nspid, err)
	}
	defer ns.Close()
	log.Infof("New network namespace from PID %d is %v", *nspid, ns)

	// Get a reference to the interface and make sure it's down
	link, err := netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}
	if err := netlink.LinkSetDown(link); err != nil {
		return fmt.Errorf("netlink.LinkSetDown(%#v) failed: %v", link, err)
	}

	// Move the interface to the new network namespace
	if err := netlink.LinkSetNsPid(link, *nspid); err != nil {
		return fmt.Errorf("netlink.SetNsPid(%#v, %d) failed: %v", link, *nspid, err)
	}

	log.Infof("Switching from %v to %v", origNS, ns)

	// Enter the new network namespace
	if err := netns.Set(ns); err != nil {
		return fmt.Errorf("netns.Set() failed: %v", err)
	}

	// Re-Get a reference to the interface (it may be a different ID in the new namespace)
	log.Infof("Getting reference to interface")
	link, err = netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}

	// User requested non-default MTU size
	if a.EncapOverhead != 0 {
		log.Info("EncapOverhead non-zero, will set MTU")
		mtu := link.Attrs().MTU - int(a.EncapOverhead)
		log.Infof("mtu %d", mtu)
		if err = netlink.LinkSetMTU(link, mtu); err != nil {
			return fmt.Errorf("netlink.LinkSetMTU(%#v, %d) failed: %v", link, mtu, err)
		}
	}

	// Configure the interface
	if a.NatEnabled {
		log.Info("Nat enabled - configuring interface")
		metric := 1
		if a.EnableLowMetric {
			metric = 500
		}

		// Bring the interface up
		if err := netlink.LinkSetUp(link); err != nil {
			return fmt.Errorf("netlink.LinkSetUp(%#v) failed: %v", link, err)
		}
		// Set IP address
		addr := &net.IPNet{
			IP: net.ParseIP(a.AllocatedIPAddress),
			// TODO(rn): This assumes/hardcodes IPv4
			Mask: net.CIDRMask(int(a.HostIPPrefixLength), 32)}
		ipAddr := &netlink.Addr{IPNet: addr, Label: ""}
		if err := netlink.AddrAdd(link, ipAddr); err != nil {
			return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr, err)
		}
		// Set gateway
		if a.HostIPAddress != "" {
			gw := net.ParseIP(a.HostIPAddress)

			if !addr.Contains(gw) {
				// In the case that a gw is not part of the subnet we are setting gw for,
				// a new addr containing this gw address need to be added into the link to avoid getting
				// unreachable error when adding this out-of-subnet gw route
				log.Infof("gw is outside of the subnet: Configure %s in %d with: %s/%d gw=%s\n",
					*ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
				addr2 := &net.IPNet{
					IP:   net.ParseIP(a.HostIPAddress),
					Mask: net.CIDRMask(32, 32)} // This assumes/hardcodes IPv4
				ipAddr2 := &netlink.Addr{IPNet: addr2, Label: ""}
				if err := netlink.AddrAdd(link, ipAddr2); err != nil {
					return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr2, err)
				}
			}

			if !a.EnableLowMetric {
				route := netlink.Route{
					Scope:     netlink.SCOPE_UNIVERSE,
					LinkIndex: link.Attrs().Index,
					Gw:        gw,
					Priority:  metric, // This is what ip route add does
				}
				if err := netlink.RouteAdd(&route); err != nil {
					return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
				}
			} else {
				// add a route rule for the new interface so packets coming on this interface
				// always go out the same interface
				srcNet := &net.IPNet{IP: net.ParseIP(a.HostIPAddress), Mask: net.CIDRMask(0, 32)}
				rule := netlink.NewRule()
				rule.Table = 101
				rule.Src = srcNet
				rule.Priority = 5

				if err := netlink.RuleAdd(rule); err != nil {
					return fmt.Errorf("netlink.RuleAdd(%#v) failed: %v", rule, err)
				}

				// add the default route in that interface specific table
				route := netlink.Route{
					Scope:     netlink.SCOPE_UNIVERSE,
					LinkIndex: link.Attrs().Index,
					Gw:        gw,
					Table:     rule.Table,
					Priority:  metric,
				}
				if err := netlink.RouteAdd(&route); err != nil {
					return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
				}

			}
		}
	} else {
		log.Infof("Execing udhcpc with timeout...")
		cmd := exec.Command("udhcpc", "-q", "-i", *ifStr, "-s", "/sbin/udhcpc_config.script")

		done := make(chan error)
		go func() {
			done <- cmd.Wait()
		}()
		defer close(done)

		select {
		case <-time.After(30 * time.Second):
			var cos string
			co, err := cmd.CombinedOutput() // In case it has written something
			if err != nil {
				cos = string(co)
			}
			cmd.Process.Kill()
			log.Infof("udhcpc timed out [%s]", cos)
			return fmt.Errorf("udhcpc timed out. Failed to get DHCP address: %s", cos)
		case err := <-done:
			var cos string
			co, err := cmd.CombinedOutput() // Something should be on stderr
			if err != nil {
				cos = string(co)
			}
			if err != nil {
				log.Infof("udhcpc failed %s [%s]", err, cos)
				return fmt.Errorf("process failed: %s (%s)", err, cos)
			}
		}
		var cos string
		co, err := cmd.CombinedOutput()
		if err != nil {
			cos = string(co)
		}
		log.Debugf("udhcpc succeeded: %s", cos)
	}

	// Add some debug logging
	curNS, _ := netns.Get()
	// Refresh link attributes/state
	link, _ = netlink.LinkByIndex(link.Attrs().Index)
	attr := link.Attrs()
	addrs, _ := netlink.AddrList(link, 0)
	log.Infof("%v: %s[idx=%d,type=%s] is %v", curNS, attr.Name, attr.Index, link.Type(), attr.OperState)
	for _, addr := range addrs {
		log.Infof("  %v", addr)
	}

	return nil
}
<commit_msg>Correctly setting the table rule to include only packets from the interface<commit_after>package main

// This utility moves a network interface into a network namespace and
// configures it. The configuration is passed in as a JSON object
// (marshalled prot.NetworkAdapter).  It is necessary to implement
// this as a separate utility as in Go one does not have tight control
// over which OS thread a given Go thread/routing executes but as can
// only enter a namespace with a specific OS thread.
//
// Note, this logs to stdout so that the caller (gcs) can log the
// output itself.

import (
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/Microsoft/opengcs/service/gcs/prot"
	"github.com/Microsoft/opengcs/service/gcsutils/gcstools/commoncli"
	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

func netnsConfigMain() {
	if err := netnsConfig(); err != nil {
		log.Errorf("netnsConfig returned: %s", err)
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(-1)
	}
	log.Info("netnsConfig succeeded")
	os.Exit(0)
}

func netnsConfig() error {
	ifStr := flag.String("if", "", "Interface/Adapter to move/configure")
	nspid := flag.Int("nspid", -1, "Process ID (to locate netns")
	cfgStr := flag.String("cfg", "", "Adapter configuration (json)")
	logArgs := commoncli.SetFlagsForLogging()

	flag.Parse()
	if err := commoncli.SetupLogging(logArgs...); err != nil {
		return err
	}
	if *ifStr == "" || *nspid == -1 || *cfgStr == "" {
		return fmt.Errorf("All three arguments must be specified")
	}

	var a prot.NetworkAdapter
	if err := json.Unmarshal([]byte(*cfgStr), &a); err != nil {
		return err
	}

	if a.NatEnabled {
		log.Infof("Configure %s in %d with: %s/%d gw=%s", *ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
	} else {
		log.Infof("Configure %s in %d with DHCP", *ifStr, *nspid)
	}

	// Lock the OS Thread so we don't accidentally switch namespaces
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// Stash current network namespace away and make sure we enter it as we leave
	log.Infof("Obtaining current namespace")
	origNS, err := netns.Get()
	if err != nil {
		return fmt.Errorf("netns.Get() failed: %v", err)
	}
	defer origNS.Close()
	log.Infof("Original namespace %v", origNS)

	// Get a reference to the new network namespace
	ns, err := netns.GetFromPid(*nspid)
	if err != nil {
		return fmt.Errorf("netns.GetFromPid(%d) failed: %v", *nspid, err)
	}
	defer ns.Close()
	log.Infof("New network namespace from PID %d is %v", *nspid, ns)

	// Get a reference to the interface and make sure it's down
	link, err := netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}
	if err := netlink.LinkSetDown(link); err != nil {
		return fmt.Errorf("netlink.LinkSetDown(%#v) failed: %v", link, err)
	}

	// Move the interface to the new network namespace
	if err := netlink.LinkSetNsPid(link, *nspid); err != nil {
		return fmt.Errorf("netlink.SetNsPid(%#v, %d) failed: %v", link, *nspid, err)
	}

	log.Infof("Switching from %v to %v", origNS, ns)

	// Enter the new network namespace
	if err := netns.Set(ns); err != nil {
		return fmt.Errorf("netns.Set() failed: %v", err)
	}

	// Re-Get a reference to the interface (it may be a different ID in the new namespace)
	log.Infof("Getting reference to interface")
	link, err = netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}

	// User requested non-default MTU size
	if a.EncapOverhead != 0 {
		log.Info("EncapOverhead non-zero, will set MTU")
		mtu := link.Attrs().MTU - int(a.EncapOverhead)
		log.Infof("mtu %d", mtu)
		if err = netlink.LinkSetMTU(link, mtu); err != nil {
			return fmt.Errorf("netlink.LinkSetMTU(%#v, %d) failed: %v", link, mtu, err)
		}
	}

	// Configure the interface
	if a.NatEnabled {
		log.Info("Nat enabled - configuring interface")
		metric := 1
		if a.EnableLowMetric {
			metric = 500
		}

		// Bring the interface up
		if err := netlink.LinkSetUp(link); err != nil {
			return fmt.Errorf("netlink.LinkSetUp(%#v) failed: %v", link, err)
		}
		// Set IP address
		addr := &net.IPNet{
			IP: net.ParseIP(a.AllocatedIPAddress),
			// TODO(rn): This assumes/hardcodes IPv4
			Mask: net.CIDRMask(int(a.HostIPPrefixLength), 32)}
		ipAddr := &netlink.Addr{IPNet: addr, Label: ""}
		if err := netlink.AddrAdd(link, ipAddr); err != nil {
			return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr, err)
		}
		// Set gateway
		if a.HostIPAddress != "" {
			gw := net.ParseIP(a.HostIPAddress)

			if !addr.Contains(gw) {
				// In the case that a gw is not part of the subnet we are setting gw for,
				// a new addr containing this gw address need to be added into the link to avoid getting
				// unreachable error when adding this out-of-subnet gw route
				log.Infof("gw is outside of the subnet: Configure %s in %d with: %s/%d gw=%s\n",
					*ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
				addr2 := &net.IPNet{
					IP:   net.ParseIP(a.HostIPAddress),
					Mask: net.CIDRMask(32, 32)} // This assumes/hardcodes IPv4
				ipAddr2 := &netlink.Addr{IPNet: addr2, Label: ""}
				if err := netlink.AddrAdd(link, ipAddr2); err != nil {
					return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr2, err)
				}
			}

			if !a.EnableLowMetric {
				route := netlink.Route{
					Scope:     netlink.SCOPE_UNIVERSE,
					LinkIndex: link.Attrs().Index,
					Gw:        gw,
					Priority:  metric, // This is what ip route add does
				}
				if err := netlink.RouteAdd(&route); err != nil {
					return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
				}
			} else {
				// add a route rule for the new interface so packets coming on this interface
				// always go out the same interface
				srcNet := &net.IPNet{IP: net.ParseIP(a.AllocatedIPAddress), Mask: net.CIDRMask(32, 32)}
				rule := netlink.NewRule()
				rule.Table = 101
				rule.Src = srcNet
				rule.Priority = 5

				if err := netlink.RuleAdd(rule); err != nil {
					return fmt.Errorf("netlink.RuleAdd(%#v) failed: %v", rule, err)
				}

				// add the default route in that interface specific table
				route := netlink.Route{
					Scope:     netlink.SCOPE_UNIVERSE,
					LinkIndex: link.Attrs().Index,
					Gw:        gw,
					Table:     rule.Table,
					Priority:  metric,
				}
				if err := netlink.RouteAdd(&route); err != nil {
					return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
				}

			}
		}
	} else {
		log.Infof("Execing udhcpc with timeout...")
		cmd := exec.Command("udhcpc", "-q", "-i", *ifStr, "-s", "/sbin/udhcpc_config.script")

		done := make(chan error)
		go func() {
			done <- cmd.Wait()
		}()
		defer close(done)

		select {
		case <-time.After(30 * time.Second):
			var cos string
			co, err := cmd.CombinedOutput() // In case it has written something
			if err != nil {
				cos = string(co)
			}
			cmd.Process.Kill()
			log.Infof("udhcpc timed out [%s]", cos)
			return fmt.Errorf("udhcpc timed out. Failed to get DHCP address: %s", cos)
		case err := <-done:
			var cos string
			co, err := cmd.CombinedOutput() // Something should be on stderr
			if err != nil {
				cos = string(co)
			}
			if err != nil {
				log.Infof("udhcpc failed %s [%s]", err, cos)
				return fmt.Errorf("process failed: %s (%s)", err, cos)
			}
		}
		var cos string
		co, err := cmd.CombinedOutput()
		if err != nil {
			cos = string(co)
		}
		log.Debugf("udhcpc succeeded: %s", cos)
	}

	// Add some debug logging
	curNS, _ := netns.Get()
	// Refresh link attributes/state
	link, _ = netlink.LinkByIndex(link.Attrs().Index)
	attr := link.Attrs()
	addrs, _ := netlink.AddrList(link, 0)
	log.Infof("%v: %s[idx=%d,type=%s] is %v", curNS, attr.Name, attr.Index, link.Type(), attr.OperState)
	for _, addr := range addrs {
		log.Infof("  %v", addr)
	}

	return nil
}
<|endoftext|>


<commit_before>package vhd

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"time"
)

// Constants for the VHD header
const cookieMagic = 0x636f6e6563746978 // "conectix" as 64 bit big endian number
const featureMask = 0x2
const fileFormatVersionMagic = 0x00010000
const fixedDataOffset = 0xFFFFFFFFFFFFFFFF
const creatorApplicationMagic = 0x77696e20 // "win " as 4 bit big endian number. It's win<SPACE>
const creatorVersionMagic = 0x000a0000
const creatorHostOSMagic = 0x5769326b // "wi2k" as 4 bit big endian number

// DiskType Constants
// DiskType = 1, 5, 6 are deprecated
const diskTypeNone = 0
const diskTypeFixed = 2
const diskTypeDynamic = 3
const diskTypeDifferencing = 4

// Saved state constants
const saveStateYes = 1
const saveStateNo = 0

// Consts for the CHS calculation
const sectorBytes = 512
const cMax = 65535
const hMax = 16
const sMax = 255
const maxCHS = cMax * hMax * sMax * sectorBytes

// Variables for time stamp
const timeStart = "00 Jan 01 00:00 UTC"

var oldTime time.Time

type fixedVHDHeader struct {
	Cookie             uint64
	Features           uint32
	FileFormatVersion  uint32
	DataOffset         uint64
	TimeStamp          uint32
	CreatorApplication uint32
	CreatorVersion     uint32
	CreatorHostOS      uint32
	OriginalSize       uint64
	CurrentSize        uint64
	DiskGeometry       uint32
	DiskType           uint32
	Checksum           uint32
	UniqueID           [16]uint8
	SavedState         uint8
	Reserved           [427]uint8
}

const fixedVHDHeaderSize int64 = 512

func init() {
	oldTime, _ = time.Parse(time.RFC822, timeStart)
}

func newFixedVHDHeader(size uint64) (*fixedVHDHeader, error) {
	header := newBasicFixedVHDHeader()

	timestamp := calculateTimeStamp()
	header.TimeStamp = timestamp

	header.OriginalSize = size
	header.CurrentSize = size

	chs, err := calculateCHS(size)
	if err != nil {
		return nil, err
	}
	header.DiskGeometry = chs

	header.DiskType = diskTypeFixed

	id, err := generateUUID()
	if err != nil {
		return nil, err
	}
	header.UniqueID = id

	chk, err := calculateCheckSum(header)
	if err != nil {
		return nil, err
	}
	header.Checksum = chk
	return header, nil
}

// Bytes serializes the VHD header into a byte slice.
func (hdr *fixedVHDHeader) Bytes() ([]byte, error) {
	b := &bytes.Buffer{}
	if err := binary.Write(b, binary.BigEndian, hdr); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func newBasicFixedVHDHeader() *fixedVHDHeader {
	return &fixedVHDHeader{
		Cookie:             cookieMagic,
		Features:           featureMask,
		FileFormatVersion:  fileFormatVersionMagic,
		DataOffset:         fixedDataOffset,
		CreatorApplication: creatorApplicationMagic,
		CreatorVersion:     creatorVersionMagic,
		CreatorHostOS:      creatorHostOSMagic,
	}
}

func calculateCHS(totalSize uint64) (uint32, error) {
	if totalSize == 0 || totalSize%sectorBytes != 0 {
		return 0, fmt.Errorf("size must be a non zero multiple of 512")
	}

	totalSectors := totalSize / sectorBytes
	if totalSectors > maxCHS {
		totalSectors = maxCHS
	}

	var sectorsPerTrack uint64
	var heads uint64
	var cylinderTimesHeads uint64
	if totalSectors >= cMax*hMax*63 {
		sectorsPerTrack = 255
		heads = 16
		cylinderTimesHeads = totalSectors / sectorsPerTrack
	} else {
		sectorsPerTrack = 17
		cylinderTimesHeads = totalSectors / sectorsPerTrack

		heads = (cylinderTimesHeads + 1023) / 1024
		if heads < 4 {
			heads = 4
		}
		if cylinderTimesHeads >= (heads*1024) || heads >= 16 {
			sectorsPerTrack = 31
			heads = 16
			cylinderTimesHeads = totalSectors / sectorsPerTrack
		}
		if cylinderTimesHeads >= (heads * 1024) {
			sectorsPerTrack = 63
			heads = 16
			cylinderTimesHeads = totalSectors / sectorsPerTrack
		}
	}
	cylinders := cylinderTimesHeads / heads

	// Sanity check
	if cylinders > cMax || heads > hMax || sectorsPerTrack > sMax {
		return 0, fmt.Errorf("invalid size value. Must be less than %d", maxCHS)
	}

	// Now the values into a single big endian int.
	var res uint32
	res |= uint32(cylinders << 16)
	res |= uint32(heads << 8)
	res |= uint32(sectorsPerTrack)
	return res, nil
}

func calculateCheckSum(header *fixedVHDHeader) (uint32, error) {
	oldchk := header.Checksum
	header.Checksum = 0

	buf := &bytes.Buffer{}
	if err := binary.Write(buf, binary.BigEndian, header); err != nil {
		return 0, err
	}

	var chk uint32
	bufBytes := buf.Bytes()
	for i := 0; i < len(bufBytes); i++ {
		chk += uint32(bufBytes[i])
	}
	header.Checksum = oldchk
	return uint32(^chk), nil
}

func calculateTimeStamp() uint32 {
	return uint32(time.Since(oldTime).Seconds())
}

func generateUUID() ([16]byte, error) {
	res := [16]byte{}
	if _, err := rand.Read(res[:]); err != nil {
		return res, err
	}
	return res, nil
}
<commit_msg>Fix int overflow on 32 bit architectures<commit_after>package vhd

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"time"
)

// Constants for the VHD header
const cookieMagic = 0x636f6e6563746978 // "conectix" as 64 bit big endian number
const featureMask = 0x2
const fileFormatVersionMagic = 0x00010000
const fixedDataOffset = 0xFFFFFFFFFFFFFFFF
const creatorApplicationMagic = 0x77696e20 // "win " as 4 bit big endian number. It's win<SPACE>
const creatorVersionMagic = 0x000a0000
const creatorHostOSMagic = 0x5769326b // "wi2k" as 4 bit big endian number

// DiskType Constants
// DiskType = 1, 5, 6 are deprecated
const diskTypeNone = 0
const diskTypeFixed = 2
const diskTypeDynamic = 3
const diskTypeDifferencing = 4

// Saved state constants
const saveStateYes = 1
const saveStateNo = 0

// Consts for the CHS calculation
const sectorBytes = 512
const cMax = 65535
const hMax = 16
const sMax = 255
const maxCHS uint64 = cMax * hMax * sMax * sectorBytes

// Variables for time stamp
const timeStart = "00 Jan 01 00:00 UTC"

var oldTime time.Time

type fixedVHDHeader struct {
	Cookie             uint64
	Features           uint32
	FileFormatVersion  uint32
	DataOffset         uint64
	TimeStamp          uint32
	CreatorApplication uint32
	CreatorVersion     uint32
	CreatorHostOS      uint32
	OriginalSize       uint64
	CurrentSize        uint64
	DiskGeometry       uint32
	DiskType           uint32
	Checksum           uint32
	UniqueID           [16]uint8
	SavedState         uint8
	Reserved           [427]uint8
}

const fixedVHDHeaderSize int64 = 512

func init() {
	oldTime, _ = time.Parse(time.RFC822, timeStart)
}

func newFixedVHDHeader(size uint64) (*fixedVHDHeader, error) {
	header := newBasicFixedVHDHeader()

	timestamp := calculateTimeStamp()
	header.TimeStamp = timestamp

	header.OriginalSize = size
	header.CurrentSize = size

	chs, err := calculateCHS(size)
	if err != nil {
		return nil, err
	}
	header.DiskGeometry = chs

	header.DiskType = diskTypeFixed

	id, err := generateUUID()
	if err != nil {
		return nil, err
	}
	header.UniqueID = id

	chk, err := calculateCheckSum(header)
	if err != nil {
		return nil, err
	}
	header.Checksum = chk
	return header, nil
}

// Bytes serializes the VHD header into a byte slice.
func (hdr *fixedVHDHeader) Bytes() ([]byte, error) {
	b := &bytes.Buffer{}
	if err := binary.Write(b, binary.BigEndian, hdr); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func newBasicFixedVHDHeader() *fixedVHDHeader {
	return &fixedVHDHeader{
		Cookie:             cookieMagic,
		Features:           featureMask,
		FileFormatVersion:  fileFormatVersionMagic,
		DataOffset:         fixedDataOffset,
		CreatorApplication: creatorApplicationMagic,
		CreatorVersion:     creatorVersionMagic,
		CreatorHostOS:      creatorHostOSMagic,
	}
}

func calculateCHS(totalSize uint64) (uint32, error) {
	if totalSize == 0 || totalSize%sectorBytes != 0 {
		return 0, fmt.Errorf("size must be a non zero multiple of 512")
	}

	totalSectors := totalSize / sectorBytes
	if totalSectors > maxCHS {
		totalSectors = maxCHS
	}

	var sectorsPerTrack uint64
	var heads uint64
	var cylinderTimesHeads uint64
	if totalSectors >= cMax*hMax*63 {
		sectorsPerTrack = 255
		heads = 16
		cylinderTimesHeads = totalSectors / sectorsPerTrack
	} else {
		sectorsPerTrack = 17
		cylinderTimesHeads = totalSectors / sectorsPerTrack

		heads = (cylinderTimesHeads + 1023) / 1024
		if heads < 4 {
			heads = 4
		}
		if cylinderTimesHeads >= (heads*1024) || heads >= 16 {
			sectorsPerTrack = 31
			heads = 16
			cylinderTimesHeads = totalSectors / sectorsPerTrack
		}
		if cylinderTimesHeads >= (heads * 1024) {
			sectorsPerTrack = 63
			heads = 16
			cylinderTimesHeads = totalSectors / sectorsPerTrack
		}
	}
	cylinders := cylinderTimesHeads / heads

	// Sanity check
	if cylinders > cMax || heads > hMax || sectorsPerTrack > sMax {
		return 0, fmt.Errorf("invalid size value. Must be less than %d", maxCHS)
	}

	// Now the values into a single big endian int.
	var res uint32
	res |= uint32(cylinders << 16)
	res |= uint32(heads << 8)
	res |= uint32(sectorsPerTrack)
	return res, nil
}

func calculateCheckSum(header *fixedVHDHeader) (uint32, error) {
	oldchk := header.Checksum
	header.Checksum = 0

	buf := &bytes.Buffer{}
	if err := binary.Write(buf, binary.BigEndian, header); err != nil {
		return 0, err
	}

	var chk uint32
	bufBytes := buf.Bytes()
	for i := 0; i < len(bufBytes); i++ {
		chk += uint32(bufBytes[i])
	}
	header.Checksum = oldchk
	return uint32(^chk), nil
}

func calculateTimeStamp() uint32 {
	return uint32(time.Since(oldTime).Seconds())
}

func generateUUID() ([16]byte, error) {
	res := [16]byte{}
	if _, err := rand.Read(res[:]); err != nil {
		return res, err
	}
	return res, nil
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/consul/testutil"
	"github.com/mitchellh/cli"
)

func TestCommand_implements(t *testing.T) {
	var _ cli.Command = new(Command)
}

func TestValidDatacenter(t *testing.T) {
	shouldMatch := []string{
		"dc1",
		"east-aws-001",
		"PROD_aws01-small",
	}
	noMatch := []string{
		"east.aws",
		"east!aws",
		"first,second",
	}
	for _, m := range shouldMatch {
		if !validDatacenter.MatchString(m) {
			t.Fatalf("expected match: %s", m)
		}
	}
	for _, m := range noMatch {
		if validDatacenter.MatchString(m) {
			t.Fatalf("expected no match: %s", m)
		}
	}
}

func TestRetryJoin(t *testing.T) {
	dir, agent := makeAgent(t, nextConfig())
	defer os.RemoveAll(dir)
	defer agent.Shutdown()

	conf2 := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	doneCh := make(chan struct{})
	shutdownCh := make(chan struct{})

	defer func() {
		close(shutdownCh)
		<-doneCh
	}()

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf(
		"%s:%d",
		agent.config.BindAddr,
		agent.config.Ports.SerfLan)

	serfWanAddr := fmt.Sprintf(
		"%s:%d",
		agent.config.BindAddr,
		agent.config.Ports.SerfWan)

	args := []string{
		"-server",
		"-data-dir", tmpDir,
		"-node", fmt.Sprintf(`"%s"`, conf2.NodeName),
		"-retry-join", serfAddr,
		"-retry-interval", "1s",
		"-retry-join-wan", serfWanAddr,
		"-retry-interval-wan", "1s",
	}

	go func() {
		if code := cmd.Run(args); code != 0 {
			log.Printf("bad: %d", code)
		}
		close(doneCh)
	}()

	testutil.WaitForResult(func() (bool, error) {
		mem := agent.LANMembers()
		if len(mem) != 2 {
			return false, fmt.Errorf("bad: %#v", mem)
		}
		mem = agent.WANMembers()
		if len(mem) != 2 {
			return false, fmt.Errorf("bad (wan): %#v", mem)
		}
		return true, nil
	}, func(err error) {
		t.Fatalf(err.Error())
	})
}

func TestReadCliConfig(t *testing.T) {

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	cmd := &Command{
		args: []string{
			"-data-dir", tmpDir,
			"-node", `"a"`,
			"-advertise-wan", "1.2.3.4",
		},
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	config := cmd.readConfig()
	if config.AdvertiseAddrWan != "1.2.3.4" {
		t.Fatalf("expected -advertise-addr-wan 1.2.3.4 got %s", config.AdvertiseAddrWan)
	}
}

func TestRetryJoinFail(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.Ports.SerfLan)

	args := []string{
		"-data-dir", tmpDir,
		"-retry-join", serfAddr,
		"-retry-max", "1",
	}

	if code := cmd.Run(args); code == 0 {
		t.Fatalf("bad: %d", code)
	}
}

func TestRetryJoinWanFail(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.Ports.SerfWan)

	args := []string{
		"-server",
		"-data-dir", tmpDir,
		"-retry-join-wan", serfAddr,
		"-retry-max-wan", "1",
	}

	if code := cmd.Run(args); code == 0 {
		t.Fatalf("bad: %d", code)
	}
}

func TestSetupAgent_RPCUnixSocket_FileExists(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	tmpFile, err := ioutil.TempFile("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tmpFile.Name())
	socketPath := tmpFile.Name()

	conf.DataDir = tmpDir
	conf.Server = true
	conf.Bootstrap = true

	// Set socket address to an existing file.
	conf.Addresses.RPC = "unix://" + socketPath

	// Custom mode for socket file
	conf.UnixSockets.Perms = "0777"

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	logWriter := NewLogWriter(512)
	logOutput := new(bytes.Buffer)

	// Ensure the server is created
	if err := cmd.setupAgent(conf, logOutput, logWriter); err != nil {
		t.Fatalf("err: %s", err)
	}

	// Ensure the file was replaced by the socket
	fi, err := os.Stat(socketPath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if fi.Mode()&os.ModeSocket == 0 {
		t.Fatalf("expected socket to replace file")
	}

	// Ensure permissions were applied to the socket file
	if fi.Mode().String() != "Srwxrwxrwx" {
		t.Fatalf("bad permissions: %s", fi.Mode())
	}
}

func TestSetupScadaConn(t *testing.T) {
	// Create a config and assign an infra name
	conf1 := nextConfig()
	conf1.AtlasInfrastructure = "hashicorp/test1"
	conf1.AtlasToken = "abc"

	dir, agent := makeAgent(t, conf1)
	defer os.RemoveAll(dir)
	defer agent.Shutdown()

	cmd := &Command{
		ShutdownCh: make(chan struct{}),
		Ui:         new(cli.MockUi),
		agent:      agent,
	}

	// First start creates the scada conn
	if err := cmd.setupScadaConn(conf1); err != nil {
		t.Fatalf("err: %s", err)
	}
	list := cmd.scadaHttp.listener.(*scadaListener)
	if list == nil || list.addr.infra != "hashicorp/test1" {
		t.Fatalf("bad: %#v", list)
	}
	http1 := cmd.scadaHttp
	provider1 := cmd.scadaProvider

	// Performing setup again tears down original and replaces
	// with a new SCADA client.
	conf2 := nextConfig()
	conf2.AtlasInfrastructure = "hashicorp/test2"
	conf2.AtlasToken = "123"
	if err := cmd.setupScadaConn(conf2); err != nil {
		t.Fatalf("err: %s", err)
	}
	if cmd.scadaHttp == http1 || cmd.scadaProvider == provider1 {
		t.Fatalf("should change: %#v %#v", cmd.scadaHttp, cmd.scadaProvider)
	}
	list = cmd.scadaHttp.listener.(*scadaListener)
	if list == nil || list.addr.infra != "hashicorp/test2" {
		t.Fatalf("bad: %#v", list)
	}

	// Original provider and listener must be closed
	if !provider1.IsShutdown() {
		t.Fatalf("should be shutdown")
	}
	if _, err := http1.listener.Accept(); !strings.Contains(err.Error(), "closed") {
		t.Fatalf("should be closed")
	}
}
<commit_msg>agent: test mdb dir protection<commit_after>package agent

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/hashicorp/consul/testutil"
	"github.com/mitchellh/cli"
)

func TestCommand_implements(t *testing.T) {
	var _ cli.Command = new(Command)
}

func TestValidDatacenter(t *testing.T) {
	shouldMatch := []string{
		"dc1",
		"east-aws-001",
		"PROD_aws01-small",
	}
	noMatch := []string{
		"east.aws",
		"east!aws",
		"first,second",
	}
	for _, m := range shouldMatch {
		if !validDatacenter.MatchString(m) {
			t.Fatalf("expected match: %s", m)
		}
	}
	for _, m := range noMatch {
		if validDatacenter.MatchString(m) {
			t.Fatalf("expected no match: %s", m)
		}
	}
}

func TestRetryJoin(t *testing.T) {
	dir, agent := makeAgent(t, nextConfig())
	defer os.RemoveAll(dir)
	defer agent.Shutdown()

	conf2 := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	doneCh := make(chan struct{})
	shutdownCh := make(chan struct{})

	defer func() {
		close(shutdownCh)
		<-doneCh
	}()

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf(
		"%s:%d",
		agent.config.BindAddr,
		agent.config.Ports.SerfLan)

	serfWanAddr := fmt.Sprintf(
		"%s:%d",
		agent.config.BindAddr,
		agent.config.Ports.SerfWan)

	args := []string{
		"-server",
		"-data-dir", tmpDir,
		"-node", fmt.Sprintf(`"%s"`, conf2.NodeName),
		"-retry-join", serfAddr,
		"-retry-interval", "1s",
		"-retry-join-wan", serfWanAddr,
		"-retry-interval-wan", "1s",
	}

	go func() {
		if code := cmd.Run(args); code != 0 {
			log.Printf("bad: %d", code)
		}
		close(doneCh)
	}()

	testutil.WaitForResult(func() (bool, error) {
		mem := agent.LANMembers()
		if len(mem) != 2 {
			return false, fmt.Errorf("bad: %#v", mem)
		}
		mem = agent.WANMembers()
		if len(mem) != 2 {
			return false, fmt.Errorf("bad (wan): %#v", mem)
		}
		return true, nil
	}, func(err error) {
		t.Fatalf(err.Error())
	})
}

func TestReadCliConfig(t *testing.T) {

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	cmd := &Command{
		args: []string{
			"-data-dir", tmpDir,
			"-node", `"a"`,
			"-advertise-wan", "1.2.3.4",
		},
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	config := cmd.readConfig()
	if config.AdvertiseAddrWan != "1.2.3.4" {
		t.Fatalf("expected -advertise-addr-wan 1.2.3.4 got %s", config.AdvertiseAddrWan)
	}
}

func TestRetryJoinFail(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.Ports.SerfLan)

	args := []string{
		"-data-dir", tmpDir,
		"-retry-join", serfAddr,
		"-retry-max", "1",
	}

	if code := cmd.Run(args); code == 0 {
		t.Fatalf("bad: %d", code)
	}
}

func TestRetryJoinWanFail(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	serfAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.Ports.SerfWan)

	args := []string{
		"-server",
		"-data-dir", tmpDir,
		"-retry-join-wan", serfAddr,
		"-retry-max-wan", "1",
	}

	if code := cmd.Run(args); code == 0 {
		t.Fatalf("bad: %d", code)
	}
}

func TestSetupAgent_RPCUnixSocket_FileExists(t *testing.T) {
	conf := nextConfig()
	tmpDir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(tmpDir)

	tmpFile, err := ioutil.TempFile("", "consul")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tmpFile.Name())
	socketPath := tmpFile.Name()

	conf.DataDir = tmpDir
	conf.Server = true
	conf.Bootstrap = true

	// Set socket address to an existing file.
	conf.Addresses.RPC = "unix://" + socketPath

	// Custom mode for socket file
	conf.UnixSockets.Perms = "0777"

	shutdownCh := make(chan struct{})
	defer close(shutdownCh)

	cmd := &Command{
		ShutdownCh: shutdownCh,
		Ui:         new(cli.MockUi),
	}

	logWriter := NewLogWriter(512)
	logOutput := new(bytes.Buffer)

	// Ensure the server is created
	if err := cmd.setupAgent(conf, logOutput, logWriter); err != nil {
		t.Fatalf("err: %s", err)
	}

	// Ensure the file was replaced by the socket
	fi, err := os.Stat(socketPath)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if fi.Mode()&os.ModeSocket == 0 {
		t.Fatalf("expected socket to replace file")
	}

	// Ensure permissions were applied to the socket file
	if fi.Mode().String() != "Srwxrwxrwx" {
		t.Fatalf("bad permissions: %s", fi.Mode())
	}
}

func TestSetupScadaConn(t *testing.T) {
	// Create a config and assign an infra name
	conf1 := nextConfig()
	conf1.AtlasInfrastructure = "hashicorp/test1"
	conf1.AtlasToken = "abc"

	dir, agent := makeAgent(t, conf1)
	defer os.RemoveAll(dir)
	defer agent.Shutdown()

	cmd := &Command{
		ShutdownCh: make(chan struct{}),
		Ui:         new(cli.MockUi),
		agent:      agent,
	}

	// First start creates the scada conn
	if err := cmd.setupScadaConn(conf1); err != nil {
		t.Fatalf("err: %s", err)
	}
	list := cmd.scadaHttp.listener.(*scadaListener)
	if list == nil || list.addr.infra != "hashicorp/test1" {
		t.Fatalf("bad: %#v", list)
	}
	http1 := cmd.scadaHttp
	provider1 := cmd.scadaProvider

	// Performing setup again tears down original and replaces
	// with a new SCADA client.
	conf2 := nextConfig()
	conf2.AtlasInfrastructure = "hashicorp/test2"
	conf2.AtlasToken = "123"
	if err := cmd.setupScadaConn(conf2); err != nil {
		t.Fatalf("err: %s", err)
	}
	if cmd.scadaHttp == http1 || cmd.scadaProvider == provider1 {
		t.Fatalf("should change: %#v %#v", cmd.scadaHttp, cmd.scadaProvider)
	}
	list = cmd.scadaHttp.listener.(*scadaListener)
	if list == nil || list.addr.infra != "hashicorp/test2" {
		t.Fatalf("bad: %#v", list)
	}

	// Original provider and listener must be closed
	if !provider1.IsShutdown() {
		t.Fatalf("should be shutdown")
	}
	if _, err := http1.listener.Accept(); !strings.Contains(err.Error(), "closed") {
		t.Fatalf("should be closed")
	}
}

func TestProtectDataDir(t *testing.T) {
	dir, err := ioutil.TempDir("", "consul")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(dir)

	if err := os.MkdirAll(filepath.Join(dir, "mdb"), 0700); err != nil {
		t.Fatalf("err: %v", err)
	}

	cfgFile, err := ioutil.TempFile("", "consul")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.Remove(cfgFile.Name())

	content := fmt.Sprintf(`{"server": true, "data_dir": "%s"}`, dir)
	_, err = cfgFile.Write([]byte(content))
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	ui := new(cli.MockUi)
	cmd := &Command{
		Ui:   ui,
		args: []string{"-config-file=" + cfgFile.Name()},
	}
	if conf := cmd.readConfig(); conf != nil {
		t.Fatalf("should fail")
	}
	if out := ui.ErrorWriter.String(); !strings.Contains(out, dir) {
		t.Fatalf("expected mdb dir error, got: %s", out)
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 Authors of Cilium

package k8sTest

import (
	"fmt"

	. "github.com/onsi/gomega"

	. "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"
)

var _ = Describe("K8sBandwidthTest", func() {
	var (
		kubectl        *helpers.Kubectl
		ciliumFilename string
		demoYAML       string
	)

	BeforeAll(func() {
		kubectl = helpers.CreateKubectl(helpers.K8s1VMName(), logger)

		ciliumFilename = helpers.TimestampFilename("cilium.yaml")

		demoYAML = helpers.ManifestGet(kubectl.BasePath(), "demo_bw.yaml")
		res := kubectl.ApplyDefault(demoYAML)
		res.ExpectSuccess("Unable to apply %s", demoYAML)
	})

	AfterAll(func() {
		kubectl.Delete(demoYAML)
		ExpectAllPodsTerminated(kubectl)

		UninstallCiliumFromManifest(kubectl, ciliumFilename)
		ExpectAllPodsTerminated(kubectl)

		kubectl.CloseSSHClient()
	})

	SkipContextIf(helpers.DoesNotRunOnNetNextKernel, "Checks Bandwidth Rate-Limiting", func() {
		const (
			testDS10       = "run=netperf-10"
			testDS25       = "run=netperf-25"
			testClientPod  = "run=netperf-client-pod"
			testClientHost = "run=netperf-client-host"

			maxRateDeviation = 5
			minBandwidth     = 1
		)

		var (
			podLabels = []string{
				testDS10,
				testDS25,
			}
		)

		AfterFailed(func() {
			kubectl.CiliumReport("cilium bpf bandwidth list", "cilium endpoint list")
		})

		JustAfterEach(func() {
			kubectl.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
		})

		waitForTestPods := func() {
			podLabels := []string{
				testDS10,
				testDS25,
				testClientPod,
				testClientHost,
			}
			for _, label := range podLabels {
				err := kubectl.WaitforPods(helpers.DefaultNamespace,
					fmt.Sprintf("-l %s", label), helpers.HelperTimeout)
				Expect(err).Should(BeNil())
			}
		}

		testNetperfFromPods := func(clientPodLabel, targetIP string, maxSessions, rate int) {
			pods, err := kubectl.GetPodNames(helpers.DefaultNamespace, clientPodLabel)
			ExpectWithOffset(1, err).Should(BeNil(), "cannot retrieve pod names by filter %q",
				clientPodLabel)
			for i := 1; i <= maxSessions; i++ {
				cmd := helpers.SuperNetperf(i, targetIP, helpers.TCP_MAERTS, "")
				for _, pod := range pods {
					By("Running %d netperf session from %s pod to pod with IP %s (expected rate: %d)",
						i, pod, targetIP, rate)
					res := kubectl.ExecPodCmd(helpers.DefaultNamespace, pod, cmd)
					ExpectWithOffset(1, res).Should(helpers.CMDSuccess(),
						"Request from %s pod to pod with IP %s failed", pod, targetIP)
					By("Session test completed, netperf result raw: %s", res.SingleOut())
					if rate > 0 {
						ExpectWithOffset(1, res.InRange(minBandwidth, rate+maxRateDeviation)).To(BeNil(),
							"Rate mismatch")
					}
				}
			}
		}

		testNetperf := func(podLabels []string, fromLabel string) {
			for _, label := range podLabels {
				podIPs, err := kubectl.GetPodsIPs(helpers.DefaultNamespace, label)
				ExpectWithOffset(1, err).Should(BeNil(), "Cannot retrieve pod IPs for %s", label)
				ExpectWithOffset(1, len(podIPs)).To(Equal(int(1)), "Expected pod IPs mismatch")
				rate := 0
				fmt.Sscanf(label, "run=netperf-%d", &rate)
				for _, podIP := range podIPs {
					testNetperfFromPods(fromLabel, podIP, 1, rate)
				}
			}
		}

		It("Checks Pod to Pod bandwidth, vxlan tunneling", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager": "true",
				"tunnel":           "vxlan",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
		It("Checks Pod to Pod bandwidth, geneve tunneling", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager": "true",
				"tunnel":           "geneve",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
		It("Checks Pod to Pod bandwidth, direct routing", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager":     "true",
				"tunnel":               "disabled",
				"autoDirectNodeRoutes": "true",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
	})
})
<commit_msg>test: Properly skip K8sBandwidth tests<commit_after>// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 Authors of Cilium

package k8sTest

import (
	"fmt"

	. "github.com/onsi/gomega"

	. "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"
)

var _ = SkipDescribeIf(helpers.DoesNotRunOnNetNextKernel, "K8sBandwidthTest", func() {
	var (
		kubectl        *helpers.Kubectl
		ciliumFilename string
		demoYAML       string
	)

	BeforeAll(func() {
		kubectl = helpers.CreateKubectl(helpers.K8s1VMName(), logger)

		ciliumFilename = helpers.TimestampFilename("cilium.yaml")

		demoYAML = helpers.ManifestGet(kubectl.BasePath(), "demo_bw.yaml")
		res := kubectl.ApplyDefault(demoYAML)
		res.ExpectSuccess("Unable to apply %s", demoYAML)
	})

	AfterAll(func() {
		kubectl.Delete(demoYAML)
		ExpectAllPodsTerminated(kubectl)

		UninstallCiliumFromManifest(kubectl, ciliumFilename)
		ExpectAllPodsTerminated(kubectl)

		kubectl.CloseSSHClient()
	})

	Context("Checks Bandwidth Rate-Limiting", func() {
		const (
			testDS10       = "run=netperf-10"
			testDS25       = "run=netperf-25"
			testClientPod  = "run=netperf-client-pod"
			testClientHost = "run=netperf-client-host"

			maxRateDeviation = 5
			minBandwidth     = 1
		)

		var (
			podLabels = []string{
				testDS10,
				testDS25,
			}
		)

		AfterFailed(func() {
			kubectl.CiliumReport("cilium bpf bandwidth list", "cilium endpoint list")
		})

		JustAfterEach(func() {
			kubectl.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
		})

		waitForTestPods := func() {
			podLabels := []string{
				testDS10,
				testDS25,
				testClientPod,
				testClientHost,
			}
			for _, label := range podLabels {
				err := kubectl.WaitforPods(helpers.DefaultNamespace,
					fmt.Sprintf("-l %s", label), helpers.HelperTimeout)
				Expect(err).Should(BeNil())
			}
		}

		testNetperfFromPods := func(clientPodLabel, targetIP string, maxSessions, rate int) {
			pods, err := kubectl.GetPodNames(helpers.DefaultNamespace, clientPodLabel)
			ExpectWithOffset(1, err).Should(BeNil(), "cannot retrieve pod names by filter %q",
				clientPodLabel)
			for i := 1; i <= maxSessions; i++ {
				cmd := helpers.SuperNetperf(i, targetIP, helpers.TCP_MAERTS, "")
				for _, pod := range pods {
					By("Running %d netperf session from %s pod to pod with IP %s (expected rate: %d)",
						i, pod, targetIP, rate)
					res := kubectl.ExecPodCmd(helpers.DefaultNamespace, pod, cmd)
					ExpectWithOffset(1, res).Should(helpers.CMDSuccess(),
						"Request from %s pod to pod with IP %s failed", pod, targetIP)
					By("Session test completed, netperf result raw: %s", res.SingleOut())
					if rate > 0 {
						ExpectWithOffset(1, res.InRange(minBandwidth, rate+maxRateDeviation)).To(BeNil(),
							"Rate mismatch")
					}
				}
			}
		}

		testNetperf := func(podLabels []string, fromLabel string) {
			for _, label := range podLabels {
				podIPs, err := kubectl.GetPodsIPs(helpers.DefaultNamespace, label)
				ExpectWithOffset(1, err).Should(BeNil(), "Cannot retrieve pod IPs for %s", label)
				ExpectWithOffset(1, len(podIPs)).To(Equal(int(1)), "Expected pod IPs mismatch")
				rate := 0
				fmt.Sscanf(label, "run=netperf-%d", &rate)
				for _, podIP := range podIPs {
					testNetperfFromPods(fromLabel, podIP, 1, rate)
				}
			}
		}

		It("Checks Pod to Pod bandwidth, vxlan tunneling", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager": "true",
				"tunnel":           "vxlan",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
		It("Checks Pod to Pod bandwidth, geneve tunneling", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager": "true",
				"tunnel":           "geneve",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
		It("Checks Pod to Pod bandwidth, direct routing", func() {
			DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
				"bandwidthManager":     "true",
				"tunnel":               "disabled",
				"autoDirectNodeRoutes": "true",
			})
			waitForTestPods()
			testNetperf(podLabels, testClientPod)
			testNetperf(podLabels, testClientHost)
		})
	})
})
<|endoftext|>


<commit_before>package krb5crypto

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"strings"
	"os"
)

const (
	s2kParamsZero = 4294967296
)

func AESStringToKey(secret string, salt string, s2kparams string, e EType) ([]byte, error) {
	//process s2kparams string
	//The parameter string is four octets indicating an unsigned
	//number in big-endian order.  This is the number of iterations to be
	//performed.  If the value is 00 00 00 00, the number of iterations to
	//be performed is 4,294,967,296 (2**32).
	var i int
	if s2kparams == "00 00 00 00" {
		i = s2kParamsZero
	} else {
		s := strings.Replace(s2kparams, " ", "", -1)
		if len(s) != 8 {
			return nil, errors.New("Invalid s2kparams")
		}
		b, err := hex.DecodeString(s)
		if err != nil {
			return nil, errors.New("Invalid s2kparams")
		}
		buf := bytes.NewBuffer(b)
		binary.Read(buf, binary.BigEndian, &i)
		if i == 0 {
			i = s2kParamsZero
		}
	}

	return AESStringToKeyIter(secret, salt, i, e)
}

func AESStringToPBKDF2(secret string, salt string, iterations int, e EType) []byte {
	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), sha1.New)
}

func AESStringToKeyIter(secret string, salt string, iterations int, e EType) ([]byte, error) {
	tkey := AESRandomToKey(AESStringToPBKDF2(secret, salt, iterations, e))
	key, err := AESDeriveKey(tkey, []byte("kerberos"), e)
	return key, err
}

func AESRandomToKey(b []byte) []byte {
	return b
}

func AESDeriveRandom(protocolKey, usage []byte, e EType) ([]byte, error) {
	r, err := deriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e)
	return r, err
}

func AESDeriveKey(protocolKey, usage []byte, e EType) ([]byte, error) {
	r, err := AESDeriveRandom(protocolKey, usage, e)
	if err != nil {
		return nil, err
	}
	return AESRandomToKey(r), nil
}

func AESCTSEncrypt(key, iv, message []byte, e EType) ([]byte, []byte, error) {
	if len(key) != e.GetKeyByteSize() {
		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
	}

	l := len(message)

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	mode := cipher.NewCBCEncrypter(block, iv)

	//Ref: https://tools.ietf.org/html/rfc3962 section 5
	/*For consistency, ciphertext stealing is always used for the last two
	blocks of the data to be encrypted, as in [RC5].  If the data length
	is a multiple of the block size, this is equivalent to plain CBC mode
	with the last two ciphertext blocks swapped.*/
	/*The initial vector carried out from one encryption for use in a
	subsequent encryption is the next-to-last block of the encryption
	output; this is the encrypted form of the last plaintext block.*/
	if l <= aes.BlockSize {
		message, _ = zeroPad(message, aes.BlockSize)
		mode.CryptBlocks(message, message)
		return message, message, nil
	}
	if l%aes.BlockSize == 0 {
		mode.CryptBlocks(message, message)
		iv = message[len(message)-aes.BlockSize:]
		rb, _ := swapLastTwoBlocks(message, aes.BlockSize)
		return iv, rb, nil
	}
	message, _ = zeroPad(message, aes.BlockSize)
	rb, pb, lb, err := tailBlocks(message, aes.BlockSize)
	var ct []byte
	if rb != nil {
		// Encrpt all but the lats 2 blocks and update the rolling iv
		mode.CryptBlocks(rb, rb)
		iv = rb[len(rb)-aes.BlockSize:]
		mode = cipher.NewCBCEncrypter(block, iv)
		ct = append(ct, rb...)
	}
	mode.CryptBlocks(pb, pb)
	mode = cipher.NewCBCEncrypter(block, pb)
	mode.CryptBlocks(lb, lb)
	// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
	// Swap the last two cipher blocks
	// Truncate the ciphertext to the length of the original plaintext
	ct = append(ct, lb...)
	ct = append(ct, pb...)
	return lb, ct[:l], nil
	//TODO do we need to add the hash to the beginning?
}

func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
	if len(b) <= c {
		return nil, nil, nil, errors.New("bytes not larger than one block so cannot tail")
	}
	// Get size of last block
	var lbs int
	if l := len(b) % aes.BlockSize; l == 0 {
		lbs = aes.BlockSize
	} else {
		lbs = l
	}
	// Get last block
	lb := b[len(b)-lbs:]
	// Get 2nd to last (penultimate) block
	pb := b[len(b)-lbs-c : len(b)-lbs]
	if len(b) > 2*c {
		rb := b[:len(b)-lbs-c]
		return rb, pb, lb, nil
	}
	return nil, pb, lb, nil
}

func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
	rb, pb, lb, err := tailBlocks(b, c)
	if err != nil {
		return nil, err
	}
	var out []byte
	if rb != nil {
		out = append(out, rb...)
	}
	out = append(out, lb...)
	out = append(out, pb...)
	return out, nil
}

func AESCTSDecrypt(key, ciphertext []byte, e EType) ([]byte, error) {
	if len(key) != e.GetKeyByteSize() {
		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))

	}
	if len(ciphertext) < aes.BlockSize {
		return nil, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ciphertext))
	}
	l := len(ciphertext)

	/*When decrypting, the next-to-last block of the supplied ciphertext is
	carried forward as the next initial vector.  If only one ciphertext
	block is available (decrypting one block, or encrypting one block or
	less), then that one block is carried out instead.*/
	var iv []byte
	if len(ciphertext) == aes.BlockSize {
		iv = ciphertext
	} else {
		tct, _ := zeroPad(ciphertext, aes.BlockSize)
		iv = tct[len(tct)-2*aes.BlockSize:len(tct)-aes.BlockSize]
	}

	//cipherMsg := ciphertext[e.GetConfounderByteSize() : len(ciphertext)-(e.GetHMACBitLength()/8)]
	//cipherHash := ciphertext[len(ciphertext)-(e.GetHMACBitLength()/8):]

	// Configure the CBC
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	//mode := getMode(block, iv, ciphertext)
	var mode cipher.BlockMode

	if len(ciphertext) > aes.BlockSize {
		// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
		// Get 2nd to last (penultimate) block and the last block
		crb, cpb, clb, _ := tailBlocks(ciphertext, aes.BlockSize)
		var ct []byte
		if crb != nil {
			ct = crb
			iv = ct[len(ct)-aes.BlockSize:]
		} else {
			iv = make([]byte, aes.BlockSize)
		}
		iv = make([]byte, aes.BlockSize)
		//Decryt the 2nd to last (penultimate) block
		pb := make([]byte, aes.BlockSize)
		mode = getMode(block, iv, ciphertext)
		mode.CryptBlocks(pb, cpb)
		// number of byte needed to pad
		npb := aes.BlockSize - len(ciphertext)%aes.BlockSize
		//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
		clb = append(clb, pb[len(pb)-npb:]...)
		// Swap the last two cipher blocks
		ct = append(ct, clb...)
		ct = append(ct, cpb...)
		ciphertext = ct
	}
	//ivz := make([]byte, 16)
	mode = getMode(block, iv, ciphertext)

	message := make([]byte, len(ciphertext))
	mode.CryptBlocks(message, ciphertext)
	//fmt.Fprintf(os.Stderr, "plain %v\n", hex.EncodeToString(message))
	//TODO verify checksum here
	return message[:l], nil
}

func getMode(block cipher.Block, iv, ct []byte) cipher.BlockMode {
	fmt.Fprintf(os.Stderr, "%v - iv for mode: %v\n",hex.EncodeToString(ct), hex.EncodeToString(iv))
	return cipher.NewCBCDecrypter(block, iv)
}
/*func DEwithHMAC(key, message []byte) (ct []byte, err error) {
	if len(key) != KeySize {
		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, len(key))
	}
	if len(message)%aes.BlockSize != 0 {
		return nil, errors.New("Plaintext is not a multiple of the block size")
	}

	iv := make([]byte, NonceSize)
	_, err = rand.Read(iv)
	if err != nil {
		return nil, fmt.Errorf("Error creating random nonce: %v", err)
	}

	// NewCipher only returns an error with an invalid key size,
	// but the key size was checked at the beginning of the function.
	c, err := aes.NewCipher(key[:CipherKeyLength])
	if err != nil {
		return nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	ctr := cipher.NewCBCEncrypter(c, iv)
	ctr.CryptBlocks(ct, message)

	h := hmac.New(sha1.New(), key[CipherKeyLength:])
	ct = append(iv, ct...)
	h.Write(ct)
	ct = h.Sum(ct)
	return ct[:HMACKeyLength], nil
}*/
<commit_msg>cts decrypt<commit_after>package krb5crypto

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"os"
	"strings"
)

const (
	s2kParamsZero = 4294967296
)

func AESStringToKey(secret string, salt string, s2kparams string, e EType) ([]byte, error) {
	//process s2kparams string
	//The parameter string is four octets indicating an unsigned
	//number in big-endian order.  This is the number of iterations to be
	//performed.  If the value is 00 00 00 00, the number of iterations to
	//be performed is 4,294,967,296 (2**32).
	var i int
	if s2kparams == "00 00 00 00" {
		i = s2kParamsZero
	} else {
		s := strings.Replace(s2kparams, " ", "", -1)
		if len(s) != 8 {
			return nil, errors.New("Invalid s2kparams")
		}
		b, err := hex.DecodeString(s)
		if err != nil {
			return nil, errors.New("Invalid s2kparams")
		}
		buf := bytes.NewBuffer(b)
		binary.Read(buf, binary.BigEndian, &i)
		if i == 0 {
			i = s2kParamsZero
		}
	}

	return AESStringToKeyIter(secret, salt, i, e)
}

func AESStringToPBKDF2(secret string, salt string, iterations int, e EType) []byte {
	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), sha1.New)
}

func AESStringToKeyIter(secret string, salt string, iterations int, e EType) ([]byte, error) {
	tkey := AESRandomToKey(AESStringToPBKDF2(secret, salt, iterations, e))
	key, err := AESDeriveKey(tkey, []byte("kerberos"), e)
	return key, err
}

func AESRandomToKey(b []byte) []byte {
	return b
}

func AESDeriveRandom(protocolKey, usage []byte, e EType) ([]byte, error) {
	r, err := deriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e)
	return r, err
}

func AESDeriveKey(protocolKey, usage []byte, e EType) ([]byte, error) {
	r, err := AESDeriveRandom(protocolKey, usage, e)
	if err != nil {
		return nil, err
	}
	return AESRandomToKey(r), nil
}

func AESCTSEncrypt(key, iv, message []byte, e EType) ([]byte, []byte, error) {
	if len(key) != e.GetKeyByteSize() {
		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
	}

	l := len(message)

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	mode := cipher.NewCBCEncrypter(block, iv)

	//Ref: https://tools.ietf.org/html/rfc3962 section 5
	/*For consistency, ciphertext stealing is always used for the last two
	blocks of the data to be encrypted, as in [RC5].  If the data length
	is a multiple of the block size, this is equivalent to plain CBC mode
	with the last two ciphertext blocks swapped.*/
	/*The initial vector carried out from one encryption for use in a
	subsequent encryption is the next-to-last block of the encryption
	output; this is the encrypted form of the last plaintext block.*/
	if l <= aes.BlockSize {
		message, _ = zeroPad(message, aes.BlockSize)
		mode.CryptBlocks(message, message)
		return message, message, nil
	}
	if l%aes.BlockSize == 0 {
		mode.CryptBlocks(message, message)
		iv = message[len(message)-aes.BlockSize:]
		rb, _ := swapLastTwoBlocks(message, aes.BlockSize)
		return iv, rb, nil
	}
	message, _ = zeroPad(message, aes.BlockSize)
	rb, pb, lb, err := tailBlocks(message, aes.BlockSize)
	var ct []byte
	if rb != nil {
		// Encrpt all but the lats 2 blocks and update the rolling iv
		mode.CryptBlocks(rb, rb)
		iv = rb[len(rb)-aes.BlockSize:]
		mode = cipher.NewCBCEncrypter(block, iv)
		ct = append(ct, rb...)
	}
	mode.CryptBlocks(pb, pb)
	mode = cipher.NewCBCEncrypter(block, pb)
	mode.CryptBlocks(lb, lb)
	// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
	// Swap the last two cipher blocks
	// Truncate the ciphertext to the length of the original plaintext
	ct = append(ct, lb...)
	ct = append(ct, pb...)
	return lb, ct[:l], nil
	//TODO do we need to add the hash to the beginning?
}

func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
	if len(b) <= c {
		return nil, nil, nil, errors.New("bytes not larger than one block so cannot tail")
	}
	// Get size of last block
	var lbs int
	if l := len(b) % aes.BlockSize; l == 0 {
		lbs = aes.BlockSize
	} else {
		lbs = l
	}
	// Get last block
	lb := b[len(b)-lbs:]
	// Get 2nd to last (penultimate) block
	pb := b[len(b)-lbs-c : len(b)-lbs]
	if len(b) > 2*c {
		rb := b[:len(b)-lbs-c]
		return rb, pb, lb, nil
	}
	return nil, pb, lb, nil
}

func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
	rb, pb, lb, err := tailBlocks(b, c)
	if err != nil {
		return nil, err
	}
	var out []byte
	if rb != nil {
		out = append(out, rb...)
	}
	out = append(out, lb...)
	out = append(out, pb...)
	return out, nil
}

func AESCTSDecrypt(key, ciphertext []byte, e EType) ([]byte, error) {
	if len(key) != e.GetKeyByteSize() {
		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))

	}
	if len(ciphertext) < aes.BlockSize {
		return nil, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ciphertext))
	}
	l := len(ciphertext)

	//cipherMsg := ciphertext[e.GetConfounderByteSize() : len(ciphertext)-(e.GetHMACBitLength()/8)]
	//cipherHash := ciphertext[len(ciphertext)-(e.GetHMACBitLength()/8):]

	// Configure the CBC
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	//mode := getMode(block, iv, ciphertext)
	var mode cipher.BlockMode
	var iv []byte

	if len(ciphertext) > aes.BlockSize && len(ciphertext)%aes.BlockSize == 0 {
		ciphertext, _ = swapLastTwoBlocks(ciphertext, aes.BlockSize)
	} else if len(ciphertext) > aes.BlockSize {
		// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
		// Get 2nd to last (penultimate) block and the last block
		crb, cpb, clb, _ := tailBlocks(ciphertext, aes.BlockSize)
		var ct []byte
		if crb != nil {
			ct = crb
		}
		iv = getIV(crb)
		fmt.Fprintf(os.Stderr, "getIV IV: %v\n", hex.EncodeToString(iv))
		//Decryt the 2nd to last (penultimate) block
		pb := make([]byte, aes.BlockSize)
		mode = getMode(block, iv, ciphertext)
		mode.CryptBlocks(pb, cpb)
		// number of byte needed to pad
		npb := aes.BlockSize - len(ciphertext)%aes.BlockSize
		//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
		clb = append(clb, pb[len(pb)-npb:]...)
		// Swap the last two cipher blocks
		ct = append(ct, clb...)
		ct = append(ct, cpb...)
		ciphertext = ct
	}
	iv = make([]byte, aes.BlockSize)
	mode = getMode(block, iv, ciphertext)

	message := make([]byte, len(ciphertext))
	mode.CryptBlocks(message, ciphertext)
	//fmt.Fprintf(os.Stderr, "plain %v\n", hex.EncodeToString(message))
	//TODO verify checksum here
	return message[:l], nil
}

//Pass the previous blocks to the one you want decrypted
func getIV(ct []byte) []byte {
	/*When decrypting, the next-to-last block of the supplied ciphertext is
	carried forward as the next initial vector.  If only one ciphertext
	block is available (decrypting one block, or encrypting one block or
	less), then that one block is carried out instead.*/
	fmt.Fprintf(os.Stderr, "getIV len: %v\n", len(ct))
	if ct == nil {
		return make([]byte, aes.BlockSize)
	}
	if len(ct) < 2*aes.BlockSize {
		return make([]byte, aes.BlockSize)
	}
	var lbs int
	if l := len(ct) % aes.BlockSize; l == 0 {
		lbs = aes.BlockSize
	} else {
		lbs = l
	}
	// Get 2nd to last (penultimate) block
	pb := ct[len(ct)-lbs-aes.BlockSize : len(ct)-lbs]
	return pb
}

func getMode(block cipher.Block, iv, ct []byte) cipher.BlockMode {
	//fmt.Fprintf(os.Stderr, "%v (%v) - iv for mode: %v\n",hex.EncodeToString(ct), len(ct), hex.EncodeToString(iv))
	return cipher.NewCBCDecrypter(block, iv)
}

/*func DEwithHMAC(key, message []byte) (ct []byte, err error) {
	if len(key) != KeySize {
		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, len(key))
	}
	if len(message)%aes.BlockSize != 0 {
		return nil, errors.New("Plaintext is not a multiple of the block size")
	}

	iv := make([]byte, NonceSize)
	_, err = rand.Read(iv)
	if err != nil {
		return nil, fmt.Errorf("Error creating random nonce: %v", err)
	}

	// NewCipher only returns an error with an invalid key size,
	// but the key size was checked at the beginning of the function.
	c, err := aes.NewCipher(key[:CipherKeyLength])
	if err != nil {
		return nil, fmt.Errorf("Error creating cipher: %v", err)
	}
	ctr := cipher.NewCBCEncrypter(c, iv)
	ctr.CryptBlocks(ct, message)

	h := hmac.New(sha1.New(), key[CipherKeyLength:])
	ct = append(iv, ct...)
	h.Write(ct)
	ct = h.Sum(ct)
	return ct[:HMACKeyLength], nil
}*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fw42/go-hpfeeds"
)

func main() {
	host := "hpfriends.honeycloud.net"
	port := 20000
	ident := "testing"
	auth := "t0ps3cr3t"

	hp := hpfeeds.NewHpfeeds(host, port, ident, auth)
	hp.Log = true

	recv := make(chan hpfeeds.Message)
	hp.Subscribe("testing", recv)
	go func() {
		for foo := range recv {
			fmt.Println(foo.Name, string(foo.Payload))
		}
	}()

	// Wait for disconnect
	<-hp.Disconnected
}
<commit_msg>Remove derelict code<commit_after><|endoftext|>


<commit_before>package main

import (
    "log"
    "os"
    "net/http"

    // "app/shared"
)

func ApiHelloPageHandler(w http.ResponseWriter, r *http.Request) {

  w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  w.WriteHeader(http.StatusOK)
  if err := json.NewEncoder(w).Encode(map[string]interface{}{ "success": true}); err != nil {
    log.Fatal(err)
  }
}
<commit_msg>compile fixes<commit_after>package main

import (
    "log"
    "encoding/json"
    "net/http"

    // "app/shared"
)

func ApiHelloPageHandler(w http.ResponseWriter, r *http.Request) {

  w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  w.WriteHeader(http.StatusOK)
  if err := json.NewEncoder(w).Encode(map[string]interface{}{ "success": true}); err != nil {
    log.Fatal(err)
  }
}
<|endoftext|>


<commit_before>package filer

import (
	"context"
	"github.com/chrislusf/seaweedfs/weed/util"
	"path/filepath"
	"strings"
)

func splitPattern(pattern string) (prefix string, restPattern string) {
	position := strings.Index(pattern, "*")
	if position >= 0 {
		return pattern[:position], pattern[position:]
	}
	position = strings.Index(pattern, "?")
	if position >= 0 {
		return pattern[:position], pattern[position:]
	}
	return "", restPattern
}

// For now, prefix and namePattern are mutually exclusive
func (f *Filer) ListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, namePattern string, namePatternExclude string) (entries []*Entry, hasMore bool, err error) {

	_, err = f.StreamListDirectoryEntries(ctx, p, startFileName, inclusive, limit+1, prefix, namePattern, namePatternExclude, func(entry *Entry) bool {
		entries = append(entries, entry)
		return true
	})

	hasMore = int64(len(entries)) >= limit+1
	if hasMore {
		entries = entries[:limit]
	}

	return entries, hasMore, err
}

// For now, prefix and namePattern are mutually exclusive
func (f *Filer) StreamListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, namePattern string, namePatternExclude string, eachEntryFunc ListEachEntryFunc) (lastFileName string, err error) {
	if strings.HasSuffix(string(p), "/") && len(p) > 1 {
		p = p[0 : len(p)-1]
	}

	prefixInNamePattern, restNamePattern := splitPattern(namePattern)
	if prefixInNamePattern != "" {
		prefix = prefixInNamePattern
	}
	var missedCount int64

	missedCount, lastFileName, err = f.doListPatternMatchedEntries(ctx, p, startFileName, inclusive, limit, prefix, restNamePattern, namePatternExclude, eachEntryFunc)

	for missedCount > 0 && err == nil {
		missedCount, lastFileName, err = f.doListPatternMatchedEntries(ctx, p, lastFileName, false, missedCount, prefix, restNamePattern, namePatternExclude, eachEntryFunc)
	}

	return
}

func (f *Filer) doListPatternMatchedEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix, restNamePattern string, namePatternExclude string, eachEntryFunc ListEachEntryFunc) (missedCount int64, lastFileName string, err error) {

	if len(restNamePattern) == 0 && len(namePatternExclude) == 0{
		lastFileName, err = f.doListValidEntries(ctx, p, startFileName, inclusive, limit, prefix, eachEntryFunc)
		return 0, lastFileName, err
	}

	lastFileName, err = f.doListValidEntries(ctx, p, startFileName, inclusive, limit, prefix, func(entry *Entry) bool {
		nameToTest := strings.ToLower(entry.Name())
		if len(namePatternExclude) > 0 {
			if matched, matchErr := filepath.Match(namePatternExclude, nameToTest); matchErr == nil && matched {
				missedCount++
				return true
			}
		}
		if len(restNamePattern) > 0 {
			if matched, matchErr := filepath.Match(restNamePattern, nameToTest[len(prefix):]); matchErr == nil && !matched {
				missedCount++
				return true
			}
		}
		if !eachEntryFunc(entry) {
			return false
		}
		return true
	})
	if err != nil {
		return
	}
	return
}

func (f *Filer) doListValidEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, eachEntryFunc ListEachEntryFunc) (lastFileName string, err error) {
	var expiredCount int64
	expiredCount, lastFileName, err = f.doListDirectoryEntries(ctx, p, startFileName, inclusive, limit, prefix, eachEntryFunc)
	for expiredCount > 0 && err == nil {
		expiredCount, lastFileName, err = f.doListDirectoryEntries(ctx, p, lastFileName, false, expiredCount, prefix, eachEntryFunc)
	}
	return
}
<commit_msg>ensure name pattern checking is case sensitive<commit_after>package filer

import (
	"context"
	"github.com/chrislusf/seaweedfs/weed/util"
	"path/filepath"
	"strings"
)

func splitPattern(pattern string) (prefix string, restPattern string) {
	position := strings.Index(pattern, "*")
	if position >= 0 {
		return pattern[:position], pattern[position:]
	}
	position = strings.Index(pattern, "?")
	if position >= 0 {
		return pattern[:position], pattern[position:]
	}
	return "", restPattern
}

// For now, prefix and namePattern are mutually exclusive
func (f *Filer) ListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, namePattern string, namePatternExclude string) (entries []*Entry, hasMore bool, err error) {

	_, err = f.StreamListDirectoryEntries(ctx, p, startFileName, inclusive, limit+1, prefix, namePattern, namePatternExclude, func(entry *Entry) bool {
		entries = append(entries, entry)
		return true
	})

	hasMore = int64(len(entries)) >= limit+1
	if hasMore {
		entries = entries[:limit]
	}

	return entries, hasMore, err
}

// For now, prefix and namePattern are mutually exclusive
func (f *Filer) StreamListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, namePattern string, namePatternExclude string, eachEntryFunc ListEachEntryFunc) (lastFileName string, err error) {
	if strings.HasSuffix(string(p), "/") && len(p) > 1 {
		p = p[0 : len(p)-1]
	}

	prefixInNamePattern, restNamePattern := splitPattern(namePattern)
	if prefixInNamePattern != "" {
		prefix = prefixInNamePattern
	}
	var missedCount int64

	missedCount, lastFileName, err = f.doListPatternMatchedEntries(ctx, p, startFileName, inclusive, limit, prefix, restNamePattern, namePatternExclude, eachEntryFunc)

	for missedCount > 0 && err == nil {
		missedCount, lastFileName, err = f.doListPatternMatchedEntries(ctx, p, lastFileName, false, missedCount, prefix, restNamePattern, namePatternExclude, eachEntryFunc)
	}

	return
}

func (f *Filer) doListPatternMatchedEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix, restNamePattern string, namePatternExclude string, eachEntryFunc ListEachEntryFunc) (missedCount int64, lastFileName string, err error) {

	if len(restNamePattern) == 0 && len(namePatternExclude) == 0{
		lastFileName, err = f.doListValidEntries(ctx, p, startFileName, inclusive, limit, prefix, eachEntryFunc)
		return 0, lastFileName, err
	}

	lastFileName, err = f.doListValidEntries(ctx, p, startFileName, inclusive, limit, prefix, func(entry *Entry) bool {
		nameToTest := entry.Name()
		if len(namePatternExclude) > 0 {
			if matched, matchErr := filepath.Match(namePatternExclude, nameToTest); matchErr == nil && matched {
				missedCount++
				return true
			}
		}
		if len(restNamePattern) > 0 {
			if matched, matchErr := filepath.Match(restNamePattern, nameToTest[len(prefix):]); matchErr == nil && !matched {
				missedCount++
				return true
			}
		}
		if !eachEntryFunc(entry) {
			return false
		}
		return true
	})
	if err != nil {
		return
	}
	return
}

func (f *Filer) doListValidEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, eachEntryFunc ListEachEntryFunc) (lastFileName string, err error) {
	var expiredCount int64
	expiredCount, lastFileName, err = f.doListDirectoryEntries(ctx, p, startFileName, inclusive, limit, prefix, eachEntryFunc)
	for expiredCount > 0 && err == nil {
		expiredCount, lastFileName, err = f.doListDirectoryEntries(ctx, p, lastFileName, false, expiredCount, prefix, eachEntryFunc)
	}
	return
}
<|endoftext|>


<commit_before>/**
 * Copyright 2016 IBM Corp.
 *
 * 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.
 */

// See reference at https://sldn.softlayer.com/article/object-filters.
// Examples in the README.md file and in the examples directory.
package filter

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

type Filter struct {
	Path string
	Op   string
	Opts map[string]interface{}
	Val  interface{}
}

type Filters []Filter

// Returns an array of Filters that you can later call .Build() on.
func New(args ...Filter) Filters {
	return args
}

// This is like calling New().Build().
// Returns a JSON string that can be used as the object filter.
func Build(args ...Filter) string {
	filters := Filters{}

	for _, arg := range args {
		filters = append(filters, arg)
	}

	return filters.Build()
}

// This creates a new Filter. The path is a dot-delimited path down
// to the attribute this filter is for. The second value parameter
// is optional.
func Path(path string, val ...interface{}) Filter {
	if len(val) > 0 {
		return Filter{Path: path, Val: val[0]}
	}

	return Filter{Path: path}
}

// Builds the filter string in JSON format
func (fs Filters) Build() string {
	// Loops around filters,
	// splitting path on '.' and looping around path pieces.
	// Idea is to create a map/tree like map[string]interface{}.
	// Every component in the path is a node to create in the tree.
	// Once we get to the leaf, we set the operation.
	// map[string]interface{}{"operation": op+" "+value}
	// If Op is "", then just map[string]interface{}{"operation": value}.
	// Afterwards, the Opts are traversed; []map[string]interface{}{}
	// For every entry in Opts, we create one map, and append it to an array of maps.
	// At the end, json.Marshal the whole thing.
	result := map[string]interface{}{}
	for _, filter := range fs {
		if filter.Path == "" {
			continue
		}

		cursor := result
		nodes := strings.Split(filter.Path, ".")
		for len(nodes) > 1 {
			branch := nodes[0]
			if _, ok := cursor[branch]; !ok {
				cursor[branch] = map[string]interface{}{}
			}
			cursor = cursor[branch].(map[string]interface{})
			nodes = nodes[1:len(nodes)]
		}

		leaf := nodes[0]
		if filter.Val != nil {
			operation := filter.Val
			if filter.Op != "" {
				format := "%s"
				if reflect.TypeOf(filter.Val).String() != "string" {
					format = "%d"
				}
				operation = filter.Op + " " + fmt.Sprintf(format, filter.Val)
			}

			cursor[leaf] = map[string]interface{}{
				"operation": operation,
			}
		}

		if filter.Opts == nil {
			continue
		}

		options := []map[string]interface{}{}
		for name, value := range filter.Opts {
			options = append(options, map[string]interface{}{
				"name":  name,
				"value": value,
			})
		}

		cursor[leaf] = map[string]interface{}{
			"operation": filter.Op,
			"options":   options,
		}
	}

	jsonStr, _ := json.Marshal(result)
	return string(jsonStr)
}

// Builds the filter string in JSON format
func (f Filter) Build() string {
	return Build(f)
}

// Add options to the filter. Can be chained for multiple options.
func (f Filter) Opt(name string, value interface{}) Filter {
	if f.Opts == nil {
		f.Opts = map[string]interface{}{}
	}

	f.Opts[name] = value
	return f
}

// Set this filter to test if property is equal to the value
func (f Filter) Eq(val interface{}) Filter {
	f.Op = ""
	f.Val = val
	return f
}

// Set this filter to test if property is not equal to the value
func (f Filter) NotEq(val interface{}) Filter {
	f.Op = "!="
	f.Val = val
	return f
}

// Set this filter to test if property is like the value
func (f Filter) Like(val interface{}) Filter {
	f.Op = "~"
	f.Val = val
	return f
}

// Set this filter to test if property is unlike value
func (f Filter) NotLike(val interface{}) Filter {
	f.Op = "!~"
	f.Val = val
	return f
}

// Set this filter to test if property is less than value
func (f Filter) LessThan(val interface{}) Filter {
	f.Op = "<"
	f.Val = val
	return f
}

// Set this filter to test if property is less than or equal to the value
func (f Filter) LessThanOrEqual(val interface{}) Filter {
	f.Op = "<="
	f.Val = val
	return f
}

// Set this filter to test if property is greater than value
func (f Filter) GreaterThan(val interface{}) Filter {
	f.Op = ">"
	f.Val = val
	return f
}

// Set this filter to test if property is greater than or equal to value
func (f Filter) GreaterThanOrEqual(val interface{}) Filter {
	f.Op = ">="
	f.Val = val
	return f
}

// Set this filter to test if property is null
func (f Filter) IsNull() Filter {
	f.Op = "is null"
	f.Val = nil
	return f
}

// Set this filter to test if property is not null
func (f Filter) NotNull() Filter {
	f.Op = "not null"
	f.Val = nil
	return f
}

// Set this filter to test if property contains the value
func (f Filter) Contains(val interface{}) Filter {
	f.Op = "*="
	f.Val = val
	return f
}

// Set this filter to test if property does not contain the value
func (f Filter) NotContains(val interface{}) Filter {
	f.Op = "!*="
	f.Val = val
	return f
}

// Set this filter to test if property starts with the value
func (f Filter) StartsWith(val interface{}) Filter {
	f.Op = "^="
	f.Val = val
	return f
}

// Set this filter to test if property does not start with the value
func (f Filter) NotStartsWith(val interface{}) Filter {
	f.Op = "!^="
	f.Val = val
	return f
}

// Set this filter to test if property ends with the value
func (f Filter) EndsWith(val interface{}) Filter {
	f.Op = "$="
	f.Val = val
	return f
}

// Set this filter to test if property does not end with the value
func (f Filter) NotEndsWith(val interface{}) Filter {
	f.Op = "!$="
	f.Val = val
	return f
}

// Set this filter to test if property is one of the values in args.
func (f Filter) In(args ...interface{}) Filter {
	f.Op = "in"
	values := []interface{}{}
	for _, arg := range args {
		values = append(values, arg)
	}

	return f.Opt("data", values)
}

// Set this filter to test if property has a date older than the value in days.
func (f Filter) DaysPast(val interface{}) Filter {
	f.Op = ">= currentDate -"
	f.Val = val
	return f
}

// Set this filter to test if property has the exact date as the value.
func (f Filter) Date(date string) Filter {
	f.Op = "isDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date before the value.
func (f Filter) DateBefore(date string) Filter {
	f.Op = "lessThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date after the value.
func (f Filter) DateAfter(date string) Filter {
	f.Op = "greaterThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date between the values.
func (f Filter) DateBetween(start string, end string) Filter {
	f.Op = "betweenDate"
	f.Val = nil
	return f.Opt("startDate", start).Opt("endDate", end)
}
<commit_msg>Avoid using the reflect library to figure out a value type<commit_after>/**
 * Copyright 2016 IBM Corp.
 *
 * 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.
 */

// See reference at https://sldn.softlayer.com/article/object-filters.
// Examples in the README.md file and in the examples directory.
package filter

import (
	"encoding/json"
	"fmt"
	"strings"
)

type Filter struct {
	Path string
	Op   string
	Opts map[string]interface{}
	Val  interface{}
}

type Filters []Filter

// Returns an array of Filters that you can later call .Build() on.
func New(args ...Filter) Filters {
	return args
}

// This is like calling New().Build().
// Returns a JSON string that can be used as the object filter.
func Build(args ...Filter) string {
	filters := Filters{}

	for _, arg := range args {
		filters = append(filters, arg)
	}

	return filters.Build()
}

// This creates a new Filter. The path is a dot-delimited path down
// to the attribute this filter is for. The second value parameter
// is optional.
func Path(path string, val ...interface{}) Filter {
	if len(val) > 0 {
		return Filter{Path: path, Val: val[0]}
	}

	return Filter{Path: path}
}

// Builds the filter string in JSON format
func (fs Filters) Build() string {
	// Loops around filters,
	// splitting path on '.' and looping around path pieces.
	// Idea is to create a map/tree like map[string]interface{}.
	// Every component in the path is a node to create in the tree.
	// Once we get to the leaf, we set the operation.
	// map[string]interface{}{"operation": op+" "+value}
	// If Op is "", then just map[string]interface{}{"operation": value}.
	// Afterwards, the Opts are traversed; []map[string]interface{}{}
	// For every entry in Opts, we create one map, and append it to an array of maps.
	// At the end, json.Marshal the whole thing.
	result := map[string]interface{}{}
	for _, filter := range fs {
		if filter.Path == "" {
			continue
		}

		cursor := result
		nodes := strings.Split(filter.Path, ".")
		for len(nodes) > 1 {
			branch := nodes[0]
			if _, ok := cursor[branch]; !ok {
				cursor[branch] = map[string]interface{}{}
			}
			cursor = cursor[branch].(map[string]interface{})
			nodes = nodes[1:len(nodes)]
		}

		leaf := nodes[0]
		if filter.Val != nil {
			operation := filter.Val
			if filter.Op != "" {
				var format string
				switch filter.Val.(type) {
				case int:
					format = "%d"
				default:
					format = "%s"
				}
				operation = filter.Op + " " + fmt.Sprintf(format, filter.Val)
			}

			cursor[leaf] = map[string]interface{}{
				"operation": operation,
			}
		}

		if filter.Opts == nil {
			continue
		}

		options := []map[string]interface{}{}
		for name, value := range filter.Opts {
			options = append(options, map[string]interface{}{
				"name":  name,
				"value": value,
			})
		}

		cursor[leaf] = map[string]interface{}{
			"operation": filter.Op,
			"options":   options,
		}
	}

	jsonStr, _ := json.Marshal(result)
	return string(jsonStr)
}

// Builds the filter string in JSON format
func (f Filter) Build() string {
	return Build(f)
}

// Add options to the filter. Can be chained for multiple options.
func (f Filter) Opt(name string, value interface{}) Filter {
	if f.Opts == nil {
		f.Opts = map[string]interface{}{}
	}

	f.Opts[name] = value
	return f
}

// Set this filter to test if property is equal to the value
func (f Filter) Eq(val interface{}) Filter {
	f.Op = ""
	f.Val = val
	return f
}

// Set this filter to test if property is not equal to the value
func (f Filter) NotEq(val interface{}) Filter {
	f.Op = "!="
	f.Val = val
	return f
}

// Set this filter to test if property is like the value
func (f Filter) Like(val interface{}) Filter {
	f.Op = "~"
	f.Val = val
	return f
}

// Set this filter to test if property is unlike value
func (f Filter) NotLike(val interface{}) Filter {
	f.Op = "!~"
	f.Val = val
	return f
}

// Set this filter to test if property is less than value
func (f Filter) LessThan(val interface{}) Filter {
	f.Op = "<"
	f.Val = val
	return f
}

// Set this filter to test if property is less than or equal to the value
func (f Filter) LessThanOrEqual(val interface{}) Filter {
	f.Op = "<="
	f.Val = val
	return f
}

// Set this filter to test if property is greater than value
func (f Filter) GreaterThan(val interface{}) Filter {
	f.Op = ">"
	f.Val = val
	return f
}

// Set this filter to test if property is greater than or equal to value
func (f Filter) GreaterThanOrEqual(val interface{}) Filter {
	f.Op = ">="
	f.Val = val
	return f
}

// Set this filter to test if property is null
func (f Filter) IsNull() Filter {
	f.Op = "is null"
	f.Val = nil
	return f
}

// Set this filter to test if property is not null
func (f Filter) NotNull() Filter {
	f.Op = "not null"
	f.Val = nil
	return f
}

// Set this filter to test if property contains the value
func (f Filter) Contains(val interface{}) Filter {
	f.Op = "*="
	f.Val = val
	return f
}

// Set this filter to test if property does not contain the value
func (f Filter) NotContains(val interface{}) Filter {
	f.Op = "!*="
	f.Val = val
	return f
}

// Set this filter to test if property starts with the value
func (f Filter) StartsWith(val interface{}) Filter {
	f.Op = "^="
	f.Val = val
	return f
}

// Set this filter to test if property does not start with the value
func (f Filter) NotStartsWith(val interface{}) Filter {
	f.Op = "!^="
	f.Val = val
	return f
}

// Set this filter to test if property ends with the value
func (f Filter) EndsWith(val interface{}) Filter {
	f.Op = "$="
	f.Val = val
	return f
}

// Set this filter to test if property does not end with the value
func (f Filter) NotEndsWith(val interface{}) Filter {
	f.Op = "!$="
	f.Val = val
	return f
}

// Set this filter to test if property is one of the values in args.
func (f Filter) In(args ...interface{}) Filter {
	f.Op = "in"
	values := []interface{}{}
	for _, arg := range args {
		values = append(values, arg)
	}

	return f.Opt("data", values)
}

// Set this filter to test if property has a date older than the value in days.
func (f Filter) DaysPast(val interface{}) Filter {
	f.Op = ">= currentDate -"
	f.Val = val
	return f
}

// Set this filter to test if property has the exact date as the value.
func (f Filter) Date(date string) Filter {
	f.Op = "isDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date before the value.
func (f Filter) DateBefore(date string) Filter {
	f.Op = "lessThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date after the value.
func (f Filter) DateAfter(date string) Filter {
	f.Op = "greaterThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date between the values.
func (f Filter) DateBetween(start string, end string) Filter {
	f.Op = "betweenDate"
	f.Val = nil
	return f.Opt("startDate", start).Opt("endDate", end)
}
<|endoftext|>