<commit_before>package gocast

import (
	"context"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/sirupsen/logrus"
)

type packetStream struct {
	stream  io.ReadWriteCloser
	packets chan packetContainer
	logger  logrus.FieldLogger
}

type packetContainer struct {
	payload []byte
	err     error
}

func NewPacketStream(stream io.ReadWriteCloser, logger logrus.FieldLogger) *packetStream {
	return &packetStream{
		stream:  stream,
		packets: make(chan packetContainer),
		logger:  logger,
	}
}

func (w *packetStream) readPackets(ctx context.Context) {
	var length uint32

	go func() {
		for {
			if ctx.Err() != nil {
				w.logger.Errorf("closing packetStream reader %s", ctx.Err())
			}
			err := binary.Read(w.stream, binary.BigEndian, &length)
			if err != nil {
				w.logger.Errorf("Failed binary.Read packet: %s", err)
				w.packets <- packetContainer{err: err, payload: nil}
				return
			}

			if length > 0 {
				packet := make([]byte, length)

				i, err := w.stream.Read(packet)
				if err != nil {
					w.logger.Errorf("Failed to read packet: %s", err)
					continue
				}

				if i != int(length) {
					w.logger.Errorf("Invalid packet size. Wanted: %d Read: %d Data: %s", length, i, string(packet))
					continue
				}

				w.packets <- packetContainer{
					payload: packet,
					err:     nil,
				}
			}
		}
	}()
}

func (w *packetStream) Write(data []byte) (int, error) {
	err := binary.Write(w.stream, binary.BigEndian, uint32(len(data)))
	if err != nil {
		err = fmt.Errorf("Failed to write packet length %d. error:%s\n", len(data), err)
		return 0, err
	}

	return w.stream.Write(data)
}
<commit_msg>Fix bug with not reading whole packets<commit_after>package gocast

import (
	"context"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/sirupsen/logrus"
)

type packetStream struct {
	stream  io.ReadWriteCloser
	packets chan packetContainer
	logger  logrus.FieldLogger
}

type packetContainer struct {
	payload []byte
	err     error
}

func NewPacketStream(stream io.ReadWriteCloser, logger logrus.FieldLogger) *packetStream {
	return &packetStream{
		stream:  stream,
		packets: make(chan packetContainer),
		logger:  logger,
	}
}

func (w *packetStream) readPackets(ctx context.Context) {
	var length uint32

	go func() {
		for {
			if ctx.Err() != nil {
				w.logger.Errorf("closing packetStream reader %s", ctx.Err())
			}
			err := binary.Read(w.stream, binary.BigEndian, &length)
			if err != nil {
				w.logger.Errorf("Failed binary.Read packet: %s", err)
				w.packets <- packetContainer{err: err, payload: nil}
				return
			}

			if length > 0 {
				packet := make([]byte, length)

				i, err := io.ReadFull(w.stream, packet)
				if err != nil {
					w.logger.Errorf("Failed to read packet: %s", err)
					continue
				}

				if i != int(length) {
					w.logger.Errorf("Invalid packet size. Wanted: %d Read: %d Data: %s ... (capped)", length, i, string(packet[:500]))
					continue
				}

				w.packets <- packetContainer{
					payload: packet,
					err:     nil,
				}
			}
		}
	}()
}

func (w *packetStream) Write(data []byte) (int, error) {
	err := binary.Write(w.stream, binary.BigEndian, uint32(len(data)))
	if err != nil {
		err = fmt.Errorf("Failed to write packet length %d. error:%s\n", len(data), err)
		return 0, err
	}

	return w.stream.Write(data)
}
<|endoftext|>


<commit_before>package spf

import (
	"errors"
	"net"
)

const spfPrefix = "spf1"

func matchingResult(qualifier tokenType) (SPFResult, error) {
	if !qualifier.isQualifier() {
		return SPFEnd, errors.New("Not a Qualifier")
	}

	var result SPFResult

	switch qualifier {
	case qPlus:
		result = Pass
	case qMinus:
		result = Fail
	case qQuestionMark:
		result = Neutral
	case qTilde:
		result = Softfail
	}
	return result, nil
}

type Parser struct {
	Sender      string
	Domain      string
	Ip          net.IP
	Query       string
	Mechanisms  []*Token
	Explanation *Token
	Redirect    *Token
}

func NewParser(sender, domain string, ip net.IP, query string) *Parser {
	return &Parser{sender, domain, ip, query, make([]*Token, 0, 10), nil, nil}
}

func (p *Parser) Parse() (SPFResult, error) {
	var result SPFResult
	tokens := Lex(p.Query)

	if err := p.sortTokens(tokens); err != nil {
		return Permerror, err
	}

	for _, token := range p.Mechanisms {
		switch token.Mechanism {
		/*
			case tVersion:
				result = p.parseVersion(token)
			case tAll:
				result = p.parseAll(token)
			case tA:
				result = p.parseA(token)
			case tIp4:
				result = p.parseIp4(token)
			case tIp6:
				result = p.parseIp6(token)
			case tMX:
				result = p.parseMX(token)
			case tPTR:
				result = p.parsePTR(token)
			case tInclude:
				result = p.parseInclude(token)
		*/
		}

	}
	return result, nil
}

func (p *Parser) sortTokens(tokens []*Token) error {
	all := false
	for _, token := range tokens {
		if token.Mechanism.isMechanism() && all == false {
			p.Mechanisms = append(p.Mechanisms, token)

			if token.Mechanism == tAll {
				all = true
			}
		} else {

			if token.Mechanism == tRedirect {
				if p.Redirect == nil {
					p.Redirect = token
				} else {
					return errors.New("Modifier redirect musn't appear more than once")
				}
			} else if token.Mechanism == tExp {
				if p.Explanation == nil {
					p.Explanation = token
				} else {
					return errors.New("Modifier exp/explanation musn't appear more than once")
				}
			}
		}
	}

	if all {
		p.Redirect = nil
	}

	return nil
}

func (p *Parser) parseVersion(t *Token) (bool, SPFResult) {
	if t.Value == spfPrefix {
		return true, None
	}
	return true, Permerror
}

func (p *Parser) parseAll(t *Token) (bool, SPFResult) {
	if result, err := matchingResult(t.Qualifier); err != nil {
		return true, Permerror
	} else {
		return true, result
	}
}

func (p *Parser) parseIp4(t *Token) (bool, SPFResult) {
	result, _ := matchingResult(t.Qualifier)

	if _, ipnet, err := net.ParseCIDR(t.Value); err == nil {
		return ipnet.Contains(p.Ip), result
	} else {
		if ip := net.ParseIP(t.Value); ip == nil {
			return true, Permerror
		} else {
			return ip.Equal(p.Ip), result
		}
	}
}
<commit_msg>Terminate parising tokens upon token with tErr<commit_after>package spf

import (
	"errors"
	"net"
)

const spfPrefix = "spf1"

func matchingResult(qualifier tokenType) (SPFResult, error) {
	if !qualifier.isQualifier() {
		return SPFEnd, errors.New("Not a Qualifier")
	}

	var result SPFResult

	switch qualifier {
	case qPlus:
		result = Pass
	case qMinus:
		result = Fail
	case qQuestionMark:
		result = Neutral
	case qTilde:
		result = Softfail
	}
	return result, nil
}

type Parser struct {
	Sender      string
	Domain      string
	Ip          net.IP
	Query       string
	Mechanisms  []*Token
	Explanation *Token
	Redirect    *Token
}

func NewParser(sender, domain string, ip net.IP, query string) *Parser {
	return &Parser{sender, domain, ip, query, make([]*Token, 0, 10), nil, nil}
}

func (p *Parser) Parse() (SPFResult, error) {
	var result SPFResult
	tokens := Lex(p.Query)

	if err := p.sortTokens(tokens); err != nil {
		return Permerror, err
	}

	for _, token := range p.Mechanisms {
		switch token.Mechanism {
		/*
			case tVersion:
				result = p.parseVersion(token)
			case tAll:
				result = p.parseAll(token)
			case tA:
				result = p.parseA(token)
			case tIp4:
				result = p.parseIp4(token)
			case tIp6:
				result = p.parseIp6(token)
			case tMX:
				result = p.parseMX(token)
			case tPTR:
				result = p.parsePTR(token)
			case tInclude:
				result = p.parseInclude(token)
		*/
		}

	}
	return result, nil
}

func (p *Parser) sortTokens(tokens []*Token) error {
	all := false
	for _, token := range tokens {
		if token.Mechanism.isErr() {
			return errors.New("Token syntax error")
		} else if token.Mechanism.isMechanism() && all == false {
			p.Mechanisms = append(p.Mechanisms, token)

			if token.Mechanism == tAll {
				all = true
			}
		} else {

			if token.Mechanism == tRedirect {
				if p.Redirect == nil {
					p.Redirect = token
				} else {
					return errors.New("Modifier redirect musn't appear more than once")
				}
			} else if token.Mechanism == tExp {
				if p.Explanation == nil {
					p.Explanation = token
				} else {
					return errors.New("Modifier exp/explanation musn't appear more than once")
				}
			}
		}
	}

	if all {
		p.Redirect = nil
	}

	return nil
}

func (p *Parser) parseVersion(t *Token) (bool, SPFResult) {
	if t.Value == spfPrefix {
		return true, None
	}
	return true, Permerror
}

func (p *Parser) parseAll(t *Token) (bool, SPFResult) {
	if result, err := matchingResult(t.Qualifier); err != nil {
		return true, Permerror
	} else {
		return true, result
	}
}

func (p *Parser) parseIp4(t *Token) (bool, SPFResult) {
	result, _ := matchingResult(t.Qualifier)

	if _, ipnet, err := net.ParseCIDR(t.Value); err == nil {
		return ipnet.Contains(p.Ip), result
	} else {
		if ip := net.ParseIP(t.Value); ip == nil {
			return true, Permerror
		} else {
			return ip.Equal(p.Ip), result
		}
	}
}
<|endoftext|>


<commit_before>// admin server to handle admin and system messages.
//
// Example server {
//      reqch  := make(chan adminport.Request)
//      server := adminport.NewHTTPServer("projector", "localhost:9999", "/adminport", reqch)
//      server.Register(&protobuf.RequestMessage{})
//
//      loop:
//      for {
//          select {
//          case req, ok := <-reqch:
//              if ok {
//                  msg := req.GetMessage()
//                  // interpret request and compose a response
//                  respMsg := &protobuf.ResponseMessage{}
//                  err := msg.Send(respMsg)
//              } else {
//                  break loop
//              }
//          }
//      }
// }

