<commit_before>package mdns

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

	"code.google.com/p/go.net/ipv4"
	"code.google.com/p/go.net/ipv6"
	"github.com/miekg/dns"
)

// ServiceEntry is returned after we query for a service
type ServiceEntry struct {
	Name   string
	Host   string
	AddrV4 net.IP
	AddrV6 net.IP
	Port   int
	Info   string

	Addr net.IP // @Deprecated

	hasTXT bool
	sent   bool
}

// complete is used to check if we have all the info we need
func (s *ServiceEntry) complete() bool {
	return (s.AddrV4 != nil || s.AddrV6 != nil || s.Addr != nil) && s.Port != 0 && s.hasTXT
}

// QueryParam is used to customize how a Lookup is performed
type QueryParam struct {
	Service             string               // Service to lookup
	Domain              string               // Lookup domain, default "local"
	Timeout             time.Duration        // Lookup timeout, default 1 second
	Interface           *net.Interface       // Multicast interface to use
	Entries             chan<- *ServiceEntry // Entries Channel
	WantUnicastResponse bool                 // Unicast response desired, as per 5.4 in RFC
}

// DefaultParams is used to return a default set of QueryParam's
func DefaultParams(service string) *QueryParam {
	return &QueryParam{
		Service:             service,
		Domain:              "local",
		Timeout:             time.Second,
		Entries:             make(chan *ServiceEntry),
		WantUnicastResponse: false, // TODO(reddaly): Change this default.
	}
}

// Query looks up a given service, in a domain, waiting at most
// for a timeout before finishing the query. The results are streamed
// to a channel. Sends will not block, so clients should make sure to
// either read or buffer.
func Query(params *QueryParam) error {
	// Create a new client
	client, err := newClient()
	if err != nil {
		return err
	}
	defer client.Close()

	// Set the multicast interface
	if params.Interface != nil {
		if err := client.setInterface(params.Interface); err != nil {
			return err
		}
	}

	// Ensure defaults are set
	if params.Domain == "" {
		params.Domain = "local"
	}
	if params.Timeout == 0 {
		params.Timeout = time.Second
	}

	// Run the query
	return client.query(params)
}

// Lookup is the same as Query, however it uses all the default parameters
func Lookup(service string, entries chan<- *ServiceEntry) error {
	params := DefaultParams(service)
	params.Entries = entries
	return Query(params)
}

// Client provides a query interface that can be used to
// search for service providers using mDNS
type client struct {
	ipv4UnicastConn *net.UDPConn
	ipv6UnicastConn *net.UDPConn

	ipv4MulticastConn *net.UDPConn
	ipv6MulticastConn *net.UDPConn

	closed    bool
	closedCh  chan struct{} // TODO(reddaly): This doesn't appear to be used.
	closeLock sync.Mutex
}

// NewClient creates a new mdns Client that can be used to query
// for records
func newClient() (*client, error) {
	// TODO(reddaly): At least attempt to bind to the port required in the spec.
	// Create a IPv4 listener
	uconn4, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	uconn6, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if uconn4 == nil && uconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any unicast udp port")
	}

	mconn4, err := net.ListenMulticastUDP("udp4", nil, ipv4Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	mconn6, err := net.ListenMulticastUDP("udp6", nil, ipv6Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if mconn4 == nil && mconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any multicast udp port")
	}

	c := &client{
		ipv4MulticastConn: mconn4,
		ipv6MulticastConn: mconn6,
		ipv4UnicastConn:   uconn4,
		ipv6UnicastConn:   uconn6,
		closedCh:          make(chan struct{}),
	}
	return c, nil
}

// Close is used to cleanup the client
func (c *client) Close() error {
	c.closeLock.Lock()
	defer c.closeLock.Unlock()

	if c.closed {
		return nil
	}
	c.closed = true

	log.Printf("[INFO] mdns: Closing client %v", *c)
	close(c.closedCh)

	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.Close()
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.Close()
	}
	if c.ipv4MulticastConn != nil {
		c.ipv4MulticastConn.Close()
	}
	if c.ipv6MulticastConn != nil {
		c.ipv6MulticastConn.Close()
	}

	return nil
}

// setInterface is used to set the query interface, uses sytem
// default if not provided
func (c *client) setInterface(iface *net.Interface) error {
	p := ipv4.NewPacketConn(c.ipv4UnicastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 := ipv6.NewPacketConn(c.ipv6UnicastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	p = ipv4.NewPacketConn(c.ipv4MulticastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 = ipv6.NewPacketConn(c.ipv6MulticastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	return nil
}

// query is used to perform a lookup and stream results
func (c *client) query(params *QueryParam) error {
	// Create the service name
	serviceAddr := fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain))

	// Start listening for response packets
	msgCh := make(chan *dns.Msg, 32)
	go c.recv(c.ipv4UnicastConn, msgCh)
	go c.recv(c.ipv6UnicastConn, msgCh)
	go c.recv(c.ipv4MulticastConn, msgCh)
	go c.recv(c.ipv6MulticastConn, msgCh)

	// Send the query
	m := new(dns.Msg)
	m.SetQuestion(serviceAddr, dns.TypePTR)
	// RFC 6762, section 18.12.  Repurposing of Top Bit of qclass in Question
	// Section
	//
	// In the Question Section of a Multicast DNS query, the top bit of the qclass
	// field is used to indicate that unicast responses are preferred for this
	// particular question.  (See Section 5.4.)
	if params.WantUnicastResponse {
		m.Question[0].Qclass |= 1 << 15
	}
	m.RecursionDesired = false
	if err := c.sendQuery(m); err != nil {
		return err
	}

	// Map the in-progress responses
	inprogress := make(map[string]*ServiceEntry)

	// Listen until we reach the timeout
	finish := time.After(params.Timeout)
	for {
		select {
		case resp := <-msgCh:
			var inp *ServiceEntry
			for _, answer := range resp.Answer {
				// TODO(reddaly): Check that response corresponds to serviceAddr?
				switch rr := answer.(type) {
				case *dns.PTR:
					// Create new entry for this
					inp = ensureName(inprogress, rr.Ptr)

				case *dns.SRV:
					// Check for a target mismatch
					if rr.Target != rr.Hdr.Name {
						alias(inprogress, rr.Hdr.Name, rr.Target)
					}

					// Get the port
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Host = rr.Target
					inp.Port = int(rr.Port)

				case *dns.TXT:
					// Pull out the txt
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Info = strings.Join(rr.Txt, "|")
					inp.hasTXT = true

				case *dns.A:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.A // @Deprecated
					inp.AddrV4 = rr.A

				case *dns.AAAA:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.AAAA // @Deprecated
					inp.AddrV6 = rr.AAAA
				}
			}

			if inp == nil {
				continue
			}

			// Check if this entry is complete
			if inp.complete() {
				if inp.sent {
					continue
				}
				inp.sent = true
				select {
				case params.Entries <- inp:
				default:
				}
			} else {
				// Fire off a node specific query
				m := new(dns.Msg)
				m.SetQuestion(inp.Name, dns.TypePTR)
				m.RecursionDesired = false
				if err := c.sendQuery(m); err != nil {
					log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err)
				}
			}
		case <-finish:
			return nil
		}
	}
}

// sendQuery is used to multicast a query out
func (c *client) sendQuery(q *dns.Msg) error {
	buf, err := q.Pack()
	if err != nil {
		return err
	}
	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr)
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr)
	}
	return nil
}

// recv is used to receive until we get a shutdown
func (c *client) recv(l *net.UDPConn, msgCh chan *dns.Msg) {
	if l == nil {
		return
	}
	buf := make([]byte, 65536)
	for !c.closed {
		n, err := l.Read(buf)
		if err != nil {
			log.Printf("[ERR] mdns: Failed to read packet: %v", err)
			continue
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
			continue
		}
		select {
		case msgCh <- msg:
		case <-c.closedCh:
			return
		}
	}
}

// ensureName is used to ensure the named node is in progress
func ensureName(inprogress map[string]*ServiceEntry, name string) *ServiceEntry {
	if inp, ok := inprogress[name]; ok {
		return inp
	}
	inp := &ServiceEntry{
		Name: name,
	}
	inprogress[name] = inp
	return inp
}

// alias is used to setup an alias between two entries
func alias(inprogress map[string]*ServiceEntry, src, dst string) {
	srcEntry := ensureName(inprogress, src)
	inprogress[dst] = srcEntry
}
<commit_msg>Change import path from code.google.com/p/go.net/context to golang.org/x/net/context.<commit_after>package mdns

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

	"github.com/miekg/dns"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

// ServiceEntry is returned after we query for a service
type ServiceEntry struct {
	Name   string
	Host   string
	AddrV4 net.IP
	AddrV6 net.IP
	Port   int
	Info   string

	Addr net.IP // @Deprecated

	hasTXT bool
	sent   bool
}

// complete is used to check if we have all the info we need
func (s *ServiceEntry) complete() bool {
	return (s.AddrV4 != nil || s.AddrV6 != nil || s.Addr != nil) && s.Port != 0 && s.hasTXT
}

