<commit_before>/*
Copyright 2016 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 e2enode

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/opencontainers/runc/libcontainer/apparmor"
	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	watchtools "k8s.io/client-go/tools/watch"
	"k8s.io/klog/v2"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"

	"github.com/davecgh/go-spew/spew"
	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var _ = SIGDescribe("AppArmor [Feature:AppArmor][NodeFeature:AppArmor]", func() {
	if isAppArmorEnabled() {
		ginkgo.BeforeEach(func() {
			ginkgo.By("Loading AppArmor profiles for testing")
			framework.ExpectNoError(loadTestProfiles(), "Could not load AppArmor test profiles")
		})
		ginkgo.Context("when running with AppArmor", func() {
			f := framework.NewDefaultFramework("apparmor-test")

			ginkgo.It("should reject an unloaded profile", func() {
				status := runAppArmorTest(f, false, v1.AppArmorBetaProfileNamePrefix+"non-existent-profile")
				expectSoftRejection(status)
			})
			ginkgo.It("should enforce a profile blocking writes", func() {
				status := runAppArmorTest(f, true, v1.AppArmorBetaProfileNamePrefix+apparmorProfilePrefix+"deny-write")
				if len(status.ContainerStatuses) == 0 {
					framework.Failf("Unexpected pod status: %s", spew.Sdump(status))
					return
				}
				state := status.ContainerStatuses[0].State.Terminated
				gomega.Expect(state).ToNot(gomega.BeNil(), "ContainerState: %+v", status.ContainerStatuses[0].State)
				gomega.Expect(state.ExitCode).To(gomega.Not(gomega.BeZero()), "ContainerStateTerminated: %+v", state)

			})
			ginkgo.It("should enforce a permissive profile", func() {
				status := runAppArmorTest(f, true, v1.AppArmorBetaProfileNamePrefix+apparmorProfilePrefix+"audit-write")
				if len(status.ContainerStatuses) == 0 {
					framework.Failf("Unexpected pod status: %s", spew.Sdump(status))
					return
				}
				state := status.ContainerStatuses[0].State.Terminated
				gomega.Expect(state).ToNot(gomega.BeNil(), "ContainerState: %+v", status.ContainerStatuses[0].State)
				gomega.Expect(state.ExitCode).To(gomega.BeZero(), "ContainerStateTerminated: %+v", state)
			})
		})
	} else {
		ginkgo.Context("when running without AppArmor", func() {
			f := framework.NewDefaultFramework("apparmor-test")

			ginkgo.It("should reject a pod with an AppArmor profile", func() {
				status := runAppArmorTest(f, false, v1.AppArmorBetaProfileRuntimeDefault)
				expectSoftRejection(status)
			})
		})
	}
})

const apparmorProfilePrefix = "e2e-node-apparmor-test-"
const testProfiles = `
#include <tunables/global>

profile e2e-node-apparmor-test-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

profile e2e-node-apparmor-test-audit-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Only audit file writes.
  audit /** w,
}
`

func loadTestProfiles() error {
	f, err := ioutil.TempFile("/tmp", "apparmor")
	if err != nil {
		return fmt.Errorf("failed to open temp file: %v", err)
	}
	defer os.Remove(f.Name())
	defer f.Close()

	if _, err := f.WriteString(testProfiles); err != nil {
		return fmt.Errorf("failed to write profiles to file: %v", err)
	}

	cmd := exec.Command("apparmor_parser", "-r", "-W", f.Name())
	stderr := &bytes.Buffer{}
	cmd.Stderr = stderr
	out, err := cmd.Output()
	// apparmor_parser does not always return an error code, so consider any stderr output an error.
	if err != nil || stderr.Len() > 0 {
		if stderr.Len() > 0 {
			klog.Warning(stderr.String())
		}
		if len(out) > 0 {
			klog.Infof("apparmor_parser: %s", out)
		}
		return fmt.Errorf("failed to load profiles: %v", err)
	}
	klog.V(2).Infof("Loaded profiles: %v", out)
	return nil
}

func runAppArmorTest(f *framework.Framework, shouldRun bool, profile string) v1.PodStatus {
	pod := createPodWithAppArmor(f, profile)
	if shouldRun {
		// The pod needs to start before it stops, so wait for the longer start timeout.
		framework.ExpectNoError(e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(
			f.ClientSet, pod.Name, f.Namespace.Name, framework.PodStartTimeout))
	} else {
		// Pod should remain in the pending state. Wait for the Reason to be set to "AppArmor".
		fieldSelector := fields.OneTermEqualSelector("metadata.name", pod.Name).String()
		w := &cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				options.FieldSelector = fieldSelector
				return f.PodClient().List(context.TODO(), options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				options.FieldSelector = fieldSelector
				return f.PodClient().Watch(context.TODO(), options)
			},
		}
		preconditionFunc := func(store cache.Store) (bool, error) {
			_, exists, err := store.Get(&metav1.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name})
			if err != nil {
				return true, err
			}
			if !exists {
				// We need to make sure we see the object in the cache before we start waiting for events
				// or we would be waiting for the timeout if such object didn't exist.
				return true, apierrors.NewNotFound(v1.Resource("pods"), pod.Name)
			}

			return false, nil
		}
		ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), framework.PodStartTimeout)
		defer cancel()
		_, err := watchtools.UntilWithSync(ctx, w, &v1.Pod{}, preconditionFunc, func(e watch.Event) (bool, error) {
			switch e.Type {
			case watch.Deleted:
				return false, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, pod.Name)
			}
			switch t := e.Object.(type) {
			case *v1.Pod:
				if t.Status.Reason == "AppArmor" {
					return true, nil
				}
			}
			return false, nil
		})
		framework.ExpectNoError(err)
	}
	p, err := f.PodClient().Get(context.TODO(), pod.Name, metav1.GetOptions{})
	framework.ExpectNoError(err)
	return p.Status
}

func createPodWithAppArmor(f *framework.Framework, profile string) *v1.Pod {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name: fmt.Sprintf("test-apparmor-%s", strings.Replace(profile, "/", "-", -1)),
			Annotations: map[string]string{
				v1.AppArmorBetaContainerAnnotationKeyPrefix + "test": profile,
			},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{{
				Name:    "test",
				Image:   busyboxImage,
				Command: []string{"touch", "foo"},
			}},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}
	return f.PodClient().Create(pod)
}

func expectSoftRejection(status v1.PodStatus) {
	args := []interface{}{"PodStatus: %+v", status}
	framework.ExpectEqual(status.Phase, v1.PodPending, args...)
	framework.ExpectEqual(status.Reason, "AppArmor", args...)
	gomega.Expect(status.Message).To(gomega.ContainSubstring("AppArmor"), args...)
	framework.ExpectEqual(status.ContainerStatuses[0].State.Waiting.Reason, "Blocked", args...)
}

func isAppArmorEnabled() bool {
	// TODO(tallclair): Pass this through the image setup rather than hardcoding.
	if strings.Contains(framework.TestContext.NodeName, "-gci-dev-") {
		gciVersionRe := regexp.MustCompile("-gci-dev-([0-9]+)-")
		matches := gciVersionRe.FindStringSubmatch(framework.TestContext.NodeName)
		if len(matches) == 2 {
			version, err := strconv.Atoi(matches[1])
			if err != nil {
				klog.Errorf("Error parsing GCI version from NodeName %q: %v", framework.TestContext.NodeName, err)
				return false
			}
			return version >= 54
		}
		return false
	}
	if strings.Contains(framework.TestContext.NodeName, "-ubuntu-") {
		return true
	}
	return apparmor.IsEnabled()
}
<commit_msg>Fix AppArmor unloaded profile e2e test<commit_after>/*
Copyright 2016 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 e2enode

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/opencontainers/runc/libcontainer/apparmor"
	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	watchtools "k8s.io/client-go/tools/watch"
	"k8s.io/klog/v2"
	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"

	"github.com/davecgh/go-spew/spew"
	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var _ = SIGDescribe("AppArmor [Feature:AppArmor][NodeFeature:AppArmor]", func() {
	if isAppArmorEnabled() {
		ginkgo.BeforeEach(func() {
			ginkgo.By("Loading AppArmor profiles for testing")
			framework.ExpectNoError(loadTestProfiles(), "Could not load AppArmor test profiles")
		})
		ginkgo.Context("when running with AppArmor", func() {
			f := framework.NewDefaultFramework("apparmor-test")

			ginkgo.It("should reject an unloaded profile", func() {
				status := runAppArmorTest(f, false, v1.AppArmorBetaProfileNamePrefix+"non-existent-profile")
				gomega.Expect(status.ContainerStatuses[0].State.Waiting.Message).To(gomega.ContainSubstring("apparmor"))
			})
			ginkgo.It("should enforce a profile blocking writes", func() {
				status := runAppArmorTest(f, true, v1.AppArmorBetaProfileNamePrefix+apparmorProfilePrefix+"deny-write")
				if len(status.ContainerStatuses) == 0 {
					framework.Failf("Unexpected pod status: %s", spew.Sdump(status))
					return
				}
				state := status.ContainerStatuses[0].State.Terminated
				gomega.Expect(state).ToNot(gomega.BeNil(), "ContainerState: %+v", status.ContainerStatuses[0].State)
				gomega.Expect(state.ExitCode).To(gomega.Not(gomega.BeZero()), "ContainerStateTerminated: %+v", state)

			})
			ginkgo.It("should enforce a permissive profile", func() {
				status := runAppArmorTest(f, true, v1.AppArmorBetaProfileNamePrefix+apparmorProfilePrefix+"audit-write")
				if len(status.ContainerStatuses) == 0 {
					framework.Failf("Unexpected pod status: %s", spew.Sdump(status))
					return
				}
				state := status.ContainerStatuses[0].State.Terminated
				gomega.Expect(state).ToNot(gomega.BeNil(), "ContainerState: %+v", status.ContainerStatuses[0].State)
				gomega.Expect(state.ExitCode).To(gomega.BeZero(), "ContainerStateTerminated: %+v", state)
			})
		})
	} else {
		ginkgo.Context("when running without AppArmor", func() {
			f := framework.NewDefaultFramework("apparmor-test")

			ginkgo.It("should reject a pod with an AppArmor profile", func() {
				status := runAppArmorTest(f, false, v1.AppArmorBetaProfileRuntimeDefault)
				expectSoftRejection(status)
			})
		})
	}
})

const apparmorProfilePrefix = "e2e-node-apparmor-test-"
const testProfiles = `
#include <tunables/global>

profile e2e-node-apparmor-test-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

profile e2e-node-apparmor-test-audit-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Only audit file writes.
  audit /** w,
}
`

func loadTestProfiles() error {
	f, err := ioutil.TempFile("/tmp", "apparmor")
	if err != nil {
		return fmt.Errorf("failed to open temp file: %v", err)
	}
	defer os.Remove(f.Name())
	defer f.Close()

	if _, err := f.WriteString(testProfiles); err != nil {
		return fmt.Errorf("failed to write profiles to file: %v", err)
	}

	cmd := exec.Command("apparmor_parser", "-r", "-W", f.Name())
	stderr := &bytes.Buffer{}
	cmd.Stderr = stderr
	out, err := cmd.Output()
	// apparmor_parser does not always return an error code, so consider any stderr output an error.
	if err != nil || stderr.Len() > 0 {
		if stderr.Len() > 0 {
			klog.Warning(stderr.String())
		}
		if len(out) > 0 {
			klog.Infof("apparmor_parser: %s", out)
		}
		return fmt.Errorf("failed to load profiles: %v", err)
	}
	klog.V(2).Infof("Loaded profiles: %v", out)
	return nil
}

func runAppArmorTest(f *framework.Framework, shouldRun bool, profile string) v1.PodStatus {
	pod := createPodWithAppArmor(f, profile)
	if shouldRun {
		// The pod needs to start before it stops, so wait for the longer start timeout.
		framework.ExpectNoError(e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(
			f.ClientSet, pod.Name, f.Namespace.Name, framework.PodStartTimeout))
	} else {
		// Pod should remain in the pending state. Wait for the Reason to be set to "AppArmor".
		fieldSelector := fields.OneTermEqualSelector("metadata.name", pod.Name).String()
		w := &cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				options.FieldSelector = fieldSelector
				return f.PodClient().List(context.TODO(), options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				options.FieldSelector = fieldSelector
				return f.PodClient().Watch(context.TODO(), options)
			},
		}
		preconditionFunc := func(store cache.Store) (bool, error) {
			_, exists, err := store.Get(&metav1.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name})
			if err != nil {
				return true, err
			}
			if !exists {
				// We need to make sure we see the object in the cache before we start waiting for events
				// or we would be waiting for the timeout if such object didn't exist.
				return true, apierrors.NewNotFound(v1.Resource("pods"), pod.Name)
			}

			return false, nil
		}
		ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), framework.PodStartTimeout)
		defer cancel()
		_, err := watchtools.UntilWithSync(ctx, w, &v1.Pod{}, preconditionFunc, func(e watch.Event) (bool, error) {
			switch e.Type {
			case watch.Deleted:
				return false, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, pod.Name)
			}
			switch t := e.Object.(type) {
			case *v1.Pod:
				if t.Status.Reason == "AppArmor" {
					return true, nil
				}
				// Loading a profile not available on disk should return a container creation error
				if len(t.Status.ContainerStatuses) > 0 && t.Status.ContainerStatuses[0].State.Waiting.Reason == kuberuntime.ErrCreateContainer.Error() {
					return true, nil
				}
			}
			return false, nil
		})
		framework.ExpectNoError(err)
	}
	p, err := f.PodClient().Get(context.TODO(), pod.Name, metav1.GetOptions{})
	framework.ExpectNoError(err)
	return p.Status
}

func createPodWithAppArmor(f *framework.Framework, profile string) *v1.Pod {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name: fmt.Sprintf("test-apparmor-%s", strings.Replace(profile, "/", "-", -1)),
			Annotations: map[string]string{
				v1.AppArmorBetaContainerAnnotationKeyPrefix + "test": profile,
			},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{{
				Name:    "test",
				Image:   busyboxImage,
				Command: []string{"touch", "foo"},
			}},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}
	return f.PodClient().Create(pod)
}

func expectSoftRejection(status v1.PodStatus) {
	args := []interface{}{"PodStatus: %+v", status}
	framework.ExpectEqual(status.Phase, v1.PodPending, args...)
	framework.ExpectEqual(status.Reason, "AppArmor", args...)
	gomega.Expect(status.Message).To(gomega.ContainSubstring("AppArmor"), args...)
	framework.ExpectEqual(status.ContainerStatuses[0].State.Waiting.Reason, "Blocked", args...)
}

func isAppArmorEnabled() bool {
	// TODO(tallclair): Pass this through the image setup rather than hardcoding.
	if strings.Contains(framework.TestContext.NodeName, "-gci-dev-") {
		gciVersionRe := regexp.MustCompile("-gci-dev-([0-9]+)-")
		matches := gciVersionRe.FindStringSubmatch(framework.TestContext.NodeName)
		if len(matches) == 2 {
			version, err := strconv.Atoi(matches[1])
			if err != nil {
				klog.Errorf("Error parsing GCI version from NodeName %q: %v", framework.TestContext.NodeName, err)
				return false
			}
			return version >= 54
		}
		return false
	}
	if strings.Contains(framework.TestContext.NodeName, "-ubuntu-") {
		return true
	}
	return apparmor.IsEnabled()
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"flag"
	"time"
	"sync/atomic"
	"runtime"
)

import (
	"crypto"
	_ "crypto/md5"
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
//	_ "golang.org/x/crypto/blake2b"
//	_ "golang.org/x/crypto/blake2s"
//	_ "golang.org/x/crypto/md4"
//	_ "golang.org/x/crypto/ripemd160"
//	_ "golang.org/x/crypto/sha3"
	"hash"
	"reflect"
)

import "github.com/splace/varbinary"
import "github.com/splace/fsflags"

const timeoutStatusCode = 124

type hashIndex struct{
	varbinary.Uint64
}

// return new hashindex whose representation is as the source hashindex but with added byte(s)
func hashIndexAppend(hi hashIndex, b ...byte) (nhi hashIndex) {
	buf,_:=hi.MarshalBinary()
	(&nhi).UnmarshalBinary(append(buf,b...))
	return
}

// return new hashindex whose representation is as the source hashindex but with removed byte(s)
func hashIndexTruncate(hi hashIndex,c int) (nhi hashIndex) {
	buf,_:=hi.MarshalBinary()
	(&nhi).UnmarshalBinary(buf[:len(buf)-c])
	return
}

func main() {
	var leadingBitCount uint
	flag.UintVar(&leadingBitCount, "bits", 1, "Number of leading bits being searched for.")
	var bitState bool
	flag.BoolVar(&bitState, "set", false, "Leading bits set.")
	var hashType string
	flag.StringVar(&hashType, "hash", "SHA1", "hash type. one of \"MD4,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160,SHA3_224,SHA3_256,SHA3_384,SHA3_512,SHA512_224,SHA512_256\"")
	//flag.StringVar(&hashType, "hash", "SHA1", "hash type. one of MD4,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160,SHA3_224,SHA3_256,SHA3_384,SHA3_512,SHA512_224,SHA512_256,BLAKE2s_256,BLAKE2b_256,BLAKE2b_384,BLAKE2b_512")
	var startHashIndex uint64
	flag.Uint64Var(&startHashIndex, "start", 0, "Hash index to start search from.(default:#0)")
	var stopHashIndex uint64
	flag.Uint64Var(&stopHashIndex, "stop", 0, "Hash index to stop search at.(default:#0 = unlimited)")
	var logInterval time.Duration
	flag.DurationVar(&logInterval, "interval", time.Second, "time between log status reports.")
	var limit time.Duration
	flag.DurationVar(&limit, "end", 0, "search time limit.")
	var help bool
	flag.BoolVar(&help, "help", false, "display help/usage.")
	flag.BoolVar(&help, "h", false, "display help/usage.")
	var quiet bool
	flag.BoolVar(&quiet, "quiet", false, "no progress logging.")
	flag.BoolVar(&quiet, "q", false, "no progress logging.")
	var source fsflags.FileValue
	flag.Var(&source, "i", "input source bytes.(default:<Stdin>)")
	flag.Var(&source, "input", "input source bytes.(default:<Stdin>)")
	var sink fsflags.CreateFileValue
	flag.Var(&sink, "o", "output file, written with input file + nonce appended.(default:Stdout just written with nonce.)")
	flag.Var(&sink, "output", "output file, written with input file + nonce appended.(default:Stdout just written with nonce.)")
	var logToo fsflags.CreateFileValue
	flag.Var(&logToo, "log", "progress log destination.(default:Stderr)")
	flag.Parse()

	if help {
		flag.Usage()
		os.Exit(0)
	}

	// for optimisation #1.1
	var arrayOfBytePerms = [0x100][]byte{}
	for i:=range(arrayOfBytePerms){
		arrayOfBytePerms[i]=[]byte{byte(i)}
	}

	var baseHasher hash.Hash
	switch hashType {
	case "MD4":
		baseHasher = crypto.MD4.New()
	case "MD5":
		baseHasher = crypto.MD5.New()
	case "SHA1":
		baseHasher = crypto.SHA1.New()
	case "SHA224":
		baseHasher = crypto.SHA224.New()
	case "SHA256":
		baseHasher = crypto.SHA256.New()
	case "SHA384":
		baseHasher = crypto.SHA384.New()
	case "SHA512":
		baseHasher = crypto.SHA512.New()
	case "SHA512_224":
		baseHasher = crypto.SHA512_224.New()
	case "SHA512_256":
		baseHasher = crypto.SHA512_256.New()
//	case "RIPEMD160":
//		baseHasher = crypto.RIPEMD160.New()
//	case "SHA3_224":
//		baseHasher = crypto.SHA3_224.New()
//	case "SHA3_256":
//		baseHasher = crypto.SHA3_256.New()
//	case "SHA3_384":
//		baseHasher = crypto.SHA3_384.New()
//	case "SHA3_512":
//		baseHasher = crypto.SHA3_512.New()
//	case "BLAKE2s_256":
//		baseHasher = crypto.BLAKE2s_256.New()
//	case "BLAKE2b_256":
//		baseHasher = crypto.BLAKE2b_256.New()
//	case "BLAKE2b_384":
//		baseHasher = crypto.BLAKE2b_384.New()
//	case "BLAKE2b_512":
//		baseHasher = crypto.BLAKE2b_512.New()
	default:
		log.Fatalf("Aborting, Unknown Hash Scheme:" + hashType)
	}

	if logToo.File == nil {
		logToo.File = os.Stderr
	}

	var progressLog *log.Logger
	if quiet {
		progressLog=log.New(ioutil.Discard, "", log.LstdFlags)
		}else{
		progressLog=log.New(logToo, "", log.LstdFlags)
	}

	if source.File == nil {
		source.File = os.Stdin
	}

	progressLog.Printf("Loading:%q", &source)
	if sink.File == nil {
		sink.File = os.Stdout
		if _, err := io.Copy(baseHasher, source); err != nil {
			log.Fatal(err)
		}
		}else{
		// if not stdout write the source to the sink now, so later writing of the nonce then is appended.
		if _, err := io.Copy(baseHasher, io.TeeReader(source, sink)); err != nil {
			log.Fatal(err)
		}
	}
	source.Close()

	if leadingBitCount > 128 {
		log.Fatalf("Aborting, leading bits matching over 128 not supported (%v)", leadingBitCount)
	}

	// because of optimisation#1, need to find hash index with one byte removed.
	if startHashIndex>0{
		startHashIndex=uint64(hashIndexTruncate(hashIndex{Uint64:varbinary.Uint64(startHashIndex)},1).Uint64)
	}
	if stopHashIndex>0{
		stopHashIndex=uint64(hashIndexTruncate(hashIndex{Uint64:varbinary.Uint64(stopHashIndex)},1).Uint64)+1
		}else{
		stopHashIndex=1<<64-1
	}

	startTime := time.Now()
	doLog := time.NewTicker(logInterval)
	go func() {
		lhashIndex := startHashIndex
		for t := range doLog.C {
			runningFor := t.Sub(startTime)
			if limit > 0 && runningFor > limit {
				progressLog.Print("Aborting: time limit reached")
				os.Exit(timeoutStatusCode)
			}
			progressLog.Printf("#%d @%v\t%.0f#/s\tMean Match:%v", startHashIndex, runningFor/time.Second*time.Second, float64(startHashIndex-lhashIndex)/logInterval.Seconds(), (logInterval / time.Duration(startHashIndex-lhashIndex) * (1 << leadingBitCount) / time.Second * time.Second))
			lhashIndex = startHashIndex
		}
	}()

	var matchCondition func([]byte) bool
	if bitState{
		matchCondition = leadingSetBits(leadingBitCount)
		}else{
		matchCondition = leadingZeroBits(leadingBitCount)
	}

	if sum := baseHasher.Sum(nil); matchCondition(sum) {
		progressLog.Printf("Match on Source file as-is. Hash(%s):[% x]", hashType, sum)
		os.Exit(0)
	}

	stride := uint64(runtime.NumCPU())

	searchStripe := func(start uint64) {
		progressLog.Printf("Starting thread @ #%d", start)
		var hasher, branchHasher hash.Hash
		var sum []byte
		var n int
		buf := make([]byte,8,8)
		for hi := start; hi<=stopHashIndex ; hi += stride {
			hasher = cloneHash(baseHasher)
			n=varbinary.Uint64Put(varbinary.Uint64(hi),buf)
			hasher.Write(buf[:n])
			// optimisation#1: rather than check hash, with existing nonce, copy it and check all possible single bytes added to it. (+20% intel core2)
			for i := range arrayOfBytePerms { // optimisation#1.1: use pre-generated array of []byte for added byte. (+5% intel core2)
				branchHasher = cloneHash(hasher)
				branchHasher.Write(arrayOfBytePerms[i])
				sum = branchHasher.Sum(nil)
				if matchCondition(sum) {
					doLog.Stop()
					progressLog.Printf("#%d @%.1fs\tMatch:%q+[%s %x] Saving:%q Hash(%s):[% x]", uint64(hashIndexAppend(hashIndex{Uint64:varbinary.Uint64(hi)},arrayOfBytePerms[i][0]).Uint64), time.Since(startTime).Seconds(), &source, varbinary.Uint64(hi), arrayOfBytePerms[i], &sink, hashType, sum)
					n=varbinary.Uint64Put(varbinary.Uint64(hi),buf)
					sink.Write(buf[:n])
					sink.Write(arrayOfBytePerms[i])
					sink.Close()
					os.Exit(0)
				}
			}
			atomic.AddUint64(&startHashIndex, 0x100) //keep track of number checked, optimisation#1: each byte tested
		}
	}

	// start a thread for each core, each searching from different start indexes, but striding the same so always missing each other.
	for t := 0; t < runtime.NumCPU()-1; t++ {
		go func(s uint64) {
			searchStripe(s)
		}(startHashIndex)
		startHashIndex++
	}
	searchStripe(startHashIndex)
	progressLog.Printf("#%d @%.1fs Stopping Search of:%q", startHashIndex, time.Since(startTime).Seconds(), &source)

}

func cloneHash(h hash.Hash) hash.Hash{
	return clone(h).(hash.Hash)
}

// copy an interface value using reflect (here for pointers to interfaces), because what we want isn't exposed.
func clone(i interface{}) interface{} {
	indirect := reflect.Indirect(reflect.ValueOf(i))
	newIndirect := reflect.New(indirect.Type())
	newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface()))
	return newIndirect.Interface()
}

<commit_msg>comment<commit_after>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"flag"
	"time"
	"sync/atomic"
	"runtime"
)

import (
	"crypto"
	_ "crypto/md5"
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
//	_ "golang.org/x/crypto/blake2b"
//	_ "golang.org/x/crypto/blake2s"
//	_ "golang.org/x/crypto/md4"
//	_ "golang.org/x/crypto/ripemd160"
//	_ "golang.org/x/crypto/sha3"
	"hash"
	"reflect"
)

import "github.com/splace/varbinary"
import "github.com/splace/fsflags"

const timeoutStatusCode = 124

type hashIndex struct{
	varbinary.Uint64
}

// return new hashindex whose representation is as the source hashindex but with added byte(s)
func hashIndexAppend(hi hashIndex, b ...byte) (nhi hashIndex) {
	buf,_:=hi.MarshalBinary()
	(&nhi).UnmarshalBinary(append(buf,b...))
	return
}

// return new hashindex whose representation is as the source hashindex but with removed byte(s)
func hashIndexTruncate(hi hashIndex,c int) (nhi hashIndex) {
	buf,_:=hi.MarshalBinary()
	(&nhi).UnmarshalBinary(buf[:len(buf)-c])
	return
}

func main() {
	var leadingBitCount uint
	flag.UintVar(&leadingBitCount, "bits", 1, "Number of leading bits being searched for.")
	var bitState bool
	flag.BoolVar(&bitState, "set", false, "Leading bits set.")
	var hashType string
	flag.StringVar(&hashType, "hash", "SHA1", "hash type. one of \"MD4,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160,SHA3_224,SHA3_256,SHA3_384,SHA3_512,SHA512_224,SHA512_256\"")
	//flag.StringVar(&hashType, "hash", "SHA1", "hash type. one of MD4,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160,SHA3_224,SHA3_256,SHA3_384,SHA3_512,SHA512_224,SHA512_256,BLAKE2s_256,BLAKE2b_256,BLAKE2b_384,BLAKE2b_512")
	var startHashIndex uint64
	flag.Uint64Var(&startHashIndex, "start", 0, "Hash index to start search from.(default:#0)")
	var stopHashIndex uint64
	flag.Uint64Var(&stopHashIndex, "stop", 0, "Hash index to stop search at.(default:#0 = unlimited)")
	var logInterval time.Duration
	flag.DurationVar(&logInterval, "interval", time.Second, "time between log status reports.")
	var limit time.Duration
	flag.DurationVar(&limit, "end", 0, "search time limit.")
	var help bool
	flag.BoolVar(&help, "help", false, "display help/usage.")
	flag.BoolVar(&help, "h", false, "display help/usage.")
	var quiet bool
	flag.BoolVar(&quiet, "quiet", false, "no progress logging.")
	flag.BoolVar(&quiet, "q", false, "no progress logging.")
	var source fsflags.FileValue
	flag.Var(&source, "i", "input source bytes.(default:<Stdin>)")
	flag.Var(&source, "input", "input source bytes.(default:<Stdin>)")
	var sink fsflags.CreateFileValue
	flag.Var(&sink, "o", "output file, written with input file + nonce appended.(default:Stdout just written with nonce.)")
	flag.Var(&sink, "output", "output file, written with input file + nonce appended.(default:Stdout just written with nonce.)")
	var logToo fsflags.CreateFileValue
	flag.Var(&logToo, "log", "progress log destination.(default:Stderr)")
	flag.Parse()

	if help {
		flag.Usage()
		os.Exit(0)
	}

	// for optimisation #1.1
	var arrayOfBytePerms = [0x100][]byte{}
	for i:=range(arrayOfBytePerms){
		arrayOfBytePerms[i]=[]byte{byte(i)}
	}

	var baseHasher hash.Hash
	switch hashType {
	case "MD4":
		baseHasher = crypto.MD4.New()
	case "MD5":
		baseHasher = crypto.MD5.New()
	case "SHA1":
		baseHasher = crypto.SHA1.New()
	case "SHA224":
		baseHasher = crypto.SHA224.New()
	case "SHA256":
		baseHasher = crypto.SHA256.New()
	case "SHA384":
		baseHasher = crypto.SHA384.New()
	case "SHA512":
		baseHasher = crypto.SHA512.New()
	case "SHA512_224":
		baseHasher = crypto.SHA512_224.New()
	case "SHA512_256":
		baseHasher = crypto.SHA512_256.New()
//	case "RIPEMD160":
//		baseHasher = crypto.RIPEMD160.New()
//	case "SHA3_224":
//		baseHasher = crypto.SHA3_224.New()
//	case "SHA3_256":
//		baseHasher = crypto.SHA3_256.New()
//	case "SHA3_384":
//		baseHasher = crypto.SHA3_384.New()
//	case "SHA3_512":
//		baseHasher = crypto.SHA3_512.New()
//	case "BLAKE2s_256":
//		baseHasher = crypto.BLAKE2s_256.New()
//	case "BLAKE2b_256":
//		baseHasher = crypto.BLAKE2b_256.New()
//	case "BLAKE2b_384":
//		baseHasher = crypto.BLAKE2b_384.New()
//	case "BLAKE2b_512":
//		baseHasher = crypto.BLAKE2b_512.New()
	default:
		log.Fatalf("Aborting, Unknown Hash Scheme:" + hashType)
	}

	if logToo.File == nil {
		logToo.File = os.Stderr
	}

	var progressLog *log.Logger
	if quiet {
		progressLog=log.New(ioutil.Discard, "", log.LstdFlags)
		}else{
		progressLog=log.New(logToo, "", log.LstdFlags)
	}

	if source.File == nil {
		source.File = os.Stdin
	}

	progressLog.Printf("Loading:%q", &source)
	if sink.File == nil {
		sink.File = os.Stdout
		if _, err := io.Copy(baseHasher, source); err != nil {
			log.Fatal(err)
		}
		}else{
		// if not stdout write the source to the sink now, so later writing of the nonce then is appended.
		if _, err := io.Copy(baseHasher, io.TeeReader(source, sink)); err != nil {
			log.Fatal(err)
		}
	}
	source.Close()

	if leadingBitCount > 128 {
		log.Fatalf("Aborting, leading bits matching over 128 not supported (%v)", leadingBitCount)
	}

	// because of optimisation#1, need to find hash index with one byte removed.
	if startHashIndex>0{
		startHashIndex=uint64(hashIndexTruncate(hashIndex{Uint64:varbinary.Uint64(startHashIndex)},1).Uint64)
	}
	if stopHashIndex>0{
		stopHashIndex=uint64(hashIndexTruncate(hashIndex{Uint64:varbinary.Uint64(stopHashIndex)},1).Uint64)+1
		}else{
		stopHashIndex=1<<64-1
	}

	startTime := time.Now()
	doLog := time.NewTicker(logInterval)
	go func() {
		lhashIndex := startHashIndex
		for t := range doLog.C {
			runningFor := t.Sub(startTime)
			if limit > 0 && runningFor > limit {
				progressLog.Print("Aborting: time limit reached")
				os.Exit(timeoutStatusCode)
			}
			progressLog.Printf("#%d @%v\t%.0f#/s\tMean Match:%v", startHashIndex, runningFor/time.Second*time.Second, float64(startHashIndex-lhashIndex)/logInterval.Seconds(), (logInterval / time.Duration(startHashIndex-lhashIndex) * (1 << leadingBitCount) / time.Second * time.Second))
			lhashIndex = startHashIndex
		}
	}()

	var matchCondition func([]byte) bool
	if bitState{
		matchCondition = leadingSetBits(leadingBitCount)
		}else{
		matchCondition = leadingZeroBits(leadingBitCount)
	}

	if sum := baseHasher.Sum(nil); matchCondition(sum) {
		progressLog.Printf("Match on Source file as-is. Hash(%s):[% x]", hashType, sum)
		os.Exit(0)
	}

	stride := uint64(runtime.NumCPU())

	searchStripe := func(start uint64) {
		progressLog.Printf("Starting thread @ #%d", start)
		var hasher, branchHasher hash.Hash
		var sum []byte
		var n int
		buf := make([]byte,8,8)
		for hi := start; hi<=stopHashIndex ; hi += stride {
			hasher = cloneHash(baseHasher)
			n=varbinary.Uint64Put(varbinary.Uint64(hi),buf)
			hasher.Write(buf[:n])
			// optimisation#1: rather than check hash, with existing nonce, copy it and check all possible single bytes added to it. (+20% intel core2)
			for i := range arrayOfBytePerms { // optimisation#1.1: use pre-generated array of []byte for added byte. (+5% intel core2)
				branchHasher = cloneHash(hasher)
				branchHasher.Write(arrayOfBytePerms[i])
				sum = branchHasher.Sum(nil)
				if matchCondition(sum) {
					doLog.Stop()
					progressLog.Printf("#%d @%.1fs\tMatch:%q+[%s %x] Saving:%q Hash(%s):[% x]", uint64(hashIndexAppend(hashIndex{Uint64:varbinary.Uint64(hi)},arrayOfBytePerms[i][0]).Uint64), time.Since(startTime).Seconds(), &source, varbinary.Uint64(hi), arrayOfBytePerms[i], &sink, hashType, sum)
					n=varbinary.Uint64Put(varbinary.Uint64(hi),buf)
					sink.Write(buf[:n])
					sink.Write(arrayOfBytePerms[i])
					sink.Close()
					os.Exit(0)
				}
			}
			atomic.AddUint64(&startHashIndex, 0x100) //keep track of number checked, optimisation#1: each byte tested
		}
	}

	// start a thread for each core, each searching from different start indexes, but striding the same so always missing each other.
	for t := 0; t < runtime.NumCPU()-1; t++ {
		go func(s uint64) {
			searchStripe(s)
		}(startHashIndex)
		startHashIndex++
	}
	searchStripe(startHashIndex)
	progressLog.Printf("#%d @%.1fs Stopping Search of:%q", startHashIndex, time.Since(startTime).Seconds(), &source)

}

//  need to clone hash because what we want isn't exposed, and forking hashing isn't possible due to its calls to the runtime.
func cloneHash(h hash.Hash) hash.Hash{
	return clone(h).(hash.Hash)
}

// copy an interface value using reflection (here for interfaces of pointers to interfaces)
func clone(i interface{}) interface{} {
	indirect := reflect.Indirect(reflect.ValueOf(i))
	newIndirect := reflect.New(indirect.Type())
	newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface()))
	return newIndirect.Interface()
}

<|endoftext|>


<commit_before>package rpm

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
)

// A Header stores metadata about a rpm package.
type Header struct {
	Version    int
	IndexCount int
	Length     int
	Indexes    IndexEntries
	Start      int
	End        int
}

// Headers is an array of Header structs.
type Headers []Header

const (
	// MAX_HEADER_SIZE is the maximum allowable header size in bytes (32 MB).
	MAX_HEADER_SIZE = 33554432
)

// Known error types
var (
	ErrBadHeaderLength    = fmt.Errorf("RPM header section is incorrect length")
	ErrNotHeader          = fmt.Errorf("invalid RPM header descriptor")
	ErrBadIndexCount      = fmt.Errorf("index count exceeds header size")
	ErrBadIndexLength     = fmt.Errorf("index section is incorrect length")
	ErrIndexOutOfRange    = fmt.Errorf("index is out of range")
	ErrBadStoreLength     = fmt.Errorf("header value store is incorrect length")
	ErrBadIndexType       = fmt.Errorf("unknown index data type")
	ErrBadIndexValueCount = fmt.Errorf("index value count is out of range")
)

// ReadPackageHeader reads an RPM package file header structure from the given
// io.Reader.
//
// This function should only be used if you intend to read a package header
// structure in isolation.
func ReadPackageHeader(r io.Reader) (*Header, error) {
	// read the "header structure header"
	header := make([]byte, 16)
	n, err := r.Read(header)
	if err != nil {
		return nil, err
	}

	if n != 16 {
		return nil, ErrBadHeaderLength
	}

	// check magic number
	if 0 != bytes.Compare(header[:3], []byte{0x8E, 0xAD, 0xE8}) {
		return nil, ErrNotHeader
	}

	// translate header
	h := &Header{
		Version:    int(header[3]),
		IndexCount: int(binary.BigEndian.Uint32(header[8:12])),
		Length:     int(binary.BigEndian.Uint32(header[12:16])),
	}

	// make sure header size is in range
	if h.Length > MAX_HEADER_SIZE {
		return nil, ErrBadHeaderLength
	}

	// Ensure index count is in range
	// This test is not entirely precise as h.Length also includes the value
	// store. It should at least help eliminate excessive buffer allocations for
	// corrupted length values in the > h.Length ranges.
	if h.IndexCount*16 > h.Length {
		return nil, ErrBadIndexCount
	}

	h.Indexes = make(IndexEntries, h.IndexCount)

	// read indexes
	indexLength := 16 * h.IndexCount
	indexes := make([]byte, indexLength)
	n, err = r.Read(indexes)
	if err != nil {
		return nil, err
	}

	if n != indexLength {
		return nil, ErrBadIndexLength
	}

	for x := 0; x < h.IndexCount; x++ {
		o := 16 * x
		index := IndexEntry{
			Tag:       int(binary.BigEndian.Uint32(indexes[o : o+4])),
			Type:      int(binary.BigEndian.Uint32(indexes[o+4 : o+8])),
			Offset:    int(binary.BigEndian.Uint32(indexes[o+8 : o+12])),
			ItemCount: int(binary.BigEndian.Uint32(indexes[o+12 : o+16])),
		}

		// validate index offset
		if index.Offset >= h.Length {
			return nil, ErrIndexOutOfRange
		}

		// append
		h.Indexes[x] = index
	}

	// read the "store"
	store := make([]byte, h.Length)
	n, err = r.Read(store)
	if err != nil {
		return nil, err
	}

	if n != h.Length {
		return nil, ErrBadStoreLength
	}

	// parse the value of each index from the store
	for x := 0; x < h.IndexCount; x++ {
		index := h.Indexes[x]
		o := index.Offset

		if index.ItemCount == 0 {
			return nil, ErrBadIndexValueCount
		}

		switch index.Type {
		case IndexDataTypeChar:
			vals := make([]uint8, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o >= len(store) {
					return nil, fmt.Errorf("uint8 value for index %d is out of range", x+1)
				}

				vals[v] = uint8(store[o])
				o += 1
			}

			index.Value = vals

		case IndexDataTypeInt8:
			vals := make([]int8, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o >= len(store) {
					return nil, fmt.Errorf("int8 value for index %d is out of range", x+1)
				}

				vals[v] = int8(store[o])
				o += 1
			}

			index.Value = vals

		case IndexDataTypeInt16:
			vals := make([]int16, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+2 > len(store) {
					return nil, fmt.Errorf("int16 value for index %d is out of range", x+1)
				}

				vals[v] = int16(binary.BigEndian.Uint16(store[o : o+2]))
				o += 2
			}

			index.Value = vals

		case IndexDataTypeInt32:
			vals := make([]int32, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+4 > len(store) {
					return nil, fmt.Errorf("int32 value for index %d is out of range", x+1)
				}

				vals[v] = int32(binary.BigEndian.Uint32(store[o : o+4]))
				o += 4
			}

			index.Value = vals

		case IndexDataTypeInt64:
			vals := make([]int64, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+8 > len(store) {
					return nil, fmt.Errorf("int64 value for index %d is out of range", x+1)
				}

				vals[v] = int64(binary.BigEndian.Uint64(store[o : o+8]))
				o += 8
			}

			index.Value = vals

		case IndexDataTypeBinary:
			if o+index.ItemCount > len(store) {
				return nil, fmt.Errorf("[]byte value for index %d is out of range", x+1)
			}

			b := make([]byte, index.ItemCount)
			copy(b, store[o:o+index.ItemCount])

			index.Value = b

		case IndexDataTypeString, IndexDataTypeStringArray, IndexDataTypeI8NString:
			// allow atleast one byte per string
			if o+index.ItemCount > len(store) {
				return nil, fmt.Errorf("[]string value for index %d is out of range", x+1)
			}

			vals := make([]string, index.ItemCount)

			for s := 0; s < index.ItemCount; s++ {
				// calculate string length
				var j int
				for j = 0; store[o+j] != 0 && (o+j) < len(store); j++ {
				}

				if j == len(store) {
					return nil, fmt.Errorf("string value for index %d is out of range", x+1)
				}

				vals[s] = string(store[o : o+j])
				o += j + 1
			}

			index.Value = vals

		case IndexDataTypeNull:
			// nothing to do here

		default:
			// unknown data type
			return nil, ErrBadIndexType
		}

		// save in array
		h.Indexes[x] = index
	}

	// calculate location of the end of the header by padding to a multiple of 8
	o := 8 - int(math.Mod(float64(h.Length), 8))

	// seek to the end of the header
	if o > 0 && o < 8 {
		pad := make([]byte, o)
		n, err = r.Read(pad)
		if err != nil {
			return nil, fmt.Errorf("Error seeking beyond header padding of %d bytes: %v", o, err)
		}

		if n != o {
			return nil, fmt.Errorf("Error seeking beyond header padding of %d bytes: only %d bytes returned", o, n)
		}
	}

	return h, nil
}
<commit_msg>Fixed overlfow for unterminated string index values<commit_after>package rpm

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
)

// A Header stores metadata about a rpm package.
type Header struct {
	Version    int
	IndexCount int
	Length     int
	Indexes    IndexEntries
	Start      int
	End        int
}

// Headers is an array of Header structs.
type Headers []Header

const (
	// MAX_HEADER_SIZE is the maximum allowable header size in bytes (32 MB).
	MAX_HEADER_SIZE = 33554432
)

// Known error types
var (
	ErrBadHeaderLength    = fmt.Errorf("RPM header section is incorrect length")
	ErrNotHeader          = fmt.Errorf("invalid RPM header descriptor")
	ErrBadIndexCount      = fmt.Errorf("index count exceeds header size")
	ErrBadIndexLength     = fmt.Errorf("index section is incorrect length")
	ErrIndexOutOfRange    = fmt.Errorf("index is out of range")
	ErrBadStoreLength     = fmt.Errorf("header value store is incorrect length")
	ErrBadIndexType       = fmt.Errorf("unknown index data type")
	ErrBadIndexValueCount = fmt.Errorf("index value count is out of range")
)

// ReadPackageHeader reads an RPM package file header structure from the given
// io.Reader.
//
// This function should only be used if you intend to read a package header
// structure in isolation.
func ReadPackageHeader(r io.Reader) (*Header, error) {
	// read the "header structure header"
	header := make([]byte, 16)
	n, err := r.Read(header)
	if err != nil {
		return nil, err
	}

	if n != 16 {
		return nil, ErrBadHeaderLength
	}

	// check magic number
	if 0 != bytes.Compare(header[:3], []byte{0x8E, 0xAD, 0xE8}) {
		return nil, ErrNotHeader
	}

	// translate header
	h := &Header{
		Version:    int(header[3]),
		IndexCount: int(binary.BigEndian.Uint32(header[8:12])),
		Length:     int(binary.BigEndian.Uint32(header[12:16])),
	}

	// make sure header size is in range
	if h.Length > MAX_HEADER_SIZE {
		return nil, ErrBadHeaderLength
	}

	// Ensure index count is in range
	// This test is not entirely precise as h.Length also includes the value
	// store. It should at least help eliminate excessive buffer allocations for
	// corrupted length values in the > h.Length ranges.
	if h.IndexCount*16 > h.Length {
		return nil, ErrBadIndexCount
	}

	h.Indexes = make(IndexEntries, h.IndexCount)

	// read indexes
	indexLength := 16 * h.IndexCount
	indexes := make([]byte, indexLength)
	n, err = r.Read(indexes)
	if err != nil {
		return nil, err
	}

	if n != indexLength {
		return nil, ErrBadIndexLength
	}

	for x := 0; x < h.IndexCount; x++ {
		o := 16 * x
		index := IndexEntry{
			Tag:       int(binary.BigEndian.Uint32(indexes[o : o+4])),
			Type:      int(binary.BigEndian.Uint32(indexes[o+4 : o+8])),
			Offset:    int(binary.BigEndian.Uint32(indexes[o+8 : o+12])),
			ItemCount: int(binary.BigEndian.Uint32(indexes[o+12 : o+16])),
		}

		// validate index offset
		if index.Offset >= h.Length {
			return nil, ErrIndexOutOfRange
		}

		// append
		h.Indexes[x] = index
	}

	// read the "store"
	store := make([]byte, h.Length)
	n, err = r.Read(store)
	if err != nil {
		return nil, err
	}

	if n != h.Length {
		return nil, ErrBadStoreLength
	}

	// parse the value of each index from the store
	for x := 0; x < h.IndexCount; x++ {
		index := h.Indexes[x]
		o := index.Offset

		if index.ItemCount == 0 {
			return nil, ErrBadIndexValueCount
		}

		switch index.Type {
		case IndexDataTypeChar:
			vals := make([]uint8, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o >= len(store) {
					return nil, fmt.Errorf("uint8 value for index %d is out of range", x+1)
				}

				vals[v] = uint8(store[o])
				o += 1
			}

			index.Value = vals

		case IndexDataTypeInt8:
			vals := make([]int8, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o >= len(store) {
					return nil, fmt.Errorf("int8 value for index %d is out of range", x+1)
				}

				vals[v] = int8(store[o])
				o += 1
			}

			index.Value = vals

		case IndexDataTypeInt16:
			vals := make([]int16, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+2 > len(store) {
					return nil, fmt.Errorf("int16 value for index %d is out of range", x+1)
				}

				vals[v] = int16(binary.BigEndian.Uint16(store[o : o+2]))
				o += 2
			}

			index.Value = vals

		case IndexDataTypeInt32:
			vals := make([]int32, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+4 > len(store) {
					return nil, fmt.Errorf("int32 value for index %d is out of range", x+1)
				}

				vals[v] = int32(binary.BigEndian.Uint32(store[o : o+4]))
				o += 4
			}

			index.Value = vals

		case IndexDataTypeInt64:
			vals := make([]int64, index.ItemCount)
			for v := 0; v < index.ItemCount; v++ {
				if o+8 > len(store) {
					return nil, fmt.Errorf("int64 value for index %d is out of range", x+1)
				}

				vals[v] = int64(binary.BigEndian.Uint64(store[o : o+8]))
				o += 8
			}

			index.Value = vals

		case IndexDataTypeBinary:
			if o+index.ItemCount > len(store) {
				return nil, fmt.Errorf("[]byte value for index %d is out of range", x+1)
			}

			b := make([]byte, index.ItemCount)
			copy(b, store[o:o+index.ItemCount])

			index.Value = b

		case IndexDataTypeString, IndexDataTypeStringArray, IndexDataTypeI8NString:
			// allow atleast one byte per string
			if o+index.ItemCount > len(store) {
				return nil, fmt.Errorf("[]string value for index %d is out of range", x+1)
			}

			vals := make([]string, index.ItemCount)

			for s := 0; s < index.ItemCount; s++ {
				// calculate string length
				var j int
				for j = 0; (o+j) < len(store) && store[o+j] != 0; j++ {
				}

				if j == len(store) {
					return nil, fmt.Errorf("string value for index %d is out of range", x+1)
				}

				vals[s] = string(store[o : o+j])
				o += j + 1
			}

			index.Value = vals

		case IndexDataTypeNull:
			// nothing to do here

		default:
			// unknown data type
			return nil, ErrBadIndexType
		}

		// save in array
		h.Indexes[x] = index
	}

	// calculate location of the end of the header by padding to a multiple of 8
	o := 8 - int(math.Mod(float64(h.Length), 8))

	// seek to the end of the header
	if o > 0 && o < 8 {
		pad := make([]byte, o)
		n, err = r.Read(pad)
		if err != nil {
			return nil, fmt.Errorf("Error seeking beyond header padding of %d bytes: %v", o, err)
		}

		if n != o {
			return nil, fmt.Errorf("Error seeking beyond header padding of %d bytes: only %d bytes returned", o, n)
		}
	}

	return h, nil
}
<|endoftext|>


<commit_before>package main

import "github.com/labstack/echo"
import "net/http"

// health returns an HTTP handler that provides health information
func health(c echo.Context) error {
	return c.JSON(http.StatusOK, []string{})
	//h := &Health{"Fluffy Radio Api", "1.0.0", "Just Keep Fluffing!"}
	//return c.JSON(http.StatusOK, h)
}

/*type (
	// Health provides basic information about the API used for health monitoring
	Health struct {
		Name    string `json:"name"`
		Version string `json:"version"`
		Message string `json:"message"`
	}
)*/
<commit_msg>Adding additional health info<commit_after>package main

