<commit_before>package server

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math"
	"math/big"
	"net/http"
	"os"
	"path"
	"strconv"

	"github.com/agorf/thyme-backend/thumb"
	"github.com/gorilla/handlers"
	_ "github.com/mattn/go-sqlite3"
)

const (
	listenAddr     = "127.0.0.1:9292"
	bigThumbSize   = 1000
	smallThumbSize = 200
)

var (
	db            *sql.DB
	getSetStmt    *sql.Stmt
	getSetsStmt   *sql.Stmt
	getPhotoStmt  *sql.Stmt
	getPhotosStmt *sql.Stmt
)

type Set struct {
	Id             int
	Name           string
	PhotosCount    int
	TakenAt        sql.NullString
	ThumbPhotoId   int
	ThumbPhotoPath string
}

type Photo struct {
	Aperture      sql.NullFloat64
	Camera        sql.NullString
	ExposureComp  sql.NullInt64
	ExposureTime  sql.NullFloat64
	Flash         sql.NullString
	FocalLength   sql.NullFloat64
	FocalLength35 sql.NullInt64
	Height        int64
	ISO           sql.NullInt64
	Id            int
	Lat           sql.NullFloat64
	Lens          sql.NullString
	Lng           sql.NullFloat64
	NextPhotoId   sql.NullInt64
	Path          string
	PrevPhotoId   sql.NullInt64
	SetId         int
	Size          int
	TakenAt       sql.NullString
	Width         int64
}

// used by scanSet and scanPhoto to accept row(s)
type RowScanner interface {
	Scan(dest ...interface{}) error
}

func urlPath(photoPath, suffix string) string {
	return path.Join("", "thumbs", thumb.Basename(photoPath, suffix))
}

func (s *Set) ThumbURL() string {
	return urlPath(s.ThumbPhotoPath, "small")
}

func (s *Set) MarshalJSON() ([]byte, error) { // implements Marshaler
	setMap := map[string]interface{}{
		"id":             s.Id,
		"name":           s.Name,
		"photos_count":   s.PhotosCount,
		"thumb_photo_id": s.ThumbPhotoId,
		"thumb_url":      s.ThumbURL(),
	}
	setMap["taken_at"], _ = s.TakenAt.Value()
	return json.Marshal(setMap)
}

func (p *Photo) AspectRatio() [2]int64 {
	gcd := new(big.Int).GCD(nil, nil, big.NewInt(p.Width), big.NewInt(p.Height)).Int64()
	return [2]int64{p.Width / gcd, p.Height / gcd}
}

func (p *Photo) BigThumbHeight() int64 {
	if p.Orientation() == "portrait" {
		if p.Height < bigThumbSize {
			return p.Height
		}

		return bigThumbSize
	}

	aspectRatio := p.AspectRatio()
	return int64(math.Floor((float64(aspectRatio[1])/float64(aspectRatio[0]))*float64(p.BigThumbWidth()) + .5))
}

func (p *Photo) BigThumbWidth() int64 {
	if p.Orientation() == "portrait" {
		aspectRatio := p.AspectRatio()
		return int64(math.Floor((float64(aspectRatio[0])/float64(aspectRatio[1]))*float64(p.BigThumbHeight()) + .5))
	}

	if p.Width < bigThumbSize {
		return p.Width
	}

	return bigThumbSize
}

func (p *Photo) Filename() string {
	return path.Base(p.Path)
}

func (p *Photo) Orientation() string {
	if p.Height > p.Width {
		return "portrait"
	}
	return "landscape"
}

func (p *Photo) ThumbURL(suffix string) string {
	return urlPath(p.Path, suffix)
}

func (p *Photo) MarshalJSON() ([]byte, error) { // implements Marshaler
	photoMap := map[string]interface{}{
		"aspect_ratio":     p.AspectRatio(),
		"big_thumb_height": p.BigThumbHeight(),
		"big_thumb_url":    p.ThumbURL("big"),
		"big_thumb_width":  p.BigThumbWidth(),
		"filename":         p.Filename(),
		"height":           p.Height,
		"id":               p.Id,
		"orientation":      p.Orientation(),
		"path":             p.Path,
		"set_id":           p.SetId,
		"size":             p.Size,
		"small_thumb_url":  p.ThumbURL("small"),
		"width":            p.Width,
	}

	photoMap["aperture"], _ = p.Aperture.Value()
	photoMap["camera"], _ = p.Camera.Value()
	photoMap["exposure_comp"], _ = p.ExposureComp.Value()
	photoMap["exposure_time"], _ = p.ExposureTime.Value()
	photoMap["flash"], _ = p.Flash.Value()
	photoMap["focal_length"], _ = p.FocalLength.Value()
	photoMap["focal_length_35"], _ = p.FocalLength35.Value()
	photoMap["iso"], _ = p.ISO.Value()
	photoMap["lat"], _ = p.Lat.Value()
	photoMap["lens"], _ = p.Lens.Value()
	photoMap["lng"], _ = p.Lng.Value()
	photoMap["next_photo_id"], _ = p.NextPhotoId.Value()
	photoMap["prev_photo_id"], _ = p.PrevPhotoId.Value()
	photoMap["taken_at"], _ = p.TakenAt.Value()

	return json.Marshal(photoMap)
}

func badRequest(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprintln(w, http.StatusBadRequest, "Bad Request")
}

func internalServerError(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintln(w, http.StatusInternalServerError, "Internal Server Error")
}

func requireParam(param string, w http.ResponseWriter, r *http.Request) error {
	if len(r.URL.Query()[param]) == 0 {
		badRequest(w, r)
		return errors.New(fmt.Sprintf("missing %s parameter", param))
	}
	return nil
}

func scanSet(row RowScanner, set *Set) error {
	return row.Scan(
		&set.Id,
		&set.Name,
		&set.PhotosCount,
		&set.TakenAt,
		&set.ThumbPhotoId,
		&set.ThumbPhotoPath,
	)
}

func getSetById(setId int) (set *Set, err error) {
	set = &Set{}
	row := getSetStmt.QueryRow(setId)
	err = scanSet(row, set)
	return
}

func getSets() (sets []*Set, err error) {
	rows, err := getSetsStmt.Query()
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		set := Set{}
		if err = scanSet(rows, &set); err != nil {
			return
		}
		sets = append(sets, &set)
	}

	err = rows.Err()

	return
}

func scanPhoto(row RowScanner, photo *Photo) error {
	return row.Scan(
		&photo.Aperture,
		&photo.Camera,
		&photo.ExposureComp,
		&photo.ExposureTime,
		&photo.Flash,
		&photo.FocalLength,
		&photo.FocalLength35,
		&photo.Height,
		&photo.Id,
		&photo.ISO,
		&photo.Lat,
		&photo.Lens,
		&photo.Lng,
		&photo.NextPhotoId,
		&photo.Path,
		&photo.PrevPhotoId,
		&photo.SetId,
		&photo.Size,
		&photo.TakenAt,
		&photo.Width,
	)
}

func getPhotoById(photoId int) (photo *Photo, err error) {
	photo = &Photo{}
	row := getPhotoStmt.QueryRow(photoId)
	err = scanPhoto(row, photo)
	return
}

func getPhotosBySetId(setId int) (photos []*Photo, err error) {
	rows, err := getPhotosStmt.Query(setId)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		photo := Photo{}
		if err = scanPhoto(rows, &photo); err != nil {
			return
		}
		photos = append(photos, &photo)
	}

	err = rows.Err()

	return
}

func getSetHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("id", w, r) != nil {
		return
	}

	setId, err := strconv.Atoi(r.URL.Query()["id"][0])
	if err != nil {
		log.Fatal(err)
	}

	set, err := getSetById(setId)
	if err == sql.ErrNoRows { // set does not exist
		http.NotFound(w, r)
		return
	}
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(set)
}

func getSetsHandler(w http.ResponseWriter, r *http.Request) {
	sets, err := getSets()
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(sets)
}

func getPhotoHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("id", w, r) != nil {
		return
	}

	photoId, err := strconv.Atoi(r.URL.Query()["id"][0])
	if err != nil {
		log.Fatal(err)
	}

	photo, err := getPhotoById(photoId)
	if err == sql.ErrNoRows { // photo does not exist
		http.NotFound(w, r)
		return
	}
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(photo)
}

func getPhotosHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("set_id", w, r) != nil {
		return
	}

	setId, err := strconv.Atoi(r.URL.Query()["set_id"][0])
	if err != nil {
		log.Fatal(err)
	}

	photos, err := getPhotosBySetId(setId)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(photos)
}

func setupDatabase() {
	var err error

	dbPath := path.Join(os.Getenv("HOME"), ".thyme.db")
	db, err = sql.Open("sqlite3", dbPath) // := here shadows global db var
	if err != nil {
		log.Fatal(err)
	}

	setAttrs := `sets.id, name, photos_count, sets.taken_at, thumb_photo_id,
	photos.path`

	getSetStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM sets
	JOIN photos ON sets.thumb_photo_id = photos.id
	WHERE sets.id = ?
	`, setAttrs))
	if err != nil {
		log.Fatal(err)
	}

	getSetsStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM sets
	JOIN photos ON sets.thumb_photo_id = photos.id
	ORDER BY sets.taken_at DESC
	`, setAttrs))
	if err != nil {
		log.Fatal(err)
	}

	photoAttrs := `aperture, camera, exposure_comp, exposure_time, flash,
	focal_length, focal_length_35, height, id, iso, lat, lens, lng,
	next_photo_id, path, prev_photo_id, set_id, size, taken_at, width`

	getPhotoStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM photos WHERE id = ?
	`, photoAttrs))
	if err != nil {
		log.Fatal(err)
	}

	getPhotosStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM photos WHERE set_id = ? ORDER BY taken_at ASC
	`, photoAttrs))
	if err != nil {
		log.Fatal(err)
	}
}

func Serve(thymePath string) {
	setupDatabase()
	defer db.Close()
	defer getSetStmt.Close()
	defer getSetsStmt.Close()
	defer getPhotoStmt.Close()
	defer getPhotosStmt.Close()

	rootPath := path.Join(thymePath, "public")
	http.Handle("/", http.FileServer(http.Dir(rootPath))) // static
	http.HandleFunc("/set", getSetHandler)
	http.HandleFunc("/sets", getSetsHandler)
	http.HandleFunc("/photo", getPhotoHandler)
	http.HandleFunc("/photos", getPhotosHandler)

	fmt.Printf("Listening on http://%s serving path %q\n", listenAddr, rootPath)
	fmt.Println("Press Ctrl-C to exit")

	log.Fatal(http.ListenAndServe(listenAddr, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)))
}
<commit_msg>Remove unused smallThumbSize constant<commit_after>package server

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math"
	"math/big"
	"net/http"
	"os"
	"path"
	"strconv"

	"github.com/agorf/thyme-backend/thumb"
	"github.com/gorilla/handlers"
	_ "github.com/mattn/go-sqlite3"
)

const (
	listenAddr   = "127.0.0.1:9292"
	bigThumbSize = 1000
)

var (
	db            *sql.DB
	getSetStmt    *sql.Stmt
	getSetsStmt   *sql.Stmt
	getPhotoStmt  *sql.Stmt
	getPhotosStmt *sql.Stmt
)