// QueryParam is used to customize how a Lookup is performed
type QueryParam struct {
	Service             string               // Service to lookup
	Domain              string               // Lookup domain, default "local"
	Timeout             time.Duration        // Lookup timeout, default 1 second
	Interface           *net.Interface       // Multicast interface to use
	Entries             chan<- *ServiceEntry // Entries Channel
	WantUnicastResponse bool                 // Unicast response desired, as per 5.4 in RFC
}

// DefaultParams is used to return a default set of QueryParam's
func DefaultParams(service string) *QueryParam {
	return &QueryParam{
		Service:             service,
		Domain:              "local",
		Timeout:             time.Second,
		Entries:             make(chan *ServiceEntry),
		WantUnicastResponse: false, // TODO(reddaly): Change this default.
	}
}

// Query looks up a given service, in a domain, waiting at most
// for a timeout before finishing the query. The results are streamed
// to a channel. Sends will not block, so clients should make sure to
// either read or buffer.
func Query(params *QueryParam) error {
	// Create a new client
	client, err := newClient()
	if err != nil {
		return err
	}
	defer client.Close()

	// Set the multicast interface
	if params.Interface != nil {
		if err := client.setInterface(params.Interface); err != nil {
			return err
		}
	}

	// Ensure defaults are set
	if params.Domain == "" {
		params.Domain = "local"
	}
	if params.Timeout == 0 {
		params.Timeout = time.Second
	}

	// Run the query
	return client.query(params)
}

// Lookup is the same as Query, however it uses all the default parameters
func Lookup(service string, entries chan<- *ServiceEntry) error {
	params := DefaultParams(service)
	params.Entries = entries
	return Query(params)
}

// Client provides a query interface that can be used to
// search for service providers using mDNS
type client struct {
	ipv4UnicastConn *net.UDPConn
	ipv6UnicastConn *net.UDPConn

	ipv4MulticastConn *net.UDPConn
	ipv6MulticastConn *net.UDPConn

	closed    bool
	closedCh  chan struct{} // TODO(reddaly): This doesn't appear to be used.
	closeLock sync.Mutex
}

// NewClient creates a new mdns Client that can be used to query
// for records
func newClient() (*client, error) {
	// TODO(reddaly): At least attempt to bind to the port required in the spec.
	// Create a IPv4 listener
	uconn4, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	uconn6, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if uconn4 == nil && uconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any unicast udp port")
	}

	mconn4, err := net.ListenMulticastUDP("udp4", nil, ipv4Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	mconn6, err := net.ListenMulticastUDP("udp6", nil, ipv6Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if mconn4 == nil && mconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any multicast udp port")
	}

	c := &client{
		ipv4MulticastConn: mconn4,
		ipv6MulticastConn: mconn6,
		ipv4UnicastConn:   uconn4,
		ipv6UnicastConn:   uconn6,
		closedCh:          make(chan struct{}),
	}
	return c, nil
}

// Close is used to cleanup the client
func (c *client) Close() error {
	c.closeLock.Lock()
	defer c.closeLock.Unlock()

	if c.closed {
		return nil
	}
	c.closed = true

	log.Printf("[INFO] mdns: Closing client %v", *c)
	close(c.closedCh)

	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.Close()
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.Close()
	}
	if c.ipv4MulticastConn != nil {
		c.ipv4MulticastConn.Close()
	}
	if c.ipv6MulticastConn != nil {
		c.ipv6MulticastConn.Close()
	}

	return nil
}

// setInterface is used to set the query interface, uses sytem
// default if not provided
func (c *client) setInterface(iface *net.Interface) error {
	p := ipv4.NewPacketConn(c.ipv4UnicastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 := ipv6.NewPacketConn(c.ipv6UnicastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	p = ipv4.NewPacketConn(c.ipv4MulticastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 = ipv6.NewPacketConn(c.ipv6MulticastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	return nil
}

// query is used to perform a lookup and stream results
func (c *client) query(params *QueryParam) error {
	// Create the service name
	serviceAddr := fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain))

	// Start listening for response packets
	msgCh := make(chan *dns.Msg, 32)
	go c.recv(c.ipv4UnicastConn, msgCh)
	go c.recv(c.ipv6UnicastConn, msgCh)
	go c.recv(c.ipv4MulticastConn, msgCh)
	go c.recv(c.ipv6MulticastConn, msgCh)

	// Send the query
	m := new(dns.Msg)
	m.SetQuestion(serviceAddr, dns.TypePTR)
	// RFC 6762, section 18.12.  Repurposing of Top Bit of qclass in Question
	// Section
	//
	// In the Question Section of a Multicast DNS query, the top bit of the qclass
	// field is used to indicate that unicast responses are preferred for this
	// particular question.  (See Section 5.4.)
	if params.WantUnicastResponse {
		m.Question[0].Qclass |= 1 << 15
	}
	m.RecursionDesired = false
	if err := c.sendQuery(m); err != nil {
		return err
	}

	// Map the in-progress responses
	inprogress := make(map[string]*ServiceEntry)

	// Listen until we reach the timeout
	finish := time.After(params.Timeout)
	for {
		select {
		case resp := <-msgCh:
			var inp *ServiceEntry
			for _, answer := range resp.Answer {
				// TODO(reddaly): Check that response corresponds to serviceAddr?
				switch rr := answer.(type) {
				case *dns.PTR:
					// Create new entry for this
					inp = ensureName(inprogress, rr.Ptr)

				case *dns.SRV:
					// Check for a target mismatch
					if rr.Target != rr.Hdr.Name {
						alias(inprogress, rr.Hdr.Name, rr.Target)
					}

					// Get the port
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Host = rr.Target
					inp.Port = int(rr.Port)

				case *dns.TXT:
					// Pull out the txt
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Info = strings.Join(rr.Txt, "|")
					inp.hasTXT = true

				case *dns.A:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.A // @Deprecated
					inp.AddrV4 = rr.A

				case *dns.AAAA:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.AAAA // @Deprecated
					inp.AddrV6 = rr.AAAA
				}
			}

			if inp == nil {
				continue
			}

			// Check if this entry is complete
			if inp.complete() {
				if inp.sent {
					continue
				}
				inp.sent = true
				select {
				case params.Entries <- inp:
				default:
				}
			} else {
				// Fire off a node specific query
				m := new(dns.Msg)
				m.SetQuestion(inp.Name, dns.TypePTR)
				m.RecursionDesired = false
				if err := c.sendQuery(m); err != nil {
					log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err)
				}
			}
		case <-finish:
			return nil
		}
	}
}

// sendQuery is used to multicast a query out
func (c *client) sendQuery(q *dns.Msg) error {
	buf, err := q.Pack()
	if err != nil {
		return err
	}
	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr)
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr)
	}
	return nil
}

// recv is used to receive until we get a shutdown
func (c *client) recv(l *net.UDPConn, msgCh chan *dns.Msg) {
	if l == nil {
		return
	}
	buf := make([]byte, 65536)
	for !c.closed {
		n, err := l.Read(buf)
		if err != nil {
			log.Printf("[ERR] mdns: Failed to read packet: %v", err)
			continue
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
			continue
		}
		select {
		case msgCh <- msg:
		case <-c.closedCh:
			return
		}
	}
}

// ensureName is used to ensure the named node is in progress
func ensureName(inprogress map[string]*ServiceEntry, name string) *ServiceEntry {
	if inp, ok := inprogress[name]; ok {
		return inp
	}
	inp := &ServiceEntry{
		Name: name,
	}
	inprogress[name] = inp
	return inp
}

// alias is used to setup an alias between two entries
func alias(inprogress map[string]*ServiceEntry, src, dst string) {
	srcEntry := ensureName(inprogress, src)
	inprogress[dst] = srcEntry
}
<|endoftext|>


<commit_before>package main

import (
	"irc"
	"fmt"
	"os"
	"bufio"
	"strings"
)

func main() {
	// create new IRC connection
	c := irc.New("GoTest", "gotest", "GoBot")
	c.AddHandler("connected",
		func(conn *irc.Conn, line *irc.Line) { conn.Join("#go-nuts") })

	// connect to server
	if err := c.Connect("irc.freenode.net", false); err != nil {
		fmt.Printf("Connection error: %s\n", err)
		return
	}

	// set up a goroutine to read commands from stdin
	in := make(chan string, 4)
	reallyquit := false
	go func() {
		con := bufio.NewReader(os.Stdin)
		for {
			s, err := con.ReadString('\n')
			if err != nil {
				// wha?, maybe ctrl-D...
				close(in)
				break
			}
			// no point in sending empty lines down the channel
			if len(s) > 2 {
				in <- s[0:len(s)-1]
			}
		}
	}()

	// set up a goroutine to do parsey things with the stuff from stdin
	go func() {
		for cmd := range in {
			if cmd[0] == ':' {
				switch idx := strings.Index(cmd, " "); {
				case cmd[1] == 'd':
					fmt.Printf(c.String())
				case cmd[1] == 'f':
					if len(cmd) > 2 && cmd[2] == 'e' {
						// enable flooding
						c.Flood = true
					} else if len(cmd) > 2 && cmd[2] == 'd' {
						// disable flooding
						c.Flood = false
					}
					for i := 0; i < 20; i++ {
						c.Privmsg("#", "flood test!")
					}
				case idx == -1:
					continue
				case cmd[1] == 'q':
					reallyquit = true
					c.Quit(cmd[idx+1 : len(cmd)])
				case cmd[1] == 'j':
					c.Join(cmd[idx+1 : len(cmd)])
				case cmd[1] == 'p':
					c.Part(cmd[idx+1 : len(cmd)])
				}
			} else {
				c.Raw(cmd)
			}
		}
	}()

	// stall here waiting for asplode on error channel
	for {
		for err := range c.Err {
			fmt.Printf("goirc error: %s\n", err)
		}
		if reallyquit {
			break
		}
		fmt.Println("Reconnecting...")
		if err := c.Connect("irc.freenode.net", false); err != nil {
			fmt.Printf("Connection error: %s\n", err)
			break
		}
	}
}
<commit_msg>Update example client with changes.<commit_after>package main

