<commit_before>package db

import (
	"errors"
	"fmt"
	"sync"

	"github.com/ardanlabs/kit/db/mongo"

	"gopkg.in/mgo.v2"
)

// Multiple master sessions can be created for different databases. Each master
// session must be registered first. A new copy of the master session can be
// acquired. The session is provided through the DB type value.

//==============================================================================

// mgoDB maintains a master session for a given database.
type mgoDB struct {
	dbName string
	ses    *mgo.Session
}

// masterMGO manages a set of different MongoDB master sessions.
var masterMGO = struct {
	sync.RWMutex
	ses map[string]mgoDB
}{
	ses: make(map[string]mgoDB),
}

// RegMasterSession adds a new master session to the set.
func RegMasterSession(context interface{}, name string, cfg mongo.Config) error {
	masterMGO.Lock()
	defer masterMGO.Unlock()

	if _, exists := masterMGO.ses[name]; exists {
		return errors.New("Master session already exists")
	}

	ses, err := mongo.New(cfg)
	if err != nil {
		return err
	}

	masterMGO.ses[name] = mgoDB{cfg.DB, ses}

	return nil
}

//==============================================================================
// Factory function for acquiring a copy of a session.

// NewMGO returns a new DB value for use with MongoDB based on a registered
// master session.
func NewMGO(context interface{}, name string) (*DB, error) {
	var db mgoDB
	var exists bool
	masterMGO.Lock()
	{
		db, exists = masterMGO.ses[name]
	}
	masterMGO.Unlock()

	if !exists {
		return nil, fmt.Errorf("Master sesssion %q does not exist", name)
	}

	ses := db.ses.Copy()
	mdb := ses.DB(db.dbName)

	return &DB{mdb, ses}, nil
}

//==============================================================================
// Methods for the DB struct type related to MongoDB.

// CloseMGO closes a DB value being used with MongoDB.
func (db *DB) CloseMGO(context interface{}) {
	db.session.Close()
}

// ExecuteMGO is used to execute MongoDB commands.
func (db *DB) ExecuteMGO(context interface{}, colName string, f func(*mgo.Collection) error) error {
	if db == nil || db.session == nil {
		return errors.New("Invalid DB provided")
	}

	return f(db.database.C(colName))
}

// CollectionMGO is used to get a collection value..
func (db *DB) CollectionMGO(context interface{}, colName string) (*mgo.Collection, error) {
	if db == nil || db.session == nil {
		return nil, errors.New("Invalid DB provided")
	}

	return db.database.C(colName), nil
}
<commit_msg>Added two methods that accept a timeout.<commit_after>package db

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/ardanlabs/kit/db/mongo"

	"gopkg.in/mgo.v2"
)

// Multiple master sessions can be created for different databases. Each master
// session must be registered first. A new copy of the master session can be
// acquired. The session is provided through the DB type value.

//==============================================================================

// mgoDB maintains a master session for a given database.
type mgoDB struct {
	dbName string
	ses    *mgo.Session
}

// masterMGO manages a set of different MongoDB master sessions.
var masterMGO = struct {
	sync.RWMutex
	ses map[string]mgoDB
}{
	ses: make(map[string]mgoDB),
}

// RegMasterSession adds a new master session to the set.
func RegMasterSession(context interface{}, name string, cfg mongo.Config) error {
	masterMGO.Lock()
	defer masterMGO.Unlock()

	if _, exists := masterMGO.ses[name]; exists {
		return errors.New("Master session already exists")
	}

	ses, err := mongo.New(cfg)
	if err != nil {
		return err
	}

	masterMGO.ses[name] = mgoDB{cfg.DB, ses}

	return nil
}

//==============================================================================
// Factory function for acquiring a copy of a session.

// NewMGO returns a new DB value for use with MongoDB based on a registered
// master session.
func NewMGO(context interface{}, name string) (*DB, error) {
	var db mgoDB
	var exists bool
	masterMGO.Lock()
	{
		db, exists = masterMGO.ses[name]
	}
	masterMGO.Unlock()

	if !exists {
		return nil, fmt.Errorf("Master sesssion %q does not exist", name)
	}

	ses := db.ses.Copy()
	mdb := ses.DB(db.dbName)

	return &DB{mdb, ses}, nil
}

//==============================================================================
// Methods for the DB struct type related to MongoDB.

// CloseMGO closes a DB value being used with MongoDB.
func (db *DB) CloseMGO(context interface{}) {
	db.session.Close()
}

// ExecuteMGO is used to execute MongoDB commands.
func (db *DB) ExecuteMGO(context interface{}, colName string, f func(*mgo.Collection) error) error {
	if db == nil || db.session == nil {
		return errors.New("Invalid DB provided")
	}

	return f(db.database.C(colName))
}

// ExecuteMGOTimeout is used to execute MongoDB commands with a timeout.
func (db *DB) ExecuteMGOTimeout(context interface{}, timeout time.Duration, colName string, f func(*mgo.Collection) error) error {
	if db == nil || db.session == nil {
		return errors.New("Invalid DB provided")
	}

	db.session.SetSocketTimeout(timeout)

	return f(db.database.C(colName))
}

// CollectionMGO is used to get a collection value.
func (db *DB) CollectionMGO(context interface{}, colName string) (*mgo.Collection, error) {
	if db == nil || db.session == nil {
		return nil, errors.New("Invalid DB provided")
	}

	return db.database.C(colName), nil
}

// CollectionMGOTimeout is used to get a collection value with a timeout.
func (db *DB) CollectionMGOTimeout(context interface{}, timeout time.Duration, colName string) (*mgo.Collection, error) {
	if db == nil || db.session == nil {
		return nil, errors.New("Invalid DB provided")
	}

	db.session.SetSocketTimeout(timeout)

	return db.database.C(colName), nil
}
<|endoftext|>


<commit_before>package db

import (
	"github.com/jinzhu/gorm"
	"github.com/notegio/openrelay/types"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"log"
	"math/big"
	"time"
	"errors"
)

const (
	StatusOpen     = int64(0)
	StatusFilled   = int64(1)
	StatusUnfunded = int64(2)
)

type Order struct {
	types.Order
	CreatedAt time.Time
	UpdatedAt time.Time
	OrderHash []byte  `gorm:"primary_key"`
	Status    int64   `gorm:"index"`
	Price     float64 `gorm:"index:price"`
	FeeRate   float64 `gorm:"index:price"`
	MakerTokenRemaining *types.Uint256
	MakerFeeRemaining *types.Uint256
}

// Save records the order in the database, defaulting to the specified status.
// Status should either be db.StatusOpen, or db.StatusUnfunded. If the order
// is filled based on order.TakerTokenAmountFilled + order.TakerTokenAmountCancelled
// the status will be recorded as db.StatusFilled regardless of the specified status.
func (order *Order) Save(db *gorm.DB, status int64) *gorm.DB {
	copy(order.Signature.Hash[:], order.Hash())
	if !order.Signature.Verify(order.Maker) {
		scope := db.New()
		scope.AddError(errors.New("Failed to verify signature"))
		return scope
	}
	order.OrderHash = order.Hash()
	log.Printf("Attempting to save order %#x", order.Hash())

	remainingAmount := new(big.Int)
	thresholdAmount := new(big.Int)
	remainingAmount.SetBytes(order.TakerTokenAmount[:])
	thresholdAmount.SetBytes(order.TakerTokenAmount[:])
	// We want to consider it filled once it's 99% filled
	thresholdAmount.Mul(thresholdAmount, new(big.Int).SetInt64(99))
	thresholdAmount.Div(thresholdAmount, new(big.Int).SetInt64(100))
	remainingAmount.Sub(remainingAmount, new(big.Int).SetBytes(order.TakerTokenAmountFilled[:]))
	remainingAmount.Sub(remainingAmount, new(big.Int).SetBytes(order.TakerTokenAmountCancelled[:]))
	thresholdAmount.Sub(thresholdAmount, new(big.Int).SetBytes(order.TakerTokenAmountFilled[:]))
	thresholdAmount.Sub(thresholdAmount, new(big.Int).SetBytes(order.TakerTokenAmountCancelled[:]))

	// makerRemainingInt = (MakerTokenAmount * RemainingAmount) / TakerTokenAmount
	makerRemainingInt := new(big.Int).Div(new(big.Int).Mul(new(big.Int).SetBytes(order.MakerTokenAmount[:]), remainingAmount), new(big.Int).SetBytes(order.TakerTokenAmount[:]))
	makerRemaining := &types.Uint256{}
	copy(makerRemaining[:], abi.U256(makerRemainingInt))
	makerFeeRemainingInt := new(big.Int).Div(new(big.Int).Mul(new(big.Int).SetBytes(order.MakerFee[:]), remainingAmount), new(big.Int).SetBytes(order.TakerTokenAmount[:]))
	makerFeeRemaining := &types.Uint256{}
	copy(makerFeeRemaining[:], abi.U256(makerFeeRemainingInt))
	updates := map[string]interface{}{
		"taker_token_amount_filled":    order.TakerTokenAmountFilled,
		"taker_token_amount_cancelled": order.TakerTokenAmountCancelled,
		"maker_token_remaining":        makerRemaining,
		"maker_fee_remaining":          makerFeeRemaining,
		"status":                       status,
	}
	if thresholdAmount.Cmp(new(big.Int).SetInt64(0)) <= 0 {
		updates["status"] = StatusFilled
	}
	updateScope := db.Model(Order{}).Where("order_hash = ?", order.OrderHash).Updates(updates)
	if updateScope.Error != nil {
		log.Printf(updateScope.Error.Error())
	}
	if updateScope.RowsAffected > 0 {
		return updateScope
	}

	takerTokenAmount := new(big.Float).SetInt(new(big.Int).SetBytes(order.TakerTokenAmount[:]))
	makerTokenAmount := new(big.Float).SetInt(new(big.Int).SetBytes(order.MakerTokenAmount[:]))
	takerFeeAmount := new(big.Float).SetInt(new(big.Int).SetBytes(order.TakerFee[:]))

	order.Price, _ = new(big.Float).Quo(takerTokenAmount, makerTokenAmount).Float64()
	order.FeeRate, _ = new(big.Float).Quo(takerFeeAmount, takerTokenAmount).Float64()
	order.Status = status
	order.MakerTokenRemaining = makerRemaining
	order.MakerFeeRemaining = makerFeeRemaining
	return db.Create(order)
}
<commit_msg>Fix 99% filled integer arithmetic<commit_after>package db

import (
	"github.com/jinzhu/gorm"
	"github.com/notegio/openrelay/types"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"log"
	"math/big"
	"time"
	"errors"
)

const (
	StatusOpen     = int64(0)
	StatusFilled   = int64(1)
	StatusUnfunded = int64(2)
)

type Order struct {
	types.Order
	CreatedAt time.Time
	UpdatedAt time.Time
	OrderHash []byte  `gorm:"primary_key"`
	Status    int64   `gorm:"index"`
	Price     float64 `gorm:"index:price"`
	FeeRate   float64 `gorm:"index:price"`
	MakerTokenRemaining *types.Uint256
	MakerFeeRemaining *types.Uint256
}

// Save records the order in the database, defaulting to the specified status.
// Status should either be db.StatusOpen, or db.StatusUnfunded. If the order
// is filled based on order.TakerTokenAmountFilled + order.TakerTokenAmountCancelled
// the status will be recorded as db.StatusFilled regardless of the specified status.
func (order *Order) Save(db *gorm.DB, status int64) *gorm.DB {
	copy(order.Signature.Hash[:], order.Hash())
	if !order.Signature.Verify(order.Maker) {
		scope := db.New()
		scope.AddError(errors.New("Failed to verify signature"))
		return scope
	}
	order.OrderHash = order.Hash()
	log.Printf("Attempting to save order %#x", order.Hash())

	remainingAmount := new(big.Int)
	thresholdAmount := new(big.Int)
	remainingAmount.SetBytes(order.TakerTokenAmount[:])
	thresholdAmount.SetBytes(order.TakerTokenAmount[:])
	// If the order is larger than 100 base units, we want to remove it from the
	// orderbook when it's 99% filled. For fewer than 100 base units, integer
	// arithmetic makes this not work the way we want it to.
	if thresholdAmount.Cmp(big.NewInt(100)) > 0 {
		thresholdAmount.Mul(thresholdAmount, big.NewInt(99))
		thresholdAmount.Div(thresholdAmount, big.NewInt(100))
	}
	remainingAmount.Sub(remainingAmount, order.TakerTokenAmountFilled.Big())
	remainingAmount.Sub(remainingAmount, order.TakerTokenAmountCancelled.Big())
	thresholdAmount.Sub(thresholdAmount, order.TakerTokenAmountFilled.Big())
	thresholdAmount.Sub(thresholdAmount, order.TakerTokenAmountCancelled.Big())

	// makerRemainingInt = (MakerTokenAmount * RemainingAmount) / TakerTokenAmount
	makerRemainingInt := new(big.Int).Div(new(big.Int).Mul(order.MakerTokenAmount.Big(), remainingAmount), order.TakerTokenAmount.Big())
	makerRemaining := &types.Uint256{}
	copy(makerRemaining[:], abi.U256(makerRemainingInt))
	makerFeeRemainingInt := new(big.Int).Div(new(big.Int).Mul(order.MakerFee.Big(), remainingAmount), order.TakerTokenAmount.Big())
	makerFeeRemaining := &types.Uint256{}
	copy(makerFeeRemaining[:], abi.U256(makerFeeRemainingInt))
	updates := map[string]interface{}{
		"taker_token_amount_filled":    order.TakerTokenAmountFilled,
		"taker_token_amount_cancelled": order.TakerTokenAmountCancelled,
		"maker_token_remaining":        makerRemaining,
		"maker_fee_remaining":          makerFeeRemaining,
		"status":                       status,
	}
	if thresholdAmount.Cmp(big.NewInt(0)) <= 0 {
		updates["status"] = StatusFilled
	}
	updateScope := db.Model(Order{}).Where("order_hash = ?", order.OrderHash).Updates(updates)
	if updateScope.Error != nil {
		log.Printf(updateScope.Error.Error())
	}
	if updateScope.RowsAffected > 0 {
		return updateScope
	}

	takerTokenAmount := new(big.Float).SetInt(order.TakerTokenAmount.Big())
	makerTokenAmount := new(big.Float).SetInt(order.MakerTokenAmount.Big())
	takerFeeAmount := new(big.Float).SetInt(order.TakerFee.Big())

	order.Price, _ = new(big.Float).Quo(takerTokenAmount, makerTokenAmount).Float64()
	order.FeeRate, _ = new(big.Float).Quo(takerFeeAmount, takerTokenAmount).Float64()
	order.Status = status
	order.MakerTokenRemaining = makerRemaining
	order.MakerFeeRemaining = makerFeeRemaining
	return db.Create(order)
}
<|endoftext|>


<commit_before>package parser

import (
	"errors"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
	"text/scanner"
)

var errTooManyErrors = errors.New("too many errors")

const maxErrors = 1

type ParseError struct {
	Err error
	Pos scanner.Position
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}

func Parse(filename string, r io.Reader, scope *Scope) (defs []Definition, errs []error) {
	p := newParser(r, scope)
	p.scanner.Filename = filename

	defer func() {
		if r := recover(); r != nil {
			if r == errTooManyErrors {
				defs = nil
				errs = p.errors
				return
			}
			panic(r)
		}
	}()

	defs = p.parseDefinitions()
	p.accept(scanner.EOF)
	errs = p.errors

	return
}

type parser struct {
	scanner scanner.Scanner
	tok     rune
	errors  []error
	scope   *Scope
}

func newParser(r io.Reader, scope *Scope) *parser {
	p := &parser{}
	p.scope = scope
	p.scanner.Init(r)
	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
		p.errorf(msg)
	}
	p.scanner.Mode = scanner.ScanIdents | scanner.ScanStrings |
		scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	p.next()
	return p
}

func (p *parser) errorf(format string, args ...interface{}) {
	pos := p.scanner.Position
	if !pos.IsValid() {
		pos = p.scanner.Pos()
	}
	err := &ParseError{
		Err: fmt.Errorf(format, args...),
		Pos: pos,
	}
	p.errors = append(p.errors, err)
	if len(p.errors) >= maxErrors {
		panic(errTooManyErrors)
	}
}

func (p *parser) accept(toks ...rune) bool {
	for _, tok := range toks {
		if p.tok != tok {
			p.errorf("expected %s, found %s", scanner.TokenString(tok),
				scanner.TokenString(p.tok))
			return false
		}
		if p.tok != scanner.EOF {
			p.tok = p.scanner.Scan()
		}
	}
	return true
}

func (p *parser) next() {
	if p.tok != scanner.EOF {
		p.tok = p.scanner.Scan()
	}
	return
}

func (p *parser) parseDefinitions() (defs []Definition) {
	for {
		switch p.tok {
		case scanner.Ident:
			ident := p.scanner.TokenText()
			pos := p.scanner.Position

			p.accept(scanner.Ident)

			switch p.tok {
			case '=':
				defs = append(defs, p.parseAssignment(ident, pos))
			case '{':
				defs = append(defs, p.parseModule(ident, pos))
			default:
				p.errorf("expected \"=\" or \"{\", found %s",
					scanner.TokenString(p.tok))
			}
		case scanner.EOF:
			return
		default:
			p.errorf("expected assignment or module definition, found %s",
				scanner.TokenString(p.tok))
			return
		}
	}
}

func (p *parser) parseAssignment(name string,
	pos scanner.Position) (assignment *Assignment) {

	assignment = new(Assignment)

	if !p.accept('=') {
		return
	}
	value := p.parseValue()

	assignment.Name = name
	assignment.Value = value
	assignment.Pos = pos

	if p.scope != nil {
		p.scope.Add(assignment)
	}

	return
}

func (p *parser) parseModule(typ string,
	pos scanner.Position) (module *Module) {

	module = new(Module)

	if !p.accept('{') {
		return
	}
	properties := p.parsePropertyList()
	p.accept('}')

	module.Type = typ
	module.Properties = properties
	module.Pos = pos
	return
}

func (p *parser) parsePropertyList() (properties []*Property) {
	for p.tok == scanner.Ident {
		property := p.parseProperty()
		properties = append(properties, property)

		if p.tok != ',' {
			// There was no comma, so the list is done.
			break
		}

		p.accept(',')
	}

	return
}

func (p *parser) parseProperty() (property *Property) {
	property = new(Property)

	name := p.scanner.TokenText()
	pos := p.scanner.Position
	if !p.accept(scanner.Ident, ':') {
		return
	}
	value := p.parseValue()

	property.Name = name
	property.Value = value
	property.Pos = pos

	return
}

func (p *parser) parseValue() (value Value) {
	switch p.tok {
	case scanner.Ident:
		return p.parseVariable()
	case scanner.String:
		return p.parseStringValue()
	case '[':
		return p.parseListValue()
	case '{':
		return p.parseMapValue()
	default:
		p.errorf("expected bool, list, or string value; found %s",
			scanner.TokenString(p.tok))
		return
	}
}

func (p *parser) parseVariable() (value Value) {
	value.Pos = p.scanner.Position
	switch text := p.scanner.TokenText(); text {
	case "true":
		value.Type = Bool
		value.BoolValue = true
	case "false":
		value.Type = Bool
		value.BoolValue = false
	default:
		assignment, err := p.scope.Get(p.scanner.TokenText())
		if err != nil {
			p.errorf(err.Error())
		}
		value = assignment.Value
	}

	p.accept(scanner.Ident)
	return
}

func (p *parser) parseStringValue() (value Value) {
	value.Type = String
	value.Pos = p.scanner.Position
	str, err := strconv.Unquote(p.scanner.TokenText())
	if err != nil {
		p.errorf("couldn't parse string: %s", err)
		return
	}
	value.StringValue = str
	p.accept(scanner.String)
	return
}

func (p *parser) parseListValue() (value Value) {
	value.Type = List
	value.Pos = p.scanner.Position
	if !p.accept('[') {
		return
	}

	var elements []Value
	for p.tok != ']' {
		element := p.parseValue()
		if element.Type != String {
			p.errorf("Expected string in list, found %s", element.String())
			return
		}
		elements = append(elements, element)

		if p.tok != ',' {
			// There was no comma, so the list is done.
			break
		}

		p.accept(',')
	}

	value.ListValue = elements

	p.accept(']')
	return
}

func (p *parser) parseMapValue() (value Value) {
	value.Type = Map
	value.Pos = p.scanner.Position
	if !p.accept('{') {
		return
	}

	properties := p.parsePropertyList()
	value.MapValue = properties

	p.accept('}')
	return
}

type ValueType int

const (
	Bool ValueType = iota
	String
	List
	Map
)

func (p ValueType) String() string {
	switch p {
	case Bool:
		return "bool"
	case String:
		return "string"
	case List:
		return "list"
	case Map:
		return "map"
	default:
		panic(fmt.Errorf("unknown value type: %d", p))
	}
}

type Definition interface {
	String() string
	definitionTag()
}

type Assignment struct {
	Name  string
	Value Value
	Pos   scanner.Position
}

func (a *Assignment) String() string {
	return fmt.Sprintf("%s@%d:%s: %s", a.Name, a.Pos.Offset, a.Pos, a.Value)
}

func (a *Assignment) definitionTag() {}

type Module struct {
	Type       string
	Properties []*Property
	Pos        scanner.Position
}

func (m *Module) String() string {
	propertyStrings := make([]string, len(m.Properties))
	for i, property := range m.Properties {
		propertyStrings[i] = property.String()
	}
	return fmt.Sprintf("%s@%d:%s{%s}", m.Type, m.Pos.Offset, m.Pos,
		strings.Join(propertyStrings, ", "))
}

func (m *Module) definitionTag() {}

type Property struct {
	Name  string
	Value Value
	Pos   scanner.Position
}

func (p *Property) String() string {
	return fmt.Sprintf("%s@%d:%s: %s", p.Name, p.Pos.Offset, p.Pos, p.Value)
}

type Value struct {
	Type        ValueType
	BoolValue   bool
	StringValue string
	ListValue   []Value
	MapValue    []*Property
	Pos         scanner.Position
}

func (p Value) String() string {
	switch p.Type {
	case Bool:
		return fmt.Sprintf("%t@%d:%s", p.BoolValue, p.Pos.Offset, p.Pos)
	case String:
		return fmt.Sprintf("%q@%d:%s", p.StringValue, p.Pos.Offset, p.Pos)
	case List:
		valueStrings := make([]string, len(p.ListValue))
		for i, value := range p.ListValue {
			valueStrings[i] = value.String()
		}
		return fmt.Sprintf("@%d:%s[%s]", p.Pos.Offset, p.Pos,
			strings.Join(valueStrings, ", "))
	case Map:
		propertyStrings := make([]string, len(p.MapValue))
		for i, property := range p.MapValue {
			propertyStrings[i] = property.String()
		}
		return fmt.Sprintf("@%d:%s{%s}", p.Pos.Offset, p.Pos,
			strings.Join(propertyStrings, ", "))
	default:
		panic(fmt.Errorf("bad property type: %d", p.Type))
	}
}

type Scope struct {
	vars map[string]*Assignment
}

func NewScope(s *Scope) *Scope {
	newScope := &Scope{
		vars: make(map[string]*Assignment),
	}

	if s != nil {
		for k, v := range s.vars {
			newScope.vars[k] = v
		}
	}

	return newScope
}

func (s *Scope) Add(assignment *Assignment) error {
	if old, ok := s.vars[assignment.Name]; ok {
		return fmt.Errorf("variable already set, previous assignment: %s", old)
	}

	s.vars[assignment.Name] = assignment

	return nil
}

func (s *Scope) Remove(name string) {
	delete(s.vars, name)
}

func (s *Scope) Get(name string) (*Assignment, error) {
	if a, ok := s.vars[name]; ok {
		return a, nil
	}

	return nil, fmt.Errorf("variable %s not set", name)
}

func (s *Scope) String() string {
	vars := []string{}

	for k := range s.vars {
		vars = append(vars, k)
	}

	sort.Strings(vars)

	ret := []string{}
	for _, v := range vars {
		ret = append(ret, s.vars[v].String())
	}

	return strings.Join(ret, "\n")
}
<commit_msg>Add support for simple expressions<commit_after>package parser

import (
	"errors"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
	"text/scanner"
)

var errTooManyErrors = errors.New("too many errors")

const maxErrors = 1

type ParseError struct {
	Err error
	Pos scanner.Position
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}

func Parse(filename string, r io.Reader, scope *Scope) (defs []Definition, errs []error) {
	p := newParser(r, scope)
	p.scanner.Filename = filename

	defer func() {
		if r := recover(); r != nil {
			if r == errTooManyErrors {
				defs = nil
				errs = p.errors
				return
			}
			panic(r)
		}
	}()

	defs = p.parseDefinitions()
	p.accept(scanner.EOF)
	errs = p.errors

	return
}

type parser struct {
	scanner scanner.Scanner
	tok     rune
	errors  []error
	scope   *Scope
}

func newParser(r io.Reader, scope *Scope) *parser {
	p := &parser{}
	p.scope = scope
	p.scanner.Init(r)
	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
		p.errorf(msg)
	}
	p.scanner.Mode = scanner.ScanIdents | scanner.ScanStrings |
		scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	p.next()
	return p
}

func (p *parser) errorf(format string, args ...interface{}) {
	pos := p.scanner.Position
	if !pos.IsValid() {
		pos = p.scanner.Pos()
	}
	err := &ParseError{
		Err: fmt.Errorf(format, args...),
		Pos: pos,
	}
	p.errors = append(p.errors, err)
	if len(p.errors) >= maxErrors {
		panic(errTooManyErrors)
	}
}

func (p *parser) accept(toks ...rune) bool {
	for _, tok := range toks {
		if p.tok != tok {
			p.errorf("expected %s, found %s", scanner.TokenString(tok),
				scanner.TokenString(p.tok))
			return false
		}
		if p.tok != scanner.EOF {
			p.tok = p.scanner.Scan()
		}
	}
	return true
}

func (p *parser) next() {
	if p.tok != scanner.EOF {
		p.tok = p.scanner.Scan()
	}
	return
}

func (p *parser) parseDefinitions() (defs []Definition) {
	for {
		switch p.tok {
		case scanner.Ident:
			ident := p.scanner.TokenText()
			pos := p.scanner.Position

			p.accept(scanner.Ident)

			switch p.tok {
			case '=':
				defs = append(defs, p.parseAssignment(ident, pos))
			case '{':
				defs = append(defs, p.parseModule(ident, pos))
			default:
				p.errorf("expected \"=\" or \"{\", found %s",
					scanner.TokenString(p.tok))
			}
		case scanner.EOF:
			return
		default:
			p.errorf("expected assignment or module definition, found %s",
				scanner.TokenString(p.tok))
			return
		}
	}
}

func (p *parser) parseAssignment(name string,
	pos scanner.Position) (assignment *Assignment) {

	assignment = new(Assignment)

	if !p.accept('=') {
		return
	}
	value := p.parseExpression()

	assignment.Name = name
	assignment.Value = value
	assignment.Pos = pos

	if p.scope != nil {
		p.scope.Add(assignment)
	}

	return
}

func (p *parser) parseModule(typ string,
	pos scanner.Position) (module *Module) {

	module = new(Module)

	if !p.accept('{') {
		return
	}
	properties := p.parsePropertyList()
	p.accept('}')

	module.Type = typ
	module.Properties = properties
	module.Pos = pos
	return
}

func (p *parser) parsePropertyList() (properties []*Property) {
	for p.tok == scanner.Ident {
		property := p.parseProperty()
		properties = append(properties, property)

		if p.tok != ',' {
			// There was no comma, so the list is done.
			break
		}

		p.accept(',')
	}

	return
}

func (p *parser) parseProperty() (property *Property) {
	property = new(Property)

	name := p.scanner.TokenText()
	pos := p.scanner.Position
	if !p.accept(scanner.Ident, ':') {
		return
	}
	value := p.parseExpression()

	property.Name = name
	property.Value = value
	property.Pos = pos

	return
}

func (p *parser) parseExpression() (value Value) {
	value = p.parseValue()
	switch p.tok {
	case '+':
		return p.parseOperator(value)
	default:
		return value
	}
}

func (p *parser) parseOperator(value1 Value) (value Value) {
	operator := p.tok
	p.accept(operator)

	value2 := p.parseExpression()

	if value1.Type != value2.Type {
		p.errorf("mismatched type in operator %c: %s != %s", operator,
			value1.Type, value2.Type)
		return
	}

	switch operator {
	case '+':
		switch value1.Type {
		case String:
			value1.StringValue += value2.StringValue
		case List:
			value1.ListValue = append(value1.ListValue, value2.ListValue...)
		default:
			p.errorf("operator %c not supported on type %s", operator, value1.Type)
			return
		}
	default:
		panic("unknown operator " + string(operator))
	}

	return value1
}

func (p *parser) parseValue() (value Value) {
	switch p.tok {
	case scanner.Ident:
		return p.parseVariable()
	case scanner.String:
		return p.parseStringValue()
	case '[':
		return p.parseListValue()
	case '{':
		return p.parseMapValue()
	default:
		p.errorf("expected bool, list, or string value; found %s",
			scanner.TokenString(p.tok))
		return
	}
}

func (p *parser) parseVariable() (value Value) {
	value.Pos = p.scanner.Position
	switch text := p.scanner.TokenText(); text {
	case "true":
		value.Type = Bool
		value.BoolValue = true
	case "false":
		value.Type = Bool
		value.BoolValue = false
	default:
		assignment, err := p.scope.Get(p.scanner.TokenText())
		if err != nil {
			p.errorf(err.Error())
		}
		value = assignment.Value
	}

	p.accept(scanner.Ident)
	return
}

func (p *parser) parseStringValue() (value Value) {
	value.Type = String
	value.Pos = p.scanner.Position
	str, err := strconv.Unquote(p.scanner.TokenText())
	if err != nil {
		p.errorf("couldn't parse string: %s", err)
		return
	}
	value.StringValue = str
	p.accept(scanner.String)
	return
}

func (p *parser) parseListValue() (value Value) {
	value.Type = List
	value.Pos = p.scanner.Position
	if !p.accept('[') {
		return
	}

	var elements []Value
	for p.tok != ']' {
		element := p.parseExpression()
		if element.Type != String {
			p.errorf("Expected string in list, found %s", element.String())
			return
		}
		elements = append(elements, element)

		if p.tok != ',' {
			// There was no comma, so the list is done.
			break
		}

		p.accept(',')
	}

	value.ListValue = elements

	p.accept(']')
	return
}

func (p *parser) parseMapValue() (value Value) {
	value.Type = Map
	value.Pos = p.scanner.Position
	if !p.accept('{') {
		return
	}

	properties := p.parsePropertyList()
	value.MapValue = properties

	p.accept('}')
	return
}

type ValueType int

const (
	Bool ValueType = iota
	String
	List
	Map
)

func (p ValueType) String() string {
	switch p {
	case Bool:
		return "bool"
	case String:
		return "string"
	case List:
		return "list"
	case Map:
		return "map"
	default:
		panic(fmt.Errorf("unknown value type: %d", p))
	}
}

type Definition interface {
	String() string
	definitionTag()
}

type Assignment struct {
	Name  string
	Value Value
	Pos   scanner.Position
}

func (a *Assignment) String() string {
	return fmt.Sprintf("%s@%d:%s: %s", a.Name, a.Pos.Offset, a.Pos, a.Value)
}

func (a *Assignment) definitionTag() {}

type Module struct {
	Type       string
	Properties []*Property
	Pos        scanner.Position
}

func (m *Module) String() string {
	propertyStrings := make([]string, len(m.Properties))
	for i, property := range m.Properties {
		propertyStrings[i] = property.String()
	}
	return fmt.Sprintf("%s@%d:%s{%s}", m.Type, m.Pos.Offset, m.Pos,
		strings.Join(propertyStrings, ", "))
}

func (m *Module) definitionTag() {}

type Property struct {
	Name  string
	Value Value
	Pos   scanner.Position
}

func (p *Property) String() string {
	return fmt.Sprintf("%s@%d:%s: %s", p.Name, p.Pos.Offset, p.Pos, p.Value)
}

type Value struct {
	Type        ValueType
	BoolValue   bool
	StringValue string
	ListValue   []Value
	MapValue    []*Property
	Pos         scanner.Position
}

func (p Value) String() string {
	switch p.Type {
	case Bool:
		return fmt.Sprintf("%t@%d:%s", p.BoolValue, p.Pos.Offset, p.Pos)
	case String:
		return fmt.Sprintf("%q@%d:%s", p.StringValue, p.Pos.Offset, p.Pos)
	case List:
		valueStrings := make([]string, len(p.ListValue))
		for i, value := range p.ListValue {
			valueStrings[i] = value.String()
		}
		return fmt.Sprintf("@%d:%s[%s]", p.Pos.Offset, p.Pos,
			strings.Join(valueStrings, ", "))
	case Map:
		propertyStrings := make([]string, len(p.MapValue))
		for i, property := range p.MapValue {
			propertyStrings[i] = property.String()
		}
		return fmt.Sprintf("@%d:%s{%s}", p.Pos.Offset, p.Pos,
			strings.Join(propertyStrings, ", "))
	default:
		panic(fmt.Errorf("bad property type: %d", p.Type))
	}
}

type Scope struct {
	vars map[string]*Assignment
}

func NewScope(s *Scope) *Scope {
	newScope := &Scope{
		vars: make(map[string]*Assignment),
	}

	if s != nil {
		for k, v := range s.vars {
			newScope.vars[k] = v
		}
	}

	return newScope
}

func (s *Scope) Add(assignment *Assignment) error {
	if old, ok := s.vars[assignment.Name]; ok {
		return fmt.Errorf("variable already set, previous assignment: %s", old)
	}

	s.vars[assignment.Name] = assignment

	return nil
}

func (s *Scope) Remove(name string) {
	delete(s.vars, name)
}

func (s *Scope) Get(name string) (*Assignment, error) {
	if a, ok := s.vars[name]; ok {
		return a, nil
	}

	return nil, fmt.Errorf("variable %s not set", name)
}

func (s *Scope) String() string {
	vars := []string{}

	for k := range s.vars {
		vars = append(vars, k)
	}

	sort.Strings(vars)

	ret := []string{}
	for _, v := range vars {
		ret = append(ret, s.vars[v].String())
	}

	return strings.Join(ret, "\n")
}
<|endoftext|>


<commit_before>package main

import (
	"golang.org/x/crypto/bcrypt"
	"net/http"
	"time"
)

func ownerNew(email string, name string, password string) (string, error) {
	if email == "" || name == "" || password == "" {
		return "", errorMissingField
	}

	ownerHex, err := randomHex(32)
	if err != nil {
		logger.Errorf("cannot generate ownerHex: %v", err)
		return "", errorInternal
	}

	passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		logger.Errorf("cannot generate hash from password: %v\n", err)
		return "", errorInternal
	}

	statement := `
		INSERT INTO
		owners (ownerHex, email, name, passwordHash, joinDate, confirmedEmail)
		VALUES ($1,       $2,    $3,   $4,           $5,       $6            );
	`
	_, err = db.Exec(statement, ownerHex, email, name, string(passwordHash), time.Now().UTC(), !smtpConfigured)
	if err != nil {
		// TODO: Make sure `err` is actually about conflicting UNIQUE, and not some
		// other error. If it is something else, we should probably return `errorInternal`.
		return "", errorEmailAlreadyExists
	}

	if smtpConfigured {
		confirmHex, err := randomHex(32)
		if err != nil {
			logger.Errorf("cannot generate confirmHex: %v", err)
			return "", errorInternal
		}

		statement = `
      INSERT INTO
      ownerConfirmHexes (confirmHex, ownerHex, sendDate)
      VALUES            ($1,         $2,       $3      );
    `
		_, err = db.Exec(statement, confirmHex, ownerHex, time.Now().UTC())
		if err != nil {
			logger.Errorf("cannot insert confirmHex: %v\n", err)
			return "", errorInternal
		}

		if err = smtpOwnerConfirmHex(email, name, confirmHex); err != nil {
			return "", err
		}
	}

	return ownerHex, nil
}

func ownerNewHandler(w http.ResponseWriter, r *http.Request) {
	type request struct {
		Email    *string `json:"email"`
		Name     *string `json:"name"`
		Password *string `json:"password"`
	}

	var x request
	if err := unmarshalBody(r, &x); err != nil {
		writeBody(w, response{"success": false, "message": err.Error()})
		return
	}

	if _, err := ownerNew(*x.Email, *x.Name, *x.Password); err != nil {
		writeBody(w, response{"success": false, "message": err.Error()})
		return
	}

	writeBody(w, response{"success": true, "confirmEmail": smtpConfigured})
}
<commit_msg>owner_new.go: create commenter user on ownerNew<commit_after>package main

import (
	"golang.org/x/crypto/bcrypt"
	"net/http"
	"time"
)

func ownerNew(email string, name string, password string) (string, error) {
	if email == "" || name == "" || password == "" {
		return "", errorMissingField
	}

	ownerHex, err := randomHex(32)
	if err != nil {
		logger.Errorf("cannot generate ownerHex: %v", err)
		return "", errorInternal
	}

	passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		logger.Errorf("cannot generate hash from password: %v\n", err)
		return "", errorInternal
	}

	statement := `
		INSERT INTO
		owners (ownerHex, email, name, passwordHash, joinDate, confirmedEmail)
		VALUES ($1,       $2,    $3,   $4,           $5,       $6            );
	`
	_, err = db.Exec(statement, ownerHex, email, name, string(passwordHash), time.Now().UTC(), !smtpConfigured)
	if err != nil {
		// TODO: Make sure `err` is actually about conflicting UNIQUE, and not some
		// other error. If it is something else, we should probably return `errorInternal`.
		return "", errorEmailAlreadyExists
	}

	if smtpConfigured {
		confirmHex, err := randomHex(32)
		if err != nil {
			logger.Errorf("cannot generate confirmHex: %v", err)
			return "", errorInternal
		}

		statement = `
      INSERT INTO
      ownerConfirmHexes (confirmHex, ownerHex, sendDate)
      VALUES            ($1,         $2,       $3      );
    `
		_, err = db.Exec(statement, confirmHex, ownerHex, time.Now().UTC())
		if err != nil {
			logger.Errorf("cannot insert confirmHex: %v\n", err)
			return "", errorInternal
		}

		if err = smtpOwnerConfirmHex(email, name, confirmHex); err != nil {
			return "", err
		}
	}

	return ownerHex, nil
}

func ownerNewHandler(w http.ResponseWriter, r *http.Request) {
	type request struct {
		Email    *string `json:"email"`
		Name     *string `json:"name"`
		Password *string `json:"password"`
	}

	var x request
	if err := unmarshalBody(r, &x); err != nil {
		writeBody(w, response{"success": false, "message": err.Error()})
		return
	}

	if _, err := ownerNew(*x.Email, *x.Name, *x.Password); err != nil {
		writeBody(w, response{"success": false, "message": err.Error()})
		return
	}

	if _, err := commenterNew(*x.Email, *x.Name, "undefined", "undefined", "commento", *x.Password); err != nil {
		writeBody(w, response{"success": false, "message": err.Error()})
		return
	}

	writeBody(w, response{"success": true, "confirmEmail": smtpConfigured})
}
<|endoftext|>


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

package affine

import (
	"math"
)

// GeoMDim is a dimension of a GeoM.
const GeoMDim = 3

// A GeoM represents a matrix to transform geometry when rendering an image.
//
// The initial value is identity.
type GeoM struct {
	initialized bool
	es          [GeoMDim - 1][GeoMDim]float64
}

func (g *GeoM) dim() int {
	return GeoMDim
}

func (g *GeoM) initialize() {
	g.initialized = true
	g.es[0][0] = 1
	g.es[1][1] = 1
}

// Element returns a value of a matrix at (i, j).
func (g *GeoM) Element(i, j int) float64 {
	if !g.initialized {
		if i == j {
			return 1
		}
		return 0
	}
	return g.es[i][j]
}

// Concat multiplies a geometry matrix with the other geometry matrix.
// This is same as muptiplying the matrix other and the matrix g in this order.
func (g *GeoM) Concat(other GeoM) {
	if !g.initialized {
		g.initialize()
	}
	result := GeoM{}
	mul(&other, g, &result)
	*g = result
}

// Add adds a geometry matrix with the other geometry matrix.
func (g *GeoM) Add(other GeoM) {
	if !g.initialized {
		g.initialize()
	}
	result := GeoM{}
	add(&other, g, &result)
	*g = result
}

// Scale scales the matrix by (x, y).
func (g *GeoM) Scale(x, y float64) {
	if !g.initialized {
		g.initialize()
	}
	for i := 0; i < GeoMDim; i++ {
		g.es[0][i] *= x
		g.es[1][i] *= y
	}
}

// Translate translates the matrix by (x, y).
func (g *GeoM) Translate(tx, ty float64) {
	if !g.initialized {
		g.initialize()
	}
	g.es[0][2] += tx
	g.es[1][2] += ty
}

// Rotate rotates the matrix by theta.
func (g *GeoM) Rotate(theta float64) {
	sin, cos := math.Sincos(theta)
	g.Concat(GeoM{
		initialized: true,
		es: [2][3]float64{
			{cos, -sin, 0},
			{sin, cos, 0},
		},
	})
}

// SetElement sets an element at (i, j).
func (g *GeoM) SetElement(i, j int, element float64) {
	if !g.initialized {
		g.initialize()
	}
	g.es[i][j] = element
}

// ScaleGeo is deprecated as of 1.2.0-alpha. Use Scale instead.
func ScaleGeo(x, y float64) GeoM {
	return GeoM{
		initialized: true,
		es: [2][3]float64{
			{x, 0, 0},
			{0, y, 0},
		},
	}
}

// TranslateGeo is deprecated as of 1.2.0-alpha. Use Translate instead.
func TranslateGeo(tx, ty float64) GeoM {
	return GeoM{
		initialized: true,
		es: [2][3]float64{
			{1, 0, tx},
			{0, 1, ty},
		},
	}
}

// RotateGeo is deprecated as of 1.2.0-alpha. Use Rotate instead.
func RotateGeo(theta float64) GeoM {
	sin, cos := math.Sincos(theta)
	return GeoM{
		initialized: true,
		es: [2][3]float64{
			{cos, -sin, 0},
			{sin, cos, 0},
		},
	}
}
<commit_msg>affine: Stop using multiple-dimension array for consistency<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package affine

import (
	"math"
)

// GeoMDim is a dimension of a GeoM.
const GeoMDim = 3

// A GeoM represents a matrix to transform geometry when rendering an image.
//
// The initial value is identity.
type GeoM struct {
	initialized bool
	es          [(GeoMDim - 1) * GeoMDim]float64
}

func (g *GeoM) dim() int {
	return GeoMDim
}

func (g *GeoM) initialize() {
	g.initialized = true
	for i := 0; i < GeoMDim-1; i++ {
		g.es[i*GeoMDim+i] = 1
	}
}

// Element returns a value of a matrix at (i, j).
func (g *GeoM) Element(i, j int) float64 {
	if !g.initialized {
		if i == j {
			return 1
		}
		return 0
	}
	return g.es[i*GeoMDim+j]
}

// Concat multiplies a geometry matrix with the other geometry matrix.
// This is same as muptiplying the matrix other and the matrix g in this order.
func (g *GeoM) Concat(other GeoM) {
	if !g.initialized {
		g.initialize()
	}
	result := GeoM{}
	mul(&other, g, &result)
	*g = result
}

// Add adds a geometry matrix with the other geometry matrix.
func (g *GeoM) Add(other GeoM) {
	if !g.initialized {
		g.initialize()
	}
	result := GeoM{}
	add(&other, g, &result)
	*g = result
}

// Scale scales the matrix by (x, y).
func (g *GeoM) Scale(x, y float64) {
	if !g.initialized {
		g.initialize()
	}
	for i := 0; i < GeoMDim; i++ {
		g.es[i] *= x
		g.es[GeoMDim+i] *= y
	}
}

// Translate translates the matrix by (x, y).
func (g *GeoM) Translate(tx, ty float64) {
	if !g.initialized {
		g.initialize()
	}
	g.es[2] += tx
	g.es[GeoMDim+2] += ty
}

// Rotate rotates the matrix by theta.
func (g *GeoM) Rotate(theta float64) {
	sin, cos := math.Sincos(theta)
	g.Concat(GeoM{
		initialized: true,
		es: [...]float64{
			cos, -sin, 0,
			sin, cos, 0,
		},
	})
}

// SetElement sets an element at (i, j).
func (g *GeoM) SetElement(i, j int, element float64) {
	if !g.initialized {
		g.initialize()
	}
	g.es[i*GeoMDim+j] = element
}

// ScaleGeo is deprecated as of 1.2.0-alpha. Use Scale instead.
func ScaleGeo(x, y float64) GeoM {
	return GeoM{
		initialized: true,
		es: [...]float64{
			x, 0, 0,
			0, y, 0,
		},
	}
}

// TranslateGeo is deprecated as of 1.2.0-alpha. Use Translate instead.
func TranslateGeo(tx, ty float64) GeoM {
	return GeoM{
		initialized: true,
		es: [...]float64{
			1, 0, tx,
			0, 1, ty,
		},
	}
}

// RotateGeo is deprecated as of 1.2.0-alpha. Use Rotate instead.
func RotateGeo(theta float64) GeoM {
	sin, cos := math.Sincos(theta)
	return GeoM{
		initialized: true,
		es: [...]float64{
			cos, -sin, 0,
			sin, cos, 0,
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 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 socks provides a SOCKS version 5 client implementation.
//
// SOCKS protocol version 5 is defined in RFC 1928.
// Username/Password authentication for SOCKS version 5 is defined in
// RFC 1929.
package socks

import (
	"context"
	"errors"
	"io"
	"net"
	"strconv"
)

// A Command represents a SOCKS command.
type Command int

func (cmd Command) String() string {
	switch cmd {
	case CmdConnect:
		return "socks connect"
	case cmdBind:
		return "socks bind"
	default:
		return "socks " + strconv.Itoa(int(cmd))
	}
}

// An AuthMethod represents a SOCKS authentication method.
type AuthMethod int

// A Reply represents a SOCKS command reply code.
type Reply int

func (code Reply) String() string {
	switch code {
	case StatusSucceeded:
		return "succeeded"
	case 0x01:
		return "general SOCKS server failure"
	case 0x02:
		return "connection not allowed by ruleset"
	case 0x03:
		return "network unreachable"
	case 0x04:
		return "host unreachable"
	case 0x05:
		return "connection refused"
	case 0x06:
		return "TTL expired"
	case 0x07:
		return "command not supported"
	case 0x08:
		return "address type not supported"
	default:
		return "unknown code: " + strconv.Itoa(int(code))
	}
}

// Wire protocol constants.
const (
	Version5 = 0x05

	AddrTypeIPv4 = 0x01
	AddrTypeFQDN = 0x03
	AddrTypeIPv6 = 0x04

	CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
	cmdBind    Command = 0x02 // establishes a passive-open forward proxy connection

	AuthMethodNotRequired         AuthMethod = 0x00 // no authentication required
	AuthMethodUsernamePassword    AuthMethod = 0x02 // use username/password
	AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods

	StatusSucceeded Reply = 0x00
)

// An Addr represents a SOCKS-specific address.
// Either Name or IP is used exclusively.
type Addr struct {
	Name string // fully-qualified domain name
	IP   net.IP
	Port int
}

func (a *Addr) Network() string { return "socks" }

func (a *Addr) String() string {
	if a == nil {
		return "<nil>"
	}
	port := strconv.Itoa(a.Port)
	if a.IP == nil {
		return net.JoinHostPort(a.Name, port)
	}
	return net.JoinHostPort(a.IP.String(), port)
}

// A Conn represents a forward proxy connection.
type Conn struct {
	net.Conn

	boundAddr net.Addr
}

// BoundAddr returns the address assigned by the proxy server for
// connecting to the command target address from the proxy server.
func (c *Conn) BoundAddr() net.Addr {
	if c == nil {
		return nil
	}
	return c.boundAddr
}

// A Dialer holds SOCKS-specific options.
type Dialer struct {
	cmd          Command // either CmdConnect or cmdBind
	proxyNetwork string  // network between a proxy server and a client
	proxyAddress string  // proxy server address

	// ProxyDial specifies the optional dial function for
	// establishing the transport connection.
	ProxyDial func(context.Context, string, string) (net.Conn, error)

	// AuthMethods specifies the list of request authention
	// methods.
	// If empty, SOCKS client requests only AuthMethodNotRequired.
	AuthMethods []AuthMethod

	// Authenticate specifies the optional authentication
	// function. It must be non-nil when AuthMethods is not empty.
	// It must return an error when the authentication is failed.
	Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
}

// DialContext connects to the provided address on the provided
// network.
//
// The returned error value may be a net.OpError. When the Op field of
// net.OpError contains "socks", the Source field contains a proxy
// server address and the Addr field contains a command target
// address.
//
// See func Dial of the net package of standard library for a
// description of the network and address parameters.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
	} else {
		var dd net.Dialer
		c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		c.Close()
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return &Conn{Conn: c, boundAddr: a}, nil
}

// DialWithConn initiates a connection from SOCKS server to the target
// network and address using the connection c that is already
// connected to the SOCKS server.
//
// It returns the connection's local address assigned by the SOCKS
// server.
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return a, nil
}

// Dial connects to the provided address on the provided network.
//
// Unlike DialContext, it returns a raw transport connection instead
// of a forward proxy connection.
//
// Deprecated: Use DialContext or DialWithConn instead.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
	} else {
		c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
		return nil, err
	}
	return c, nil
}

func (d *Dialer) validateTarget(network, address string) error {
	switch network {
	case "tcp", "tcp6", "tcp4":
	default:
		return errors.New("network not implemented")
	}
	switch d.cmd {
	case CmdConnect, cmdBind:
	default:
		return errors.New("command not implemented")
	}
	return nil
}

func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
	for i, s := range []string{d.proxyAddress, address} {
		host, port, err := splitHostPort(s)
		if err != nil {
			return nil, nil, err
		}
		a := &Addr{Port: port}
		a.IP = net.ParseIP(host)
		if a.IP == nil {
			a.Name = host
		}
		if i == 0 {
			proxy = a
		} else {
			dst = a
		}
	}
	return
}

// NewDialer returns a new Dialer that dials through the provided
// proxy server's network and address.
func NewDialer(network, address string) *Dialer {
	return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
}

const (
	authUsernamePasswordVersion = 0x01
	authStatusSucceeded         = 0x00
)

// UsernamePassword are the credentials for the username/password
// authentication method.
type UsernamePassword struct {
	Username string
	Password string
}

// Authenticate authenticates a pair of username and password with the
// proxy server.
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
	switch auth {
	case AuthMethodNotRequired:
		return nil
	case AuthMethodUsernamePassword:
		if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
			return errors.New("invalid username/password")
		}
		b := []byte{authUsernamePasswordVersion}
		b = append(b, byte(len(up.Username)))
		b = append(b, up.Username...)
		b = append(b, byte(len(up.Password)))
		b = append(b, up.Password...)
		// TODO(mikio): handle IO deadlines and cancelation if
		// necessary
		if _, err := rw.Write(b); err != nil {
			return err
		}
		if _, err := io.ReadFull(rw, b[:2]); err != nil {
			return err
		}
		if b[0] != authUsernamePasswordVersion {
			return errors.New("invalid username/password version")
		}
		if b[1] != authStatusSucceeded {
			return errors.New("username/password authentication failed")
		}
		return nil
	}
	return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
}
<commit_msg>internal/socks: fix socket descriptor leakage in Dialer.Dial<commit_after>// Copyright 2018 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 socks provides a SOCKS version 5 client implementation.
//
// SOCKS protocol version 5 is defined in RFC 1928.
// Username/Password authentication for SOCKS version 5 is defined in
// RFC 1929.
package socks

import (
	"context"
	"errors"
	"io"
	"net"
	"strconv"
)

// A Command represents a SOCKS command.
type Command int

func (cmd Command) String() string {
	switch cmd {
	case CmdConnect:
		return "socks connect"
	case cmdBind:
		return "socks bind"
	default:
		return "socks " + strconv.Itoa(int(cmd))
	}
}

// An AuthMethod represents a SOCKS authentication method.
type AuthMethod int

// A Reply represents a SOCKS command reply code.
type Reply int

func (code Reply) String() string {
	switch code {
	case StatusSucceeded:
		return "succeeded"
	case 0x01:
		return "general SOCKS server failure"
	case 0x02:
		return "connection not allowed by ruleset"
	case 0x03:
		return "network unreachable"
	case 0x04:
		return "host unreachable"
	case 0x05:
		return "connection refused"
	case 0x06:
		return "TTL expired"
	case 0x07:
		return "command not supported"
	case 0x08:
		return "address type not supported"
	default:
		return "unknown code: " + strconv.Itoa(int(code))
	}
}

// Wire protocol constants.
const (
	Version5 = 0x05

	AddrTypeIPv4 = 0x01
	AddrTypeFQDN = 0x03
	AddrTypeIPv6 = 0x04

	CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
	cmdBind    Command = 0x02 // establishes a passive-open forward proxy connection

	AuthMethodNotRequired         AuthMethod = 0x00 // no authentication required
	AuthMethodUsernamePassword    AuthMethod = 0x02 // use username/password
	AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods

	StatusSucceeded Reply = 0x00
)

// An Addr represents a SOCKS-specific address.
// Either Name or IP is used exclusively.
type Addr struct {
	Name string // fully-qualified domain name
	IP   net.IP
	Port int
}

func (a *Addr) Network() string { return "socks" }

func (a *Addr) String() string {
	if a == nil {
		return "<nil>"
	}
	port := strconv.Itoa(a.Port)
	if a.IP == nil {
		return net.JoinHostPort(a.Name, port)
	}
	return net.JoinHostPort(a.IP.String(), port)
}

// A Conn represents a forward proxy connection.
type Conn struct {
	net.Conn

	boundAddr net.Addr
}

// BoundAddr returns the address assigned by the proxy server for
// connecting to the command target address from the proxy server.
func (c *Conn) BoundAddr() net.Addr {
	if c == nil {
		return nil
	}
	return c.boundAddr
}

// A Dialer holds SOCKS-specific options.
type Dialer struct {
	cmd          Command // either CmdConnect or cmdBind
	proxyNetwork string  // network between a proxy server and a client
	proxyAddress string  // proxy server address

	// ProxyDial specifies the optional dial function for
	// establishing the transport connection.
	ProxyDial func(context.Context, string, string) (net.Conn, error)

	// AuthMethods specifies the list of request authention
	// methods.
	// If empty, SOCKS client requests only AuthMethodNotRequired.
	AuthMethods []AuthMethod

	// Authenticate specifies the optional authentication
	// function. It must be non-nil when AuthMethods is not empty.
	// It must return an error when the authentication is failed.
	Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
}

// DialContext connects to the provided address on the provided
// network.
//
// The returned error value may be a net.OpError. When the Op field of
// net.OpError contains "socks", the Source field contains a proxy
// server address and the Addr field contains a command target
// address.
//
// See func Dial of the net package of standard library for a
// description of the network and address parameters.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
	} else {
		var dd net.Dialer
		c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		c.Close()
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return &Conn{Conn: c, boundAddr: a}, nil
}

// DialWithConn initiates a connection from SOCKS server to the target
// network and address using the connection c that is already
// connected to the SOCKS server.
//
// It returns the connection's local address assigned by the SOCKS
// server.
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return a, nil
}

// Dial connects to the provided address on the provided network.
//
// Unlike DialContext, it returns a raw transport connection instead
// of a forward proxy connection.
//
// Deprecated: Use DialContext or DialWithConn instead.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
	} else {
		c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
		c.Close()
		return nil, err
	}
	return c, nil
}

func (d *Dialer) validateTarget(network, address string) error {
	switch network {
	case "tcp", "tcp6", "tcp4":
	default:
		return errors.New("network not implemented")
	}
	switch d.cmd {
	case CmdConnect, cmdBind:
	default:
		return errors.New("command not implemented")
	}
	return nil
}

func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
	for i, s := range []string{d.proxyAddress, address} {
		host, port, err := splitHostPort(s)
		if err != nil {
			return nil, nil, err
		}
		a := &Addr{Port: port}
		a.IP = net.ParseIP(host)
		if a.IP == nil {
			a.Name = host
		}
		if i == 0 {
			proxy = a
		} else {
			dst = a
		}
	}
	return
}

// NewDialer returns a new Dialer that dials through the provided
// proxy server's network and address.
func NewDialer(network, address string) *Dialer {
	return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
}

const (
	authUsernamePasswordVersion = 0x01
	authStatusSucceeded         = 0x00
)

// UsernamePassword are the credentials for the username/password
// authentication method.
type UsernamePassword struct {
	Username string
	Password string
}

// Authenticate authenticates a pair of username and password with the
// proxy server.
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
	switch auth {
	case AuthMethodNotRequired:
		return nil
	case AuthMethodUsernamePassword:
		if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
			return errors.New("invalid username/password")
		}
		b := []byte{authUsernamePasswordVersion}
		b = append(b, byte(len(up.Username)))
		b = append(b, up.Username...)
		b = append(b, byte(len(up.Password)))
		b = append(b, up.Password...)
		// TODO(mikio): handle IO deadlines and cancelation if
		// necessary
		if _, err := rw.Write(b); err != nil {
			return err
		}
		if _, err := io.ReadFull(rw, b[:2]); err != nil {
			return err
		}
		if b[0] != authUsernamePasswordVersion {
			return errors.New("invalid username/password version")
		}
		if b[1] != authStatusSucceeded {
			return errors.New("username/password authentication failed")
		}
		return nil
	}
	return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
}
<|endoftext|>


<commit_before>package botReactions

import (
	"fmt"
	"log"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/bwmarrin/discordgo"

	"github.com/adayoung/ada-bot/settings"
	"github.com/adayoung/ada-bot/utils/httpclient"
)

type character struct {
	Name string
	URI  string
}

type qwho struct {
	Count      string      `json:"count"`
	Characters []character `json:"characters"`
}

type charactersByName []character        // Implements sort.Interface
func (q charactersByName) Len() int      { return len(q) }
func (q charactersByName) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q charactersByName) Less(i, j int) bool {
	return q[i].Name < q[j].Name
}

type qwhoTrigger struct {
	Trigger string

	sync.RWMutex
	qWhoLast       time.Time
	qWhoLastUsedBy string
}

func (q *qwhoTrigger) Help() string {
	return "Check for online players visible on Achaea at the moment."
}

func (q *qwhoTrigger) HelpDetail() string {
	return q.Help()
}

func (q *qwhoTrigger) Reaction(m *discordgo.Message, a *discordgo.Member, mType string) Reaction {
	q.Lock() // This, because we're maintaining state in the trigger itself
	defer q.Unlock()

	/* begin rate limit qwho */
	timeNow := time.Now()
	if timeNow.Sub(q.qWhoLast) < time.Second*60 {
		return Reaction{Text: fmt.Sprintf("Oops, %s%s is rate limited to once per minute only :shrug:\nLast used by %s at %s",
			settings.Settings.Discord.BotPrefix,
			q.Trigger,
			q.qWhoLastUsedBy,
			q.qWhoLast.Format("15:04:05 -0700 MST"),
		)}
	} else {
		q.qWhoLast = timeNow
		if a != nil {
			if a.Nick != "" {
				q.qWhoLastUsedBy = a.Nick
			} else {
				if a.User != nil {
					q.qWhoLastUsedBy = a.User.Username
				}
			}
		} else {
			if m.Author != nil {
				q.qWhoLastUsedBy = m.Author.Username
			}
		}
	}
	/* end rate limit qwho */

	url := "http://api.achaea.com/characters.json"
	response := "```"

	var _results qwho
	var characters []string
	if err := httpclient.GetJSON(url, &_results); err == nil {
		sort.Sort(charactersByName(_results.Characters))
		for _, character := range _results.Characters {
			characters = append(characters, character.Name)
		}
		response = fmt.Sprintf("%sPlayers: %s", response, strings.Join(characters, ", "))
		response = fmt.Sprintf("%s\nVisible: %s", response, _results.Characters)
		response = fmt.Sprintf("%s\nTotal: %s", response, len(_results.Count))
	} else {
		log.Printf("error: %v", err) // Non fatal error at httpclient.GetJSON() call
	}

	response = fmt.Sprintf("%s```", response)
	return Reaction{Text: response}
}

func init() {
	_qwho := &qwhoTrigger{
		Trigger: "qwho",
	}
	addReaction(_qwho.Trigger, "CREATE", _qwho)

	_qwho.qWhoLast = time.Now().AddDate(0, 0, -1)
	_qwho.qWhoLastUsedBy = "Unknown"
}
<commit_msg>Fix wrong printing verb<commit_after>package botReactions

import (
	"fmt"
	"log"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/bwmarrin/discordgo"

	"github.com/adayoung/ada-bot/settings"
	"github.com/adayoung/ada-bot/utils/httpclient"
)

type character struct {
	Name string
	URI  string
}

type qwho struct {
	Count      string      `json:"count"`
	Characters []character `json:"characters"`
}

type charactersByName []character        // Implements sort.Interface
func (q charactersByName) Len() int      { return len(q) }
func (q charactersByName) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q charactersByName) Less(i, j int) bool {
	return q[i].Name < q[j].Name
}

type qwhoTrigger struct {
	Trigger string

	sync.RWMutex
	qWhoLast       time.Time
	qWhoLastUsedBy string
}

func (q *qwhoTrigger) Help() string {
	return "Check for online players visible on Achaea at the moment."
}

func (q *qwhoTrigger) HelpDetail() string {
	return q.Help()
}

func (q *qwhoTrigger) Reaction(m *discordgo.Message, a *discordgo.Member, mType string) Reaction {
	q.Lock() // This, because we're maintaining state in the trigger itself
	defer q.Unlock()

	/* begin rate limit qwho */
	timeNow := time.Now()
	if timeNow.Sub(q.qWhoLast) < time.Second*60 {
		return Reaction{Text: fmt.Sprintf("Oops, %s%s is rate limited to once per minute only :shrug:\nLast used by %s at %s",
			settings.Settings.Discord.BotPrefix,
			q.Trigger,
			q.qWhoLastUsedBy,
			q.qWhoLast.Format("15:04:05 -0700 MST"),
		)}
	} else {
		q.qWhoLast = timeNow
		if a != nil {
			if a.Nick != "" {
				q.qWhoLastUsedBy = a.Nick
			} else {
				if a.User != nil {
					q.qWhoLastUsedBy = a.User.Username
				}
			}
		} else {
			if m.Author != nil {
				q.qWhoLastUsedBy = m.Author.Username
			}
		}
	}
	/* end rate limit qwho */

	url := "http://api.achaea.com/characters.json"
	response := "```"

	var _results qwho
	var characters []string
	if err := httpclient.GetJSON(url, &_results); err == nil {
		sort.Sort(charactersByName(_results.Characters))
		for _, character := range _results.Characters {
			characters = append(characters, character.Name)
		}
		response = fmt.Sprintf("%sPlayers: %s", response, strings.Join(characters, ", "))
		response = fmt.Sprintf("%s\nVisible: %d", response, len(_results.Characters))
		response = fmt.Sprintf("%s\nTotal: %s", response, len(_results.Count))
	} else {
		log.Printf("error: %v", err) // Non fatal error at httpclient.GetJSON() call
	}

	response = fmt.Sprintf("%s```", response)
	return Reaction{Text: response}
}

func init() {
	_qwho := &qwhoTrigger{
		Trigger: "qwho",
	}
	addReaction(_qwho.Trigger, "CREATE", _qwho)

	_qwho.qWhoLast = time.Now().AddDate(0, 0, -1)
	_qwho.qWhoLastUsedBy = "Unknown"
}
<|endoftext|>


<commit_before>// Package packet is a wrapper for GoPacket and sub packages
package packet

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/google/gopacket/pcapgo"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"

	"github.com/mehrdadrad/mylg/cli"
)

// Packet holds all layers information
type Packet struct {
	// packet layers data
	Eth    *layers.Ethernet
	ARP    *layers.ARP
	IPv4   *layers.IPv4
	IPv6   *layers.IPv6
	TCP    *layers.TCP
	UDP    *layers.UDP
	ICMPv4 *layers.ICMPv4
	ICMPv6 *layers.ICMPv6

	SrcHost []string
	DstHost []string
	Payload string
	// info
	device string
}

// logWriter represents custom writer
type logWriter struct {
}

type Options struct {
	// no color
	nc bool
	// count
	c int
	// device list
	d bool
	// write pcap
	w string
}

var (
	snapLen     int32 = 6 * 1024
	promiscuous       = false
	err         error
	timeout     = 100 * time.Nanosecond
	handle      *pcap.Handle
	addrs       = make(map[string]struct{}, 20)
	ifName      string

	options Options
	filter  string
)

// NewPacket creates an empty packet info
func NewPacket(args string) (*Packet, error) {
	var flag map[string]interface{}

	filter, flag = cli.Flag(args)

	// help
	if _, ok := flag["help"]; ok {
		help()
		return nil, nil
	}

	options = Options{
		nc: cli.SetFlag(flag, "nc", false).(bool),
		c:  cli.SetFlag(flag, "c", 1000000).(int),
		d:  cli.SetFlag(flag, "d", false).(bool),
		w:  cli.SetFlag(flag, "w", "").(string),
	}

	if options.d {
		printDev()
		return nil, nil
	}

	log.SetFlags(0)
	log.SetOutput(new(logWriter))

	return &Packet{
		device: cli.SetFlag(flag, "i", "").(string),
	}, nil
}

// Open is a loop over packets
func (p *Packet) Open() chan *Packet {
	var (
		c    = make(chan *Packet, 1)
		s    = make(chan os.Signal, 1)
		w    *pcapgo.Writer
		loop = true
	)
	// capture interrupt w/ s channel
	signal.Notify(s, os.Interrupt)

	// return first available interface and all ip addresses
	ifName, addrs = lookupDev()
	if p.device == "" {
		p.device = ifName
	}

	go func() {
		var counter int
		defer signal.Stop(s)
		defer close(s)
		defer close(c)

		// write to pcap if needed
		if options.w != "" {
			f, _ := os.Create(options.w)
			w = pcapgo.NewWriter(f)
			w.WriteFileHeader(uint32(snapLen), layers.LinkTypeEthernet)
			defer f.Close()
		}

		handle, err = pcap.OpenLive(p.device, snapLen, promiscuous, timeout)
		if err != nil {
			log.Println(err.Error())
			return
		}
		if err := handle.SetBPFFilter(filter); err != nil {
			log.Println(err.Error())
			return
		}

		defer handle.Close()
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		for loop {
			select {
			case packet := <-packetSource.Packets():
				// write to pcap if needed
				if options.w != "" {
					w.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
				}
				c <- ParsePacketLayers(packet)
				if counter++; counter > options.c-1 {
					loop = false
				}
			case <-s:
				loop = false
			}
		}
	}()
	return c
}

// PrintPretty prints out the captured data
// to the stdout
func (p *Packet) PrintPretty() {
	switch p.Eth.EthernetType {
	case layers.EthernetTypeIPv4:
		p.PrintIPv4()
	case layers.EthernetTypeIPv6:
		p.PrintIPv6()
	case layers.EthernetTypeARP:
		p.PrintARP()
	default:
		// todo
	}
}

// PrintARP prints ARP header
func (p *Packet) PrintARP() {
	log.Printf("%s %s > %s",
		czStr("ARP      ", color.FgWhite, color.BgBlue),
		net.HardwareAddr(p.ARP.SourceHwAddress),
		net.HardwareAddr(p.ARP.DstHwAddress))
}

// PrintIPv4 prints IPv4 packets
func (p *Packet) PrintIPv4() {

	src := czIP(p.IPv4.SrcIP, p.SrcHost, color.Bold)
	dst := czIP(p.IPv4.DstIP, p.DstHost, color.Bold)

	switch {
	case p.IPv4.Protocol == layers.IPProtocolTCP:
		log.Printf("%s %s:%s > %s:%s [%s], win %d, len: %d\n",
			czStr("IPv4/TCP ", color.FgBlack, color.BgWhite),
			src, p.TCP.SrcPort, dst, p.TCP.DstPort,
			czStr(p.flagsString(), color.Bold),
			p.TCP.Window, len(p.Payload))
	case p.IPv4.Protocol == layers.IPProtocolUDP:
		log.Printf("%s %s:%s > %s:%s , len: %d\n",
			czStr("IPv4/UDP ", color.FgBlack, color.BgCyan),
			src, p.UDP.SrcPort, dst, p.UDP.DstPort, len(p.Payload))
	case p.IPv4.Protocol == layers.IPProtocolICMPv4:
		log.Printf("%s %s > %s: %s id %d, seq %d, len: %d\n",
			czStr("IPv4/ICMP", color.FgBlack, color.BgYellow),
			src, dst, p.ICMPv4.TypeCode.String(), p.ICMPv4.Id,
			p.ICMPv4.Seq, len(p.Payload))
	}
}

// PrintIPv6 prints IPv6 packets
func (p *Packet) PrintIPv6() {

	src := czIP(p.IPv6.SrcIP, p.SrcHost, color.Bold)
	dst := czIP(p.IPv6.DstIP, p.DstHost, color.Bold)

	switch {
	case p.IPv6.NextHeader == layers.IPProtocolTCP:
		log.Printf("%s %s:%s > %s:%s, len: %d\n",
			czStr("IPv6/TCP ", color.FgBlack, color.BgHiWhite),
			src, p.TCP.SrcPort, dst, p.TCP.DstPort,
			len(p.Payload))
	case p.IPv6.NextHeader == layers.IPProtocolUDP:
		log.Printf("%s %s:%s > %s:%s, len: %d\n",
			czStr("IPv6/UDP ", color.FgBlack, color.BgHiCyan),
			src, p.UDP.SrcPort, dst, p.UDP.DstPort, len(p.Payload))
	case p.IPv6.NextHeader == layers.IPProtocolICMPv6:
		log.Printf("%s %s > %s: %s, len: %d\n",
			czStr("IPv6/ICMP", color.FgBlack, color.BgYellow),
			src, dst, p.ICMPv6.TypeCode.String(), len(p.Payload))
	}

}

// flags returns flags string except ack
func (p *Packet) flagsString() string {
	var (
		r     []string
		flags = []bool{p.TCP.FIN, p.TCP.SYN, p.TCP.RST, p.TCP.PSH, p.TCP.URG, p.TCP.ECE, p.TCP.NS}
		sign  = "FSRPUECN"
	)
	for i, flag := range flags {
		if flag {
			r = append(r, string(sign[i]))
		}
	}
	r = append(r, ".")
	return strings.Join(r, "")
}

func (writer logWriter) Write(bytes []byte) (int, error) {
	return fmt.Printf("%s %s", time.Now().Format("15:04:05.000"), string(bytes))
}

// ParsePacketLayers decodes layers (Lazy Decoding)
func ParsePacketLayers(packet gopacket.Packet) *Packet {
	var p Packet
	for _, l := range packet.Layers() {
		switch l.LayerType() {
		case layers.LayerTypeEthernet:
			p.Eth, _ = l.(*layers.Ethernet)
		case layers.LayerTypeARP:
			p.ARP, _ = l.(*layers.ARP)
		case layers.LayerTypeIPv4:
			p.IPv4, _ = l.(*layers.IPv4)
			p.SrcHost = lookup(p.IPv4.SrcIP)
			p.DstHost = lookup(p.IPv4.DstIP)
		case layers.LayerTypeIPv6:
			p.IPv6, _ = l.(*layers.IPv6)
			p.SrcHost = lookup(p.IPv6.SrcIP)
			p.DstHost = lookup(p.IPv6.DstIP)
		case layers.LayerTypeICMPv4:
			p.ICMPv4, _ = l.(*layers.ICMPv4)
		case layers.LayerTypeICMPv6:
			p.ICMPv6, _ = l.(*layers.ICMPv6)
		case layers.LayerTypeTCP:
			p.TCP, _ = l.(*layers.TCP)
		case layers.LayerTypeUDP:
			p.UDP, _ = l.(*layers.UDP)
		}
	}

	// Application
	applicationLayer := packet.ApplicationLayer()
	if applicationLayer != nil {
		p.Payload = string(applicationLayer.Payload())
	}

	// Check for errors
	if err := packet.ErrorLayer(); err != nil {
		//fmt.Println("Error decoding some part of the packet:", err)
		// todo
	}
	return &p
}

// czIP makes colorize IP/Host
func czIP(ip net.IP, host []string, attr ...color.Attribute) string {
	var (
		src string
	)
	if _, ok := addrs[ip.String()]; ok && !options.nc {
		if len(host) > 0 {
			src = czStr(host[0], attr...)
		} else {
			src = czStr(ip.String(), attr...)
		}
	} else {
		if len(host) > 0 {
			src = host[0]
		} else {
			src = ip.String()
		}
	}
	return src
}

// czStr makes colorize string
func czStr(i string, attr ...color.Attribute) string {
	c := color.New(attr...).SprintfFunc()
	if !options.nc {
		return c(i)
	}
	return i
}

func lookup(ip net.IP) []string {
	host, _ := net.LookupAddr(ip.String())
	return host
}

func lookupDev() (string, map[string]struct{}) {
	var (
		ips    = make(map[string]struct{}, 20)
		ifName = ""
	)
	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		addrs, _ := i.Addrs()
		if i.Flags == 19 && ifName == "" {
			ifName = i.Name
		}
		for _, addr := range addrs {
			ips[strings.Split(addr.String(), "/")[0]] = struct{}{}
		}
	}
	return ifName, ips
}

func printDev() {
	var (
		status  = "DOWN"
		columns []string
	)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "MAC", "Status", "MTU", "Multicast", "Broadcast", "PointToPoint", "Loopback"})
	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		if strings.Contains(i.Flags.String(), "up") {
			status = "UP"
		} else {
			status = "DOWN"
		}
		columns = append(columns, i.Name, i.HardwareAddr.String(), status, fmt.Sprintf("%d", i.MTU))
		for _, flag := range []string{"multicast", "broadcast", "pointtopoint", "loopback"} {
			if strings.Contains(i.Flags.String(), flag) {
				columns = append(columns, "\u2713")
			} else {
				columns = append(columns, "")
			}
		}
		table.Append(columns)
		columns = columns[:0]
	}
	table.Render()
}

func help() {
	fmt.Println(`
    usage:
          dump [-c count][-i interface][-w filename][-d][-nc]
    options:
          -c count       Stop after receiving count packets (default: 1M)
          -i interface   Listen on specified interface (default: first non-loopback)
          -w filename    Write packets to a pcap format file
          -d             Print list of available interfaces
          -nc            Shows dumps without color
    Example:
          dump tcp and port 443 -c 1000
          dump !udp
          dump -i eth0
          dump -w /tmp/mypcap
	`)
}
<commit_msg>complete ARP basic print<commit_after>// Package packet is a wrapper for GoPacket and sub packages
package packet

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/google/gopacket/pcapgo"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"

	"github.com/mehrdadrad/mylg/cli"
)

// Packet holds all layers information
type Packet struct {
	// packet layers data
	Eth    *layers.Ethernet
	ARP    *layers.ARP
	IPv4   *layers.IPv4
	IPv6   *layers.IPv6
	TCP    *layers.TCP
	UDP    *layers.UDP
	ICMPv4 *layers.ICMPv4
	ICMPv6 *layers.ICMPv6

	SrcHost []string
	DstHost []string
	Payload string
	// info
	device string
}

// logWriter represents custom writer
type logWriter struct {
}

type Options struct {
	// no color
	nc bool
	// count
	c int
	// device list
	d bool
	// write pcap
	w string
}

var (
	snapLen     int32 = 6 * 1024
	promiscuous       = false
	err         error
	timeout     = 100 * time.Nanosecond
	handle      *pcap.Handle
	addrs       = make(map[string]struct{}, 20)
	ifName      string

	options Options
	filter  string
)

// NewPacket creates an empty packet info
func NewPacket(args string) (*Packet, error) {
	var flag map[string]interface{}

	filter, flag = cli.Flag(args)

	// help
	if _, ok := flag["help"]; ok {
		help()
		return nil, nil
	}

	options = Options{
		nc: cli.SetFlag(flag, "nc", false).(bool),
		c:  cli.SetFlag(flag, "c", 1000000).(int),
		d:  cli.SetFlag(flag, "d", false).(bool),
		w:  cli.SetFlag(flag, "w", "").(string),
	}

	if options.d {
		printDev()
		return nil, nil
	}

	log.SetFlags(0)
	log.SetOutput(new(logWriter))

	return &Packet{
		device: cli.SetFlag(flag, "i", "").(string),
	}, nil
}

// Open is a loop over packets
func (p *Packet) Open() chan *Packet {
	var (
		c    = make(chan *Packet, 1)
		s    = make(chan os.Signal, 1)
		w    *pcapgo.Writer
		loop = true
	)
	// capture interrupt w/ s channel
	signal.Notify(s, os.Interrupt)

	// return first available interface and all ip addresses
	ifName, addrs = lookupDev()
	if p.device == "" {
		p.device = ifName
	}

	go func() {
		var counter int
		defer signal.Stop(s)
		defer close(s)
		defer close(c)

		// write to pcap if needed
		if options.w != "" {
			f, _ := os.Create(options.w)
			w = pcapgo.NewWriter(f)
			w.WriteFileHeader(uint32(snapLen), layers.LinkTypeEthernet)
			defer f.Close()
		}

		handle, err = pcap.OpenLive(p.device, snapLen, promiscuous, timeout)
		if err != nil {
			log.Println(err.Error())
			return
		}
		if err := handle.SetBPFFilter(filter); err != nil {
			log.Println(err.Error())
			return
		}

		defer handle.Close()
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		for loop {
			select {
			case packet := <-packetSource.Packets():
				// write to pcap if needed
				if options.w != "" {
					w.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
				}
				c <- ParsePacketLayers(packet)
				if counter++; counter > options.c-1 {
					loop = false
				}
			case <-s:
				loop = false
			}
		}
	}()
	return c
}

// PrintPretty prints out the captured data
// to the stdout
func (p *Packet) PrintPretty() {
	switch p.Eth.EthernetType {
	case layers.EthernetTypeIPv4:
		p.PrintIPv4()
	case layers.EthernetTypeIPv6:
		p.PrintIPv6()
	case layers.EthernetTypeARP:
		p.PrintARP()
	default:
		// todo
	}
}

// PrintARP prints ARP header
func (p *Packet) PrintARP() {
	var (
		operation = "ARP/Reply"
		bgColor   = color.BgBlue
		srcAddr   = p.ARP.SourceProtAddress
		dstAddr   = p.ARP.DstProtAddress
		dstIP     net.IP
		srcIP     net.IP
	)
	if p.ARP.Protocol == layers.EthernetTypeIPv4 {
		srcIP = net.IPv4(srcAddr[0], srcAddr[1], srcAddr[2], srcAddr[3])
		dstIP = net.IPv4(dstAddr[0], dstAddr[1], dstAddr[2], dstAddr[3])
	}

	if p.ARP.Operation == layers.ARPRequest {
		operation = "ARP/Req. "
		bgColor = color.BgHiBlue
		log.Printf("%s %s who-has %s tell %s (%s)",
			czStr(operation, color.FgWhite, bgColor),
			p.ARP.Protocol,
			dstIP.String(),
			srcIP.String(),
			net.HardwareAddr(p.ARP.SourceHwAddress))
	} else {
		log.Printf("%s %s %s is-at %s",
			czStr(operation, color.FgWhite, bgColor),
			p.ARP.Protocol,
			srcIP.String(),
			net.HardwareAddr(p.ARP.SourceHwAddress))
	}
}

// PrintIPv4 prints IPv4 packets
func (p *Packet) PrintIPv4() {

	src := czIP(p.IPv4.SrcIP, p.SrcHost, color.Bold)
	dst := czIP(p.IPv4.DstIP, p.DstHost, color.Bold)

	switch {
	case p.IPv4.Protocol == layers.IPProtocolTCP:
		log.Printf("%s %s:%s > %s:%s [%s], win %d, len: %d\n",
			czStr("IPv4/TCP ", color.FgBlack, color.BgWhite),
			src, p.TCP.SrcPort, dst, p.TCP.DstPort,
			czStr(p.flagsString(), color.Bold),
			p.TCP.Window, len(p.Payload))
	case p.IPv4.Protocol == layers.IPProtocolUDP:
		log.Printf("%s %s:%s > %s:%s , len: %d\n",
			czStr("IPv4/UDP ", color.FgBlack, color.BgCyan),
			src, p.UDP.SrcPort, dst, p.UDP.DstPort, len(p.Payload))
	case p.IPv4.Protocol == layers.IPProtocolICMPv4:
		log.Printf("%s %s > %s: %s id %d, seq %d, len: %d\n",
			czStr("IPv4/ICMP", color.FgBlack, color.BgYellow),
			src, dst, p.ICMPv4.TypeCode.String(), p.ICMPv4.Id,
			p.ICMPv4.Seq, len(p.Payload))
	}
}

// PrintIPv6 prints IPv6 packets
func (p *Packet) PrintIPv6() {

	src := czIP(p.IPv6.SrcIP, p.SrcHost, color.Bold)
	dst := czIP(p.IPv6.DstIP, p.DstHost, color.Bold)

	switch {
	case p.IPv6.NextHeader == layers.IPProtocolTCP:
		log.Printf("%s %s:%s > %s:%s, len: %d\n",
			czStr("IPv6/TCP ", color.FgBlack, color.BgHiWhite),
			src, p.TCP.SrcPort, dst, p.TCP.DstPort,
			len(p.Payload))
	case p.IPv6.NextHeader == layers.IPProtocolUDP:
		log.Printf("%s %s:%s > %s:%s, len: %d\n",
			czStr("IPv6/UDP ", color.FgBlack, color.BgHiCyan),
			src, p.UDP.SrcPort, dst, p.UDP.DstPort, len(p.Payload))
	case p.IPv6.NextHeader == layers.IPProtocolICMPv6:
		log.Printf("%s %s > %s: %s, len: %d\n",
			czStr("IPv6/ICMP", color.FgBlack, color.BgYellow),
			src, dst, p.ICMPv6.TypeCode.String(), len(p.Payload))
	}

}

// flags returns flags string except ack
func (p *Packet) flagsString() string {
	var (
		r     []string
		flags = []bool{p.TCP.FIN, p.TCP.SYN, p.TCP.RST, p.TCP.PSH, p.TCP.URG, p.TCP.ECE, p.TCP.NS}
		sign  = "FSRPUECN"
	)
	for i, flag := range flags {
		if flag {
			r = append(r, string(sign[i]))
		}
	}
	r = append(r, ".")
	return strings.Join(r, "")
}

func (writer logWriter) Write(bytes []byte) (int, error) {
	return fmt.Printf("%s %s", time.Now().Format("15:04:05.000"), string(bytes))
}

// ParsePacketLayers decodes layers (Lazy Decoding)
func ParsePacketLayers(packet gopacket.Packet) *Packet {
	var p Packet
	for _, l := range packet.Layers() {
		switch l.LayerType() {
		case layers.LayerTypeEthernet:
			p.Eth, _ = l.(*layers.Ethernet)
		case layers.LayerTypeARP:
			p.ARP, _ = l.(*layers.ARP)
		case layers.LayerTypeIPv4:
			p.IPv4, _ = l.(*layers.IPv4)
			p.SrcHost = lookup(p.IPv4.SrcIP)
			p.DstHost = lookup(p.IPv4.DstIP)
		case layers.LayerTypeIPv6:
			p.IPv6, _ = l.(*layers.IPv6)
			p.SrcHost = lookup(p.IPv6.SrcIP)
			p.DstHost = lookup(p.IPv6.DstIP)
		case layers.LayerTypeICMPv4:
			p.ICMPv4, _ = l.(*layers.ICMPv4)
		case layers.LayerTypeICMPv6:
			p.ICMPv6, _ = l.(*layers.ICMPv6)
		case layers.LayerTypeTCP:
			p.TCP, _ = l.(*layers.TCP)
		case layers.LayerTypeUDP:
			p.UDP, _ = l.(*layers.UDP)
		}
	}

	// Application
	applicationLayer := packet.ApplicationLayer()
	if applicationLayer != nil {
		p.Payload = string(applicationLayer.Payload())
	}

	// Check for errors
	if err := packet.ErrorLayer(); err != nil {
		//fmt.Println("Error decoding some part of the packet:", err)
		// todo
	}
	return &p
}

// czIP makes colorize IP/Host
func czIP(ip net.IP, host []string, attr ...color.Attribute) string {
	var (
		src string
	)
	if _, ok := addrs[ip.String()]; ok && !options.nc {
		if len(host) > 0 {
			src = czStr(host[0], attr...)
		} else {
			src = czStr(ip.String(), attr...)
		}
	} else {
		if len(host) > 0 {
			src = host[0]
		} else {
			src = ip.String()
		}
	}
	return src
}

// czStr makes colorize string
func czStr(i string, attr ...color.Attribute) string {
	c := color.New(attr...).SprintfFunc()
	if !options.nc {
		return c(i)
	}
	return i
}

func lookup(ip net.IP) []string {
	host, _ := net.LookupAddr(ip.String())
	return host
}

