<commit_before>package firmata

import (
	//	"gobot.io/x/gobot/drivers/i2c"
	"gobot.io/x/gobot/platforms/firmata/client"
)

type firmataI2cConnection struct {
	address int
	adaptor *Adaptor
}

// NewFirmataI2cConnection creates an I2C connection to an I2C device at
// the specified address
func NewFirmataI2cConnection(adaptor *Adaptor, address int) (connection *firmataI2cConnection) {
	return &firmataI2cConnection{adaptor: adaptor, address: address}
}

// Read tries to read a full buffer from the i2c device.
// Returns an empty array if the response from the board has timed out.
func (c *firmataI2cConnection) Read(b []byte) (read int, err error) {
	ret := make(chan []byte)

	if err = c.adaptor.board.I2cRead(c.address, len(b)); err != nil {
		return
	}

	c.adaptor.board.Once(c.adaptor.board.Event("I2cReply"), func(data interface{}) {
		ret <- data.(client.I2cReply).Data
	})

	result := <-ret
	copy(b, result)

	read = len(result)

	return
}

func (c *firmataI2cConnection) Write(data []byte) (written int, err error) {
	var chunk []byte
	for len(data) >= 16 {
		chunk, data = data[:16], data[16:]
		err = c.adaptor.board.I2cWrite(c.address, chunk)
		written += len(chunk)
		if err != nil {
			return
		}
	}
	if len(data) > 0 {
		err = c.adaptor.board.I2cWrite(c.address, data[:len(data)])
		written += len(data)
	}
	return
}

func (c *firmataI2cConnection) Close() error {
	return nil
}

func (c *firmataI2cConnection) ReadByte() (val byte, err error) {
	buf := []byte{0}
	if _, err = c.Read(buf); err != nil {
		return
	}
	val = buf[0]
	return
}

func (c *firmataI2cConnection) ReadByteData(reg uint8) (val uint8, err error) {
	if err = c.WriteByte(reg); err != nil {
		return
	}
	return c.ReadByte()
}

func (c *firmataI2cConnection) ReadWordData(reg uint8) (val uint16, err error) {
	if err = c.WriteByte(reg); err != nil {
		return
	}

	buf := []byte{0, 0}
	if _, err = c.Read(buf); err != nil {
		return
	}
	low, high := buf[0], buf[1]

	val = (uint16(high) << 8) | uint16(low)
	return
}

func (c *firmataI2cConnection) ReadBlockData(reg uint8, b []byte) (n int, err error) {
	// Assume target device handles this well, will not behave exactly as on SMBus
	if err = c.WriteByte(reg); err != nil {
		return
	}

	// Try to read full 32 bytes
	buf := make([]byte, 32)
	bytesRead, err := c.Read(buf)
	copy(b, buf)

	return bytesRead, err
}