import "github.com/labstack/echo"
import "net/http"

// health returns an HTTP handler that provides health information
func health(c echo.Context) error {
	h := &Health{"Fluffy Radio Api", "1.0.0", "Just Keep Fluffing!"}
	return c.JSON(http.StatusOK, h)
}

type (
	// Health provides basic information about the API used for health monitoring
	Health struct {
		Name    string `json:"name"`
		Version string `json:"version"`
		Message string `json:"message"`
	}
)
<|endoftext|>


<commit_before>// +build integration

/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
	"time"

	"net/http"
	"net/url"

	"github.com/elazarl/goproxy"
	retryablehttp "github.com/hashicorp/go-retryablehttp"
	"github.com/phayes/freeport"
	"github.com/pkg/errors"
)

// setUpProxy runs a local http proxy and sets the env vars for it.
func setUpProxy(t *testing.T) (*http.Server, error) {
	port, err := freeport.GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get an open port")
	}

	addr := fmt.Sprintf("localhost:%d", port)
	err = os.Setenv("NO_PROXY", "")
	if err != nil {
		return nil, errors.Wrap(err, "Failed to set no proxy env")
	}
	err = os.Setenv("HTTP_PROXY", addr)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to set http proxy env")
	}

	proxy := goproxy.NewProxyHttpServer()
	srv := &http.Server{Addr: addr, Handler: proxy}
	go func(s *http.Server, t *testing.T) {
		if err := s.ListenAndServe(); err != http.ErrServerClosed {
			t.Errorf("Failed to start http server for proxy mock")
		}
	}(srv, t)
	return srv, nil
}

