Use and vendor golang.org/x/net/ipv4 and golang.org/x/net/ipv6 (#594)

This commit is contained in:
James Hartig 2017-12-04 04:59:24 -05:00 committed by Miek Gieben
parent 6da3249dfb
commit 325e98bb86
625 changed files with 159981 additions and 577 deletions

8
Gopkg.lock generated
View File

@ -7,9 +7,15 @@
packages = ["ed25519","ed25519/internal/edwards25519"]
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "5046e265393bd5e54f570ce29ae8bc6fa3f30ef5110e922996540400f287c64a"
inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,7 +0,0 @@
// +build linux
package socket
func (h *cmsghdr) len() int { return int(h.Len) }
func (h *cmsghdr) lvl() int { return int(h.Level) }
func (h *cmsghdr) typ() int { return int(h.Type) }

View File

@ -1,118 +0,0 @@
package socket
import (
"errors"
"unsafe"
)
func controlHeaderLen() int {
return roundup(sizeofCmsghdr)
}
func controlMessageLen(dataLen int) int {
return roundup(sizeofCmsghdr) + dataLen
}
// returns the whole length of control message.
func ControlMessageSpace(dataLen int) int {
return roundup(sizeofCmsghdr) + roundup(dataLen)
}
// A ControlMessage represents the head message in a stream of control
// messages.
//
// A control message comprises of a header, data and a few padding
// fields to conform to the interface to the kernel.
//
// See RFC 3542 for further information.
type ControlMessage []byte
// Data returns the data field of the control message at the head.
func (m ControlMessage) Data(dataLen int) []byte {
l := controlHeaderLen()
if len(m) < l || len(m) < l+dataLen {
return nil
}
return m[l : l+dataLen]
}
// ParseHeader parses and returns the header fields of the control
// message at the head.
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
l := controlHeaderLen()
if len(m) < l {
return 0, 0, 0, errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
}
// Next returns the control message at the next.
func (m ControlMessage) Next(dataLen int) ControlMessage {
l := ControlMessageSpace(dataLen)
if len(m) < l {
return nil
}
return m[l:]
}
// MarshalHeader marshals the header fields of the control message at
// the head.
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
if len(m) < controlHeaderLen() {
return errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
h.set(controlMessageLen(dataLen), lvl, typ)
return nil
}
// Marshal marshals the control message at the head, and returns the next
// control message.
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
l := len(data)
if len(m) < ControlMessageSpace(l) {
return nil, errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
h.set(controlMessageLen(l), lvl, typ)
if l > 0 {
copy(m.Data(l), data)
}
return m.Next(l), nil
}
// Parse parses as a single or multiple control messages.
func (m ControlMessage) Parse() ([]ControlMessage, error) {
var ms []ControlMessage
for len(m) >= controlHeaderLen() {
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
l := h.len()
if l <= 0 {
return nil, errors.New("invalid header length")
}
if uint64(l) < uint64(controlHeaderLen()) {
return nil, errors.New("invalid message length")
}
if uint64(l) > uint64(len(m)) {
return nil, errors.New("short buffer")
}
ms = append(ms, ControlMessage(m[:l]))
ll := l - controlHeaderLen()
if len(m) >= ControlMessageSpace(ll) {
m = m[ControlMessageSpace(ll):]
} else {
m = m[controlMessageLen(ll):]
}
}
return ms, nil
}
// NewControlMessage returns a new stream of control messages.
func NewControlMessage(dataLen []int) ControlMessage {
var l int
for i := range dataLen {
l += ControlMessageSpace(dataLen[i])
}
return make([]byte, l)
}

View File

@ -1,103 +0,0 @@
// +build linux
package socket
import (
"bytes"
"testing"
)
type mockControl struct {
Level int
Type int
Data []byte
}
func TestControlMessage(t *testing.T) {
for _, tt := range []struct {
cs []mockControl
}{
{
[]mockControl{
{Level: 1, Type: 1},
},
},
{
[]mockControl{
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
{
[]mockControl{
{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
} {
var w []byte
var tailPadLen int
mm := NewControlMessage([]int{0})
for i, c := range tt.cs {
m := NewControlMessage([]int{len(c.Data)})
l := len(m) - len(mm)
if i == len(tt.cs)-1 && l > len(c.Data) {
tailPadLen = l - len(c.Data)
}
w = append(w, m...)
}
var err error
ww := make([]byte, len(w))
copy(ww, w)
m := ControlMessage(ww)
for _, c := range tt.cs {
if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
}
copy(m.Data(len(c.Data)), c.Data)
m = m.Next(len(c.Data))
}
m = ControlMessage(w)
for _, c := range tt.cs {
m, err = m.Marshal(c.Level, c.Type, c.Data)
if err != nil {
t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
}
}
if !bytes.Equal(ww, w) {
t.Fatalf("got %#v; want %#v", ww, w)
}
ws := [][]byte{w}
if tailPadLen > 0 {
// Test a message with no tail padding.
nopad := w[:len(w)-tailPadLen]
ws = append(ws, [][]byte{nopad}...)
}
for _, w := range ws {
ms, err := ControlMessage(w).Parse()
if err != nil {
t.Fatalf("(%v).Parse() = %v", tt.cs, err)
}
for i, m := range ms {
lvl, typ, dataLen, err := m.ParseHeader()
if err != nil {
t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
}
if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
}
}
}
}
}

View File

@ -1,4 +0,0 @@
// Package socket contains ControlMessage parsing code from
// golang.org/x/net/internal/socket. Instead of supporting all possible
// architectures, we're only supporting linux 32/64 bit.
package socket

View File

@ -1,14 +0,0 @@
package socket
import "unsafe"
var (
kernelAlign = func() int {
var p uintptr
return int(unsafe.Sizeof(p))
}()
)
func roundup(l int) int {
return (l + kernelAlign - 1) & ^(kernelAlign - 1)
}

52
udp.go
View File

@ -4,6 +4,9 @@ package dns
import (
"net"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// SessionUDP holds the remote address and the associated
@ -34,12 +37,53 @@ func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, e
return n, err
}
func setUDPSocketOptions(conn *net.UDPConn) error {
// Try setting the flags for both families and ignore the errors unless they
// both error.
err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
if err6 != nil && err4 != nil {
return err4
}
return nil
}
// parseDstFromOOB takes oob data and returns the destination IP.
func parseDstFromOOB(oob []byte) net.IP {
// Start with IPv6 and then fallback to IPv4
// TODO(fastest963): Figure out a way to prefer one or the other. Looking at
// the lvl of the header for a 0 or 41 isn't cross-platform.
var dst net.IP
cm6 := new(ipv6.ControlMessage)
if cm6.Parse(oob) == nil {
dst = cm6.Dst
}
if dst == nil {
cm4 := new(ipv4.ControlMessage)
if cm4.Parse(oob) == nil {
dst = cm4.Dst
}
}
return dst
}
// correctSource takes oob data and returns new oob data with the Src equal to the Dst
func correctSource(oob []byte) []byte {
dst, err := parseUDPSocketDst(oob)
// If the destination could not be determined, ignore.
if err != nil || dst == nil {
dst := parseDstFromOOB(oob)
if dst == nil {
return nil
}
return marshalUDPSocketSrc(dst)
// If the dst is definitely an IPv6, then use ipv6's ControlMessage to
// respond otherwise use ipv4's because ipv6's marshal ignores ipv4
// addresses.
if dst.To4() == nil {
cm := new(ipv6.ControlMessage)
cm.Src = dst
oob = cm.Marshal()
} else {
cm := new(ipv4.ControlMessage)
cm.Src = dst
oob = cm.Marshal()
}
return oob
}

View File

@ -1,220 +0,0 @@
// +build linux,!appengine
package dns
// See:
// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
//
// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
// interface, this might not always be the correct one. This code will make sure the egress
// packet's interface matched the ingress' one.
import (
"net"
"syscall"
"unsafe"
"github.com/miekg/dns/internal/socket"
)
const (
sizeofInet6Pktinfo = 0x14
sizeofInetPktinfo = 0xc
protocolIP = 0
protocolIPv6 = 41
)
type inetPktinfo struct {
Ifindex int32
Spec_dst [4]byte /* in_addr */
Addr [4]byte /* in_addr */
}
type inet6Pktinfo struct {
Addr [16]byte /* in6_addr */
Ifindex int32
}
type inetControlMessage struct {
Src net.IP // source address, specifying only
Dst net.IP // destination address, receiving only
}
// setUDPSocketOptions sets the UDP socket options.
// This function is implemented on a per platform basis. See udp_*.go for more details
func setUDPSocketOptions(conn *net.UDPConn) error {
sa, err := getUDPSocketName(conn)
if err != nil {
return err
}
switch sa.(type) {
case *syscall.SockaddrInet6:
v6only, err := getUDPSocketOptions6Only(conn)
if err != nil {
return err
}
setUDPSocketOptions6(conn)
if !v6only {
setUDPSocketOptions4(conn)
}
case *syscall.SockaddrInet4:
setUDPSocketOptions4(conn)
}
return nil
}
// setUDPSocketOptions4 prepares the v4 socket for sessions.
func setUDPSocketOptions4(conn *net.UDPConn) error {
file, err := conn.File()
if err != nil {
return err
}
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
file.Close()
return err
}
// Calling File() above results in the connection becoming blocking, we must fix that.
// See https://github.com/miekg/dns/issues/279
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
file.Close()
return err
}
file.Close()
return nil
}
// setUDPSocketOptions6 prepares the v6 socket for sessions.
func setUDPSocketOptions6(conn *net.UDPConn) error {
file, err := conn.File()
if err != nil {
return err
}
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
file.Close()
return err
}
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
file.Close()
return err
}
file.Close()
return nil
}
// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
// (dualstack).
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
file, err := conn.File()
if err != nil {
return false, err
}
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
if err != nil {
file.Close()
return false, err
}
file.Close()
return v6only == 1, nil
}
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
file, err := conn.File()
if err != nil {
return nil, err
}
defer file.Close()
return syscall.Getsockname(int(file.Fd()))
}
// marshalInetPacketInfo marshals a ipv4 control message, returning
// the byte slice for the next marshal, if any
func marshalInetPacketInfo(b []byte, cm *inetControlMessage) []byte {
m := socket.ControlMessage(b)
m.MarshalHeader(protocolIP, syscall.IP_PKTINFO, sizeofInetPktinfo)
if cm != nil {
pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
if ip := cm.Src.To4(); ip != nil {
copy(pi.Spec_dst[:], ip)
}
}
return m.Next(sizeofInetPktinfo)
}
// marshalInet6PacketInfo marshals a ipv6 control message, returning
// the byte slice for the next marshal, if any
func marshalInet6PacketInfo(b []byte, cm *inetControlMessage) []byte {
m := socket.ControlMessage(b)
m.MarshalHeader(protocolIPv6, syscall.IPV6_PKTINFO, sizeofInet6Pktinfo)
if cm != nil {
pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0]))
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
copy(pi.Addr[:], ip)
}
}
return m.Next(sizeofInet6Pktinfo)
}
func parseInetPacketInfo(cm *inetControlMessage, b []byte) {
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
if len(cm.Dst) < net.IPv4len {
cm.Dst = make(net.IP, net.IPv4len)
}
copy(cm.Dst, pi.Addr[:])
}
func parseInet6PacketInfo(cm *inetControlMessage, b []byte) {
pi := (*inet6Pktinfo)(unsafe.Pointer(&b[0]))
if len(cm.Dst) < net.IPv6len {
cm.Dst = make(net.IP, net.IPv6len)
}
copy(cm.Dst, pi.Addr[:])
}
// parseUDPSocketDst takes out-of-band data from ReadMsgUDP and parses it for
// the Dst address
func parseUDPSocketDst(oob []byte) (net.IP, error) {
cm := new(inetControlMessage)
ms, err := socket.ControlMessage(oob).Parse()
if err != nil {
return nil, err
}
for _, m := range ms {
lvl, typ, l, err := m.ParseHeader()
if err != nil {
return nil, err
}
if lvl == protocolIPv6 { // IPv6
if typ == syscall.IPV6_PKTINFO && l >= sizeofInet6Pktinfo {
parseInet6PacketInfo(cm, m.Data(l))
}
} else if lvl == protocolIP { // IPv4
if typ == syscall.IP_PKTINFO && l >= sizeofInetPktinfo {
parseInetPacketInfo(cm, m.Data(l))
}
}
}
return cm.Dst, nil
}
// marshalUDPSocketSrc takes the given src address and returns out-of-band data
// to give to WriteMsgUDP
func marshalUDPSocketSrc(src net.IP) []byte {
var oob []byte
// If the dst is definitely an ipv6, then use ipv6 control to respond
// otherwise use ipv4 because the ipv6 marshal ignores ipv4 messages.
// See marshalInet6PacketInfo
cm := new(inetControlMessage)
cm.Src = src
if src.To4() == nil {
oob = make([]byte, socket.ControlMessageSpace(sizeofInet6Pktinfo))
marshalInet6PacketInfo(oob, cm)
} else {
oob = make([]byte, socket.ControlMessageSpace(sizeofInetPktinfo))
marshalInetPacketInfo(oob, cm)
}
return oob
}