func lookupDev() (string, map[string]struct{}) {
	var (
		ips    = make(map[string]struct{}, 20)
		ifName = ""
	)
	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		addrs, _ := i.Addrs()
		if i.Flags == 19 && ifName == "" {
			ifName = i.Name
		}
		for _, addr := range addrs {
			ips[strings.Split(addr.String(), "/")[0]] = struct{}{}
		}
	}
	return ifName, ips
}

func printDev() {
	var (
		status  = "DOWN"
		columns []string
	)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "MAC", "Status", "MTU", "Multicast", "Broadcast", "PointToPoint", "Loopback"})
	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		if strings.Contains(i.Flags.String(), "up") {
			status = "UP"
		} else {
			status = "DOWN"
		}
		columns = append(columns, i.Name, i.HardwareAddr.String(), status, fmt.Sprintf("%d", i.MTU))
		for _, flag := range []string{"multicast", "broadcast", "pointtopoint", "loopback"} {
			if strings.Contains(i.Flags.String(), flag) {
				columns = append(columns, "\u2713")
			} else {
				columns = append(columns, "")
			}
		}
		table.Append(columns)
		columns = columns[:0]
	}
	table.Render()
}

func help() {
	fmt.Println(`
    usage:
          dump [-c count][-i interface][-w filename][-d][-nc]
    options:
          -c count       Stop after receiving count packets (default: 1M)
          -i interface   Listen on specified interface (default: first non-loopback)
          -w filename    Write packets to a pcap format file
          -d             Print list of available interfaces
          -nc            Shows dumps without color
    Example:
          dump tcp and port 443 -c 1000
          dump !udp
          dump -i eth0
          dump -w /tmp/mypcap
	`)
}
<|endoftext|>


<commit_before>/*
 * Network packet analysis framework.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Provides the interfaces for implementing packet encoders and decoders. Every
// supported protocol implements the Packet interface as a submodule of this
// package (e.g. packet/ipv4, packet/tcp, ...).
package packet

import "fmt"
import "reflect"
import "strconv"
import "strings"

// Type represents the protocol of a packet.
type Type uint16

const (
	None Type = iota
	ARP
	Bluetooth /* TODO */
	Eth
	GRE       /* TODO */
	ICMPv4
	ICMPv6
	IGMP      /* TODO */
	IPSec     /* TODO */
	IPv4
	IPv6
	ISIS      /* TODO */
	L2TP      /* TODO */
	LLC
	LLDP      /* TODO */
	OSPF      /* TODO */
	RadioTap  /* TODO */
	Raw
	SCTP      /* TODO */
	SLL
	SNAP
	TCP
	TRILL     /* TODO */
	UDP
	UDPLite   /* TODO */
	VLAN
	WiFi      /* TODO */
	WoL       /* TODO */
)

// Packet is the interface used internally to implement packet encoding and
// decoding independently of the packet wire format.
type Packet interface {
	GetType() Type
	GetLength() uint16

	Pack(*Buffer) error
	Unpack(raw_pkt *Buffer) error

	Payload() Packet
	PayloadType() Type
	SetPayload(p Packet) error

	InitChecksum(csum uint32)

	String() string
}

var pcap_link_type_to_type_map = [][2]uint32{
	{   1, uint32(Eth)  },
	{ 113, uint32(SLL)  },
}

// Create a new type from the given PCAP link type.
func LinkType(link_type uint32) Type {
	for _, t := range pcap_link_type_to_type_map {
		if t[0] == link_type {
			return Type(t[1])
		}
	}

	return None
}

// Convert the Type to the corresponding PCAP link type.
func (pkttype Type) ToLinkType() uint32 {
	for _, t := range pcap_link_type_to_type_map {
		if t[1] == uint32(pkttype) {
			return t[0]
		}
	}

	return 0x00
}

type type_map struct {
	name    Type
	sname   string
	payload bool
}

var types_map = []type_map{
	{ None,      "None",      true  },
	{ ARP,       "ARP",       true  },
	{ Bluetooth, "Bluetooth", false },
	{ Eth,       "Eth",       false },
	{ GRE,       "GRE",       false },
	{ ICMPv4,    "ICMPv4",    true  },
	{ ICMPv6,    "ICMPv6",    true  },
	{ IGMP,      "IGMP",      true  },
	{ IPSec,     "IPSec",     false },
	{ IPv4,      "IPv4",      false },
	{ IPv6,      "IPv6",      false },
	{ ISIS,      "ISIS",      true  },
	{ L2TP,      "L2TP",      false },
	{ LLC,       "LLC",       false },
	{ LLDP,      "LLDP",      true  },
	{ OSPF,      "OSPF",      true  },
	{ RadioTap,  "RadioTap",  false },
	{ Raw,       "Data",      true  },
	{ SCTP,      "SCTP",      false },
	{ SLL,       "SLL",       false },
	{ SNAP,      "SNAP",      false },
	{ TCP,       "TCP",       false },
	{ TRILL,     "TRILL",     true  },
	{ UDP,       "UDP",       false },
	{ UDPLite,   "UDPLite",   false },
	{ VLAN,      "VLAN",      false },
	{ WiFi,      "WiFi",      false },
	{ WoL,       "WoL",       true  },
}

// Return whether the packet type is the last of the chain.
func (t Type) IsPayload() bool {
	for _, entry := range types_map {
		if entry.name == t {
			return entry.payload
		}
	}

	return true
}

func (t Type) String() string {
	for _, entry := range types_map {
		if entry.name == t {
			return entry.sname
		}
	}

	return "Data"

}

func Stringify(p Packet) string {
	value := reflect.ValueOf(p).Elem()
	name  := strings.ToLower(p.GetType().String())

	var fields []string
	for i := 0; i < value.NumField(); i++ {
		field := value.Field(i)
		ftype := value.Type().Field(i)

		key := strings.ToLower(ftype.Name)

		if ftype.Tag.Get("name") != "" {
			key = ftype.Tag.Get("name")
		}

		if key == "skip" {
			continue
		}

		val := stringify_value(key, field)
		if val != "" {
			fields = append(fields, fmt.Sprintf("%s=%s", key, val))
		}
	}

	s := fmt.Sprintf("%s(%s)", name, strings.Join(fields, ", "))

	if p.Payload() != nil {
		s = strings.Join([]string{s, p.Payload().String()}, " | ")
	}

	return s
}

func stringify_value(key string, val reflect.Value) string {
	var s string
	var m reflect.Value

	if !val.IsValid() {
		goto end
	}

	switch val.Kind() {
	case reflect.Uint8, reflect.Uint16:
		if val.Uint() > 0 {
			if key == "sum" || key == "type" {
				s = "0x" + strconv.FormatUint(val.Uint(), 16)
			} else {
				s = strconv.FormatUint(val.Uint(), 10)
			}
		}

	case reflect.Interface, reflect.Slice, reflect.Struct:
		if val.IsNil() {
			goto end
		}

	case reflect.Bool:
		if val.Bool() {
			s = "true"
		}
	}

	m = val.MethodByName("String")
	if m.IsValid() {
		res := m.Call([]reflect.Value{})
		s = res[0].String()
	}

end:
	return s
}
<commit_msg>Revert "packet: refactor IsPayload() and String() to avoid repeasting the switch cases"<commit_after>/*
 * Network packet analysis framework.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Provides the interfaces for implementing packet encoders and decoders. Every
// supported protocol implements the Packet interface as a submodule of this
// package (e.g. packet/ipv4, packet/tcp, ...).
package packet

import "fmt"
import "reflect"
import "strconv"
import "strings"

// Type represents the protocol of a packet.
type Type uint16

const (
	None Type = iota
	ARP
	Bluetooth /* TODO */
	Eth
	GRE       /* TODO */
	ICMPv4
	ICMPv6
	IGMP      /* TODO */
	IPSec     /* TODO */
	IPv4
	IPv6
	ISIS      /* TODO */
	L2TP      /* TODO */
	LLC
	LLDP      /* TODO */
	OSPF      /* TODO */
	RadioTap  /* TODO */
	Raw
	SCTP      /* TODO */
	SLL
	SNAP
	TCP
	TRILL     /* TODO */
	UDP
	UDPLite   /* TODO */
	VLAN
	WiFi      /* TODO */
	WoL       /* TODO */
)

// Packet is the interface used internally to implement packet encoding and
// decoding independently of the packet wire format.
type Packet interface {
	GetType() Type
	GetLength() uint16

	Pack(*Buffer) error
	Unpack(raw_pkt *Buffer) error

	Payload() Packet
	PayloadType() Type
	SetPayload(p Packet) error

	InitChecksum(csum uint32)

	String() string
}

var pcap_link_type_to_type_map = [][2]uint32{
	{   1, uint32(Eth)  },
	{ 113, uint32(SLL)  },
}

// Create a new type from the given PCAP link type.
func LinkType(link_type uint32) Type {
	for _, t := range pcap_link_type_to_type_map {
		if t[0] == link_type {
			return Type(t[1])
		}
	}

	return None
}

// Convert the Type to the corresponding PCAP link type.
func (pkttype Type) ToLinkType() uint32 {
	for _, t := range pcap_link_type_to_type_map {
		if t[1] == uint32(pkttype) {
			return t[0]
		}
	}

	return 0x00
}

// Return whether the packet type is the last of the chain.
func (t Type) IsPayload() bool {
	switch t {
	case Bluetooth: return false
	case WiFi:     return false
	case Eth:       return false
	case GRE:       return false
	case IPSec:     return false
	case IPv4:      return false
	case IPv6:      return false
	case L2TP:      return false
	case LLC:       return false
	case RadioTap:  return false
	case SCTP:      return false
	case SLL:       return false
	case SNAP:      return false
	case TCP:       return false
	case UDP:       return false
	case UDPLite:   return false
	case VLAN:      return false
	default:        return true
	}
}

func (t Type) String() string {
	switch t {
	case ARP:       return "ARP"
	case Bluetooth: return "Bluetooth"
	case Eth:       return "Ethernet"
	case GRE:       return "GRE"
	case ICMPv4:    return "ICMPv4"
	case ICMPv6:    return "ICMPv6"
	case IGMP:      return "IGMP"
	case IPSec:     return "IPSec"
	case IPv4:      return "IPv4"
	case IPv6:      return "IPv6"
	case ISIS:      return "IS-IS"
	case L2TP:      return "L2TP"
	case LLC:       return "LLC"
	case LLDP:      return "LLDP"
	case None:      return "None"
	case OSPF:      return "OSPF"
	case RadioTap:  return "RadioTap"
	case SCTP:      return "SCTP"
	case SNAP:      return "SNAP"
	case SLL:       return "SLL"
	case TCP:       return "TCP"
	case TRILL:     return "TRILL"
	case UDPLite:   return "UDP Lite"
	case UDP:       return "UDP"
	case VLAN:      return "VLAN"
	case WiFi:      return "WiFi"
	case WoL:       return "WoL"
	/* case Raw: */
	default:        return "Data"
	}
}

func Stringify(p Packet) string {
	value := reflect.ValueOf(p).Elem()
	name  := strings.ToLower(p.GetType().String())

	var fields []string
	for i := 0; i < value.NumField(); i++ {
		field := value.Field(i)
		ftype := value.Type().Field(i)

		key := strings.ToLower(ftype.Name)

		if ftype.Tag.Get("name") != "" {
			key = ftype.Tag.Get("name")
		}

		if key == "skip" {
			continue
		}

		val := stringify_value(key, field)
		if val != "" {
			fields = append(fields, fmt.Sprintf("%s=%s", key, val))
		}
	}

	s := fmt.Sprintf("%s(%s)", name, strings.Join(fields, ", "))

	if p.Payload() != nil {
		s = strings.Join([]string{s, p.Payload().String()}, " | ")
	}

	return s
}

func stringify_value(key string, val reflect.Value) string {
	var s string
	var m reflect.Value

	if !val.IsValid() {
		goto end
	}

	switch val.Kind() {
	case reflect.Uint8, reflect.Uint16:
		if val.Uint() > 0 {
			if key == "sum" || key == "type" {
				s = "0x" + strconv.FormatUint(val.Uint(), 16)
			} else {
				s = strconv.FormatUint(val.Uint(), 10)
			}
		}

	case reflect.Interface, reflect.Slice, reflect.Struct:
		if val.IsNil() {
			goto end
		}

	case reflect.Bool:
		if val.Bool() {
			s = "true"
		}
	}

	m = val.MethodByName("String")
	if m.IsValid() {
		res := m.Call([]reflect.Value{})
		s = res[0].String()
	}

end:
	return s
}
<|endoftext|>