<commit_before>package common

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
	"time"
)

func TestIsEnabled(t *testing.T) {
	t.Parallel()
	expected := "Enabled"
	actual := IsEnabled(true)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}

	expected = "Disabled"
	actual = IsEnabled(false)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}
}

func TestGetMD5(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the MD5 function in common!")
	var expectedOutput = []byte("18fddf4a41ba90a7352765e62e7a8744")
	actualOutput := GetMD5(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, []byte(actualStr)))
	}

}

func TestGetSHA512(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA512 function in common!")
	var expectedOutput = []byte("a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b")
	actualOutput := GetSHA512(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestGetSHA256(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA256 function in common!")
	var expectedOutput = []byte("0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303")
	actualOutput := GetSHA256(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestStringToLower(t *testing.T) {
	t.Parallel()
	upperCaseString := "HEY MAN"
	expectedResult := "hey man"
	actualResult := StringToLower(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestStringToUpper(t *testing.T) {
	t.Parallel()
	upperCaseString := "hey man"
	expectedResult := "HEY MAN"
	actualResult := StringToUpper(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestHexEncodeToString(t *testing.T) {
	t.Parallel()
	originalInput := []byte("string")
	expectedOutput := "737472696e67"
	actualResult := HexEncodeToString(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestBase64Decode(t *testing.T) {
	t.Parallel()
	originalInput := "aGVsbG8="
	expectedOutput := []byte("hello")
	actualResult, err := Base64Decode(originalInput)
	if !bytes.Equal(actualResult, expectedOutput) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s", expectedOutput, actualResult, err))
	}
}

func TestBase64Encode(t *testing.T) {
	t.Parallel()
	originalInput := []byte("hello")
	expectedOutput := "aGVsbG8="
	actualResult := Base64Encode(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringSliceDifference(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello"}
	originalInputTwo := []string{"moto"}
	expectedOutput := []string{"hello moto"}
	actualResult := StringSliceDifference(originalInputOne, originalInputTwo)
	if reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringContains(t *testing.T) {
	t.Parallel()
	originalInput := "hello"
	originalInputSubstring := "he"
	expectedOutput := true
	actualResult := StringContains(originalInput, originalInputSubstring)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
	}
}

func TestJoinStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello", "moto"}
	seperator := ","
	expectedOutput := "hello,moto"
	actualResult := JoinStrings(originalInputOne, seperator)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestSplitStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := "hello,moto"
	seperator := ","
	expectedOutput := []string{"hello", "moto"}
	actualResult := SplitStrings(originalInputOne, seperator)
	if !reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestRoundFloat(t *testing.T) {
	t.Parallel()
	originalInput := float64(1.4545445445)
	precisionInput := 2
	expectedOutput := float64(1.45)
	actualResult := RoundFloat(originalInput, precisionInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(0.01)
	actualResult := CalculateFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateAmountWithFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(1.01)
	actualResult := CalculateAmountWithFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageGainOrLoss(t *testing.T) {
	t.Parallel()
	originalInput := float64(9300)
	secondInput := float64(9000)
	expectedOutput := 3.3333333333333335
	actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageDifference(t *testing.T) {
	t.Parallel()
	originalInput := float64(10)
	secondAmount := float64(5)
	expectedOutput := 66.66666666666666
	actualResult := CalculatePercentageDifference(originalInput, secondAmount)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateNetProfit(t *testing.T) {
	t.Parallel()
	amount := float64(5)
	priceThen := float64(1)
	priceNow := float64(10)
	costs := float64(1)
	expectedOutput := float64(44)
	actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestExtractHost(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := "localhost"
	actualResult := ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}

	address = "192.168.1.100:1337"
	expectedOutput = "192.168.1.100"
	actualResult = ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestExtractPort(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := 1337
	actualResult := ExtractPort(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampToTime(t *testing.T) {
	t.Parallel()
	testTime := int64(1489439831)
	tm := time.Unix(testTime, 0)
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult := UnixTimestampToTime(testTime)
	if tm.String() != actualResult.String() {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampStrToTime(t *testing.T) {
	t.Parallel()
	testTime := "1489439831"
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult, err := UnixTimestampStrToTime(testTime)
	if err != nil {
		t.Error(err)
	}

	if actualResult.UTC().String() != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestGetURIPath(t *testing.T) {
	// mapping of input vs expected result
	testTable := map[string]string{
		"https://api.gdax.com/accounts":         "/accounts",
		"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
		"ht:tp:/invalidurl":                     "",
	}
	for testInput, expectedOutput := range testTable {
		actualOutput := GetURIPath(testInput)
		if actualOutput != expectedOutput {
			t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.",
				expectedOutput, actualOutput))
		}
	}
}
<commit_msg>run test in parallel with (and only with) other parallel tests.<commit_after>package common

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
	"time"
)

func TestIsEnabled(t *testing.T) {
	t.Parallel()
	expected := "Enabled"
	actual := IsEnabled(true)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}

	expected = "Disabled"
	actual = IsEnabled(false)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}
}

func TestGetMD5(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the MD5 function in common!")
	var expectedOutput = []byte("18fddf4a41ba90a7352765e62e7a8744")
	actualOutput := GetMD5(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, []byte(actualStr)))
	}

}

func TestGetSHA512(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA512 function in common!")
	var expectedOutput = []byte("a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b")
	actualOutput := GetSHA512(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestGetSHA256(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA256 function in common!")
	var expectedOutput = []byte("0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303")
	actualOutput := GetSHA256(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestStringToLower(t *testing.T) {
	t.Parallel()
	upperCaseString := "HEY MAN"
	expectedResult := "hey man"
	actualResult := StringToLower(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestStringToUpper(t *testing.T) {
	t.Parallel()
	upperCaseString := "hey man"
	expectedResult := "HEY MAN"
	actualResult := StringToUpper(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestHexEncodeToString(t *testing.T) {
	t.Parallel()
	originalInput := []byte("string")
	expectedOutput := "737472696e67"
	actualResult := HexEncodeToString(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestBase64Decode(t *testing.T) {
	t.Parallel()
	originalInput := "aGVsbG8="
	expectedOutput := []byte("hello")
	actualResult, err := Base64Decode(originalInput)
	if !bytes.Equal(actualResult, expectedOutput) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s", expectedOutput, actualResult, err))
	}
}

func TestBase64Encode(t *testing.T) {
	t.Parallel()
	originalInput := []byte("hello")
	expectedOutput := "aGVsbG8="
	actualResult := Base64Encode(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringSliceDifference(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello"}
	originalInputTwo := []string{"moto"}
	expectedOutput := []string{"hello moto"}
	actualResult := StringSliceDifference(originalInputOne, originalInputTwo)
	if reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringContains(t *testing.T) {
	t.Parallel()
	originalInput := "hello"
	originalInputSubstring := "he"
	expectedOutput := true
	actualResult := StringContains(originalInput, originalInputSubstring)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
	}
}

func TestJoinStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello", "moto"}
	seperator := ","
	expectedOutput := "hello,moto"
	actualResult := JoinStrings(originalInputOne, seperator)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestSplitStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := "hello,moto"
	seperator := ","
	expectedOutput := []string{"hello", "moto"}
	actualResult := SplitStrings(originalInputOne, seperator)
	if !reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestRoundFloat(t *testing.T) {
	t.Parallel()
	originalInput := float64(1.4545445445)
	precisionInput := 2
	expectedOutput := float64(1.45)
	actualResult := RoundFloat(originalInput, precisionInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(0.01)
	actualResult := CalculateFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateAmountWithFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(1.01)
	actualResult := CalculateAmountWithFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageGainOrLoss(t *testing.T) {
	t.Parallel()
	originalInput := float64(9300)
	secondInput := float64(9000)
	expectedOutput := 3.3333333333333335
	actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageDifference(t *testing.T) {
	t.Parallel()
	originalInput := float64(10)
	secondAmount := float64(5)
	expectedOutput := 66.66666666666666
	actualResult := CalculatePercentageDifference(originalInput, secondAmount)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateNetProfit(t *testing.T) {
	t.Parallel()
	amount := float64(5)
	priceThen := float64(1)
	priceNow := float64(10)
	costs := float64(1)
	expectedOutput := float64(44)
	actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestExtractHost(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := "localhost"
	actualResult := ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}

	address = "192.168.1.100:1337"
	expectedOutput = "192.168.1.100"
	actualResult = ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestExtractPort(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := 1337
	actualResult := ExtractPort(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampToTime(t *testing.T) {
	t.Parallel()
	testTime := int64(1489439831)
	tm := time.Unix(testTime, 0)
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult := UnixTimestampToTime(testTime)
	if tm.String() != actualResult.String() {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampStrToTime(t *testing.T) {
	t.Parallel()
	testTime := "1489439831"
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult, err := UnixTimestampStrToTime(testTime)
	if err != nil {
		t.Error(err)
	}

	if actualResult.UTC().String() != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestGetURIPath(t *testing.T) {
	t.Parallel()
	// mapping of input vs expected result
	testTable := map[string]string{
		"https://api.gdax.com/accounts":         "/accounts",
		"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
		"ht:tp:/invalidurl":                     "",
	}
	for testInput, expectedOutput := range testTable {
		actualOutput := GetURIPath(testInput)
		if actualOutput != expectedOutput {
			t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.",
				expectedOutput, actualOutput))
		}
	}
}
<|endoftext|>


<commit_before>package orb

// A Point is a Lon/Lat 2d point.
type Point [2]float64

// NewPoint creates a new point.
func NewPoint(lon, lat float64) Point {
	return Point{lon, lat}
}

// GeoJSONType returns the GeoJSON type for the object.
func (p Point) GeoJSONType() string {
	return "Point"
}

// Dimensions returns 0 because a point is a 0d object.
func (p Point) Dimensions() int {
	return 0
}

// Bound returns a single point bound of the point.
func (p Point) Bound() Bound {
	return Bound{p, p}
}

// Equal checks if the point represents the same point or vector.
func (p Point) Equal(point Point) bool {
	return p[0] == point[0] && p[1] == point[1]
}
<commit_msg>add point helper functions<commit_after>package orb

// A Point is a Lon/Lat 2d point.
type Point [2]float64

// NewPoint creates a new point.
func NewPoint(lon, lat float64) Point {
	return Point{lon, lat}
}

// GeoJSONType returns the GeoJSON type for the object.
func (p Point) GeoJSONType() string {
	return "Point"
}

// Dimensions returns 0 because a point is a 0d object.
func (p Point) Dimensions() int {
	return 0
}

// Bound returns a single point bound of the point.
func (p Point) Bound() Bound {
	return Bound{p, p}
}

// Y returns the vertical coordinate of the point.
func (p Point) Y() float64 {
	return p[1]
}

// X returns the horizontal coordinate of the point.
func (p Point) X() float64 {
	return p[0]
}

// Lat returns the vertical, latitude coordinate of the point.
func (p Point) Lat() float64 {
	return p[1]
}

// Lon returns the horizontal, longitude coordinate of the point.
func (p Point) Lon() float64 {
	return p[0]
}

// Equal checks if the point represents the same point or vector.
func (p Point) Equal(point Point) bool {
	return p[0] == point[0] && p[1] == point[1]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"rpc"
	"rpc/jsonrpc"
	"sync"
	"time"

	"github.com/bytbox/procfs.go/procfs"
)

var pfs *procfs.ProcFS
var pfsMutex sync.Mutex
var interval = flag.Int64("interval", 10, "update interval")
var http_port = flag.String("http", ":6070", "HTTP service port")

func main() {
	flag.Parse()

	go updater()
	go serveHTTP()

	<-make(chan int)
}

// Maintain an updated version of ProcFS
func updater() {
	ticker := time.Tick(1e9 * *interval)
	for true {
		log.Print("Updating...")
		sn := time.Nanoseconds()
		var pfs2 procfs.ProcFS
		pfs2.Fill()
		pfsMutex.Lock()
		pfs = &pfs2
		pfsMutex.Unlock()
		en := time.Nanoseconds()
		log.Print("Done in ", (en-sn)/1000, "μs")
		<-ticker
	}
}

type ProcFSServer struct {}

func (ProcFSServer) Get(req string, reply *procfs.ProcFS) error {
	reply = pfs
	return nil
}

func serveHTTP() {
	server := ProcFSServer{}
	rpc.RegisterName("ProcFS", server)
	http.HandleFunc("/", HTMLServer)
	http.HandleFunc("/jsonrpc.js", JSServer)
	http.HandleFunc("/_goRPC_", RPCServer)

	err := http.ListenAndServe(*http_port, http.DefaultServeMux)
	if err != nil {
		log.Print("ERR: ", err)
	}
}

func HTMLServer(w http.ResponseWriter, req *http.Request) {
	c, err := ioutil.ReadFile("html/proc.html")
	if err != nil {
		log.Fatal("ERR: html/index.html not openable")
		return
	}
	w.Write(c)
}

func JSServer(w http.ResponseWriter, req *http.Request) {
	c, err := ioutil.ReadFile("html/jsonrpc.js")
	if err != nil {
		log.Fatal("ERR: html/index.html not openable")
		return
	}
	w.Write(c)
}

func RPCServer(w http.ResponseWriter, req *http.Request) {
	h, _, err := w.(http.Hijacker).Hijack()
	if err != nil {
		log.Print("ERR: ", err)
	}
	connected := "200 Connected to JSON-RPC"
	io.WriteString(h, "HTTP/1.0 "+connected+"\n\n")
	codec := jsonrpc.NewServerCodec(h)
	rpc.ServeRequest(codec)
	h.Close()
}

<commit_msg>Bugfix<commit_after>package main

import (
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"rpc"
	"rpc/jsonrpc"
	"sync"
	"time"

	"github.com/bytbox/procfs.go/procfs"
)

var pfs *procfs.ProcFS
var pfsMutex sync.Mutex
var interval = flag.Int64("interval", 10, "update interval")
var http_port = flag.String("http", ":6070", "HTTP service port")

func main() {
	flag.Parse()

	go updater()
	go serveHTTP()

	<-make(chan int)
}

// Maintain an updated version of ProcFS
func updater() {
	ticker := time.Tick(1e9 * *interval)
	for true {
		log.Print("Updating...")
		sn := time.Nanoseconds()
		var pfs2 procfs.ProcFS
		pfs2.Fill()
		pfsMutex.Lock()
		pfs = &pfs2
		pfsMutex.Unlock()
		en := time.Nanoseconds()
		log.Print("Done in ", (en-sn)/1000, "μs")
		<-ticker
	}
}

type ProcFSServer struct {}

func (ProcFSServer) Get(req string, reply *procfs.ProcFS) error {
	*reply = *pfs
	return nil
}

func serveHTTP() {
	server := ProcFSServer{}
	rpc.RegisterName("ProcFS", server)
	http.HandleFunc("/", HTMLServer)
	http.HandleFunc("/jsonrpc.js", JSServer)
	http.HandleFunc("/_goRPC_", RPCServer)

	err := http.ListenAndServe(*http_port, http.DefaultServeMux)
	if err != nil {
		log.Print("ERR: ", err)
	}
}

func HTMLServer(w http.ResponseWriter, req *http.Request) {
	c, err := ioutil.ReadFile("html/proc.html")
	if err != nil {
		log.Fatal("ERR: html/index.html not openable")
		return
	}
	w.Write(c)
}

func JSServer(w http.ResponseWriter, req *http.Request) {
	c, err := ioutil.ReadFile("html/jsonrpc.js")
	if err != nil {
		log.Fatal("ERR: html/index.html not openable")
		return
	}
	w.Write(c)
}

func RPCServer(w http.ResponseWriter, req *http.Request) {
	h, _, err := w.(http.Hijacker).Hijack()
	if err != nil {
		log.Print("ERR: ", err)
	}
	connected := "200 Connected to JSON-RPC"
	io.WriteString(h, "HTTP/1.0 "+connected+"\n\n")
	codec := jsonrpc.NewServerCodec(h)
	rpc.ServeRequest(codec)
	h.Close()
}

<|endoftext|>


<commit_before>/*
   Copyright 2015 Ian Bishop

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

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

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

package main

import (
	"fmt"
	"log"
	"net"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/vishvananda/netlink"
)

//const routeCheckInterval = 30
const routeCheckInterval = 120

// snaplen should be large enough to capture the layers we're interested in
const snaplen = 100

type listener struct {
	sync.RWMutex
	ifname            string
	extChan, intChan  chan ndp
	errChan           chan error
	ruleNet           *net.IPNet
	started, finished bool
}

type sessionStatus int

// sessions track clients who've previously sent neighbor solicits
type session struct {
	upstream             *listener
	srcIP, dstIP, target net.IP
	status               sessionStatus
	expiry               time.Time
}

type ndp struct {
	payload gopacket.Payload
	icmp    layers.ICMPv6
	ip6     layers.IPv6
	eth     layers.Ethernet
}

const (
	waiting sessionStatus = iota
	valid
	invalid

	ttl = time.Duration(500 * time.Millisecond)
)

func Proxy(wg *sync.WaitGroup, ifname string, rules []string) {
	defer wg.Done()

	var err error
	upstreams := make(map[string]*listener)
	// shared channels upstreams send to
	errChan := make(chan error)
	intChan := make(chan ndp)
	mainExtChan := make(chan ndp)
	tickChan := time.NewTicker(time.Second * routeCheckInterval).C

	defer func() {
		for _, upstream := range upstreams {
			close(upstream.extChan)
			delete(upstreams, upstream.ifname)
		}
	}()

	var sessions []session

	// launch handler for main interface 'ifname'
	l := &listener{ifname: ifname, intChan: intChan, extChan: mainExtChan, errChan: errChan}
	go l.handler()

	err = refreshRoutes(rules, intChan, errChan, upstreams)
	if err != nil {
		fmt.Printf("%s\n", err)
		return
	}

	for {
		select {
		case err = <-errChan:
			fmt.Printf("%s\n", err)
			return
		case n := <-intChan:
			sessions = proxyPacket(n, mainExtChan, upstreams, sessions)
		case <-tickChan:
			sessions = updateSessions(sessions)
			err := refreshRoutes(rules, intChan, errChan, upstreams)
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
		}
	}
}

func proxyPacket(n ndp, extChan chan ndp, upstreams map[string]*listener, sessions []session) []session {
	var target net.IP
	// IPv6 bounds check
	if len(n.payload) >= 16 {
		target = net.IP(n.payload[:16])
	} else {
		return sessions
	}

	switch n.icmp.TypeCode.Type() {
	case layers.ICMPv6TypeNeighborAdvertisement:
		for i, s := range sessions {
			if s.target.Equal(target) && sessions[i].status == waiting {
				vlog.Printf("advert, using existing session for target %s\n", target)
				sessions[i].status = valid
				n.ip6.DstIP = s.srcIP
				extChan <- n
				return sessions
			}
		}
	case layers.ICMPv6TypeNeighborSolicitation:
		if !n.ip6.DstIP.IsMulticast() {
			return sessions
		}
		for _, s := range sessions {
			if s.target.Equal(target) {

				switch s.status {
				case waiting, invalid:
					break
				case valid:
					// swap solicit for advert and send back out main interface
					vlog.Printf("solicit, using existing session for target %s\n", target)
					n.icmp.TypeCode = layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborAdvertisement, 0)
					n.ip6.DstIP = n.ip6.SrcIP
					n.ip6.SrcIP = nil
					extChan <- n
				}
				return sessions
			}
		}

		var s *session
		// if msg arrived from the main interface, then send to matching upstreams
		for _, upstream := range upstreams {
			if upstream.ruleNet.Contains(target) {
				vlog.Printf("session not found when handling solicit for target %s. Creating new session...\n", net.IP(n.payload[:16]))
				s = &session{
					upstream: upstream,
					srcIP:    n.ip6.SrcIP,
					dstIP:    n.ip6.DstIP,
					target:   target,
					status:   waiting,
					expiry:   time.Now().Add(ttl),
				}
			}
		}

		if s != nil {
			if !s.upstream.started {
				// launch upstream handler
				go s.upstream.handler()
			}
			sessions = append(sessions, *s)
			s.upstream.extChan <- n
		}
	}

	return sessions
}

func updateSessions(sessions []session) []session {
	vlog.Println("updating sessions...")
	for i := len(sessions) - 1; i >= 0; i-- {

		if sessions[i].expiry.After(time.Now()) {
			continue
		}

		switch sessions[i].status {
		case waiting:
			sessions[i].status = invalid
			sessions[i].expiry = time.Now().Add(ttl)
		default:
			vlog.Printf("remove session %d, target %s", i, sessions[i].target)
			sessions = append(sessions[:i], sessions[i+1:]...)
		}
	}
	return sessions
}

func refreshRoutes(rules []string, intChan chan ndp, errChan chan error, upstreams map[string]*listener) error {
	vlog.Println("refreshing routes...")
	for _, rule := range rules {
		_, ruleNet, err := net.ParseCIDR(rule)
		if err != nil {
			return fmt.Errorf("invalid rule '%s', %s", rule, err)
		}

		routes, err := netlink.RouteList(nil, netlink.FAMILY_V6)
		if err != nil {
			return fmt.Errorf("error enumerating routes, %s", err)
		}
		var route *netlink.Route
		for _, r := range routes {
			if r.Dst != nil && r.Dst.Contains(ruleNet.IP) {
				route = &r
				break
			}
		}

		if route == nil {
			// cancel any proxies for removed routes
			for _, upstream := range upstreams {
				if upstream.ruleNet.IP.Equal(ruleNet.IP) {
					log.Printf("route for upstream if %s went away. Removing listener...\n", upstream.ifname)
					close(upstream.extChan)
					delete(upstreams, upstream.ifname)
				}
			}
			// route not found, skip
			continue
		}
		links, err := netlink.LinkList()
		if err != nil {
			return fmt.Errorf("error enumerating links, %s", err)
		}
		for _, link := range links {
			if link.Attrs().Index == route.LinkIndex {
				if _, ok := upstreams[link.Attrs().Name]; !ok {
					log.Printf("new upstream for link '%s', rule '%s', route '%s'\n", link.Attrs().Name, rule, route.Dst)
					upstreams[link.Attrs().Name] = &listener{
						ifname:  link.Attrs().Name,
						extChan: make(chan ndp),
						intChan: intChan,
						errChan: errChan,
						ruleNet: ruleNet,
					}
				}
			}
		}
	}
	for name, listener := range upstreams {
		listener.RLock()
		if listener.finished {
			delete(upstreams, name)
		}
		listener.RUnlock()
	}
	return nil
}

func (l *listener) handler() {
	var err error
	var handle *pcap.Handle
	log.Printf("spawning listener for if %s\n", l.ifname)
	l.Lock()
	l.started = true
	l.Unlock()

	defer func() {
		if err != nil {
			l.errChan <- err
		}
		l.Lock()
		l.finished = true
		l.Unlock()
		log.Printf("exiting listener for if %s\n", l.ifname)
	}()

	handle, err = pcap.OpenLive(l.ifname, snaplen, false, pcap.BlockForever)
	if err != nil {
		err = fmt.Errorf("pcap open error: %s", err)
		return
	}
	defer handle.Close()

	var iface *net.Interface
	iface, err = net.InterfaceByName(l.ifname)
	if err != nil {
		return
	}

	var addrs []net.Addr
	var linklocal net.IP
	addrs, err = iface.Addrs()
	if err != nil {
		return
	}

	for _, addr := range addrs {
		switch v := addr.(type) {
		case *net.IPNet:
			if v.IP.IsLinkLocalUnicast() {
				linklocal = v.IP
			}
		}
	}

	if linklocal.IsUnspecified() {
		err = fmt.Errorf("error finding link local unicast address for if %s", l.ifname)
		return
	}

	var eth layers.Ethernet
	var ip6 layers.IPv6
	var ip6extensions layers.IPv6ExtensionSkipper
	var icmp layers.ICMPv6
	var payload gopacket.Payload
	decoded := []gopacket.LayerType{}
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip6, &ip6extensions, &icmp, &payload)
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	packetsChan := packetSource.Packets()
	for {
		select {
		case packet := <-packetsChan:
			parser.DecodeLayers(packet.Data(), &decoded)
			for _, layerType := range decoded {
				switch layerType {
				case layers.LayerTypeICMPv6:
					var target net.IP
					// IPv6 bounds check
					if len(payload) >= 16 {
						target = net.IP(payload[:16])
					} else {
						continue
					}

					switch icmp.TypeCode.Type() {
					case layers.ICMPv6TypeNeighborSolicitation, layers.ICMPv6TypeNeighborAdvertisement:
						n := ndp{eth: eth, ip6: ip6, icmp: icmp, payload: payload}
						vlog.Printf("%s\tread\t%s\tmac_src %s\tip6_src %s\tip6_dst %s\ttarget %s\n", l.ifname, icmp.TypeCode, eth.SrcMAC, ip6.SrcIP, ip6.DstIP, target)
						l.intChan <- n
					}
				}
			}

		case n, ok := <-l.extChan:
			if !ok {
				// channel was closed
				return
			}

			n.eth.DstMAC = nil
			if n.ip6.DstIP.IsLinkLocalMulticast() {
				// Ethernet MAC is derived by the four low-order octets of IPv6 address
				n.eth.DstMAC = append(net.HardwareAddr{0x33, 0x33}, n.ip6.DstIP[12:]...)
			} else {
				var neighbors []netlink.Neigh
				neighbors, err = netlink.NeighList(iface.Index, netlink.FAMILY_V6)
				if err != nil {
					return
				}
				for _, neighbor := range neighbors {
					if neighbor.IP.Equal(n.ip6.DstIP) {
						n.eth.DstMAC = neighbor.HardwareAddr
					}
				}
			}
			if n.eth.DstMAC == nil {
				vlog.Printf("%s: could not find destination MAC address. %s mac_src %s ip6_dst %s ip6_src %s target %s", l.ifname, n.icmp.TypeCode, n.eth.SrcMAC, n.ip6.DstIP, n.ip6.SrcIP, net.IP(n.payload[:16]))
				// Try Solicited-Node multicast address
				// dst IP is derived by the first 13 octets of multicast address +
				// last 3 octets of dst IP
				n.ip6.DstIP = append(net.IPv6linklocalallnodes[:13], n.ip6.DstIP[13:]...)
				n.eth.DstMAC = append(net.HardwareAddr{0x33, 0x33}, n.ip6.DstIP[12:]...)
			}
			n.eth.SrcMAC = iface.HardwareAddr
			n.ip6.SrcIP = linklocal
			buf := gopacket.NewSerializeBuffer()
			n.icmp.SetNetworkLayerForChecksum(&n.ip6)
			opts := gopacket.SerializeOptions{ComputeChecksums: true}
			switch n.icmp.TypeCode.Type() {
			case layers.ICMPv6TypeNeighborSolicitation:
				// source link-layer address opt type, opt length
				n.payload = append(n.payload[:16], 0x01, 0x01)
			case layers.ICMPv6TypeNeighborAdvertisement:
				// target link-layer address opt type, opt length
				n.payload = append(n.payload[:16], 0x02, 0x01)
				n.icmp.TypeBytes[0] = 0xc0 // router,solicit,override flags
			}
			n.payload = append(n.payload[:18], iface.HardwareAddr...)

			err = gopacket.SerializeLayers(buf, opts, &n.eth, &n.ip6, &n.icmp, &n.payload)
			if err != nil {
				err = fmt.Errorf("serialize layers error: %s", err)
				return
			}

			err = handle.WritePacketData(buf.Bytes())
			if err != nil {
				err = fmt.Errorf("pcap write error: %s", err)
				return
			}
			vlog.Printf("%s\twrite\t%s\tmac_dst %s\tip6_src %s\tip6_dst %s\ttarget %s\n", l.ifname, n.icmp.TypeCode, n.eth.DstMAC, n.ip6.SrcIP, n.ip6.DstIP, net.IP(n.payload[:16]))
		}
	}
}
<commit_msg>tweaks<commit_after>/*
   Copyright 2015 Ian Bishop

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

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

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

package main

import (
	"fmt"
	"log"
	"net"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/vishvananda/netlink"
)

type listener struct {
	sync.RWMutex
	ifname            string
	extChan, intChan  chan ndp
	errChan           chan error
	ruleNet           *net.IPNet
	started, finished bool
}

type sessionStatus int

// sessions track clients who've previously sent neighbor solicits
type session struct {
	upstream             *listener
	srcIP, dstIP, target net.IP
	status               sessionStatus
	expiry               time.Time
}

type ndp struct {
	payload gopacket.Payload
	icmp    layers.ICMPv6
	ip6     layers.IPv6
	eth     layers.Ethernet
}

const (
	waiting sessionStatus = iota
	valid
	invalid

	timeout = time.Duration(500 * time.Millisecond)
	ttl     = time.Duration(30 * time.Second)

	routeCheckInterval = 30

	// snaplen should be large enough to capture the layers we're interested in
	snaplen = 100
)

func Proxy(wg *sync.WaitGroup, ifname string, rules []string) {
	defer wg.Done()

	var err error
	upstreams := make(map[string]*listener)
	// shared channels upstreams send to
	errChan := make(chan error)
	intChan := make(chan ndp)
	mainExtChan := make(chan ndp)
	tickRouteChan := time.NewTicker(time.Second * routeCheckInterval).C
	tickSessChan := time.NewTicker(time.Millisecond * 100).C

	defer func() {
		for _, upstream := range upstreams {
			close(upstream.extChan)
			delete(upstreams, upstream.ifname)
		}
	}()

	var sessions []session

	// launch handler for main interface 'ifname'
	l := &listener{ifname: ifname, intChan: intChan, extChan: mainExtChan, errChan: errChan}
	go l.handler()

	err = refreshRoutes(rules, intChan, errChan, upstreams)
	if err != nil {
		fmt.Printf("%s\n", err)
		return
	}

	for {
		select {
		case err = <-errChan:
			fmt.Printf("%s\n", err)
			return
		case n := <-intChan:
			sessions = proxyPacket(n, mainExtChan, upstreams, sessions)
		case <-tickSessChan:
			sessions = updateSessions(sessions)
		case <-tickRouteChan:
			err := refreshRoutes(rules, intChan, errChan, upstreams)
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
		}
	}
}

func proxyPacket(n ndp, extChan chan ndp, upstreams map[string]*listener, sessions []session) []session {
	var target net.IP
	// IPv6 bounds check
	if len(n.payload) >= 16 {
		target = net.IP(n.payload[:16])
	} else {
		return sessions
	}

	switch n.icmp.TypeCode.Type() {
	case layers.ICMPv6TypeNeighborAdvertisement:
		for i, s := range sessions {
			if s.target.Equal(target) && sessions[i].status == waiting {
				vlog.Printf("advert, using existing session for target %s\n", target)
				sessions[i].status = valid
				sessions[i].expiry = time.Now().Add(ttl)
				n.ip6.DstIP = s.srcIP
				extChan <- n
				return sessions
			}
		}
	case layers.ICMPv6TypeNeighborSolicitation:
		if !n.ip6.DstIP.IsMulticast() {
			return sessions
		}
		for _, s := range sessions {
			if s.target.Equal(target) {

				switch s.status {
				case waiting, invalid:
					break
				case valid:
					// swap solicit for advert and send back out main interface
					vlog.Printf("solicit, using existing session for target %s\n", target)
					n.icmp.TypeCode = layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborAdvertisement, 0)
					n.ip6.DstIP = n.ip6.SrcIP
					n.ip6.SrcIP = nil
					extChan <- n
				}
				return sessions
			}
		}

		var s *session
		// if msg arrived from the main interface, then send to matching upstreams
		for _, upstream := range upstreams {
			if upstream.ruleNet.Contains(target) {
				vlog.Printf("session not found when handling solicit for target %s. Creating new session...\n", net.IP(n.payload[:16]))
				s = &session{
					upstream: upstream,
					srcIP:    n.ip6.SrcIP,
					dstIP:    n.ip6.DstIP,
					target:   target,
					status:   waiting,
					expiry:   time.Now().Add(timeout),
				}
			}
		}

		if s != nil {
			if !s.upstream.started {
				// launch upstream handler
				go s.upstream.handler()
			}
			sessions = append(sessions, *s)
			s.upstream.extChan <- n
		}
	}

	return sessions
}

func updateSessions(sessions []session) []session {
	for i := len(sessions) - 1; i >= 0; i-- {

		if sessions[i].expiry.After(time.Now()) {
			continue
		}

		switch sessions[i].status {
		case waiting:
			vlog.Printf("set waiting session %d to invalid, target %s", i, sessions[i].target)
			sessions[i].status = invalid
			sessions[i].expiry = time.Now().Add(ttl)
		default:
			vlog.Printf("remove session %d, target %s", i, sessions[i].target)
			sessions = append(sessions[:i], sessions[i+1:]...)
		}
	}
	return sessions
}

func refreshRoutes(rules []string, intChan chan ndp, errChan chan error, upstreams map[string]*listener) error {
	vlog.Println("refreshing routes...")
	for _, rule := range rules {
		_, ruleNet, err := net.ParseCIDR(rule)
		if err != nil {
			return fmt.Errorf("invalid rule '%s', %s", rule, err)
		}

		routes, err := netlink.RouteList(nil, netlink.FAMILY_V6)
		if err != nil {
			return fmt.Errorf("error enumerating routes, %s", err)
		}
		var route *netlink.Route
		for _, r := range routes {
			if r.Dst != nil && r.Dst.Contains(ruleNet.IP) {
				route = &r
				break
			}
		}

		if route == nil {
			// cancel any proxies for removed routes
			for _, upstream := range upstreams {
				if upstream.ruleNet.IP.Equal(ruleNet.IP) {
					log.Printf("route for upstream if %s went away. Removing listener...\n", upstream.ifname)
					close(upstream.extChan)
					delete(upstreams, upstream.ifname)
				}
			}
			// route not found, skip
			continue
		}
		links, err := netlink.LinkList()
		if err != nil {
			return fmt.Errorf("error enumerating links, %s", err)
		}
		for _, link := range links {
			if link.Attrs().Index == route.LinkIndex {
				if _, ok := upstreams[link.Attrs().Name]; !ok {
					log.Printf("new upstream for link '%s', rule '%s', route '%s'\n", link.Attrs().Name, rule, route.Dst)
					upstreams[link.Attrs().Name] = &listener{
						ifname:  link.Attrs().Name,
						extChan: make(chan ndp),
						intChan: intChan,
						errChan: errChan,
						ruleNet: ruleNet,
					}
				}
			}
		}
	}
	for name, listener := range upstreams {
		listener.RLock()
		if listener.finished {
			delete(upstreams, name)
		}
		listener.RUnlock()
	}
	return nil
}

func (l *listener) handler() {
	var err error
	var handle *pcap.Handle
	log.Printf("spawning listener for if %s\n", l.ifname)
	l.Lock()
	l.started = true
	l.Unlock()

	defer func() {
		if err != nil {
			l.errChan <- err
		}
		l.Lock()
		l.finished = true
		l.Unlock()
		log.Printf("exiting listener for if %s\n", l.ifname)
	}()

	// open interface in promiscuous mode in order to pickup solicited-node multicasts
	handle, err = pcap.OpenLive(l.ifname, snaplen, true, pcap.BlockForever)
	if err != nil {
		err = fmt.Errorf("pcap open error: %s", err)
		return
	}
	defer handle.Close()

	var iface *net.Interface
	iface, err = net.InterfaceByName(l.ifname)
	if err != nil {
		return
	}

	var addrs []net.Addr
	var linklocal net.IP
	addrs, err = iface.Addrs()
	if err != nil {
		return
	}

	for _, addr := range addrs {
		switch v := addr.(type) {
		case *net.IPNet:
			if v.IP.IsLinkLocalUnicast() {
				linklocal = v.IP
			}
		}
	}

	if linklocal.IsUnspecified() {
		err = fmt.Errorf("error finding link local unicast address for if %s", l.ifname)
		return
	}

	var eth layers.Ethernet
	var ip6 layers.IPv6
	var ip6extensions layers.IPv6ExtensionSkipper
	var icmp layers.ICMPv6
	var payload gopacket.Payload
	decoded := []gopacket.LayerType{}
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip6, &ip6extensions, &icmp, &payload)
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	packetsChan := packetSource.Packets()
	for {
		select {
		case packet := <-packetsChan:
			parser.DecodeLayers(packet.Data(), &decoded)
			for _, layerType := range decoded {
				switch layerType {
				case layers.LayerTypeICMPv6:
					var target net.IP
					// IPv6 bounds check
					if len(payload) >= 16 {
						target = net.IP(payload[:16])
					} else {
						continue
					}

					switch icmp.TypeCode.Type() {
					case layers.ICMPv6TypeNeighborSolicitation, layers.ICMPv6TypeNeighborAdvertisement:
						n := ndp{eth: eth, ip6: ip6, icmp: icmp, payload: payload}
						vlog.Printf("%s\tread\t%s\tmac_src %s\tip6_src %s\tip6_dst %s\ttarget %s\n", l.ifname, icmp.TypeCode, eth.SrcMAC, ip6.SrcIP, ip6.DstIP, target)
						l.intChan <- n
					}
				}
			}

		case n, ok := <-l.extChan:
			if !ok {
				// channel was closed
				return
			}

			n.eth.DstMAC = nil
			if n.ip6.DstIP.IsLinkLocalMulticast() {
				// Ethernet MAC is derived by the four low-order octets of IPv6 address
				n.eth.DstMAC = append(net.HardwareAddr{0x33, 0x33}, n.ip6.DstIP[12:]...)
			} else {
				var neighbors []netlink.Neigh
				neighbors, err = netlink.NeighList(iface.Index, netlink.FAMILY_V6)
				if err != nil {
					return
				}
				for _, neighbor := range neighbors {
					if neighbor.IP.Equal(n.ip6.DstIP) {
						n.eth.DstMAC = neighbor.HardwareAddr
					}
				}
			}
			if n.eth.DstMAC == nil {
				vlog.Printf("%s: could not find destination MAC address. %s mac_src %s ip6_dst %s ip6_src %s target %s", l.ifname, n.icmp.TypeCode, n.eth.SrcMAC, n.ip6.DstIP, n.ip6.SrcIP, net.IP(n.payload[:16]))
				// Try Solicited-Node multicast address
				// dst IP is derived by the first 13 octets of multicast address +
				// last 3 octets of dst IP
				n.ip6.DstIP = append(net.IPv6linklocalallnodes[:13], n.ip6.DstIP[13:]...)
				n.eth.DstMAC = append(net.HardwareAddr{0x33, 0x33}, n.ip6.DstIP[12:]...)
			}
			n.eth.SrcMAC = iface.HardwareAddr
			n.ip6.SrcIP = linklocal
			buf := gopacket.NewSerializeBuffer()
			n.icmp.SetNetworkLayerForChecksum(&n.ip6)
			opts := gopacket.SerializeOptions{ComputeChecksums: true}
			switch n.icmp.TypeCode.Type() {
			case layers.ICMPv6TypeNeighborSolicitation:
				// source link-layer address opt type, opt length
				n.payload = append(n.payload[:16], 0x01, 0x01)
			case layers.ICMPv6TypeNeighborAdvertisement:
				// target link-layer address opt type, opt length
				n.payload = append(n.payload[:16], 0x02, 0x01)
				n.icmp.TypeBytes[0] = 0xc0 // router,solicit,override flags
			}
			n.payload = append(n.payload[:18], iface.HardwareAddr...)

			err = gopacket.SerializeLayers(buf, opts, &n.eth, &n.ip6, &n.icmp, &n.payload)
			if err != nil {
				err = fmt.Errorf("serialize layers error: %s", err)
				return
			}

			err = handle.WritePacketData(buf.Bytes())
			if err != nil {
				err = fmt.Errorf("pcap write error: %s", err)
				return
			}
			vlog.Printf("%s\twrite\t%s\tmac_dst %s\tip6_src %s\tip6_dst %s\ttarget %s\n", l.ifname, n.icmp.TypeCode, n.eth.DstMAC, n.ip6.SrcIP, n.ip6.DstIP, net.IP(n.payload[:16]))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "container/list"
    "fmt"
    "github.com/fzzy/radix/redis"
    "io"
    "log"
    "net"
    "os"
    "math/rand"
    "strconv"
    "strings"
)

func getBackend(hostname string, redisClient *redis.Client) string {
    fmt.Println("Looking up", hostname)
    backends, error := redisClient.Cmd("smembers", "hostnames:" + hostname + ":backends").List()
    if error != nil {
        fmt.Println("Error in redis lookup", error)
    }
    fmt.Println("Found backends:", backends)
    backend := backends[int(rand.Float32() * float32(len(backends)))]
    return backend
}

func handleHTTPConnection(downstream net.Conn, redisClient *redis.Client) {
    reader := bufio.NewReader(downstream)
    hostname := ""
    readLines := list.New()
    for hostname == "" {
        bytes, _, error := reader.ReadLine()
        if error != nil {
            fmt.Println("Error reading")
            return
        }
        line := string(bytes)
        readLines.PushBack(line)
        if strings.HasPrefix(line, "Host: ") {
            hostname = strings.TrimPrefix(line, "Host: ")
            break
        }
    }
    if hostname == "" {
        fmt.Println("No host!")
        return
    }
    backendAddress := getBackend(hostname, redisClient)

    upstream, error := net.Dial("tcp", backendAddress + ":80")
    if error != nil {
        fmt.Println("Couldn't connect to upstream", error)
        return
    }

    for element := readLines.Front(); element != nil; element = element.Next() {
        line := element.Value.(string)
        upstream.Write([]byte(line))
        upstream.Write([]byte("\n"))
    }

    go io.Copy(upstream, reader)
    go io.Copy(downstream, upstream)
}

func handleHTTPSConnection(downstream net.Conn, redisClient *redis.Client) {
    firstByte := make([]byte, 1)
    _, error := downstream.Read(firstByte)
    if error != nil {
        fmt.Println("Couldn't read first byte :-(")
        return
    }
    if firstByte[0] != 0x16 {
        fmt.Println("Not TLS :-(")
    }

    versionBytes := make([]byte, 2)
    _, error = downstream.Read(versionBytes)
    if error != nil {
        fmt.Println("Couldn't read version bytes :-(")
        return
    }
    if versionBytes[0] < 3 || (versionBytes[0] == 3 && versionBytes[1] < 1) {
        fmt.Println("SSL < 3.1 so it's still not TLS")
        return
    }

    restLengthBytes := make([]byte, 2)
    _, error = downstream.Read(restLengthBytes)
    if error != nil {
        fmt.Println("Couldn't read restLength bytes :-(")
        return
    }
    restLength := (int(restLengthBytes[0]) << 8) + int(restLengthBytes[1])

    rest := make([]byte, restLength)
    _, error = downstream.Read(rest)
    if error != nil {
        fmt.Println("Couldn't read rest of bytes")
        return
    }

    current := 0

    handshakeType := rest[0]
    current += 1
    if handshakeType != 0x1 {
        fmt.Println("Not a ClientHello")
        return
    }

    // Skip over another length
    current += 3
    // Skip over protocolversion
    current += 2
    // Skip over random number
    current += 4 + 28
    // Skip over session ID
    sessionIDLength := int(rest[current])
    current += 1
    current += sessionIDLength

    cipherSuiteLength := (int(rest[current]) << 8) + int(rest[current + 1])
    current += 2
    current += cipherSuiteLength

    compressionMethodLength := int(rest[current])
    current += 1
    current += compressionMethodLength

    if current > restLength {
        fmt.Println("no extensions")
        return
    }

    // Skip over extensionsLength
    // extensionsLength := (int(rest[current]) << 8) + int(rest[current + 1])
    current += 2

    hostname := ""
    for current < restLength && hostname == "" {
        extensionType := (int(rest[current]) << 8) + int(rest[current + 1])
        current += 2

        extensionDataLength := (int(rest[current]) << 8) + int(rest[current + 1])
        current += 2

        if extensionType == 0 {

            // Skip over number of names as we're assuming there's just one
            current += 2

            nameType := rest[current]
            current += 1
            if nameType != 0 {
                fmt.Println("Not a hostname")
                return
            }
            nameLen := (int(rest[current]) << 8) + int(rest[current+1])
            current += 2
            hostname = string(rest[current:current + nameLen])
        }

        current += extensionDataLength
    }
    if hostname == "" {
        fmt.Println("No hostname")
        return
    }

    backendAddress := getBackend(hostname, redisClient)
    upstream, error := net.Dial("tcp", backendAddress + ":443")
    if error != nil {
        log.Fatal(error)
        return
    }

    upstream.Write(firstByte)
    upstream.Write(versionBytes)
    upstream.Write(restLengthBytes)
    upstream.Write(rest)

    go io.Copy(upstream, downstream)
    go io.Copy(downstream, upstream)
}

func doProxy(done chan int, port int, handle func(net.Conn, *redis.Client), redisClient *redis.Client) {
    listener, error := net.Listen("tcp", "0.0.0.0:" + strconv.Itoa(port))
    if error != nil {
        fmt.Println("Couldn't start listening", error)
    }
    fmt.Println("Started proxy on", port, "-- listening...")
    for {
        connection, error := listener.Accept()
        if error != nil {
            fmt.Println("Accept error", error)
            return
        }

        go handle(connection, redisClient)
    }
    done <- 1
}

func main() {
    redisClient, error := redis.Dial("tcp", "127.0.0.1:6379")
    if error != nil {
        fmt.Println("Error connecting to redis", error)
        os.Exit(1)
    }

    httpDone := make(chan int)
    go doProxy(httpDone, 80, handleHTTPConnection, redisClient)

    httpsDone := make(chan int)
    go doProxy(httpsDone, 443, handleHTTPSConnection, redisClient)

    <- httpDone
    <- httpsDone
}
<commit_msg>Improved error handling, added fallback backends for when we can find a host name but there's nothing in redis.<commit_after>package main

import (
    "bufio"
    "container/list"
    "errors"
    "fmt"
    "github.com/fzzy/radix/redis"
    "io"
    "log"
    "net"
    "os"
    "math/rand"
    "strconv"
    "strings"
)

func getBackend(hostname string, fallback string, redisClient *redis.Client) (string, error) {
    fmt.Println("Looking up", hostname)

    backends, error := redisClient.Cmd("smembers", "hostnames:" + hostname + ":backends").List()
    if error != nil {
        fmt.Println("Error in redis lookup", error)
        return "", error
    }

    if len(backends) == 0 {
        backends, error = redisClient.Cmd("smembers", "hostnames:" + fallback + ":backends").List()
        if len(backends) == 0 {
            fmt.Println("No fallback of type", fallback)
            return "", errors.New("Could not find fallback of type " + fallback)
        }
    }
    if error != nil {
        fmt.Println("Error in redis lookup", error)
        return "", error
    }

    fmt.Println("Found backends:", backends)
    backend := backends[int(rand.Float32() * float32(len(backends)))]
    return backend, nil
}

func handleHTTPConnection(downstream net.Conn, redisClient *redis.Client) {
    reader := bufio.NewReader(downstream)
    hostname := ""
    readLines := list.New()
    for hostname == "" {
        bytes, _, error := reader.ReadLine()
        if error != nil {
            fmt.Println("Error reading")
            return
        }
        line := string(bytes)
        readLines.PushBack(line)
        if strings.HasPrefix(line, "Host: ") {
            hostname = strings.TrimPrefix(line, "Host: ")
            break
        }
    }
    if hostname == "" {
        fmt.Println("No host!")
        return
    }
    backendAddress, error := getBackend(hostname, "httpFallback", redisClient)
    if error != nil {
        fmt.Println("Couldn't get backend for ", hostname, "-- got error", error)
        return
    }

    upstream, error := net.Dial("tcp", backendAddress + ":80")
    if error != nil {
        fmt.Println("Couldn't connect to upstream", error)
        return
    }

    for element := readLines.Front(); element != nil; element = element.Next() {
        line := element.Value.(string)
        upstream.Write([]byte(line))
        upstream.Write([]byte("\n"))
    }

    go io.Copy(upstream, reader)
    go io.Copy(downstream, upstream)
}

func handleHTTPSConnection(downstream net.Conn, redisClient *redis.Client) {
    firstByte := make([]byte, 1)
    _, error := downstream.Read(firstByte)
    if error != nil {
        fmt.Println("Couldn't read first byte :-(")
        return
    }
    if firstByte[0] != 0x16 {
        fmt.Println("Not TLS :-(")
    }

    versionBytes := make([]byte, 2)
    _, error = downstream.Read(versionBytes)
    if error != nil {
        fmt.Println("Couldn't read version bytes :-(")
        return
    }
    if versionBytes[0] < 3 || (versionBytes[0] == 3 && versionBytes[1] < 1) {
        fmt.Println("SSL < 3.1 so it's still not TLS")
        return
    }

    restLengthBytes := make([]byte, 2)
    _, error = downstream.Read(restLengthBytes)
    if error != nil {
        fmt.Println("Couldn't read restLength bytes :-(")
        return
    }
    restLength := (int(restLengthBytes[0]) << 8) + int(restLengthBytes[1])

    rest := make([]byte, restLength)
    _, error = downstream.Read(rest)
    if error != nil {
        fmt.Println("Couldn't read rest of bytes")
        return
    }

    current := 0

    handshakeType := rest[0]
    current += 1
    if handshakeType != 0x1 {
        fmt.Println("Not a ClientHello")
        return
    }

    // Skip over another length
    current += 3
    // Skip over protocolversion
    current += 2
    // Skip over random number
    current += 4 + 28
    // Skip over session ID
    sessionIDLength := int(rest[current])
    current += 1
    current += sessionIDLength

    cipherSuiteLength := (int(rest[current]) << 8) + int(rest[current + 1])
    current += 2
    current += cipherSuiteLength

    compressionMethodLength := int(rest[current])
    current += 1
    current += compressionMethodLength

    if current > restLength {
        fmt.Println("no extensions")
        return
    }

    // Skip over extensionsLength
    // extensionsLength := (int(rest[current]) << 8) + int(rest[current + 1])
    current += 2

    hostname := ""
    for current < restLength && hostname == "" {
        extensionType := (int(rest[current]) << 8) + int(rest[current + 1])
        current += 2

        extensionDataLength := (int(rest[current]) << 8) + int(rest[current + 1])
        current += 2

        if extensionType == 0 {

            // Skip over number of names as we're assuming there's just one
            current += 2

            nameType := rest[current]
            current += 1
            if nameType != 0 {
                fmt.Println("Not a hostname")
                return
            }
            nameLen := (int(rest[current]) << 8) + int(rest[current+1])
            current += 2
            hostname = string(rest[current:current + nameLen])
        }

        current += extensionDataLength
    }
    if hostname == "" {
        fmt.Println("No hostname")
        return
    }

    backendAddress, error := getBackend(hostname, "httpsFallback", redisClient)
    if error != nil {
        fmt.Println("Couldn't get backend for ", hostname, "-- got error", error)
        return
    }

    upstream, error := net.Dial("tcp", backendAddress + ":443")
    if error != nil {
        log.Fatal(error)
        return
    }

    upstream.Write(firstByte)
    upstream.Write(versionBytes)
    upstream.Write(restLengthBytes)
    upstream.Write(rest)

    go io.Copy(upstream, downstream)
    go io.Copy(downstream, upstream)
}

func doProxy(done chan int, port int, handle func(net.Conn, *redis.Client), redisClient *redis.Client) {
    listener, error := net.Listen("tcp", "0.0.0.0:" + strconv.Itoa(port))
    if error != nil {
        fmt.Println("Couldn't start listening", error)
    }
    fmt.Println("Started proxy on", port, "-- listening...")
    for {
        connection, error := listener.Accept()
        if error != nil {
            fmt.Println("Accept error", error)
            return
        }

        go handle(connection, redisClient)
    }
    done <- 1
}

func main() {
    redisClient, error := redis.Dial("tcp", "127.0.0.1:6379")
    if error != nil {
        fmt.Println("Error connecting to redis", error)
        os.Exit(1)
    }

    httpDone := make(chan int)
    go doProxy(httpDone, 80, handleHTTPConnection, redisClient)

    httpsDone := make(chan int)
    go doProxy(httpsDone, 443, handleHTTPSConnection, redisClient)

    <- httpDone
    <- httpsDone
}
<|endoftext|>


<commit_before>package pi

import "strings"

type route struct {
	RouteURL     string
	ChildRoutes  routes
	Methods      map[string]HandlerFunction
	Interceptors interceptors
}

type routes []*route

// Route adds a new sub route to a parent Route.
// See pi.Route to add root Routes.
func Route(RouteURL string, ChildRoutes ...*route) *route {
	return &route{
		RouteURL:    RouteURL,
		ChildRoutes: ChildRoutes,
		Methods:     make(map[string]HandlerFunction),
	}
}

// Before registers an HandlerFunction to be called before the request is handled.
func (r *route) Before(handler HandlerFunction) *route {
	r.Interceptors.addBefore(handler)
	return r
}

// After registers an HandlerFunction to be called after the request has been handled.
func (r *route) After(handler HandlerFunction) *route {
	r.Interceptors.addAfter(handler)
	return r
}

// OnError registers an HandlerFunction to be called when an error occurs in the main handler or any Before interceptor.
func (r *route) OnError(handler HandlerFunction) *route {
	r.Interceptors.addOnError(handler)
	return r
}

func (r *route) Any(handlerFunc HandlerFunction) *route {
	r.Get(handlerFunc)
	r.Post(handlerFunc)
	r.Put(handlerFunc)
	r.Delete(handlerFunc)
	r.Patch(handlerFunc)
	r.Options(handlerFunc)
	return r
}

// Get registers an HandlerFunction to handle GET requests.
func (r *route) Get(handlerFunc HandlerFunction) *route {
	r.Methods["GET"] = handlerFunc
	return r
}

// Post registers an HandlerFunction to handle POST requests.
func (r *route) Post(handlerFunc HandlerFunction) *route {
	r.Methods["POST"] = handlerFunc
	return r
}

// Put registers an HandlerFunction to handle PUT requests.
func (r *route) Put(handlerFunc HandlerFunction) *route {
	r.Methods["PUT"] = handlerFunc
	return r
}

// Delete registers an HandlerFunction to handle DELETE requests.
func (r *route) Delete(handlerFunc HandlerFunction) *route {
	r.Methods["DELETE"] = handlerFunc
	return r
}

// Patch registers an HandlerFunction to handle PATCH requests.
func (r *route) Patch(handlerFunc HandlerFunction) *route {
	r.Methods["PATCH"] = handlerFunc
	return r
}

// Options registers an HandlerFunction to handle OPTIONS requests.
func (r *route) Options(handlerFunc HandlerFunction) *route {
	r.Methods["OPTIONS"] = handlerFunc
	return r
}

func (r routes) Len() int {
	return len(r)
}

func (r routes) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r routes) Less(i, j int) bool {
	return strings.Count(r[i].RouteURL, "/") > strings.Count(r[j].RouteURL, "/")
}
<commit_msg>Added HEAD func to routes<commit_after>package pi

import "strings"

type route struct {
	RouteURL     string
	ChildRoutes  routes
	Methods      map[string]HandlerFunction
	Interceptors interceptors
}

type routes []*route

// Route adds a new sub route to a parent Route.
// See pi.Route to add root Routes.
func Route(RouteURL string, ChildRoutes ...*route) *route {
	return &route{
		RouteURL:    RouteURL,
		ChildRoutes: ChildRoutes,
		Methods:     make(map[string]HandlerFunction),
	}
}

// Before registers an HandlerFunction to be called before the request is handled.
func (r *route) Before(handler HandlerFunction) *route {
	r.Interceptors.addBefore(handler)
	return r
}

// After registers an HandlerFunction to be called after the request has been handled.
func (r *route) After(handler HandlerFunction) *route {
	r.Interceptors.addAfter(handler)
	return r
}

// OnError registers an HandlerFunction to be called when an error occurs in the main handler or any Before interceptor.
func (r *route) OnError(handler HandlerFunction) *route {
	r.Interceptors.addOnError(handler)
	return r
}

func (r *route) Any(handlerFunc HandlerFunction) *route {
	r.Get(handlerFunc)
	r.Post(handlerFunc)
	r.Put(handlerFunc)
	r.Delete(handlerFunc)
	r.Patch(handlerFunc)
	r.Options(handlerFunc)
	return r
}

// Get registers an HandlerFunction to handle GET requests.
func (r *route) Get(handlerFunc HandlerFunction) *route {
	r.Methods["GET"] = handlerFunc
	return r
}

// Post registers an HandlerFunction to handle POST requests.
func (r *route) Post(handlerFunc HandlerFunction) *route {
	r.Methods["POST"] = handlerFunc
	return r
}

// Put registers an HandlerFunction to handle PUT requests.
func (r *route) Put(handlerFunc HandlerFunction) *route {
	r.Methods["PUT"] = handlerFunc
	return r
}

// Delete registers an HandlerFunction to handle DELETE requests.
func (r *route) Delete(handlerFunc HandlerFunction) *route {
	r.Methods["DELETE"] = handlerFunc
	return r
}

// Patch registers an HandlerFunction to handle PATCH requests.
func (r *route) Patch(handlerFunc HandlerFunction) *route {
	r.Methods["PATCH"] = handlerFunc
	return r
}

// Options registers an HandlerFunction to handle OPTIONS requests.
func (r *route) Options(handlerFunc HandlerFunction) *route {
	r.Methods["OPTIONS"] = handlerFunc
	return r
}

// Head registers an HandlerFunction to handle HEAD requests.
func (r *route) Head(handlerFunc HandlerFunction) *route {
	r.Methods["HEAD"] = handlerFunc
	return r
}

func (r routes) Len() int {
	return len(r)
}

func (r routes) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r routes) Less(i, j int) bool {
	return strings.Count(r[i].RouteURL, "/") > strings.Count(r[j].RouteURL, "/")
}
<|endoftext|>


<commit_before>// The RS232 package lets you access old serial junk from go
package rs232

//go:generate ./genc.sh

/*
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>

void initBaudRates();
*/
import "C"

import (
	"fmt"
	"os"
	"syscall"
)

var baudConversionMap = map[int]_Ctype_speed_t{}

// This is your serial port handle.
type SerialPort struct {
	port *os.File
}

func init() {
	C.initBaudRates()
}

//export addBaudRate
func addBaudRate(num int, val _Ctype_speed_t) {
	baudConversionMap[num] = val
}

func baudConversion(rate int) (flag _Ctype_speed_t) {
	return baudConversionMap[rate]
}

// SerConf represents the basic serial configuration to provide to OpenPort.
type SerConf int

const (
	S_8N1 SerConf = iota
	S_7E1
	S_7O1
)

// Opens and returns a non-blocking serial port.
// The device, baud rate, and SerConf is specified.
//
// Example:  rs232.OpenPort("/dev/ttyS0", 115200, rs232.S_8N1)
func OpenPort(port string, baudRate int, serconf SerConf) (*SerialPort, error) {
	rv := &SerialPort{}
	f, err := os.OpenFile(port,
		syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0666)
	if err != nil {
		return nil, err
	}
	rv.port = f

	fd := rv.port.Fd()

	var options C.struct_termios
	if C.tcgetattr(C.int(fd), &options) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("tcgetattr failed")
	}

	if C.cfsetispeed(&options, baudConversion(baudRate)) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("cfsetispeed failed")
	}
	if C.cfsetospeed(&options, baudConversion(baudRate)) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("cfsetospeed failed")
	}
	switch serconf {
	case S_8N1:
		{
			options.c_cflag &^= C.PARENB
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS8
		}
	case S_7E1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag &^= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	case S_7O1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag |= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	}
	// Local
	options.c_cflag |= (C.CLOCAL | C.CREAD)
	// no hardware flow control
	options.c_cflag &^= C.CRTSCTS
	// Don't EOF on a zero read, just block
	options.c_cc[C.VMIN] = 1

	if C.tcsetattr(C.int(fd), C.TCSANOW, &options) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("tcsetattr failed")
	}

	if syscall.SetNonblock(int(fd), false) != nil {
		defer f.Close()
		return nil, fmt.Errorf("Error disabling blocking")
	}

	return rv, nil
}

// SetNonblock enables nonblocking serial IO.
func (port *SerialPort) SetNonblock() error {
	fd := port.port.Fd()

	return syscall.SetNonblock(int(fd), true)
}

// Read from the port.
func (port *SerialPort) Read(p []byte) (int, error) {
	return port.port.Read(p)
}

// Write to the port.
func (port *SerialPort) Write(p []byte) (int, error) {
	return port.port.Write(p)
}

// Close the port.
func (port *SerialPort) Close() error {
	return port.port.Close()
}
<commit_msg>Don't open the serial port in nonblock<commit_after>// The RS232 package lets you access old serial junk from go
package rs232

//go:generate ./genc.sh

/*
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>

void initBaudRates();
*/
import "C"

import (
	"fmt"
	"os"
	"syscall"
)

var baudConversionMap = map[int]_Ctype_speed_t{}

// This is your serial port handle.
type SerialPort struct {
	port *os.File
}

func init() {
	C.initBaudRates()
}

//export addBaudRate
func addBaudRate(num int, val _Ctype_speed_t) {
	baudConversionMap[num] = val
}

func baudConversion(rate int) (flag _Ctype_speed_t) {
	return baudConversionMap[rate]
}

// SerConf represents the basic serial configuration to provide to OpenPort.
type SerConf int

const (
	S_8N1 SerConf = iota
	S_7E1
	S_7O1
)

// Opens and returns a non-blocking serial port.
// The device, baud rate, and SerConf is specified.
//
// Example:  rs232.OpenPort("/dev/ttyS0", 115200, rs232.S_8N1)
func OpenPort(port string, baudRate int, serconf SerConf) (*SerialPort, error) {
	rv := &SerialPort{}
	f, err := os.OpenFile(port,
		syscall.O_RDWR|syscall.O_NOCTTY, 0666)
	if err != nil {
		return nil, err
	}
	rv.port = f

	fd := rv.port.Fd()

	var options C.struct_termios
	if C.tcgetattr(C.int(fd), &options) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("tcgetattr failed")
	}

	if C.cfsetispeed(&options, baudConversion(baudRate)) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("cfsetispeed failed")
	}
	if C.cfsetospeed(&options, baudConversion(baudRate)) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("cfsetospeed failed")
	}
	switch serconf {
	case S_8N1:
		{
			options.c_cflag &^= C.PARENB
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS8
		}
	case S_7E1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag &^= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	case S_7O1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag |= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	}
	// Local
	options.c_cflag |= (C.CLOCAL | C.CREAD)
	// no hardware flow control
	options.c_cflag &^= C.CRTSCTS
	// Don't EOF on a zero read, just block
	options.c_cc[C.VMIN] = 1

	if C.tcsetattr(C.int(fd), C.TCSANOW, &options) < 0 {
		defer f.Close()
		return nil, fmt.Errorf("tcsetattr failed")
	}

	return rv, nil
}

// SetNonblock enables nonblocking serial IO.
func (port *SerialPort) SetNonblock() error {
	fd := port.port.Fd()

	return syscall.SetNonblock(int(fd), true)
}

// Read from the port.
func (port *SerialPort) Read(p []byte) (int, error) {
	return port.port.Read(p)
}

// Write to the port.
func (port *SerialPort) Write(p []byte) (int, error) {
	return port.port.Write(p)
}

// Close the port.
func (port *SerialPort) Close() error {
	return port.port.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"gopkg.in/alecthomas/kingpin.v2"
	"io"
)

var (
	pathExp = kingpin.Arg("pathExp", "Sax Path Expression").Required().String()
	filename = kingpin.Arg("xml-file", "file").Required().String()
)

type StartElement struct {
	buffer   []byte
	position int
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.Parse()

	fmt.Println(*pathExp)

	absFilename, err := abs(*filename)
	if err != nil {
		panic(err.Error())
	}
	SaxFile(absFilename)
}

func NewStartElement(bufferSize int) StartElement {
	return StartElement{buffer: make([]byte, bufferSize), position: 0}
}

func SaxFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		panic(err.Error())
	}
	defer file.Close()
	startElement := NewStartElement(1024 * 4)
	SaxReader(file, 1024 * 4, startElement)
}

func SaxReader(reader io.Reader, bufferSize int, startElement StartElement) {
	buffer := make([]byte, bufferSize)

	var readCount int = 0
	for {
		n, err := reader.Read(buffer)

		if n != 0 && err != nil {
			panic("Error while reading xml")
		}
		if n == 0 {
			break
		}
		startElemFrom := -1
		startElemTo := -1
		for index, value := range buffer {
			if value == byte('<') {
				startElemFrom = index
			}
			if value == byte('>') {
				startElemTo = index
			}
			if startElemFrom != -1 && startElemTo != -1 && startElement.position == 0{
//				fmt.Print("inx",startElemFrom,startElemTo)
				startElem(buffer[startElemFrom:startElemTo])
				startElemFrom = -1
				startElemTo = -1
			}
			if startElemTo != -1 && startElement.position > 0 {
				copy(startElement.buffer[startElement.position:], buffer[:startElemTo])
				startElem(startElement.buffer)
				startElemFrom = -1
				startElemTo = -1
				startElement.position = 0
			}
		}
		readCount = readCount + n
		if startElemFrom != -1 && startElemTo == -1 && startElement.position > 0 {
			copy(startElement.buffer[startElement.position:], buffer[:n])
			startElement.position = startElement.position + n
		}
		if startElemFrom != -1 && startElemTo == -1 && startElement.position == 0 {

			if (n < startElemFrom){
				fmt.Print("inx",startElemFrom,n,string(buffer))
			}
			copy(startElement.buffer, buffer[startElemFrom:n])
			startElement.position = startElement.position + n

		}
	}
	fmt.Println(readCount)
}

func startElem(bytes []byte) {
	//fmt.Println(string(bytes))
}

func abs(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	wd, err := os.Getwd()
	return path.Join(wd, name), err
}
<commit_msg>startElem done<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"gopkg.in/alecthomas/kingpin.v2"
	"io"
)

var (
	pathExp = kingpin.Arg("pathExp", "Sax Path Expression").Required().String()
	filename = kingpin.Arg("xml-file", "file").Required().String()
)

type StartElement struct {
	buffer   []byte
	position int
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.Parse()

	fmt.Println(*pathExp)

	absFilename, err := abs(*filename)
	if err != nil {
		panic(err.Error())
	}
	SaxFile(absFilename)
}

func NewStartElement(bufferSize int) StartElement {
	return StartElement{buffer: make([]byte, bufferSize), position: 0}
}

func SaxFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		panic(err.Error())
	}
	defer file.Close()
	startElement := NewStartElement(1024 * 4)
	SaxReader(file, 1024 * 4, startElement)
}

func SaxReader(reader io.Reader, bufferSize int, startElement StartElement) {
	buffer := make([]byte, bufferSize)

	var readCount int = 0
	for {
		n, err := reader.Read(buffer)

		if n != 0 && err != nil {
			panic("Error while reading xml")
		}
		if n == 0 {
			break
		}
		startElemFrom := -1
		startElemTo := -1
		for index, value := range buffer {
			if value == byte('<') {
				startElemFrom = index
			}
			if value == byte('>') {
				startElemTo = index
			}
			if startElemTo != -1 && startElemFrom == -1 && startElement.position > 0{
				copy(startElement.buffer[startElement.position:], buffer[:startElemTo])
				startElement.position = 0
				startElemFrom = -1
				startElemTo = -1
			}
			if startElemFrom != -1 && startElemTo != -1{
				startElem(buffer[startElemFrom:startElemTo])
				startElemFrom = -1
				startElemTo = -1
			}
		}
		if startElemFrom != -1 && startElemTo != -1 && startElement.position > 0 {
			copy(startElement.buffer[startElement.position:], buffer)
			startElement.position = startElement.position + n
		}

		if startElemFrom != -1  {
			copy(startElement.buffer, buffer[:n])
			startElement.position = startElement.position + n
		}
		if startElemTo != -1  {
			copy(startElement.buffer[startElement.position:], buffer[:startElemTo])
			startElement.position = 0
		}

		readCount = readCount + n
	}
	fmt.Println(readCount)
}

func startElem(bytes []byte) {
	//fmt.Println(string(bytes))
}

func abs(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	wd, err := os.Getwd()
	return path.Join(wd, name), err
}
<|endoftext|>


<commit_before>// Contains the node database, storing previously seen nodes and any collected
// metadata about them for QoS purposes.

package discover

import (
	"bytes"
	"encoding/binary"
	"os"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/iterator"
	"github.com/syndtr/goleveldb/leveldb/storage"
	"github.com/syndtr/goleveldb/leveldb/util"
)

var (
	nodeDBNilNodeID      = NodeID{}       // Special node ID to use as a nil element.
	nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
	nodeDBCleanupCycle   = time.Hour      // Time period for running the expiration task.
)

// nodeDB stores all nodes we know about.
type nodeDB struct {
	lvl    *leveldb.DB       // Interface to the database itself
	seeder iterator.Iterator // Iterator for fetching possible seed nodes

	runner sync.Once     // Ensures we can start at most one expirer
	quit   chan struct{} // Channel to signal the expiring thread to stop
}

// Schema layout for the node database
var (
	nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
	nodeDBItemPrefix = []byte("n:")      // Identifier to prefix node entries with

	nodeDBDiscoverRoot = ":discover"
	nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
	nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
)

// newNodeDB creates a new node database for storing and retrieving infos about
// known peers in the network. If no path is given, an in-memory, temporary
// database is constructed.
func newNodeDB(path string, version int) (*nodeDB, error) {
	if path == "" {
		return newMemoryNodeDB()
	}
	return newPersistentNodeDB(path, version)
}

// newMemoryNodeDB creates a new in-memory node database without a persistent
// backend.
func newMemoryNodeDB() (*nodeDB, error) {
	db, err := leveldb.Open(storage.NewMemStorage(), nil)
	if err != nil {
		return nil, err
	}
	return &nodeDB{
		lvl:  db,
		quit: make(chan struct{}),
	}, nil
}

// newPersistentNodeDB creates/opens a leveldb backed persistent node database,
// also flushing its contents in case of a version mismatch.
func newPersistentNodeDB(path string, version int) (*nodeDB, error) {
	// Try to open the cache, recovering any corruption
	db, err := leveldb.OpenFile(path, nil)
	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
		db, err = leveldb.RecoverFile(path, nil)
	}
	if err != nil {
		return nil, err
	}
	// The nodes contained in the cache correspond to a certain protocol version.
	// Flush all nodes if the version doesn't match.
	currentVer := make([]byte, binary.MaxVarintLen64)
	currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))]

	blob, err := db.Get(nodeDBVersionKey, nil)
	switch err {
	case leveldb.ErrNotFound:
		// Version not found (i.e. empty cache), insert it
		if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil {
			db.Close()
			return nil, err
		}

	case nil:
		// Version present, flush if different
		if !bytes.Equal(blob, currentVer) {
			db.Close()
			if err = os.RemoveAll(path); err != nil {
				return nil, err
			}
			return newPersistentNodeDB(path, version)
		}
	}
	return &nodeDB{
		lvl:  db,
		quit: make(chan struct{}),
	}, nil
}

// makeKey generates the leveldb key-blob from a node id and its particular
// field of interest.
func makeKey(id NodeID, field string) []byte {
	if bytes.Equal(id[:], nodeDBNilNodeID[:]) {
		return []byte(field)
	}
	return append(nodeDBItemPrefix, append(id[:], field...)...)
}

// splitKey tries to split a database key into a node id and a field part.
func splitKey(key []byte) (id NodeID, field string) {
	// If the key is not of a node, return it plainly
	if !bytes.HasPrefix(key, nodeDBItemPrefix) {
		return NodeID{}, string(key)
	}
	// Otherwise split the id and field
	item := key[len(nodeDBItemPrefix):]
	copy(id[:], item[:len(id)])
	field = string(item[len(id):])

	return id, field
}

// fetchInt64 retrieves an integer instance associated with a particular
// database key.
func (db *nodeDB) fetchInt64(key []byte) int64 {
	blob, err := db.lvl.Get(key, nil)
	if err != nil {
		return 0
	}
	val, read := binary.Varint(blob)
	if read <= 0 {
		return 0
	}
	return val
}

// storeInt64 update a specific database entry to the current time instance as a
// unix timestamp.
func (db *nodeDB) storeInt64(key []byte, n int64) error {
	blob := make([]byte, binary.MaxVarintLen64)
	blob = blob[:binary.PutVarint(blob, n)]

	return db.lvl.Put(key, blob, nil)
}

// node retrieves a node with a given id from the database.
func (db *nodeDB) node(id NodeID) *Node {
	blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil)
	if err != nil {
		glog.V(logger.Detail).Infof("failed to retrieve node %v: %v", id, err)
		return nil
	}
	node := new(Node)
	if err := rlp.DecodeBytes(blob, node); err != nil {
		glog.V(logger.Warn).Infof("failed to decode node RLP: %v", err)
		return nil
	}
	node.sha = crypto.Sha3Hash(node.ID[:])
	return node
}

// updateNode inserts - potentially overwriting - a node into the peer database.
func (db *nodeDB) updateNode(node *Node) error {
	blob, err := rlp.EncodeToBytes(node)
	if err != nil {
		return err
	}
	return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil)
}

// ensureExpirer is a small helper method ensuring that the data expiration
// mechanism is running. If the expiration goroutine is already running, this
// method simply returns.
//
// The goal is to start the data evacuation only after the network successfully
// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since
// it would require significant overhead to exactly trace the first successful
// convergence, it's simpler to "ensure" the correct state when an appropriate
// condition occurs (i.e. a successful bonding), and discard further events.
func (db *nodeDB) ensureExpirer() {
	db.runner.Do(func() { go db.expirer() })
}

// expirer should be started in a go routine, and is responsible for looping ad
// infinitum and dropping stale data from the database.
func (db *nodeDB) expirer() {
	tick := time.Tick(nodeDBCleanupCycle)
	for {
		select {
		case <-tick:
			if err := db.expireNodes(); err != nil {
				glog.V(logger.Error).Infof("Failed to expire nodedb items: %v", err)
			}

		case <-db.quit:
			return
		}
	}
}

// expireNodes iterates over the database and deletes all nodes that have not
// been seen (i.e. received a pong from) for some alloted time.
func (db *nodeDB) expireNodes() error {
	threshold := time.Now().Add(-nodeDBNodeExpiration)

	// Find discovered nodes that are older than the allowance
	it := db.lvl.NewIterator(nil, nil)
	defer it.Release()

	for it.Next() {
		// Skip the item if not a discovery node
		id, field := splitKey(it.Key())
		if field != nodeDBDiscoverRoot {
			continue
		}
		// Skip the node if not expired yet
		if seen := db.lastPong(id); seen.After(threshold) {
			continue
		}
		// Otherwise delete all associated information
		deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
		for deleter.Next() {
			if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
				return err
			}
		}
	}
	return nil
}

// lastPing retrieves the time of the last ping packet send to a remote node,
// requesting binding.
func (db *nodeDB) lastPing(id NodeID) time.Time {
	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0)
}

// updateLastPing updates the last time we tried contacting a remote node.
func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error {
	return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix())
}

// lastPong retrieves the time of the last successful contact from remote node.
func (db *nodeDB) lastPong(id NodeID) time.Time {
	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0)
}

// updateLastPong updates the last time a remote node successfully contacted.
func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error {
	return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
}

// querySeeds retrieves a batch of nodes to be used as potential seed servers
// during bootstrapping the node into the network.
//
// Ideal seeds are the most recently seen nodes (highest probability to be still
// alive), but yet untried. However, since leveldb only supports dumb iteration
// we will instead start pulling in potential seeds that haven't been yet pinged
// since the start of the boot procedure.
//
// If the database runs out of potential seeds, we restart the startup counter
// and start iterating over the peers again.
func (db *nodeDB) querySeeds(n int) []*Node {
	// Create a new seed iterator if none exists
	if db.seeder == nil {
		db.seeder = db.lvl.NewIterator(nil, nil)
	}
	// Iterate over the nodes and find suitable seeds
	nodes := make([]*Node, 0, n)
	for len(nodes) < n && db.seeder.Next() {
		// Iterate until a discovery node is found
		id, field := splitKey(db.seeder.Key())
		if field != nodeDBDiscoverRoot {
			continue
		}
		// Load it as a potential seed
		if node := db.node(id); node != nil {
			nodes = append(nodes, node)
		}
	}
	// Release the iterator if we reached the end
	if len(nodes) == 0 {
		db.seeder.Release()
		db.seeder = nil
	}
	return nodes
}

// close flushes and closes the database files.
func (db *nodeDB) close() {
	if db.seeder != nil {
		db.seeder.Release()
	}
	close(db.quit)
	db.lvl.Close()
}
<commit_msg>p2p/discover: limit open files for node database<commit_after>// Contains the node database, storing previously seen nodes and any collected
// metadata about them for QoS purposes.

package discover

import (
	"bytes"
	"encoding/binary"
	"os"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/iterator"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/storage"
	"github.com/syndtr/goleveldb/leveldb/util"
)

var (
	nodeDBNilNodeID      = NodeID{}       // Special node ID to use as a nil element.
	nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
	nodeDBCleanupCycle   = time.Hour      // Time period for running the expiration task.
)

// nodeDB stores all nodes we know about.
type nodeDB struct {
	lvl    *leveldb.DB       // Interface to the database itself
	seeder iterator.Iterator // Iterator for fetching possible seed nodes

	runner sync.Once     // Ensures we can start at most one expirer
	quit   chan struct{} // Channel to signal the expiring thread to stop
}

// Schema layout for the node database
var (
	nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
	nodeDBItemPrefix = []byte("n:")      // Identifier to prefix node entries with

	nodeDBDiscoverRoot = ":discover"
	nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
	nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
)

// newNodeDB creates a new node database for storing and retrieving infos about
// known peers in the network. If no path is given, an in-memory, temporary
// database is constructed.
func newNodeDB(path string, version int) (*nodeDB, error) {
	if path == "" {
		return newMemoryNodeDB()
	}
	return newPersistentNodeDB(path, version)
}

// newMemoryNodeDB creates a new in-memory node database without a persistent
// backend.
func newMemoryNodeDB() (*nodeDB, error) {
	db, err := leveldb.Open(storage.NewMemStorage(), nil)
	if err != nil {
		return nil, err
	}
	return &nodeDB{
		lvl:  db,
		quit: make(chan struct{}),
	}, nil
}

// newPersistentNodeDB creates/opens a leveldb backed persistent node database,
// also flushing its contents in case of a version mismatch.
func newPersistentNodeDB(path string, version int) (*nodeDB, error) {
	opts := &opt.Options{OpenFilesCacheCapacity: 5}
	db, err := leveldb.OpenFile(path, opts)
	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
		db, err = leveldb.RecoverFile(path, nil)
	}
	if err != nil {
		return nil, err
	}
	// The nodes contained in the cache correspond to a certain protocol version.
	// Flush all nodes if the version doesn't match.
	currentVer := make([]byte, binary.MaxVarintLen64)
	currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))]

	blob, err := db.Get(nodeDBVersionKey, nil)
	switch err {
	case leveldb.ErrNotFound:
		// Version not found (i.e. empty cache), insert it
		if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil {
			db.Close()
			return nil, err
		}

	case nil:
		// Version present, flush if different
		if !bytes.Equal(blob, currentVer) {
			db.Close()
			if err = os.RemoveAll(path); err != nil {
				return nil, err
			}
			return newPersistentNodeDB(path, version)
		}
	}
	return &nodeDB{
		lvl:  db,
		quit: make(chan struct{}),
	}, nil
}

// makeKey generates the leveldb key-blob from a node id and its particular
// field of interest.
func makeKey(id NodeID, field string) []byte {
	if bytes.Equal(id[:], nodeDBNilNodeID[:]) {
		return []byte(field)
	}
	return append(nodeDBItemPrefix, append(id[:], field...)...)
}

// splitKey tries to split a database key into a node id and a field part.
func splitKey(key []byte) (id NodeID, field string) {
	// If the key is not of a node, return it plainly
	if !bytes.HasPrefix(key, nodeDBItemPrefix) {
		return NodeID{}, string(key)
	}
	// Otherwise split the id and field
	item := key[len(nodeDBItemPrefix):]
	copy(id[:], item[:len(id)])
	field = string(item[len(id):])

	return id, field
}

// fetchInt64 retrieves an integer instance associated with a particular
// database key.
func (db *nodeDB) fetchInt64(key []byte) int64 {
	blob, err := db.lvl.Get(key, nil)
	if err != nil {
		return 0
	}
	val, read := binary.Varint(blob)
	if read <= 0 {
		return 0
	}
	return val
}

// storeInt64 update a specific database entry to the current time instance as a
// unix timestamp.
func (db *nodeDB) storeInt64(key []byte, n int64) error {
	blob := make([]byte, binary.MaxVarintLen64)
	blob = blob[:binary.PutVarint(blob, n)]

	return db.lvl.Put(key, blob, nil)
}

// node retrieves a node with a given id from the database.
func (db *nodeDB) node(id NodeID) *Node {
	blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil)
	if err != nil {
		glog.V(logger.Detail).Infof("failed to retrieve node %v: %v", id, err)
		return nil
	}
	node := new(Node)
	if err := rlp.DecodeBytes(blob, node); err != nil {
		glog.V(logger.Warn).Infof("failed to decode node RLP: %v", err)
		return nil
	}
	node.sha = crypto.Sha3Hash(node.ID[:])
	return node
}

// updateNode inserts - potentially overwriting - a node into the peer database.
func (db *nodeDB) updateNode(node *Node) error {
	blob, err := rlp.EncodeToBytes(node)
	if err != nil {
		return err
	}
	return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil)
}

// ensureExpirer is a small helper method ensuring that the data expiration
// mechanism is running. If the expiration goroutine is already running, this
// method simply returns.
//
// The goal is to start the data evacuation only after the network successfully
// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since
// it would require significant overhead to exactly trace the first successful
// convergence, it's simpler to "ensure" the correct state when an appropriate
// condition occurs (i.e. a successful bonding), and discard further events.
func (db *nodeDB) ensureExpirer() {
	db.runner.Do(func() { go db.expirer() })
}

// expirer should be started in a go routine, and is responsible for looping ad
// infinitum and dropping stale data from the database.
func (db *nodeDB) expirer() {
	tick := time.Tick(nodeDBCleanupCycle)
	for {
		select {
		case <-tick:
			if err := db.expireNodes(); err != nil {
				glog.V(logger.Error).Infof("Failed to expire nodedb items: %v", err)
			}

		case <-db.quit:
			return
		}
	}
}

// expireNodes iterates over the database and deletes all nodes that have not
// been seen (i.e. received a pong from) for some alloted time.
func (db *nodeDB) expireNodes() error {
	threshold := time.Now().Add(-nodeDBNodeExpiration)

	// Find discovered nodes that are older than the allowance
	it := db.lvl.NewIterator(nil, nil)
	defer it.Release()

	for it.Next() {
		// Skip the item if not a discovery node
		id, field := splitKey(it.Key())
		if field != nodeDBDiscoverRoot {
			continue
		}
		// Skip the node if not expired yet
		if seen := db.lastPong(id); seen.After(threshold) {
			continue
		}
		// Otherwise delete all associated information
		deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
		for deleter.Next() {
			if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
				return err
			}
		}
	}
	return nil
}

// lastPing retrieves the time of the last ping packet send to a remote node,
// requesting binding.
func (db *nodeDB) lastPing(id NodeID) time.Time {
	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0)
}

// updateLastPing updates the last time we tried contacting a remote node.
func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error {
	return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix())
}

// lastPong retrieves the time of the last successful contact from remote node.
func (db *nodeDB) lastPong(id NodeID) time.Time {
	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0)
}

// updateLastPong updates the last time a remote node successfully contacted.
func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error {
	return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
}

// querySeeds retrieves a batch of nodes to be used as potential seed servers
// during bootstrapping the node into the network.
//
// Ideal seeds are the most recently seen nodes (highest probability to be still
// alive), but yet untried. However, since leveldb only supports dumb iteration
// we will instead start pulling in potential seeds that haven't been yet pinged
// since the start of the boot procedure.
//
// If the database runs out of potential seeds, we restart the startup counter
// and start iterating over the peers again.
func (db *nodeDB) querySeeds(n int) []*Node {
	// Create a new seed iterator if none exists
	if db.seeder == nil {
		db.seeder = db.lvl.NewIterator(nil, nil)
	}
	// Iterate over the nodes and find suitable seeds
	nodes := make([]*Node, 0, n)
	for len(nodes) < n && db.seeder.Next() {
		// Iterate until a discovery node is found
		id, field := splitKey(db.seeder.Key())
		if field != nodeDBDiscoverRoot {
			continue
		}
		// Load it as a potential seed
		if node := db.node(id); node != nil {
			nodes = append(nodes, node)
		}
	}
	// Release the iterator if we reached the end
	if len(nodes) == 0 {
		db.seeder.Release()
		db.seeder = nil
	}
	return nodes
}

// close flushes and closes the database files.
func (db *nodeDB) close() {
	if db.seeder != nil {
		db.seeder.Release()
	}
	close(db.quit)
	db.lvl.Close()
}
<|endoftext|>