View File

@ -1,68 +0,0 @@
// +build linux,!appengine
package dns
import (
"bytes"
"net"
"testing"
)
func TestParseUDPSocketDst(t *testing.T) {
// dst is :ffff:100.100.100.100
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
dst, err := parseUDPSocketDst(oob)
if err != nil {
t.Fatalf("error parsing ipv6 oob: %v", err)
}
dst4 := dst.To4()
if dst4 == nil {
t.Errorf("failed to parse ipv4: %v", dst)
} else if dst4.String() != "100.100.100.100" {
t.Errorf("unexpected ipv4: %v", dst4)
}
// dst is 2001:db8::1
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
dst, err = parseUDPSocketDst(oob)
if err != nil {
t.Fatalf("error parsing ipv6 oob: %v", err)
}
dst6 := dst.To16()
if dst6 == nil {
t.Errorf("failed to parse ipv6: %v", dst)
} else if dst6.String() != "2001:db8::1" {
t.Errorf("unexpected ipv6: %v", dst4)
}
// dst is 100.100.100.100 but was received on 10.10.10.10
oob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}
dst, err = parseUDPSocketDst(oob)
if err != nil {
t.Fatalf("error parsing ipv4 oob: %v", err)
}
dst4 = dst.To4()
if dst4 == nil {
t.Errorf("failed to parse ipv4: %v", dst)
} else if dst4.String() != "100.100.100.100" {
t.Errorf("unexpected ipv4: %v", dst4)
}
}
func TestMarshalUDPSocketSrc(t *testing.T) {
// src is 100.100.100.100
exoob := []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0}
oob := marshalUDPSocketSrc(net.ParseIP("100.100.100.100"))
if !bytes.Equal(exoob, oob) {
t.Errorf("expected ipv4 oob:\n%v", exoob)
t.Errorf("actual ipv4 oob:\n%v", oob)
}
// src is 2001:db8::1
exoob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}
oob = marshalUDPSocketSrc(net.ParseIP("2001:db8::1"))
if !bytes.Equal(exoob, oob) {
t.Errorf("expected ipv6 oob:\n%v", exoob)
t.Errorf("actual ipv6 oob:\n%v", oob)
}
}