// TODO: IMPORTANT:
//  Go 1.3 is supposed to have graceful shutdown of http server.
//  Refer https://code.google.com/p/go/issues/detail?id=4674

package adminport

import (
	"encoding/json"
	"fmt"
	c "github.com/couchbase/indexing/secondary/common"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"
)

// httpServer is a concrete type implementing adminport Server interface.
type httpServer struct {
	mu        sync.Mutex   // handle concurrent updates to this object
	lis       net.Listener // TCP listener
	srv       *http.Server // http server
	urlPrefix string       // URL path prefix for adminport
	messages  map[string]MessageMarshaller
	reqch     chan<- Request // request channel back to application

	logPrefix string
	stats     *c.ComponentStat
}

// NewHTTPServer creates an instance of admin-server. Start() will actually
// start the server.
func NewHTTPServer(name, connAddr, urlPrefix string, reqch chan<- Request) Server {
	s := &httpServer{
		reqch:     reqch,
		messages:  make(map[string]MessageMarshaller),
		urlPrefix: urlPrefix,
		logPrefix: fmt.Sprintf("[%s:%s]", name, connAddr),
	}
	mux := http.NewServeMux()
	mux.HandleFunc(s.urlPrefix, s.systemHandler)
	s.srv = &http.Server{
		Addr:           connAddr,
		Handler:        mux,
		ReadTimeout:    c.AdminportReadTimeout * time.Millisecond,
		WriteTimeout:   c.AdminportWriteTimeout * time.Millisecond,
		MaxHeaderBytes: 1 << 20,
	}
	return s
}

// Register is part of Server interface.
func (s *httpServer) Register(msg MessageMarshaller) (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		return ErrorRegisteringRequest
	}
	key := fmt.Sprintf("%v%v", s.urlPrefix, msg.Name())
	s.messages[key] = msg
	c.Infof("%s registered %s\n", s.logPrefix, s.getURL(msg))
	return
}

// Unregister is part of Server interface.
func (s *httpServer) Unregister(msg MessageMarshaller) (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		return ErrorRegisteringRequest
	}
	name := msg.Name()
	if s.messages[name] == nil {
		return ErrorMessageUnknown
	}
	delete(s.messages, name)
	c.Infof("%s unregistered %s\n", s.logPrefix, s.getURL(msg))
	return
}

// Start is part of Server interface.
func (s *httpServer) Start() (err error) {
	s.stats = s.newStats() // initialize statistics

	if s.lis, err = net.Listen("tcp", s.srv.Addr); err != nil {
		return err
	}

	// Server routine
	go func() {
		defer s.shutdown()

		c.Infof("%s starting ...\n", s.logPrefix)
		err := s.srv.Serve(s.lis) // serve until listener is closed.
		if err != nil {
			c.Errorf("%s %v\n", s.logPrefix, err)
		}
	}()
	return
}

func (s *httpServer) GetStatistics() *c.ComponentStat {
	return s.stats
}

// Stop is part of Server interface.
func (s *httpServer) Stop() {
	s.shutdown()
	c.Infof("%s ... stopped\n", s.logPrefix)
}

func (s *httpServer) shutdown() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		s.lis.Close()
		close(s.reqch)
		s.lis = nil
	}
}

func (s *httpServer) getURL(msg MessageMarshaller) string {
	return s.urlPrefix + msg.Name()
}

// handle incoming request.
func (s *httpServer) systemHandler(w http.ResponseWriter, r *http.Request) {
	var err error
	var statPath string

	c.Infof("%s Request %q\n", s.logPrefix, r.URL.Path)

	// Fault-tolerance. No need to crash the server in case of panic.
	defer func() {
		var rsCount, erCount int

		if r := recover(); r != nil {
			c.Errorf("%s, adminport.request.recovered `%v`\n", s.logPrefix, r)
			erCount = 1 // count error
		} else if err != nil {
			c.Errorf("%s %v\n", s.logPrefix, err)
			rsCount, erCount = 1, 1 // count response&error
		} else {
			rsCount = 1 // count response
		}
		if statPath != "" {
			s.stats.Incr(statPath, 0, rsCount, erCount)
		}
	}()

	var msg MessageMarshaller

	// check wether it is for stats.
	prefix := c.StatsURLPath(s.urlPrefix, "")
	if strings.HasPrefix(r.URL.Path, prefix) {
		msg = &c.ComponentStat{}
	} else {
		msg = s.messages[r.URL.Path]
		data := make([]byte, r.ContentLength)
		if err := requestRead(r.Body, data); err != nil {
			err = fmt.Errorf("%v, %v", ErrorRequest, err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		// Get an instance of request type and decode request into that.
		typeOfMsg := reflect.ValueOf(msg).Elem().Type()
		msg = reflect.New(typeOfMsg).Interface().(MessageMarshaller)
		if err = msg.Decode(data); err != nil {
			err = fmt.Errorf("%v, %v", ErrorDecodeRequest, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	statPath = "/request." + msg.Name()
	s.stats.Incr(statPath, 1, 0, 0) // count request

	if msg == nil {
		err = ErrorPathNotFound
		http.Error(w, "path not found", http.StatusNotFound)
		return
	}

	waitch := make(chan interface{}, 1)
	// send and wait
	s.reqch <- &httpAdminRequest{srv: s, msg: msg, waitch: waitch}
	val := <-waitch

	switch v := (val).(type) {
	case *c.ComponentStat:
		val = v.Get(c.ParseStatsPath(r.URL.Path))
		if data, err := json.Marshal(&val); err == nil {
			header := w.Header()
			// TODO: no magic
			header["Content-Type"] = []string{"application/json"}
			w.Write(data)
			s.stats.Incr("/payload", 0, len(data))
		} else {
			err = fmt.Errorf("%v, %v", ErrorDecodeRequest, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			c.Errorf("%v, %v", s.logPrefix, err)
		}

	case MessageMarshaller:
		if data, err := v.Encode(); err == nil {
			header := w.Header()
			header["Content-Type"] = []string{v.ContentType()}
			w.Write(data)
			s.stats.Incr("/payload", 0, len(data))
		} else {
			err = fmt.Errorf("%v, %v", ErrorEncodeResponse, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			c.Errorf("%v, %v", s.logPrefix, err)
		}

	case error:
		http.Error(w, v.Error(), http.StatusInternalServerError)
		err = fmt.Errorf("%v, %v", ErrorInternal, v)
		c.Errorf("%v, %v", s.logPrefix, err)
	}
}

func requestRead(r io.Reader, data []byte) error {
	n, start := len(data), 0
	for n > 0 {
		c, err := r.Read(data[start:])
		//Per http://golang.org/pkg/io/#Reader, it is valid for Read to
		//return EOF with non-zero number of bytes at the end of the
		//input stream
		if err != nil && err != io.EOF {
			return err
		}
		n -= c
		start += c
	}
	return nil
}

// concrete type implementing Request interface
type httpAdminRequest struct {
	srv    *httpServer
	msg    MessageMarshaller
	waitch chan interface{}
}

// GetMessage is part of Request interface.
func (r *httpAdminRequest) GetMessage() MessageMarshaller {
	return r.msg
}

// Send is part of Request interface.
func (r *httpAdminRequest) Send(msg MessageMarshaller) error {
	r.waitch <- msg
	close(r.waitch)
	return nil
}

// SendError is part of Request interface.
func (r *httpAdminRequest) SendError(err error) error {
	r.waitch <- err
	close(r.waitch)
	return nil
}
<commit_msg>Bugfix: implementing Siri's review comment.<commit_after>// admin server to handle admin and system messages.
//
// Example server {
//      reqch  := make(chan adminport.Request)
//      server := adminport.NewHTTPServer("projector", "localhost:9999", "/adminport", reqch)
//      server.Register(&protobuf.RequestMessage{})
//
//      loop:
//      for {
//          select {
//          case req, ok := <-reqch:
//              if ok {
//                  msg := req.GetMessage()
//                  // interpret request and compose a response
//                  respMsg := &protobuf.ResponseMessage{}
//                  err := msg.Send(respMsg)
//              } else {
//                  break loop
//              }
//          }
//      }
// }

// TODO: IMPORTANT:
//  Go 1.3 is supposed to have graceful shutdown of http server.
//  Refer https://code.google.com/p/go/issues/detail?id=4674

package adminport

import (
	"encoding/json"
	"fmt"
	c "github.com/couchbase/indexing/secondary/common"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"
)

// httpServer is a concrete type implementing adminport Server interface.
type httpServer struct {
	mu        sync.Mutex   // handle concurrent updates to this object
	lis       net.Listener // TCP listener
	srv       *http.Server // http server
	urlPrefix string       // URL path prefix for adminport
	messages  map[string]MessageMarshaller
	reqch     chan<- Request // request channel back to application

	logPrefix string
	stats     *c.ComponentStat
}

// NewHTTPServer creates an instance of admin-server. Start() will actually
// start the server.
func NewHTTPServer(name, connAddr, urlPrefix string, reqch chan<- Request) Server {
	s := &httpServer{
		reqch:     reqch,
		messages:  make(map[string]MessageMarshaller),
		urlPrefix: urlPrefix,
		logPrefix: fmt.Sprintf("[%s:%s]", name, connAddr),
	}
	mux := http.NewServeMux()
	mux.HandleFunc(s.urlPrefix, s.systemHandler)
	s.srv = &http.Server{
		Addr:           connAddr,
		Handler:        mux,
		ReadTimeout:    c.AdminportReadTimeout * time.Millisecond,
		WriteTimeout:   c.AdminportWriteTimeout * time.Millisecond,
		MaxHeaderBytes: 1 << 20,
	}
	return s
}

// Register is part of Server interface.
func (s *httpServer) Register(msg MessageMarshaller) (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		return ErrorRegisteringRequest
	}
	key := fmt.Sprintf("%v%v", s.urlPrefix, msg.Name())
	s.messages[key] = msg
	c.Infof("%s registered %s\n", s.logPrefix, s.getURL(msg))
	return
}

// Unregister is part of Server interface.
func (s *httpServer) Unregister(msg MessageMarshaller) (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		return ErrorRegisteringRequest
	}
	name := msg.Name()
	if s.messages[name] == nil {
		return ErrorMessageUnknown
	}
	delete(s.messages, name)
	c.Infof("%s unregistered %s\n", s.logPrefix, s.getURL(msg))
	return
}

// Start is part of Server interface.
func (s *httpServer) Start() (err error) {
	s.stats = s.newStats() // initialize statistics

	if s.lis, err = net.Listen("tcp", s.srv.Addr); err != nil {
		return err
	}

	// Server routine
	go func() {
		defer s.shutdown()

		c.Infof("%s starting ...\n", s.logPrefix)
		err := s.srv.Serve(s.lis) // serve until listener is closed.
		if err != nil {
			c.Errorf("%s %v\n", s.logPrefix, err)
		}
	}()
	return
}

func (s *httpServer) GetStatistics() *c.ComponentStat {
	return s.stats
}

// Stop is part of Server interface.
func (s *httpServer) Stop() {
	s.shutdown()
	c.Infof("%s ... stopped\n", s.logPrefix)
}

func (s *httpServer) shutdown() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lis != nil {
		s.lis.Close()
		close(s.reqch)
		s.lis = nil
	}
}

func (s *httpServer) getURL(msg MessageMarshaller) string {
	return s.urlPrefix + msg.Name()
}

// handle incoming request.
func (s *httpServer) systemHandler(w http.ResponseWriter, r *http.Request) {
	var err error
	var statPath string

	c.Infof("%s Request %q\n", s.logPrefix, r.URL.Path)

	// Fault-tolerance. No need to crash the server in case of panic.
	defer func() {
		var rsCount, erCount int

		if r := recover(); r != nil {
			c.Errorf("%s, adminport.request.recovered `%v`\n", s.logPrefix, r)
			erCount = 1 // count error
		} else if err != nil {
			c.Errorf("%s %v\n", s.logPrefix, err)
			rsCount, erCount = 1, 1 // count response&error
		} else {
			rsCount = 1 // count response
		}
		if statPath != "" {
			s.stats.Incr(statPath, 0, rsCount, erCount)
		}
	}()

	var msg MessageMarshaller

	// check wether it is for stats.
	prefix := c.StatsURLPath(s.urlPrefix, "")
	if strings.HasPrefix(r.URL.Path, prefix) {
		msg = &c.ComponentStat{}
	} else {
		msg = s.messages[r.URL.Path]
		data := make([]byte, r.ContentLength)
		if err := requestRead(r.Body, data); err != nil {
			err = fmt.Errorf("%v, %v", ErrorRequest, err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		// Get an instance of request type and decode request into that.
		typeOfMsg := reflect.ValueOf(msg).Elem().Type()
		msg = reflect.New(typeOfMsg).Interface().(MessageMarshaller)
		if err = msg.Decode(data); err != nil {
			err = fmt.Errorf("%v, %v", ErrorDecodeRequest, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	statPath = "/request." + msg.Name()
	s.stats.Incr(statPath, 1, 0, 0) // count request

	if msg == nil {
		err = ErrorPathNotFound
		http.Error(w, "path not found", http.StatusNotFound)
		return
	}

	waitch := make(chan interface{}, 1)
	// send and wait
	s.reqch <- &httpAdminRequest{srv: s, msg: msg, waitch: waitch}
	val := <-waitch

	switch v := (val).(type) {
	case *c.ComponentStat:
		val = v.Get(c.ParseStatsPath(r.URL.Path))
		if data, err := json.Marshal(&val); err == nil {
			header := w.Header()
			// TODO: no magic
			header["Content-Type"] = []string{"application/json"}
			w.Write(data)
			s.stats.Incr("/payload", 0, len(data))
		} else {
			err = fmt.Errorf("%v, %v", ErrorDecodeRequest, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			c.Errorf("%v, %v", s.logPrefix, err)
		}

	case MessageMarshaller:
		if data, err := v.Encode(); err == nil {
			header := w.Header()
			header["Content-Type"] = []string{v.ContentType()}
			w.Write(data)
			s.stats.Incr("/payload", 0, len(data))
		} else {
			err = fmt.Errorf("%v, %v", ErrorEncodeResponse, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			c.Errorf("%v, %v", s.logPrefix, err)
		}

	case error:
		http.Error(w, v.Error(), http.StatusInternalServerError)
		err = fmt.Errorf("%v, %v", ErrorInternal, v)
		c.Errorf("%v, %v", s.logPrefix, err)
	}
}

func requestRead(r io.Reader, data []byte) (err error) {
	var c int

	n, start := len(data), 0
	for n > 0 && err == nil {
		// Per http://golang.org/pkg/io/#Reader, it is valid for Read to
		// return EOF with non-zero number of bytes at the end of the
		// input stream
		c, err = r.Read(data[start:])
		n -= c
		start += c
	}
	if n == 0 {
		return nil
	}
	return err
}

// concrete type implementing Request interface
type httpAdminRequest struct {
	srv    *httpServer
	msg    MessageMarshaller
	waitch chan interface{}
}

// GetMessage is part of Request interface.
func (r *httpAdminRequest) GetMessage() MessageMarshaller {
	return r.msg
}

// Send is part of Request interface.
func (r *httpAdminRequest) Send(msg MessageMarshaller) error {
	r.waitch <- msg
	close(r.waitch)
	return nil
}

// SendError is part of Request interface.
func (r *httpAdminRequest) SendError(err error) error {
	r.waitch <- err
	close(r.waitch)
	return nil
}
<|endoftext|>


<commit_before>package netlink

import (
	"fmt"
	"net"
	"syscall"

	"github.com/vishvananda/netlink/nl"
)

// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func RuleAdd(rule *Rule) error {
	return pkgHandle.RuleAdd(rule)
}

// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func (h *Handle) RuleAdd(rule *Rule) error {
	req := h.newNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return ruleHandle(rule, req)
}

// RuleDel deletes a rule from the system.
// Equivalent to: ip rule del
func RuleDel(rule *Rule) error {
	return pkgHandle.RuleDel(rule)
}

// RuleDel deletes a rule from the system.
// Equivalent to: ip rule del
func (h *Handle) RuleDel(rule *Rule) error {
	req := h.newNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return ruleHandle(rule, req)
}

func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
	msg := nl.NewRtMsg()
	msg.Family = syscall.AF_INET
	var dstFamily uint8

	var rtAttrs []*nl.RtAttr
	if rule.Dst != nil && rule.Dst.IP != nil {
		dstLen, _ := rule.Dst.Mask.Size()
		msg.Dst_len = uint8(dstLen)
		msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
		dstFamily = msg.Family
		var dstData []byte
		if msg.Family == syscall.AF_INET {
			dstData = rule.Dst.IP.To4()
		} else {
			dstData = rule.Dst.IP.To16()
		}
		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
	}

	if rule.Src != nil && rule.Src.IP != nil {
		msg.Family = uint8(nl.GetIPFamily(rule.Src.IP))
		if dstFamily != 0 && dstFamily != msg.Family {
			return fmt.Errorf("source and destination ip are not the same IP family")
		}
		srcLen, _ := rule.Src.Mask.Size()
		msg.Src_len = uint8(srcLen)
		var srcData []byte
		if msg.Family == syscall.AF_INET {
			srcData = rule.Src.IP.To4()
		} else {
			srcData = rule.Src.IP.To16()
		}
		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData))
	}

	if rule.Table >= 0 {
		msg.Table = uint8(rule.Table)
		if rule.Table >= 256 {
			msg.Table = syscall.RT_TABLE_UNSPEC
		}
	}

	req.AddData(msg)
	for i := range rtAttrs {
		req.AddData(rtAttrs[i])
	}

	var (
		b      = make([]byte, 4)
		native = nl.NativeEndian()
	)

	if rule.Priority >= 0 {
		native.PutUint32(b, uint32(rule.Priority))
		req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
	}
	if rule.Mark >= 0 {
		native.PutUint32(b, uint32(rule.Mark))
		req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
	}
	if rule.Mask >= 0 {
		native.PutUint32(b, uint32(rule.Mask))
		req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
	}
	if rule.Flow >= 0 {
		native.PutUint32(b, uint32(rule.Flow))
		req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
	}
	if rule.TunID > 0 {
		native.PutUint32(b, uint32(rule.TunID))
		req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
	}
	if rule.Table >= 256 {
		native.PutUint32(b, uint32(rule.Table))
		req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
	}
	if msg.Table > 0 {
		if rule.SuppressPrefixlen >= 0 {
			native.PutUint32(b, uint32(rule.SuppressPrefixlen))
			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
		}
		if rule.SuppressIfgroup >= 0 {
			native.PutUint32(b, uint32(rule.SuppressIfgroup))
			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
		}
	}
	if rule.IifName != "" {
		req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
	}
	if rule.OifName != "" {
		req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
	}
	if rule.Goto >= 0 {
		msg.Type = nl.FR_ACT_NOP
		native.PutUint32(b, uint32(rule.Goto))
		req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
	}

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

// RuleList lists rules in the system.
// Equivalent to: ip rule list
func RuleList(family int) ([]Rule, error) {
	return pkgHandle.RuleList(family)
}

// RuleList lists rules in the system.
// Equivalent to: ip rule list
func (h *Handle) RuleList(family int) ([]Rule, error) {
	req := h.newNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
	msg := nl.NewIfInfomsg(family)
	req.AddData(msg)

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

	native := nl.NativeEndian()
	var res = make([]Rule, 0)
	for i := range msgs {
		msg := nl.DeserializeRtMsg(msgs[i])
		attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():])
		if err != nil {
			return nil, err
		}

		rule := NewRule()

		for j := range attrs {
			switch attrs[j].Attr.Type {
			case syscall.RTA_TABLE:
				rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_SRC:
				rule.Src = &net.IPNet{
					IP:   attrs[j].Value,
					Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)),
				}
			case nl.FRA_DST:
				rule.Dst = &net.IPNet{
					IP:   attrs[j].Value,
					Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)),
				}
			case nl.FRA_FWMARK:
				rule.Mark = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_FWMASK:
				rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_TUN_ID:
				rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
			case nl.FRA_IIFNAME:
				rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
			case nl.FRA_OIFNAME:
				rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
			case nl.FRA_SUPPRESS_PREFIXLEN:
				i := native.Uint32(attrs[j].Value[0:4])
				if i != 0xffffffff {
					rule.SuppressPrefixlen = int(i)
				}
			case nl.FRA_SUPPRESS_IFGROUP:
				i := native.Uint32(attrs[j].Value[0:4])
				if i != 0xffffffff {
					rule.SuppressIfgroup = int(i)
				}
			case nl.FRA_FLOW:
				rule.Flow = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_GOTO:
				rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_PRIORITY:
				rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
			}
		}
		res = append(res, *rule)
	}

	return res, nil
}
<commit_msg>Fix bug in ruleHandle: allocate different buffers for each rtattr<commit_after>package netlink

import (
	"fmt"
	"net"
	"syscall"

	"github.com/vishvananda/netlink/nl"
)

// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func RuleAdd(rule *Rule) error {
	return pkgHandle.RuleAdd(rule)
}

// RuleAdd adds a rule to the system.
// Equivalent to: ip rule add
func (h *Handle) RuleAdd(rule *Rule) error {
	req := h.newNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return ruleHandle(rule, req)
}

// RuleDel deletes a rule from the system.
// Equivalent to: ip rule del
func RuleDel(rule *Rule) error {
	return pkgHandle.RuleDel(rule)
}

// RuleDel deletes a rule from the system.
// Equivalent to: ip rule del
func (h *Handle) RuleDel(rule *Rule) error {
	req := h.newNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return ruleHandle(rule, req)
}

func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
	msg := nl.NewRtMsg()
	msg.Family = syscall.AF_INET
	var dstFamily uint8

	var rtAttrs []*nl.RtAttr
	if rule.Dst != nil && rule.Dst.IP != nil {
		dstLen, _ := rule.Dst.Mask.Size()
		msg.Dst_len = uint8(dstLen)
		msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
		dstFamily = msg.Family
		var dstData []byte
		if msg.Family == syscall.AF_INET {
			dstData = rule.Dst.IP.To4()
		} else {
			dstData = rule.Dst.IP.To16()
		}
		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
	}

	if rule.Src != nil && rule.Src.IP != nil {
		msg.Family = uint8(nl.GetIPFamily(rule.Src.IP))
		if dstFamily != 0 && dstFamily != msg.Family {
			return fmt.Errorf("source and destination ip are not the same IP family")
		}
		srcLen, _ := rule.Src.Mask.Size()
		msg.Src_len = uint8(srcLen)
		var srcData []byte
		if msg.Family == syscall.AF_INET {
			srcData = rule.Src.IP.To4()
		} else {
			srcData = rule.Src.IP.To16()
		}
		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData))
	}

	if rule.Table >= 0 {
		msg.Table = uint8(rule.Table)
		if rule.Table >= 256 {
			msg.Table = syscall.RT_TABLE_UNSPEC
		}
	}

	req.AddData(msg)
	for i := range rtAttrs {
		req.AddData(rtAttrs[i])
	}

	native := nl.NativeEndian()

	if rule.Priority >= 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Priority))
		req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
	}
	if rule.Mark >= 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Mark))
		req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
	}
	if rule.Mask >= 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Mask))
		req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
	}
	if rule.Flow >= 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Flow))
		req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
	}
	if rule.TunID > 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.TunID))
		req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
	}
	if rule.Table >= 256 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Table))
		req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
	}
	if msg.Table > 0 {
		if rule.SuppressPrefixlen >= 0 {
			b := make([]byte, 4)
			native.PutUint32(b, uint32(rule.SuppressPrefixlen))
			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
		}
		if rule.SuppressIfgroup >= 0 {
			b := make([]byte, 4)
			native.PutUint32(b, uint32(rule.SuppressIfgroup))
			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
		}
	}
	if rule.IifName != "" {
		req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
	}
	if rule.OifName != "" {
		req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
	}
	if rule.Goto >= 0 {
		msg.Type = nl.FR_ACT_NOP
		b := make([]byte, 4)
		native.PutUint32(b, uint32(rule.Goto))
		req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
	}

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

// RuleList lists rules in the system.
// Equivalent to: ip rule list
func RuleList(family int) ([]Rule, error) {
	return pkgHandle.RuleList(family)
}

// RuleList lists rules in the system.
// Equivalent to: ip rule list
func (h *Handle) RuleList(family int) ([]Rule, error) {
	req := h.newNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
	msg := nl.NewIfInfomsg(family)
	req.AddData(msg)

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

	native := nl.NativeEndian()
	var res = make([]Rule, 0)
	for i := range msgs {
		msg := nl.DeserializeRtMsg(msgs[i])
		attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():])
		if err != nil {
			return nil, err
		}

		rule := NewRule()

		for j := range attrs {
			switch attrs[j].Attr.Type {
			case syscall.RTA_TABLE:
				rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_SRC:
				rule.Src = &net.IPNet{
					IP:   attrs[j].Value,
					Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)),
				}
			case nl.FRA_DST:
				rule.Dst = &net.IPNet{
					IP:   attrs[j].Value,
					Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)),
				}
			case nl.FRA_FWMARK:
				rule.Mark = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_FWMASK:
				rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_TUN_ID:
				rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
			case nl.FRA_IIFNAME:
				rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
			case nl.FRA_OIFNAME:
				rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
			case nl.FRA_SUPPRESS_PREFIXLEN:
				i := native.Uint32(attrs[j].Value[0:4])
				if i != 0xffffffff {
					rule.SuppressPrefixlen = int(i)
				}
			case nl.FRA_SUPPRESS_IFGROUP:
				i := native.Uint32(attrs[j].Value[0:4])
				if i != 0xffffffff {
					rule.SuppressIfgroup = int(i)
				}
			case nl.FRA_FLOW:
				rule.Flow = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_GOTO:
				rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
			case nl.FRA_PRIORITY:
				rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
			}
		}
		res = append(res, *rule)
	}

	return res, nil
}
<|endoftext|>


<commit_before>package rules

