<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"encoding/xml"
	"fmt"
	update "github.com/coreos-inc/updatectl/client/update/v1"
	"github.com/coreos/go-omaha/omaha"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
	}

	cmdInstance = &Command{
		Name:        "instance",
		Usage:       "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil ||
		instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	call.GroupId(instanceFlags.groupId.String())
	call.AppId(instanceFlags.appId.String())
	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tStatus\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId,
			cl.Status, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil ||
		instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Appversion.List()

	call.GroupId(instanceFlags.groupId.String())
	call.AppId(instanceFlags.appId.String())

	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             fmt.Sprintf("{fake-client-%03d}", i),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<commit_msg>fix(instance): remove app-id and group-id requirements<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"encoding/xml"
	"fmt"
	update "github.com/coreos-inc/updatectl/client/update/v1"
	"github.com/coreos/go-omaha/omaha"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
	}

	cmdInstance = &Command{
		Name:        "instance",
		Usage:       "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.groupId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tStatus\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId,
			cl.Status, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Appversion.List()

	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.appId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             fmt.Sprintf("{fake-client-%03d}", i),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"

	"github.com/coreos/updateservicectl/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/updateservicectl/Godeps/_workspace/src/github.com/coreos/go-omaha/omaha"
	update "github.com/coreos/updateservicectl/client/update/v1"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
		forceUpdate   bool
	}

	cmdInstance = &Command{
		Name:    "instance",
		Usage:   "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
	cmdInstanceFake.Flags.BoolVar(&instanceFlags.forceUpdate, "force-update", false, "Force updates regardless of rate limiting")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.groupId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Appversion.List()

	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.appId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
	forceUpdate    bool
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM
	if c.forceUpdate {
		req.InstallSource = "ondemandupdate"
	} else {
		req.InstallSource = "scheduler"
	}

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v\n", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             fmt.Sprintf("{fake-client-%03d}", i),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
			forceUpdate:    instanceFlags.forceUpdate,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<commit_msg>instance: use uuid for clientId<commit_after>package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"

	"github.com/coreos/updateservicectl/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/updateservicectl/Godeps/_workspace/src/github.com/coreos/go-omaha/omaha"
	update "github.com/coreos/updateservicectl/client/update/v1"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
		forceUpdate   bool
	}

	cmdInstance = &Command{
		Name:    "instance",
		Usage:   "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
	cmdInstanceFake.Flags.BoolVar(&instanceFlags.forceUpdate, "force-update", false, "Force updates regardless of rate limiting")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.groupId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Appversion.List()

	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.appId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
	forceUpdate    bool
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM
	if c.forceUpdate {
		req.InstallSource = "ondemandupdate"
	} else {
		req.InstallSource = "scheduler"
	}

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v\n", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             uuid.New(),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
			forceUpdate:    instanceFlags.forceUpdate,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"math"
	"sync"
	"sync/atomic"
	"time"

	"github.com/cockroachdb/cockroach/gossip"
	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
	gogoproto "github.com/gogo/protobuf/proto"
)

const (
	// scanQueueMaxSize is the max size of the scan queue.
	scanQueueMaxSize = 100
	// gcByteCountNormalization is the count of GC'able bytes which
	// amount to a score of "1" added to total range priority.
	gcByteCountNormalization = 1 << 20 // 1 MB
	// intentAgeNormalization is the average age of outstanding intents
	// which amount to a score of "1" added to total range priority.
	intentAgeNormalization = 24 * time.Hour // 1 day
	// intentAgeThreshold is the threshold after which an extant intent
	// will be resolved.
	intentAgeThreshold = 2 * time.Hour // 2 hour
	// verificationInterval is the target duration for verifying on-disk
	// checksums via full scan.
	verificationInterval = 30 * 24 * time.Hour // 30 days
)

// scanQueue manages a queue of ranges slated to be scanned in their
// entirety using the MVCC versions iterator. Currently, range scans
// manage the following tasks:
//
//  - GC of version data via TTL expiration (and more complex schemes
//    as implemented going forward).
//  - Resolve extant write intents and determine oldest non-resolvable
//    intent.
//  - Periodic verification of on-disk checksums to identify bit-rot
//    in read-only data sets. See http://en.wikipedia.org/wiki/Data_degradation.
//
// The shouldQueue function combines the need for all three tasks into
// a single priority. If any task is overdue, shouldQueue returns true.
type scanQueue struct {
	*baseQueue
}

// newScanQueue returns a new instance of scanQueue.
func newScanQueue() *scanQueue {
	sq := &scanQueue{}
	sq.baseQueue = newBaseQueue("scan", sq.shouldQueue, sq.process, scanQueueMaxSize)
	return sq
}

// shouldQueue determines whether a range should be queued for
// scanning, and if so, at what priority. Returns true for shouldQ in
// the event that there are GC'able bytes, or it's been longer since
// the last scan than the intent sweep or verification
// intervals. Priority is derived from the addition of priority from
// GC'able bytes and how many multiples of intent or verification
// intervals have elapsed since the last scan.
func (sq *scanQueue) shouldQueue(now proto.Timestamp, rng *Range) (shouldQ bool, priority float64) {
	// Get last scan metadata & GC policy.
	scanMeta, err := rng.GetScanMetadata()
	if err != nil {
		log.Errorf("unable to fetch scan metadata: %s", err)
		return
	}
	policy, err := sq.lookupGCPolicy(rng)
	if err != nil {
		log.Errorf("GC policy: %s", err)
		return
	}

	// GC score is the total GC'able bytes age normalized by 1 MB * the range's TTL in seconds.
	gcScore := float64(rng.stats.GetGCBytesAge(now.WallTime)) / float64(policy.TTLSeconds) / float64(gcByteCountNormalization)

	// Intent score. This computes the average age of outstanding intents
	// and normalizes.
	intentScore := rng.stats.GetAvgIntentAge(now.WallTime) / float64(intentAgeNormalization.Nanoseconds()/1E9)

	// Verify score.
	verifyScore := float64(now.WallTime-scanMeta.LastScanNanos) / float64(verificationInterval.Nanoseconds())

	// Compute priority.
	if gcScore > 1 {
		priority += gcScore
	}
	if intentScore > 1 {
		priority += intentScore
	}
	if verifyScore > 1 {
		priority += verifyScore
	}
	shouldQ = priority > 0
	return
}

// process iterates through all keys in a range, calling the garbage
// collector for each key and associated set of values. GC'd keys are
// batched into InternalGC calls. Extant intents are resolved if
// intents are older than intentAgeThreshold. The very act of scanning
// keys verifies on-disk checksums, as each block checksum is checked
// on load.
func (sq *scanQueue) process(now proto.Timestamp, rng *Range) error {
	snap := rng.rm.Engine().NewSnapshot()
	iter := newRangeDataIterator(rng, snap)
	defer iter.Close()
	defer snap.Stop()

	// Lookup the GC policy for the zone containing this key range.
	policy, err := sq.lookupGCPolicy(rng)
	if err != nil {
		return err
	}

	scanMeta := proto.NewScanMetadata(now.WallTime)
	gc := engine.NewGarbageCollector(now, policy)

	// Compute intent expiration (intent age at which we attempt to resolve).
	intentExp := now
	intentExp.WallTime -= intentAgeThreshold.Nanoseconds()

	gcArgs := &proto.InternalGCRequest{
		RequestHeader: proto.RequestHeader{
			Key:       rng.Desc.StartKey,
			Timestamp: now,
			RaftID:    rng.Desc.RaftID,
		},
	}
	var oldestIntentNanos int64 = math.MaxInt64
	var wg sync.WaitGroup
	var expBaseKey proto.Key
	var keys []proto.EncodedKey
	var vals [][]byte

	// processKeysAndValues is invoked with each key and its set of
	// values. Intents older than the intent age threshold are sent for
	// resolution and values after the MVCC metadata, and possible
	// intent, are sent for garbage collection.
	processKeysAndValues := func() {
		// If there's more than a single value for the key, possibly send for GC.
		if len(keys) > 1 {
			meta := &proto.MVCCMetadata{}
			if err := gogoproto.Unmarshal(vals[0], meta); err != nil {
				log.Errorf("unable to unmarshal MVCC metadata for key %q: %s", keys[0], err)
			} else {
				// In the event that there's an active intent, send for
				// intent resolution if older than the threshold.
				startIdx := 1
				if meta.Txn != nil {
					// Resolve intent asynchronously in a goroutine if the intent
					// is older than the intent expiration threshold.
					if meta.Timestamp.Less(intentExp) {
						log.Infof("base=%q, now=%s, meta ts=%s, intentExp=%s", expBaseKey, now, meta.Timestamp, intentExp)
						wg.Add(1)
						go sq.resolveIntent(rng, expBaseKey, meta, &oldestIntentNanos, &wg)
					} else if meta.Timestamp.WallTime < atomic.LoadInt64(&oldestIntentNanos) {
						atomic.StoreInt64(&oldestIntentNanos, meta.Timestamp.WallTime)
					}
					// With an active intent, GC ignores MVCC metadata & intent value.
					startIdx = 2
				}
				// See if any values may be GC'd.
				if gcTS := gc.Filter(keys[startIdx:], vals[startIdx:]); !gcTS.Equal(proto.ZeroTimestamp) {
					// TODO(spencer): need to split the requests up into
					// multiple requests in the event that more than X keys
					// are added to the request.
					gcArgs.Keys = append(gcArgs.Keys, proto.InternalGCRequest_GCKey{Key: expBaseKey, Timestamp: gcTS})
				}
			}
		}
	}

	// Iterate through this range's keys and values.
	for ; iter.Valid(); iter.Next() {
		baseKey, ts, isValue := engine.MVCCDecodeKey(iter.Key())
		log.Infof("base key: %q, ts=%s", baseKey, ts)
		if !isValue {
			// Moving to the next key (& values).
			processKeysAndValues()
			expBaseKey = baseKey
			keys = []proto.EncodedKey{iter.Key()}
			vals = [][]byte{iter.Value()}
		} else {
			if !baseKey.Equal(expBaseKey) {
				log.Errorf("unexpectedly found a value for %q with ts=%s; expected key %q", baseKey, ts, expBaseKey)
				continue
			}
			keys = append(keys, iter.Key())
			vals = append(vals, iter.Value())
		}
	}
	// Handle last collected set of keys/vals.
	processKeysAndValues()

	// An error during iteration is presumed to means a checksum failure
	// while iterating over the underlying key/value data.
	if iter.Error() != nil {
		// TODO(spencer): do something other than fatal error here. We
		// want to quarantine this range, make it a non-participating raft
		// follower until it can be replaced and then destroyed.
		log.Fatalf("unhandled failure when scanning range %s; probable data corruption: %s", rng, iter.Error())
	}
	// Wait for any outstanding intent resolves and set oldest extant intent.
	wg.Wait()
	scanMeta.OldestIntentNanos = gogoproto.Int64(oldestIntentNanos)

	// Send GC request through range.
	gcArgs.ScanMeta = *scanMeta
	if err := rng.AddCmd(proto.InternalGC, gcArgs, &proto.InternalGCResponse{}, true); err != nil {
		return err
	}

	return nil
}

// resolveIntent resolves the intent at key by attempting to abort the
// transaction and resolve the intent. If the transaction cannot be
// aborted or intent cannot be resolved, the oldestIntentNanos value
// is atomically updated to the min of oldestIntentNanos and the
// intent's timestamp. The wait group is signaled on completion.
func (sq *scanQueue) resolveIntent(rng *Range, key proto.Key, meta *proto.MVCCMetadata,
	oldestIntentNanos *int64, wg *sync.WaitGroup) {
	defer wg.Done() // signal wait group always on completion

	log.Infof("resolving intent at %q ts=%s", key, meta.Timestamp)

	// Attempt to push the transaction which created the intent.
	now := rng.rm.Clock().Now()
	pushArgs := &proto.InternalPushTxnRequest{
		RequestHeader: proto.RequestHeader{
			Timestamp:    now,
			Key:          meta.Txn.Key,
			User:         UserRoot,
			UserPriority: gogoproto.Int32(proto.MaxPriority),
			Txn:          nil,
		},
		PusheeTxn: *meta.Txn,
		Abort:     true,
	}
	pushReply := &proto.InternalPushTxnResponse{}
	if err := rng.rm.DB().Call(proto.InternalPushTxn, pushArgs, pushReply); err != nil {
		log.Warningf("push of txn %s failed: %s", meta.Txn, err)
		// Atomically update the oldest intent.
		if meta.Timestamp.WallTime < atomic.LoadInt64(oldestIntentNanos) {
			atomic.StoreInt64(oldestIntentNanos, meta.Timestamp.WallTime)
		}
		return
	}

	// We pushed the transaction successfully, so resolve the intent.
	resolveArgs := &proto.InternalResolveIntentRequest{
		RequestHeader: proto.RequestHeader{
			Timestamp: now,
			Key:       key,
			User:      UserRoot,
			Txn:       pushReply.PusheeTxn,
		},
	}
	if err := rng.AddCmd(proto.InternalResolveIntent, resolveArgs, &proto.InternalResolveIntentResponse{}, true); err != nil {
		log.Warningf("resolve of key %q failed: %s", key, err)
		// Atomically update the oldest intent.
		if meta.Timestamp.WallTime < atomic.LoadInt64(oldestIntentNanos) {
			atomic.StoreInt64(oldestIntentNanos, meta.Timestamp.WallTime)
		}
	}
}

// lookupGCPolicy queries the gossip prefix config map based on the
// supplied range's start key. It queries all matching config prefixes
// and then iterates from most specific to least, returning the first
// non-nil GC policy.
func (sq *scanQueue) lookupGCPolicy(rng *Range) (proto.GCPolicy, error) {
	info, err := rng.rm.Gossip().GetInfo(gossip.KeyConfigZone)
	if err != nil {
		return proto.GCPolicy{}, util.Errorf("unable to fetch zone config from gossip: %s", err)
	}
	configMap, ok := info.(PrefixConfigMap)
	if !ok {
		return proto.GCPolicy{}, util.Errorf("gossiped info is not a prefix configuration map: %+v", info)
	}

	// Verify that the range doesn't cross over the zone config prefix.
	// This could be the case if the zone config is new and the range
	// hasn't been split yet along the new boundary.
	prefixConfigs := configMap.MatchesByPrefix(rng.Desc.StartKey)
	var zone *proto.ZoneConfig
	for _, prefixConfig := range prefixConfigs {
		zone = prefixConfig.Config.(*proto.ZoneConfig)
		if zone.GC != nil {
			rng.RLock()
			isCovered := !prefixConfig.Prefix.PrefixEnd().Less(rng.Desc.EndKey)
			rng.RUnlock()
			if !isCovered {
				return proto.GCPolicy{}, util.Errorf("range is only partially covered by zone %s; must wait for range split", prefixConfig)
			}
			return *zone.GC, nil
		}
		log.V(1).Infof("skipping zone config %+v, because no GC policy is set", zone)
	}

	// We should always match the default GC.
	return proto.GCPolicy{}, util.Errorf("no zone for range with start key %q", rng.Desc.StartKey)
}
<commit_msg>respond to bens feedback<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"math"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/gossip"
	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
	gogoproto "github.com/gogo/protobuf/proto"
)

const (
	// scanQueueMaxSize is the max size of the scan queue.
	scanQueueMaxSize = 100
	// gcByteCountNormalization is the count of GC'able bytes which
	// amount to a score of "1" added to total range priority.
	gcByteCountNormalization = 1 << 20 // 1 MB
	// intentAgeNormalization is the average age of outstanding intents
	// which amount to a score of "1" added to total range priority.
	intentAgeNormalization = 24 * time.Hour // 1 day
	// intentAgeThreshold is the threshold after which an extant intent
	// will be resolved.
	intentAgeThreshold = 2 * time.Hour // 2 hour
	// verificationInterval is the target duration for verifying on-disk
	// checksums via full scan.
	verificationInterval = 30 * 24 * time.Hour // 30 days
)

// scanQueue manages a queue of ranges slated to be scanned in their
// entirety using the MVCC versions iterator. Currently, range scans
// manage the following tasks:
//
//  - GC of version data via TTL expiration (and more complex schemes
//    as implemented going forward).
//  - Resolve extant write intents and determine oldest non-resolvable
//    intent.
//  - Periodic verification of on-disk checksums to identify bit-rot
//    in read-only data sets. See http://en.wikipedia.org/wiki/Data_degradation.
//
// The shouldQueue function combines the need for all three tasks into
// a single priority. If any task is overdue, shouldQueue returns true.
type scanQueue struct {
	*baseQueue
}

// newScanQueue returns a new instance of scanQueue.
func newScanQueue() *scanQueue {
	sq := &scanQueue{}
	sq.baseQueue = newBaseQueue("scan", sq.shouldQueue, sq.process, scanQueueMaxSize)
	return sq
}

// shouldQueue determines whether a range should be queued for
// scanning, and if so, at what priority. Returns true for shouldQ in
// the event that there are GC'able bytes, or it's been longer since
// the last scan than the intent sweep or verification
// intervals. Priority is derived from the addition of priority from
// GC'able bytes and how many multiples of intent or verification
// intervals have elapsed since the last scan.
func (sq *scanQueue) shouldQueue(now proto.Timestamp, rng *Range) (shouldQ bool, priority float64) {
	// Get last scan metadata & GC policy.
	scanMeta, err := rng.GetScanMetadata()
	if err != nil {
		log.Errorf("unable to fetch scan metadata: %s", err)
		return
	}
	policy, err := sq.lookupGCPolicy(rng)
	if err != nil {
		log.Errorf("GC policy: %s", err)
		return
	}

	// GC score is the total GC'able bytes age normalized by 1 MB * the range's TTL in seconds.
	gcScore := float64(rng.stats.GetGCBytesAge(now.WallTime)) / float64(policy.TTLSeconds) / float64(gcByteCountNormalization)

	// Intent score. This computes the average age of outstanding intents
	// and normalizes.
	intentScore := rng.stats.GetAvgIntentAge(now.WallTime) / float64(intentAgeNormalization.Nanoseconds()/1E9)

	// Verify score.
	verifyScore := float64(now.WallTime-scanMeta.LastScanNanos) / float64(verificationInterval.Nanoseconds())

	// Compute priority.
	if gcScore > 1 {
		priority += gcScore
	}
	if intentScore > 1 {
		priority += intentScore
	}
	if verifyScore > 1 {
		priority += verifyScore
	}
	shouldQ = priority > 0
	return
}

// process iterates through all keys in a range, calling the garbage
// collector for each key and associated set of values. GC'd keys are
// batched into InternalGC calls. Extant intents are resolved if
// intents are older than intentAgeThreshold. The very act of scanning
// keys verifies on-disk checksums, as each block checksum is checked
// on load.
func (sq *scanQueue) process(now proto.Timestamp, rng *Range) error {
	snap := rng.rm.Engine().NewSnapshot()
	iter := newRangeDataIterator(rng, snap)
	defer iter.Close()
	defer snap.Stop()

	// Lookup the GC policy for the zone containing this key range.
	policy, err := sq.lookupGCPolicy(rng)
	if err != nil {
		return err
	}

	scanMeta := proto.NewScanMetadata(now.WallTime)
	gc := engine.NewGarbageCollector(now, policy)

	// Compute intent expiration (intent age at which we attempt to resolve).
	intentExp := now
	intentExp.WallTime -= intentAgeThreshold.Nanoseconds()

	gcArgs := &proto.InternalGCRequest{
		RequestHeader: proto.RequestHeader{
			Key:       rng.Desc.StartKey,
			Timestamp: now,
			RaftID:    rng.Desc.RaftID,
		},
	}
	var mu sync.Mutex
	var oldestIntentNanos int64 = math.MaxInt64
	var wg sync.WaitGroup
	var expBaseKey proto.Key
	var keys []proto.EncodedKey
	var vals [][]byte

	// updateOldestIntent atomically updates the oldest intent.
	updateOldestIntent := func(intentNanos int64) {
		mu.Lock()
		defer mu.Unlock()
		if intentNanos < oldestIntentNanos {
			oldestIntentNanos = intentNanos
		}
	}

	// processKeysAndValues is invoked with each key and its set of
	// values. Intents older than the intent age threshold are sent for
	// resolution and values after the MVCC metadata, and possible
	// intent, are sent for garbage collection.
	processKeysAndValues := func() {
		// If there's more than a single value for the key, possibly send for GC.
		if len(keys) > 1 {
			meta := &proto.MVCCMetadata{}
			if err := gogoproto.Unmarshal(vals[0], meta); err != nil {
				log.Errorf("unable to unmarshal MVCC metadata for key %q: %s", keys[0], err)
			} else {
				// In the event that there's an active intent, send for
				// intent resolution if older than the threshold.
				startIdx := 1
				if meta.Txn != nil {
					// Resolve intent asynchronously in a goroutine if the intent
					// is older than the intent expiration threshold.
					if meta.Timestamp.Less(intentExp) {
						log.Infof("base=%q, now=%s, meta ts=%s, intentExp=%s", expBaseKey, now, meta.Timestamp, intentExp)
						wg.Add(1)
						go sq.resolveIntent(rng, expBaseKey, meta, updateOldestIntent, &wg)
					} else {
						updateOldestIntent(meta.Timestamp.WallTime)
					}
					// With an active intent, GC ignores MVCC metadata & intent value.
					startIdx = 2
				}
				// See if any values may be GC'd.
				if gcTS := gc.Filter(keys[startIdx:], vals[startIdx:]); !gcTS.Equal(proto.ZeroTimestamp) {
					// TODO(spencer): need to split the requests up into
					// multiple requests in the event that more than X keys
					// are added to the request.
					gcArgs.Keys = append(gcArgs.Keys, proto.InternalGCRequest_GCKey{Key: expBaseKey, Timestamp: gcTS})
				}
			}
		}
	}

	// Iterate through this range's keys and values.
	for ; iter.Valid(); iter.Next() {
		baseKey, ts, isValue := engine.MVCCDecodeKey(iter.Key())
		if !isValue {
			// Moving to the next key (& values).
			processKeysAndValues()
			expBaseKey = baseKey
			keys = []proto.EncodedKey{iter.Key()}
			vals = [][]byte{iter.Value()}
		} else {
			if !baseKey.Equal(expBaseKey) {
				log.Errorf("unexpectedly found a value for %q with ts=%s; expected key %q", baseKey, ts, expBaseKey)
				continue
			}
			keys = append(keys, iter.Key())
			vals = append(vals, iter.Value())
		}
	}
	// Handle last collected set of keys/vals.
	processKeysAndValues()

	// An error during iteration is presumed to means a checksum failure
	// while iterating over the underlying key/value data.
	if iter.Error() != nil {
		// TODO(spencer): do something other than fatal error here. We
		// want to quarantine this range, make it a non-participating raft
		// follower until it can be replaced and then destroyed.
		log.Fatalf("unhandled failure when scanning range %s; probable data corruption: %s", rng, iter.Error())
	}
	// Wait for any outstanding intent resolves and set oldest extant intent.
	wg.Wait()
	scanMeta.OldestIntentNanos = gogoproto.Int64(oldestIntentNanos)

	// Send GC request through range.
	gcArgs.ScanMeta = *scanMeta
	if err := rng.AddCmd(proto.InternalGC, gcArgs, &proto.InternalGCResponse{}, true); err != nil {
		return err
	}

	return nil
}

// resolveIntent resolves the intent at key by attempting to abort the
// transaction and resolve the intent. If the transaction cannot be
// aborted or intent cannot be resolved, the oldestIntentNanos value
// is atomically updated to the min of oldestIntentNanos and the
// intent's timestamp. The wait group is signaled on completion.
func (sq *scanQueue) resolveIntent(rng *Range, key proto.Key, meta *proto.MVCCMetadata,
	updateOldestIntent func(int64), wg *sync.WaitGroup) {
	defer wg.Done() // signal wait group always on completion

	log.Infof("resolving intent at %q ts=%s", key, meta.Timestamp)

	// Attempt to push the transaction which created the intent.
	now := rng.rm.Clock().Now()
	pushArgs := &proto.InternalPushTxnRequest{
		RequestHeader: proto.RequestHeader{
			Timestamp:    now,
			Key:          meta.Txn.Key,
			User:         UserRoot,
			UserPriority: gogoproto.Int32(proto.MaxPriority),
			Txn:          nil,
		},
		PusheeTxn: *meta.Txn,
		Abort:     true,
	}
	pushReply := &proto.InternalPushTxnResponse{}
	if err := rng.rm.DB().Call(proto.InternalPushTxn, pushArgs, pushReply); err != nil {
		log.Warningf("push of txn %s failed: %s", meta.Txn, err)
		updateOldestIntent(meta.Timestamp.WallTime)
		return
	}

	// We pushed the transaction successfully, so resolve the intent.
	resolveArgs := &proto.InternalResolveIntentRequest{
		RequestHeader: proto.RequestHeader{
			Timestamp: now,
			Key:       key,
			User:      UserRoot,
			Txn:       pushReply.PusheeTxn,
		},
	}
	if err := rng.AddCmd(proto.InternalResolveIntent, resolveArgs, &proto.InternalResolveIntentResponse{}, true); err != nil {
		log.Warningf("resolve of key %q failed: %s", key, err)
		updateOldestIntent(meta.Timestamp.WallTime)
	}
}

// lookupGCPolicy queries the gossip prefix config map based on the
// supplied range's start key. It queries all matching config prefixes
// and then iterates from most specific to least, returning the first
// non-nil GC policy.
func (sq *scanQueue) lookupGCPolicy(rng *Range) (proto.GCPolicy, error) {
	info, err := rng.rm.Gossip().GetInfo(gossip.KeyConfigZone)
	if err != nil {
		return proto.GCPolicy{}, util.Errorf("unable to fetch zone config from gossip: %s", err)
	}
	configMap, ok := info.(PrefixConfigMap)
	if !ok {
		return proto.GCPolicy{}, util.Errorf("gossiped info is not a prefix configuration map: %+v", info)
	}

	// Verify that the range doesn't cross over the zone config prefix.
	// This could be the case if the zone config is new and the range
	// hasn't been split yet along the new boundary.
	prefixConfigs := configMap.MatchesByPrefix(rng.Desc.StartKey)
	var zone *proto.ZoneConfig
	for _, prefixConfig := range prefixConfigs {
		zone = prefixConfig.Config.(*proto.ZoneConfig)
		if zone.GC != nil {
			rng.RLock()
			isCovered := !prefixConfig.Prefix.PrefixEnd().Less(rng.Desc.EndKey)
			rng.RUnlock()
			if !isCovered {
				return proto.GCPolicy{}, util.Errorf("range is only partially covered by zone %s; must wait for range split", prefixConfig)
			}
			return *zone.GC, nil
		}
		log.V(1).Infof("skipping zone config %+v, because no GC policy is set", zone)
	}

	// We should always match the default GC.
	return proto.GCPolicy{}, util.Errorf("no zone for range with start key %q", rng.Desc.StartKey)
}
<|endoftext|>


<commit_before>// Copyright 2010-2012 The W32 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package w32

/*
#include <windows.h>

typedef struct _MIB_TCPROW {
  DWORD dwState;
  DWORD dwLocalAddr;
  DWORD dwLocalPort;
  DWORD dwRemoteAddr;
  DWORD dwRemotePort;
} MIB_TCPROW, *PMIB_TCPROW;

typedef struct _MIB_TCPTABLE {
  DWORD      dwNumEntries;
  MIB_TCPROW table[];
} MIB_TCPTABLE, *PMIB_TCPTABLE;

typedef struct _MIB_TCP6ROW {
  int       State;
  byte      LocalAddr[16];
  DWORD     dwLocalScopeId;
  DWORD     dwLocalPort;
  byte      RemoteAddr[16];
  DWORD     dwRemoteScopeId;
  DWORD     dwRemotePort;
} MIB_TCP6ROW, *PMIB_TCP6ROW;

typedef struct _MIB_TCP6TABLE {
  DWORD       dwNumEntries;
  MIB_TCP6ROW table[];
} MIB_TCP6TABLE, *PMIB_TCP6TABLE;

MIB_TCP6TABLE* GetTcp6TableBuffer(DWORD buffersize){
	 MIB_TCP6TABLE *pTable = NULL;
	 pTable = (MIB_TCP6TABLE *) malloc(buffersize);

	return pTable;
}

MIB_TCPTABLE* GetTcpTableBuffer(DWORD buffersize){
	 MIB_TCPTABLE *pTable = NULL;
	 pTable = (MIB_TCPTABLE *) malloc(buffersize);

	return pTable;
}
*/
import "C"

import (
	//	"fmt"
	"reflect"
	"syscall"
	"unsafe"
)

var (
	modiphlpapi = syscall.NewLazyDLL("Iphlpapi.dll")

	procGetTcpTable  = modiphlpapi.NewProc("GetTcpTable")
	procGetTcp6Table = modiphlpapi.NewProc("GetTcp6Table")
)

const (
	ERROR_INSUFFICIENT_BUFFER = 122
	ERROR_INVALID_PARAMETER   = 87
	ERROR_NOT_SUPPORTED       = 50
)

func GetTcpTable(sortResults bool) (table MIB_TCPTABLE, status uint32) {
	sort := FALSE
	if sortResults {
		sort = TRUE
	}

	bufSize := uint32(0)
	ret, _, _ := procGetTcpTable.Call(
		uintptr(0),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != ERROR_INSUFFICIENT_BUFFER {
		return
	}

	tableBuf := C.GetTcpTableBuffer(C.DWORD(bufSize))
	defer C.free(unsafe.Pointer(tableBuf))

	ret, _, _ = procGetTcpTable.Call(
		uintptr(unsafe.Pointer(tableBuf)),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != NO_ERROR {
		return
	}

	table.NumEntries = uint32(tableBuf.dwNumEntries)
	table.Table = make([]MIB_TCPROW, table.NumEntries)

	buf := *(*[]C.MIB_TCPROW)(unsafe.Pointer(&reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&tableBuf.table)),
		Len:  int(table.NumEntries),
		Cap:  int(table.NumEntries),
	}))

	for i := 0; i < int(table.NumEntries); i++ {
		table.Table[i] = MIB_TCPROW{
			State:      uint32(buf[i].dwState),
			LocalAddr:  uint32(buf[i].dwLocalAddr),
			LocalPort:  uint32(buf[i].dwLocalPort),
			RemoteAddr: uint32(buf[i].dwRemoteAddr),
			RemotePort: uint32(buf[i].dwRemotePort),
		}
	}

	return
}

type MIB_TCPTABLE struct {
	NumEntries uint32
	Table      []MIB_TCPROW
}

type MIB_TCPROW struct {
	State      uint32
	LocalAddr  uint32
	LocalPort  uint32
	RemoteAddr uint32
	RemotePort uint32
}

func GetTcp6Table(sortResults bool) (table MIB_TCP6TABLE, status uint32) {
	sort := FALSE
	if sortResults {
		sort = TRUE
	}

	bufSize := uint32(0)
	ret, _, _ := procGetTcp6Table.Call(
		uintptr(0),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != ERROR_INSUFFICIENT_BUFFER {
		return
	}

	tableBuf := C.GetTcp6TableBuffer(C.DWORD(bufSize))
	defer C.free(unsafe.Pointer(tableBuf))

	ret, _, _ = procGetTcp6Table.Call(
		uintptr(unsafe.Pointer(tableBuf)),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != NO_ERROR {
		return
	}

	table.NumEntries = uint32(tableBuf.dwNumEntries)
	table.Table = make([]MIB_TCP6ROW, table.NumEntries)

	buf := *(*[]C.MIB_TCP6ROW)(unsafe.Pointer(&reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&tableBuf.table)),
		Len:  int(table.NumEntries),
		Cap:  int(table.NumEntries),
	}))

	for i := 0; i < int(table.NumEntries); i++ {
		table.Table[i] = MIB_TCP6ROW{
			State:         MIB_TCP_STATE(buf[i].State),
			LocalAddr:     []byte(C.GoBytes(unsafe.Pointer(&buf[i].LocalAddr), 16)),
			LocalScopeId:  uint32(buf[i].dwLocalScopeId),
			LocalPort:     uint32(buf[i].dwLocalPort),
			RemoteAddr:    []byte(C.GoBytes(unsafe.Pointer(&buf[i].RemoteAddr), 16)),
			RemoteScopeId: uint32(buf[i].dwRemoteScopeId),
			RemotePort:    uint32(buf[i].dwRemotePort),
		}
	}

	return
}

//ULONG WINAPI GetTcp6Table(
//  _Out_   PMIB_TCP6TABLE TcpTable,
//  _Inout_ PULONG         SizePointer,
//  _In_    BOOL           Order
//);

// https://msdn.microsoft.com/en-us/library/aa814506(v=vs.85).aspx
type MIB_TCP6TABLE struct {
	NumEntries uint32
	Table      []MIB_TCP6ROW
}

// https://msdn.microsoft.com/en-us/library/aa814505(v=vs.85).aspx
type MIB_TCP6ROW struct {
	State         MIB_TCP_STATE
	LocalAddr     []byte
	LocalScopeId  uint32
	LocalPort     uint32
	RemoteAddr    []byte
	RemoteScopeId uint32
	RemotePort    uint32
}

type MIB_TCP_STATE uint32

const (
	MIB_TCP_STATE_CLOSED     = 1
	MIB_TCP_STATE_LISTEN     = 2
	MIB_TCP_STATE_SYN_SENT   = 3
	MIB_TCP_STATE_SYN_RCVD   = 4
	MIB_TCP_STATE_ESTAB      = 5
	MIB_TCP_STATE_FIN_WAIT1  = 6
	MIB_TCP_STATE_FIN_WAIT2  = 7
	MIB_TCP_STATE_CLOSE_WAIT = 8
	MIB_TCP_STATE_CLOSING    = 9
	MIB_TCP_STATE_LAST_ACK   = 10
	MIB_TCP_STATE_TIME_WAIT  = 11
	MIB_TCP_STATE_DELETE_TCB = 12
)
<commit_msg>address review comment<commit_after>// Copyright 2010-2012 The W32 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package w32

/*
#include <windows.h>

typedef struct _MIB_TCPROW {
  DWORD dwState;
  DWORD dwLocalAddr;
  DWORD dwLocalPort;
  DWORD dwRemoteAddr;
  DWORD dwRemotePort;
} MIB_TCPROW, *PMIB_TCPROW;

typedef struct _MIB_TCPTABLE {
  DWORD      dwNumEntries;
  MIB_TCPROW table[];
} MIB_TCPTABLE, *PMIB_TCPTABLE;

typedef struct _MIB_TCP6ROW {
  int       State;
  byte      LocalAddr[16];
  DWORD     dwLocalScopeId;
  DWORD     dwLocalPort;
  byte      RemoteAddr[16];
  DWORD     dwRemoteScopeId;
  DWORD     dwRemotePort;
} MIB_TCP6ROW, *PMIB_TCP6ROW;

typedef struct _MIB_TCP6TABLE {
  DWORD       dwNumEntries;
  MIB_TCP6ROW table[];
} MIB_TCP6TABLE, *PMIB_TCP6TABLE;

MIB_TCP6TABLE* GetTcp6TableBuffer(DWORD buffersize){
	 MIB_TCP6TABLE *pTable = NULL;
	 pTable = (MIB_TCP6TABLE *) malloc(buffersize);

	return pTable;
}

MIB_TCPTABLE* GetTcpTableBuffer(DWORD buffersize){
	 MIB_TCPTABLE *pTable = NULL;
	 pTable = (MIB_TCPTABLE *) malloc(buffersize);

	return pTable;
}
*/
import "C"

import (
	//	"fmt"
	"reflect"
	"syscall"
	"unsafe"
)

var (
	modiphlpapi = syscall.NewLazyDLL("Iphlpapi.dll")

	procGetTcpTable  = modiphlpapi.NewProc("GetTcpTable")
	procGetTcp6Table = modiphlpapi.NewProc("GetTcp6Table")
)

const (
	ERROR_INSUFFICIENT_BUFFER = 122
	ERROR_INVALID_PARAMETER   = 87
	ERROR_NOT_SUPPORTED       = 50
)

var tcp4RowSize = uint32(unsafe.Sizeof(MIB_TCPROW{}))

func GetTcpTable(sortResults bool) (table MIB_TCPTABLE, status uint32) {
	sort := FALSE
	if sortResults {
		sort = TRUE
	}

	bufSize := uint32(0)
	ret, _, _ := procGetTcpTable.Call(
		uintptr(0),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != ERROR_INSUFFICIENT_BUFFER {
		return
	}

	// Allocate our table with a buffer for a few extra rows, just in case
	tableBuf := C.GetTcpTableBuffer(C.DWORD(bufSize + tcp4RowSize*20))
	defer C.free(unsafe.Pointer(tableBuf))

	ret, _, _ = procGetTcpTable.Call(
		uintptr(unsafe.Pointer(tableBuf)),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != NO_ERROR {
		return
	}

	table.NumEntries = uint32(tableBuf.dwNumEntries)
	table.Table = make([]MIB_TCPROW, table.NumEntries)

	buf := *(*[]C.MIB_TCPROW)(unsafe.Pointer(&reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&tableBuf.table)),
		Len:  int(table.NumEntries),
		Cap:  int(table.NumEntries),
	}))

	for i := 0; i < int(table.NumEntries); i++ {
		table.Table[i] = MIB_TCPROW{
			State:      uint32(buf[i].dwState),
			LocalAddr:  uint32(buf[i].dwLocalAddr),
			LocalPort:  uint32(buf[i].dwLocalPort),
			RemoteAddr: uint32(buf[i].dwRemoteAddr),
			RemotePort: uint32(buf[i].dwRemotePort),
		}
	}

	return
}

// https://msdn.microsoft.com/en-us/library/aa366917(v=vs.85).aspx
type MIB_TCPTABLE struct {
	NumEntries uint32
	Table      []MIB_TCPROW
}

// https://msdn.microsoft.com/en-us/library/aa366909(v=vs.85).aspx
type MIB_TCPROW struct {
	State      uint32
	LocalAddr  uint32
	LocalPort  uint32
	RemoteAddr uint32
	RemotePort uint32
}

var tcp6RowSize = uint32(unsafe.Sizeof(MIB_TCP6ROW{}))

func GetTcp6Table(sortResults bool) (table MIB_TCP6TABLE, status uint32) {
	sort := FALSE
	if sortResults {
		sort = TRUE
	}

	bufSize := uint32(0)
	ret, _, _ := procGetTcp6Table.Call(
		uintptr(0),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != ERROR_INSUFFICIENT_BUFFER {
		return
	}

	// Allocate our table with a buffer for a few extra rows, just in case
	tableBuf := C.GetTcp6TableBuffer(C.DWORD(bufSize + tcp6RowSize*20))
	defer C.free(unsafe.Pointer(tableBuf))

	ret, _, _ = procGetTcp6Table.Call(
		uintptr(unsafe.Pointer(tableBuf)),
		uintptr(unsafe.Pointer(&bufSize)),
		uintptr(sort),
	)

	if status = uint32(ret); status != NO_ERROR {
		return
	}

	table.NumEntries = uint32(tableBuf.dwNumEntries)
	table.Table = make([]MIB_TCP6ROW, table.NumEntries)

	buf := *(*[]C.MIB_TCP6ROW)(unsafe.Pointer(&reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&tableBuf.table)),
		Len:  int(table.NumEntries),
		Cap:  int(table.NumEntries),
	}))

	for i := 0; i < int(table.NumEntries); i++ {
		table.Table[i] = MIB_TCP6ROW{
			State:         MIB_TCP_STATE(buf[i].State),
			LocalAddr:     []byte(C.GoBytes(unsafe.Pointer(&buf[i].LocalAddr), 16)),
			LocalScopeId:  uint32(buf[i].dwLocalScopeId),
			LocalPort:     uint32(buf[i].dwLocalPort),
			RemoteAddr:    []byte(C.GoBytes(unsafe.Pointer(&buf[i].RemoteAddr), 16)),
			RemoteScopeId: uint32(buf[i].dwRemoteScopeId),
			RemotePort:    uint32(buf[i].dwRemotePort),
		}
	}

	return
}

// https://msdn.microsoft.com/en-us/library/aa814506(v=vs.85).aspx
type MIB_TCP6TABLE struct {
	NumEntries uint32
	Table      []MIB_TCP6ROW
}

// https://msdn.microsoft.com/en-us/library/aa814505(v=vs.85).aspx
type MIB_TCP6ROW struct {
	State         MIB_TCP_STATE
	LocalAddr     []byte
	LocalScopeId  uint32
	LocalPort     uint32
	RemoteAddr    []byte
	RemoteScopeId uint32
	RemotePort    uint32
}

type MIB_TCP_STATE uint32

const (
	MIB_TCP_STATE_CLOSED     = 1
	MIB_TCP_STATE_LISTEN     = 2
	MIB_TCP_STATE_SYN_SENT   = 3
	MIB_TCP_STATE_SYN_RCVD   = 4
	MIB_TCP_STATE_ESTAB      = 5
	MIB_TCP_STATE_FIN_WAIT1  = 6
	MIB_TCP_STATE_FIN_WAIT2  = 7
	MIB_TCP_STATE_CLOSE_WAIT = 8
	MIB_TCP_STATE_CLOSING    = 9
	MIB_TCP_STATE_LAST_ACK   = 10
	MIB_TCP_STATE_TIME_WAIT  = 11
	MIB_TCP_STATE_DELETE_TCB = 12
)
<|endoftext|>


<commit_before>package iptables

// This package is originally from Docker and has been modified for use by the
// Flynn project. See the NOTICE and LICENSE files for licensing and copyright
// details.

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

type Action string

const (
	Add    Action = "-A"
	Delete Action = "-D"
)

var (
	ErrIptablesNotFound = errors.New("Iptables not found")
	nat                 = []string{"-t", "nat"}
	supportsXlock       = false
)

type Chain struct {
	Name   string
	Bridge string
}

func init() {
	supportsXlock = exec.Command("iptables", "--wait", "-L", "-n").Run() == nil
}

func NewChain(name, bridge string) (*Chain, error) {
	if output, err := Raw("-t", "nat", "-N", name); err != nil {
		return nil, err
	} else if len(output) != 0 {
		return nil, fmt.Errorf("Error creating new iptables chain: %s", output)
	}
	chain := &Chain{
		Name:   name,
		Bridge: bridge,
	}

	if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
		return nil, fmt.Errorf("Failed to inject update PREROUTING chain: %s", err)
	}
	if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
		return nil, fmt.Errorf("Failed to inject update OUTPUT chain: %s", err)
	}
	return chain, nil
}

func RemoveExistingChain(name string) error {
	chain := &Chain{
		Name: name,
	}
	return chain.Remove()
}

func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
	daddr := ip.String()
	if ip.IsUnspecified() {
		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
		// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
		// value" by both iptables and ip6tables.
		daddr = "0/0"
	}
	if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
		"-p", proto,
		"-d", daddr,
		"--dport", strconv.Itoa(port),
		"-j", "DNAT",
		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	fAction := action
	if fAction == Add {
		fAction = "-I"
	}
	if output, err := Raw(string(fAction), "FORWARD",
		"!", "-i", c.Bridge,
		"-o", c.Bridge,
		"-p", proto,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "ACCEPT"); err != nil {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	if output, err := Raw("-t", "nat", string(fAction), "POSTROUTING",
		"-p", proto,
		"-s", destAddr,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "MASQUERADE"); err != nil {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	return nil
}

func (c *Chain) Prerouting(action Action, args ...string) error {
	a := append(nat, fmt.Sprint(action), "PREROUTING")
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables prerouting: %s", output)
	}
	return nil
}

func (c *Chain) Output(action Action, args ...string) error {
	a := append(nat, fmt.Sprint(action), "OUTPUT")
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables output: %s", output)
	}
	return nil
}

func (c *Chain) Remove() error {
	// Ignore errors - This could mean the chains were never set up
	c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6

	c.Prerouting(Delete)
	c.Output(Delete)

	Raw("-t", "nat", "-F", c.Name)
	Raw("-t", "nat", "-X", c.Name)

	return nil
}

// Check if an existing rule exists
func Exists(args ...string) bool {
	if _, err := Raw(append([]string{"-C"}, args...)...); err != nil {
		return false
	}
	return true
}

func Raw(args ...string) ([]byte, error) {
	path, err := exec.LookPath("iptables")
	if err != nil {
		return nil, ErrIptablesNotFound
	}

	if supportsXlock {
		args = append([]string{"--wait"}, args...)
	}

	if os.Getenv("DEBUG") != "" {
		fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s, %v\n", path, args))
	}

	output, err := exec.Command(path, args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
	}

	// ignore iptables' message about xtables lock
	if strings.Contains(string(output), "waiting for it to exit") {
		output = []byte("")
	}

	return output, err
}
<commit_msg>pkg/iptables: Add more delete error skip conditionals<commit_after>package iptables

// This package is originally from Docker and has been modified for use by the
// Flynn project. See the NOTICE and LICENSE files for licensing and copyright
// details.

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

type Action string

const (
	Add    Action = "-A"
	Delete Action = "-D"
)

var (
	ErrIptablesNotFound = errors.New("Iptables not found")
	nat                 = []string{"-t", "nat"}
	supportsXlock       = false
)