View File

@ -1,17 +0,0 @@
// +build !linux appengine
package dns
import (
"net"
)
// These do nothing. See udp_linux.go for an example of how to implement this.
// We tried to adhire to some kind of naming scheme.
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
func setUDPSocketOptions4(conn *net.UDPConn) error { return nil }
func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) { return false, nil }
func parseUDPSocketDst(oob []byte) (net.IP, error) { return nil, nil }
func marshalUDPSocketSrc(src net.IP) []byte { return nil }

126
udp_test.go Normal file
View File

@ -0,0 +1,126 @@
// +build linux,!appengine
package dns
import (
"bytes"
"net"
"strings"
"testing"
"time"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
func TestSetUDPSocketOptions(t *testing.T) {
// returns an error if we cannot resolve that address
testFamily := func(n, addr string) error {
a, err := net.ResolveUDPAddr(n, addr)
if err != nil {
return err
}
c, err := net.ListenUDP(n, a)
if err != nil {
return err
}
if err := setUDPSocketOptions(c); err != nil {
t.Fatalf("failed to set socket options: %v", err)
}
ch := make(chan *SessionUDP)
go func() {
// Set some deadline so this goroutine doesn't hang forever
c.SetReadDeadline(time.Now().Add(time.Minute))
b := make([]byte, 1)
_, sess, err := ReadFromSessionUDP(c, b)
if err != nil {
t.Fatalf("failed to read from conn: %v", err)
}
ch <- sess
}()
c2, err := net.Dial("udp", c.LocalAddr().String())
if err != nil {
t.Fatalf("failed to dial udp: %v", err)
}
if _, err := c2.Write([]byte{1}); err != nil {
t.Fatalf("failed to write to conn: %v", err)
}
sess := <-ch
if len(sess.context) == 0 {
t.Fatalf("empty session context: %v", sess)
}
ip := parseDstFromOOB(sess.context)
if ip == nil {
t.Fatalf("failed to parse dst: %v", sess)
}
if !strings.Contains(c.LocalAddr().String(), ip.String()) {
t.Fatalf("dst was different than listen addr: %v != %v", ip.String(), c.LocalAddr().String())
}
return nil
}
// we require that ipv4 be supported
if err := testFamily("udp4", "127.0.0.1:0"); err != nil {
t.Fatalf("failed to test socket options on IPv4: %v", err)
}
// IPv6 might not be supported so these will just log
if err := testFamily("udp6", "[::1]:0"); err != nil {
t.Logf("failed to test socket options on IPv6-only: %v", err)
}
if err := testFamily("udp", "[::1]:0"); err != nil {
t.Logf("failed to test socket options on IPv6/IPv4: %v", err)
}
}
func TestParseDstFromOOB(t *testing.T) {
// dst is :ffff:100.100.100.100
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
dst := parseDstFromOOB(oob)
dst4 := dst.To4()
if dst4 == nil {
t.Errorf("failed to parse IPv4 in IPv6: %v", dst)
} else if dst4.String() != "100.100.100.100" {
t.Errorf("unexpected IPv4: %v", dst4)
}
// dst is 2001:db8::1
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
dst = parseDstFromOOB(oob)
dst6 := dst.To16()
if dst6 == nil {
t.Errorf("failed to parse IPv6: %v", dst)
} else if dst6.String() != "2001:db8::1" {
t.Errorf("unexpected IPv6: %v", dst4)
}
// dst is 100.100.100.100 but was received on 10.10.10.10
oob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}
dst = parseDstFromOOB(oob)
dst4 = dst.To4()
if dst4 == nil {
t.Errorf("failed to parse IPv4: %v", dst)
} else if dst4.String() != "100.100.100.100" {
t.Errorf("unexpected IPv4: %v", dst4)
}
}
func TestCorrectSource(t *testing.T) {
// dst is :ffff:100.100.100.100 which should be counted as IPv4
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
soob := correctSource(oob)
cm4 := new(ipv4.ControlMessage)
cm4.Src = net.ParseIP("100.100.100.100")
if !bytes.Equal(soob, cm4.Marshal()) {
t.Errorf("unexpected oob for ipv4 address: %v", soob)
}
// dst is 2001:db8::1
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
soob = correctSource(oob)
cm6 := new(ipv6.ControlMessage)
cm6.Src = net.ParseIP("2001:db8::1")
if !bytes.Equal(soob, cm6.Marshal()) {
t.Errorf("unexpected oob for IPv6 address: %v", soob)
}
}