func (c *firmataI2cConnection) WriteByte(val byte) (err error) {
	buf := []byte{val}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteByteData(reg uint8, val byte) (err error) {
	buf := []byte{reg, val}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteWordData(reg uint8, val uint16) (err error) {
	low := uint8(val & 0xff)
	high := uint8((val >> 8) & 0xff)
	buf := []byte{reg, low, high}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteBlockData(reg uint8, data []byte) (err error) {
	if len(data) > 32 {
		data = data[:32]
	}

	buf := make([]byte, len(data)+1)
	copy(buf[:1], data)
	buf[0] = reg
	_, err = c.Write(buf)
	return
}
<commit_msg>firmata: cleanup slice expression to remove redundant clause<commit_after>package firmata

import (
	//	"gobot.io/x/gobot/drivers/i2c"
	"gobot.io/x/gobot/platforms/firmata/client"
)

type firmataI2cConnection struct {
	address int
	adaptor *Adaptor
}

// NewFirmataI2cConnection creates an I2C connection to an I2C device at
// the specified address
func NewFirmataI2cConnection(adaptor *Adaptor, address int) (connection *firmataI2cConnection) {
	return &firmataI2cConnection{adaptor: adaptor, address: address}
}

// Read tries to read a full buffer from the i2c device.
// Returns an empty array if the response from the board has timed out.
func (c *firmataI2cConnection) Read(b []byte) (read int, err error) {
	ret := make(chan []byte)

	if err = c.adaptor.board.I2cRead(c.address, len(b)); err != nil {
		return
	}

	c.adaptor.board.Once(c.adaptor.board.Event("I2cReply"), func(data interface{}) {
		ret <- data.(client.I2cReply).Data
	})

	result := <-ret
	copy(b, result)

	read = len(result)

	return
}

func (c *firmataI2cConnection) Write(data []byte) (written int, err error) {
	var chunk []byte
	for len(data) >= 16 {
		chunk, data = data[:16], data[16:]
		err = c.adaptor.board.I2cWrite(c.address, chunk)
		written += len(chunk)
		if err != nil {
			return
		}
	}
	if len(data) > 0 {
		err = c.adaptor.board.I2cWrite(c.address, data[:])
		written += len(data)
	}
	return
}

func (c *firmataI2cConnection) Close() error {
	return nil
}

func (c *firmataI2cConnection) ReadByte() (val byte, err error) {
	buf := []byte{0}
	if _, err = c.Read(buf); err != nil {
		return
	}
	val = buf[0]
	return
}

func (c *firmataI2cConnection) ReadByteData(reg uint8) (val uint8, err error) {
	if err = c.WriteByte(reg); err != nil {
		return
	}
	return c.ReadByte()
}

func (c *firmataI2cConnection) ReadWordData(reg uint8) (val uint16, err error) {
	if err = c.WriteByte(reg); err != nil {
		return
	}

	buf := []byte{0, 0}
	if _, err = c.Read(buf); err != nil {
		return
	}
	low, high := buf[0], buf[1]

	val = (uint16(high) << 8) | uint16(low)
	return
}

func (c *firmataI2cConnection) ReadBlockData(reg uint8, b []byte) (n int, err error) {
	// Assume target device handles this well, will not behave exactly as on SMBus
	if err = c.WriteByte(reg); err != nil {
		return
	}

	// Try to read full 32 bytes
	buf := make([]byte, 32)
	bytesRead, err := c.Read(buf)
	copy(b, buf)

	return bytesRead, err
}

func (c *firmataI2cConnection) WriteByte(val byte) (err error) {
	buf := []byte{val}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteByteData(reg uint8, val byte) (err error) {
	buf := []byte{reg, val}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteWordData(reg uint8, val uint16) (err error) {
	low := uint8(val & 0xff)
	high := uint8((val >> 8) & 0xff)
	buf := []byte{reg, low, high}
	_, err = c.Write(buf)
	return
}

func (c *firmataI2cConnection) WriteBlockData(reg uint8, data []byte) (err error) {
	if len(data) > 32 {
		data = data[:32]
	}

	buf := make([]byte, len(data)+1)
	copy(buf[:1], data)
	buf[0] = reg
	_, err = c.Write(buf)
	return
}
<|endoftext|>


<commit_before>package job

import (
	"context"
	"github.com/pkg/errors"
	"github.com/problame/go-streamrpc"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon/logging"
	"github.com/zrepl/zrepl/daemon/serve"
	"github.com/zrepl/zrepl/endpoint"
	"path"
	"github.com/zrepl/zrepl/zfs"
)

type Sink struct {
	name     string
	l        serve.ListenerFactory
	rpcConf  *streamrpc.ConnConfig
	rootDataset *zfs.DatasetPath
}

func SinkFromConfig(g *config.Global, in *config.SinkJob) (s *Sink, err error) {

	s = &Sink{name: in.Name}
	if s.l, s.rpcConf, err = serve.FromConfig(g, in.Serve); err != nil {
		return nil, errors.Wrap(err, "cannot build server")
	}

	s.rootDataset, err = zfs.NewDatasetPath(in.RootDataset)
	if err != nil {
		return nil, errors.New("root dataset is not a valid zfs filesystem path")
	}
	if s.rootDataset.Length() <= 0 {
		return nil, errors.New("root dataset must not be empty") // duplicates error check of receiver
	}

	return s, nil
}

func (j *Sink) Name() string { return j.name }

func (*Sink) Status() interface{} {
	// FIXME
	return nil
}

func (*Sink) RegisterMetrics(registerer prometheus.Registerer) {}

func (j *Sink) Run(ctx context.Context) {

	log := GetLogger(ctx)
	defer log.Info("job exiting")

	l, err := j.l.Listen()
	if err != nil {
		log.WithError(err).Error("cannot listen")
		return
	}
	defer l.Close()

	log.WithField("addr", l.Addr()).Debug("accepting connections")

	var connId int

outer:
	for {

		select {
		case res := <-accept(ctx, l):
			if res.err != nil {
				log.WithError(res.err).Info("accept error")
				continue
			}
			connId++
			connLog := log.
				WithField("connID", connId)
			j.handleConnection(WithLogger(ctx, connLog), res.conn)

		case <-ctx.Done():
			break outer
		}

	}

}

func (j *Sink) handleConnection(ctx context.Context, conn serve.AuthenticatedConn) {
	defer conn.Close()

	log := GetLogger(ctx)
	log.
		WithField("addr", conn.RemoteAddr()).
		WithField("client_identity", conn.ClientIdentity()).
		Info("handling connection")
	defer log.Info("finished handling connection")

	clientRootStr := path.Join(j.rootDataset.ToString(), conn.ClientIdentity())
	clientRoot, err := zfs.NewDatasetPath(clientRootStr)
	if err != nil {
		log.WithError(err).
			WithField("client_identity", conn.ClientIdentity()).
			Error("cannot build client filesystem map (client identity must be a valid ZFS FS name")
	}
	log.WithField("client_root", clientRoot).Debug("client root")

	ctx = logging.WithSubsystemLoggers(ctx, log)

	local, err := endpoint.NewReceiver(clientRoot)
	if err != nil {
		log.WithError(err).Error("unexpected error: cannot convert mapping to filter")
		return
	}

	handler := endpoint.NewHandler(local)
	if err := streamrpc.ServeConn(ctx, conn, j.rpcConf, handler.Handle); err != nil {
		log.WithError(err).Error("error serving client")
	}
}

type acceptResult struct {
	conn serve.AuthenticatedConn
	err  error
}

func accept(ctx context.Context, listener serve.AuthenticatedListener) <-chan acceptResult {
	c := make(chan acceptResult, 1)
	go func() {
		conn, err := listener.Accept(ctx)
		c <- acceptResult{conn, err}
	}()
	return c
}
<commit_msg>sink: concurrent connection handling<commit_after>package job

import (
	"context"
	"github.com/pkg/errors"
	"github.com/problame/go-streamrpc"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon/logging"
	"github.com/zrepl/zrepl/daemon/serve"
	"github.com/zrepl/zrepl/endpoint"
	"path"
	"github.com/zrepl/zrepl/zfs"
)

type Sink struct {
	name     string
	l        serve.ListenerFactory
	rpcConf  *streamrpc.ConnConfig
	rootDataset *zfs.DatasetPath
}

func SinkFromConfig(g *config.Global, in *config.SinkJob) (s *Sink, err error) {

	s = &Sink{name: in.Name}
	if s.l, s.rpcConf, err = serve.FromConfig(g, in.Serve); err != nil {
		return nil, errors.Wrap(err, "cannot build server")
	}

	s.rootDataset, err = zfs.NewDatasetPath(in.RootDataset)
	if err != nil {
		return nil, errors.New("root dataset is not a valid zfs filesystem path")
	}
	if s.rootDataset.Length() <= 0 {
		return nil, errors.New("root dataset must not be empty") // duplicates error check of receiver
	}

	return s, nil
}

func (j *Sink) Name() string { return j.name }

func (*Sink) Status() interface{} {
	// FIXME
	return nil
}

func (*Sink) RegisterMetrics(registerer prometheus.Registerer) {}

func (j *Sink) Run(ctx context.Context) {

	log := GetLogger(ctx)
	defer log.Info("job exiting")

	l, err := j.l.Listen()
	if err != nil {
		log.WithError(err).Error("cannot listen")
		return
	}
	defer l.Close()

	log.WithField("addr", l.Addr()).Debug("accepting connections")

	var connId int

outer:
	for {

		select {
		case res := <-accept(ctx, l):
			if res.err != nil {
				log.WithError(res.err).Info("accept error")
				continue
			}
			connId++
			connLog := log.
				WithField("connID", connId)
			go j.handleConnection(WithLogger(ctx, connLog), res.conn)

		case <-ctx.Done():
			break outer
		}

	}

}

func (j *Sink) handleConnection(ctx context.Context, conn serve.AuthenticatedConn) {
	defer conn.Close()

	log := GetLogger(ctx)
	log.
		WithField("addr", conn.RemoteAddr()).
		WithField("client_identity", conn.ClientIdentity()).
		Info("handling connection")
	defer log.Info("finished handling connection")

	clientRootStr := path.Join(j.rootDataset.ToString(), conn.ClientIdentity())
	clientRoot, err := zfs.NewDatasetPath(clientRootStr)
	if err != nil {
		log.WithError(err).
			WithField("client_identity", conn.ClientIdentity()).
			Error("cannot build client filesystem map (client identity must be a valid ZFS FS name")
	}
	log.WithField("client_root", clientRoot).Debug("client root")

	ctx = logging.WithSubsystemLoggers(ctx, log)

	local, err := endpoint.NewReceiver(clientRoot)
	if err != nil {
		log.WithError(err).Error("unexpected error: cannot convert mapping to filter")
		return
	}

	handler := endpoint.NewHandler(local)
	if err := streamrpc.ServeConn(ctx, conn, j.rpcConf, handler.Handle); err != nil {
		log.WithError(err).Error("error serving client")
	}
}

type acceptResult struct {
	conn serve.AuthenticatedConn
	err  error
}

func accept(ctx context.Context, listener serve.AuthenticatedListener) <-chan acceptResult {
	c := make(chan acceptResult, 1)
	go func() {
		conn, err := listener.Accept(ctx)
		c <- acceptResult{conn, err}
	}()
	return c
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	r "github.com/dancannon/gorethink"
	"github.com/jaracil/ei"
	"github.com/jaracil/nxcli/nxcore"
)

type LoginResponse struct {
	User string                            `json:"user"`
	Tags map[string]map[string]interface{} `json:"tags"`
}

func (nc *NexusConn) handleSysReq(req *JsonRpcReq) {
	switch req.Method {
	case "sys.ping":
		req.Result("pong")

	case "sys.watchdog":
		wdt := ei.N(req.Params).Int64Z()
		if wdt < 10 {
			wdt = 10
		}
		atomic.StoreInt64(&nc.wdog, wdt)
		req.Result(ei.M{"ok": true, "watchdog": wdt})

	case "sys.login":
		var user string
		var mask map[string]map[string]interface{}

		// Auth
		method := ei.N(req.Params).M("method").Lower().StringZ()
		switch method {
		case "", "basic":
			var err int
			user, mask, err = nc.BasicAuth(req.Params)
			if err != ErrNoError {
				req.Error(err, "", nil)
				return
			}

		default:
			nic := NewInternalClient()
			defer nic.Close()
			ret, err := nic.TaskPush(fmt.Sprintf("sys.login.%s.login", method), req.Params, time.Second*10, &nxcore.TaskOpts{})
			if err != nil {
				req.Error(ErrPermissionDenied, err.Error(), nil)
				return
			}

			var loginResponse LoginResponse
			b, err := json.Marshal(ret)
			if err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}

			if err := json.Unmarshal(b, &loginResponse); err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}
			user = loginResponse.User
			mask = loginResponse.Tags
		}

		ud, err := loadUserData(user)
		if err != ErrNoError {
			req.Error(err, "", nil)
			return
		}

		atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(&UserData{User: ud.User, Mask: mask, Tags: maskTags(ud.Tags, mask)}))

		nc.updateSession()
		req.Result(ei.M{"ok": true, "user": nc.user.User, "connId": nc.connId})
	case "sys.reload":
		if done, errcode := nc.reload(true); !done {
			req.Error(errcode, "", nil)
		} else {
			req.Result(ei.M{"ok": true})
		}
	default:
		req.Error(ErrMethodNotFound, "", nil)
	}
}

// Return tags that are equal in both A and B
func maskTags(src map[string]map[string]interface{}, mask map[string]map[string]interface{}) (m map[string]map[string]interface{}) {
	m = make(map[string]map[string]interface{})

	if mask == nil {
		return src
	}

	for prefix, tags := range src {
		if bprefix, ok := mask[prefix]; ok {
			m[prefix] = make(map[string]interface{})
			for k, v := range tags {
				if vb, ok := bprefix[k]; ok && reflect.DeepEqual(v, vb) {
					m[prefix][k] = v
				}
			}
		}
	}
	return
}

func loadUserData(user string) (*UserData, int) {
	return loadUserDataWithTemplates(user, make(map[string]bool))
}

func loadUserDataWithTemplates(user string, loadedTemplates map[string]bool) (*UserData, int) {
	ud := &UserData{}
	cur, err := r.Table("users").Get(strings.ToLower(user)).Run(db)
	if err != nil {
		return nil, ErrInternal
	}
	defer cur.Close()
	err = cur.One(ud)
	if err != nil {
		if err == r.ErrEmptyResult {
			return nil, ErrPermissionDenied
		}
		return nil, ErrInternal
	}
	grabTemplates(ud, loadedTemplates)
	return ud, ErrNoError
}

func grabTemplates(ud *UserData, loadedTemplates map[string]bool) {
	for _, template := range ud.Templates {
		if loadedTemplates[template] {
			continue
		}
		loadedTemplates[template] = true

		tud, err := loadUserDataWithTemplates(template, loadedTemplates)
		if err != ErrNoError {
			continue
		}

		mergeTags(tud, ud)
	}
}

// Bring from src tags that did not exist on dst
func mergeTags(src, dst *UserData) {
	for prefix, tags := range src.Tags {
		for tag, val := range tags {
			if _, ok := dst.Tags[prefix]; !ok {
				dst.Tags[prefix] = make(map[string]interface{})
			}
			if _, ok := dst.Tags[prefix][tag]; !ok {
				dst.Tags[prefix][tag] = val
			}
		}
	}
}

func (nc *NexusConn) BasicAuth(params interface{}) (string, map[string]map[string]interface{}, int) {
	user, err := ei.N(params).M("user").Lower().String()
	if err != nil {
		return "", nil, ErrInvalidParams
	}
	pass, err := ei.N(params).M("pass").Lower().String()
	if err != nil {
		return "", nil, ErrInvalidParams
	}
	var suser string
	split := strings.Split(user, ">")
	switch len(split) {
	case 1:
	case 2:
		if len(split[0]) > 0 && len(split[1]) > 0 {
			user = split[0]
			suser = split[1]
		} else {
			return "", nil, ErrInvalidParams

		}
	default:
		return "", nil, ErrInvalidParams
	}

	ud, rerr := loadUserData(user)
	if rerr != ErrNoError {
		return "", nil, rerr
	}
	dk, err := HashPass(pass, ud.Salt)
	if err != nil {
		return "", nil, ErrInternal
	}
	if ud.Pass != dk {
		return "", nil, ErrPermissionDenied
	}

	if suser != "" {
		tags := getTags(ud, suser)
		if !(ei.N(tags).M("@admin").BoolZ()) {
			return "", nil, ErrPermissionDenied
		}
		sud, err := loadUserData(suser)
		if err != ErrNoError {
			return "", nil, ErrPermissionDenied
		}
		return sud.User, sud.Tags, ErrNoError
	}

	return ud.User, ud.Tags, ErrNoError
}
<commit_msg>Fix lowercasing passwords on basicauth<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	r "github.com/dancannon/gorethink"
	"github.com/jaracil/ei"
	"github.com/jaracil/nxcli/nxcore"
)

type LoginResponse struct {
	User string                            `json:"user"`
	Tags map[string]map[string]interface{} `json:"tags"`
}

func (nc *NexusConn) handleSysReq(req *JsonRpcReq) {
	switch req.Method {
	case "sys.ping":
		req.Result("pong")

	case "sys.watchdog":
		wdt := ei.N(req.Params).Int64Z()
		if wdt < 10 {
			wdt = 10
		}
		atomic.StoreInt64(&nc.wdog, wdt)
		req.Result(ei.M{"ok": true, "watchdog": wdt})

	case "sys.login":
		var user string
		var mask map[string]map[string]interface{}

		// Auth
		method := ei.N(req.Params).M("method").Lower().StringZ()
		switch method {
		case "", "basic":
			var err int
			user, mask, err = nc.BasicAuth(req.Params)
			if err != ErrNoError {
				req.Error(err, "", nil)
				return
			}

		default:
			nic := NewInternalClient()
			defer nic.Close()
			ret, err := nic.TaskPush(fmt.Sprintf("sys.login.%s.login", method), req.Params, time.Second*10, &nxcore.TaskOpts{})
			if err != nil {
				req.Error(ErrPermissionDenied, err.Error(), nil)
				return
			}

			var loginResponse LoginResponse
			b, err := json.Marshal(ret)
			if err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}

			if err := json.Unmarshal(b, &loginResponse); err != nil {
				req.Error(ErrInternal, "", nil)
				return
			}
			user = loginResponse.User
			mask = loginResponse.Tags
		}

		ud, err := loadUserData(user)
		if err != ErrNoError {
			req.Error(err, "", nil)
			return
		}

		atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&nc.user)), unsafe.Pointer(&UserData{User: ud.User, Mask: mask, Tags: maskTags(ud.Tags, mask)}))

		nc.updateSession()
		req.Result(ei.M{"ok": true, "user": nc.user.User, "connId": nc.connId})
	case "sys.reload":
		if done, errcode := nc.reload(true); !done {
			req.Error(errcode, "", nil)
		} else {
			req.Result(ei.M{"ok": true})
		}
	default:
		req.Error(ErrMethodNotFound, "", nil)
	}
}

