mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-02 02:49:07 -05:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3915b8dbf | ||
|
|
671d5cace6 | ||
|
|
aa3d73d322 | ||
|
|
d30a286f38 | ||
|
|
15699a39cf | ||
|
|
a1f32095df | ||
|
|
76e0960a51 | ||
|
|
8e33288156 | ||
|
|
f4d3a9980f | ||
|
|
fc8ce7c6e0 | ||
|
|
aaf0604601 | ||
|
|
dddf563105 | ||
|
|
dbe12cca4b | ||
|
|
ddf0ddbd05 | ||
|
|
5eb5a056bf | ||
|
|
2a5c0646c0 | ||
|
|
7d3c51df9e | ||
|
|
a67bb5e720 | ||
|
|
6d314cdc04 | ||
|
|
1139ea2c81 | ||
|
|
f87b1520e8 | ||
|
|
3700eb1e61 | ||
|
|
17a21102b3 | ||
|
|
e37441627f | ||
|
|
f4c6cd1676 | ||
|
|
bcd3fd40e4 | ||
|
|
d3d1a79996 | ||
|
|
fb4a2c9b5a |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
syncthing
|
||||
syncthing.exe
|
||||
stcli
|
||||
stcli.exe
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.asc
|
||||
|
||||
@@ -1,9 +1,41 @@
|
||||
Please do contribute!
|
||||
Please do contribute! If you want to contribute but are unsure where to
|
||||
start, the [Contributions Needed
|
||||
page](https://github.com/calmh/syncthing/wiki/Contributions-Needed)
|
||||
lists areas in need of attention.
|
||||
|
||||
## Licensing
|
||||
|
||||
All contributions are made under the same MIT License as the rest of the
|
||||
project, except documentation which is licensed under the Creative
|
||||
Commons Attribution 4.0 International License. You retain the copyright
|
||||
to code you have written.
|
||||
|
||||
When accepting your first contribution, the maintainer of the project
|
||||
will ensure that you are added to the CONTRIBUTORS file.
|
||||
|
||||
## Building
|
||||
|
||||
[See the wiki](https://github.com/calmh/syncthing/wiki/Building)
|
||||
|
||||
## Branches
|
||||
|
||||
- `master` is the main branch containing good code that will end up in
|
||||
the next release. You should base your work on it. It won't ever be
|
||||
rebased or force-pushed to.
|
||||
|
||||
- `vx.y` branches exist to make patch releases on otherwise obsolete
|
||||
minor releases. Should only contain fixes cherry picked from master.
|
||||
Don't base any work on them.
|
||||
|
||||
- Other branches are probably topic branches and may be subject to
|
||||
rebasing. Don't base any work on them unless you specifically know
|
||||
otherwise.
|
||||
|
||||
## Tags
|
||||
|
||||
All releases are tagged semver style as `vx.y.z`. Release tags are
|
||||
signed by GPG key BCE524C7.
|
||||
|
||||
## Tests
|
||||
|
||||
Yes please!
|
||||
|
||||
4
CONTRIBUTORS
Normal file
4
CONTRIBUTORS
Normal file
@@ -0,0 +1,4 @@
|
||||
Aaron Bieber <qbit@deftly.net>
|
||||
Brandon Philips <brandon@ifup.org>
|
||||
James Patterson <jamespatterson@operamail.com>
|
||||
Philippe Schommers <philippe@schommers.be>
|
||||
11
Godeps/Godeps.json
generated
11
Godeps/Godeps.json
generated
@@ -1,15 +1,10 @@
|
||||
{
|
||||
"ImportPath": "github.com/calmh/syncthing",
|
||||
"GoVersion": "devel +3ca54dd30864 Sat Mar 22 11:05:40 2014 -0700",
|
||||
"GoVersion": "go1.2.1",
|
||||
"Packages": [
|
||||
"./cmd/syncthing"
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.net/ipv6",
|
||||
"Comment": "null-117",
|
||||
"Rev": "c17ad62118ea511e1051721b429779fa40bddc74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.text/transform",
|
||||
"Comment": "null-81",
|
||||
@@ -32,6 +27,10 @@
|
||||
"ImportPath": "github.com/codegangsta/martini",
|
||||
"Comment": "v0.1-142-g8659df7",
|
||||
"Rev": "8659df7a51aebe6c6120268cd5a8b4c34fa8441a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/juju/ratelimit",
|
||||
"Rev": "cbaa435c80a9716e086f25d409344b26c4039358"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
83
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go
generated
vendored
83
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
)
|
||||
|
||||
// References:
|
||||
//
|
||||
// RFC 2292 Advanced Sockets API for IPv6
|
||||
// http://tools.ietf.org/html/rfc2292
|
||||
// RFC 2460 Internet Protocol, Version 6 (IPv6) Specification
|
||||
// http://tools.ietf.org/html/rfc2460
|
||||
// RFC 3493 Basic Socket Interface Extensions for IPv6
|
||||
// http://tools.ietf.org/html/rfc3493.html
|
||||
// RFC 3542 Advanced Sockets Application Program Interface (API) for IPv6
|
||||
// http://tools.ietf.org/html/rfc3542
|
||||
//
|
||||
// Note that RFC 3542 obsoltes RFC 2292 but OS X Snow Leopard and the
|
||||
// former still support RFC 2292 only. Please be aware that almost
|
||||
// all protocol implementations prohibit using a combination of RFC
|
||||
// 2292 and RFC 3542 for some practical reasons.
|
||||
|
||||
type rawOpt struct {
|
||||
sync.Mutex
|
||||
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 }
|
||||
|
||||
// A ControlFlags reprensents per packet basis IP-level socket option
|
||||
// control flags.
|
||||
type ControlFlags uint
|
||||
|
||||
const (
|
||||
FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
|
||||
FlagHopLimit // pass the hop limit 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
|
||||
FlagPathMTU // pass the path MTU on the received packet path
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Specifying socket options: ControlMessage for WriteTo
|
||||
// method of PacketConn allows to send the options to the
|
||||
// protocol stack.
|
||||
//
|
||||
TrafficClass int // traffic class, must be 1 <= value <= 255 when specifying
|
||||
HopLimit int // hop limit, must be 1 <= value <= 255 when specifying
|
||||
Src net.IP // source address, specifying only
|
||||
Dst net.IP // destination address, receiving only
|
||||
IfIndex int // interface index, must be 1 <= value when specifying
|
||||
NextHop net.IP // next hop address, specifying only
|
||||
MTU int // path MTU, receiving only
|
||||
}
|
||||
|
||||
func (cm *ControlMessage) String() string {
|
||||
if cm == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("tclass: %#x, hoplim: %v, src: %v, dst: %v, ifindex: %v, nexthop: %v, mtu: %v", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
|
||||
}
|
||||
151
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go
generated
vendored
151
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go
generated
vendored
@@ -1,151 +0,0 @@
|
||||
// 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 darwin
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const pktinfo = FlagDst | FlagInterface
|
||||
|
||||
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
opt.Lock()
|
||||
defer opt.Unlock()
|
||||
if cf&FlagHopLimit != 0 {
|
||||
if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagHopLimit)
|
||||
} else {
|
||||
opt.clear(FlagHopLimit)
|
||||
}
|
||||
}
|
||||
if cf&pktinfo != 0 {
|
||||
if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(cf & pktinfo)
|
||||
} else {
|
||||
opt.clear(cf & pktinfo)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||
opt.Lock()
|
||||
defer opt.Unlock()
|
||||
l, off := 0, 0
|
||||
if opt.isset(FlagHopLimit) {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(pktinfo) {
|
||||
l += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
if opt.isset(FlagHopLimit) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockopt2292HopLimit
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(pktinfo) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockopt2292PacketInfo
|
||||
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
|
||||
off += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
}
|
||||
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 != ianaProtocolIPv6 {
|
||||
continue
|
||||
}
|
||||
switch m.Header.Type {
|
||||
case sysSockopt2292HopLimit:
|
||||
cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
|
||||
case sysSockopt2292PacketInfo:
|
||||
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
|
||||
cm.IfIndex = int(pi.IfIndex)
|
||||
cm.Dst = pi.IP[:]
|
||||
}
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||
if cm == nil {
|
||||
return
|
||||
}
|
||||
l, off := 0, 0
|
||||
if cm.HopLimit > 0 {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
pion := false
|
||||
if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
|
||||
pion = true
|
||||
l += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if len(cm.NextHop) == net.IPv6len {
|
||||
l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
if cm.HopLimit > 0 {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockopt2292HopLimit
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
data := oob[off+syscall.CmsgLen(0):]
|
||||
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if pion {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockopt2292PacketInfo
|
||||
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
|
||||
pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
|
||||
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||
copy(pi.IP[:], ip)
|
||||
}
|
||||
if cm.IfIndex != 0 {
|
||||
pi.IfIndex = uint32(cm.IfIndex)
|
||||
}
|
||||
off += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if len(cm.NextHop) == net.IPv6len {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockopt2292NextHop
|
||||
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
|
||||
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
|
||||
setSockaddr(sa, cm.NextHop, cm.IfIndex)
|
||||
off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
27
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go
generated
vendored
27
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil
|
||||
}
|
||||
210
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go
generated
vendored
210
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go
generated
vendored
@@ -1,210 +0,0 @@
|
||||
// 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 freebsd linux netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const pktinfo = FlagDst | FlagInterface
|
||||
|
||||
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
opt.Lock()
|
||||
defer opt.Unlock()
|
||||
if cf&FlagTrafficClass != 0 {
|
||||
if err := setIPv6ReceiveTrafficClass(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagTrafficClass)
|
||||
} else {
|
||||
opt.clear(FlagTrafficClass)
|
||||
}
|
||||
}
|
||||
if cf&FlagHopLimit != 0 {
|
||||
if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagHopLimit)
|
||||
} else {
|
||||
opt.clear(FlagHopLimit)
|
||||
}
|
||||
}
|
||||
if cf&pktinfo != 0 {
|
||||
if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(cf & pktinfo)
|
||||
} else {
|
||||
opt.clear(cf & pktinfo)
|
||||
}
|
||||
}
|
||||
if cf&FlagPathMTU != 0 {
|
||||
if err := setIPv6ReceivePathMTU(fd, on); err != nil {
|
||||
return err
|
||||
}
|
||||
if on {
|
||||
opt.set(FlagPathMTU)
|
||||
} else {
|
||||
opt.clear(FlagPathMTU)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||
opt.Lock()
|
||||
defer opt.Unlock()
|
||||
l, off := 0, 0
|
||||
if opt.isset(FlagTrafficClass) {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(FlagHopLimit) {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(pktinfo) {
|
||||
l += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if opt.isset(FlagPathMTU) {
|
||||
l += syscall.CmsgSpace(sysSizeofMTUInfo)
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
if opt.isset(FlagTrafficClass) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptReceiveTrafficClass
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(FlagHopLimit) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptReceiveHopLimit
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if opt.isset(pktinfo) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptReceivePacketInfo
|
||||
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
|
||||
off += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if opt.isset(FlagPathMTU) {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptReceivePathMTU
|
||||
m.SetLen(syscall.CmsgLen(sysSizeofMTUInfo))
|
||||
off += syscall.CmsgSpace(sysSizeofMTUInfo)
|
||||
}
|
||||
}
|
||||
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 != ianaProtocolIPv6 {
|
||||
continue
|
||||
}
|
||||
switch m.Header.Type {
|
||||
case sysSockoptTrafficClass:
|
||||
cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
|
||||
case sysSockoptHopLimit:
|
||||
cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
|
||||
case sysSockoptPacketInfo:
|
||||
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
|
||||
cm.Dst = pi.IP[:]
|
||||
cm.IfIndex = int(pi.IfIndex)
|
||||
case sysSockoptPathMTU:
|
||||
mi := (*sysMTUInfo)(unsafe.Pointer(&m.Data[0]))
|
||||
cm.Dst = mi.Addr.Addr[:]
|
||||
cm.IfIndex = int(mi.Addr.Scope_id)
|
||||
cm.MTU = int(mi.MTU)
|
||||
}
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||
if cm == nil {
|
||||
return
|
||||
}
|
||||
l, off := 0, 0
|
||||
if cm.TrafficClass > 0 {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
if cm.HopLimit > 0 {
|
||||
l += syscall.CmsgSpace(4)
|
||||
}
|
||||
pion := false
|
||||
if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
|
||||
pion = true
|
||||
l += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if len(cm.NextHop) == net.IPv6len {
|
||||
l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
|
||||
}
|
||||
if l > 0 {
|
||||
oob = make([]byte, l)
|
||||
if cm.TrafficClass > 0 {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptTrafficClass
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
data := oob[off+syscall.CmsgLen(0):]
|
||||
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if cm.HopLimit > 0 {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptHopLimit
|
||||
m.SetLen(syscall.CmsgLen(4))
|
||||
data := oob[off+syscall.CmsgLen(0):]
|
||||
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
|
||||
off += syscall.CmsgSpace(4)
|
||||
}
|
||||
if pion {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptPacketInfo
|
||||
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
|
||||
pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
|
||||
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||
copy(pi.IP[:], ip)
|
||||
}
|
||||
if cm.IfIndex != 0 {
|
||||
pi.IfIndex = uint32(cm.IfIndex)
|
||||
}
|
||||
off += syscall.CmsgSpace(sysSizeofPacketInfo)
|
||||
}
|
||||
if len(cm.NextHop) == net.IPv6len {
|
||||
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
||||
m.Level = ianaProtocolIPv6
|
||||
m.Type = sysSockoptNextHop
|
||||
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
|
||||
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
|
||||
setSockaddr(sa, cm.NextHop, cm.IfIndex)
|
||||
off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
27
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_windows.go
generated
vendored
27
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_windows.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func newControlMessage(opt *rawOpt) (oob []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) (oob []byte) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil
|
||||
}
|
||||
178
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_posix.go
generated
vendored
178
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_posix.go
generated
vendored
@@ -1,178 +0,0 @@
|
||||
// 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 darwin freebsd linux netbsd openbsd windows
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// MulticastHopLimit returns the hop limit field value for outgoing
|
||||
// multicast packets.
|
||||
func (c *dgramOpt) MulticastHopLimit() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ipv6MulticastHopLimit(fd)
|
||||
}
|
||||
|
||||
// SetMulticastHopLimit sets the hop limit field value for future
|
||||
// outgoing multicast packets.
|
||||
func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6MulticastHopLimit(fd, hoplim)
|
||||
}
|
||||
|
||||
// MulticastInterface returns the default interface for multicast
|
||||
// packet transmissions.
|
||||
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||
if !c.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ipv6MulticastInterface(fd)
|
||||
}
|
||||
|
||||
// SetMulticastInterface sets the default interface for future
|
||||
// multicast packet transmissions.
|
||||
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6MulticastInterface(fd, 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
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ipv6MulticastLoopback(fd)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6MulticastLoopback(fd, on)
|
||||
}
|
||||
|
||||
// JoinGroup joins the group address group on the interface ifi.
|
||||
// It 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
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP16(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return joinIPv6Group(fd, ifi, grp)
|
||||
}
|
||||
|
||||
// LeaveGroup leaves the group address group on the interface ifi.
|
||||
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
grp := netAddrToIP16(group)
|
||||
if grp == nil {
|
||||
return errMissingAddress
|
||||
}
|
||||
return leaveIPv6Group(fd, ifi, grp)
|
||||
}
|
||||
|
||||
// Checksum reports whether the kernel will compute, store or verify a
|
||||
// checksum for both incoming and outgoing packets. If on is true, it
|
||||
// returns an offset in bytes into the data of where the checksum
|
||||
// field is located.
|
||||
func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
|
||||
if !c.ok() {
|
||||
return false, 0, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
return ipv6Checksum(fd)
|
||||
}
|
||||
|
||||
// SetChecksum enables the kernel checksum processing. If on is ture,
|
||||
// the offset should be an offset in bytes into the data of where the
|
||||
// checksum field is located.
|
||||
func (c *dgramOpt) SetChecksum(on bool, offset int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6Checksum(fd, on, offset)
|
||||
}
|
||||
|
||||
// ICMPFilter returns an ICMP filter.
|
||||
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||
if !c.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ipv6ICMPFilter(fd)
|
||||
}
|
||||
|
||||
// SetICMPFilter deploys the ICMP filter.
|
||||
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6ICMPFilter(fd, f)
|
||||
}
|
||||
95
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go
generated
vendored
95
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go
generated
vendored
@@ -1,95 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
import "net"
|
||||
|
||||
// MulticastHopLimit returns the hop limit field value for outgoing
|
||||
// multicast packets.
|
||||
func (c *dgramOpt) MulticastHopLimit() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetMulticastHopLimit sets the hop limit field value for future
|
||||
// outgoing multicast packets.
|
||||
func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// MulticastInterface returns the default interface for multicast
|
||||
// packet transmissions.
|
||||
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetMulticastInterface sets the default interface for future
|
||||
// multicast packet transmissions.
|
||||
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// MulticastLoopback reports whether transmitted multicast packets
|
||||
// should be copied and send back to the originator.
|
||||
func (c *dgramOpt) MulticastLoopback() (bool, error) {
|
||||
// TODO(mikio): Implement this
|
||||
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 {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// JoinGroup joins the group address group on the interface ifi.
|
||||
// It 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 {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// LeaveGroup leaves the group address group on the interface ifi.
|
||||
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// Checksum reports whether the kernel will compute, store or verify a
|
||||
// checksum for both incoming and outgoing packets. If on is true, it
|
||||
// returns an offset in bytes into the data of where the checksum
|
||||
// field is located.
|
||||
func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetChecksum enables the kernel checksum processing. If on is ture,
|
||||
// the offset should be an offset in bytes into the data of where the
|
||||
// checksum field is located.
|
||||
func (c *dgramOpt) SetChecksum(on bool, offset int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// ICMPFilter returns an ICMP filter.
|
||||
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetICMPFilter deploys the ICMP filter.
|
||||
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
193
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/doc.go
generated
vendored
193
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/doc.go
generated
vendored
@@ -1,193 +0,0 @@
|
||||
// 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 ipv6 implements IP-level socket options for the Internet
|
||||
// Protocol version 6.
|
||||
//
|
||||
// The package provides IP-level socket options that allow
|
||||
// manipulation of IPv6 facilities. The IPv6 and socket options for
|
||||
// IPv6 are defined in RFC 2460, RFC 3493 and RFC 3542.
|
||||
//
|
||||
//
|
||||
// Unicasting
|
||||
//
|
||||
// The options for unicasting are available for net.TCPConn,
|
||||
// net.UDPConn and net.IPConn which are created as network connections
|
||||
// that use the IPv6 transport. When a single TCP connection carrying
|
||||
// a data flow of multiple packets needs to indicate the flow is
|
||||
// important, ipv6.Conn is used to set the traffic class field on the
|
||||
// IPv6 header for each packet.
|
||||
//
|
||||
// ln, err := net.Listen("tcp6", "[::]: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, as known as AF11 packets.
|
||||
//
|
||||
// if err := ipv6.NewConn(c).SetTrafficClass(DiffServAF11); 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
|
||||
// IPv6 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.ParseIP("ff02::114")
|
||||
//
|
||||
// First, an application listens to an appropriate address with an
|
||||
// appropriate service port.
|
||||
//
|
||||
// c, err := net.ListenPacket("udp6", "[::]: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 IPv6 and Ethernet.
|
||||
//
|
||||
// p := ipv6.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 ipv6.PacketConn is used to enable control
|
||||
// message transmissons.
|
||||
//
|
||||
// if err := p.SetControlMessage(ipv6.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, rcm, src, err := p.ReadFrom(b)
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if rcm.Dst.IsMulticast() {
|
||||
// if rcm.Dst.Equal(group)
|
||||
// // joined group, do something
|
||||
// } else {
|
||||
// // unknown group, discard
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The application can also send both unicast and multicast packets.
|
||||
//
|
||||
// p.SetTrafficClass(DiffServCS0)
|
||||
// p.SetHopLimit(16)
|
||||
// if _, err := p.WriteTo(data[:n], nil, src); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// dst := &net.UDPAddr{IP: group, Port: 1024}
|
||||
// wcm := ipv6.ControlMessage{TrafficClass: DiffServCS7, HopLimit: 1}
|
||||
// for _, ifi := range []*net.Interface{en0, en1} {
|
||||
// wcm.IfIndex = ifi.Index
|
||||
// if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// More multicasting
|
||||
//
|
||||
// An application that uses PacketConn 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("udp6", "[::]:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c.Close()
|
||||
// p := ipv6.NewPacketConn(c)
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); 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("udp6", "[ff02::]:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c1.Close()
|
||||
// c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
|
||||
// if err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// defer c2.Close()
|
||||
// p1 := ipv6.NewPacketConn(c1)
|
||||
// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// p2 := ipv6.NewPacketConn(c2)
|
||||
// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); 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.ParseIP("ff02::114")}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
|
||||
// // error handling
|
||||
// }
|
||||
package ipv6
|
||||
119
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/endpoint.go
generated
vendored
119
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/endpoint.go
generated
vendored
@@ -1,119 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Conn represents a network endpoint that uses IPv6 transport.
|
||||
// It allows to set basic IP-level socket options such as traffic
|
||||
// class and hop limit.
|
||||
type Conn struct {
|
||||
genericOpt
|
||||
}
|
||||
|
||||
type genericOpt struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
|
||||
|
||||
// PathMTU returns a path MTU value for the destination associated
|
||||
// with the endpoint.
|
||||
func (c *Conn) PathMTU() (int, error) {
|
||||
if !c.genericOpt.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.genericOpt.sysfd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ipv6PathMTU(fd)
|
||||
}
|
||||
|
||||
// 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 IPv6
|
||||
// transport. It is used to control several IP-level socket options
|
||||
// including IPv6 header manipulation. It also provides datagram
|
||||
// based network I/O methods specific to the IPv6 and higher layer
|
||||
// protocols such as OSPF, GRE, and UDP.
|
||||
type PacketConn struct {
|
||||
genericOpt
|
||||
dgramOpt
|
||||
payloadHandler
|
||||
}
|
||||
|
||||
type dgramOpt struct {
|
||||
net.PacketConn
|
||||
}
|
||||
|
||||
func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
|
||||
|
||||
// SetControlMessage allows to receive the per packet basis IP-level
|
||||
// socket options.
|
||||
func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.payloadHandler.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setControlMessage(fd, &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.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.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.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// Close closes the endpoint.
|
||||
func (c *PacketConn) Close() error {
|
||||
if !c.payloadHandler.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return c.payloadHandler.Close()
|
||||
}
|
||||
|
||||
// NewPacketConn returns a new PacketConn using c as its underlying
|
||||
// transport.
|
||||
func NewPacketConn(c net.PacketConn) *PacketConn {
|
||||
return &PacketConn{
|
||||
genericOpt: genericOpt{Conn: c.(net.Conn)},
|
||||
dgramOpt: dgramOpt{PacketConn: c},
|
||||
payloadHandler: payloadHandler{PacketConn: c},
|
||||
}
|
||||
}
|
||||
241
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/gen.go
generated
vendored
241
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/gen.go
generated
vendored
@@ -1,241 +0,0 @@
|
||||
// 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
|
||||
|
||||
// This program generates internet protocol constatns and tables by
|
||||
// reading IANA protocol registries.
|
||||
//
|
||||
// Usage:
|
||||
// go run gen.go > iana.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var registries = []struct {
|
||||
url string
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
|
||||
parseICMPv6Parameters,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go run gen.go\n")
|
||||
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(&bb, "package ipv6\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)
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
}
|
||||
|
||||
func parseICMPv6Parameters(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var icp icmpv6Parameters
|
||||
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.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", pr.OrigName)
|
||||
}
|
||||
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.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName))
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type icmpv6Parameters 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"`
|
||||
Name string `xml:"name"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
type canonICMPv6ParamRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord {
|
||||
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([]canonICMPv6ParamRecord, len(icp.Registries[id].Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Messages", "",
|
||||
"Message", "",
|
||||
"ICMP", "",
|
||||
"+", "P",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, pr := range icp.Registries[id].Records {
|
||||
if strings.Contains(pr.Name, "Reserved") ||
|
||||
strings.Contains(pr.Name, "Unassigned") ||
|
||||
strings.Contains(pr.Name, "Deprecated") ||
|
||||
strings.Contains(pr.Name, "Experiment") ||
|
||||
strings.Contains(pr.Name, "experiment") {
|
||||
continue
|
||||
}
|
||||
ss := strings.Split(pr.Name, "\n")
|
||||
if len(ss) > 1 {
|
||||
prs[i].Name = strings.Join(ss, " ")
|
||||
} else {
|
||||
prs[i].Name = ss[0]
|
||||
}
|
||||
s := strings.TrimSpace(prs[i].Name)
|
||||
prs[i].OrigName = s
|
||||
prs[i].Name = sr.Replace(s)
|
||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||
}
|
||||
return prs
|
||||
}
|
||||
|
||||
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()
|
||||
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, "ianaProtocol%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 {
|
||||
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
|
||||
}
|
||||
60
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_posix.go
generated
vendored
60
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_posix.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
// 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 darwin freebsd linux netbsd openbsd windows
|
||||
|
||||
package ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
// TrafficClass returns the traffic class field value for outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) TrafficClass() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ipv6TrafficClass(fd)
|
||||
}
|
||||
|
||||
// SetTrafficClass sets the traffic class field value for future
|
||||
// outgoing packets.
|
||||
func (c *genericOpt) SetTrafficClass(tclass int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6TrafficClass(fd, tclass)
|
||||
}
|
||||
|
||||
// HopLimit returns the hop limit field value for outgoing packets.
|
||||
func (c *genericOpt) HopLimit() (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ipv6HopLimit(fd)
|
||||
}
|
||||
|
||||
// SetHopLimit sets the hop limit field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetHopLimit(hoplim int) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd, err := c.sysfd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setIPv6HopLimit(fd, hoplim)
|
||||
}
|
||||
34
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_stub.go
generated
vendored
34
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_stub.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
// TrafficClass returns the traffic class field value for outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) TrafficClass() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetTrafficClass sets the traffic class field value for future
|
||||
// outgoing packets.
|
||||
func (c *genericOpt) SetTrafficClass(tclass int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
// HopLimit returns the hop limit field value for outgoing packets.
|
||||
func (c *genericOpt) HopLimit() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
// SetHopLimit sets the hop limit field value for future outgoing
|
||||
// packets.
|
||||
func (c *genericOpt) SetHopLimit(hoplim int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return errOpNoSupport
|
||||
}
|
||||
195
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/gentest.go
generated
vendored
195
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/gentest.go
generated
vendored
@@ -1,195 +0,0 @@
|
||||
// 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
|
||||
|
||||
// This program generates internet protocol constants by reading IANA
|
||||
// protocol registries.
|
||||
//
|
||||
// Usage:
|
||||
// go run gentest.go > iana_test.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"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,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go run gentest.go\n")
|
||||
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(&bb, "package ipv6_test\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)
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go
generated
vendored
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var errOpNoSupport = errors.New("operation not supported")
|
||||
|
||||
func boolint(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func netAddrToIP16(a net.Addr) net.IP {
|
||||
switch v := a.(type) {
|
||||
case *net.UDPAddr:
|
||||
if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
|
||||
return ip
|
||||
}
|
||||
case *net.IPAddr:
|
||||
if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_stub.go
generated
vendored
22
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_stub.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
func (c *genericOpt) sysfd() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
func (c *dgramOpt) sysfd() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
func (c *payloadHandler) sysfd() (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
46
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_unix.go
generated
vendored
46
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_unix.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
// 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 darwin freebsd linux netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func (c *genericOpt) sysfd() (int, error) {
|
||||
switch p := c.Conn.(type) {
|
||||
case *net.TCPConn, *net.UDPConn, *net.IPConn:
|
||||
return sysfd(p)
|
||||
}
|
||||
return 0, errInvalidConnType
|
||||
}
|
||||
|
||||
func (c *dgramOpt) sysfd() (int, error) {
|
||||
switch p := c.PacketConn.(type) {
|
||||
case *net.UDPConn, *net.IPConn:
|
||||
return sysfd(p.(net.Conn))
|
||||
}
|
||||
return 0, errInvalidConnType
|
||||
}
|
||||
|
||||
func (c *payloadHandler) sysfd() (int, error) {
|
||||
return sysfd(c.PacketConn.(net.Conn))
|
||||
}
|
||||
|
||||
func sysfd(c net.Conn) (int, error) {
|
||||
cv := reflect.ValueOf(c)
|
||||
switch ce := cv.Elem(); ce.Kind() {
|
||||
case reflect.Struct:
|
||||
nfd := ce.FieldByName("conn").FieldByName("fd")
|
||||
switch fe := nfd.Elem(); fe.Kind() {
|
||||
case reflect.Struct:
|
||||
fd := fe.FieldByName("sysfd")
|
||||
return int(fd.Int()), nil
|
||||
}
|
||||
}
|
||||
return 0, errInvalidConnType
|
||||
}
|
||||
45
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_windows.go
generated
vendored
45
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_windows.go
generated
vendored
@@ -1,45 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (c *genericOpt) sysfd() (syscall.Handle, error) {
|
||||
switch p := c.Conn.(type) {
|
||||
case *net.TCPConn, *net.UDPConn, *net.IPConn:
|
||||
return sysfd(p)
|
||||
}
|
||||
return syscall.InvalidHandle, errInvalidConnType
|
||||
}
|
||||
|
||||
func (c *dgramOpt) sysfd() (syscall.Handle, error) {
|
||||
switch p := c.PacketConn.(type) {
|
||||
case *net.UDPConn, *net.IPConn:
|
||||
return sysfd(p.(net.Conn))
|
||||
}
|
||||
return syscall.InvalidHandle, errInvalidConnType
|
||||
}
|
||||
|
||||
func (c *payloadHandler) sysfd() (syscall.Handle, error) {
|
||||
return sysfd(c.PacketConn.(net.Conn))
|
||||
}
|
||||
|
||||
func sysfd(c net.Conn) (syscall.Handle, error) {
|
||||
cv := reflect.ValueOf(c)
|
||||
switch ce := cv.Elem(); ce.Kind() {
|
||||
case reflect.Struct:
|
||||
netfd := ce.FieldByName("conn").FieldByName("fd")
|
||||
switch fe := netfd.Elem(); fe.Kind() {
|
||||
case reflect.Struct:
|
||||
fd := fe.FieldByName("sysfd")
|
||||
return syscall.Handle(fd.Uint()), nil
|
||||
}
|
||||
}
|
||||
return syscall.InvalidHandle, errInvalidConnType
|
||||
}
|
||||
224
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/iana.go
generated
vendored
224
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/iana.go
generated
vendored
@@ -1,224 +0,0 @@
|
||||
// go run gen.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package ipv6
|
||||
|
||||
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03
|
||||
const (
|
||||
ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable
|
||||
ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big
|
||||
ICMPTypeTimeExceeded ICMPType = 3 // Time Exceeded
|
||||
ICMPTypeParameterProblem ICMPType = 4 // Parameter Problem
|
||||
ICMPTypeEchoRequest ICMPType = 128 // Echo Request
|
||||
ICMPTypeEchoReply ICMPType = 129 // Echo Reply
|
||||
ICMPTypeMulticastListenerQuery ICMPType = 130 // Multicast Listener Query
|
||||
ICMPTypeMulticastListenerReport ICMPType = 131 // Multicast Listener Report
|
||||
ICMPTypeMulticastListenerDone ICMPType = 132 // Multicast Listener Done
|
||||
ICMPTypeRouterSolicitation ICMPType = 133 // Router Solicitation
|
||||
ICMPTypeRouterAdvertisement ICMPType = 134 // Router Advertisement
|
||||
ICMPTypeNeighborSolicitation ICMPType = 135 // Neighbor Solicitation
|
||||
ICMPTypeNeighborAdvertisement ICMPType = 136 // Neighbor Advertisement
|
||||
ICMPTypeRedirect ICMPType = 137 // Redirect Message
|
||||
ICMPTypeRouterRenumbering ICMPType = 138 // Router Renumbering
|
||||
ICMPTypeNodeInformationQuery ICMPType = 139 // ICMP Node Information Query
|
||||
ICMPTypeNodeInformationResponse ICMPType = 140 // ICMP Node Information Response
|
||||
ICMPTypeInverseNeighborDiscoverySolicitation ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message
|
||||
ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message
|
||||
ICMPTypeVersion2MulticastListenerReport ICMPType = 143 // Version 2 Multicast Listener Report
|
||||
ICMPTypeHomeAgentAddressDiscoveryRequest ICMPType = 144 // Home Agent Address Discovery Request Message
|
||||
ICMPTypeHomeAgentAddressDiscoveryReply ICMPType = 145 // Home Agent Address Discovery Reply Message
|
||||
ICMPTypeMobilePrefixSolicitation ICMPType = 146 // Mobile Prefix Solicitation
|
||||
ICMPTypeMobilePrefixAdvertisement ICMPType = 147 // Mobile Prefix Advertisement
|
||||
ICMPTypeCertificationPathSolicitation ICMPType = 148 // Certification Path Solicitation Message
|
||||
ICMPTypeCertificationPathAdvertisement ICMPType = 149 // Certification Path Advertisement Message
|
||||
ICMPTypeMulticastRouterAdvertisement ICMPType = 151 // Multicast Router Advertisement
|
||||
ICMPTypeMulticastRouterSolicitation ICMPType = 152 // Multicast Router Solicitation
|
||||
ICMPTypeMulticastRouterTermination ICMPType = 153 // Multicast Router Termination
|
||||
ICMPTypeFMIPv6 ICMPType = 154 // FMIPv6 Messages
|
||||
ICMPTypeRPLControl ICMPType = 155 // RPL Control Message
|
||||
ICMPTypeILNPv6LocatorUpdate ICMPType = 156 // ILNPv6 Locator Update Message
|
||||
ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request
|
||||
ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation
|
||||
)
|
||||
|
||||
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03
|
||||
var icmpTypes = map[ICMPType]string{
|
||||
1: "destination unreachable",
|
||||
2: "packet too big",
|
||||
3: "time exceeded",
|
||||
4: "parameter problem",
|
||||
128: "echo request",
|
||||
129: "echo reply",
|
||||
130: "multicast listener query",
|
||||
131: "multicast listener report",
|
||||
132: "multicast listener done",
|
||||
133: "router solicitation",
|
||||
134: "router advertisement",
|
||||
135: "neighbor solicitation",
|
||||
136: "neighbor advertisement",
|
||||
137: "redirect message",
|
||||
138: "router renumbering",
|
||||
139: "icmp node information query",
|
||||
140: "icmp node information response",
|
||||
141: "inverse neighbor discovery solicitation message",
|
||||
142: "inverse neighbor discovery advertisement message",
|
||||
143: "version 2 multicast listener report",
|
||||
144: "home agent address discovery request message",
|
||||
145: "home agent address discovery reply message",
|
||||
146: "mobile prefix solicitation",
|
||||
147: "mobile prefix advertisement",
|
||||
148: "certification path solicitation message",
|
||||
149: "certification path advertisement message",
|
||||
151: "multicast router advertisement",
|
||||
152: "multicast router solicitation",
|
||||
153: "multicast router termination",
|
||||
154: "fmipv6 messages",
|
||||
155: "rpl control message",
|
||||
156: "ilnpv6 locator update message",
|
||||
157: "duplicate address request",
|
||||
158: "duplicate address confirmation",
|
||||
}
|
||||
|
||||
// Protocol Numbers, Updated: 2013-02-17
|
||||
const (
|
||||
ianaProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
||||
ianaProtocolICMP = 1 // Internet Control Message
|
||||
ianaProtocolIGMP = 2 // Internet Group Management
|
||||
ianaProtocolGGP = 3 // Gateway-to-Gateway
|
||||
ianaProtocolIPv4 = 4 // IPv4 encapsulation
|
||||
ianaProtocolST = 5 // Stream
|
||||
ianaProtocolTCP = 6 // Transmission Control
|
||||
ianaProtocolCBT = 7 // CBT
|
||||
ianaProtocolEGP = 8 // Exterior Gateway Protocol
|
||||
ianaProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||
ianaProtocolBBNRCCMON = 10 // BBN RCC Monitoring
|
||||
ianaProtocolNVPII = 11 // Network Voice Protocol
|
||||
ianaProtocolPUP = 12 // PUP
|
||||
ianaProtocolARGUS = 13 // ARGUS
|
||||
ianaProtocolEMCON = 14 // EMCON
|
||||
ianaProtocolXNET = 15 // Cross Net Debugger
|
||||
ianaProtocolCHAOS = 16 // Chaos
|
||||
ianaProtocolUDP = 17 // User Datagram
|
||||
ianaProtocolMUX = 18 // Multiplexing
|
||||
ianaProtocolDCNMEAS = 19 // DCN Measurement Subsystems
|
||||
ianaProtocolHMP = 20 // Host Monitoring
|
||||
ianaProtocolPRM = 21 // Packet Radio Measurement
|
||||
ianaProtocolXNSIDP = 22 // XEROX NS IDP
|
||||
ianaProtocolTRUNK1 = 23 // Trunk-1
|
||||
ianaProtocolTRUNK2 = 24 // Trunk-2
|
||||
ianaProtocolLEAF1 = 25 // Leaf-1
|
||||
ianaProtocolLEAF2 = 26 // Leaf-2
|
||||
ianaProtocolRDP = 27 // Reliable Data Protocol
|
||||
ianaProtocolIRTP = 28 // Internet Reliable Transaction
|
||||
ianaProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
|
||||
ianaProtocolNETBLT = 30 // Bulk Data Transfer Protocol
|
||||
ianaProtocolMFENSP = 31 // MFE Network Services Protocol
|
||||
ianaProtocolMERITINP = 32 // MERIT Internodal Protocol
|
||||
ianaProtocolDCCP = 33 // Datagram Congestion Control Protocol
|
||||
ianaProtocol3PC = 34 // Third Party Connect Protocol
|
||||
ianaProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
|
||||
ianaProtocolXTP = 36 // XTP
|
||||
ianaProtocolDDP = 37 // Datagram Delivery Protocol
|
||||
ianaProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
|
||||
ianaProtocolTPPP = 39 // TP++ Transport Protocol
|
||||
ianaProtocolIL = 40 // IL Transport Protocol
|
||||
ianaProtocolIPv6 = 41 // IPv6 encapsulation
|
||||
ianaProtocolSDRP = 42 // Source Demand Routing Protocol
|
||||
ianaProtocolIPv6Route = 43 // Routing Header for IPv6
|
||||
ianaProtocolIPv6Frag = 44 // Fragment Header for IPv6
|
||||
ianaProtocolIDRP = 45 // Inter-Domain Routing Protocol
|
||||
ianaProtocolRSVP = 46 // Reservation Protocol
|
||||
ianaProtocolGRE = 47 // Generic Routing Encapsulation
|
||||
ianaProtocolDSR = 48 // Dynamic Source Routing Protocol
|
||||
ianaProtocolBNA = 49 // BNA
|
||||
ianaProtocolESP = 50 // Encap Security Payload
|
||||
ianaProtocolAH = 51 // Authentication Header
|
||||
ianaProtocolINLSP = 52 // Integrated Net Layer Security TUBA
|
||||
ianaProtocolSWIPE = 53 // IP with Encryption
|
||||
ianaProtocolNARP = 54 // NBMA Address Resolution Protocol
|
||||
ianaProtocolMOBILE = 55 // IP Mobility
|
||||
ianaProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
|
||||
ianaProtocolSKIP = 57 // SKIP
|
||||
ianaProtocolIPv6ICMP = 58 // ICMP for IPv6
|
||||
ianaProtocolIPv6NoNxt = 59 // No Next Header for IPv6
|
||||
ianaProtocolIPv6Opts = 60 // Destination Options for IPv6
|
||||
ianaProtocolCFTP = 62 // CFTP
|
||||
ianaProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
|
||||
ianaProtocolKRYPTOLAN = 65 // Kryptolan
|
||||
ianaProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
|
||||
ianaProtocolIPPC = 67 // Internet Pluribus Packet Core
|
||||
ianaProtocolSATMON = 69 // SATNET Monitoring
|
||||
ianaProtocolVISA = 70 // VISA Protocol
|
||||
ianaProtocolIPCV = 71 // Internet Packet Core Utility
|
||||
ianaProtocolCPNX = 72 // Computer Protocol Network Executive
|
||||
ianaProtocolCPHB = 73 // Computer Protocol Heart Beat
|
||||
ianaProtocolWSN = 74 // Wang Span Network
|
||||
ianaProtocolPVP = 75 // Packet Video Protocol
|
||||
ianaProtocolBRSATMON = 76 // Backroom SATNET Monitoring
|
||||
ianaProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
|
||||
ianaProtocolWBMON = 78 // WIDEBAND Monitoring
|
||||
ianaProtocolWBEXPAK = 79 // WIDEBAND EXPAK
|
||||
ianaProtocolISOIP = 80 // ISO Internet Protocol
|
||||
ianaProtocolVMTP = 81 // VMTP
|
||||
ianaProtocolSECUREVMTP = 82 // SECURE-VMTP
|
||||
ianaProtocolVINES = 83 // VINES
|
||||
ianaProtocolTTP = 84 // TTP
|
||||
ianaProtocolIPTM = 84 // Protocol Internet Protocol Traffic Manager
|
||||
ianaProtocolNSFNETIGP = 85 // NSFNET-IGP
|
||||
ianaProtocolDGP = 86 // Dissimilar Gateway Protocol
|
||||
ianaProtocolTCF = 87 // TCF
|
||||
ianaProtocolEIGRP = 88 // EIGRP
|
||||
ianaProtocolOSPFIGP = 89 // OSPFIGP
|
||||
ianaProtocolSpriteRPC = 90 // Sprite RPC Protocol
|
||||
ianaProtocolLARP = 91 // Locus Address Resolution Protocol
|
||||
ianaProtocolMTP = 92 // Multicast Transport Protocol
|
||||
ianaProtocolAX25 = 93 // AX.25 Frames
|
||||
ianaProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
|
||||
ianaProtocolMICP = 95 // Mobile Internetworking Control Pro.
|
||||
ianaProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
|
||||
ianaProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
|
||||
ianaProtocolENCAP = 98 // Encapsulation Header
|
||||
ianaProtocolGMTP = 100 // GMTP
|
||||
ianaProtocolIFMP = 101 // Ipsilon Flow Management Protocol
|
||||
ianaProtocolPNNI = 102 // PNNI over IP
|
||||
ianaProtocolPIM = 103 // Protocol Independent Multicast
|
||||
ianaProtocolARIS = 104 // ARIS
|
||||
ianaProtocolSCPS = 105 // SCPS
|
||||
ianaProtocolQNX = 106 // QNX
|
||||
ianaProtocolAN = 107 // Active Networks
|
||||
ianaProtocolIPComp = 108 // IP Payload Compression Protocol
|
||||
ianaProtocolSNP = 109 // Sitara Networks Protocol
|
||||
ianaProtocolCompaqPeer = 110 // Compaq Peer Protocol
|
||||
ianaProtocolIPXinIP = 111 // IPX in IP
|
||||
ianaProtocolVRRP = 112 // Virtual Router Redundancy Protocol
|
||||
ianaProtocolPGM = 113 // PGM Reliable Transport Protocol
|
||||
ianaProtocolL2TP = 115 // Layer Two Tunneling Protocol
|
||||
ianaProtocolDDX = 116 // D-II Data Exchange (DDX)
|
||||
ianaProtocolIATP = 117 // Interactive Agent Transfer Protocol
|
||||
ianaProtocolSTP = 118 // Schedule Transfer Protocol
|
||||
ianaProtocolSRP = 119 // SpectraLink Radio Protocol
|
||||
ianaProtocolUTI = 120 // UTI
|
||||
ianaProtocolSMP = 121 // Simple Message Protocol
|
||||
ianaProtocolSM = 122 // SM
|
||||
ianaProtocolPTP = 123 // Performance Transparency Protocol
|
||||
ianaProtocolISIS = 124 // ISIS over IPv4
|
||||
ianaProtocolFIRE = 125 // FIRE
|
||||
ianaProtocolCRTP = 126 // Combat Radio Transport Protocol
|
||||
ianaProtocolCRUDP = 127 // Combat Radio User Datagram
|
||||
ianaProtocolSSCOPMCE = 128 // SSCOPMCE
|
||||
ianaProtocolIPLT = 129 // IPLT
|
||||
ianaProtocolSPS = 130 // Secure Packet Shield
|
||||
ianaProtocolPIPE = 131 // Private IP Encapsulation within IP
|
||||
ianaProtocolSCTP = 132 // Stream Control Transmission Protocol
|
||||
ianaProtocolFC = 133 // Fibre Channel
|
||||
ianaProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
|
||||
ianaProtocolMobilityHeader = 135 // Mobility Header
|
||||
ianaProtocolUDPLite = 136 // UDPLite
|
||||
ianaProtocolMPLSinIP = 137 // MPLS-in-IP
|
||||
ianaProtocolMANET = 138 // MANET Protocols
|
||||
ianaProtocolHIP = 139 // Host Identity Protocol
|
||||
ianaProtocolShim6 = 140 // Shim6 Protocol
|
||||
ianaProtocolWESP = 141 // Wrapped Encapsulating Security Payload
|
||||
ianaProtocolROHC = 142 // Robust Header Compression
|
||||
ianaProtocolReserved = 255 // Reserved
|
||||
)
|
||||
38
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/iana_test.go
generated
vendored
38
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/iana_test.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
// go run gentest.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package ipv6_test
|
||||
|
||||
// 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)
|
||||
)
|
||||
47
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go
generated
vendored
47
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go
generated
vendored
@@ -1,47 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import "sync"
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// An ICMPFilter represents an ICMP message filter for incoming
|
||||
// packets.
|
||||
type ICMPFilter struct {
|
||||
mu sync.RWMutex
|
||||
sysICMPFilter
|
||||
}
|
||||
|
||||
// Set sets the ICMP type and filter action to the filter.
|
||||
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
|
||||
f.mu.Lock()
|
||||
f.set(typ, block)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetAll sets the filter action to the filter.
|
||||
func (f *ICMPFilter) SetAll(block bool) {
|
||||
f.mu.Lock()
|
||||
f.setAll(block)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
// WillBlock reports whether the ICMP type will be blocked.
|
||||
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
||||
f.mu.RLock()
|
||||
ok := f.willBlock(typ)
|
||||
f.mu.RUnlock()
|
||||
return ok
|
||||
}
|
||||
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go
generated
vendored
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
// 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 darwin freebsd netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
type sysICMPFilter struct {
|
||||
Filt [8]uint32
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||
if block {
|
||||
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||
} else {
|
||||
f.Filt[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) setAll(block bool) {
|
||||
for i := range f.Filt {
|
||||
if block {
|
||||
f.Filt[i] = 0
|
||||
} else {
|
||||
f.Filt[i] = 1<<32 - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
|
||||
}
|
||||
31
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go
generated
vendored
31
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
type sysICMPFilter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||
if block {
|
||||
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||
} else {
|
||||
f.Data[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) setAll(block bool) {
|
||||
for i := range f.Data {
|
||||
if block {
|
||||
f.Data[i] = 1<<32 - 1
|
||||
} else {
|
||||
f.Data[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
|
||||
}
|
||||
24
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_stub.go
generated
vendored
24
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_stub.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
type sysICMPFilter struct {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) setAll(block bool) {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||
// TODO(mikio): Implement this
|
||||
return false
|
||||
}
|
||||
102
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go
generated
vendored
102
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var icmpStringTests = []struct {
|
||||
in ipv6.ICMPType
|
||||
out string
|
||||
}{
|
||||
{ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"},
|
||||
|
||||
{256, "<nil>"},
|
||||
}
|
||||
|
||||
func TestICMPString(t *testing.T) {
|
||||
for _, tt := range icmpStringTests {
|
||||
s := tt.in.String()
|
||||
if s != tt.out {
|
||||
t.Errorf("got %s; expected %s", s, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestICMPFilter(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
var f ipv6.ICMPFilter
|
||||
for _, toggle := range []bool{false, true} {
|
||||
f.SetAll(toggle)
|
||||
var wg sync.WaitGroup
|
||||
for _, typ := range []ipv6.ICMPType{
|
||||
ipv6.ICMPTypeDestinationUnreachable,
|
||||
ipv6.ICMPTypeEchoReply,
|
||||
ipv6.ICMPTypeNeighborSolicitation,
|
||||
ipv6.ICMPTypeDuplicateAddressConfirmation,
|
||||
} {
|
||||
wg.Add(1)
|
||||
go func(typ ipv6.ICMPType) {
|
||||
defer wg.Done()
|
||||
f.Set(typ, false)
|
||||
if f.WillBlock(typ) {
|
||||
t.Errorf("ipv6.ICMPFilter.Set(%v, false) failed", typ)
|
||||
}
|
||||
f.Set(typ, true)
|
||||
if !f.WillBlock(typ) {
|
||||
t.Errorf("ipv6.ICMPFilter.Set(%v, true) failed", typ)
|
||||
}
|
||||
}(typ)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetICMPFilter(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
|
||||
var f ipv6.ICMPFilter
|
||||
f.SetAll(true)
|
||||
f.Set(ipv6.ICMPTypeEchoRequest, false)
|
||||
f.Set(ipv6.ICMPTypeEchoReply, false)
|
||||
if err := p.SetICMPFilter(&f); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||
}
|
||||
kf, err := p.ICMPFilter()
|
||||
if err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.ICMPFilter failed: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(kf, &f) {
|
||||
t.Fatalf("got unexpected filter %#v; expected %#v", kf, f)
|
||||
}
|
||||
}
|
||||
22
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go
generated
vendored
22
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
type sysICMPFilter struct {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) setAll(block bool) {
|
||||
// TODO(mikio): Implement this
|
||||
}
|
||||
|
||||
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||
// TODO(mikio): Implement this
|
||||
return false
|
||||
}
|
||||
130
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go
generated
vendored
130
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go
generated
vendored
@@ -1,130 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
ipv6PseudoHeaderLen = 2*net.IPv6len + 8
|
||||
ianaProtocolIPv6ICMP = 58
|
||||
)
|
||||
|
||||
func ipv6PseudoHeader(src, dst net.IP, nextHeader int) []byte {
|
||||
b := make([]byte, ipv6PseudoHeaderLen)
|
||||
copy(b[:net.IPv6len], src)
|
||||
copy(b[net.IPv6len:], dst)
|
||||
b[len(b)-1] = byte(nextHeader)
|
||||
return b
|
||||
}
|
||||
|
||||
// icmpMessage represents an ICMP message.
|
||||
type icmpMessage struct {
|
||||
Type ipv6.ICMPType // type
|
||||
Code int // code
|
||||
Checksum int // checksum
|
||||
Body icmpMessageBody // body
|
||||
}
|
||||
|
||||
// icmpMessageBody represents an ICMP message body.
|
||||
type icmpMessageBody interface {
|
||||
Len() int
|
||||
Marshal() ([]byte, error)
|
||||
}
|
||||
|
||||
// Marshal returns the binary enconding of the ICMP echo request or
|
||||
// reply message m.
|
||||
func (m *icmpMessage) Marshal(psh []byte) ([]byte, error) {
|
||||
b := []byte{byte(m.Type), byte(m.Code), 0, 0}
|
||||
if psh != nil {
|
||||
b = append(psh, b...)
|
||||
}
|
||||
if m.Body != nil && m.Body.Len() != 0 {
|
||||
mb, err := m.Body.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, mb...)
|
||||
}
|
||||
if psh == nil {
|
||||
return b, nil
|
||||
}
|
||||
off, l := 2*net.IPv6len, len(b)-len(psh)
|
||||
b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
csumcv := len(b) - 1 // checksum coverage
|
||||
s := uint32(0)
|
||||
for i := 0; i < csumcv; i += 2 {
|
||||
s += uint32(b[i+1])<<8 | uint32(b[i])
|
||||
}
|
||||
if csumcv&1 == 0 {
|
||||
s += uint32(b[csumcv])
|
||||
}
|
||||
s = s>>16 + s&0xffff
|
||||
s = s + s>>16
|
||||
// Place checksum back in header; using ^= avoids the
|
||||
// assumption the checksum bytes are zero.
|
||||
b[len(psh)+2] ^= byte(^s)
|
||||
b[len(psh)+3] ^= byte(^s >> 8)
|
||||
return b[len(psh):], nil
|
||||
}
|
||||
|
||||
// parseICMPMessage parses b as an ICMP message.
|
||||
func parseICMPMessage(b []byte) (*icmpMessage, error) {
|
||||
msglen := len(b)
|
||||
if msglen < 4 {
|
||||
return nil, errors.New("message too short")
|
||||
}
|
||||
m := &icmpMessage{Type: ipv6.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
|
||||
if msglen > 4 {
|
||||
var err error
|
||||
switch m.Type {
|
||||
case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
|
||||
m.Body, err = parseICMPEcho(b[4:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// imcpEcho represenets an ICMP echo request or reply message body.
|
||||
type icmpEcho struct {
|
||||
ID int // identifier
|
||||
Seq int // sequence number
|
||||
Data []byte // data
|
||||
}
|
||||
|
||||
func (p *icmpEcho) Len() int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return 4 + len(p.Data)
|
||||
}
|
||||
|
||||
// Marshal returns the binary enconding of the ICMP echo request or
|
||||
// reply message body p.
|
||||
func (p *icmpEcho) Marshal() ([]byte, error) {
|
||||
b := make([]byte, 4+len(p.Data))
|
||||
b[0], b[1] = byte(p.ID>>8), byte(p.ID)
|
||||
b[2], b[3] = byte(p.Seq>>8), byte(p.Seq)
|
||||
copy(b[4:], p.Data)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseICMPEcho parses b as an ICMP echo request or reply message
|
||||
// body.
|
||||
func parseICMPEcho(b []byte) (*icmpEcho, error) {
|
||||
bodylen := len(b)
|
||||
p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
|
||||
if bodylen > 4 {
|
||||
p.Data = make([]byte, bodylen-4)
|
||||
copy(p.Data, b[4:])
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
88
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go
generated
vendored
88
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go
generated
vendored
@@ -1,88 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func isLinkLocalUnicast(ip net.IP) bool {
|
||||
return ip.To4() == nil && ip.To16() != nil && ip.IsLinkLocalUnicast()
|
||||
}
|
||||
|
||||
func loopbackInterface() *net.Interface {
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, ifi := range ift {
|
||||
if ifi.Flags&net.FlagLoopback == 0 || ifi.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if isLinkLocalUnicast(ifa.IP) {
|
||||
return &ifi
|
||||
}
|
||||
case *net.IPNet:
|
||||
if isLinkLocalUnicast(ifa.IP) {
|
||||
return &ifi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
|
||||
if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
|
||||
return nil, false
|
||||
}
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if isLinkLocalUnicast(ifa.IP) {
|
||||
return ifa.IP, true
|
||||
}
|
||||
case *net.IPNet:
|
||||
if isLinkLocalUnicast(ifa.IP) {
|
||||
return ifa.IP, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func connector(t *testing.T, network, addr string, done chan<- bool) {
|
||||
defer func() { done <- true }()
|
||||
|
||||
c, err := net.Dial(network, addr)
|
||||
if err != nil {
|
||||
t.Errorf("net.Dial failed: %v", err)
|
||||
return
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
|
||||
func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
|
||||
defer func() { done <- true }()
|
||||
|
||||
c, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("net.Listener.Accept failed: %v", err)
|
||||
return
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
205
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go
generated
vendored
205
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go
generated
vendored
@@ -1,205 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "freebsd": // due to a bug on loopback marking
|
||||
// See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
ifi := loopbackInterface()
|
||||
if ifi == nil {
|
||||
t.Skipf("not available on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp6", "[ff02::114]:0") // see RFC 4727
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
_, port, err := net.SplitHostPort(c.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("net.SplitHostPort failed: %v", err)
|
||||
}
|
||||
dst, err := net.ResolveUDPAddr("udp6", "[ff02::114]:"+port) // see RFC 4727
|
||||
if err != nil {
|
||||
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
|
||||
}
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
defer p.Close()
|
||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
||||
}
|
||||
if _, err := p.MulticastInterface(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
|
||||
}
|
||||
if err := p.SetMulticastLoopback(true); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
||||
}
|
||||
if _, err := p.MulticastLoopback(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
|
||||
}
|
||||
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
Src: net.IPv6loopback,
|
||||
IfIndex: ifi.Index,
|
||||
}
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
wb := []byte("HELLO-R-U-THERE")
|
||||
|
||||
for i, toggle := range []bool{true, false, true} {
|
||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
}
|
||||
if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
|
||||
}
|
||||
cm.HopLimit = i + 1
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
} else if n != len(wb) {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
}
|
||||
rb := make([]byte, 128)
|
||||
if n, cm, _, err := p.ReadFrom(rb); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
} else if !bytes.Equal(rb[:n], wb) {
|
||||
t.Fatalf("got %v; expected %v", rb[:n], wb)
|
||||
} else {
|
||||
t.Logf("rcvd cmsg: %v", cm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
ifi := loopbackInterface()
|
||||
if ifi == nil {
|
||||
t.Skipf("not available on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
dst, err := net.ResolveIPAddr("ip6", "ff02::114") // see RFC 4727
|
||||
if err != nil {
|
||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||
}
|
||||
|
||||
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||
p := ipv6.NewPacketConn(c)
|
||||
defer p.Close()
|
||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
||||
}
|
||||
if _, err := p.MulticastInterface(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
|
||||
}
|
||||
if err := p.SetMulticastLoopback(true); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
||||
}
|
||||
if _, err := p.MulticastLoopback(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
|
||||
}
|
||||
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
Src: net.IPv6loopback,
|
||||
IfIndex: ifi.Index,
|
||||
}
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
|
||||
var f ipv6.ICMPFilter
|
||||
f.SetAll(true)
|
||||
f.Set(ipv6.ICMPTypeEchoReply, false)
|
||||
if err := p.SetICMPFilter(&f); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||
}
|
||||
|
||||
var psh []byte
|
||||
for i, toggle := range []bool{true, false, true} {
|
||||
if toggle {
|
||||
psh = nil
|
||||
if err := p.SetChecksum(true, 2); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
psh = pshicmp
|
||||
// Some platforms never allow to disable the
|
||||
// kernel checksum processing.
|
||||
p.SetChecksum(false, -1)
|
||||
}
|
||||
wb, err := (&icmpMessage{
|
||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||
Body: &icmpEcho{
|
||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
},
|
||||
}).Marshal(psh)
|
||||
if err != nil {
|
||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||
}
|
||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
}
|
||||
if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
|
||||
}
|
||||
cm.HopLimit = i + 1
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
} else if n != len(wb) {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
}
|
||||
rb := make([]byte, 128)
|
||||
if n, cm, _, err := p.ReadFrom(rb); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
} else {
|
||||
t.Logf("rcvd cmsg: %v", cm)
|
||||
if m, err := parseICMPMessage(rb[:n]); err != nil {
|
||||
t.Fatalf("parseICMPMessage failed: %v", err)
|
||||
} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
|
||||
t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
243
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go
generated
vendored
243
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go
generated
vendored
@@ -1,243 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var udpMultipleGroupListenerTests = []net.Addr{
|
||||
&net.UDPAddr{IP: net.ParseIP("ff02::114")}, // see RFC 4727
|
||||
&net.UDPAddr{IP: net.ParseIP("ff02::1:114")},
|
||||
&net.UDPAddr{IP: net.ParseIP("ff02::2:114")},
|
||||
}
|
||||
|
||||
func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||
c, err := net.ListenPacket("udp6", "[::]:0") // wildcard address with non-reusable port
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
var mift []*net.Interface
|
||||
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Interfaces failed: %v", err)
|
||||
}
|
||||
for i, ifi := range ift {
|
||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||
continue
|
||||
}
|
||||
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||
}
|
||||
mift = append(mift, &ift[i])
|
||||
}
|
||||
for _, ifi := range mift {
|
||||
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||
c1, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
|
||||
c2, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c2.Close()
|
||||
|
||||
var ps [2]*ipv6.PacketConn
|
||||
ps[0] = ipv6.NewPacketConn(c1)
|
||||
ps[1] = ipv6.NewPacketConn(c2)
|
||||
var mift []*net.Interface
|
||||
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Interfaces failed: %v", err)
|
||||
}
|
||||
for i, ifi := range ift {
|
||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||
continue
|
||||
}
|
||||
for _, p := range ps {
|
||||
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||
}
|
||||
}
|
||||
mift = append(mift, &ift[i])
|
||||
}
|
||||
for _, ifi := range mift {
|
||||
for _, p := range ps {
|
||||
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||
type ml struct {
|
||||
c *ipv6.PacketConn
|
||||
ifi *net.Interface
|
||||
}
|
||||
var mlt []*ml
|
||||
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Interfaces failed: %v", err)
|
||||
}
|
||||
for i, ifi := range ift {
|
||||
ip, ok := isMulticastAvailable(&ifi)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
c, err := net.ListenPacket("udp6", fmt.Sprintf("[%s%%%s]:1024", ip.String(), ifi.Name)) // unicast address with non-reusable port
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
||||
}
|
||||
defer c.Close()
|
||||
p := ipv6.NewPacketConn(c)
|
||||
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
mlt = append(mlt, &ml{p, &ift[i]})
|
||||
}
|
||||
for _, m := range mlt {
|
||||
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||
var mift []*net.Interface
|
||||
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Interfaces failed: %v", err)
|
||||
}
|
||||
for i, ifi := range ift {
|
||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||
continue
|
||||
}
|
||||
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
mift = append(mift, &ift[i])
|
||||
}
|
||||
for _, ifi := range mift {
|
||||
if err := p.LeaveGroup(ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
|
||||
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||
type ml struct {
|
||||
c *ipv6.PacketConn
|
||||
ifi *net.Interface
|
||||
}
|
||||
var mlt []*ml
|
||||
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Interfaces failed: %v", err)
|
||||
}
|
||||
for i, ifi := range ift {
|
||||
ip, ok := isMulticastAvailable(&ifi)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
p := ipv6.NewPacketConn(c)
|
||||
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||
}
|
||||
mlt = append(mlt, &ml{p, &ift[i]})
|
||||
}
|
||||
for _, m := range mlt {
|
||||
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go
generated
vendored
76
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go
generated
vendored
@@ -1,76 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var packetConnMulticastSocketOptionTests = []struct {
|
||||
net, proto, addr string
|
||||
gaddr net.Addr
|
||||
}{
|
||||
{"udp6", "", "[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727
|
||||
{"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727
|
||||
}
|
||||
|
||||
func TestPacketConnMulticastSocketOptions(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
ifi := loopbackInterface()
|
||||
if ifi == nil {
|
||||
t.Skipf("not available on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tt := range packetConnMulticastSocketOptionTests {
|
||||
if tt.net == "ip6" && os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
|
||||
hoplim := 255
|
||||
if err := p.SetMulticastHopLimit(hoplim); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastHopLimit failed: %v", err)
|
||||
}
|
||||
if v, err := p.MulticastHopLimit(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastHopLimit failed: %v", err)
|
||||
} else if v != hoplim {
|
||||
t.Fatalf("got unexpected multicast hop limit %v; expected %v", v, hoplim)
|
||||
}
|
||||
|
||||
for _, toggle := range []bool{true, false} {
|
||||
if err := p.SetMulticastLoopback(toggle); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
||||
}
|
||||
if v, err := p.MulticastLoopback(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
|
||||
} else if v != toggle {
|
||||
t.Fatalf("got unexpected multicast loopback %v; expected %v", v, toggle)
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.JoinGroup(ifi, tt.gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.JoinGroup(%v, %v) failed: %v", ifi, tt.gaddr, err)
|
||||
}
|
||||
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.LeaveGroup(%v, %v) failed: %v", ifi, tt.gaddr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload.go
generated
vendored
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import "net"
|
||||
|
||||
// A payloadHandler represents the IPv6 datagram payload handler.
|
||||
type payloadHandler struct {
|
||||
net.PacketConn
|
||||
rawOpt
|
||||
}
|
||||
|
||||
func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }
|
||||
70
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload_cmsg.go
generated
vendored
70
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload_cmsg.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
// 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 !plan9,!windows
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadFrom reads a payload of the received IPv6 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 n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
default:
|
||||
return 0, nil, nil, errInvalidConnType
|
||||
}
|
||||
if cm, err = parseControlMessage(oob[:oobn]); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
if cm != nil {
|
||||
cm.Src = netAddrToIP16(src)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo writes a payload of the IPv6 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 IPv6 header fields and the datagram path to be specified. 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
|
||||
}
|
||||
41
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload_noncmsg.go
generated
vendored
41
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/payload_noncmsg.go
generated
vendored
@@ -1,41 +0,0 @@
|
||||
// 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 plan9 windows
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadFrom reads a payload of the received IPv6 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 IPv6 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 IPv6 header fields and the datagram path to be specified. 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)
|
||||
}
|
||||
168
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/readwrite_test.go
generated
vendored
168
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/readwrite_test.go
generated
vendored
@@ -1,168 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
|
||||
c, err := net.ListenPacket("udp6", "[::1]:0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return c, dst, nil
|
||||
}
|
||||
|
||||
func BenchmarkReadWriteNetUDP(b *testing.B) {
|
||||
c, dst, err := benchmarkUDPListener()
|
||||
if err != nil {
|
||||
b.Fatalf("benchmarkUDPListener failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkReadWriteNetUDP(b, c, wb, rb, dst)
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) {
|
||||
if _, err := c.WriteTo(wb, dst); err != nil {
|
||||
b.Fatalf("net.PacketConn.WriteTo failed: %v", err)
|
||||
}
|
||||
if _, _, err := c.ReadFrom(rb); err != nil {
|
||||
b.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReadWriteIPv6UDP(b *testing.B) {
|
||||
c, dst, err := benchmarkUDPListener()
|
||||
if err != nil {
|
||||
b.Fatalf("benchmarkUDPListener failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
if err := p.SetControlMessage(cf, true); err != nil {
|
||||
b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
}
|
||||
ifi := loopbackInterface()
|
||||
|
||||
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi)
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) {
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
HopLimit: 1,
|
||||
}
|
||||
if ifi != nil {
|
||||
cm.IfIndex = ifi.Index
|
||||
}
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
} else if n != len(wb) {
|
||||
b.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
}
|
||||
if _, _, _, err := p.ReadFrom(rb); err != nil {
|
||||
b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp6", "[::1]:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
p := ipv6.NewPacketConn(c)
|
||||
defer p.Close()
|
||||
|
||||
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
|
||||
}
|
||||
|
||||
ifi := loopbackInterface()
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
wb := []byte("HELLO-R-U-THERE")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
reader := func() {
|
||||
defer wg.Done()
|
||||
rb := make([]byte, 128)
|
||||
if n, cm, _, err := p.ReadFrom(rb); err != nil {
|
||||
t.Errorf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
return
|
||||
} else if !bytes.Equal(rb[:n], wb) {
|
||||
t.Errorf("got %v; expected %v", rb[:n], wb)
|
||||
return
|
||||
} else {
|
||||
t.Logf("rcvd cmsg: %v", cm)
|
||||
}
|
||||
}
|
||||
writer := func(toggle bool) {
|
||||
defer wg.Done()
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
Src: net.IPv6loopback,
|
||||
Dst: net.IPv6loopback,
|
||||
}
|
||||
if ifi != nil {
|
||||
cm.IfIndex = ifi.Index
|
||||
}
|
||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||
t.Errorf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
return
|
||||
}
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
t.Errorf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
return
|
||||
} else if n != len(wb) {
|
||||
t.Errorf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const N = 10
|
||||
wg.Add(N)
|
||||
for i := 0; i < N; i++ {
|
||||
go reader()
|
||||
}
|
||||
wg.Add(2 * N)
|
||||
for i := 0; i < 2*N; i++ {
|
||||
go writer(i%2 != 0)
|
||||
}
|
||||
wg.Add(N)
|
||||
for i := 0; i < N; i++ {
|
||||
go reader()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
73
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go
generated
vendored
73
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
// 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 darwin
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
||||
return false, errOpNoSupport
|
||||
}
|
||||
|
||||
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func ipv6ReceiveHopLimit(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceiveHopLimit(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6ReceivePacketInfo(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceivePacketInfo(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6PathMTU(fd int) (int, error) {
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
|
||||
func ipv6ReceivePathMTU(fd int) (bool, error) {
|
||||
return false, errOpNoSupport
|
||||
}
|
||||
|
||||
func setIPv6ReceivePathMTU(fd int, v bool) error {
|
||||
return errOpNoSupport
|
||||
}
|
||||
|
||||
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
|
||||
var v ICMPFilter
|
||||
l := sysSockoptLen(sysSizeofICMPFilter)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
|
||||
}
|
||||
20
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go
generated
vendored
20
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// 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 darwin freebsd netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||
if !on {
|
||||
offset = -1
|
||||
}
|
||||
v := int32(offset)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
|
||||
}
|
||||
18
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go
generated
vendored
18
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||
if !on {
|
||||
offset = -1
|
||||
}
|
||||
v := int32(offset)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
|
||||
}
|
||||
124
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go
generated
vendored
124
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go
generated
vendored
@@ -1,124 +0,0 @@
|
||||
// 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 darwin freebsd linux netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ipv6TrafficClass(fd int) (int, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func setIPv6TrafficClass(fd, v int) error {
|
||||
vv := int32(v)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6HopLimit(fd int) (int, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func setIPv6HopLimit(fd, v int) error {
|
||||
vv := int32(v)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6Checksum(fd int) (bool, int, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
on := true
|
||||
if v == -1 {
|
||||
on = false
|
||||
}
|
||||
return on, int(v), nil
|
||||
}
|
||||
|
||||
func ipv6MulticastHopLimit(fd int) (int, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastHopLimit(fd, v int) error {
|
||||
vv := int32(v)
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
if v == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ifi, err := net.InterfaceByIndex(int(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
|
||||
var v int32
|
||||
if ifi != nil {
|
||||
v = int32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4))
|
||||
}
|
||||
|
||||
func ipv6MulticastLoopback(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastLoopback(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := sysMulticastReq{}
|
||||
copy(mreq.IP[:], grp)
|
||||
if ifi != nil {
|
||||
mreq.IfIndex = uint32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
|
||||
}
|
||||
|
||||
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := sysMulticastReq{}
|
||||
copy(mreq.IP[:], grp)
|
||||
if ifi != nil {
|
||||
mreq.IfIndex = uint32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
|
||||
}
|
||||
116
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go
generated
vendored
116
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go
generated
vendored
@@ -1,116 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ipv6TrafficClass(fd syscall.Handle) (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6TrafficClass(fd syscall.Handle, v int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
||||
var v int32
|
||||
l := int32(4)
|
||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func setIPv6HopLimit(fd syscall.Handle, v int) error {
|
||||
vv := int32(v)
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, 0, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
||||
var v int32
|
||||
l := int32(4)
|
||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
|
||||
vv := int32(v)
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
|
||||
var v int32
|
||||
l := int32(4)
|
||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
if v == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ifi, err := net.InterfaceByIndex(int(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
|
||||
var v int32
|
||||
if ifi != nil {
|
||||
v = int32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), 4))
|
||||
}
|
||||
|
||||
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
||||
var v int32
|
||||
l := int32(4)
|
||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := sysMulticastReq{}
|
||||
copy(mreq.IP[:], grp)
|
||||
if ifi != nil {
|
||||
mreq.IfIndex = uint32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
|
||||
}
|
||||
|
||||
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := sysMulticastReq{}
|
||||
copy(mreq.IP[:], grp)
|
||||
if ifi != nil {
|
||||
mreq.IfIndex = uint32(ifi.Index)
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
|
||||
}
|
||||
|
||||
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
12
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go
generated
vendored
12
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go
generated
vendored
@@ -1,12 +0,0 @@
|
||||
// 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 dragonfly plan9 solaris
|
||||
|
||||
package ipv6
|
||||
|
||||
func ipv6PathMTU(fd int) (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, errOpNoSupport
|
||||
}
|
||||
90
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go
generated
vendored
90
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
// 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 freebsd linux netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6ReceiveHopLimit(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceiveHopLimit(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6ReceivePacketInfo(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceivePacketInfo(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6PathMTU(fd int) (int, error) {
|
||||
var v sysMTUInfo
|
||||
l := sysSockoptLen(sysSizeofMTUInfo)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptPathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return 0, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return int(v.MTU), nil
|
||||
}
|
||||
|
||||
func ipv6ReceivePathMTU(fd int) (bool, error) {
|
||||
var v int32
|
||||
l := sysSockoptLen(4)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||
return false, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return v == 1, nil
|
||||
}
|
||||
|
||||
func setIPv6ReceivePathMTU(fd int, v bool) error {
|
||||
vv := int32(boolint(v))
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&vv)), 4))
|
||||
}
|
||||
|
||||
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
|
||||
var v ICMPFilter
|
||||
l := sysSockoptLen(sysSizeofICMPFilter)
|
||||
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
|
||||
return nil, os.NewSyscallError("getsockopt", err)
|
||||
}
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
|
||||
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
|
||||
}
|
||||
62
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_windows.go
generated
vendored
62
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_windows.go
generated
vendored
@@ -1,62 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
func ipv6ReceiveTrafficClass(fd syscall.Handle) (bool, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6ReceiveTrafficClass(fd syscall.Handle, v bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6ReceiveHopLimit(fd syscall.Handle) (bool, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6ReceiveHopLimit(fd syscall.Handle, v bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6ReceivePacketInfo(fd syscall.Handle) (bool, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6ReceivePacketInfo(fd syscall.Handle, v bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6PathMTU(fd syscall.Handle) (int, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return 0, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6ReceivePathMTU(fd syscall.Handle) (bool, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return false, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6ReceivePathMTU(fd syscall.Handle, v bool) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func ipv6ICMPFilter(fd syscall.Handle) (*ICMPFilter, error) {
|
||||
// TODO(mikio): Implement this
|
||||
return nil, syscall.EWINDOWS
|
||||
}
|
||||
|
||||
func setIPv6ICMPFilter(fd syscall.Handle, f *ICMPFilter) error {
|
||||
// TODO(mikio): Implement this
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
136
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go
generated
vendored
136
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go
generated
vendored
@@ -1,136 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var supportsIPv6 bool
|
||||
|
||||
func init() {
|
||||
if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
|
||||
ln.Close()
|
||||
supportsIPv6 = true
|
||||
}
|
||||
}
|
||||
|
||||
var condFatalf = func() func(*testing.T, string, ...interface{}) {
|
||||
// A few APIs are not implemented yet on some platforms.
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "dragonfly", "plan9", "solaris", "windows":
|
||||
return (*testing.T).Logf
|
||||
}
|
||||
return (*testing.T).Fatalf
|
||||
}()
|
||||
|
||||
func TestConnInitiatorPathMTU(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp6", "[::1]:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen failed: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go acceptor(t, ln, done)
|
||||
|
||||
c, err := net.Dial("tcp6", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("net.Dial failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
|
||||
condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err)
|
||||
} else {
|
||||
t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
|
||||
}
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestConnResponderPathMTU(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp6", "[::1]:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen failed: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go connector(t, "tcp6", ln.Addr().String(), done)
|
||||
|
||||
c, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("net.Accept failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
|
||||
condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err)
|
||||
} else {
|
||||
t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
|
||||
}
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestPacketConnChecksum(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv6.NewPacketConn(c)
|
||||
offset := 12 // see RFC 5340
|
||||
|
||||
for _, toggle := range []bool{false, true} {
|
||||
if err := p.SetChecksum(toggle, offset); err != nil {
|
||||
if toggle {
|
||||
t.Fatalf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
|
||||
} else {
|
||||
// Some platforms never allow to disable the kernel
|
||||
// checksum processing.
|
||||
t.Logf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
|
||||
}
|
||||
}
|
||||
if on, offset, err := p.Checksum(); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.Checksum failed: %v", err)
|
||||
} else {
|
||||
t.Logf("kernel checksum processing enabled=%v, offset=%v", on, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys.go
generated
vendored
23
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
type sysSockoptLen uint32
|
||||
|
||||
const (
|
||||
sysSizeofPacketInfo = 0x14
|
||||
sysSizeofMulticastReq = 0x14
|
||||
sysSizeofICMPFilter = 0x20
|
||||
)
|
||||
|
||||
type sysPacketInfo struct {
|
||||
IP [16]byte
|
||||
IfIndex uint32
|
||||
}
|
||||
|
||||
type sysMulticastReq struct {
|
||||
IP [16]byte
|
||||
IfIndex uint32
|
||||
}
|
||||
48
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_bsd.go
generated
vendored
48
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_bsd.go
generated
vendored
@@ -1,48 +0,0 @@
|
||||
// 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 freebsd netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RFC 3493 options
|
||||
const (
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptUnicastHopLimit = 0x4
|
||||
sysSockoptMulticastHopLimit = 0xa
|
||||
sysSockoptMulticastInterface = 0x9
|
||||
sysSockoptMulticastLoopback = 0xb
|
||||
sysSockoptJoinGroup = 0xc
|
||||
sysSockoptLeaveGroup = 0xd
|
||||
)
|
||||
|
||||
// RFC 3542 options
|
||||
const (
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptReceiveTrafficClass = 0x39
|
||||
sysSockoptTrafficClass = 0x3d
|
||||
sysSockoptReceiveHopLimit = 0x25
|
||||
sysSockoptHopLimit = 0x2f
|
||||
sysSockoptReceivePacketInfo = 0x24
|
||||
sysSockoptPacketInfo = 0x2e
|
||||
sysSockoptReceivePathMTU = 0x2b
|
||||
sysSockoptPathMTU = 0x2c
|
||||
sysSockoptNextHop = 0x30
|
||||
sysSockoptChecksum = 0x1a
|
||||
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptICMPFilter = 0x12
|
||||
)
|
||||
|
||||
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
|
||||
sa.Len = syscall.SizeofSockaddrInet6
|
||||
sa.Family = syscall.AF_INET6
|
||||
copy(sa.Addr[:], ip)
|
||||
sa.Scope_id = uint32(ifindex)
|
||||
}
|
||||
54
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_darwin.go
generated
vendored
54
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_darwin.go
generated
vendored
@@ -1,54 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RFC 2292 options
|
||||
const (
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockopt2292HopLimit = 0x14
|
||||
sysSockopt2292PacketInfo = 0x13
|
||||
sysSockopt2292NextHop = 0x15
|
||||
)
|
||||
|
||||
// RFC 3493 options
|
||||
const (
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptUnicastHopLimit = 0x4
|
||||
sysSockoptMulticastHopLimit = 0xa
|
||||
sysSockoptMulticastInterface = 0x9
|
||||
sysSockoptMulticastLoopback = 0xb
|
||||
sysSockoptJoinGroup = 0xc
|
||||
sysSockoptLeaveGroup = 0xd
|
||||
)
|
||||
|
||||
// RFC 3542 options
|
||||
const (
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptReceiveTrafficClass = 0x23
|
||||
sysSockoptTrafficClass = 0x24
|
||||
sysSockoptReceiveHopLimit = 0x25
|
||||
sysSockoptHopLimit = 0x2f
|
||||
sysSockoptReceivePacketInfo = 0x3d
|
||||
sysSockoptPacketInfo = 0x2e
|
||||
sysSockoptReceivePathMTU = 0x2b
|
||||
sysSockoptPathMTU = 0x2c
|
||||
sysSockoptNextHop = 0x30
|
||||
sysSockoptChecksum = 0x1a
|
||||
|
||||
// See /usr/include/netinet6/in6.h.
|
||||
sysSockoptICMPFilter = 0x12
|
||||
)
|
||||
|
||||
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
|
||||
sa.Len = syscall.SizeofSockaddrInet6
|
||||
sa.Family = syscall.AF_INET6
|
||||
copy(sa.Addr[:], ip)
|
||||
sa.Scope_id = uint32(ifindex)
|
||||
}
|
||||
45
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_linux.go
generated
vendored
45
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_linux.go
generated
vendored
@@ -1,45 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RFC 3493 options
|
||||
const (
|
||||
// See /usr/include/linux/in6.h.
|
||||
sysSockoptUnicastHopLimit = 0x10
|
||||
sysSockoptMulticastHopLimit = 0x12
|
||||
sysSockoptMulticastInterface = 0x11
|
||||
sysSockoptMulticastLoopback = 0x13
|
||||
sysSockoptJoinGroup = 0x14
|
||||
sysSockoptLeaveGroup = 0x15
|
||||
)
|
||||
|
||||
// RFC 3542 options
|
||||
const (
|
||||
// See /usr/include/linux/ipv6.h,in6.h.
|
||||
sysSockoptReceiveTrafficClass = 0x42
|
||||
sysSockoptTrafficClass = 0x43
|
||||
sysSockoptReceiveHopLimit = 0x33
|
||||
sysSockoptHopLimit = 0x34
|
||||
sysSockoptReceivePacketInfo = 0x31
|
||||
sysSockoptPacketInfo = 0x32
|
||||
sysSockoptReceivePathMTU = 0x3c
|
||||
sysSockoptPathMTU = 0x3d
|
||||
sysSockoptNextHop = 0x9
|
||||
sysSockoptChecksum = 0x7
|
||||
|
||||
// See /usr/include/linux/icmpv6.h.
|
||||
sysSockoptICMPFilter = 0x1
|
||||
)
|
||||
|
||||
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
|
||||
sa.Family = syscall.AF_INET6
|
||||
copy(sa.Addr[:], ip)
|
||||
sa.Scope_id = uint32(ifindex)
|
||||
}
|
||||
16
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_unix.go
generated
vendored
16
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_unix.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
// 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 darwin freebsd linux netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
const sysSizeofMTUInfo = 0x20
|
||||
|
||||
type sysMTUInfo struct {
|
||||
Addr syscall.RawSockaddrInet6
|
||||
MTU uint32
|
||||
}
|
||||
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_windows.go
generated
vendored
33
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_windows.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
// 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 ipv6
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RFC 3493 options
|
||||
const (
|
||||
// See ws2tcpip.h.
|
||||
sysSockoptUnicastHopLimit = 0x4
|
||||
sysSockoptMulticastHopLimit = 0xa
|
||||
sysSockoptMulticastInterface = 0x9
|
||||
sysSockoptMulticastLoopback = 0xb
|
||||
sysSockoptJoinGroup = 0xc
|
||||
sysSockoptLeaveGroup = 0xd
|
||||
)
|
||||
|
||||
// RFC 3542 options
|
||||
const (
|
||||
// See ws2tcpip.h.
|
||||
sysSockoptPacketInfo = 0x13
|
||||
)
|
||||
|
||||
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
|
||||
sa.Family = syscall.AF_INET6
|
||||
copy(sa.Addr[:], ip)
|
||||
sa.Scope_id = uint32(ifindex)
|
||||
}
|
||||
42
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go
generated
vendored
42
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// This code is a duplicate of syscall/syscall_linux_386.go with small
|
||||
// modifications.
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// On x86 Linux, all the socket calls go through an extra indirection,
|
||||
// I think because the 5-register system call interface can't handle
|
||||
// the 6-argument calls like sendto and recvfrom. Instead the
|
||||
// arguments to the underlying system call are the number below and a
|
||||
// pointer to an array of uintptr. We hide the pointer in the
|
||||
// socketcall assembly to avoid allocation on every system call.
|
||||
|
||||
const (
|
||||
// See /usr/include/linux/net.h.
|
||||
_SETSOCKOPT = 14
|
||||
_GETSOCKOPT = 15
|
||||
)
|
||||
|
||||
var socketcall func(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
|
||||
|
||||
func getsockopt(fd int, level int, name int, v uintptr, l *sysSockoptLen) error {
|
||||
if _, errno := socketcall(_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
|
||||
if _, errno := socketcall(_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), v, l, 0); errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s
generated
vendored
56
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s
generated
vendored
@@ -1,56 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// This code is a duplicate of syscall/syscall_linux_386.s with small
|
||||
// modifications.
|
||||
|
||||
#define SYS_SOCKETCALL 102 // from zsysnum_linux_386.go
|
||||
|
||||
// func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
|
||||
// Kernel interface gets call sub-number and pointer to a0 for Go 1.1.
|
||||
TEXT ·socketcallnosplit7(SB),7,$0
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL $SYS_SOCKETCALL, AX // syscall entry
|
||||
MOVL 4(SP), BX // socket call number
|
||||
LEAL 8(SP), CX // pointer to call arguments
|
||||
MOVL $0, DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
CALL *runtime·_vdso(SB)
|
||||
CMPL AX, $0xfffff001
|
||||
JLS ok1
|
||||
MOVL $-1, 32(SP) // n
|
||||
NEGL AX
|
||||
MOVL AX, 36(SP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok1:
|
||||
MOVL AX, 32(SP) // n
|
||||
MOVL $0, 36(SP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
|
||||
// Kernel interface gets call sub-number and pointer to a0 for Go 1.2.
|
||||
TEXT ·socketcallnosplit4(SB),4,$0-40
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL $SYS_SOCKETCALL, AX // syscall entry
|
||||
MOVL 4(SP), BX // socket call number
|
||||
LEAL 8(SP), CX // pointer to call arguments
|
||||
MOVL $0, DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
CALL *runtime·_vdso(SB)
|
||||
CMPL AX, $0xfffff001
|
||||
JLS ok2
|
||||
MOVL $-1, 32(SP) // n
|
||||
NEGL AX
|
||||
MOVL AX, 36(SP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok2:
|
||||
MOVL AX, 32(SP) // n
|
||||
MOVL $0, 36(SP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go
generated
vendored
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
// 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 go1.2
|
||||
|
||||
package ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
|
||||
|
||||
func init() {
|
||||
socketcall = socketcallnosplit4
|
||||
}
|
||||
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go
generated
vendored
15
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
// 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 go1.1,!go1.2
|
||||
|
||||
package ipv6
|
||||
|
||||
import "syscall"
|
||||
|
||||
func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
|
||||
|
||||
func init() {
|
||||
socketcall = socketcallnosplit7
|
||||
}
|
||||
26
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_unix.go
generated
vendored
26
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_unix.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// 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 darwin freebsd linux,amd64 linux,arm netbsd openbsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getsockopt(fd int, level, name int, v uintptr, l *sysSockoptLen) error {
|
||||
if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
|
||||
if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
172
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go
generated
vendored
172
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go
generated
vendored
@@ -1,172 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp6", "[::1]:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
p := ipv6.NewPacketConn(c)
|
||||
defer p.Close()
|
||||
|
||||
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
|
||||
}
|
||||
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
Src: net.IPv6loopback,
|
||||
Dst: net.IPv6loopback,
|
||||
}
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
ifi := loopbackInterface()
|
||||
if ifi != nil {
|
||||
cm.IfIndex = ifi.Index
|
||||
}
|
||||
wb := []byte("HELLO-R-U-THERE")
|
||||
|
||||
for i, toggle := range []bool{true, false, true} {
|
||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
}
|
||||
cm.HopLimit = i + 1
|
||||
if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
|
||||
}
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
} else if n != len(wb) {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
}
|
||||
rb := make([]byte, 128)
|
||||
if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
|
||||
}
|
||||
if n, cm, _, err := p.ReadFrom(rb); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
} else if !bytes.Equal(rb[:n], wb) {
|
||||
t.Fatalf("got %v; expected %v", rb[:n], wb)
|
||||
} else {
|
||||
t.Logf("rcvd cmsg: %v", cm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
p := ipv6.NewPacketConn(c)
|
||||
defer p.Close()
|
||||
|
||||
dst, err := net.ResolveIPAddr("ip6", "::1")
|
||||
if err != nil {
|
||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||
}
|
||||
|
||||
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||
cm := ipv6.ControlMessage{
|
||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||
Src: net.IPv6loopback,
|
||||
Dst: net.IPv6loopback,
|
||||
}
|
||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||
ifi := loopbackInterface()
|
||||
if ifi != nil {
|
||||
cm.IfIndex = ifi.Index
|
||||
}
|
||||
|
||||
var f ipv6.ICMPFilter
|
||||
f.SetAll(true)
|
||||
f.Set(ipv6.ICMPTypeEchoReply, false)
|
||||
if err := p.SetICMPFilter(&f); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||
}
|
||||
|
||||
var psh []byte
|
||||
for i, toggle := range []bool{true, false, true} {
|
||||
if toggle {
|
||||
psh = nil
|
||||
if err := p.SetChecksum(true, 2); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
psh = pshicmp
|
||||
// Some platforms never allow to disable the
|
||||
// kernel checksum processing.
|
||||
p.SetChecksum(false, -1)
|
||||
}
|
||||
wb, err := (&icmpMessage{
|
||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||
Body: &icmpEcho{
|
||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
},
|
||||
}).Marshal(psh)
|
||||
if err != nil {
|
||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||
}
|
||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||
}
|
||||
cm.HopLimit = i + 1
|
||||
if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
|
||||
}
|
||||
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||
} else if n != len(wb) {
|
||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
|
||||
}
|
||||
rb := make([]byte, 128)
|
||||
if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
|
||||
}
|
||||
if n, cm, _, err := p.ReadFrom(rb); err != nil {
|
||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||
} else {
|
||||
t.Logf("rcvd cmsg: %v", cm)
|
||||
if m, err := parseICMPMessage(rb[:n]); err != nil {
|
||||
t.Fatalf("parseICMPMessage failed: %v", err)
|
||||
} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
|
||||
t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go
generated
vendored
101
Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
// 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 ipv6_test
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConnUnicastSocketOptions(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp6", "[::1]:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen failed: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go acceptor(t, ln, done)
|
||||
|
||||
c, err := net.Dial("tcp6", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("net.Dial failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
testUnicastSocketOptions(t, ipv6.NewConn(c))
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
var packetConnUnicastSocketOptionTests = []struct {
|
||||
net, proto, addr string
|
||||
}{
|
||||
{"udp6", "", "[::1]:0"},
|
||||
{"ip6", ":ipv6-icmp", "::1"},
|
||||
}
|
||||
|
||||
func TestPacketConnUnicastSocketOptions(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "plan9", "solaris", "windows":
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
if !supportsIPv6 {
|
||||
t.Skip("ipv6 is not supported")
|
||||
}
|
||||
|
||||
for _, tt := range packetConnUnicastSocketOptionTests {
|
||||
if tt.net == "ip6" && os.Getuid() != 0 {
|
||||
t.Skip("must be root")
|
||||
}
|
||||
c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
|
||||
if err != nil {
|
||||
t.Fatalf("net.ListenPacket(%q, %q) failed: %v", tt.net+tt.proto, tt.addr, err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
testUnicastSocketOptions(t, ipv6.NewPacketConn(c))
|
||||
}
|
||||
}
|
||||
|
||||
type testIPv6UnicastConn interface {
|
||||
TrafficClass() (int, error)
|
||||
SetTrafficClass(int) error
|
||||
HopLimit() (int, error)
|
||||
SetHopLimit(int) error
|
||||
}
|
||||
|
||||
func testUnicastSocketOptions(t *testing.T, c testIPv6UnicastConn) {
|
||||
tclass := DiffServCS0 | NotECNTransport
|
||||
if err := c.SetTrafficClass(tclass); err != nil {
|
||||
t.Fatalf("ipv6.Conn.SetTrafficClass failed: %v", err)
|
||||
}
|
||||
if v, err := c.TrafficClass(); err != nil {
|
||||
t.Fatalf("ipv6.Conn.TrafficClass failed: %v", err)
|
||||
} else if v != tclass {
|
||||
t.Fatalf("got unexpected traffic class %v; expected %v", v, tclass)
|
||||
}
|
||||
|
||||
hoplim := 255
|
||||
if err := c.SetHopLimit(hoplim); err != nil {
|
||||
t.Fatalf("ipv6.Conn.SetHopLimit failed: %v", err)
|
||||
}
|
||||
if v, err := c.HopLimit(); err != nil {
|
||||
t.Fatalf("ipv6.Conn.HopLimit failed: %v", err)
|
||||
} else if v != hoplim {
|
||||
t.Fatalf("got unexpected hop limit %v; expected %v", v, hoplim)
|
||||
}
|
||||
}
|
||||
185
Godeps/_workspace/src/github.com/juju/ratelimit/LICENSE
generated
vendored
Normal file
185
Godeps/_workspace/src/github.com/juju/ratelimit/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
109
Godeps/_workspace/src/github.com/juju/ratelimit/README.md
generated
vendored
Normal file
109
Godeps/_workspace/src/github.com/juju/ratelimit/README.md
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
# ratelimit
|
||||
--
|
||||
import "github.com/juju/ratelimit"
|
||||
|
||||
The ratelimit package provides an efficient token bucket implementation. See
|
||||
http://en.wikipedia.org/wiki/Token_bucket.
|
||||
|
||||
## Usage
|
||||
|
||||
#### func Reader
|
||||
|
||||
```go
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader
|
||||
```
|
||||
Reader returns a reader that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### func Writer
|
||||
|
||||
```go
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer
|
||||
```
|
||||
Writer returns a reader that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### type Bucket
|
||||
|
||||
```go
|
||||
type Bucket struct {
|
||||
}
|
||||
```
|
||||
|
||||
Bucket represents a token bucket that fills at a predetermined rate. Methods on
|
||||
Bucket may be called concurrently.
|
||||
|
||||
#### func NewBucket
|
||||
|
||||
```go
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
|
||||
```
|
||||
NewBucket returns a new token bucket that fills at the rate of one token every
|
||||
fillInterval, up to the given maximum capacity. Both arguments must be positive.
|
||||
The bucket is initially full.
|
||||
|
||||
#### func NewBucketWithRate
|
||||
|
||||
```go
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket
|
||||
```
|
||||
NewBucketWithRate returns a token bucket that fills the bucket at the rate of
|
||||
rate tokens per second up to the given maximum capacity. Because of limited
|
||||
clock resolution, at high rates, the actual rate may be up to 1% different from
|
||||
the specified rate.
|
||||
|
||||
#### func (*Bucket) Rate
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Rate() float64
|
||||
```
|
||||
Rate returns the fill rate of the bucket, in tokens per second.
|
||||
|
||||
#### func (*Bucket) Take
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Take(count int64) time.Duration
|
||||
```
|
||||
Take takes count tokens from the bucket without blocking. It returns the time
|
||||
that the caller should wait until the tokens are actually available.
|
||||
|
||||
Note that if the request is irrevocable - there is no way to return tokens to
|
||||
the bucket once this method commits us to taking them.
|
||||
|
||||
#### func (*Bucket) TakeAvailable
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64
|
||||
```
|
||||
TakeAvailable takes up to count immediately available tokens from the bucket. It
|
||||
returns the number of tokens removed, or zero if there are no available tokens.
|
||||
It does not block.
|
||||
|
||||
#### func (*Bucket) TakeMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)
|
||||
```
|
||||
TakeMaxDuration is like Take, except that it will only take tokens from the
|
||||
bucket if the wait time for the tokens is no greater than maxWait.
|
||||
|
||||
If it would take longer than maxWait for the tokens to become available, it does
|
||||
nothing and reports false, otherwise it returns the time that the caller should
|
||||
wait until the tokens are actually available, and reports true.
|
||||
|
||||
#### func (*Bucket) Wait
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Wait(count int64)
|
||||
```
|
||||
Wait takes count tokens from the bucket, waiting until they are available.
|
||||
|
||||
#### func (*Bucket) WaitMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool
|
||||
```
|
||||
WaitMaxDuration is like Wait except that it will only take tokens from the
|
||||
bucket if it needs to wait for no greater than maxWait. It reports whether any
|
||||
tokens have been removed from the bucket If no tokens have been removed, it
|
||||
returns immediately.
|
||||
227
Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit.go
generated
vendored
Normal file
227
Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit.go
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
// The ratelimit package provides an efficient token bucket implementation.
|
||||
// See http://en.wikipedia.org/wiki/Token_bucket.
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bucket represents a token bucket that fills at a predetermined rate.
|
||||
// Methods on Bucket may be called concurrently.
|
||||
type Bucket struct {
|
||||
startTime time.Time
|
||||
capacity int64
|
||||
quantum int64
|
||||
fillInterval time.Duration
|
||||
|
||||
// The mutex guards the fields following it.
|
||||
mu sync.Mutex
|
||||
|
||||
// avail holds the number of available tokens
|
||||
// in the bucket, as of availTick ticks from startTime.
|
||||
// It will be negative when there are consumers
|
||||
// waiting for tokens.
|
||||
avail int64
|
||||
availTick int64
|
||||
}
|
||||
|
||||
// NewBucket returns a new token bucket that fills at the
|
||||
// rate of one token every fillInterval, up to the given
|
||||
// maximum capacity. Both arguments must be
|
||||
// positive. The bucket is initially full.
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket {
|
||||
return newBucketWithQuantum(fillInterval, capacity, 1)
|
||||
}
|
||||
|
||||
// rateMargin specifes the allowed variance of actual
|
||||
// rate from specified rate. 1% seems reasonable.
|
||||
const rateMargin = 0.01
|
||||
|
||||
// NewBucketWithRate returns a token bucket that fills the bucket
|
||||
// at the rate of rate tokens per second up to the given
|
||||
// maximum capacity. Because of limited clock resolution,
|
||||
// at high rates, the actual rate may be up to 1% different from the
|
||||
// specified rate.
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket {
|
||||
for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) {
|
||||
fillInterval := time.Duration(1e9 * float64(quantum) / rate)
|
||||
if fillInterval <= 0 {
|
||||
continue
|
||||
}
|
||||
tb := newBucketWithQuantum(fillInterval, capacity, quantum)
|
||||
if diff := abs(tb.Rate() - rate); diff/rate <= rateMargin {
|
||||
return tb
|
||||
}
|
||||
}
|
||||
panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64))
|
||||
}
|
||||
|
||||
// nextQuantum returns the next quantum to try after q.
|
||||
// We grow the quantum exponentially, but slowly, so we
|
||||
// get a good fit in the lower numbers.
|
||||
func nextQuantum(q int64) int64 {
|
||||
q1 := q * 11 / 10
|
||||
if q1 == q {
|
||||
q1++
|
||||
}
|
||||
return q1
|
||||
}
|
||||
|
||||
// newBucketWithQuantum is similar to NewBucket, but allows
|
||||
// the specification of the quantum size - quantum tokens
|
||||
// are added every fillInterval. This is so that we can get accurate
|
||||
// rates even when we want to add more than one token per ns.
|
||||
func newBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket {
|
||||
if fillInterval <= 0 {
|
||||
panic("token bucket fill interval is not > 0")
|
||||
}
|
||||
if capacity <= 0 {
|
||||
panic("token bucket capacity is not > 0")
|
||||
}
|
||||
if quantum <= 0 {
|
||||
panic("token bucket quantum is not > 0")
|
||||
}
|
||||
return &Bucket{
|
||||
startTime: time.Now(),
|
||||
capacity: capacity,
|
||||
quantum: quantum,
|
||||
avail: capacity,
|
||||
fillInterval: fillInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// Wait takes count tokens from the bucket, waiting until they are
|
||||
// available.
|
||||
func (tb *Bucket) Wait(count int64) {
|
||||
if d := tb.Take(count); d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
|
||||
// WaitMaxDuration is like Wait except that it will
|
||||
// only take tokens from the bucket if it needs to wait
|
||||
// for no greater than maxWait. It reports whether
|
||||
// any tokens have been removed from the bucket
|
||||
// If no tokens have been removed, it returns immediately.
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool {
|
||||
d, ok := tb.TakeMaxDuration(count, maxWait)
|
||||
if d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
const infinityDuration time.Duration = 0x7fffffffffffffff
|
||||
|
||||
// Take takes count tokens from the bucket without blocking. It returns
|
||||
// the time that the caller should wait until the tokens are actually
|
||||
// available.
|
||||
//
|
||||
// Note that if the request is irrevocable - there is no way to return
|
||||
// tokens to the bucket once this method commits us to taking them.
|
||||
func (tb *Bucket) Take(count int64) time.Duration {
|
||||
d, _ := tb.take(time.Now(), count, infinityDuration)
|
||||
return d
|
||||
}
|
||||
|
||||
// TakeMaxDuration is like Take, except that
|
||||
// it will only take tokens from the bucket if the wait
|
||||
// time for the tokens is no greater than maxWait.
|
||||
//
|
||||
// If it would take longer than maxWait for the tokens
|
||||
// to become available, it does nothing and reports false,
|
||||
// otherwise it returns the time that the caller should
|
||||
// wait until the tokens are actually available, and reports
|
||||
// true.
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
return tb.take(time.Now(), count, maxWait)
|
||||
}
|
||||
|
||||
// TakeAvailable takes up to count immediately available tokens from the
|
||||
// bucket. It returns the number of tokens removed, or zero if there are
|
||||
// no available tokens. It does not block.
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64 {
|
||||
return tb.takeAvailable(time.Now(), count)
|
||||
}
|
||||
|
||||
// takeAvailable is the internal version of TakeAvailable - it takes the
|
||||
// current time as an argument to enable easy testing.
|
||||
func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
|
||||
if count <= 0 {
|
||||
return 0
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
tb.adjust(now)
|
||||
if tb.avail <= 0 {
|
||||
return 0
|
||||
}
|
||||
if count > tb.avail {
|
||||
count = tb.avail
|
||||
}
|
||||
tb.avail -= count
|
||||
return count
|
||||
}
|
||||
|
||||
// Rate returns the fill rate of the bucket, in tokens per second.
|
||||
func (tb *Bucket) Rate() float64 {
|
||||
return 1e9 * float64(tb.quantum) / float64(tb.fillInterval)
|
||||
}
|
||||
|
||||
// take is the internal version of Take - it takes the current time as
|
||||
// an argument to enable easy testing.
|
||||
func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
if count <= 0 {
|
||||
return 0, true
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
currentTick := tb.adjust(now)
|
||||
avail := tb.avail - count
|
||||
if avail >= 0 {
|
||||
tb.avail = avail
|
||||
return 0, true
|
||||
}
|
||||
// Round up the missing tokens to the nearest multiple
|
||||
// of quantum - the tokens won't be available until
|
||||
// that tick.
|
||||
endTick := currentTick + (-avail+tb.quantum-1)/tb.quantum
|
||||
endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval)
|
||||
waitTime := endTime.Sub(now)
|
||||
if waitTime > maxWait {
|
||||
return 0, false
|
||||
}
|
||||
tb.avail = avail
|
||||
return waitTime, true
|
||||
}
|
||||
|
||||
// adjust adjusts the current bucket capacity based on the current time.
|
||||
// It returns the current tick.
|
||||
func (tb *Bucket) adjust(now time.Time) (currentTick int64) {
|
||||
currentTick = int64(now.Sub(tb.startTime) / tb.fillInterval)
|
||||
|
||||
if tb.avail >= tb.capacity {
|
||||
return
|
||||
}
|
||||
tb.avail += (currentTick - tb.availTick) * tb.quantum
|
||||
if tb.avail > tb.capacity {
|
||||
tb.avail = tb.capacity
|
||||
}
|
||||
tb.availTick = currentTick
|
||||
return
|
||||
}
|
||||
|
||||
func abs(f float64) float64 {
|
||||
if f < 0 {
|
||||
return -f
|
||||
}
|
||||
return f
|
||||
}
|
||||
328
Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go
generated
vendored
Normal file
328
Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go
generated
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
gc "launchpad.net/gocheck"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPackage(t *testing.T) {
|
||||
gc.TestingT(t)
|
||||
}
|
||||
|
||||
type rateLimitSuite struct{}
|
||||
|
||||
var _ = gc.Suite(rateLimitSuite{})
|
||||
|
||||
type takeReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expectWait time.Duration
|
||||
}
|
||||
|
||||
var takeTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 500 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 1000 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 1250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expectWait: 5 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "sub-quantum time",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 7 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 3 * time.Millisecond,
|
||||
}, {
|
||||
time: 8 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 12 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}, {
|
||||
time: 80 * time.Millisecond,
|
||||
count: 2,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}},
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTake(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
if req.expectWait > 0 {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1)
|
||||
c.Assert(ok, gc.Equals, false)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
}
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type takeAvailableReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expect int64
|
||||
}
|
||||
|
||||
var takeAvailableTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeAvailableReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expect: 2,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 3,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expect: 10,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 70 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTakeAvailable(c *gc.C) {
|
||||
for i, test := range takeAvailableTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d := tb.takeAvailable(tb.startTime.Add(req.time), req.count)
|
||||
if d != req.expect {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestPanics(c *gc.C) {
|
||||
c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
}
|
||||
|
||||
func isCloseTo(x, y, tolerance float64) bool {
|
||||
return abs(x-y)/y < tolerance
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestRate(c *gc.C) {
|
||||
tb := NewBucket(1, 1)
|
||||
if !isCloseTo(tb.Rate(), 1e9, 0.00001) {
|
||||
c.Fatalf("got %v want 1e9", tb.Rate())
|
||||
}
|
||||
tb = NewBucket(2*time.Second, 1)
|
||||
if !isCloseTo(tb.Rate(), 0.5, 0.00001) {
|
||||
c.Fatalf("got %v want 0.5", tb.Rate())
|
||||
}
|
||||
tb = newBucketWithQuantum(100*time.Millisecond, 1, 5)
|
||||
if !isCloseTo(tb.Rate(), 50, 0.00001) {
|
||||
c.Fatalf("got %v want 50", tb.Rate())
|
||||
}
|
||||
}
|
||||
|
||||
func checkRate(c *gc.C, rate float64) {
|
||||
tb := NewBucketWithRate(rate, 1<<62)
|
||||
if !isCloseTo(tb.Rate(), rate, rateMargin) {
|
||||
c.Fatalf("got %g want %v", tb.Rate(), rate)
|
||||
}
|
||||
d, ok := tb.take(tb.startTime, 1<<62, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
|
||||
// Check that the actual rate is as expected by
|
||||
// asking for a not-quite multiple of the bucket's
|
||||
// quantum and checking that the wait time
|
||||
// correct.
|
||||
d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
expectTime := 1e9 * float64(tb.quantum) * 2 / rate
|
||||
if !isCloseTo(float64(d), expectTime, rateMargin) {
|
||||
c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestNewWithRate(c *gc.C) {
|
||||
for rate := float64(1); rate < 1e6; rate += 7 {
|
||||
checkRate(c, rate)
|
||||
}
|
||||
for _, rate := range []float64{
|
||||
1024 * 1024 * 1024,
|
||||
1e-5,
|
||||
0.9e-5,
|
||||
0.5,
|
||||
0.9,
|
||||
0.9e8,
|
||||
3e12,
|
||||
4e18,
|
||||
} {
|
||||
checkRate(c, rate)
|
||||
checkRate(c, rate/3)
|
||||
checkRate(c, rate*1.3)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWait(b *testing.B) {
|
||||
tb := NewBucket(1, 16*1024)
|
||||
for i := b.N - 1; i >= 0; i-- {
|
||||
tb.Wait(1)
|
||||
}
|
||||
}
|
||||
51
Godeps/_workspace/src/github.com/juju/ratelimit/reader.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/juju/ratelimit/reader.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import "io"
|
||||
|
||||
type reader struct {
|
||||
r io.Reader
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Reader returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader {
|
||||
return &reader{
|
||||
r: r,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) Read(buf []byte) (int, error) {
|
||||
n, err := r.r.Read(buf)
|
||||
if n <= 0 {
|
||||
return n, err
|
||||
}
|
||||
r.bucket.Wait(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
w io.Writer
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Writer returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer {
|
||||
return &writer{
|
||||
w: w,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) Write(buf []byte) (int, error) {
|
||||
w.bucket.Wait(int64(len(buf)))
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (C) 2013-2014 Jakob Borg
|
||||
Copyright (C) 2014 Jakob Borg and other contributors (see the CONTRIBUTORS file).
|
||||
|
||||
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
|
||||
|
||||
File diff suppressed because one or more lines are too long
35
build.sh
35
build.sh
@@ -3,10 +3,10 @@
|
||||
export COPYFILE_DISABLE=true
|
||||
|
||||
distFiles=(README.md LICENSE) # apart from the binary itself
|
||||
version=$(git describe --always)
|
||||
version=$(git describe --always --dirty)
|
||||
|
||||
build() {
|
||||
if which -s godep ; then
|
||||
if command -v godep >/dev/null ; then
|
||||
godep=godep
|
||||
else
|
||||
echo "Warning: no godep, using \"go get\" instead."
|
||||
@@ -14,7 +14,8 @@ build() {
|
||||
go get -d ./cmd/syncthing
|
||||
godep=
|
||||
fi
|
||||
${godep} go build -ldflags "-w -X main.Version $version" ./cmd/syncthing
|
||||
${godep} go build $* -ldflags "-w -X main.Version $version" ./cmd/syncthing
|
||||
${godep} go build -ldflags "-w -X main.Version $version" ./cmd/stcli
|
||||
}
|
||||
|
||||
prepare() {
|
||||
@@ -26,9 +27,12 @@ test() {
|
||||
}
|
||||
|
||||
sign() {
|
||||
id=BCE524C7
|
||||
if gpg --list-keys "$id" >/dev/null 2>&1 ; then
|
||||
gpg -ab -u "$id" "$1"
|
||||
if git describe --exact-match 2>/dev/null >/dev/null ; then
|
||||
# HEAD is a tag
|
||||
id=BCE524C7
|
||||
if gpg --list-keys "$id" >/dev/null 2>&1 ; then
|
||||
gpg -ab -u "$id" "$1"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -57,6 +61,10 @@ case "$1" in
|
||||
build
|
||||
;;
|
||||
|
||||
race)
|
||||
build -race
|
||||
;;
|
||||
|
||||
test)
|
||||
test
|
||||
;;
|
||||
@@ -78,8 +86,7 @@ case "$1" in
|
||||
prepare
|
||||
test || exit 1
|
||||
|
||||
export GOARM=7
|
||||
for os in darwin-amd64 linux-amd64 linux-arm freebsd-amd64 ; do
|
||||
for os in darwin-amd64 linux-386 linux-amd64 freebsd-amd64 windows-amd64 ; do
|
||||
export GOOS=${os%-*}
|
||||
export GOARCH=${os#*-}
|
||||
|
||||
@@ -97,6 +104,18 @@ case "$1" in
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
export GOOS=linux
|
||||
export GOARCH=arm
|
||||
|
||||
export GOARM=7
|
||||
build
|
||||
tarDist "syncthing-linux-armv7-$version"
|
||||
|
||||
export GOARM=6
|
||||
build
|
||||
tarDist "syncthing-linux-armv6-$version"
|
||||
|
||||
;;
|
||||
|
||||
upload)
|
||||
|
||||
47
cid/cid.go
47
cid/cid.go
@@ -1,18 +1,30 @@
|
||||
// Package cid provides a manager for mappings between node ID:s and connection ID:s.
|
||||
package cid
|
||||
|
||||
import "sync"
|
||||
|
||||
type Map struct {
|
||||
toCid map[string]int
|
||||
sync.Mutex
|
||||
toCid map[string]uint
|
||||
toName []string
|
||||
}
|
||||
|
||||
var (
|
||||
LocalName = "<local>"
|
||||
LocalID uint = 0
|
||||
)
|
||||
|
||||
func NewMap() *Map {
|
||||
return &Map{
|
||||
toCid: make(map[string]int),
|
||||
toCid: map[string]uint{"<local>": 0},
|
||||
toName: []string{"<local>"},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map) Get(name string) int {
|
||||
func (m *Map) Get(name string) uint {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
cid, ok := m.toCid[name]
|
||||
if ok {
|
||||
return cid
|
||||
@@ -22,22 +34,45 @@ func (m *Map) Get(name string) int {
|
||||
for i, n := range m.toName {
|
||||
if n == "" {
|
||||
m.toName[i] = name
|
||||
m.toCid[name] = i
|
||||
return i
|
||||
m.toCid[name] = uint(i)
|
||||
return uint(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Add it to the end since we didn't find a free slot
|
||||
m.toName = append(m.toName, name)
|
||||
cid = len(m.toName) - 1
|
||||
cid = uint(len(m.toName) - 1)
|
||||
m.toCid[name] = cid
|
||||
return cid
|
||||
}
|
||||
|
||||
func (m *Map) Name(cid uint) string {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
return m.toName[cid]
|
||||
}
|
||||
|
||||
func (m *Map) Names() []string {
|
||||
m.Lock()
|
||||
|
||||
var names []string
|
||||
for _, name := range m.toName {
|
||||
if name != "" {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
|
||||
m.Unlock()
|
||||
return names
|
||||
}
|
||||
|
||||
func (m *Map) Clear(name string) {
|
||||
m.Lock()
|
||||
cid, ok := m.toCid[name]
|
||||
if ok {
|
||||
m.toName[cid] = ""
|
||||
delete(m.toCid, name)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
27
cid/cid_test.go
Normal file
27
cid/cid_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package cid
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
m := NewMap()
|
||||
|
||||
if i := m.Get("foo"); i != 1 {
|
||||
t.Errorf("Unexpected id %d != 1", i)
|
||||
}
|
||||
if i := m.Get("bar"); i != 2 {
|
||||
t.Errorf("Unexpected id %d != 2", i)
|
||||
}
|
||||
if i := m.Get("foo"); i != 1 {
|
||||
t.Errorf("Unexpected id %d != 1", i)
|
||||
}
|
||||
if i := m.Get("bar"); i != 2 {
|
||||
t.Errorf("Unexpected id %d != 2", i)
|
||||
}
|
||||
|
||||
if LocalID != 0 {
|
||||
t.Error("LocalID should be 0")
|
||||
}
|
||||
if i := m.Get(LocalName); i != LocalID {
|
||||
t.Errorf("Unexpected id %d != %c", i, LocalID)
|
||||
}
|
||||
}
|
||||
1
cmd/.gitignore
vendored
1
cmd/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
!syncthing
|
||||
!stcli
|
||||
|
||||
@@ -63,7 +63,7 @@ func walkerFor(basePath string) filepath.WalkFunc {
|
||||
|
||||
name, _ = filepath.Rel(basePath, name)
|
||||
assets = append(assets, asset{
|
||||
Name: name,
|
||||
Name: filepath.ToSlash(name),
|
||||
HexData: fmt.Sprintf("%x", buf.Bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
72
cmd/stcli/logger.go
Normal file
72
cmd/stcli/logger.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var logger *log.Logger
|
||||
|
||||
func init() {
|
||||
log.SetOutput(os.Stderr)
|
||||
logger = log.New(os.Stderr, "", log.Flags())
|
||||
}
|
||||
|
||||
func debugln(vals ...interface{}) {
|
||||
s := fmt.Sprintln(vals...)
|
||||
logger.Output(2, "DEBUG: "+s)
|
||||
}
|
||||
|
||||
func debugf(format string, vals ...interface{}) {
|
||||
s := fmt.Sprintf(format, vals...)
|
||||
logger.Output(2, "DEBUG: "+s)
|
||||
}
|
||||
|
||||
func infoln(vals ...interface{}) {
|
||||
s := fmt.Sprintln(vals...)
|
||||
logger.Output(2, "INFO: "+s)
|
||||
}
|
||||
|
||||
func infof(format string, vals ...interface{}) {
|
||||
s := fmt.Sprintf(format, vals...)
|
||||
logger.Output(2, "INFO: "+s)
|
||||
}
|
||||
|
||||
func okln(vals ...interface{}) {
|
||||
s := fmt.Sprintln(vals...)
|
||||
logger.Output(2, "OK: "+s)
|
||||
}
|
||||
|
||||
func okf(format string, vals ...interface{}) {
|
||||
s := fmt.Sprintf(format, vals...)
|
||||
logger.Output(2, "OK: "+s)
|
||||
}
|
||||
|
||||
func warnln(vals ...interface{}) {
|
||||
s := fmt.Sprintln(vals...)
|
||||
logger.Output(2, "WARNING: "+s)
|
||||
}
|
||||
|
||||
func warnf(format string, vals ...interface{}) {
|
||||
s := fmt.Sprintf(format, vals...)
|
||||
logger.Output(2, "WARNING: "+s)
|
||||
}
|
||||
|
||||
func fatalln(vals ...interface{}) {
|
||||
s := fmt.Sprintln(vals...)
|
||||
logger.Output(2, "FATAL: "+s)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
func fatalf(format string, vals ...interface{}) {
|
||||
s := fmt.Sprintf(format, vals...)
|
||||
logger.Output(2, "FATAL: "+s)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
func fatalErr(err error) {
|
||||
if err != nil {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
137
cmd/stcli/main.go
Normal file
137
cmd/stcli/main.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
exit bool
|
||||
cmd string
|
||||
confDir string
|
||||
target string
|
||||
get string
|
||||
pc protocol.Connection
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
flag.StringVar(&cmd, "cmd", "idx", "Command")
|
||||
flag.StringVar(&confDir, "home", ".", "Certificates directory")
|
||||
flag.StringVar(&target, "target", "127.0.0.1:22000", "Target node")
|
||||
flag.StringVar(&get, "get", "", "Get file")
|
||||
flag.BoolVar(&exit, "exit", false, "Exit after command")
|
||||
flag.Parse()
|
||||
|
||||
connect(target)
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
func connect(target string) {
|
||||
cert, err := loadCert(confDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
myID := string(certID(cert.Certificate[0]))
|
||||
|
||||
tlsCfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
NextProtos: []string{"bep/1.0"},
|
||||
ServerName: myID,
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", target, tlsCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
remoteID := certID(conn.ConnectionState().PeerCertificates[0].Raw)
|
||||
|
||||
pc = protocol.NewConnection(remoteID, conn, conn, Model{}, nil)
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
}
|
||||
|
||||
func prtIndex(files []protocol.FileInfo) {
|
||||
for _, f := range files {
|
||||
log.Printf("%q (v:%d mod:%d flags:0%o nblocks:%d)", f.Name, f.Version, f.Modified, f.Flags, len(f.Blocks))
|
||||
for _, b := range f.Blocks {
|
||||
log.Printf(" %6d %x", b.Size, b.Hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) Index(nodeID string, repo string, files []protocol.FileInfo) {
|
||||
log.Printf("Received index for repo %q", repo)
|
||||
if cmd == "idx" {
|
||||
prtIndex(files)
|
||||
if get != "" {
|
||||
for _, f := range files {
|
||||
if f.Name == get {
|
||||
go getFile(f)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if exit {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFile(f protocol.FileInfo) {
|
||||
fn := filepath.Base(f.Name)
|
||||
fd, err := os.Create(fn)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var offset int64
|
||||
for _, b := range f.Blocks {
|
||||
log.Printf("Request %q %d - %d", f.Name, offset, offset+int64(b.Size))
|
||||
bs, err := pc.Request("default", f.Name, offset, int(b.Size))
|
||||
log.Printf(" - got %d bytes", len(bs))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
offset += int64(b.Size)
|
||||
fd.Write(bs)
|
||||
}
|
||||
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
func (m Model) IndexUpdate(nodeID string, repo string, files []protocol.FileInfo) {
|
||||
log.Printf("Received index update for repo %q", repo)
|
||||
if cmd == "idx" {
|
||||
prtIndex(files)
|
||||
if exit {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) Request(nodeID, repo string, name string, offset int64, size int) ([]byte, error) {
|
||||
log.Println("Received request")
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (m Model) Close(nodeID string, err error) {
|
||||
log.Println("Received close")
|
||||
}
|
||||
71
cmd/stcli/tls.go
Normal file
71
cmd/stcli/tls.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base32"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tlsRSABits = 3072
|
||||
tlsName = "syncthing"
|
||||
)
|
||||
|
||||
func loadCert(dir string) (tls.Certificate, error) {
|
||||
return tls.LoadX509KeyPair(filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem"))
|
||||
}
|
||||
|
||||
func certID(bs []byte) string {
|
||||
hf := sha256.New()
|
||||
hf.Write(bs)
|
||||
id := hf.Sum(nil)
|
||||
return strings.Trim(base32.StdEncoding.EncodeToString(id), "=")
|
||||
}
|
||||
|
||||
func newCertificate(dir string) {
|
||||
infoln("Generating RSA certificate and key...")
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, tlsRSABits)
|
||||
fatalErr(err)
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: new(big.Int).SetInt64(0),
|
||||
Subject: pkix.Name{
|
||||
CommonName: tlsName,
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
fatalErr(err)
|
||||
|
||||
certOut, err := os.Create(filepath.Join(dir, "cert.pem"))
|
||||
fatalErr(err)
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
okln("Created RSA certificate file")
|
||||
|
||||
keyOut, err := os.OpenFile(filepath.Join(dir, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
fatalErr(err)
|
||||
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
keyOut.Close()
|
||||
okln("Created RSA key file")
|
||||
}
|
||||
1
cmd/syncthing/.gitignore
vendored
Normal file
1
cmd/syncthing/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.idx.gz
|
||||
94
cmd/syncthing/blockqueue.go
Normal file
94
cmd/syncthing/blockqueue.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import "github.com/calmh/syncthing/scanner"
|
||||
|
||||
type bqAdd struct {
|
||||
file scanner.File
|
||||
have []scanner.Block
|
||||
need []scanner.Block
|
||||
}
|
||||
|
||||
type bqBlock struct {
|
||||
file scanner.File
|
||||
block scanner.Block // get this block from the network
|
||||
copy []scanner.Block // copy these blocks from the old version of the file
|
||||
last bool
|
||||
}
|
||||
|
||||
type blockQueue struct {
|
||||
inbox chan bqAdd
|
||||
outbox chan bqBlock
|
||||
|
||||
queued []bqBlock
|
||||
}
|
||||
|
||||
func newBlockQueue() *blockQueue {
|
||||
q := &blockQueue{
|
||||
inbox: make(chan bqAdd),
|
||||
outbox: make(chan bqBlock),
|
||||
}
|
||||
go q.run()
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *blockQueue) addBlock(a bqAdd) {
|
||||
// If we already have it queued, return
|
||||
for _, b := range q.queued {
|
||||
if b.file.Name == a.file.Name {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(a.have) > 0 {
|
||||
// First queue a copy operation
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
copy: a.have,
|
||||
})
|
||||
}
|
||||
// Queue the needed blocks individually
|
||||
l := len(a.need)
|
||||
for i, b := range a.need {
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
block: b,
|
||||
last: i == l-1,
|
||||
})
|
||||
}
|
||||
|
||||
if l == 0 {
|
||||
// If we didn't have anything to fetch, queue an empty block with the "last" flag set to close the file.
|
||||
q.queued = append(q.queued, bqBlock{
|
||||
file: a.file,
|
||||
last: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (q *blockQueue) run() {
|
||||
for {
|
||||
if len(q.queued) == 0 {
|
||||
q.addBlock(<-q.inbox)
|
||||
} else {
|
||||
next := q.queued[0]
|
||||
select {
|
||||
case a := <-q.inbox:
|
||||
q.addBlock(a)
|
||||
case q.outbox <- next:
|
||||
q.queued = q.queued[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *blockQueue) put(a bqAdd) {
|
||||
q.inbox <- a
|
||||
}
|
||||
|
||||
func (q *blockQueue) get() bqBlock {
|
||||
return <-q.outbox
|
||||
}
|
||||
|
||||
func (q *blockQueue) empty() bool {
|
||||
// There is a race condition here. We're only mostly sure the queue is empty if the expression below is true.
|
||||
return len(q.queued) == 0 && len(q.inbox) == 0 && len(q.outbox) == 0
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type RepositoryConfiguration struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Directory string `xml:"directory,attr"`
|
||||
Nodes []NodeConfiguration `xml:"node"`
|
||||
}
|
||||
@@ -32,7 +33,6 @@ type NodeConfiguration struct {
|
||||
type OptionsConfiguration struct {
|
||||
ListenAddress []string `xml:"listenAddress" default:":22000" ini:"listen-address"`
|
||||
ReadOnly bool `xml:"readOnly" ini:"read-only"`
|
||||
AllowDelete bool `xml:"allowDelete" default:"true" ini:"allow-delete"`
|
||||
FollowSymlinks bool `xml:"followSymlinks" default:"true" ini:"follow-symlinks"`
|
||||
GUIEnabled bool `xml:"guiEnabled" default:"true" ini:"gui-enabled"`
|
||||
GUIAddress string `xml:"guiAddress" default:"127.0.0.1:8080" ini:"gui-address"`
|
||||
@@ -182,6 +182,20 @@ func readConfigXML(rd io.Reader) (Configuration, error) {
|
||||
fillNilSlices(&cfg.Options)
|
||||
|
||||
cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
|
||||
|
||||
var seenRepos = map[string]bool{}
|
||||
for i := range cfg.Repositories {
|
||||
if cfg.Repositories[i].ID == "" {
|
||||
cfg.Repositories[i].ID = "default"
|
||||
}
|
||||
|
||||
id := cfg.Repositories[i].ID
|
||||
if seenRepos[id] {
|
||||
panic("duplicate repository ID " + id)
|
||||
}
|
||||
seenRepos[id] = true
|
||||
}
|
||||
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ func TestDefaultValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
ListenAddress: []string{":22000"},
|
||||
ReadOnly: false,
|
||||
AllowDelete: true,
|
||||
FollowSymlinks: true,
|
||||
GUIEnabled: true,
|
||||
GUIAddress: "127.0.0.1:8080",
|
||||
@@ -90,7 +89,6 @@ func TestOverriddenValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
ListenAddress: []string{":23000"},
|
||||
ReadOnly: true,
|
||||
AllowDelete: false,
|
||||
FollowSymlinks: false,
|
||||
GUIEnabled: false,
|
||||
GUIAddress: "125.2.2.2:8080",
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/buffers"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
type fileMonitor struct {
|
||||
name string // in-repo name
|
||||
path string // full path
|
||||
writeDone sync.WaitGroup
|
||||
model *Model
|
||||
global scanner.File
|
||||
localBlocks []scanner.Block
|
||||
copyError error
|
||||
writeError error
|
||||
}
|
||||
|
||||
func (m *fileMonitor) FileBegins(cc <-chan content) error {
|
||||
if debugPull {
|
||||
dlog.Println("file begins:", m.name)
|
||||
}
|
||||
|
||||
tmp := defTempNamer.TempName(m.path)
|
||||
|
||||
dir := path.Dir(tmp)
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
outFile, err := os.Create(tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.writeDone.Add(1)
|
||||
|
||||
var writeWg sync.WaitGroup
|
||||
if len(m.localBlocks) > 0 {
|
||||
writeWg.Add(1)
|
||||
inFile, err := os.Open(m.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy local blocks, close infile when done
|
||||
go m.copyLocalBlocks(inFile, outFile, &writeWg)
|
||||
}
|
||||
|
||||
// Write remote blocks,
|
||||
writeWg.Add(1)
|
||||
go m.copyRemoteBlocks(cc, outFile, &writeWg)
|
||||
|
||||
// Wait for both writing routines, then close the outfile
|
||||
go func() {
|
||||
writeWg.Wait()
|
||||
outFile.Close()
|
||||
m.writeDone.Done()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *fileMonitor) copyLocalBlocks(inFile, outFile *os.File, writeWg *sync.WaitGroup) {
|
||||
defer inFile.Close()
|
||||
defer writeWg.Done()
|
||||
|
||||
var buf = buffers.Get(BlockSize)
|
||||
defer buffers.Put(buf)
|
||||
|
||||
for _, lb := range m.localBlocks {
|
||||
buf = buf[:lb.Size]
|
||||
_, err := inFile.ReadAt(buf, lb.Offset)
|
||||
if err != nil {
|
||||
m.copyError = err
|
||||
return
|
||||
}
|
||||
_, err = outFile.WriteAt(buf, lb.Offset)
|
||||
if err != nil {
|
||||
m.copyError = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fileMonitor) copyRemoteBlocks(cc <-chan content, outFile *os.File, writeWg *sync.WaitGroup) {
|
||||
defer writeWg.Done()
|
||||
|
||||
for content := range cc {
|
||||
_, err := outFile.WriteAt(content.data, content.offset)
|
||||
buffers.Put(content.data)
|
||||
if err != nil {
|
||||
m.writeError = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fileMonitor) FileDone() error {
|
||||
if debugPull {
|
||||
dlog.Println("file done:", m.name)
|
||||
}
|
||||
|
||||
m.writeDone.Wait()
|
||||
|
||||
tmp := defTempNamer.TempName(m.path)
|
||||
defer os.Remove(tmp)
|
||||
|
||||
if m.copyError != nil {
|
||||
return m.copyError
|
||||
}
|
||||
if m.writeError != nil {
|
||||
return m.writeError
|
||||
}
|
||||
|
||||
err := hashCheck(tmp, m.global.Blocks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chtimes(tmp, time.Unix(m.global.Modified, 0), time.Unix(m.global.Modified, 0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chmod(tmp, os.FileMode(m.global.Flags&0777))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Rename(tmp, m.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.model.updateLocal(m.global)
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashCheck(name string, correct []scanner.Block) error {
|
||||
rf, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rf.Close()
|
||||
|
||||
current, err := scanner.Blocks(rf, BlockSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(current) != len(correct) {
|
||||
return errors.New("incorrect number of blocks")
|
||||
}
|
||||
for i := range current {
|
||||
if bytes.Compare(current[i].Hash, correct[i].Hash) != 0 {
|
||||
return fmt.Errorf("hash mismatch: %x != %x", current[i], correct[i])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
type Monitor interface {
|
||||
FileBegins(<-chan content) error
|
||||
FileDone() error
|
||||
}
|
||||
|
||||
type FileQueue struct {
|
||||
files queuedFileList
|
||||
sorted bool
|
||||
fmut sync.Mutex // protects files and sorted
|
||||
availability map[string][]string
|
||||
amut sync.Mutex // protects availability
|
||||
queued map[string]bool
|
||||
}
|
||||
|
||||
type queuedFile struct {
|
||||
name string
|
||||
blocks []scanner.Block
|
||||
activeBlocks []bool
|
||||
given int
|
||||
remaining int
|
||||
channel chan content
|
||||
nodes []string
|
||||
nodesChecked time.Time
|
||||
monitor Monitor
|
||||
}
|
||||
|
||||
type content struct {
|
||||
offset int64
|
||||
data []byte
|
||||
}
|
||||
|
||||
type queuedFileList []queuedFile
|
||||
|
||||
func (l queuedFileList) Len() int { return len(l) }
|
||||
|
||||
func (l queuedFileList) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
|
||||
|
||||
func (l queuedFileList) Less(a, b int) bool {
|
||||
// Sort by most blocks already given out, then alphabetically
|
||||
if l[a].given != l[b].given {
|
||||
return l[a].given > l[b].given
|
||||
}
|
||||
return l[a].name < l[b].name
|
||||
}
|
||||
|
||||
type queuedBlock struct {
|
||||
name string
|
||||
block scanner.Block
|
||||
index int
|
||||
}
|
||||
|
||||
func NewFileQueue() *FileQueue {
|
||||
return &FileQueue{
|
||||
availability: make(map[string][]string),
|
||||
queued: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *FileQueue) Add(name string, blocks []scanner.Block, monitor Monitor) {
|
||||
q.fmut.Lock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
if q.queued[name] {
|
||||
return
|
||||
}
|
||||
|
||||
q.files = append(q.files, queuedFile{
|
||||
name: name,
|
||||
blocks: blocks,
|
||||
activeBlocks: make([]bool, len(blocks)),
|
||||
remaining: len(blocks),
|
||||
channel: make(chan content),
|
||||
monitor: monitor,
|
||||
})
|
||||
q.queued[name] = true
|
||||
q.sorted = false
|
||||
}
|
||||
|
||||
func (q *FileQueue) Len() int {
|
||||
q.fmut.Lock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
return len(q.files)
|
||||
}
|
||||
|
||||
func (q *FileQueue) Get(nodeID string) (queuedBlock, bool) {
|
||||
q.fmut.Lock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
if !q.sorted {
|
||||
sort.Sort(q.files)
|
||||
q.sorted = true
|
||||
}
|
||||
|
||||
for i := range q.files {
|
||||
qf := &q.files[i]
|
||||
|
||||
q.amut.Lock()
|
||||
av := q.availability[qf.name]
|
||||
q.amut.Unlock()
|
||||
|
||||
if len(av) == 0 {
|
||||
// Noone has the file we want; abort.
|
||||
if qf.remaining != len(qf.blocks) {
|
||||
// We have already started on this file; close it down
|
||||
close(qf.channel)
|
||||
if mon := qf.monitor; mon != nil {
|
||||
mon.FileDone()
|
||||
}
|
||||
}
|
||||
delete(q.queued, qf.name)
|
||||
q.deleteAt(i)
|
||||
return queuedBlock{}, false
|
||||
}
|
||||
|
||||
for _, ni := range av {
|
||||
// Find and return the next block in the queue
|
||||
if ni == nodeID {
|
||||
for j, b := range qf.blocks {
|
||||
if !qf.activeBlocks[j] {
|
||||
qf.activeBlocks[j] = true
|
||||
qf.given++
|
||||
return queuedBlock{
|
||||
name: qf.name,
|
||||
block: b,
|
||||
index: j,
|
||||
}, true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found nothing to do
|
||||
return queuedBlock{}, false
|
||||
}
|
||||
|
||||
func (q *FileQueue) Done(file string, offset int64, data []byte) {
|
||||
q.fmut.Lock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
c := content{
|
||||
offset: offset,
|
||||
data: data,
|
||||
}
|
||||
for i := range q.files {
|
||||
qf := &q.files[i]
|
||||
|
||||
if qf.name == file {
|
||||
if qf.monitor != nil && qf.remaining == len(qf.blocks) {
|
||||
err := qf.monitor.FileBegins(qf.channel)
|
||||
if err != nil {
|
||||
log.Printf("WARNING: %s: %v (not synced)", qf.name, err)
|
||||
delete(q.queued, qf.name)
|
||||
q.deleteAt(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
qf.channel <- c
|
||||
qf.remaining--
|
||||
|
||||
if qf.remaining == 0 {
|
||||
close(qf.channel)
|
||||
if qf.monitor != nil {
|
||||
err := qf.monitor.FileDone()
|
||||
if err != nil {
|
||||
log.Printf("WARNING: %s: %v", qf.name, err)
|
||||
}
|
||||
}
|
||||
delete(q.queued, qf.name)
|
||||
q.deleteAt(i)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// We found nothing, might have errored out already
|
||||
}
|
||||
|
||||
func (q *FileQueue) QueuedFiles() (files []string) {
|
||||
q.fmut.Lock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
for _, qf := range q.files {
|
||||
files = append(files, qf.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (q *FileQueue) deleteAt(i int) {
|
||||
q.files = append(q.files[:i], q.files[i+1:]...)
|
||||
}
|
||||
|
||||
func (q *FileQueue) deleteFile(n string) {
|
||||
for i, file := range q.files {
|
||||
if n == file.name {
|
||||
q.deleteAt(i)
|
||||
delete(q.queued, file.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *FileQueue) SetAvailable(file string, nodes []string) {
|
||||
q.amut.Lock()
|
||||
defer q.amut.Unlock()
|
||||
|
||||
q.availability[file] = nodes
|
||||
}
|
||||
|
||||
func (q *FileQueue) RemoveAvailable(toRemove string) {
|
||||
q.fmut.Lock()
|
||||
q.amut.Lock()
|
||||
defer q.amut.Unlock()
|
||||
defer q.fmut.Unlock()
|
||||
|
||||
for file, nodes := range q.availability {
|
||||
for i, node := range nodes {
|
||||
if node == toRemove {
|
||||
q.availability[file] = nodes[:i+copy(nodes[i:], nodes[i+1:])]
|
||||
if len(q.availability[file]) == 0 {
|
||||
q.deleteFile(file)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
func TestFileQueueAdd(t *testing.T) {
|
||||
q := NewFileQueue()
|
||||
q.Add("foo", nil, nil)
|
||||
}
|
||||
|
||||
func TestFileQueueAddSorting(t *testing.T) {
|
||||
q := NewFileQueue()
|
||||
q.SetAvailable("zzz", []string{"nodeID"})
|
||||
q.SetAvailable("aaa", []string{"nodeID"})
|
||||
|
||||
q.Add("zzz", []scanner.Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||
q.Add("aaa", []scanner.Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||
b, _ := q.Get("nodeID")
|
||||
if b.name != "aaa" {
|
||||
t.Errorf("Incorrectly sorted get: %+v", b)
|
||||
}
|
||||
|
||||
q = NewFileQueue()
|
||||
q.SetAvailable("zzz", []string{"nodeID"})
|
||||
q.SetAvailable("aaa", []string{"nodeID"})
|
||||
|
||||
q.Add("zzz", []scanner.Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||
b, _ = q.Get("nodeID") // Start on zzzz
|
||||
if b.name != "zzz" {
|
||||
t.Errorf("Incorrectly sorted get: %+v", b)
|
||||
}
|
||||
q.Add("aaa", []scanner.Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||
b, _ = q.Get("nodeID")
|
||||
if b.name != "zzz" {
|
||||
// Continue rather than starting a new file
|
||||
t.Errorf("Incorrectly sorted get: %+v", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileQueueLen(t *testing.T) {
|
||||
q := NewFileQueue()
|
||||
q.Add("foo", nil, nil)
|
||||
q.Add("bar", nil, nil)
|
||||
|
||||
if l := q.Len(); l != 2 {
|
||||
t.Errorf("Incorrect len %d != 2 after adds", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileQueueGet(t *testing.T) {
|
||||
q := NewFileQueue()
|
||||
q.SetAvailable("foo", []string{"nodeID"})
|
||||
q.SetAvailable("bar", []string{"nodeID"})
|
||||
|
||||
q.Add("foo", []scanner.Block{
|
||||
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
||||
{Offset: 128, Size: 128, Hash: []byte("some other foo hash bytes")},
|
||||
{Offset: 256, Size: 128, Hash: []byte("more foo hash bytes")},
|
||||
}, nil)
|
||||
q.Add("bar", []scanner.Block{
|
||||
{Offset: 0, Size: 128, Hash: []byte("some bar hash bytes")},
|
||||
{Offset: 128, Size: 128, Hash: []byte("some other bar hash bytes")},
|
||||
}, nil)
|
||||
|
||||
// First get should return the first block of the first file
|
||||
|
||||
expected := queuedBlock{
|
||||
name: "bar",
|
||||
block: scanner.Block{
|
||||
Offset: 0,
|
||||
Size: 128,
|
||||
Hash: []byte("some bar hash bytes"),
|
||||
},
|
||||
}
|
||||
actual, ok := q.Get("nodeID")
|
||||
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned (first)\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
|
||||
// Second get should return the next block of the first file
|
||||
|
||||
expected = queuedBlock{
|
||||
name: "bar",
|
||||
block: scanner.Block{
|
||||
Offset: 128,
|
||||
Size: 128,
|
||||
Hash: []byte("some other bar hash bytes"),
|
||||
},
|
||||
index: 1,
|
||||
}
|
||||
actual, ok = q.Get("nodeID")
|
||||
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned (second)\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
|
||||
// Third get should return the first block of the second file
|
||||
|
||||
expected = queuedBlock{
|
||||
name: "foo",
|
||||
block: scanner.Block{
|
||||
Offset: 0,
|
||||
Size: 128,
|
||||
Hash: []byte("some foo hash bytes"),
|
||||
},
|
||||
}
|
||||
actual, ok = q.Get("nodeID")
|
||||
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned (third)\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestFileQueueDone(t *testing.T) {
|
||||
ch := make(chan content)
|
||||
var recv sync.WaitGroup
|
||||
recv.Add(1)
|
||||
go func() {
|
||||
content := <-ch
|
||||
if bytes.Compare(content.data, []byte("first block bytes")) != 0 {
|
||||
t.Error("Incorrect data in first content block")
|
||||
}
|
||||
|
||||
content = <-ch
|
||||
if bytes.Compare(content.data, []byte("second block bytes")) != 0 {
|
||||
t.Error("Incorrect data in second content block")
|
||||
}
|
||||
|
||||
_, ok := <-ch
|
||||
if ok {
|
||||
t.Error("Content channel not closed")
|
||||
}
|
||||
|
||||
recv.Done()
|
||||
}()
|
||||
|
||||
q := FileQueue{resolver: fakeResolver{}}
|
||||
q.Add("foo", []scanner.Block{
|
||||
{Offset: 0, Length: 128, Hash: []byte("some foo hash bytes")},
|
||||
{Offset: 128, Length: 128, Hash: []byte("some other foo hash bytes")},
|
||||
}, ch)
|
||||
|
||||
b0, _ := q.Get("nodeID")
|
||||
b1, _ := q.Get("nodeID")
|
||||
|
||||
q.Done(b0.name, b0.block.Offset, []byte("first block bytes"))
|
||||
q.Done(b1.name, b1.block.Offset, []byte("second block bytes"))
|
||||
|
||||
recv.Wait()
|
||||
|
||||
// Queue should now have one file less
|
||||
|
||||
if l := q.Len(); l != 0 {
|
||||
t.Error("Queue not empty")
|
||||
}
|
||||
|
||||
_, ok := q.Get("nodeID")
|
||||
if ok {
|
||||
t.Error("Unexpected OK Get()")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestFileQueueGetNodeIDs(t *testing.T) {
|
||||
q := NewFileQueue()
|
||||
q.SetAvailable("a-foo", []string{"nodeID", "a"})
|
||||
q.SetAvailable("b-bar", []string{"nodeID", "b"})
|
||||
|
||||
q.Add("a-foo", []scanner.Block{
|
||||
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
||||
{Offset: 128, Size: 128, Hash: []byte("some other foo hash bytes")},
|
||||
{Offset: 256, Size: 128, Hash: []byte("more foo hash bytes")},
|
||||
}, nil)
|
||||
q.Add("b-bar", []scanner.Block{
|
||||
{Offset: 0, Size: 128, Hash: []byte("some bar hash bytes")},
|
||||
{Offset: 128, Size: 128, Hash: []byte("some other bar hash bytes")},
|
||||
}, nil)
|
||||
|
||||
expected := queuedBlock{
|
||||
name: "b-bar",
|
||||
block: scanner.Block{
|
||||
Offset: 0,
|
||||
Size: 128,
|
||||
Hash: []byte("some bar hash bytes"),
|
||||
},
|
||||
}
|
||||
actual, ok := q.Get("b")
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
|
||||
expected = queuedBlock{
|
||||
name: "a-foo",
|
||||
block: scanner.Block{
|
||||
Offset: 0,
|
||||
Size: 128,
|
||||
Hash: []byte("some foo hash bytes"),
|
||||
},
|
||||
}
|
||||
actual, ok = q.Get("a")
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
|
||||
expected = queuedBlock{
|
||||
name: "a-foo",
|
||||
block: scanner.Block{
|
||||
Offset: 128,
|
||||
Size: 128,
|
||||
Hash: []byte("some other foo hash bytes"),
|
||||
},
|
||||
index: 1,
|
||||
}
|
||||
actual, ok = q.Get("nodeID")
|
||||
if !ok {
|
||||
t.Error("Unexpected non-OK Get()")
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Incorrect block returned\n E: %+v\n A: %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileQueueThreadHandling(t *testing.T) {
|
||||
// This should pass with go test -race
|
||||
|
||||
const n = 100
|
||||
var total int
|
||||
var blocks []scanner.Block
|
||||
for i := 1; i <= n; i++ {
|
||||
blocks = append(blocks, scanner.Block{Offset: int64(i), Size: 1})
|
||||
total += i
|
||||
}
|
||||
|
||||
q := NewFileQueue()
|
||||
q.Add("foo", blocks, nil)
|
||||
q.SetAvailable("foo", []string{"nodeID"})
|
||||
|
||||
var start = make(chan bool)
|
||||
var gotTot uint32
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(n)
|
||||
for i := 1; i <= n; i++ {
|
||||
go func() {
|
||||
<-start
|
||||
b, _ := q.Get("nodeID")
|
||||
atomic.AddUint32(&gotTot, uint32(b.block.Offset))
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
close(start)
|
||||
wg.Wait()
|
||||
if int(gotTot) != total {
|
||||
t.Errorf("Total mismatch; %d != %d", gotTot, total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAt(t *testing.T) {
|
||||
q := FileQueue{}
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
q.files = queuedFileList{{name: "a"}, {name: "b"}, {name: "c"}, {name: "d"}}
|
||||
q.deleteAt(i)
|
||||
if l := len(q.files); l != 3 {
|
||||
t.Fatalf("deleteAt(%d) failed; %d != 3", i, l)
|
||||
}
|
||||
}
|
||||
|
||||
q.files = queuedFileList{{name: "a"}}
|
||||
q.deleteAt(0)
|
||||
if l := len(q.files); l != 0 {
|
||||
t.Fatalf("deleteAt(only) failed; %d != 0", l)
|
||||
}
|
||||
}
|
||||
@@ -38,12 +38,14 @@ func startGUI(addr string, m *Model) {
|
||||
|
||||
router.Post("/rest/config", restPostConfig)
|
||||
router.Post("/rest/restart", restPostRestart)
|
||||
router.Post("/rest/reset", restPostReset)
|
||||
router.Post("/rest/error", restPostError)
|
||||
|
||||
go func() {
|
||||
mr := martini.New()
|
||||
mr.Use(embeddedStatic())
|
||||
mr.Use(martini.Recovery())
|
||||
mr.Use(restMiddleware)
|
||||
mr.Action(router.Handle)
|
||||
mr.Map(m)
|
||||
err := http.ListenAndServe(addr, mr)
|
||||
@@ -57,6 +59,12 @@ func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/index.html", 302)
|
||||
}
|
||||
|
||||
func restMiddleware(w http.ResponseWriter, r *http.Request) {
|
||||
if len(r.URL.Path) >= 6 && r.URL.Path[:6] == "/rest/" {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
}
|
||||
}
|
||||
|
||||
func restGetVersion() string {
|
||||
return Version
|
||||
}
|
||||
@@ -105,19 +113,28 @@ func restGetConfigInSync(w http.ResponseWriter) {
|
||||
}
|
||||
|
||||
func restPostRestart(req *http.Request) {
|
||||
restart()
|
||||
go restart()
|
||||
}
|
||||
|
||||
func restPostReset(req *http.Request) {
|
||||
resetRepositories()
|
||||
go restart()
|
||||
}
|
||||
|
||||
type guiFile scanner.File
|
||||
|
||||
func (f guiFile) MarshalJSON() ([]byte, error) {
|
||||
type t struct {
|
||||
Name string
|
||||
Size int64
|
||||
Name string
|
||||
Size int64
|
||||
Modified int64
|
||||
Flags uint32
|
||||
}
|
||||
return json.Marshal(t{
|
||||
Name: f.Name,
|
||||
Size: scanner.File(f).Size,
|
||||
Name: f.Name,
|
||||
Size: scanner.File(f).Size,
|
||||
Modified: f.Modified,
|
||||
Flags: f.Flags,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
19
cmd/syncthing/limitedwriter.go
Normal file
19
cmd/syncthing/limitedwriter.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type limitedWriter struct {
|
||||
w io.Writer
|
||||
bucket *ratelimit.Bucket
|
||||
}
|
||||
|
||||
func (w *limitedWriter) Write(buf []byte) (int, error) {
|
||||
if w.bucket != nil {
|
||||
w.bucket.Wait(int64(len(buf)))
|
||||
}
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/calmh/ini"
|
||||
"github.com/calmh/syncthing/discover"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
const BlockSize = 128 * 1024
|
||||
@@ -29,13 +29,9 @@ var cfg Configuration
|
||||
var Version = "unknown-dev"
|
||||
|
||||
var (
|
||||
myID string
|
||||
)
|
||||
|
||||
var (
|
||||
showVersion bool
|
||||
confDir string
|
||||
verbose bool
|
||||
myID string
|
||||
confDir string
|
||||
rateBucket *ratelimit.Bucket
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -51,18 +47,23 @@ const (
|
||||
|
||||
STTRACE A comma separated string of facilities to trace. The valid
|
||||
facility strings:
|
||||
- "scanner" (the file change scanner)
|
||||
- "discover" (the node discovery package)
|
||||
- "net" (connecting and disconnecting, network messages)
|
||||
- "files" (file set store)
|
||||
- "idx" (index sending and receiving)
|
||||
- "mc" (multicast beacon)
|
||||
- "need" (file need calculations)
|
||||
- "pull" (file pull activity)`
|
||||
- "net" (connecting and disconnecting, network messages)
|
||||
- "pull" (file pull activity)
|
||||
- "scanner" (the file change scanner)
|
||||
`
|
||||
)
|
||||
|
||||
func main() {
|
||||
var reset bool
|
||||
var showVersion bool
|
||||
flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
|
||||
flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster")
|
||||
flag.BoolVar(&showVersion, "version", false, "Show version")
|
||||
flag.BoolVar(&verbose, "v", false, "Be more verbose")
|
||||
flag.Usage = usageFor(flag.CommandLine, usage, extraUsage)
|
||||
flag.Parse()
|
||||
|
||||
@@ -72,7 +73,7 @@ func main() {
|
||||
}
|
||||
|
||||
if showVersion {
|
||||
fmt.Println(Version)
|
||||
fmt.Printf("syncthing %s (%s %s-%s)\n", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ func main() {
|
||||
|
||||
// Prepare to be able to save configuration
|
||||
|
||||
cfgFile := path.Join(confDir, "config.xml")
|
||||
cfgFile := filepath.Join(confDir, "config.xml")
|
||||
go saveConfigLoop(cfgFile)
|
||||
|
||||
// Load the configuration file, if it exists.
|
||||
@@ -121,13 +122,13 @@ func main() {
|
||||
cf.Close()
|
||||
} else {
|
||||
// No config.xml, let's try the old syncthing.ini
|
||||
iniFile := path.Join(confDir, "syncthing.ini")
|
||||
iniFile := filepath.Join(confDir, "syncthing.ini")
|
||||
cf, err := os.Open(iniFile)
|
||||
if err == nil {
|
||||
infoln("Migrating syncthing.ini to config.xml")
|
||||
iniCfg := ini.Parse(cf)
|
||||
cf.Close()
|
||||
os.Rename(iniFile, path.Join(confDir, "migrated_syncthing.ini"))
|
||||
Rename(iniFile, filepath.Join(confDir, "migrated_syncthing.ini"))
|
||||
|
||||
cfg, _ = readConfigXML(nil)
|
||||
cfg.Repositories = []RepositoryConfiguration{
|
||||
@@ -152,7 +153,8 @@ func main() {
|
||||
cfg, err = readConfigXML(nil)
|
||||
cfg.Repositories = []RepositoryConfiguration{
|
||||
{
|
||||
Directory: path.Join(getHomeDir(), "Sync"),
|
||||
ID: "default",
|
||||
Directory: filepath.Join(getHomeDir(), "Sync"),
|
||||
Nodes: []NodeConfiguration{
|
||||
{NodeID: myID, Addresses: []string{"dynamic"}},
|
||||
},
|
||||
@@ -163,10 +165,10 @@ func main() {
|
||||
infof("Edit %s to taste or use the GUI\n", cfgFile)
|
||||
}
|
||||
|
||||
// Make sure the local node is in the node list.
|
||||
cfg.Repositories[0].Nodes = cleanNodeList(cfg.Repositories[0].Nodes, myID)
|
||||
|
||||
var dir = expandTilde(cfg.Repositories[0].Directory)
|
||||
if reset {
|
||||
resetRepositories()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if profiler := os.Getenv("STPROFILER"); len(profiler) > 0 {
|
||||
go func() {
|
||||
@@ -191,10 +193,20 @@ func main() {
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
ensureDir(dir, -1)
|
||||
m := NewModel(dir, cfg.Options.MaxChangeKbps*1000)
|
||||
// If the write rate should be limited, set up a rate limiter for it.
|
||||
// This will be used on connections created in the connect and listen routines.
|
||||
|
||||
if cfg.Options.MaxSendKbps > 0 {
|
||||
m.LimitRate(cfg.Options.MaxSendKbps)
|
||||
rateBucket = ratelimit.NewBucketWithRate(float64(1000*cfg.Options.MaxSendKbps), int64(5*1000*cfg.Options.MaxSendKbps))
|
||||
}
|
||||
|
||||
m := NewModel(cfg.Options.MaxChangeKbps * 1000)
|
||||
|
||||
for i := range cfg.Repositories {
|
||||
cfg.Repositories[i].Nodes = cleanNodeList(cfg.Repositories[i].Nodes, myID)
|
||||
dir := expandTilde(cfg.Repositories[i].Directory)
|
||||
ensureDir(dir, -1)
|
||||
m.AddRepo(cfg.Repositories[i].ID, dir, cfg.Repositories[i].Nodes)
|
||||
}
|
||||
|
||||
// GUI
|
||||
@@ -227,22 +239,10 @@ func main() {
|
||||
// Walk the repository and update the local model before establishing any
|
||||
// connections to other nodes.
|
||||
|
||||
if verbose {
|
||||
infoln("Populating repository index")
|
||||
}
|
||||
loadIndex(m)
|
||||
|
||||
sup := &suppressor{threshold: int64(cfg.Options.MaxChangeKbps)}
|
||||
w := &scanner.Walker{
|
||||
Dir: m.dir,
|
||||
IgnoreFile: ".stignore",
|
||||
FollowSymlinks: cfg.Options.FollowSymlinks,
|
||||
BlockSize: BlockSize,
|
||||
TempNamer: defTempNamer,
|
||||
Suppressor: sup,
|
||||
CurrentFiler: m,
|
||||
}
|
||||
updateLocalModel(m, w)
|
||||
infoln("Populating repository index")
|
||||
m.LoadIndexes(confDir)
|
||||
m.ScanRepos()
|
||||
m.SaveIndexes(confDir)
|
||||
|
||||
connOpts := map[string]string{
|
||||
"clientId": "syncthing",
|
||||
@@ -251,48 +251,41 @@ func main() {
|
||||
}
|
||||
|
||||
// Routine to connect out to configured nodes
|
||||
if verbose {
|
||||
infoln("Attempting to connect to other nodes")
|
||||
}
|
||||
disc := discovery()
|
||||
go listenConnect(myID, disc, m, tlsCfg, connOpts)
|
||||
|
||||
// Routine to pull blocks from other nodes to synchronize the local
|
||||
// repository. Does not run when we are in read only (publish only) mode.
|
||||
if !cfg.Options.ReadOnly {
|
||||
if verbose {
|
||||
if cfg.Options.AllowDelete {
|
||||
infoln("Deletes from peer nodes are allowed")
|
||||
} else {
|
||||
infoln("Deletes from peer nodes will be ignored")
|
||||
}
|
||||
okln("Ready to synchronize (read-write)")
|
||||
}
|
||||
m.StartRW(cfg.Options.AllowDelete, cfg.Options.ParallelRequests)
|
||||
} else if verbose {
|
||||
if cfg.Options.ReadOnly {
|
||||
okln("Ready to synchronize (read only; no external updates accepted)")
|
||||
}
|
||||
|
||||
// Periodically scan the repository and update the local
|
||||
// XXX: Should use some fsnotify mechanism.
|
||||
go func() {
|
||||
td := time.Duration(cfg.Options.RescanIntervalS) * time.Second
|
||||
for {
|
||||
time.Sleep(td)
|
||||
if m.LocalAge() > (td / 2).Seconds() {
|
||||
updateLocalModel(m, w)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if verbose {
|
||||
// Periodically print statistics
|
||||
go printStatsLoop(m)
|
||||
m.StartRO()
|
||||
} else {
|
||||
okln("Ready to synchronize (read-write)")
|
||||
m.StartRW(cfg.Options.ParallelRequests)
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
func resetRepositories() {
|
||||
suffix := fmt.Sprintf(".syncthing-reset-%d", time.Now().UnixNano())
|
||||
for _, repo := range cfg.Repositories {
|
||||
if _, err := os.Stat(repo.Directory); err == nil {
|
||||
infof("Reset: Moving %s -> %s", repo.Directory, repo.Directory+suffix)
|
||||
os.Rename(repo.Directory, repo.Directory+suffix)
|
||||
}
|
||||
}
|
||||
|
||||
pat := filepath.Join(confDir, "*.idx.gz")
|
||||
idxs, err := filepath.Glob(pat)
|
||||
if err == nil {
|
||||
for _, idx := range idxs {
|
||||
infof("Reset: Removing %s", idx)
|
||||
os.Remove(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restart() {
|
||||
infoln("Restarting")
|
||||
if os.Getenv("SMF_FMRI") != "" || os.Getenv("STNORESTART") != "" {
|
||||
@@ -344,14 +337,7 @@ func saveConfigLoop(cfgFile string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
err := os.Remove(cfgFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
warnln(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Rename(cfgFile+".tmp", cfgFile)
|
||||
err = Rename(cfgFile+".tmp", cfgFile)
|
||||
if err != nil {
|
||||
warnln(err)
|
||||
}
|
||||
@@ -362,37 +348,6 @@ func saveConfig() {
|
||||
saveConfigCh <- struct{}{}
|
||||
}
|
||||
|
||||
func printStatsLoop(m *Model) {
|
||||
var lastUpdated int64
|
||||
var lastStats = make(map[string]ConnectionInfo)
|
||||
|
||||
for {
|
||||
time.Sleep(60 * time.Second)
|
||||
|
||||
for node, stats := range m.ConnectionStats() {
|
||||
secs := time.Since(lastStats[node].At).Seconds()
|
||||
inbps := 8 * int(float64(stats.InBytesTotal-lastStats[node].InBytesTotal)/secs)
|
||||
outbps := 8 * int(float64(stats.OutBytesTotal-lastStats[node].OutBytesTotal)/secs)
|
||||
|
||||
if inbps+outbps > 0 {
|
||||
infof("%s: %sb/s in, %sb/s out", node[0:5], MetricPrefix(int64(inbps)), MetricPrefix(int64(outbps)))
|
||||
}
|
||||
|
||||
lastStats[node] = stats
|
||||
}
|
||||
|
||||
if lu := m.Generation(); lu > lastUpdated {
|
||||
lastUpdated = lu
|
||||
files, _, bytes := m.GlobalSize()
|
||||
infof("%6d files, %9sB in cluster", files, BinaryPrefix(bytes))
|
||||
files, _, bytes = m.LocalSize()
|
||||
infof("%6d files, %9sB in local repo", files, BinaryPrefix(bytes))
|
||||
needFiles, bytes := m.NeedFiles()
|
||||
infof("%6d files, %9sB to synchronize", len(needFiles), BinaryPrefix(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listenConnect(myID string, disc *discover.Discoverer, m *Model, tlsCfg *tls.Config, connOpts map[string]string) {
|
||||
var conns = make(chan *tls.Conn)
|
||||
|
||||
@@ -490,7 +445,11 @@ next:
|
||||
|
||||
for _, nodeCfg := range cfg.Repositories[0].Nodes {
|
||||
if nodeCfg.NodeID == remoteID {
|
||||
protoConn := protocol.NewConnection(remoteID, conn, conn, m, connOpts)
|
||||
var wr io.Writer = conn
|
||||
if rateBucket != nil {
|
||||
wr = &limitedWriter{conn, rateBucket}
|
||||
}
|
||||
protoConn := protocol.NewConnection(remoteID, conn, wr, m, connOpts)
|
||||
m.AddConnection(conn, protoConn)
|
||||
continue next
|
||||
}
|
||||
@@ -508,7 +467,7 @@ func discovery() *discover.Discoverer {
|
||||
|
||||
if !cfg.Options.GlobalAnnEnabled {
|
||||
cfg.Options.GlobalAnnServer = ""
|
||||
} else if verbose {
|
||||
} else {
|
||||
infoln("Sending external discovery announcements")
|
||||
}
|
||||
|
||||
@@ -521,53 +480,6 @@ func discovery() *discover.Discoverer {
|
||||
return disc
|
||||
}
|
||||
|
||||
func updateLocalModel(m *Model, w *scanner.Walker) {
|
||||
files, _ := w.Walk()
|
||||
m.ReplaceLocal(files)
|
||||
saveIndex(m)
|
||||
}
|
||||
|
||||
func saveIndex(m *Model) {
|
||||
name := m.RepoID() + ".idx.gz"
|
||||
fullName := path.Join(confDir, name)
|
||||
idxf, err := os.Create(fullName + ".tmp")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
gzw := gzip.NewWriter(idxf)
|
||||
|
||||
protocol.IndexMessage{
|
||||
Repository: "local",
|
||||
Files: m.ProtocolIndex(),
|
||||
}.EncodeXDR(gzw)
|
||||
gzw.Close()
|
||||
idxf.Close()
|
||||
os.Rename(fullName+".tmp", fullName)
|
||||
}
|
||||
|
||||
func loadIndex(m *Model) {
|
||||
name := m.RepoID() + ".idx.gz"
|
||||
idxf, err := os.Open(path.Join(confDir, name))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer idxf.Close()
|
||||
|
||||
gzr, err := gzip.NewReader(idxf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer gzr.Close()
|
||||
|
||||
var im protocol.IndexMessage
|
||||
err = im.DecodeXDR(gzr)
|
||||
if err != nil || im.Repository != "local" {
|
||||
return
|
||||
}
|
||||
m.SeedLocal(im.Files)
|
||||
}
|
||||
|
||||
func ensureDir(dir string, mode int) {
|
||||
fi, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
@@ -611,7 +523,7 @@ func getHomeDir() string {
|
||||
|
||||
func getDefaultConfDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return path.Join(os.Getenv("AppData"), "syncthing")
|
||||
return filepath.Join(os.Getenv("AppData"), "syncthing")
|
||||
}
|
||||
return expandTilde("~/.syncthing")
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,30 +4,14 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/cid"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
func TestNewModel(t *testing.T) {
|
||||
m := NewModel("foo", 1e6)
|
||||
|
||||
if m == nil {
|
||||
t.Fatalf("NewModel returned nil")
|
||||
}
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) > 0 {
|
||||
t.Errorf("New model should have no Need")
|
||||
}
|
||||
|
||||
if len(m.local) > 0 {
|
||||
t.Errorf("New model should have no Have")
|
||||
}
|
||||
}
|
||||
|
||||
var testDataExpected = map[string]scanner.File{
|
||||
"foo": scanner.File{
|
||||
Name: "foo",
|
||||
@@ -62,300 +46,10 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLocal(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) > 0 {
|
||||
t.Fatalf("Model with only local data should have no need")
|
||||
}
|
||||
|
||||
if l1, l2 := len(m.local), len(testDataExpected); l1 != l2 {
|
||||
t.Fatalf("Model len(local) incorrect, %d != %d", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(testDataExpected); l1 != l2 {
|
||||
t.Fatalf("Model len(global) incorrect, %d != %d", l1, l2)
|
||||
}
|
||||
for name, file := range testDataExpected {
|
||||
if f, ok := m.local[name]; ok {
|
||||
if !reflect.DeepEqual(f, file) {
|
||||
t.Errorf("Incorrect local\n%v !=\n%v\nfor file %q", f, file, name)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Missing file %q in local table", name)
|
||||
}
|
||||
if f, ok := m.global[name]; ok {
|
||||
if !reflect.DeepEqual(f, file) {
|
||||
t.Errorf("Incorrect global\n%v !=\n%v\nfor file %q", f, file, name)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Missing file %q in global table", name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
if hf, ok := m.local[f.Name]; !ok || hf.Modified != f.Modified {
|
||||
t.Fatalf("Incorrect local for %q", f.Name)
|
||||
}
|
||||
if cf, ok := m.global[f.Name]; !ok || cf.Modified != f.Modified {
|
||||
t.Fatalf("Incorrect global for %q", f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteUpdateExisting(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
newFile := protocol.FileInfo{
|
||||
Name: "foo",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.Index("42", []protocol.FileInfo{newFile})
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 1 {
|
||||
t.Errorf("Model missing Need for one file (%d != 1)", len(fs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteAddNew(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
newFile := protocol.FileInfo{
|
||||
Name: "a new file",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.Index("42", []protocol.FileInfo{newFile})
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 1 {
|
||||
t.Errorf("Model len(m.need) incorrect (%d != 1)", len(fs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteUpdateOld(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
oldTimeStamp := int64(1234)
|
||||
newFile := protocol.FileInfo{
|
||||
Name: "foo",
|
||||
Modified: oldTimeStamp,
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.Index("42", []protocol.FileInfo{newFile})
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 0 {
|
||||
t.Errorf("Model len(need) incorrect (%d != 0)", len(fs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteIndexUpdate(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
foo := protocol.FileInfo{
|
||||
Name: "foo",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
|
||||
bar := protocol.FileInfo{
|
||||
Name: "bar",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
|
||||
m.Index("42", []protocol.FileInfo{foo})
|
||||
|
||||
if fs, _ := m.NeedFiles(); fs[0].Name != "foo" {
|
||||
t.Error("Model doesn't need 'foo'")
|
||||
}
|
||||
|
||||
m.IndexUpdate("42", []protocol.FileInfo{bar})
|
||||
|
||||
if fs, _ := m.NeedFiles(); fs[0].Name != "foo" {
|
||||
t.Error("Model doesn't need 'foo'")
|
||||
}
|
||||
if fs, _ := m.NeedFiles(); fs[1].Name != "bar" {
|
||||
t.Error("Model doesn't need 'bar'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
|
||||
ot := time.Now().Unix()
|
||||
newFile := scanner.File{
|
||||
Name: "a new file",
|
||||
Modified: ot,
|
||||
Blocks: []scanner.Block{{0, 100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.updateLocal(newFile)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
|
||||
// The deleted file is kept in the local and global tables and marked as deleted.
|
||||
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
|
||||
if m.local["a new file"].Flags&(1<<12) == 0 {
|
||||
t.Error("Unexpected deleted flag = 0 in local table")
|
||||
}
|
||||
if len(m.local["a new file"].Blocks) != 0 {
|
||||
t.Error("Unexpected non-zero blocks for deleted file in local")
|
||||
}
|
||||
if ft := m.local["a new file"].Modified; ft != ot {
|
||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
||||
}
|
||||
if fv := m.local["a new file"].Version; fv != 1 {
|
||||
t.Errorf("Unexpected version %d != 1 for deleted file in local", fv)
|
||||
}
|
||||
|
||||
if m.global["a new file"].Flags&(1<<12) == 0 {
|
||||
t.Error("Unexpected deleted flag = 0 in global table")
|
||||
}
|
||||
if len(m.global["a new file"].Blocks) != 0 {
|
||||
t.Error("Unexpected non-zero blocks for deleted file in global")
|
||||
}
|
||||
if ft := m.global["a new file"].Modified; ft != ot {
|
||||
t.Errorf("Unexpected time %d != %d for deleted file in global", ft, ot+1)
|
||||
}
|
||||
if fv := m.local["a new file"].Version; fv != 1 {
|
||||
t.Errorf("Unexpected version %d != 1 for deleted file in global", fv)
|
||||
}
|
||||
|
||||
// Another update should change nothing
|
||||
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
|
||||
if m.local["a new file"].Flags&(1<<12) == 0 {
|
||||
t.Error("Unexpected deleted flag = 0 in local table")
|
||||
}
|
||||
if len(m.local["a new file"].Blocks) != 0 {
|
||||
t.Error("Unexpected non-zero blocks for deleted file in local")
|
||||
}
|
||||
if ft := m.local["a new file"].Modified; ft != ot {
|
||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot)
|
||||
}
|
||||
if fv := m.local["a new file"].Version; fv != 1 {
|
||||
t.Errorf("Unexpected version %d != 1 for deleted file in local", fv)
|
||||
}
|
||||
|
||||
if m.global["a new file"].Flags&(1<<12) == 0 {
|
||||
t.Error("Unexpected deleted flag = 0 in global table")
|
||||
}
|
||||
if len(m.global["a new file"].Blocks) != 0 {
|
||||
t.Error("Unexpected non-zero blocks for deleted file in global")
|
||||
}
|
||||
if ft := m.global["a new file"].Modified; ft != ot {
|
||||
t.Errorf("Unexpected time %d != %d for deleted file in global", ft, ot)
|
||||
}
|
||||
if fv := m.local["a new file"].Version; fv != 1 {
|
||||
t.Errorf("Unexpected version %d != 1 for deleted file in global", fv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgetNode(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 0 {
|
||||
t.Errorf("Model len(need) incorrect (%d != 0)", len(fs))
|
||||
}
|
||||
|
||||
newFile := protocol.FileInfo{
|
||||
Name: "new file",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.Index("42", []protocol.FileInfo{newFile})
|
||||
|
||||
newFile = protocol.FileInfo{
|
||||
Name: "new file 2",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
}
|
||||
m.Index("43", []protocol.FileInfo{newFile})
|
||||
|
||||
if l1, l2 := len(m.local), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs)+2; l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 2 {
|
||||
t.Errorf("Model len(need) incorrect (%d != 2)", len(fs))
|
||||
}
|
||||
|
||||
m.Close("42", nil)
|
||||
|
||||
if l1, l2 := len(m.local), len(fs); l1 != l2 {
|
||||
t.Errorf("Model len(local) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
if l1, l2 := len(m.global), len(fs)+1; l1 != l2 {
|
||||
t.Errorf("Model len(global) incorrect (%d != %d)", l1, l2)
|
||||
}
|
||||
|
||||
if fs, _ := m.NeedFiles(); len(fs) != 1 {
|
||||
t.Errorf("Model len(need) incorrect (%d != 1)", len(fs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequest(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
|
||||
bs, err := m.Request("some node", "default", "foo", 0, 6)
|
||||
if err != nil {
|
||||
@@ -374,36 +68,6 @@ func TestRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreWithUnknownFlags(t *testing.T) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
|
||||
valid := protocol.FileInfo{
|
||||
Name: "valid",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
Flags: protocol.FlagDeleted | 0755,
|
||||
}
|
||||
|
||||
invalid := protocol.FileInfo{
|
||||
Name: "invalid",
|
||||
Modified: time.Now().Unix(),
|
||||
Blocks: []protocol.BlockInfo{{100, []byte("some hash bytes")}},
|
||||
Flags: 1<<27 | protocol.FlagDeleted | 0755,
|
||||
}
|
||||
|
||||
m.Index("42", []protocol.FileInfo{valid, invalid})
|
||||
|
||||
if _, ok := m.global[valid.Name]; !ok {
|
||||
t.Error("Model should include", valid)
|
||||
}
|
||||
if _, ok := m.global[invalid.Name]; ok {
|
||||
t.Error("Model not should include", invalid)
|
||||
}
|
||||
}
|
||||
|
||||
func genFiles(n int) []protocol.FileInfo {
|
||||
files := make([]protocol.FileInfo, n)
|
||||
t := time.Now().Unix()
|
||||
@@ -419,72 +83,67 @@ func genFiles(n int) []protocol.FileInfo {
|
||||
}
|
||||
|
||||
func BenchmarkIndex10000(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndex00100(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(100)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexUpdate10000f10000(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", files)
|
||||
m.IndexUpdate("42", "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexUpdate10000f00100(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
|
||||
ufiles := genFiles(100)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", ufiles)
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexUpdate10000f00001(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
|
||||
ufiles := genFiles(1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", ufiles)
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,10 +179,9 @@ func (FakeConnection) Statistics() protocol.Statistics {
|
||||
}
|
||||
|
||||
func BenchmarkRequest(b *testing.B) {
|
||||
m := NewModel("testdata", 1e6)
|
||||
w := scanner.Walker{Dir: "testdata", IgnoreFile: ".stignore", BlockSize: 128 * 1024}
|
||||
fs, _ := w.Walk()
|
||||
m.ReplaceLocal(fs)
|
||||
m := NewModel(1e6)
|
||||
m.AddRepo("default", "testdata", nil)
|
||||
m.ScanRepo("default")
|
||||
|
||||
const n = 1000
|
||||
files := make([]protocol.FileInfo, n)
|
||||
@@ -541,11 +199,11 @@ func BenchmarkRequest(b *testing.B) {
|
||||
requestData: []byte("some data to return"),
|
||||
}
|
||||
m.AddConnection(fc, fc)
|
||||
m.Index("42", files)
|
||||
m.Index("42", "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
data, err := m.requestGlobal("42", files[i%n].Name, 0, 32, nil)
|
||||
data, err := m.requestGlobal("42", "default", files[i%n].Name, 0, 32, nil)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
@@ -554,3 +212,29 @@ func BenchmarkRequest(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActivityMap(t *testing.T) {
|
||||
cm := cid.NewMap()
|
||||
fooID := cm.Get("foo")
|
||||
if fooID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
barID := cm.Get("bar")
|
||||
if barID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
|
||||
m := make(activityMap)
|
||||
if node := m.leastBusyNode(1<<fooID, cm); node != "foo" {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<barID, cm); node != "bar" {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "foo" {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "bar" {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//+build !darwin
|
||||
|
||||
package main
|
||||
|
||||
import "code.google.com/p/go.text/unicode/norm"
|
||||
|
||||
// FSNormalize returns the string with the required unicode normalization for
|
||||
// the host operating system.
|
||||
func FSNormalize(s string) string {
|
||||
return norm.NFC.String(s)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
//+build darwin
|
||||
|
||||
package main
|
||||
|
||||
import "code.google.com/p/go.text/unicode/norm"
|
||||
|
||||
// FSNormalize returns the string with the required unicode normalization for
|
||||
// the host operating system.
|
||||
func FSNormalize(s string) string {
|
||||
return norm.NFD.String(s)
|
||||
}
|
||||
536
cmd/syncthing/puller.go
Normal file
536
cmd/syncthing/puller.go
Normal file
@@ -0,0 +1,536 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/buffers"
|
||||
"github.com/calmh/syncthing/cid"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
type requestResult struct {
|
||||
node string
|
||||
file scanner.File
|
||||
filepath string // full filepath name
|
||||
offset int64
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
type openFile struct {
|
||||
filepath string // full filepath name
|
||||
temp string // temporary filename
|
||||
availability uint64 // availability bitset
|
||||
file *os.File
|
||||
err error // error when opening or writing to file, all following operations are cancelled
|
||||
outstanding int // number of requests we still have outstanding
|
||||
done bool // we have sent all requests for this file
|
||||
}
|
||||
|
||||
type activityMap map[string]int
|
||||
|
||||
func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) string {
|
||||
var low int = 2<<30 - 1
|
||||
var selected string
|
||||
for _, node := range cm.Names() {
|
||||
id := cm.Get(node)
|
||||
if id == cid.LocalID {
|
||||
continue
|
||||
}
|
||||
usage := m[node]
|
||||
if availability&(1<<id) != 0 {
|
||||
if usage < low {
|
||||
low = usage
|
||||
selected = node
|
||||
}
|
||||
}
|
||||
}
|
||||
m[selected]++
|
||||
return selected
|
||||
}
|
||||
|
||||
func (m activityMap) decrease(node string) {
|
||||
m[node]--
|
||||
}
|
||||
|
||||
var errNoNode = errors.New("no available source node")
|
||||
|
||||
type puller struct {
|
||||
repo string
|
||||
dir string
|
||||
bq *blockQueue
|
||||
model *Model
|
||||
oustandingPerNode activityMap
|
||||
openFiles map[string]openFile
|
||||
requestSlots chan bool
|
||||
blocks chan bqBlock
|
||||
requestResults chan requestResult
|
||||
}
|
||||
|
||||
func newPuller(repo, dir string, model *Model, slots int) *puller {
|
||||
p := &puller{
|
||||
repo: repo,
|
||||
dir: dir,
|
||||
bq: newBlockQueue(),
|
||||
model: model,
|
||||
oustandingPerNode: make(activityMap),
|
||||
openFiles: make(map[string]openFile),
|
||||
requestSlots: make(chan bool, slots),
|
||||
blocks: make(chan bqBlock),
|
||||
requestResults: make(chan requestResult),
|
||||
}
|
||||
|
||||
if slots > 0 {
|
||||
// Read/write
|
||||
for i := 0; i < slots; i++ {
|
||||
p.requestSlots <- true
|
||||
}
|
||||
if debugPull {
|
||||
dlog.Printf("starting puller; repo %q dir %q slots %d", repo, dir, slots)
|
||||
}
|
||||
go p.run()
|
||||
} else {
|
||||
// Read only
|
||||
if debugPull {
|
||||
dlog.Printf("starting puller; repo %q dir %q (read only)", repo, dir)
|
||||
}
|
||||
go p.runRO()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *puller) run() {
|
||||
go func() {
|
||||
// fill blocks queue when there are free slots
|
||||
for {
|
||||
<-p.requestSlots
|
||||
b := p.bq.get()
|
||||
if debugPull {
|
||||
dlog.Printf("filler: queueing %q / %q offset %d copy %d", p.repo, b.file.Name, b.block.Offset, len(b.copy))
|
||||
}
|
||||
p.blocks <- b
|
||||
}
|
||||
}()
|
||||
|
||||
walkTicker := time.Tick(time.Duration(cfg.Options.RescanIntervalS) * time.Second)
|
||||
timeout := time.Tick(5 * time.Second)
|
||||
changed := true
|
||||
|
||||
for {
|
||||
// Run the pulling loop as long as there are blocks to fetch
|
||||
pull:
|
||||
for {
|
||||
select {
|
||||
case res := <-p.requestResults:
|
||||
changed = true
|
||||
p.requestSlots <- true
|
||||
p.handleRequestResult(res)
|
||||
|
||||
case b := <-p.blocks:
|
||||
changed = true
|
||||
p.handleBlock(b)
|
||||
|
||||
case <-timeout:
|
||||
if len(p.openFiles) == 0 && p.bq.empty() {
|
||||
// Nothing more to do for the moment
|
||||
break pull
|
||||
}
|
||||
if debugPull {
|
||||
dlog.Printf("%q: idle but have %d open files", p.repo, len(p.openFiles))
|
||||
i := 5
|
||||
for _, f := range p.openFiles {
|
||||
dlog.Printf(" %v", f)
|
||||
i--
|
||||
if i == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
p.fixupDirectories()
|
||||
changed = false
|
||||
}
|
||||
|
||||
// Do a rescan if it's time for it
|
||||
select {
|
||||
case <-walkTicker:
|
||||
if debugPull {
|
||||
dlog.Printf("%q: time for rescan", p.repo)
|
||||
}
|
||||
p.model.ScanRepo(p.repo)
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// Queue more blocks to fetch, if any
|
||||
p.queueNeededBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) runRO() {
|
||||
walkTicker := time.Tick(time.Duration(cfg.Options.RescanIntervalS) * time.Second)
|
||||
|
||||
for _ = range walkTicker {
|
||||
if debugPull {
|
||||
dlog.Printf("%q: time for rescan", p.repo)
|
||||
}
|
||||
p.model.ScanRepo(p.repo)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) fixupDirectories() {
|
||||
var deleteDirs []string
|
||||
fn := func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
rn, err := filepath.Rel(p.dir, path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rn == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
cur := p.model.CurrentGlobalFile(p.repo, rn)
|
||||
if cur.Name != rn {
|
||||
// No matching dir in current list; weird
|
||||
return nil
|
||||
}
|
||||
|
||||
if cur.Flags&protocol.FlagDeleted != 0 {
|
||||
if debugPull {
|
||||
dlog.Printf("queue delete dir: %v", cur)
|
||||
}
|
||||
|
||||
// We queue the directories to delete since we walk the
|
||||
// tree in depth first order and need to remove the
|
||||
// directories in the opposite order.
|
||||
|
||||
deleteDirs = append(deleteDirs, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
if cur.Flags&uint32(os.ModePerm) != uint32(info.Mode()&os.ModePerm) {
|
||||
os.Chmod(path, os.FileMode(cur.Flags)&os.ModePerm)
|
||||
if debugPull {
|
||||
dlog.Printf("restored dir flags: %o -> %v", info.Mode()&os.ModePerm, cur)
|
||||
}
|
||||
}
|
||||
|
||||
if cur.Modified != info.ModTime().Unix() {
|
||||
t := time.Unix(cur.Modified, 0)
|
||||
os.Chtimes(path, t, t)
|
||||
if debugPull {
|
||||
dlog.Printf("restored dir modtime: %d -> %v", info.ModTime().Unix(), cur)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
filepath.Walk(p.dir, fn)
|
||||
|
||||
// Delete any queued directories
|
||||
for i := len(deleteDirs) - 1; i >= 0; i-- {
|
||||
if debugPull {
|
||||
dlog.Println("delete dir:", deleteDirs[i])
|
||||
}
|
||||
err := os.Remove(deleteDirs[i])
|
||||
if err != nil {
|
||||
warnln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) handleRequestResult(res requestResult) {
|
||||
p.oustandingPerNode.decrease(res.node)
|
||||
f := res.file
|
||||
|
||||
of, ok := p.openFiles[f.Name]
|
||||
if !ok || of.err != nil {
|
||||
// no entry in openFiles means there was an error and we've cancelled the operation
|
||||
return
|
||||
}
|
||||
|
||||
_, of.err = of.file.WriteAt(res.data, res.offset)
|
||||
buffers.Put(res.data)
|
||||
|
||||
of.outstanding--
|
||||
p.openFiles[f.Name] = of
|
||||
|
||||
if debugPull {
|
||||
dlog.Printf("pull: wrote %q / %q offset %d outstanding %d done %v", p.repo, f.Name, res.offset, of.outstanding, of.done)
|
||||
}
|
||||
|
||||
if of.done && of.outstanding == 0 {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: closing %q / %q", p.repo, f.Name)
|
||||
}
|
||||
of.file.Close()
|
||||
defer os.Remove(of.temp)
|
||||
|
||||
delete(p.openFiles, f.Name)
|
||||
|
||||
fd, err := os.Open(of.temp)
|
||||
if err != nil {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
hb, _ := scanner.Blocks(fd, BlockSize)
|
||||
fd.Close()
|
||||
|
||||
if l0, l1 := len(hb), len(f.Blocks); l0 != l1 {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: %q / %q: nblocks %d != %d", p.repo, f.Name, l0, l1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for i := range hb {
|
||||
if bytes.Compare(hb[i].Hash, f.Blocks[i].Hash) != 0 {
|
||||
dlog.Printf("pull: %q / %q: block %d hash mismatch", p.repo, f.Name, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t := time.Unix(f.Modified, 0)
|
||||
os.Chtimes(of.temp, t, t)
|
||||
os.Chmod(of.temp, os.FileMode(f.Flags&0777))
|
||||
if debugPull {
|
||||
dlog.Printf("pull: rename %q / %q: %q", p.repo, f.Name, of.filepath)
|
||||
}
|
||||
if err := Rename(of.temp, of.filepath); err == nil {
|
||||
p.model.updateLocal(p.repo, f)
|
||||
} else {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) handleBlock(b bqBlock) {
|
||||
f := b.file
|
||||
|
||||
// For directories, simply making sure they exist is enough
|
||||
if f.Flags&protocol.FlagDirectory != 0 {
|
||||
path := filepath.Join(p.dir, f.Name)
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
os.MkdirAll(path, 0777)
|
||||
}
|
||||
p.model.updateLocal(p.repo, f)
|
||||
p.requestSlots <- true
|
||||
return
|
||||
}
|
||||
|
||||
of, ok := p.openFiles[f.Name]
|
||||
of.done = b.last
|
||||
|
||||
if !ok {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: %q: opening file %q", p.repo, f.Name)
|
||||
}
|
||||
|
||||
of.availability = uint64(p.model.repoFiles[p.repo].Availability(f.Name))
|
||||
of.filepath = filepath.Join(p.dir, f.Name)
|
||||
of.temp = filepath.Join(p.dir, defTempNamer.TempName(f.Name))
|
||||
|
||||
dirName := filepath.Dir(of.filepath)
|
||||
_, err := os.Stat(dirName)
|
||||
if err != nil {
|
||||
err = os.MkdirAll(dirName, 0777)
|
||||
}
|
||||
if err != nil {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, err)
|
||||
}
|
||||
|
||||
of.file, of.err = os.Create(of.temp)
|
||||
if of.err != nil {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, of.err)
|
||||
}
|
||||
if !b.last {
|
||||
p.openFiles[f.Name] = of
|
||||
}
|
||||
p.requestSlots <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if of.err != nil {
|
||||
// We have already failed this file.
|
||||
if debugPull {
|
||||
dlog.Printf("pull: error: %q / %q has already failed: %v", p.repo, f.Name, of.err)
|
||||
}
|
||||
if b.last {
|
||||
dlog.Printf("pull: removing failed file %q / %q", p.repo, f.Name)
|
||||
delete(p.openFiles, f.Name)
|
||||
}
|
||||
|
||||
p.requestSlots <- true
|
||||
return
|
||||
}
|
||||
|
||||
p.openFiles[f.Name] = of
|
||||
|
||||
switch {
|
||||
case len(b.copy) > 0:
|
||||
p.handleCopyBlock(b)
|
||||
p.requestSlots <- true
|
||||
|
||||
case b.block.Size > 0:
|
||||
p.handleRequestBlock(b)
|
||||
// Request slot gets freed in <-p.blocks case
|
||||
|
||||
default:
|
||||
p.handleEmptyBlock(b)
|
||||
p.requestSlots <- true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) handleCopyBlock(b bqBlock) {
|
||||
// We have blocks to copy from the existing file
|
||||
f := b.file
|
||||
of := p.openFiles[f.Name]
|
||||
|
||||
if debugPull {
|
||||
dlog.Printf("pull: copying %d blocks for %q / %q", len(b.copy), p.repo, f.Name)
|
||||
}
|
||||
|
||||
var exfd *os.File
|
||||
exfd, of.err = os.Open(of.filepath)
|
||||
if of.err != nil {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, of.err)
|
||||
}
|
||||
of.file.Close()
|
||||
of.file = nil
|
||||
|
||||
p.openFiles[f.Name] = of
|
||||
return
|
||||
}
|
||||
defer exfd.Close()
|
||||
|
||||
for _, b := range b.copy {
|
||||
bs := buffers.Get(int(b.Size))
|
||||
_, of.err = exfd.ReadAt(bs, b.Offset)
|
||||
if of.err == nil {
|
||||
_, of.err = of.file.WriteAt(bs, b.Offset)
|
||||
}
|
||||
buffers.Put(bs)
|
||||
if of.err != nil {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: error: %q / %q: %v", p.repo, f.Name, of.err)
|
||||
}
|
||||
exfd.Close()
|
||||
of.file.Close()
|
||||
of.file = nil
|
||||
|
||||
p.openFiles[f.Name] = of
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *puller) handleRequestBlock(b bqBlock) {
|
||||
// We have a block to get from the network
|
||||
|
||||
f := b.file
|
||||
of := p.openFiles[f.Name]
|
||||
|
||||
node := p.oustandingPerNode.leastBusyNode(of.availability, p.model.cm)
|
||||
if len(node) == 0 {
|
||||
of.err = errNoNode
|
||||
if of.file != nil {
|
||||
of.file.Close()
|
||||
of.file = nil
|
||||
os.Remove(of.temp)
|
||||
}
|
||||
if b.last {
|
||||
delete(p.openFiles, f.Name)
|
||||
} else {
|
||||
p.openFiles[f.Name] = of
|
||||
}
|
||||
p.requestSlots <- true
|
||||
return
|
||||
}
|
||||
|
||||
of.outstanding++
|
||||
p.openFiles[f.Name] = of
|
||||
|
||||
go func(node string, b bqBlock) {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: requesting %q / %q offset %d size %d from %q outstanding %d", p.repo, f.Name, b.block.Offset, b.block.Size, node, of.outstanding)
|
||||
}
|
||||
|
||||
bs, err := p.model.requestGlobal(node, p.repo, f.Name, b.block.Offset, int(b.block.Size), nil)
|
||||
p.requestResults <- requestResult{
|
||||
node: node,
|
||||
file: f,
|
||||
filepath: of.filepath,
|
||||
offset: b.block.Offset,
|
||||
data: bs,
|
||||
err: err,
|
||||
}
|
||||
}(node, b)
|
||||
}
|
||||
|
||||
func (p *puller) handleEmptyBlock(b bqBlock) {
|
||||
f := b.file
|
||||
of := p.openFiles[f.Name]
|
||||
|
||||
if b.last {
|
||||
if of.err == nil {
|
||||
of.file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if f.Flags&protocol.FlagDeleted != 0 {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: delete %q", f.Name)
|
||||
}
|
||||
os.Remove(of.temp)
|
||||
os.Remove(of.filepath)
|
||||
} else {
|
||||
if debugPull {
|
||||
dlog.Printf("pull: no blocks to fetch and nothing to copy for %q / %q", p.repo, f.Name)
|
||||
}
|
||||
t := time.Unix(f.Modified, 0)
|
||||
os.Chtimes(of.temp, t, t)
|
||||
os.Chmod(of.temp, os.FileMode(f.Flags&0777))
|
||||
Rename(of.temp, of.filepath)
|
||||
}
|
||||
delete(p.openFiles, f.Name)
|
||||
p.model.updateLocal(p.repo, f)
|
||||
}
|
||||
|
||||
func (p *puller) queueNeededBlocks() {
|
||||
queued := 0
|
||||
for _, f := range p.model.NeedFilesRepo(p.repo) {
|
||||
lf := p.model.CurrentRepoFile(p.repo, f.Name)
|
||||
have, need := scanner.BlockDiff(lf.Blocks, f.Blocks)
|
||||
if debugNeed {
|
||||
dlog.Printf("need:\n local: %v\n global: %v\n haveBlocks: %v\n needBlocks: %v", lf, f, have, need)
|
||||
}
|
||||
queued++
|
||||
p.bq.put(bqAdd{
|
||||
file: f,
|
||||
have: have,
|
||||
need: need,
|
||||
})
|
||||
}
|
||||
if debugPull && queued > 0 {
|
||||
dlog.Printf("%q: queued %d blocks", p.repo, queued)
|
||||
}
|
||||
}
|
||||
BIN
cmd/syncthing/syncthing
Executable file
BIN
cmd/syncthing/syncthing
Executable file
Binary file not shown.
@@ -2,9 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -15,14 +13,11 @@ type tempNamer struct {
|
||||
var defTempNamer = tempNamer{".syncthing"}
|
||||
|
||||
func (t tempNamer) IsTemporary(name string) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
name = filepath.ToSlash(name)
|
||||
}
|
||||
return strings.HasPrefix(path.Base(name), t.prefix)
|
||||
return strings.HasPrefix(filepath.Base(name), t.prefix)
|
||||
}
|
||||
|
||||
func (t tempNamer) TempName(name string) string {
|
||||
tdir := path.Dir(name)
|
||||
tname := fmt.Sprintf("%s.%s", t.prefix, path.Base(name))
|
||||
return path.Join(tdir, tname)
|
||||
tdir := filepath.Dir(name)
|
||||
tname := fmt.Sprintf("%s.%s", t.prefix, filepath.Base(name))
|
||||
return filepath.Join(tdir, tname)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
)
|
||||
|
||||
func loadCert(dir string) (tls.Certificate, error) {
|
||||
return tls.LoadX509KeyPair(path.Join(dir, "cert.pem"), path.Join(dir, "key.pem"))
|
||||
return tls.LoadX509KeyPair(filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem"))
|
||||
}
|
||||
|
||||
func certID(bs []byte) string {
|
||||
@@ -57,13 +57,13 @@ func newCertificate(dir string) {
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
fatalErr(err)
|
||||
|
||||
certOut, err := os.Create(path.Join(dir, "cert.pem"))
|
||||
certOut, err := os.Create(filepath.Join(dir, "cert.pem"))
|
||||
fatalErr(err)
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
okln("Created RSA certificate file")
|
||||
|
||||
keyOut, err := os.OpenFile(path.Join(dir, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
keyOut, err := os.OpenFile(filepath.Join(dir, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
fatalErr(err)
|
||||
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
keyOut.Close()
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func MetricPrefix(n int64) string {
|
||||
if n > 1e9 {
|
||||
@@ -27,3 +31,13 @@ func BinaryPrefix(n int64) string {
|
||||
}
|
||||
return fmt.Sprintf("%d ", n)
|
||||
}
|
||||
|
||||
func Rename(from, to string) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
err := os.Remove(to)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
warnln(err)
|
||||
}
|
||||
}
|
||||
return os.Rename(from, to)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/discover"
|
||||
"github.com/golang/groupcache/lru"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
@@ -28,10 +30,12 @@ var (
|
||||
lock sync.Mutex
|
||||
queries = 0
|
||||
answered = 0
|
||||
limited = 0
|
||||
debug = false
|
||||
limiter = lru.New(1024)
|
||||
)
|
||||
|
||||
func main() {
|
||||
var debug bool
|
||||
var listen string
|
||||
var timestamp bool
|
||||
|
||||
@@ -48,37 +52,25 @@ func main() {
|
||||
addr, _ := net.ResolveUDPAddr("udp", listen)
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(600 * time.Second)
|
||||
|
||||
lock.Lock()
|
||||
|
||||
var deleted = 0
|
||||
for id, node := range nodes {
|
||||
if time.Since(node.Updated) > 60*time.Minute {
|
||||
delete(nodes, id)
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
log.Printf("Expired %d nodes; %d nodes in registry; %d queries (%d answered)", deleted, len(nodes), queries, answered)
|
||||
queries = 0
|
||||
answered = 0
|
||||
|
||||
lock.Unlock()
|
||||
}
|
||||
}()
|
||||
go logStats()
|
||||
|
||||
var buf = make([]byte, 1024)
|
||||
for {
|
||||
buf = buf[:cap(buf)]
|
||||
n, addr, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
if limit(addr) {
|
||||
// Rate limit in effect for source
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if n < 4 {
|
||||
log.Printf("Received short packet (%d bytes)", n)
|
||||
continue
|
||||
@@ -89,149 +81,219 @@ func main() {
|
||||
|
||||
switch magic {
|
||||
case discover.AnnouncementMagicV1:
|
||||
var pkt discover.AnnounceV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
node := Node{
|
||||
Addresses: []Address{{
|
||||
IP: ip,
|
||||
Port: pkt.Port,
|
||||
}},
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
handleAnnounceV1(addr, buf)
|
||||
|
||||
case discover.QueryMagicV1:
|
||||
var pkt discover.QueryV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV1{
|
||||
Magic: discover.AnnouncementMagicV1,
|
||||
NodeID: pkt.NodeID,
|
||||
Port: node.Addresses[0].Port,
|
||||
IP: node.Addresses[0].IP,
|
||||
}
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("QueryV1 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
handleQueryV1(conn, addr, buf)
|
||||
|
||||
case discover.AnnouncementMagicV2:
|
||||
var pkt discover.AnnounceV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
|
||||
var addrs []Address
|
||||
for _, addr := range pkt.Addresses {
|
||||
tip := addr.IP
|
||||
if len(tip) == 0 {
|
||||
tip = ip
|
||||
}
|
||||
addrs = append(addrs, Address{
|
||||
IP: tip,
|
||||
Port: addr.Port,
|
||||
})
|
||||
}
|
||||
|
||||
node := Node{
|
||||
Addresses: addrs,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
handleAnnounceV2(addr, buf)
|
||||
|
||||
case discover.QueryMagicV2:
|
||||
var pkt discover.QueryV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV2{
|
||||
Magic: discover.AnnouncementMagicV2,
|
||||
NodeID: pkt.NodeID,
|
||||
}
|
||||
for _, addr := range node.Addresses {
|
||||
pkt.Addresses = append(pkt.Addresses, discover.Address{IP: addr.IP, Port: addr.Port})
|
||||
}
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
handleQueryV2(conn, addr, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func limit(addr *net.UDPAddr) bool {
|
||||
key := addr.IP.String()
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
bkt, ok := limiter.Get(key)
|
||||
if ok {
|
||||
bkt := bkt.(*ratelimit.Bucket)
|
||||
if bkt.TakeAvailable(1) != 1 {
|
||||
// Rate limit exceeded; ignore packet
|
||||
if debug {
|
||||
log.Printf("Rate limit exceeded for", key)
|
||||
}
|
||||
limited++
|
||||
return true
|
||||
} else if debug {
|
||||
log.Printf("Rate limit OK for", key)
|
||||
}
|
||||
} else {
|
||||
if debug {
|
||||
log.Printf("New limiter for", key)
|
||||
}
|
||||
// One packet per ten seconds average rate, burst ten packets
|
||||
limiter.Add(key, ratelimit.NewBucket(10*time.Second, 10))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func handleAnnounceV1(addr *net.UDPAddr, buf []byte) {
|
||||
var pkt discover.AnnounceV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
node := Node{
|
||||
Addresses: []Address{{
|
||||
IP: ip,
|
||||
Port: pkt.Port,
|
||||
}},
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
func handleQueryV1(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) {
|
||||
var pkt discover.QueryV1
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV1 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV1{
|
||||
Magic: discover.AnnouncementMagicV1,
|
||||
NodeID: pkt.NodeID,
|
||||
Port: node.Addresses[0].Port,
|
||||
IP: node.Addresses[0].IP,
|
||||
}
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("QueryV1 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func handleAnnounceV2(addr *net.UDPAddr, buf []byte) {
|
||||
var pkt discover.AnnounceV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("AnnounceV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
ip := addr.IP.To4()
|
||||
if ip == nil {
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
|
||||
var addrs []Address
|
||||
for _, addr := range pkt.Addresses {
|
||||
tip := addr.IP
|
||||
if len(tip) == 0 {
|
||||
tip = ip
|
||||
}
|
||||
addrs = append(addrs, Address{
|
||||
IP: tip,
|
||||
Port: addr.Port,
|
||||
})
|
||||
}
|
||||
|
||||
node := Node{
|
||||
Addresses: addrs,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.NodeID] = node
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
func handleQueryV2(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) {
|
||||
var pkt discover.QueryV2
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 Unmarshal:", err)
|
||||
log.Println(hex.Dump(buf))
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
if ok && len(node.Addresses) > 0 {
|
||||
pkt := discover.AnnounceV2{
|
||||
Magic: discover.AnnouncementMagicV2,
|
||||
NodeID: pkt.NodeID,
|
||||
}
|
||||
for _, addr := range node.Addresses {
|
||||
pkt.Addresses = append(pkt.Addresses, discover.Address{IP: addr.IP, Port: addr.Port})
|
||||
}
|
||||
if debug {
|
||||
log.Printf("-> %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
tb := pkt.MarshalXDR()
|
||||
_, _, err = conn.WriteMsgUDP(tb, nil, addr)
|
||||
if err != nil {
|
||||
log.Println("QueryV2 response write:", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
answered++
|
||||
lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func logStats() {
|
||||
for {
|
||||
time.Sleep(600 * time.Second)
|
||||
|
||||
lock.Lock()
|
||||
|
||||
var deleted = 0
|
||||
for id, node := range nodes {
|
||||
if time.Since(node.Updated) > 60*time.Minute {
|
||||
delete(nodes, id)
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Expired %d nodes; %d nodes in registry; %d queries (%d answered)", deleted, len(nodes), queries, answered)
|
||||
log.Printf("Limited %d queries; %d entries in limiter cache", limited, limiter.Len())
|
||||
queries = 0
|
||||
answered = 0
|
||||
limited = 0
|
||||
|
||||
lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"code.google.com/p/go.net/ipv6"
|
||||
|
||||
"github.com/calmh/syncthing/buffers"
|
||||
"github.com/calmh/syncthing/mc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,18 +19,14 @@ const (
|
||||
)
|
||||
|
||||
type Discoverer struct {
|
||||
MyID string
|
||||
ListenAddresses []string
|
||||
BroadcastIntv time.Duration
|
||||
ExtBroadcastIntv time.Duration
|
||||
|
||||
conn *ipv6.PacketConn
|
||||
intfs []*net.Interface
|
||||
registry map[string][]string
|
||||
registryLock sync.RWMutex
|
||||
extServer string
|
||||
group *net.UDPAddr
|
||||
|
||||
MyID string
|
||||
ListenAddresses []string
|
||||
BroadcastIntv time.Duration
|
||||
ExtBroadcastIntv time.Duration
|
||||
beacon *mc.Beacon
|
||||
registry map[string][]string
|
||||
registryLock sync.RWMutex
|
||||
extServer string
|
||||
localBroadcastTick <-chan time.Time
|
||||
forcedBroadcastTick chan time.Time
|
||||
}
|
||||
@@ -50,43 +46,9 @@ func NewDiscoverer(id string, addresses []string, extServer string) (*Discoverer
|
||||
ListenAddresses: addresses,
|
||||
BroadcastIntv: 30 * time.Second,
|
||||
ExtBroadcastIntv: 1800 * time.Second,
|
||||
beacon: mc.NewBeacon("239.21.0.25", 21025),
|
||||
registry: make(map[string][]string),
|
||||
extServer: extServer,
|
||||
group: &net.UDPAddr{IP: net.ParseIP("ff02::2012:1025"), Port: AnnouncementPort},
|
||||
}
|
||||
|
||||
// Listen on a multicast socket. This enables sharing the socket, i.e.
|
||||
// other instances of syncting on the same box can listen on the same
|
||||
// group/port.
|
||||
|
||||
conn, err := net.ListenPacket("udp6", fmt.Sprintf("[ff02::]:%d", AnnouncementPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disc.conn = ipv6.NewPacketConn(conn)
|
||||
|
||||
// Join the multicast group on as many interfaces as possible. Remember
|
||||
// which those were.
|
||||
|
||||
intfs, err := net.Interfaces()
|
||||
if err != nil {
|
||||
log.Printf("discover/interfaces: %v; no local announcements", err)
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, intf := range intfs {
|
||||
intf := intf
|
||||
addrs, err := intf.Addrs()
|
||||
if err == nil && len(addrs) > 0 && intf.Flags&net.FlagMulticast != 0 && intf.Flags&net.FlagUp != 0 {
|
||||
if err := disc.conn.JoinGroup(&intf, disc.group); err != nil {
|
||||
if debug {
|
||||
dlog.Printf("%v; not joining on %s", err, intf.Name)
|
||||
}
|
||||
} else {
|
||||
disc.intfs = append(disc.intfs, &intf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Receive announcements sent to the local multicast group.
|
||||
@@ -140,21 +102,9 @@ func (d *Discoverer) announcementPkt() []byte {
|
||||
|
||||
func (d *Discoverer) sendLocalAnnouncements() {
|
||||
var buf = d.announcementPkt()
|
||||
var errCounter = 0
|
||||
var err error
|
||||
|
||||
wcm := ipv6.ControlMessage{HopLimit: 1}
|
||||
for errCounter < maxErrors {
|
||||
for _, intf := range d.intfs {
|
||||
wcm.IfIndex = intf.Index
|
||||
if _, err = d.conn.WriteTo(buf, &wcm, d.group); err != nil {
|
||||
log.Printf("discover/sendLocalAnnouncements: on %s: %v; no local announcement", intf.Name, err)
|
||||
errCounter++
|
||||
continue
|
||||
} else {
|
||||
errCounter = 0
|
||||
}
|
||||
}
|
||||
for {
|
||||
d.beacon.Send(buf)
|
||||
|
||||
select {
|
||||
case <-d.localBroadcastTick:
|
||||
@@ -196,26 +146,16 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
}
|
||||
|
||||
func (d *Discoverer) recvAnnouncements() {
|
||||
var buf = make([]byte, 1024)
|
||||
var errCounter = 0
|
||||
var err error
|
||||
for errCounter < maxErrors {
|
||||
n, _, addr, err := d.conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
errCounter++
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
for {
|
||||
buf, addr := d.beacon.Recv()
|
||||
|
||||
if debug {
|
||||
dlog.Printf("read announcement:\n%s", hex.Dump(buf[:n]))
|
||||
dlog.Printf("read announcement:\n%s", hex.Dump(buf))
|
||||
}
|
||||
|
||||
var pkt AnnounceV2
|
||||
err = pkt.UnmarshalXDR(buf[:n])
|
||||
err := pkt.UnmarshalXDR(buf)
|
||||
if err != nil {
|
||||
errCounter++
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -223,8 +163,6 @@ func (d *Discoverer) recvAnnouncements() {
|
||||
dlog.Printf("parsed announcement: %#v", pkt)
|
||||
}
|
||||
|
||||
errCounter = 0
|
||||
|
||||
if pkt.NodeID != d.MyID {
|
||||
var addrs []string
|
||||
for _, a := range pkt.Addresses {
|
||||
@@ -252,7 +190,6 @@ func (d *Discoverer) recvAnnouncements() {
|
||||
d.registryLock.Unlock()
|
||||
}
|
||||
}
|
||||
log.Println("discover/read: stopping due to too many errors:", err)
|
||||
}
|
||||
|
||||
func (d *Discoverer) externalLookup(node string) []string {
|
||||
|
||||
12
files/debug.go
Normal file
12
files/debug.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
dlog = log.New(os.Stderr, "files: ", log.Lmicroseconds|log.Lshortfile)
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "files")
|
||||
)
|
||||
328
files/set.go
Normal file
328
files/set.go
Normal file
@@ -0,0 +1,328 @@
|
||||
// Package files provides a set type to track local/remote files with newness checks.
|
||||
package files
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"sync"
|
||||
|
||||
"github.com/calmh/syncthing/cid"
|
||||
"github.com/calmh/syncthing/lamport"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
type key struct {
|
||||
Name string
|
||||
Version uint64
|
||||
Modified int64
|
||||
Hash [md5.Size]byte
|
||||
}
|
||||
|
||||
type fileRecord struct {
|
||||
Usage int
|
||||
File scanner.File
|
||||
}
|
||||
|
||||
type bitset uint64
|
||||
|
||||
func keyFor(f scanner.File) key {
|
||||
h := md5.New()
|
||||
for _, b := range f.Blocks {
|
||||
h.Write(b.Hash)
|
||||
}
|
||||
return key{
|
||||
Name: f.Name,
|
||||
Version: f.Version,
|
||||
Modified: f.Modified,
|
||||
Hash: md5.Sum(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (a key) newerThan(b key) bool {
|
||||
if a.Version != b.Version {
|
||||
return a.Version > b.Version
|
||||
}
|
||||
if a.Modified != b.Modified {
|
||||
return a.Modified > b.Modified
|
||||
}
|
||||
for i := 0; i < md5.Size; i++ {
|
||||
if a.Hash[i] != b.Hash[i] {
|
||||
return a.Hash[i] > b.Hash[i]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Set struct {
|
||||
sync.Mutex
|
||||
files map[key]fileRecord
|
||||
remoteKey [64]map[string]key
|
||||
changes [64]uint64
|
||||
globalAvailability map[string]bitset
|
||||
globalKey map[string]key
|
||||
}
|
||||
|
||||
func NewSet() *Set {
|
||||
var m = Set{
|
||||
files: make(map[key]fileRecord),
|
||||
globalAvailability: make(map[string]bitset),
|
||||
globalKey: make(map[string]key),
|
||||
}
|
||||
return &m
|
||||
}
|
||||
|
||||
func (m *Set) Replace(id uint, fs []scanner.File) {
|
||||
if debug {
|
||||
dlog.Printf("Replace(%d, [%d])", id, len(fs))
|
||||
}
|
||||
if id > 63 {
|
||||
panic("Connection ID must be in the range 0 - 63 inclusive")
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
if len(fs) == 0 || !m.equals(id, fs) {
|
||||
m.changes[id]++
|
||||
m.replace(id, fs)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func (m *Set) ReplaceWithDelete(id uint, fs []scanner.File) {
|
||||
if debug {
|
||||
dlog.Printf("ReplaceWithDelete(%d, [%d])", id, len(fs))
|
||||
}
|
||||
if id > 63 {
|
||||
panic("Connection ID must be in the range 0 - 63 inclusive")
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
if len(fs) == 0 || !m.equals(id, fs) {
|
||||
m.changes[id]++
|
||||
|
||||
var nf = make(map[string]key, len(fs))
|
||||
for _, f := range fs {
|
||||
nf[f.Name] = keyFor(f)
|
||||
}
|
||||
|
||||
// For previously existing files not in the list, add them to the list
|
||||
// with the relevant delete flags etc set. Previously existing files
|
||||
// with the delete bit already set are not modified.
|
||||
|
||||
for _, ck := range m.remoteKey[cid.LocalID] {
|
||||
if _, ok := nf[ck.Name]; !ok {
|
||||
cf := m.files[ck].File
|
||||
if cf.Flags&protocol.FlagDeleted != protocol.FlagDeleted {
|
||||
cf.Flags |= protocol.FlagDeleted
|
||||
cf.Blocks = nil
|
||||
cf.Size = 0
|
||||
cf.Version = lamport.Default.Tick(cf.Version)
|
||||
}
|
||||
fs = append(fs, cf)
|
||||
if debug {
|
||||
dlog.Println("deleted:", ck.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.replace(id, fs)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func (m *Set) Update(id uint, fs []scanner.File) {
|
||||
if debug {
|
||||
dlog.Printf("Update(%d, [%d])", id, len(fs))
|
||||
}
|
||||
m.Lock()
|
||||
m.update(id, fs)
|
||||
m.changes[id]++
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func (m *Set) Need(id uint) []scanner.File {
|
||||
if debug {
|
||||
dlog.Printf("Need(%d)", id)
|
||||
}
|
||||
var fs []scanner.File
|
||||
m.Lock()
|
||||
rkID := m.remoteKey[id]
|
||||
for name, gk := range m.globalKey {
|
||||
if gk.newerThan(rkID[name]) {
|
||||
if m.files[gk].File.Flags&protocol.FlagDirectory == 0 || // Regular file
|
||||
m.files[gk].File.Flags&(protocol.FlagDirectory|protocol.FlagDeleted) == protocol.FlagDirectory { // Non-deleted directory
|
||||
fs = append(fs, m.files[gk].File)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Unlock()
|
||||
return fs
|
||||
}
|
||||
|
||||
func (m *Set) Have(id uint) []scanner.File {
|
||||
if debug {
|
||||
dlog.Printf("Have(%d)", id)
|
||||
}
|
||||
var fs []scanner.File
|
||||
m.Lock()
|
||||
for _, rk := range m.remoteKey[id] {
|
||||
fs = append(fs, m.files[rk].File)
|
||||
}
|
||||
m.Unlock()
|
||||
return fs
|
||||
}
|
||||
|
||||
func (m *Set) Global() []scanner.File {
|
||||
if debug {
|
||||
dlog.Printf("Global()")
|
||||
}
|
||||
var fs []scanner.File
|
||||
m.Lock()
|
||||
for _, rk := range m.globalKey {
|
||||
fs = append(fs, m.files[rk].File)
|
||||
}
|
||||
m.Unlock()
|
||||
return fs
|
||||
}
|
||||
|
||||
func (m *Set) Get(id uint, file string) scanner.File {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if debug {
|
||||
dlog.Printf("Get(%d, %q)", id, file)
|
||||
}
|
||||
return m.files[m.remoteKey[id][file]].File
|
||||
}
|
||||
|
||||
func (m *Set) GetGlobal(file string) scanner.File {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if debug {
|
||||
dlog.Printf("GetGlobal(%q)", file)
|
||||
}
|
||||
return m.files[m.globalKey[file]].File
|
||||
}
|
||||
|
||||
func (m *Set) Availability(name string) bitset {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
av := m.globalAvailability[name]
|
||||
if debug {
|
||||
dlog.Printf("Availability(%q) = %0x", name, av)
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
func (m *Set) Changes(id uint) uint64 {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if debug {
|
||||
dlog.Printf("Changes(%d)", id)
|
||||
}
|
||||
return m.changes[id]
|
||||
}
|
||||
|
||||
func (m *Set) equals(id uint, fs []scanner.File) bool {
|
||||
curWithoutDeleted := make(map[string]key)
|
||||
for _, k := range m.remoteKey[id] {
|
||||
f := m.files[k].File
|
||||
if f.Flags&protocol.FlagDeleted == 0 {
|
||||
curWithoutDeleted[f.Name] = k
|
||||
}
|
||||
}
|
||||
if len(curWithoutDeleted) != len(fs) {
|
||||
return false
|
||||
}
|
||||
for _, f := range fs {
|
||||
if curWithoutDeleted[f.Name] != keyFor(f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Set) update(cid uint, fs []scanner.File) {
|
||||
remFiles := m.remoteKey[cid]
|
||||
for _, f := range fs {
|
||||
n := f.Name
|
||||
fk := keyFor(f)
|
||||
|
||||
if ck, ok := remFiles[n]; ok && ck == fk {
|
||||
// The remote already has exactly this file, skip it
|
||||
continue
|
||||
}
|
||||
|
||||
remFiles[n] = fk
|
||||
|
||||
// Keep the block list or increment the usage
|
||||
if br, ok := m.files[fk]; !ok {
|
||||
m.files[fk] = fileRecord{
|
||||
Usage: 1,
|
||||
File: f,
|
||||
}
|
||||
} else {
|
||||
br.Usage++
|
||||
m.files[fk] = br
|
||||
}
|
||||
|
||||
// Update global view
|
||||
gk, ok := m.globalKey[n]
|
||||
switch {
|
||||
case ok && fk == gk:
|
||||
av := m.globalAvailability[n]
|
||||
av |= 1 << cid
|
||||
m.globalAvailability[n] = av
|
||||
case fk.newerThan(gk):
|
||||
m.globalKey[n] = fk
|
||||
m.globalAvailability[n] = 1 << cid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Set) replace(cid uint, fs []scanner.File) {
|
||||
// Decrement usage for all files belonging to this remote, and remove
|
||||
// those that are no longer needed.
|
||||
for _, fk := range m.remoteKey[cid] {
|
||||
br, ok := m.files[fk]
|
||||
switch {
|
||||
case ok && br.Usage == 1:
|
||||
delete(m.files, fk)
|
||||
case ok && br.Usage > 1:
|
||||
br.Usage--
|
||||
m.files[fk] = br
|
||||
}
|
||||
}
|
||||
|
||||
// Clear existing remote remoteKey
|
||||
m.remoteKey[cid] = make(map[string]key)
|
||||
|
||||
// Recalculate global based on all remaining remoteKey
|
||||
for n := range m.globalKey {
|
||||
var nk key // newest key
|
||||
var na bitset // newest availability
|
||||
|
||||
for i, rem := range m.remoteKey {
|
||||
if rk, ok := rem[n]; ok {
|
||||
switch {
|
||||
case rk == nk:
|
||||
na |= 1 << uint(i)
|
||||
case rk.newerThan(nk):
|
||||
nk = rk
|
||||
na = 1 << uint(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if na != 0 {
|
||||
// Someone had the file
|
||||
m.globalKey[n] = nk
|
||||
m.globalAvailability[n] = na
|
||||
} else {
|
||||
// Noone had the file
|
||||
delete(m.globalKey, n)
|
||||
delete(m.globalAvailability, n)
|
||||
}
|
||||
}
|
||||
|
||||
// Add new remote remoteKey to the mix
|
||||
m.update(cid, fs)
|
||||
}
|
||||
324
files/set_test.go
Normal file
324
files/set_test.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/cid"
|
||||
"github.com/calmh/syncthing/lamport"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
type fileList []scanner.File
|
||||
|
||||
func (l fileList) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func (l fileList) Less(a, b int) bool {
|
||||
return l[a].Name < l[b].Name
|
||||
}
|
||||
|
||||
func (l fileList) Swap(a, b int) {
|
||||
l[a], l[b] = l[b], l[a]
|
||||
}
|
||||
|
||||
func TestGlobalSet(t *testing.T) {
|
||||
m := NewSet()
|
||||
|
||||
local := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1000},
|
||||
scanner.File{Name: "c", Version: 1000},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
}
|
||||
|
||||
remote := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1001},
|
||||
scanner.File{Name: "c", Version: 1002},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
expectedGlobal := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1001},
|
||||
scanner.File{Name: "c", Version: 1002},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
m.Replace(1, remote)
|
||||
|
||||
g := m.Global()
|
||||
|
||||
sort.Sort(fileList(g))
|
||||
sort.Sort(fileList(expectedGlobal))
|
||||
|
||||
if !reflect.DeepEqual(g, expectedGlobal) {
|
||||
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal)
|
||||
}
|
||||
|
||||
if lb := len(m.files); lb != 7 {
|
||||
t.Errorf("Num files incorrect %d != 7\n%v", lb, m.files)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalDeleted(t *testing.T) {
|
||||
m := NewSet()
|
||||
lamport.Default = lamport.Clock{}
|
||||
|
||||
local1 := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1000},
|
||||
scanner.File{Name: "c", Version: 1000},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
scanner.File{Name: "z", Version: 1000, Flags: protocol.FlagDirectory},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local1)
|
||||
|
||||
local2 := []scanner.File{
|
||||
local1[0],
|
||||
local1[2],
|
||||
}
|
||||
|
||||
expectedGlobal1 := []scanner.File{
|
||||
local1[0],
|
||||
scanner.File{Name: "b", Version: 1001, Flags: protocol.FlagDeleted},
|
||||
local1[2],
|
||||
scanner.File{Name: "d", Version: 1002, Flags: protocol.FlagDeleted},
|
||||
scanner.File{Name: "z", Version: 1003, Flags: protocol.FlagDeleted | protocol.FlagDirectory},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local2)
|
||||
g := m.Global()
|
||||
sort.Sort(fileList(g))
|
||||
sort.Sort(fileList(expectedGlobal1))
|
||||
|
||||
if !reflect.DeepEqual(g, expectedGlobal1) {
|
||||
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal1)
|
||||
}
|
||||
|
||||
local3 := []scanner.File{
|
||||
local1[0],
|
||||
}
|
||||
|
||||
expectedGlobal2 := []scanner.File{
|
||||
local1[0],
|
||||
scanner.File{Name: "b", Version: 1001, Flags: protocol.FlagDeleted},
|
||||
scanner.File{Name: "c", Version: 1004, Flags: protocol.FlagDeleted},
|
||||
scanner.File{Name: "d", Version: 1002, Flags: protocol.FlagDeleted},
|
||||
scanner.File{Name: "z", Version: 1003, Flags: protocol.FlagDeleted | protocol.FlagDirectory},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local3)
|
||||
g = m.Global()
|
||||
sort.Sort(fileList(g))
|
||||
sort.Sort(fileList(expectedGlobal2))
|
||||
|
||||
if !reflect.DeepEqual(g, expectedGlobal2) {
|
||||
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal2)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetLocal10k(b *testing.B) {
|
||||
m := NewSet()
|
||||
|
||||
var local []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
local = append(local, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
var remote []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
remote = append(remote, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
m.Replace(1, remote)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetLocal10(b *testing.B) {
|
||||
m := NewSet()
|
||||
|
||||
var local []scanner.File
|
||||
for i := 0; i < 10; i++ {
|
||||
local = append(local, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
var remote []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
remote = append(remote, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
m.Replace(1, remote)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddLocal10k(b *testing.B) {
|
||||
m := NewSet()
|
||||
|
||||
var local []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
local = append(local, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
var remote []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
remote = append(remote, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
m.Replace(1, remote)
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
for j := range local {
|
||||
local[j].Version++
|
||||
}
|
||||
b.StartTimer()
|
||||
m.Update(cid.LocalID, local)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddLocal10(b *testing.B) {
|
||||
m := NewSet()
|
||||
|
||||
var local []scanner.File
|
||||
for i := 0; i < 10; i++ {
|
||||
local = append(local, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
var remote []scanner.File
|
||||
for i := 0; i < 10000; i++ {
|
||||
remote = append(remote, scanner.File{Name: fmt.Sprintf("file%d"), Version: 1000})
|
||||
}
|
||||
|
||||
m.Replace(1, remote)
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range local {
|
||||
local[j].Version++
|
||||
}
|
||||
m.Update(cid.LocalID, local)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobalReset(t *testing.T) {
|
||||
m := NewSet()
|
||||
|
||||
local := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1000},
|
||||
scanner.File{Name: "c", Version: 1000},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
}
|
||||
|
||||
remote := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1001},
|
||||
scanner.File{Name: "c", Version: 1002},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
expectedGlobalKey := map[string]key{
|
||||
"a": keyFor(local[0]),
|
||||
"b": keyFor(local[1]),
|
||||
"c": keyFor(local[2]),
|
||||
"d": keyFor(local[3]),
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
m.Replace(1, remote)
|
||||
m.Replace(1, nil)
|
||||
|
||||
if !reflect.DeepEqual(m.globalKey, expectedGlobalKey) {
|
||||
t.Errorf("Global incorrect;\n%v !=\n%v", m.globalKey, expectedGlobalKey)
|
||||
}
|
||||
|
||||
if lb := len(m.files); lb != 4 {
|
||||
t.Errorf("Num files incorrect %d != 4\n%v", lb, m.files)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeed(t *testing.T) {
|
||||
m := NewSet()
|
||||
|
||||
local := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1000},
|
||||
scanner.File{Name: "c", Version: 1000},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
}
|
||||
|
||||
remote := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1001},
|
||||
scanner.File{Name: "c", Version: 1002},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
shouldNeed := []scanner.File{
|
||||
scanner.File{Name: "b", Version: 1001},
|
||||
scanner.File{Name: "c", Version: 1002},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local)
|
||||
m.Replace(1, remote)
|
||||
|
||||
need := m.Need(0)
|
||||
if !reflect.DeepEqual(need, shouldNeed) {
|
||||
t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChanges(t *testing.T) {
|
||||
m := NewSet()
|
||||
|
||||
local1 := []scanner.File{
|
||||
scanner.File{Name: "a", Version: 1000},
|
||||
scanner.File{Name: "b", Version: 1000},
|
||||
scanner.File{Name: "c", Version: 1000},
|
||||
scanner.File{Name: "d", Version: 1000},
|
||||
}
|
||||
|
||||
local2 := []scanner.File{
|
||||
local1[0],
|
||||
// [1] deleted
|
||||
local1[2],
|
||||
scanner.File{Name: "d", Version: 1002},
|
||||
scanner.File{Name: "e", Version: 1000},
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local1)
|
||||
c0 := m.Changes(cid.LocalID)
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local2)
|
||||
c1 := m.Changes(cid.LocalID)
|
||||
if !(c1 > c0) {
|
||||
t.Fatal("Change number should have incremented")
|
||||
}
|
||||
|
||||
m.ReplaceWithDelete(cid.LocalID, local2)
|
||||
c2 := m.Changes(cid.LocalID)
|
||||
if c2 != c1 {
|
||||
t.Fatal("Change number should be unchanged")
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
{id: 'MaxChangeKbps', descr: 'Max File Change Rate (KBps)', type: 'number', restart: true},
|
||||
|
||||
{id: 'ReadOnly', descr: 'Read Only', type: 'bool', restart: true},
|
||||
{id: 'AllowDelete', descr: 'Allow Delete', type: 'bool', restart: true},
|
||||
{id: 'FollowSymlinks', descr: 'Follow Symlinks', type: 'bool', restart: true},
|
||||
{id: 'GlobalAnnEnabled', descr: 'Global Announce', type: 'bool', restart: true},
|
||||
{id: 'LocalAnnEnabled', descr: 'Local Announce', type: 'bool', restart: true},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user