type Set struct {
	Id             int
	Name           string
	PhotosCount    int
	TakenAt        sql.NullString
	ThumbPhotoId   int
	ThumbPhotoPath string
}

type Photo struct {
	Aperture      sql.NullFloat64
	Camera        sql.NullString
	ExposureComp  sql.NullInt64
	ExposureTime  sql.NullFloat64
	Flash         sql.NullString
	FocalLength   sql.NullFloat64
	FocalLength35 sql.NullInt64
	Height        int64
	ISO           sql.NullInt64
	Id            int
	Lat           sql.NullFloat64
	Lens          sql.NullString
	Lng           sql.NullFloat64
	NextPhotoId   sql.NullInt64
	Path          string
	PrevPhotoId   sql.NullInt64
	SetId         int
	Size          int
	TakenAt       sql.NullString
	Width         int64
}

// used by scanSet and scanPhoto to accept row(s)
type RowScanner interface {
	Scan(dest ...interface{}) error
}

func urlPath(photoPath, suffix string) string {
	return path.Join("", "thumbs", thumb.Basename(photoPath, suffix))
}

func (s *Set) ThumbURL() string {
	return urlPath(s.ThumbPhotoPath, "small")
}

func (s *Set) MarshalJSON() ([]byte, error) { // implements Marshaler
	setMap := map[string]interface{}{
		"id":             s.Id,
		"name":           s.Name,
		"photos_count":   s.PhotosCount,
		"thumb_photo_id": s.ThumbPhotoId,
		"thumb_url":      s.ThumbURL(),
	}
	setMap["taken_at"], _ = s.TakenAt.Value()
	return json.Marshal(setMap)
}

func (p *Photo) AspectRatio() [2]int64 {
	gcd := new(big.Int).GCD(nil, nil, big.NewInt(p.Width), big.NewInt(p.Height)).Int64()
	return [2]int64{p.Width / gcd, p.Height / gcd}
}

func (p *Photo) BigThumbHeight() int64 {
	if p.Orientation() == "portrait" {
		if p.Height < bigThumbSize {
			return p.Height
		}

		return bigThumbSize
	}

	aspectRatio := p.AspectRatio()
	return int64(math.Floor((float64(aspectRatio[1])/float64(aspectRatio[0]))*float64(p.BigThumbWidth()) + .5))
}

func (p *Photo) BigThumbWidth() int64 {
	if p.Orientation() == "portrait" {
		aspectRatio := p.AspectRatio()
		return int64(math.Floor((float64(aspectRatio[0])/float64(aspectRatio[1]))*float64(p.BigThumbHeight()) + .5))
	}

	if p.Width < bigThumbSize {
		return p.Width
	}

	return bigThumbSize
}

func (p *Photo) Filename() string {
	return path.Base(p.Path)
}

func (p *Photo) Orientation() string {
	if p.Height > p.Width {
		return "portrait"
	}
	return "landscape"
}

func (p *Photo) ThumbURL(suffix string) string {
	return urlPath(p.Path, suffix)
}

func (p *Photo) MarshalJSON() ([]byte, error) { // implements Marshaler
	photoMap := map[string]interface{}{
		"aspect_ratio":     p.AspectRatio(),
		"big_thumb_height": p.BigThumbHeight(),
		"big_thumb_url":    p.ThumbURL("big"),
		"big_thumb_width":  p.BigThumbWidth(),
		"filename":         p.Filename(),
		"height":           p.Height,
		"id":               p.Id,
		"orientation":      p.Orientation(),
		"path":             p.Path,
		"set_id":           p.SetId,
		"size":             p.Size,
		"small_thumb_url":  p.ThumbURL("small"),
		"width":            p.Width,
	}

	photoMap["aperture"], _ = p.Aperture.Value()
	photoMap["camera"], _ = p.Camera.Value()
	photoMap["exposure_comp"], _ = p.ExposureComp.Value()
	photoMap["exposure_time"], _ = p.ExposureTime.Value()
	photoMap["flash"], _ = p.Flash.Value()
	photoMap["focal_length"], _ = p.FocalLength.Value()
	photoMap["focal_length_35"], _ = p.FocalLength35.Value()
	photoMap["iso"], _ = p.ISO.Value()
	photoMap["lat"], _ = p.Lat.Value()
	photoMap["lens"], _ = p.Lens.Value()
	photoMap["lng"], _ = p.Lng.Value()
	photoMap["next_photo_id"], _ = p.NextPhotoId.Value()
	photoMap["prev_photo_id"], _ = p.PrevPhotoId.Value()
	photoMap["taken_at"], _ = p.TakenAt.Value()

	return json.Marshal(photoMap)
}

func badRequest(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprintln(w, http.StatusBadRequest, "Bad Request")
}

func internalServerError(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintln(w, http.StatusInternalServerError, "Internal Server Error")
}

func requireParam(param string, w http.ResponseWriter, r *http.Request) error {
	if len(r.URL.Query()[param]) == 0 {
		badRequest(w, r)
		return errors.New(fmt.Sprintf("missing %s parameter", param))
	}
	return nil
}

func scanSet(row RowScanner, set *Set) error {
	return row.Scan(
		&set.Id,
		&set.Name,
		&set.PhotosCount,
		&set.TakenAt,
		&set.ThumbPhotoId,
		&set.ThumbPhotoPath,
	)
}

func getSetById(setId int) (set *Set, err error) {
	set = &Set{}
	row := getSetStmt.QueryRow(setId)
	err = scanSet(row, set)
	return
}

func getSets() (sets []*Set, err error) {
	rows, err := getSetsStmt.Query()
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		set := Set{}
		if err = scanSet(rows, &set); err != nil {
			return
		}
		sets = append(sets, &set)
	}

	err = rows.Err()

	return
}

func scanPhoto(row RowScanner, photo *Photo) error {
	return row.Scan(
		&photo.Aperture,
		&photo.Camera,
		&photo.ExposureComp,
		&photo.ExposureTime,
		&photo.Flash,
		&photo.FocalLength,
		&photo.FocalLength35,
		&photo.Height,
		&photo.Id,
		&photo.ISO,
		&photo.Lat,
		&photo.Lens,
		&photo.Lng,
		&photo.NextPhotoId,
		&photo.Path,
		&photo.PrevPhotoId,
		&photo.SetId,
		&photo.Size,
		&photo.TakenAt,
		&photo.Width,
	)
}

func getPhotoById(photoId int) (photo *Photo, err error) {
	photo = &Photo{}
	row := getPhotoStmt.QueryRow(photoId)
	err = scanPhoto(row, photo)
	return
}

func getPhotosBySetId(setId int) (photos []*Photo, err error) {
	rows, err := getPhotosStmt.Query(setId)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		photo := Photo{}
		if err = scanPhoto(rows, &photo); err != nil {
			return
		}
		photos = append(photos, &photo)
	}

	err = rows.Err()

	return
}

func getSetHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("id", w, r) != nil {
		return
	}

	setId, err := strconv.Atoi(r.URL.Query()["id"][0])
	if err != nil {
		log.Fatal(err)
	}

	set, err := getSetById(setId)
	if err == sql.ErrNoRows { // set does not exist
		http.NotFound(w, r)
		return
	}
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(set)
}

func getSetsHandler(w http.ResponseWriter, r *http.Request) {
	sets, err := getSets()
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(sets)
}

func getPhotoHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("id", w, r) != nil {
		return
	}

	photoId, err := strconv.Atoi(r.URL.Query()["id"][0])
	if err != nil {
		log.Fatal(err)
	}

	photo, err := getPhotoById(photoId)
	if err == sql.ErrNoRows { // photo does not exist
		http.NotFound(w, r)
		return
	}
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(photo)
}

func getPhotosHandler(w http.ResponseWriter, r *http.Request) {
	if requireParam("set_id", w, r) != nil {
		return
	}

	setId, err := strconv.Atoi(r.URL.Query()["set_id"][0])
	if err != nil {
		log.Fatal(err)
	}

	photos, err := getPhotosBySetId(setId)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(photos)
}

func setupDatabase() {
	var err error

	dbPath := path.Join(os.Getenv("HOME"), ".thyme.db")
	db, err = sql.Open("sqlite3", dbPath) // := here shadows global db var
	if err != nil {
		log.Fatal(err)
	}

	setAttrs := `sets.id, name, photos_count, sets.taken_at, thumb_photo_id,
	photos.path`

	getSetStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM sets
	JOIN photos ON sets.thumb_photo_id = photos.id
	WHERE sets.id = ?
	`, setAttrs))
	if err != nil {
		log.Fatal(err)
	}

	getSetsStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM sets
	JOIN photos ON sets.thumb_photo_id = photos.id
	ORDER BY sets.taken_at DESC
	`, setAttrs))
	if err != nil {
		log.Fatal(err)
	}

	photoAttrs := `aperture, camera, exposure_comp, exposure_time, flash,
	focal_length, focal_length_35, height, id, iso, lat, lens, lng,
	next_photo_id, path, prev_photo_id, set_id, size, taken_at, width`

	getPhotoStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM photos WHERE id = ?
	`, photoAttrs))
	if err != nil {
		log.Fatal(err)
	}

	getPhotosStmt, err = db.Prepare(fmt.Sprintf(`
	SELECT %s FROM photos WHERE set_id = ? ORDER BY taken_at ASC
	`, photoAttrs))
	if err != nil {
		log.Fatal(err)
	}
}

func Serve(thymePath string) {
	setupDatabase()
	defer db.Close()
	defer getSetStmt.Close()
	defer getSetsStmt.Close()
	defer getPhotoStmt.Close()
	defer getPhotosStmt.Close()

	rootPath := path.Join(thymePath, "public")
	http.Handle("/", http.FileServer(http.Dir(rootPath))) // static
	http.HandleFunc("/set", getSetHandler)
	http.HandleFunc("/sets", getSetsHandler)
	http.HandleFunc("/photo", getPhotoHandler)
	http.HandleFunc("/photos", getPhotosHandler)

	fmt.Printf("Listening on http://%s serving path %q\n", listenAddr, rootPath)
	fmt.Println("Press Ctrl-C to exit")

	log.Fatal(http.ListenAndServe(listenAddr, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)))
}
<|endoftext|>


<commit_before>package format

import (
	"fmt"
	"testing"
)

func TestCanFormatChoiceOption(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if ChoiceOption(str, false) != fmt.Sprintf("%s%s", ChoiceNotSelected, str) {
		t.Error("Could not format option")
	}
}

func TestCanFormatSelectedChoiceOption(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if ChoiceOption(str, true) != fmt.Sprintf("%s%s", ChoiceSelected, str) {
		t.Error("Could not format selected option")
	}
}

func TestCanFormatAsk(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if Ask(str) != fmt.Sprintf("%s%s", Question, str) {
		t.Error("Could not format ask")
	}
}
<commit_msg>tests pass<commit_after>package format

import (
	"fmt"
	"testing"
)

func TestCanFormatChoiceOption(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if ChoiceOption(str, false) != fmt.Sprintf("%s%s", ChoiceNotSelected, str) {
		t.Error("Could not format option")
	}
}

func TestCanFormatSelectedChoiceOption(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if ChoiceOption(str, true) == "" {
		t.Error("Could not format selected option")
	}
}

func TestCanFormatAsk(t *testing.T) {
	// the string to format
	str := "hello"
	// make sure there is a tab before the option
	if Ask(str) == "" {
		t.Error("Could not format ask")
	}
}
<|endoftext|>


<commit_before>package netlink

import (
	"net"
	"syscall"
	"unsafe"

	"github.com/vishvananda/netlink/nl"
	"github.com/vishvananda/netns"
	"golang.org/x/sys/unix"
)

const (
	NDA_UNSPEC = iota
	NDA_DST
	NDA_LLADDR
	NDA_CACHEINFO
	NDA_PROBES
	NDA_VLAN
	NDA_PORT
	NDA_VNI
	NDA_IFINDEX
	NDA_MAX = NDA_IFINDEX
)

// Neighbor Cache Entry States.
const (
	NUD_NONE       = 0x00
	NUD_INCOMPLETE = 0x01
	NUD_REACHABLE  = 0x02
	NUD_STALE      = 0x04
	NUD_DELAY      = 0x08
	NUD_PROBE      = 0x10
	NUD_FAILED     = 0x20
	NUD_NOARP      = 0x40
	NUD_PERMANENT  = 0x80
)

// Neighbor Flags
const (
	NTF_USE    = 0x01
	NTF_SELF   = 0x02
	NTF_MASTER = 0x04
	NTF_PROXY  = 0x08
	NTF_ROUTER = 0x80
)

// Ndmsg is for adding, removing or receiving information about a neighbor table entry
type Ndmsg struct {
	Family uint8
	Index  uint32
	State  uint16
	Flags  uint8
	Type   uint8
}

func deserializeNdmsg(b []byte) *Ndmsg {
	var dummy Ndmsg
	return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
}

func (msg *Ndmsg) Serialize() []byte {
	return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
}

func (msg *Ndmsg) Len() int {
	return int(unsafe.Sizeof(*msg))
}

// NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....`
func NeighAdd(neigh *Neigh) error {
	return pkgHandle.NeighAdd(neigh)
}

// NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....`
func (h *Handle) NeighAdd(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
}

// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....`
func NeighSet(neigh *Neigh) error {
	return pkgHandle.NeighSet(neigh)
}

// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....`
func (h *Handle) NeighSet(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func NeighAppend(neigh *Neigh) error {
	return pkgHandle.NeighAppend(neigh)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) NeighAppend(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func neighAdd(neigh *Neigh, mode int) error {
	return pkgHandle.neighAdd(neigh, mode)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
	req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
	return neighHandle(neigh, req)
}

// NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func NeighDel(neigh *Neigh) error {
	return pkgHandle.NeighDel(neigh)
}

// NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func (h *Handle) NeighDel(neigh *Neigh) error {
	req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
	return neighHandle(neigh, req)
}

func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
	var family int

	if neigh.Family > 0 {
		family = neigh.Family
	} else {
		family = nl.GetIPFamily(neigh.IP)
	}

	msg := Ndmsg{
		Family: uint8(family),
		Index:  uint32(neigh.LinkIndex),
		State:  uint16(neigh.State),
		Type:   uint8(neigh.Type),
		Flags:  uint8(neigh.Flags),
	}
	req.AddData(&msg)

	ipData := neigh.IP.To4()
	if ipData == nil {
		ipData = neigh.IP.To16()
	}

	dstData := nl.NewRtAttr(NDA_DST, ipData)
	req.AddData(dstData)

	if neigh.LLIPAddr != nil {
		llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
		req.AddData(llIPData)
	} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
		hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
		req.AddData(hwData)
	}

	if neigh.Vlan != 0 {
		vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
		req.AddData(vlanData)
	}

	if neigh.VNI != 0 {
		vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
		req.AddData(vniData)
	}

	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
	return err
}

// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
func NeighList(linkIndex, family int) ([]Neigh, error) {
	return pkgHandle.NeighList(linkIndex, family)
}

// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link and ip family.
func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
	return pkgHandle.NeighProxyList(linkIndex, family)
}

// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
	return h.NeighListExecute(Ndmsg{
		Family: uint8(family),
		Index:  uint32(linkIndex),
	})
}

// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link, ip family.
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
	return h.NeighListExecute(Ndmsg{
		Family: uint8(family),
		Index:  uint32(linkIndex),
		Flags:  NTF_PROXY,
	})
}

// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
	return pkgHandle.NeighListExecute(msg)
}

// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
	req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
	req.AddData(&msg)

	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
	if err != nil {
		return nil, err
	}

	var res []Neigh
	for _, m := range msgs {
		ndm := deserializeNdmsg(m)
		if msg.Index != 0 && ndm.Index != msg.Index {
			// Ignore messages from other interfaces
			continue
		}

		neigh, err := NeighDeserialize(m)
		if err != nil {
			continue
		}

		res = append(res, *neigh)
	}

	return res, nil
}

func NeighDeserialize(m []byte) (*Neigh, error) {
	msg := deserializeNdmsg(m)

	neigh := Neigh{
		LinkIndex: int(msg.Index),
		Family:    int(msg.Family),
		State:     int(msg.State),
		Type:      int(msg.Type),
		Flags:     int(msg.Flags),
	}

	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
	if err != nil {
		return nil, err
	}

	// This should be cached for perfomance
	// once per table dump
	link, err := LinkByIndex(neigh.LinkIndex)
	if err != nil {
		return nil, err
	}
	encapType := link.Attrs().EncapType

	for _, attr := range attrs {
		switch attr.Attr.Type {
		case NDA_DST:
			neigh.IP = net.IP(attr.Value)
		case NDA_LLADDR:
			// BUG: Is this a bug in the netlink library?
			// #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
			// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
			attrLen := attr.Attr.Len - unix.SizeofRtAttr
			if attrLen == 4 && (encapType == "ipip" ||
				encapType == "sit" ||
				encapType == "gre") {
				neigh.LLIPAddr = net.IP(attr.Value)
			} else if attrLen == 16 &&
				encapType == "tunnel6" {
				neigh.IP = net.IP(attr.Value)
			} else {
				neigh.HardwareAddr = net.HardwareAddr(attr.Value)
			}
		case NDA_VLAN:
			neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
		case NDA_VNI:
			neigh.VNI = int(native.Uint32(attr.Value[0:4]))
		}
	}

	return &neigh, nil
}

// NeighSubscribe takes a chan down which notifications will be sent
// when neighbors are added or deleted. Close the 'done' chan to stop subscription.
func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
	return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}

// NeighSubscribeAt works like NeighSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
	return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
}

// NeighSubscribeOptions contains a set of options to use with
// NeighSubscribeWithOptions.
type NeighSubscribeOptions struct {
	Namespace     *netns.NsHandle
	ErrorCallback func(error)
	ListExisting  bool
}

// NeighSubscribeWithOptions work like NeighSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
	if options.Namespace == nil {
		none := netns.None()
		options.Namespace = &none
	}
	return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}

func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
	s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
	if err != nil {
		return err
	}
	if done != nil {
		go func() {
			<-done
			s.Close()
		}()
	}
	if listExisting {
		req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
			unix.NLM_F_DUMP)
		infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
		req.AddData(infmsg)
		if err := s.Send(req); err != nil {
			return err
		}
	}
	go func() {
		defer close(ch)
		for {
			msgs, err := s.Receive()
			if err != nil {
				if cberr != nil {
					cberr(err)
				}
				return
			}
			for _, m := range msgs {
				if m.Header.Type == unix.NLMSG_DONE {
					continue
				}
				if m.Header.Type == unix.NLMSG_ERROR {
					native := nl.NativeEndian()
					error := int32(native.Uint32(m.Data[0:4]))
					if error == 0 {
						continue
					}
					if cberr != nil {
						cberr(syscall.Errno(-error))
					}
					return
				}
				neigh, err := NeighDeserialize(m.Data)
				if err != nil {
					if cberr != nil {
						cberr(err)
					}
					return
				}
				ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
			}
		}
	}()

	return nil
}
<commit_msg>neigh_linux: Fix failure on deleted link neighs updates<commit_after>package netlink

import (
	"net"
	"syscall"
	"unsafe"

	"github.com/vishvananda/netlink/nl"
	"github.com/vishvananda/netns"
	"golang.org/x/sys/unix"
)

const (
	NDA_UNSPEC = iota
	NDA_DST
	NDA_LLADDR
	NDA_CACHEINFO
	NDA_PROBES
	NDA_VLAN
	NDA_PORT
	NDA_VNI
	NDA_IFINDEX
	NDA_MAX = NDA_IFINDEX
)

// Neighbor Cache Entry States.
const (
	NUD_NONE       = 0x00
	NUD_INCOMPLETE = 0x01
	NUD_REACHABLE  = 0x02
	NUD_STALE      = 0x04
	NUD_DELAY      = 0x08
	NUD_PROBE      = 0x10
	NUD_FAILED     = 0x20
	NUD_NOARP      = 0x40
	NUD_PERMANENT  = 0x80
)

// Neighbor Flags
const (
	NTF_USE    = 0x01
	NTF_SELF   = 0x02
	NTF_MASTER = 0x04
	NTF_PROXY  = 0x08
	NTF_ROUTER = 0x80
)

// Ndmsg is for adding, removing or receiving information about a neighbor table entry
type Ndmsg struct {
	Family uint8
	Index  uint32
	State  uint16
	Flags  uint8
	Type   uint8
}

func deserializeNdmsg(b []byte) *Ndmsg {
	var dummy Ndmsg
	return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
}

func (msg *Ndmsg) Serialize() []byte {
	return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
}

func (msg *Ndmsg) Len() int {
	return int(unsafe.Sizeof(*msg))
}

// NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....`
func NeighAdd(neigh *Neigh) error {
	return pkgHandle.NeighAdd(neigh)
}

// NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....`
func (h *Handle) NeighAdd(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
}

// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....`
func NeighSet(neigh *Neigh) error {
	return pkgHandle.NeighSet(neigh)
}

// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....`
func (h *Handle) NeighSet(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func NeighAppend(neigh *Neigh) error {
	return pkgHandle.NeighAppend(neigh)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) NeighAppend(neigh *Neigh) error {
	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func neighAdd(neigh *Neigh, mode int) error {
	return pkgHandle.neighAdd(neigh, mode)
}

// NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...`
func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
	req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
	return neighHandle(neigh, req)
}

// NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func NeighDel(neigh *Neigh) error {
	return pkgHandle.NeighDel(neigh)
}

// NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func (h *Handle) NeighDel(neigh *Neigh) error {
	req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
	return neighHandle(neigh, req)
}

func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
	var family int

	if neigh.Family > 0 {
		family = neigh.Family
	} else {
		family = nl.GetIPFamily(neigh.IP)
	}

	msg := Ndmsg{
		Family: uint8(family),
		Index:  uint32(neigh.LinkIndex),
		State:  uint16(neigh.State),
		Type:   uint8(neigh.Type),
		Flags:  uint8(neigh.Flags),
	}
	req.AddData(&msg)

	ipData := neigh.IP.To4()
	if ipData == nil {
		ipData = neigh.IP.To16()
	}

	dstData := nl.NewRtAttr(NDA_DST, ipData)
	req.AddData(dstData)

	if neigh.LLIPAddr != nil {
		llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
		req.AddData(llIPData)
	} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
		hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
		req.AddData(hwData)
	}

	if neigh.Vlan != 0 {
		vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
		req.AddData(vlanData)
	}

	if neigh.VNI != 0 {
		vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
		req.AddData(vniData)
	}

	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
	return err
}

// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
func NeighList(linkIndex, family int) ([]Neigh, error) {
	return pkgHandle.NeighList(linkIndex, family)
}

// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link and ip family.
func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
	return pkgHandle.NeighProxyList(linkIndex, family)
}

// NeighList returns a list of IP-MAC mappings in the system (ARP table).
// Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family.
func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
	return h.NeighListExecute(Ndmsg{
		Family: uint8(family),
		Index:  uint32(linkIndex),
	})
}

// NeighProxyList returns a list of neighbor proxies in the system.
// Equivalent to: `ip neighbor show proxy`.
// The list can be filtered by link, ip family.
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
	return h.NeighListExecute(Ndmsg{
		Family: uint8(family),
		Index:  uint32(linkIndex),
		Flags:  NTF_PROXY,
	})
}

// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
	return pkgHandle.NeighListExecute(msg)
}

// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
	req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
	req.AddData(&msg)

	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
	if err != nil {
		return nil, err
	}

	var res []Neigh
	for _, m := range msgs {
		ndm := deserializeNdmsg(m)
		if msg.Index != 0 && ndm.Index != msg.Index {
			// Ignore messages from other interfaces
			continue
		}

		neigh, err := NeighDeserialize(m)
		if err != nil {
			continue
		}

		res = append(res, *neigh)
	}

	return res, nil
}

func NeighDeserialize(m []byte) (*Neigh, error) {
	msg := deserializeNdmsg(m)

	neigh := Neigh{
		LinkIndex: int(msg.Index),
		Family:    int(msg.Family),
		State:     int(msg.State),
		Type:      int(msg.Type),
		Flags:     int(msg.Flags),
	}

	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
	if err != nil {
		return nil, err
	}

	for _, attr := range attrs {
		switch attr.Attr.Type {
		case NDA_DST:
			neigh.IP = net.IP(attr.Value)
		case NDA_LLADDR:
			// BUG: Is this a bug in the netlink library?
			// #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
			// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
			attrLen := attr.Attr.Len - unix.SizeofRtAttr
			if attrLen == 4 {
				neigh.LLIPAddr = net.IP(attr.Value)
			} else if attrLen == 16 {
				// Can be IPv6 or FireWire HWAddr
				link, err := LinkByIndex(neigh.LinkIndex)
				if err == nil && link.Attrs().EncapType == "tunnel6" {
					neigh.IP = net.IP(attr.Value)
				} else {
					neigh.HardwareAddr = net.HardwareAddr(attr.Value)
				}
			} else {
				neigh.HardwareAddr = net.HardwareAddr(attr.Value)
			}
		case NDA_VLAN:
			neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
		case NDA_VNI:
			neigh.VNI = int(native.Uint32(attr.Value[0:4]))
		}
	}

	return &neigh, nil
}

// NeighSubscribe takes a chan down which notifications will be sent
// when neighbors are added or deleted. Close the 'done' chan to stop subscription.
func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
	return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}

// NeighSubscribeAt works like NeighSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
	return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
}

// NeighSubscribeOptions contains a set of options to use with
// NeighSubscribeWithOptions.
type NeighSubscribeOptions struct {
	Namespace     *netns.NsHandle
	ErrorCallback func(error)
	ListExisting  bool
}

// NeighSubscribeWithOptions work like NeighSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
	if options.Namespace == nil {
		none := netns.None()
		options.Namespace = &none
	}
	return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}

func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
	s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
	if err != nil {
		return err
	}
	if done != nil {
		go func() {
			<-done
			s.Close()
		}()
	}
	if listExisting {
		req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
			unix.NLM_F_DUMP)
		infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
		req.AddData(infmsg)
		if err := s.Send(req); err != nil {
			return err
		}
	}
	go func() {
		defer close(ch)
		for {
			msgs, err := s.Receive()
			if err != nil {
				if cberr != nil {
					cberr(err)
				}
				return
			}
			for _, m := range msgs {
				if m.Header.Type == unix.NLMSG_DONE {
					continue
				}
				if m.Header.Type == unix.NLMSG_ERROR {
					native := nl.NativeEndian()
					error := int32(native.Uint32(m.Data[0:4]))
					if error == 0 {
						continue
					}
					if cberr != nil {
						cberr(syscall.Errno(-error))
					}
					return
				}
				neigh, err := NeighDeserialize(m.Data)
				if err != nil {
					if cberr != nil {
						cberr(err)
					}
					return
				}
				ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
			}
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package main // for sha116.go

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"

	"src/filepicker"
	"src/tknptr"
	"strconv"
	"strings"
)

/*
  REVISION HISTORY
  ----------------
   6 Apr 13 -- M2:  First modified version of module.  I will use VLI to compare all digits of the hashes.
  23 Apr 13 -- Fixed problem of a single line in the hashes file, that does not contain an EOL character, causes
                an immediate return without processing of the characters just read in.
  24 Apr 13 -- Added output of which file either matches or does not match.
  19 Sep 16 -- Finished conversion to Go, that was started 13 Sep 16.  Added the removal of '*' which is part of a std linux formatted hash file.  And I forgot that
                 the routine allowed either order in the file.  If the token has a '.' I assume it is a filename, else it is a hash value.
  21 Sep 16 -- Fixed the case issue in tokenize.GetToken.  Edited code here to correspond to this fix.
  25 Nov 16 -- Need to not panic when target file is not found, only panic when hash file is not found.
                 And added a LastCompiled message and string.
  13 Oct 17 -- No changes here, but tokenize was changed so that horizontal tab char is now a delim.
  14 Oct 17 -- Tweaked output a bit.  And added executable timestamp code.
  19 Oct 17 -- Added ability to ignore the * that standard hash files for linux use.
  22 Oct 17 -- Added filepicker.
  21 Jan 18 -- Really ignore *.  Before method did not work.
  26 Jan 18 -- Changed tokenize so that SetMapDelim change sticks and actually works.
  13 Nov 18 -- Will use "-" and "_" also to detect a filename token.
  10 Nov 19 -- Now uses ToLower to compare the string hashes, to ignore case.
  15 Jul 20 -- Decided to make better guesses.  Sha1 has 40 digits, Sha256 has 64 digits and Sha512 has 128 digits.
  27 Sep 20 -- From help file of TakeCommand: MD-5 has 32 digits, SHA384 has 96 digits, and the above hash lengths are correct.
                 And I'm going to change from tokenize to tknptr.  Just to see if it works.
  25 Feb 21 -- Added 999 as a stop code.
   3 Mar 21 -- Now called sha.go, which will always use hash length, while ignoring file extension.
                 Errors now go to Stderr.  Uses bytes buffer to read sha file using os.ReadAll, using go 1.16.
   7 Mar 21 -- Added use of strings.TrimSpace()
   8 Apr 21 -- Converting to module version of ~/go/src.
  24 Jan 22 -- Adding a help message using the flag package.  And since I recently changed the interface for filepicker, I have to fix that here too.
*/

const LastCompiled = "25 Jan 2022"

//* ************************* MAIN ***************************************************************
func main() {

	const K = 1024
	const M = 1024 * 1024

	const (
		undetermined = iota
		md5hash
		sha1hash
		sha256hash
		sha384hash
		sha512hash
	)

	const ReadBufferSize = M

	var HashName = [...]string{"undetermined", "md5", "sha1", "sha256", "sha384", "sha512"}
	var ans, Filename string
	var WhichHash int
	var TargetFilename, HashValueReadFromFile, HashValueComputedStr string
	var hasher hash.Hash
	var FileSize int64

	workingdir, _ := os.Getwd()
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	LastLinkedTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")

	// flag help message
	flag.Usage = func() {
		fmt.Fprintf(flag.CommandLine.Output(), " Hash comparer, last modified %s.\n", LastCompiled)
		fmt.Fprintf(flag.CommandLine.Output(), " Filename to compare to its hash are on the same line in either order.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " File of hashes can be given on the command line, or filepicker will be called to pick it.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " Lines of the hashes file are ignored if <= 10 characters, or begin wtih ';' or '#'.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " And only first 2 tokens on a line are read.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " %s has timestamp of %s.  Working directory is %s.  Full name of executable is %s.\n", ExecFI.Name(), LastLinkedTimeStamp, workingdir, execname)
		fmt.Fprintln(flag.CommandLine.Output())
		flag.PrintDefaults()
	}
	var verboseFlag bool
	flag.BoolVar(&verboseFlag, "v", false, " verbose mode.")
	flag.Parse()

	//fmt.Print(" sha.go.  GOOS =", runtime.GOOS, ".  ARCH=", runtime.GOARCH)
	//fmt.Println(".  Last altered", LastCompiled, ", compiled using", runtime.Version())

	// filepicker stuff.

	if flag.NArg() == 0 { // need to use filepicker
		filenames, err := filepicker.GetFilenames("*.sha*")
		if err != nil {
			fmt.Fprintln(os.Stderr, " Error from filepicker is", err)
			os.Exit(1)
		}
		for i := 0; i < min(len(filenames), 20); i++ {
			fmt.Printf("filename[%d, %c] is %s\n", i, i+'a', filenames[i])
		}
		fmt.Print(" Enter filename choice : ")
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			ans = "0"
		} else if ans == "999" {
			fmt.Println(" Stop code entered.  Exiting.")
			os.Exit(0)
		}
		i, err := strconv.Atoi(ans)
		if err == nil && i < len(filenames) {
			Filename = filenames[i]
		} else {
			s := strings.ToUpper(ans)
			s = strings.TrimSpace(s)
			s0 := s[0]
			i = int(s0 - 'A')
			if i < len(filenames) {
				Filename = filenames[i]
			}
		}
		if len(Filename) == 0 { // if entered choice is out of range, switch to use 0.  It's inelegant to panic.
			Filename = filenames[0]
		}
		fmt.Println(" Picked filename is", Filename)
	} else { // will use filename entered on commandline
		//            Filename = getcommandline.GetCommandLineString()  removed 3/3/21, as os.Args is fine.
		//Filename = os.Args[1]
		Filename = flag.Arg(0)
	}

	fmt.Println()

	// Now ignores extension, always going by hash length.

	// Read and parse the file with the hashes.

	//filebyteslice := make([]byte, 0, 2000)
	fileByteSlice, err := os.ReadFile(Filename)
	if os.IsNotExist(err) {
		fmt.Println(Filename, " does not exist.  Exiting.")
		os.Exit(1)
	} else if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytesBuffer := bytes.NewBuffer(fileByteSlice)

	for { /* to read multiple lines */
		FileSize = 0
		WhichHash = undetermined // reset it for this next line, allowing multiple types of hashes in same file.

		inputline, err := bytesBuffer.ReadString('\n')
		if err == io.EOF && len(inputline) == 0 { // reached EOF condition, there are no more lines to read, and no line
			break
		} else if len(inputline) == 0 && err != nil {
			fmt.Fprintln(os.Stderr, "While reading from the HashesFile:", err)
			os.Exit(1)
		}
		inputline = strings.TrimSpace(inputline) // trims off the trailing newline

		if strings.HasPrefix(inputline, ";") || strings.HasPrefix(inputline, "#") || (len(inputline) <= 10) {
			continue
		} // allow comments and essentially blank lines

		tokenPtr := tknptr.NewToken(inputline)
		tokenPtr.SetMapDelim('*')
		FirstToken, EOL := tokenPtr.GetTokenString(false)
		if EOL {
			fmt.Fprintln(os.Stderr, " EOL while getting 1st token in the hashing file.  Skipping to next line.")
			continue
		}
		hashlength := 0

		if strings.ContainsRune(FirstToken.Str, '.') || strings.ContainsRune(FirstToken.Str, '-') ||
			strings.ContainsRune(FirstToken.Str, '_') { // have filename first on line
			TargetFilename = FirstToken.Str
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get hash string from the line in the file
			if EOL {
				fmt.Fprintln(os.Stderr, " Got EOL while getting HashValue (2nd) token in the hashing file.  Skipping")
				continue
			}
			HashValueReadFromFile = SecondToken.Str
			hashlength = len(SecondToken.Str)

		} else { // have hash first on line
			HashValueReadFromFile = FirstToken.Str
			hashlength = len(FirstToken.Str)
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get name of file on which to compute the hash
			if EOL {
				fmt.Fprintln(os.Stderr, " EOL while gatting TargetFilename token in the hashing file.  Skipping")
				continue
			}

			if strings.ContainsRune(SecondToken.Str, '*') { // If it contains a *, it will be the first position.
				SecondToken.Str = SecondToken.Str[1:]
				if strings.ContainsRune(SecondToken.Str, '*') { // this should not happen
					fmt.Println(" Filename token still contains a * character.  Str:", SecondToken.Str, "  Skipping.")
					continue
				}
			}
			TargetFilename = (SecondToken.Str)
		} /* if have filename first or hash value first */

		//   now to compute the hash, compare them, and output results

		// Create Hash Section
		TargetFile, err := os.Open(TargetFilename)
		if os.IsNotExist(err) {
			fmt.Fprintln(os.Stderr, TargetFilename, " does not exist.  Skipping.")
			continue
		} else { // we know that the file exists
			check(err, " Error opening TargetFilename.")
		}

		defer TargetFile.Close()

		if WhichHash == undetermined {
			if hashlength == 64 {
				WhichHash = sha256hash
			} else if hashlength == 128 {
				WhichHash = sha512hash
			} else if hashlength == 40 {
				WhichHash = sha1hash
			} else if hashlength == 96 {
				WhichHash = sha384hash
			} else if hashlength == 32 {
				WhichHash = md5hash
			} else {
				fmt.Fprintln(os.Stderr, " Could not determine hash type for file.  Skipping.")
				continue
			}
			fmt.Println(" hash determined by length to be", HashName[WhichHash])
			fmt.Println()
		}

		switch WhichHash { // Initialing case switch on WhichHash
		case md5hash:
			hasher = md5.New()
		case sha1hash:
			hasher = sha1.New()
		case sha256hash:
			hasher = sha256.New()
		case sha384hash:
			hasher = sha512.New384()
		case sha512hash:
			hasher = sha512.New()
		default:
			hasher = sha256.New()
		}

		FileSize, err = io.Copy(hasher, TargetFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "skipped.")
			continue
		}
		HashValueComputedStr = hex.EncodeToString(hasher.Sum(nil))

		// I got the idea to use the different base64 versions and my own hex converter code, just to see.
		// And I can also use Sfprintf with the %x verb.  base64 versions are not useful as they use a larger
		// character set than hex.  I deleted all references to the base64 versions.  And the hex encoded and
		// Sprintf using %x were the same, so I removed the sprintf code.
		//    HashValueComputedSprintf := fmt.Sprintf("%x",hasher.Sum(nil));

		fmt.Printf(" Filename  = %s, filesize = %d, using hash %s.\n", TargetFilename, FileSize, HashName[WhichHash])
		fmt.Println("       Read From File:", HashValueReadFromFile)
		fmt.Println(" Computed hex encoded:", HashValueComputedStr)

		if strings.ToLower(HashValueReadFromFile) == strings.ToLower(HashValueComputedStr) {
			fmt.Print(" Matched.")
		} else {
			fmt.Print(" Not matched.")
		}                  /* if hashes */
		TargetFile.Close() // Close the handle to allow opening a target from the next line, if there is one.
		fmt.Println()
		fmt.Println()
	} // outer LOOP to read multiple lines
} // Main for sha116.go.