View File

@ -14,6 +14,7 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
// net.UDPAddr.
// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
n, raddr, err := conn.ReadFrom(b)
if err != nil {
@ -24,7 +25,13 @@ func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
}
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
n, err := conn.WriteTo(b, session.raddr)
return n, err
}
// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
// use the standard method in udp.go for these.
func setUDPSocketOptions(*net.UDPConn) error { return nil }
func parseDstFromOOB([]byte, net.IP) net.IP { return nil }

10
vendor/golang.org/x/net/.gitattributes generated vendored Normal file
View File

@ -0,0 +1,10 @@
# Treat all files in this repo as binary, with no git magic updating
# line endings. Windows users contributing to Go will need to use a
# modern version of git and editors capable of LF line endings.
#
# We'll prevent accidental CRLF line endings from entering the repo
# via the git-review gofmt checks.
#
# See golang.org/issue/9281
* -text

2
vendor/golang.org/x/net/.gitignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
# Add no patterns to .hgignore except for files generated by the build.
last-change

3
vendor/golang.org/x/net/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

31
vendor/golang.org/x/net/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,31 @@
# Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
## Contributing code
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.

3
vendor/golang.org/x/net/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/net/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/net/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

16
vendor/golang.org/x/net/README.md generated vendored Normal file
View File