import (
	"irc"
	"fmt"
	"os"
	"bufio"
	"strings"
)

func main() {
	// create new IRC connection
	c := irc.New("GoTest", "gotest", "GoBot")
	c.Debug = true
	c.AddHandler("connected",
		func(conn *irc.Conn, line *irc.Line) { conn.Join("#go-nuts") })

	// connect to server
	if err := c.Connect("irc.freenode.net"); err != nil {
		fmt.Printf("Connection error: %s\n", err)
		return
	}

	// set up a goroutine to read commands from stdin
	in := make(chan string, 4)
	reallyquit := false
	go func() {
		con := bufio.NewReader(os.Stdin)
		for {
			s, err := con.ReadString('\n')
			if err != nil {
				// wha?, maybe ctrl-D...
				close(in)
				break
			}
			// no point in sending empty lines down the channel
			if len(s) > 2 {
				in <- s[0:len(s)-1]
			}
		}
	}()

	// set up a goroutine to do parsey things with the stuff from stdin
	go func() {
		for cmd := range in {
			if cmd[0] == ':' {
				switch idx := strings.Index(cmd, " "); {
				case cmd[1] == 'd':
					fmt.Printf(c.String())
				case cmd[1] == 'f':
					if len(cmd) > 2 && cmd[2] == 'e' {
						// enable flooding
						c.Flood = true
					} else if len(cmd) > 2 && cmd[2] == 'd' {
						// disable flooding
						c.Flood = false
					}
					for i := 0; i < 20; i++ {
						c.Privmsg("#", "flood test!")
					}
				case idx == -1:
					continue
				case cmd[1] == 'q':
					reallyquit = true
					c.Quit(cmd[idx+1 : len(cmd)])
				case cmd[1] == 'j':
					c.Join(cmd[idx+1 : len(cmd)])
				case cmd[1] == 'p':
					c.Part(cmd[idx+1 : len(cmd)])
				}
			} else {
				c.Raw(cmd)
			}
		}
	}()

	// stall here waiting for asplode on error channel
	for {
		for err := range c.Err {
			fmt.Printf("goirc error: %s\n", err)
		}
		if reallyquit {
			break
		}
		fmt.Println("Reconnecting...")
		if err := c.Connect("irc.freenode.net"); err != nil {
			fmt.Printf("Connection error: %s\n", err)
			break
		}
	}
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"errors"
	"net"
	"strings"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	var cert tls.Certificate

	if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
	}

	if err != nil {
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName: gatewayParts[0],
	}

	conn, err := net.Dial("tcp", client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}
<commit_msg>Store a DialFunc for alternate dialers<commit_after>package apns

import (
	"crypto/tls"
	"errors"
	"net"
	"strings"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	DialFunction      func(address string) (net.Conn, error)
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	var cert tls.Certificate

	if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
	}

	if err != nil {
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}
<|endoftext|>


<commit_before>package dns

// A client implementation.

import (
	"bytes"
	"io"
	"net"
	"time"
)

const dnsTimeout time.Duration = 2 * time.Second
const tcpIdleTimeout time.Duration = 8 * time.Second

// A Conn represents a connection to a DNS server.
type Conn struct {
	net.Conn                         // a net.Conn holding the connection
	UDPSize        uint16            // minimum receive buffer for UDP messages
	TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
	rtt            time.Duration
	t              time.Time
	tsigRequestMAC string
}

// A Client defines parameters for a DNS client.
type Client struct {
	Net            string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	UDPSize        uint16            // minimum receive buffer for UDP messages
	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
	ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
	WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
	TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
	SingleInflight bool              // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
	group          singleflight
}

// Exchange performs a synchronous UDP query. It sends the message m to the address
// contained in a and waits for an reply. Exchange does not retry a failed query, nor
// will it fall back to TCP in case of truncation.
// If you need to send a DNS message on an already existing connection, you can use the
// following:
//
//	co := &dns.Conn{Conn: c} // c is your net.Conn
//	co.WriteMsg(m)
//	in, err  := co.ReadMsg()
//	co.Close()
//
func Exchange(m *Msg, a string) (r *Msg, err error) {
	var co *Conn
	co, err = DialTimeout("udp", a, dnsTimeout)
	if err != nil {
		return nil, err
	}

	defer co.Close()
	co.SetReadDeadline(time.Now().Add(dnsTimeout))
	co.SetWriteDeadline(time.Now().Add(dnsTimeout))

	opt := m.IsEdns0()
	// If EDNS0 is used use that for size.
	if opt != nil && opt.UDPSize() >= MinMsgSize {
		co.UDPSize = opt.UDPSize()
	}

	if err = co.WriteMsg(m); err != nil {
		return nil, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, err
}

// ExchangeConn performs a synchronous query. It sends the message m via the connection
// c and waits for a reply. The connection c is not closed by ExchangeConn.
// This function is going away, but can easily be mimicked:
//
//	co := &dns.Conn{Conn: c} // c is your net.Conn
//	co.WriteMsg(m)
//	in, _  := co.ReadMsg()
//	co.Close()
//
func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
	println("dns: this function is deprecated")
	co := new(Conn)
	co.Conn = c
	if err = co.WriteMsg(m); err != nil {
		return nil, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, err
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *dns.Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.Exchange(message, "127.0.0.1:53")
//
// Exchange does not retry a failed query, nor will it fall back to TCP in
// case of truncation.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	if !c.SingleInflight {
		return c.exchange(m, a)
	}
	// This adds a bunch of garbage, TODO(miek).
	t := "nop"
	if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
		t = t1
	}
	cl := "nop"
	if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
		cl = cl1
	}
	r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
		return c.exchange(m, a)
	})
	if err != nil {
		return r, rtt, err
	}
	if shared {
		return r.Copy(), rtt, nil
	}
	return r, rtt, nil
}

func (c *Client) dialTimeout() time.Duration {
	if c.DialTimeout != 0 {
		return c.DialTimeout
	}
	return dnsTimeout
}

func (c *Client) readTimeout() time.Duration {
	if c.ReadTimeout != 0 {
		return c.ReadTimeout
	}
	return dnsTimeout
}

func (c *Client) writeTimeout() time.Duration {
	if c.WriteTimeout != 0 {
		return c.WriteTimeout
	}
	return dnsTimeout
}

func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	var co *Conn
	if c.Net == "" {
		co, err = DialTimeout("udp", a, c.dialTimeout())
	} else {
		co, err = DialTimeout(c.Net, a, c.dialTimeout())
	}
	if err != nil {
		return nil, 0, err
	}
	defer co.Close()

	opt := m.IsEdns0()
	// If EDNS0 is used use that for size.
	if opt != nil && opt.UDPSize() >= MinMsgSize {
		co.UDPSize = opt.UDPSize()
	}
	// Otherwise use the client's configured UDP size.
	if opt == nil && c.UDPSize >= MinMsgSize {
		co.UDPSize = c.UDPSize
	}

	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))

	co.TsigSecret = c.TsigSecret
	if err = co.WriteMsg(m); err != nil {
		return nil, 0, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, co.rtt, err
}

// ReadMsg reads a message from the connection co.
// If the received message contains a TSIG record the transaction
// signature is verified.
func (co *Conn) ReadMsg() (*Msg, error) {
	var p []byte
	m := new(Msg)
	if _, ok := co.Conn.(*net.TCPConn); ok {
		p = make([]byte, MaxMsgSize)
	} else {
		if co.UDPSize >= 512 {
			p = make([]byte, co.UDPSize)
		} else {
			p = make([]byte, MinMsgSize)
		}
	}
	n, err := co.Read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	co.rtt = time.Since(co.t)
	if t := m.IsTsig(); t != nil {
		if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
			return m, ErrSecret
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
	}
	return m, err
}

// Read implements the net.Conn read method.
func (co *Conn) Read(p []byte) (n int, err error) {
	if co.Conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	if t, ok := co.Conn.(*net.TCPConn); ok {
		n, err = t.Read(p[0:2])
		if err != nil || n != 2 {
			return n, err
		}
		l, _ := unpackUint16(p[0:2], 0)
		if l == 0 {
			return 0, ErrShortRead
		}
		if int(l) > len(p) {
			return int(l), io.ErrShortBuffer
		}
		n, err = t.Read(p[:l])
		if err != nil {
			return n, err
		}
		i := n
		for i < int(l) {
			j, err := t.Read(p[i:int(l)])
			if err != nil {
				return i, err
			}
			i += j
		}
		n = i
		return n, err
	}
	// UDP connection
	n, err = co.Conn.Read(p)
	if err != nil {
		return n, err
	}
	return n, err
}