func TestProxy(t *testing.T) {
	origHP := os.Getenv("HTTP_PROXY")
	origNP := os.Getenv("NO_PROXY")
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}

	srv, err := setUpProxy(t)
	if err != nil {
		t.Fatalf("Failed to set up the test proxy: %s", err)
	}

	// making sure there is no running minikube to avoid https://github.com/kubernetes/minikube/issues/4132
	mk := NewMinikubeRunner(t, p)
	// Clean up after setting up proxy
	defer func(t *testing.T) {
		err = os.Setenv("HTTP_PROXY", origHP)
		if err != nil {
			t.Errorf("Error reverting the HTTP_PROXY env")
		}
		err = os.Setenv("NO_PROXY", origNP)
		if err != nil {
			t.Errorf("Error reverting the NO_PROXY env")
		}

		err := srv.Shutdown(context.TODO()) // shutting down the http proxy after tests
		if err != nil {
			t.Errorf("Error shutting down the http proxy")
		}
		if !isTestNoneDriver() {
			mk.TearDown(t)
		}

	}(t)

	t.Run("ProxyConsoleWarnning", testProxyWarning)
	t.Run("ProxyDashboard", testProxyDashboard)

}

// testProxyWarning checks user is warned correctly about the proxy related env vars
func testProxyWarning(t *testing.T) {
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}
	mk := NewMinikubeRunner(t, p)
	stdout, stderr, err := mk.Start()
	if err != nil {
		t.Fatalf("%s minikube start failed : %v\nstdout: %s\nstderr: %s", t.Name(), err, stdout, stderr)
	}

	msg := "Found network options:"
	if !strings.Contains(stdout, msg) {
		t.Errorf("Proxy wranning (%s) is missing from the output: %s", msg, stderr)
	}

	msg = "You appear to be using a proxy"
	if !strings.Contains(stderr, msg) {
		t.Errorf("Proxy wranning (%s) is missing from the output: %s", msg, stderr)
	}
}