@ -0,0 +1,16 @@
# Go Networking
This repository holds supplementary Go networking libraries.
## Download/Install
The easiest way to install is to run `go get -u golang.org/x/net`. You can
also manually git clone the repository to `$GOPATH/src/golang.org/x/net`.
## Report Issues / Send Patches
This repository uses Gerrit for code changes. To learn how to submit
changes to this repository, see https://golang.org/doc/contribute.html.
The main issue tracker for the net repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the
subject line, so it is easy to find.

41
vendor/golang.org/x/net/bpf/asm.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bpf
import "fmt"
// Assemble converts insts into raw instructions suitable for loading
// into a BPF virtual machine.
//
// Currently, no optimization is attempted, the assembled program flow
// is exactly as provided.
func Assemble(insts []Instruction) ([]RawInstruction, error) {
ret := make([]RawInstruction, len(insts))
var err error
for i, inst := range insts {
ret[i], err = inst.Assemble()
if err != nil {
return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err)
}
}
return ret, nil
}
// Disassemble attempts to parse raw back into
// Instructions. Unrecognized RawInstructions are assumed to be an
// extension not implemented by this package, and are passed through
// unchanged to the output. The allDecoded value reports whether insts
// contains no RawInstructions.
func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) {
insts = make([]Instruction, len(raw))
allDecoded = true
for i, r := range raw {
insts[i] = r.Disassemble()
if _, ok := insts[i].(RawInstruction); ok {
allDecoded = false
}
}
return insts, allDecoded
}