// WriteMsg sends a message throught the connection co.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (co *Conn) WriteMsg(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
		// Set for the next read, allthough only used in zone transfers
		co.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	co.t = time.Now()
	if _, err = co.Write(out); err != nil {
		return err
	}
	return nil
}

// Write implements the net.Conn Write method.
func (co *Conn) Write(p []byte) (n int, err error) {
	if t, ok := co.Conn.(*net.TCPConn); ok {
		lp := len(p)
		if lp < 2 {
			return 0, io.ErrShortBuffer
		}
		if lp > MaxMsgSize {
			return 0, &Error{err: "message too large"}
		}
		l := make([]byte, 2, lp+2)
		l[0], l[1] = packUint16(uint16(lp))
		p = append(l, p...)
		n, err := io.Copy(t, bytes.NewReader(p))
		return int(n), err
	}
	n, err = co.Conn.(*net.UDPConn).Write(p)
	return n, err
}

// Dial connects to the address on the named network.
func Dial(network, address string) (conn *Conn, err error) {
	conn = new(Conn)
	conn.Conn, err = net.Dial(network, address)
	if err != nil {
		return nil, err
	}
	return conn, nil
}

// DialTimeout acts like Dial but takes a timeout.
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
	conn = new(Conn)
	conn.Conn, err = net.DialTimeout(network, address, timeout)
	if err != nil {
		return nil, err
	}
	return conn, nil
}

// Close implements the net.Conn Close method.
func (co *Conn) Close() error { return co.Conn.Close() }

// LocalAddr implements the net.Conn LocalAddr method.
func (co *Conn) LocalAddr() net.Addr { return co.Conn.LocalAddr() }

// RemoteAddr implements the net.Conn RemoteAddr method.
func (co *Conn) RemoteAddr() net.Addr { return co.Conn.RemoteAddr() }

// SetDeadline implements the net.Conn SetDeadline method.
func (co *Conn) SetDeadline(t time.Time) error { return co.Conn.SetDeadline(t) }

// SetReadDeadline implements the net.Conn SetReadDeadline method.
func (co *Conn) SetReadDeadline(t time.Time) error { return co.Conn.SetReadDeadline(t) }

// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (co *Conn) SetWriteDeadline(t time.Time) error { return co.Conn.SetWriteDeadline(t) }
<commit_msg>Removed redundant code #220<commit_after>package dns

// A client implementation.

import (
	"bytes"
	"io"
	"net"
	"time"
)

const dnsTimeout time.Duration = 2 * time.Second
const tcpIdleTimeout time.Duration = 8 * time.Second

// A Conn represents a connection to a DNS server.
type Conn struct {
	net.Conn                         // a net.Conn holding the connection
	UDPSize        uint16            // minimum receive buffer for UDP messages
	TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
	rtt            time.Duration
	t              time.Time
	tsigRequestMAC string
}

// A Client defines parameters for a DNS client.
type Client struct {
	Net            string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	UDPSize        uint16            // minimum receive buffer for UDP messages
	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
	ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
	WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
	TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
	SingleInflight bool              // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
	group          singleflight
}

// Exchange performs a synchronous UDP query. It sends the message m to the address
// contained in a and waits for an reply. Exchange does not retry a failed query, nor
// will it fall back to TCP in case of truncation.
// If you need to send a DNS message on an already existing connection, you can use the
// following:
//
//	co := &dns.Conn{Conn: c} // c is your net.Conn
//	co.WriteMsg(m)
//	in, err  := co.ReadMsg()
//	co.Close()
//
func Exchange(m *Msg, a string) (r *Msg, err error) {
	var co *Conn
	co, err = DialTimeout("udp", a, dnsTimeout)
	if err != nil {
		return nil, err
	}

	defer co.Close()
	co.SetReadDeadline(time.Now().Add(dnsTimeout))
	co.SetWriteDeadline(time.Now().Add(dnsTimeout))

	opt := m.IsEdns0()
	// If EDNS0 is used use that for size.
	if opt != nil && opt.UDPSize() >= MinMsgSize {
		co.UDPSize = opt.UDPSize()
	}

	if err = co.WriteMsg(m); err != nil {
		return nil, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, err
}

// ExchangeConn performs a synchronous query. It sends the message m via the connection
// c and waits for a reply. The connection c is not closed by ExchangeConn.
// This function is going away, but can easily be mimicked:
//
//	co := &dns.Conn{Conn: c} // c is your net.Conn
//	co.WriteMsg(m)
//	in, _  := co.ReadMsg()
//	co.Close()
//
func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
	println("dns: this function is deprecated")
	co := new(Conn)
	co.Conn = c
	if err = co.WriteMsg(m); err != nil {
		return nil, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, err
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *dns.Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.Exchange(message, "127.0.0.1:53")
//
// Exchange does not retry a failed query, nor will it fall back to TCP in
// case of truncation.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	if !c.SingleInflight {
		return c.exchange(m, a)
	}
	// This adds a bunch of garbage, TODO(miek).
	t := "nop"
	if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
		t = t1
	}
	cl := "nop"
	if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
		cl = cl1
	}
	r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
		return c.exchange(m, a)
	})
	if err != nil {
		return r, rtt, err
	}
	if shared {
		return r.Copy(), rtt, nil
	}
	return r, rtt, nil
}

func (c *Client) dialTimeout() time.Duration {
	if c.DialTimeout != 0 {
		return c.DialTimeout
	}
	return dnsTimeout
}

func (c *Client) readTimeout() time.Duration {
	if c.ReadTimeout != 0 {
		return c.ReadTimeout
	}
	return dnsTimeout
}

func (c *Client) writeTimeout() time.Duration {
	if c.WriteTimeout != 0 {
		return c.WriteTimeout
	}
	return dnsTimeout
}

func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	var co *Conn
	if c.Net == "" {
		co, err = DialTimeout("udp", a, c.dialTimeout())
	} else {
		co, err = DialTimeout(c.Net, a, c.dialTimeout())
	}
	if err != nil {
		return nil, 0, err
	}
	defer co.Close()

	opt := m.IsEdns0()
	// If EDNS0 is used use that for size.
	if opt != nil && opt.UDPSize() >= MinMsgSize {
		co.UDPSize = opt.UDPSize()
	}
	// Otherwise use the client's configured UDP size.
	if opt == nil && c.UDPSize >= MinMsgSize {
		co.UDPSize = c.UDPSize
	}

	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))

	co.TsigSecret = c.TsigSecret
	if err = co.WriteMsg(m); err != nil {
		return nil, 0, err
	}
	r, err = co.ReadMsg()
	if err == nil && r.Id != m.Id {
		err = ErrId
	}
	return r, co.rtt, err
}

// ReadMsg reads a message from the connection co.
// If the received message contains a TSIG record the transaction
// signature is verified.
func (co *Conn) ReadMsg() (*Msg, error) {
	var p []byte
	m := new(Msg)
	if _, ok := co.Conn.(*net.TCPConn); ok {
		p = make([]byte, MaxMsgSize)
	} else {
		if co.UDPSize >= 512 {
			p = make([]byte, co.UDPSize)
		} else {
			p = make([]byte, MinMsgSize)
		}
	}
	n, err := co.Read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	co.rtt = time.Since(co.t)
	if t := m.IsTsig(); t != nil {
		if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
			return m, ErrSecret
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
	}
	return m, err
}

// Read implements the net.Conn read method.
func (co *Conn) Read(p []byte) (n int, err error) {
	if co.Conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	if t, ok := co.Conn.(*net.TCPConn); ok {
		n, err = t.Read(p[0:2])
		if err != nil || n != 2 {
			return n, err
		}
		l, _ := unpackUint16(p[0:2], 0)
		if l == 0 {
			return 0, ErrShortRead
		}
		if int(l) > len(p) {
			return int(l), io.ErrShortBuffer
		}
		n, err = t.Read(p[:l])
		if err != nil {
			return n, err
		}
		i := n
		for i < int(l) {
			j, err := t.Read(p[i:int(l)])
			if err != nil {
				return i, err
			}
			i += j
		}
		n = i
		return n, err
	}
	// UDP connection
	n, err = co.Conn.Read(p)
	if err != nil {
		return n, err
	}
	return n, err
}

// WriteMsg sends a message throught the connection co.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (co *Conn) WriteMsg(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
		// Set for the next read, allthough only used in zone transfers
		co.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	co.t = time.Now()
	if _, err = co.Write(out); err != nil {
		return err
	}
	return nil
}

// Write implements the net.Conn Write method.
func (co *Conn) Write(p []byte) (n int, err error) {
	if t, ok := co.Conn.(*net.TCPConn); ok {
		lp := len(p)
		if lp < 2 {
			return 0, io.ErrShortBuffer
		}
		if lp > MaxMsgSize {
			return 0, &Error{err: "message too large"}
		}
		l := make([]byte, 2, lp+2)
		l[0], l[1] = packUint16(uint16(lp))
		p = append(l, p...)
		n, err := io.Copy(t, bytes.NewReader(p))
		return int(n), err
	}
	n, err = co.Conn.(*net.UDPConn).Write(p)
	return n, err
}