// Return tags that are equal in both A and B
func maskTags(src map[string]map[string]interface{}, mask map[string]map[string]interface{}) (m map[string]map[string]interface{}) {
	m = make(map[string]map[string]interface{})

	if mask == nil {
		return src
	}

	for prefix, tags := range src {
		if bprefix, ok := mask[prefix]; ok {
			m[prefix] = make(map[string]interface{})
			for k, v := range tags {
				if vb, ok := bprefix[k]; ok && reflect.DeepEqual(v, vb) {
					m[prefix][k] = v
				}
			}
		}
	}
	return
}

func loadUserData(user string) (*UserData, int) {
	return loadUserDataWithTemplates(user, make(map[string]bool))
}

func loadUserDataWithTemplates(user string, loadedTemplates map[string]bool) (*UserData, int) {
	ud := &UserData{}
	cur, err := r.Table("users").Get(strings.ToLower(user)).Run(db)
	if err != nil {
		return nil, ErrInternal
	}
	defer cur.Close()
	err = cur.One(ud)
	if err != nil {
		if err == r.ErrEmptyResult {
			return nil, ErrPermissionDenied
		}
		return nil, ErrInternal
	}
	grabTemplates(ud, loadedTemplates)
	return ud, ErrNoError
}

func grabTemplates(ud *UserData, loadedTemplates map[string]bool) {
	for _, template := range ud.Templates {
		if loadedTemplates[template] {
			continue
		}
		loadedTemplates[template] = true

		tud, err := loadUserDataWithTemplates(template, loadedTemplates)
		if err != ErrNoError {
			continue
		}

		mergeTags(tud, ud)
	}
}

// Bring from src tags that did not exist on dst
func mergeTags(src, dst *UserData) {
	for prefix, tags := range src.Tags {
		for tag, val := range tags {
			if _, ok := dst.Tags[prefix]; !ok {
				dst.Tags[prefix] = make(map[string]interface{})
			}
			if _, ok := dst.Tags[prefix][tag]; !ok {
				dst.Tags[prefix][tag] = val
			}
		}
	}
}

func (nc *NexusConn) BasicAuth(params interface{}) (string, map[string]map[string]interface{}, int) {
	user, err := ei.N(params).M("user").Lower().String()
	if err != nil {
		return "", nil, ErrInvalidParams
	}
	pass, err := ei.N(params).M("pass").String()
	if err != nil {
		return "", nil, ErrInvalidParams
	}
	var suser string
	split := strings.Split(user, ">")
	switch len(split) {
	case 1:
	case 2:
		if len(split[0]) > 0 && len(split[1]) > 0 {
			user = split[0]
			suser = split[1]
		} else {
			return "", nil, ErrInvalidParams

		}
	default:
		return "", nil, ErrInvalidParams
	}

	ud, rerr := loadUserData(user)
	if rerr != ErrNoError {
		return "", nil, rerr
	}
	dk, err := HashPass(pass, ud.Salt)
	if err != nil {
		return "", nil, ErrInternal
	}
	if ud.Pass != dk {
		return "", nil, ErrPermissionDenied
	}

	if suser != "" {
		tags := getTags(ud, suser)
		if !(ei.N(tags).M("@admin").BoolZ()) {
			return "", nil, ErrPermissionDenied
		}
		sud, err := loadUserData(suser)
		if err != ErrNoError {
			return "", nil, ErrPermissionDenied
		}
		return sud.User, sud.Tags, ErrNoError
	}

	return ud.User, ud.Tags, ErrNoError
}
<|endoftext|>


<commit_before>package id3v2

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/bogem/id3v2/frame"
	"github.com/bogem/id3v2/util"
	"io/ioutil"
	"os"
)

type Tag struct {
	Header    *TagHeader
	frames    map[string]frame.Framer
	commonIDs map[string]string

	File         *os.File
	OriginalSize uint32
}

func (t *Tag) SetAttachedPicture(pf frame.PictureFramer) {
	var f frame.APICSequencer
	id := t.commonIDs["Attached Picture"]

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = frame.NewAPICSequence()
		f.SetID(id)
	} else {
		f = existingFrame.(frame.APICSequencer)
	}

	f.AddPicture(pf)
	t.frames[id] = f
}

func (t *Tag) SetTextFrame(id string, text string) {
	var f frame.TextFramer

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = new(frame.TextFrame)
		f.SetID(id)
	} else {
		f = existingFrame.(frame.TextFramer)
	}

	f.SetText(text)
	t.frames[id] = f
}

func (t *Tag) SetTitle(title string) {
	t.SetTextFrame(t.commonIDs["Title"], title)
}

func (t *Tag) SetArtist(artist string) {
	t.SetTextFrame(t.commonIDs["Artist"], artist)
}

func (t *Tag) SetAlbum(album string) {
	t.SetTextFrame(t.commonIDs["Album"], album)
}

func (t *Tag) SetYear(year string) {
	t.SetTextFrame(t.commonIDs["Year"], year)
}

func (t *Tag) SetGenre(genre string) {
	t.SetTextFrame(t.commonIDs["Genre"], genre)
}

func NewTag(file *os.File) *Tag {
	header := &TagHeader{
		Version:    4,
		FramesSize: 0,
	}

	return &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: 0,
	}
}

func ParseTag(file *os.File) (*Tag, error) {
	header, err := ParseHeader(file)
	if err != nil {
		err = errors.New("Trying to parse tag header: " + err.Error())
		return nil, err
	}
	if header == nil {
		return NewTag(file), nil
	}
	if header.Version < 3 {
		err = errors.New("Unsupported version of ID3 tag")
		return nil, err
	}

	tag := &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: TagHeaderSize + header.FramesSize,
	}

	return tag, nil
}

func (t *Tag) Flush() error {
	f := t.File
	defer f.Close()

	if _, err := f.Seek(0, os.SEEK_SET); err != nil {
		return err
	}

	originalFileBuf := bufio.NewReader(f)
	if _, err := originalFileBuf.Discard(int(t.OriginalSize)); err != nil {
		return err
	}

	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}
	defer os.Remove(newFile.Name())
	newFileBuf := bufio.NewWriter(newFile)

	newTag := bytesBufPool.Get().(*bytes.Buffer)
	newTag.Reset()
	if header, err := FormTagHeader(*t.Header); err != nil {
		return err
	} else {
		newTag.Write(header)
	}
	if frames, err := t.FormFrames(); err != nil {
		return err
	} else {
		newTag.Write(frames)
	}

	tagSize, err := newFileBuf.ReadFrom(newTag)
	if err != nil {
		return err
	}
	bytesBufPool.Put(newTag)
	if _, err = newFileBuf.ReadFrom(originalFileBuf); err != nil {
		return err
	}
	if err = newFileBuf.Flush(); err != nil {
		return err
	}
	setSize(f, uint32(tagSize))

	os.Rename(newFile.Name(), f.Name())
	return nil
}

func setSize(f *os.File, size uint32) (err error) {
	sizeBytes, err := util.FormSize(size)
	if err != nil {
		return err
	}

	if _, err = f.WriteAt(sizeBytes, 6); err != nil {
		return err
	}

	return nil
}

func (t Tag) FormFrames() ([]byte, error) {
	b := bytesBufPool.Get().(*bytes.Buffer)
	b.Reset()
	for _, f := range t.frames {
		formedFrame := f.Form()
		frameHeader, err := frame.FormFrameHeader(f, uint32(len(formedFrame)))
		if err != nil {
			return nil, err
		}
		b.Write(frameHeader)
		b.Write(formedFrame)
	}
	bytesBufPool.Put(b)
	return b.Bytes(), nil
}
<commit_msg>Cleanup in tag.setsize()<commit_after>package id3v2

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/bogem/id3v2/frame"
	"github.com/bogem/id3v2/util"
	"io/ioutil"
	"os"
)

type Tag struct {
	Header    *TagHeader
	frames    map[string]frame.Framer
	commonIDs map[string]string

	File         *os.File
	OriginalSize uint32
}

func (t *Tag) SetAttachedPicture(pf frame.PictureFramer) {
	var f frame.APICSequencer
	id := t.commonIDs["Attached Picture"]

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = frame.NewAPICSequence()
		f.SetID(id)
	} else {
		f = existingFrame.(frame.APICSequencer)
	}

	f.AddPicture(pf)
	t.frames[id] = f
}

func (t *Tag) SetTextFrame(id string, text string) {
	var f frame.TextFramer

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = new(frame.TextFrame)
		f.SetID(id)
	} else {
		f = existingFrame.(frame.TextFramer)
	}

	f.SetText(text)
	t.frames[id] = f
}

func (t *Tag) SetTitle(title string) {
	t.SetTextFrame(t.commonIDs["Title"], title)
}

func (t *Tag) SetArtist(artist string) {
	t.SetTextFrame(t.commonIDs["Artist"], artist)
}

func (t *Tag) SetAlbum(album string) {
	t.SetTextFrame(t.commonIDs["Album"], album)
}

func (t *Tag) SetYear(year string) {
	t.SetTextFrame(t.commonIDs["Year"], year)
}

func (t *Tag) SetGenre(genre string) {
	t.SetTextFrame(t.commonIDs["Genre"], genre)
}

func NewTag(file *os.File) *Tag {
	header := &TagHeader{
		Version:    4,
		FramesSize: 0,
	}

	return &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: 0,
	}
}

func ParseTag(file *os.File) (*Tag, error) {
	header, err := ParseHeader(file)
	if err != nil {
		err = errors.New("Trying to parse tag header: " + err.Error())
		return nil, err
	}
	if header == nil {
		return NewTag(file), nil
	}
	if header.Version < 3 {
		err = errors.New("Unsupported version of ID3 tag")
		return nil, err
	}

	tag := &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: TagHeaderSize + header.FramesSize,
	}

	return tag, nil
}

func (t *Tag) Flush() error {
	f := t.File
	defer f.Close()

	if _, err := f.Seek(0, os.SEEK_SET); err != nil {
		return err
	}

	originalFileBuf := bufio.NewReader(f)
	if _, err := originalFileBuf.Discard(int(t.OriginalSize)); err != nil {
		return err
	}

	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}
	defer os.Remove(newFile.Name())
	newFileBuf := bufio.NewWriter(newFile)

	newTag := bytesBufPool.Get().(*bytes.Buffer)
	newTag.Reset()
	if header, err := FormTagHeader(*t.Header); err != nil {
		return err
	} else {
		newTag.Write(header)
	}
	if frames, err := t.FormFrames(); err != nil {
		return err
	} else {
		newTag.Write(frames)
	}

	tagSize, err := newFileBuf.ReadFrom(newTag)
	if err != nil {
		return err
	}
	bytesBufPool.Put(newTag)
	if _, err = newFileBuf.ReadFrom(originalFileBuf); err != nil {
		return err
	}
	if err = newFileBuf.Flush(); err != nil {
		return err
	}
	setSize(f, uint32(tagSize))

	os.Rename(newFile.Name(), f.Name())
	return nil
}

func setSize(f *os.File, size uint32) (err error) {
	sizeBytes, err := util.FormSize(size)
	if err != nil {
		return
	}

	if _, err = f.WriteAt(sizeBytes, 6); err != nil {
		return
	}

	return
}

func (t Tag) FormFrames() ([]byte, error) {
	b := bytesBufPool.Get().(*bytes.Buffer)
	b.Reset()
	for _, f := range t.frames {
		formedFrame := f.Form()
		frameHeader, err := frame.FormFrameHeader(f, uint32(len(formedFrame)))
		if err != nil {
			return nil, err
		}
		b.Write(frameHeader)
		b.Write(formedFrame)
	}
	bytesBufPool.Put(b)
	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>// Copyright 2015 go-dockerclient 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 docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/fileutils"
)

func createTarStream(srcPath, dockerfileName string) (io.ReadCloser, error) {
	excludes, err := parseDockerignore(srcPath)
	if err != nil {
		return nil, err
	}

	includes := []string{"."}

	// If .dockerignore mentions .dockerignore or the Dockerfile
	// then make sure we send both files over to the daemon
	// because Dockerfile is, obviously, needed no matter what, and
	// .dockerignore is needed to know if either one needs to be
	// removed.  The deamon will remove them for us, if needed, after it
	// parses the Dockerfile.
	//
	// https://github.com/docker/docker/issues/8330
	//
	if keepThem, _ := fileutils.Matches(".dockerignore", excludes); keepThem {
		includes = append(includes, ".dockerignore")
	}
	if keepThem, _ := fileutils.Matches(dockerfileName, excludes); keepThem && dockerfileName != "" {
		includes = append(includes, dockerfileName)
	}

	if err := validateContextDirectory(srcPath, excludes); err != nil {
		return nil, err
	}
	tarOpts := &archive.TarOptions{
		ExcludePatterns: excludes,
		IncludeFiles:    includes,
		Compression:     archive.Uncompressed,
		NoLchown:        true,
	}
	return archive.TarWithOptions(srcPath, tarOpts)
}

// validateContextDirectory checks if all the contents of the directory
// can be read and returns an error if some files can't be read.
// Symlinks which point to non-existing files don't trigger an error
func validateContextDirectory(srcPath string, excludes []string) error {
	return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
		// skip this directory/file if it's not in the path, it won't get added to the context
		if relFilePath, err := filepath.Rel(srcPath, filePath); err != nil {
			return err
		} else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil {
			return err
		} else if skip {
			if f.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("can't stat '%s'", filePath)
			}
			if os.IsNotExist(err) {
				return nil
			}
			return err
		}

		// skip checking if symlinks point to non-existing files, such symlinks can be useful
		// also skip named pipes, because they hanging on open
		if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
			return nil
		}

		if !f.IsDir() {
			currentFile, err := os.Open(filePath)
			if err != nil && os.IsPermission(err) {
				return fmt.Errorf("no permission to read from '%s'", filePath)
			}
			currentFile.Close()
		}
		return nil
	})
}

func parseDockerignore(root string) ([]string, error) {
	var excludes []string
	ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
	if err != nil && !os.IsNotExist(err) {
		return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
	}
	for _, pattern := range strings.Split(string(ignore), "\n") {
		matches, err := filepath.Match(pattern, "Dockerfile")
		if err != nil {
			return excludes, fmt.Errorf("bad .dockerignore pattern: '%s', error: %s", pattern, err)
		}
		if matches {
			return excludes, fmt.Errorf("dockerfile was excluded by .dockerignore pattern '%s'", pattern)
		}
		excludes = append(excludes, pattern)
	}

	return excludes, nil
}
<commit_msg>no need for checking Dockerfile/.dockerignore excluded anymore<commit_after>// Copyright 2015 go-dockerclient 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 docker

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/fileutils"
)

func createTarStream(srcPath, dockerfileName string) (io.ReadCloser, error) {
	excludes, err := parseDockerignore(srcPath)
	if err != nil {
		return nil, err
	}

	includes := []string{"."}

	// If .dockerignore mentions .dockerignore or the Dockerfile
	// then make sure we send both files over to the daemon
	// because Dockerfile is, obviously, needed no matter what, and
	// .dockerignore is needed to know if either one needs to be
	// removed.  The deamon will remove them for us, if needed, after it
	// parses the Dockerfile.
	//
	// https://github.com/docker/docker/issues/8330
	//
	if keepThem, _ := fileutils.Matches(".dockerignore", excludes); keepThem {
		includes = append(includes, ".dockerignore")
	}
	if keepThem, _ := fileutils.Matches(dockerfileName, excludes); keepThem && dockerfileName != "" {
		includes = append(includes, dockerfileName)
	}

	if err := validateContextDirectory(srcPath, excludes); err != nil {
		return nil, err
	}
	tarOpts := &archive.TarOptions{
		ExcludePatterns: excludes,
		IncludeFiles:    includes,
		Compression:     archive.Uncompressed,
		NoLchown:        true,
	}
	return archive.TarWithOptions(srcPath, tarOpts)
}

// validateContextDirectory checks if all the contents of the directory
// can be read and returns an error if some files can't be read.
// Symlinks which point to non-existing files don't trigger an error
func validateContextDirectory(srcPath string, excludes []string) error {
	return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
		// skip this directory/file if it's not in the path, it won't get added to the context
		if relFilePath, err := filepath.Rel(srcPath, filePath); err != nil {
			return err
		} else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil {
			return err
		} else if skip {
			if f.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("can't stat '%s'", filePath)
			}
			if os.IsNotExist(err) {
				return nil
			}
			return err
		}

		// skip checking if symlinks point to non-existing files, such symlinks can be useful
		// also skip named pipes, because they hanging on open
		if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
			return nil
		}

		if !f.IsDir() {
			currentFile, err := os.Open(filePath)
			if err != nil && os.IsPermission(err) {
				return fmt.Errorf("no permission to read from '%s'", filePath)
			}
			currentFile.Close()
		}
		return nil
	})
}