// ------------------------------------------------------- check -------------------------------
func check(e error, msg string) {
	if e != nil {
		fmt.Println(msg)
		panic(e)
	}
}

// ------------------------------------------------------- min ---------------------------------
func min(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}
<commit_msg>01/25/2022 06:57:30 PM sha116/sha116.go<commit_after>package main // for sha116.go

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"

	"src/filepicker"
	"src/tknptr"
	"strconv"
	"strings"
)

/*
  REVISION HISTORY
  ----------------
   6 Apr 13 -- M2:  First modified version of module.  I will use VLI to compare all digits of the hashes.
  23 Apr 13 -- Fixed problem of a single line in the hashes file, that does not contain an EOL character, causes
                an immediate return without processing of the characters just read in.
  24 Apr 13 -- Added output of which file either matches or does not match.
  19 Sep 16 -- Finished conversion to Go, that was started 13 Sep 16.  Added the removal of '*' which is part of a std linux formatted hash file.  And I forgot that
                 the routine allowed either order in the file.  If the token has a '.' I assume it is a filename, else it is a hash value.
  21 Sep 16 -- Fixed the case issue in tokenize.GetToken.  Edited code here to correspond to this fix.
  25 Nov 16 -- Need to not panic when target file is not found, only panic when hash file is not found.
                 And added a LastCompiled message and string.
  13 Oct 17 -- No changes here, but tokenize was changed so that horizontal tab char is now a delim.
  14 Oct 17 -- Tweaked output a bit.  And added executable timestamp code.
  19 Oct 17 -- Added ability to ignore the * that standard hash files for linux use.
  22 Oct 17 -- Added filepicker.
  21 Jan 18 -- Really ignore *.  Before method did not work.
  26 Jan 18 -- Changed tokenize so that SetMapDelim change sticks and actually works.
  13 Nov 18 -- Will use "-" and "_" also to detect a filename token.
  10 Nov 19 -- Now uses ToLower to compare the string hashes, to ignore case.
  15 Jul 20 -- Decided to make better guesses.  Sha1 has 40 digits, Sha256 has 64 digits and Sha512 has 128 digits.
  27 Sep 20 -- From help file of TakeCommand: MD-5 has 32 digits, SHA384 has 96 digits, and the above hash lengths are correct.
                 And I'm going to change from tokenize to tknptr.  Just to see if it works.
  25 Feb 21 -- Added 999 as a stop code.
   3 Mar 21 -- Now called sha.go, which will always use hash length, while ignoring file extension.
                 Errors now go to Stderr.  Uses bytes buffer to read sha file using os.ReadAll, using go 1.16.
   7 Mar 21 -- Added use of strings.TrimSpace()
   8 Apr 21 -- Converting to module version of ~/go/src.
  24 Jan 22 -- Adding a help message using the flag package.  And since I recently changed the interface for filepicker, I have to fix that here too.
*/

const LastCompiled = "25 Jan 2022"