// Dial connects to the address on the named network.
func Dial(network, address string) (conn *Conn, err error) {
	conn = new(Conn)
	conn.Conn, err = net.Dial(network, address)
	if err != nil {
		return nil, err
	}
	return conn, nil
}

// DialTimeout acts like Dial but takes a timeout.
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
	conn = new(Conn)
	conn.Conn, err = net.DialTimeout(network, address, timeout)
	if err != nil {
		return nil, err
	}
	return conn, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

// Generate lookup table for CRC-16 calculation
func gen_crc16(poly uint16) {
	fmt.Printf("// Lookup table for CRC-16 calculation with polynomial 0x%04X\n", poly)
	fmt.Printf("var crc16Table = []uint16{\n")
	for i := 0; i < 256; i++ {
		res := uint16(0)
		b := uint16(i << 8)
		for n := 0; n < 8; n++ {
			c := (res ^ b) & (1 << 15)
			res <<= 1
			b <<= 1
			if c != 0 {
				res ^= poly
			}
		}
		if i%8 == 0 {
			fmt.Printf("\t")
		} else {
			fmt.Printf(" ")
		}
		fmt.Printf("0x%04X,", res)
		if (i+1)%8 == 0 {
			fmt.Printf("\n")
		}
	}
	fmt.Printf("}\n")
}

func main() {
	// CCITT polynomial
	gen_crc16(0x1021)
}
<commit_msg>Make gen_crc_table usable from go:generate<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
)

var prog = os.Args[0]
var goFile = os.Getenv("GOFILE")
var goPackage = os.Getenv("GOPACKAGE")

func main() {
	if goFile == "" || goPackage == "" {
		log.Fatal(fmt.Errorf("%s must be run via \"go:generate\" or have GOFILE and GOPACKAGE set", prog))
	}
	kind := goFile[0 : len(goFile)-len(filepath.Ext(goFile))]
	switch kind {
	case "crc8":
		gen_crc8(setup(kind), 0x9B) // WCDMA polynomial
	case "crc16":
		gen_crc16(setup(kind), 0x1021) // CCITT polynomial
	default:
		log.Fatal(fmt.Errorf("GOFILE environment variable must be \"crc8\" or \"crc16\""))
	}
}

func setup(kind string) *os.File {
	f, err := os.Create(fmt.Sprintf("%s_table.go", kind))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(f, "// Generated by gen_crc_table: do not edit\n\n")
	fmt.Fprintf(f, "package %s\n\n", goPackage)
	return f
}

// Generate lookup table for CRC-8 calculation
func gen_crc8(f *os.File, poly uint8) {
	fmt.Fprintf(f, "// Lookup table for CRC-8 calculation with polyomial 0x%02X\n", poly)
	fmt.Fprintf(f, "var crc8Table = []uint8{\n")
	for i := 0; i < 256; i++ {
		res := uint8(i)
		for n := 0; n < 8; n++ {
			c := res & (1 << 7)
			res <<= 1
			if c != 0 {
				res ^= poly
			}
		}
		if i%8 == 0 {
			fmt.Fprintf(f, "\t")
		} else {
			fmt.Fprintf(f, " ")
		}
		fmt.Fprintf(f, "0x%02X,", res)
		if (i+1)%8 == 0 {
			fmt.Fprintf(f, "\n")
		}
	}
	fmt.Fprintf(f, "}\n")
}

// Generate lookup table for CRC-16 calculation
func gen_crc16(f *os.File, poly uint16) {
	fmt.Fprintf(f, "// Lookup table for CRC-16 calculation with polynomial 0x%04X\n", poly)
	fmt.Fprintf(f, "var crc16Table = []uint16{\n")
	for i := 0; i < 256; i++ {
		res := uint16(0)
		b := uint16(i << 8)
		for n := 0; n < 8; n++ {
			c := (res ^ b) & (1 << 15)
			res <<= 1
			b <<= 1
			if c != 0 {
				res ^= poly
			}
		}
		if i%8 == 0 {
			fmt.Fprintf(f, "\t")
		} else {
			fmt.Fprintf(f, " ")
		}
		fmt.Fprintf(f, "0x%04X,", res)
		if (i+1)%8 == 0 {
			fmt.Fprintf(f, "\n")
		}
	}
	fmt.Fprintf(f, "}\n")
}
<|endoftext|>