func parseDockerignore(root string) ([]string, error) {
	var excludes []string
	ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
	if err != nil && !os.IsNotExist(err) {
		return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
	}
	excludes = strings.Split(string(ignore), "\n")

	return excludes, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 Bell Canada, Pantheon Technologies and/or its affiliates.
//
// 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.

//go:generate descriptor-adapter --descriptor-name LocalSID --value-type *vpp_srv6.LocalSID --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name Policy --value-type *vpp_srv6.Policy --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name PolicySegmentList --value-type *vpp_srv6.PolicySegmentList --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name Steering --value-type *vpp_srv6.Steering --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"

package srplugin

import (
	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	scheduler "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor/cache"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls"
	"github.com/pkg/errors"

	_ "github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls/vpp1810"
	_ "github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls/vpp1901"
)

// SRPlugin configures segment routing.
type SRPlugin struct {
	Deps

	// GoVPP channels
	vppCh govppapi.Channel

	// VPP handler
	srHandler vppcalls.SRv6VppAPI

	// descriptors
	localSIDDescriptor    *descriptor.LocalSIDDescriptor
	policyDescriptor      *descriptor.PolicyDescriptor
	segmentListDescriptor *descriptor.PolicySegmentListDescriptor
	steeringDescriptor    *descriptor.SteeringDescriptor
}

// Deps lists dependencies of the interface p.
type Deps struct {
	infra.PluginDeps
	Scheduler   scheduler.KVScheduler
	GoVppmux    govppmux.API
	IfPlugin    ifplugin.API
	StatusCheck statuscheck.PluginStatusWriter // optional
}

// Init initializes and registers descriptors for Linux ARPs and Routes.
func (p *SRPlugin) Init() error {
	var err error

	// GoVPP channels
	if p.vppCh, err = p.GoVppmux.NewAPIChannel(); err != nil {
		return errors.Errorf("failed to create GoVPP API channel: %v", err)
	}

	// init handlers
	p.srHandler = vppcalls.CompatibleSRv6VppHandler(p.vppCh, p.IfPlugin.GetInterfaceIndex(), p.Log)

	// global caches
	policyInfoCache := make(cache.PolicyInfoCache)
	policyIndexCache := cache.NewPolicyIndexCache()
	policySegmentListIndexCache := cache.NewPolicySegmentListIndexCache()

	// init & register descriptors
	p.localSIDDescriptor = descriptor.NewLocalSIDDescriptor(p.srHandler, p.Log)
	localSIDDescriptor := adapter.NewLocalSIDDescriptor(p.localSIDDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(localSIDDescriptor)

	p.segmentListDescriptor = descriptor.NewPolicySegmentListDescriptor(p.srHandler, p.Deps.Scheduler, p.Log,
		policyInfoCache, policyIndexCache, policySegmentListIndexCache)
	segmentListDescriptor := adapter.NewPolicySegmentListDescriptor(p.segmentListDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(segmentListDescriptor)

	p.policyDescriptor = descriptor.NewPolicyDescriptor(p.srHandler, p.Deps.Scheduler, p.Log, policyInfoCache,
		policyIndexCache, policySegmentListIndexCache, p.segmentListDescriptor)
	policyDescriptor := adapter.NewPolicyDescriptor(p.policyDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(policyDescriptor)

	p.steeringDescriptor = descriptor.NewSteeringDescriptor(p.srHandler, p.Deps.Scheduler, p.Log, policyIndexCache)
	steeringDescriptor := adapter.NewSteeringDescriptor(p.steeringDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(steeringDescriptor)

	return nil
}

// AfterInit registers plugin with StatusCheck.
func (p *SRPlugin) AfterInit() error {
	if p.StatusCheck != nil {
		p.StatusCheck.Register(p.PluginName, nil)
	}
	return nil
}
<commit_msg>temporally forcing vpp1901 for srplugin until vpp switching functions properly<commit_after>// Copyright (c) 2019 Bell Canada, Pantheon Technologies and/or its affiliates.
//
// 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.

//go:generate descriptor-adapter --descriptor-name LocalSID --value-type *vpp_srv6.LocalSID --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name Policy --value-type *vpp_srv6.Policy --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name PolicySegmentList --value-type *vpp_srv6.PolicySegmentList --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"
//go:generate descriptor-adapter --descriptor-name Steering --value-type *vpp_srv6.Steering --import "github.com/ligato/vpp-agent/api/models/vpp/srv6" --output-dir "descriptor"

package srplugin

import (
	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	scheduler "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/descriptor/cache"
	"github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls"
	"github.com/pkg/errors"

	//_ "github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls/vpp1810"  // TODO FIXME forcing everything to vpp 19.01 due to not functioning proper switching mechanism
	_ "github.com/ligato/vpp-agent/plugins/vpp/srplugin/vppcalls/vpp1901"
)

// SRPlugin configures segment routing.
type SRPlugin struct {
	Deps

	// GoVPP channels
	vppCh govppapi.Channel

	// VPP handler
	srHandler vppcalls.SRv6VppAPI

	// descriptors
	localSIDDescriptor    *descriptor.LocalSIDDescriptor
	policyDescriptor      *descriptor.PolicyDescriptor
	segmentListDescriptor *descriptor.PolicySegmentListDescriptor
	steeringDescriptor    *descriptor.SteeringDescriptor
}

// Deps lists dependencies of the interface p.
type Deps struct {
	infra.PluginDeps
	Scheduler   scheduler.KVScheduler
	GoVppmux    govppmux.API
	IfPlugin    ifplugin.API
	StatusCheck statuscheck.PluginStatusWriter // optional
}

// Init initializes and registers descriptors for Linux ARPs and Routes.
func (p *SRPlugin) Init() error {
	var err error

	// GoVPP channels
	if p.vppCh, err = p.GoVppmux.NewAPIChannel(); err != nil {
		return errors.Errorf("failed to create GoVPP API channel: %v", err)
	}

	// init handlers
	p.srHandler = vppcalls.CompatibleSRv6VppHandler(p.vppCh, p.IfPlugin.GetInterfaceIndex(), p.Log)

	// global caches
	policyInfoCache := make(cache.PolicyInfoCache)
	policyIndexCache := cache.NewPolicyIndexCache()
	policySegmentListIndexCache := cache.NewPolicySegmentListIndexCache()

	// init & register descriptors
	p.localSIDDescriptor = descriptor.NewLocalSIDDescriptor(p.srHandler, p.Log)
	localSIDDescriptor := adapter.NewLocalSIDDescriptor(p.localSIDDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(localSIDDescriptor)

	p.segmentListDescriptor = descriptor.NewPolicySegmentListDescriptor(p.srHandler, p.Deps.Scheduler, p.Log,
		policyInfoCache, policyIndexCache, policySegmentListIndexCache)
	segmentListDescriptor := adapter.NewPolicySegmentListDescriptor(p.segmentListDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(segmentListDescriptor)

	p.policyDescriptor = descriptor.NewPolicyDescriptor(p.srHandler, p.Deps.Scheduler, p.Log, policyInfoCache,
		policyIndexCache, policySegmentListIndexCache, p.segmentListDescriptor)
	policyDescriptor := adapter.NewPolicyDescriptor(p.policyDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(policyDescriptor)

	p.steeringDescriptor = descriptor.NewSteeringDescriptor(p.srHandler, p.Deps.Scheduler, p.Log, policyIndexCache)
	steeringDescriptor := adapter.NewSteeringDescriptor(p.steeringDescriptor.GetDescriptor())
	p.Deps.Scheduler.RegisterKVDescriptor(steeringDescriptor)

	return nil
}

// AfterInit registers plugin with StatusCheck.
func (p *SRPlugin) AfterInit() error {
	if p.StatusCheck != nil {
		p.StatusCheck.Register(p.PluginName, nil)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"

	"github.com/vtolstov/gopacket"
	"github.com/vtolstov/gopacket/layers"
	"golang.org/x/net/ipv4"
)

func cidr2bcast(cidr string) string {
	var a uint32
	var m uint32
	var s string
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return s
	}
	a |= uint32(ip.To4()[0])
	a |= uint32(ip.To4()[1]) << 8
	a |= uint32(ip.To4()[2]) << 16
	a |= uint32(ip.To4()[3]) << 24
	m |= uint32(net.IP(ipnet.Mask).To4()[0])
	m |= uint32(net.IP(ipnet.Mask).To4()[1]) << 8
	m |= uint32(net.IP(ipnet.Mask).To4()[2]) << 16
	m |= uint32(net.IP(ipnet.Mask).To4()[3]) << 24
	b := a | ^m
	s = fmt.Sprintf("%d.%d.%d.%d", byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
	return s
}

func (s *Server) ListenAndServeUDPv4() {
	ipAddr := &net.IPAddr{IP: net.IPv4zero}
	conn, err := net.ListenIP("ip4:udp", ipAddr)
	if err != nil {
		l.Info(err.Error())
		return
	}
	defer conn.Close()
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		l.Info(err.Error())
		return
	}

	s.ipv4conn, err = ipv4.NewRawConn(conn)
	if err != nil {
		l.Info(err.Error())
		return
	}

	if err = s.ipv4conn.SetControlMessage(ipv4.FlagDst, true); err != nil {
		l.Warning(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	var gw net.IP
	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" {
			gw = net.ParseIP(addr.Address)
		}
	}
	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		l.Info(fmt.Sprintf("failed to get iface: %s", err.Error()))
		return
	}

	_ = gw
	_ = iface
	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv4conn.SetReadDeadline(time.Now().Add(time.Second))

		hdr, _, _, err := s.ipv4conn.ReadFrom(buffer)

		if err != nil {
			switch v := err.(type) {
			case *net.OpError:
				if v.Timeout() {
					continue
				}
			case *net.AddrError:
				if v.Timeout() {
					continue
				}
			case *net.UnknownNetworkError:
				if v.Timeout() {
					continue
				}
			default:
				l.Warning(err.Error())
				return
			}
		}
		var ip4 layers.IPv4
		var udp layers.UDP
		var dhcp4req layers.DHCPv4
		parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &udp, &dhcp4req)
		decoded := []gopacket.LayerType{}
		err = parser.DecodeLayers(buffer, &decoded)
		for _, layerType := range decoded {
			switch layerType {
			/*
				case layers.LayerTypeIPv4:
					fmt.Printf("IP4: %+v\n", ip4)
				case layers.LayerTypeUDP:
					fmt.Printf("UDP: %+v\n", udp)
			*/
			case layers.LayerTypeDHCPv4:
				//			fmt.Printf("DHCP4: %+v\n", dhcp4req)
				if dhcp4req.Operation == layers.DHCP_MSG_REQ {

					dhcp4res, err := s.ServeUDPv4(&dhcp4req)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
					if dhcp4res == nil {
						// ignore empty dhcp packets
						continue
					}

					buf := gopacket.NewSerializeBuffer()
					opts := gopacket.SerializeOptions{true, true}
					gopacket.SerializeLayers(buf, opts,
						&layers.UDP{SrcPort: 67, DstPort: 68},
						dhcp4res)

					wcm := ipv4.ControlMessage{TTL: 255}
					wcm.Dst = net.IPv4bcast.To4()
					wcm.Src = gw.To4()
					wcm.IfIndex = iface.Index
					err = s.ipv4conn.WriteTo(&ipv4.Header{Len: 20, TOS: hdr.TOS, TotalLen: 20 + int(len(buf.Bytes())), FragOff: 0, TTL: 255, Protocol: int(layers.IPProtocolUDP), Src: gw.To4(), Dst: net.IPv4bcast.To4()}, buf.Bytes(), &wcm)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
				} else {
					//l.Warning("unknown dhcp operation %#+v\n", dhcp4req)
					// ignore response packets
					continue
				}
			}
		}
	}
}

func (s *Server) ServeUDPv4(dhcpreq *layers.DHCPv4) (*layers.DHCPv4, error) {
	dhcpres := &layers.DHCPv4{}

	l.Info(fmt.Sprintf("%s dhcpv4 req: %+v\n", s.name, dhcpreq))

	leaseTime := 6000
	var ip net.IP
	var gw net.IP
	var mac net.HardwareAddr
	var ipnet *net.IPNet
	var err error
	var cidr string
	mac = dhcpreq.ClientHWAddr

	if s.metadata == nil {
		return nil, fmt.Errorf("err: metadata is nil")
	}

	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "false" {
			cidr = addr.Address + "/" + addr.Prefix
			ip, ipnet, err = net.ParseCIDR(cidr)
			if err != nil {
				return nil, err
			}
		}
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" && ipnet != nil && ipnet.Contains(net.ParseIP(addr.Address)) {
			gw = net.ParseIP(addr.Address)
			break
		}

	}
	if ipnet == nil || ipnet.Mask == nil || gw == nil || ip == nil {
		return nil, fmt.Errorf("failed to get ipv4 info")
	}

	opt := dhcpreq.Options[0]
	switch opt.Type {
	case layers.DHCP_OPT_MESSAGE_TYPE:
		switch layers.DHCPOperation(opt.Data[0]) {
		case layers.DHCPOperation(layers.DHCP_MSG_DISCOVER):
			dhcpres, err = layers.NewDHCPOffer(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_REQUEST):
			dhcpres, err = layers.NewDHCPAck(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_OFFER), layers.DHCPOperation(layers.DHCP_MSG_ACK):
			return nil, nil
		default:
			return nil, fmt.Errorf("unk dhcp msg: %d\n", layers.DHCPOperation(opt.Data[0]))
		}
	}

	return dhcpres, nil
}
<commit_msg>add deubg<commit_after>package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"

	"github.com/vtolstov/gopacket"
	"github.com/vtolstov/gopacket/layers"
	"golang.org/x/net/ipv4"
)

func cidr2bcast(cidr string) string {
	var a uint32
	var m uint32
	var s string
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return s
	}
	a |= uint32(ip.To4()[0])
	a |= uint32(ip.To4()[1]) << 8
	a |= uint32(ip.To4()[2]) << 16
	a |= uint32(ip.To4()[3]) << 24
	m |= uint32(net.IP(ipnet.Mask).To4()[0])
	m |= uint32(net.IP(ipnet.Mask).To4()[1]) << 8
	m |= uint32(net.IP(ipnet.Mask).To4()[2]) << 16
	m |= uint32(net.IP(ipnet.Mask).To4()[3]) << 24
	b := a | ^m
	s = fmt.Sprintf("%d.%d.%d.%d", byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
	return s
}

func (s *Server) ListenAndServeUDPv4() {
	ipAddr := &net.IPAddr{IP: net.IPv4zero}
	conn, err := net.ListenIP("ip4:udp", ipAddr)
	if err != nil {
		l.Info(err.Error())
		return
	}
	defer conn.Close()
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		l.Info(err.Error())
		return
	}

	s.ipv4conn, err = ipv4.NewRawConn(conn)
	if err != nil {
		l.Info(err.Error())
		return
	}

	if err = s.ipv4conn.SetControlMessage(ipv4.FlagDst, true); err != nil {
		l.Warning(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	var gw net.IP
	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" {
			gw = net.ParseIP(addr.Address)
		}
	}
	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		l.Info(fmt.Sprintf("failed to get iface: %s", err.Error()))
		return
	}

	_ = gw
	_ = iface
	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv4conn.SetReadDeadline(time.Now().Add(time.Second))

		hdr, _, _, err := s.ipv4conn.ReadFrom(buffer)

		if err != nil {
			switch v := err.(type) {
			case *net.OpError:
				if v.Timeout() {
					continue
				}
			case *net.AddrError:
				if v.Timeout() {
					continue
				}
			case *net.UnknownNetworkError:
				if v.Timeout() {
					continue
				}
			default:
				l.Warning(err.Error())
				return
			}
		}
		var ip4 layers.IPv4
		var udp layers.UDP
		var dhcp4req layers.DHCPv4
		parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &udp, &dhcp4req)
		decoded := []gopacket.LayerType{}
		err = parser.DecodeLayers(buffer, &decoded)
		for _, layerType := range decoded {
			switch layerType {
			/*
				case layers.LayerTypeIPv4:
					fmt.Printf("IP4: %+v\n", ip4)
				case layers.LayerTypeUDP:
					fmt.Printf("UDP: %+v\n", udp)
			*/
			case layers.LayerTypeDHCPv4:
				//			fmt.Printf("DHCP4: %+v\n", dhcp4req)
				if dhcp4req.Operation == layers.DHCP_MSG_REQ {

					dhcp4res, err := s.ServeUDPv4(&dhcp4req)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
					if dhcp4res == nil {
						// ignore empty dhcp packets
						continue
					}

					buf := gopacket.NewSerializeBuffer()
					opts := gopacket.SerializeOptions{true, true}
					gopacket.SerializeLayers(buf, opts,
						&layers.UDP{SrcPort: 67, DstPort: 68},
						dhcp4res)

					wcm := ipv4.ControlMessage{TTL: 255}
					wcm.Dst = net.IPv4bcast.To4()
					wcm.Src = gw.To4()
					wcm.IfIndex = iface.Index
					err = s.ipv4conn.WriteTo(&ipv4.Header{Len: 20, TOS: hdr.TOS, TotalLen: 20 + int(len(buf.Bytes())), FragOff: 0, TTL: 255, Protocol: int(layers.IPProtocolUDP), Src: gw.To4(), Dst: net.IPv4bcast.To4()}, buf.Bytes(), &wcm)
					if err != nil {
						l.Warning(err.Error())
						continue
					}
				} else {
					//l.Warning("unknown dhcp operation %#+v\n", dhcp4req)
					// ignore response packets
					continue
				}
			}
		}
	}
}