// testProxyDashboard checks if dashboard URL is accessible if proxy is set
func testProxyDashboard(t *testing.T) {
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}
	mk := NewMinikubeRunner(t, p)
	cmd, out := mk.RunDaemon("dashboard --url")
	defer func() {
		err := cmd.Process.Kill()
		if err != nil {
			t.Logf("Failed to kill dashboard command: %v", err)
		}
	}()

	s, err := readLineWithTimeout(out, 180*time.Second)
	if err != nil {
		t.Fatalf("failed to read url: %v", err)
	}

	u, err := url.Parse(strings.TrimSpace(s))
	if err != nil {
		t.Fatalf("failed to parse %q: %v", s, err)
	}

	resp, err := retryablehttp.Get(u.String())
	if err != nil {
		t.Fatalf("failed get: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("Unable to read http response body: %v", err)
		}
		t.Errorf("%s returned status code %d, expected %d.\nbody:\n%s", u, resp.StatusCode, http.StatusOK, body)
	}
}
<commit_msg>skipping proxy warnning<commit_after>// +build integration

/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
	"time"

	"net/http"
	"net/url"

	"github.com/elazarl/goproxy"
	retryablehttp "github.com/hashicorp/go-retryablehttp"
	"github.com/phayes/freeport"
	"github.com/pkg/errors"
)