218
vendor/golang.org/x/net/bpf/constants.go generated vendored Normal file
View File

@ -0,0 +1,218 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bpf
// A Register is a register of the BPF virtual machine.
type Register uint16
const (
// RegA is the accumulator register. RegA is always the
// destination register of ALU operations.
RegA Register = iota
// RegX is the indirection register, used by LoadIndirect
// operations.
RegX
)
// An ALUOp is an arithmetic or logic operation.
type ALUOp uint16
// ALU binary operation types.
const (
ALUOpAdd ALUOp = iota << 4
ALUOpSub
ALUOpMul
ALUOpDiv
ALUOpOr
ALUOpAnd
ALUOpShiftLeft
ALUOpShiftRight
aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type.
ALUOpMod
ALUOpXor
)
// A JumpTest is a comparison operator used in conditional jumps.
type JumpTest uint16
// Supported operators for conditional jumps.
const (
// K == A
JumpEqual JumpTest = iota
// K != A
JumpNotEqual
// K > A
JumpGreaterThan
// K < A
JumpLessThan
// K >= A
JumpGreaterOrEqual
// K <= A
JumpLessOrEqual
// K & A != 0
JumpBitsSet
// K & A == 0
JumpBitsNotSet
)
// An Extension is a function call provided by the kernel that
// performs advanced operations that are expensive or impossible
// within the BPF virtual machine.
//
// Extensions are only implemented by the Linux kernel.
//
// TODO: should we prune this list? Some of these extensions seem
// either broken or near-impossible to use correctly, whereas other
// (len, random, ifindex) are quite useful.
type Extension int
// Extension functions available in the Linux kernel.
const (
// extOffset is the negative maximum number of instructions used
// to load instructions by overloading the K argument.
extOffset = -0x1000
// ExtLen returns the length of the packet.
ExtLen Extension = 1
// ExtProto returns the packet's L3 protocol type.
ExtProto Extension = 0
// ExtType returns the packet's type (skb->pkt_type in the kernel)
//
// TODO: better documentation. How nice an API do we want to
// provide for these esoteric extensions?
ExtType Extension = 4
// ExtPayloadOffset returns the offset of the packet payload, or
// the first protocol header that the kernel does not know how to
// parse.
ExtPayloadOffset Extension = 52
// ExtInterfaceIndex returns the index of the interface on which
// the packet was received.
ExtInterfaceIndex Extension = 8
// ExtNetlinkAttr returns the netlink attribute of type X at
// offset A.
ExtNetlinkAttr Extension = 12
// ExtNetlinkAttrNested returns the nested netlink attribute of
// type X at offset A.
ExtNetlinkAttrNested Extension = 16
// ExtMark returns the packet's mark value.
ExtMark Extension = 20
// ExtQueue returns the packet's assigned hardware queue.
ExtQueue Extension = 24
// ExtLinkLayerType returns the packet's hardware address type
// (e.g. Ethernet, Infiniband).
ExtLinkLayerType Extension = 28
// ExtRXHash returns the packets receive hash.
//
// TODO: figure out what this rxhash actually is.
ExtRXHash Extension = 32
// ExtCPUID returns the ID of the CPU processing the current
// packet.
ExtCPUID Extension = 36
// ExtVLANTag returns the packet's VLAN tag.
ExtVLANTag Extension = 44
// ExtVLANTagPresent returns non-zero if the packet has a VLAN
// tag.
//
// TODO: I think this might be a lie: it reads bit 0x1000 of the
// VLAN header, which changed meaning in recent revisions of the
// spec - this extension may now return meaningless information.
ExtVLANTagPresent Extension = 48
// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
// other value if no VLAN information is present.
ExtVLANProto Extension = 60
// ExtRand returns a uniformly random uint32.
ExtRand Extension = 56
)
// The following gives names to various bit patterns used in opcode construction.
const (
opMaskCls uint16 = 0x7
// opClsLoad masks
opMaskLoadDest = 0x01
opMaskLoadWidth = 0x18
opMaskLoadMode = 0xe0
// opClsALU
opMaskOperandSrc = 0x08
opMaskOperator = 0xf0
// opClsJump
opMaskJumpConst = 0x0f
opMaskJumpCond = 0xf0
)
const (
// +---------------+-----------------+---+---+---+
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 |
// +---------------+-----------------+---+---+---+
opClsLoadA uint16 = iota
// +---------------+-----------------+---+---+---+
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 |
// +---------------+-----------------+---+---+---+
opClsLoadX
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
// +---+---+---+---+---+---+---+---+
opClsStoreA
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
// +---+---+---+---+---+---+---+---+
opClsStoreX
// +---------------+-----------------+---+---+---+
// | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 |
// +---------------+-----------------+---+---+---+
opClsALU
// +-----------------------------+---+---+---+---+
// | TestOperator (4b) | 0 | 1 | 0 | 1 |
// +-----------------------------+---+---+---+---+
opClsJump
// +---+-------------------------+---+---+---+---+
// | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 |
// +---+-------------------------+---+---+---+---+
opClsReturn
// +---+-------------------------+---+---+---+---+
// | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 |
// +---+-------------------------+---+---+---+---+
opClsMisc
)
const (
opAddrModeImmediate uint16 = iota << 5
opAddrModeAbsolute
opAddrModeIndirect
opAddrModeScratch
opAddrModePacketLen // actually an extension, not an addressing mode.
opAddrModeMemShift
)
const (
opLoadWidth4 uint16 = iota << 3
opLoadWidth2
opLoadWidth1
)
// Operator defined by ALUOp*
const (
opALUSrcConstant uint16 = iota << 3
opALUSrcX
)
const (
opJumpAlways = iota << 4
opJumpEqual
opJumpGT
opJumpGE
opJumpSet
)
const (
opRetSrcConstant uint16 = iota << 4
opRetSrcA
)
const (
opMiscTAX = 0x00
opMiscTXA = 0x80
)