import (
	"strings"
	"sync"
	"time"

	"github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

type baseReadAPI struct {
	cancelFunc context.CancelFunc
}

func (bra *baseReadAPI) getContext() context.Context {
	var ctx context.Context
	ctx, bra.cancelFunc = context.WithTimeout(context.Background(), time.Duration(60)*time.Second)
	return ctx
}

func (bra *baseReadAPI) cancel() {
	bra.cancelFunc()
}

type etcdReadAPI struct {
	baseReadAPI
	keysAPI client.KeysAPI
}

func (edra *etcdReadAPI) get(key string) (*string, error) {
	ctx := edra.getContext()
	defer edra.cancel()
	resp, err := edra.keysAPI.Get(ctx, key, nil)
	if err != nil {
		if !strings.HasPrefix(err.Error(), "100") {
			return nil, err
		}
		return nil, nil
	}
	return &resp.Node.Value, nil
}

type etcdV3ReadAPI struct {
	baseReadAPI
	kV clientv3.KV
}

func (edv3ra *etcdV3ReadAPI) get(key string) (*string, error) {
	ctx := edv3ra.baseReadAPI.getContext()
	defer edv3ra.cancel()
	resp, err := edv3ra.kV.Get(ctx, key)
	if err != nil {
		return nil, err
	}
	if resp.Count == 0 {
		return nil, nil
	}
	val := string(resp.Kvs[0].Value)
	return &val, nil
}

type keyWatcher interface {
	next() (string, *string, error)
	cancel()
}

func newEtcdKeyWatcher(api client.KeysAPI, prefix string, timeout time.Duration) keyWatcher {
	w := api.Watcher(prefix, &client.WatcherOptions{
		Recursive: true,
	})
	watcher := etcdKeyWatcher{
		baseKeyWatcher: baseKeyWatcher{
			prefix:  prefix,
			timeout: timeout,
		},
		api: api,
		w:   w,
	}
	return &watcher
}

func newEtcdV3KeyWatcher(watcher clientv3.Watcher, prefix string, timeout time.Duration) *etcdV3KeyWatcher {
	kw := etcdV3KeyWatcher{
		baseKeyWatcher: baseKeyWatcher{
			prefix:  prefix,
			timeout: timeout,
		},
		w: watcher,
	}
	return &kw
}

type baseKeyWatcher struct {
	cancelFunc  context.CancelFunc
	cancelMutex sync.Mutex
	prefix      string
	timeout     time.Duration
	stopping    uint32
}

func (bkw *baseKeyWatcher) getContext() context.Context {
	ctx := context.Background()
	bkw.cancelMutex.Lock()
	defer bkw.cancelMutex.Unlock()
	if bkw.timeout > 0 {
		ctx, bkw.cancelFunc = context.WithTimeout(ctx, bkw.timeout)
	} else {
		ctx, bkw.cancelFunc = context.WithCancel(ctx)
	}
	ctx = SetMethod(ctx, "watcher")
	return ctx
}

type etcdKeyWatcher struct {
	baseKeyWatcher
	api client.KeysAPI
	w   client.Watcher
}

func (ekw *etcdKeyWatcher) next() (string, *string, error) {
	resp, err := ekw.w.Next(ekw.getContext())
	if err != nil {
		// Get a new watcher to clear the event index
		ekw.w = ekw.api.Watcher(ekw.prefix, &client.WatcherOptions{
			Recursive: true,
		})
		return "", nil, err
	}
	node := resp.Node
	if resp.Action == "delete" || resp.Action == "expire" {
		return node.Key, nil, nil
	}
	return node.Key, &node.Value, nil
}

func (bkw *baseKeyWatcher) cancel() {
	atomicSet(&bkw.stopping, true)
	bkw.cancelMutex.Lock()
	defer bkw.cancelMutex.Unlock()
	if bkw.cancelFunc != nil {
		bkw.cancelFunc()
		bkw.cancelFunc = nil
	}
}

type etcdV3KeyWatcher struct {
	baseKeyWatcher
	ch         clientv3.WatchChan
	eventIndex int
	events     []*clientv3.Event
	w          clientv3.Watcher
}

func (ev3kw *etcdV3KeyWatcher) next() (string, *string, error) {
	if is(&ev3kw.stopping) {
		return "", nil, nil
	}
	if ev3kw.ch == nil {
		ev3kw.ch = ev3kw.w.Watch(ev3kw.getContext(), ev3kw.prefix, clientv3.WithPrefix())
	}
	if ev3kw.events == nil {
		ev3kw.eventIndex = 0
		wr := <-ev3kw.ch
		ev3kw.events = wr.Events
	}
	if len(ev3kw.events) == 0 {
		ev3kw.events = nil
		// This avoids a potential endless loop due to a closed channel
		ev3kw.ch = nil
		return ev3kw.next()
	}
	event := ev3kw.events[ev3kw.eventIndex]
	ev3kw.eventIndex = ev3kw.eventIndex + 1
	if ev3kw.eventIndex >= len(ev3kw.events) {
		ev3kw.events = nil
	}
	key := string(event.Kv.Key[:])
	if event.Type == clientv3.EventTypeDelete { // Expire?
		return key, nil, nil
	}
	val := string(event.Kv.Value[:])
	return key, &val, nil
}
<commit_msg>Set method for rule read API for metrics<commit_after>package rules

import (
	"strings"
	"sync"
	"time"

	"github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

type baseReadAPI struct {
	cancelFunc context.CancelFunc
}

func (bra *baseReadAPI) getContext() context.Context {
	var ctx context.Context
	ctx, bra.cancelFunc = context.WithTimeout(context.Background(), time.Duration(60)*time.Second)
	ctx = SetMethod(ctx, "rule_eval")
	return ctx
}

func (bra *baseReadAPI) cancel() {
	bra.cancelFunc()
}

type etcdReadAPI struct {
	baseReadAPI
	keysAPI client.KeysAPI
}

func (edra *etcdReadAPI) get(key string) (*string, error) {
	ctx := edra.getContext()
	defer edra.cancel()
	resp, err := edra.keysAPI.Get(ctx, key, nil)
	if err != nil {
		if !strings.HasPrefix(err.Error(), "100") {
			return nil, err
		}
		return nil, nil
	}
	return &resp.Node.Value, nil
}

type etcdV3ReadAPI struct {
	baseReadAPI
	kV clientv3.KV
}

func (edv3ra *etcdV3ReadAPI) get(key string) (*string, error) {
	ctx := edv3ra.baseReadAPI.getContext()
	defer edv3ra.cancel()
	resp, err := edv3ra.kV.Get(ctx, key)
	if err != nil {
		return nil, err
	}
	if resp.Count == 0 {
		return nil, nil
	}
	val := string(resp.Kvs[0].Value)
	return &val, nil
}

type keyWatcher interface {
	next() (string, *string, error)
	cancel()
}

func newEtcdKeyWatcher(api client.KeysAPI, prefix string, timeout time.Duration) keyWatcher {
	w := api.Watcher(prefix, &client.WatcherOptions{
		Recursive: true,
	})
	watcher := etcdKeyWatcher{
		baseKeyWatcher: baseKeyWatcher{
			prefix:  prefix,
			timeout: timeout,
		},
		api: api,
		w:   w,
	}
	return &watcher
}

func newEtcdV3KeyWatcher(watcher clientv3.Watcher, prefix string, timeout time.Duration) *etcdV3KeyWatcher {
	kw := etcdV3KeyWatcher{
		baseKeyWatcher: baseKeyWatcher{
			prefix:  prefix,
			timeout: timeout,
		},
		w: watcher,
	}
	return &kw
}

type baseKeyWatcher struct {
	cancelFunc  context.CancelFunc
	cancelMutex sync.Mutex
	prefix      string
	timeout     time.Duration
	stopping    uint32
}

func (bkw *baseKeyWatcher) getContext() context.Context {
	ctx := context.Background()
	bkw.cancelMutex.Lock()
	defer bkw.cancelMutex.Unlock()
	if bkw.timeout > 0 {
		ctx, bkw.cancelFunc = context.WithTimeout(ctx, bkw.timeout)
	} else {
		ctx, bkw.cancelFunc = context.WithCancel(ctx)
	}
	ctx = SetMethod(ctx, "watcher")
	return ctx
}

type etcdKeyWatcher struct {
	baseKeyWatcher
	api client.KeysAPI
	w   client.Watcher
}

func (ekw *etcdKeyWatcher) next() (string, *string, error) {
	resp, err := ekw.w.Next(ekw.getContext())
	if err != nil {
		// Get a new watcher to clear the event index
		ekw.w = ekw.api.Watcher(ekw.prefix, &client.WatcherOptions{
			Recursive: true,
		})
		return "", nil, err
	}
	node := resp.Node
	if resp.Action == "delete" || resp.Action == "expire" {
		return node.Key, nil, nil
	}
	return node.Key, &node.Value, nil
}

func (bkw *baseKeyWatcher) cancel() {
	atomicSet(&bkw.stopping, true)
	bkw.cancelMutex.Lock()
	defer bkw.cancelMutex.Unlock()
	if bkw.cancelFunc != nil {
		bkw.cancelFunc()
		bkw.cancelFunc = nil
	}
}

type etcdV3KeyWatcher struct {
	baseKeyWatcher
	ch         clientv3.WatchChan
	eventIndex int
	events     []*clientv3.Event
	w          clientv3.Watcher
}

func (ev3kw *etcdV3KeyWatcher) next() (string, *string, error) {
	if is(&ev3kw.stopping) {
		return "", nil, nil
	}
	if ev3kw.ch == nil {
		ev3kw.ch = ev3kw.w.Watch(ev3kw.getContext(), ev3kw.prefix, clientv3.WithPrefix())
	}
	if ev3kw.events == nil {
		ev3kw.eventIndex = 0
		wr := <-ev3kw.ch
		ev3kw.events = wr.Events
	}
	if len(ev3kw.events) == 0 {
		ev3kw.events = nil
		// This avoids a potential endless loop due to a closed channel
		ev3kw.ch = nil
		return ev3kw.next()
	}
	event := ev3kw.events[ev3kw.eventIndex]
	ev3kw.eventIndex = ev3kw.eventIndex + 1
	if ev3kw.eventIndex >= len(ev3kw.events) {
		ev3kw.events = nil
	}
	key := string(event.Kv.Key[:])
	if event.Type == clientv3.EventTypeDelete { // Expire?
		return key, nil, nil
	}
	val := string(event.Kv.Value[:])
	return key, &val, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. 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.
package sippy

import (
    "sync"
    "time"

    "sippy/conf"
    "sippy/headers"
    "sippy/time"
    "sippy/types"
)

type clientTransaction struct {
    *baseTransaction
    teB             *Timeout
    teC             *Timeout
    teG             *Timeout
    r408            sippy_types.SipResponse
    resp_receiver   sippy_types.ResponseReceiver
    expires         time.Duration
    ack             sippy_types.SipRequest
    outbound_proxy  *sippy_conf.HostPort
    cancel          sippy_types.SipRequest
    cancelPending   bool
    session_lock    sync.Locker
    lock            sync.Mutex
    uack            bool
    ack_rAddr       *sippy_conf.HostPort
    ack_checksum    string
    ua              sippy_types.UA
}

func NewClientTransaction(req sippy_types.SipRequest, tid *sippy_header.TID, userv sippy_types.UdpServer, data []byte, sip_tm *sipTransactionManager, resp_receiver sippy_types.ResponseReceiver, session_lock sync.Locker, address *sippy_conf.HostPort, ua sippy_types.UA) sippy_types.ClientTransaction {
    var r408 sippy_types.SipResponse = nil
    if resp_receiver != nil {
        r408 = req.GenResponse(408, "Request Timeout", /*body*/ nil, /*server*/ nil)
    }
    expires := 300 * time.Second
    if req.GetExpires() != nil && req.GetExpires().Number > 0 {
        expires = time.Duration(req.GetExpires().Number) * time.Second
    }
    needack := false
    var ack, cancel sippy_types.SipRequest
    if req.GetMethod() == "INVITE" {
        needack = true
        ack = req.GenACK(nil, sip_tm.config)
        cancel = req.GenCANCEL(sip_tm.config)
    }
    self := &clientTransaction{
        resp_receiver   : resp_receiver,
        cancelPending   : false,
        r408            : r408,
        expires         : expires,
        ack             : ack,
        cancel          : cancel,
        session_lock    : session_lock,
        uack            : false,
        ua              : ua,
    }
    self.baseTransaction = newBaseTransaction(self, tid, userv, sip_tm, address, data, needack, sip_tm.config.ErrorLogger())
    return self
}

func (self *clientTransaction) StartTimers() {
    self.startTeA()
    self.startTeB(32 * time.Second)
}

func (self *clientTransaction) cleanup() {
    self.baseTransaction.cleanup()
    self.ack = nil
    self.resp_receiver = nil
    if self.teB != nil { self.teB.Cancel(); self.teB = nil }
    if self.teC != nil { self.teC.Cancel(); self.teC = nil }
    if self.teG != nil { self.teG.Cancel(); self.teG = nil }
    self.r408 = nil
    self.cancel = nil
}

func (self *clientTransaction) SetOutboundProxy(outbound_proxy *sippy_conf.HostPort) {
    self.outbound_proxy = outbound_proxy
}

func (self *clientTransaction) startTeC() {
    if self.teC != nil {
        self.teC.Cancel()
    }
    self.teC = StartTimeout(self.timerC, self, 32 * time.Second, 1, self.logger)
}

func (self *clientTransaction) timerB() {
    if self.sip_tm == nil {
        return
    }
    //println("timerB", self.tid.String())
    self.cancelTeA()
    self.cancelTeB()
    self.state = TERMINATED
    self.startTeC()
    rtime, _ := sippy_time.NewMonoTime()
    if self.r408 != nil {
        self.r408.SetRtime(rtime)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(self.r408, self)
    }
}

func (self *clientTransaction) timerC() {
    if self.sip_tm == nil {
        return
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) timerG() {
    if self.sip_tm == nil {
        return
    }
    self.teG = nil
    if self.state == UACK {
        self.logger.Error("INVITE transaction stuck in the UACK state, possible UAC bug")
    }
}

func (self *clientTransaction) cancelTeB() {
    if self.teB != nil {
        self.teB.Cancel()
        self.teB = nil
    }
}

func (self *clientTransaction) startTeB(timeout time.Duration) {
    if self.teB != nil {
        self.teB.Cancel()
    }
    self.teB = StartTimeout(self.timerB, self, timeout, 1, self.logger)
}

func (self *clientTransaction) IncomingResponse(resp sippy_types.SipResponse, checksum string) {
    if self.sip_tm == nil {
        return
    }
    // In those two states upper level already notified, only do ACK retransmit
    // if needed
    if self.state == TERMINATED {
        return
    }
    if self.state == TRYING {
        // Stop timers
        self.cancelTeA()
    }
    self.cancelTeB()
    if resp.GetSCodeNum() < 200 {
        self.process_provisional_response(checksum, resp)
    } else {
        self.process_final_response(checksum, resp)
    }
}

func (self *clientTransaction) process_provisional_response(checksum string, resp sippy_types.SipResponse) {
    // Privisional response - leave everything as is, except that
    // change state and reload timeout timer
    if self.state == TRYING {
        self.state = RINGING
        if self.cancelPending {
            self.sip_tm.NewClientTransaction(self.cancel, nil, self.session_lock, nil, self.userv, self.ua)
            self.cancelPending = false
        }
    }
    self.startTeB(self.expires)
    self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
}

func (self *clientTransaction) process_final_response(checksum string, resp sippy_types.SipResponse) {
    // Final response - notify upper layer and remove transaction
    if self.needack {
        // Prepare and send ACK if necessary
        fcode := resp.GetSCodeNum()
        tag := resp.GetTo().GetTag()
        if tag != "" {
            self.ack.GetTo().SetTag(tag)
        }
        var rAddr *sippy_conf.HostPort
        var rTarget *sippy_header.SipURL
        if resp.GetSCodeNum() >= 200 && resp.GetSCodeNum() < 300 {
            // Some hairy code ahead
            if len(resp.GetContacts()) > 0 {
                rTarget = resp.GetContacts()[0].GetUrl().GetCopy()
            } else {
                rTarget = nil
            }
            routes := make([]*sippy_header.SipRoute, len(resp.GetRecordRoutes()))
            for idx, r := range resp.GetRecordRoutes() {
                r2 := r.AsSipRoute() // r.getCopy()
                routes[len(resp.GetRecordRoutes()) - 1 + idx] = r2 // reverse order
            }
            if len(routes) > 0 {
                if ! routes[0].GetUrl().Lr {
                    if rTarget != nil {
                        routes = append(routes, sippy_header.NewSipRoute(/*address =*/ sippy_header.NewSipAddress(/*name*/ "", /*url =*/ rTarget)))
                    }
                    rTarget = routes[0].GetUrl()
                    routes = routes[1:]
                    rAddr = rTarget.GetAddr(self.sip_tm.config)
                } else {
                    rAddr = routes[0].GetAddr(self.sip_tm.config)
                }
            } else if rTarget != nil {
                rAddr = rTarget.GetAddr(self.sip_tm.config)
            }
            if rTarget != nil {
                self.ack.SetRURI(rTarget)
            }
            if self.outbound_proxy != nil {
                routes = append([]*sippy_header.SipRoute{ sippy_header.NewSipRoute(sippy_header.NewSipAddress("", sippy_header.NewSipURL("", self.outbound_proxy.Host, self.outbound_proxy.Port, true))) }, routes...)
                rAddr = self.outbound_proxy
            }
            if rAddr != nil {
                self.ack.SetTarget(rAddr)
            }
            self.ack.SetRoutes(routes)
        }
        if fcode >= 200 && fcode < 300 {
            self.ack.GetVias()[0].GenBranch()
        }
        if rAddr == nil {
            rAddr = self.address
        }
        if ! self.uack {
            self.sip_tm.transmitMsg(self.userv, self.ack, rAddr, checksum, self.tid.CallId)
        } else {
            self.state = UACK
            self.ack_rAddr = rAddr
            self.ack_checksum = checksum
            self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
            self.teG = StartTimeout(self.timerG, self, 64 * time.Second, 1, self.logger)
            return
        }
    } else {
        self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) Cancel(extra_headers ...sippy_header.SipHeader) {
    if self.sip_tm == nil {
        return
    }
    // If we got at least one provisional reply then (state == RINGING)
    // then start CANCEL transaction, otherwise deffer it
    if self.state != RINGING {
        self.cancelPending = true
    } else {
        if extra_headers != nil {
            for _, h := range extra_headers {
                self.cancel.AppendHeader(h)
            }
        }
        self.sip_tm.NewClientTransaction(self.cancel, nil, self.session_lock, nil, self.userv, self.ua)
    }
}

func (self *clientTransaction) Lock() {
    if self.session_lock != nil {
        self.session_lock.Lock()
    } else {
        self.lock.Lock()
    }
}

func (self *clientTransaction) Unlock() {
    if self.session_lock != nil {
        self.session_lock.Unlock()
    } else {
        self.lock.Unlock()
    }
}

func (self *clientTransaction) SendACK() {
    if self.teG != nil {
        self.teG.Cancel()
        self.teG = nil
    }
    self.sip_tm.transmitMsg(self.userv, self.ack, self.ack_rAddr, self.ack_checksum, self.tid.CallId)
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) GetACK() sippy_types.SipRequest {
    return self.ack
}

func (self *clientTransaction) SetUAck(uack bool) {
    self.uack = uack
}
<commit_msg>Simplify locking in the ClientTransaction.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. 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.
package sippy

import (
    "sync"
    "time"

    "sippy/conf"
    "sippy/headers"
    "sippy/time"
    "sippy/types"
)

type clientTransaction struct {
    *baseTransaction
    teB             *Timeout
    teC             *Timeout
    teG             *Timeout
    r408            sippy_types.SipResponse
    resp_receiver   sippy_types.ResponseReceiver
    expires         time.Duration
    ack             sippy_types.SipRequest
    outbound_proxy  *sippy_conf.HostPort
    cancel          sippy_types.SipRequest
    cancelPending   bool
    uack            bool
    ack_rAddr       *sippy_conf.HostPort
    ack_checksum    string
    ua              sippy_types.UA
}

func NewClientTransaction(req sippy_types.SipRequest, tid *sippy_header.TID, userv sippy_types.UdpServer, data []byte, sip_tm *sipTransactionManager, resp_receiver sippy_types.ResponseReceiver, session_lock sync.Locker, address *sippy_conf.HostPort, ua sippy_types.UA) sippy_types.ClientTransaction {
    var r408 sippy_types.SipResponse = nil
    if resp_receiver != nil {
        r408 = req.GenResponse(408, "Request Timeout", /*body*/ nil, /*server*/ nil)
    }
    expires := 300 * time.Second
    if req.GetExpires() != nil && req.GetExpires().Number > 0 {
        expires = time.Duration(req.GetExpires().Number) * time.Second
    }
    needack := false
    var ack, cancel sippy_types.SipRequest
    if req.GetMethod() == "INVITE" {
        needack = true
        ack = req.GenACK(nil, sip_tm.config)
        cancel = req.GenCANCEL(sip_tm.config)
    }
    self := &clientTransaction{
        resp_receiver   : resp_receiver,
        cancelPending   : false,
        r408            : r408,
        expires         : expires,
        ack             : ack,
        cancel          : cancel,
        uack            : false,
        ua              : ua,
    }
    self.baseTransaction = newBaseTransaction(session_lock, tid, userv, sip_tm, address, data, needack, sip_tm.config.ErrorLogger())
    return self
}

func (self *clientTransaction) StartTimers() {
    self.startTeA()
    self.startTeB(32 * time.Second)
}

func (self *clientTransaction) cleanup() {
    self.baseTransaction.cleanup()
    self.ack = nil
    self.resp_receiver = nil
    if self.teB != nil { self.teB.Cancel(); self.teB = nil }
    if self.teC != nil { self.teC.Cancel(); self.teC = nil }
    if self.teG != nil { self.teG.Cancel(); self.teG = nil }
    self.r408 = nil
    self.cancel = nil
}

func (self *clientTransaction) SetOutboundProxy(outbound_proxy *sippy_conf.HostPort) {
    self.outbound_proxy = outbound_proxy
}

func (self *clientTransaction) startTeC() {
    if self.teC != nil {
        self.teC.Cancel()
    }
    self.teC = StartTimeout(self.timerC, self.lock, 32 * time.Second, 1, self.logger)
}

func (self *clientTransaction) timerB() {
    if self.sip_tm == nil {
        return
    }
    //println("timerB", self.tid.String())
    self.cancelTeA()
    self.cancelTeB()
    self.state = TERMINATED
    self.startTeC()
    rtime, _ := sippy_time.NewMonoTime()
    if self.r408 != nil {
        self.r408.SetRtime(rtime)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(self.r408, self)
    }
}

func (self *clientTransaction) timerC() {
    if self.sip_tm == nil {
        return
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) timerG() {
    if self.sip_tm == nil {
        return
    }
    self.teG = nil
    if self.state == UACK {
        self.logger.Error("INVITE transaction stuck in the UACK state, possible UAC bug")
    }
}

func (self *clientTransaction) cancelTeB() {
    if self.teB != nil {
        self.teB.Cancel()
        self.teB = nil
    }
}

func (self *clientTransaction) startTeB(timeout time.Duration) {
    if self.teB != nil {
        self.teB.Cancel()
    }
    self.teB = StartTimeout(self.timerB, self.lock, timeout, 1, self.logger)
}

func (self *clientTransaction) IncomingResponse(resp sippy_types.SipResponse, checksum string) {
    if self.sip_tm == nil {
        return
    }
    // In those two states upper level already notified, only do ACK retransmit
    // if needed
    if self.state == TERMINATED {
        return
    }
    if self.state == TRYING {
        // Stop timers
        self.cancelTeA()
    }
    self.cancelTeB()
    if resp.GetSCodeNum() < 200 {
        self.process_provisional_response(checksum, resp)
    } else {
        self.process_final_response(checksum, resp)
    }
}

func (self *clientTransaction) process_provisional_response(checksum string, resp sippy_types.SipResponse) {
    // Privisional response - leave everything as is, except that
    // change state and reload timeout timer
    if self.state == TRYING {
        self.state = RINGING
        if self.cancelPending {
            self.sip_tm.NewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.ua)
            self.cancelPending = false
        }
    }
    self.startTeB(self.expires)
    self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
}

func (self *clientTransaction) process_final_response(checksum string, resp sippy_types.SipResponse) {
    // Final response - notify upper layer and remove transaction
    if self.needack {
        // Prepare and send ACK if necessary
        fcode := resp.GetSCodeNum()
        tag := resp.GetTo().GetTag()
        if tag != "" {
            self.ack.GetTo().SetTag(tag)
        }
        var rAddr *sippy_conf.HostPort
        var rTarget *sippy_header.SipURL
        if resp.GetSCodeNum() >= 200 && resp.GetSCodeNum() < 300 {
            // Some hairy code ahead
            if len(resp.GetContacts()) > 0 {
                rTarget = resp.GetContacts()[0].GetUrl().GetCopy()
            } else {
                rTarget = nil
            }
            routes := make([]*sippy_header.SipRoute, len(resp.GetRecordRoutes()))
            for idx, r := range resp.GetRecordRoutes() {
                r2 := r.AsSipRoute() // r.getCopy()
                routes[len(resp.GetRecordRoutes()) - 1 + idx] = r2 // reverse order
            }
            if len(routes) > 0 {
                if ! routes[0].GetUrl().Lr {
                    if rTarget != nil {
                        routes = append(routes, sippy_header.NewSipRoute(/*address =*/ sippy_header.NewSipAddress(/*name*/ "", /*url =*/ rTarget)))
                    }
                    rTarget = routes[0].GetUrl()
                    routes = routes[1:]
                    rAddr = rTarget.GetAddr(self.sip_tm.config)
                } else {
                    rAddr = routes[0].GetAddr(self.sip_tm.config)
                }
            } else if rTarget != nil {
                rAddr = rTarget.GetAddr(self.sip_tm.config)
            }
            if rTarget != nil {
                self.ack.SetRURI(rTarget)
            }
            if self.outbound_proxy != nil {
                routes = append([]*sippy_header.SipRoute{ sippy_header.NewSipRoute(sippy_header.NewSipAddress("", sippy_header.NewSipURL("", self.outbound_proxy.Host, self.outbound_proxy.Port, true))) }, routes...)
                rAddr = self.outbound_proxy
            }
            if rAddr != nil {
                self.ack.SetTarget(rAddr)
            }
            self.ack.SetRoutes(routes)
        }
        if fcode >= 200 && fcode < 300 {
            self.ack.GetVias()[0].GenBranch()
        }
        if rAddr == nil {
            rAddr = self.address
        }
        if ! self.uack {
            self.sip_tm.transmitMsg(self.userv, self.ack, rAddr, checksum, self.tid.CallId)
        } else {
            self.state = UACK
            self.ack_rAddr = rAddr
            self.ack_checksum = checksum
            self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
            self.teG = StartTimeout(self.timerG, self.lock, 64 * time.Second, 1, self.logger)
            return
        }
    } else {
        self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) Cancel(extra_headers ...sippy_header.SipHeader) {
    if self.sip_tm == nil {
        return
    }
    // If we got at least one provisional reply then (state == RINGING)
    // then start CANCEL transaction, otherwise deffer it
    if self.state != RINGING {
        self.cancelPending = true
    } else {
        if extra_headers != nil {
            for _, h := range extra_headers {
                self.cancel.AppendHeader(h)
            }
        }
        self.sip_tm.NewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.ua)
    }
}

func (self *clientTransaction) Lock() {
    self.lock.Lock()
}

func (self *clientTransaction) Unlock() {
    self.lock.Unlock()
}

func (self *clientTransaction) SendACK() {
    if self.teG != nil {
        self.teG.Cancel()
        self.teG = nil
    }
    self.sip_tm.transmitMsg(self.userv, self.ack, self.ack_rAddr, self.ack_checksum, self.tid.CallId)
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) GetACK() sippy_types.SipRequest {
    return self.ack
}

func (self *clientTransaction) SetUAck(uack bool) {
    self.uack = uack
}
<|endoftext|>


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

package prober

import (
	"bytes"
	"context"
	"math/rand"
	"net"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"

	"github.com/prometheus/blackbox_exporter/config"
)

var (
	icmpID            int
	icmpSequence      uint16
	icmpSequenceMutex sync.Mutex
)

func init() {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	// PID is typically 1 when running in a container; in that case, set
	// the ICMP echo ID to a random value to avoid potential clashes with
	// other blackbox_exporter instances. See #411.
	if pid := os.Getpid(); pid == 1 {
		icmpID = r.Intn(1 << 16)
	} else {
		icmpID = pid & 0xffff
	}

	// Start the ICMP echo sequence at a random offset to prevent them from
	// being in sync when several blackbox_exporter instances are restarted
	// at the same time. See #411.
	icmpSequence = uint16(r.Intn(1 << 16))
}

func getICMPSequence() uint16 {
	icmpSequenceMutex.Lock()
	defer icmpSequenceMutex.Unlock()
	icmpSequence++
	return icmpSequence
}

func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) {
	var (
		socket      net.PacketConn
		requestType icmp.Type
		replyType   icmp.Type

		durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "probe_icmp_duration_seconds",
			Help: "Duration of icmp request by phase",
		}, []string{"phase"})
	)

	for _, lv := range []string{"resolve", "setup", "rtt"} {
		durationGaugeVec.WithLabelValues(lv)
	}

	registry.MustRegister(durationGaugeVec)

	ip, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger)
	if err != nil {
		level.Warn(logger).Log("msg", "Error resolving address", "err", err)
		return false
	}
	durationGaugeVec.WithLabelValues("resolve").Add(lookupTime)

	var srcIP net.IP
	if len(module.ICMP.SourceIPAddress) > 0 {
		if srcIP = net.ParseIP(module.ICMP.SourceIPAddress); srcIP == nil {
			level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.ICMP.SourceIPAddress)
			return false
		}
		level.Info(logger).Log("msg", "Using source address", "srcIP", srcIP)
	}

	setupStart := time.Now()
	level.Info(logger).Log("msg", "Creating socket")

	unprivileged := false
	// Unprivileged sockets are supported on Darwin and Linux only.
	tryUnprivileged := runtime.GOOS == "darwin" || runtime.GOOS == "linux"

	if ip.IP.To4() == nil {
		requestType = ipv6.ICMPTypeEchoRequest
		replyType = ipv6.ICMPTypeEchoReply

		if srcIP == nil {
			srcIP = net.ParseIP("::")
		}

		var icmpConn *icmp.PacketConn
		if tryUnprivileged {
			// "udp" here means unprivileged -- not the protocol "udp".
			icmpConn, err = icmp.ListenPacket("udp6", srcIP.String())
			if err != nil {
				level.Debug(logger).Log("msg", "Unable to do unprivileged listen on socket, will attempt privileged", "err", err)
			} else {
				unprivileged = true
			}
		}

		if !unprivileged {
			icmpConn, err = icmp.ListenPacket("ip6:ipv6-icmp", srcIP.String())
			if err != nil {
				level.Error(logger).Log("msg", "Error listening to socket", "err", err)
				return
			}
		}

		socket = icmpConn
	} else {
		requestType = ipv4.ICMPTypeEcho
		replyType = ipv4.ICMPTypeEchoReply

		if srcIP == nil {
			srcIP = net.ParseIP("0.0.0.0")
		}

		var icmpConn *icmp.PacketConn
		// If the user has set the don't fragment option we cannot use unprivileged
		// sockets as it is not possible to set IP header level options.
		if tryUnprivileged && !module.ICMP.DontFragment {
			icmpConn, err = icmp.ListenPacket("udp4", srcIP.String())
			if err != nil {
				level.Debug(logger).Log("msg", "Unable to do unprivileged listen on socket, will attempt privileged", "err", err)
			} else {
				unprivileged = true
			}
		}

		if !unprivileged {
			icmpConn, err = icmp.ListenPacket("ip4:icmp", srcIP.String())
			if err != nil {
				level.Error(logger).Log("msg", "Error listening to socket", "err", err)
				return
			}
		}

		if module.ICMP.DontFragment {
			rc, err := ipv4.NewRawConn(icmpConn)
			if err != nil {
				level.Error(logger).Log("msg", "Error creating raw connection", "err", err)
				return
			}
			socket = &v4Conn{c: rc, df: true}
		} else {
			socket = icmpConn
		}
	}

	defer socket.Close()

	var dst net.Addr = ip
	if unprivileged {
		dst = &net.UDPAddr{IP: ip.IP, Zone: ip.Zone}
	}

	var data []byte
	if module.ICMP.PayloadSize != 0 {
		data = make([]byte, module.ICMP.PayloadSize)
		copy(data, "Prometheus Blackbox Exporter")
	} else {
		data = []byte("Prometheus Blackbox Exporter")
	}

	body := &icmp.Echo{
		ID:   icmpID,
		Seq:  int(getICMPSequence()),
		Data: data,
	}
	level.Info(logger).Log("msg", "Creating ICMP packet", "seq", body.Seq, "id", body.ID)
	wm := icmp.Message{
		Type: requestType,
		Code: 0,
		Body: body,
	}

	wb, err := wm.Marshal(nil)
	if err != nil {
		level.Error(logger).Log("msg", "Error marshalling packet", "err", err)
		return
	}

	durationGaugeVec.WithLabelValues("setup").Add(time.Since(setupStart).Seconds())
	level.Info(logger).Log("msg", "Writing out packet")
	rttStart := time.Now()
	if _, err = socket.WriteTo(wb, dst); err != nil {
		level.Warn(logger).Log("msg", "Error writing to socket", "err", err)
		return
	}

	// Reply should be the same except for the message type and ID if
	// unprivileged sockets were used and the kernel used its own.
	wm.Type = replyType
	// Unprivileged cannot set IDs on Linux.
	idUnknown := unprivileged && runtime.GOOS == "linux"
	if idUnknown {
		body.ID = 0
	}
	wb, err = wm.Marshal(nil)
	if err != nil {
		level.Error(logger).Log("msg", "Error marshalling packet", "err", err)
		return
	}

	if idUnknown {
		// If the ID is unknown (due to unprivileged sockets) we also cannot know
		// the checksum in userspace.
		wb[2] = 0
		wb[3] = 0
	}

	rb := make([]byte, 65536)
	deadline, _ := ctx.Deadline()
	if err := socket.SetReadDeadline(deadline); err != nil {
		level.Error(logger).Log("msg", "Error setting socket deadline", "err", err)
		return
	}
	level.Info(logger).Log("msg", "Waiting for reply packets")
	for {
		n, peer, err := socket.ReadFrom(rb)
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				level.Warn(logger).Log("msg", "Timeout reading from socket", "err", err)
				return
			}
			level.Error(logger).Log("msg", "Error reading from socket", "err", err)
			continue
		}
		if peer.String() != dst.String() {
			continue
		}
		if idUnknown {
			// Clear the ID from the packet, as the kernel will have replaced it (and
			// kept track of our packet for us, hence clearing is safe).
			rb[4] = 0
			rb[5] = 0
		}
		if idUnknown || replyType == ipv6.ICMPTypeEchoReply {
			// Clear checksum to make comparison succeed.
			rb[2] = 0
			rb[3] = 0
		}
		if bytes.Equal(rb[:n], wb) {
			durationGaugeVec.WithLabelValues("rtt").Add(time.Since(rttStart).Seconds())
			level.Info(logger).Log("msg", "Found matching reply packet")
			return true
		}
	}
}