// setUpProxy runs a local http proxy and sets the env vars for it.
func setUpProxy(t *testing.T) (*http.Server, error) {
	port, err := freeport.GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get an open port")
	}

	addr := fmt.Sprintf("localhost:%d", port)
	err = os.Setenv("NO_PROXY", "")
	if err != nil {
		return nil, errors.Wrap(err, "Failed to set no proxy env")
	}
	err = os.Setenv("HTTP_PROXY", addr)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to set http proxy env")
	}

	proxy := goproxy.NewProxyHttpServer()
	srv := &http.Server{Addr: addr, Handler: proxy}
	go func(s *http.Server, t *testing.T) {
		if err := s.ListenAndServe(); err != http.ErrServerClosed {
			t.Errorf("Failed to start http server for proxy mock")
		}
	}(srv, t)
	return srv, nil
}

func TestProxy(t *testing.T) {
	origHP := os.Getenv("HTTP_PROXY")
	origNP := os.Getenv("NO_PROXY")
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}

	srv, err := setUpProxy(t)
	if err != nil {
		t.Fatalf("Failed to set up the test proxy: %s", err)
	}

	// making sure there is no running minikube to avoid https://github.com/kubernetes/minikube/issues/4132
	mk := NewMinikubeRunner(t, p)
	// Clean up after setting up proxy
	defer func(t *testing.T) {
		err = os.Setenv("HTTP_PROXY", origHP)
		if err != nil {
			t.Errorf("Error reverting the HTTP_PROXY env")
		}
		err = os.Setenv("NO_PROXY", origNP)
		if err != nil {
			t.Errorf("Error reverting the NO_PROXY env")
		}

		err := srv.Shutdown(context.TODO()) // shutting down the http proxy after tests
		if err != nil {
			t.Errorf("Error shutting down the http proxy")
		}
		if !isTestNoneDriver() {
			mk.TearDown(t)
		}

	}(t)
	t.Run("ProxyConsoleWarnning", testProxyWarning)
	t.Run("ProxyDashboard", testProxyDashboard)

}