82
vendor/golang.org/x/net/bpf/doc.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package bpf implements marshaling and unmarshaling of programs for the
Berkeley Packet Filter virtual machine, and provides a Go implementation
of the virtual machine.
BPF's main use is to specify a packet filter for network taps, so that
the kernel doesn't have to expensively copy every packet it sees to
userspace. However, it's been repurposed to other areas where running
user code in-kernel is needed. For example, Linux's seccomp uses BPF
to apply security policies to system calls. For simplicity, this
documentation refers only to packets, but other uses of BPF have their
own data payloads.
BPF programs run in a restricted virtual machine. It has almost no
access to kernel functions, and while conditional branches are
allowed, they can only jump forwards, to guarantee that there are no
infinite loops.
The virtual machine
The BPF VM is an accumulator machine. Its main register, called
register A, is an implicit source and destination in all arithmetic
and logic operations. The machine also has 16 scratch registers for
temporary storage, and an indirection register (register X) for
indirect memory access. All registers are 32 bits wide.
Each run of a BPF program is given one packet, which is placed in the
VM's read-only "main memory". LoadAbsolute and LoadIndirect
instructions can fetch up to 32 bits at a time into register A for
examination.
The goal of a BPF program is to produce and return a verdict (uint32),
which tells the kernel what to do with the packet. In the context of
packet filtering, the returned value is the number of bytes of the
packet to forward to userspace, or 0 to ignore the packet. Other
contexts like seccomp define their own return values.
In order to simplify programs, attempts to read past the end of the
packet terminate the program execution with a verdict of 0 (ignore
packet). This means that the vast majority of BPF programs don't need
to do any explicit bounds checking.
In addition to the bytes of the packet, some BPF programs have access
to extensions, which are essentially calls to kernel utility
functions. Currently, the only extensions supported by this package
are the Linux packet filter extensions.
Examples
This packet filter selects all ARP packets.
bpf.Assemble([]bpf.Instruction{
// Load "EtherType" field from the ethernet header.
bpf.LoadAbsolute{Off: 12, Size: 2},
// Skip over the next instruction if EtherType is not ARP.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1},
// Verdict is "send up to 4k of the packet to userspace."
bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet."
bpf.RetConstant{Val: 0},
})
This packet filter captures a random 1% sample of traffic.
bpf.Assemble([]bpf.Instruction{
// Get a 32-bit random number from the Linux kernel.
bpf.LoadExtension{Num: bpf.ExtRand},
// 1% dice roll?
bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1},
// Capture.
bpf.RetConstant{Val: 4096},
// Ignore.
bpf.RetConstant{Val: 0},
})
*/
package bpf // import "golang.org/x/net/bpf"