type v4Conn struct {
	c *ipv4.RawConn

	df  bool
	src net.IP
}

func (c *v4Conn) ReadFrom(b []byte) (int, net.Addr, error) {
	h, p, _, err := c.c.ReadFrom(b)
	if err != nil {
		return 0, nil, err
	}

	copy(b, p)
	n := len(b)
	if len(p) < len(b) {
		n = len(p)
	}
	return n, &net.IPAddr{IP: h.Src}, nil
}

func (d *v4Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
	ipAddr, err := net.ResolveIPAddr(addr.Network(), addr.String())
	if err != nil {
		return 0, err
	}

	header := &ipv4.Header{
		Version:  ipv4.Version,
		Len:      ipv4.HeaderLen,
		Protocol: 1,
		TotalLen: ipv4.HeaderLen + len(b),
		TTL:      64,
		Dst:      ipAddr.IP,
		Src:      d.src,
	}

	if d.df {
		header.Flags |= ipv4.DontFragment
	}

	return len(b), d.c.WriteTo(header, b, nil)
}

func (d *v4Conn) Close() error {
	return d.c.Close()
}

func (d *v4Conn) LocalAddr() net.Addr {
	return nil
}

func (d *v4Conn) SetDeadline(t time.Time) error {
	return d.c.SetDeadline(t)
}