type Chain struct {
	Name   string
	Bridge string
}

func init() {
	supportsXlock = exec.Command("iptables", "--wait", "-L", "-n").Run() == nil
}

func NewChain(name, bridge string) (*Chain, error) {
	if output, err := Raw("-t", "nat", "-N", name); err != nil {
		return nil, err
	} else if len(output) != 0 {
		return nil, fmt.Errorf("Error creating new iptables chain: %s", output)
	}
	chain := &Chain{
		Name:   name,
		Bridge: bridge,
	}

	if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
		return nil, fmt.Errorf("Failed to inject update PREROUTING chain: %s", err)
	}
	if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
		return nil, fmt.Errorf("Failed to inject update OUTPUT chain: %s", err)
	}
	return chain, nil
}

func RemoveExistingChain(name string) error {
	chain := &Chain{
		Name: name,
	}
	return chain.Remove()
}

func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
	daddr := ip.String()
	if ip.IsUnspecified() {
		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
		// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
		// value" by both iptables and ip6tables.
		daddr = "0/0"
	}
	if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
		"-p", proto,
		"-d", daddr,
		"--dport", strconv.Itoa(port),
		"-j", "DNAT",
		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil && action != Delete {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	fAction := action
	if fAction == Add {
		fAction = "-I"
	}
	if output, err := Raw(string(fAction), "FORWARD",
		"!", "-i", c.Bridge,
		"-o", c.Bridge,
		"-p", proto,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "ACCEPT"); err != nil && action != Delete {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	if output, err := Raw("-t", "nat", string(fAction), "POSTROUTING",
		"-p", proto,
		"-s", destAddr,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "MASQUERADE"); err != nil && action != Delete {
		return err
	} else if len(output) != 0 && action != Delete {
		return fmt.Errorf("Error iptables forward: %s", output)
	}

	return nil
}

func (c *Chain) Prerouting(action Action, args ...string) error {
	a := append(nat, fmt.Sprint(action), "PREROUTING")
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables prerouting: %s", output)
	}
	return nil
}

func (c *Chain) Output(action Action, args ...string) error {
	a := append(nat, fmt.Sprint(action), "OUTPUT")
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables output: %s", output)
	}
	return nil
}

func (c *Chain) Remove() error {
	// Ignore errors - This could mean the chains were never set up
	c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6

	c.Prerouting(Delete)
	c.Output(Delete)

	Raw("-t", "nat", "-F", c.Name)
	Raw("-t", "nat", "-X", c.Name)

	return nil
}

// Check if an existing rule exists
func Exists(args ...string) bool {
	if _, err := Raw(append([]string{"-C"}, args...)...); err != nil {
		return false
	}
	return true
}

func Raw(args ...string) ([]byte, error) {
	path, err := exec.LookPath("iptables")
	if err != nil {
		return nil, ErrIptablesNotFound
	}

	if supportsXlock {
		args = append([]string{"--wait"}, args...)
	}

	if os.Getenv("DEBUG") != "" {
		fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s, %v\n", path, args))
	}

	output, err := exec.Command(path, args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
	}

	// ignore iptables' message about xtables lock
	if strings.Contains(string(output), "waiting for it to exit") {
		output = []byte("")
	}

	return output, err
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv4 implements IP-level socket options for the Internet
// Protocol version 4.
//
// The package provides IP-level socket options that allow
// manipulation of IPv4 facilities.
//
// The IPv4 protocol and basic host requirements for IPv4 are defined
// in RFC 791 and RFC 1122.
// Host extensions for multicasting and socket interface extensions
// for multicast source filters are defined in RFC 1112 and RFC 3678.
// IGMPv1, IGMPv2 and IGMPv3 are defined in RFC 1112, RFC 2236 and RFC
// 3376.
// Source-specific multicast is defined in RFC 4607.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv4 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv4.Conn is used to set the type-of-service field on
// the IPv4 header for each packet.
//
//	ln, err := net.Listen("tcp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv4 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.IPv4(224, 0, 0, 250)
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv4 and Ethernet.
//
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv4.PacketConn is used to enable control
// message transmissons.
//
//	if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, cm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if cm.Dst.IsMulticast() {
//			if cm.Dst.Equal(group)
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTOS(DiffServCS0)
//		p.SetTTL(16)
//		if _, err := p.WriteTo(data, nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			if err := p.SetMulticastInterface(ifi); err != nil {
//				// error handling
//			}
//			p.SetMulticastTTL(2)
//			if _, err := p.WriteTo(data, nil, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn or RawConn may join multiple
// multicast groups.  For example, a UDP listener with port 1024 might
// join two different groups across over two different network
// interfaces by using:
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv4.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	p2 := ipv4.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn or RawConn on IGMPv3 supported
// platform is able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
//	ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on IGMPv3 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// IGMPv1 or IGMPv2 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv4 // import "golang.org/x/net/ipv4"
<commit_msg>ipv4: fix typo in package documentation<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv4 implements IP-level socket options for the Internet
// Protocol version 4.
//
// The package provides IP-level socket options that allow
// manipulation of IPv4 facilities.
//
// The IPv4 protocol and basic host requirements for IPv4 are defined
// in RFC 791 and RFC 1122.
// Host extensions for multicasting and socket interface extensions
// for multicast source filters are defined in RFC 1112 and RFC 3678.
// IGMPv1, IGMPv2 and IGMPv3 are defined in RFC 1112, RFC 2236 and RFC
// 3376.
// Source-specific multicast is defined in RFC 4607.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv4 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv4.Conn is used to set the type-of-service field on
// the IPv4 header for each packet.
//
//	ln, err := net.Listen("tcp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv4 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.IPv4(224, 0, 0, 250)
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv4 and Ethernet.
//
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv4.PacketConn is used to enable control
// message transmissons.
//
//	if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, cm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if cm.Dst.IsMulticast() {
//			if cm.Dst.Equal(group) {
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTOS(DiffServCS0)
//		p.SetTTL(16)
//		if _, err := p.WriteTo(data, nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			if err := p.SetMulticastInterface(ifi); err != nil {
//				// error handling
//			}
//			p.SetMulticastTTL(2)
//			if _, err := p.WriteTo(data, nil, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn or RawConn may join multiple
// multicast groups.  For example, a UDP listener with port 1024 might
// join two different groups across over two different network
// interfaces by using:
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv4.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	p2 := ipv4.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn or RawConn on IGMPv3 supported
// platform is able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
//	ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on IGMPv3 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// IGMPv1 or IGMPv2 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv4 // import "golang.org/x/net/ipv4"
<|endoftext|>