//* ************************* MAIN ***************************************************************
func main() {

	const K = 1024
	const M = 1024 * 1024

	const (
		undetermined = iota
		md5hash
		sha1hash
		sha256hash
		sha384hash
		sha512hash
	)

	const ReadBufferSize = M

	var HashName = [...]string{"undetermined", "md5", "sha1", "sha256", "sha384", "sha512"}
	var ans, Filename string
	var WhichHash int
	var TargetFilename, HashValueReadFromFile, HashValueComputedStr string
	var hasher hash.Hash
	var FileSize int64

	workingdir, _ := os.Getwd()
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	LastLinkedTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")

	// flag help message
	flag.Usage = func() {
		fmt.Fprintf(flag.CommandLine.Output(), " Hash comparer, last modified %s.\n", LastCompiled)
		fmt.Fprintf(flag.CommandLine.Output(), " Filename to compare to its hash are on the same line in either order.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " File of hashes can be given on the command line, or filepicker will be called to pick it.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " Lines of the hashes file are ignored if <= 10 characters, or begin wtih ';' or '#'.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " And only first 2 tokens on a line are read.\n")
		fmt.Fprintf(flag.CommandLine.Output(), " %s has timestamp of %s.  Working directory is %s.  Full name of executable is %s.\n", ExecFI.Name(), LastLinkedTimeStamp, workingdir, execname)
		fmt.Fprintln(flag.CommandLine.Output())
		flag.PrintDefaults()
	}
	var verboseFlag bool
	flag.BoolVar(&verboseFlag, "v", false, " verbose mode.")
	flag.Parse()

	//fmt.Print(" sha.go.  GOOS =", runtime.GOOS, ".  ARCH=", runtime.GOARCH)
	//fmt.Println(".  Last altered", LastCompiled, ", compiled using", runtime.Version())

	// filepicker stuff.

	if flag.NArg() == 0 { // need to use filepicker
		filenames, err := filepicker.GetFilenames("*.sha*")
		if err != nil {
			fmt.Fprintln(os.Stderr, " Error from filepicker is", err)
			os.Exit(1)
		}
		for i := 0; i < min(len(filenames), 20); i++ {
			fmt.Printf("filename[%d, %c] is %s\n", i, i+'a', filenames[i])
		}
		fmt.Print(" Enter filename choice : ")
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			ans = "0"
		} else if ans == "999" {
			fmt.Println(" Stop code entered.  Exiting.")
			os.Exit(0)
		}
		i, err := strconv.Atoi(ans)
		if err == nil && i < len(filenames) {
			Filename = filenames[i]
		} else {
			s := strings.ToUpper(ans)
			s = strings.TrimSpace(s)
			s0 := s[0]
			i = int(s0 - 'A')
			if i < len(filenames) {
				Filename = filenames[i]
			}
		}
		if len(Filename) == 0 { // if entered choice is out of range, switch to use 0.  It's inelegant to panic.
			Filename = filenames[0]
		}
		fmt.Println(" Picked filename is", Filename)
	} else { // will use filename entered on commandline
		//            Filename = getcommandline.GetCommandLineString()  removed 3/3/21, as os.Args is fine.
		//Filename = os.Args[1]
		Filename = flag.Arg(0)
	}

	fmt.Println()

	// Now ignores extension, always going by hash length.

	// Read and parse the file with the hashes.

	//filebyteslice := make([]byte, 0, 2000)
	fileByteSlice, err := os.ReadFile(Filename)
	if os.IsNotExist(err) {
		fmt.Println(Filename, " does not exist.  Exiting.")
		os.Exit(1)
	} else if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytesBuffer := bytes.NewBuffer(fileByteSlice)

	for { /* to read multiple lines */
		FileSize = 0
		WhichHash = undetermined // reset it for this next line, allowing multiple types of hashes in same file.

		inputline, err := bytesBuffer.ReadString('\n')
		if err == io.EOF && len(inputline) == 0 { // reached EOF condition, there are no more lines to read, and no line
			break
		} else if len(inputline) == 0 && err != nil {
			fmt.Fprintln(os.Stderr, "While reading from the HashesFile:", err)
			os.Exit(1)
		}
		inputline = strings.TrimSpace(inputline) // trims off the trailing newline

		if strings.HasPrefix(inputline, ";") || strings.HasPrefix(inputline, "#") || (len(inputline) <= 10) {
			continue
		} // allow comments and essentially blank lines

		tokenPtr := tknptr.NewToken(inputline)
		tokenPtr.SetMapDelim('*')
		FirstToken, EOL := tokenPtr.GetTokenString(false)
		if EOL {
			fmt.Fprintln(os.Stderr, " EOL while getting 1st token in the hashing file.  Skipping to next line.")
			continue
		}
		hashlength := 0

		if strings.ContainsRune(FirstToken.Str, '.') || strings.ContainsRune(FirstToken.Str, '-') ||
			strings.ContainsRune(FirstToken.Str, '_') { // have filename first on line
			TargetFilename = FirstToken.Str
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get hash string from the line in the file
			if EOL {
				fmt.Fprintln(os.Stderr, " Got EOL while getting HashValue (2nd) token in the hashing file.  Skipping")
				continue
			}
			HashValueReadFromFile = SecondToken.Str
			hashlength = len(SecondToken.Str)

		} else { // have hash first on line
			HashValueReadFromFile = FirstToken.Str
			hashlength = len(FirstToken.Str)
			SecondToken, EOL := tokenPtr.GetTokenString(false) // Get name of file on which to compute the hash
			if EOL {
				fmt.Fprintln(os.Stderr, " EOL while gatting TargetFilename token in the hashing file.  Skipping")
				continue
			}

			if strings.ContainsRune(SecondToken.Str, '*') { // If it contains a *, it will be the first position.
				SecondToken.Str = SecondToken.Str[1:]
				if strings.ContainsRune(SecondToken.Str, '*') { // this should not happen
					fmt.Println(" Filename token still contains a * character.  Str:", SecondToken.Str, "  Skipping.")
					continue
				}
			}
			TargetFilename = (SecondToken.Str)
		} /* if have filename first or hash value first */

		//   now to compute the hash, compare them, and output results

		// Create Hash Section
		TargetFile, err := os.Open(TargetFilename)
		if os.IsNotExist(err) {
			fmt.Fprintln(os.Stderr, TargetFilename, " does not exist.  Skipping.")
			continue
		} else { // we know that the file exists
			check(err, " Error opening TargetFilename.")
		}

		defer TargetFile.Close()

		if WhichHash == undetermined {
			if hashlength == 64 {
				WhichHash = sha256hash
			} else if hashlength == 128 {
				WhichHash = sha512hash
			} else if hashlength == 40 {
				WhichHash = sha1hash
			} else if hashlength == 96 {
				WhichHash = sha384hash
			} else if hashlength == 32 {
				WhichHash = md5hash
			} else {
				fmt.Fprintln(os.Stderr, " Could not determine hash type for file.  Skipping.")
				continue
			}
			fmt.Println(" hash determined by length to be", HashName[WhichHash])
			fmt.Println()
		}

		switch WhichHash { // Initialing case switch on WhichHash
		case md5hash:
			hasher = md5.New()
		case sha1hash:
			hasher = sha1.New()
		case sha256hash:
			hasher = sha256.New()
		case sha384hash:
			hasher = sha512.New384()
		case sha512hash:
			hasher = sha512.New()
		default:
			hasher = sha256.New()
		}

		FileSize, err = io.Copy(hasher, TargetFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "skipped.")
			continue
		}
		HashValueComputedStr = hex.EncodeToString(hasher.Sum(nil))

		// I got the idea to use the different base64 versions and my own hex converter code, just to see.
		// And I can also use Sfprintf with the %x verb.  base64 versions are not useful as they use a larger
		// character set than hex.  I deleted all references to the base64 versions.  And the hex encoded and
		// Sprintf using %x were the same, so I removed the sprintf code.
		//    HashValueComputedSprintf := fmt.Sprintf("%x",hasher.Sum(nil));

		fmt.Printf(" Filename  = %s, filesize = %d, using hash %s.\n", TargetFilename, FileSize, HashName[WhichHash])
		fmt.Println("       Read From File:", HashValueReadFromFile)
		fmt.Println(" Computed hex encoded:", HashValueComputedStr)

		if strings.ToLower(HashValueReadFromFile) == strings.ToLower(HashValueComputedStr) {
			fmt.Print(" Matched.")
		} else {
			fmt.Print(" Not matched.")
		} /* if hashes */
		TargetFile.Close() // Close the handle to allow opening a target from the next line, if there is one.
		fmt.Println()
		fmt.Println()
	} // outer LOOP to read multiple lines
} // Main for sha116.go.

// ------------------------------------------------------- check -------------------------------
func check(e error, msg string) {
	if e != nil {
		fmt.Println(msg)
		panic(e)
	}
}

// ------------------------------------------------------- min ---------------------------------
func min(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}
<|endoftext|>


<commit_before>// +build linux

package nsinit

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
	"github.com/dotcloud/docker/pkg/libcontainer/network"
	"github.com/dotcloud/docker/pkg/libcontainer/utils"
	"github.com/dotcloud/docker/pkg/system"
	"github.com/dotcloud/docker/pkg/user"
	"os"
	"syscall"
)

// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
// and other options required for the new container.
func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
	rootfs, err := utils.ResolveRootfs(uncleanRootfs)
	if err != nil {
		return err
	}

	// We always read this as it is a way to sync with the parent as well
	context, err := syncPipe.ReadFromParent()
	if err != nil {
		syncPipe.Close()
		return err
	}
	syncPipe.Close()

	if console != "" {
		slave, err := system.OpenTerminal(console, syscall.O_RDWR)
		if err != nil {
			return fmt.Errorf("open terminal %s", err)
		}
		if err := dupSlave(slave); err != nil {
			return fmt.Errorf("dup2 slave %s", err)
		}
	}
	if _, err := system.Setsid(); err != nil {
		return fmt.Errorf("setsid %s", err)
	}
	if console != "" {
		if err := system.Setctty(); err != nil {
			return fmt.Errorf("setctty %s", err)
		}
	}
	/*
		if err := system.ParentDeathSignal(); err != nil {
			return fmt.Errorf("parent death signal %s", err)
		}
	*/
	if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
		return fmt.Errorf("setup mount namespace %s", err)
	}
	if err := setupNetwork(container, context); err != nil {
		return fmt.Errorf("setup networking %s", err)
	}
	if err := system.Sethostname(container.Hostname); err != nil {
		return fmt.Errorf("sethostname %s", err)
	}
	if err := finalizeNamespace(container); err != nil {
		return fmt.Errorf("finalize namespace %s", err)
	}

	if err := apparmor.ApplyProfile(os.Getpid(), container.Context["apparmor_profile"]); err != nil {
		return err
	}
	return system.Execv(args[0], args[0:], container.Env)
}

func setupUser(container *libcontainer.Container) error {
	switch container.User {
	case "root", "":
		if err := system.Setgroups(nil); err != nil {
			return err
		}
		if err := system.Setresgid(0, 0, 0); err != nil {
			return err
		}
		if err := system.Setresuid(0, 0, 0); err != nil {
			return err
		}
	default:
		uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
		if err != nil {
			return err
		}
		if err := system.Setgroups(suppGids); err != nil {
			return err
		}
		if err := system.Setgid(gid); err != nil {
			return err
		}
		if err := system.Setuid(uid); err != nil {
			return err
		}
	}
	return nil
}

// dupSlave dup2 the pty slave's fd into stdout and stdin and ensures that
// the slave's fd is 0, or stdin
func dupSlave(slave *os.File) error {
	if err := system.Dup2(slave.Fd(), 0); err != nil {
		return err
	}
	if err := system.Dup2(slave.Fd(), 1); err != nil {
		return err
	}
	if err := system.Dup2(slave.Fd(), 2); err != nil {
		return err
	}
	return nil
}

// setupVethNetwork uses the Network config if it is not nil to initialize
// the new veth interface inside the container for use by changing the name to eth0
// setting the MTU and IP address along with the default gateway
func setupNetwork(container *libcontainer.Container, context libcontainer.Context) error {
	for _, config := range container.Networks {
		strategy, err := network.GetStrategy(config.Type)
		if err != nil {
			return err
		}
		return strategy.Initialize(config, context)
	}
	return nil
}

// finalizeNamespace drops the caps and sets the correct user
// and working dir before execing the command inside the namespace
func finalizeNamespace(container *libcontainer.Container) error {
	if err := capabilities.DropCapabilities(container); err != nil {
		return fmt.Errorf("drop capabilities %s", err)
	}
	if err := setupUser(container); err != nil {
		return fmt.Errorf("setup user %s", err)
	}
	if container.WorkingDir != "" {
		if err := system.Chdir(container.WorkingDir); err != nil {
			return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
		}
	}
	return nil
}
<commit_msg>Ensure that native containers die with the parent Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)<commit_after>// +build linux

package nsinit

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
	"github.com/dotcloud/docker/pkg/libcontainer/network"
	"github.com/dotcloud/docker/pkg/libcontainer/utils"
	"github.com/dotcloud/docker/pkg/system"
	"github.com/dotcloud/docker/pkg/user"
	"os"
	"syscall"
)

// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
// and other options required for the new container.
func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
	rootfs, err := utils.ResolveRootfs(uncleanRootfs)
	if err != nil {
		return err
	}

	// We always read this as it is a way to sync with the parent as well
	context, err := syncPipe.ReadFromParent()
	if err != nil {
		syncPipe.Close()
		return err
	}
	syncPipe.Close()

	if console != "" {
		slave, err := system.OpenTerminal(console, syscall.O_RDWR)
		if err != nil {
			return fmt.Errorf("open terminal %s", err)
		}
		if err := dupSlave(slave); err != nil {
			return fmt.Errorf("dup2 slave %s", err)
		}
	}
	if _, err := system.Setsid(); err != nil {
		return fmt.Errorf("setsid %s", err)
	}
	if console != "" {
		if err := system.Setctty(); err != nil {
			return fmt.Errorf("setctty %s", err)
		}
	}
	if err := system.ParentDeathSignal(); err != nil {
		return fmt.Errorf("parent death signal %s", err)
	}
	if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
		return fmt.Errorf("setup mount namespace %s", err)
	}
	if err := setupNetwork(container, context); err != nil {
		return fmt.Errorf("setup networking %s", err)
	}
	if err := system.Sethostname(container.Hostname); err != nil {
		return fmt.Errorf("sethostname %s", err)
	}
	if err := finalizeNamespace(container); err != nil {
		return fmt.Errorf("finalize namespace %s", err)
	}

	if err := apparmor.ApplyProfile(os.Getpid(), container.Context["apparmor_profile"]); err != nil {
		return err
	}
	return system.Execv(args[0], args[0:], container.Env)
}

func setupUser(container *libcontainer.Container) error {
	switch container.User {
	case "root", "":
		if err := system.Setgroups(nil); err != nil {
			return err
		}
		if err := system.Setresgid(0, 0, 0); err != nil {
			return err
		}
		if err := system.Setresuid(0, 0, 0); err != nil {
			return err
		}
	default:
		uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
		if err != nil {
			return err
		}
		if err := system.Setgroups(suppGids); err != nil {
			return err
		}
		if err := system.Setgid(gid); err != nil {
			return err
		}
		if err := system.Setuid(uid); err != nil {
			return err
		}
	}
	return nil
}