704
vendor/golang.org/x/net/bpf/instructions.go generated vendored Normal file
View File

@ -0,0 +1,704 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bpf
import "fmt"
// An Instruction is one instruction executed by the BPF virtual
// machine.
type Instruction interface {
// Assemble assembles the Instruction into a RawInstruction.
Assemble() (RawInstruction, error)
}
// A RawInstruction is a raw BPF virtual machine instruction.
type RawInstruction struct {
// Operation to execute.
Op uint16
// For conditional jump instructions, the number of instructions
// to skip if the condition is true/false.
Jt uint8
Jf uint8
// Constant parameter. The meaning depends on the Op.
K uint32
}
// Assemble implements the Instruction Assemble method.
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
// Disassemble parses ri into an Instruction and returns it. If ri is
// not recognized by this package, ri itself is returned.
func (ri RawInstruction) Disassemble() Instruction {
switch ri.Op & opMaskCls {
case opClsLoadA, opClsLoadX:
reg := Register(ri.Op & opMaskLoadDest)
sz := 0
switch ri.Op & opMaskLoadWidth {
case opLoadWidth4:
sz = 4
case opLoadWidth2:
sz = 2
case opLoadWidth1:
sz = 1
default:
return ri
}
switch ri.Op & opMaskLoadMode {
case opAddrModeImmediate:
if sz != 4 {
return ri
}
return LoadConstant{Dst: reg, Val: ri.K}
case opAddrModeScratch:
if sz != 4 || ri.K > 15 {
return ri
}
return LoadScratch{Dst: reg, N: int(ri.K)}
case opAddrModeAbsolute:
if ri.K > extOffset+0xffffffff {
return LoadExtension{Num: Extension(-extOffset + ri.K)}
}
return LoadAbsolute{Size: sz, Off: ri.K}
case opAddrModeIndirect:
return LoadIndirect{Size: sz, Off: ri.K}
case opAddrModePacketLen:
if sz != 4 {
return ri
}
return LoadExtension{Num: ExtLen}
case opAddrModeMemShift:
return LoadMemShift{Off: ri.K}
default:
return ri
}
case opClsStoreA:
if ri.Op != opClsStoreA || ri.K > 15 {
return ri
}
return StoreScratch{Src: RegA, N: int(ri.K)}
case opClsStoreX:
if ri.Op != opClsStoreX || ri.K > 15 {
return ri
}
return StoreScratch{Src: RegX, N: int(ri.K)}
case opClsALU:
switch op := ALUOp(ri.Op & opMaskOperator); op {
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
if ri.Op&opMaskOperandSrc != 0 {
return ALUOpX{Op: op}
}
return ALUOpConstant{Op: op, Val: ri.K}
case aluOpNeg:
return NegateA{}
default:
return ri
}
case opClsJump:
if ri.Op&opMaskJumpConst != opClsJump {
return ri
}
switch ri.Op & opMaskJumpCond {
case opJumpAlways:
return Jump{Skip: ri.K}
case opJumpEqual:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpNotEqual,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpEqual,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpGT:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpLessOrEqual,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpGreaterThan,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpGE:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpLessThan,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpGreaterOrEqual,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpSet:
return JumpIf{
Cond: JumpBitsSet,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
default:
return ri
}
case opClsReturn:
switch ri.Op {