// testProxyWarning checks user is warned correctly about the proxy related env vars
func testProxyWarning(t *testing.T) {
	if isTestNoneDriver() {
		// TODO fix this later
		t.Skip("Skipping proxy warning for none")
	}
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}
	mk := NewMinikubeRunner(t, p)
	stdout, stderr, err := mk.Start()
	if err != nil {
		t.Fatalf("%s minikube start failed : %v\nstdout: %s\nstderr: %s", t.Name(), err, stdout, stderr)
	}

	msg := "Found network options:"
	if !strings.Contains(stdout, msg) {
		t.Errorf("Proxy wranning (%s) is missing from the output: %s", msg, stderr)
	}

	msg = "You appear to be using a proxy"
	if !strings.Contains(stderr, msg) {
		t.Errorf("Proxy wranning (%s) is missing from the output: %s", msg, stderr)
	}
}

// testProxyDashboard checks if dashboard URL is accessible if proxy is set
func testProxyDashboard(t *testing.T) {
	p := profile(t) // profile name
	if isTestNoneDriver() {
		p = "minikube"
	}
	mk := NewMinikubeRunner(t, p)
	cmd, out := mk.RunDaemon("dashboard --url")
	defer func() {
		err := cmd.Process.Kill()
		if err != nil {
			t.Logf("Failed to kill dashboard command: %v", err)
		}
	}()

	s, err := readLineWithTimeout(out, 180*time.Second)
	if err != nil {
		t.Fatalf("failed to read url: %v", err)
	}

	u, err := url.Parse(strings.TrimSpace(s))
	if err != nil {
		t.Fatalf("failed to parse %q: %v", s, err)
	}

	resp, err := retryablehttp.Get(u.String())
	if err != nil {
		t.Fatalf("failed get: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("Unable to read http response body: %v", err)
		}
		t.Errorf("%s returned status code %d, expected %d.\nbody:\n%s", u, resp.StatusCode, http.StatusOK, body)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"

	mmap "github.com/edsrzf/mmap-go"

	"github.com/nsf/termbox-go"
)

const PROGRAM_NAME = "hecate"

type FileInfo struct {
	filename string
	bytes    []byte
}

func mainLoop(files []FileInfo, style Style) {
	screens := defaultScreensForFiles(files)
	active_idx := DATA_SCREEN_INDEX

	var screen_key_channels []chan termbox.Event
	var screen_quit_channels []chan bool
	switch_channel := make(chan int)
	main_key_channel := make(chan termbox.Event, 10)

	layoutAndDrawScreen(screens[active_idx], style)

	for _ = range screens {
		key_channel := make(chan termbox.Event, 10)
		screen_key_channels = append(screen_key_channels, key_channel)

		quit_channel := make(chan bool, 10)
		screen_quit_channels = append(screen_quit_channels, quit_channel)
	}

	for i, s := range screens {
		go func(index int, screen Screen) {
			screen.receiveEvents(screen_key_channels[index], switch_channel,
				screen_quit_channels[index])
		}(i, s)
	}

	go func() {
		for {
			main_key_channel <- termbox.PollEvent()
		}
	}()

	for {
		do_quit := false
		select {
		case event := <-main_key_channel:
			if event.Type == termbox.EventKey {
				handleSpecialKeys(event.Key)

				screen_key_channels[active_idx] <- event
			}
			if event.Type == termbox.EventResize {
				layoutAndDrawScreen(screens[active_idx], style)
			}
		case new_screen_index := <-switch_channel:
			if new_screen_index < len(screens) {
				active_idx = new_screen_index
				layoutAndDrawScreen(screens[active_idx], style)
			} else {
				do_quit = true
			}
		}
		if do_quit {
			for _, c := range screen_quit_channels {
				c <- true
			}
			break
		}
	}
}

func main() {
	var err error

	if len(os.Args) < 2 {
		fmt.Printf("Usage: %s <filename> [...]\n", PROGRAM_NAME)
		os.Exit(1)
	}
	var files []FileInfo
	for i := 1; i < len(os.Args); i++ {
		file_path := os.Args[i]

		file, err := os.Open(file_path)
		if err != nil {
			fmt.Printf("Error opening file: %q\n", err.Error())
			os.Exit(1)
		}

		fi, err := file.Stat()
		if err != nil {
			fmt.Printf("Error stat'ing file: %q\n", err.Error())
			os.Exit(1)
		}

		if fi.Size() < 8 {
			fmt.Printf("File %s is too short to be edited\n", file_path)
			os.Exit(1)
		}

		mm, err := mmap.Map(file, mmap.RDONLY, 0)
		if err != nil {
			fmt.Printf("Error mmap'ing file: %q\n", err.Error())
			os.Exit(1)
		}
		files = append(files, FileInfo{filename: path.Base(file_path), bytes: mm})
	}

	err = termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	style := defaultStyle()
	termbox.SetOutputMode(outputMode)

	mainLoop(files, style)
}
<commit_msg>Spin down event-poll goroutine during exit<commit_after>package main

import (
	"fmt"
	"os"
	"path"

	mmap "github.com/edsrzf/mmap-go"

	"github.com/nsf/termbox-go"
)

const PROGRAM_NAME = "hecate"

type FileInfo struct {
	filename string
	bytes    []byte
}

func mainLoop(files []FileInfo, style Style) {
	screens := defaultScreensForFiles(files)
	active_idx := DATA_SCREEN_INDEX

	var screen_key_channels []chan termbox.Event
	var screen_quit_channels []chan bool
	switch_channel := make(chan int)
	main_key_channel := make(chan termbox.Event, 10)

	layoutAndDrawScreen(screens[active_idx], style)

	for _ = range screens {
		key_channel := make(chan termbox.Event, 10)
		screen_key_channels = append(screen_key_channels, key_channel)

		quit_channel := make(chan bool, 10)
		screen_quit_channels = append(screen_quit_channels, quit_channel)
	}

	for i, s := range screens {
		go func(index int, screen Screen) {
			screen.receiveEvents(screen_key_channels[index], switch_channel,
				screen_quit_channels[index])
		}(i, s)
	}

	go func() {
		for {
			event := termbox.PollEvent()
			if event.Type == termbox.EventInterrupt {
				break
			} else {
				main_key_channel <- event
			}
		}
	}()

	for {
		do_quit := false
		select {
		case event := <-main_key_channel:
			if event.Type == termbox.EventKey {
				handleSpecialKeys(event.Key)

				screen_key_channels[active_idx] <- event
			}
			if event.Type == termbox.EventResize {
				layoutAndDrawScreen(screens[active_idx], style)
			}
		case new_screen_index := <-switch_channel:
			if new_screen_index < len(screens) {
				active_idx = new_screen_index
				layoutAndDrawScreen(screens[active_idx], style)
			} else {
				do_quit = true
			}
		}
		if do_quit {
			for _, c := range screen_quit_channels {
				c <- true
			}
			termbox.Interrupt()
			break
		}
	}
}

func main() {
	var err error

	if len(os.Args) < 2 {
		fmt.Printf("Usage: %s <filename> [...]\n", PROGRAM_NAME)
		os.Exit(1)
	}
	var files []FileInfo
	for i := 1; i < len(os.Args); i++ {
		file_path := os.Args[i]

		file, err := os.Open(file_path)
		if err != nil {
			fmt.Printf("Error opening file: %q\n", err.Error())
			os.Exit(1)
		}

		fi, err := file.Stat()
		if err != nil {
			fmt.Printf("Error stat'ing file: %q\n", err.Error())
			os.Exit(1)
		}

		if fi.Size() < 8 {
			fmt.Printf("File %s is too short to be edited\n", file_path)
			os.Exit(1)
		}

		mm, err := mmap.Map(file, mmap.RDONLY, 0)
		if err != nil {
			fmt.Printf("Error mmap'ing file: %q\n", err.Error())
			os.Exit(1)
		}
		files = append(files, FileInfo{filename: path.Base(file_path), bytes: mm})
	}

	err = termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	style := defaultStyle()
	termbox.SetOutputMode(outputMode)

	mainLoop(files, style)
}
<|endoftext|>


<commit_before>package xmpp

import (
	"fmt"
	"net"
)

const (
	// Standard port for XMPP clients to connect to.
	XMPP_CLIENT_PORT = 5222
)

// Perform a DNS SRV lookup and return an ordered list of "host:port" TCP
// addresses for the JID's home server. If no SRV records are found then assume
// the JID's domain is also the home server.
func HomeServerAddrs(jid JID) (addr []string, err error) {

	// DNS lookup.
	_, addrs, err := net.LookupSRV("xmpp-client", "tcp", jid.Domain)
	if err != nil {
		return
	}

	// If there's nothing in DNS then assume the JID's domain and the standard
	// port will work.
	if len(addrs) == 0 {
		addr = []string{fmt.Sprintf("%s:%d", jid.Domain, XMPP_CLIENT_PORT)}
		return
	}

	// Build list of "host:port" strings.
	for _, a := range addrs {
		addr = append(addr, fmt.Sprintf("%s:%d", a.Target, a.Port))
	}
	return
}
<commit_msg>Relax error checking for home server lookup to handle localhost, etc.<commit_after>package xmpp

import (
	"fmt"
	"net"
)

const (
	// Standard port for XMPP clients to connect to.
	XMPP_CLIENT_PORT = 5222
)

// Perform a DNS SRV lookup and return an ordered list of "host:port" TCP
// addresses for the JID's home server. If no SRV records are found then assume
// the JID's domain is also the home server.
func HomeServerAddrs(jid JID) (addr []string, err error) {

	// DNS lookup.
	_, addrs, _ := net.LookupSRV("xmpp-client", "tcp", jid.Domain)

	// If there's nothing in DNS then assume the JID's domain and the standard
	// port will work.
	if len(addrs) == 0 {
		addr = []string{fmt.Sprintf("%s:%d", jid.Domain, XMPP_CLIENT_PORT)}
		return
	}

	// Build list of "host:port" strings.
	for _, a := range addrs {
		addr = append(addr, fmt.Sprintf("%s:%d", a.Target, a.Port))
	}
	return
}
<|endoftext|>


<commit_before>package state

import (
	"fmt"

	"github.com/juju/errors"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"
)

var _ RebootFlagSetter = (*Machine)(nil)
var _ RebootActionGetter = (*Machine)(nil)

// RebootAction defines the action a machine should
// take when a hook needs to reboot
type RebootAction string

const (
	// ShouldDoNothing instructs a machine agent that no action
	// is required on its part
	ShouldDoNothing RebootAction = "noop"
	// ShouldReboot instructs a machine to reboot
	// this happens when a hook running on a machine, requests
	// a reboot
	ShouldReboot RebootAction = "reboot"
	// ShouldShutdown instructs a machine to shut down. This usually
	// happens when running inside a container, and a hook on the parent
	// machine requests a reboot
	ShouldShutdown RebootAction = "shutdown"
)

// rebootDoc will hold the reboot flag for a machine.
type rebootDoc struct {
	DocID   string `bson:"_id"`
	Id      string `bson:"machineid"`
	EnvUUID string `bson:"env-uuid"`
}

func (m *Machine) setFlag() error {
	if m.Life() == Dead {
		return mgo.ErrNotFound
	}
	ops := []txn.Op{{
		C:      machinesC,
		Id:     m.doc.DocID,
		Assert: notDeadDoc,
	}, {
		C:      rebootC,
		Id:     m.doc.DocID,
		Insert: &rebootDoc{Id: m.Id()},
	}}
	err := m.st.runTransaction(ops)
	if err == txn.ErrAborted {
		return mgo.ErrNotFound
	} else if err != nil {
		return errors.Errorf("failed to set reboot flag: %v", err)
	}
	return nil
}

func removeRebootDocOp(st *State, machineId string) txn.Op {
	op := txn.Op{
		C:      rebootC,
		Id:     st.docID(machineId),
		Remove: true,
	}
	return op
}

func (m *Machine) clearFlag() error {
	reboot, closer := m.st.getCollection(rebootC)
	defer closer()

	docID := m.doc.DocID
	count, err := reboot.FindId(docID).Count()
	if count == 0 {
		return nil
	}
	ops := []txn.Op{removeRebootDocOp(m.st, m.Id())}
	err = m.st.runTransaction(ops)
	if err != nil {
		return errors.Errorf("failed to clear reboot flag: %v", err)
	}
	return nil
}

// SetRebootFlag sets the reboot flag of a machine to a boolean value. It will also
// do a lazy create of a reboot document if needed; i.e. If a document
// does not exist yet for this machine, it will create it.
func (m *Machine) SetRebootFlag(flag bool) error {
	if flag {
		return m.setFlag()
	}
	return m.clearFlag()
}

// GetRebootFlag returns the reboot flag for this machine.
func (m *Machine) GetRebootFlag() (bool, error) {
	rebootCol, closer := m.st.getCollection(rebootC)
	defer closer()

	count, err := rebootCol.FindId(m.doc.DocID).Count()
	if err != nil {
		return false, fmt.Errorf("failed to get reboot flag: %v", err)
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}

func (m *Machine) machinesToCareAboutRebootsFor() []string {
	var possibleIds []string
	for currentId := m.Id(); currentId != ""; {
		possibleIds = append(possibleIds, currentId)
		currentId = ParentId(currentId)
	}
	return possibleIds
}

// ShouldRebootOrShutdown check if the current node should reboot or shutdown
// If we are a container, and our parent needs to reboot, this should return:
// ShouldShutdown
func (m *Machine) ShouldRebootOrShutdown() (RebootAction, error) {
	rebootCol, closer := m.st.getCollection(rebootC)
	defer closer()

	machines := m.machinesToCareAboutRebootsFor()

	docs := []rebootDoc{}
	sel := bson.D{{"machineid", bson.D{{"$in", machines}}}}
	if err := rebootCol.Find(sel).All(&docs); err != nil {
		return ShouldDoNothing, errors.Trace(err)
	}

	iNeedReboot := false
	for _, val := range docs {
		if val.Id != m.doc.Id {
			return ShouldShutdown, nil
		}
		iNeedReboot = true
	}
	if iNeedReboot {
		return ShouldReboot, nil
	}
	return ShouldDoNothing, nil
}

type RebootFlagSetter interface {
	SetRebootFlag(flag bool) error
}

type RebootActionGetter interface {
	ShouldRebootOrShutdown() (RebootAction, error)
}
<commit_msg>state: added env life assertion for rebootC<commit_after>package state

import (
	"fmt"

	"github.com/juju/errors"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"
)

var _ RebootFlagSetter = (*Machine)(nil)
var _ RebootActionGetter = (*Machine)(nil)

// RebootAction defines the action a machine should
// take when a hook needs to reboot
type RebootAction string

const (
	// ShouldDoNothing instructs a machine agent that no action
	// is required on its part
	ShouldDoNothing RebootAction = "noop"
	// ShouldReboot instructs a machine to reboot
	// this happens when a hook running on a machine, requests
	// a reboot
	ShouldReboot RebootAction = "reboot"
	// ShouldShutdown instructs a machine to shut down. This usually
	// happens when running inside a container, and a hook on the parent
	// machine requests a reboot
	ShouldShutdown RebootAction = "shutdown"
)

// rebootDoc will hold the reboot flag for a machine.
type rebootDoc struct {
	DocID   string `bson:"_id"`
	Id      string `bson:"machineid"`
	EnvUUID string `bson:"env-uuid"`
}

func (m *Machine) setFlag() error {
	if m.Life() == Dead {
		return mgo.ErrNotFound
	}
	ops := []txn.Op{
		assertEnvAliveOp(m.st.EnvironUUID()),
		{
			C:      machinesC,
			Id:     m.doc.DocID,
			Assert: notDeadDoc,
		}, {
			C:      rebootC,
			Id:     m.doc.DocID,
			Insert: &rebootDoc{Id: m.Id()},
		},
	}
	err := m.st.runTransaction(ops)
	if err == txn.ErrAborted {
		if err := checkEnvLife(m.st); err != nil {
			return errors.Trace(err)
		}
		return mgo.ErrNotFound
	} else if err != nil {
		return errors.Errorf("failed to set reboot flag: %v", err)
	}
	return nil
}

func removeRebootDocOp(st *State, machineId string) txn.Op {
	op := txn.Op{
		C:      rebootC,
		Id:     st.docID(machineId),
		Remove: true,
	}
	return op
}

func (m *Machine) clearFlag() error {
	reboot, closer := m.st.getCollection(rebootC)
	defer closer()

	docID := m.doc.DocID
	count, err := reboot.FindId(docID).Count()
	if count == 0 {
		return nil
	}
	ops := []txn.Op{removeRebootDocOp(m.st, m.Id())}
	err = m.st.runTransaction(ops)
	if err != nil {
		return errors.Errorf("failed to clear reboot flag: %v", err)
	}
	return nil
}

// SetRebootFlag sets the reboot flag of a machine to a boolean value. It will also
// do a lazy create of a reboot document if needed; i.e. If a document
// does not exist yet for this machine, it will create it.
func (m *Machine) SetRebootFlag(flag bool) error {
	if flag {
		return m.setFlag()
	}
	return m.clearFlag()
}

// GetRebootFlag returns the reboot flag for this machine.
func (m *Machine) GetRebootFlag() (bool, error) {
	rebootCol, closer := m.st.getCollection(rebootC)
	defer closer()

	count, err := rebootCol.FindId(m.doc.DocID).Count()
	if err != nil {
		return false, fmt.Errorf("failed to get reboot flag: %v", err)
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}

func (m *Machine) machinesToCareAboutRebootsFor() []string {
	var possibleIds []string
	for currentId := m.Id(); currentId != ""; {
		possibleIds = append(possibleIds, currentId)
		currentId = ParentId(currentId)
	}
	return possibleIds
}

// ShouldRebootOrShutdown check if the current node should reboot or shutdown
// If we are a container, and our parent needs to reboot, this should return:
// ShouldShutdown
func (m *Machine) ShouldRebootOrShutdown() (RebootAction, error) {
	rebootCol, closer := m.st.getCollection(rebootC)
	defer closer()

	machines := m.machinesToCareAboutRebootsFor()

	docs := []rebootDoc{}
	sel := bson.D{{"machineid", bson.D{{"$in", machines}}}}
	if err := rebootCol.Find(sel).All(&docs); err != nil {
		return ShouldDoNothing, errors.Trace(err)
	}

	iNeedReboot := false
	for _, val := range docs {
		if val.Id != m.doc.Id {
			return ShouldShutdown, nil
		}
		iNeedReboot = true
	}
	if iNeedReboot {
		return ShouldReboot, nil
	}
	return ShouldDoNothing, nil
}

type RebootFlagSetter interface {
	SetRebootFlag(flag bool) error
}

type RebootActionGetter interface {
	ShouldRebootOrShutdown() (RebootAction, error)
}
<|endoftext|>


<commit_before>package storage

import (
	"errors"
	"os"
)

var (
	// ErrIDMismatch returned when read Header.ID is not equal to provided Link.ID and is possible data corruption.
	ErrIDMismatch = errors.New("BulkBackend Header.ID != Link.ID")
)

// An BulkBackend describes a backend that is used for file store.
type BulkBackend interface {
	ReadAt(b []byte, off int64) (int, error)
	WriteAt(b []byte, off int64) (int, error)
	Stat() (os.FileInfo, error)
}

// Bulk is collection of data slices, prepended with File header. Implements basic operations on files.
type Bulk struct {
	Backend BulkBackend
}

// ReadHeader returns Header and error, if any, reading File by Link from backend.
func (b Bulk) ReadHeader(l Link, buf []byte) (Header, error) {
	var (
		f Header
	)
	f.ID = l.ID
	f.Offset = l.Offset
	_, err := b.Backend.ReadAt(buf[:LinkStructureSize], l.Offset)
	if err != nil {
		return f, err
	}
	f.Read(buf[:LinkStructureSize])
	if f.ID != l.ID {
		return f, ErrIDMismatch
	}
	return f, err
}

// ReadData reads h.Size bytes into buffer from f.DataOffset.
func (b Bulk) ReadData(h Header, buf []byte) error {
	buf = buf[:h.Size]
	_, err := b.Backend.ReadAt(buf, h.DataOffset())
	return err
}

// Write returns error if any, writing Header and data to backend.
func (b Bulk) Write(h Header, data []byte) error {
	// saving first HeaderStructureSize bytes to temporary slice on stack
	tmp := make([]byte, HeaderStructureSize)
	copy(tmp, data[:HeaderStructureSize])

	// serializing header to data, preventing heap escape
	h.Put(data[:HeaderStructureSize])
	if _, err := b.Backend.WriteAt(data[:HeaderStructureSize], h.Offset); err != nil {
		return err
	}
	// loading back first bytes and writing data
	copy(data[:HeaderStructureSize], tmp)
	_, err := b.Backend.WriteAt(data, h.DataOffset())
	return err
}
<commit_msg>prevent data corruption<commit_after>package storage

import (
	"errors"
	"os"
)

var (
	// ErrIDMismatch returned when read Header.ID is not equal to provided Link.ID and is possible data corruption.
	ErrIDMismatch = errors.New("BulkBackend Header.ID != Link.ID")
)

// An BulkBackend describes a backend that is used for file store.
type BulkBackend interface {
	ReadAt(b []byte, off int64) (int, error)
	WriteAt(b []byte, off int64) (int, error)
	Stat() (os.FileInfo, error)
}

// Bulk is collection of data slices, prepended with File header. Implements basic operations on files.
type Bulk struct {
	Backend BulkBackend
}

// ReadHeader returns Header and error, if any, reading File by Link from backend.
func (b Bulk) ReadHeader(l Link, buf []byte) (Header, error) {
	var (
		f Header
	)
	f.ID = l.ID
	f.Offset = l.Offset
	_, err := b.Backend.ReadAt(buf[:LinkStructureSize], l.Offset)
	if err != nil {
		return f, err
	}
	f.Read(buf[:LinkStructureSize])
	if f.ID != l.ID {
		return f, ErrIDMismatch
	}
	return f, err
}

// ReadData reads h.Size bytes into buffer from f.DataOffset.
func (b Bulk) ReadData(h Header, buf []byte) error {
	buf = buf[:h.Size]
	_, err := b.Backend.ReadAt(buf, h.DataOffset())
	return err
}

// Write returns error if any, writing Header and data to backend.
func (b Bulk) Write(h Header, data []byte) error {
	// saving first HeaderStructureSize bytes to temporary slice on stack
	tmp := make([]byte, HeaderStructureSize)
	copy(tmp, data[:HeaderStructureSize])
	// serializing header to data, preventing heap escape
	h.Put(data[:HeaderStructureSize])
	_, err := b.Backend.WriteAt(data[:HeaderStructureSize], h.Offset)
	// loading back first bytes
	copy(data[:HeaderStructureSize], tmp)
	if err != nil {
		return err
	}
	_, err = b.Backend.WriteAt(data, h.DataOffset())
	return err
}
<|endoftext|>


<commit_before>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	"github.com/markus-wa/demoinfocs-golang/events"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        int64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *Parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}
	p.processModelPreCacheUpdate()
	p.bitReader.EndChunk()
}

func (p *Parser) parseSingleStringTable(name string) {
	strings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < strings; i++ {
		stringName := p.bitReader.ReadString()
		if len(stringName) >= 100 {
			panic("Someone said that Roy said I should panic")
		}
		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)
			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.rawPlayers[int(pid)] = player

			case stNameInstanceBaseline:
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.instanceBaselines[int(pid)] = data

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default:
				// Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()
			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		// Create fake CreateStringTable and handle it like one of those
		cTab.NumEntries = tab.NumChangedEntries
		cTab.StringData = tab.StringData
		p.processStringTable(cTab)
	}
}