func (d *v4Conn) SetReadDeadline(t time.Time) error {
	return d.c.SetReadDeadline(t)
}

func (d *v4Conn) SetWriteDeadline(t time.Time) error {
	return d.c.SetWriteDeadline(t)
}
<commit_msg>Fix panic when running ICMPv4 probe with DontFragment (#686)<commit_after>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prober

import (
	"bytes"
	"context"
	"math/rand"
	"net"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"

	"github.com/prometheus/blackbox_exporter/config"
)

var (
	icmpID            int
	icmpSequence      uint16
	icmpSequenceMutex sync.Mutex
)

func init() {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	// PID is typically 1 when running in a container; in that case, set
	// the ICMP echo ID to a random value to avoid potential clashes with
	// other blackbox_exporter instances. See #411.
	if pid := os.Getpid(); pid == 1 {
		icmpID = r.Intn(1 << 16)
	} else {
		icmpID = pid & 0xffff
	}

	// Start the ICMP echo sequence at a random offset to prevent them from
	// being in sync when several blackbox_exporter instances are restarted
	// at the same time. See #411.
	icmpSequence = uint16(r.Intn(1 << 16))
}

func getICMPSequence() uint16 {
	icmpSequenceMutex.Lock()
	defer icmpSequenceMutex.Unlock()
	icmpSequence++
	return icmpSequence
}

func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) {
	var (
		socket      net.PacketConn
		requestType icmp.Type
		replyType   icmp.Type

		durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "probe_icmp_duration_seconds",
			Help: "Duration of icmp request by phase",
		}, []string{"phase"})
	)

	for _, lv := range []string{"resolve", "setup", "rtt"} {
		durationGaugeVec.WithLabelValues(lv)
	}

	registry.MustRegister(durationGaugeVec)

	ip, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger)
	if err != nil {
		level.Warn(logger).Log("msg", "Error resolving address", "err", err)
		return false
	}
	durationGaugeVec.WithLabelValues("resolve").Add(lookupTime)

	var srcIP net.IP
	if len(module.ICMP.SourceIPAddress) > 0 {
		if srcIP = net.ParseIP(module.ICMP.SourceIPAddress); srcIP == nil {
			level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.ICMP.SourceIPAddress)
			return false
		}
		level.Info(logger).Log("msg", "Using source address", "srcIP", srcIP)
	}

	setupStart := time.Now()
	level.Info(logger).Log("msg", "Creating socket")

	privileged := true
	// Unprivileged sockets are supported on Darwin and Linux only.
	tryUnprivileged := runtime.GOOS == "darwin" || runtime.GOOS == "linux"

	if ip.IP.To4() == nil {
		requestType = ipv6.ICMPTypeEchoRequest
		replyType = ipv6.ICMPTypeEchoReply

		if srcIP == nil {
			srcIP = net.ParseIP("::")
		}

		var icmpConn *icmp.PacketConn
		if tryUnprivileged {
			// "udp" here means unprivileged -- not the protocol "udp".
			icmpConn, err = icmp.ListenPacket("udp6", srcIP.String())
			if err != nil {
				level.Debug(logger).Log("msg", "Unable to do unprivileged listen on socket, will attempt privileged", "err", err)
			} else {
				privileged = false
			}
		}

		if privileged {
			icmpConn, err = icmp.ListenPacket("ip6:ipv6-icmp", srcIP.String())
			if err != nil {
				level.Error(logger).Log("msg", "Error listening to socket", "err", err)
				return
			}
		}

		socket = icmpConn
	} else {
		requestType = ipv4.ICMPTypeEcho
		replyType = ipv4.ICMPTypeEchoReply

		if srcIP == nil {
			srcIP = net.ParseIP("0.0.0.0")
		}

		if module.ICMP.DontFragment {
			// If the user has set the don't fragment option we cannot use unprivileged
			// sockets as it is not possible to set IP header level options.
			icmpConn, err := net.ListenPacket("ip4:icmp", srcIP.String())
			if err != nil {
				level.Error(logger).Log("msg", "Error listening to socket", "err", err)
				return
			}

			rc, err := ipv4.NewRawConn(icmpConn)
			if err != nil {
				level.Error(logger).Log("msg", "Error creating raw connection", "err", err)
				return
			}
			socket = &v4Conn{c: rc, df: true}
		} else {
			var icmpConn *icmp.PacketConn

			if tryUnprivileged {
				icmpConn, err = icmp.ListenPacket("udp4", srcIP.String())
				if err != nil {
					level.Debug(logger).Log("msg", "Unable to do unprivileged listen on socket, will attempt privileged", "err", err)
				} else {
					privileged = false
				}
			}

			if privileged {
				icmpConn, err = icmp.ListenPacket("ip4:icmp", srcIP.String())
				if err != nil {
					level.Error(logger).Log("msg", "Error listening to socket", "err", err)
					return
				}
			}

			socket = icmpConn
		}
	}

	defer socket.Close()

	var dst net.Addr = ip
	if !privileged {
		dst = &net.UDPAddr{IP: ip.IP, Zone: ip.Zone}
	}

	var data []byte
	if module.ICMP.PayloadSize != 0 {
		data = make([]byte, module.ICMP.PayloadSize)
		copy(data, "Prometheus Blackbox Exporter")
	} else {
		data = []byte("Prometheus Blackbox Exporter")
	}

	body := &icmp.Echo{
		ID:   icmpID,
		Seq:  int(getICMPSequence()),
		Data: data,
	}
	level.Info(logger).Log("msg", "Creating ICMP packet", "seq", body.Seq, "id", body.ID)
	wm := icmp.Message{
		Type: requestType,
		Code: 0,
		Body: body,
	}

	wb, err := wm.Marshal(nil)
	if err != nil {
		level.Error(logger).Log("msg", "Error marshalling packet", "err", err)
		return
	}

	durationGaugeVec.WithLabelValues("setup").Add(time.Since(setupStart).Seconds())
	level.Info(logger).Log("msg", "Writing out packet")
	rttStart := time.Now()
	if _, err = socket.WriteTo(wb, dst); err != nil {
		level.Warn(logger).Log("msg", "Error writing to socket", "err", err)
		return
	}

	// Reply should be the same except for the message type and ID if
	// unprivileged sockets were used and the kernel used its own.
	wm.Type = replyType
	// Unprivileged cannot set IDs on Linux.
	idUnknown := !privileged && runtime.GOOS == "linux"
	if idUnknown {
		body.ID = 0
	}
	wb, err = wm.Marshal(nil)
	if err != nil {
		level.Error(logger).Log("msg", "Error marshalling packet", "err", err)
		return
	}

	if idUnknown {
		// If the ID is unknown (due to unprivileged sockets) we also cannot know
		// the checksum in userspace.
		wb[2] = 0
		wb[3] = 0
	}

	rb := make([]byte, 65536)
	deadline, _ := ctx.Deadline()
	if err := socket.SetReadDeadline(deadline); err != nil {
		level.Error(logger).Log("msg", "Error setting socket deadline", "err", err)
		return
	}
	level.Info(logger).Log("msg", "Waiting for reply packets")
	for {
		n, peer, err := socket.ReadFrom(rb)
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				level.Warn(logger).Log("msg", "Timeout reading from socket", "err", err)
				return
			}
			level.Error(logger).Log("msg", "Error reading from socket", "err", err)
			continue
		}
		if peer.String() != dst.String() {
			continue
		}
		if idUnknown {
			// Clear the ID from the packet, as the kernel will have replaced it (and
			// kept track of our packet for us, hence clearing is safe).
			rb[4] = 0
			rb[5] = 0
		}
		if idUnknown || replyType == ipv6.ICMPTypeEchoReply {
			// Clear checksum to make comparison succeed.
			rb[2] = 0
			rb[3] = 0
		}
		if bytes.Equal(rb[:n], wb) {
			durationGaugeVec.WithLabelValues("rtt").Add(time.Since(rttStart).Seconds())
			level.Info(logger).Log("msg", "Found matching reply packet")
			return true
		}
	}
}