<commit_before>/*
Copyright Cognition Foundry / Conquex 2017 All Rights Reserved.

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 gohfc

import (
	"github.com/hyperledger/fabric/protos/common"
	"github.com/hyperledger/fabric/protos/peer"
	"github.com/golang/protobuf/proto"
	"encoding/hex"
)

//TODO create channel
//TODO joinChannel
//TODO queryInfo
//TODO queryBlockByHash
//TODO queryBlock
//TODO queryTransaction

// GohfcClient provides higher level API to execute different transactions and operations to fabric
type GohfcClient struct {
	Crypt    CryptSuite
	KVStore  KeyValueStore
	CAClient CAClient
	Peers    []*Peer
	Orderers []*Orderer
}

// QueryResponse is response from query transaction
type QueryResponse struct {
	// TxId is transaction id
	TxId string
	// Input is a slice of parameters used for this query
	Input []string
	// Response is query response from one or more peer
	Response []*PeerResponse
}

// InstallResponse is response from Install request
type InstallResponse struct {
	// TxId is transaction id
	TxId string
	// Response is response from one or more peers
	Response []*PeerResponse
}

// Enroll enrolls already registered user and gets ECert. Request is executed over CAClient implementation
// Note that if enrollmentID is found in key-Value store no request will be executed and data from
// Key-Value store will be returned. This is true even when ECert is revoked. It is a responsibility of developers
// to "clean" Key-Value store.
func (c *GohfcClient) Enroll(enrollmentId, password string) (*Identity, error) {
	prevCert, ok, err := c.KVStore.Get(enrollmentId)
	if err != nil {
		return nil, err
	}
	//return Identity if enrollmentId was found in kv store
	if len(prevCert) > 1 && ok {
		identity, err := UnmarshalIdentity(prevCert)
		if err != nil {
			return nil, err
		}
		return identity, nil
	}

	identity, err := c.CAClient.Enroll(enrollmentId, password)
	if err != nil {
		return nil, err
	}
	marsh, err := MarshalIdentity(identity)

	if err != nil {
		return identity, err
	}

	if err := c.KVStore.Set(enrollmentId, marsh); err != nil {
		return identity, err
	}
	return identity, nil
}

// Register registers new user using CAClient implementation.
func (c *GohfcClient) Register(certificate *Certificate, req *RegistrationRequest) (*CAResponse, error) {
	return c.CAClient.Register(certificate, req)
}

// Query executes query operation over one or many peers.
// Note that this invocation will NOT execute chaincode on ledger and will NOT change height of block-chain.
// Result will be from peers local block-chain data copy. It is very fast and scalable approach but in some rare cases
// peers can be out of sync and return different result from data in actual ledger.
func (c *GohfcClient) Query(certificate *Certificate, chain *Chain, peers []*Peer, args []string) (*QueryResponse, error) {
	prop, err := chain.CreateTransactionProposal(certificate, args)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	return &QueryResponse{Input: r.Input, Response: r.EndorsersResponse, TxId: r.TxId}, nil
}

// Invoke prepares transaction proposal, sends this transaction proposal to peers for endorsement and sends endorsed
// transaction to orderer for execution. This operation will change block-chain and ledger states.
// Note that this operation is asynchronous. Even if this method returns successful execution this does not guaranty
// that actual ledger and block-chain operations are finished and/or are successful.
// Events must be used to listen for block events and compare transaction id (TxId) from this method
// to transaction ids  from events.
func (c *GohfcClient) Invoke(certificate *Certificate, chain *Chain, peers []*Peer, orderers []*Orderer, args []string) (*InvokeResponse, error) {
	prop, err := chain.CreateTransactionProposal(certificate, args)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	result, err := chain.SendTransaction(certificate, r, orderers)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Install will install chaincode to provided peers.
// Note that in this version only Go chaincode is supported for installation.
func (c *GohfcClient) Install(certificate *Certificate, chain *Chain, peers []*Peer, req *InstallRequest) (*InstallResponse, error) {
	prop, err := chain.CreateInstallProposal(certificate, req)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	return &InstallResponse{TxId: r.TxId, Response: r.EndorsersResponse}, nil
}

// GetChannels returns a list of channels that peer has joined.
func (c *GohfcClient) GetChannels(certificate *Certificate, qPeer *Peer, mspId string) (*peer.ChannelQueryResponse, error) {
	chain, err := NewChain("", "cscc", mspId, peer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetChannels"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(peer.ChannelQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// GetInstalledChainCodes returns list of chaincodes that are installed on peer.
// Note that this list contains only chaincodes that are installed but not instantiated.
func (c *GohfcClient) GetInstalledChainCodes(certificate *Certificate, qPeer *Peer, mspId string) (*peer.ChaincodeQueryResponse, error) {
	chain, err := NewChain("", "lccc", mspId, peer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"getinstalledchaincodes"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(peer.ChaincodeQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// GetChannelChainCodes returns list of chaincodes that are instantiated on peer.
// Note that this list contains only chaincodes that are instantiated.
func (c *GohfcClient) GetChannelChainCodes(certificate *Certificate, qPeer *Peer, channelName string, mspId string) (*peer.ChaincodeQueryResponse, error) {
	chain, err := NewChain(channelName, "lccc", mspId, peer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"getchaincodes"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(peer.ChaincodeQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// QueryTransaction will execute query over transaction id. If transaction is not found error is returned.
// Note that this operation is executed on peer not on orderer.
func (c *GohfcClient) QueryTransaction(certificate *Certificate, qPeer *Peer, channelName, txid string, mspId string) (*peer.ProcessedTransaction, *common.Payload, error) {
	chain, err := NewChain("", "qscc", mspId, peer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetTransactionByID", channelName, txid})
	if err != nil {
		return nil, nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, nil, ErrBadTransactionStatus
	}
	transaction := new(peer.ProcessedTransaction)
	payload := new(common.Payload)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, transaction); err != nil {
		return nil, nil, err
	}
	if err := proto.Unmarshal(transaction.TransactionEnvelope.Payload, payload); err != nil {
		return nil, nil, err
	}
	return transaction, payload, nil
}

// Instantiate instantiates already installed chaincode.
func (c *GohfcClient) Instantiate(certificate *Certificate, chain *Chain, peer *Peer, orderer *Orderer, req *InstallRequest, policy *common.SignaturePolicyEnvelope) (*InvokeResponse, error) {

	prop, err := chain.CreateInstantiateProposal(certificate, req, policy)
	if err != nil {
		return nil, err
	}
	rbb := chain.SendTransactionProposal(prop, []*Peer{peer})

	result, err := chain.SendTransaction(certificate, rbb, []*Orderer{orderer})
	if result != nil {
		return nil, err
	}
	return result, nil
}

// RevokeCert revokes ECert on CA
func (c *GohfcClient) RevokeCert(identity *Identity, reason int) (*CAResponse, error) {
	aki := string(hex.EncodeToString(identity.Cert.AuthorityKeyId))
	serial := identity.Cert.SerialNumber.String()
	return c.CAClient.Revoke(identity.Certificate, &(RevocationRequest{AKI: aki, EnrollmentId: identity.EnrollmentId, Serial: serial, Reason: reason}))
}

// JoinChannel will join peers from peers slice to channel. If peer is already in channel error will be returned for
// this particular peer, others will join channel.
func (c *GohfcClient) JoinChannel(certificate *Certificate, channelName string,mspId string, peers []*Peer, pOrderer *Orderer) (*ProposalTransactionResponse, error) {
	chain, err := NewChain("", "cscc", mspId, peer.ChaincodeSpec_GOLANG, c.Crypt)
	if err != nil {
		Logger.Errorf("Error creating new chain: %s", err)
		return nil, err
	}
	prop, err := chain.CreateSeekProposal(certificate, peers, pOrderer, channelName, 0)
	if err != nil {
		return nil, err
	}
	block,err:=pOrderer.GetBlock(&common.Envelope{Payload:prop.Payload,Signature:prop.Proposal.Signature})
	if err != nil {
		return nil, err
	}
	//send proposal with block to peers
	blockData, err := proto.Marshal(block.Block)
	if err != nil {
		Logger.Errorf("Error marshal orderer.DeliverResponse_Block: %s", err)
		return nil, err
	}

	proposal, err := chain.CreateTransactionProposal(certificate, []string{"JoinChain", string(blockData)})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(proposal, peers)
	return r, nil
}

// NewClientFromJSONConfig creates new GohfcClient from json config
func NewClientFromJSONConfig(path string, kvStore KeyValueStore) (*GohfcClient, error) {
	config, err := NewConfigFromJSON(path)
	if err != nil {
		return nil, err
	}
	crypto, err := NewECCryptSuite(&config.Crypt)
	if err != nil {
		return nil, err
	}
	caClient, err := NewFabricCAClientFromConfig(&config.CAServer, crypto, nil)
	if err != nil {
		return nil, err
	}

	peers := make([]*Peer, 0, len(config.Peers))
	for _, peer := range config.Peers {
		peers = append(peers, NewPeerFromConfig(&peer))
	}

	orderers := make([]*Orderer, 0, len(config.Orderers))
	for _, orderer := range config.Orderers {
		orderers = append(orderers, NewOrdererFromConfig(&orderer))
	}
	return &GohfcClient{Crypt: crypto, KVStore: kvStore, CAClient: caClient, Peers: peers, Orderers: orderers}, nil
}
<commit_msg>Implementing queryInfo,queryBlockByHash,queryBlock<commit_after>/*
Copyright Cognition Foundry / Conquex 2017 All Rights Reserved.

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 gohfc

import (
	"github.com/hyperledger/fabric/protos/common"
	protoPeer "github.com/hyperledger/fabric/protos/peer"
	"github.com/golang/protobuf/proto"
	"encoding/hex"
	"strconv"
)

//TODO create channel

// GohfcClient provides higher level API to execute different transactions and operations to fabric
type GohfcClient struct {
	Crypt    CryptSuite
	KVStore  KeyValueStore
	CAClient CAClient
	Peers    []*Peer
	Orderers []*Orderer
}

// QueryResponse is response from query transaction
type QueryResponse struct {
	// TxId is transaction id
	TxId string
	// Input is a slice of parameters used for this query
	Input []string
	// Response is query response from one or more peer
	Response []*PeerResponse
}

// InstallResponse is response from Install request
type InstallResponse struct {
	// TxId is transaction id
	TxId string
	// Response is response from one or more peers
	Response []*PeerResponse
}

// Enroll enrolls already registered user and gets ECert. Request is executed over CAClient implementation
// Note that if enrollmentID is found in key-Value store no request will be executed and data from
// Key-Value store will be returned. This is true even when ECert is revoked. It is a responsibility of developers
// to "clean" Key-Value store.
func (c *GohfcClient) Enroll(enrollmentId, password string) (*Identity, error) {
	prevCert, ok, err := c.KVStore.Get(enrollmentId)
	if err != nil {
		return nil, err
	}
	//return Identity if enrollmentId was found in kv store
	if len(prevCert) > 1 && ok {
		identity, err := UnmarshalIdentity(prevCert)
		if err != nil {
			return nil, err
		}
		return identity, nil
	}

	identity, err := c.CAClient.Enroll(enrollmentId, password)
	if err != nil {
		return nil, err
	}
	marsh, err := MarshalIdentity(identity)

	if err != nil {
		return identity, err
	}

	if err := c.KVStore.Set(enrollmentId, marsh); err != nil {
		return identity, err
	}
	return identity, nil
}

// Register registers new user using CAClient implementation.
func (c *GohfcClient) Register(certificate *Certificate, req *RegistrationRequest) (*CAResponse, error) {
	return c.CAClient.Register(certificate, req)
}

// Query executes query operation over one or many peers.
// Note that this invocation will NOT execute chaincode on ledger and will NOT change height of block-chain.
// Result will be from peers local block-chain data copy. It is very fast and scalable approach but in some rare cases
// peers can be out of sync and return different result from data in actual ledger.
func (c *GohfcClient) Query(certificate *Certificate, chain *Chain, peers []*Peer, args []string) (*QueryResponse, error) {
	prop, err := chain.CreateTransactionProposal(certificate, args)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	return &QueryResponse{Input: r.Input, Response: r.EndorsersResponse, TxId: r.TxId}, nil
}

// Invoke prepares transaction proposal, sends this transaction proposal to peers for endorsement and sends endorsed
// transaction to orderer for execution. This operation will change block-chain and ledger states.
// Note that this operation is asynchronous. Even if this method returns successful execution this does not guaranty
// that actual ledger and block-chain operations are finished and/or are successful.
// Events must be used to listen for block events and compare transaction id (TxId) from this method
// to transaction ids  from events.
func (c *GohfcClient) Invoke(certificate *Certificate, chain *Chain, peers []*Peer, orderers []*Orderer, args []string) (*InvokeResponse, error) {
	prop, err := chain.CreateTransactionProposal(certificate, args)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	result, err := chain.SendTransaction(certificate, r, orderers)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Install will install chaincode to provided peers.
// Note that in this version only Go chaincode is supported for installation.
func (c *GohfcClient) Install(certificate *Certificate, chain *Chain, peers []*Peer, req *InstallRequest) (*InstallResponse, error) {
	prop, err := chain.CreateInstallProposal(certificate, req)
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, peers)
	return &InstallResponse{TxId: r.TxId, Response: r.EndorsersResponse}, nil
}

// GetChannels returns a list of channels that peer has joined.
func (c *GohfcClient) GetChannels(certificate *Certificate, qPeer *Peer, mspId string) (*protoPeer.ChannelQueryResponse, error) {
	chain, err := NewChain("", "cscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetChannels"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(protoPeer.ChannelQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// GetInstalledChainCodes returns list of chaincodes that are installed on peer.
// Note that this list contains only chaincodes that are installed but not instantiated.
func (c *GohfcClient) GetInstalledChainCodes(certificate *Certificate, qPeer *Peer, mspId string) (*protoPeer.ChaincodeQueryResponse, error) {
	chain, err := NewChain("", "lccc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"getinstalledchaincodes"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(protoPeer.ChaincodeQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// GetChannelChainCodes returns list of chaincodes that are instantiated on peer.
// Note that this list contains only chaincodes that are instantiated.
func (c *GohfcClient) GetChannelChainCodes(certificate *Certificate, qPeer *Peer, channelName string, mspId string) (*protoPeer.ChaincodeQueryResponse, error) {
	chain, err := NewChain(channelName, "lccc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"getchaincodes"})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	ch := new(protoPeer.ChaincodeQueryResponse)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

// QueryTransaction will execute query over transaction id. If transaction is not found error is returned.
// Note that this operation is executed on peer not on orderer.
func (c *GohfcClient) QueryTransaction(certificate *Certificate, qPeer *Peer, channelName, txid string, mspId string) (*protoPeer.ProcessedTransaction, *common.Payload, error) {
	chain, err := NewChain("", "qscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetTransactionByID", channelName, txid})
	if err != nil {
		return nil, nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{qPeer})
	if r.EndorsersResponse[0].Err != nil {
		return nil, nil, r.EndorsersResponse[0].Err
	}
	if r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, nil, ErrBadTransactionStatus
	}
	transaction := new(protoPeer.ProcessedTransaction)
	payload := new(common.Payload)
	if err := proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, transaction); err != nil {
		return nil, nil, err
	}
	if err := proto.Unmarshal(transaction.TransactionEnvelope.Payload, payload); err != nil {
		return nil, nil, err
	}
	return transaction, payload, nil
}

// Instantiate instantiates already installed chaincode.
func (c *GohfcClient) Instantiate(certificate *Certificate, chain *Chain, peer *Peer, orderer *Orderer, req *InstallRequest, policy *common.SignaturePolicyEnvelope) (*InvokeResponse, error) {

	prop, err := chain.CreateInstantiateProposal(certificate, req, policy)
	if err != nil {
		return nil, err
	}
	rbb := chain.SendTransactionProposal(prop, []*Peer{peer})

	result, err := chain.SendTransaction(certificate, rbb, []*Orderer{orderer})
	if result != nil {
		return nil, err
	}
	return result, nil
}

// RevokeCert revokes ECert on CA
func (c *GohfcClient) RevokeCert(identity *Identity, reason int) (*CAResponse, error) {
	aki := string(hex.EncodeToString(identity.Cert.AuthorityKeyId))
	serial := identity.Cert.SerialNumber.String()
	return c.CAClient.Revoke(identity.Certificate, &(RevocationRequest{AKI: aki, EnrollmentId: identity.EnrollmentId, Serial: serial, Reason: reason}))
}

// JoinChannel will join peers from peers slice to channel. If peer is already in channel error will be returned for
// this particular peer, others will join channel.
func (c *GohfcClient) JoinChannel(certificate *Certificate, channelName string, mspId string, peers []*Peer, pOrderer *Orderer) (*ProposalTransactionResponse, error) {
	chain, err := NewChain("", "cscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	if err != nil {
		Logger.Errorf("Error creating new chain: %s", err)
		return nil, err
	}
	prop, err := chain.CreateSeekProposal(certificate, peers, pOrderer, channelName, 0)
	if err != nil {
		return nil, err
	}
	block, err := pOrderer.GetBlock(&common.Envelope{Payload: prop.Payload, Signature: prop.Proposal.Signature})
	if err != nil {
		return nil, err
	}
	//send proposal with block to peers
	blockData, err := proto.Marshal(block.Block)
	if err != nil {
		Logger.Errorf("Error marshal orderer.DeliverResponse_Block: %s", err)
		return nil, err
	}

	proposal, err := chain.CreateTransactionProposal(certificate, []string{"JoinChain", string(blockData)})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(proposal, peers)
	return r, nil
}

//QueryInfo gets blockchain data as current height, current hash and previous hash.
func (c *GohfcClient) QueryInfo(certificate *Certificate, channelName string, mspId string, peer *Peer) (*common.BlockchainInfo, error) {
	chain, err := NewChain(channelName, "qscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	if err != nil {
		Logger.Errorf("Error creating new chain: %s", err)
		return nil, err
	}
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetChainInfo", channelName})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{peer})
	if r.EndorsersResponse[0].Err != nil || r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	var info = new(common.BlockchainInfo)
	err = proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, info)
	if err != nil {
		Logger.Errorf("Error unmarshal common.BlockchainInfo: %s", err)
		return nil, ErrBadTransactionStatus
	}
	return info, nil
}

//QueryBlockByHash returns data stored in block that is identified with provided hash.
func (c *GohfcClient) QueryBlockByHash(certificate *Certificate, channelName, mspId, blockHash string, peer *Peer) (*common.Block, error) {
	chain, err := NewChain(channelName, "qscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	if err != nil {
		Logger.Errorf("Error creating new chain: %s", err)
		return nil, err
	}

	decHash, err := hex.DecodeString(blockHash)
	if err != nil {
		Logger.Errorf("Error decode hex string: %s", err)
		return nil, err
	}
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetBlockByHash", channelName, string(decHash)})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{peer})
	if r.EndorsersResponse[0].Err != nil || r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	var block = new(common.Block)
	err = proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, block)
	if err != nil {
		Logger.Errorf("Error unmarshal common.BlockchainInfo: %s", err)
		return nil, ErrBadTransactionStatus
	}
	return block, nil
}

//QueryBlock returns data stored in block that is identified with provided number.
func (c *GohfcClient) QueryBlock(certificate *Certificate, channelName, mspId string, blockNumber uint64, peer *Peer) (*common.Block, error) {
	chain, err := NewChain(channelName, "qscc", mspId, protoPeer.ChaincodeSpec_GOLANG, c.Crypt)
	if err != nil {
		Logger.Errorf("Error creating new chain: %s", err)
		return nil, err
	}
	prop, err := chain.CreateTransactionProposal(certificate, []string{"GetBlockByNumber", channelName, strconv.FormatUint(blockNumber,10)})
	if err != nil {
		return nil, err
	}
	r := chain.SendTransactionProposal(prop, []*Peer{peer})
	if r.EndorsersResponse[0].Err != nil || r.EndorsersResponse[0].Response.Response.Status != 200 {
		return nil, ErrBadTransactionStatus
	}
	var block = new(common.Block)
	err = proto.Unmarshal(r.EndorsersResponse[0].Response.Response.Payload, block)
	if err != nil {
		Logger.Errorf("Error unmarshal common.BlockchainInfo: %s", err)
		return nil, ErrBadTransactionStatus
	}
	return block, nil
}

// NewClientFromJSONConfig creates new GohfcClient from json config
func NewClientFromJSONConfig(path string, kvStore KeyValueStore) (*GohfcClient, error) {
	config, err := NewConfigFromJSON(path)
	if err != nil {
		return nil, err
	}
	crypto, err := NewECCryptSuite(&config.Crypt)
	if err != nil {
		return nil, err
	}
	caClient, err := NewFabricCAClientFromConfig(&config.CAServer, crypto, nil)
	if err != nil {
		return nil, err
	}

	peers := make([]*Peer, 0, len(config.Peers))
	for _, peer := range config.Peers {
		peers = append(peers, NewPeerFromConfig(&peer))
	}

	orderers := make([]*Orderer, 0, len(config.Orderers))
	for _, orderer := range config.Orderers {
		orderers = append(orderers, NewOrdererFromConfig(&orderer))
	}
	return &GohfcClient{Crypt: crypto, KVStore: kvStore, CAClient: caClient, Peers: peers, Orderers: orderers}, nil
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"net"
	"strings"
	"appengine"
	"sync"
	"encoding/binary"
	"bytes"
	"errors"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	sync.Mutex

	ctx			  appengine.Context

	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	DialFunction      func(address string) (net.Conn, error)
	Closed			  bool

	pushNotifCh	  chan *PushNotification
	FailCh		  chan *PushNotificationResponse

	SocketCloseCh chan struct{}

	doneCh		  chan struct{}
	apnsRespCh	  chan []byte

	certificate		  tls.Certificate
	apnsConn		*tls.Conn
}

type errResponse struct {
	Command uint8
	Status uint8
	Identifier int32
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(ctx appengine.Context, gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(ctx appengine.Context, gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

func (client *Client) Open() error {
	if client.apnsConn == nil {
		return client.openConnection()
	}
	return nil
}

func (client *Client) openConnection() error {
	err := client.getCertificate()
	if err != nil {
		client.ctx.Errorf("Error getting cert: %v", err)
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
		ServerName:   gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		client.ctx.Errorf("Error dialing on gateway: %v, %v", client.Gateway, err)
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		client.ctx.Errorf("Error doing handshake: %v", err)
		return err
	}

	client.apnsConn = tlsConn
	client.initChans()

	go client.loop()
	return nil
}

func (client *Client) initChans() {
	client.pushNotifCh = make(chan *PushNotification)
	client.FailCh = make(chan *PushNotificationResponse)

	client.SocketCloseCh = make(chan struct{})

	client.doneCh = make(chan struct{})

	client.apnsRespCh = make(chan []byte)
}

func (client *Client) Close() {
	client.ctx.Debugf("Closing")

	client.Lock()
	defer client.Unlock()

	if client.apnsConn == nil {
		return
	}
	close(client.SocketCloseCh)
	close(client.doneCh)
	client.apnsConn.Close()
	client.apnsConn = nil
	client.Closed = true
}

func (client *Client) EnqueuePushNotif(pn *PushNotification) error {
	select {
	case client.pushNotifCh <- pn:
		return nil
	case <- client.doneCh:
		return errors.New("Done channel was fired probably because client was closed.")
	}
}

func (client *Client) readLoop() {
	client.ctx.Debugf("Starting read loop")
	outter: for {
		if client.apnsConn == nil {
			return
		}
		time.Sleep(time.Millisecond * 1200)

		buffer := make([]byte, 6, 6)
		_, err := client.apnsConn.Read(buffer)
		if err != nil {
			client.ctx.Warningf("Got error reading apnsConn: %v", err)
			for strings.HasPrefix(err.Error(), "API error 1") {
				time.Sleep(time.Millisecond * 100)
				continue outter
			}
			client.ctx.Warningf("Closing", err)
			client.Close()
		}
		client.apnsRespCh <- buffer
	}
}

func (client *Client) loop() {
	firstRun := false
	outer: for {
		client.ctx.Infof("Next iteration is starting")
		select {
		case <-client.doneCh:
			client.ctx.Debugf("DoneCh finishing up loop")
			return
		case pn := <-client.pushNotifCh:
			if pn == nil {
				client.ctx.Errorf("Client got nil push notification.")
				continue outer
			}
			// resp := client.Send(pn)
			// client.ctx.Debugf("Sending pn got resp: %+v", resp)

			client.ctx.Debugf("Got push notif from channel")
			payload, err := pn.ToBytes()
			if err != nil {
				client.ctx.Errorf("Erorr serializing pn to bytes: %v", err)
				client.Close()
			}

			client.ctx.Debugf("Writing notif to socket")
			_, err = client.apnsConn.Write(payload)
			if err != nil {
				client.ctx.Warningf("1 Got error writing apnsConn: %v", err)
				client.ctx.Warningf("Closing")
				client.Close()
			}
			client.ctx.Debugf("Succeeded write")

			if !firstRun {
				firstRun = true
				go client.readLoop()
			}
		case buffer := <-client.apnsRespCh:
			client.ctx.Debugf("Got buffer from respch")
			errRsp := &errResponse{
				Command: uint8(buffer[0]),
				Status:  uint8(buffer[1]),
			}

			if err := binary.Read(bytes.NewBuffer(buffer[2:]), binary.BigEndian, &errRsp.Identifier); err != nil {
				client.ctx.Errorf("Read identifier err: %v", err)
				return
			}

			client.ctx.Debugf("Got response of: %+v", errRsp)

			resp := new(PushNotificationResponse)
			resp.Success = false
			client.FailCh <- resp
		}
	}
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	var cert tls.Certificate

	if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
	}

	if err != nil {
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName: gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}

// From: https://github.com/quexer/apns/blob/master/client.go
// Returns a certificate to use to send the notification.
// The certificate is only created once to save on
// the overhead of the crypto libraries.
func (client *Client) getCertificate() error {
	var err error

	/*if client.certificate.PrivateKey == nil {*/
		if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
			// The user did not specify raw block contents, so check the filesystem.
			client.certificate, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
		} else {
			// The user provided the raw block contents, so use that.
			client.certificate, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
		}
	/*}*/

	return err
}
<commit_msg>Make readLoop respect done channel<commit_after>package apns