func (s *Server) ServeUDPv4(dhcpreq *layers.DHCPv4) (*layers.DHCPv4, error) {
	dhcpres := &layers.DHCPv4{}

	l.Info(fmt.Sprintf("%s dhcpv4 req: %+v\n", s.name, dhcpreq))

	leaseTime := 6000
	var ip net.IP
	var gw net.IP
	var mac net.HardwareAddr
	var ipnet *net.IPNet
	var err error
	var cidr string
	mac = dhcpreq.ClientHWAddr

	if s.metadata == nil {
		return nil, fmt.Errorf("err: metadata is nil")
	}

	for _, addr := range s.metadata.Network.IP {
		if addr.Family == "ipv4" && addr.Host == "false" {
			cidr = addr.Address + "/" + addr.Prefix
			ip, ipnet, err = net.ParseCIDR(cidr)
			if err != nil {
				return nil, err
			}
		}
		if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" && ipnet != nil && ipnet.Contains(net.ParseIP(addr.Address)) {
			gw = net.ParseIP(addr.Address)
			break
		}

	}
	if ipnet == nil || ipnet.Mask == nil || gw == nil || ip == nil {
		return nil, fmt.Errorf("failed to get ipv4 info")
	}

	opt := dhcpreq.Options[0]
	switch opt.Type {
	case layers.DHCP_OPT_MESSAGE_TYPE:
		switch layers.DHCPOperation(opt.Data[0]) {
		case layers.DHCPOperation(layers.DHCP_MSG_DISCOVER):
			dhcpres, err = layers.NewDHCPOffer(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_REQUEST):
			dhcpres, err = layers.NewDHCPAck(dhcpreq.Xid)
			if err != nil {
				return nil, err
			}
			copy(dhcpres.ClientHWAddr, mac[:dhcpres.HardwareLen])
			copy(dhcpres.YourIP, ip.To4())
			copy(dhcpres.ServerIP, gw.To4())
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(1, []byte(net.IP(ipnet.Mask).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(3, []byte(gw.To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(5, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(6, []byte(net.ParseIP(s.metadata.Network.NameServer[0]).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(28, []byte(net.ParseIP(cidr2bcast(cidr)).To4())))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(15, []byte(s.metadata.Network.DomainName)))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(12, []byte(s.name)))
			var b [8]byte
			var bs []byte
			bs = b[:4]
			binary.BigEndian.PutUint32(bs, uint32(leaseTime))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(51, bs))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(54, []byte(gw.To4())))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*50))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T1, bs))
			binary.BigEndian.PutUint32(bs, uint32(leaseTime/100*88))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_T2, bs))
			bs = b[:2]
			binary.BigEndian.PutUint16(bs, uint16(1500))
			dhcpres.Options = append(dhcpres.Options, layers.NewDHCPOption(layers.DHCP_OPT_INTERFACE_MTU, bs))
		case layers.DHCPOperation(layers.DHCP_MSG_OFFER), layers.DHCPOperation(layers.DHCP_MSG_ACK):
			return nil, nil
		default:
			return nil, fmt.Errorf("unk dhcp msg: %d\n", layers.DHCPOperation(opt.Data[0]))
		}
	}
	l.Info(fmt.Sprintf("%s dhcpv4 res: %+v\n", s.name, dhcpres))
	return dhcpres, nil
}
<|endoftext|>


<commit_before>package couchbase

import (
	"log"
	"sync"
	"time"

	"fmt"
	"github.com/couchbase/gomemcached"
	"github.com/couchbase/gomemcached/client"
)

// A UprFeed streams mutation events from a bucket.
//
// Events from the bucket can be read from the channel 'C'.  Remember
// to call Close() on it when you're done, unless its channel has
// closed itself already.
type UprFeed struct {
	C <-chan *memcached.UprEvent

	bucket       *Bucket
	nodeFeeds    map[string]*FeedInfo     // The UPR feeds of the individual nodes
	output       chan *memcached.UprEvent // Same as C but writeably-typed
	outputClosed bool
	quit         chan bool
	name         string // name of this UPR feed
	sequence     uint32 // sequence number for this feed
	connected    bool
	killSwitch   chan bool
	closing      bool
	wg           sync.WaitGroup
}

// UprFeed from a single connection
type FeedInfo struct {
	uprFeed   *memcached.UprFeed // UPR feed handle
	host      string             // hostname
	connected bool               // connected
	quit      chan bool          // quit channel
}

type FailoverLog map[uint16]memcached.FailoverLog

// GetFailoverLogs, get the failover logs for a set of vbucket ids
func (b *Bucket) GetFailoverLogs(vBuckets []uint16) (FailoverLog, error) {

	// map vbids to their corresponding hosts
	vbHostList := make(map[string][]uint16)
	vbm := b.VBServerMap()
	if len(vbm.VBucketMap) < len(vBuckets) {
		return nil, fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vbm.VBucketMap, vBuckets)
	}

	for _, vb := range vBuckets {
		masterID := vbm.VBucketMap[vb][0]
		master := b.getMasterNode(masterID)
		if master == "" {
			return nil, fmt.Errorf("No master found for vb %d", vb)
		}

		vbList := vbHostList[master]
		if vbList == nil {
			vbList = make([]uint16, 0)
		}
		vbList = append(vbList, vb)
		vbHostList[master] = vbList
	}

	failoverLogMap := make(FailoverLog)
	for _, serverConn := range b.getConnPools() {

		vbList := vbHostList[serverConn.host]
		if vbList == nil {
			continue
		}

		mc, err := serverConn.Get()
		if err != nil {
			log.Printf("No Free connections for vblist %v", vbList)
			return nil, fmt.Errorf("No Free connections for host %s",
				serverConn.host)

		}
		// close the connection so that it doesn't get reused for upr data
		// connection
		defer mc.Close()
		failoverlogs, err := mc.UprGetFailoverLog(vbList)
		if err != nil {
			return nil, fmt.Errorf("Error getting failover log %s host %s",
				err.Error(), serverConn.host)

		}

		for vb, log := range failoverlogs {
			failoverLogMap[vb] = *log
		}
	}

	return failoverLogMap, nil
}

// StartUprFeed creates and starts a new Upr feed
// No data will be sent on the channel unless vbuckets streams are requested
func (b *Bucket) StartUprFeed(name string, sequence uint32) (*UprFeed, error) {

	feed := &UprFeed{
		bucket:     b,
		output:     make(chan *memcached.UprEvent, 10),
		quit:       make(chan bool),
		nodeFeeds:  make(map[string]*FeedInfo, 0),
		name:       name,
		sequence:   sequence,
		killSwitch: make(chan bool),
	}

	err := feed.connectToNodes()
	if err != nil {
		return nil, fmt.Errorf("Cannot connect to bucket %s", err.Error())
	}
	feed.connected = true
	go feed.run()

	feed.C = feed.output
	return feed, nil
}

// UprRequestStream starts a stream for a vb on a feed
func (feed *UprFeed) UprRequestStream(vb uint16, opaque uint16, flags uint32,
	vuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	defer func() {
		if r := recover(); r != nil {
			log.Panic("Panic in UprRequestStream. Feed %v Bucket %v ", feed, feed.bucket)
		}
	}()

	vbm := feed.bucket.VBServerMap()
	if len(vbm.VBucketMap) < int(vb) {
		return fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vb, vbm.VBucketMap)
	}

	if int(vb) >= len(vbm.VBucketMap) {
		return fmt.Errorf("Invalid vbucket id %d", vb)
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		return fmt.Errorf("Master node not found for vbucket %d", vb)
	}
	singleFeed := feed.nodeFeeds[master]
	if singleFeed == nil {
		return fmt.Errorf("UprFeed for this host not found")
	}

	if err := singleFeed.uprFeed.UprRequestStream(vb, opaque, flags,
		vuuid, startSequence, endSequence, snapStart, snapEnd); err != nil {
		return err
	}

	return nil
}

// UprCloseStream ends a vbucket stream.
func (feed *UprFeed) UprCloseStream(vb, opaqueMSB uint16) error {

	defer func() {
		if r := recover(); r != nil {
			log.Panic("Panic in UprCloseStream. Feed %v Bucket %v ", feed, feed.bucket)
		}
	}()

	vbm := feed.bucket.VBServerMap()
	if len(vbm.VBucketMap) < int(vb) {
		return fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vb, vbm.VBucketMap)
	}

	if int(vb) >= len(vbm.VBucketMap) {
		return fmt.Errorf("Invalid vbucket id %d", vb)
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		return fmt.Errorf("Master node not found for vbucket %d", vb)
	}
	singleFeed := feed.nodeFeeds[master]
	if singleFeed == nil {
		return fmt.Errorf("UprFeed for this host not found")
	}

	if err := singleFeed.uprFeed.CloseStream(vb, opaqueMSB); err != nil {
		return err
	}
	return nil
}