type v4Conn struct {
	c *ipv4.RawConn

	df  bool
	src net.IP
}

func (c *v4Conn) ReadFrom(b []byte) (int, net.Addr, error) {
	h, p, _, err := c.c.ReadFrom(b)
	if err != nil {
		return 0, nil, err
	}

	copy(b, p)
	n := len(b)
	if len(p) < len(b) {
		n = len(p)
	}
	return n, &net.IPAddr{IP: h.Src}, nil
}

func (d *v4Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
	ipAddr, err := net.ResolveIPAddr(addr.Network(), addr.String())
	if err != nil {
		return 0, err
	}

	header := &ipv4.Header{
		Version:  ipv4.Version,
		Len:      ipv4.HeaderLen,
		Protocol: 1,
		TotalLen: ipv4.HeaderLen + len(b),
		TTL:      64,
		Dst:      ipAddr.IP,
		Src:      d.src,
	}

	if d.df {
		header.Flags |= ipv4.DontFragment
	}

	return len(b), d.c.WriteTo(header, b, nil)
}

func (d *v4Conn) Close() error {
	return d.c.Close()
}

func (d *v4Conn) LocalAddr() net.Addr {
	return nil
}

func (d *v4Conn) SetDeadline(t time.Time) error {
	return d.c.SetDeadline(t)
}

func (d *v4Conn) SetReadDeadline(t time.Time) error {
	return d.c.SetReadDeadline(t)
}

func (d *v4Conn) SetWriteDeadline(t time.Time) error {
	return d.c.SetWriteDeadline(t)
}
<|endoftext|>