import (
	"crypto/tls"
	"net"
	"strings"
	"appengine"
	"sync"
	"encoding/binary"
	"bytes"
	"errors"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	sync.Mutex

	ctx			  appengine.Context

	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	DialFunction      func(address string) (net.Conn, error)
	Closed			  bool

	pushNotifCh	  chan *PushNotification
	FailCh		  chan *PushNotificationResponse

	SocketCloseCh chan struct{}

	doneCh		  chan struct{}
	apnsRespCh	  chan []byte

	certificate		  tls.Certificate
	apnsConn		*tls.Conn
}

type errResponse struct {
	Command uint8
	Status uint8
	Identifier int32
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(ctx appengine.Context, gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(ctx appengine.Context, gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

func (client *Client) Open() error {
	if client.apnsConn == nil {
		return client.openConnection()
	}
	return nil
}

func (client *Client) openConnection() error {
	err := client.getCertificate()
	if err != nil {
		client.ctx.Errorf("Error getting cert: %v", err)
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
		ServerName:   gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		client.ctx.Errorf("Error dialing on gateway: %v, %v", client.Gateway, err)
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		client.ctx.Errorf("Error doing handshake: %v", err)
		return err
	}

	client.apnsConn = tlsConn
	client.initChans()

	go client.loop()
	return nil
}

func (client *Client) initChans() {
	client.pushNotifCh = make(chan *PushNotification)
	client.FailCh = make(chan *PushNotificationResponse)

	client.SocketCloseCh = make(chan struct{})

	client.doneCh = make(chan struct{})

	client.apnsRespCh = make(chan []byte)
}

func (client *Client) Close() {
	client.ctx.Debugf("Closing")

	client.Lock()
	defer client.Unlock()

	if client.apnsConn == nil {
		return
	}
	close(client.SocketCloseCh)
	close(client.doneCh)
	client.apnsConn.Close()
	client.apnsConn = nil
	client.Closed = true
}

func (client *Client) EnqueuePushNotif(pn *PushNotification) error {
	select {
	case client.pushNotifCh <- pn:
		return nil
	case <- client.doneCh:
		return errors.New("Done channel was fired probably because client was closed.")
	}
}

func (client *Client) readLoop() {
	client.ctx.Debugf("Starting read loop")
	outter: for {
		if client.apnsConn == nil {
			return
		}
		select {
		case <- time.Tick(time.Millisecond * 1200):
			client.ctx.Infof("Tyring to read response from socket")
		case <- client.doneCh:
			client.ctx.Infof("Closing read loop as client has been closed")
			return
		}

		buffer := make([]byte, 6, 6)
		_, err := client.apnsConn.Read(buffer)
		if err != nil {
			client.ctx.Warningf("Got error reading apnsConn: %v", err)
			for strings.HasPrefix(err.Error(), "API error 1") {
				time.Sleep(time.Millisecond * 100)
				continue outter
			}
			client.ctx.Warningf("Closing: %+v", err)
			client.Close()
		}
		client.apnsRespCh <- buffer
	}
}

func (client *Client) loop() {
	firstRun := false
	outer: for {
		client.ctx.Infof("Next iteration is starting")
		select {
		case <-client.doneCh:
			client.ctx.Debugf("DoneCh finishing up loop")
			return
		case pn := <-client.pushNotifCh:
			if pn == nil {
				client.ctx.Errorf("Client got nil push notification.")
				continue outer
			}
			// resp := client.Send(pn)
			// client.ctx.Debugf("Sending pn got resp: %+v", resp)

			client.ctx.Debugf("Got push notif from channel")
			payload, err := pn.ToBytes()
			if err != nil {
				client.ctx.Errorf("Erorr serializing pn to bytes: %v", err)
				client.Close()
			}

			client.ctx.Debugf("Writing notif to socket")
			_, err = client.apnsConn.Write(payload)
			if err != nil {
				client.ctx.Warningf("1 Got error writing apnsConn: %v", err)
				client.ctx.Warningf("Closing")
				client.Close()
			}
			client.ctx.Debugf("Succeeded write")

			if !firstRun {
				firstRun = true
				go client.readLoop()
			}
		case buffer := <-client.apnsRespCh:
			client.ctx.Debugf("Got buffer from respch")
			errRsp := &errResponse{
				Command: uint8(buffer[0]),
				Status:  uint8(buffer[1]),
			}

			if err := binary.Read(bytes.NewBuffer(buffer[2:]), binary.BigEndian, &errRsp.Identifier); err != nil {
				client.ctx.Errorf("Read identifier err: %v", err)
				return
			}

			client.ctx.Debugf("Got response of: %+v", errRsp)

			resp := new(PushNotificationResponse)
			resp.Success = false
			client.FailCh <- resp
		}
	}
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	var cert tls.Certificate

	if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
	}

	if err != nil {
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName: gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}

// From: https://github.com/quexer/apns/blob/master/client.go
// Returns a certificate to use to send the notification.
// The certificate is only created once to save on
// the overhead of the crypto libraries.
func (client *Client) getCertificate() error {
	var err error

	/*if client.certificate.PrivateKey == nil {*/
		if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
			// The user did not specify raw block contents, so check the filesystem.
			client.certificate, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
		} else {
			// The user provided the raw block contents, so use that.
			client.certificate, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
		}
	/*}*/

	return err
}
<|endoftext|>