// Goroutine that runs the feed
func (feed *UprFeed) run() {
	retryInterval := initialRetryInterval
	bucketOK := true
	for {
		// Connect to the UPR feed of each server node:
		if bucketOK {
			// Run until one of the sub-feeds fails:
			select {
			case <-feed.killSwitch:
			case <-feed.quit:
				return
			}
			//feed.closeNodeFeeds()
			retryInterval = initialRetryInterval
		}

		if feed.closing == true {
			// we have been asked to shut down
			return
		}

		// On error, try to refresh the bucket in case the list of nodes changed:
		log.Printf("go-couchbase: UPR connection lost; reconnecting to bucket %q in %v",
			feed.bucket.Name, retryInterval)

		if err := feed.bucket.Refresh(); err != nil {
			log.Printf("Unable to refresh bucket %s ", err.Error())
			feed.closeNodeFeeds()
		}

		// this will only connect to nodes that are not connected or changed
		// user will have to reconnect the stream
		err := feed.connectToNodes()
		if err != nil {
			log.Printf("Unable to connect to nodes..exit ")
			close(feed.output)
			feed.outputClosed = true
			feed.closeNodeFeeds()
			return
		}
		bucketOK = err == nil

		select {
		case <-time.After(retryInterval):
		case <-feed.quit:
			return
		}
		if retryInterval *= 2; retryInterval > maximumRetryInterval {
			retryInterval = maximumRetryInterval
		}
	}
}

func (feed *UprFeed) connectToNodes() (err error) {
	nodeCount := 0
	for _, serverConn := range feed.bucket.getConnPools() {

		// this maybe a reconnection, so check if the connection to the node
		// already exists. Connect only if the node is not found in the list
		// or connected == false
		nodeFeed := feed.nodeFeeds[serverConn.host]

		if nodeFeed != nil && nodeFeed.connected == true {
			continue
		}

		var singleFeed *memcached.UprFeed
		var name string
		if feed.name == "" {
			name = "DefaultUprClient"
		} else {
			name = feed.name
		}
		singleFeed, err = serverConn.StartUprFeed(name, feed.sequence)
		if err != nil {
			log.Printf("go-couchbase: Error connecting to upr feed of %s: %v", serverConn.host, err)
			feed.closeNodeFeeds()
			return
		}
		// add the node to the connection map
		feedInfo := &FeedInfo{
			uprFeed:   singleFeed,
			connected: true,
			host:      serverConn.host,
			quit:      make(chan bool),
		}
		feed.nodeFeeds[serverConn.host] = feedInfo
		go feed.forwardUprEvents(feedInfo, feed.killSwitch, serverConn.host)
		feed.wg.Add(1)
		nodeCount++
	}
	if nodeCount == 0 {
		return fmt.Errorf("No connection to bucket")
	}

	return nil
}

// Goroutine that forwards Upr events from a single node's feed to the aggregate feed.
func (feed *UprFeed) forwardUprEvents(nodeFeed *FeedInfo, killSwitch chan bool, host string) {
	singleFeed := nodeFeed.uprFeed

	defer func() {
		feed.wg.Done()
		if r := recover(); r != nil {
			//if feed is not closing, re-throw the panic
			if feed.outputClosed != true && feed.closing != true {
				panic(r)
			} else {
				log.Printf("Panic is recovered. Since feed is closed, exit gracefully")

			}
		}
	}()

	for {
		select {
		case <-nodeFeed.quit:
			nodeFeed.connected = false
			return

		case event, ok := <-singleFeed.C:
			if !ok {
				if singleFeed.Error != nil {
					log.Printf("go-couchbase: Upr feed from %s failed: %v", host, singleFeed.Error)
				}
				killSwitch <- true
				return
			}
			if feed.outputClosed == true {
				// someone closed the node feed
				log.Printf("Node need closed, returning from forwardUprEvent")
				return
			}
			feed.output <- event
			if event.Status == gomemcached.NOT_MY_VBUCKET {
				log.Printf(" Got a not my vbucket error !! ")
				if err := feed.bucket.Refresh(); err != nil {
					log.Printf("Unable to refresh bucket %s ", err.Error())
					feed.closeNodeFeeds()
					return
				}
				// this will only connect to nodes that are not connected or changed
				// user will have to reconnect the stream
				if err := feed.connectToNodes(); err != nil {
					log.Printf("Unable to connect to nodes %s", err.Error())
					return
				}

			}
		}
	}
}

func (feed *UprFeed) closeNodeFeeds() {
	for _, f := range feed.nodeFeeds {
		log.Printf(" Sending close to forwardUprEvent ")
		close(f.quit)
		f.uprFeed.Close()
	}
	feed.nodeFeeds = nil
}

// Close a Upr feed.
func (feed *UprFeed) Close() error {
	select {
	case <-feed.quit:
		return nil
	default:
	}

	feed.closing = true
	feed.closeNodeFeeds()
	close(feed.quit)

	feed.wg.Wait()
	if feed.outputClosed == false {
		feed.outputClosed = true
		close(feed.output)
	}

	return nil
}
<commit_msg>MB-14917. Close feed if bucket refresh fails<commit_after>package couchbase

import (
	"log"
	"sync"
	"time"

	"fmt"
	"github.com/couchbase/gomemcached"
	"github.com/couchbase/gomemcached/client"
)

// A UprFeed streams mutation events from a bucket.
//
// Events from the bucket can be read from the channel 'C'.  Remember
// to call Close() on it when you're done, unless its channel has
// closed itself already.
type UprFeed struct {
	C <-chan *memcached.UprEvent

	bucket       *Bucket
	nodeFeeds    map[string]*FeedInfo     // The UPR feeds of the individual nodes
	output       chan *memcached.UprEvent // Same as C but writeably-typed
	outputClosed bool
	quit         chan bool
	name         string // name of this UPR feed
	sequence     uint32 // sequence number for this feed
	connected    bool
	killSwitch   chan bool
	closing      bool
	wg           sync.WaitGroup
}

// UprFeed from a single connection
type FeedInfo struct {
	uprFeed   *memcached.UprFeed // UPR feed handle
	host      string             // hostname
	connected bool               // connected
	quit      chan bool          // quit channel
}

type FailoverLog map[uint16]memcached.FailoverLog

// GetFailoverLogs, get the failover logs for a set of vbucket ids
func (b *Bucket) GetFailoverLogs(vBuckets []uint16) (FailoverLog, error) {

	// map vbids to their corresponding hosts
	vbHostList := make(map[string][]uint16)
	vbm := b.VBServerMap()
	if len(vbm.VBucketMap) < len(vBuckets) {
		return nil, fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vbm.VBucketMap, vBuckets)
	}

	for _, vb := range vBuckets {
		masterID := vbm.VBucketMap[vb][0]
		master := b.getMasterNode(masterID)
		if master == "" {
			return nil, fmt.Errorf("No master found for vb %d", vb)
		}

		vbList := vbHostList[master]
		if vbList == nil {
			vbList = make([]uint16, 0)
		}
		vbList = append(vbList, vb)
		vbHostList[master] = vbList
	}

	failoverLogMap := make(FailoverLog)
	for _, serverConn := range b.getConnPools() {

		vbList := vbHostList[serverConn.host]
		if vbList == nil {
			continue
		}

		mc, err := serverConn.Get()
		if err != nil {
			log.Printf("No Free connections for vblist %v", vbList)
			return nil, fmt.Errorf("No Free connections for host %s",
				serverConn.host)

		}
		// close the connection so that it doesn't get reused for upr data
		// connection
		defer mc.Close()
		failoverlogs, err := mc.UprGetFailoverLog(vbList)
		if err != nil {
			return nil, fmt.Errorf("Error getting failover log %s host %s",
				err.Error(), serverConn.host)

		}

		for vb, log := range failoverlogs {
			failoverLogMap[vb] = *log
		}
	}

	return failoverLogMap, nil
}

// StartUprFeed creates and starts a new Upr feed
// No data will be sent on the channel unless vbuckets streams are requested
func (b *Bucket) StartUprFeed(name string, sequence uint32) (*UprFeed, error) {

	feed := &UprFeed{
		bucket:     b,
		output:     make(chan *memcached.UprEvent, 10),
		quit:       make(chan bool),
		nodeFeeds:  make(map[string]*FeedInfo, 0),
		name:       name,
		sequence:   sequence,
		killSwitch: make(chan bool),
	}

	err := feed.connectToNodes()
	if err != nil {
		return nil, fmt.Errorf("Cannot connect to bucket %s", err.Error())
	}
	feed.connected = true
	go feed.run()

	feed.C = feed.output
	return feed, nil
}

// UprRequestStream starts a stream for a vb on a feed
func (feed *UprFeed) UprRequestStream(vb uint16, opaque uint16, flags uint32,
	vuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	defer func() {
		if r := recover(); r != nil {
			log.Panic("Panic in UprRequestStream. Feed %v Bucket %v ", feed, feed.bucket)
		}
	}()

	vbm := feed.bucket.VBServerMap()
	if len(vbm.VBucketMap) < int(vb) {
		return fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vb, vbm.VBucketMap)
	}

	if int(vb) >= len(vbm.VBucketMap) {
		return fmt.Errorf("Invalid vbucket id %d", vb)
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		return fmt.Errorf("Master node not found for vbucket %d", vb)
	}
	singleFeed := feed.nodeFeeds[master]
	if singleFeed == nil {
		return fmt.Errorf("UprFeed for this host not found")
	}

	if err := singleFeed.uprFeed.UprRequestStream(vb, opaque, flags,
		vuuid, startSequence, endSequence, snapStart, snapEnd); err != nil {
		return err
	}

	return nil
}

// UprCloseStream ends a vbucket stream.
func (feed *UprFeed) UprCloseStream(vb, opaqueMSB uint16) error {

	defer func() {
		if r := recover(); r != nil {
			log.Panic("Panic in UprCloseStream. Feed %v Bucket %v ", feed, feed.bucket)
		}
	}()

	vbm := feed.bucket.VBServerMap()
	if len(vbm.VBucketMap) < int(vb) {
		return fmt.Errorf("vbmap smaller than vbucket list: %v vs. %v",
			vb, vbm.VBucketMap)
	}

	if int(vb) >= len(vbm.VBucketMap) {
		return fmt.Errorf("Invalid vbucket id %d", vb)
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		return fmt.Errorf("Master node not found for vbucket %d", vb)
	}
	singleFeed := feed.nodeFeeds[master]
	if singleFeed == nil {
		return fmt.Errorf("UprFeed for this host not found")
	}

	if err := singleFeed.uprFeed.CloseStream(vb, opaqueMSB); err != nil {
		return err
	}
	return nil
}