func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	p.processStringTable(tab)

	p.stringTables = append(p.stringTables, tab)

	p.eventDispatcher.Dispatch(events.StringTableCreatedEvent{TableName: tab.Name})
}

func (p *Parser) processStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp = nTmp >> 1
		nEntryBits++
	}
	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1
	for i := 0; i < int(tab.NumEntries); i++ {
		entryIndex := lastEntry + 1
		if !br.ReadBit() {
			entryIndex = int(br.ReadInt(nEntryBits))
		}

		lastEntry = entryIndex

		var entry string
		if entryIndex < 0 || entryIndex >= int(tab.MaxEntries) {
			panic("Something went to shit")
		}
		if br.ReadBit() {
			if br.ReadBit() {
				idx := br.ReadInt(5)
				bytes2cp := int(br.ReadInt(5))
				entry = hist[idx][:bytes2cp]

				entry += br.ReadString()
			} else {
				entry = br.ReadString()
			}
		}

		if len(hist) > 31 {
			hist = hist[1:]
		}
		hist = append(hist, entry)

		var userdat []byte
		if br.ReadBit() {
			if tab.UserDataFixedSize {
				// Should always be < 8 bits => use faster ReadBitsToByte() over ReadBits()
				userdat = []byte{br.ReadBitsToByte(int(tab.UserDataSizeBits))}
			} else {
				userdat = br.ReadBytes(int(br.ReadInt(14)))
			}
		}

		if len(userdat) == 0 {
			break
		}

		switch tab.Name {
		case stNameUserInfo:
			p.rawPlayers[entryIndex] = parsePlayerInfo(bytes.NewReader(userdat))

		case stNameInstanceBaseline:
			classID, err := strconv.ParseInt(entry, 10, 64)
			if err != nil {
				panic("WTF VOLVO PLS")
			}
			p.instanceBaselines[int(classID)] = userdat

		case stNameModelPreCache:
			p.modelPreCache[entryIndex] = entry
		}
	}

	if tab.Name == stNameModelPreCache {
		p.processModelPreCacheUpdate()
	}

	br.Pool()
}