// dupSlave dup2 the pty slave's fd into stdout and stdin and ensures that
// the slave's fd is 0, or stdin
func dupSlave(slave *os.File) error {
	if err := system.Dup2(slave.Fd(), 0); err != nil {
		return err
	}
	if err := system.Dup2(slave.Fd(), 1); err != nil {
		return err
	}
	if err := system.Dup2(slave.Fd(), 2); err != nil {
		return err
	}
	return nil
}

// setupVethNetwork uses the Network config if it is not nil to initialize
// the new veth interface inside the container for use by changing the name to eth0
// setting the MTU and IP address along with the default gateway
func setupNetwork(container *libcontainer.Container, context libcontainer.Context) error {
	for _, config := range container.Networks {
		strategy, err := network.GetStrategy(config.Type)
		if err != nil {
			return err
		}
		return strategy.Initialize(config, context)
	}
	return nil
}

// finalizeNamespace drops the caps and sets the correct user
// and working dir before execing the command inside the namespace
func finalizeNamespace(container *libcontainer.Container) error {
	if err := capabilities.DropCapabilities(container); err != nil {
		return fmt.Errorf("drop capabilities %s", err)
	}
	if err := setupUser(container); err != nil {
		return fmt.Errorf("setup user %s", err)
	}
	if container.WorkingDir != "" {
		if err := system.Chdir(container.WorkingDir); err != nil {
			return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"log"
)

const Version = "3.38.0"

func PrintVersion() {
	log.Printf("[INFO] terraform-provider-oci %s\n", Version)
}
<commit_msg>Finalize changelog and release for version v3.39.0<commit_after>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"log"
)

const Version = "3.39.0"

func PrintVersion() {
	log.Printf("[INFO] terraform-provider-oci %s\n", Version)
}
<|endoftext|>


<commit_before>package of10

import (
	"bytes"
	"encoding"
	"encoding/binary"
	"errors"
	"io"
)

var (
	errUnsupportedAction = errors.New("Unsupported action type")
)

type ActionType uint16

type Action interface {
	GetType() ActionType
	encoding.BinaryUnmarshaler
}

type actionDecoder struct {
	rd  *bytes.Reader
	end int
}

func readActions(rd *bytes.Reader, length int) []Action {
	actions := make([]Action, 0, 8)
	decoder := &actionDecoder{rd, rd.Len() - length}
	for decoder.canDecode() {
		action, err := decoder.decode()
		if err != nil {
			break
		}
		actions = append(actions, action)
	}
	return actions
}

func (d *actionDecoder) canDecode() bool {
	return d.rd.Len() > d.end
}

func (d *actionDecoder) header() (*ActionHeader, error) {
	start := d.rd.Len()
	var header ActionHeader
	if err := binary.Read(d.rd, binary.BigEndian, &header); err != nil {
		return nil, err
	}

	// unread length of header
	offset := d.rd.Len() - start
	d.rd.Seek(int64(offset), 1)

	return &header, nil
}

func (d *actionDecoder) decode() (Action, error) {
	header, err := d.header()
	if err != nil {
		return nil, err
	}

	action := newAction(header.Type)
	if action == nil {
		return nil, errUnsupportedAction
	}

	data := make([]byte, header.Length)
	if _, err := io.ReadFull(d.rd, data); err != nil {
		return nil, err
	}

	if err := action.UnmarshalBinary(data); err != nil {
		return nil, err
	}

	return action, nil
}

func newAction(t ActionType) Action {
	switch t {
	case ActionTypes.Output:
		return new(SendOutPort)
	case ActionTypes.SetVlanId:
		return new(SetVlanVid)
	case ActionTypes.SetVlanPcp:
		return new(SetVlanPcp)
	case ActionTypes.StripVlan:
		return new(StripVlan)
	case ActionTypes.SetEtherSrc:
		return new(SetEtherSrc)
	case ActionTypes.SetEtherDst:
		return new(SetEtherDst)
	case ActionTypes.SetIpSrc:
		return new(SetIpSrc)
	case ActionTypes.SetIpDst:
		return new(SetIpDst)
	case ActionTypes.SetIpTos:
		return new(SetIpTos)
	case ActionTypes.SetNetworkSrc:
		return new(SetTransportSrc)
	case ActionTypes.SetNetworkDst:
		return new(SetTransportDst)
	case ActionTypes.Enqueue:
		return new(Enqueue)
	case ActionTypes.Vendor:
		return new(VendorActionHeader)
	default:
		return nil
	}
}

type ActionHeader struct {
	Type   ActionType
	Length uint16
}

func (header *ActionHeader) GetType() ActionType {
	return header.Type
}

type SendOutPort struct {
	ActionHeader
	Port      PortNumber
	MaxLength uint16
}

func (m *SendOutPort) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type Enqueue struct {
	ActionHeader
	Port    PortNumber
	_       [6]uint8
	QueueId uint32
}

func (m *Enqueue) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetVlanVid struct {
	ActionHeader
	Id VlanId
	_  [2]uint32
}

func (m *SetVlanVid) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetVlanPcp struct {
	ActionHeader
	Priority VlanPriority
	_        [3]uint8
}

func (m *SetVlanPcp) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type StripVlan struct {
	ActionHeader
}

func (m *StripVlan) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetEtherSrc struct {
	ActionHeader
	Address [EthernetAddressLength]uint8
	pad     [6]uint8
}

func (a *SetEtherSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetEtherDst struct {
	ActionHeader
	Address [EthernetAddressLength]uint8
	pad     [6]uint8
}

func (a *SetEtherDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpSrc struct {
	ActionHeader
	Address [4]uint8
}

func (a *SetIpSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpDst struct {
	ActionHeader
	Address [4]uint8
}

func (a *SetIpDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpTos struct {
	ActionHeader
	Tos Dscp
	pad [3]uint8
}

func (a *SetIpTos) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetTransportSrc struct {
	ActionHeader
	Port TransportPort
	pad  [2]uint8
}

func (a *SetTransportSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetTransportDst struct {
	ActionHeader
	Port TransportPort
	pad  [2]uint8
}

func (a *SetTransportDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type VendorActionHeader struct {
	ActionHeader
	Vendor VendorId
}

func (a *VendorActionHeader) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type VendorId uint32
<commit_msg>Refactor actionDecoder<commit_after>package of10

import (
	"bytes"
	"encoding"
	"encoding/binary"
	"errors"
	"io"
)

var (
	errUnsupportedAction = errors.New("Unsupported action type")
)

type ActionType uint16

type Action interface {
	GetType() ActionType
	encoding.BinaryUnmarshaler
}

type actionDecoder struct {
	rd     *bytes.Reader
	length int
	read   int
}

func readActions(rd *bytes.Reader, length int) []Action {
	actions := make([]Action, 0, 8)
	decoder := &actionDecoder{rd: rd, length: length}
	for {
		action, err := decoder.decode()
		if err != nil {
			break
		}
		actions = append(actions, action)
		if !decoder.canDecode() {
			break
		}
	}
	return actions
}

func (d *actionDecoder) canDecode() bool {
	return d.read >= d.length
}

func (d *actionDecoder) header() (*ActionHeader, error) {
	start := d.rd.Len()
	var header ActionHeader
	if err := binary.Read(d.rd, binary.BigEndian, &header); err != nil {
		return nil, err
	}

	// unread length of header
	offset := d.rd.Len() - start
	d.rd.Seek(int64(offset), 1)

	return &header, nil
}

func (d *actionDecoder) decode() (Action, error) {
	header, err := d.header()
	if err != nil {
		return nil, err
	}

	action := newAction(header.Type)
	if action == nil {
		return nil, errUnsupportedAction
	}

	data := make([]byte, header.Length)
	if _, err := io.ReadFull(d.rd, data); err != nil {
		return nil, err
	}
	d.read += len(data)

	if err := action.UnmarshalBinary(data); err != nil {
		return nil, err
	}

	return action, nil
}

func newAction(t ActionType) Action {
	switch t {
	case ActionTypes.Output:
		return new(SendOutPort)
	case ActionTypes.SetVlanId:
		return new(SetVlanVid)
	case ActionTypes.SetVlanPcp:
		return new(SetVlanPcp)
	case ActionTypes.StripVlan:
		return new(StripVlan)
	case ActionTypes.SetEtherSrc:
		return new(SetEtherSrc)
	case ActionTypes.SetEtherDst:
		return new(SetEtherDst)
	case ActionTypes.SetIpSrc:
		return new(SetIpSrc)
	case ActionTypes.SetIpDst:
		return new(SetIpDst)
	case ActionTypes.SetIpTos:
		return new(SetIpTos)
	case ActionTypes.SetNetworkSrc:
		return new(SetTransportSrc)
	case ActionTypes.SetNetworkDst:
		return new(SetTransportDst)
	case ActionTypes.Enqueue:
		return new(Enqueue)
	case ActionTypes.Vendor:
		return new(VendorActionHeader)
	default:
		return nil
	}
}

type ActionHeader struct {
	Type   ActionType
	Length uint16
}

func (header *ActionHeader) GetType() ActionType {
	return header.Type
}

type SendOutPort struct {
	ActionHeader
	Port      PortNumber
	MaxLength uint16
}

func (m *SendOutPort) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type Enqueue struct {
	ActionHeader
	Port    PortNumber
	_       [6]uint8
	QueueId uint32
}

func (m *Enqueue) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetVlanVid struct {
	ActionHeader
	Id VlanId
	_  [2]uint32
}

func (m *SetVlanVid) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetVlanPcp struct {
	ActionHeader
	Priority VlanPriority
	_        [3]uint8
}

func (m *SetVlanPcp) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type StripVlan struct {
	ActionHeader
}

func (m *StripVlan) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), m)
}

type SetEtherSrc struct {
	ActionHeader
	Address [EthernetAddressLength]uint8
	pad     [6]uint8
}

func (a *SetEtherSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetEtherDst struct {
	ActionHeader
	Address [EthernetAddressLength]uint8
	pad     [6]uint8
}

func (a *SetEtherDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpSrc struct {
	ActionHeader
	Address [4]uint8
}

func (a *SetIpSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpDst struct {
	ActionHeader
	Address [4]uint8
}

func (a *SetIpDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetIpTos struct {
	ActionHeader
	Tos Dscp
	pad [3]uint8
}

func (a *SetIpTos) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetTransportSrc struct {
	ActionHeader
	Port TransportPort
	pad  [2]uint8
}

func (a *SetTransportSrc) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type SetTransportDst struct {
	ActionHeader
	Port TransportPort
	pad  [2]uint8
}

func (a *SetTransportDst) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type VendorActionHeader struct {
	ActionHeader
	Vendor VendorId
}

func (a *VendorActionHeader) UnmarshalBinary(data []byte) error {
	return unmarshalFields(bytes.NewReader(data), a)
}

type VendorId uint32
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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 proxy provides a record/replay HTTP proxy. It is designed to support
// both an in-memory API (cloud.google.com/go/httpreplay) and a standalone server
// (cloud.google.com/go/httpreplay/cmd/httpr).
package proxy

// See github.com/google/martian/cmd/proxy/main.go for the origin of much of this.

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/google/martian/v3"
	"github.com/google/martian/v3/fifo"
	"github.com/google/martian/v3/httpspec"
	"github.com/google/martian/v3/martianlog"
	"github.com/google/martian/v3/mitm"
)

// A Proxy is an HTTP proxy that supports recording or replaying requests.
type Proxy struct {
	// The certificate that the proxy uses to participate in TLS.
	CACert *x509.Certificate

	// The URL of the proxy.
	URL *url.URL

	// Initial state of the client.
	Initial []byte

	mproxy        *martian.Proxy
	filename      string          // for log
	logger        *Logger         // for recording only
	ignoreHeaders map[string]bool // headers the user has asked to ignore
}

// ForRecording returns a Proxy configured to record.
func ForRecording(filename string, port int) (*Proxy, error) {
	p, err := newProxy(filename)
	if err != nil {
		return nil, err
	}

	// Construct a group that performs the standard proxy stack of request/response
	// modifications.
	stack, _ := httpspec.NewStack("httpr") // second arg is an internal group that we don't need
	p.mproxy.SetRequestModifier(stack)
	p.mproxy.SetResponseModifier(stack)

	// Make a group for logging requests and responses.
	logGroup := fifo.NewGroup()
	skipAuth := skipLoggingByHost("accounts.google.com")
	logGroup.AddRequestModifier(skipAuth)
	logGroup.AddResponseModifier(skipAuth)
	p.logger = newLogger()
	logGroup.AddRequestModifier(p.logger)
	logGroup.AddResponseModifier(p.logger)

	stack.AddRequestModifier(logGroup)
	stack.AddResponseModifier(logGroup)

	// Ordinary debug logging.
	logger := martianlog.NewLogger()
	logger.SetDecode(true)
	stack.AddRequestModifier(logger)
	stack.AddResponseModifier(logger)

	if err := p.start(port); err != nil {
		return nil, err
	}
	return p, nil
}

var (
	configOnce sync.Once
	cert       *x509.Certificate
	config     *mitm.Config
	configErr  error
)

func newProxy(filename string) (*Proxy, error) {
	configOnce.Do(func() {
		// Set up a man-in-the-middle configuration with a CA certificate so the proxy can
		// participate in TLS.
		x509c, priv, err := mitm.NewAuthority("cloud.google.com/go/httpreplay", "HTTPReplay Authority", 100*time.Hour)
		if err != nil {
			configErr = err
			return
		}
		cert = x509c
		config, configErr = mitm.NewConfig(x509c, priv)
		if config != nil {
			config.SetValidity(100 * time.Hour)
			config.SetOrganization("cloud.google.com/go/httpreplay")
			config.SkipTLSVerify(false)
		}
	})
	if configErr != nil {
		return nil, configErr
	}
	mproxy := martian.NewProxy()
	mproxy.SetMITM(config)
	return &Proxy{
		mproxy:        mproxy,
		CACert:        cert,
		filename:      filename,
		ignoreHeaders: map[string]bool{},
	}, nil
}

func (p *Proxy) start(port int) error {
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return err
	}
	p.URL = &url.URL{Scheme: "http", Host: l.Addr().String()}
	go p.mproxy.Serve(l)
	return nil
}

// Transport returns an http.Transport for clients who want to talk to the proxy.
func (p *Proxy) Transport() *http.Transport {
	caCertPool := x509.NewCertPool()
	caCertPool.AddCert(p.CACert)
	return &http.Transport{
		TLSClientConfig: &tls.Config{RootCAs: caCertPool},
		Proxy:           func(*http.Request) (*url.URL, error) { return p.URL, nil },
	}
}

// RemoveRequestHeaders will remove request headers matching patterns from the log,
// and skip matching them. Pattern is taken literally except for *, which matches any
// sequence of characters.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) RemoveRequestHeaders(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerRemoveRequestHeaders(pat)
	}
}

// ClearHeaders will replace matching headers with CLEARED.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) ClearHeaders(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerClearHeaders(pat)
	}
}

// RemoveQueryParams will remove query parameters matching patterns from the request
// URL before logging, and skip matching them. Pattern is taken literally except for
// *, which matches any sequence of characters.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) RemoveQueryParams(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerRemoveParams(pat)
	}
}