// Goroutine that runs the feed
func (feed *UprFeed) run() {
	retryInterval := initialRetryInterval
	bucketOK := true
	for {
		// Connect to the UPR feed of each server node:
		if bucketOK {
			// Run until one of the sub-feeds fails:
			select {
			case <-feed.killSwitch:
			case <-feed.quit:
				return
			}
			//feed.closeNodeFeeds()
			retryInterval = initialRetryInterval
		}

		if feed.closing == true {
			// we have been asked to shut down
			return
		}

		// On error, try to refresh the bucket in case the list of nodes changed:
		log.Printf("go-couchbase: UPR connection lost; reconnecting to bucket %q in %v",
			feed.bucket.Name, retryInterval)

		if err := feed.bucket.Refresh(); err != nil {
			// if we fail to refresh the bucket, exit the feed
			// MB-14917
			log.Printf("Unable to refresh bucket %s ", err.Error())
			close(feed.output)
			feed.outputClosed = true
			feed.closeNodeFeeds()
			return
		}

		// this will only connect to nodes that are not connected or changed
		// user will have to reconnect the stream
		err := feed.connectToNodes()
		if err != nil {
			log.Printf("Unable to connect to nodes..exit ")
			close(feed.output)
			feed.outputClosed = true
			feed.closeNodeFeeds()
			return
		}
		bucketOK = err == nil

		select {
		case <-time.After(retryInterval):
		case <-feed.quit:
			return
		}
		if retryInterval *= 2; retryInterval > maximumRetryInterval {
			retryInterval = maximumRetryInterval
		}
	}
}

func (feed *UprFeed) connectToNodes() (err error) {
	nodeCount := 0
	for _, serverConn := range feed.bucket.getConnPools() {

		// this maybe a reconnection, so check if the connection to the node
		// already exists. Connect only if the node is not found in the list
		// or connected == false
		nodeFeed := feed.nodeFeeds[serverConn.host]

		if nodeFeed != nil && nodeFeed.connected == true {
			continue
		}

		var singleFeed *memcached.UprFeed
		var name string
		if feed.name == "" {
			name = "DefaultUprClient"
		} else {
			name = feed.name
		}
		singleFeed, err = serverConn.StartUprFeed(name, feed.sequence)
		if err != nil {
			log.Printf("go-couchbase: Error connecting to upr feed of %s: %v", serverConn.host, err)
			feed.closeNodeFeeds()
			return
		}
		// add the node to the connection map
		feedInfo := &FeedInfo{
			uprFeed:   singleFeed,
			connected: true,
			host:      serverConn.host,
			quit:      make(chan bool),
		}
		feed.nodeFeeds[serverConn.host] = feedInfo
		go feed.forwardUprEvents(feedInfo, feed.killSwitch, serverConn.host)
		feed.wg.Add(1)
		nodeCount++
	}
	if nodeCount == 0 {
		return fmt.Errorf("No connection to bucket")
	}

	return nil
}

// Goroutine that forwards Upr events from a single node's feed to the aggregate feed.
func (feed *UprFeed) forwardUprEvents(nodeFeed *FeedInfo, killSwitch chan bool, host string) {
	singleFeed := nodeFeed.uprFeed

	defer func() {
		feed.wg.Done()
		if r := recover(); r != nil {
			//if feed is not closing, re-throw the panic
			if feed.outputClosed != true && feed.closing != true {
				panic(r)
			} else {
				log.Printf("Panic is recovered. Since feed is closed, exit gracefully")

			}
		}
	}()

	for {
		select {
		case <-nodeFeed.quit:
			nodeFeed.connected = false
			return

		case event, ok := <-singleFeed.C:
			if !ok {
				if singleFeed.Error != nil {
					log.Printf("go-couchbase: Upr feed from %s failed: %v", host, singleFeed.Error)
				}
				killSwitch <- true
				return
			}
			if feed.outputClosed == true {
				// someone closed the node feed
				log.Printf("Node need closed, returning from forwardUprEvent")
				return
			}
			feed.output <- event
			if event.Status == gomemcached.NOT_MY_VBUCKET {
				log.Printf(" Got a not my vbucket error !! ")
				if err := feed.bucket.Refresh(); err != nil {
					log.Printf("Unable to refresh bucket %s ", err.Error())
					feed.closeNodeFeeds()
					return
				}
				// this will only connect to nodes that are not connected or changed
				// user will have to reconnect the stream
				if err := feed.connectToNodes(); err != nil {
					log.Printf("Unable to connect to nodes %s", err.Error())
					return
				}

			}
		}
	}
}

func (feed *UprFeed) closeNodeFeeds() {
	for _, f := range feed.nodeFeeds {
		log.Printf(" Sending close to forwardUprEvent ")
		close(f.quit)
		f.uprFeed.Close()
	}
	feed.nodeFeeds = nil
}

// Close a Upr feed.
func (feed *UprFeed) Close() error {
	select {
	case <-feed.quit:
		return nil
	default:
	}

	feed.closing = true
	feed.closeNodeFeeds()
	close(feed.quit)

	feed.wg.Wait()
	if feed.outputClosed == false {
		feed.outputClosed = true
		close(feed.output)
	}

	return nil
}
<|endoftext|>


<commit_before>package seabird

import (
	"crypto/tls"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"code.google.com/p/go.net/html"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
)

func init() {
	bot.RegisterPlugin("url", NewURLPlugin)
}

// NOTE: This isn't perfect in any sense of the word, but it's pretty close
// and I don't know if it's worth the time to make it better.
var urlRegex = regexp.MustCompile(`https?://[^ ]+`)
var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

// NOTE: This nasty work is done so we ignore invalid ssl certs
var client = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	},
	Timeout: 5 * time.Second,
}

type URLPlugin struct{}

func NewURLPlugin(b *bot.Bot) (bot.Plugin, error) {
	p := &URLPlugin{}

	b.Event("PRIVMSG", p.Msg)
	b.Command("down", "[website]", p.IsItDown)

	return p, nil
}

func (p *URLPlugin) Reload(b *bot.Bot) error {
	// noop
	return nil
}

func (p *URLPlugin) Msg(b *bot.Bot, e *irc.Event) {
	for _, url := range urlRegex.FindAllString(e.Trailing(), -1) {
		go func(url string) {
			r, err := client.Get(url)
			if err != nil {
				return
			}
			defer r.Body.Close()

			if r.StatusCode != 200 {
				return
			}

			// We search the first 1K and if a title isn't in there, we deal with it
			z, err := html.Parse(io.LimitReader(r.Body, 1024*1024))
			if err != nil {
				return
			}

			// DFS that searches the tree for any node named title then
			// returns the data of that node's first child
			var f func(*html.Node) (string, bool)
			f = func(n *html.Node) (string, bool) {
				// If it's an element and it's a title node, look for a child
				if n.Type == html.ElementNode && n.Data == "title" {
					if n.FirstChild != nil {
						t := n.FirstChild.Data
						t = titleRegex.ReplaceAllString(t, " ")
						t = strings.TrimSpace(t)

						if t != "" {
							return t, true
						} else {
							return "", false
						}
					}
				}

				// Loop through all nodes and try recursing
				for c := n.FirstChild; c != nil; c = c.NextSibling {
					if str, ok := f(c); ok {
						return str, true
					}
				}

				return "", false
			}

			if str, ok := f(z); ok {
				b.Reply(e, "Title: %s", str)
			}
		}(url)
	}
}

func (p *URLPlugin) IsItDown(b *bot.Bot, e *irc.Event) {
	go func() {
		url, err := url.Parse(strings.TrimSpace(e.Trailing()))
		if err != nil {
			b.Reply(e, "URL doesn't appear to be valid")
			return
		}

		if url.Scheme == "" {
			url.Scheme = "http"
		}

		r, err := client.Head(url.String())
		if err != nil || r.StatusCode != 200 {
			b.Reply(e, "It's not just you! %s looks down from here.", url)
			return
		}

		b.Reply(e, "It's just you! %s looks up from here!", url)
	}()
}
<commit_msg>Migrate to new official golang repo dirs. Closes #1.<commit_after>package seabird

import (
	"crypto/tls"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/html"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
)

func init() {
	bot.RegisterPlugin("url", NewURLPlugin)
}

// NOTE: This isn't perfect in any sense of the word, but it's pretty close
// and I don't know if it's worth the time to make it better.
var urlRegex = regexp.MustCompile(`https?://[^ ]+`)
var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

// NOTE: This nasty work is done so we ignore invalid ssl certs
var client = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	},
	Timeout: 5 * time.Second,
}

type URLPlugin struct{}

func NewURLPlugin(b *bot.Bot) (bot.Plugin, error) {
	p := &URLPlugin{}

	b.Event("PRIVMSG", p.Msg)
	b.Command("down", "[website]", p.IsItDown)

	return p, nil
}

func (p *URLPlugin) Reload(b *bot.Bot) error {
	// noop
	return nil
}

func (p *URLPlugin) Msg(b *bot.Bot, e *irc.Event) {
	for _, url := range urlRegex.FindAllString(e.Trailing(), -1) {
		go func(url string) {
			r, err := client.Get(url)
			if err != nil {
				return
			}
			defer r.Body.Close()

			if r.StatusCode != 200 {
				return
			}

			// We search the first 1K and if a title isn't in there, we deal with it
			z, err := html.Parse(io.LimitReader(r.Body, 1024*1024))
			if err != nil {
				return
			}

			// DFS that searches the tree for any node named title then
			// returns the data of that node's first child
			var f func(*html.Node) (string, bool)
			f = func(n *html.Node) (string, bool) {
				// If it's an element and it's a title node, look for a child
				if n.Type == html.ElementNode && n.Data == "title" {
					if n.FirstChild != nil {
						t := n.FirstChild.Data
						t = titleRegex.ReplaceAllString(t, " ")
						t = strings.TrimSpace(t)

						if t != "" {
							return t, true
						} else {
							return "", false
						}
					}
				}

				// Loop through all nodes and try recursing
				for c := n.FirstChild; c != nil; c = c.NextSibling {
					if str, ok := f(c); ok {
						return str, true
					}
				}

				return "", false
			}

			if str, ok := f(z); ok {
				b.Reply(e, "Title: %s", str)
			}
		}(url)
	}
}

func (p *URLPlugin) IsItDown(b *bot.Bot, e *irc.Event) {
	go func() {
		url, err := url.Parse(strings.TrimSpace(e.Trailing()))
		if err != nil {
			b.Reply(e, "URL doesn't appear to be valid")
			return
		}

		if url.Scheme == "" {
			url.Scheme = "http"
		}

		r, err := client.Head(url.String())
		if err != nil || r.StatusCode != 200 {
			b.Reply(e, "It's not just you! %s looks down from here.", url)
			return
		}

		b.Reply(e, "It's just you! %s looks up from here!", url)
	}()
}
<|endoftext|>


<commit_before>package utp

import "time"

const (
	version = 1

	st_data  = 0
	st_fin   = 1
	st_state = 2
	st_reset = 3
	st_syn   = 4

	ext_none          = 0
	ext_selective_ack = 1

	header_size = 20
	mtu         = 1400
	mss         = mtu - header_size
	window_size = 100

	reset_timeout = time.Second
)

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<commit_msg>Double MTU<commit_after>package utp

import "time"

const (
	version = 1

	st_data  = 0
	st_fin   = 1
	st_state = 2
	st_reset = 3
	st_syn   = 4

	ext_none          = 0
	ext_selective_ack = 1

	header_size = 20
	mtu         = 2800
	mss         = mtu - header_size
	window_size = 100

	reset_timeout = time.Second
)

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<|endoftext|>