func parsePlayerInfo(reader io.Reader) *playerInfo {
	br := bit.NewSmallBitReader(reader)

	res := &playerInfo{
		version:     int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		xuid:        int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		name:        br.ReadCString(128),
		userID:      int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		guid:        br.ReadCString(33),
		friendsID:   int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		friendsName: br.ReadCString(128),

		isFakePlayer: br.ReadSingleByte()&0xff != 0,
		isHltv:       br.ReadSingleByte()&0xff != 0,

		customFiles0: int(br.ReadInt(32)),
		customFiles1: int(br.ReadInt(32)),
		customFiles2: int(br.ReadInt(32)),
		customFiles3: int(br.ReadInt(32)),

		filesDownloaded: br.ReadSingleByte(),
	}

	br.Pool()
	return res
}
<commit_msg>Fix players being skipped in processStringTable (#52)<commit_after>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	"github.com/markus-wa/demoinfocs-golang/events"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        int64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *Parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}
	p.processModelPreCacheUpdate()
	p.bitReader.EndChunk()
}

func (p *Parser) parseSingleStringTable(name string) {
	strings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < strings; i++ {
		stringName := p.bitReader.ReadString()
		if len(stringName) >= 100 {
			panic("Someone said that Roy said I should panic")
		}
		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)
			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.rawPlayers[int(pid)] = player

			case stNameInstanceBaseline:
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.instanceBaselines[int(pid)] = data

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default:
				// Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()
			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		// Create fake CreateStringTable and handle it like one of those
		cTab.NumEntries = tab.NumChangedEntries
		cTab.StringData = tab.StringData
		p.processStringTable(cTab)
	}
}

func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	p.processStringTable(tab)

	p.stringTables = append(p.stringTables, tab)

	p.eventDispatcher.Dispatch(events.StringTableCreatedEvent{TableName: tab.Name})
}

func (p *Parser) processStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp = nTmp >> 1
		nEntryBits++
	}
	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1
	for i := 0; i < int(tab.NumEntries); i++ {
		entryIndex := lastEntry + 1
		if !br.ReadBit() {
			entryIndex = int(br.ReadInt(nEntryBits))
		}

		lastEntry = entryIndex

		var entry string
		if entryIndex < 0 || entryIndex >= int(tab.MaxEntries) {
			panic("Something went to shit")
		}
		if br.ReadBit() {
			if br.ReadBit() {
				idx := br.ReadInt(5)
				bytes2cp := int(br.ReadInt(5))
				entry = hist[idx][:bytes2cp]

				entry += br.ReadString()
			} else {
				entry = br.ReadString()
			}
		}

		if len(hist) > 31 {
			hist = hist[1:]
		}
		hist = append(hist, entry)

		var userdat []byte
		if br.ReadBit() {
			if tab.UserDataFixedSize {
				// Should always be < 8 bits => use faster ReadBitsToByte() over ReadBits()
				userdat = []byte{br.ReadBitsToByte(int(tab.UserDataSizeBits))}
			} else {
				userdat = br.ReadBytes(int(br.ReadInt(14)))
			}
		}

		if len(userdat) == 0 {
			continue
		}

		switch tab.Name {
		case stNameUserInfo:
			p.rawPlayers[entryIndex] = parsePlayerInfo(bytes.NewReader(userdat))

		case stNameInstanceBaseline:
			classID, err := strconv.ParseInt(entry, 10, 64)
			if err != nil {
				panic("WTF VOLVO PLS")
			}
			p.instanceBaselines[int(classID)] = userdat

		case stNameModelPreCache:
			p.modelPreCache[entryIndex] = entry
		}
	}

	if tab.Name == stNameModelPreCache {
		p.processModelPreCacheUpdate()
	}

	br.Pool()
}

func parsePlayerInfo(reader io.Reader) *playerInfo {
	br := bit.NewSmallBitReader(reader)

	res := &playerInfo{
		version:     int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		xuid:        int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		name:        br.ReadCString(128),
		userID:      int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		guid:        br.ReadCString(33),
		friendsID:   int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		friendsName: br.ReadCString(128),

		isFakePlayer: br.ReadSingleByte()&0xff != 0,
		isHltv:       br.ReadSingleByte()&0xff != 0,

		customFiles0: int(br.ReadInt(32)),
		customFiles1: int(br.ReadInt(32)),
		customFiles2: int(br.ReadInt(32)),
		customFiles3: int(br.ReadInt(32)),

		filesDownloaded: br.ReadSingleByte(),
	}

	br.Pool()
	return res
}
<|endoftext|>