// ClearQueryParams will replace matching query params in the request URL with CLEARED.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) ClearQueryParams(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerClearParams(pat)
	}
}

// IgnoreHeader will cause h to be ignored during matching on replay.
// Deprecated: use RemoveRequestHeaders instead.
func (p *Proxy) IgnoreHeader(h string) {
	p.ignoreHeaders[http.CanonicalHeaderKey(h)] = true
}

// Close closes the proxy. If the proxy is recording, it also writes the log.
func (p *Proxy) Close() error {
	p.mproxy.Close()
	if p.logger != nil {
		return p.writeLog()
	}
	return nil
}

func (p *Proxy) writeLog() error {
	lg := p.logger.Extract()
	lg.Initial = p.Initial
	bytes, err := json.MarshalIndent(lg, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(p.filename, bytes, 0600) // only accessible by owner
}

// skipLoggingByHost disables logging for traffic to a particular host.
type skipLoggingByHost string

func (s skipLoggingByHost) ModifyRequest(req *http.Request) error {
	if strings.HasPrefix(req.Host, string(s)) {
		martian.NewContext(req).SkipLogging()
	}
	return nil
}

func (s skipLoggingByHost) ModifyResponse(res *http.Response) error {
	return s.ModifyRequest(res.Request)
}
<commit_msg>chore(httpreplay): specify localhost for proxy listener (#7088)<commit_after>// Copyright 2018 Google LLC
//
// 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 proxy provides a record/replay HTTP proxy. It is designed to support
// both an in-memory API (cloud.google.com/go/httpreplay) and a standalone server
// (cloud.google.com/go/httpreplay/cmd/httpr).
package proxy

// See github.com/google/martian/cmd/proxy/main.go for the origin of much of this.

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/google/martian/v3"
	"github.com/google/martian/v3/fifo"
	"github.com/google/martian/v3/httpspec"
	"github.com/google/martian/v3/martianlog"
	"github.com/google/martian/v3/mitm"
)

// A Proxy is an HTTP proxy that supports recording or replaying requests.
type Proxy struct {
	// The certificate that the proxy uses to participate in TLS.
	CACert *x509.Certificate

	// The URL of the proxy.
	URL *url.URL

	// Initial state of the client.
	Initial []byte

	mproxy        *martian.Proxy
	filename      string          // for log
	logger        *Logger         // for recording only
	ignoreHeaders map[string]bool // headers the user has asked to ignore
}

// ForRecording returns a Proxy configured to record.
func ForRecording(filename string, port int) (*Proxy, error) {
	p, err := newProxy(filename)
	if err != nil {
		return nil, err
	}

	// Construct a group that performs the standard proxy stack of request/response
	// modifications.
	stack, _ := httpspec.NewStack("httpr") // second arg is an internal group that we don't need
	p.mproxy.SetRequestModifier(stack)
	p.mproxy.SetResponseModifier(stack)

	// Make a group for logging requests and responses.
	logGroup := fifo.NewGroup()
	skipAuth := skipLoggingByHost("accounts.google.com")
	logGroup.AddRequestModifier(skipAuth)
	logGroup.AddResponseModifier(skipAuth)
	p.logger = newLogger()
	logGroup.AddRequestModifier(p.logger)
	logGroup.AddResponseModifier(p.logger)

	stack.AddRequestModifier(logGroup)
	stack.AddResponseModifier(logGroup)

	// Ordinary debug logging.
	logger := martianlog.NewLogger()
	logger.SetDecode(true)
	stack.AddRequestModifier(logger)
	stack.AddResponseModifier(logger)

	if err := p.start(port); err != nil {
		return nil, err
	}
	return p, nil
}

var (
	configOnce sync.Once
	cert       *x509.Certificate
	config     *mitm.Config
	configErr  error
)

func newProxy(filename string) (*Proxy, error) {
	configOnce.Do(func() {
		// Set up a man-in-the-middle configuration with a CA certificate so the proxy can
		// participate in TLS.
		x509c, priv, err := mitm.NewAuthority("cloud.google.com/go/httpreplay", "HTTPReplay Authority", 100*time.Hour)
		if err != nil {
			configErr = err
			return
		}
		cert = x509c
		config, configErr = mitm.NewConfig(x509c, priv)
		if config != nil {
			config.SetValidity(100 * time.Hour)
			config.SetOrganization("cloud.google.com/go/httpreplay")
			config.SkipTLSVerify(false)
		}
	})
	if configErr != nil {
		return nil, configErr
	}
	mproxy := martian.NewProxy()
	mproxy.SetMITM(config)
	return &Proxy{
		mproxy:        mproxy,
		CACert:        cert,
		filename:      filename,
		ignoreHeaders: map[string]bool{},
	}, nil
}

func (p *Proxy) start(port int) error {
	l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		return err
	}
	p.URL = &url.URL{Scheme: "http", Host: l.Addr().String()}
	go p.mproxy.Serve(l)
	return nil
}

// Transport returns an http.Transport for clients who want to talk to the proxy.
func (p *Proxy) Transport() *http.Transport {
	caCertPool := x509.NewCertPool()
	caCertPool.AddCert(p.CACert)
	return &http.Transport{
		TLSClientConfig: &tls.Config{RootCAs: caCertPool},
		Proxy:           func(*http.Request) (*url.URL, error) { return p.URL, nil },
	}
}

// RemoveRequestHeaders will remove request headers matching patterns from the log,
// and skip matching them. Pattern is taken literally except for *, which matches any
// sequence of characters.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) RemoveRequestHeaders(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerRemoveRequestHeaders(pat)
	}
}

// ClearHeaders will replace matching headers with CLEARED.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) ClearHeaders(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerClearHeaders(pat)
	}
}

// RemoveQueryParams will remove query parameters matching patterns from the request
// URL before logging, and skip matching them. Pattern is taken literally except for
// *, which matches any sequence of characters.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) RemoveQueryParams(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerRemoveParams(pat)
	}
}

// ClearQueryParams will replace matching query params in the request URL with CLEARED.
//
// This only needs to be called during recording; the patterns will be saved to the
// log for replay.
func (p *Proxy) ClearQueryParams(patterns []string) {
	for _, pat := range patterns {
		p.logger.log.Converter.registerClearParams(pat)
	}
}

// IgnoreHeader will cause h to be ignored during matching on replay.
// Deprecated: use RemoveRequestHeaders instead.
func (p *Proxy) IgnoreHeader(h string) {
	p.ignoreHeaders[http.CanonicalHeaderKey(h)] = true
}

// Close closes the proxy. If the proxy is recording, it also writes the log.
func (p *Proxy) Close() error {
	p.mproxy.Close()
	if p.logger != nil {
		return p.writeLog()
	}
	return nil
}

func (p *Proxy) writeLog() error {
	lg := p.logger.Extract()
	lg.Initial = p.Initial
	bytes, err := json.MarshalIndent(lg, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(p.filename, bytes, 0600) // only accessible by owner
}

// skipLoggingByHost disables logging for traffic to a particular host.
type skipLoggingByHost string

func (s skipLoggingByHost) ModifyRequest(req *http.Request) error {
	if strings.HasPrefix(req.Host, string(s)) {
		martian.NewContext(req).SkipLogging()
	}
	return nil
}

func (s skipLoggingByHost) ModifyResponse(res *http.Response) error {
	return s.ModifyRequest(res.Request)
}
<|endoftext|>