add ln, smux, kcp to deps
This commit is contained in:
parent
1876477140
commit
dd0856cfa5
20
vendor-log
20
vendor-log
|
@ -73,3 +73,23 @@ f2499483f923065a842d38eb4c7f1927e6fc6e6d golang.org/x/net/trace
|
|||
50955793b0183f9de69bd78e2ec251cf20aab121 google.golang.org/grpc/stats
|
||||
50955793b0183f9de69bd78e2ec251cf20aab121 google.golang.org/grpc/tap
|
||||
50955793b0183f9de69bd78e2ec251cf20aab121 google.golang.org/grpc/transport
|
||||
49bd2f58881c34d534aa97bd64bdbdf37be0df91 github.com/Xe/ln
|
||||
09cded8978dc9e80714c4d85b0322337b0a1e5e0 github.com/klauspost/cpuid
|
||||
5abf0ee302ccf4834e84f63ff74eca3e8b88e4e2 github.com/klauspost/reedsolomon
|
||||
b270c2c35fc775243f87c58cf3f6969c5d9369d6 github.com/mtneug/pkg/ulid
|
||||
a59d62b5095aa2c7b1d220f880b0d0a4c6df3e51 github.com/oklog/ulid
|
||||
ff09b135c25aae272398c51a07235b90a75aa4f0 github.com/pkg/errors
|
||||
0423ef7410efae0a2b792e34bc23b01aa935e273 github.com/xtaci/kcp-go
|
||||
a1a5df8f92af764f378f07d6a3dd8eb3f7aa190a github.com/xtaci/smux
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/blowfish
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/cast5
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/pbkdf2
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/salsa20
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/salsa20/salsa
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/tea
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/twofish
|
||||
459e26527287adbc2adcc5d0d49abff9a5f315a7 golang.org/x/crypto/xtea
|
||||
6c23252515492caf9b228a9d5cabcdbde29f7f82 golang.org/x/net/bpf
|
||||
6c23252515492caf9b228a9d5cabcdbde29f7f82 golang.org/x/net/internal/iana
|
||||
6c23252515492caf9b228a9d5cabcdbde29f7f82 golang.org/x/net/internal/netreflect
|
||||
6c23252515492caf9b228a9d5cabcdbde29f7f82 golang.org/x/net/ipv4
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Filter interface for defining chain filters
|
||||
type Filter interface {
|
||||
Apply(Event) bool
|
||||
Run()
|
||||
Close()
|
||||
}
|
||||
|
||||
// FilterFunc allows simple functions to implement the Filter interface
|
||||
type FilterFunc func(e Event) bool
|
||||
|
||||
// Apply implements the Filter interface
|
||||
func (ff FilterFunc) Apply(e Event) bool {
|
||||
return ff(e)
|
||||
}
|
||||
|
||||
// Run implements the Filter interface
|
||||
func (ff FilterFunc) Run() {}
|
||||
|
||||
// Close implements the Filter interface
|
||||
func (ff FilterFunc) Close() {}
|
||||
|
||||
// WriterFilter implements a filter, which arbitrarily writes to an io.Writer
|
||||
type WriterFilter struct {
|
||||
sync.Mutex
|
||||
Out io.Writer
|
||||
Formatter Formatter
|
||||
}
|
||||
|
||||
// NewWriterFilter creates a filter to add to the chain
|
||||
func NewWriterFilter(out io.Writer, format Formatter) *WriterFilter {
|
||||
if format == nil {
|
||||
format = DefaultFormatter
|
||||
}
|
||||
return &WriterFilter{
|
||||
Out: out,
|
||||
Formatter: format,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply implements the Filter interface
|
||||
func (w *WriterFilter) Apply(e Event) bool {
|
||||
output, err := w.Formatter.Format(e)
|
||||
if err == nil {
|
||||
w.Lock()
|
||||
w.Out.Write(output)
|
||||
w.Unlock()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Run implements the Filter interface
|
||||
func (w *WriterFilter) Run() {}
|
||||
|
||||
// Close implements the Filter interface
|
||||
func (w *WriterFilter) Close() {}
|
||||
|
||||
// NilFilter is safe to return as a Filter, but does nothing
|
||||
var NilFilter = FilterFunc(func(e Event) bool { return true })
|
|
@ -0,0 +1,100 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTimeFormat represents the way in which time will be formatted by default
|
||||
DefaultTimeFormat = time.RFC3339
|
||||
)
|
||||
|
||||
// Formatter defines the formatting of events
|
||||
type Formatter interface {
|
||||
Format(Event) ([]byte, error)
|
||||
}
|
||||
|
||||
// DefaultFormatter is the default way in which to format events
|
||||
var DefaultFormatter Formatter
|
||||
|
||||
func init() {
|
||||
DefaultFormatter = NewTextFormatter()
|
||||
}
|
||||
|
||||
// TextFormatter formats events as key value pairs.
|
||||
// Any remaining text not wrapped in an instance of `F` will be
|
||||
// placed at the end.
|
||||
type TextFormatter struct {
|
||||
TimeFormat string
|
||||
}
|
||||
|
||||
// NewTextFormatter returns a Formatter that outputs as text.
|
||||
func NewTextFormatter() Formatter {
|
||||
return &TextFormatter{TimeFormat: DefaultTimeFormat}
|
||||
}
|
||||
|
||||
// Format implements the Formatter interface
|
||||
func (t *TextFormatter) Format(e Event) ([]byte, error) {
|
||||
var writer bytes.Buffer
|
||||
|
||||
writer.WriteString("time=\"")
|
||||
writer.WriteString(e.Time.Format(t.TimeFormat))
|
||||
writer.WriteString("\"")
|
||||
|
||||
for k, v := range e.Data {
|
||||
writer.WriteByte(' ')
|
||||
if shouldQuote(k) {
|
||||
writer.WriteString(fmt.Sprintf("%q", k))
|
||||
} else {
|
||||
writer.WriteString(k)
|
||||
}
|
||||
|
||||
writer.WriteByte('=')
|
||||
|
||||
switch v.(type) {
|
||||
case string:
|
||||
vs, _ := v.(string)
|
||||
if shouldQuote(vs) {
|
||||
fmt.Fprintf(&writer, "%q", vs)
|
||||
} else {
|
||||
writer.WriteString(vs)
|
||||
}
|
||||
case error:
|
||||
tmperr, _ := v.(error)
|
||||
es := tmperr.Error()
|
||||
|
||||
if shouldQuote(es) {
|
||||
fmt.Fprintf(&writer, "%q", es)
|
||||
} else {
|
||||
writer.WriteString(es)
|
||||
}
|
||||
case time.Time:
|
||||
tmptime, _ := v.(time.Time)
|
||||
writer.WriteString(tmptime.Format(time.RFC3339))
|
||||
default:
|
||||
fmt.Fprint(&writer, v)
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Message) > 0 {
|
||||
fmt.Fprintf(&writer, " _msg=%q", e.Message)
|
||||
}
|
||||
|
||||
writer.WriteByte('\n')
|
||||
return writer.Bytes(), nil
|
||||
}
|
||||
|
||||
func shouldQuote(s string) bool {
|
||||
for _, b := range s {
|
||||
if !((b >= 'A' && b <= 'Z') ||
|
||||
(b >= 'a' && b <= 'z') ||
|
||||
(b >= '0' && b <= '9') ||
|
||||
(b == '-' || b == '.' || b == '#' ||
|
||||
b == '/' || b == '_')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Logger holds the current priority and list of filters
|
||||
type Logger struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// DefaultLogger is the default implementation of Logger
|
||||
var DefaultLogger *Logger
|
||||
|
||||
func init() {
|
||||
var defaultFilters []Filter
|
||||
|
||||
// Default to STDOUT for logging, but allow LN_OUT to change it.
|
||||
out := os.Stdout
|
||||
if os.Getenv("LN_OUT") == "<stderr>" {
|
||||
out = os.Stderr
|
||||
}
|
||||
|
||||
defaultFilters = append(defaultFilters, NewWriterFilter(out, nil))
|
||||
|
||||
DefaultLogger = &Logger{
|
||||
Filters: defaultFilters,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// F is a key-value mapping for structured data.
|
||||
type F map[string]interface{}
|
||||
|
||||
type Fer interface {
|
||||
F() map[string]interface{}
|
||||
}
|
||||
|
||||
// Event represents an event
|
||||
type Event struct {
|
||||
Time time.Time
|
||||
Data F
|
||||
Message string
|
||||
}
|
||||
|
||||
// Log is the generic logging method.
|
||||
func (l *Logger) Log(xs ...interface{}) {
|
||||
var bits []interface{}
|
||||
event := Event{Time: time.Now()}
|
||||
|
||||
addF := func(bf F) {
|
||||
if event.Data == nil {
|
||||
event.Data = bf
|
||||
} else {
|
||||
for k, v := range bf {
|
||||
event.Data[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble the event
|
||||
for _, b := range xs {
|
||||
if bf, ok := b.(F); ok {
|
||||
addF(bf)
|
||||
} else if fer, ok := b.(Fer); ok {
|
||||
addF(F(fer.F()))
|
||||
} else {
|
||||
bits = append(bits, b)
|
||||
}
|
||||
}
|
||||
|
||||
event.Message = fmt.Sprint(bits...)
|
||||
|
||||
if os.Getenv("LN_DEBUG_ALL_EVENTS") == "1" {
|
||||
frame := callersFrame()
|
||||
if event.Data == nil {
|
||||
event.Data = make(F)
|
||||
}
|
||||
event.Data["_lineno"] = frame.lineno
|
||||
event.Data["_function"] = frame.function
|
||||
event.Data["_filename"] = frame.filename
|
||||
}
|
||||
|
||||
l.filter(event)
|
||||
}
|
||||
|
||||
func (l *Logger) filter(e Event) {
|
||||
for _, f := range l.Filters {
|
||||
if !f.Apply(e) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs an error and information about the context of said error.
|
||||
func (l *Logger) Error(err error, xs ...interface{}) {
|
||||
data := F{}
|
||||
frame := callersFrame()
|
||||
|
||||
data["_lineno"] = frame.lineno
|
||||
data["_function"] = frame.function
|
||||
data["_filename"] = frame.filename
|
||||
data["err"] = err
|
||||
|
||||
cause := errors.Cause(err)
|
||||
if cause != nil {
|
||||
data["cause"] = cause.Error()
|
||||
}
|
||||
|
||||
xs = append(xs, data)
|
||||
|
||||
l.Log(xs...)
|
||||
}
|
||||
|
||||
// Fatal logs this set of values, then exits with status code 1.
|
||||
func (l *Logger) Fatal(xs ...interface{}) {
|
||||
l.Log(xs...)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Default Implementation
|
||||
|
||||
// Log is the generic logging method.
|
||||
func Log(xs ...interface{}) {
|
||||
DefaultLogger.Log(xs...)
|
||||
}
|
||||
|
||||
// Error logs an error and information about the context of said error.
|
||||
func Error(err error, xs ...interface{}) {
|
||||
DefaultLogger.Error(err, xs...)
|
||||
}
|
||||
|
||||
// Fatal logs this set of values, then exits with status code 1.
|
||||
func Fatal(xs ...interface{}) {
|
||||
DefaultLogger.Fatal(xs...)
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type frame struct {
|
||||
filename string
|
||||
function string
|
||||
lineno int
|
||||
}
|
||||
|
||||
// skips 2 frames, since Caller returns the current frame, and we need
|
||||
// the caller's caller.
|
||||
func callersFrame() frame {
|
||||
var out frame
|
||||
pc, file, line, ok := runtime.Caller(3)
|
||||
if !ok {
|
||||
return out
|
||||
}
|
||||
srcLoc := strings.LastIndex(file, "/src/")
|
||||
if srcLoc >= 0 {
|
||||
file = file[srcLoc+5:]
|
||||
}
|
||||
out.filename = file
|
||||
out.function = functionName(pc)
|
||||
out.lineno = line
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func functionName(pc uintptr) string {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
return "???"
|
||||
}
|
||||
name := fn.Name()
|
||||
beg := strings.LastIndex(name, string(os.PathSeparator))
|
||||
return name[beg+1:]
|
||||
// end := strings.LastIndex(name, string(os.PathSeparator))
|
||||
// return name[end+1 : len(name)]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build amd64,!gccgo
|
||||
|
||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuid(SB), 7, $0
|
||||
XORQ CX, CX
|
||||
MOVL op+0(FP), AX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuidex(SB), 7, $0
|
||||
MOVL op+0(FP), AX
|
||||
MOVL op2+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func asmXgetbv(index uint32) (eax, edx uint32)
|
||||
TEXT ·asmXgetbv(SB), 7, $0
|
||||
MOVL index+0(FP), CX
|
||||
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmRdtscpAsm(SB), 7, $0
|
||||
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
|
||||
MOVL AX, eax+0(FP)
|
||||
MOVL BX, ebx+4(FP)
|
||||
MOVL CX, ecx+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
// +build 386,!gccgo amd64,!gccgo
|
||||
|
||||
package cpuid
|
||||
|
||||
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmXgetbv(index uint32) (eax, edx uint32)
|
||||
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
|
||||
func initCPU() {
|
||||
cpuid = asmCpuid
|
||||
cpuidex = asmCpuidex
|
||||
xgetbv = asmXgetbv
|
||||
rdtscpAsm = asmRdtscpAsm
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
// +build !amd64,!386 gccgo
|
||||
|
||||
package cpuid
|
||||
|
||||
func initCPU() {
|
||||
cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
|
||||
cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
|
||||
xgetbv = func(index uint32) (eax, edx uint32) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
rdtscpAsm = func() (eax, ebx, ecx, edx uint32) {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package cpuid
|
||||
|
||||
//go:generate go run private-gen.go
|
|
@ -0,0 +1,476 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var inFiles = []string{"cpuid.go", "cpuid_test.go"}
|
||||
var copyFiles = []string{"cpuid_amd64.s", "cpuid_386.s", "detect_ref.go", "detect_intel.go"}
|
||||
var fileSet = token.NewFileSet()
|
||||
var reWrites = []rewrite{
|
||||
initRewrite("CPUInfo -> cpuInfo"),
|
||||
initRewrite("Vendor -> vendor"),
|
||||
initRewrite("Flags -> flags"),
|
||||
initRewrite("Detect -> detect"),
|
||||
initRewrite("CPU -> cpu"),
|
||||
}
|
||||
var excludeNames = map[string]bool{"string": true, "join": true, "trim": true,
|
||||
// cpuid_test.go
|
||||
"t": true, "println": true, "logf": true, "log": true, "fatalf": true, "fatal": true,
|
||||
}
|
||||
|
||||
var excludePrefixes = []string{"test", "benchmark"}
|
||||
|
||||
func main() {
|
||||
Package := "private"
|
||||
parserMode := parser.ParseComments
|
||||
exported := make(map[string]rewrite)
|
||||
for _, file := range inFiles {
|
||||
in, err := os.Open(file)
|
||||
if err != nil {
|
||||
log.Fatalf("opening input", err)
|
||||
}
|
||||
|
||||
src, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
log.Fatalf("reading input", err)
|
||||
}
|
||||
|
||||
astfile, err := parser.ParseFile(fileSet, file, src, parserMode)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing input", err)
|
||||
}
|
||||
|
||||
for _, rw := range reWrites {
|
||||
astfile = rw(astfile)
|
||||
}
|
||||
|
||||
// Inspect the AST and print all identifiers and literals.
|
||||
var startDecl token.Pos
|
||||
var endDecl token.Pos
|
||||
ast.Inspect(astfile, func(n ast.Node) bool {
|
||||
var s string
|
||||
switch x := n.(type) {
|
||||
case *ast.Ident:
|
||||
if x.IsExported() {
|
||||
t := strings.ToLower(x.Name)
|
||||
for _, pre := range excludePrefixes {
|
||||
if strings.HasPrefix(t, pre) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if excludeNames[t] != true {
|
||||
//if x.Pos() > startDecl && x.Pos() < endDecl {
|
||||
exported[x.Name] = initRewrite(x.Name + " -> " + t)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.GenDecl:
|
||||
if x.Tok == token.CONST && x.Lparen > 0 {
|
||||
startDecl = x.Lparen
|
||||
endDecl = x.Rparen
|
||||
// fmt.Printf("Decl:%s -> %s\n", fileSet.Position(startDecl), fileSet.Position(endDecl))
|
||||
}
|
||||
}
|
||||
if s != "" {
|
||||
fmt.Printf("%s:\t%s\n", fileSet.Position(n.Pos()), s)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
for _, rw := range exported {
|
||||
astfile = rw(astfile)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
printer.Fprint(&buf, fileSet, astfile)
|
||||
|
||||
// Remove package documentation and insert information
|
||||
s := buf.String()
|
||||
ind := strings.Index(buf.String(), "\npackage cpuid")
|
||||
s = s[ind:]
|
||||
s = "// Generated, DO NOT EDIT,\n" +
|
||||
"// but copy it to your own project and rename the package.\n" +
|
||||
"// See more at http://github.com/klauspost/cpuid\n" +
|
||||
s
|
||||
|
||||
outputName := Package + string(os.PathSeparator) + file
|
||||
|
||||
err = ioutil.WriteFile(outputName, []byte(s), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("writing output: %s", err)
|
||||
}
|
||||
log.Println("Generated", outputName)
|
||||
}
|
||||
|
||||
for _, file := range copyFiles {
|
||||
dst := ""
|
||||
if strings.HasPrefix(file, "cpuid") {
|
||||
dst = Package + string(os.PathSeparator) + file
|
||||
} else {
|
||||
dst = Package + string(os.PathSeparator) + "cpuid_" + file
|
||||
}
|
||||
err := copyFile(file, dst)
|
||||
if err != nil {
|
||||
log.Fatalf("copying file: %s", err)
|
||||
}
|
||||
log.Println("Copied", dst)
|
||||
}
|
||||
}
|
||||
|
||||
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
||||
// the same, then return success. Copy the file contents from src to dst.
|
||||
func copyFile(src, dst string) (err error) {
|
||||
sfi, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !sfi.Mode().IsRegular() {
|
||||
// cannot copy non-regular files (e.g., directories,
|
||||
// symlinks, devices, etc.)
|
||||
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
|
||||
}
|
||||
dfi, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !(dfi.Mode().IsRegular()) {
|
||||
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
|
||||
}
|
||||
if os.SameFile(sfi, dfi) {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = copyFileContents(src, dst)
|
||||
return
|
||||
}
|
||||
|
||||
// copyFileContents copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFileContents(src, dst string) (err error) {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
cerr := out.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return
|
||||
}
|
||||
err = out.Sync()
|
||||
return
|
||||
}
|
||||
|
||||
type rewrite func(*ast.File) *ast.File
|
||||
|
||||
// Mostly copied from gofmt
|
||||
func initRewrite(rewriteRule string) rewrite {
|
||||
f := strings.Split(rewriteRule, "->")
|
||||
if len(f) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
pattern := parseExpr(f[0], "pattern")
|
||||
replace := parseExpr(f[1], "replacement")
|
||||
return func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
|
||||
}
|
||||
|
||||
// parseExpr parses s as an expression.
|
||||
// It might make sense to expand this to allow statement patterns,
|
||||
// but there are problems with preserving formatting and also
|
||||
// with what a wildcard for a statement looks like.
|
||||
func parseExpr(s, what string) ast.Expr {
|
||||
x, err := parser.ParseExpr(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Keep this function for debugging.
|
||||
/*
|
||||
func dump(msg string, val reflect.Value) {
|
||||
fmt.Printf("%s:\n", msg)
|
||||
ast.Print(fileSet, val.Interface())
|
||||
fmt.Println()
|
||||
}
|
||||
*/
|
||||
|
||||
// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
|
||||
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
|
||||
cmap := ast.NewCommentMap(fileSet, p, p.Comments)
|
||||
m := make(map[string]reflect.Value)
|
||||
pat := reflect.ValueOf(pattern)
|
||||
repl := reflect.ValueOf(replace)
|
||||
|
||||
var rewriteVal func(val reflect.Value) reflect.Value
|
||||
rewriteVal = func(val reflect.Value) reflect.Value {
|
||||
// don't bother if val is invalid to start with
|
||||
if !val.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
val = apply(rewriteVal, val)
|
||||
if match(m, pat, val) {
|
||||
val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos()))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File)
|
||||
r.Comments = cmap.Filter(r).Comments() // recreate comments list
|
||||
return r
|
||||
}
|
||||
|
||||
// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y.
|
||||
func set(x, y reflect.Value) {
|
||||
// don't bother if x cannot be set or y is invalid
|
||||
if !x.CanSet() || !y.IsValid() {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if s, ok := x.(string); ok &&
|
||||
(strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
|
||||
// x cannot be set to y - ignore this rewrite
|
||||
return
|
||||
}
|
||||
panic(x)
|
||||
}
|
||||
}()
|
||||
x.Set(y)
|
||||
}
|
||||
|
||||
// Values/types for special cases.
|
||||
var (
|
||||
objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
|
||||
scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
|
||||
|
||||
identType = reflect.TypeOf((*ast.Ident)(nil))
|
||||
objectPtrType = reflect.TypeOf((*ast.Object)(nil))
|
||||
positionType = reflect.TypeOf(token.NoPos)
|
||||
callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
|
||||
scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
|
||||
)
|
||||
|
||||
// apply replaces each AST field x in val with f(x), returning val.
|
||||
// To avoid extra conversions, f operates on the reflect.Value form.
|
||||
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
|
||||
if !val.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// *ast.Objects introduce cycles and are likely incorrect after
|
||||
// rewrite; don't follow them but replace with nil instead
|
||||
if val.Type() == objectPtrType {
|
||||
return objectPtrNil
|
||||
}
|
||||
|
||||
// similarly for scopes: they are likely incorrect after a rewrite;
|
||||
// replace them with nil
|
||||
if val.Type() == scopePtrType {
|
||||
return scopePtrNil
|
||||
}
|
||||
|
||||
switch v := reflect.Indirect(val); v.Kind() {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
e := v.Index(i)
|
||||
set(e, f(e))
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
e := v.Field(i)
|
||||
set(e, f(e))
|
||||
}
|
||||
case reflect.Interface:
|
||||
e := v.Elem()
|
||||
set(v, f(e))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func isWildcard(s string) bool {
|
||||
rune, size := utf8.DecodeRuneInString(s)
|
||||
return size == len(s) && unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
// match returns true if pattern matches val,
|
||||
// recording wildcard submatches in m.
|
||||
// If m == nil, match checks whether pattern == val.
|
||||
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
|
||||
// Wildcard matches any expression. If it appears multiple
|
||||
// times in the pattern, it must match the same expression
|
||||
// each time.
|
||||
if m != nil && pattern.IsValid() && pattern.Type() == identType {
|
||||
name := pattern.Interface().(*ast.Ident).Name
|
||||
if isWildcard(name) && val.IsValid() {
|
||||
// wildcards only match valid (non-nil) expressions.
|
||||
if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
|
||||
if old, ok := m[name]; ok {
|
||||
return match(nil, old, val)
|
||||
}
|
||||
m[name] = val
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, pattern and val must match recursively.
|
||||
if !pattern.IsValid() || !val.IsValid() {
|
||||
return !pattern.IsValid() && !val.IsValid()
|
||||
}
|
||||
if pattern.Type() != val.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Special cases.
|
||||
switch pattern.Type() {
|
||||
case identType:
|
||||
// For identifiers, only the names need to match
|
||||
// (and none of the other *ast.Object information).
|
||||
// This is a common case, handle it all here instead
|
||||
// of recursing down any further via reflection.
|
||||
p := pattern.Interface().(*ast.Ident)
|
||||
v := val.Interface().(*ast.Ident)
|
||||
return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
|
||||
case objectPtrType, positionType:
|
||||
// object pointers and token positions always match
|
||||
return true
|
||||
case callExprType:
|
||||
// For calls, the Ellipsis fields (token.Position) must
|
||||
// match since that is how f(x) and f(x...) are different.
|
||||
// Check them here but fall through for the remaining fields.
|
||||
p := pattern.Interface().(*ast.CallExpr)
|
||||
v := val.Interface().(*ast.CallExpr)
|
||||
if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
p := reflect.Indirect(pattern)
|
||||
v := reflect.Indirect(val)
|
||||
if !p.IsValid() || !v.IsValid() {
|
||||
return !p.IsValid() && !v.IsValid()
|
||||
}
|
||||
|
||||
switch p.Kind() {
|
||||
case reflect.Slice:
|
||||
if p.Len() != v.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < p.Len(); i++ {
|
||||
if !match(m, p.Index(i), v.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < p.NumField(); i++ {
|
||||
if !match(m, p.Field(i), v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Interface:
|
||||
return match(m, p.Elem(), v.Elem())
|
||||
}
|
||||
|
||||
// Handle token integers, etc.
|
||||
return p.Interface() == v.Interface()
|
||||
}
|
||||
|
||||
// subst returns a copy of pattern with values from m substituted in place
|
||||
// of wildcards and pos used as the position of tokens from the pattern.
|
||||
// if m == nil, subst returns a copy of pattern and doesn't change the line
|
||||
// number information.
|
||||
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
|
||||
if !pattern.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// Wildcard gets replaced with map value.
|
||||
if m != nil && pattern.Type() == identType {
|
||||
name := pattern.Interface().(*ast.Ident).Name
|
||||
if isWildcard(name) {
|
||||
if old, ok := m[name]; ok {
|
||||
return subst(nil, old, reflect.Value{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pos.IsValid() && pattern.Type() == positionType {
|
||||
// use new position only if old position was valid in the first place
|
||||
if old := pattern.Interface().(token.Pos); !old.IsValid() {
|
||||
return pattern
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
// Otherwise copy.
|
||||
switch p := pattern; p.Kind() {
|
||||
case reflect.Slice:
|
||||
v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
|
||||
for i := 0; i < p.Len(); i++ {
|
||||
v.Index(i).Set(subst(m, p.Index(i), pos))
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Struct:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
for i := 0; i < p.NumField(); i++ {
|
||||
v.Field(i).Set(subst(m, p.Field(i), pos))
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
if elem := p.Elem(); elem.IsValid() {
|
||||
v.Set(subst(m, elem, pos).Addr())
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Interface:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
if elem := p.Elem(); elem.IsValid() {
|
||||
v.Set(subst(m, elem, pos))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
return pattern
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,73 @@
|
|||
//+build !noasm
|
||||
//+build !appengine
|
||||
|
||||
// Copyright 2015, Klaus Post, see LICENSE for details.
|
||||
|
||||
package reedsolomon
|
||||
|
||||
//go:noescape
|
||||
func galMulSSSE3(low, high, in, out []byte)
|
||||
|
||||
//go:noescape
|
||||
func galMulSSSE3Xor(low, high, in, out []byte)
|
||||
|
||||
//go:noescape
|
||||
func galMulAVX2Xor(low, high, in, out []byte)
|
||||
|
||||
//go:noescape
|
||||
func galMulAVX2(low, high, in, out []byte)
|
||||
|
||||
// This is what the assembler rountes does in blocks of 16 bytes:
|
||||
/*
|
||||
func galMulSSSE3(low, high, in, out []byte) {
|
||||
for n, input := range in {
|
||||
l := input & 0xf
|
||||
h := input >> 4
|
||||
out[n] = low[l] ^ high[h]
|
||||
}
|
||||
}
|
||||
|
||||
func galMulSSSE3Xor(low, high, in, out []byte) {
|
||||
for n, input := range in {
|
||||
l := input & 0xf
|
||||
h := input >> 4
|
||||
out[n] ^= low[l] ^ high[h]
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func galMulSlice(c byte, in, out []byte, ssse3, avx2 bool) {
|
||||
var done int
|
||||
if avx2 {
|
||||
galMulAVX2(mulTableLow[c][:], mulTableHigh[c][:], in, out)
|
||||
done = (len(in) >> 5) << 5
|
||||
} else if ssse3 {
|
||||
galMulSSSE3(mulTableLow[c][:], mulTableHigh[c][:], in, out)
|
||||
done = (len(in) >> 4) << 4
|
||||
}
|
||||
remain := len(in) - done
|
||||
if remain > 0 {
|
||||
mt := mulTable[c]
|
||||
for i := done; i < len(in); i++ {
|
||||
out[i] = mt[in[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func galMulSliceXor(c byte, in, out []byte, ssse3, avx2 bool) {
|
||||
var done int
|
||||
if avx2 {
|
||||
galMulAVX2Xor(mulTableLow[c][:], mulTableHigh[c][:], in, out)
|
||||
done = (len(in) >> 5) << 5
|
||||
} else if ssse3 {
|
||||
galMulSSSE3Xor(mulTableLow[c][:], mulTableHigh[c][:], in, out)
|
||||
done = (len(in) >> 4) << 4
|
||||
}
|
||||
remain := len(in) - done
|
||||
if remain > 0 {
|
||||
mt := mulTable[c]
|
||||
for i := done; i < len(in); i++ {
|
||||
out[i] ^= mt[in[i]]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
//+build !noasm !appengine
|
||||
|
||||
// Copyright 2015, Klaus Post, see LICENSE for details.
|
||||
|
||||
// Based on http://www.snia.org/sites/default/files2/SDC2013/presentations/NewThinking/EthanMiller_Screaming_Fast_Galois_Field%20Arithmetic_SIMD%20Instructions.pdf
|
||||
// and http://jerasure.org/jerasure/gf-complete/tree/master
|
||||
|
||||
// func galMulSSSE3Xor(low, high, in, out []byte)
|
||||
TEXT ·galMulSSSE3Xor(SB), 7, $0
|
||||
MOVQ low+0(FP), SI // SI: &low
|
||||
MOVQ high+24(FP), DX // DX: &high
|
||||
MOVOU (SI), X6 // X6 low
|
||||
MOVOU (DX), X7 // X7: high
|
||||
MOVQ $15, BX // BX: low mask
|
||||
MOVQ BX, X8
|
||||
PXOR X5, X5
|
||||
MOVQ in+48(FP), SI // R11: &in
|
||||
MOVQ in_len+56(FP), R9 // R9: len(in)
|
||||
MOVQ out+72(FP), DX // DX: &out
|
||||
PSHUFB X5, X8 // X8: lomask (unpacked)
|
||||
SHRQ $4, R9 // len(in) / 16
|
||||
CMPQ R9, $0
|
||||
JEQ done_xor
|
||||
|
||||
loopback_xor:
|
||||
MOVOU (SI), X0 // in[x]
|
||||
MOVOU (DX), X4 // out[x]
|
||||
MOVOU X0, X1 // in[x]
|
||||
MOVOU X6, X2 // low copy
|
||||
MOVOU X7, X3 // high copy
|
||||
PSRLQ $4, X1 // X1: high input
|
||||
PAND X8, X0 // X0: low input
|
||||
PAND X8, X1 // X0: high input
|
||||
PSHUFB X0, X2 // X2: mul low part
|
||||
PSHUFB X1, X3 // X3: mul high part
|
||||
PXOR X2, X3 // X3: Result
|
||||
PXOR X4, X3 // X3: Result xor existing out
|
||||
MOVOU X3, (DX) // Store
|
||||
ADDQ $16, SI // in+=16
|
||||
ADDQ $16, DX // out+=16
|
||||
SUBQ $1, R9
|
||||
JNZ loopback_xor
|
||||
|
||||
done_xor:
|
||||
RET
|
||||
|
||||
// func galMulSSSE3(low, high, in, out []byte)
|
||||
TEXT ·galMulSSSE3(SB), 7, $0
|
||||
MOVQ low+0(FP), SI // SI: &low
|
||||
MOVQ high+24(FP), DX // DX: &high
|
||||
MOVOU (SI), X6 // X6 low
|
||||
MOVOU (DX), X7 // X7: high
|
||||
MOVQ $15, BX // BX: low mask
|
||||
MOVQ BX, X8
|
||||
PXOR X5, X5
|
||||
MOVQ in+48(FP), SI // R11: &in
|
||||
MOVQ in_len+56(FP), R9 // R9: len(in)
|
||||
MOVQ out+72(FP), DX // DX: &out
|
||||
PSHUFB X5, X8 // X8: lomask (unpacked)
|
||||
SHRQ $4, R9 // len(in) / 16
|
||||
CMPQ R9, $0
|
||||
JEQ done
|
||||
|
||||
loopback:
|
||||
MOVOU (SI), X0 // in[x]
|
||||
MOVOU X0, X1 // in[x]
|
||||
MOVOU X6, X2 // low copy
|
||||
MOVOU X7, X3 // high copy
|
||||
PSRLQ $4, X1 // X1: high input
|
||||
PAND X8, X0 // X0: low input
|
||||
PAND X8, X1 // X0: high input
|
||||
PSHUFB X0, X2 // X2: mul low part
|
||||
PSHUFB X1, X3 // X3: mul high part
|
||||
PXOR X2, X3 // X3: Result
|
||||
MOVOU X3, (DX) // Store
|
||||
ADDQ $16, SI // in+=16
|
||||
ADDQ $16, DX // out+=16
|
||||
SUBQ $1, R9
|
||||
JNZ loopback
|
||||
|
||||
done:
|
||||
RET
|
||||
|
||||
// func galMulAVX2Xor(low, high, in, out []byte)
|
||||
TEXT ·galMulAVX2Xor(SB), 7, $0
|
||||
MOVQ low+0(FP), SI // SI: &low
|
||||
MOVQ high+24(FP), DX // DX: &high
|
||||
MOVQ $15, BX // BX: low mask
|
||||
MOVQ BX, X5
|
||||
MOVOU (SI), X6 // X6 low
|
||||
MOVOU (DX), X7 // X7: high
|
||||
MOVQ in_len+56(FP), R9 // R9: len(in)
|
||||
|
||||
LONG $0x384de3c4; WORD $0x01f6 // VINSERTI128 YMM6, YMM6, XMM6, 1 ; low
|
||||
LONG $0x3845e3c4; WORD $0x01ff // VINSERTI128 YMM7, YMM7, XMM7, 1 ; high
|
||||
LONG $0x787d62c4; BYTE $0xc5 // VPBROADCASTB YMM8, XMM5 ; X8: lomask (unpacked)
|
||||
|
||||
SHRQ $5, R9 // len(in) /32
|
||||
MOVQ out+72(FP), DX // DX: &out
|
||||
MOVQ in+48(FP), SI // R11: &in
|
||||
TESTQ R9, R9
|
||||
JZ done_xor_avx2
|
||||
|
||||
loopback_xor_avx2:
|
||||
LONG $0x066ffec5 // VMOVDQU YMM0, [rsi]
|
||||
LONG $0x226ffec5 // VMOVDQU YMM4, [rdx]
|
||||
LONG $0xd073f5c5; BYTE $0x04 // VPSRLQ YMM1, YMM0, 4 ; X1: high input
|
||||
LONG $0xdb7dc1c4; BYTE $0xc0 // VPAND YMM0, YMM0, YMM8 ; X0: low input
|
||||
LONG $0xdb75c1c4; BYTE $0xc8 // VPAND YMM1, YMM1, YMM8 ; X1: high input
|
||||
LONG $0x004de2c4; BYTE $0xd0 // VPSHUFB YMM2, YMM6, YMM0 ; X2: mul low part
|
||||
LONG $0x0045e2c4; BYTE $0xd9 // VPSHUFB YMM3, YMM7, YMM1 ; X2: mul high part
|
||||
LONG $0xdbefedc5 // VPXOR YMM3, YMM2, YMM3 ; X3: Result
|
||||
LONG $0xe4efe5c5 // VPXOR YMM4, YMM3, YMM4 ; X4: Result
|
||||
LONG $0x227ffec5 // VMOVDQU [rdx], YMM4
|
||||
|
||||
ADDQ $32, SI // in+=32
|
||||
ADDQ $32, DX // out+=32
|
||||
SUBQ $1, R9
|
||||
JNZ loopback_xor_avx2
|
||||
|
||||
done_xor_avx2:
|
||||
// VZEROUPPER
|
||||
BYTE $0xc5; BYTE $0xf8; BYTE $0x77
|
||||
RET
|
||||
|
||||
// func galMulAVX2(low, high, in, out []byte)
|
||||
TEXT ·galMulAVX2(SB), 7, $0
|
||||
MOVQ low+0(FP), SI // SI: &low
|
||||
MOVQ high+24(FP), DX // DX: &high
|
||||
MOVQ $15, BX // BX: low mask
|
||||
MOVQ BX, X5
|
||||
MOVOU (SI), X6 // X6 low
|
||||
MOVOU (DX), X7 // X7: high
|
||||
MOVQ in_len+56(FP), R9 // R9: len(in)
|
||||
|
||||
LONG $0x384de3c4; WORD $0x01f6 // VINSERTI128 YMM6, YMM6, XMM6, 1 ; low
|
||||
LONG $0x3845e3c4; WORD $0x01ff // VINSERTI128 YMM7, YMM7, XMM7, 1 ; high
|
||||
LONG $0x787d62c4; BYTE $0xc5 // VPBROADCASTB YMM8, XMM5 ; X8: lomask (unpacked)
|
||||
|
||||
SHRQ $5, R9 // len(in) /32
|
||||
MOVQ out+72(FP), DX // DX: &out
|
||||
MOVQ in+48(FP), SI // R11: &in
|
||||
TESTQ R9, R9
|
||||
JZ done_avx2
|
||||
|
||||
loopback_avx2:
|
||||
LONG $0x066ffec5 // VMOVDQU YMM0, [rsi]
|
||||
LONG $0xd073f5c5; BYTE $0x04 // VPSRLQ YMM1, YMM0, 4 ; X1: high input
|
||||
LONG $0xdb7dc1c4; BYTE $0xc0 // VPAND YMM0, YMM0, YMM8 ; X0: low input
|
||||
LONG $0xdb75c1c4; BYTE $0xc8 // VPAND YMM1, YMM1, YMM8 ; X1: high input
|
||||
LONG $0x004de2c4; BYTE $0xd0 // VPSHUFB YMM2, YMM6, YMM0 ; X2: mul low part
|
||||
LONG $0x0045e2c4; BYTE $0xd9 // VPSHUFB YMM3, YMM7, YMM1 ; X2: mul high part
|
||||
LONG $0xe3efedc5 // VPXOR YMM4, YMM2, YMM3 ; X4: Result
|
||||
LONG $0x227ffec5 // VMOVDQU [rdx], YMM4
|
||||
|
||||
ADDQ $32, SI // in+=32
|
||||
ADDQ $32, DX // out+=32
|
||||
SUBQ $1, R9
|
||||
JNZ loopback_avx2
|
||||
|
||||
done_avx2:
|
||||
|
||||
BYTE $0xc5; BYTE $0xf8; BYTE $0x77 // VZEROUPPER
|
||||
RET
|
|
@ -0,0 +1,19 @@
|
|||
//+build !amd64 noasm appengine
|
||||
|
||||
// Copyright 2015, Klaus Post, see LICENSE for details.
|
||||
|
||||
package reedsolomon
|
||||
|
||||
func galMulSlice(c byte, in, out []byte, ssse3, avx2 bool) {
|
||||
mt := mulTable[c]
|
||||
for n, input := range in {
|
||||
out[n] = mt[input]
|
||||
}
|
||||
}
|
||||
|
||||
func galMulSliceXor(c byte, in, out []byte, ssse3, avx2 bool) {
|
||||
mt := mulTable[c]
|
||||
for n, input := range in {
|
||||
out[n] ^= mt[input]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var logTable = [fieldSize]int16{
|
||||
-1, 0, 1, 25, 2, 50, 26, 198,
|
||||
3, 223, 51, 238, 27, 104, 199, 75,
|
||||
4, 100, 224, 14, 52, 141, 239, 129,
|
||||
28, 193, 105, 248, 200, 8, 76, 113,
|
||||
5, 138, 101, 47, 225, 36, 15, 33,
|
||||
53, 147, 142, 218, 240, 18, 130, 69,
|
||||
29, 181, 194, 125, 106, 39, 249, 185,
|
||||
201, 154, 9, 120, 77, 228, 114, 166,
|
||||
6, 191, 139, 98, 102, 221, 48, 253,
|
||||
226, 152, 37, 179, 16, 145, 34, 136,
|
||||
54, 208, 148, 206, 143, 150, 219, 189,
|
||||
241, 210, 19, 92, 131, 56, 70, 64,
|
||||
30, 66, 182, 163, 195, 72, 126, 110,
|
||||
107, 58, 40, 84, 250, 133, 186, 61,
|
||||
202, 94, 155, 159, 10, 21, 121, 43,
|
||||
78, 212, 229, 172, 115, 243, 167, 87,
|
||||
7, 112, 192, 247, 140, 128, 99, 13,
|
||||
103, 74, 222, 237, 49, 197, 254, 24,
|
||||
227, 165, 153, 119, 38, 184, 180, 124,
|
||||
17, 68, 146, 217, 35, 32, 137, 46,
|
||||
55, 63, 209, 91, 149, 188, 207, 205,
|
||||
144, 135, 151, 178, 220, 252, 190, 97,
|
||||
242, 86, 211, 171, 20, 42, 93, 158,
|
||||
132, 60, 57, 83, 71, 109, 65, 162,
|
||||
31, 45, 67, 216, 183, 123, 164, 118,
|
||||
196, 23, 73, 236, 127, 12, 111, 246,
|
||||
108, 161, 59, 82, 41, 157, 85, 170,
|
||||
251, 96, 134, 177, 187, 204, 62, 90,
|
||||
203, 89, 95, 176, 156, 169, 160, 81,
|
||||
11, 245, 22, 235, 122, 117, 44, 215,
|
||||
79, 174, 213, 233, 230, 231, 173, 232,
|
||||
116, 214, 244, 234, 168, 80, 88, 175,
|
||||
}
|
||||
|
||||
const (
|
||||
// The number of elements in the field.
|
||||
fieldSize = 256
|
||||
|
||||
// The polynomial used to generate the logarithm table.
|
||||
//
|
||||
// There are a number of polynomials that work to generate
|
||||
// a Galois field of 256 elements. The choice is arbitrary,
|
||||
// and we just use the first one.
|
||||
//
|
||||
// The possibilities are: 29, 43, 45, 77, 95, 99, 101, 105,
|
||||
//* 113, 135, 141, 169, 195, 207, 231, and 245.
|
||||
generatingPolynomial = 29
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := generateExpTable()
|
||||
fmt.Printf("var expTable = %#v\n", t)
|
||||
//t2 := generateMulTableSplit(t)
|
||||
//fmt.Printf("var mulTable = %#v\n", t2)
|
||||
low, high := generateMulTableHalf(t)
|
||||
fmt.Printf("var mulTableLow = %#v\n", low)
|
||||
fmt.Printf("var mulTableHigh = %#v\n", high)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the inverse log table.
|
||||
*/
|
||||
func generateExpTable() []byte {
|
||||
result := make([]byte, fieldSize*2-2)
|
||||
for i := 1; i < fieldSize; i++ {
|
||||
log := logTable[i]
|
||||
result[log] = byte(i)
|
||||
result[log+fieldSize-1] = byte(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func generateMulTable(expTable []byte) []byte {
|
||||
result := make([]byte, 256*256)
|
||||
for v := range result {
|
||||
a := byte(v & 0xff)
|
||||
b := byte(v >> 8)
|
||||
if a == 0 || b == 0 {
|
||||
result[v] = 0
|
||||
continue
|
||||
}
|
||||
logA := int(logTable[a])
|
||||
logB := int(logTable[b])
|
||||
result[v] = expTable[logA+logB]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func generateMulTableSplit(expTable []byte) [256][256]byte {
|
||||
var result [256][256]byte
|
||||
for a := range result {
|
||||
for b := range result[a] {
|
||||
if a == 0 || b == 0 {
|
||||
result[a][b] = 0
|
||||
continue
|
||||
}
|
||||
logA := int(logTable[a])
|
||||
logB := int(logTable[b])
|
||||
result[a][b] = expTable[logA+logB]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func generateMulTableHalf(expTable []byte) (low [256][16]byte, high [256][16]byte) {
|
||||
for a := range low {
|
||||
for b := range low {
|
||||
result := 0
|
||||
if !(a == 0 || b == 0) {
|
||||
logA := int(logTable[a])
|
||||
logB := int(logTable[b])
|
||||
result = int(expTable[logA+logB])
|
||||
}
|
||||
if (b & 0xf) == b {
|
||||
low[a][b] = byte(result)
|
||||
}
|
||||
if (b & 0xf0) == b {
|
||||
high[a][b>>4] = byte(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* A thread-safe tree which caches inverted matrices.
|
||||
*
|
||||
* Copyright 2016, Peter Collins
|
||||
*/
|
||||
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// The tree uses a Reader-Writer mutex to make it thread-safe
|
||||
// when accessing cached matrices and inserting new ones.
|
||||
type inversionTree struct {
|
||||
mutex *sync.RWMutex
|
||||
root inversionNode
|
||||
}
|
||||
|
||||
type inversionNode struct {
|
||||
matrix matrix
|
||||
children []*inversionNode
|
||||
}
|
||||
|
||||
// newInversionTree initializes a tree for storing inverted matrices.
|
||||
// Note that the root node is the identity matrix as it implies
|
||||
// there were no errors with the original data.
|
||||
func newInversionTree(dataShards, parityShards int) inversionTree {
|
||||
identity, _ := identityMatrix(dataShards)
|
||||
root := inversionNode{
|
||||
matrix: identity,
|
||||
children: make([]*inversionNode, dataShards+parityShards),
|
||||
}
|
||||
return inversionTree{
|
||||
mutex: &sync.RWMutex{},
|
||||
root: root,
|
||||
}
|
||||
}
|
||||
|
||||
// GetInvertedMatrix returns the cached inverted matrix or nil if it
|
||||
// is not found in the tree keyed on the indices of invalid rows.
|
||||
func (t inversionTree) GetInvertedMatrix(invalidIndices []int) matrix {
|
||||
// Lock the tree for reading before accessing the tree.
|
||||
t.mutex.RLock()
|
||||
defer t.mutex.RUnlock()
|
||||
|
||||
// If no invalid indices were give we should return the root
|
||||
// identity matrix.
|
||||
if len(invalidIndices) == 0 {
|
||||
return t.root.matrix
|
||||
}
|
||||
|
||||
// Recursively search for the inverted matrix in the tree, passing in
|
||||
// 0 as the parent index as we start at the root of the tree.
|
||||
return t.root.getInvertedMatrix(invalidIndices, 0)
|
||||
}
|
||||
|
||||
// errAlreadySet is returned if the root node matrix is overwritten
|
||||
var errAlreadySet = errors.New("the root node identity matrix is already set")
|
||||
|
||||
// InsertInvertedMatrix inserts a new inverted matrix into the tree
|
||||
// keyed by the indices of invalid rows. The total number of shards
|
||||
// is required for creating the proper length lists of child nodes for
|
||||
// each node.
|
||||
func (t inversionTree) InsertInvertedMatrix(invalidIndices []int, matrix matrix, shards int) error {
|
||||
// If no invalid indices were given then we are done because the
|
||||
// root node is already set with the identity matrix.
|
||||
if len(invalidIndices) == 0 {
|
||||
return errAlreadySet
|
||||
}
|
||||
|
||||
if !matrix.IsSquare() {
|
||||
return errNotSquare
|
||||
}
|
||||
|
||||
// Lock the tree for writing and reading before accessing the tree.
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
// Recursively create nodes for the inverted matrix in the tree until
|
||||
// we reach the node to insert the matrix to. We start by passing in
|
||||
// 0 as the parent index as we start at the root of the tree.
|
||||
t.root.insertInvertedMatrix(invalidIndices, matrix, shards, 0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n inversionNode) getInvertedMatrix(invalidIndices []int, parent int) matrix {
|
||||
// Get the child node to search next from the list of children. The
|
||||
// list of children starts relative to the parent index passed in
|
||||
// because the indices of invalid rows is sorted (by default). As we
|
||||
// search recursively, the first invalid index gets popped off the list,
|
||||
// so when searching through the list of children, use that first invalid
|
||||
// index to find the child node.
|
||||
firstIndex := invalidIndices[0]
|
||||
node := n.children[firstIndex-parent]
|
||||
|
||||
// If the child node doesn't exist in the list yet, fail fast by
|
||||
// returning, so we can construct and insert the proper inverted matrix.
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If there's more than one invalid index left in the list we should
|
||||
// keep searching recursively.
|
||||
if len(invalidIndices) > 1 {
|
||||
// Search recursively on the child node by passing in the invalid indices
|
||||
// with the first index popped off the front. Also the parent index to
|
||||
// pass down is the first index plus one.
|
||||
return node.getInvertedMatrix(invalidIndices[1:], firstIndex+1)
|
||||
}
|
||||
// If there aren't any more invalid indices to search, we've found our
|
||||
// node. Return it, however keep in mind that the matrix could still be
|
||||
// nil because intermediary nodes in the tree are created sometimes with
|
||||
// their inversion matrices uninitialized.
|
||||
return node.matrix
|
||||
}
|
||||
|
||||
func (n inversionNode) insertInvertedMatrix(invalidIndices []int, matrix matrix, shards, parent int) {
|
||||
// As above, get the child node to search next from the list of children.
|
||||
// The list of children starts relative to the parent index passed in
|
||||
// because the indices of invalid rows is sorted (by default). As we
|
||||
// search recursively, the first invalid index gets popped off the list,
|
||||
// so when searching through the list of children, use that first invalid
|
||||
// index to find the child node.
|
||||
firstIndex := invalidIndices[0]
|
||||
node := n.children[firstIndex-parent]
|
||||
|
||||
// If the child node doesn't exist in the list yet, create a new
|
||||
// node because we have the writer lock and add it to the list
|
||||
// of children.
|
||||
if node == nil {
|
||||
// Make the length of the list of children equal to the number
|
||||
// of shards minus the first invalid index because the list of
|
||||
// invalid indices is sorted, so only this length of errors
|
||||
// are possible in the tree.
|
||||
node = &inversionNode{
|
||||
children: make([]*inversionNode, shards-firstIndex),
|
||||
}
|
||||
// Insert the new node into the tree at the first index relative
|
||||
// to the parent index that was given in this recursive call.
|
||||
n.children[firstIndex-parent] = node
|
||||
}
|
||||
|
||||
// If there's more than one invalid index left in the list we should
|
||||
// keep searching recursively in order to find the node to add our
|
||||
// matrix.
|
||||
if len(invalidIndices) > 1 {
|
||||
// As above, search recursively on the child node by passing in
|
||||
// the invalid indices with the first index popped off the front.
|
||||
// Also the total number of shards and parent index are passed down
|
||||
// which is equal to the first index plus one.
|
||||
node.insertInvertedMatrix(invalidIndices[1:], matrix, shards, firstIndex+1)
|
||||
} else {
|
||||
// If there aren't any more invalid indices to search, we've found our
|
||||
// node. Cache the inverted matrix in this node.
|
||||
node.matrix = matrix
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/**
|
||||
* Matrix Algebra over an 8-bit Galois Field
|
||||
*
|
||||
* Copyright 2015, Klaus Post
|
||||
* Copyright 2015, Backblaze, Inc.
|
||||
*/
|
||||
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// byte[row][col]
|
||||
type matrix [][]byte
|
||||
|
||||
// newMatrix returns a matrix of zeros.
|
||||
func newMatrix(rows, cols int) (matrix, error) {
|
||||
if rows <= 0 {
|
||||
return nil, errInvalidRowSize
|
||||
}
|
||||
if cols <= 0 {
|
||||
return nil, errInvalidColSize
|
||||
}
|
||||
|
||||
m := matrix(make([][]byte, rows))
|
||||
for i := range m {
|
||||
m[i] = make([]byte, cols)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewMatrixData initializes a matrix with the given row-major data.
|
||||
// Note that data is not copied from input.
|
||||
func newMatrixData(data [][]byte) (matrix, error) {
|
||||
m := matrix(data)
|
||||
err := m.Check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// IdentityMatrix returns an identity matrix of the given size.
|
||||
func identityMatrix(size int) (matrix, error) {
|
||||
m, err := newMatrix(size, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range m {
|
||||
m[i][i] = 1
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// errInvalidRowSize will be returned if attempting to create a matrix with negative or zero row number.
|
||||
var errInvalidRowSize = errors.New("invalid row size")
|
||||
|
||||
// errInvalidColSize will be returned if attempting to create a matrix with negative or zero column number.
|
||||
var errInvalidColSize = errors.New("invalid column size")
|
||||
|
||||
// errColSizeMismatch is returned if the size of matrix columns mismatch.
|
||||
var errColSizeMismatch = errors.New("column size is not the same for all rows")
|
||||
|
||||
func (m matrix) Check() error {
|
||||
rows := len(m)
|
||||
if rows <= 0 {
|
||||
return errInvalidRowSize
|
||||
}
|
||||
cols := len(m[0])
|
||||
if cols <= 0 {
|
||||
return errInvalidColSize
|
||||
}
|
||||
|
||||
for _, col := range m {
|
||||
if len(col) != cols {
|
||||
return errColSizeMismatch
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a human-readable string of the matrix contents.
|
||||
//
|
||||
// Example: [[1, 2], [3, 4]]
|
||||
func (m matrix) String() string {
|
||||
rowOut := make([]string, 0, len(m))
|
||||
for _, row := range m {
|
||||
colOut := make([]string, 0, len(row))
|
||||
for _, col := range row {
|
||||
colOut = append(colOut, strconv.Itoa(int(col)))
|
||||
}
|
||||
rowOut = append(rowOut, "["+strings.Join(colOut, ", ")+"]")
|
||||
}
|
||||
return "[" + strings.Join(rowOut, ", ") + "]"
|
||||
}
|
||||
|
||||
// Multiply multiplies this matrix (the one on the left) by another
|
||||
// matrix (the one on the right) and returns a new matrix with the result.
|
||||
func (m matrix) Multiply(right matrix) (matrix, error) {
|
||||
if len(m[0]) != len(right) {
|
||||
return nil, fmt.Errorf("columns on left (%d) is different than rows on right (%d)", len(m[0]), len(right))
|
||||
}
|
||||
result, _ := newMatrix(len(m), len(right[0]))
|
||||
for r, row := range result {
|
||||
for c := range row {
|
||||
var value byte
|
||||
for i := range m[0] {
|
||||
value ^= galMultiply(m[r][i], right[i][c])
|
||||
}
|
||||
result[r][c] = value
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Augment returns the concatenation of this matrix and the matrix on the right.
|
||||
func (m matrix) Augment(right matrix) (matrix, error) {
|
||||
if len(m) != len(right) {
|
||||
return nil, errMatrixSize
|
||||
}
|
||||
|
||||
result, _ := newMatrix(len(m), len(m[0])+len(right[0]))
|
||||
for r, row := range m {
|
||||
for c := range row {
|
||||
result[r][c] = m[r][c]
|
||||
}
|
||||
cols := len(m[0])
|
||||
for c := range right[0] {
|
||||
result[r][cols+c] = right[r][c]
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// errMatrixSize is returned if matrix dimensions are doesn't match.
|
||||
var errMatrixSize = errors.New("matrix sizes does not match")
|
||||
|
||||
func (m matrix) SameSize(n matrix) error {
|
||||
if len(m) != len(n) {
|
||||
return errMatrixSize
|
||||
}
|
||||
for i := range m {
|
||||
if len(m[i]) != len(n[i]) {
|
||||
return errMatrixSize
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a part of this matrix. Data is copied.
|
||||
func (m matrix) SubMatrix(rmin, cmin, rmax, cmax int) (matrix, error) {
|
||||
result, err := newMatrix(rmax-rmin, cmax-cmin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// OPTME: If used heavily, use copy function to copy slice
|
||||
for r := rmin; r < rmax; r++ {
|
||||
for c := cmin; c < cmax; c++ {
|
||||
result[r-rmin][c-cmin] = m[r][c]
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SwapRows Exchanges two rows in the matrix.
|
||||
func (m matrix) SwapRows(r1, r2 int) error {
|
||||
if r1 < 0 || len(m) <= r1 || r2 < 0 || len(m) <= r2 {
|
||||
return errInvalidRowSize
|
||||
}
|
||||
m[r2], m[r1] = m[r1], m[r2]
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsSquare will return true if the matrix is square
|
||||
// and nil if the matrix is square
|
||||
func (m matrix) IsSquare() bool {
|
||||
return len(m) == len(m[0])
|
||||
}
|
||||
|
||||
// errSingular is returned if the matrix is singular and cannot be inversed
|
||||
var errSingular = errors.New("matrix is singular")
|
||||
|
||||
// errNotSquare is returned if attempting to inverse a non-square matrix.
|
||||
var errNotSquare = errors.New("only square matrices can be inverted")
|
||||
|
||||
// Invert returns the inverse of this matrix.
|
||||
// Returns ErrSingular when the matrix is singular and doesn't have an inverse.
|
||||
// The matrix must be square, otherwise ErrNotSquare is returned.
|
||||
func (m matrix) Invert() (matrix, error) {
|
||||
if !m.IsSquare() {
|
||||
return nil, errNotSquare
|
||||
}
|
||||
|
||||
size := len(m)
|
||||
work, _ := identityMatrix(size)
|
||||
work, _ = m.Augment(work)
|
||||
|
||||
err := work.gaussianElimination()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return work.SubMatrix(0, size, size, size*2)
|
||||
}
|
||||
|
||||
func (m matrix) gaussianElimination() error {
|
||||
rows := len(m)
|
||||
columns := len(m[0])
|
||||
// Clear out the part below the main diagonal and scale the main
|
||||
// diagonal to be 1.
|
||||
for r := 0; r < rows; r++ {
|
||||
// If the element on the diagonal is 0, find a row below
|
||||
// that has a non-zero and swap them.
|
||||
if m[r][r] == 0 {
|
||||
for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
|
||||
if m[rowBelow][r] != 0 {
|
||||
m.SwapRows(r, rowBelow)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we couldn't find one, the matrix is singular.
|
||||
if m[r][r] == 0 {
|
||||
return errSingular
|
||||
}
|
||||
// Scale to 1.
|
||||
if m[r][r] != 1 {
|
||||
scale := galDivide(1, m[r][r])
|
||||
for c := 0; c < columns; c++ {
|
||||
m[r][c] = galMultiply(m[r][c], scale)
|
||||
}
|
||||
}
|
||||
// Make everything below the 1 be a 0 by subtracting
|
||||
// a multiple of it. (Subtraction and addition are
|
||||
// both exclusive or in the Galois field.)
|
||||
for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
|
||||
if m[rowBelow][r] != 0 {
|
||||
scale := m[rowBelow][r]
|
||||
for c := 0; c < columns; c++ {
|
||||
m[rowBelow][c] ^= galMultiply(scale, m[r][c])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now clear the part above the main diagonal.
|
||||
for d := 0; d < rows; d++ {
|
||||
for rowAbove := 0; rowAbove < d; rowAbove++ {
|
||||
if m[rowAbove][d] != 0 {
|
||||
scale := m[rowAbove][d]
|
||||
for c := 0; c < columns; c++ {
|
||||
m[rowAbove][c] ^= galMultiply(scale, m[d][c])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Vandermonde matrix, which is guaranteed to have the
|
||||
// property that any subset of rows that forms a square matrix
|
||||
// is invertible.
|
||||
func vandermonde(rows, cols int) (matrix, error) {
|
||||
result, err := newMatrix(rows, cols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for r, row := range result {
|
||||
for c := range row {
|
||||
result[r][c] = galExp(byte(r), c)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package reedsolomon
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/klauspost/cpuid"
|
||||
)
|
||||
|
||||
// Option allows to override processing parameters.
|
||||
type Option func(*options)
|
||||
|
||||
type options struct {
|
||||
maxGoroutines int
|
||||
minSplitSize int
|
||||
useAVX2, useSSSE3 bool
|
||||
}
|
||||
|
||||
var defaultOptions = options{
|
||||
maxGoroutines: 50,
|
||||
minSplitSize: 512,
|
||||
}
|
||||
|
||||
func init() {
|
||||
if runtime.GOMAXPROCS(0) <= 1 {
|
||||
defaultOptions.maxGoroutines = 1
|
||||
}
|
||||
// Detect CPU capabilities.
|
||||
defaultOptions.useSSSE3 = cpuid.CPU.SSSE3()
|
||||
defaultOptions.useAVX2 = cpuid.CPU.AVX2()
|
||||
}
|
||||
|
||||
// WithMaxGoroutines is the maximum number of goroutines number for encoding & decoding.
|
||||
// Jobs will be split into this many parts, unless each goroutine would have to process
|
||||
// less than minSplitSize bytes (set with WithMinSplitSize).
|
||||
// For the best speed, keep this well above the GOMAXPROCS number for more fine grained
|
||||
// scheduling.
|
||||
// If n <= 0, it is ignored.
|
||||
func WithMaxGoroutines(n int) Option {
|
||||
return func(o *options) {
|
||||
if n > 0 {
|
||||
o.maxGoroutines = n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MinSplitSize Is the minimum encoding size in bytes per goroutine.
|
||||
// See WithMaxGoroutines on how jobs are split.
|
||||
// If n <= 0, it is ignored.
|
||||
func WithMinSplitSize(n int) Option {
|
||||
return func(o *options) {
|
||||
if n > 0 {
|
||||
o.maxGoroutines = n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withSSE3(enabled bool) Option {
|
||||
return func(o *options) {
|
||||
o.useSSSE3 = enabled
|
||||
}
|
||||
}
|
||||
|
||||
func withAVX2(enabled bool) Option {
|
||||
return func(o *options) {
|
||||
o.useAVX2 = enabled
|
||||
}
|
||||
}
|
|
@ -0,0 +1,596 @@
|
|||
/**
|
||||
* Reed-Solomon Coding over 8-bit values.
|
||||
*
|
||||
* Copyright 2015, Klaus Post
|
||||
* Copyright 2015, Backblaze, Inc.
|
||||
*/
|
||||
|
||||
// Package reedsolomon enables Erasure Coding in Go
|
||||
//
|
||||
// For usage and examples, see https://github.com/klauspost/reedsolomon
|
||||
//
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Encoder is an interface to encode Reed-Salomon parity sets for your data.
|
||||
type Encoder interface {
|
||||
// Encodes parity for a set of data shards.
|
||||
// Input is 'shards' containing data shards followed by parity shards.
|
||||
// The number of shards must match the number given to New().
|
||||
// Each shard is a byte array, and they must all be the same size.
|
||||
// The parity shards will always be overwritten and the data shards
|
||||
// will remain the same, so it is safe for you to read from the
|
||||
// data shards while this is running.
|
||||
Encode(shards [][]byte) error
|
||||
|
||||
// Verify returns true if the parity shards contain correct data.
|
||||
// The data is the same format as Encode. No data is modified, so
|
||||
// you are allowed to read from data while this is running.
|
||||
Verify(shards [][]byte) (bool, error)
|
||||
|
||||
// Reconstruct will recreate the missing shards if possible.
|
||||
//
|
||||
// Given a list of shards, some of which contain data, fills in the
|
||||
// ones that don't have data.
|
||||
//
|
||||
// The length of the array must be equal to the total number of shards.
|
||||
// You indicate that a shard is missing by setting it to nil.
|
||||
//
|
||||
// If there are too few shards to reconstruct the missing
|
||||
// ones, ErrTooFewShards will be returned.
|
||||
//
|
||||
// The reconstructed shard set is complete, but integrity is not verified.
|
||||
// Use the Verify function to check if data set is ok.
|
||||
Reconstruct(shards [][]byte) error
|
||||
|
||||
// Split a data slice into the number of shards given to the encoder,
|
||||
// and create empty parity shards.
|
||||
//
|
||||
// The data will be split into equally sized shards.
|
||||
// If the data size isn't dividable by the number of shards,
|
||||
// the last shard will contain extra zeros.
|
||||
//
|
||||
// There must be at least 1 byte otherwise ErrShortData will be
|
||||
// returned.
|
||||
//
|
||||
// The data will not be copied, except for the last shard, so you
|
||||
// should not modify the data of the input slice afterwards.
|
||||
Split(data []byte) ([][]byte, error)
|
||||
|
||||
// Join the shards and write the data segment to dst.
|
||||
//
|
||||
// Only the data shards are considered.
|
||||
// You must supply the exact output size you want.
|
||||
// If there are to few shards given, ErrTooFewShards will be returned.
|
||||
// If the total data size is less than outSize, ErrShortData will be returned.
|
||||
Join(dst io.Writer, shards [][]byte, outSize int) error
|
||||
}
|
||||
|
||||
// reedSolomon contains a matrix for a specific
|
||||
// distribution of datashards and parity shards.
|
||||
// Construct if using New()
|
||||
type reedSolomon struct {
|
||||
DataShards int // Number of data shards, should not be modified.
|
||||
ParityShards int // Number of parity shards, should not be modified.
|
||||
Shards int // Total number of shards. Calculated, and should not be modified.
|
||||
m matrix
|
||||
tree inversionTree
|
||||
parity [][]byte
|
||||
o options
|
||||
}
|
||||
|
||||
// ErrInvShardNum will be returned by New, if you attempt to create
|
||||
// an Encoder where either data or parity shards is zero or less.
|
||||
var ErrInvShardNum = errors.New("cannot create Encoder with zero or less data/parity shards")
|
||||
|
||||
// ErrMaxShardNum will be returned by New, if you attempt to create
|
||||
// an Encoder where data and parity shards cannot be bigger than
|
||||
// Galois field GF(2^8) - 1.
|
||||
var ErrMaxShardNum = errors.New("cannot create Encoder with 255 or more data+parity shards")
|
||||
|
||||
// New creates a new encoder and initializes it to
|
||||
// the number of data shards and parity shards that
|
||||
// you want to use. You can reuse this encoder.
|
||||
// Note that the maximum number of data shards is 256.
|
||||
// If no options are supplied, default options are used.
|
||||
func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
|
||||
r := reedSolomon{
|
||||
DataShards: dataShards,
|
||||
ParityShards: parityShards,
|
||||
Shards: dataShards + parityShards,
|
||||
o: defaultOptions,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&r.o)
|
||||
}
|
||||
if dataShards <= 0 || parityShards <= 0 {
|
||||
return nil, ErrInvShardNum
|
||||
}
|
||||
|
||||
if dataShards+parityShards > 255 {
|
||||
return nil, ErrMaxShardNum
|
||||
}
|
||||
|
||||
// Start with a Vandermonde matrix. This matrix would work,
|
||||
// in theory, but doesn't have the property that the data
|
||||
// shards are unchanged after encoding.
|
||||
vm, err := vandermonde(r.Shards, dataShards)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Multiply by the inverse of the top square of the matrix.
|
||||
// This will make the top square be the identity matrix, but
|
||||
// preserve the property that any square subset of rows is
|
||||
// invertible.
|
||||
top, _ := vm.SubMatrix(0, 0, dataShards, dataShards)
|
||||
top, _ = top.Invert()
|
||||
r.m, _ = vm.Multiply(top)
|
||||
|
||||
// Inverted matrices are cached in a tree keyed by the indices
|
||||
// of the invalid rows of the data to reconstruct.
|
||||
// The inversion root node will have the identity matrix as
|
||||
// its inversion matrix because it implies there are no errors
|
||||
// with the original data.
|
||||
r.tree = newInversionTree(dataShards, parityShards)
|
||||
|
||||
r.parity = make([][]byte, parityShards)
|
||||
for i := range r.parity {
|
||||
r.parity[i] = r.m[dataShards+i]
|
||||
}
|
||||
|
||||
return &r, err
|
||||
}
|
||||
|
||||
// ErrTooFewShards is returned if too few shards where given to
|
||||
// Encode/Verify/Reconstruct. It will also be returned from Reconstruct
|
||||
// if there were too few shards to reconstruct the missing data.
|
||||
var ErrTooFewShards = errors.New("too few shards given")
|
||||
|
||||
// Encodes parity for a set of data shards.
|
||||
// An array 'shards' containing data shards followed by parity shards.
|
||||
// The number of shards must match the number given to New.
|
||||
// Each shard is a byte array, and they must all be the same size.
|
||||
// The parity shards will always be overwritten and the data shards
|
||||
// will remain the same.
|
||||
func (r reedSolomon) Encode(shards [][]byte) error {
|
||||
if len(shards) != r.Shards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
err := checkShards(shards, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the slice of output buffers.
|
||||
output := shards[r.DataShards:]
|
||||
|
||||
// Do the coding.
|
||||
r.codeSomeShards(r.parity, shards[0:r.DataShards], output, r.ParityShards, len(shards[0]))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify returns true if the parity shards contain the right data.
|
||||
// The data is the same format as Encode. No data is modified.
|
||||
func (r reedSolomon) Verify(shards [][]byte) (bool, error) {
|
||||
if len(shards) != r.Shards {
|
||||
return false, ErrTooFewShards
|
||||
}
|
||||
err := checkShards(shards, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Slice of buffers being checked.
|
||||
toCheck := shards[r.DataShards:]
|
||||
|
||||
// Do the checking.
|
||||
return r.checkSomeShards(r.parity, shards[0:r.DataShards], toCheck, r.ParityShards, len(shards[0])), nil
|
||||
}
|
||||
|
||||
// Multiplies a subset of rows from a coding matrix by a full set of
|
||||
// input shards to produce some output shards.
|
||||
// 'matrixRows' is The rows from the matrix to use.
|
||||
// 'inputs' An array of byte arrays, each of which is one input shard.
|
||||
// The number of inputs used is determined by the length of each matrix row.
|
||||
// outputs Byte arrays where the computed shards are stored.
|
||||
// The number of outputs computed, and the
|
||||
// number of matrix rows used, is determined by
|
||||
// outputCount, which is the number of outputs to compute.
|
||||
func (r reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
|
||||
if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
|
||||
r.codeSomeShardsP(matrixRows, inputs, outputs, outputCount, byteCount)
|
||||
return
|
||||
}
|
||||
for c := 0; c < r.DataShards; c++ {
|
||||
in := inputs[c]
|
||||
for iRow := 0; iRow < outputCount; iRow++ {
|
||||
if c == 0 {
|
||||
galMulSlice(matrixRows[iRow][c], in, outputs[iRow], r.o.useSSSE3, r.o.useAVX2)
|
||||
} else {
|
||||
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], r.o.useSSSE3, r.o.useAVX2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the same as codeSomeShards, but split the workload into
|
||||
// several goroutines.
|
||||
func (r reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
|
||||
var wg sync.WaitGroup
|
||||
do := byteCount / r.o.maxGoroutines
|
||||
if do < r.o.minSplitSize {
|
||||
do = r.o.minSplitSize
|
||||
}
|
||||
start := 0
|
||||
for start < byteCount {
|
||||
if start+do > byteCount {
|
||||
do = byteCount - start
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(start, stop int) {
|
||||
for c := 0; c < r.DataShards; c++ {
|
||||
in := inputs[c]
|
||||
for iRow := 0; iRow < outputCount; iRow++ {
|
||||
if c == 0 {
|
||||
galMulSlice(matrixRows[iRow][c], in[start:stop], outputs[iRow][start:stop], r.o.useSSSE3, r.o.useAVX2)
|
||||
} else {
|
||||
galMulSliceXor(matrixRows[iRow][c], in[start:stop], outputs[iRow][start:stop], r.o.useSSSE3, r.o.useAVX2)
|
||||
}
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(start, start+do)
|
||||
start += do
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// checkSomeShards is mostly the same as codeSomeShards,
|
||||
// except this will check values and return
|
||||
// as soon as a difference is found.
|
||||
func (r reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
|
||||
if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
|
||||
return r.checkSomeShardsP(matrixRows, inputs, toCheck, outputCount, byteCount)
|
||||
}
|
||||
outputs := make([][]byte, len(toCheck))
|
||||
for i := range outputs {
|
||||
outputs[i] = make([]byte, byteCount)
|
||||
}
|
||||
for c := 0; c < r.DataShards; c++ {
|
||||
in := inputs[c]
|
||||
for iRow := 0; iRow < outputCount; iRow++ {
|
||||
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], r.o.useSSSE3, r.o.useAVX2)
|
||||
}
|
||||
}
|
||||
|
||||
for i, calc := range outputs {
|
||||
if !bytes.Equal(calc, toCheck[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r reedSolomon) checkSomeShardsP(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
|
||||
same := true
|
||||
var mu sync.RWMutex // For above
|
||||
|
||||
var wg sync.WaitGroup
|
||||
do := byteCount / r.o.maxGoroutines
|
||||
if do < r.o.minSplitSize {
|
||||
do = r.o.minSplitSize
|
||||
}
|
||||
start := 0
|
||||
for start < byteCount {
|
||||
if start+do > byteCount {
|
||||
do = byteCount - start
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(start, do int) {
|
||||
defer wg.Done()
|
||||
outputs := make([][]byte, len(toCheck))
|
||||
for i := range outputs {
|
||||
outputs[i] = make([]byte, do)
|
||||
}
|
||||
for c := 0; c < r.DataShards; c++ {
|
||||
mu.RLock()
|
||||
if !same {
|
||||
mu.RUnlock()
|
||||
return
|
||||
}
|
||||
mu.RUnlock()
|
||||
in := inputs[c][start : start+do]
|
||||
for iRow := 0; iRow < outputCount; iRow++ {
|
||||
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], r.o.useSSSE3, r.o.useAVX2)
|
||||
}
|
||||
}
|
||||
|
||||
for i, calc := range outputs {
|
||||
if !bytes.Equal(calc, toCheck[i][start:start+do]) {
|
||||
mu.Lock()
|
||||
same = false
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
}(start, do)
|
||||
start += do
|
||||
}
|
||||
wg.Wait()
|
||||
return same
|
||||
}
|
||||
|
||||
// ErrShardNoData will be returned if there are no shards,
|
||||
// or if the length of all shards is zero.
|
||||
var ErrShardNoData = errors.New("no shard data")
|
||||
|
||||
// ErrShardSize is returned if shard length isn't the same for all
|
||||
// shards.
|
||||
var ErrShardSize = errors.New("shard sizes does not match")
|
||||
|
||||
// checkShards will check if shards are the same size
|
||||
// or 0, if allowed. An error is returned if this fails.
|
||||
// An error is also returned if all shards are size 0.
|
||||
func checkShards(shards [][]byte, nilok bool) error {
|
||||
size := shardSize(shards)
|
||||
if size == 0 {
|
||||
return ErrShardNoData
|
||||
}
|
||||
for _, shard := range shards {
|
||||
if len(shard) != size {
|
||||
if len(shard) != 0 || !nilok {
|
||||
return ErrShardSize
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// shardSize return the size of a single shard.
|
||||
// The first non-zero size is returned,
|
||||
// or 0 if all shards are size 0.
|
||||
func shardSize(shards [][]byte) int {
|
||||
for _, shard := range shards {
|
||||
if len(shard) != 0 {
|
||||
return len(shard)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Reconstruct will recreate the missing shards, if possible.
|
||||
//
|
||||
// Given a list of shards, some of which contain data, fills in the
|
||||
// ones that don't have data.
|
||||
//
|
||||
// The length of the array must be equal to Shards.
|
||||
// You indicate that a shard is missing by setting it to nil.
|
||||
//
|
||||
// If there are too few shards to reconstruct the missing
|
||||
// ones, ErrTooFewShards will be returned.
|
||||
//
|
||||
// The reconstructed shard set is complete, but integrity is not verified.
|
||||
// Use the Verify function to check if data set is ok.
|
||||
func (r reedSolomon) Reconstruct(shards [][]byte) error {
|
||||
if len(shards) != r.Shards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
// Check arguments.
|
||||
err := checkShards(shards, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shardSize := shardSize(shards)
|
||||
|
||||
// Quick check: are all of the shards present? If so, there's
|
||||
// nothing to do.
|
||||
numberPresent := 0
|
||||
for i := 0; i < r.Shards; i++ {
|
||||
if len(shards[i]) != 0 {
|
||||
numberPresent++
|
||||
}
|
||||
}
|
||||
if numberPresent == r.Shards {
|
||||
// Cool. All of the shards data data. We don't
|
||||
// need to do anything.
|
||||
return nil
|
||||
}
|
||||
|
||||
// More complete sanity check
|
||||
if numberPresent < r.DataShards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
// Pull out an array holding just the shards that
|
||||
// correspond to the rows of the submatrix. These shards
|
||||
// will be the input to the decoding process that re-creates
|
||||
// the missing data shards.
|
||||
//
|
||||
// Also, create an array of indices of the valid rows we do have
|
||||
// and the invalid rows we don't have up until we have enough valid rows.
|
||||
subShards := make([][]byte, r.DataShards)
|
||||
validIndices := make([]int, r.DataShards)
|
||||
invalidIndices := make([]int, 0)
|
||||
subMatrixRow := 0
|
||||
for matrixRow := 0; matrixRow < r.Shards && subMatrixRow < r.DataShards; matrixRow++ {
|
||||
if len(shards[matrixRow]) != 0 {
|
||||
subShards[subMatrixRow] = shards[matrixRow]
|
||||
validIndices[subMatrixRow] = matrixRow
|
||||
subMatrixRow++
|
||||
} else {
|
||||
invalidIndices = append(invalidIndices, matrixRow)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to get the cached inverted matrix out of the tree
|
||||
// based on the indices of the invalid rows.
|
||||
dataDecodeMatrix := r.tree.GetInvertedMatrix(invalidIndices)
|
||||
|
||||
// If the inverted matrix isn't cached in the tree yet we must
|
||||
// construct it ourselves and insert it into the tree for the
|
||||
// future. In this way the inversion tree is lazily loaded.
|
||||
if dataDecodeMatrix == nil {
|
||||
// Pull out the rows of the matrix that correspond to the
|
||||
// shards that we have and build a square matrix. This
|
||||
// matrix could be used to generate the shards that we have
|
||||
// from the original data.
|
||||
subMatrix, _ := newMatrix(r.DataShards, r.DataShards)
|
||||
for subMatrixRow, validIndex := range validIndices {
|
||||
for c := 0; c < r.DataShards; c++ {
|
||||
subMatrix[subMatrixRow][c] = r.m[validIndex][c]
|
||||
}
|
||||
}
|
||||
// Invert the matrix, so we can go from the encoded shards
|
||||
// back to the original data. Then pull out the row that
|
||||
// generates the shard that we want to decode. Note that
|
||||
// since this matrix maps back to the original data, it can
|
||||
// be used to create a data shard, but not a parity shard.
|
||||
dataDecodeMatrix, err = subMatrix.Invert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Cache the inverted matrix in the tree for future use keyed on the
|
||||
// indices of the invalid rows.
|
||||
err = r.tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix, r.Shards)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Re-create any data shards that were missing.
|
||||
//
|
||||
// The input to the coding is all of the shards we actually
|
||||
// have, and the output is the missing data shards. The computation
|
||||
// is done using the special decode matrix we just built.
|
||||
outputs := make([][]byte, r.ParityShards)
|
||||
matrixRows := make([][]byte, r.ParityShards)
|
||||
outputCount := 0
|
||||
|
||||
for iShard := 0; iShard < r.DataShards; iShard++ {
|
||||
if len(shards[iShard]) == 0 {
|
||||
shards[iShard] = make([]byte, shardSize)
|
||||
outputs[outputCount] = shards[iShard]
|
||||
matrixRows[outputCount] = dataDecodeMatrix[iShard]
|
||||
outputCount++
|
||||
}
|
||||
}
|
||||
r.codeSomeShards(matrixRows, subShards, outputs[:outputCount], outputCount, shardSize)
|
||||
|
||||
// Now that we have all of the data shards intact, we can
|
||||
// compute any of the parity that is missing.
|
||||
//
|
||||
// The input to the coding is ALL of the data shards, including
|
||||
// any that we just calculated. The output is whichever of the
|
||||
// data shards were missing.
|
||||
outputCount = 0
|
||||
for iShard := r.DataShards; iShard < r.Shards; iShard++ {
|
||||
if len(shards[iShard]) == 0 {
|
||||
shards[iShard] = make([]byte, shardSize)
|
||||
outputs[outputCount] = shards[iShard]
|
||||
matrixRows[outputCount] = r.parity[iShard-r.DataShards]
|
||||
outputCount++
|
||||
}
|
||||
}
|
||||
r.codeSomeShards(matrixRows, shards[:r.DataShards], outputs[:outputCount], outputCount, shardSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrShortData will be returned by Split(), if there isn't enough data
|
||||
// to fill the number of shards.
|
||||
var ErrShortData = errors.New("not enough data to fill the number of requested shards")
|
||||
|
||||
// Split a data slice into the number of shards given to the encoder,
|
||||
// and create empty parity shards.
|
||||
//
|
||||
// The data will be split into equally sized shards.
|
||||
// If the data size isn't divisible by the number of shards,
|
||||
// the last shard will contain extra zeros.
|
||||
//
|
||||
// There must be at least 1 byte otherwise ErrShortData will be
|
||||
// returned.
|
||||
//
|
||||
// The data will not be copied, except for the last shard, so you
|
||||
// should not modify the data of the input slice afterwards.
|
||||
func (r reedSolomon) Split(data []byte) ([][]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, ErrShortData
|
||||
}
|
||||
// Calculate number of bytes per shard.
|
||||
perShard := (len(data) + r.DataShards - 1) / r.DataShards
|
||||
|
||||
// Pad data to r.Shards*perShard.
|
||||
padding := make([]byte, (r.Shards*perShard)-len(data))
|
||||
data = append(data, padding...)
|
||||
|
||||
// Split into equal-length shards.
|
||||
dst := make([][]byte, r.Shards)
|
||||
for i := range dst {
|
||||
dst[i] = data[:perShard]
|
||||
data = data[perShard:]
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// ErrReconstructRequired is returned if too few data shards are intact and a
|
||||
// reconstruction is required before you can successfully join the shards.
|
||||
var ErrReconstructRequired = errors.New("reconstruction required as one or more required data shards are nil")
|
||||
|
||||
// Join the shards and write the data segment to dst.
|
||||
//
|
||||
// Only the data shards are considered.
|
||||
// You must supply the exact output size you want.
|
||||
//
|
||||
// If there are to few shards given, ErrTooFewShards will be returned.
|
||||
// If the total data size is less than outSize, ErrShortData will be returned.
|
||||
// If one or more required data shards are nil, ErrReconstructRequired will be returned.
|
||||
func (r reedSolomon) Join(dst io.Writer, shards [][]byte, outSize int) error {
|
||||
// Do we have enough shards?
|
||||
if len(shards) < r.DataShards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
shards = shards[:r.DataShards]
|
||||
|
||||
// Do we have enough data?
|
||||
size := 0
|
||||
for _, shard := range shards {
|
||||
if shard == nil {
|
||||
return ErrReconstructRequired
|
||||
}
|
||||
size += len(shard)
|
||||
|
||||
// Do we have enough data already?
|
||||
if size >= outSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
if size < outSize {
|
||||
return ErrShortData
|
||||
}
|
||||
|
||||
// Copy data to dst
|
||||
write := outSize
|
||||
for _, shard := range shards {
|
||||
if write < len(shard) {
|
||||
_, err := dst.Write(shard[:write])
|
||||
return err
|
||||
}
|
||||
n, err := dst.Write(shard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
write -= n
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,575 @@
|
|||
/**
|
||||
* Reed-Solomon Coding over 8-bit values.
|
||||
*
|
||||
* Copyright 2015, Klaus Post
|
||||
* Copyright 2015, Backblaze, Inc.
|
||||
*/
|
||||
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// StreamEncoder is an interface to encode Reed-Salomon parity sets for your data.
|
||||
// It provides a fully streaming interface, and processes data in blocks of up to 4MB.
|
||||
//
|
||||
// For small shard sizes, 10MB and below, it is recommended to use the in-memory interface,
|
||||
// since the streaming interface has a start up overhead.
|
||||
//
|
||||
// For all operations, no readers and writers should not assume any order/size of
|
||||
// individual reads/writes.
|
||||
//
|
||||
// For usage examples, see "stream-encoder.go" and "streamdecoder.go" in the examples
|
||||
// folder.
|
||||
type StreamEncoder interface {
|
||||
// Encodes parity shards for a set of data shards.
|
||||
//
|
||||
// Input is 'shards' containing readers for data shards followed by parity shards
|
||||
// io.Writer.
|
||||
//
|
||||
// The number of shards must match the number given to NewStream().
|
||||
//
|
||||
// Each reader must supply the same number of bytes.
|
||||
//
|
||||
// The parity shards will be written to the writer.
|
||||
// The number of bytes written will match the input size.
|
||||
//
|
||||
// If a data stream returns an error, a StreamReadError type error
|
||||
// will be returned. If a parity writer returns an error, a
|
||||
// StreamWriteError will be returned.
|
||||
Encode(data []io.Reader, parity []io.Writer) error
|
||||
|
||||
// Verify returns true if the parity shards contain correct data.
|
||||
//
|
||||
// The number of shards must match the number total data+parity shards
|
||||
// given to NewStream().
|
||||
//
|
||||
// Each reader must supply the same number of bytes.
|
||||
// If a shard stream returns an error, a StreamReadError type error
|
||||
// will be returned.
|
||||
Verify(shards []io.Reader) (bool, error)
|
||||
|
||||
// Reconstruct will recreate the missing shards if possible.
|
||||
//
|
||||
// Given a list of valid shards (to read) and invalid shards (to write)
|
||||
//
|
||||
// You indicate that a shard is missing by setting it to nil in the 'valid'
|
||||
// slice and at the same time setting a non-nil writer in "fill".
|
||||
// An index cannot contain both non-nil 'valid' and 'fill' entry.
|
||||
// If both are provided 'ErrReconstructMismatch' is returned.
|
||||
//
|
||||
// If there are too few shards to reconstruct the missing
|
||||
// ones, ErrTooFewShards will be returned.
|
||||
//
|
||||
// The reconstructed shard set is complete, but integrity is not verified.
|
||||
// Use the Verify function to check if data set is ok.
|
||||
Reconstruct(valid []io.Reader, fill []io.Writer) error
|
||||
|
||||
// Split a an input stream into the number of shards given to the encoder.
|
||||
//
|
||||
// The data will be split into equally sized shards.
|
||||
// If the data size isn't dividable by the number of shards,
|
||||
// the last shard will contain extra zeros.
|
||||
//
|
||||
// You must supply the total size of your input.
|
||||
// 'ErrShortData' will be returned if it is unable to retrieve the
|
||||
// number of bytes indicated.
|
||||
Split(data io.Reader, dst []io.Writer, size int64) (err error)
|
||||
|
||||
// Join the shards and write the data segment to dst.
|
||||
//
|
||||
// Only the data shards are considered.
|
||||
//
|
||||
// You must supply the exact output size you want.
|
||||
// If there are to few shards given, ErrTooFewShards will be returned.
|
||||
// If the total data size is less than outSize, ErrShortData will be returned.
|
||||
Join(dst io.Writer, shards []io.Reader, outSize int64) error
|
||||
}
|
||||
|
||||
// StreamReadError is returned when a read error is encountered
|
||||
// that relates to a supplied stream.
|
||||
// This will allow you to find out which reader has failed.
|
||||
type StreamReadError struct {
|
||||
Err error // The error
|
||||
Stream int // The stream number on which the error occurred
|
||||
}
|
||||
|
||||
// Error returns the error as a string
|
||||
func (s StreamReadError) Error() string {
|
||||
return fmt.Sprintf("error reading stream %d: %s", s.Stream, s.Err)
|
||||
}
|
||||
|
||||
// String returns the error as a string
|
||||
func (s StreamReadError) String() string {
|
||||
return s.Error()
|
||||
}
|
||||
|
||||
// StreamWriteError is returned when a write error is encountered
|
||||
// that relates to a supplied stream. This will allow you to
|
||||
// find out which reader has failed.
|
||||
type StreamWriteError struct {
|
||||
Err error // The error
|
||||
Stream int // The stream number on which the error occurred
|
||||
}
|
||||
|
||||
// Error returns the error as a string
|
||||
func (s StreamWriteError) Error() string {
|
||||
return fmt.Sprintf("error writing stream %d: %s", s.Stream, s.Err)
|
||||
}
|
||||
|
||||
// String returns the error as a string
|
||||
func (s StreamWriteError) String() string {
|
||||
return s.Error()
|
||||
}
|
||||
|
||||
// rsStream contains a matrix for a specific
|
||||
// distribution of datashards and parity shards.
|
||||
// Construct if using NewStream()
|
||||
type rsStream struct {
|
||||
r *reedSolomon
|
||||
bs int // Block size
|
||||
// Shard reader
|
||||
readShards func(dst [][]byte, in []io.Reader) error
|
||||
// Shard writer
|
||||
writeShards func(out []io.Writer, in [][]byte) error
|
||||
creads bool
|
||||
cwrites bool
|
||||
}
|
||||
|
||||
// NewStream creates a new encoder and initializes it to
|
||||
// the number of data shards and parity shards that
|
||||
// you want to use. You can reuse this encoder.
|
||||
// Note that the maximum number of data shards is 256.
|
||||
func NewStream(dataShards, parityShards int, o ...Option) (StreamEncoder, error) {
|
||||
enc, err := New(dataShards, parityShards, o...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs := enc.(*reedSolomon)
|
||||
r := rsStream{r: rs, bs: 4 << 20}
|
||||
r.readShards = readShards
|
||||
r.writeShards = writeShards
|
||||
return &r, err
|
||||
}
|
||||
|
||||
// NewStreamC creates a new encoder and initializes it to
|
||||
// the number of data shards and parity shards given.
|
||||
//
|
||||
// This functions as 'NewStream', but allows you to enable CONCURRENT reads and writes.
|
||||
func NewStreamC(dataShards, parityShards int, conReads, conWrites bool, o ...Option) (StreamEncoder, error) {
|
||||
enc, err := New(dataShards, parityShards, o...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs := enc.(*reedSolomon)
|
||||
r := rsStream{r: rs, bs: 4 << 20}
|
||||
r.readShards = readShards
|
||||
r.writeShards = writeShards
|
||||
if conReads {
|
||||
r.readShards = cReadShards
|
||||
}
|
||||
if conWrites {
|
||||
r.writeShards = cWriteShards
|
||||
}
|
||||
return &r, err
|
||||
}
|
||||
|
||||
func createSlice(n, length int) [][]byte {
|
||||
out := make([][]byte, n)
|
||||
for i := range out {
|
||||
out[i] = make([]byte, length)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Encodes parity shards for a set of data shards.
|
||||
//
|
||||
// Input is 'shards' containing readers for data shards followed by parity shards
|
||||
// io.Writer.
|
||||
//
|
||||
// The number of shards must match the number given to NewStream().
|
||||
//
|
||||
// Each reader must supply the same number of bytes.
|
||||
//
|
||||
// The parity shards will be written to the writer.
|
||||
// The number of bytes written will match the input size.
|
||||
//
|
||||
// If a data stream returns an error, a StreamReadError type error
|
||||
// will be returned. If a parity writer returns an error, a
|
||||
// StreamWriteError will be returned.
|
||||
func (r rsStream) Encode(data []io.Reader, parity []io.Writer) error {
|
||||
if len(data) != r.r.DataShards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
if len(parity) != r.r.ParityShards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
all := createSlice(r.r.Shards, r.bs)
|
||||
in := all[:r.r.DataShards]
|
||||
out := all[r.r.DataShards:]
|
||||
read := 0
|
||||
|
||||
for {
|
||||
err := r.readShards(in, data)
|
||||
switch err {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
if read == 0 {
|
||||
return ErrShardNoData
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
out = trimShards(out, shardSize(in))
|
||||
read += shardSize(in)
|
||||
err = r.r.Encode(all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.writeShards(parity, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the shards so they are all the same size
|
||||
func trimShards(in [][]byte, size int) [][]byte {
|
||||
for i := range in {
|
||||
if in[i] != nil {
|
||||
in[i] = in[i][0:size]
|
||||
}
|
||||
if len(in[i]) < size {
|
||||
in[i] = nil
|
||||
}
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func readShards(dst [][]byte, in []io.Reader) error {
|
||||
if len(in) != len(dst) {
|
||||
panic("internal error: in and dst size does not match")
|
||||
}
|
||||
size := -1
|
||||
for i := range in {
|
||||
if in[i] == nil {
|
||||
dst[i] = nil
|
||||
continue
|
||||
}
|
||||
n, err := io.ReadFull(in[i], dst[i])
|
||||
// The error is EOF only if no bytes were read.
|
||||
// If an EOF happens after reading some but not all the bytes,
|
||||
// ReadFull returns ErrUnexpectedEOF.
|
||||
switch err {
|
||||
case io.ErrUnexpectedEOF, io.EOF:
|
||||
if size < 0 {
|
||||
size = n
|
||||
} else if n != size {
|
||||
// Shard sizes must match.
|
||||
return ErrShardSize
|
||||
}
|
||||
dst[i] = dst[i][0:n]
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
return StreamReadError{Err: err, Stream: i}
|
||||
}
|
||||
}
|
||||
if size == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeShards(out []io.Writer, in [][]byte) error {
|
||||
if len(out) != len(in) {
|
||||
panic("internal error: in and out size does not match")
|
||||
}
|
||||
for i := range in {
|
||||
if out[i] == nil {
|
||||
continue
|
||||
}
|
||||
n, err := out[i].Write(in[i])
|
||||
if err != nil {
|
||||
return StreamWriteError{Err: err, Stream: i}
|
||||
}
|
||||
//
|
||||
if n != len(in[i]) {
|
||||
return StreamWriteError{Err: io.ErrShortWrite, Stream: i}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type readResult struct {
|
||||
n int
|
||||
size int
|
||||
err error
|
||||
}
|
||||
|
||||
// cReadShards reads shards concurrently
|
||||
func cReadShards(dst [][]byte, in []io.Reader) error {
|
||||
if len(in) != len(dst) {
|
||||
panic("internal error: in and dst size does not match")
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(in))
|
||||
res := make(chan readResult, len(in))
|
||||
for i := range in {
|
||||
if in[i] == nil {
|
||||
dst[i] = nil
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
n, err := io.ReadFull(in[i], dst[i])
|
||||
// The error is EOF only if no bytes were read.
|
||||
// If an EOF happens after reading some but not all the bytes,
|
||||
// ReadFull returns ErrUnexpectedEOF.
|
||||
res <- readResult{size: n, err: err, n: i}
|
||||
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
close(res)
|
||||
size := -1
|
||||
for r := range res {
|
||||
switch r.err {
|
||||
case io.ErrUnexpectedEOF, io.EOF:
|
||||
if size < 0 {
|
||||
size = r.size
|
||||
} else if r.size != size {
|
||||
// Shard sizes must match.
|
||||
return ErrShardSize
|
||||
}
|
||||
dst[r.n] = dst[r.n][0:r.size]
|
||||
case nil:
|
||||
default:
|
||||
return StreamReadError{Err: r.err, Stream: r.n}
|
||||
}
|
||||
}
|
||||
if size == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cWriteShards writes shards concurrently
|
||||
func cWriteShards(out []io.Writer, in [][]byte) error {
|
||||
if len(out) != len(in) {
|
||||
panic("internal error: in and out size does not match")
|
||||
}
|
||||
var errs = make(chan error, len(out))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(out))
|
||||
for i := range in {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
if out[i] == nil {
|
||||
errs <- nil
|
||||
return
|
||||
}
|
||||
n, err := out[i].Write(in[i])
|
||||
if err != nil {
|
||||
errs <- StreamWriteError{Err: err, Stream: i}
|
||||
return
|
||||
}
|
||||
if n != len(in[i]) {
|
||||
errs <- StreamWriteError{Err: io.ErrShortWrite, Stream: i}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
close(errs)
|
||||
for err := range errs {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify returns true if the parity shards contain correct data.
|
||||
//
|
||||
// The number of shards must match the number total data+parity shards
|
||||
// given to NewStream().
|
||||
//
|
||||
// Each reader must supply the same number of bytes.
|
||||
// If a shard stream returns an error, a StreamReadError type error
|
||||
// will be returned.
|
||||
func (r rsStream) Verify(shards []io.Reader) (bool, error) {
|
||||
if len(shards) != r.r.Shards {
|
||||
return false, ErrTooFewShards
|
||||
}
|
||||
|
||||
read := 0
|
||||
all := createSlice(r.r.Shards, r.bs)
|
||||
for {
|
||||
err := r.readShards(all, shards)
|
||||
if err == io.EOF {
|
||||
if read == 0 {
|
||||
return false, ErrShardNoData
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
read += shardSize(all)
|
||||
ok, err := r.r.Verify(all)
|
||||
if !ok || err != nil {
|
||||
return ok, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrReconstructMismatch is returned by the StreamEncoder, if you supply
|
||||
// "valid" and "fill" streams on the same index.
|
||||
// Therefore it is impossible to see if you consider the shard valid
|
||||
// or would like to have it reconstructed.
|
||||
var ErrReconstructMismatch = errors.New("valid shards and fill shards are mutually exclusive")
|
||||
|
||||
// Reconstruct will recreate the missing shards if possible.
|
||||
//
|
||||
// Given a list of valid shards (to read) and invalid shards (to write)
|
||||
//
|
||||
// You indicate that a shard is missing by setting it to nil in the 'valid'
|
||||
// slice and at the same time setting a non-nil writer in "fill".
|
||||
// An index cannot contain both non-nil 'valid' and 'fill' entry.
|
||||
//
|
||||
// If there are too few shards to reconstruct the missing
|
||||
// ones, ErrTooFewShards will be returned.
|
||||
//
|
||||
// The reconstructed shard set is complete, but integrity is not verified.
|
||||
// Use the Verify function to check if data set is ok.
|
||||
func (r rsStream) Reconstruct(valid []io.Reader, fill []io.Writer) error {
|
||||
if len(valid) != r.r.Shards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
if len(fill) != r.r.Shards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
all := createSlice(r.r.Shards, r.bs)
|
||||
for i := range valid {
|
||||
if valid[i] != nil && fill[i] != nil {
|
||||
return ErrReconstructMismatch
|
||||
}
|
||||
}
|
||||
|
||||
read := 0
|
||||
for {
|
||||
err := r.readShards(all, valid)
|
||||
if err == io.EOF {
|
||||
if read == 0 {
|
||||
return ErrShardNoData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
read += shardSize(all)
|
||||
all = trimShards(all, shardSize(all))
|
||||
|
||||
err = r.r.Reconstruct(all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.writeShards(fill, all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join the shards and write the data segment to dst.
|
||||
//
|
||||
// Only the data shards are considered.
|
||||
//
|
||||
// You must supply the exact output size you want.
|
||||
// If there are to few shards given, ErrTooFewShards will be returned.
|
||||
// If the total data size is less than outSize, ErrShortData will be returned.
|
||||
func (r rsStream) Join(dst io.Writer, shards []io.Reader, outSize int64) error {
|
||||
// Do we have enough shards?
|
||||
if len(shards) < r.r.DataShards {
|
||||
return ErrTooFewShards
|
||||
}
|
||||
|
||||
// Trim off parity shards if any
|
||||
shards = shards[:r.r.DataShards]
|
||||
for i := range shards {
|
||||
if shards[i] == nil {
|
||||
return StreamReadError{Err: ErrShardNoData, Stream: i}
|
||||
}
|
||||
}
|
||||
// Join all shards
|
||||
src := io.MultiReader(shards...)
|
||||
|
||||
// Copy data to dst
|
||||
n, err := io.CopyN(dst, src, outSize)
|
||||
if err == io.EOF {
|
||||
return ErrShortData
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != outSize {
|
||||
return ErrShortData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Split a an input stream into the number of shards given to the encoder.
|
||||
//
|
||||
// The data will be split into equally sized shards.
|
||||
// If the data size isn't dividable by the number of shards,
|
||||
// the last shard will contain extra zeros.
|
||||
//
|
||||
// You must supply the total size of your input.
|
||||
// 'ErrShortData' will be returned if it is unable to retrieve the
|
||||
// number of bytes indicated.
|
||||
func (r rsStream) Split(data io.Reader, dst []io.Writer, size int64) error {
|
||||
if size == 0 {
|
||||
return ErrShortData
|
||||
}
|
||||
if len(dst) != r.r.DataShards {
|
||||
return ErrInvShardNum
|
||||
}
|
||||
|
||||
for i := range dst {
|
||||
if dst[i] == nil {
|
||||
return StreamWriteError{Err: ErrShardNoData, Stream: i}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate number of bytes per shard.
|
||||
perShard := (size + int64(r.r.DataShards) - 1) / int64(r.r.DataShards)
|
||||
|
||||
// Pad data to r.Shards*perShard.
|
||||
padding := make([]byte, (int64(r.r.Shards)*perShard)-size)
|
||||
data = io.MultiReader(data, bytes.NewBuffer(padding))
|
||||
|
||||
// Split into equal-length shards and copy.
|
||||
for i := range dst {
|
||||
n, err := io.CopyN(dst[i], data, perShard)
|
||||
if err != io.EOF && err != nil {
|
||||
return err
|
||||
}
|
||||
if n != perShard {
|
||||
return ErrShortData
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2016 Matthias Neugebauer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Package ulid is a simple adapter over `github.com/oklog/ulid` for quickly
|
||||
// creating ULIDs.
|
||||
package ulid
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2016 Matthias Neugebauer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package ulid
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/oklog/ulid"
|
||||
)
|
||||
|
||||
var (
|
||||
entropy = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
mutex = sync.Mutex{}
|
||||
)
|
||||
|
||||
// New generates a new ULID
|
||||
func New() ulid.ULID {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
id, _ := ulid.New(ulid.Now(), entropy)
|
||||
return id
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
// Copyright 2016 The Oklog Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ulid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
An ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier
|
||||
|
||||
The components are encoded as 16 octets.
|
||||
Each component is encoded with the MSB first (network byte order).
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_time_high |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16_bit_uint_time_low | 16_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type ULID [16]byte
|
||||
|
||||
var (
|
||||
// ErrDataSize is returned when parsing or unmarshaling ULIDs with the wrong
|
||||
// data size.
|
||||
ErrDataSize = errors.New("ulid: bad data size when unmarshaling")
|
||||
|
||||
// ErrBufferSize is returned when marshalling ULIDs to a buffer of insufficient
|
||||
// size.
|
||||
ErrBufferSize = errors.New("ulid: bad buffer size when marshaling")
|
||||
|
||||
// ErrBigTime is returned when constructing an ULID with a time that is larger
|
||||
// than MaxTime.
|
||||
ErrBigTime = errors.New("ulid: time too big")
|
||||
)
|
||||
|
||||
// New returns an ULID with the given Unix milliseconds timestamp and an
|
||||
// optional entropy source. Use the Timestamp function to convert
|
||||
// a time.Time to Unix milliseconds.
|
||||
//
|
||||
// ErrBigTime is returned when passing a timestamp bigger than MaxTime.
|
||||
// Reading from the entropy source may also return an error.
|
||||
func New(ms uint64, entropy io.Reader) (id ULID, err error) {
|
||||
if err = id.SetTime(ms); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
if entropy != nil {
|
||||
_, err = entropy.Read(id[6:])
|
||||
}
|
||||
|
||||
return id, err
|
||||
}
|
||||
|
||||
// MustNew is a convenience function equivalent to New that panics on failure
|
||||
// instead of returning an error.
|
||||
func MustNew(ms uint64, entropy io.Reader) ULID {
|
||||
id, err := New(ms, entropy)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Parse parses a string encoded ULID, returning an error in case of failure.
|
||||
func Parse(ulid string) (id ULID, err error) {
|
||||
return id, id.UnmarshalText([]byte(ulid))
|
||||
}
|
||||
|
||||
// MustParse is a convenience function equivalent to Parse that panics on failure
|
||||
// instead of returning an error.
|
||||
func MustParse(ulid string) ULID {
|
||||
id, err := Parse(ulid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// String returns a lexicographically sortable string encoded ULID
|
||||
// (26 characters, non-standard base 32) e.g. 01AN4Z07BY79KA1307SR9X4MV3
|
||||
// Format: tttttttttteeeeeeeeeeeeeeee where t is time and e is entropy
|
||||
func (id ULID) String() string {
|
||||
ulid := make([]byte, EncodedSize)
|
||||
_ = id.MarshalTextTo(ulid)
|
||||
return string(ulid)
|
||||
}
|
||||
|
||||
// MarshalBinary implements the encoding.BinaryMarshaler interface by
|
||||
// returning the ULID as a byte slice.
|
||||
func (id ULID) MarshalBinary() ([]byte, error) {
|
||||
ulid := make([]byte, len(id))
|
||||
return ulid, id.MarshalBinaryTo(ulid)
|
||||
}
|
||||
|
||||
// MarshalBinaryTo writes the binary encoding of the ULID to the given buffer.
|
||||
// ErrBufferSize is returned when the len(dst) != 16.
|
||||
func (id ULID) MarshalBinaryTo(dst []byte) error {
|
||||
if len(dst) != len(id) {
|
||||
return ErrBufferSize
|
||||
}
|
||||
|
||||
copy(dst, id[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface by
|
||||
// copying the passed data and converting it to an ULID. ErrDataSize is
|
||||
// returned if the data length is different from ULID length.
|
||||
func (id *ULID) UnmarshalBinary(data []byte) error {
|
||||
if len(data) != len(*id) {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
copy((*id)[:], data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encoding is the base 32 encoding alphabet used in ULID strings.
|
||||
const Encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface by
|
||||
// returning the string encoded ULID.
|
||||
func (id ULID) MarshalText() ([]byte, error) {
|
||||
ulid := make([]byte, EncodedSize)
|
||||
return ulid, id.MarshalTextTo(ulid)
|
||||
}
|
||||
|
||||
// MarshalTextTo writes the ULID as a string to the given buffer.
|
||||
// ErrBufferSize is returned when the len(dst) != 26.
|
||||
func (id ULID) MarshalTextTo(dst []byte) error {
|
||||
// Optimized unrolled loop ahead.
|
||||
// From https://github.com/RobThree/NUlid
|
||||
|
||||
if len(dst) != EncodedSize {
|
||||
return ErrBufferSize
|
||||
}
|
||||
|
||||
// 10 byte timestamp
|
||||
dst[0] = Encoding[(id[0]&224)>>5]
|
||||
dst[1] = Encoding[id[0]&31]
|
||||
dst[2] = Encoding[(id[1]&248)>>3]
|
||||
dst[3] = Encoding[((id[1]&7)<<2)|((id[2]&192)>>6)]
|
||||
dst[4] = Encoding[(id[2]&62)>>1]
|
||||
dst[5] = Encoding[((id[2]&1)<<4)|((id[3]&240)>>4)]
|
||||
dst[6] = Encoding[((id[3]&15)<<1)|((id[4]&128)>>7)]
|
||||
dst[7] = Encoding[(id[4]&124)>>2]
|
||||
dst[8] = Encoding[((id[4]&3)<<3)|((id[5]&224)>>5)]
|
||||
dst[9] = Encoding[id[5]&31]
|
||||
|
||||
// 16 bytes of entropy
|
||||
dst[10] = Encoding[(id[6]&248)>>3]
|
||||
dst[11] = Encoding[((id[6]&7)<<2)|((id[7]&192)>>6)]
|
||||
dst[12] = Encoding[(id[7]&62)>>1]
|
||||
dst[13] = Encoding[((id[7]&1)<<4)|((id[8]&240)>>4)]
|
||||
dst[14] = Encoding[((id[8]&15)<<1)|((id[9]&128)>>7)]
|
||||
dst[15] = Encoding[(id[9]&124)>>2]
|
||||
dst[16] = Encoding[((id[9]&3)<<3)|((id[10]&224)>>5)]
|
||||
dst[17] = Encoding[id[10]&31]
|
||||
dst[18] = Encoding[(id[11]&248)>>3]
|
||||
dst[19] = Encoding[((id[11]&7)<<2)|((id[12]&192)>>6)]
|
||||
dst[20] = Encoding[(id[12]&62)>>1]
|
||||
dst[21] = Encoding[((id[12]&1)<<4)|((id[13]&240)>>4)]
|
||||
dst[22] = Encoding[((id[13]&15)<<1)|((id[14]&128)>>7)]
|
||||
dst[23] = Encoding[(id[14]&124)>>2]
|
||||
dst[24] = Encoding[((id[14]&3)<<3)|((id[15]&224)>>5)]
|
||||
dst[25] = Encoding[id[15]&31]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Byte to index table for O(1) lookups when unmarshaling.
|
||||
// We use 0xFF as sentinel value for invalid indexes.
|
||||
var dec = [...]byte{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF,
|
||||
0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E,
|
||||
0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C,
|
||||
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14,
|
||||
0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
|
||||
0x1D, 0x1E, 0x1F,
|
||||
}
|
||||
|
||||
// EncodedSize is the length of a text encoded ULID.
|
||||
const EncodedSize = 26
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface by
|
||||
// parsing the data as string encoded ULID.
|
||||
func (id *ULID) UnmarshalText(v []byte) error {
|
||||
// Optimized unrolled loop ahead.
|
||||
// From https://github.com/RobThree/NUlid
|
||||
if len(v) != EncodedSize {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
// 6 bytes timestamp (48 bits)
|
||||
(*id)[0] = ((dec[v[0]] << 5) | dec[v[1]])
|
||||
(*id)[1] = ((dec[v[2]] << 3) | (dec[v[3]] >> 2))
|
||||
(*id)[2] = ((dec[v[3]] << 6) | (dec[v[4]] << 1) | (dec[v[5]] >> 4))
|
||||
(*id)[3] = ((dec[v[5]] << 4) | (dec[v[6]] >> 1))
|
||||
(*id)[4] = ((dec[v[6]] << 7) | (dec[v[7]] << 2) | (dec[v[8]] >> 3))
|
||||
(*id)[5] = ((dec[v[8]] << 5) | dec[v[9]])
|
||||
|
||||
// 10 bytes of entropy (80 bits)
|
||||
(*id)[6] = ((dec[v[10]] << 3) | (dec[v[11]] >> 2))
|
||||
(*id)[7] = ((dec[v[11]] << 6) | (dec[v[12]] << 1) | (dec[v[13]] >> 4))
|
||||
(*id)[8] = ((dec[v[13]] << 4) | (dec[v[14]] >> 1))
|
||||
(*id)[9] = ((dec[v[14]] << 7) | (dec[v[15]] << 2) | (dec[v[16]] >> 3))
|
||||
(*id)[10] = ((dec[v[16]] << 5) | dec[v[17]])
|
||||
(*id)[11] = ((dec[v[18]] << 3) | dec[v[19]]>>2)
|
||||
(*id)[12] = ((dec[v[19]] << 6) | (dec[v[20]] << 1) | (dec[v[21]] >> 4))
|
||||
(*id)[13] = ((dec[v[21]] << 4) | (dec[v[22]] >> 1))
|
||||
(*id)[14] = ((dec[v[22]] << 7) | (dec[v[23]] << 2) | (dec[v[24]] >> 3))
|
||||
(*id)[15] = ((dec[v[24]] << 5) | dec[v[25]])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns the Unix time in milliseconds encoded in the ULID.
|
||||
func (id ULID) Time() uint64 {
|
||||
return uint64(id[5]) | uint64(id[4])<<8 |
|
||||
uint64(id[3])<<16 | uint64(id[2])<<24 |
|
||||
uint64(id[1])<<32 | uint64(id[0])<<40
|
||||
}
|
||||
|
||||
// maxTime is the maximum Unix time in milliseconds that can be
|
||||
// represented in an ULID.
|
||||
var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time()
|
||||
|
||||
// MaxTime returns the maximum Unix time in milliseconds that
|
||||
// can be encoded in an ULID.
|
||||
func MaxTime() uint64 { return maxTime }
|
||||
|
||||
// Now is a convenience function that returns the current
|
||||
// UTC time in Unix miliseconds. Equivalent to:
|
||||
// Timestamp(time.Now().UTC())
|
||||
func Now() uint64 { return Timestamp(time.Now().UTC()) }
|
||||
|
||||
// Timestamp converts a time.Time to Unix milliseconds.
|
||||
//
|
||||
// BUG(tsenart): Because of the way time.Time is internally
|
||||
// represented, times from the year 2262 on have undefined
|
||||
// results.
|
||||
func Timestamp(t time.Time) uint64 {
|
||||
return uint64(t.Truncate(time.Millisecond).UnixNano() / int64(time.Millisecond))
|
||||
}
|
||||
|
||||
// SetTime sets the time component of the ULID to the given Unix time
|
||||
// in milliseconds.
|
||||
func (id *ULID) SetTime(ms uint64) error {
|
||||
if ms > maxTime {
|
||||
return ErrBigTime
|
||||
}
|
||||
|
||||
(*id)[0] = byte(ms >> 40)
|
||||
(*id)[1] = byte(ms >> 32)
|
||||
(*id)[2] = byte(ms >> 24)
|
||||
(*id)[3] = byte(ms >> 16)
|
||||
(*id)[4] = byte(ms >> 8)
|
||||
(*id)[5] = byte(ms)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Entropy returns the entropy from the ULID.
|
||||
func (id ULID) Entropy() []byte {
|
||||
e := make([]byte, 10)
|
||||
copy(e, id[6:])
|
||||
return e
|
||||
}
|
||||
|
||||
// SetEntropy sets the ULID entropy to the passed byte slice.
|
||||
// ErrDataSize is returned if len(e) != 10.
|
||||
func (id *ULID) SetEntropy(e []byte) error {
|
||||
if len(e) != 10 {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
copy((*id)[6:], e)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// and the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required the errors.WithStack and errors.WithMessage
|
||||
// functions destructure errors.Wrap into its component operations of annotating
|
||||
// an error with a stack trace and an a message, respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error which does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// causer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface.
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// Where errors.StackTrace is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// stackTracer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is call, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s path of source file relative to the compile time GOPATH
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
func trimGOPATH(name, file string) string {
|
||||
// Here we want to get the source file path relative to the compile time
|
||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||
// the import path, which does not include the GOPATH. Thus we can trim
|
||||
// segments from the beginning of the file path until the number of path
|
||||
// separators remaining is one more than the number of path separators in
|
||||
// the function name. For example, given:
|
||||
//
|
||||
// GOPATH /home/user
|
||||
// file /home/user/src/pkg/sub/file.go
|
||||
// fn.Name() pkg/sub.Type.Method
|
||||
//
|
||||
// We want to produce:
|
||||
//
|
||||
// pkg/sub/file.go
|
||||
//
|
||||
// From this we can easily see that fn.Name() has one less path separator
|
||||
// than our desired output. We count separators from the end of the file
|
||||
// path until it finds two more than in the function name and then move
|
||||
// one character forward to preserve the initial path segment without a
|
||||
// leading separator.
|
||||
const sep = "/"
|
||||
goal := strings.Count(name, sep) + 2
|
||||
i := len(file)
|
||||
for n := 0; n < goal; n++ {
|
||||
i = strings.LastIndex(file[:i], sep)
|
||||
if i == -1 {
|
||||
// not enough separators found, set i so that the slice expression
|
||||
// below leaves file unmodified
|
||||
i = -len(sep)
|
||||
break
|
||||
}
|
||||
}
|
||||
// get back to 0 or trim the leading separator
|
||||
file = file[i+len(sep):]
|
||||
return file
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
package kcp
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/sha1"
|
||||
|
||||
"golang.org/x/crypto/blowfish"
|
||||
"golang.org/x/crypto/cast5"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/salsa20"
|
||||
"golang.org/x/crypto/tea"
|
||||
"golang.org/x/crypto/twofish"
|
||||
"golang.org/x/crypto/xtea"
|
||||
)
|
||||
|
||||
var (
|
||||
initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
|
||||
saltxor = `sH3CIVoF#rWLtJo6`
|
||||
)
|
||||
|
||||
// BlockCrypt defines encryption/decryption methods for a given byte slice.
|
||||
// Notes on implementing: the data to be encrypted contains a builtin
|
||||
// nonce at the first 16 bytes
|
||||
type BlockCrypt interface {
|
||||
// Encrypt encrypts the whole block in src into dst.
|
||||
// Dst and src may point at the same memory.
|
||||
Encrypt(dst, src []byte)
|
||||
|
||||
// Decrypt decrypts the whole block in src into dst.
|
||||
// Dst and src may point at the same memory.
|
||||
Decrypt(dst, src []byte)
|
||||
}
|
||||
|
||||
type salsa20BlockCrypt struct {
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
|
||||
func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(salsa20BlockCrypt)
|
||||
copy(c.key[:], key)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
|
||||
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
|
||||
copy(dst[:8], src[:8])
|
||||
}
|
||||
func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
|
||||
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
|
||||
copy(dst[:8], src[:8])
|
||||
}
|
||||
|
||||
type twofishBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
|
||||
func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(twofishBlockCrypt)
|
||||
block, err := twofish.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, twofish.BlockSize)
|
||||
c.decbuf = make([]byte, 2*twofish.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *twofishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *twofishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type tripleDESBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
|
||||
func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(tripleDESBlockCrypt)
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, des.BlockSize)
|
||||
c.decbuf = make([]byte, 2*des.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *tripleDESBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *tripleDESBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type cast5BlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
|
||||
func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(cast5BlockCrypt)
|
||||
block, err := cast5.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, cast5.BlockSize)
|
||||
c.decbuf = make([]byte, 2*cast5.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *cast5BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *cast5BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type blowfishBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
|
||||
func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(blowfishBlockCrypt)
|
||||
block, err := blowfish.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, blowfish.BlockSize)
|
||||
c.decbuf = make([]byte, 2*blowfish.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *blowfishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *blowfishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type aesBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||
func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(aesBlockCrypt)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, aes.BlockSize)
|
||||
c.decbuf = make([]byte, 2*aes.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *aesBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *aesBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type teaBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
|
||||
func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(teaBlockCrypt)
|
||||
block, err := tea.NewCipherWithRounds(key, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, tea.BlockSize)
|
||||
c.decbuf = make([]byte, 2*tea.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *teaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *teaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type xteaBlockCrypt struct {
|
||||
encbuf []byte
|
||||
decbuf []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
|
||||
func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(xteaBlockCrypt)
|
||||
block, err := xtea.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.block = block
|
||||
c.encbuf = make([]byte, xtea.BlockSize)
|
||||
c.decbuf = make([]byte, 2*xtea.BlockSize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *xteaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
|
||||
func (c *xteaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }
|
||||
|
||||
type simpleXORBlockCrypt struct {
|
||||
xortbl []byte
|
||||
}
|
||||
|
||||
// NewSimpleXORBlockCrypt simple xor with key expanding
|
||||
func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
c := new(simpleXORBlockCrypt)
|
||||
c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) { xorBytes(dst, src, c.xortbl) }
|
||||
func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xorBytes(dst, src, c.xortbl) }
|
||||
|
||||
type noneBlockCrypt struct{}
|
||||
|
||||
// NewNoneBlockCrypt does nothing but copying
|
||||
func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
|
||||
return new(noneBlockCrypt), nil
|
||||
}
|
||||
|
||||
func (c *noneBlockCrypt) Encrypt(dst, src []byte) { copy(dst, src) }
|
||||
func (c *noneBlockCrypt) Decrypt(dst, src []byte) { copy(dst, src) }
|
||||
|
||||
// packet encryption with local CFB mode
|
||||
func encrypt(block cipher.Block, dst, src, buf []byte) {
|
||||
blocksize := block.BlockSize()
|
||||
tbl := buf[:blocksize]
|
||||
block.Encrypt(tbl, initialVector)
|
||||
n := len(src) / blocksize
|
||||
base := 0
|
||||
for i := 0; i < n; i++ {
|
||||
xorWords(dst[base:], src[base:], tbl)
|
||||
block.Encrypt(tbl, dst[base:])
|
||||
base += blocksize
|
||||
}
|
||||
xorBytes(dst[base:], src[base:], tbl)
|
||||
}
|
||||
|
||||
func decrypt(block cipher.Block, dst, src, buf []byte) {
|
||||
blocksize := block.BlockSize()
|
||||
tbl := buf[:blocksize]
|
||||
next := buf[blocksize:]
|
||||
block.Encrypt(tbl, initialVector)
|
||||
n := len(src) / blocksize
|
||||
base := 0
|
||||
for i := 0; i < n; i++ {
|
||||
block.Encrypt(next, src[base:])
|
||||
xorWords(dst[base:], src[base:], tbl)
|
||||
tbl, next = next, tbl
|
||||
base += blocksize
|
||||
}
|
||||
xorBytes(dst[base:], src[base:], tbl)
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
package kcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/klauspost/reedsolomon"
|
||||
)
|
||||
|
||||
const (
|
||||
fecHeaderSize = 6
|
||||
fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size
|
||||
typeData = 0xf1
|
||||
typeFEC = 0xf2
|
||||
)
|
||||
|
||||
type (
|
||||
// fecPacket is a decoded FEC packet
|
||||
fecPacket struct {
|
||||
seqid uint32
|
||||
flag uint16
|
||||
data []byte
|
||||
}
|
||||
|
||||
// FECDecoder for decoding incoming packets
|
||||
FECDecoder struct {
|
||||
rxlimit int // queue size limit
|
||||
dataShards int
|
||||
parityShards int
|
||||
shardSize int
|
||||
rx []fecPacket // ordered receive queue
|
||||
|
||||
// caches
|
||||
decodeCache [][]byte
|
||||
flagCache []bool
|
||||
|
||||
// RS decoder
|
||||
codec reedsolomon.Encoder
|
||||
}
|
||||
)
|
||||
|
||||
func newFECDecoder(rxlimit, dataShards, parityShards int) *FECDecoder {
|
||||
if dataShards <= 0 || parityShards <= 0 {
|
||||
return nil
|
||||
}
|
||||
if rxlimit < dataShards+parityShards {
|
||||
return nil
|
||||
}
|
||||
|
||||
fec := new(FECDecoder)
|
||||
fec.rxlimit = rxlimit
|
||||
fec.dataShards = dataShards
|
||||
fec.parityShards = parityShards
|
||||
fec.shardSize = dataShards + parityShards
|
||||
enc, err := reedsolomon.New(dataShards, parityShards, reedsolomon.WithMaxGoroutines(1))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
fec.codec = enc
|
||||
fec.decodeCache = make([][]byte, fec.shardSize)
|
||||
fec.flagCache = make([]bool, fec.shardSize)
|
||||
return fec
|
||||
}
|
||||
|
||||
// decodeBytes a fec packet
|
||||
func (dec *FECDecoder) decodeBytes(data []byte) fecPacket {
|
||||
var pkt fecPacket
|
||||
pkt.seqid = binary.LittleEndian.Uint32(data)
|
||||
pkt.flag = binary.LittleEndian.Uint16(data[4:])
|
||||
// allocate memory & copy
|
||||
buf := xmitBuf.Get().([]byte)[:len(data)-6]
|
||||
copy(buf, data[6:])
|
||||
pkt.data = buf
|
||||
return pkt
|
||||
}
|
||||
|
||||
// Decode a fec packet
|
||||
func (dec *FECDecoder) Decode(pkt fecPacket) (recovered [][]byte) {
|
||||
// insertion
|
||||
n := len(dec.rx) - 1
|
||||
insertIdx := 0
|
||||
for i := n; i >= 0; i-- {
|
||||
if pkt.seqid == dec.rx[i].seqid { // de-duplicate
|
||||
xmitBuf.Put(pkt.data)
|
||||
return nil
|
||||
} else if _itimediff(pkt.seqid, dec.rx[i].seqid) > 0 { // insertion
|
||||
insertIdx = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// insert into ordered rx queue
|
||||
if insertIdx == n+1 {
|
||||
dec.rx = append(dec.rx, pkt)
|
||||
} else {
|
||||
dec.rx = append(dec.rx, fecPacket{})
|
||||
copy(dec.rx[insertIdx+1:], dec.rx[insertIdx:]) // shift right
|
||||
dec.rx[insertIdx] = pkt
|
||||
}
|
||||
|
||||
// shard range for current packet
|
||||
shardBegin := pkt.seqid - pkt.seqid%uint32(dec.shardSize)
|
||||
shardEnd := shardBegin + uint32(dec.shardSize) - 1
|
||||
|
||||
// max search range in ordered queue for current shard
|
||||
searchBegin := insertIdx - int(pkt.seqid%uint32(dec.shardSize))
|
||||
if searchBegin < 0 {
|
||||
searchBegin = 0
|
||||
}
|
||||
searchEnd := searchBegin + dec.shardSize - 1
|
||||
if searchEnd >= len(dec.rx) {
|
||||
searchEnd = len(dec.rx) - 1
|
||||
}
|
||||
|
||||
// re-construct datashards
|
||||
if searchEnd-searchBegin+1 >= dec.dataShards {
|
||||
var numshard, numDataShard, first, maxlen int
|
||||
|
||||
// zero cache
|
||||
shards := dec.decodeCache
|
||||
shardsflag := dec.flagCache
|
||||
for k := range dec.decodeCache {
|
||||
shards[k] = nil
|
||||
shardsflag[k] = false
|
||||
}
|
||||
|
||||
// shard assembly
|
||||
for i := searchBegin; i <= searchEnd; i++ {
|
||||
seqid := dec.rx[i].seqid
|
||||
if _itimediff(seqid, shardEnd) > 0 {
|
||||
break
|
||||
} else if _itimediff(seqid, shardBegin) >= 0 {
|
||||
shards[seqid%uint32(dec.shardSize)] = dec.rx[i].data
|
||||
shardsflag[seqid%uint32(dec.shardSize)] = true
|
||||
numshard++
|
||||
if dec.rx[i].flag == typeData {
|
||||
numDataShard++
|
||||
}
|
||||
if numshard == 1 {
|
||||
first = i
|
||||
}
|
||||
if len(dec.rx[i].data) > maxlen {
|
||||
maxlen = len(dec.rx[i].data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if numDataShard == dec.dataShards {
|
||||
// case 1: no lost data shards
|
||||
dec.rx = dec.freeRange(first, numshard, dec.rx)
|
||||
} else if numshard >= dec.dataShards {
|
||||
// case 2: data shard lost, but recoverable from parity shard
|
||||
for k := range shards {
|
||||
if shards[k] != nil {
|
||||
dlen := len(shards[k])
|
||||
shards[k] = shards[k][:maxlen]
|
||||
xorBytes(shards[k][dlen:], shards[k][dlen:], shards[k][dlen:])
|
||||
}
|
||||
}
|
||||
if err := dec.codec.Reconstruct(shards); err == nil {
|
||||
for k := range shards[:dec.dataShards] {
|
||||
if !shardsflag[k] {
|
||||
recovered = append(recovered, shards[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
dec.rx = dec.freeRange(first, numshard, dec.rx)
|
||||
}
|
||||
}
|
||||
|
||||
// keep rxlimit
|
||||
if len(dec.rx) > dec.rxlimit {
|
||||
if dec.rx[0].flag == typeData { // record unrecoverable data
|
||||
atomic.AddUint64(&DefaultSnmp.FECShortShards, 1)
|
||||
}
|
||||
dec.rx = dec.freeRange(0, 1, dec.rx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// free a range of fecPacket, and zero for GC recycling
|
||||
func (dec *FECDecoder) freeRange(first, n int, q []fecPacket) []fecPacket {
|
||||
for i := first; i < first+n; i++ { // free
|
||||
xmitBuf.Put(q[i].data)
|
||||
}
|
||||
copy(q[first:], q[first+n:])
|
||||
for i := 0; i < n; i++ { // dereference data
|
||||
q[len(q)-1-i].data = nil
|
||||
}
|
||||
return q[:len(q)-n]
|
||||
}
|
||||
|
||||
type (
|
||||
// FECEncoder for encoding outgoing packets
|
||||
FECEncoder struct {
|
||||
dataShards int
|
||||
parityShards int
|
||||
shardSize int
|
||||
paws uint32 // Protect Against Wrapped Sequence numbers
|
||||
next uint32 // next seqid
|
||||
|
||||
shardCount int // count the number of datashards collected
|
||||
maxSize int // record maximum data length in datashard
|
||||
|
||||
headerOffset int // FEC header offset
|
||||
payloadOffset int // FEC payload offset
|
||||
|
||||
// caches
|
||||
shardCache [][]byte
|
||||
encodeCache [][]byte
|
||||
|
||||
// RS encoder
|
||||
codec reedsolomon.Encoder
|
||||
}
|
||||
)
|
||||
|
||||
func newFECEncoder(dataShards, parityShards, offset int) *FECEncoder {
|
||||
if dataShards <= 0 || parityShards <= 0 {
|
||||
return nil
|
||||
}
|
||||
fec := new(FECEncoder)
|
||||
fec.dataShards = dataShards
|
||||
fec.parityShards = parityShards
|
||||
fec.shardSize = dataShards + parityShards
|
||||
fec.paws = (0xffffffff/uint32(fec.shardSize) - 1) * uint32(fec.shardSize)
|
||||
fec.headerOffset = offset
|
||||
fec.payloadOffset = fec.headerOffset + fecHeaderSize
|
||||
|
||||
enc, err := reedsolomon.New(dataShards, parityShards, reedsolomon.WithMaxGoroutines(1))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
fec.codec = enc
|
||||
|
||||
// caches
|
||||
fec.encodeCache = make([][]byte, fec.shardSize)
|
||||
fec.shardCache = make([][]byte, fec.shardSize)
|
||||
for k := range fec.shardCache {
|
||||
fec.shardCache[k] = make([]byte, mtuLimit)
|
||||
}
|
||||
return fec
|
||||
}
|
||||
|
||||
// Encode the packet, output parity shards if we have enough datashards
|
||||
// the content of returned parityshards will change in next Encode
|
||||
func (enc *FECEncoder) Encode(b []byte) (ps [][]byte) {
|
||||
enc.markData(b[enc.headerOffset:])
|
||||
binary.LittleEndian.PutUint16(b[enc.payloadOffset:], uint16(len(b[enc.payloadOffset:])))
|
||||
|
||||
// copy data to fec datashards
|
||||
sz := len(b)
|
||||
enc.shardCache[enc.shardCount] = enc.shardCache[enc.shardCount][:sz]
|
||||
copy(enc.shardCache[enc.shardCount], b)
|
||||
enc.shardCount++
|
||||
|
||||
// record max datashard length
|
||||
if sz > enc.maxSize {
|
||||
enc.maxSize = sz
|
||||
}
|
||||
|
||||
// calculate Reed-Solomon Erasure Code
|
||||
if enc.shardCount == enc.dataShards {
|
||||
// bzero each datashard's tail
|
||||
for i := 0; i < enc.dataShards; i++ {
|
||||
shard := enc.shardCache[i]
|
||||
slen := len(shard)
|
||||
xorBytes(shard[slen:enc.maxSize], shard[slen:enc.maxSize], shard[slen:enc.maxSize])
|
||||
}
|
||||
|
||||
// construct equal-sized slice with stripped header
|
||||
cache := enc.encodeCache
|
||||
for k := range cache {
|
||||
cache[k] = enc.shardCache[k][enc.payloadOffset:enc.maxSize]
|
||||
}
|
||||
|
||||
// rs encode
|
||||
if err := enc.codec.Encode(cache); err == nil {
|
||||
ps = enc.shardCache[enc.dataShards:]
|
||||
for k := range ps {
|
||||
enc.markFEC(ps[k][enc.headerOffset:])
|
||||
ps[k] = ps[k][:enc.maxSize]
|
||||
}
|
||||
}
|
||||
|
||||
// reset counters to zero
|
||||
enc.shardCount = 0
|
||||
enc.maxSize = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (enc *FECEncoder) markData(data []byte) {
|
||||
binary.LittleEndian.PutUint32(data, enc.next)
|
||||
binary.LittleEndian.PutUint16(data[4:], typeData)
|
||||
enc.next++
|
||||
}
|
||||
|
||||
func (enc *FECEncoder) markFEC(data []byte) {
|
||||
binary.LittleEndian.PutUint32(data, enc.next)
|
||||
binary.LittleEndian.PutUint16(data[4:], typeFEC)
|
||||
enc.next = (enc.next + 1) % enc.paws
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,922 @@
|
|||
package kcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
type errTimeout struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (errTimeout) Timeout() bool { return true }
|
||||
func (errTimeout) Temporary() bool { return true }
|
||||
func (errTimeout) Error() string { return "i/o timeout" }
|
||||
|
||||
const (
|
||||
// 16-bytes magic number for each packet
|
||||
nonceSize = 16
|
||||
|
||||
// 4-bytes packet checksum
|
||||
crcSize = 4
|
||||
|
||||
// overall crypto header size
|
||||
cryptHeaderSize = nonceSize + crcSize
|
||||
|
||||
// maximum packet size
|
||||
mtuLimit = 1500
|
||||
|
||||
// FEC keeps rxFECMulti* (dataShard+parityShard) ordered packets in memory
|
||||
rxFECMulti = 3
|
||||
|
||||
// accept backlog
|
||||
acceptBacklog = 128
|
||||
)
|
||||
|
||||
const (
|
||||
errBrokenPipe = "broken pipe"
|
||||
errInvalidOperation = "invalid operation"
|
||||
)
|
||||
|
||||
var (
|
||||
// global packet buffer
|
||||
// shared among sending/receiving/FEC
|
||||
xmitBuf sync.Pool
|
||||
|
||||
// monotonic session id
|
||||
sid uint32
|
||||
)
|
||||
|
||||
func init() {
|
||||
xmitBuf.New = func() interface{} {
|
||||
return make([]byte, mtuLimit)
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// UDPSession defines a KCP session implemented by UDP
|
||||
UDPSession struct {
|
||||
sid uint32 // session id(monotonic)
|
||||
conn net.PacketConn // the underlying packet connection
|
||||
kcp *KCP // KCP ARQ protocol
|
||||
l *Listener // point to the Listener if it's accepted by Listener
|
||||
block BlockCrypt // block encryption
|
||||
|
||||
// kcp receiving is based on packets
|
||||
// recvbuf turns packets into stream
|
||||
recvbuf []byte
|
||||
buffer bytes.Buffer
|
||||
// extended output buffer(with header)
|
||||
ext []byte
|
||||
|
||||
// FEC
|
||||
fecDecoder *FECDecoder
|
||||
fecEncoder *FECEncoder
|
||||
|
||||
// settings
|
||||
remote net.Addr // remote peer address
|
||||
rd time.Time // read deadline
|
||||
wd time.Time // write deadline
|
||||
headerSize int // the overall header size added before KCP frame
|
||||
updateInterval time.Duration // interval in seconds to call kcp.flush()
|
||||
ackNoDelay bool // send ack immediately for each incoming packet
|
||||
writeDelay bool // delay kcp.flush() for Write() for bulk transfer
|
||||
|
||||
// notifications
|
||||
die chan struct{} // notify session has Closed
|
||||
chReadEvent chan struct{} // notify Read() can be called without blocking
|
||||
chWriteEvent chan struct{} // notify Write() can be called without blocking
|
||||
|
||||
isClosed bool // flag the session has Closed
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
setReadBuffer interface {
|
||||
SetReadBuffer(bytes int) error
|
||||
}
|
||||
|
||||
setWriteBuffer interface {
|
||||
SetWriteBuffer(bytes int) error
|
||||
}
|
||||
)
|
||||
|
||||
// newUDPSession create a new udp session for client or server
|
||||
func newUDPSession(conv uint32, dataShards, parityShards int, l *Listener, conn net.PacketConn, remote net.Addr, block BlockCrypt) *UDPSession {
|
||||
sess := new(UDPSession)
|
||||
sess.sid = atomic.AddUint32(&sid, 1)
|
||||
sess.die = make(chan struct{})
|
||||
sess.chReadEvent = make(chan struct{}, 1)
|
||||
sess.chWriteEvent = make(chan struct{}, 1)
|
||||
sess.remote = remote
|
||||
sess.conn = conn
|
||||
sess.l = l
|
||||
sess.block = block
|
||||
sess.recvbuf = make([]byte, mtuLimit)
|
||||
sess.ext = make([]byte, mtuLimit)
|
||||
|
||||
// FEC initialization
|
||||
sess.fecDecoder = newFECDecoder(rxFECMulti*(dataShards+parityShards), dataShards, parityShards)
|
||||
if sess.block != nil {
|
||||
sess.fecEncoder = newFECEncoder(dataShards, parityShards, cryptHeaderSize)
|
||||
} else {
|
||||
sess.fecEncoder = newFECEncoder(dataShards, parityShards, 0)
|
||||
}
|
||||
|
||||
// calculate header size
|
||||
if sess.block != nil {
|
||||
sess.headerSize += cryptHeaderSize
|
||||
}
|
||||
if sess.fecEncoder != nil {
|
||||
sess.headerSize += fecHeaderSizePlus2
|
||||
}
|
||||
|
||||
sess.kcp = NewKCP(conv, func(buf []byte, size int) {
|
||||
if size >= IKCP_OVERHEAD {
|
||||
sess.output(buf[:size])
|
||||
}
|
||||
})
|
||||
sess.kcp.SetMtu(IKCP_MTU_DEF - sess.headerSize)
|
||||
|
||||
// add current session to the global updater,
|
||||
// which periodically calls sess.update()
|
||||
updater.addSession(sess)
|
||||
|
||||
if sess.l == nil { // it's a client connection
|
||||
go sess.readLoop()
|
||||
atomic.AddUint64(&DefaultSnmp.ActiveOpens, 1)
|
||||
} else {
|
||||
atomic.AddUint64(&DefaultSnmp.PassiveOpens, 1)
|
||||
}
|
||||
currestab := atomic.AddUint64(&DefaultSnmp.CurrEstab, 1)
|
||||
maxconn := atomic.LoadUint64(&DefaultSnmp.MaxConn)
|
||||
if currestab > maxconn {
|
||||
atomic.CompareAndSwapUint64(&DefaultSnmp.MaxConn, maxconn, currestab)
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
// Read implements net.Conn
|
||||
func (s *UDPSession) Read(b []byte) (n int, err error) {
|
||||
for {
|
||||
s.mu.Lock()
|
||||
if s.buffer.Len() > 0 { // copy from buffer into b
|
||||
n, _ = s.buffer.Read(b)
|
||||
s.mu.Unlock()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
if s.isClosed {
|
||||
s.mu.Unlock()
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
}
|
||||
|
||||
if !s.rd.IsZero() {
|
||||
if time.Now().After(s.rd) { // read timeout
|
||||
s.mu.Unlock()
|
||||
return 0, errTimeout{}
|
||||
}
|
||||
}
|
||||
|
||||
if size := s.kcp.PeekSize(); size > 0 { // peek data size from kcp
|
||||
atomic.AddUint64(&DefaultSnmp.BytesReceived, uint64(size))
|
||||
if len(b) >= size { // direct write to b
|
||||
s.kcp.Recv(b)
|
||||
s.mu.Unlock()
|
||||
return size, nil
|
||||
}
|
||||
|
||||
if len(s.recvbuf) < size { // resize kcp receive buffer
|
||||
s.recvbuf = make([]byte, size)
|
||||
}
|
||||
s.kcp.Recv(s.recvbuf)
|
||||
n = copy(b, s.recvbuf[:size]) // direct copy to b
|
||||
s.buffer.Write(s.recvbuf[n:size]) // save rest bytes to bytes.Buffer
|
||||
s.mu.Unlock()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var timeout *time.Timer
|
||||
var c <-chan time.Time
|
||||
if !s.rd.IsZero() {
|
||||
delay := s.rd.Sub(time.Now())
|
||||
timeout = time.NewTimer(delay)
|
||||
c = timeout.C
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
// wait for read event or timeout
|
||||
select {
|
||||
case <-s.chReadEvent:
|
||||
case <-c:
|
||||
case <-s.die:
|
||||
}
|
||||
|
||||
if timeout != nil {
|
||||
timeout.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements net.Conn
|
||||
func (s *UDPSession) Write(b []byte) (n int, err error) {
|
||||
for {
|
||||
s.mu.Lock()
|
||||
if s.isClosed {
|
||||
s.mu.Unlock()
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
}
|
||||
|
||||
if !s.wd.IsZero() {
|
||||
if time.Now().After(s.wd) { // write timeout
|
||||
s.mu.Unlock()
|
||||
return 0, errTimeout{}
|
||||
}
|
||||
}
|
||||
|
||||
// api flow control
|
||||
if s.kcp.WaitSnd() < int(s.kcp.Cwnd()) {
|
||||
n = len(b)
|
||||
for {
|
||||
if len(b) <= int(s.kcp.mss) {
|
||||
s.kcp.Send(b)
|
||||
break
|
||||
} else {
|
||||
s.kcp.Send(b[:s.kcp.mss])
|
||||
b = b[s.kcp.mss:]
|
||||
}
|
||||
}
|
||||
|
||||
if !s.writeDelay {
|
||||
s.kcp.flush(false)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
atomic.AddUint64(&DefaultSnmp.BytesSent, uint64(n))
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var timeout *time.Timer
|
||||
var c <-chan time.Time
|
||||
if !s.wd.IsZero() {
|
||||
delay := s.wd.Sub(time.Now())
|
||||
timeout = time.NewTimer(delay)
|
||||
c = timeout.C
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
// wait for write event or timeout
|
||||
select {
|
||||
case <-s.chWriteEvent:
|
||||
case <-c:
|
||||
case <-s.die:
|
||||
}
|
||||
|
||||
if timeout != nil {
|
||||
timeout.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (s *UDPSession) Close() error {
|
||||
updater.removeSession(s)
|
||||
if s.l != nil { // notify listener
|
||||
s.l.closeSession(s.remote)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.isClosed {
|
||||
return errors.New(errBrokenPipe)
|
||||
}
|
||||
close(s.die)
|
||||
s.isClosed = true
|
||||
atomic.AddUint64(&DefaultSnmp.CurrEstab, ^uint64(0))
|
||||
if s.l == nil { // client socket close
|
||||
return s.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it.
|
||||
func (s *UDPSession) LocalAddr() net.Addr { return s.conn.LocalAddr() }
|
||||
|
||||
// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
|
||||
func (s *UDPSession) RemoteAddr() net.Addr { return s.remote }
|
||||
|
||||
// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
|
||||
func (s *UDPSession) SetDeadline(t time.Time) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.rd = t
|
||||
s.wd = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the Conn SetReadDeadline method.
|
||||
func (s *UDPSession) SetReadDeadline(t time.Time) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.rd = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the Conn SetWriteDeadline method.
|
||||
func (s *UDPSession) SetWriteDeadline(t time.Time) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.wd = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDelay delays write for bulk transfer until the next update interval
|
||||
func (s *UDPSession) SetWriteDelay(delay bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.writeDelay = delay
|
||||
}
|
||||
|
||||
// SetWindowSize set maximum window size
|
||||
func (s *UDPSession) SetWindowSize(sndwnd, rcvwnd int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.kcp.WndSize(sndwnd, rcvwnd)
|
||||
}
|
||||
|
||||
// SetMtu sets the maximum transmission unit(not including UDP header)
|
||||
func (s *UDPSession) SetMtu(mtu int) bool {
|
||||
if mtu > mtuLimit {
|
||||
return false
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.kcp.SetMtu(mtu - s.headerSize)
|
||||
return true
|
||||
}
|
||||
|
||||
// SetStreamMode toggles the stream mode on/off
|
||||
func (s *UDPSession) SetStreamMode(enable bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if enable {
|
||||
s.kcp.stream = 1
|
||||
} else {
|
||||
s.kcp.stream = 0
|
||||
}
|
||||
}
|
||||
|
||||
// SetACKNoDelay changes ack flush option, set true to flush ack immediately,
|
||||
func (s *UDPSession) SetACKNoDelay(nodelay bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.ackNoDelay = nodelay
|
||||
}
|
||||
|
||||
// SetNoDelay calls nodelay() of kcp
|
||||
func (s *UDPSession) SetNoDelay(nodelay, interval, resend, nc int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.kcp.NoDelay(nodelay, interval, resend, nc)
|
||||
s.updateInterval = time.Duration(interval) * time.Millisecond
|
||||
}
|
||||
|
||||
// SetDSCP sets the 6bit DSCP field of IP header, no effect if it's accepted from Listener
|
||||
func (s *UDPSession) SetDSCP(dscp int) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.l == nil {
|
||||
if nc, ok := s.conn.(*ConnectedUDPConn); ok {
|
||||
return ipv4.NewConn(nc.Conn).SetTOS(dscp << 2)
|
||||
} else if nc, ok := s.conn.(net.Conn); ok {
|
||||
return ipv4.NewConn(nc).SetTOS(dscp << 2)
|
||||
}
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the socket read buffer, no effect if it's accepted from Listener
|
||||
func (s *UDPSession) SetReadBuffer(bytes int) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.l == nil {
|
||||
if nc, ok := s.conn.(setReadBuffer); ok {
|
||||
return nc.SetReadBuffer(bytes)
|
||||
}
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// SetWriteBuffer sets the socket write buffer, no effect if it's accepted from Listener
|
||||
func (s *UDPSession) SetWriteBuffer(bytes int) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.l == nil {
|
||||
if nc, ok := s.conn.(setWriteBuffer); ok {
|
||||
return nc.SetWriteBuffer(bytes)
|
||||
}
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// output pipeline entry
|
||||
// steps for output data processing:
|
||||
// 0. Header extends
|
||||
// 1. FEC
|
||||
// 2. CRC32
|
||||
// 3. Encryption
|
||||
// 4. WriteTo kernel
|
||||
func (s *UDPSession) output(buf []byte) {
|
||||
var ecc [][]byte
|
||||
|
||||
// extend buf's header space
|
||||
ext := s.ext[:s.headerSize+len(buf)]
|
||||
copy(ext[s.headerSize:], buf)
|
||||
|
||||
// FEC stage
|
||||
if s.fecEncoder != nil {
|
||||
ecc = s.fecEncoder.Encode(ext)
|
||||
}
|
||||
|
||||
// encryption stage
|
||||
if s.block != nil {
|
||||
io.ReadFull(rand.Reader, ext[:nonceSize])
|
||||
checksum := crc32.ChecksumIEEE(ext[cryptHeaderSize:])
|
||||
binary.LittleEndian.PutUint32(ext[nonceSize:], checksum)
|
||||
s.block.Encrypt(ext, ext)
|
||||
|
||||
if ecc != nil {
|
||||
for k := range ecc {
|
||||
io.ReadFull(rand.Reader, ecc[k][:nonceSize])
|
||||
checksum := crc32.ChecksumIEEE(ecc[k][cryptHeaderSize:])
|
||||
binary.LittleEndian.PutUint32(ecc[k][nonceSize:], checksum)
|
||||
s.block.Encrypt(ecc[k], ecc[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo kernel
|
||||
nbytes := 0
|
||||
npkts := 0
|
||||
// if mrand.Intn(100) < 50 {
|
||||
if n, err := s.conn.WriteTo(ext, s.remote); err == nil {
|
||||
nbytes += n
|
||||
npkts++
|
||||
}
|
||||
// }
|
||||
|
||||
if ecc != nil {
|
||||
for k := range ecc {
|
||||
if n, err := s.conn.WriteTo(ecc[k], s.remote); err == nil {
|
||||
nbytes += n
|
||||
npkts++
|
||||
}
|
||||
}
|
||||
}
|
||||
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
|
||||
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
|
||||
}
|
||||
|
||||
// kcp update, returns interval for next calling
|
||||
func (s *UDPSession) update() (interval time.Duration) {
|
||||
s.mu.Lock()
|
||||
s.kcp.flush(false)
|
||||
if s.kcp.WaitSnd() < int(s.kcp.Cwnd()) {
|
||||
s.notifyWriteEvent()
|
||||
}
|
||||
interval = s.updateInterval
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetConv gets conversation id of a session
|
||||
func (s *UDPSession) GetConv() uint32 {
|
||||
return s.kcp.conv
|
||||
}
|
||||
|
||||
func (s *UDPSession) notifyReadEvent() {
|
||||
select {
|
||||
case s.chReadEvent <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UDPSession) notifyWriteEvent() {
|
||||
select {
|
||||
case s.chWriteEvent <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UDPSession) kcpInput(data []byte) {
|
||||
var kcpInErrors, fecErrs, fecRecovered, fecParityShards uint64
|
||||
|
||||
if s.fecDecoder != nil {
|
||||
f := s.fecDecoder.decodeBytes(data)
|
||||
s.mu.Lock()
|
||||
if f.flag == typeData {
|
||||
if ret := s.kcp.Input(data[fecHeaderSizePlus2:], true, s.ackNoDelay); ret != 0 {
|
||||
kcpInErrors++
|
||||
}
|
||||
}
|
||||
|
||||
if f.flag == typeData || f.flag == typeFEC {
|
||||
if f.flag == typeFEC {
|
||||
fecParityShards++
|
||||
}
|
||||
|
||||
if recovers := s.fecDecoder.Decode(f); recovers != nil {
|
||||
for _, r := range recovers {
|
||||
if len(r) >= 2 { // must be larger than 2bytes
|
||||
sz := binary.LittleEndian.Uint16(r)
|
||||
if int(sz) <= len(r) && sz >= 2 {
|
||||
if ret := s.kcp.Input(r[2:sz], false, s.ackNoDelay); ret == 0 {
|
||||
fecRecovered++
|
||||
} else {
|
||||
kcpInErrors++
|
||||
}
|
||||
} else {
|
||||
fecErrs++
|
||||
}
|
||||
} else {
|
||||
fecErrs++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify reader
|
||||
if n := s.kcp.PeekSize(); n > 0 {
|
||||
s.notifyReadEvent()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
} else {
|
||||
s.mu.Lock()
|
||||
if ret := s.kcp.Input(data, true, s.ackNoDelay); ret != 0 {
|
||||
kcpInErrors++
|
||||
}
|
||||
// notify reader
|
||||
if n := s.kcp.PeekSize(); n > 0 {
|
||||
s.notifyReadEvent()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
atomic.AddUint64(&DefaultSnmp.InPkts, 1)
|
||||
atomic.AddUint64(&DefaultSnmp.InBytes, uint64(len(data)))
|
||||
if fecParityShards > 0 {
|
||||
atomic.AddUint64(&DefaultSnmp.FECParityShards, fecParityShards)
|
||||
}
|
||||
if kcpInErrors > 0 {
|
||||
atomic.AddUint64(&DefaultSnmp.KCPInErrors, kcpInErrors)
|
||||
}
|
||||
if fecErrs > 0 {
|
||||
atomic.AddUint64(&DefaultSnmp.FECErrs, fecErrs)
|
||||
}
|
||||
if fecRecovered > 0 {
|
||||
atomic.AddUint64(&DefaultSnmp.FECRecovered, fecRecovered)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UDPSession) receiver(ch chan []byte) {
|
||||
for {
|
||||
data := xmitBuf.Get().([]byte)[:mtuLimit]
|
||||
if n, _, err := s.conn.ReadFrom(data); err == nil && n >= s.headerSize+IKCP_OVERHEAD {
|
||||
select {
|
||||
case ch <- data[:n]:
|
||||
case <-s.die:
|
||||
}
|
||||
} else if err != nil {
|
||||
return
|
||||
} else {
|
||||
atomic.AddUint64(&DefaultSnmp.InErrs, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read loop for client session
|
||||
func (s *UDPSession) readLoop() {
|
||||
chPacket := make(chan []byte)
|
||||
go s.receiver(chPacket)
|
||||
|
||||
for {
|
||||
select {
|
||||
case data := <-chPacket:
|
||||
raw := data
|
||||
dataValid := false
|
||||
if s.block != nil {
|
||||
s.block.Decrypt(data, data)
|
||||
data = data[nonceSize:]
|
||||
checksum := crc32.ChecksumIEEE(data[crcSize:])
|
||||
if checksum == binary.LittleEndian.Uint32(data) {
|
||||
data = data[crcSize:]
|
||||
dataValid = true
|
||||
} else {
|
||||
atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
|
||||
}
|
||||
} else if s.block == nil {
|
||||
dataValid = true
|
||||
}
|
||||
|
||||
if dataValid {
|
||||
s.kcpInput(data)
|
||||
}
|
||||
xmitBuf.Put(raw)
|
||||
case <-s.die:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// Listener defines a server listening for connections
|
||||
Listener struct {
|
||||
block BlockCrypt // block encryption
|
||||
dataShards int // FEC data shard
|
||||
parityShards int // FEC parity shard
|
||||
fecDecoder *FECDecoder // FEC mock initialization
|
||||
conn net.PacketConn // the underlying packet connection
|
||||
|
||||
sessions map[string]*UDPSession // all sessions accepted by this Listener
|
||||
chAccepts chan *UDPSession // Listen() backlog
|
||||
chSessionClosed chan net.Addr // session close queue
|
||||
headerSize int // the overall header size added before KCP frame
|
||||
die chan struct{} // notify the listener has closed
|
||||
rd atomic.Value // read deadline for Accept()
|
||||
wd atomic.Value
|
||||
}
|
||||
|
||||
// incoming packet
|
||||
inPacket struct {
|
||||
from net.Addr
|
||||
data []byte
|
||||
}
|
||||
)
|
||||
|
||||
// monitor incoming data for all connections of server
|
||||
func (l *Listener) monitor() {
|
||||
chPacket := make(chan inPacket)
|
||||
go l.receiver(chPacket)
|
||||
for {
|
||||
select {
|
||||
case p := <-chPacket:
|
||||
raw := p.data
|
||||
data := p.data
|
||||
from := p.from
|
||||
dataValid := false
|
||||
if l.block != nil {
|
||||
l.block.Decrypt(data, data)
|
||||
data = data[nonceSize:]
|
||||
checksum := crc32.ChecksumIEEE(data[crcSize:])
|
||||
if checksum == binary.LittleEndian.Uint32(data) {
|
||||
data = data[crcSize:]
|
||||
dataValid = true
|
||||
} else {
|
||||
atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
|
||||
}
|
||||
} else if l.block == nil {
|
||||
dataValid = true
|
||||
}
|
||||
|
||||
if dataValid {
|
||||
addr := from.String()
|
||||
s, ok := l.sessions[addr]
|
||||
if !ok { // new session
|
||||
if len(l.chAccepts) < cap(l.chAccepts) { // do not let new session overwhelm accept queue
|
||||
var conv uint32
|
||||
convValid := false
|
||||
if l.fecDecoder != nil {
|
||||
isfec := binary.LittleEndian.Uint16(data[4:])
|
||||
if isfec == typeData {
|
||||
conv = binary.LittleEndian.Uint32(data[fecHeaderSizePlus2:])
|
||||
convValid = true
|
||||
}
|
||||
} else {
|
||||
conv = binary.LittleEndian.Uint32(data)
|
||||
convValid = true
|
||||
}
|
||||
|
||||
if convValid {
|
||||
s := newUDPSession(conv, l.dataShards, l.parityShards, l, l.conn, from, l.block)
|
||||
s.kcpInput(data)
|
||||
l.sessions[addr] = s
|
||||
l.chAccepts <- s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.kcpInput(data)
|
||||
}
|
||||
}
|
||||
|
||||
xmitBuf.Put(raw)
|
||||
case deadlink := <-l.chSessionClosed:
|
||||
delete(l.sessions, deadlink.String())
|
||||
case <-l.die:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) receiver(ch chan inPacket) {
|
||||
for {
|
||||
data := xmitBuf.Get().([]byte)[:mtuLimit]
|
||||
if n, from, err := l.conn.ReadFrom(data); err == nil && n >= l.headerSize+IKCP_OVERHEAD {
|
||||
select {
|
||||
case ch <- inPacket{from, data[:n]}:
|
||||
case <-l.die:
|
||||
}
|
||||
} else if err != nil {
|
||||
return
|
||||
} else {
|
||||
atomic.AddUint64(&DefaultSnmp.InErrs, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the socket read buffer for the Listener
|
||||
func (l *Listener) SetReadBuffer(bytes int) error {
|
||||
if nc, ok := l.conn.(setReadBuffer); ok {
|
||||
return nc.SetReadBuffer(bytes)
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// SetWriteBuffer sets the socket write buffer for the Listener
|
||||
func (l *Listener) SetWriteBuffer(bytes int) error {
|
||||
if nc, ok := l.conn.(setWriteBuffer); ok {
|
||||
return nc.SetWriteBuffer(bytes)
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// SetDSCP sets the 6bit DSCP field of IP header
|
||||
func (l *Listener) SetDSCP(dscp int) error {
|
||||
if nc, ok := l.conn.(net.Conn); ok {
|
||||
return ipv4.NewConn(nc).SetTOS(dscp << 2)
|
||||
}
|
||||
return errors.New(errInvalidOperation)
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
return l.AcceptKCP()
|
||||
}
|
||||
|
||||
// AcceptKCP accepts a KCP connection
|
||||
func (l *Listener) AcceptKCP() (*UDPSession, error) {
|
||||
var timeout <-chan time.Time
|
||||
if tdeadline, ok := l.rd.Load().(time.Time); ok && !tdeadline.IsZero() {
|
||||
timeout = time.After(tdeadline.Sub(time.Now()))
|
||||
}
|
||||
|
||||
select {
|
||||
case <-timeout:
|
||||
return nil, &errTimeout{}
|
||||
case c := <-l.chAccepts:
|
||||
return c, nil
|
||||
case <-l.die:
|
||||
return nil, errors.New(errBrokenPipe)
|
||||
}
|
||||
}
|
||||
|
||||
// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
|
||||
func (l *Listener) SetDeadline(t time.Time) error {
|
||||
l.SetReadDeadline(t)
|
||||
l.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the Conn SetReadDeadline method.
|
||||
func (l *Listener) SetReadDeadline(t time.Time) error {
|
||||
l.rd.Store(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the Conn SetWriteDeadline method.
|
||||
func (l *Listener) SetWriteDeadline(t time.Time) error {
|
||||
l.wd.Store(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close stops listening on the UDP address. Already Accepted connections are not closed.
|
||||
func (l *Listener) Close() error {
|
||||
close(l.die)
|
||||
return l.conn.Close()
|
||||
}
|
||||
|
||||
// closeSession notify the listener that a session has closed
|
||||
func (l *Listener) closeSession(remote net.Addr) {
|
||||
select {
|
||||
case l.chSessionClosed <- remote:
|
||||
case <-l.die:
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it.
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return l.conn.LocalAddr()
|
||||
}
|
||||
|
||||
// Listen listens for incoming KCP packets addressed to the local address laddr on the network "udp",
|
||||
func Listen(laddr string) (net.Listener, error) {
|
||||
return ListenWithOptions(laddr, nil, 0, 0)
|
||||
}
|
||||
|
||||
// ListenWithOptions listens for incoming KCP packets addressed to the local address laddr on the network "udp" with packet encryption,
|
||||
// dataShards, parityShards defines Reed-Solomon Erasure Coding parameters
|
||||
func ListenWithOptions(laddr string, block BlockCrypt, dataShards, parityShards int) (*Listener, error) {
|
||||
udpaddr, err := net.ResolveUDPAddr("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "net.ResolveUDPAddr")
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", udpaddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "net.ListenUDP")
|
||||
}
|
||||
|
||||
return ServeConn(block, dataShards, parityShards, conn)
|
||||
}
|
||||
|
||||
// ServeConn serves KCP protocol for a single packet connection.
|
||||
func ServeConn(block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*Listener, error) {
|
||||
l := new(Listener)
|
||||
l.conn = conn
|
||||
l.sessions = make(map[string]*UDPSession)
|
||||
l.chAccepts = make(chan *UDPSession, acceptBacklog)
|
||||
l.chSessionClosed = make(chan net.Addr)
|
||||
l.die = make(chan struct{})
|
||||
l.dataShards = dataShards
|
||||
l.parityShards = parityShards
|
||||
l.block = block
|
||||
l.fecDecoder = newFECDecoder(rxFECMulti*(dataShards+parityShards), dataShards, parityShards)
|
||||
|
||||
// calculate header size
|
||||
if l.block != nil {
|
||||
l.headerSize += cryptHeaderSize
|
||||
}
|
||||
if l.fecDecoder != nil {
|
||||
l.headerSize += fecHeaderSizePlus2
|
||||
}
|
||||
|
||||
go l.monitor()
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Dial connects to the remote address "raddr" on the network "udp"
|
||||
func Dial(raddr string) (net.Conn, error) {
|
||||
return DialWithOptions(raddr, nil, 0, 0)
|
||||
}
|
||||
|
||||
// DialWithOptions connects to the remote address "raddr" on the network "udp" with packet encryption
|
||||
func DialWithOptions(raddr string, block BlockCrypt, dataShards, parityShards int) (*UDPSession, error) {
|
||||
udpaddr, err := net.ResolveUDPAddr("udp", raddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "net.ResolveUDPAddr")
|
||||
}
|
||||
|
||||
udpconn, err := net.DialUDP("udp", nil, udpaddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "net.DialUDP")
|
||||
}
|
||||
|
||||
return NewConn(raddr, block, dataShards, parityShards, &ConnectedUDPConn{udpconn, udpconn})
|
||||
}
|
||||
|
||||
// NewConn establishes a session and talks KCP protocol over a packet connection.
|
||||
func NewConn(raddr string, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*UDPSession, error) {
|
||||
udpaddr, err := net.ResolveUDPAddr("udp", raddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "net.ResolveUDPAddr")
|
||||
}
|
||||
|
||||
var convid uint32
|
||||
binary.Read(rand.Reader, binary.LittleEndian, &convid)
|
||||
return newUDPSession(convid, dataShards, parityShards, nil, conn, udpaddr, block), nil
|
||||
}
|
||||
|
||||
func currentMs() uint32 {
|
||||
return uint32(time.Now().UnixNano() / int64(time.Millisecond))
|
||||
}
|
||||
|
||||
// ConnectedUDPConn is a wrapper for net.UDPConn which converts WriteTo syscalls
|
||||
// to Write syscalls that are 4 times faster on some OS'es. This should only be
|
||||
// used for connections that were produced by a net.Dial* call.
|
||||
type ConnectedUDPConn struct {
|
||||
*net.UDPConn
|
||||
Conn net.Conn // underlying connection if any
|
||||
}
|
||||
|
||||
// WriteTo redirects all writes to the Write syscall, which is 4 times faster.
|
||||
func (c *ConnectedUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
return c.Write(b)
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package kcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // bytes sent from upper level
|
||||
BytesReceived uint64 // bytes received to upper level
|
||||
MaxConn uint64 // max number of connections ever reached
|
||||
ActiveOpens uint64 // accumulated active open connections
|
||||
PassiveOpens uint64 // accumulated passive open connections
|
||||
CurrEstab uint64 // current number of established connections
|
||||
InErrs uint64 // UDP read errors reported from net.PacketConn
|
||||
InCsumErrors uint64 // checksum errors from CRC32
|
||||
KCPInErrors uint64 // packet iput errors reported from KCP
|
||||
InPkts uint64 // incoming packets count
|
||||
OutPkts uint64 // outgoing packets count
|
||||
InSegs uint64 // incoming KCP segments
|
||||
OutSegs uint64 // outgoing KCP segments
|
||||
InBytes uint64 // UDP bytes received
|
||||
OutBytes uint64 // UDP bytes sent
|
||||
RetransSegs uint64 // accmulated retransmited segments
|
||||
FastRetransSegs uint64 // accmulated fast retransmitted segments
|
||||
EarlyRetransSegs uint64 // accmulated early retransmitted segments
|
||||
LostSegs uint64 // number of segs infered as lost
|
||||
RepeatSegs uint64 // number of segs duplicated
|
||||
FECRecovered uint64 // correct packets recovered from FEC
|
||||
FECErrs uint64 // incorrect packets recovered from FEC
|
||||
FECParityShards uint64 // FEC segments received
|
||||
FECShortShards uint64 // number of data shards that's not enough for recovery
|
||||
}
|
||||
|
||||
func newSnmp() *Snmp {
|
||||
return new(Snmp)
|
||||
}
|
||||
|
||||
// Header returns all field names
|
||||
func (s *Snmp) Header() []string {
|
||||
return []string{
|
||||
"BytesSent",
|
||||
"BytesReceived",
|
||||
"MaxConn",
|
||||
"ActiveOpens",
|
||||
"PassiveOpens",
|
||||
"CurrEstab",
|
||||
"InErrs",
|
||||
"InCsumErrors",
|
||||
"KCPInErrors",
|
||||
"InPkts",
|
||||
"OutPkts",
|
||||
"InSegs",
|
||||
"OutSegs",
|
||||
"InBytes",
|
||||
"OutBytes",
|
||||
"RetransSegs",
|
||||
"FastRetransSegs",
|
||||
"EarlyRetransSegs",
|
||||
"LostSegs",
|
||||
"RepeatSegs",
|
||||
"FECParityShards",
|
||||
"FECErrs",
|
||||
"FECRecovered",
|
||||
"FECShortShards",
|
||||
}
|
||||
}
|
||||
|
||||
// ToSlice returns current snmp info as slice
|
||||
func (s *Snmp) ToSlice() []string {
|
||||
snmp := s.Copy()
|
||||
return []string{
|
||||
fmt.Sprint(snmp.BytesSent),
|
||||
fmt.Sprint(snmp.BytesReceived),
|
||||
fmt.Sprint(snmp.MaxConn),
|
||||
fmt.Sprint(snmp.ActiveOpens),
|
||||
fmt.Sprint(snmp.PassiveOpens),
|
||||
fmt.Sprint(snmp.CurrEstab),
|
||||
fmt.Sprint(snmp.InErrs),
|
||||
fmt.Sprint(snmp.InCsumErrors),
|
||||
fmt.Sprint(snmp.KCPInErrors),
|
||||
fmt.Sprint(snmp.InPkts),
|
||||
fmt.Sprint(snmp.OutPkts),
|
||||
fmt.Sprint(snmp.InSegs),
|
||||
fmt.Sprint(snmp.OutSegs),
|
||||
fmt.Sprint(snmp.InBytes),
|
||||
fmt.Sprint(snmp.OutBytes),
|
||||
fmt.Sprint(snmp.RetransSegs),
|
||||
fmt.Sprint(snmp.FastRetransSegs),
|
||||
fmt.Sprint(snmp.EarlyRetransSegs),
|
||||
fmt.Sprint(snmp.LostSegs),
|
||||
fmt.Sprint(snmp.RepeatSegs),
|
||||
fmt.Sprint(snmp.FECParityShards),
|
||||
fmt.Sprint(snmp.FECErrs),
|
||||
fmt.Sprint(snmp.FECRecovered),
|
||||
fmt.Sprint(snmp.FECShortShards),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy make a copy of current snmp snapshot
|
||||
func (s *Snmp) Copy() *Snmp {
|
||||
d := newSnmp()
|
||||
d.BytesSent = atomic.LoadUint64(&s.BytesSent)
|
||||
d.BytesReceived = atomic.LoadUint64(&s.BytesReceived)
|
||||
d.MaxConn = atomic.LoadUint64(&s.MaxConn)
|
||||
d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
|
||||
d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
|
||||
d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
|
||||
d.InErrs = atomic.LoadUint64(&s.InErrs)
|
||||
d.InCsumErrors = atomic.LoadUint64(&s.InCsumErrors)
|
||||
d.KCPInErrors = atomic.LoadUint64(&s.KCPInErrors)
|
||||
d.InPkts = atomic.LoadUint64(&s.InPkts)
|
||||
d.OutPkts = atomic.LoadUint64(&s.OutPkts)
|
||||
d.InSegs = atomic.LoadUint64(&s.InSegs)
|
||||
d.OutSegs = atomic.LoadUint64(&s.OutSegs)
|
||||
d.InBytes = atomic.LoadUint64(&s.InBytes)
|
||||
d.OutBytes = atomic.LoadUint64(&s.OutBytes)
|
||||
d.RetransSegs = atomic.LoadUint64(&s.RetransSegs)
|
||||
d.FastRetransSegs = atomic.LoadUint64(&s.FastRetransSegs)
|
||||
d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
|
||||
d.LostSegs = atomic.LoadUint64(&s.LostSegs)
|
||||
d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
|
||||
d.FECParityShards = atomic.LoadUint64(&s.FECParityShards)
|
||||
d.FECErrs = atomic.LoadUint64(&s.FECErrs)
|
||||
d.FECRecovered = atomic.LoadUint64(&s.FECRecovered)
|
||||
d.FECShortShards = atomic.LoadUint64(&s.FECShortShards)
|
||||
return d
|
||||
}
|
||||
|
||||
// Reset values to zero
|
||||
func (s *Snmp) Reset() {
|
||||
atomic.StoreUint64(&s.BytesSent, 0)
|
||||
atomic.StoreUint64(&s.BytesReceived, 0)
|
||||
atomic.StoreUint64(&s.MaxConn, 0)
|
||||
atomic.StoreUint64(&s.ActiveOpens, 0)
|
||||
atomic.StoreUint64(&s.PassiveOpens, 0)
|
||||
atomic.StoreUint64(&s.CurrEstab, 0)
|
||||
atomic.StoreUint64(&s.InErrs, 0)
|
||||
atomic.StoreUint64(&s.InCsumErrors, 0)
|
||||
atomic.StoreUint64(&s.KCPInErrors, 0)
|
||||
atomic.StoreUint64(&s.InPkts, 0)
|
||||
atomic.StoreUint64(&s.OutPkts, 0)
|
||||
atomic.StoreUint64(&s.InSegs, 0)
|
||||
atomic.StoreUint64(&s.OutSegs, 0)
|
||||
atomic.StoreUint64(&s.InBytes, 0)
|
||||
atomic.StoreUint64(&s.OutBytes, 0)
|
||||
atomic.StoreUint64(&s.RetransSegs, 0)
|
||||
atomic.StoreUint64(&s.FastRetransSegs, 0)
|
||||
atomic.StoreUint64(&s.EarlyRetransSegs, 0)
|
||||
atomic.StoreUint64(&s.LostSegs, 0)
|
||||
atomic.StoreUint64(&s.RepeatSegs, 0)
|
||||
atomic.StoreUint64(&s.FECParityShards, 0)
|
||||
atomic.StoreUint64(&s.FECErrs, 0)
|
||||
atomic.StoreUint64(&s.FECRecovered, 0)
|
||||
atomic.StoreUint64(&s.FECShortShards, 0)
|
||||
}
|
||||
|
||||
// DefaultSnmp is the global KCP connection statistics collector
|
||||
var DefaultSnmp *Snmp
|
||||
|
||||
func init() {
|
||||
DefaultSnmp = newSnmp()
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package kcp
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var updater updateHeap
|
||||
|
||||
func init() {
|
||||
updater.init()
|
||||
go updater.updateTask()
|
||||
}
|
||||
|
||||
// entry contains a session update info
|
||||
type entry struct {
|
||||
sid uint32
|
||||
ts time.Time
|
||||
s *UDPSession
|
||||
}
|
||||
|
||||
// a global heap managed kcp.flush() caller
|
||||
type updateHeap struct {
|
||||
entries []entry
|
||||
indices map[uint32]int
|
||||
mu sync.Mutex
|
||||
chWakeUp chan struct{}
|
||||
}
|
||||
|
||||
func (h *updateHeap) Len() int { return len(h.entries) }
|
||||
func (h *updateHeap) Less(i, j int) bool { return h.entries[i].ts.Before(h.entries[j].ts) }
|
||||
func (h *updateHeap) Swap(i, j int) {
|
||||
h.entries[i], h.entries[j] = h.entries[j], h.entries[i]
|
||||
h.indices[h.entries[i].sid] = i
|
||||
h.indices[h.entries[j].sid] = j
|
||||
}
|
||||
|
||||
func (h *updateHeap) Push(x interface{}) {
|
||||
h.entries = append(h.entries, x.(entry))
|
||||
n := len(h.entries)
|
||||
h.indices[h.entries[n-1].sid] = n - 1
|
||||
}
|
||||
|
||||
func (h *updateHeap) Pop() interface{} {
|
||||
n := len(h.entries)
|
||||
x := h.entries[n-1]
|
||||
h.entries[n-1] = entry{} // manual set nil for GC
|
||||
h.entries = h.entries[0 : n-1]
|
||||
delete(h.indices, x.sid)
|
||||
return x
|
||||
}
|
||||
|
||||
func (h *updateHeap) init() {
|
||||
h.indices = make(map[uint32]int)
|
||||
h.chWakeUp = make(chan struct{}, 1)
|
||||
}
|
||||
|
||||
func (h *updateHeap) addSession(s *UDPSession) {
|
||||
h.mu.Lock()
|
||||
heap.Push(h, entry{s.sid, time.Now(), s})
|
||||
h.mu.Unlock()
|
||||
h.wakeup()
|
||||
}
|
||||
|
||||
func (h *updateHeap) removeSession(s *UDPSession) {
|
||||
h.mu.Lock()
|
||||
if idx, ok := h.indices[s.sid]; ok {
|
||||
heap.Remove(h, idx)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
func (h *updateHeap) wakeup() {
|
||||
select {
|
||||
case h.chWakeUp <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (h *updateHeap) updateTask() {
|
||||
var timer <-chan time.Time
|
||||
for {
|
||||
select {
|
||||
case <-timer:
|
||||
case <-h.chWakeUp:
|
||||
}
|
||||
|
||||
h.mu.Lock()
|
||||
hlen := h.Len()
|
||||
now := time.Now()
|
||||
for i := 0; i < hlen; i++ {
|
||||
entry := heap.Pop(h).(entry)
|
||||
if now.After(entry.ts) {
|
||||
entry.ts = now.Add(entry.s.update())
|
||||
heap.Push(h, entry)
|
||||
} else {
|
||||
heap.Push(h, entry)
|
||||
break
|
||||
}
|
||||
}
|
||||
if h.Len() > 0 {
|
||||
timer = time.After(h.entries[0].ts.Sub(now))
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2013 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 kcp
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
|
||||
|
||||
// fastXORBytes xors in bulk. It only works on architectures that
|
||||
// support unaligned read/writes.
|
||||
func fastXORBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
|
||||
w := n / wordSize
|
||||
if w > 0 {
|
||||
wordBytes := w * wordSize
|
||||
fastXORWords(dst[:wordBytes], a[:wordBytes], b[:wordBytes])
|
||||
}
|
||||
|
||||
for i := (n - n%wordSize); i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func safeXORBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
ex := n % 8
|
||||
for i := 0; i < ex; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
|
||||
for i := ex; i < n; i += 8 {
|
||||
_dst := dst[i : i+8]
|
||||
_a := a[i : i+8]
|
||||
_b := b[i : i+8]
|
||||
_dst[0] = _a[0] ^ _b[0]
|
||||
_dst[1] = _a[1] ^ _b[1]
|
||||
_dst[2] = _a[2] ^ _b[2]
|
||||
_dst[3] = _a[3] ^ _b[3]
|
||||
|
||||
_dst[4] = _a[4] ^ _b[4]
|
||||
_dst[5] = _a[5] ^ _b[5]
|
||||
_dst[6] = _a[6] ^ _b[6]
|
||||
_dst[7] = _a[7] ^ _b[7]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
|
||||
// space. Returns the number of bytes xor'd.
|
||||
func xorBytes(dst, a, b []byte) int {
|
||||
if supportsUnaligned {
|
||||
return fastXORBytes(dst, a, b)
|
||||
}
|
||||
// TODO(hanwen): if (dst, a, b) have common alignment
|
||||
// we could still try fastXORBytes. It is not clear
|
||||
// how often this happens, and it's only worth it if
|
||||
// the block encryption itself is hardware
|
||||
// accelerated.
|
||||
return safeXORBytes(dst, a, b)
|
||||
}
|
||||
|
||||
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
|
||||
// The arguments are assumed to be of equal length.
|
||||
func fastXORWords(dst, a, b []byte) {
|
||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
||||
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
||||
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
||||
n := len(b) / wordSize
|
||||
ex := n % 8
|
||||
for i := 0; i < ex; i++ {
|
||||
dw[i] = aw[i] ^ bw[i]
|
||||
}
|
||||
|
||||
for i := ex; i < n; i += 8 {
|
||||
_dw := dw[i : i+8]
|
||||
_aw := aw[i : i+8]
|
||||
_bw := bw[i : i+8]
|
||||
_dw[0] = _aw[0] ^ _bw[0]
|
||||
_dw[1] = _aw[1] ^ _bw[1]
|
||||
_dw[2] = _aw[2] ^ _bw[2]
|
||||
_dw[3] = _aw[3] ^ _bw[3]
|
||||
_dw[4] = _aw[4] ^ _bw[4]
|
||||
_dw[5] = _aw[5] ^ _bw[5]
|
||||
_dw[6] = _aw[6] ^ _bw[6]
|
||||
_dw[7] = _aw[7] ^ _bw[7]
|
||||
}
|
||||
}
|
||||
|
||||
func xorWords(dst, a, b []byte) {
|
||||
if supportsUnaligned {
|
||||
fastXORWords(dst, a, b)
|
||||
} else {
|
||||
safeXORBytes(dst, a, b)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package smux
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
version = 1
|
||||
)
|
||||
|
||||
const ( // cmds
|
||||
cmdSYN byte = iota // stream open
|
||||
cmdFIN // stream close, a.k.a EOF mark
|
||||
cmdPSH // data push
|
||||
cmdNOP // no operation
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfVer = 1
|
||||
sizeOfCmd = 1
|
||||
sizeOfLength = 2
|
||||
sizeOfSid = 4
|
||||
headerSize = sizeOfVer + sizeOfCmd + sizeOfSid + sizeOfLength
|
||||
)
|
||||
|
||||
// Frame defines a packet from or to be multiplexed into a single connection
|
||||
type Frame struct {
|
||||
ver byte
|
||||
cmd byte
|
||||
sid uint32
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newFrame(cmd byte, sid uint32) Frame {
|
||||
return Frame{ver: version, cmd: cmd, sid: sid}
|
||||
}
|
||||
|
||||
type rawHeader []byte
|
||||
|
||||
func (h rawHeader) Version() byte {
|
||||
return h[0]
|
||||
}
|
||||
|
||||
func (h rawHeader) Cmd() byte {
|
||||
return h[1]
|
||||
}
|
||||
|
||||
func (h rawHeader) Length() uint16 {
|
||||
return binary.LittleEndian.Uint16(h[2:])
|
||||
}
|
||||
|
||||
func (h rawHeader) StreamID() uint32 {
|
||||
return binary.LittleEndian.Uint32(h[4:])
|
||||
}
|
||||
|
||||
func (h rawHeader) String() string {
|
||||
return fmt.Sprintf("Version:%d Cmd:%d StreamID:%d Length:%d",
|
||||
h.Version(), h.Cmd(), h.StreamID(), h.Length())
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package smux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Config is used to tune the Smux session
|
||||
type Config struct {
|
||||
// KeepAliveInterval is how often to send a NOP command to the remote
|
||||
KeepAliveInterval time.Duration
|
||||
|
||||
// KeepAliveTimeout is how long the session
|
||||
// will be closed if no data has arrived
|
||||
KeepAliveTimeout time.Duration
|
||||
|
||||
// MaxFrameSize is used to control the maximum
|
||||
// frame size to sent to the remote
|
||||
MaxFrameSize int
|
||||
|
||||
// MaxReceiveBuffer is used to control the maximum
|
||||
// number of data in the buffer pool
|
||||
MaxReceiveBuffer int
|
||||
}
|
||||
|
||||
// DefaultConfig is used to return a default configuration
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
KeepAliveInterval: 10 * time.Second,
|
||||
KeepAliveTimeout: 30 * time.Second,
|
||||
MaxFrameSize: 4096,
|
||||
MaxReceiveBuffer: 4194304,
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyConfig is used to verify the sanity of configuration
|
||||
func VerifyConfig(config *Config) error {
|
||||
if config.KeepAliveInterval == 0 {
|
||||
return errors.New("keep-alive interval must be positive")
|
||||
}
|
||||
if config.KeepAliveTimeout < config.KeepAliveInterval {
|
||||
return fmt.Errorf("keep-alive timeout must be larger than keep-alive interval")
|
||||
}
|
||||
if config.MaxFrameSize <= 0 {
|
||||
return errors.New("max frame size must be positive")
|
||||
}
|
||||
if config.MaxFrameSize > 65535 {
|
||||
return errors.New("max frame size must not be larger than 65535")
|
||||
}
|
||||
if config.MaxReceiveBuffer <= 0 {
|
||||
return errors.New("max receive buffer must be positive")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Server is used to initialize a new server-side connection.
|
||||
func Server(conn io.ReadWriteCloser, config *Config) (*Session, error) {
|
||||
if config == nil {
|
||||
config = DefaultConfig()
|
||||
}
|
||||
if err := VerifyConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSession(config, conn, false), nil
|
||||
}
|
||||
|
||||
// Client is used to initialize a new client-side connection.
|
||||
func Client(conn io.ReadWriteCloser, config *Config) (*Session, error) {
|
||||
if config == nil {
|
||||
config = DefaultConfig()
|
||||
}
|
||||
|
||||
if err := VerifyConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSession(config, conn, true), nil
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
package smux
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultAcceptBacklog = 1024
|
||||
)
|
||||
|
||||
const (
|
||||
errBrokenPipe = "broken pipe"
|
||||
errInvalidProtocol = "invalid protocol version"
|
||||
)
|
||||
|
||||
type writeRequest struct {
|
||||
frame Frame
|
||||
result chan writeResult
|
||||
}
|
||||
|
||||
type writeResult struct {
|
||||
n int
|
||||
err error
|
||||
}
|
||||
|
||||
// Session defines a multiplexed connection for streams
|
||||
type Session struct {
|
||||
conn io.ReadWriteCloser
|
||||
|
||||
config *Config
|
||||
nextStreamID uint32 // next stream identifier
|
||||
|
||||
bucket int32 // token bucket
|
||||
bucketNotify chan struct{} // used for waiting for tokens
|
||||
|
||||
streams map[uint32]*Stream // all streams in this session
|
||||
streamLock sync.Mutex // locks streams
|
||||
|
||||
die chan struct{} // flag session has died
|
||||
dieLock sync.Mutex
|
||||
chAccepts chan *Stream
|
||||
|
||||
dataReady int32 // flag data has arrived
|
||||
|
||||
deadline atomic.Value
|
||||
|
||||
writes chan writeRequest
|
||||
}
|
||||
|
||||
func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
|
||||
s := new(Session)
|
||||
s.die = make(chan struct{})
|
||||
s.conn = conn
|
||||
s.config = config
|
||||
s.streams = make(map[uint32]*Stream)
|
||||
s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
|
||||
s.bucket = int32(config.MaxReceiveBuffer)
|
||||
s.bucketNotify = make(chan struct{}, 1)
|
||||
s.writes = make(chan writeRequest)
|
||||
|
||||
if client {
|
||||
s.nextStreamID = 1
|
||||
} else {
|
||||
s.nextStreamID = 2
|
||||
}
|
||||
go s.recvLoop()
|
||||
go s.sendLoop()
|
||||
go s.keepalive()
|
||||
return s
|
||||
}
|
||||
|
||||
// OpenStream is used to create a new stream
|
||||
func (s *Session) OpenStream() (*Stream, error) {
|
||||
if s.IsClosed() {
|
||||
return nil, errors.New(errBrokenPipe)
|
||||
}
|
||||
|
||||
sid := atomic.AddUint32(&s.nextStreamID, 2)
|
||||
stream := newStream(sid, s.config.MaxFrameSize, s)
|
||||
|
||||
if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
|
||||
return nil, errors.Wrap(err, "writeFrame")
|
||||
}
|
||||
|
||||
s.streamLock.Lock()
|
||||
s.streams[sid] = stream
|
||||
s.streamLock.Unlock()
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// AcceptStream is used to block until the next available stream
|
||||
// is ready to be accepted.
|
||||
func (s *Session) AcceptStream() (*Stream, error) {
|
||||
var deadline <-chan time.Time
|
||||
if d, ok := s.deadline.Load().(time.Time); ok && !d.IsZero() {
|
||||
timer := time.NewTimer(d.Sub(time.Now()))
|
||||
defer timer.Stop()
|
||||
deadline = timer.C
|
||||
}
|
||||
select {
|
||||
case stream := <-s.chAccepts:
|
||||
return stream, nil
|
||||
case <-deadline:
|
||||
return nil, errTimeout
|
||||
case <-s.die:
|
||||
return nil, errors.New(errBrokenPipe)
|
||||
}
|
||||
}
|
||||
|
||||
// Close is used to close the session and all streams.
|
||||
func (s *Session) Close() (err error) {
|
||||
s.dieLock.Lock()
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
s.dieLock.Unlock()
|
||||
return errors.New(errBrokenPipe)
|
||||
default:
|
||||
close(s.die)
|
||||
s.dieLock.Unlock()
|
||||
s.streamLock.Lock()
|
||||
for k := range s.streams {
|
||||
s.streams[k].sessionClose()
|
||||
}
|
||||
s.streamLock.Unlock()
|
||||
s.notifyBucket()
|
||||
return s.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// notifyBucket notifies recvLoop that bucket is available
|
||||
func (s *Session) notifyBucket() {
|
||||
select {
|
||||
case s.bucketNotify <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// IsClosed does a safe check to see if we have shutdown
|
||||
func (s *Session) IsClosed() bool {
|
||||
select {
|
||||
case <-s.die:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NumStreams returns the number of currently open streams
|
||||
func (s *Session) NumStreams() int {
|
||||
if s.IsClosed() {
|
||||
return 0
|
||||
}
|
||||
s.streamLock.Lock()
|
||||
defer s.streamLock.Unlock()
|
||||
return len(s.streams)
|
||||
}
|
||||
|
||||
// SetDeadline sets a deadline used by Accept* calls.
|
||||
// A zero time value disables the deadline.
|
||||
func (s *Session) SetDeadline(t time.Time) error {
|
||||
s.deadline.Store(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// notify the session that a stream has closed
|
||||
func (s *Session) streamClosed(sid uint32) {
|
||||
s.streamLock.Lock()
|
||||
if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
|
||||
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
||||
s.notifyBucket()
|
||||
}
|
||||
}
|
||||
delete(s.streams, sid)
|
||||
s.streamLock.Unlock()
|
||||
}
|
||||
|
||||
// returnTokens is called by stream to return token after read
|
||||
func (s *Session) returnTokens(n int) {
|
||||
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
||||
s.notifyBucket()
|
||||
}
|
||||
}
|
||||
|
||||
// session read a frame from underlying connection
|
||||
// it's data is pointed to the input buffer
|
||||
func (s *Session) readFrame(buffer []byte) (f Frame, err error) {
|
||||
if _, err := io.ReadFull(s.conn, buffer[:headerSize]); err != nil {
|
||||
return f, errors.Wrap(err, "readFrame")
|
||||
}
|
||||
|
||||
dec := rawHeader(buffer)
|
||||
if dec.Version() != version {
|
||||
return f, errors.New(errInvalidProtocol)
|
||||
}
|
||||
|
||||
f.ver = dec.Version()
|
||||
f.cmd = dec.Cmd()
|
||||
f.sid = dec.StreamID()
|
||||
if length := dec.Length(); length > 0 {
|
||||
if _, err := io.ReadFull(s.conn, buffer[headerSize:headerSize+length]); err != nil {
|
||||
return f, errors.Wrap(err, "readFrame")
|
||||
}
|
||||
f.data = buffer[headerSize : headerSize+length]
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// recvLoop keeps on reading from underlying connection if tokens are available
|
||||
func (s *Session) recvLoop() {
|
||||
buffer := make([]byte, (1<<16)+headerSize)
|
||||
for {
|
||||
for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
|
||||
<-s.bucketNotify
|
||||
}
|
||||
|
||||
if f, err := s.readFrame(buffer); err == nil {
|
||||
atomic.StoreInt32(&s.dataReady, 1)
|
||||
|
||||
switch f.cmd {
|
||||
case cmdNOP:
|
||||
case cmdSYN:
|
||||
s.streamLock.Lock()
|
||||
if _, ok := s.streams[f.sid]; !ok {
|
||||
stream := newStream(f.sid, s.config.MaxFrameSize, s)
|
||||
s.streams[f.sid] = stream
|
||||
select {
|
||||
case s.chAccepts <- stream:
|
||||
case <-s.die:
|
||||
}
|
||||
}
|
||||
s.streamLock.Unlock()
|
||||
case cmdFIN:
|
||||
s.streamLock.Lock()
|
||||
if stream, ok := s.streams[f.sid]; ok {
|
||||
stream.markRST()
|
||||
stream.notifyReadEvent()
|
||||
}
|
||||
s.streamLock.Unlock()
|
||||
case cmdPSH:
|
||||
s.streamLock.Lock()
|
||||
if stream, ok := s.streams[f.sid]; ok {
|
||||
atomic.AddInt32(&s.bucket, -int32(len(f.data)))
|
||||
stream.pushBytes(f.data)
|
||||
stream.notifyReadEvent()
|
||||
}
|
||||
s.streamLock.Unlock()
|
||||
default:
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) keepalive() {
|
||||
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
|
||||
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
|
||||
defer tickerPing.Stop()
|
||||
defer tickerTimeout.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tickerPing.C:
|
||||
s.writeFrame(newFrame(cmdNOP, 0))
|
||||
s.notifyBucket() // force a signal to the recvLoop
|
||||
case <-tickerTimeout.C:
|
||||
if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
case <-s.die:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) sendLoop() {
|
||||
buf := make([]byte, (1<<16)+headerSize)
|
||||
for {
|
||||
select {
|
||||
case <-s.die:
|
||||
return
|
||||
case request, ok := <-s.writes:
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
buf[0] = request.frame.ver
|
||||
buf[1] = request.frame.cmd
|
||||
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
|
||||
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
|
||||
copy(buf[headerSize:], request.frame.data)
|
||||
n, err := s.conn.Write(buf[:headerSize+len(request.frame.data)])
|
||||
|
||||
n -= headerSize
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
|
||||
result := writeResult{
|
||||
n: n,
|
||||
err: err,
|
||||
}
|
||||
|
||||
request.result <- result
|
||||
close(request.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeFrame writes the frame to the underlying connection
|
||||
// and returns the number of bytes written if successful
|
||||
func (s *Session) writeFrame(f Frame) (n int, err error) {
|
||||
req := writeRequest{
|
||||
frame: f,
|
||||
result: make(chan writeResult, 1),
|
||||
}
|
||||
select {
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
case s.writes <- req:
|
||||
}
|
||||
|
||||
result := <-req.result
|
||||
return result.n, result.err
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
package smux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Stream implements net.Conn
|
||||
type Stream struct {
|
||||
id uint32
|
||||
rstflag int32
|
||||
sess *Session
|
||||
buffer bytes.Buffer
|
||||
bufferLock sync.Mutex
|
||||
frameSize int
|
||||
chReadEvent chan struct{} // notify a read event
|
||||
die chan struct{} // flag the stream has closed
|
||||
dieLock sync.Mutex
|
||||
readDeadline atomic.Value
|
||||
writeDeadline atomic.Value
|
||||
}
|
||||
|
||||
// newStream initiates a Stream struct
|
||||
func newStream(id uint32, frameSize int, sess *Session) *Stream {
|
||||
s := new(Stream)
|
||||
s.id = id
|
||||
s.chReadEvent = make(chan struct{}, 1)
|
||||
s.frameSize = frameSize
|
||||
s.sess = sess
|
||||
s.die = make(chan struct{})
|
||||
return s
|
||||
}
|
||||
|
||||
// ID returns the unique stream ID.
|
||||
func (s *Stream) ID() uint32 {
|
||||
return s.id
|
||||
}
|
||||
|
||||
// Read implements net.Conn
|
||||
func (s *Stream) Read(b []byte) (n int, err error) {
|
||||
var deadline <-chan time.Time
|
||||
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||
timer := time.NewTimer(d.Sub(time.Now()))
|
||||
defer timer.Stop()
|
||||
deadline = timer.C
|
||||
}
|
||||
|
||||
READ:
|
||||
select {
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
case <-deadline:
|
||||
return n, errTimeout
|
||||
default:
|
||||
}
|
||||
|
||||
s.bufferLock.Lock()
|
||||
n, err = s.buffer.Read(b)
|
||||
s.bufferLock.Unlock()
|
||||
|
||||
if n > 0 {
|
||||
s.sess.returnTokens(n)
|
||||
return n, nil
|
||||
} else if atomic.LoadInt32(&s.rstflag) == 1 {
|
||||
_ = s.Close()
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.chReadEvent:
|
||||
goto READ
|
||||
case <-deadline:
|
||||
return n, errTimeout
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements net.Conn
|
||||
func (s *Stream) Write(b []byte) (n int, err error) {
|
||||
var deadline <-chan time.Time
|
||||
if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||
timer := time.NewTimer(d.Sub(time.Now()))
|
||||
defer timer.Stop()
|
||||
deadline = timer.C
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
default:
|
||||
}
|
||||
|
||||
frames := s.split(b, cmdPSH, s.id)
|
||||
sent := 0
|
||||
for k := range frames {
|
||||
req := writeRequest{
|
||||
frame: frames[k],
|
||||
result: make(chan writeResult, 1),
|
||||
}
|
||||
|
||||
select {
|
||||
case s.sess.writes <- req:
|
||||
case <-s.die:
|
||||
return sent, errors.New(errBrokenPipe)
|
||||
case <-deadline:
|
||||
return sent, errTimeout
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-req.result:
|
||||
sent += result.n
|
||||
if result.err != nil {
|
||||
return sent, result.err
|
||||
}
|
||||
case <-s.die:
|
||||
return sent, errors.New(errBrokenPipe)
|
||||
case <-deadline:
|
||||
return sent, errTimeout
|
||||
}
|
||||
}
|
||||
return sent, nil
|
||||
}
|
||||
|
||||
// Close implements net.Conn
|
||||
func (s *Stream) Close() error {
|
||||
s.dieLock.Lock()
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
s.dieLock.Unlock()
|
||||
return errors.New(errBrokenPipe)
|
||||
default:
|
||||
close(s.die)
|
||||
s.dieLock.Unlock()
|
||||
s.sess.streamClosed(s.id)
|
||||
_, err := s.sess.writeFrame(newFrame(cmdFIN, s.id))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline as defined by
|
||||
// net.Conn.SetReadDeadline.
|
||||
// A zero time value disables the deadline.
|
||||
func (s *Stream) SetReadDeadline(t time.Time) error {
|
||||
s.readDeadline.Store(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline as defined by
|
||||
// net.Conn.SetWriteDeadline.
|
||||
// A zero time value disables the deadline.
|
||||
func (s *Stream) SetWriteDeadline(t time.Time) error {
|
||||
s.writeDeadline.Store(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDeadline sets both read and write deadlines as defined by
|
||||
// net.Conn.SetDeadline.
|
||||
// A zero time value disables the deadlines.
|
||||
func (s *Stream) SetDeadline(t time.Time) error {
|
||||
if err := s.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.SetWriteDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// session closes the stream
|
||||
func (s *Stream) sessionClose() {
|
||||
s.dieLock.Lock()
|
||||
defer s.dieLock.Unlock()
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
default:
|
||||
close(s.die)
|
||||
}
|
||||
}
|
||||
|
||||
// LocalAddr satisfies net.Conn interface
|
||||
func (s *Stream) LocalAddr() net.Addr {
|
||||
if ts, ok := s.sess.conn.(interface {
|
||||
LocalAddr() net.Addr
|
||||
}); ok {
|
||||
return ts.LocalAddr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoteAddr satisfies net.Conn interface
|
||||
func (s *Stream) RemoteAddr() net.Addr {
|
||||
if ts, ok := s.sess.conn.(interface {
|
||||
RemoteAddr() net.Addr
|
||||
}); ok {
|
||||
return ts.RemoteAddr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pushBytes a slice into buffer
|
||||
func (s *Stream) pushBytes(p []byte) {
|
||||
s.bufferLock.Lock()
|
||||
s.buffer.Write(p)
|
||||
s.bufferLock.Unlock()
|
||||
}
|
||||
|
||||
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
||||
func (s *Stream) recycleTokens() (n int) {
|
||||
s.bufferLock.Lock()
|
||||
n = s.buffer.Len()
|
||||
s.buffer.Reset()
|
||||
s.bufferLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// split large byte buffer into smaller frames, reference only
|
||||
func (s *Stream) split(bts []byte, cmd byte, sid uint32) []Frame {
|
||||
frames := make([]Frame, 0, len(bts)/s.frameSize+1)
|
||||
for len(bts) > s.frameSize {
|
||||
frame := newFrame(cmd, sid)
|
||||
frame.data = bts[:s.frameSize]
|
||||
bts = bts[s.frameSize:]
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
if len(bts) > 0 {
|
||||
frame := newFrame(cmd, sid)
|
||||
frame.data = bts
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
// notify read event
|
||||
func (s *Stream) notifyReadEvent() {
|
||||
select {
|
||||
case s.chReadEvent <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// mark this stream has been reset
|
||||
func (s *Stream) markRST() {
|
||||
atomic.StoreInt32(&s.rstflag, 1)
|
||||
}
|
||||
|
||||
var errTimeout error = &timeoutError{}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *timeoutError) Timeout() bool { return true }
|
||||
func (e *timeoutError) Temporary() bool { return true }
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2010 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 blowfish
|
||||
|
||||
// getNextWord returns the next big-endian uint32 value from the byte slice
|
||||
// at the given position in a circular manner, updating the position.
|
||||
func getNextWord(b []byte, pos *int) uint32 {
|
||||
var w uint32
|
||||
j := *pos
|
||||
for i := 0; i < 4; i++ {
|
||||
w = w<<8 | uint32(b[j])
|
||||
j++
|
||||
if j >= len(b) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
*pos = j
|
||||
return w
|
||||
}
|
||||
|
||||
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
|
||||
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
|
||||
// pi and substitution tables for calls to Encrypt. This is used, primarily,
|
||||
// by the bcrypt package to reuse the Blowfish key schedule during its
|
||||
// set up. It's unlikely that you need to use this directly.
|
||||
func ExpandKey(key []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
// Using inlined getNextWord for performance.
|
||||
var d uint32
|
||||
for k := 0; k < 4; k++ {
|
||||
d = d<<8 | uint32(key[j])
|
||||
j++
|
||||
if j >= len(key) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.p[i] ^= d
|
||||
}
|
||||
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
// This is similar to ExpandKey, but folds the salt during the key
|
||||
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
|
||||
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
|
||||
// and specializing it here is useful.
|
||||
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
c.p[i] ^= getNextWord(key, &j)
|
||||
}
|
||||
|
||||
j = 0
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[0]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
|
||||
xr ^= c.p[17]
|
||||
return xr, xl
|
||||
}
|
||||
|
||||
func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[17]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
|
||||
xr ^= c.p[0]
|
||||
return xr, xl
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2010 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 blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||
|
||||
// The code is a port of Bruce Schneier's C implementation.
|
||||
// See http://www.schneier.com/blowfish.html.
|
||||
|
||||
import "strconv"
|
||||
|
||||
// The Blowfish block size in bytes.
|
||||
const BlockSize = 8
|
||||
|
||||
// A Cipher is an instance of Blowfish encryption using a particular key.
|
||||
type Cipher struct {
|
||||
p [18]uint32
|
||||
s0, s1, s2, s3 [256]uint32
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// NewCipher creates and returns a Cipher.
|
||||
// The key argument should be the Blowfish key, from 1 to 56 bytes.
|
||||
func NewCipher(key []byte) (*Cipher, error) {
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 || k > 56 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
ExpandKey(key, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||
// bytes.
|
||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||
if len(salt) == 0 {
|
||||
return NewCipher(key)
|
||||
}
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
expandKeyWithSalt(key, salt, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the Blowfish block size, 8 bytes.
|
||||
// It is necessary to satisfy the Block interface in the
|
||||
// package "crypto/cipher".
|
||||
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
// Encrypt encrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = encryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = decryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
func initCipher(c *Cipher) {
|
||||
copy(c.p[0:], p[0:])
|
||||
copy(c.s0[0:], s0[0:])
|
||||
copy(c.s1[0:], s1[0:])
|
||||
copy(c.s2[0:], s2[0:])
|
||||
copy(c.s3[0:], s3[0:])
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2010 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.
|
||||
|
||||
// The startup permutation array and substitution boxes.
|
||||
// They are the hexadecimal digits of PI; see:
|
||||
// http://www.schneier.com/code/constants.txt.
|
||||
|
||||
package blowfish
|
||||
|
||||
var s0 = [256]uint32{
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
|
||||
0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
|
||||
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
|
||||
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
|
||||
0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
|
||||
0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
|
||||
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
|
||||
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
|
||||
0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
|
||||
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
|
||||
0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
|
||||
0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
|
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
|
||||
0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
|
||||
0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
|
||||
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
|
||||
0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
|
||||
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
|
||||
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
|
||||
0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
|
||||
0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||
}
|
||||
|
||||
var s1 = [256]uint32{
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
|
||||
0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
|
||||
0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
|
||||
0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
|
||||
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
|
||||
0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
|
||||
0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
|
||||
0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
|
||||
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
|
||||
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
|
||||
0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
|
||||
0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
|
||||
0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
|
||||
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
|
||||
0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
|
||||
0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
|
||||
0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
|
||||
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
|
||||
0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||
}
|
||||
|
||||
var s2 = [256]uint32{
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
|
||||
0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
|
||||
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
|
||||
0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
|
||||
0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
|
||||
0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
|
||||
0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
|
||||
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
|
||||
0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
|
||||
0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
|
||||
0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
|
||||
0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
|
||||
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
|
||||
0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
|
||||
0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
|
||||
0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
|
||||
0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
|
||||
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
|
||||
0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
|
||||
0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
|
||||
0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
|
||||
0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||
}
|
||||
|
||||
var s3 = [256]uint32{
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
|
||||
0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
|
||||
0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
|
||||
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
|
||||
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
|
||||
0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
|
||||
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
|
||||
0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
|
||||
0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
|
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
|
||||
0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
|
||||
0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
|
||||
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
|
||||
0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
|
||||
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
|
||||
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
|
||||
0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
|
||||
0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
|
||||
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
|
||||
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
|
||||
0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
|
||||
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
||||
}
|
||||
|
||||
var p = [18]uint32{
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
|
||||
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
|
||||
}
|
|
@ -0,0 +1,526 @@
|
|||
// Copyright 2010 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 cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common
|
||||
// OpenPGP cipher.
|
||||
package cast5 // import "golang.org/x/crypto/cast5"
|
||||
|
||||
import "errors"
|
||||
|
||||
const BlockSize = 8
|
||||
const KeySize = 16
|
||||
|
||||
type Cipher struct {
|
||||
masking [16]uint32
|
||||
rotate [16]uint8
|
||||
}
|
||||
|
||||
func NewCipher(key []byte) (c *Cipher, err error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("CAST5: keys must be 16 bytes")
|
||||
}
|
||||
|
||||
c = new(Cipher)
|
||||
c.keySchedule(key)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cipher) BlockSize() int {
|
||||
return BlockSize
|
||||
}
|
||||
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||
|
||||
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||
|
||||
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||
|
||||
dst[0] = uint8(r >> 24)
|
||||
dst[1] = uint8(r >> 16)
|
||||
dst[2] = uint8(r >> 8)
|
||||
dst[3] = uint8(r)
|
||||
dst[4] = uint8(l >> 24)
|
||||
dst[5] = uint8(l >> 16)
|
||||
dst[6] = uint8(l >> 8)
|
||||
dst[7] = uint8(l)
|
||||
}
|
||||
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||
|
||||
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||
|
||||
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||
|
||||
dst[0] = uint8(r >> 24)
|
||||
dst[1] = uint8(r >> 16)
|
||||
dst[2] = uint8(r >> 8)
|
||||
dst[3] = uint8(r)
|
||||
dst[4] = uint8(l >> 24)
|
||||
dst[5] = uint8(l >> 16)
|
||||
dst[6] = uint8(l >> 8)
|
||||
dst[7] = uint8(l)
|
||||
}
|
||||
|
||||
type keyScheduleA [4][7]uint8
|
||||
type keyScheduleB [4][5]uint8
|
||||
|
||||
// keyScheduleRound contains the magic values for a round of the key schedule.
|
||||
// The keyScheduleA deals with the lines like:
|
||||
// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]
|
||||
// Conceptually, both x and z are in the same array, x first. The first
|
||||
// element describes which word of this array gets written to and the
|
||||
// second, which word gets read. So, for the line above, it's "4, 0", because
|
||||
// it's writing to the first word of z, which, being after x, is word 4, and
|
||||
// reading from the first word of x: word 0.
|
||||
//
|
||||
// Next are the indexes into the S-boxes. Now the array is treated as bytes. So
|
||||
// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear
|
||||
// that it's z that we're indexing.
|
||||
//
|
||||
// keyScheduleB deals with lines like:
|
||||
// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]
|
||||
// "K1" is ignored because key words are always written in order. So the five
|
||||
// elements are the S-box indexes. They use the same form as in keyScheduleA,
|
||||
// above.
|
||||
|
||||
type keyScheduleRound struct{}
|
||||
type keySchedule []keyScheduleRound
|
||||
|
||||
var schedule = []struct {
|
||||
a keyScheduleA
|
||||
b keyScheduleB
|
||||
}{
|
||||
{
|
||||
keyScheduleA{
|
||||
{4, 0, 0xd, 0xf, 0xc, 0xe, 0x8},
|
||||
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||
},
|
||||
keyScheduleB{
|
||||
{16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2},
|
||||
{16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6},
|
||||
{16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9},
|
||||
{16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||
},
|
||||
keyScheduleB{
|
||||
{3, 2, 0xc, 0xd, 8},
|
||||
{1, 0, 0xe, 0xf, 0xd},
|
||||
{7, 6, 8, 9, 3},
|
||||
{5, 4, 0xa, 0xb, 7},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{4, 0, 0xd, 0xf, 0xc, 0xe, 8},
|
||||
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||
},
|
||||
keyScheduleB{
|
||||
{16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9},
|
||||
{16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc},
|
||||
{16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2},
|
||||
{16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||
},
|
||||
keyScheduleB{
|
||||
{8, 9, 7, 6, 3},
|
||||
{0xa, 0xb, 5, 4, 7},
|
||||
{0xc, 0xd, 3, 2, 8},
|
||||
{0xe, 0xf, 1, 0, 0xd},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (c *Cipher) keySchedule(in []byte) {
|
||||
var t [8]uint32
|
||||
var k [32]uint32
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
j := i * 4
|
||||
t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3])
|
||||
}
|
||||
|
||||
x := []byte{6, 7, 4, 5}
|
||||
ki := 0
|
||||
|
||||
for half := 0; half < 2; half++ {
|
||||
for _, round := range schedule {
|
||||
for j := 0; j < 4; j++ {
|
||||
var a [7]uint8
|
||||
copy(a[:], round.a[j][:])
|
||||
w := t[a[1]]
|
||||
w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff]
|
||||
w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff]
|
||||
w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff]
|
||||
w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff]
|
||||
w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff]
|
||||
t[a[0]] = w
|
||||
}
|
||||
|
||||
for j := 0; j < 4; j++ {
|
||||
var b [5]uint8
|
||||
copy(b[:], round.b[j][:])
|
||||
w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff]
|
||||
w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff]
|
||||
w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff]
|
||||
w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff]
|
||||
w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff]
|
||||
k[ki] = w
|
||||
ki++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
c.masking[i] = k[i]
|
||||
c.rotate[i] = uint8(k[16+i] & 0x1f)
|
||||
}
|
||||
}
|
||||
|
||||
// These are the three 'f' functions. See RFC 2144, section 2.2.
|
||||
func f1(d, m uint32, r uint8) uint32 {
|
||||
t := m + d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f2(d, m uint32, r uint8) uint32 {
|
||||
t := m ^ d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f3(d, m uint32, r uint8) uint32 {
|
||||
t := m - d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
var sBox = [8][256]uint32{
|
||||
{
|
||||
0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
|
||||
0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
|
||||
0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
|
||||
0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
|
||||
0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
|
||||
0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
|
||||
0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
|
||||
0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
|
||||
0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
|
||||
0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
|
||||
0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
|
||||
0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
|
||||
0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
|
||||
0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
|
||||
0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
|
||||
0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
|
||||
0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
|
||||
0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
|
||||
0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
|
||||
0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
|
||||
0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
|
||||
0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
|
||||
0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
|
||||
0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
|
||||
0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
|
||||
0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
|
||||
0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
|
||||
0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
|
||||
0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
|
||||
0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
|
||||
0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
|
||||
0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf,
|
||||
},
|
||||
{
|
||||
0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
|
||||
0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
|
||||
0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
|
||||
0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
|
||||
0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
|
||||
0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
|
||||
0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
|
||||
0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
|
||||
0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
|
||||
0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
|
||||
0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
|
||||
0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
|
||||
0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
|
||||
0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
|
||||
0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
|
||||
0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
|
||||
0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
|
||||
0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
|
||||
0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
|
||||
0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
|
||||
0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
|
||||
0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
|
||||
0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
|
||||
0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
|
||||
0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
|
||||
0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
|
||||
0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
|
||||
0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
|
||||
0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
|
||||
0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
|
||||
0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
|
||||
0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1,
|
||||
},
|
||||
{
|
||||
0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
|
||||
0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
|
||||
0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
|
||||
0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
|
||||
0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
|
||||
0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
|
||||
0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
|
||||
0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
|
||||
0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
|
||||
0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
|
||||
0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
|
||||
0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
|
||||
0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
|
||||
0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
|
||||
0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
|
||||
0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
|
||||
0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
|
||||
0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
|
||||
0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
|
||||
0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
|
||||
0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
|
||||
0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
|
||||
0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
|
||||
0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
|
||||
0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
|
||||
0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
|
||||
0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
|
||||
0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
|
||||
0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
|
||||
0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
|
||||
0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
|
||||
0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783,
|
||||
},
|
||||
{
|
||||
0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
|
||||
0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
|
||||
0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
|
||||
0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
|
||||
0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
|
||||
0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
|
||||
0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
|
||||
0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
|
||||
0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
|
||||
0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
|
||||
0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
|
||||
0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
|
||||
0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
|
||||
0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
|
||||
0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
|
||||
0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
|
||||
0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
|
||||
0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
|
||||
0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
|
||||
0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
|
||||
0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
|
||||
0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
|
||||
0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
|
||||
0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
|
||||
0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
|
||||
0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
|
||||
0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
|
||||
0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
|
||||
0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
|
||||
0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
|
||||
0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
|
||||
0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2,
|
||||
},
|
||||
{
|
||||
0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
|
||||
0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
|
||||
0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
|
||||
0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
|
||||
0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
|
||||
0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
|
||||
0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
|
||||
0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
|
||||
0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
|
||||
0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
|
||||
0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
|
||||
0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
|
||||
0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
|
||||
0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
|
||||
0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
|
||||
0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
|
||||
0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
|
||||
0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
|
||||
0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
|
||||
0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
|
||||
0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
|
||||
0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
|
||||
0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
|
||||
0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
|
||||
0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
|
||||
0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
|
||||
0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
|
||||
0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
|
||||
0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
|
||||
0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
|
||||
0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
|
||||
0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4,
|
||||
},
|
||||
{
|
||||
0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
|
||||
0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
|
||||
0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
|
||||
0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
|
||||
0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
|
||||
0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
|
||||
0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
|
||||
0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
|
||||
0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
|
||||
0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
|
||||
0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
|
||||
0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
|
||||
0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
|
||||
0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
|
||||
0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
|
||||
0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
|
||||
0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
|
||||
0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
|
||||
0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
|
||||
0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
|
||||
0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
|
||||
0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
|
||||
0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
|
||||
0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
|
||||
0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
|
||||
0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
|
||||
0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
|
||||
0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
|
||||
0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
|
||||
0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
|
||||
0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
|
||||
0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f,
|
||||
},
|
||||
{
|
||||
0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
|
||||
0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
|
||||
0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
|
||||
0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
|
||||
0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
|
||||
0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
|
||||
0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
|
||||
0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
|
||||
0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
|
||||
0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
|
||||
0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
|
||||
0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
|
||||
0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
|
||||
0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
|
||||
0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
|
||||
0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
|
||||
0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
|
||||
0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
|
||||
0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
|
||||
0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
|
||||
0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
|
||||
0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
|
||||
0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
|
||||
0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
|
||||
0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
|
||||
0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
|
||||
0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
|
||||
0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
|
||||
0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
|
||||
0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
|
||||
0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
|
||||
0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3,
|
||||
},
|
||||
{
|
||||
0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
|
||||
0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
|
||||
0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
|
||||
0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
|
||||
0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
|
||||
0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
|
||||
0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
|
||||
0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
|
||||
0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
|
||||
0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
|
||||
0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
|
||||
0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
|
||||
0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
|
||||
0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
|
||||
0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
|
||||
0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
|
||||
0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
|
||||
0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
|
||||
0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
|
||||
0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
|
||||
0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
|
||||
0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
|
||||
0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
|
||||
0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
|
||||
0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
|
||||
0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
|
||||
0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
|
||||
0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
|
||||
0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
|
||||
0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
|
||||
0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
|
||||
0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2012 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 salsa20 implements the Salsa20 stream cipher as specified in http://cr.yp.to/snuffle/spec.pdf.
|
||||
|
||||
Salsa20 differs from many other stream ciphers in that it is message orientated
|
||||
rather than byte orientated. Keystream blocks are not preserved between calls,
|
||||
therefore each side must encrypt/decrypt data with the same segmentation.
|
||||
|
||||
Another aspect of this difference is that part of the counter is exposed as
|
||||
an nonce in each call. Encrypting two different messages with the same (key,
|
||||
nonce) pair leads to trivial plaintext recovery. This is analogous to
|
||||
encrypting two different messages with the same key with a traditional stream
|
||||
cipher.
|
||||
|
||||
This package also implements XSalsa20: a version of Salsa20 with a 24-byte
|
||||
nonce as specified in http://cr.yp.to/snuffle/xsalsa-20081128.pdf. Simply
|
||||
passing a 24-byte slice as the nonce triggers XSalsa20.
|
||||
*/
|
||||
package salsa20 // import "golang.org/x/crypto/salsa20"
|
||||
|
||||
// TODO(agl): implement XORKeyStream12 and XORKeyStream8 - the reduced round variants of Salsa20.
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/salsa20/salsa"
|
||||
)
|
||||
|
||||
// XORKeyStream crypts bytes from in to out using the given key and nonce. In
|
||||
// and out may be the same slice but otherwise should not overlap. Nonce must
|
||||
// be either 8 or 24 bytes long.
|
||||
func XORKeyStream(out, in []byte, nonce []byte, key *[32]byte) {
|
||||
if len(out) < len(in) {
|
||||
in = in[:len(out)]
|
||||
}
|
||||
|
||||
var subNonce [16]byte
|
||||
|
||||
if len(nonce) == 24 {
|
||||
var subKey [32]byte
|
||||
var hNonce [16]byte
|
||||
copy(hNonce[:], nonce[:16])
|
||||
salsa.HSalsa20(&subKey, &hNonce, key, &salsa.Sigma)
|
||||
copy(subNonce[:], nonce[16:])
|
||||
key = &subKey
|
||||
} else if len(nonce) == 8 {
|
||||
copy(subNonce[:], nonce[:])
|
||||
} else {
|
||||
panic("salsa20: nonce must be 8 or 24 bytes")
|
||||
}
|
||||
|
||||
salsa.XORKeyStream(out, in, &subNonce, key)
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2015 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 tea implements the TEA algorithm, as defined in Needham and
|
||||
// Wheeler's 1994 technical report, “TEA, a Tiny Encryption Algorithm”. See
|
||||
// http://www.cix.co.uk/~klockstone/tea.pdf for details.
|
||||
|
||||
package tea
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// BlockSize is the size of a TEA block, in bytes.
|
||||
BlockSize = 8
|
||||
|
||||
// KeySize is the size of a TEA key, in bytes.
|
||||
KeySize = 16
|
||||
|
||||
// delta is the TEA key schedule constant.
|
||||
delta = 0x9e3779b9
|
||||
|
||||
// numRounds is the standard number of rounds in TEA.
|
||||
numRounds = 64
|
||||
)
|
||||
|
||||
// tea is an instance of the TEA cipher with a particular key.
|
||||
type tea struct {
|
||||
key [16]byte
|
||||
rounds int
|
||||
}
|
||||
|
||||
// NewCipher returns an instance of the TEA cipher with the standard number of
|
||||
// rounds. The key argument must be 16 bytes long.
|
||||
func NewCipher(key []byte) (cipher.Block, error) {
|
||||
return NewCipherWithRounds(key, numRounds)
|
||||
}
|
||||
|
||||
// NewCipherWithRounds returns an instance of the TEA cipher with a given
|
||||
// number of rounds, which must be even. The key argument must be 16 bytes
|
||||
// long.
|
||||
func NewCipherWithRounds(key []byte, rounds int) (cipher.Block, error) {
|
||||
if len(key) != 16 {
|
||||
return nil, errors.New("tea: incorrect key size")
|
||||
}
|
||||
|
||||
if rounds&1 != 0 {
|
||||
return nil, errors.New("tea: odd number of rounds specified")
|
||||
}
|
||||
|
||||
c := &tea{
|
||||
rounds: rounds,
|
||||
}
|
||||
copy(c.key[:], key)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the TEA block size, which is eight bytes. It is necessary
|
||||
// to satisfy the Block interface in the package "crypto/cipher".
|
||||
func (*tea) BlockSize() int {
|
||||
return BlockSize
|
||||
}
|
||||
|
||||
// Encrypt encrypts the 8 byte buffer src using the key in t and stores the
|
||||
// result in dst. Note that for amounts of data larger than a block, it is not
|
||||
// safe to just call Encrypt on successive blocks; instead, use an encryption
|
||||
// mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (t *tea) Encrypt(dst, src []byte) {
|
||||
e := binary.BigEndian
|
||||
v0, v1 := e.Uint32(src), e.Uint32(src[4:])
|
||||
k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
|
||||
|
||||
sum := uint32(0)
|
||||
delta := uint32(delta)
|
||||
|
||||
for i := 0; i < t.rounds/2; i++ {
|
||||
sum += delta
|
||||
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
|
||||
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
|
||||
}
|
||||
|
||||
e.PutUint32(dst, v0)
|
||||
e.PutUint32(dst[4:], v1)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the 8 byte buffer src using the key in t and stores the
|
||||
// result in dst.
|
||||
func (t *tea) Decrypt(dst, src []byte) {
|
||||
e := binary.BigEndian
|
||||
v0, v1 := e.Uint32(src), e.Uint32(src[4:])
|
||||
k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
|
||||
|
||||
delta := uint32(delta)
|
||||
sum := delta * uint32(t.rounds/2) // in general, sum = delta * n
|
||||
|
||||
for i := 0; i < t.rounds/2; i++ {
|
||||
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
|
||||
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
|
||||
sum -= delta
|
||||
}
|
||||
|
||||
e.PutUint32(dst, v0)
|
||||
e.PutUint32(dst[4:], v1)
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
// Copyright 2011 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 twofish implements Bruce Schneier's Twofish encryption algorithm.
|
||||
package twofish // import "golang.org/x/crypto/twofish"
|
||||
|
||||
// Twofish is defined in http://www.schneier.com/paper-twofish-paper.pdf [TWOFISH]
|
||||
|
||||
// This code is a port of the LibTom C implementation.
|
||||
// See http://libtom.org/?page=features&newsitems=5&whatfile=crypt.
|
||||
// LibTomCrypt is free for all purposes under the public domain.
|
||||
// It was heavily inspired by the go blowfish package.
|
||||
|
||||
import "strconv"
|
||||
|
||||
// BlockSize is the constant block size of Twofish.
|
||||
const BlockSize = 16
|
||||
|
||||
const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2
|
||||
const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3
|
||||
|
||||
// A Cipher is an instance of Twofish encryption using a particular key.
|
||||
type Cipher struct {
|
||||
s [4][256]uint32
|
||||
k [40]uint32
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "crypto/twofish: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// NewCipher creates and returns a Cipher.
|
||||
// The key argument should be the Twofish key, 16, 24 or 32 bytes.
|
||||
func NewCipher(key []byte) (*Cipher, error) {
|
||||
keylen := len(key)
|
||||
|
||||
if keylen != 16 && keylen != 24 && keylen != 32 {
|
||||
return nil, KeySizeError(keylen)
|
||||
}
|
||||
|
||||
// k is the number of 64 bit words in key
|
||||
k := keylen / 8
|
||||
|
||||
// Create the S[..] words
|
||||
var S [4 * 4]byte
|
||||
for i := 0; i < k; i++ {
|
||||
// Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7]
|
||||
for j, rsRow := range rs {
|
||||
for k, rsVal := range rsRow {
|
||||
S[4*i+j] ^= gfMult(key[8*i+k], rsVal, rsPolynomial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate subkeys
|
||||
c := new(Cipher)
|
||||
var tmp [4]byte
|
||||
for i := byte(0); i < 20; i++ {
|
||||
// A = h(p * 2x, Me)
|
||||
for j := range tmp {
|
||||
tmp[j] = 2 * i
|
||||
}
|
||||
A := h(tmp[:], key, 0)
|
||||
|
||||
// B = rolc(h(p * (2x + 1), Mo), 8)
|
||||
for j := range tmp {
|
||||
tmp[j] = 2*i + 1
|
||||
}
|
||||
B := h(tmp[:], key, 1)
|
||||
B = rol(B, 8)
|
||||
|
||||
c.k[2*i] = A + B
|
||||
|
||||
// K[2i+1] = (A + 2B) <<< 9
|
||||
c.k[2*i+1] = rol(2*B+A, 9)
|
||||
}
|
||||
|
||||
// Calculate sboxes
|
||||
switch k {
|
||||
case 2:
|
||||
for i := range c.s[0] {
|
||||
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0)
|
||||
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1)
|
||||
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2)
|
||||
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3)
|
||||
}
|
||||
case 3:
|
||||
for i := range c.s[0] {
|
||||
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0)
|
||||
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1)
|
||||
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2)
|
||||
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3)
|
||||
}
|
||||
default:
|
||||
for i := range c.s[0] {
|
||||
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0)
|
||||
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1)
|
||||
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2)
|
||||
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3)
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the Twofish block size, 16 bytes.
|
||||
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
// store32l stores src in dst in little-endian form.
|
||||
func store32l(dst []byte, src uint32) {
|
||||
dst[0] = byte(src)
|
||||
dst[1] = byte(src >> 8)
|
||||
dst[2] = byte(src >> 16)
|
||||
dst[3] = byte(src >> 24)
|
||||
return
|
||||
}
|
||||
|
||||
// load32l reads a little-endian uint32 from src.
|
||||
func load32l(src []byte) uint32 {
|
||||
return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
|
||||
}
|
||||
|
||||
// rol returns x after a left circular rotation of y bits.
|
||||
func rol(x, y uint32) uint32 {
|
||||
return (x << (y & 31)) | (x >> (32 - (y & 31)))
|
||||
}
|
||||
|
||||
// ror returns x after a right circular rotation of y bits.
|
||||
func ror(x, y uint32) uint32 {
|
||||
return (x >> (y & 31)) | (x << (32 - (y & 31)))
|
||||
}
|
||||
|
||||
// The RS matrix. See [TWOFISH] 4.3
|
||||
var rs = [4][8]byte{
|
||||
{0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E},
|
||||
{0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5},
|
||||
{0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19},
|
||||
{0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03},
|
||||
}
|
||||
|
||||
// sbox tables
|
||||
var sbox = [2][256]byte{
|
||||
{
|
||||
0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38,
|
||||
0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48,
|
||||
0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82,
|
||||
0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61,
|
||||
0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1,
|
||||
0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7,
|
||||
0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71,
|
||||
0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7,
|
||||
0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90,
|
||||
0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef,
|
||||
0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64,
|
||||
0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a,
|
||||
0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d,
|
||||
0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
|
||||
0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4,
|
||||
0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0,
|
||||
},
|
||||
{
|
||||
0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b,
|
||||
0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f,
|
||||
0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5,
|
||||
0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51,
|
||||
0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c,
|
||||
0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8,
|
||||
0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2,
|
||||
0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17,
|
||||
0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e,
|
||||
0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9,
|
||||
0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48,
|
||||
0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64,
|
||||
0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69,
|
||||
0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc,
|
||||
0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9,
|
||||
0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91,
|
||||
},
|
||||
}
|
||||
|
||||
// gfMult returns a·b in GF(2^8)/p
|
||||
func gfMult(a, b byte, p uint32) byte {
|
||||
B := [2]uint32{0, uint32(b)}
|
||||
P := [2]uint32{0, p}
|
||||
var result uint32
|
||||
|
||||
// branchless GF multiplier
|
||||
for i := 0; i < 7; i++ {
|
||||
result ^= B[a&1]
|
||||
a >>= 1
|
||||
B[1] = P[B[1]>>7] ^ (B[1] << 1)
|
||||
}
|
||||
result ^= B[a&1]
|
||||
return byte(result)
|
||||
}
|
||||
|
||||
// mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS · [x0]
|
||||
func mdsColumnMult(in byte, col int) uint32 {
|
||||
mul01 := in
|
||||
mul5B := gfMult(in, 0x5B, mdsPolynomial)
|
||||
mulEF := gfMult(in, 0xEF, mdsPolynomial)
|
||||
|
||||
switch col {
|
||||
case 0:
|
||||
return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24
|
||||
case 1:
|
||||
return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24
|
||||
case 2:
|
||||
return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24
|
||||
case 3:
|
||||
return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// h implements the S-box generation function. See [TWOFISH] 4.3.5
|
||||
func h(in, key []byte, offset int) uint32 {
|
||||
var y [4]byte
|
||||
for x := range y {
|
||||
y[x] = in[x]
|
||||
}
|
||||
switch len(key) / 8 {
|
||||
case 4:
|
||||
y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0]
|
||||
y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1]
|
||||
y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2]
|
||||
y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3]
|
||||
fallthrough
|
||||
case 3:
|
||||
y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0]
|
||||
y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1]
|
||||
y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2]
|
||||
y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3]
|
||||
fallthrough
|
||||
case 2:
|
||||
y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]]
|
||||
y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]]
|
||||
y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]]
|
||||
y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]]
|
||||
}
|
||||
// [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3]
|
||||
var mdsMult uint32
|
||||
for i := range y {
|
||||
mdsMult ^= mdsColumnMult(y[i], i)
|
||||
}
|
||||
return mdsMult
|
||||
}
|
||||
|
||||
// Encrypt encrypts a 16-byte block from src to dst, which may overlap.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
S1 := c.s[0]
|
||||
S2 := c.s[1]
|
||||
S3 := c.s[2]
|
||||
S4 := c.s[3]
|
||||
|
||||
// Load input
|
||||
ia := load32l(src[0:4])
|
||||
ib := load32l(src[4:8])
|
||||
ic := load32l(src[8:12])
|
||||
id := load32l(src[12:16])
|
||||
|
||||
// Pre-whitening
|
||||
ia ^= c.k[0]
|
||||
ib ^= c.k[1]
|
||||
ic ^= c.k[2]
|
||||
id ^= c.k[3]
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
k := c.k[8+i*4 : 12+i*4]
|
||||
t2 := S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)]
|
||||
t1 := S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2
|
||||
ic = ror(ic^(t1+k[0]), 1)
|
||||
id = rol(id, 1) ^ (t2 + t1 + k[1])
|
||||
|
||||
t2 = S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)]
|
||||
t1 = S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2
|
||||
ia = ror(ia^(t1+k[2]), 1)
|
||||
ib = rol(ib, 1) ^ (t2 + t1 + k[3])
|
||||
}
|
||||
|
||||
// Output with "undo last swap"
|
||||
ta := ic ^ c.k[4]
|
||||
tb := id ^ c.k[5]
|
||||
tc := ia ^ c.k[6]
|
||||
td := ib ^ c.k[7]
|
||||
|
||||
store32l(dst[0:4], ta)
|
||||
store32l(dst[4:8], tb)
|
||||
store32l(dst[8:12], tc)
|
||||
store32l(dst[12:16], td)
|
||||
}
|
||||
|
||||
// Decrypt decrypts a 16-byte block from src to dst, which may overlap.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
S1 := c.s[0]
|
||||
S2 := c.s[1]
|
||||
S3 := c.s[2]
|
||||
S4 := c.s[3]
|
||||
|
||||
// Load input
|
||||
ta := load32l(src[0:4])
|
||||
tb := load32l(src[4:8])
|
||||
tc := load32l(src[8:12])
|
||||
td := load32l(src[12:16])
|
||||
|
||||
// Undo undo final swap
|
||||
ia := tc ^ c.k[6]
|
||||
ib := td ^ c.k[7]
|
||||
ic := ta ^ c.k[4]
|
||||
id := tb ^ c.k[5]
|
||||
|
||||
for i := 8; i > 0; i-- {
|
||||
k := c.k[4+i*4 : 8+i*4]
|
||||
t2 := S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)]
|
||||
t1 := S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2
|
||||
ia = rol(ia, 1) ^ (t1 + k[2])
|
||||
ib = ror(ib^(t2+t1+k[3]), 1)
|
||||
|
||||
t2 = S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)]
|
||||
t1 = S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2
|
||||
ic = rol(ic, 1) ^ (t1 + k[0])
|
||||
id = ror(id^(t2+t1+k[1]), 1)
|
||||
}
|
||||
|
||||
// Undo pre-whitening
|
||||
ia ^= c.k[0]
|
||||
ib ^= c.k[1]
|
||||
ic ^= c.k[2]
|
||||
id ^= c.k[3]
|
||||
|
||||
store32l(dst[0:4], ia)
|
||||
store32l(dst[4:8], ib)
|
||||
store32l(dst[8:12], ic)
|
||||
store32l(dst[12:16], id)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2009 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.
|
||||
|
||||
/*
|
||||
Implementation adapted from Needham and Wheeler's paper:
|
||||
http://www.cix.co.uk/~klockstone/xtea.pdf
|
||||
|
||||
A precalculated look up table is used during encryption/decryption for values that are based purely on the key.
|
||||
*/
|
||||
|
||||
package xtea
|
||||
|
||||
// XTEA is based on 64 rounds.
|
||||
const numRounds = 64
|
||||
|
||||
// blockToUint32 reads an 8 byte slice into two uint32s.
|
||||
// The block is treated as big endian.
|
||||
func blockToUint32(src []byte) (uint32, uint32) {
|
||||
r0 := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r1 := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// uint32ToBlock writes two uint32s into an 8 byte data block.
|
||||
// Values are written as big endian.
|
||||
func uint32ToBlock(v0, v1 uint32, dst []byte) {
|
||||
dst[0] = byte(v0 >> 24)
|
||||
dst[1] = byte(v0 >> 16)
|
||||
dst[2] = byte(v0 >> 8)
|
||||
dst[3] = byte(v0)
|
||||
dst[4] = byte(v1 >> 24)
|
||||
dst[5] = byte(v1 >> 16)
|
||||
dst[6] = byte(v1 >> 8)
|
||||
dst[7] = byte(v1 >> 0)
|
||||
}
|
||||
|
||||
// encryptBlock encrypts a single 8 byte block using XTEA.
|
||||
func encryptBlock(c *Cipher, dst, src []byte) {
|
||||
v0, v1 := blockToUint32(src)
|
||||
|
||||
// Two rounds of XTEA applied per loop
|
||||
for i := 0; i < numRounds; {
|
||||
v0 += ((v1<<4 ^ v1>>5) + v1) ^ c.table[i]
|
||||
i++
|
||||
v1 += ((v0<<4 ^ v0>>5) + v0) ^ c.table[i]
|
||||
i++
|
||||
}
|
||||
|
||||
uint32ToBlock(v0, v1, dst)
|
||||
}
|
||||
|
||||
// decryptBlock decrypt a single 8 byte block using XTEA.
|
||||
func decryptBlock(c *Cipher, dst, src []byte) {
|
||||
v0, v1 := blockToUint32(src)
|
||||
|
||||
// Two rounds of XTEA applied per loop
|
||||
for i := numRounds; i > 0; {
|
||||
i--
|
||||
v1 -= ((v0<<4 ^ v0>>5) + v0) ^ c.table[i]
|
||||
i--
|
||||
v0 -= ((v1<<4 ^ v1>>5) + v1) ^ c.table[i]
|
||||
}
|
||||
|
||||
uint32ToBlock(v0, v1, dst)
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2009 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 xtea implements XTEA encryption, as defined in Needham and Wheeler's
|
||||
// 1997 technical report, "Tea extensions."
|
||||
package xtea // import "golang.org/x/crypto/xtea"
|
||||
|
||||
// For details, see http://www.cix.co.uk/~klockstone/xtea.pdf
|
||||
|
||||
import "strconv"
|
||||
|
||||
// The XTEA block size in bytes.
|
||||
const BlockSize = 8
|
||||
|
||||
// A Cipher is an instance of an XTEA cipher using a particular key.
|
||||
// table contains a series of precalculated values that are used each round.
|
||||
type Cipher struct {
|
||||
table [64]uint32
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "crypto/xtea: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// NewCipher creates and returns a new Cipher.
|
||||
// The key argument should be the XTEA key.
|
||||
// XTEA only supports 128 bit (16 byte) keys.
|
||||
func NewCipher(key []byte) (*Cipher, error) {
|
||||
k := len(key)
|
||||
switch k {
|
||||
default:
|
||||
return nil, KeySizeError(k)
|
||||
case 16:
|
||||
break
|
||||
}
|
||||
|
||||
c := new(Cipher)
|
||||
initCipher(c, key)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the XTEA block size, 8 bytes.
|
||||
// It is necessary to satisfy the Block interface in the
|
||||
// package "crypto/cipher".
|
||||
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
// Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) }
|
||||
|
||||
// Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) }
|
||||
|
||||
// initCipher initializes the cipher context by creating a look up table
|
||||
// of precalculated values that are based on the key.
|
||||
func initCipher(c *Cipher, key []byte) {
|
||||
// Load the key into four uint32s
|
||||
var k [4]uint32
|
||||
for i := 0; i < len(k); i++ {
|
||||
j := i << 2 // Multiply by 4
|
||||
k[i] = uint32(key[j+0])<<24 | uint32(key[j+1])<<16 | uint32(key[j+2])<<8 | uint32(key[j+3])
|
||||
}
|
||||
|
||||
// Precalculate the table
|
||||
const delta = 0x9E3779B9
|
||||
var sum uint32 = 0
|
||||
|
||||
// Two rounds of XTEA applied per loop
|
||||
for i := 0; i < numRounds; {
|
||||
c.table[i] = sum + k[sum&3]
|
||||
i++
|
||||
sum += delta
|
||||
c.table[i] = sum + k[(sum>>11)&3]
|
||||
i++
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 = 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 = 4
|
||||
// ExtPayloadOffset returns the offset of the packet payload, or
|
||||
// the first protocol header that the kernel does not know how to
|
||||
// parse.
|
||||
ExtPayloadOffset = 52
|
||||
// ExtInterfaceIndex returns the index of the interface on which
|
||||
// the packet was received.
|
||||
ExtInterfaceIndex = 8
|
||||
// ExtNetlinkAttr returns the netlink attribute of type X at
|
||||
// offset A.
|
||||
ExtNetlinkAttr = 12
|
||||
// ExtNetlinkAttrNested returns the nested netlink attribute of
|
||||
// type X at offset A.
|
||||
ExtNetlinkAttrNested = 16
|
||||
// ExtMark returns the packet's mark value.
|
||||
ExtMark = 20
|
||||
// ExtQueue returns the packet's assigned hardware queue.
|
||||
ExtQueue = 24
|
||||
// ExtLinkLayerType returns the packet's hardware address type
|
||||
// (e.g. Ethernet, Infiniband).
|
||||
ExtLinkLayerType = 28
|
||||
// ExtRXHash returns the packets receive hash.
|
||||
//
|
||||
// TODO: figure out what this rxhash actually is.
|
||||
ExtRXHash = 32
|
||||
// ExtCPUID returns the ID of the CPU processing the current
|
||||
// packet.
|
||||
ExtCPUID = 36
|
||||
// ExtVLANTag returns the packet's VLAN tag.
|
||||
ExtVLANTag = 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 = 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 = 60
|
||||
// ExtRand returns a uniformly random uint32.
|
||||
ExtRand = 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
|
||||
)
|
|
@ -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"
|
|
@ -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 {
|
||||
case opClsReturn | opRetSrcA:
|
||||
return RetA{}
|
||||
case opClsReturn | opRetSrcConstant:
|
||||
return RetConstant{Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsMisc:
|
||||
switch ri.Op {
|
||||
case opClsMisc | opMiscTAX:
|
||||
return TAX{}
|
||||
case opClsMisc | opMiscTXA:
|
||||
return TXA{}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable") // switch is exhaustive on the bit pattern
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConstant loads Val into register Dst.
|
||||
type LoadConstant struct {
|
||||
Dst Register
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadConstant) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadConstant) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld #%d", a.Val)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadScratch loads scratch[N] into register Dst.
|
||||
type LoadScratch struct {
|
||||
Dst Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadScratch) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAbsolute loads packet[Off:Off+Size] as an integer value into
|
||||
// register A.
|
||||
type LoadAbsolute struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadAbsolute) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadAbsolute) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [%d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [%d]", a.Off)
|
||||
case 4: // word
|
||||
if a.Off > extOffset+0xffffffff {
|
||||
return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
|
||||
}
|
||||
return fmt.Sprintf("ld [%d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
|
||||
// into register A.
|
||||
type LoadIndirect struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadIndirect) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadIndirect) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [x + %d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [x + %d]", a.Off)
|
||||
case 4: // word
|
||||
return fmt.Sprintf("ld [x + %d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
|
||||
// by 4 and stores the result in register X.
|
||||
//
|
||||
// This instruction is mainly useful to load into X the length of an
|
||||
// IPv4 packet header in a single instruction, rather than have to do
|
||||
// the arithmetic on the header's first byte by hand.
|
||||
type LoadMemShift struct {
|
||||
Off uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadMemShift) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadMemShift) String() string {
|
||||
return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
|
||||
}
|
||||
|
||||
// LoadExtension invokes a linux-specific extension and stores the
|
||||
// result in register A.
|
||||
type LoadExtension struct {
|
||||
Num Extension
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadExtension) Assemble() (RawInstruction, error) {
|
||||
if a.Num == ExtLen {
|
||||
return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
|
||||
}
|
||||
return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a LoadExtension) String() string {
|
||||
switch a.Num {
|
||||
case ExtLen:
|
||||
return "ld #len"
|
||||
case ExtProto:
|
||||
return "ld #proto"
|
||||
case ExtType:
|
||||
return "ld #type"
|
||||
case ExtPayloadOffset:
|
||||
return "ld #poff"
|
||||
case ExtInterfaceIndex:
|
||||
return "ld #ifidx"
|
||||
case ExtNetlinkAttr:
|
||||
return "ld #nla"
|
||||
case ExtNetlinkAttrNested:
|
||||
return "ld #nlan"
|
||||
case ExtMark:
|
||||
return "ld #mark"
|
||||
case ExtQueue:
|
||||
return "ld #queue"
|
||||
case ExtLinkLayerType:
|
||||
return "ld #hatype"
|
||||
case ExtRXHash:
|
||||
return "ld #rxhash"
|
||||
case ExtCPUID:
|
||||
return "ld #cpu"
|
||||
case ExtVLANTag:
|
||||
return "ld #vlan_tci"
|
||||
case ExtVLANTagPresent:
|
||||
return "ld #vlan_avail"
|
||||
case ExtVLANProto:
|
||||
return "ld #vlan_tpid"
|
||||
case ExtRand:
|
||||
return "ld #rand"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// StoreScratch stores register Src into scratch[N].
|
||||
type StoreScratch struct {
|
||||
Src Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a StoreScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
var op uint16
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
op = opClsStoreA
|
||||
case RegX:
|
||||
op = opClsStoreX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
|
||||
}
|
||||
|
||||
return RawInstruction{
|
||||
Op: op,
|
||||
K: uint32(a.N),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a StoreScratch) String() string {
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
return fmt.Sprintf("st M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("stx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpConstant executes A = A <Op> Val.
|
||||
type ALUOpConstant struct {
|
||||
Op ALUOp
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | opALUSrcConstant | uint16(a.Op),
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a ALUOpConstant) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return fmt.Sprintf("add #%d", a.Val)
|
||||
case ALUOpSub:
|
||||
return fmt.Sprintf("sub #%d", a.Val)
|
||||
case ALUOpMul:
|
||||
return fmt.Sprintf("mul #%d", a.Val)
|
||||
case ALUOpDiv:
|
||||
return fmt.Sprintf("div #%d", a.Val)
|
||||
case ALUOpMod:
|
||||
return fmt.Sprintf("mod #%d", a.Val)
|
||||
case ALUOpAnd:
|
||||
return fmt.Sprintf("and #%d", a.Val)
|
||||
case ALUOpOr:
|
||||
return fmt.Sprintf("or #%d", a.Val)
|
||||
case ALUOpXor:
|
||||
return fmt.Sprintf("xor #%d", a.Val)
|
||||
case ALUOpShiftLeft:
|
||||
return fmt.Sprintf("lsh #%d", a.Val)
|
||||
case ALUOpShiftRight:
|
||||
return fmt.Sprintf("rsh #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpX executes A = A <Op> X
|
||||
type ALUOpX struct {
|
||||
Op ALUOp
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | opALUSrcX | uint16(a.Op),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a ALUOpX) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return "add x"
|
||||
case ALUOpSub:
|
||||
return "sub x"
|
||||
case ALUOpMul:
|
||||
return "mul x"
|
||||
case ALUOpDiv:
|
||||
return "div x"
|
||||
case ALUOpMod:
|
||||
return "mod x"
|
||||
case ALUOpAnd:
|
||||
return "and x"
|
||||
case ALUOpOr:
|
||||
return "or x"
|
||||
case ALUOpXor:
|
||||
return "xor x"
|
||||
case ALUOpShiftLeft:
|
||||
return "lsh x"
|
||||
case ALUOpShiftRight:
|
||||
return "rsh x"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// NegateA executes A = -A.
|
||||
type NegateA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a NegateA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(aluOpNeg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a NegateA) String() string {
|
||||
return fmt.Sprintf("neg")
|
||||
}
|
||||
|
||||
// Jump skips the following Skip instructions in the program.
|
||||
type Jump struct {
|
||||
Skip uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a Jump) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsJump | opJumpAlways,
|
||||
K: a.Skip,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a Jump) String() string {
|
||||
return fmt.Sprintf("ja %d", a.Skip)
|
||||
}
|
||||
|
||||
// JumpIf skips the following Skip instructions in the program if A
|
||||
// <Cond> Val is true.
|
||||
type JumpIf struct {
|
||||
Cond JumpTest
|
||||
Val uint32
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
var (
|
||||
cond uint16
|
||||
flip bool
|
||||
)
|
||||
switch a.Cond {
|
||||
case JumpEqual:
|
||||
cond = opJumpEqual
|
||||
case JumpNotEqual:
|
||||
cond, flip = opJumpEqual, true
|
||||
case JumpGreaterThan:
|
||||
cond = opJumpGT
|
||||
case JumpLessThan:
|
||||
cond, flip = opJumpGE, true
|
||||
case JumpGreaterOrEqual:
|
||||
cond = opJumpGE
|
||||
case JumpLessOrEqual:
|
||||
cond, flip = opJumpGT, true
|
||||
case JumpBitsSet:
|
||||
cond = opJumpSet
|
||||
case JumpBitsNotSet:
|
||||
cond, flip = opJumpSet, true
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", a.Cond)
|
||||
}
|
||||
jt, jf := a.SkipTrue, a.SkipFalse
|
||||
if flip {
|
||||
jt, jf = jf, jt
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: opClsJump | cond,
|
||||
Jt: jt,
|
||||
Jf: jf,
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
switch a.Cond {
|
||||
// K == A
|
||||
case JumpEqual:
|
||||
return conditionalJump(a, "jeq", "jneq")
|
||||
// K != A
|
||||
case JumpNotEqual:
|
||||
return fmt.Sprintf("jneq #%d,%d", a.Val, a.SkipTrue)
|
||||
// K > A
|
||||
case JumpGreaterThan:
|
||||
return conditionalJump(a, "jgt", "jle")
|
||||
// K < A
|
||||
case JumpLessThan:
|
||||
return fmt.Sprintf("jlt #%d,%d", a.Val, a.SkipTrue)
|
||||
// K >= A
|
||||
case JumpGreaterOrEqual:
|
||||
return conditionalJump(a, "jge", "jlt")
|
||||
// K <= A
|
||||
case JumpLessOrEqual:
|
||||
return fmt.Sprintf("jle #%d,%d", a.Val, a.SkipTrue)
|
||||
// K & A != 0
|
||||
case JumpBitsSet:
|
||||
if a.SkipFalse > 0 {
|
||||
return fmt.Sprintf("jset #%d,%d,%d", a.Val, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
return fmt.Sprintf("jset #%d,%d", a.Val, a.SkipTrue)
|
||||
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
|
||||
case JumpBitsNotSet:
|
||||
return JumpIf{Cond: JumpBitsSet, SkipTrue: a.SkipFalse, SkipFalse: a.SkipTrue, Val: a.Val}.String()
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
func conditionalJump(inst JumpIf, positiveJump, negativeJump string) string {
|
||||
if inst.SkipTrue > 0 {
|
||||
if inst.SkipFalse > 0 {
|
||||
return fmt.Sprintf("%s #%d,%d,%d", positiveJump, inst.Val, inst.SkipTrue, inst.SkipFalse)
|
||||
}
|
||||
return fmt.Sprintf("%s #%d,%d", positiveJump, inst.Val, inst.SkipTrue)
|
||||
}
|
||||
return fmt.Sprintf("%s #%d,%d", negativeJump, inst.Val, inst.SkipFalse)
|
||||
}
|
||||
|
||||
// RetA exits the BPF program, returning the value of register A.
|
||||
type RetA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a RetA) String() string {
|
||||
return fmt.Sprintf("ret a")
|
||||
}
|
||||
|
||||
// RetConstant exits the BPF program, returning a constant value.
|
||||
type RetConstant struct {
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcConstant,
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a RetConstant) String() string {
|
||||
return fmt.Sprintf("ret #%d", a.Val)
|
||||
}
|
||||
|
||||
// TXA copies the value of register X to register A.
|
||||
type TXA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TXA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTXA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a TXA) String() string {
|
||||
return fmt.Sprintf("txa")
|
||||
}
|
||||
|
||||
// TAX copies the value of register A to register X.
|
||||
type TAX struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TAX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTAX,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
func (a TAX) String() string {
|
||||
return fmt.Sprintf("tax")
|
||||
}
|
||||
|
||||
func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
|
||||
var (
|
||||
cls uint16
|
||||
sz uint16
|
||||
)
|
||||
switch dst {
|
||||
case RegA:
|
||||
cls = opClsLoadA
|
||||
case RegX:
|
||||
cls = opClsLoadX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
|
||||
}
|
||||
switch loadSize {
|
||||
case 1:
|
||||
sz = opLoadWidth1
|
||||
case 2:
|
||||
sz = opLoadWidth2
|
||||
case 4:
|
||||
sz = opLoadWidth4
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: cls | sz | mode,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A VM is an emulated BPF virtual machine.
|
||||
type VM struct {
|
||||
filter []Instruction
|
||||
}
|
||||
|
||||
// NewVM returns a new VM using the input BPF program.
|
||||
func NewVM(filter []Instruction) (*VM, error) {
|
||||
if len(filter) == 0 {
|
||||
return nil, errors.New("one or more Instructions must be specified")
|
||||
}
|
||||
|
||||
for i, ins := range filter {
|
||||
check := len(filter) - (i + 1)
|
||||
switch ins := ins.(type) {
|
||||
// Check for out-of-bounds jumps in instructions
|
||||
case Jump:
|
||||
if check <= int(ins.Skip) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
|
||||
}
|
||||
case JumpIf:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
// Check for division or modulus by zero
|
||||
case ALUOpConstant:
|
||||
if ins.Val != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return nil, errors.New("cannot divide by zero using ALUOpConstant")
|
||||
}
|
||||
// Check for unknown extensions
|
||||
case LoadExtension:
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
default:
|
||||
return nil, fmt.Errorf("extension %d not implemented", ins.Num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure last instruction is a return instruction
|
||||
switch filter[len(filter)-1].(type) {
|
||||
case RetA, RetConstant:
|
||||
default:
|
||||
return nil, errors.New("BPF program must end with RetA or RetConstant")
|
||||
}
|
||||
|
||||
// Though our VM works using disassembled instructions, we
|
||||
// attempt to assemble the input filter anyway to ensure it is compatible
|
||||
// with an operating system VM.
|
||||
_, err := Assemble(filter)
|
||||
|
||||
return &VM{
|
||||
filter: filter,
|
||||
}, err
|
||||
}
|
||||
|
||||
// Run runs the VM's BPF program against the input bytes.
|
||||
// Run returns the number of bytes accepted by the BPF program, and any errors
|
||||
// which occurred while processing the program.
|
||||
func (v *VM) Run(in []byte) (int, error) {
|
||||
var (
|
||||
// Registers of the virtual machine
|
||||
regA uint32
|
||||
regX uint32
|
||||
regScratch [16]uint32
|
||||
|
||||
// OK is true if the program should continue processing the next
|
||||
// instruction, or false if not, causing the loop to break
|
||||
ok = true
|
||||
)
|
||||
|
||||
// TODO(mdlayher): implement:
|
||||
// - NegateA:
|
||||
// - would require a change from uint32 registers to int32
|
||||
// registers
|
||||
|
||||
// TODO(mdlayher): add interop tests that check signedness of ALU
|
||||
// operations against kernel implementation, and make sure Go
|
||||
// implementation matches behavior
|
||||
|
||||
for i := 0; i < len(v.filter) && ok; i++ {
|
||||
ins := v.filter[i]
|
||||
|
||||
switch ins := ins.(type) {
|
||||
case ALUOpConstant:
|
||||
regA = aluOpConstant(ins, regA)
|
||||
case ALUOpX:
|
||||
regA, ok = aluOpX(ins, regA, regX)
|
||||
case Jump:
|
||||
i += int(ins.Skip)
|
||||
case JumpIf:
|
||||
jump := jumpIf(ins, regA)
|
||||
i += jump
|
||||
case LoadAbsolute:
|
||||
regA, ok = loadAbsolute(ins, in)
|
||||
case LoadConstant:
|
||||
regA, regX = loadConstant(ins, regA, regX)
|
||||
case LoadExtension:
|
||||
regA = loadExtension(ins, in)
|
||||
case LoadIndirect:
|
||||
regA, ok = loadIndirect(ins, in, regX)
|
||||
case LoadMemShift:
|
||||
regX, ok = loadMemShift(ins, in)
|
||||
case LoadScratch:
|
||||
regA, regX = loadScratch(ins, regScratch, regA, regX)
|
||||
case RetA:
|
||||
return int(regA), nil
|
||||
case RetConstant:
|
||||
return int(ins.Val), nil
|
||||
case StoreScratch:
|
||||
regScratch = storeScratch(ins, regScratch, regA, regX)
|
||||
case TAX:
|
||||
regX = regA
|
||||
case TXA:
|
||||
regA = regX
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
// 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 (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 {
|
||||
return aluOpCommon(ins.Op, regA, ins.Val)
|
||||
}
|
||||
|
||||
func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) {
|
||||
// Guard against division or modulus by zero by terminating
|
||||
// the program, as the OS BPF VM does
|
||||
if regX == 0 {
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return aluOpCommon(ins.Op, regA, regX), true
|
||||
}
|
||||
|
||||
func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
|
||||
switch op {
|
||||
case ALUOpAdd:
|
||||
return regA + value
|
||||
case ALUOpSub:
|
||||
return regA - value
|
||||
case ALUOpMul:
|
||||
return regA * value
|
||||
case ALUOpDiv:
|
||||
// Division by zero not permitted by NewVM and aluOpX checks
|
||||
return regA / value
|
||||
case ALUOpOr:
|
||||
return regA | value
|
||||
case ALUOpAnd:
|
||||
return regA & value
|
||||
case ALUOpShiftLeft:
|
||||
return regA << value
|
||||
case ALUOpShiftRight:
|
||||
return regA >> value
|
||||
case ALUOpMod:
|
||||
// Modulus by zero not permitted by NewVM and aluOpX checks
|
||||
return regA % value
|
||||
case ALUOpXor:
|
||||
return regA ^ value
|
||||
default:
|
||||
return regA
|
||||
}
|
||||
}
|
||||
|
||||
func jumpIf(ins JumpIf, value uint32) int {
|
||||
var ok bool
|
||||
inV := uint32(ins.Val)
|
||||
|
||||
switch ins.Cond {
|
||||
case JumpEqual:
|
||||
ok = value == inV
|
||||
case JumpNotEqual:
|
||||
ok = value != inV
|
||||
case JumpGreaterThan:
|
||||
ok = value > inV
|
||||
case JumpLessThan:
|
||||
ok = value < inV
|
||||
case JumpGreaterOrEqual:
|
||||
ok = value >= inV
|
||||
case JumpLessOrEqual:
|
||||
ok = value <= inV
|
||||
case JumpBitsSet:
|
||||
ok = (value & inV) != 0
|
||||
case JumpBitsNotSet:
|
||||
ok = (value & inV) == 0
|
||||
}
|
||||
|
||||
if ok {
|
||||
return int(ins.SkipTrue)
|
||||
}
|
||||
|
||||
return int(ins.SkipFalse)
|
||||
}
|
||||
|
||||
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
size := int(ins.Size)
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = ins.Val
|
||||
case RegX:
|
||||
regX = ins.Val
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func loadExtension(ins LoadExtension, in []byte) uint32 {
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
return uint32(len(in))
|
||||
default:
|
||||
panic(fmt.Sprintf("unimplemented extension: %d", ins.Num))
|
||||
}
|
||||
}
|
||||
|
||||
func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
|
||||
offset := int(ins.Off) + int(regX)
|
||||
size := int(ins.Size)
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
|
||||
if !inBounds(len(in), offset, 0) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Mask off high 4 bits and multiply low 4 bits by 4
|
||||
return uint32(in[offset]&0x0f) * 4, true
|
||||
}
|
||||
|
||||
func inBounds(inLen int, offset int, size int) bool {
|
||||
return offset+size <= inLen
|
||||
}
|
||||
|
||||
func loadCommon(in []byte, offset int, size int) (uint32, bool) {
|
||||
if !inBounds(len(in), offset, size) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
return uint32(in[offset]), true
|
||||
case 2:
|
||||
return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true
|
||||
case 4:
|
||||
return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid load size: %d", size))
|
||||
}
|
||||
}
|
||||
|
||||
func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = regScratch[ins.N]
|
||||
case RegX:
|
||||
regX = regScratch[ins.N]
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 {
|
||||
switch ins.Src {
|
||||
case RegA:
|
||||
regScratch[ins.N] = regA
|
||||
case RegX:
|
||||
regScratch[ins.N] = regX
|
||||
}
|
||||
|
||||
return regScratch
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
// go generate gen.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
|
||||
package iana // import "golang.org/x/net/internal/iana"
|
||||
|
||||
// Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25
|
||||
const (
|
||||
DiffServCS0 = 0x0 // CS0
|
||||
DiffServCS1 = 0x20 // CS1
|
||||
DiffServCS2 = 0x40 // CS2
|
||||
DiffServCS3 = 0x60 // CS3
|
||||
DiffServCS4 = 0x80 // CS4
|
||||
DiffServCS5 = 0xa0 // CS5
|
||||
DiffServCS6 = 0xc0 // CS6
|
||||
DiffServCS7 = 0xe0 // CS7
|
||||
DiffServAF11 = 0x28 // AF11
|
||||
DiffServAF12 = 0x30 // AF12
|
||||
DiffServAF13 = 0x38 // AF13
|
||||
DiffServAF21 = 0x48 // AF21
|
||||
DiffServAF22 = 0x50 // AF22
|
||||
DiffServAF23 = 0x58 // AF23
|
||||
DiffServAF31 = 0x68 // AF31
|
||||
DiffServAF32 = 0x70 // AF32
|
||||
DiffServAF33 = 0x78 // AF33
|
||||
DiffServAF41 = 0x88 // AF41
|
||||
DiffServAF42 = 0x90 // AF42
|
||||
DiffServAF43 = 0x98 // AF43
|
||||
DiffServEFPHB = 0xb8 // EF PHB
|
||||
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
||||
)
|
||||
|
||||
// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
|
||||
const (
|
||||
NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport)
|
||||
ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1))
|
||||
ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0))
|
||||
CongestionExperienced = 0x3 // CE (Congestion Experienced)
|
||||
)
|
||||
|
||||
// Protocol Numbers, Updated: 2015-10-06
|
||||
const (
|
||||
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
|
||||
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
||||
ProtocolICMP = 1 // Internet Control Message
|
||||
ProtocolIGMP = 2 // Internet Group Management
|
||||
ProtocolGGP = 3 // Gateway-to-Gateway
|
||||
ProtocolIPv4 = 4 // IPv4 encapsulation
|
||||
ProtocolST = 5 // Stream
|
||||
ProtocolTCP = 6 // Transmission Control
|
||||
ProtocolCBT = 7 // CBT
|
||||
ProtocolEGP = 8 // Exterior Gateway Protocol
|
||||
ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||
ProtocolBBNRCCMON = 10 // BBN RCC Monitoring
|
||||
ProtocolNVPII = 11 // Network Voice Protocol
|
||||
ProtocolPUP = 12 // PUP
|
||||
ProtocolEMCON = 14 // EMCON
|
||||
ProtocolXNET = 15 // Cross Net Debugger
|
||||
ProtocolCHAOS = 16 // Chaos
|
||||
ProtocolUDP = 17 // User Datagram
|
||||
ProtocolMUX = 18 // Multiplexing
|
||||
ProtocolDCNMEAS = 19 // DCN Measurement Subsystems
|
||||
ProtocolHMP = 20 // Host Monitoring
|
||||
ProtocolPRM = 21 // Packet Radio Measurement
|
||||
ProtocolXNSIDP = 22 // XEROX NS IDP
|
||||
ProtocolTRUNK1 = 23 // Trunk-1
|
||||
ProtocolTRUNK2 = 24 // Trunk-2
|
||||
ProtocolLEAF1 = 25 // Leaf-1
|
||||
ProtocolLEAF2 = 26 // Leaf-2
|
||||
ProtocolRDP = 27 // Reliable Data Protocol
|
||||
ProtocolIRTP = 28 // Internet Reliable Transaction
|
||||
ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
|
||||
ProtocolNETBLT = 30 // Bulk Data Transfer Protocol
|
||||
ProtocolMFENSP = 31 // MFE Network Services Protocol
|
||||
ProtocolMERITINP = 32 // MERIT Internodal Protocol
|
||||
ProtocolDCCP = 33 // Datagram Congestion Control Protocol
|
||||
Protocol3PC = 34 // Third Party Connect Protocol
|
||||
ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
|
||||
ProtocolXTP = 36 // XTP
|
||||
ProtocolDDP = 37 // Datagram Delivery Protocol
|
||||
ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
|
||||
ProtocolTPPP = 39 // TP++ Transport Protocol
|
||||
ProtocolIL = 40 // IL Transport Protocol
|
||||
ProtocolIPv6 = 41 // IPv6 encapsulation
|
||||
ProtocolSDRP = 42 // Source Demand Routing Protocol
|
||||
ProtocolIPv6Route = 43 // Routing Header for IPv6
|
||||
ProtocolIPv6Frag = 44 // Fragment Header for IPv6
|
||||
ProtocolIDRP = 45 // Inter-Domain Routing Protocol
|
||||
ProtocolRSVP = 46 // Reservation Protocol
|
||||
ProtocolGRE = 47 // Generic Routing Encapsulation
|
||||
ProtocolDSR = 48 // Dynamic Source Routing Protocol
|
||||
ProtocolBNA = 49 // BNA
|
||||
ProtocolESP = 50 // Encap Security Payload
|
||||
ProtocolAH = 51 // Authentication Header
|
||||
ProtocolINLSP = 52 // Integrated Net Layer Security TUBA
|
||||
ProtocolNARP = 54 // NBMA Address Resolution Protocol
|
||||
ProtocolMOBILE = 55 // IP Mobility
|
||||
ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
|
||||
ProtocolSKIP = 57 // SKIP
|
||||
ProtocolIPv6ICMP = 58 // ICMP for IPv6
|
||||
ProtocolIPv6NoNxt = 59 // No Next Header for IPv6
|
||||
ProtocolIPv6Opts = 60 // Destination Options for IPv6
|
||||
ProtocolCFTP = 62 // CFTP
|
||||
ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
|
||||
ProtocolKRYPTOLAN = 65 // Kryptolan
|
||||
ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
|
||||
ProtocolIPPC = 67 // Internet Pluribus Packet Core
|
||||
ProtocolSATMON = 69 // SATNET Monitoring
|
||||
ProtocolVISA = 70 // VISA Protocol
|
||||
ProtocolIPCV = 71 // Internet Packet Core Utility
|
||||
ProtocolCPNX = 72 // Computer Protocol Network Executive
|
||||
ProtocolCPHB = 73 // Computer Protocol Heart Beat
|
||||
ProtocolWSN = 74 // Wang Span Network
|
||||
ProtocolPVP = 75 // Packet Video Protocol
|
||||
ProtocolBRSATMON = 76 // Backroom SATNET Monitoring
|
||||
ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
|
||||
ProtocolWBMON = 78 // WIDEBAND Monitoring
|
||||
ProtocolWBEXPAK = 79 // WIDEBAND EXPAK
|
||||
ProtocolISOIP = 80 // ISO Internet Protocol
|
||||
ProtocolVMTP = 81 // VMTP
|
||||
ProtocolSECUREVMTP = 82 // SECURE-VMTP
|
||||
ProtocolVINES = 83 // VINES
|
||||
ProtocolTTP = 84 // Transaction Transport Protocol
|
||||
ProtocolIPTM = 84 // Internet Protocol Traffic Manager
|
||||
ProtocolNSFNETIGP = 85 // NSFNET-IGP
|
||||
ProtocolDGP = 86 // Dissimilar Gateway Protocol
|
||||
ProtocolTCF = 87 // TCF
|
||||
ProtocolEIGRP = 88 // EIGRP
|
||||
ProtocolOSPFIGP = 89 // OSPFIGP
|
||||
ProtocolSpriteRPC = 90 // Sprite RPC Protocol
|
||||
ProtocolLARP = 91 // Locus Address Resolution Protocol
|
||||
ProtocolMTP = 92 // Multicast Transport Protocol
|
||||
ProtocolAX25 = 93 // AX.25 Frames
|
||||
ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
|
||||
ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
|
||||
ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
|
||||
ProtocolENCAP = 98 // Encapsulation Header
|
||||
ProtocolGMTP = 100 // GMTP
|
||||
ProtocolIFMP = 101 // Ipsilon Flow Management Protocol
|
||||
ProtocolPNNI = 102 // PNNI over IP
|
||||
ProtocolPIM = 103 // Protocol Independent Multicast
|
||||
ProtocolARIS = 104 // ARIS
|
||||
ProtocolSCPS = 105 // SCPS
|
||||
ProtocolQNX = 106 // QNX
|
||||
ProtocolAN = 107 // Active Networks
|
||||
ProtocolIPComp = 108 // IP Payload Compression Protocol
|
||||
ProtocolSNP = 109 // Sitara Networks Protocol
|
||||
ProtocolCompaqPeer = 110 // Compaq Peer Protocol
|
||||
ProtocolIPXinIP = 111 // IPX in IP
|
||||
ProtocolVRRP = 112 // Virtual Router Redundancy Protocol
|
||||
ProtocolPGM = 113 // PGM Reliable Transport Protocol
|
||||
ProtocolL2TP = 115 // Layer Two Tunneling Protocol
|
||||
ProtocolDDX = 116 // D-II Data Exchange (DDX)
|
||||
ProtocolIATP = 117 // Interactive Agent Transfer Protocol
|
||||
ProtocolSTP = 118 // Schedule Transfer Protocol
|
||||
ProtocolSRP = 119 // SpectraLink Radio Protocol
|
||||
ProtocolUTI = 120 // UTI
|
||||
ProtocolSMP = 121 // Simple Message Protocol
|
||||
ProtocolPTP = 123 // Performance Transparency Protocol
|
||||
ProtocolISIS = 124 // ISIS over IPv4
|
||||
ProtocolFIRE = 125 // FIRE
|
||||
ProtocolCRTP = 126 // Combat Radio Transport Protocol
|
||||
ProtocolCRUDP = 127 // Combat Radio User Datagram
|
||||
ProtocolSSCOPMCE = 128 // SSCOPMCE
|
||||
ProtocolIPLT = 129 // IPLT
|
||||
ProtocolSPS = 130 // Secure Packet Shield
|
||||
ProtocolPIPE = 131 // Private IP Encapsulation within IP
|
||||
ProtocolSCTP = 132 // Stream Control Transmission Protocol
|
||||
ProtocolFC = 133 // Fibre Channel
|
||||
ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
|
||||
ProtocolMobilityHeader = 135 // Mobility Header
|
||||
ProtocolUDPLite = 136 // UDPLite
|
||||
ProtocolMPLSinIP = 137 // MPLS-in-IP
|
||||
ProtocolMANET = 138 // MANET Protocols
|
||||
ProtocolHIP = 139 // Host Identity Protocol
|
||||
ProtocolShim6 = 140 // Shim6 Protocol
|
||||
ProtocolWESP = 141 // Wrapped Encapsulating Security Payload
|
||||
ProtocolROHC = 142 // Robust Header Compression
|
||||
ProtocolReserved = 255 // Reserved
|
||||
)
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// This program generates internet protocol constants and tables by
|
||||
// reading IANA protocol registries.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var registries = []struct {
|
||||
url string
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
parseDSCPRegistry,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
|
||||
parseTOSTCByte,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
|
||||
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := r.parse(&bb, resp.Body); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(&bb, "\n")
|
||||
}
|
||||
b, err := format.Source(bb.Bytes())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var dr dscpRegistry
|
||||
if err := dec.Decode(&dr); err != nil {
|
||||
return err
|
||||
}
|
||||
drs := dr.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, dr := range drs {
|
||||
fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type dscpRegistry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
PoolRecords []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"registry>record"`
|
||||
Records []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"registry>registry>record"`
|
||||
}
|
||||
|
||||
type canonDSCPRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escape() []canonDSCPRecord {
|
||||
drs := make([]canonDSCPRecord, len(drr.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, dr := range drr.Records {
|
||||
s := strings.TrimSpace(dr.Name)
|
||||
drs[i].OrigName = s
|
||||
drs[i].Name = sr.Replace(s)
|
||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
drs[i].Value = int(n) << 2
|
||||
}
|
||||
return drs
|
||||
}
|
||||
|
||||
func parseTOSTCByte(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var ttb tosTCByte
|
||||
if err := dec.Decode(&ttb); err != nil {
|
||||
return err
|
||||
}
|
||||
trs := ttb.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, tr := range trs {
|
||||
fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type tosTCByte struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Records []struct {
|
||||
Binary string `xml:"binary"`
|
||||
Keyword string `xml:"keyword"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonTOSTCByteRecord struct {
|
||||
OrigKeyword string
|
||||
Keyword string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
|
||||
trs := make([]canonTOSTCByteRecord, len(ttb.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Capable", "",
|
||||
"(", "",
|
||||
")", "",
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, tr := range ttb.Records {
|
||||
s := strings.TrimSpace(tr.Keyword)
|
||||
trs[i].OrigKeyword = s
|
||||
ss := strings.Split(s, " ")
|
||||
if len(ss) > 1 {
|
||||
trs[i].Keyword = strings.Join(ss[1:], " ")
|
||||
} else {
|
||||
trs[i].Keyword = ss[0]
|
||||
}
|
||||
trs[i].Keyword = sr.Replace(trs[i].Keyword)
|
||||
n, err := strconv.ParseUint(tr.Binary, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
trs[i].Value = int(n)
|
||||
}
|
||||
return trs
|
||||
}
|
||||
|
||||
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var pn protocolNumbers
|
||||
if err := dec.Decode(&pn); err != nil {
|
||||
return err
|
||||
}
|
||||
prs := pn.escape()
|
||||
prs = append([]canonProtocolRecord{{
|
||||
Name: "IP",
|
||||
Descr: "IPv4 encapsulation, pseudo protocol number",
|
||||
Value: 0,
|
||||
}}, prs...)
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, pr := range prs {
|
||||
if pr.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
|
||||
s := pr.Descr
|
||||
if s == "" {
|
||||
s = pr.OrigName
|
||||
}
|
||||
fmt.Fprintf(w, "// %s\n", s)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type protocolNumbers struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Note string `xml:"registry>note"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Name string `xml:"name"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonProtocolRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
||||
prs := make([]canonProtocolRecord, len(pn.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"-in-", "in",
|
||||
"-within-", "within",
|
||||
"-over-", "over",
|
||||
"+", "P",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, pr := range pn.Records {
|
||||
if strings.Contains(pr.Name, "Deprecated") ||
|
||||
strings.Contains(pr.Name, "deprecated") {
|
||||
continue
|
||||
}
|
||||
prs[i].OrigName = pr.Name
|
||||
s := strings.TrimSpace(pr.Name)
|
||||
switch pr.Name {
|
||||
case "ISIS over IPv4":
|
||||
prs[i].Name = "ISIS"
|
||||
case "manet":
|
||||
prs[i].Name = "MANET"
|
||||
default:
|
||||
prs[i].Name = sr.Replace(s)
|
||||
}
|
||||
ss := strings.Split(pr.Descr, "\n")
|
||||
for i := range ss {
|
||||
ss[i] = strings.TrimSpace(ss[i])
|
||||
}
|
||||
if len(ss) > 1 {
|
||||
prs[i].Descr = strings.Join(ss, " ")
|
||||
} else {
|
||||
prs[i].Descr = ss[0]
|
||||
}
|
||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||
}
|
||||
return prs
|
||||
}
|
|
@ -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.
|
||||
|
||||
// +build !go1.9
|
||||
|
||||
// Package netreflect implements run-time reflection for the
|
||||
// facilities of net package.
|
||||
//
|
||||
// This package works only for Go 1.8 or below.
|
||||
package netreflect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidType = errors.New("invalid type")
|
||||
errOpNoSupport = errors.New("operation not supported")
|
||||
)
|
||||
|
||||
// SocketOf returns the socket descriptor of c.
|
||||
func SocketOf(c net.Conn) (uintptr, error) {
|
||||
switch c.(type) {
|
||||
case *net.TCPConn, *net.UDPConn, *net.IPConn, *net.UnixConn:
|
||||
return socketOf(c)
|
||||
default:
|
||||
return 0, errInvalidType
|
||||
}
|
||||
}
|
||||
|
||||
// PacketSocketOf returns the socket descriptor of c.
|
||||
func PacketSocketOf(c net.PacketConn) (uintptr, error) {
|
||||
switch c.(type) {
|
||||
case *net.UDPConn, *net.IPConn, *net.UnixConn:
|
||||
return socketOf(c.(net.Conn))
|
||||
default:
|
||||
return 0, errInvalidType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package netreflect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidType = errors.New("invalid type")
|
||||
errOpNoSupport = errors.New("operation not supported")
|
||||
)
|
||||
|
||||
// SocketOf returns the socket descriptor of c.
|
||||
func SocketOf(c net.Conn) (uintptr, error) {
|
||||
switch c.(type) {
|
||||
case *net.TCPConn, *net.UDPConn, *net.IPConn, *net.UnixConn:
|
||||
return 0, errOpNoSupport
|
||||
default:
|
||||
return 0, errInvalidType
|
||||
}
|
||||
}
|
||||
|
||||
// PacketSocketOf returns the socket descriptor of c.
|
||||
func PacketSocketOf(c net.PacketConn) (uintptr, error) {
|
||||
switch c.(type) {
|
||||
case *net.UDPConn, *net.IPConn, *net.UnixConn:
|
||||
return 0, errOpNoSupport
|
||||
default:
|
||||
return 0, errInvalidType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// 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.
|
||||
|
||||
// +build !go1.9
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package netreflect
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func socketOf(c net.Conn) (uintptr, error) {
|
||||
v := reflect.ValueOf(c)
|
||||
switch e := v.Elem(); e.Kind() {
|
||||
case reflect.Struct:
|
||||
fd := e.FieldByName("conn").FieldByName("fd")
|
||||
switch e := fd.Elem(); e.Kind() {
|
||||
case reflect.Struct:
|
||||
sysfd := e.FieldByName("sysfd")
|
||||
if runtime.GOOS == "windows" {
|
||||
return uintptr(sysfd.Uint()), nil
|
||||
}
|
||||
return uintptr(sysfd.Int()), nil
|
||||
}
|
||||
}
|
||||
return 0, errInvalidType
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
// +build !go1.9
|
||||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
|
||||
|
||||
package netreflect
|
||||
|
||||
import "net"
|
||||
|
||||
func socketOf(c net.Conn) (uintptr, error) { return 0, errOpNoSupport }
|
|
@ -0,0 +1,28 @@
|
|||
// 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 ipv4
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/internal/netreflect"
|
||||
)
|
||||
|
||||
// SetBPF attaches a BPF program to the connection.
|
||||
//
|
||||
// Only supported on Linux.
|
||||
func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error {
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prog := sockFProg{
|
||||
Len: uint16(len(filter)),
|
||||
Filter: (*sockFilter)(unsafe.Pointer(&filter[0])),
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, sysSOL_SOCKET, sysSO_ATTACH_FILTER, unsafe.Pointer(&prog), uint32(unsafe.Sizeof(prog))))
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package ipv4
|
||||
|
||||
import "golang.org/x/net/bpf"
|
||||
|
||||
// SetBPF attaches a BPF program to the connection.
|
||||
//
|
||||
// Only supported on Linux.
|
||||
func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type rawOpt struct {
|
||||
sync.RWMutex
|
||||
cflags ControlFlags
|
||||
}
|
||||
|
||||
func (c *rawOpt) set(f ControlFlags) { c.cflags |= f }
|
||||
func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f }
|
||||
func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
|
||||
|
||||
type ControlFlags uint
|
||||
|
||||
const (
|
||||
FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet
|
||||
FlagSrc // pass the source address on the received packet
|
||||
FlagDst // pass the destination address on the received packet
|
||||
FlagInterface // pass the interface index on the received packet
|
||||
)
|
||||
|
||||
// A ControlMessage represents per packet basis IP-level socket options.
|
||||
type ControlMessage struct {
|
||||
// Receiving socket options: SetControlMessage allows to
|
||||
// receive the options from the protocol stack using ReadFrom
|
||||
// method of PacketConn or RawConn.
|
||||
//
|
||||
// Specifying socket options: ControlMessage for WriteTo
|
||||
// method of PacketConn or RawConn allows to send the options
|
||||
// to the protocol stack.
|
||||
//
|
||||
TTL int // time-to-live, receiving only
|
||||
Src net.IP // source address, specifying only
|
||||
Dst net.IP // destination address, receiving only
|
||||
IfIndex int // interface index, must be 1 <= value when specifying
|
||||
}
|
||||
|
||||
func (cm *ControlMessage) String() string {
|
||||
if cm == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("ttl=%d src=%v dst=%v ifindex=%d", cm.TTL, cm.Src, cm.Dst, cm.IfIndex)
|
||||
}
|
||||
|
||||
// Ancillary data socket options
|
||||
const (
|
||||
ctlTTL = iota // header field
|
||||
ctlSrc // header field
|
||||
ctlDst // header field
|
||||
ctlInterface // inbound or outbound interface
|
||||
ctlPacketInfo // inbound or outbound packet path
|
||||
ctlMax
|
||||
)
|
||||
|
||||
// A ctlOpt represents a binding for ancillary data socket option.
|
||||
type ctlOpt struct {
|
||||
name int // option name, must be equal or greater than 1
|
||||
length int // option length
|
||||
marshal func([]byte, *ControlMessage) []byte
|
||||
parse func(*ControlMessage, []byte)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func marshalDst(b []byte, cm *ControlMessage) []byte {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
m.Level = iana.ProtocolIP
|
||||
m.Type = sysIP_RECVDSTADDR
|
||||
m.SetLen(syscall.CmsgLen(net.IPv4len))
|
||||
return b[syscall.CmsgSpace(net.IPv4len):]
|
||||
}
|
||||
|
||||
func parseDst(cm *ControlMessage, b []byte) {
|
||||
cm.Dst = b[:net.IPv4len]
|
||||
}
|
||||
|
||||
func marshalInterface(b []byte, cm *ControlMessage) []byte {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
m.Level = iana.ProtocolIP
|
||||
m.Type = sysIP_RECVIF
|
||||
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
|
||||
return b[syscall.CmsgSpace(syscall.SizeofSockaddrDatalink):]
|
||||
}
|
||||
|
||||
func parseInterface(cm *ControlMessage, b []byte) {
|
||||
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0]))
|
||||
cm.IfIndex = int(sadl.Index)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build darwin linux solaris
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
m.Level = iana.ProtocolIP
|
||||
m.Type = sysIP_PKTINFO
|
||||
m.SetLen(syscall.CmsgLen(sizeofInetPktinfo))
|
||||
if cm != nil {
|
||||
pi := (*inetPktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
|
||||
if ip := cm.Src.To4(); ip != nil {
|
||||
copy(pi.Spec_dst[:], ip)
|
||||
}
|
||||
if cm.IfIndex > 0 {
|
||||
pi.setIfindex(cm.IfIndex)
|
||||
}
|
||||
}
|
||||
return b[syscall.CmsgSpace(sizeofInetPktinfo):]
|
||||
}
|
||||
|
||||
func parsePacketInfo(cm *ControlMessage, b []byte) {
|
||||
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
|
||||
cm.IfIndex = int(pi.Ifindex)
|
||||
cm.Dst = pi.Addr[:]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build nacl plan9
|
||||
|
||||
package ipv4
|
||||
|
||||
func setControlMessage(s uintptr, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) []byte {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func setControlMessage(s uintptr, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
opt.Lock()
|
||||
defer opt.Unlock()
|
||||
if cf&FlagTTL != 0 && sockOpts[ssoReceiveTTL].name > 0 {
|
||||
if err := setInt(s, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagTTL)
|
||||
} else {
|
||||
opt.clear(FlagTTL)
|
||||
}
|
||||
}
|
||||
if sockOpts[ssoPacketInfo].name > 0 {
|
||||
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
|
||||
if err := setInt(s, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
|
||||
} else {
|
||||
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cf&FlagDst != 0 && sockOpts[ssoReceiveDst].name > 0 {
|
||||
if err := setInt(s, &sockOpts[ssoReceiveDst], boolint(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagDst)
|
||||
} else {
|
||||
opt.clear(FlagDst)
|
||||
}
|
||||
}
|
||||
if cf&FlagInterface != 0 && sockOpts[ssoReceiveInterface].name > 0 {
|
||||
if err := setInt(s, &sockOpts[ssoReceiveInterface], boolint(on)); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagInterface)
|
||||
} else {
|
||||
opt.clear(FlagInterface)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||
opt.RLock()
|
||||
var l int
|
||||
if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 {
|
||||
l += syscall.CmsgSpace(ctlOpts[ctlTTL].length)
|
||||
}
|
||||
if ctlOpts[ctlPacketInfo].name > 0 {
|
||||
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
|
||||
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
|
||||
}
|
||||
} else {
|
||||
if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 {
|
||||
l += syscall.CmsgSpace(ctlOpts[ctlDst].length)
|
||||
}
|
||||
if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 {
|
||||
l += syscall.CmsgSpace(ctlOpts[ctlInterface].length)
|
||||
}
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
}
|
||||
opt.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
cmsgs, err := syscall.ParseSocketControlMessage(b)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("parse socket control message", err)
|
||||
}
|
||||
cm := &ControlMessage{}
|
||||
for _, m := range cmsgs {
|
||||
if m.Header.Level != iana.ProtocolIP {
|
||||
continue
|
||||
}
|
||||
switch int(m.Header.Type) {
|
||||
case ctlOpts[ctlTTL].name:
|
||||
ctlOpts[ctlTTL].parse(cm, m.Data[:])
|
||||
case ctlOpts[ctlDst].name:
|
||||
ctlOpts[ctlDst].parse(cm, m.Data[:])
|
||||
case ctlOpts[ctlInterface].name:
|
||||
ctlOpts[ctlInterface].parse(cm, m.Data[:])
|
||||
case ctlOpts[ctlPacketInfo].name:
|
||||
ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
|
||||
}
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||
if cm == nil {
|
||||
return nil
|
||||
}
|
||||
var l int
|
||||
pktinfo := false
|
||||
if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex > 0) {
|
||||
pktinfo = true
|
||||
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
b := oob
|
||||
if pktinfo {
|
||||
b = ctlOpts[ctlPacketInfo].marshal(b, cm)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalTTL(b []byte, cm *ControlMessage) []byte {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
m.Level = iana.ProtocolIP
|
||||
m.Type = sysIP_RECVTTL
|
||||
m.SetLen(syscall.CmsgLen(1))
|
||||
return b[syscall.CmsgSpace(1):]
|
||||
}
|
||||
|
||||
func parseTTL(cm *ControlMessage, b []byte) {
|
||||
cm.TTL = int(*(*byte)(unsafe.Pointer(&b[:1][0])))
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import "syscall"
|
||||
|
||||
func setControlMessage(s uintptr, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
// TODO(mikio): implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) []byte {
|
||||
// TODO(mikio): implement this
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||
// TODO(mikio): implement this
|
||||
return nil, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) []byte {
|
||||
// TODO(mikio): implement this
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_STRIPHDR = C.IP_STRIPHDR
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
sysIP_BOUND_IF = C.IP_BOUND_IF
|
||||
sysIP_PKTINFO = C.IP_PKTINFO
|
||||
sysIP_RECVPKTINFO = C.IP_RECVPKTINFO
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
|
||||
sysIP_MULTICAST_IFINDEX = C.IP_MULTICAST_IFINDEX
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
|
||||
sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
|
||||
sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
|
||||
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||
|
||||
sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofInetPktinfo = C.sizeof_struct_in_pktinfo
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
sizeofIPMreqn = C.sizeof_struct_ip_mreqn
|
||||
sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
|
||||
sizeofGroupReq = C.sizeof_struct_group_req
|
||||
sizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||
)
|
||||
|
||||
type sockaddrStorage C.struct_sockaddr_storage
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type inetPktinfo C.struct_in_pktinfo
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
||||
|
||||
type ipMreqn C.struct_ip_mreqn
|
||||
|
||||
type ipMreqSource C.struct_ip_mreq_source
|
||||
|
||||
type groupReq C.struct_group_req
|
||||
|
||||
type groupSourceReq C.struct_group_source_req
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
)
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_SENDSRCADDR = C.IP_SENDSRCADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_ONESBCAST = C.IP_ONESBCAST
|
||||
sysIP_BINDANY = C.IP_BINDANY
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
sysIP_MINTTL = C.IP_MINTTL
|
||||
sysIP_DONTFRAG = C.IP_DONTFRAG
|
||||
sysIP_RECVTOS = C.IP_RECVTOS
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
|
||||
sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
|
||||
sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
|
||||
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||
|
||||
sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
sizeofIPMreqn = C.sizeof_struct_ip_mreqn
|
||||
sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
|
||||
sizeofGroupReq = C.sizeof_struct_group_req
|
||||
sizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||
)
|
||||
|
||||
type sockaddrStorage C.struct_sockaddr_storage
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
||||
|
||||
type ipMreqn C.struct_ip_mreqn
|
||||
|
||||
type ipMreqSource C.struct_ip_mreq_source
|
||||
|
||||
type groupReq C.struct_group_req
|
||||
|
||||
type groupSourceReq C.struct_group_source_req
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <time.h>
|
||||
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/filter.h>
|
||||
#include <sys/socket.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_ROUTER_ALERT = C.IP_ROUTER_ALERT
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_PKTINFO = C.IP_PKTINFO
|
||||
sysIP_PKTOPTIONS = C.IP_PKTOPTIONS
|
||||
sysIP_MTU_DISCOVER = C.IP_MTU_DISCOVER
|
||||
sysIP_RECVERR = C.IP_RECVERR
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
sysIP_RECVTOS = C.IP_RECVTOS
|
||||
sysIP_MTU = C.IP_MTU
|
||||
sysIP_FREEBIND = C.IP_FREEBIND
|
||||
sysIP_TRANSPARENT = C.IP_TRANSPARENT
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_ORIGDSTADDR = C.IP_ORIGDSTADDR
|
||||
sysIP_RECVORIGDSTADDR = C.IP_RECVORIGDSTADDR
|
||||
sysIP_MINTTL = C.IP_MINTTL
|
||||
sysIP_NODEFRAG = C.IP_NODEFRAG
|
||||
sysIP_UNICAST_IF = C.IP_UNICAST_IF
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
|
||||
sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
|
||||
sysIP_MSFILTER = C.IP_MSFILTER
|
||||
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||
sysMCAST_MSFILTER = C.MCAST_MSFILTER
|
||||
sysIP_MULTICAST_ALL = C.IP_MULTICAST_ALL
|
||||
|
||||
//sysIP_PMTUDISC_DONT = C.IP_PMTUDISC_DONT
|
||||
//sysIP_PMTUDISC_WANT = C.IP_PMTUDISC_WANT
|
||||
//sysIP_PMTUDISC_DO = C.IP_PMTUDISC_DO
|
||||
//sysIP_PMTUDISC_PROBE = C.IP_PMTUDISC_PROBE
|
||||
//sysIP_PMTUDISC_INTERFACE = C.IP_PMTUDISC_INTERFACE
|
||||
//sysIP_PMTUDISC_OMIT = C.IP_PMTUDISC_OMIT
|
||||
|
||||
sysICMP_FILTER = C.ICMP_FILTER
|
||||
|
||||
sysSO_EE_ORIGIN_NONE = C.SO_EE_ORIGIN_NONE
|
||||
sysSO_EE_ORIGIN_LOCAL = C.SO_EE_ORIGIN_LOCAL
|
||||
sysSO_EE_ORIGIN_ICMP = C.SO_EE_ORIGIN_ICMP
|
||||
sysSO_EE_ORIGIN_ICMP6 = C.SO_EE_ORIGIN_ICMP6
|
||||
sysSO_EE_ORIGIN_TXSTATUS = C.SO_EE_ORIGIN_TXSTATUS
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = C.SO_EE_ORIGIN_TIMESTAMPING
|
||||
|
||||
sysSOL_SOCKET = C.SOL_SOCKET
|
||||
sysSO_ATTACH_FILTER = C.SO_ATTACH_FILTER
|
||||
|
||||
sizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofInetPktinfo = C.sizeof_struct_in_pktinfo
|
||||
sizeofSockExtendedErr = C.sizeof_struct_sock_extended_err
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
sizeofIPMreqn = C.sizeof_struct_ip_mreqn
|
||||
sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
|
||||
sizeofGroupReq = C.sizeof_struct_group_req
|
||||
sizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||
|
||||
sizeofICMPFilter = C.sizeof_struct_icmp_filter
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage C.struct___kernel_sockaddr_storage
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type inetPktinfo C.struct_in_pktinfo
|
||||
|
||||
type sockExtendedErr C.struct_sock_extended_err
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
||||
|
||||
type ipMreqn C.struct_ip_mreqn
|
||||
|
||||
type ipMreqSource C.struct_ip_mreq_source
|
||||
|
||||
type groupReq C.struct_group_req
|
||||
|
||||
type groupSourceReq C.struct_group_source_req
|
||||
|
||||
type icmpFilter C.struct_icmp_filter
|
||||
|
||||
type sockFProg C.struct_sock_fprog
|
||||
|
||||
type sockFilter C.struct_sock_filter
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
)
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
)
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
|
||||
package ipv4
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = C.IP_OPTIONS
|
||||
sysIP_HDRINCL = C.IP_HDRINCL
|
||||
sysIP_TOS = C.IP_TOS
|
||||
sysIP_TTL = C.IP_TTL
|
||||
sysIP_RECVOPTS = C.IP_RECVOPTS
|
||||
sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
|
||||
sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
|
||||
sysIP_RETOPTS = C.IP_RETOPTS
|
||||
sysIP_RECVIF = C.IP_RECVIF
|
||||
sysIP_RECVSLLA = C.IP_RECVSLLA
|
||||
sysIP_RECVTTL = C.IP_RECVTTL
|
||||
|
||||
sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
|
||||
sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
|
||||
sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
|
||||
sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
|
||||
sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
|
||||
sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
|
||||
sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
|
||||
sysIP_NEXTHOP = C.IP_NEXTHOP
|
||||
|
||||
sysIP_PKTINFO = C.IP_PKTINFO
|
||||
sysIP_RECVPKTINFO = C.IP_RECVPKTINFO
|
||||
sysIP_DONTFRAG = C.IP_DONTFRAG
|
||||
|
||||
sysIP_BOUND_IF = C.IP_BOUND_IF
|
||||
sysIP_UNSPEC_SRC = C.IP_UNSPEC_SRC
|
||||
sysIP_BROADCAST_TTL = C.IP_BROADCAST_TTL
|
||||
sysIP_DHCPINIT_IF = C.IP_DHCPINIT_IF
|
||||
|
||||
sysIP_REUSEADDR = C.IP_REUSEADDR
|
||||
sysIP_DONTROUTE = C.IP_DONTROUTE
|
||||
sysIP_BROADCAST = C.IP_BROADCAST
|
||||
|
||||
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||
|
||||
sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofInetPktinfo = C.sizeof_struct_in_pktinfo
|
||||
|
||||
sizeofIPMreq = C.sizeof_struct_ip_mreq
|
||||
sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
|
||||
sizeofGroupReq = C.sizeof_struct_group_req
|
||||
sizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||
)
|
||||
|
||||
type sockaddrStorage C.struct_sockaddr_storage
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type inetPktinfo C.struct_in_pktinfo
|
||||
|
||||
type ipMreq C.struct_ip_mreq
|
||||
|
||||
type ipMreqSource C.struct_ip_mreq_source
|
||||
|
||||
type groupReq C.struct_group_req
|
||||
|
||||
type groupSourceReq C.struct_group_source_req
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/internal/netreflect"
|
||||
)
|
||||
|
||||
// MulticastTTL returns the time-to-live field value for outgoing
|
||||
// multicast packets.
|
||||
func (c *dgramOpt) MulticastTTL() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getInt(s, &sockOpts[ssoMulticastTTL])
|
||||
}
|
||||
|
||||
// SetMulticastTTL sets the time-to-live field value for future
|
||||
// outgoing multicast packets.
|
||||
func (c *dgramOpt) SetMulticastTTL(ttl int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setInt(s, &sockOpts[ssoMulticastTTL], ttl)
|
||||
}
|
||||
|
||||
// MulticastInterface returns the default interface for multicast
|
||||
// packet transmissions.
|
||||
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||
if !c.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getInterface(s, &sockOpts[ssoMulticastInterface])
|
||||
}
|
||||
|
||||
// SetMulticastInterface sets the default interface for future
|
||||
// multicast packet transmissions.
|
||||
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setInterface(s, &sockOpts[ssoMulticastInterface], ifi)
|
||||
}
|
||||
|
||||
// MulticastLoopback reports whether transmitted multicast packets
|
||||
// should be copied and send back to the originator.
|
||||
func (c *dgramOpt) MulticastLoopback() (bool, error) {
|
||||
if !c.ok() {
|
||||
return false, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
on, err := getInt(s, &sockOpts[ssoMulticastLoopback])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return on == 1, nil
|
||||
}
|
||||
|
||||
// SetMulticastLoopback sets whether transmitted multicast packets
|
||||
// should be copied and send back to the originator.
|
||||
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setInt(s, &sockOpts[ssoMulticastLoopback], boolint(on))
|
||||
}
|
||||
|
||||
// JoinGroup joins the group address group on the interface ifi.
|
||||
// By default all sources that can cast data to group are accepted.
|
||||
// It's possible to mute and unmute data transmission from a specific
|
||||
// source by using ExcludeSourceSpecificGroup and
|
||||
// IncludeSourceSpecificGroup.
|
||||
// JoinGroup uses the system assigned multicast interface when ifi is
|
||||
// nil, although this is not recommended because the assignment
|
||||
// depends on platforms and sometimes it might require routing
|
||||
// configuration.
|
||||
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setGroup(s, &sockOpts[ssoJoinGroup], ifi, grp)
|
||||
}
|
||||
|
||||
// LeaveGroup leaves the group address group on the interface ifi
|
||||
// regardless of whether the group is any-source group or
|
||||
// source-specific group.
|
||||
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setGroup(s, &sockOpts[ssoLeaveGroup], ifi, grp)
|
||||
}
|
||||
|
||||
// JoinSourceSpecificGroup joins the source-specific group comprising
|
||||
// group and source on the interface ifi.
|
||||
// JoinSourceSpecificGroup uses the system assigned multicast
|
||||
// interface when ifi is nil, although this is not recommended because
|
||||
// the assignment depends on platforms and sometimes it might require
|
||||
// routing configuration.
|
||||
func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
src := netAddrToIP4(source)
|
||||
if src == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setSourceGroup(s, &sockOpts[ssoJoinSourceGroup], ifi, grp, src)
|
||||
}
|
||||
|
||||
// LeaveSourceSpecificGroup leaves the source-specific group on the
|
||||
// interface ifi.
|
||||
func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
src := netAddrToIP4(source)
|
||||
if src == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setSourceGroup(s, &sockOpts[ssoLeaveSourceGroup], ifi, grp, src)
|
||||
}
|
||||
|
||||
// ExcludeSourceSpecificGroup excludes the source-specific group from
|
||||
// the already joined any-source groups by JoinGroup on the interface
|
||||
// ifi.
|
||||
func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
src := netAddrToIP4(source)
|
||||
if src == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setSourceGroup(s, &sockOpts[ssoBlockSourceGroup], ifi, grp, src)
|
||||
}
|
||||
|
||||
// IncludeSourceSpecificGroup includes the excluded source-specific
|
||||
// group by ExcludeSourceSpecificGroup again on the interface ifi.
|
||||
func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP4(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
src := netAddrToIP4(source)
|
||||
if src == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return setSourceGroup(s, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
|
||||
}
|
||||
|
||||
// ICMPFilter returns an ICMP filter.
|
||||
// Currently only Linux supports this.
|
||||
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||
if !c.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getICMPFilter(s, &sockOpts[ssoICMPFilter])
|
||||
}
|
||||
|
||||
// SetICMPFilter deploys the ICMP filter.
|
||||
// Currently only Linux supports this.
|
||||
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setICMPFilter(s, &sockOpts[ssoICMPFilter], f)
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build nacl plan9
|
||||
|
||||
package ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
// MulticastTTL returns the time-to-live field value for outgoing
|
||||
// multicast packets.
|
||||
func (c *dgramOpt) MulticastTTL() (int, error) {
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetMulticastTTL sets the time-to-live field value for future
|
||||
// outgoing multicast packets.
|
||||
func (c *dgramOpt) SetMulticastTTL(ttl int) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// MulticastInterface returns the default interface for multicast
|
||||
// packet transmissions.
|
||||
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetMulticastInterface sets the default interface for future
|
||||
// multicast packet transmissions.
|
||||
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// MulticastLoopback reports whether transmitted multicast packets
|
||||
// should be copied and send back to the originator.
|
||||
func (c *dgramOpt) MulticastLoopback() (bool, error) {
|
||||
return false, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetMulticastLoopback sets whether transmitted multicast packets
|
||||
// should be copied and send back to the originator.
|
||||
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// JoinGroup joins the group address group on the interface ifi.
|
||||
// By default all sources that can cast data to group are accepted.
|
||||
// It's possible to mute and unmute data transmission from a specific
|
||||
// source by using ExcludeSourceSpecificGroup and
|
||||
// IncludeSourceSpecificGroup.
|
||||
// JoinGroup uses the system assigned multicast interface when ifi is
|
||||
// nil, although this is not recommended because the assignment
|
||||
// depends on platforms and sometimes it might require routing
|
||||
// configuration.
|
||||
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// LeaveGroup leaves the group address group on the interface ifi
|
||||
// regardless of whether the group is any-source group or
|
||||
// source-specific group.
|
||||
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// JoinSourceSpecificGroup joins the source-specific group comprising
|
||||
// group and source on the interface ifi.
|
||||
// JoinSourceSpecificGroup uses the system assigned multicast
|
||||
// interface when ifi is nil, although this is not recommended because
|
||||
// the assignment depends on platforms and sometimes it might require
|
||||
// routing configuration.
|
||||
func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// LeaveSourceSpecificGroup leaves the source-specific group on the
|
||||
// interface ifi.
|
||||
func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// ExcludeSourceSpecificGroup excludes the source-specific group from
|
||||
// the already joined any-source groups by JoinGroup on the interface
|
||||
// ifi.
|
||||
func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// IncludeSourceSpecificGroup includes the excluded source-specific
|
||||
// group by ExcludeSourceSpecificGroup again on the interface ifi.
|
||||
func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// ICMPFilter returns an ICMP filter.
|
||||
// Currently only Linux supports this.
|
||||
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetICMPFilter deploys the ICMP filter.
|
||||
// Currently only Linux supports this.
|
||||
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
// Copyright 2012 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 ipv4 implements IP-level socket options for the Internet
|
||||
// Protocol version 4.
|
||||
//
|
||||
// The package provides IP-level socket options that allow
|
||||
// manipulation of IPv4 facilities.
|
||||
//
|
||||
// The IPv4 protocol and basic host requirements for IPv4 are defined
|
||||
// in RFC 791 and RFC 1122.
|
||||
// Host extensions for multicasting and socket interface extensions
|
||||
// for multicast source filters are defined in RFC 1112 and RFC 3678.
|
||||
// IGMPv1, IGMPv2 and IGMPv3 are defined in RFC 1112, RFC 2236 and RFC
|
||||
// 3376.
|
||||
// Source-specific multicast is defined in RFC 4607.
|
||||
//
|
||||
//
|
||||
// Unicasting
|
||||
//
|
||||
// The options for unicasting are available for net.TCPConn,
|
||||
// net.UDPConn and net.IPConn which are created as network connections
|
||||
// that use the IPv4 transport. When a single TCP connection carrying
|
||||
// a data flow of multiple packets needs to indicate the flow is
|
||||
// important, Conn is used to set the type-of-service field on the
|
||||
// IPv4 header for each packet.
|
||||
//
|
||||
// ln, err := net.Listen("tcp4", "0.0.0.0:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer ln.Close()
|
||||
// for {
|
||||
// c, err := ln.Accept()
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// go func(c net.Conn) {
|
||||
// defer c.Close()
|
||||
//
|
||||
// The outgoing packets will be labeled DiffServ assured forwarding
|
||||
// class 1 low drop precedence, known as AF11 packets.
|
||||
//
|
||||
// if err := ipv4.NewConn(c).SetTOS(0x28); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if _, err := c.Write(data); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// }(c)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Multicasting
|
||||
//
|
||||
// The options for multicasting are available for net.UDPConn and
|
||||
// net.IPconn which are created as network connections that use the
|
||||
// IPv4 transport. A few network facilities must be prepared before
|
||||
// you begin multicasting, at a minimum joining network interfaces and
|
||||
// multicast groups.
|
||||
//
|
||||
// en0, err := net.InterfaceByName("en0")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// en1, err := net.InterfaceByIndex(911)
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// group := net.IPv4(224, 0, 0, 250)
|
||||
//
|
||||
// First, an application listens to an appropriate address with an
|
||||
// appropriate service port.
|
||||
//
|
||||
// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c.Close()
|
||||
//
|
||||
// Second, the application joins multicast groups, starts listening to
|
||||
// the groups on the specified network interfaces. Note that the
|
||||
// service port for transport layer protocol does not matter with this
|
||||
// operation as joining groups affects only network and link layer
|
||||
// protocols, such as IPv4 and Ethernet.
|
||||
//
|
||||
// p := ipv4.NewPacketConn(c)
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// The application might set per packet control message transmissions
|
||||
// between the protocol stack within the kernel. When the application
|
||||
// needs a destination address on an incoming packet,
|
||||
// SetControlMessage of PacketConn is used to enable control message
|
||||
// transmissions.
|
||||
//
|
||||
// if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// The application could identify whether the received packets are
|
||||
// of interest by using the control message that contains the
|
||||
// destination address of the received packet.
|
||||
//
|
||||
// b := make([]byte, 1500)
|
||||
// for {
|
||||
// n, cm, src, err := p.ReadFrom(b)
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if cm.Dst.IsMulticast() {
|
||||
// if cm.Dst.Equal(group) {
|
||||
// // joined group, do something
|
||||
// } else {
|
||||
// // unknown group, discard
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The application can also send both unicast and multicast packets.
|
||||
//
|
||||
// p.SetTOS(0x0)
|
||||
// p.SetTTL(16)
|
||||
// if _, err := p.WriteTo(data, nil, src); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// dst := &net.UDPAddr{IP: group, Port: 1024}
|
||||
// for _, ifi := range []*net.Interface{en0, en1} {
|
||||
// if err := p.SetMulticastInterface(ifi); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// p.SetMulticastTTL(2)
|
||||
// if _, err := p.WriteTo(data, nil, dst); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// More multicasting
|
||||
//
|
||||
// An application that uses PacketConn or RawConn may join multiple
|
||||
// multicast groups. For example, a UDP listener with port 1024 might
|
||||
// join two different groups across over two different network
|
||||
// interfaces by using:
|
||||
//
|
||||
// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c.Close()
|
||||
// p := ipv4.NewPacketConn(c)
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// It is possible for multiple UDP listeners that listen on the same
|
||||
// UDP port to join the same multicast group. The net package will
|
||||
// provide a socket that listens to a wildcard address with reusable
|
||||
// UDP port when an appropriate multicast address prefix is passed to
|
||||
// the net.ListenPacket or net.ListenUDP.
|
||||
//
|
||||
// c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c1.Close()
|
||||
// c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c2.Close()
|
||||
// p1 := ipv4.NewPacketConn(c1)
|
||||
// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// p2 := ipv4.NewPacketConn(c2)
|
||||
// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// Also it is possible for the application to leave or rejoin a
|
||||
// multicast group on the network interface.
|
||||
//
|
||||
// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Source-specific multicasting
|
||||
//
|
||||
// An application that uses PacketConn or RawConn on IGMPv3 supported
|
||||
// platform is able to join source-specific multicast groups.
|
||||
// The application may use JoinSourceSpecificGroup and
|
||||
// LeaveSourceSpecificGroup for the operation known as "include" mode,
|
||||
//
|
||||
// ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
|
||||
// ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
|
||||
// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// or JoinGroup, ExcludeSourceSpecificGroup,
|
||||
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
|
||||
// as "exclude" mode.
|
||||
//
|
||||
// exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
|
||||
// if err := p.JoinGroup(en0, &ssmgroup); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
//
|
||||
// Note that it depends on each platform implementation what happens
|
||||
// when an application which runs on IGMPv3 unsupported platform uses
|
||||
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
|
||||
// In general the platform tries to fall back to conversations using
|
||||
// IGMPv1 or IGMPv2 and starts to listen to multicast traffic.
|
||||
// In the fallback case, ExcludeSourceSpecificGroup and
|
||||
// IncludeSourceSpecificGroup may return an error.
|
||||
package ipv4 // import "golang.org/x/net/ipv4"
|
||||
|
||||
// BUG(mikio): This package is not implemented on NaCl and Plan 9.
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/internal/netreflect"
|
||||
)
|
||||
|
||||
// BUG(mikio): On Windows, the JoinSourceSpecificGroup,
|
||||
// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and
|
||||
// IncludeSourceSpecificGroup methods of PacketConn and RawConn are
|
||||
// not implemented.
|
||||
|
||||
// A Conn represents a network endpoint that uses the IPv4 transport.
|
||||
// It is used to control basic IP-level socket options such as TOS and
|
||||
// TTL.
|
||||
type Conn struct {
|
||||
genericOpt
|
||||
}
|
||||
|
||||
type genericOpt struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
|
||||
|
||||
// NewConn returns a new Conn.
|
||||
func NewConn(c net.Conn) *Conn {
|
||||
return &Conn{
|
||||
genericOpt: genericOpt{Conn: c},
|
||||
}
|
||||
}
|
||||
|
||||
// A PacketConn represents a packet network endpoint that uses the
|
||||
// IPv4 transport. It is used to control several IP-level socket
|
||||
// options including multicasting. It also provides datagram based
|
||||
// network I/O methods specific to the IPv4 and higher layer protocols
|
||||
// such as UDP.
|
||||
type PacketConn struct {
|
||||
genericOpt
|
||||
dgramOpt
|
||||
payloadHandler
|
||||
}
|
||||
|
||||
type dgramOpt struct {
|
||||
net.PacketConn
|
||||
}
|
||||
|
||||
func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
|
||||
|
||||
// SetControlMessage sets the per packet IP-level socket options.
|
||||
func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.dgramOpt.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setControlMessage(s, &c.payloadHandler.rawOpt, cf, on)
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.payloadHandler.PacketConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.payloadHandler.PacketConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.payloadHandler.PacketConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// Close closes the endpoint.
|
||||
func (c *PacketConn) Close() error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.payloadHandler.PacketConn.Close()
|
||||
}
|
||||
|
||||
// NewPacketConn returns a new PacketConn using c as its underlying
|
||||
// transport.
|
||||
func NewPacketConn(c net.PacketConn) *PacketConn {
|
||||
p := &PacketConn{
|
||||
genericOpt: genericOpt{Conn: c.(net.Conn)},
|
||||
dgramOpt: dgramOpt{PacketConn: c},
|
||||
payloadHandler: payloadHandler{PacketConn: c},
|
||||
}
|
||||
if _, ok := c.(*net.IPConn); ok && sockOpts[ssoStripHeader].name > 0 {
|
||||
if s, err := netreflect.PacketSocketOf(c); err == nil {
|
||||
setInt(s, &sockOpts[ssoStripHeader], boolint(true))
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// A RawConn represents a packet network endpoint that uses the IPv4
|
||||
// transport. It is used to control several IP-level socket options
|
||||
// including IPv4 header manipulation. It also provides datagram
|
||||
// based network I/O methods specific to the IPv4 and higher layer
|
||||
// protocols that handle IPv4 datagram directly such as OSPF, GRE.
|
||||
type RawConn struct {
|
||||
genericOpt
|
||||
dgramOpt
|
||||
packetHandler
|
||||
}
|
||||
|
||||
// SetControlMessage sets the per packet IP-level socket options.
|
||||
func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error {
|
||||
if !c.packetHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c.dgramOpt.PacketConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setControlMessage(s, &c.packetHandler.rawOpt, cf, on)
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the
|
||||
// endpoint.
|
||||
func (c *RawConn) SetDeadline(t time.Time) error {
|
||||
if !c.packetHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.packetHandler.c.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the
|
||||
// endpoint.
|
||||
func (c *RawConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.packetHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.packetHandler.c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the
|
||||
// endpoint.
|
||||
func (c *RawConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.packetHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.packetHandler.c.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// Close closes the endpoint.
|
||||
func (c *RawConn) Close() error {
|
||||
if !c.packetHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.packetHandler.c.Close()
|
||||
}
|
||||
|
||||
// NewRawConn returns a new RawConn using c as its underlying
|
||||
// transport.
|
||||
func NewRawConn(c net.PacketConn) (*RawConn, error) {
|
||||
r := &RawConn{
|
||||
genericOpt: genericOpt{Conn: c.(net.Conn)},
|
||||
dgramOpt: dgramOpt{PacketConn: c},
|
||||
packetHandler: packetHandler{c: c.(*net.IPConn)},
|
||||
}
|
||||
s, err := netreflect.PacketSocketOf(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setInt(s, &sockOpts[ssoHeaderPrepend], boolint(true)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// This program generates system adaptation constants and types,
|
||||
// internet protocol constants and tables by reading template files
|
||||
// and IANA protocol registries.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := genzsys(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := geniana(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func genzsys() error {
|
||||
defs := "defs_" + runtime.GOOS + ".go"
|
||||
f, err := os.Open(defs)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err = format.Source(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zsys := "zsys_" + runtime.GOOS + ".go"
|
||||
switch runtime.GOOS {
|
||||
case "freebsd", "linux":
|
||||
zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
|
||||
}
|
||||
if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var registries = []struct {
|
||||
url string
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml",
|
||||
parseICMPv4Parameters,
|
||||
},
|
||||
}
|
||||
|
||||
func geniana() error {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(&bb, "package ipv4\n\n")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||
}
|
||||
if err := r.parse(&bb, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(&bb, "\n")
|
||||
}
|
||||
b, err := format.Source(bb.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseICMPv4Parameters(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var icp icmpv4Parameters
|
||||
if err := dec.Decode(&icp); err != nil {
|
||||
return err
|
||||
}
|
||||
prs := icp.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, pr := range prs {
|
||||
if pr.Descr == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Descr, pr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", pr.OrigDescr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n\n")
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
|
||||
fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
|
||||
for _, pr := range prs {
|
||||
if pr.Descr == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigDescr))
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type icmpv4Parameters struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
type canonICMPv4ParamRecord struct {
|
||||
OrigDescr string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (icp *icmpv4Parameters) escape() []canonICMPv4ParamRecord {
|
||||
id := -1
|
||||
for i, r := range icp.Registries {
|
||||
if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
|
||||
id = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if id < 0 {
|
||||
return nil
|
||||
}
|
||||
prs := make([]canonICMPv4ParamRecord, len(icp.Registries[id].Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Messages", "",
|
||||
"Message", "",
|
||||
"ICMP", "",
|
||||
"+", "P",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, pr := range icp.Registries[id].Records {
|
||||
if strings.Contains(pr.Descr, "Reserved") ||
|
||||
strings.Contains(pr.Descr, "Unassigned") ||
|
||||
strings.Contains(pr.Descr, "Deprecated") ||
|
||||
strings.Contains(pr.Descr, "Experiment") ||
|
||||
strings.Contains(pr.Descr, "experiment") {
|
||||
continue
|
||||
}
|
||||
ss := strings.Split(pr.Descr, "\n")
|
||||
if len(ss) > 1 {
|
||||
prs[i].Descr = strings.Join(ss, " ")
|
||||
} else {
|
||||
prs[i].Descr = ss[0]
|
||||
}
|
||||
s := strings.TrimSpace(prs[i].Descr)
|
||||
prs[i].OrigDescr = s
|
||||
prs[i].Descr = sr.Replace(s)
|
||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||
}
|
||||
return prs
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/internal/netreflect"
|
||||
)
|
||||
|
||||
// TOS returns the type-of-service field value for outgoing packets.
|
||||
func (c *genericOpt) TOS() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.SocketOf(c.Conn)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getInt(s, &sockOpts[ssoTOS])
|
||||
}
|
||||
|
||||
// SetTOS sets the type-of-service field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetTOS(tos int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.SocketOf(c.Conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setInt(s, &sockOpts[ssoTOS], tos)
|
||||
}
|
||||
|
||||
// TTL returns the time-to-live field value for outgoing packets.
|
||||
func (c *genericOpt) TTL() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.SocketOf(c.Conn)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getInt(s, &sockOpts[ssoTTL])
|
||||
}
|
||||
|
||||
// SetTTL sets the time-to-live field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetTTL(ttl int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
s, err := netreflect.SocketOf(c.Conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setInt(s, &sockOpts[ssoTTL], ttl)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build nacl plan9
|
||||
|
||||
package ipv4
|
||||
|
||||
// TOS returns the type-of-service field value for outgoing packets.
|
||||
func (c *genericOpt) TOS() (int, error) {
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetTOS sets the type-of-service field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetTOS(tos int) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// TTL returns the time-to-live field value for outgoing packets.
|
||||
func (c *genericOpt) TTL() (int, error) {
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetTTL sets the time-to-live field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetTTL(ttl int) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package ipv4
|
||||
|
||||
func init() {
|
||||
disableTests = true
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = 4 // protocol version
|
||||
HeaderLen = 20 // header length without extension headers
|
||||
maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
|
||||
)
|
||||
|
||||
type HeaderFlags int
|
||||
|
||||
const (
|
||||
MoreFragments HeaderFlags = 1 << iota // more fragments flag
|
||||
DontFragment // don't fragment flag
|
||||
)
|
||||
|
||||
// A Header represents an IPv4 header.
|
||||
type Header struct {
|
||||
Version int // protocol version
|
||||
Len int // header length
|
||||
TOS int // type-of-service
|
||||
TotalLen int // packet total length
|
||||
ID int // identification
|
||||
Flags HeaderFlags // flags
|
||||
FragOff int // fragment offset
|
||||
TTL int // time-to-live
|
||||
Protocol int // next protocol
|
||||
Checksum int // checksum
|
||||
Src net.IP // source address
|
||||
Dst net.IP // destination address
|
||||
Options []byte // options, extension headers
|
||||
}
|
||||
|
||||
func (h *Header) String() string {
|
||||
if h == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("ver=%d hdrlen=%d tos=%#x totallen=%d id=%#x flags=%#x fragoff=%#x ttl=%d proto=%d cksum=%#x src=%v dst=%v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
|
||||
}
|
||||
|
||||
// Marshal returns the binary encoding of the IPv4 header h.
|
||||
func (h *Header) Marshal() ([]byte, error) {
|
||||
if h == nil {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if h.Len < HeaderLen {
|
||||
return nil, errHeaderTooShort
|
||||
}
|
||||
hdrlen := HeaderLen + len(h.Options)
|
||||
b := make([]byte, hdrlen)
|
||||
b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
|
||||
b[1] = byte(h.TOS)
|
||||
flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "dragonfly", "netbsd":
|
||||
nativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
|
||||
nativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
|
||||
case "freebsd":
|
||||
if freebsdVersion < 1100000 {
|
||||
nativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
|
||||
nativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
|
||||
binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
|
||||
}
|
||||
default:
|
||||
binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
|
||||
binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
|
||||
}
|
||||
binary.BigEndian.PutUint16(b[4:6], uint16(h.ID))
|
||||
b[8] = byte(h.TTL)
|
||||
b[9] = byte(h.Protocol)
|
||||
binary.BigEndian.PutUint16(b[10:12], uint16(h.Checksum))
|
||||
if ip := h.Src.To4(); ip != nil {
|
||||
copy(b[12:16], ip[:net.IPv4len])
|
||||
}
|
||||
if ip := h.Dst.To4(); ip != nil {
|
||||
copy(b[16:20], ip[:net.IPv4len])
|
||||
} else {
|
||||
return nil, errMissingAddress
|
||||
}
|
||||
if len(h.Options) > 0 {
|
||||
copy(b[HeaderLen:], h.Options)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// ParseHeader parses b as an IPv4 header.
|
||||
func ParseHeader(b []byte) (*Header, error) {
|
||||
if len(b) < HeaderLen {
|
||||
return nil, errHeaderTooShort
|
||||
}
|
||||
hdrlen := int(b[0]&0x0f) << 2
|
||||
if hdrlen > len(b) {
|
||||
return nil, errBufferTooShort
|
||||
}
|
||||
h := &Header{
|
||||
Version: int(b[0] >> 4),
|
||||
Len: hdrlen,
|
||||
TOS: int(b[1]),
|
||||
ID: int(binary.BigEndian.Uint16(b[4:6])),
|
||||
TTL: int(b[8]),
|
||||
Protocol: int(b[9]),
|
||||
Checksum: int(binary.BigEndian.Uint16(b[10:12])),
|
||||
Src: net.IPv4(b[12], b[13], b[14], b[15]),
|
||||
Dst: net.IPv4(b[16], b[17], b[18], b[19]),
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "dragonfly", "netbsd":
|
||||
h.TotalLen = int(nativeEndian.Uint16(b[2:4])) + hdrlen
|
||||
h.FragOff = int(nativeEndian.Uint16(b[6:8]))
|
||||
case "freebsd":
|
||||
if freebsdVersion < 1100000 {
|
||||
h.TotalLen = int(nativeEndian.Uint16(b[2:4]))
|
||||
if freebsdVersion < 1000000 {
|
||||
h.TotalLen += hdrlen
|
||||
}
|
||||
h.FragOff = int(nativeEndian.Uint16(b[6:8]))
|
||||
} else {
|
||||
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
|
||||
h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
|
||||
}
|
||||
default:
|
||||
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
|
||||
h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
|
||||
}
|
||||
h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
|
||||
h.FragOff = h.FragOff & 0x1fff
|
||||
if hdrlen-HeaderLen > 0 {
|
||||
h.Options = make([]byte, hdrlen-HeaderLen)
|
||||
copy(h.Options, b[HeaderLen:])
|
||||
}
|
||||
return h, nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errMissingHeader = errors.New("missing header")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errBufferTooShort = errors.New("buffer too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errOpNoSupport = errors.New("operation not supported")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
errNoSuchMulticastInterface = errors.New("no such multicast interface")
|
||||
|
||||
// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
|
||||
freebsdVersion uint32
|
||||
|
||||
nativeEndian binary.ByteOrder
|
||||
)
|
||||
|
||||
func init() {
|
||||
i := uint32(1)
|
||||
b := (*[4]byte)(unsafe.Pointer(&i))
|
||||
if b[0] == 1 {
|
||||
nativeEndian = binary.LittleEndian
|
||||
} else {
|
||||
nativeEndian = binary.BigEndian
|
||||
}
|
||||
}
|
||||
|
||||
func boolint(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func netAddrToIP4(a net.Addr) net.IP {
|
||||
switch v := a.(type) {
|
||||
case *net.UDPAddr:
|
||||
if ip := v.IP.To4(); ip != nil {
|
||||
return ip
|
||||
}
|
||||
case *net.IPAddr:
|
||||
if ip := v.IP.To4(); ip != nil {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// go generate gen.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package ipv4
|
||||
|
||||
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19
|
||||
const (
|
||||
ICMPTypeEchoReply ICMPType = 0 // Echo Reply
|
||||
ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable
|
||||
ICMPTypeRedirect ICMPType = 5 // Redirect
|
||||
ICMPTypeEcho ICMPType = 8 // Echo
|
||||
ICMPTypeRouterAdvertisement ICMPType = 9 // Router Advertisement
|
||||
ICMPTypeRouterSolicitation ICMPType = 10 // Router Solicitation
|
||||
ICMPTypeTimeExceeded ICMPType = 11 // Time Exceeded
|
||||
ICMPTypeParameterProblem ICMPType = 12 // Parameter Problem
|
||||
ICMPTypeTimestamp ICMPType = 13 // Timestamp
|
||||
ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply
|
||||
ICMPTypePhoturis ICMPType = 40 // Photuris
|
||||
)
|
||||
|
||||
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19
|
||||
var icmpTypes = map[ICMPType]string{
|
||||
0: "echo reply",
|
||||
3: "destination unreachable",
|
||||
5: "redirect",
|
||||
8: "echo",
|
||||
9: "router advertisement",
|
||||
10: "router solicitation",
|
||||
11: "time exceeded",
|
||||
12: "parameter problem",
|
||||
13: "timestamp",
|
||||
14: "timestamp reply",
|
||||
40: "photuris",
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2013 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 ipv4
|
||||
|
||||
import "golang.org/x/net/internal/iana"
|
||||
|
||||
// An ICMPType represents a type of ICMP message.
|
||||
type ICMPType int
|
||||
|
||||
func (typ ICMPType) String() string {
|
||||
s, ok := icmpTypes[typ]
|
||||
if !ok {
|
||||
return "<nil>"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Protocol returns the ICMPv4 protocol number.
|
||||
func (typ ICMPType) Protocol() int {
|
||||
return iana.ProtocolICMP
|
||||
}
|
||||
|
||||
// An ICMPFilter represents an ICMP message filter for incoming
|
||||
// packets. The filter belongs to a packet delivery path on a host and
|
||||
// it cannot interact with forwarding packets or tunnel-outer packets.
|
||||
//
|
||||
// Note: RFC 2460 defines a reasonable role model and it works not
|
||||
// only for IPv6 but IPv4. A node means a device that implements IP.
|
||||
// A router means a node that forwards IP packets not explicitly
|
||||
// addressed to itself, and a host means a node that is not a router.
|
||||
type ICMPFilter struct {
|
||||
icmpFilter
|
||||
}
|
||||
|
||||
// Accept accepts incoming ICMP packets including the type field value
|
||||
// typ.
|
||||
func (f *ICMPFilter) Accept(typ ICMPType) {
|
||||
f.accept(typ)
|
||||
}
|
||||
|
||||
// Block blocks incoming ICMP packets including the type field value
|
||||
// typ.
|
||||
func (f *ICMPFilter) Block(typ ICMPType) {
|
||||
f.block(typ)
|
||||
}
|
||||
|
||||
// SetAll sets the filter action to the filter.
|
||||
func (f *ICMPFilter) SetAll(block bool) {
|
||||
f.setAll(block)
|
||||
}
|
||||
|
||||
// WillBlock reports whether the ICMP type will be blocked.
|
||||
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
||||
return f.willBlock(typ)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2014 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 ipv4
|
||||
|
||||
func (f *icmpFilter) accept(typ ICMPType) {
|
||||
f.Data &^= 1 << (uint32(typ) & 31)
|
||||
}
|
||||
|
||||
func (f *icmpFilter) block(typ ICMPType) {
|
||||
f.Data |= 1 << (uint32(typ) & 31)
|
||||
}
|
||||
|
||||
func (f *icmpFilter) setAll(block bool) {
|
||||
if block {
|
||||
f.Data = 1<<32 - 1
|
||||
} else {
|
||||
f.Data = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (f *icmpFilter) willBlock(typ ICMPType) bool {
|
||||
return f.Data&(1<<(uint32(typ)&31)) != 0
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package ipv4
|
||||
|
||||
const sizeofICMPFilter = 0x0
|
||||
|
||||
type icmpFilter struct {
|
||||
}
|
||||
|
||||
func (f *icmpFilter) accept(typ ICMPType) {
|
||||
}
|
||||
|
||||
func (f *icmpFilter) block(typ ICMPType) {
|
||||
}
|
||||
|
||||
func (f *icmpFilter) setAll(block bool) {
|
||||
}
|
||||
|
||||
func (f *icmpFilter) willBlock(typ ICMPType) bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// BUG(mikio): On Windows, the ReadFrom and WriteTo methods of RawConn
|
||||
// are not implemented.
|
||||
|
||||
// A packetHandler represents the IPv4 datagram handler.
|
||||
type packetHandler struct {
|
||||
c *net.IPConn
|
||||
rawOpt
|
||||
}
|
||||
|
||||
func (c *packetHandler) ok() bool { return c != nil && c.c != nil }
|
||||
|
||||
// ReadFrom reads an IPv4 datagram from the endpoint c, copying the
|
||||
// datagram into b. It returns the received datagram as the IPv4
|
||||
// header h, the payload p and the control message cm.
|
||||
func (c *packetHandler) ReadFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) {
|
||||
if !c.ok() {
|
||||
return nil, nil, nil, syscall.EINVAL
|
||||
}
|
||||
oob := newControlMessage(&c.rawOpt)
|
||||
n, oobn, _, src, err := c.c.ReadMsgIP(b, oob)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
var hs []byte
|
||||
if hs, p, err = slicePacket(b[:n]); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if h, err = ParseHeader(hs); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if cm, err = parseControlMessage(oob[:oobn]); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if src != nil && cm != nil {
|
||||
cm.Src = src.IP
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func slicePacket(b []byte) (h, p []byte, err error) {
|
||||
if len(b) < HeaderLen {
|
||||
return nil, nil, errHeaderTooShort
|
||||
}
|
||||
hdrlen := int(b[0]&0x0f) << 2
|
||||
return b[:hdrlen], b[hdrlen:], nil
|
||||
}
|
||||
|
||||
// WriteTo writes an IPv4 datagram through the endpoint c, copying the
|
||||
// datagram from the IPv4 header h and the payload p. The control
|
||||
// message cm allows the datagram path and the outgoing interface to be
|
||||
// specified. Currently only Darwin and Linux support this. The cm
|
||||
// may be nil if control of the outgoing datagram is not required.
|
||||
//
|
||||
// The IPv4 header h must contain appropriate fields that include:
|
||||
//
|
||||
// Version = <must be specified>
|
||||
// Len = <must be specified>
|
||||
// TOS = <must be specified>
|
||||
// TotalLen = <must be specified>
|
||||
// ID = platform sets an appropriate value if ID is zero
|
||||
// FragOff = <must be specified>
|
||||
// TTL = <must be specified>
|
||||
// Protocol = <must be specified>
|
||||
// Checksum = platform sets an appropriate value if Checksum is zero
|
||||
// Src = platform sets an appropriate value if Src is nil
|
||||
// Dst = <must be specified>
|
||||
// Options = optional
|
||||
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
oob := marshalControlMessage(cm)
|
||||
wh, err := h.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst := &net.IPAddr{}
|
||||
if cm != nil {
|
||||
if ip := cm.Dst.To4(); ip != nil {
|
||||
dst.IP = ip
|
||||
}
|
||||
}
|
||||
if dst.IP == nil {
|
||||
dst.IP = h.Dst
|
||||
}
|
||||
wh = append(wh, p...)
|
||||
_, _, err = c.c.WriteMsgIP(wh, oob, dst)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2012 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 ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo
|
||||
// methods of PacketConn is not implemented.
|
||||
|
||||
// A payloadHandler represents the IPv4 datagram payload handler.
|
||||
type payloadHandler struct {
|
||||
net.PacketConn
|
||||
rawOpt
|
||||
}
|
||||
|
||||
func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build !plan9,!windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadFrom reads a payload of the received IPv4 datagram, from the
|
||||
// endpoint c, copying the payload into b. It returns the number of
|
||||
// bytes copied into b, the control message cm and the source address
|
||||
// src of the received datagram.
|
||||
func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
|
||||
if !c.ok() {
|
||||
return 0, nil, nil, syscall.EINVAL
|
||||
}
|
||||
oob := newControlMessage(&c.rawOpt)
|
||||
var oobn int
|
||||
switch c := c.PacketConn.(type) {
|
||||
case *net.UDPConn:
|
||||
if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
case *net.IPConn:
|
||||
if sockOpts[ssoStripHeader].name > 0 {
|
||||
if n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
} else {
|
||||
nb := make([]byte, maxHeaderLen+len(b))
|
||||
if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
hdrlen := int(nb[0]&0x0f) << 2
|
||||
copy(b, nb[hdrlen:])
|
||||
n -= hdrlen
|
||||
}
|
||||
default:
|
||||
return 0, nil, nil, errInvalidConnType
|
||||
}
|
||||
if cm, err = parseControlMessage(oob[:oobn]); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
if cm != nil {
|
||||
cm.Src = netAddrToIP4(src)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo writes a payload of the IPv4 datagram, to the destination
|
||||
// address dst through the endpoint c, copying the payload from b. It
|
||||
// returns the number of bytes written. The control message cm allows
|
||||
// the datagram path and the outgoing interface to be specified.
|
||||
// Currently only Darwin and Linux support this. The cm may be nil if
|
||||
// control of the outgoing datagram is not required.
|
||||
func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
oob := marshalControlMessage(cm)
|
||||
if dst == nil {
|
||||
return 0, errMissingAddress
|
||||
}
|
||||
switch c := c.PacketConn.(type) {
|
||||
case *net.UDPConn:
|
||||
n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
|
||||
case *net.IPConn:
|
||||
n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
|
||||
default:
|
||||
return 0, errInvalidConnType
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build plan9 windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadFrom reads a payload of the received IPv4 datagram, from the
|
||||
// endpoint c, copying the payload into b. It returns the number of
|
||||
// bytes copied into b, the control message cm and the source address
|
||||
// src of the received datagram.
|
||||
func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
|
||||
if !c.ok() {
|
||||
return 0, nil, nil, syscall.EINVAL
|
||||
}
|
||||
if n, src, err = c.PacketConn.ReadFrom(b); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo writes a payload of the IPv4 datagram, to the destination
|
||||
// address dst through the endpoint c, copying the payload from b. It
|
||||
// returns the number of bytes written. The control message cm allows
|
||||
// the datagram path and the outgoing interface to be specified.
|
||||
// Currently only Darwin and Linux support this. The cm may be nil if
|
||||
// control of the outgoing datagram is not required.
|
||||
func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
if dst == nil {
|
||||
return 0, errMissingAddress
|
||||
}
|
||||
return c.PacketConn.WriteTo(b, dst)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2014 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 ipv4
|
||||
|
||||
// Sticky socket options
|
||||
const (
|
||||
ssoTOS = iota // header field for unicast packet
|
||||
ssoTTL // header field for unicast packet
|
||||
ssoMulticastTTL // header field for multicast packet
|
||||
ssoMulticastInterface // outbound interface for multicast packet
|
||||
ssoMulticastLoopback // loopback for multicast packet
|
||||
ssoReceiveTTL // header field on received packet
|
||||
ssoReceiveDst // header field on received packet
|
||||
ssoReceiveInterface // inbound interface on received packet
|
||||
ssoPacketInfo // incbound or outbound packet path
|
||||
ssoHeaderPrepend // ipv4 header prepend
|
||||
ssoStripHeader // strip ipv4 header
|
||||
ssoICMPFilter // icmp filter
|
||||
ssoJoinGroup // any-source multicast
|
||||
ssoLeaveGroup // any-source multicast
|
||||
ssoJoinSourceGroup // source-specific multicast
|
||||
ssoLeaveSourceGroup // source-specific multicast
|
||||
ssoBlockSourceGroup // any-source or source-specific multicast
|
||||
ssoUnblockSourceGroup // any-source or source-specific multicast
|
||||
ssoMax
|
||||
)
|
||||
|
||||
// Sticky socket option value types
|
||||
const (
|
||||
ssoTypeByte = iota + 1
|
||||
ssoTypeInt
|
||||
ssoTypeInterface
|
||||
ssoTypeICMPFilter
|
||||
ssoTypeIPMreq
|
||||
ssoTypeIPMreqn
|
||||
ssoTypeGroupReq
|
||||
ssoTypeGroupSourceReq
|
||||
)
|
||||
|
||||
// A sockOpt represents a binding for sticky socket option.
|
||||
type sockOpt struct {
|
||||
name int // option name, must be equal or greater than 1
|
||||
typ int // option value type, must be equal or greater than 1
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd solaris windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
func setIPMreqInterface(mreq *ipMreq, ifi *net.Interface) error {
|
||||
if ifi == nil {
|
||||
return nil
|
||||
}
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if ip := ifa.IP.To4(); ip != nil {
|
||||
copy(mreq.Interface[:], ip)
|
||||
return nil
|
||||
}
|
||||
case *net.IPNet:
|
||||
if ip := ifa.IP.To4(); ip != nil {
|
||||
copy(mreq.Interface[:], ip)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errNoSuchInterface
|
||||
}
|
||||
|
||||
func netIP4ToInterface(ip net.IP) (*net.Interface, error) {
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifi := range ift {
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if ip.Equal(ifa.IP) {
|
||||
return &ifi, nil
|
||||
}
|
||||
case *net.IPNet:
|
||||
if ip.Equal(ifa.IP) {
|
||||
return &ifi, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errNoSuchInterface
|
||||
}
|
||||
|
||||
func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) {
|
||||
if ifi == nil {
|
||||
return net.IPv4zero.To4(), nil
|
||||
}
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if ip := ifa.IP.To4(); ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
case *net.IPNet:
|
||||
if ip := ifa.IP.To4(); ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errNoSuchInterface
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd solaris windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func setsockoptIPMreq(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := ipMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
|
||||
if err := setIPMreqInterface(&mreq, ifi); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, name, unsafe.Pointer(&mreq), sizeofIPMreq))
|
||||
}
|
||||
|
||||
func getsockoptInterface(s uintptr, name int) (*net.Interface, error) {
|
||||
var b [4]byte
|
||||
l := uint32(4)
|
||||
if err := getsockopt(s, iana.ProtocolIP, name, unsafe.Pointer(&b[0]), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
|
||||
func setsockoptInterface(s uintptr, name int, ifi *net.Interface) error {
|
||||
ip, err := netInterfaceToIP4(ifi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var b [4]byte
|
||||
copy(b[:], ip)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, name, unsafe.Pointer(&b[0]), uint32(4)))
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
func setsockoptIPMreq(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func getsockoptInterface(s uintptr, name int) (*net.Interface, error) {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
func setsockoptInterface(s uintptr, name int, ifi *net.Interface) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build !darwin,!freebsd,!linux
|
||||
|
||||
package ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
func getsockoptIPMreqn(s uintptr, name int) (*net.Interface, error) {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
func setsockoptIPMreqn(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build darwin freebsd linux
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func getsockoptIPMreqn(s uintptr, name int) (*net.Interface, error) {
|
||||
var mreqn ipMreqn
|
||||
l := uint32(sizeofIPMreqn)
|
||||
if err := getsockopt(s, iana.ProtocolIP, name, unsafe.Pointer(&mreqn), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
if mreqn.Ifindex == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ifi, err := net.InterfaceByIndex(int(mreqn.Ifindex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
|
||||
func setsockoptIPMreqn(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
var mreqn ipMreqn
|
||||
if ifi != nil {
|
||||
mreqn.Ifindex = int32(ifi.Index)
|
||||
}
|
||||
if grp != nil {
|
||||
mreqn.Multiaddr = [4]byte{grp[0], grp[1], grp[2], grp[3]}
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, name, unsafe.Pointer(&mreqn), sizeofIPMreqn))
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
func getInt(s uintptr, opt *sockOpt) (int, error) {
|
||||
if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
var i int32
|
||||
var b byte
|
||||
p := unsafe.Pointer(&i)
|
||||
l := uint32(4)
|
||||
if opt.typ == ssoTypeByte {
|
||||
p = unsafe.Pointer(&b)
|
||||
l = 1
|
||||
}
|
||||
if err := getsockopt(s, iana.ProtocolIP, opt.name, p, &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
if opt.typ == ssoTypeByte {
|
||||
return int(b), nil
|
||||
}
|
||||
return int(i), nil
|
||||
}
|
||||
|
||||
func setInt(s uintptr, opt *sockOpt, v int) error {
|
||||
if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
|
||||
return errOpNoSupport
|
||||
}
|
||||
i := int32(v)
|
||||
var b byte
|
||||
p := unsafe.Pointer(&i)
|
||||
l := uint32(4)
|
||||
if opt.typ == ssoTypeByte {
|
||||
b = byte(v)
|
||||
p = unsafe.Pointer(&b)
|
||||
l = 1
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, opt.name, p, l))
|
||||
}
|
||||
|
||||
func getInterface(s uintptr, opt *sockOpt) (*net.Interface, error) {
|
||||
if opt.name < 1 {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
switch opt.typ {
|
||||
case ssoTypeInterface:
|
||||
return getsockoptInterface(s, opt.name)
|
||||
case ssoTypeIPMreqn:
|
||||
return getsockoptIPMreqn(s, opt.name)
|
||||
default:
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
}
|
||||
|
||||
func setInterface(s uintptr, opt *sockOpt, ifi *net.Interface) error {
|
||||
if opt.name < 1 {
|
||||
return errOpNoSupport
|
||||
}
|
||||
switch opt.typ {
|
||||
case ssoTypeInterface:
|
||||
return setsockoptInterface(s, opt.name, ifi)
|
||||
case ssoTypeIPMreqn:
|
||||
return setsockoptIPMreqn(s, opt.name, ifi, nil)
|
||||
default:
|
||||
return errOpNoSupport
|
||||
}
|
||||
}
|
||||
|
||||
func getICMPFilter(s uintptr, opt *sockOpt) (*ICMPFilter, error) {
|
||||
if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
var f ICMPFilter
|
||||
l := uint32(sizeofICMPFilter)
|
||||
if err := getsockopt(s, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.icmpFilter), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func setICMPFilter(s uintptr, opt *sockOpt, f *ICMPFilter) error {
|
||||
if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
|
||||
return errOpNoSupport
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.icmpFilter), sizeofICMPFilter))
|
||||
}
|
||||
|
||||
func setGroup(s uintptr, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
|
||||
if opt.name < 1 {
|
||||
return errOpNoSupport
|
||||
}
|
||||
switch opt.typ {
|
||||
case ssoTypeIPMreq:
|
||||
return setsockoptIPMreq(s, opt.name, ifi, grp)
|
||||
case ssoTypeIPMreqn:
|
||||
return setsockoptIPMreqn(s, opt.name, ifi, grp)
|
||||
case ssoTypeGroupReq:
|
||||
return setsockoptGroupReq(s, opt.name, ifi, grp)
|
||||
default:
|
||||
return errOpNoSupport
|
||||
}
|
||||
}
|
||||
|
||||
func setSourceGroup(s uintptr, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
|
||||
if opt.name < 1 || opt.typ != ssoTypeGroupSourceReq {
|
||||
return errOpNoSupport
|
||||
}
|
||||
return setsockoptGroupSourceReq(s, opt.name, ifi, grp, src)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build !darwin,!freebsd,!linux,!solaris
|
||||
|
||||
package ipv4
|
||||
|
||||
import "net"
|
||||
|
||||
func setsockoptGroupReq(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func setsockoptGroupSourceReq(s uintptr, name int, ifi *net.Interface, grp, src net.IP) error {
|
||||
return errOpNoSupport
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build darwin freebsd linux solaris
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
var freebsd32o64 bool
|
||||
|
||||
func setsockoptGroupReq(s uintptr, name int, ifi *net.Interface, grp net.IP) error {
|
||||
var gr groupReq
|
||||
if ifi != nil {
|
||||
gr.Interface = uint32(ifi.Index)
|
||||
}
|
||||
gr.setGroup(grp)
|
||||
var p unsafe.Pointer
|
||||
var l uint32
|
||||
if freebsd32o64 {
|
||||
var d [sizeofGroupReq + 4]byte
|
||||
s := (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr))
|
||||
copy(d[:4], s[:4])
|
||||
copy(d[8:], s[4:])
|
||||
p = unsafe.Pointer(&d[0])
|
||||
l = sizeofGroupReq + 4
|
||||
} else {
|
||||
p = unsafe.Pointer(&gr)
|
||||
l = sizeofGroupReq
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, name, p, l))
|
||||
}
|
||||
|
||||
func setsockoptGroupSourceReq(s uintptr, name int, ifi *net.Interface, grp, src net.IP) error {
|
||||
var gsr groupSourceReq
|
||||
if ifi != nil {
|
||||
gsr.Interface = uint32(ifi.Index)
|
||||
}
|
||||
gsr.setSourceGroup(grp, src)
|
||||
var p unsafe.Pointer
|
||||
var l uint32
|
||||
if freebsd32o64 {
|
||||
var d [sizeofGroupSourceReq + 4]byte
|
||||
s := (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))
|
||||
copy(d[:4], s[:4])
|
||||
copy(d[8:], s[4:])
|
||||
p = unsafe.Pointer(&d[0])
|
||||
l = sizeofGroupSourceReq + 4
|
||||
} else {
|
||||
p = unsafe.Pointer(&gsr)
|
||||
l = sizeofGroupSourceReq
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(s, iana.ProtocolIP, name, p, l))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue