├── .gitignore
├── logging_test.go
├── logging.go
├── prop_sheets
├── x64-2012.props
├── RunTimeRelease.props
├── RunTimeDebug.props
├── win32-2012.props
├── debug-2012.props
└── release-2012.props
├── deadline.go
├── status.go
├── lsan_test.go
├── expvars.go
├── socket_test.go
├── README.md
├── libapi.go
├── conn_test.go
├── callbacks_test.go
├── .github
└── workflows
│ └── go.yml
├── LICENSE
├── go.mod
├── bench_test.go
├── utp_utils.h
├── cmd
└── ucat
│ └── main.go
├── utp_packedsockaddr.h
├── utp.go
├── libutp-2012.vcxproj.filters
├── sockaddr.go
├── utp_callbacks.h
├── libutp_inet_ntop.h
├── utp_packedsockaddr.cpp
├── utp_api.cpp
├── utp_internal.h
├── utp_types.h
├── utp_test.go
├── callbacks.go
├── utp_hash.h
├── utp.h
├── utp_templates.h
├── conn.go
├── utp_callbacks.cpp
├── utp_hash.cpp
├── utp_utils.cpp
├── parse_log.py
├── go.sum
├── libutp.vcxproj
└── socket.go
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.a
3 | *.so
4 | tags
5 | *~
6 | _obj
7 |
--------------------------------------------------------------------------------
/logging_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import "testing"
4 |
5 | func TestLogger(t *testing.T) {
6 | Logger.Printf("hello!")
7 | }
8 |
--------------------------------------------------------------------------------
/logging.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import "github.com/anacrolix/log"
4 |
5 | const (
6 | logCallbacks = false
7 | utpLogging = false
8 | )
9 |
10 | // The default Socket Logger. Override per Socket by using WithLogger with NewSocket.
11 | var Logger = log.Default.WithContextText("go-libutp")
12 |
--------------------------------------------------------------------------------
/prop_sheets/x64-2012.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/deadline.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import "net"
4 |
5 | type errDeadlineExceeded struct{}
6 |
7 | var _ net.Error = errDeadlineExceeded{}
8 |
9 | func (errDeadlineExceeded) Error() string { return "deadline exceeded" }
10 | func (errDeadlineExceeded) Temporary() bool { return false }
11 | func (errDeadlineExceeded) Timeout() bool { return true }
12 |
--------------------------------------------------------------------------------
/status.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | )
7 |
8 | func WriteStatus(w io.Writer) {
9 | mu.Lock()
10 | defer mu.Unlock()
11 | for _, s := range libContextToSocket {
12 | fmt.Fprintf(w, "listening at %s\n", s.Addr())
13 | fmt.Fprintf(w, "has %d conns\n", len(s.conns))
14 | fmt.Fprintf(w, "backlog: %d\n", len(s.backlog))
15 | fmt.Fprintf(w, "\n")
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lsan_test.go:
--------------------------------------------------------------------------------
1 | //go:build lsan
2 | // +build lsan
3 |
4 | package utp
5 |
6 | import (
7 | "log"
8 | "os"
9 | "testing"
10 |
11 | "github.com/anacrolix/lsan"
12 | )
13 |
14 | // Use this with ASAN_OPTIONS=detect_leaks=1 go test -tags lsan.
15 | func TestMain(m *testing.M) {
16 | log.Printf("lsan main running")
17 | //lsan.LeakABit()
18 | code := m.Run()
19 | lsan.LsanDoLeakCheck()
20 | os.Exit(code)
21 | }
22 |
--------------------------------------------------------------------------------
/prop_sheets/RunTimeRelease.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | MultiThreaded
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/prop_sheets/RunTimeDebug.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | MultiThreadedDebug
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/expvars.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "expvar"
5 | )
6 |
7 | var (
8 | expMap = expvar.NewMap("go-libutp")
9 | socketUtpPacketsReceived = expvar.NewInt("utpSocketUtpPacketsReceived")
10 | socketNonUtpPacketsReceived = expvar.NewInt("utpSocketNonUtpPacketsReceived")
11 | nonUtpPacketsDropped = expvar.NewInt("utpNonUtpPacketsDropped")
12 | multiMsgRecvs = expvar.NewInt("utpMultiMsgRecvs")
13 | singleMsgRecvs = expvar.NewInt("utpSingleMsgRecvs")
14 | )
15 |
--------------------------------------------------------------------------------
/prop_sheets/win32-2012.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | MachineX86
9 |
10 |
11 | 4Bytes
12 | NoExtensions
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/socket_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestUseClosedSocket(t *testing.T) {
11 | s, err := NewSocket("udp", "localhost:0")
12 | require.NoError(t, err)
13 | assert.NoError(t, s.Close())
14 | assert.NotPanics(t, func() { s.Close() })
15 | c, err := s.Dial(neverResponds)
16 | assert.Error(t, err)
17 | assert.Nil(t, c)
18 | }
19 |
20 | func TestSocketNetwork(t *testing.T) {
21 | s, err := NewSocket("udp", "localhost:0")
22 | require.NoError(t, err)
23 | defer s.Close()
24 | assert.Equal(t, "udp", s.Addr().Network())
25 | }
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-libutp
2 |
3 | [](http://godoc.org/github.com/anacrolix/go-libutp)
4 | [](https://circleci.com/gh/anacrolix/go-libutp)
5 | [](https://goreportcard.com/report/github.com/anacrolix/go-libutp)
6 | [](https://ci.appveyor.com/project/anacrolix/go-libutp)
7 |
8 | This is a Go wrapper for [libutp](https://github.com/bittorrent/libutp).
9 |
--------------------------------------------------------------------------------
/libapi.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #include "utp.h"
5 | */
6 | import "C"
7 |
8 | import (
9 | "errors"
10 |
11 | "github.com/anacrolix/sync"
12 | )
13 |
14 | type Option = C.int
15 |
16 | const (
17 | LogNormal Option = C.UTP_LOG_NORMAL
18 | LogMtu Option = C.UTP_LOG_MTU
19 | LogDebug Option = C.UTP_LOG_DEBUG
20 | SendBuffer Option = C.UTP_SNDBUF
21 | RecvBuffer Option = C.UTP_RCVBUF
22 | TargetDelay Option = C.UTP_TARGET_DELAY
23 |
24 | TimedOut = C.UTP_ETIMEDOUT
25 | )
26 |
27 | var (
28 | mu sync.Mutex
29 | libContextToSocket = map[*utpContext]*Socket{}
30 | )
31 |
32 | func getSocketForLibContext(uc *utpContext) *Socket {
33 | return libContextToSocket[uc]
34 | }
35 |
36 | func errorForCode(code C.int) error {
37 | return errors.New(libErrorCodeNames(code))
38 | }
39 |
--------------------------------------------------------------------------------
/conn_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "net"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | "github.com/stretchr/testify/require"
9 | )
10 |
11 | func TestConnMethodsAfterClose(t *testing.T) {
12 | s, err := NewSocket("udp", "localhost:0")
13 | require.NoError(t, err)
14 | defer s.Close()
15 | d, a := connPairSocket(s)
16 | // We need to trigger libutp to destroy the Conns, the fastest way to do
17 | // this is destroy the parent Socket.
18 | assert.NoError(t, s.Close())
19 | for _, c := range []net.Conn{d, a} {
20 | // We're trying to test what happens when the Conn isn't known to
21 | // libutp anymore.
22 | assert.Nil(t, c.(*Conn).us)
23 | // These functions must not panic. I'm not sure we care what they
24 | // return.
25 | assert.NotPanics(t, func() { c.RemoteAddr() })
26 | assert.NotPanics(t, func() { c.LocalAddr() })
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/callbacks_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "context"
5 | "sync"
6 | "testing"
7 |
8 | "github.com/bradfitz/iter"
9 | qt "github.com/frankban/quicktest"
10 | )
11 |
12 | // Test for a race that occurs if the error returned from PacketConn.WriteTo in sendtoCallback holds
13 | // a reference to the addr passed to the call, and the addr storage is reused between calls to
14 | // sendtoCallback in this instance.
15 | func TestSendToRaceErrorAddr(t *testing.T) {
16 | c := qt.New(t)
17 | s, err := NewSocket("udp", "localhost:0")
18 | c.Assert(err, qt.IsNil)
19 | defer s.Close()
20 | ctx, cancel := context.WithCancel(context.Background())
21 | cancel()
22 | var wg sync.WaitGroup
23 | for range iter.N(2) {
24 | wg.Add(1)
25 | go func() {
26 | defer wg.Done()
27 | _, err := s.DialContext(ctx, "udp", "1.1.1.1:1")
28 | c.Log(err.Error())
29 | c.Assert(err, qt.Not(qt.IsNil))
30 | }()
31 | }
32 | wg.Wait()
33 | }
34 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 |
7 | test:
8 | timeout-minutes: 30
9 | runs-on: ${{ matrix.os }}
10 | strategy:
11 | matrix:
12 | go-version: [ 'stable', 'oldstable' ]
13 | os: [ubuntu-latest, macos-latest, windows-latest]
14 | fail-fast: false
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-go@v6
18 | with:
19 | go-version: ${{ matrix.go-version }}
20 | - run: go version
21 | - run: go test -race -count 2 -skip '.*Nettest.*/ReadTimeout' ./...
22 | - run: go test -bench . -run @ ./...
23 |
24 | asan:
25 | timeout-minutes: 30
26 | runs-on: ${{ matrix.os }}
27 | strategy:
28 | matrix:
29 | go-version: [ 'stable', 'oldstable' ]
30 | os: [ubuntu-latest]
31 | fail-fast: false
32 | steps:
33 | - uses: actions/checkout@v2
34 | - uses: actions/setup-go@v6
35 | with:
36 | go-version: ${{ matrix.go-version }}
37 | - run: go version
38 | - run: ASAN_OPTIONS=detect_leaks=1 go test -tags lsan ./...
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2013 BitTorrent, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/anacrolix/go-libutp
2 |
3 | go 1.24.0
4 |
5 | require (
6 | github.com/anacrolix/envpprof v1.1.0
7 | github.com/anacrolix/log v0.13.1
8 | github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62
9 | github.com/anacrolix/missinggo v1.2.1
10 | github.com/anacrolix/missinggo/v2 v2.10.0
11 | github.com/anacrolix/mmsg v1.0.1
12 | github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778
13 | github.com/anacrolix/tagflag v1.1.0
14 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8
15 | github.com/frankban/quicktest v1.14.6
16 | github.com/stretchr/testify v1.7.0
17 | golang.org/x/net v0.47.0
18 | )
19 |
20 | require (
21 | github.com/anacrolix/missinggo/perf v1.0.0 // indirect
22 | github.com/davecgh/go-spew v1.1.1 // indirect
23 | github.com/dustin/go-humanize v1.0.0 // indirect
24 | github.com/google/go-cmp v0.6.0 // indirect
25 | github.com/huandu/xstrings v1.3.2 // indirect
26 | github.com/kr/pretty v0.3.1 // indirect
27 | github.com/kr/text v0.2.0 // indirect
28 | github.com/pkg/errors v0.9.1 // indirect
29 | github.com/pmezard/go-difflib v1.0.0 // indirect
30 | github.com/rogpeppe/go-internal v1.9.0 // indirect
31 | golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
32 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
33 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
34 | )
35 |
--------------------------------------------------------------------------------
/bench_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "io"
5 | "io/ioutil"
6 | "net"
7 | "testing"
8 |
9 | "github.com/anacrolix/missinggo"
10 | "github.com/bradfitz/iter"
11 | "github.com/stretchr/testify/require"
12 | )
13 |
14 | func benchmarkThroughput(t *testing.B, n int64) {
15 | s1, err := NewSocket("udp", "localhost:0")
16 | require.NoError(t, err)
17 | defer s1.Close()
18 | s2, err := NewSocket("udp", "localhost:0")
19 | require.NoError(t, err)
20 | defer s2.Close()
21 | var c2 net.Conn
22 | accepted := make(chan struct{})
23 | go func() {
24 | defer close(accepted)
25 | var err error
26 | c2, err = s2.Accept()
27 | require.NoError(t, err)
28 | }()
29 | c1, err := s1.Dial(s2.Addr().String())
30 | require.NoError(t, err)
31 | defer c1.Close()
32 | <-accepted
33 | defer c2.Close()
34 | t.SetBytes(n)
35 | t.ReportAllocs()
36 | for range iter.N(t.N) {
37 | doneReading := make(chan struct{})
38 | go func() {
39 | defer close(doneReading)
40 | wn, err := io.CopyN(ioutil.Discard, c2, n)
41 | require.NoError(t, err)
42 | require.EqualValues(t, n, wn)
43 | }()
44 | wn, err := io.CopyN(c1, missinggo.ZeroReader, n)
45 | require.NoError(t, err)
46 | require.EqualValues(t, n, wn)
47 | <-doneReading
48 | }
49 | }
50 |
51 | func BenchmarkThroughput100MB(t *testing.B) {
52 | benchmarkThroughput(t, 100<<20)
53 | }
54 |
55 | func BenchmarkThroughput10MB(t *testing.B) {
56 | benchmarkThroughput(t, 10<<20)
57 | }
58 |
59 | func BenchmarkThroughput1MB(t *testing.B) {
60 | benchmarkThroughput(t, 1<<20)
61 | }
62 |
--------------------------------------------------------------------------------
/utp_utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | uint64 utp_default_get_udp_mtu(utp_callback_arguments *args);
24 | uint64 utp_default_get_udp_overhead(utp_callback_arguments *args);
25 | uint64 utp_default_get_random(utp_callback_arguments *args);
26 | uint64 utp_default_get_milliseconds(utp_callback_arguments *args);
27 | uint64 utp_default_get_microseconds(utp_callback_arguments *args);
28 |
--------------------------------------------------------------------------------
/cmd/ucat/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io"
5 | "log"
6 | "net"
7 | "os"
8 | "time"
9 |
10 | _ "github.com/anacrolix/envpprof"
11 | "github.com/anacrolix/tagflag"
12 |
13 | "github.com/anacrolix/go-libutp"
14 | )
15 |
16 | func getConn(listen bool, addr string, s *utp.Socket) net.Conn {
17 | if listen {
18 | c, err := s.Accept()
19 | if err != nil {
20 | panic(err)
21 | }
22 | return c
23 | } else {
24 | c, err := s.Dial(addr)
25 | if err != nil {
26 | panic(err)
27 | }
28 | return c
29 | }
30 | }
31 |
32 | func main() {
33 | log.SetFlags(log.Lshortfile | log.Flags())
34 | flags := struct {
35 | Listen bool `name:"l"`
36 | tagflag.StartPos
37 | Addr string
38 | }{}
39 | tagflag.Parse(&flags)
40 | s, err := func() (*utp.Socket, error) {
41 | if flags.Listen {
42 | return utp.NewSocket("udp", flags.Addr)
43 | } else {
44 | return utp.NewSocket("udp", ":0")
45 | }
46 | }()
47 | if err != nil {
48 | log.Fatal(err)
49 | }
50 | defer s.Close()
51 | c, err := func() (net.Conn, error) {
52 | if flags.Listen {
53 | return s.Accept()
54 | } else {
55 | return s.Dial(flags.Addr)
56 | }
57 | }()
58 | if err != nil {
59 | log.Fatal(err)
60 | }
61 | doneReading := make(chan struct{})
62 | doneWriting := make(chan struct{})
63 | go func() {
64 | defer close(doneReading)
65 | n, err := io.Copy(os.Stdout, c)
66 | log.Printf("read %d bytes then got error: %v", n, err)
67 | }()
68 | go func() {
69 | defer close(doneWriting)
70 | n, err := io.Copy(c, os.Stdin)
71 | log.Printf("wrote %d bytes then got error: %v", n, err)
72 | }()
73 | select {
74 | case <-doneReading:
75 | case <-doneWriting:
76 | }
77 | c.Close()
78 | time.Sleep(time.Second)
79 | }
80 |
--------------------------------------------------------------------------------
/utp_packedsockaddr.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_PACKEDSOCKADDR_H__
24 | #define __UTP_PACKEDSOCKADDR_H__
25 |
26 | #include "utp_types.h"
27 |
28 | struct PACKED_ATTRIBUTE PackedSockAddr {
29 | // The values are always stored here in network byte order
30 | union {
31 | byte _in6[16]; // IPv6
32 | uint16 _in6w[8]; // IPv6, word based (for convenience)
33 | uint32 _in6d[4]; // Dword access
34 | in6_addr _in6addr; // For convenience
35 | } _in;
36 |
37 | // Host byte order
38 | uint16 _port;
39 |
40 | #define _sin4 _in._in6d[3] // IPv4 is stored where it goes if mapped
41 |
42 | #define _sin6 _in._in6
43 | #define _sin6w _in._in6w
44 | #define _sin6d _in._in6d
45 |
46 | byte get_family() const;
47 | bool operator==(const PackedSockAddr& rhs) const;
48 | bool operator!=(const PackedSockAddr& rhs) const;
49 | void set(const SOCKADDR_STORAGE* sa, socklen_t len);
50 |
51 | PackedSockAddr(const SOCKADDR_STORAGE* sa, socklen_t len);
52 | PackedSockAddr(void);
53 |
54 | SOCKADDR_STORAGE get_sockaddr_storage(socklen_t *len) const;
55 | cstr fmt(str s, size_t len) const;
56 |
57 | uint32 compute_hash() const;
58 | } ALIGNED_ATTRIBUTE(4);
59 |
60 | #endif //__UTP_PACKEDSOCKADDR_H__
61 |
--------------------------------------------------------------------------------
/prop_sheets/debug-2012.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | $(SolutionDir)Build\$(PlatformName)\$(Configuration)\
9 | $(OutDir)$(ProjectName)\
10 |
11 |
12 |
13 | Level3
14 | _DEBUG;WIN32;ENABLE_I18N;ENABLE_SRP=1;%(PreprocessorDefinitions)
15 | false
16 | false
17 | false
18 | Default
19 | $(SolutionDir);$(SolutionDir)\yajl\src;$(SolutionDir)\ut_core\src;$(SolutionDir)\verification_lib;$(SolutionDir)\..\libtomcrypt\src\headers
20 | true
21 | false
22 | false
23 | c:\temp\$(ProjectName)-$(ConfigurationName)-$(PlatformName)-master.pch
24 | true
25 | false
26 |
27 |
28 | true
29 |
30 |
31 | true
32 | false
33 |
34 |
35 | true
36 |
37 |
38 | BRANDED_UTORRENT;%(PreprocessorDefinitions)
39 |
40 |
41 |
42 | $(SolutionDir)\ut_core\src;%(AdditionalIncludeDirectories)
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/utp.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #cgo CPPFLAGS: -DPOSIX -DUTP_DEBUG_LOGGING=0
5 | #cgo CFLAGS: -Wall -O3
6 | // These are all copied from the libutp Makefile.
7 | #cgo CXXFLAGS: -Wall -O3 -fPIC -Wno-sign-compare
8 | // There are some variables that aren't used unless UTP_DEBUG_LOGGING is defined.
9 | #cgo CXXFLAGS: -Wno-unused-const-variable
10 | // Windows additional flags
11 | #cgo windows LDFLAGS: -lws2_32
12 | #cgo windows CXXFLAGS: -D_WIN32_WINNT=0x600
13 | #include "utp.h"
14 |
15 | uint64_t firewallCallback(utp_callback_arguments *);
16 | uint64_t errorCallback(utp_callback_arguments *);
17 | uint64_t logCallback(utp_callback_arguments *);
18 | uint64_t acceptCallback(utp_callback_arguments *);
19 | uint64_t sendtoCallback(utp_callback_arguments *);
20 | uint64_t stateChangeCallback(utp_callback_arguments *);
21 | uint64_t readCallback(utp_callback_arguments *);
22 | uint64_t getReadBufferSizeCallback(utp_callback_arguments *);
23 | */
24 | import "C"
25 | import "unsafe"
26 |
27 | type socklen C.socklen_t
28 |
29 | type utpContext C.utp_context
30 |
31 | func (ctx *utpContext) asCPtr() *C.utp_context {
32 | return (*C.utp_context)(ctx)
33 | }
34 |
35 | func (ctx *utpContext) setCallbacks() {
36 | C.utp_set_callback(ctx.asCPtr(), C.UTP_ON_FIREWALL, (*C.utp_callback_t)(C.firewallCallback))
37 | C.utp_set_callback(ctx.asCPtr(), C.UTP_LOG, (*C.utp_callback_t)(C.logCallback))
38 | C.utp_set_callback(ctx.asCPtr(), C.UTP_ON_ACCEPT, (*C.utp_callback_t)(C.acceptCallback))
39 | C.utp_set_callback(ctx.asCPtr(), C.UTP_SENDTO, (*C.utp_callback_t)(C.sendtoCallback))
40 | C.utp_set_callback(ctx.asCPtr(), C.UTP_ON_STATE_CHANGE, (*C.utp_callback_t)(C.stateChangeCallback))
41 | C.utp_set_callback(ctx.asCPtr(), C.UTP_ON_READ, (*C.utp_callback_t)(C.readCallback))
42 | C.utp_set_callback(ctx.asCPtr(), C.UTP_ON_ERROR, (*C.utp_callback_t)(C.errorCallback))
43 | C.utp_set_callback(ctx.asCPtr(), C.UTP_GET_READ_BUFFER_SIZE, (*C.utp_callback_t)(C.getReadBufferSizeCallback))
44 | }
45 |
46 | func (ctx *utpContext) setOption(opt Option, val int) int {
47 | return int(C.utp_context_set_option(ctx.asCPtr(), opt, C.int(val)))
48 | }
49 |
50 | func libStateName(state C.int) string {
51 | return C.GoString((*[5]*C.char)(unsafe.Pointer(&C.utp_state_names))[state])
52 | }
53 |
54 | func libErrorCodeNames(errorCode C.int) string {
55 | return C.GoString((*[3]*C.char)(unsafe.Pointer(&C.utp_error_code_names))[errorCode])
56 | }
57 |
--------------------------------------------------------------------------------
/libutp-2012.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 |
14 |
15 | Header Files
16 |
17 |
18 | Header Files
19 |
20 |
21 | Header Files
22 |
23 |
24 | Header Files
25 |
26 |
27 | Header Files
28 |
29 |
30 | Header Files
31 |
32 |
33 | Header Files
34 |
35 |
36 | Header Files
37 |
38 |
39 | Header Files
40 |
41 |
42 |
43 |
44 | Source Files
45 |
46 |
47 | Source Files
48 |
49 |
50 | Source Files
51 |
52 |
53 | Source Files
54 |
55 |
56 | Source Files
57 |
58 |
59 | Source Files
60 |
61 |
62 | Source Files
63 |
64 |
65 |
--------------------------------------------------------------------------------
/prop_sheets/release-2012.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | $(SolutionDir)Build\$(PlatformName)\$(Configuration)\
9 | $(OutDir)$(ProjectName)\
10 |
11 |
12 |
13 | Level3
14 | $(SolutionDir);$(SolutionDir)\yajl\src;$(SolutionDir)\ut_core\src;$(SolutionDir)\verification_lib;$(SolutionDir)\..\libtomcrypt\src\headers
15 | MinSpace
16 | AnySuitable
17 | true
18 | Size
19 | true
20 | true
21 | false
22 | false
23 | false
24 | true
25 | false
26 | true
27 | StdCall
28 | Default
29 | false
30 | NDEBUG;WIN32;ENABLE_I18N;ENABLE_SRP=1;%(PreprocessorDefinitions)
31 | false
32 | c:\temp\$(ProjectName)-$(ConfigurationName)-$(PlatformName)-master.pch
33 | true
34 |
35 |
36 |
37 | true
38 |
39 |
40 | BRANDED_UTORRENT;%(PreprocessorDefinitions)
41 |
42 |
43 |
44 | $(SolutionDir)\ut_core\src;%(AdditionalIncludeDirectories)
45 |
46 |
47 | false
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/sockaddr.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #include "utp.h"
5 | */
6 | import "C"
7 |
8 | import (
9 | "net"
10 | "strconv"
11 | "syscall"
12 | "unsafe"
13 |
14 | "github.com/anacrolix/missinggo/inproc"
15 | "github.com/anacrolix/missinggo/v2/panicif"
16 | )
17 |
18 | func toSockaddrInet(ip net.IP, port int, zone string) (rsa syscall.RawSockaddrAny, len C.socklen_t) {
19 | if ip4 := ip.To4(); ip4 != nil && zone == "" {
20 | rsa4 := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&rsa))
21 | rsa4.Family = syscall.AF_INET
22 | rsa4.Port = uint16(port)
23 | if n := copy(rsa4.Addr[:], ip4); n != 4 {
24 | panic(n)
25 | }
26 | len = C.socklen_t(unsafe.Sizeof(*rsa4))
27 | return
28 | }
29 | rsa6 := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&rsa))
30 | rsa6.Family = syscall.AF_INET6
31 | rsa6.Scope_id = zoneToScopeId(zone)
32 | rsa6.Port = uint16(port)
33 | if ip != nil {
34 | if n := copy(rsa6.Addr[:], ip); n != 16 {
35 | panic(n)
36 | }
37 | }
38 | len = C.socklen_t(unsafe.Sizeof(*rsa6))
39 | return
40 | }
41 |
42 | func zoneToScopeId(zone string) uint32 {
43 | if zone == "" {
44 | return 0
45 | }
46 | if ifi, err := net.InterfaceByName(zone); err == nil {
47 | return uint32(ifi.Index)
48 | }
49 | ui64, _ := strconv.ParseUint(zone, 10, 32)
50 | return uint32(ui64)
51 | }
52 |
53 | func structSockaddrToUDPAddr(sa *C.struct_sockaddr, udp *net.UDPAddr) error {
54 | return anySockaddrToUdp((*syscall.RawSockaddrAny)(unsafe.Pointer(sa)), udp)
55 | }
56 |
57 | func anySockaddrToUdp(rsa *syscall.RawSockaddrAny, udp *net.UDPAddr) error {
58 | switch rsa.Addr.Family {
59 | case syscall.AF_INET:
60 | sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
61 | udp.Port = int(sa.Port)
62 | udp.IP = append(udp.IP[:0], sa.Addr[:]...)
63 | return nil
64 | case syscall.AF_INET6:
65 | sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
66 | udp.Port = int(sa.Port)
67 | udp.IP = append(udp.IP[:0], sa.Addr[:]...)
68 | return nil
69 | default:
70 | return syscall.EAFNOSUPPORT
71 | }
72 | }
73 |
74 | func sockaddrToUDP(sa syscall.Sockaddr) net.Addr {
75 | switch sa := sa.(type) {
76 | case *syscall.SockaddrInet4:
77 | return &net.UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
78 | case *syscall.SockaddrInet6:
79 | return &net.UDPAddr{IP: sa.Addr[0:], Port: sa.Port /*Zone: zoneToString(int(sa.ZoneId))*/}
80 | }
81 | return nil
82 | }
83 |
84 | func netAddrToLibSockaddr(na net.Addr) (rsa syscall.RawSockaddrAny, len C.socklen_t) {
85 | switch v := na.(type) {
86 | case *net.UDPAddr:
87 | return toSockaddrInet(v.IP, v.Port, v.Zone)
88 | case inproc.Addr:
89 | rsa6 := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&rsa))
90 | u16Port := uint16(v.Port)
91 | panicif.NotEq(int(u16Port), v.Port)
92 | rsa6.Port = u16Port
93 | len = C.socklen_t(unsafe.Sizeof(rsa))
94 | return
95 | default:
96 | panic(na)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/utp_callbacks.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_CALLBACKS_H__
24 | #define __UTP_CALLBACKS_H__
25 |
26 | #include "utp.h"
27 | #include "utp_internal.h"
28 |
29 | // Generated by running: grep ^[a-z] utp_callbacks.cpp | sed 's/$/;/'
30 | int utp_call_on_firewall(utp_context *ctx, const struct sockaddr *address, socklen_t address_len);
31 | void utp_call_on_accept(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
32 | void utp_call_on_connect(utp_context *ctx, utp_socket *s);
33 | void utp_call_on_error(utp_context *ctx, utp_socket *s, int error_code);
34 | void utp_call_on_read(utp_context *ctx, utp_socket *s, const byte *buf, size_t len);
35 | void utp_call_on_overhead_statistics(utp_context *ctx, utp_socket *s, int send, size_t len, int type);
36 | void utp_call_on_delay_sample(utp_context *ctx, utp_socket *s, int sample_ms);
37 | void utp_call_on_state_change(utp_context *ctx, utp_socket *s, int state);
38 | uint16 utp_call_get_udp_mtu(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
39 | uint16 utp_call_get_udp_overhead(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
40 | uint64 utp_call_get_milliseconds(utp_context *ctx, utp_socket *s);
41 | uint64 utp_call_get_microseconds(utp_context *ctx, utp_socket *s);
42 | uint32 utp_call_get_random(utp_context *ctx, utp_socket *s);
43 | size_t utp_call_get_read_buffer_size(utp_context *ctx, utp_socket *s);
44 | void utp_call_log(utp_context *ctx, utp_socket *s, const byte *buf);
45 | void utp_call_sendto(utp_context *ctx, utp_socket *s, const byte *buf, size_t len, const struct sockaddr *address, socklen_t address_len, uint32 flags);
46 |
47 | #endif // __UTP_CALLBACKS_H__
48 |
--------------------------------------------------------------------------------
/libutp_inet_ntop.h:
--------------------------------------------------------------------------------
1 | #ifndef LIBUTP_INET_NTOP_H
2 | #define LIBUTP_INET_NTOP_H
3 |
4 | /*
5 | * Copyright (c) 2010-2013 BitTorrent, Inc.
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | // About us linking the system inet_pton and inet_ntop symbols:
27 | // 1) These symbols are usually defined on POSIX systems
28 | // 2) They are not defined on Windows versions earlier than Vista
29 | // Defined in:
30 | // ut_utils/src/sockaddr.cpp
31 | // libutp/win32_inet_ntop.obj
32 | //
33 | // When we drop support for XP we can just #include , and use the system functions
34 | // For now, we will always use our functions on windows, on all builds
35 | // The reason is: we would like the debug build to behave as much as the release build as possible
36 | // It is much better to catch a problem in the debug build, than to link the system version
37 | // in debug, and our version int he wild.
38 |
39 | #if defined(_WIN32_WINNT)
40 | #if _WIN32_WINNT >= 0x600 // Win32, post-XP
41 | #include // for inet_ntop, inet_pton
42 | #define INET_NTOP inet_ntop
43 | #define INET_PTON inet_pton
44 | #else
45 | #define INET_NTOP libutp::inet_ntop // Win32, pre-XP: Use ours
46 | #define INET_PTON libutp::inet_pton
47 | #endif
48 | #else // not WIN32
49 | #include // for inet_ntop, inet_pton
50 | #define INET_NTOP inet_ntop
51 | #define INET_PTON inet_pton
52 | #endif
53 |
54 | //######################################################################
55 | //######################################################################
56 | namespace libutp {
57 |
58 |
59 | //######################################################################
60 | const char *inet_ntop(int af, const void *src, char *dest, size_t length);
61 |
62 | //######################################################################
63 | int inet_pton(int af, const char* src, void* dest);
64 |
65 |
66 | } //namespace libutp
67 |
68 | #endif // LIBUTP_INET_NTOP_H
--------------------------------------------------------------------------------
/utp_packedsockaddr.cpp:
--------------------------------------------------------------------------------
1 | // vim:set ts=4 sw=4 ai:
2 |
3 | /*
4 | * Copyright (c) 2010-2013 BitTorrent, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | #include
26 | #include
27 | #include
28 |
29 | #include "utp_types.h"
30 | #include "utp_hash.h"
31 | #include "utp_packedsockaddr.h"
32 |
33 | #include "libutp_inet_ntop.h"
34 |
35 | byte PackedSockAddr::get_family() const
36 | {
37 | #if defined(__sh__)
38 | return ((_sin6d[0] == 0) && (_sin6d[1] == 0) && (_sin6d[2] == htonl(0xffff)) != 0) ?
39 | AF_INET : AF_INET6;
40 | #else
41 | return (IN6_IS_ADDR_V4MAPPED(&_in._in6addr) != 0) ? AF_INET : AF_INET6;
42 | #endif // defined(__sh__)
43 | }
44 |
45 | bool PackedSockAddr::operator==(const PackedSockAddr& rhs) const
46 | {
47 | if (&rhs == this)
48 | return true;
49 | if (_port != rhs._port)
50 | return false;
51 | return memcmp(_sin6, rhs._sin6, sizeof(_sin6)) == 0;
52 | }
53 |
54 | bool PackedSockAddr::operator!=(const PackedSockAddr& rhs) const
55 | {
56 | return !(*this == rhs);
57 | }
58 |
59 | uint32 PackedSockAddr::compute_hash() const {
60 | return utp_hash_mem(&_in, sizeof(_in)) ^ _port;
61 | }
62 |
63 | void PackedSockAddr::set(const SOCKADDR_STORAGE* sa, socklen_t len)
64 | {
65 | if (sa->ss_family == AF_INET) {
66 | assert(len >= sizeof(sockaddr_in));
67 | const sockaddr_in *sin = (sockaddr_in*)sa;
68 | _sin6w[0] = 0;
69 | _sin6w[1] = 0;
70 | _sin6w[2] = 0;
71 | _sin6w[3] = 0;
72 | _sin6w[4] = 0;
73 | _sin6w[5] = 0xffff;
74 | _sin4 = sin->sin_addr.s_addr;
75 | _port = ntohs(sin->sin_port);
76 | } else {
77 | assert(len >= sizeof(sockaddr_in6));
78 | const sockaddr_in6 *sin6 = (sockaddr_in6*)sa;
79 | _in._in6addr = sin6->sin6_addr;
80 | _port = ntohs(sin6->sin6_port);
81 | }
82 | }
83 |
84 | PackedSockAddr::PackedSockAddr(const SOCKADDR_STORAGE* sa, socklen_t len)
85 | {
86 | set(sa, len);
87 | }
88 |
89 | PackedSockAddr::PackedSockAddr(void)
90 | {
91 | SOCKADDR_STORAGE sa;
92 | socklen_t len = sizeof(SOCKADDR_STORAGE);
93 | memset(&sa, 0, len);
94 | sa.ss_family = AF_INET;
95 | set(&sa, len);
96 | }
97 |
98 | SOCKADDR_STORAGE PackedSockAddr::get_sockaddr_storage(socklen_t *len = NULL) const
99 | {
100 | SOCKADDR_STORAGE sa;
101 | const byte family = get_family();
102 | if (family == AF_INET) {
103 | sockaddr_in *sin = (sockaddr_in*)&sa;
104 | if (len) *len = sizeof(sockaddr_in);
105 | memset(sin, 0, sizeof(sockaddr_in));
106 | sin->sin_family = family;
107 | sin->sin_port = htons(_port);
108 | sin->sin_addr.s_addr = _sin4;
109 | } else {
110 | sockaddr_in6 *sin6 = (sockaddr_in6*)&sa;
111 | memset(sin6, 0, sizeof(sockaddr_in6));
112 | if (len) *len = sizeof(sockaddr_in6);
113 | sin6->sin6_family = family;
114 | sin6->sin6_addr = _in._in6addr;
115 | sin6->sin6_port = htons(_port);
116 | }
117 | return sa;
118 | }
119 |
120 | // #define addrfmt(x, s) x.fmt(s, sizeof(s))
121 | cstr PackedSockAddr::fmt(str s, size_t len) const
122 | {
123 | memset(s, 0, len);
124 | const byte family = get_family();
125 | str i;
126 | if (family == AF_INET) {
127 | INET_NTOP(family, (uint32*)&_sin4, s, len);
128 | i = s;
129 | while (*++i) {}
130 | } else {
131 | i = s;
132 | *i++ = '[';
133 | INET_NTOP(family, (in6_addr*)&_in._in6addr, i, len-1);
134 | while (*++i) {}
135 | *i++ = ']';
136 | }
137 | snprintf(i, len - (i-s), ":%u", _port);
138 | return s;
139 | }
140 |
--------------------------------------------------------------------------------
/utp_api.cpp:
--------------------------------------------------------------------------------
1 | // vim:set ts=4 sw=4 ai:
2 |
3 | /*
4 | * Copyright (c) 2010-2013 BitTorrent, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | #include
26 | #include "utp_internal.h"
27 | #include "utp_utils.h"
28 |
29 | extern "C" {
30 |
31 | const char * utp_callback_names[] = {
32 | "UTP_ON_FIREWALL",
33 | "UTP_ON_ACCEPT",
34 | "UTP_ON_CONNECT",
35 | "UTP_ON_ERROR",
36 | "UTP_ON_READ",
37 | "UTP_ON_OVERHEAD_STATISTICS",
38 | "UTP_ON_STATE_CHANGE",
39 | "UTP_GET_READ_BUFFER_SIZE",
40 | "UTP_ON_DELAY_SAMPLE",
41 | "UTP_GET_UDP_MTU",
42 | "UTP_GET_UDP_OVERHEAD",
43 | "UTP_GET_MILLISECONDS",
44 | "UTP_GET_MICROSECONDS",
45 | "UTP_GET_RANDOM",
46 | "UTP_LOG",
47 | "UTP_SENDTO",
48 | };
49 |
50 | const char * utp_error_code_names[] = {
51 | "UTP_ECONNREFUSED",
52 | "UTP_ECONNRESET",
53 | "UTP_ETIMEDOUT",
54 | };
55 |
56 | const char *utp_state_names[] = {
57 | NULL,
58 | "UTP_STATE_CONNECT",
59 | "UTP_STATE_WRITABLE",
60 | "UTP_STATE_EOF",
61 | "UTP_STATE_DESTROYING",
62 | };
63 |
64 | struct_utp_context::struct_utp_context()
65 | : userdata(NULL)
66 | , current_ms(0)
67 | , last_utp_socket(NULL)
68 | , log_normal(false)
69 | , log_mtu(false)
70 | , log_debug(false)
71 | {
72 | memset(&context_stats, 0, sizeof(context_stats));
73 | memset(callbacks, 0, sizeof(callbacks));
74 | target_delay = CCONTROL_TARGET;
75 | utp_sockets = new UTPSocketHT;
76 |
77 | callbacks[UTP_GET_UDP_MTU] = &utp_default_get_udp_mtu;
78 | callbacks[UTP_GET_UDP_OVERHEAD] = &utp_default_get_udp_overhead;
79 | callbacks[UTP_GET_MILLISECONDS] = &utp_default_get_milliseconds;
80 | callbacks[UTP_GET_MICROSECONDS] = &utp_default_get_microseconds;
81 | callbacks[UTP_GET_RANDOM] = &utp_default_get_random;
82 |
83 | // 1 MB of receive buffer (i.e. max bandwidth delay product)
84 | // means that from a peer with 200 ms RTT, we cannot receive
85 | // faster than 5 MB/s
86 | // from a peer with 10 ms RTT, we cannot receive faster than
87 | // 100 MB/s. This is assumed to be good enough, since bandwidth
88 | // often is proportional to RTT anyway
89 | // when setting a download rate limit, all sockets should have
90 | // their receive buffer set much lower, to say 60 kiB or so
91 | opt_rcvbuf = opt_sndbuf = 1024 * 1024;
92 | last_check = 0;
93 | }
94 |
95 | struct_utp_context::~struct_utp_context() {
96 | delete this->utp_sockets;
97 | }
98 |
99 | utp_context* utp_init (int version)
100 | {
101 | assert(version == 2);
102 | if (version != 2)
103 | return NULL;
104 | utp_context *ctx = new utp_context;
105 | return ctx;
106 | }
107 |
108 | void utp_destroy(utp_context *ctx) {
109 | assert(ctx);
110 | if (ctx) delete ctx;
111 | }
112 |
113 | void utp_set_callback(utp_context *ctx, int callback_name, utp_callback_t *proc) {
114 | assert(ctx);
115 | if (ctx) ctx->callbacks[callback_name] = proc;
116 | }
117 |
118 | void* utp_context_set_userdata(utp_context *ctx, void *userdata) {
119 | assert(ctx);
120 | if (ctx) ctx->userdata = userdata;
121 | return ctx ? ctx->userdata : NULL;
122 | }
123 |
124 | void* utp_context_get_userdata(utp_context *ctx) {
125 | assert(ctx);
126 | return ctx ? ctx->userdata : NULL;
127 | }
128 |
129 | utp_context_stats* utp_get_context_stats(utp_context *ctx) {
130 | assert(ctx);
131 | return ctx ? &ctx->context_stats : NULL;
132 | }
133 |
134 | ssize_t utp_write(utp_socket *socket, void *buf, size_t len) {
135 | struct utp_iovec iovec = { buf, len };
136 | return utp_writev(socket, &iovec, 1);
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/utp_internal.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_INTERNAL_H__
24 | #define __UTP_INTERNAL_H__
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #include "utp.h"
32 | #include "utp_callbacks.h"
33 | #include "utp_templates.h"
34 | #include "utp_hash.h"
35 | #include "utp_hash.h"
36 | #include "utp_packedsockaddr.h"
37 |
38 | /* These originally lived in utp_config.h */
39 | #define CCONTROL_TARGET (100 * 1000) // us
40 |
41 | enum bandwidth_type_t {
42 | payload_bandwidth, connect_overhead,
43 | close_overhead, ack_overhead,
44 | header_overhead, retransmit_overhead
45 | };
46 |
47 | #ifdef WIN32
48 | #ifdef _MSC_VER
49 | #include "libutp_inet_ntop.h"
50 | #endif
51 |
52 | // newer versions of MSVC define these in errno.h
53 | #ifndef ECONNRESET
54 | #define ECONNRESET WSAECONNRESET
55 | #define EMSGSIZE WSAEMSGSIZE
56 | #define ECONNREFUSED WSAECONNREFUSED
57 | #define ETIMEDOUT WSAETIMEDOUT
58 | #endif
59 | #endif
60 |
61 | struct PACKED_ATTRIBUTE RST_Info {
62 | PackedSockAddr addr;
63 | uint32 connid;
64 | uint16 ack_nr;
65 | uint64 timestamp;
66 | };
67 |
68 | // It's really important that we don't have duplicate keys in the hash table.
69 | // If we do, we'll eventually crash. if we try to remove the second instance
70 | // of the key, we'll accidentally remove the first instead. then later,
71 | // checkTimeouts will try to access the second one's already freed memory.
72 | void UTP_FreeAll(struct UTPSocketHT *utp_sockets);
73 |
74 | struct UTPSocketKey {
75 | PackedSockAddr addr;
76 | uint32 recv_id; // "conn_seed", "conn_id"
77 |
78 | UTPSocketKey(const PackedSockAddr& _addr, uint32 _recv_id) {
79 | memset((void*)this, 0, sizeof(*this));
80 | addr = _addr;
81 | recv_id = _recv_id;
82 | }
83 |
84 | bool operator == (const UTPSocketKey &other) const {
85 | return recv_id == other.recv_id && addr == other.addr;
86 | }
87 |
88 | uint32 compute_hash() const {
89 | return recv_id ^ addr.compute_hash();
90 | }
91 | };
92 |
93 | struct UTPSocketKeyData {
94 | UTPSocketKey key;
95 | UTPSocket *socket;
96 | utp_link_t link;
97 | };
98 |
99 | #define UTP_SOCKET_BUCKETS 79
100 | #define UTP_SOCKET_INIT 15
101 |
102 | struct UTPSocketHT : utpHashTable {
103 | UTPSocketHT() {
104 | const int buckets = UTP_SOCKET_BUCKETS;
105 | const int initial = UTP_SOCKET_INIT;
106 | this->Create(buckets, initial);
107 | }
108 | ~UTPSocketHT() {
109 | UTP_FreeAll(this);
110 | this->Free();
111 | }
112 | };
113 |
114 | struct struct_utp_context {
115 | void *userdata;
116 | utp_callback_t* callbacks[UTP_ARRAY_SIZE];
117 |
118 | uint64 current_ms;
119 | utp_context_stats context_stats;
120 | UTPSocket *last_utp_socket;
121 | Array ack_sockets;
122 | Array rst_info;
123 | UTPSocketHT *utp_sockets;
124 | size_t target_delay;
125 | size_t opt_sndbuf;
126 | size_t opt_rcvbuf;
127 | uint64 last_check;
128 |
129 | struct_utp_context();
130 | ~struct_utp_context();
131 |
132 | void log(int level, utp_socket *socket, char const *fmt, ...);
133 | void log_unchecked(utp_socket *socket, char const *fmt, ...);
134 | bool would_log(int level);
135 |
136 | bool log_normal:1; // log normal events?
137 | bool log_mtu:1; // log MTU related events?
138 | bool log_debug:1; // log debugging events? (Must also compile with UTP_DEBUG_LOGGING defined)
139 | };
140 |
141 | #endif //__UTP_INTERNAL_H__
142 |
--------------------------------------------------------------------------------
/utp_types.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_TYPES_H__
24 | #define __UTP_TYPES_H__
25 |
26 | // Allow libutp consumers or prerequisites to override PACKED_ATTRIBUTE
27 | #ifndef PACKED_ATTRIBUTE
28 | #if defined BROKEN_GCC_STRUCTURE_PACKING && defined __GNUC__
29 | // Used for gcc tool chains accepting but not supporting pragma pack
30 | // See http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html
31 | #define PACKED_ATTRIBUTE __attribute__((__packed__))
32 | #else
33 | #define PACKED_ATTRIBUTE
34 | #endif // defined BROKEN_GCC_STRUCTURE_PACKING && defined __GNUC__
35 | #endif // ndef PACKED_ATTRIBUTE
36 |
37 | #ifdef __GNUC__
38 | #define ALIGNED_ATTRIBUTE(x) __attribute__((aligned(x)))
39 | #else
40 | #define ALIGNED_ATTRIBUTE(x)
41 | #endif
42 |
43 | // hash.cpp needs socket definitions, which is why this networking specific
44 | // code is inclued in utypes.h
45 | #ifdef WIN32
46 | #define _CRT_SECURE_NO_DEPRECATE
47 | #define WIN32_LEAN_AND_MEAN
48 | #include
49 | #include
50 | #include
51 | #define IP_OPT_DONTFRAG IP_DONTFRAGMENT
52 | #define SHUT_RD SD_RECEIVE
53 | #define SHUT_WR SD_SEND
54 | #define SHUT_RDWR SD_BOTH
55 | #else
56 | #include
57 | #include
58 | #include
59 | #include
60 |
61 | #ifdef IP_DONTFRAG
62 | #define IP_OPT_DONTFRAG IP_DONTFRAG
63 | #elif defined IP_DONTFRAGMENT
64 | #define IP_OPT_DONTFRAG IP_DONTFRAGMENT
65 | #else
66 | // #warning "I don't know how to set DF bit on this system"
67 | #endif
68 | #endif
69 |
70 | #ifdef _MSC_VER
71 | #include
72 | typedef SSIZE_T ssize_t;
73 | #endif
74 |
75 | #ifdef POSIX
76 | typedef struct sockaddr_storage SOCKADDR_STORAGE;
77 | #endif
78 |
79 | #ifdef WIN32
80 | #define I64u "%I64u"
81 | #else
82 | #define I64u "%Lu"
83 | #endif
84 |
85 | // Use standard integer and boolean types where possible.
86 | #include
87 |
88 | // Prefer stdbool for boolean type in C; C++ has built-in bool.
89 | #if !defined(__cplusplus)
90 | #include
91 | #endif
92 |
93 | // standard types: define short aliases only if not already provided.
94 | #ifndef byte
95 | typedef unsigned char byte;
96 | #endif
97 |
98 | #ifndef uint8
99 | typedef uint8_t uint8;
100 | #endif
101 |
102 | #ifndef int8
103 | typedef int8_t int8;
104 | #endif
105 |
106 | #ifndef uint16
107 | typedef uint16_t uint16;
108 | #endif
109 |
110 | #ifndef int16
111 | typedef int16_t int16;
112 | #endif
113 |
114 | #ifndef uint
115 | typedef unsigned int uint;
116 | #endif
117 |
118 | #ifndef uint32
119 | typedef uint32_t uint32;
120 | #endif
121 |
122 | #ifndef int32
123 | typedef int32_t int32;
124 | #endif
125 |
126 | #ifndef uint64
127 | typedef uint64_t uint64;
128 | #endif
129 |
130 | #ifndef int64
131 | typedef int64_t int64;
132 | #endif
133 |
134 | /* compile-time assert */
135 | #ifndef CASSERT
136 | #define CASSERT(exp, name) typedef int is_not_##name[(exp) ? 1 : -1];
137 | #endif
138 |
139 | CASSERT(8 == sizeof(uint64), sizeof_uint64_is_8)
140 | CASSERT(8 == sizeof(int64), sizeof_int64_is_8)
141 |
142 | #ifndef INT64_MAX
143 | #define INT64_MAX 0x7fffffffffffffffLL
144 | #endif
145 |
146 | // always ANSI
147 | typedef const char *cstr;
148 | typedef char *str;
149 |
150 | /* No explicit typedef for `bool` here: we include above for C
151 | and rely on built-in `bool` in C++. Guarding typedefs for `bool` would
152 | ` clash with platforms that already provide it. */
153 |
154 | #endif //__UTP_TYPES_H__
155 |
--------------------------------------------------------------------------------
/utp_test.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | import (
4 | "context"
5 | "math"
6 | "net"
7 | "sync"
8 | "testing"
9 | "testing/quick"
10 | "time"
11 |
12 | _ "github.com/anacrolix/envpprof"
13 | "github.com/stretchr/testify/assert"
14 | "github.com/stretchr/testify/require"
15 | "golang.org/x/net/nettest"
16 | )
17 |
18 | func doNettestTestConn(t *testing.T, swapConns bool, host string) {
19 | nettest.TestConn(t, func() (c1, c2 net.Conn, stop func(), err error) {
20 | s, err := NewSocket("inproc", net.JoinHostPort(host, "0"))
21 | if err != nil {
22 | return
23 | }
24 | c1, c2 = connPairSocket(s)
25 | if swapConns {
26 | c1, c2 = c2, c1
27 | }
28 | stop = func() {
29 | s.Close()
30 | }
31 | return
32 | })
33 | }
34 |
35 | func TestNettestTestConn(t *testing.T) {
36 | doNettestTestConn(t, false, "127.0.0.1")
37 | }
38 |
39 | func TestNettestTestConnIp6(t *testing.T) {
40 | doNettestTestConn(t, false, "::1")
41 | }
42 |
43 | func TestNettestTestConnSwapped(t *testing.T) {
44 | doNettestTestConn(t, true, "127.0.0.1")
45 | }
46 |
47 | func TestNettestTestConnSwappedIp6(t *testing.T) {
48 | doNettestTestConn(t, true, "::1")
49 | }
50 |
51 | func connPairSocket(s *Socket) (dialed net.Conn, accepted net.Conn) {
52 | var wg sync.WaitGroup
53 | wg.Add(2)
54 | go func() {
55 | defer wg.Done()
56 | var err error
57 | dialed, err = s.Dial(s.Addr().String())
58 | if err != nil {
59 | panic(err)
60 | }
61 | }()
62 | go func() {
63 | defer wg.Done()
64 | var err error
65 | accepted, err = s.Accept()
66 | if err != nil {
67 | panic(err)
68 | }
69 | }()
70 | wg.Wait()
71 | return
72 | }
73 |
74 | const neverResponds = "localhost:1"
75 |
76 | // Ensure that libutp dial timeouts out by itself.
77 | func TestLibutpDialTimesOut(t *testing.T) {
78 | t.Parallel()
79 | if testing.Short() {
80 | t.SkipNow()
81 | }
82 | s, err := NewSocket("udp", "localhost:0")
83 | require.NoError(t, err)
84 | defer s.Close()
85 | _, err = s.Dial(neverResponds)
86 | require.Error(t, err)
87 | }
88 |
89 | // Ensure that our timeout is honored during dialing.
90 | func TestDialTimeout(t *testing.T) {
91 | t.Parallel()
92 | s, err := NewSocket("udp", "localhost:0")
93 | require.NoError(t, err)
94 | defer s.Close()
95 | const timeout = time.Second
96 | started := time.Now()
97 | _, err = s.DialTimeout(neverResponds, timeout)
98 | timeTaken := time.Since(started)
99 | t.Logf("dial returned after %s", timeTaken)
100 | assert.Equal(t, context.DeadlineExceeded, err)
101 | assert.True(t, timeTaken >= timeout)
102 | }
103 |
104 | func TestConnSendBuffer(t *testing.T) {
105 | s0, err := NewSocket("udp", "localhost:0")
106 | require.NoError(t, err)
107 | defer s0.Close()
108 | s1, err := NewSocket("udp", "localhost:0")
109 | require.NoError(t, err)
110 | defer s1.Close()
111 | var (
112 | c1 net.Conn
113 | acceptErr error
114 | accepted = make(chan struct{})
115 | )
116 | go func() {
117 | defer close(accepted)
118 | c1, acceptErr = s1.Accept()
119 | }()
120 | c0, err := s0.Dial(s1.Addr().String())
121 | require.NoError(t, err)
122 | <-accepted
123 | require.NoError(t, acceptErr)
124 | defer c0.Close()
125 | defer c1.Close()
126 | buf := make([]byte, 1024)
127 | written := 0
128 | for {
129 | require.NoError(t, c0.SetWriteDeadline(time.Now().Add(time.Second)))
130 | n, err := c0.Write(buf)
131 | written += n
132 | if err != nil {
133 | t.Logf("stopped writing after error: %s", err)
134 | break
135 | }
136 | }
137 | t.Logf("write buffered %d bytes", written)
138 | }
139 |
140 | func TestCanHandleConnectWriteErrors(t *testing.T) {
141 | t.Parallel()
142 | s, err := NewSocket("udp", "localhost:0")
143 | require.NoError(t, err)
144 | defer s.Close()
145 | _, err = s.DialContext(context.Background(), "", "localhost:0")
146 | require.Error(t, err)
147 | }
148 |
149 | func TestConnectConnAfterSocketClose(t *testing.T) {
150 | s, err := NewSocket("udp", "localhost:0")
151 | require.NoError(t, err)
152 | s.Close()
153 | _, err = s.DialContext(context.Background(), "", "")
154 | require.Equal(t, errSocketClosed, err)
155 | }
156 |
157 | func assertSocketConnsLen(t *testing.T, s *Socket, l int) {
158 | mu.Lock()
159 | for len(s.conns) != l {
160 | s.logger.Printf("%v has %v conns (waiting for %v)", s, len(s.conns), l)
161 | mu.Unlock()
162 | time.Sleep(time.Second)
163 | mu.Lock()
164 | }
165 | mu.Unlock()
166 | }
167 |
168 | func TestSocketConnsAfterConnClosed(t *testing.T) {
169 | s, err := NewSocket("udp", "localhost:0")
170 | require.NoError(t, err)
171 | defer s.Close()
172 | c, err := s.DialContext(context.Background(), "", s.LocalAddr().String())
173 | t.Logf("connecting to own socket: %v", err)
174 | if err == nil {
175 | c.Close()
176 | go func() {
177 | c, err := s.Accept()
178 | s.logger.Printf("accepted: %v", err)
179 | c.Close()
180 | }()
181 | }
182 | assertSocketConnsLen(t, s, 0)
183 | }
184 |
185 | // Ensure that adding math.MaxInt64 to any current timestamp will result in the maximum "when" field
186 | // for a Timer.
187 | func TestMaxExpiryTimerMath(t *testing.T) {
188 | quick.Check(func(i int64) bool {
189 | i += math.MaxInt64
190 | return i == math.MaxInt64 || i < 0
191 | }, nil)
192 | }
193 |
--------------------------------------------------------------------------------
/callbacks.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #include "utp.h"
5 | */
6 | import "C"
7 |
8 | import (
9 | "net"
10 | "reflect"
11 | "strings"
12 | "sync/atomic"
13 | "unsafe"
14 |
15 | "github.com/anacrolix/log"
16 | )
17 |
18 | type utpCallbackArguments C.utp_callback_arguments
19 |
20 | func (a *utpCallbackArguments) goContext() *utpContext {
21 | return (*utpContext)(a.context)
22 | }
23 |
24 | func (a *utpCallbackArguments) bufBytes() []byte {
25 | return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
26 | uintptr(unsafe.Pointer(a.buf)),
27 | int(a.len),
28 | int(a.len),
29 | }))
30 | }
31 |
32 | func (a *utpCallbackArguments) state() C.int {
33 | return *(*C.int)(unsafe.Pointer(&a.anon0))
34 | }
35 |
36 | func (a *utpCallbackArguments) error_code() C.int {
37 | return *(*C.int)(unsafe.Pointer(&a.anon0))
38 | }
39 |
40 | func (a *utpCallbackArguments) address() *C.struct_sockaddr {
41 | return *(**C.struct_sockaddr)(unsafe.Pointer(&a.anon0[0]))
42 | }
43 |
44 | func (a *utpCallbackArguments) addressLen() C.socklen_t {
45 | return *(*C.socklen_t)(unsafe.Pointer(&a.anon1[0]))
46 | }
47 |
48 | var sends int64
49 |
50 | //export sendtoCallback
51 | func sendtoCallback(a *utpCallbackArguments) (ret C.uint64) {
52 | s := getSocketForLibContext(a.goContext())
53 | b := a.bufBytes()
54 | var sendToUdpAddr net.UDPAddr
55 | if err := structSockaddrToUDPAddr(a.address(), &sendToUdpAddr); err != nil {
56 | panic(err)
57 | }
58 | newSends := atomic.AddInt64(&sends, 1)
59 | if logCallbacks {
60 | s.logger.Printf("sending %d bytes, %d packets", len(b), newSends)
61 | }
62 | expMap.Add("socket PacketConn writes", 1)
63 | n, err := s.pc.WriteTo(b, &sendToUdpAddr)
64 | c := s.conns[a.socket]
65 | if err != nil {
66 | expMap.Add("socket PacketConn write errors", 1)
67 | if c != nil && c.userOnError != nil {
68 | go c.userOnError(err)
69 | } else if c != nil &&
70 | (strings.Contains(err.Error(), "can't assign requested address") ||
71 | strings.Contains(err.Error(), "invalid argument")) {
72 | // Should be an bad argument or network configuration problem we
73 | // can't recover from.
74 | c.onError(err)
75 | } else if c != nil && strings.Contains(err.Error(), "operation not permitted") {
76 | // Rate-limited. Probably Linux. The implementation might try
77 | // again later.
78 | } else {
79 | s.logger.Levelf(log.Debug, "error sending packet: %v", err)
80 | }
81 | return
82 | }
83 | if n != len(b) {
84 | expMap.Add("socket PacketConn short writes", 1)
85 | s.logger.Printf("expected to send %d bytes but only sent %d", len(b), n)
86 | }
87 | return
88 | }
89 |
90 | //export errorCallback
91 | func errorCallback(a *utpCallbackArguments) C.uint64 {
92 | s := getSocketForLibContext(a.goContext())
93 | err := errorForCode(a.error_code())
94 | if logCallbacks {
95 | s.logger.Printf("error callback: socket %p: %s", a.socket, err)
96 | }
97 | libContextToSocket[a.goContext()].conns[a.socket].onError(err)
98 | return 0
99 | }
100 |
101 | //export logCallback
102 | func logCallback(a *utpCallbackArguments) C.uint64 {
103 | s := getSocketForLibContext(a.goContext())
104 | s.logger.Printf("libutp: %s", C.GoString((*C.char)(unsafe.Pointer(a.buf))))
105 | return 0
106 | }
107 |
108 | //export stateChangeCallback
109 | func stateChangeCallback(a *utpCallbackArguments) C.uint64 {
110 | s := libContextToSocket[a.goContext()]
111 | c := s.conns[a.socket]
112 | if logCallbacks {
113 | s.logger.Printf("state changed: conn %p: %s", c, libStateName(a.state()))
114 | }
115 | switch a.state() {
116 | case C.UTP_STATE_CONNECT:
117 | c.setConnected()
118 | // A dialled connection will not tell the remote it's ready until it
119 | // writes. If the dialer has no intention of writing, this will stall
120 | // everything. We do an empty write to get things rolling again. This
121 | // circumstance occurs when c1 in the RacyRead nettest is the dialer.
122 | C.utp_write(a.socket, nil, 0)
123 | case C.UTP_STATE_WRITABLE:
124 | c.cond.Broadcast()
125 | case C.UTP_STATE_EOF:
126 | c.setGotEOF()
127 | case C.UTP_STATE_DESTROYING:
128 | c.onDestroyed()
129 | s.onLibSocketDestroyed(a.socket)
130 | default:
131 | panic(a.state)
132 | }
133 | return 0
134 | }
135 |
136 | //export readCallback
137 | func readCallback(a *utpCallbackArguments) C.uint64 {
138 | s := libContextToSocket[a.goContext()]
139 | c := s.conns[a.socket]
140 | b := a.bufBytes()
141 | if logCallbacks {
142 | s.logger.Printf("read callback: conn %p: %d bytes", c, len(b))
143 | }
144 | if len(b) == 0 {
145 | panic("that will break the read drain invariant")
146 | }
147 | c.readBuf.Write(b)
148 | c.cond.Broadcast()
149 | return 0
150 | }
151 |
152 | //export acceptCallback
153 | func acceptCallback(a *utpCallbackArguments) C.uint64 {
154 | s := getSocketForLibContext(a.goContext())
155 | if logCallbacks {
156 | s.logger.Printf("accept callback: %#v", *a)
157 | }
158 | c := s.newConn(a.socket)
159 | c.setRemoteAddr()
160 | s.pushBacklog(c)
161 | return 0
162 | }
163 |
164 | //export getReadBufferSizeCallback
165 | func getReadBufferSizeCallback(a *utpCallbackArguments) (ret C.uint64) {
166 | s := libContextToSocket[a.goContext()]
167 | c := s.conns[a.socket]
168 | if c == nil {
169 | // socket hasn't been added to the Socket.conns yet. The read buffer
170 | // starts out empty, and the default implementation for this callback
171 | // returns 0, so we'll return that.
172 | return 0
173 | }
174 | ret = C.uint64(c.readBuf.Len())
175 | return
176 | }
177 |
178 | //export firewallCallback
179 | func firewallCallback(a *utpCallbackArguments) C.uint64 {
180 | s := getSocketForLibContext(a.goContext())
181 | if s.syncFirewallCallback != nil {
182 | var addr net.UDPAddr
183 | structSockaddrToUDPAddr(a.address(), &addr)
184 | if s.syncFirewallCallback(&addr) {
185 | return 1
186 | }
187 | } else if s.asyncBlock {
188 | return 1
189 | }
190 | return 0
191 | }
192 |
--------------------------------------------------------------------------------
/utp_hash.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_HASH_H__
24 | #define __UTP_HASH_H__
25 |
26 | #include // memset
27 | #include // malloc
28 |
29 | #include "utp_types.h"
30 | #include "utp_templates.h"
31 |
32 | // TODO: make utp_link_t a template parameter to HashTable
33 | typedef uint32 utp_link_t;
34 |
35 | #ifdef _MSC_VER
36 | // Silence the warning about the C99-compliant zero-length array at the end of the structure
37 | #pragma warning (disable: 4200)
38 | #endif
39 |
40 | typedef uint32 (*utp_hash_compute_t)(const void *keyp, size_t keysize);
41 | typedef uint (*utp_hash_equal_t)(const void *key_a, const void *key_b, size_t keysize);
42 |
43 | // In memory the HashTable is laid out as follows:
44 | // ---------------------------- low
45 | // | hash table data members |
46 | // ---------------------------- _
47 | // | indices | ^
48 | // | . | | utp_link_t indices into the key-values.
49 | // | . | .
50 | // ---------------------------- - <----- bep
51 | // | keys and values | each key-value pair has size total_size
52 | // | . |
53 | // | . |
54 | // ---------------------------- high
55 | //
56 | // The code depends on the ability of the compiler to pad the length
57 | // of the hash table data members structure to
58 | // a length divisible by 32-bits with no remainder.
59 | //
60 | // Since the number of hash buckets (indices) should be odd, the code
61 | // asserts this and adds one to the hash bucket count to ensure that the
62 | // following key-value pairs array starts on a 32-bit boundary.
63 | //
64 | // The key-value pairs array should start on a 32-bit boundary, otherwise
65 | // processors like the ARM will silently mangle 32-bit data in these structures
66 | // (e.g., turning 0xABCD into 0XCDAB when moving a value from memory to register
67 | // when the memory address is 16 bits offset from a 32-bit boundary),
68 | // also, the value will be stored at an address two bytes lower than the address
69 | // value would ordinarily indicate.
70 | //
71 | // The key-value pair is of type T. The first field in T must
72 | // be the key, i.e., the first K bytes of T contains the key.
73 | // total_size = sizeof(T) and thus sizeof(T) >= sizeof(K)
74 | //
75 | // N is the number of buckets.
76 | //
77 | struct utp_hash_t {
78 | utp_link_t N;
79 | byte K;
80 | byte E;
81 | size_t count;
82 | utp_hash_compute_t hash_compute;
83 | utp_hash_equal_t hash_equal;
84 | utp_link_t allocated;
85 | utp_link_t used;
86 | utp_link_t free;
87 | utp_link_t inits[0];
88 | };
89 |
90 | #ifdef _MSC_VER
91 | #pragma warning (default: 4200)
92 | #endif
93 |
94 | struct utp_hash_iterator_t {
95 | utp_link_t bucket;
96 | utp_link_t elem;
97 |
98 | utp_hash_iterator_t() : bucket(0xffffffff), elem(0xffffffff) {}
99 | };
100 |
101 | uint utp_hash_mem(const void *keyp, size_t keysize);
102 | uint utp_hash_comp(const void *key_a, const void *key_b, size_t keysize);
103 |
104 | utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun = utp_hash_mem, utp_hash_equal_t eqfun = NULL);
105 | void *utp_hash_lookup(utp_hash_t *hash, const void *key);
106 | void *utp_hash_add(utp_hash_t **hashp, const void *key);
107 | void *utp_hash_del(utp_hash_t *hash, const void *key);
108 |
109 | void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter);
110 | void utp_hash_free_mem(utp_hash_t *hash);
111 |
112 | /*
113 | This HashTable requires that T have at least sizeof(K)+sizeof(utp_link_t) bytes.
114 | Usually done like this:
115 |
116 | struct K {
117 | int whatever;
118 | };
119 |
120 | struct T {
121 | K wtf;
122 | utp_link_t link; // also wtf
123 | };
124 | */
125 |
126 | template class utpHashTable {
127 | utp_hash_t *hash;
128 | public:
129 | static uint compare(const void *k1, const void *k2, size_t ks) {
130 | return *((K*)k1) == *((K*)k2);
131 | }
132 | static uint32 compute_hash(const void *k, size_t ks) {
133 | return ((K*)k)->compute_hash();
134 | }
135 | void Init() { hash = NULL; }
136 | bool Allocated() { return (hash != NULL); }
137 | void Free() { utp_hash_free_mem(hash); hash = NULL; }
138 | void Create(int N, int initial) { hash = utp_hash_create(N, sizeof(K), sizeof(T), initial, &compute_hash, &compare); }
139 | T *Lookup(const K &key) { return (T*)utp_hash_lookup(hash, &key); }
140 | T *Add(const K &key) { return (T*)utp_hash_add(&hash, &key); }
141 | T *Delete(const K &key) { return (T*)utp_hash_del(hash, &key); }
142 | T *Iterate(utp_hash_iterator_t &iterator) { return (T*)utp_hash_iterate(hash, &iterator); }
143 | size_t GetCount() { return hash->count; }
144 | };
145 |
146 | #endif //__UTP_HASH_H__
147 |
--------------------------------------------------------------------------------
/utp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __UTP_H__
24 | #define __UTP_H__
25 |
26 | #ifdef __cplusplus
27 | extern "C" {
28 | #endif
29 |
30 | #ifdef _WIN32
31 | #include
32 | #endif
33 |
34 | #include
35 | #include "utp_types.h"
36 |
37 | typedef struct UTPSocket utp_socket;
38 | typedef struct struct_utp_context utp_context;
39 |
40 | enum {
41 | UTP_UDP_DONTFRAG = 2, // Used to be a #define as UDP_IP_DONTFRAG
42 | };
43 |
44 | enum {
45 | // socket has reveived syn-ack (notification only for outgoing connection completion)
46 | // this implies writability
47 | UTP_STATE_CONNECT = 1,
48 |
49 | // socket is able to send more data
50 | UTP_STATE_WRITABLE = 2,
51 |
52 | // connection closed
53 | UTP_STATE_EOF = 3,
54 |
55 | // socket is being destroyed, meaning all data has been sent if possible.
56 | // it is not valid to refer to the socket after this state change occurs
57 | UTP_STATE_DESTROYING = 4,
58 | };
59 |
60 | extern const char *utp_state_names[];
61 |
62 | // Errors codes that can be passed to UTP_ON_ERROR callback
63 | enum {
64 | UTP_ECONNREFUSED = 0,
65 | UTP_ECONNRESET,
66 | UTP_ETIMEDOUT,
67 | };
68 |
69 | extern const char *utp_error_code_names[];
70 |
71 | enum {
72 | // callback names
73 | UTP_ON_FIREWALL = 0,
74 | UTP_ON_ACCEPT,
75 | UTP_ON_CONNECT,
76 | UTP_ON_ERROR,
77 | UTP_ON_READ,
78 | UTP_ON_OVERHEAD_STATISTICS,
79 | UTP_ON_STATE_CHANGE,
80 | UTP_GET_READ_BUFFER_SIZE,
81 | UTP_ON_DELAY_SAMPLE,
82 | UTP_GET_UDP_MTU,
83 | UTP_GET_UDP_OVERHEAD,
84 | UTP_GET_MILLISECONDS,
85 | UTP_GET_MICROSECONDS,
86 | UTP_GET_RANDOM,
87 | UTP_LOG,
88 | UTP_SENDTO,
89 |
90 | // context and socket options that may be set/queried
91 | UTP_LOG_NORMAL,
92 | UTP_LOG_MTU,
93 | UTP_LOG_DEBUG,
94 | UTP_SNDBUF,
95 | UTP_RCVBUF,
96 | UTP_TARGET_DELAY,
97 |
98 | UTP_ARRAY_SIZE, // must be last
99 | };
100 |
101 | extern const char *utp_callback_names[];
102 |
103 | typedef struct {
104 | utp_context *context;
105 | utp_socket *socket;
106 | size_t len;
107 | uint32 flags;
108 | int callback_type;
109 | const byte *buf;
110 |
111 | union {
112 | const struct sockaddr *address;
113 | int send;
114 | int sample_ms;
115 | int error_code;
116 | int state;
117 | };
118 |
119 | union {
120 | socklen_t address_len;
121 | int type;
122 | };
123 | } utp_callback_arguments;
124 |
125 | typedef uint64 utp_callback_t(utp_callback_arguments *);
126 |
127 | // Returned by utp_get_context_stats()
128 | typedef struct {
129 | uint32 _nraw_recv[5]; // total packets recieved less than 300/600/1200/MTU bytes fpr all connections (context-wide)
130 | uint32 _nraw_send[5]; // total packets sent less than 300/600/1200/MTU bytes for all connections (context-wide)
131 | } utp_context_stats;
132 |
133 | // Returned by utp_get_stats()
134 | typedef struct {
135 | uint64 nbytes_recv; // total bytes received
136 | uint64 nbytes_xmit; // total bytes transmitted
137 | uint32 rexmit; // retransmit counter
138 | uint32 fastrexmit; // fast retransmit counter
139 | uint32 nxmit; // transmit counter
140 | uint32 nrecv; // receive counter (total)
141 | uint32 nduprecv; // duplicate receive counter
142 | uint32 mtu_guess; // Best guess at MTU
143 | } utp_socket_stats;
144 |
145 | #define UTP_IOV_MAX 1024
146 |
147 | // For utp_writev, to writes data from multiple buffers
148 | struct utp_iovec {
149 | void *iov_base;
150 | size_t iov_len;
151 | };
152 |
153 | // Public Functions
154 | utp_context* utp_init (int version);
155 | void utp_destroy (utp_context *ctx);
156 | void utp_set_callback (utp_context *ctx, int callback_name, utp_callback_t *proc);
157 | void* utp_context_set_userdata (utp_context *ctx, void *userdata);
158 | void* utp_context_get_userdata (utp_context *ctx);
159 | int utp_context_set_option (utp_context *ctx, int opt, int val);
160 | int utp_context_get_option (utp_context *ctx, int opt);
161 | int utp_process_udp (utp_context *ctx, const byte *buf, size_t len, const struct sockaddr *to, socklen_t tolen);
162 | int utp_process_icmp_error (utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen);
163 | int utp_process_icmp_fragmentation (utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen, uint16 next_hop_mtu);
164 | void utp_check_timeouts (utp_context *ctx);
165 | void utp_issue_deferred_acks (utp_context *ctx);
166 | utp_context_stats* utp_get_context_stats (utp_context *ctx);
167 | utp_socket* utp_create_socket (utp_context *ctx);
168 | void* utp_set_userdata (utp_socket *s, void *userdata);
169 | void* utp_get_userdata (utp_socket *s);
170 | int utp_setsockopt (utp_socket *s, int opt, int val);
171 | int utp_getsockopt (utp_socket *s, int opt);
172 | int utp_connect (utp_socket *s, const struct sockaddr *to, socklen_t tolen);
173 | ssize_t utp_write (utp_socket *s, void *buf, size_t count);
174 | ssize_t utp_writev (utp_socket *s, struct utp_iovec *iovec, size_t num_iovecs);
175 | int utp_getpeername (utp_socket *s, struct sockaddr *addr, socklen_t *addrlen);
176 | void utp_read_drained (utp_socket *s);
177 | int utp_get_delays (utp_socket *s, uint32 *ours, uint32 *theirs, uint32 *age);
178 | utp_socket_stats* utp_get_stats (utp_socket *s);
179 | utp_context* utp_get_context (utp_socket *s);
180 | void utp_shutdown (utp_socket *s, int how);
181 | void utp_close (utp_socket *s);
182 |
183 | #ifdef __cplusplus
184 | }
185 | #endif
186 |
187 | #endif //__UTP_H__
188 |
--------------------------------------------------------------------------------
/utp_templates.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #ifndef __TEMPLATES_H__
24 | #define __TEMPLATES_H__
25 |
26 | #include "utp_types.h"
27 | #include
28 |
29 | #if defined(POSIX)
30 | /* Allow over-writing FORCEINLINE from makefile because gcc 3.4.4 for buffalo
31 | doesn't seem to support __attribute__((always_inline)) in -O0 build
32 | (strangely, it works in -Os build) */
33 | #ifndef FORCEINLINE
34 | // The always_inline attribute asks gcc to inline the function even if no optimization is being requested.
35 | // This macro should be used exclusive-or with the inline directive (use one or the other but not both)
36 | // since Microsoft uses __forceinline to also mean inline,
37 | // and this code is following a Microsoft compatibility model.
38 | // Just setting the attribute without also specifying the inline directive apparently won't inline the function,
39 | // as evidenced by multiply-defined symbols found at link time.
40 | #define FORCEINLINE inline __attribute__((always_inline))
41 | #endif
42 | #endif
43 |
44 | // Utility templates
45 | #undef min
46 | #undef max
47 |
48 | template static inline T min(T a, T b) { if (a < b) return a; return b; }
49 | template static inline T max(T a, T b) { if (a > b) return a; return b; }
50 |
51 | template static inline T min(T a, T b, T c) { return min(min(a,b),c); }
52 | template static inline T max(T a, T b, T c) { return max(max(a,b),c); }
53 | template static inline T clamp(T v, T mi, T ma)
54 | {
55 | if (v > ma) v = ma;
56 | if (v < mi) v = mi;
57 | return v;
58 | }
59 |
60 | #if (defined(__SVR4) && defined(__sun))
61 | #pragma pack(1)
62 | #else
63 | #pragma pack(push,1)
64 | #endif
65 |
66 |
67 | namespace aux
68 | {
69 | FORCEINLINE uint16 host_to_network(uint16 i) { return htons(i); }
70 | FORCEINLINE uint32 host_to_network(uint32 i) { return htonl(i); }
71 | FORCEINLINE int32 host_to_network(int32 i) { return htonl(i); }
72 | FORCEINLINE uint16 network_to_host(uint16 i) { return ntohs(i); }
73 | FORCEINLINE uint32 network_to_host(uint32 i) { return ntohl(i); }
74 | FORCEINLINE int32 network_to_host(int32 i) { return ntohl(i); }
75 | }
76 |
77 | template
78 | struct PACKED_ATTRIBUTE big_endian
79 | {
80 | T operator=(T i) { m_integer = aux::host_to_network(i); return i; }
81 | operator T() const { return aux::network_to_host(m_integer); }
82 | private:
83 | T m_integer;
84 | };
85 |
86 | typedef big_endian int32_big;
87 | typedef big_endian uint32_big;
88 | typedef big_endian uint16_big;
89 |
90 | #if (defined(__SVR4) && defined(__sun))
91 | #pragma pack(0)
92 | #else
93 | #pragma pack(pop)
94 | #endif
95 |
96 | template static inline void zeromem(T *a, size_t count = 1) { memset(a, 0, count * sizeof(T)); }
97 |
98 | typedef int SortCompareProc(const void *, const void *);
99 |
100 | template static FORCEINLINE void QuickSortT(T *base, size_t num, int (*comp)(const T *, const T *)) { qsort(base, num, sizeof(T), (SortCompareProc*)comp); }
101 |
102 |
103 | // WARNING: The template parameter MUST be a POD type!
104 | template class Array {
105 | protected:
106 | T *mem;
107 | size_t alloc,count;
108 |
109 | public:
110 | Array(size_t init) { Init(init); }
111 | Array() { Init(); }
112 | ~Array() { Free(); }
113 |
114 | void inline Init() { mem = NULL; alloc = count = 0; }
115 | void inline Init(size_t init) { Init(); if (init) Resize(init); }
116 | size_t inline GetCount() const { return count; }
117 | size_t inline GetAlloc() const { return alloc; }
118 | void inline SetCount(size_t c) { count = c; }
119 |
120 | inline T& operator[](size_t offset) { assert(offset ==0 || offset(minsize, alloc * 2)); }
129 |
130 | inline size_t Append(const T &t) {
131 | if (count >= alloc) Grow();
132 | size_t r=count++;
133 | mem[r] = t;
134 | return r;
135 | }
136 |
137 | T inline &Append() {
138 | if (count >= alloc) Grow();
139 | return mem[count++];
140 | }
141 |
142 | void inline Compact() {
143 | Resize(count);
144 | }
145 |
146 | void inline Free() {
147 | free(mem);
148 | Init();
149 | }
150 |
151 | void inline Clear() {
152 | count = 0;
153 | }
154 |
155 | bool inline MoveUpLast(size_t index) {
156 | assert(index < count);
157 | size_t c = --count;
158 | if (index != c) {
159 | mem[index] = mem[c];
160 | return true;
161 | }
162 | return false;
163 | }
164 |
165 | bool inline MoveUpLastExist(const T &v) {
166 | return MoveUpLast(LookupElementExist(v));
167 | }
168 |
169 | size_t inline LookupElement(const T &v) const {
170 | for(size_t i = 0; i != count; i++)
171 | if (mem[i] == v)
172 | return i;
173 | return (size_t) -1;
174 | }
175 |
176 | bool inline HasElement(const T &v) const {
177 | return LookupElement(v) != -1;
178 | }
179 |
180 | typedef int SortCompareProc(const T *a, const T *b);
181 |
182 | void Sort(SortCompareProc* proc, size_t start, size_t end) {
183 | QuickSortT(&mem[start], end - start, proc);
184 | }
185 |
186 | void Sort(SortCompareProc* proc, size_t start) {
187 | Sort(proc, start, count);
188 | }
189 |
190 | void Sort(SortCompareProc* proc) {
191 | Sort(proc, 0, count);
192 | }
193 | };
194 |
195 | #endif //__TEMPLATES_H__
196 |
--------------------------------------------------------------------------------
/conn.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #include "utp.h"
5 | */
6 | import "C"
7 |
8 | import (
9 | "bytes"
10 | "context"
11 | "errors"
12 | "io"
13 | "net"
14 | "sync"
15 | "syscall"
16 | "time"
17 | "unsafe"
18 | )
19 |
20 | var (
21 | ErrConnClosed = errors.New("closed")
22 | errConnDestroyed = errors.New("destroyed")
23 | errDeadlineExceededValue = errDeadlineExceeded{}
24 | )
25 |
26 | type Conn struct {
27 | s *Socket
28 | us *C.utp_socket
29 | cond sync.Cond
30 | readBuf bytes.Buffer
31 | gotEOF bool
32 | gotConnect bool
33 | // Set on state changed to UTP_STATE_DESTROYING. Not valid to refer to the
34 | // socket after getting this.
35 | destroyed bool
36 | // Conn.Close was called.
37 | closed bool
38 |
39 | err error
40 |
41 | writeDeadline time.Time
42 | writeDeadlineTimer *time.Timer
43 | readDeadline time.Time
44 | readDeadlineTimer *time.Timer
45 |
46 | numBytesRead int64
47 | numBytesWritten int64
48 |
49 | localAddr net.Addr
50 | remoteAddr net.Addr
51 |
52 | // Called for non-fatal errors, such as packet write errors.
53 | userOnError func(error)
54 | }
55 |
56 | func (c *Conn) onError(err error) {
57 | c.err = err
58 | c.cond.Broadcast()
59 | }
60 |
61 | func (c *Conn) setConnected() {
62 | c.gotConnect = true
63 | c.cond.Broadcast()
64 | }
65 |
66 | func (c *Conn) waitForConnect(ctx context.Context) error {
67 | ctx, cancel := context.WithCancel(ctx)
68 | defer cancel()
69 | go func() {
70 | <-ctx.Done()
71 | mu := c.cond.L
72 | mu.Lock()
73 | c.cond.Broadcast()
74 | mu.Unlock()
75 | }()
76 | for {
77 | if c.closed {
78 | return ErrConnClosed
79 | }
80 | if c.err != nil {
81 | return c.err
82 | }
83 | if c.gotConnect {
84 | return nil
85 | }
86 | if ctx.Err() != nil {
87 | return ctx.Err()
88 | }
89 | c.cond.Wait()
90 | }
91 | }
92 |
93 | func (c *Conn) Close() error {
94 | mu.Lock()
95 | defer mu.Unlock()
96 | c.close()
97 | return nil
98 | }
99 |
100 | func (c *Conn) close() {
101 | if !c.destroyed && !c.closed {
102 | C.utp_close(c.us)
103 | }
104 | c.closed = true
105 | c.cond.Broadcast()
106 | }
107 |
108 | func (c *Conn) LocalAddr() net.Addr {
109 | return c.localAddr
110 | }
111 |
112 | func (c *Conn) readNoWait(b []byte) (n int, err error) {
113 | n, _ = c.readBuf.Read(b)
114 | if n != 0 && c.readBuf.Len() == 0 {
115 | // Can we call this if the utp_socket is closed, destroyed or errored?
116 | if c.us != nil {
117 | C.utp_read_drained(c.us)
118 | // C.utp_issue_deferred_acks(C.utp_get_context(c.s))
119 | }
120 | }
121 | if c.readBuf.Len() != 0 {
122 | return
123 | }
124 | err = func() error {
125 | switch {
126 | case c.gotEOF:
127 | return io.EOF
128 | case c.err != nil:
129 | return c.err
130 | case c.destroyed:
131 | return errConnDestroyed
132 | case c.closed:
133 | return ErrConnClosed
134 | case !c.readDeadline.IsZero() && !time.Now().Before(c.readDeadline):
135 | return errDeadlineExceededValue
136 | default:
137 | return nil
138 | }
139 | }()
140 | return
141 | }
142 |
143 | func (c *Conn) Read(b []byte) (int, error) {
144 | mu.Lock()
145 | defer mu.Unlock()
146 | for {
147 | n, err := c.readNoWait(b)
148 | c.numBytesRead += int64(n)
149 | // log.Printf("read %d bytes", c.numBytesRead)
150 | if n != 0 || len(b) == 0 || err != nil {
151 | // log.Printf("conn %p: read %d bytes: %s", c, n, err)
152 | return n, err
153 | }
154 | c.cond.Wait()
155 | }
156 | }
157 |
158 | func (c *Conn) writeNoWait(b []byte) (n int, err error) {
159 | err = func() error {
160 | switch {
161 | case c.err != nil:
162 | return c.err
163 | case c.closed:
164 | return ErrConnClosed
165 | case c.destroyed:
166 | return errConnDestroyed
167 | case !c.writeDeadline.IsZero() && !time.Now().Before(c.writeDeadline):
168 | return errDeadlineExceededValue
169 | default:
170 | return nil
171 | }
172 | }()
173 | if err != nil {
174 | return
175 | }
176 | n = int(C.utp_write(c.us, unsafe.Pointer(&b[0]), C.size_t(len(b))))
177 | if n < 0 {
178 | panic(n)
179 | }
180 | return
181 | }
182 |
183 | func (c *Conn) Write(b []byte) (n int, err error) {
184 | mu.Lock()
185 | defer mu.Unlock()
186 | for len(b) != 0 {
187 | var n1 int
188 | n1, err = c.writeNoWait(b)
189 | b = b[n1:]
190 | n += n1
191 | if err != nil {
192 | break
193 | }
194 | if n1 != 0 {
195 | continue
196 | }
197 | c.cond.Wait()
198 | }
199 | c.numBytesWritten += int64(n)
200 | // log.Printf("wrote %d bytes", c.numBytesWritten)
201 | return
202 | }
203 |
204 | func (c *Conn) setRemoteAddr() {
205 | var rsa syscall.RawSockaddrAny
206 | var addrlen C.socklen_t = C.socklen_t(unsafe.Sizeof(rsa))
207 | C.utp_getpeername(c.us, (*C.struct_sockaddr)(unsafe.Pointer(&rsa)), &addrlen)
208 | var udp net.UDPAddr
209 | if err := anySockaddrToUdp(&rsa, &udp); err != nil {
210 | panic(err)
211 | }
212 | c.remoteAddr = &udp
213 | }
214 |
215 | func (c *Conn) RemoteAddr() net.Addr {
216 | return c.remoteAddr
217 | }
218 |
219 | func (c *Conn) SetDeadline(t time.Time) error {
220 | mu.Lock()
221 | defer mu.Unlock()
222 | c.readDeadline = t
223 | c.writeDeadline = t
224 | if t.IsZero() {
225 | c.readDeadlineTimer.Stop()
226 | c.writeDeadlineTimer.Stop()
227 | } else {
228 | d := t.Sub(time.Now())
229 | c.readDeadlineTimer.Reset(d)
230 | c.writeDeadlineTimer.Reset(d)
231 | }
232 | c.cond.Broadcast()
233 | return nil
234 | }
235 |
236 | func (c *Conn) SetReadDeadline(t time.Time) error {
237 | mu.Lock()
238 | defer mu.Unlock()
239 | c.readDeadline = t
240 | if t.IsZero() {
241 | c.readDeadlineTimer.Stop()
242 | } else {
243 | d := t.Sub(time.Now())
244 | c.readDeadlineTimer.Reset(d)
245 | }
246 | c.cond.Broadcast()
247 | return nil
248 | }
249 |
250 | func (c *Conn) SetWriteDeadline(t time.Time) error {
251 | mu.Lock()
252 | defer mu.Unlock()
253 | c.writeDeadline = t
254 | if t.IsZero() {
255 | c.writeDeadlineTimer.Stop()
256 | } else {
257 | d := t.Sub(time.Now())
258 | c.writeDeadlineTimer.Reset(d)
259 | }
260 | c.cond.Broadcast()
261 | return nil
262 | }
263 |
264 | func (c *Conn) setGotEOF() {
265 | c.gotEOF = true
266 | c.cond.Broadcast()
267 | }
268 |
269 | func (c *Conn) onDestroyed() {
270 | c.destroyed = true
271 | c.us = nil
272 | c.cond.Broadcast()
273 | }
274 |
275 | func (c *Conn) WriteBufferLen() int {
276 | mu.Lock()
277 | defer mu.Unlock()
278 | return int(C.utp_getsockopt(c.us, C.UTP_SNDBUF))
279 | }
280 |
281 | func (c *Conn) SetWriteBufferLen(len int) {
282 | mu.Lock()
283 | defer mu.Unlock()
284 | i := C.utp_setsockopt(c.us, C.UTP_SNDBUF, C.int(len))
285 | if i != 0 {
286 | panic(i)
287 | }
288 | }
289 |
290 | // utp_connect *must* be called on a created socket or it's impossible to correctly deallocate it
291 | // (at least through utp API?). See https://github.com/bittorrent/libutp/issues/113. This function
292 | // does both in a single step to prevent incorrect use. Note that accept automatically creates a
293 | // socket (after the firewall check) and it arrives initialized correctly.
294 | func utpCreateSocketAndConnect(
295 | ctx *C.utp_context,
296 | addr syscall.RawSockaddrAny,
297 | addrlen C.socklen_t,
298 | ) *C.utp_socket {
299 | utpSock := C.utp_create_socket(ctx)
300 | if n := C.utp_connect(utpSock, (*C.struct_sockaddr)(unsafe.Pointer(&addr)), addrlen); n != 0 {
301 | panic(n)
302 | }
303 | return utpSock
304 | }
305 |
306 | func (c *Conn) OnError(f func(error)) {
307 | mu.Lock()
308 | c.userOnError = f
309 | mu.Unlock()
310 | }
311 |
--------------------------------------------------------------------------------
/utp_callbacks.cpp:
--------------------------------------------------------------------------------
1 | // vim:set ts=4 sw=4 ai:
2 |
3 | /*
4 | * Copyright (c) 2010-2013 BitTorrent, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | #include "utp_callbacks.h"
26 |
27 | int utp_call_on_firewall(utp_context *ctx, const struct sockaddr *address, socklen_t address_len)
28 | {
29 | utp_callback_arguments args;
30 | if (!ctx->callbacks[UTP_ON_FIREWALL]) return 0;
31 | args.callback_type = UTP_ON_FIREWALL;
32 | args.context = ctx;
33 | args.socket = NULL;
34 | args.address = address;
35 | args.address_len = address_len;
36 | return (int)ctx->callbacks[UTP_ON_FIREWALL](&args);
37 | }
38 |
39 | void utp_call_on_accept(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
40 | {
41 | utp_callback_arguments args;
42 | if (!ctx->callbacks[UTP_ON_ACCEPT]) return;
43 | args.callback_type = UTP_ON_ACCEPT;
44 | args.context = ctx;
45 | args.socket = socket;
46 | args.address = address;
47 | args.address_len = address_len;
48 | ctx->callbacks[UTP_ON_ACCEPT](&args);
49 | }
50 |
51 | void utp_call_on_connect(utp_context *ctx, utp_socket *socket)
52 | {
53 | utp_callback_arguments args;
54 | if (!ctx->callbacks[UTP_ON_CONNECT]) return;
55 | args.callback_type = UTP_ON_CONNECT;
56 | args.context = ctx;
57 | args.socket = socket;
58 | ctx->callbacks[UTP_ON_CONNECT](&args);
59 | }
60 |
61 | void utp_call_on_error(utp_context *ctx, utp_socket *socket, int error_code)
62 | {
63 | utp_callback_arguments args;
64 | if (!ctx->callbacks[UTP_ON_ERROR]) return;
65 | args.callback_type = UTP_ON_ERROR;
66 | args.context = ctx;
67 | args.socket = socket;
68 | args.error_code = error_code;
69 | ctx->callbacks[UTP_ON_ERROR](&args);
70 | }
71 |
72 | void utp_call_on_read(utp_context *ctx, utp_socket *socket, const byte *buf, size_t len)
73 | {
74 | utp_callback_arguments args;
75 | if (!ctx->callbacks[UTP_ON_READ]) return;
76 | args.callback_type = UTP_ON_READ;
77 | args.context = ctx;
78 | args.socket = socket;
79 | args.buf = buf;
80 | args.len = len;
81 | ctx->callbacks[UTP_ON_READ](&args);
82 | }
83 |
84 | void utp_call_on_overhead_statistics(utp_context *ctx, utp_socket *socket, int send, size_t len, int type)
85 | {
86 | utp_callback_arguments args;
87 | if (!ctx->callbacks[UTP_ON_OVERHEAD_STATISTICS]) return;
88 | args.callback_type = UTP_ON_OVERHEAD_STATISTICS;
89 | args.context = ctx;
90 | args.socket = socket;
91 | args.send = send;
92 | args.len = len;
93 | args.type = type;
94 | ctx->callbacks[UTP_ON_OVERHEAD_STATISTICS](&args);
95 | }
96 |
97 | void utp_call_on_delay_sample(utp_context *ctx, utp_socket *socket, int sample_ms)
98 | {
99 | utp_callback_arguments args;
100 | if (!ctx->callbacks[UTP_ON_DELAY_SAMPLE]) return;
101 | args.callback_type = UTP_ON_DELAY_SAMPLE;
102 | args.context = ctx;
103 | args.socket = socket;
104 | args.sample_ms = sample_ms;
105 | ctx->callbacks[UTP_ON_DELAY_SAMPLE](&args);
106 | }
107 |
108 | void utp_call_on_state_change(utp_context *ctx, utp_socket *socket, int state)
109 | {
110 | utp_callback_arguments args;
111 | if (!ctx->callbacks[UTP_ON_STATE_CHANGE]) return;
112 | args.callback_type = UTP_ON_STATE_CHANGE;
113 | args.context = ctx;
114 | args.socket = socket;
115 | args.state = state;
116 | ctx->callbacks[UTP_ON_STATE_CHANGE](&args);
117 | }
118 |
119 | uint16 utp_call_get_udp_mtu(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
120 | {
121 | utp_callback_arguments args;
122 | if (!ctx->callbacks[UTP_GET_UDP_MTU]) return 0;
123 | args.callback_type = UTP_GET_UDP_MTU;
124 | args.context = ctx;
125 | args.socket = socket;
126 | args.address = address;
127 | args.address_len = address_len;
128 | return (uint16)ctx->callbacks[UTP_GET_UDP_MTU](&args);
129 | }
130 |
131 | uint16 utp_call_get_udp_overhead(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
132 | {
133 | utp_callback_arguments args;
134 | if (!ctx->callbacks[UTP_GET_UDP_OVERHEAD]) return 0;
135 | args.callback_type = UTP_GET_UDP_OVERHEAD;
136 | args.context = ctx;
137 | args.socket = socket;
138 | args.address = address;
139 | args.address_len = address_len;
140 | return (uint16)ctx->callbacks[UTP_GET_UDP_OVERHEAD](&args);
141 | }
142 |
143 | uint64 utp_call_get_milliseconds(utp_context *ctx, utp_socket *socket)
144 | {
145 | utp_callback_arguments args;
146 | if (!ctx->callbacks[UTP_GET_MILLISECONDS]) return 0;
147 | args.callback_type = UTP_GET_MILLISECONDS;
148 | args.context = ctx;
149 | args.socket = socket;
150 | return ctx->callbacks[UTP_GET_MILLISECONDS](&args);
151 | }
152 |
153 | uint64 utp_call_get_microseconds(utp_context *ctx, utp_socket *socket)
154 | {
155 | utp_callback_arguments args;
156 | if (!ctx->callbacks[UTP_GET_MICROSECONDS]) return 0;
157 | args.callback_type = UTP_GET_MICROSECONDS;
158 | args.context = ctx;
159 | args.socket = socket;
160 | return ctx->callbacks[UTP_GET_MICROSECONDS](&args);
161 | }
162 |
163 | uint32 utp_call_get_random(utp_context *ctx, utp_socket *socket)
164 | {
165 | utp_callback_arguments args;
166 | if (!ctx->callbacks[UTP_GET_RANDOM]) return 0;
167 | args.callback_type = UTP_GET_RANDOM;
168 | args.context = ctx;
169 | args.socket = socket;
170 | return (uint32)ctx->callbacks[UTP_GET_RANDOM](&args);
171 | }
172 |
173 | size_t utp_call_get_read_buffer_size(utp_context *ctx, utp_socket *socket)
174 | {
175 | utp_callback_arguments args;
176 | if (!ctx->callbacks[UTP_GET_READ_BUFFER_SIZE]) return 0;
177 | args.callback_type = UTP_GET_READ_BUFFER_SIZE;
178 | args.context = ctx;
179 | args.socket = socket;
180 | return (size_t)ctx->callbacks[UTP_GET_READ_BUFFER_SIZE](&args);
181 | }
182 |
183 | void utp_call_log(utp_context *ctx, utp_socket *socket, const byte *buf)
184 | {
185 | utp_callback_arguments args;
186 | if (!ctx->callbacks[UTP_LOG]) return;
187 | args.callback_type = UTP_LOG;
188 | args.context = ctx;
189 | args.socket = socket;
190 | args.buf = buf;
191 | ctx->callbacks[UTP_LOG](&args);
192 | }
193 |
194 | void utp_call_sendto(utp_context *ctx, utp_socket *socket, const byte *buf, size_t len, const struct sockaddr *address, socklen_t address_len, uint32 flags)
195 | {
196 | utp_callback_arguments args;
197 | if (!ctx->callbacks[UTP_SENDTO]) return;
198 | args.callback_type = UTP_SENDTO;
199 | args.context = ctx;
200 | args.socket = socket;
201 | args.buf = buf;
202 | args.len = len;
203 | args.address = address;
204 | args.address_len = address_len;
205 | args.flags = flags;
206 | ctx->callbacks[UTP_SENDTO](&args);
207 | }
208 |
209 |
--------------------------------------------------------------------------------
/utp_hash.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #include "utp_hash.h"
24 | #include "utp_types.h"
25 |
26 | #define LIBUTP_HASH_UNUSED ((utp_link_t)-1)
27 |
28 | #ifdef STRICT_ALIGN
29 | inline uint32 Read32(const void *p)
30 | {
31 | uint32 tmp;
32 | memcpy(&tmp, p, sizeof tmp);
33 | return tmp;
34 | }
35 |
36 | #else
37 | inline uint32 Read32(const void *p) { return *(uint32*)p; }
38 | #endif
39 |
40 |
41 | // Get the amount of memory required for the hash parameters and the bucket set
42 | // Waste a space for an unused bucket in order to ensure the following managed memory have 32-bit aligned addresses
43 | // TODO: make this 64-bit clean
44 | #define BASE_SIZE(bc) (sizeof(utp_hash_t) + sizeof(utp_link_t) * ((bc) + 1))
45 |
46 | // Get a pointer to the base of the structure array managed by the hash table
47 | #define get_bep(h) ((byte*)(h)) + BASE_SIZE((h)->N)
48 |
49 | // Get the address of the information associated with a specific structure in the array,
50 | // given the address of the base of the structure.
51 | // This assumes a utp_link_t link member is at the end of the structure.
52 | // Given compilers filling out the memory to a 32-bit clean value, this may mean that
53 | // the location named in the structure may not be the location actually used by the hash table,
54 | // since the compiler may have padded the end of the structure with 2 bytes after the utp_link_t member.
55 | // TODO: this macro should not require that the variable pointing at the hash table be named 'hash'
56 | #define ptr_to_link(p) (utp_link_t *) (((byte *) (p)) + hash->E - sizeof(utp_link_t))
57 |
58 | // Calculate how much to allocate for a hash table with bucket count, total size, and structure count
59 | // TODO: make this 64-bit clean
60 | #define ALLOCATION_SIZE(bc, ts, sc) (BASE_SIZE((bc)) + (ts) * (sc))
61 |
62 | utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun, utp_hash_equal_t compfun)
63 | {
64 | // Must have odd number of hash buckets (prime number is best)
65 | assert(N % 2);
66 | // Ensure structures will be at aligned memory addresses
67 | // TODO: make this 64-bit clean
68 | assert(0 == (total_size % 4));
69 |
70 | int size = ALLOCATION_SIZE(N, total_size, initial);
71 | utp_hash_t *hash = (utp_hash_t *) malloc( size );
72 | memset( hash, 0, size );
73 |
74 | for (int i = 0; i < N + 1; ++i)
75 | hash->inits[i] = LIBUTP_HASH_UNUSED;
76 | hash->N = N;
77 | hash->K = key_size;
78 | hash->E = total_size;
79 | hash->hash_compute = hashfun;
80 | hash->hash_equal = compfun;
81 | hash->allocated = initial;
82 | hash->count = 0;
83 | hash->used = 0;
84 | hash->free = LIBUTP_HASH_UNUSED;
85 | return hash;
86 | }
87 |
88 | uint utp_hash_mem(const void *keyp, size_t keysize)
89 | {
90 | uint hash = 0;
91 | uint n = keysize;
92 | while (n >= 4) {
93 | hash ^= Read32(keyp);
94 | keyp = (byte*)keyp + sizeof(uint32);
95 | hash = (hash << 13) | (hash >> 19);
96 | n -= 4;
97 | }
98 | while (n != 0) {
99 | hash ^= *(byte*)keyp;
100 | keyp = (byte*)keyp + sizeof(byte);
101 | hash = (hash << 8) | (hash >> 24);
102 | n--;
103 | }
104 | return hash;
105 | }
106 |
107 | uint utp_hash_mkidx(utp_hash_t *hash, const void *keyp)
108 | {
109 | // Generate a key from the hash
110 | return hash->hash_compute(keyp, hash->K) % hash->N;
111 | }
112 |
113 | static inline bool compare(byte *a, byte *b,int n)
114 | {
115 | assert(n >= 4);
116 | if (Read32(a) != Read32(b)) return false;
117 | return memcmp(a+4, b+4, n-4) == 0;
118 | }
119 |
120 | #define COMPARE(h,k1,k2,ks) (((h)->hash_equal) ? (h)->hash_equal((void*)k1,(void*)k2,ks) : compare(k1,k2,ks))
121 |
122 | // Look-up a key in the hash table.
123 | // Returns NULL if not found
124 | void *utp_hash_lookup(utp_hash_t *hash, const void *key)
125 | {
126 | utp_link_t idx = utp_hash_mkidx(hash, key);
127 |
128 | // base pointer
129 | byte *bep = get_bep(hash);
130 |
131 | utp_link_t cur = hash->inits[idx];
132 | while (cur != LIBUTP_HASH_UNUSED) {
133 | byte *key2 = bep + (cur * hash->E);
134 | if (COMPARE(hash, (byte*)key, key2, hash->K))
135 | return key2;
136 | cur = *ptr_to_link(key2);
137 | }
138 |
139 | return NULL;
140 | }
141 |
142 | // Add a new element to the hash table.
143 | // Returns a pointer to the new element.
144 | // This assumes the element is not already present!
145 | void *utp_hash_add(utp_hash_t **hashp, const void *key)
146 | {
147 | //Allocate a new entry
148 | byte *elemp;
149 | utp_link_t elem;
150 | utp_hash_t *hash = *hashp;
151 | utp_link_t idx = utp_hash_mkidx(hash, key);
152 |
153 | if ((elem=hash->free) == LIBUTP_HASH_UNUSED) {
154 | utp_link_t all = hash->allocated;
155 | if (hash->used == all) {
156 | utp_hash_t *nhash;
157 | if (all <= (LIBUTP_HASH_UNUSED/2)) {
158 | all *= 2;
159 | } else if (all != LIBUTP_HASH_UNUSED) {
160 | all = LIBUTP_HASH_UNUSED;
161 | } else {
162 | // too many items! can't grow!
163 | assert(0);
164 | return NULL;
165 | }
166 | // otherwise need to allocate.
167 | nhash = (utp_hash_t*)realloc(hash, ALLOCATION_SIZE(hash->N, hash->E, all));
168 | if (!nhash) {
169 | // out of memory (or too big to allocate)
170 | assert(nhash);
171 | return NULL;
172 | }
173 | hash = *hashp = nhash;
174 | hash->allocated = all;
175 | }
176 |
177 | elem = hash->used++;
178 | elemp = get_bep(hash) + elem * hash->E;
179 | } else {
180 | elemp = get_bep(hash) + elem * hash->E;
181 | hash->free = *ptr_to_link(elemp);
182 | }
183 |
184 | *ptr_to_link(elemp) = hash->inits[idx];
185 | hash->inits[idx] = elem;
186 | hash->count++;
187 |
188 | // copy key into it
189 | memcpy(elemp, key, hash->K);
190 | return elemp;
191 | }
192 |
193 | // Delete an element from the utp_hash_t
194 | // Returns a pointer to the already deleted element.
195 | void *utp_hash_del(utp_hash_t *hash, const void *key)
196 | {
197 | utp_link_t idx = utp_hash_mkidx(hash, key);
198 |
199 | // base pointer
200 | byte *bep = get_bep(hash);
201 |
202 | utp_link_t *curp = &hash->inits[idx];
203 | utp_link_t cur;
204 | while ((cur=*curp) != LIBUTP_HASH_UNUSED) {
205 | byte *key2 = bep + (cur * hash->E);
206 | if (COMPARE(hash,(byte*)key,(byte*)key2, hash->K )) {
207 | // found an item that matched. unlink it
208 | *curp = *ptr_to_link(key2);
209 | // Insert into freelist
210 | *ptr_to_link(key2) = hash->free;
211 | hash->free = cur;
212 | hash->count--;
213 | return key2;
214 | }
215 | curp = ptr_to_link(key2);
216 | }
217 |
218 | return NULL;
219 | }
220 |
221 | void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter)
222 | {
223 | utp_link_t elem;
224 |
225 | if ((elem=iter->elem) == LIBUTP_HASH_UNUSED) {
226 | // Find a bucket with an element
227 | utp_link_t buck = iter->bucket + 1;
228 | for(;;) {
229 | if (buck >= hash->N)
230 | return NULL;
231 | if ((elem = hash->inits[buck]) != LIBUTP_HASH_UNUSED)
232 | break;
233 | buck++;
234 | }
235 | iter->bucket = buck;
236 | }
237 |
238 | byte *elemp = get_bep(hash) + (elem * hash->E);
239 | iter->elem = *ptr_to_link(elemp);
240 | return elemp;
241 | }
242 |
243 | void utp_hash_free_mem(utp_hash_t* hash)
244 | {
245 | free(hash);
246 | }
247 |
--------------------------------------------------------------------------------
/utp_utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2013 BitTorrent, Inc.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | #include
24 | #include
25 | #include "utp.h"
26 | #include "utp_types.h"
27 |
28 | #ifdef WIN32
29 | #define WIN32_LEAN_AND_MEAN
30 | #include
31 | #include
32 | #include
33 | #else //!WIN32
34 | #include
35 | #include // Linux needs both time.h and sys/time.h
36 | #endif
37 |
38 | #if defined(__APPLE__)
39 | #include
40 | #endif
41 |
42 | #include "utp_utils.h"
43 |
44 | #ifdef WIN32
45 |
46 | typedef ULONGLONG (WINAPI GetTickCount64Proc)(void);
47 | static GetTickCount64Proc *pt2GetTickCount64;
48 | static GetTickCount64Proc *pt2RealGetTickCount;
49 |
50 | static uint64 startPerformanceCounter;
51 | static uint64 startGetTickCount;
52 | // MSVC 6 standard doesn't like division with uint64s
53 | static double counterPerMicrosecond;
54 |
55 | static uint64 UTGetTickCount64()
56 | {
57 | if (pt2GetTickCount64) {
58 | return pt2GetTickCount64();
59 | }
60 | if (pt2RealGetTickCount) {
61 | uint64 v = pt2RealGetTickCount();
62 | // fix return value from GetTickCount
63 | return (DWORD)v | ((v >> 0x18) & 0xFFFFFFFF00000000);
64 | }
65 | return (uint64)GetTickCount();
66 | }
67 |
68 | static void Time_Initialize()
69 | {
70 | HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
71 | pt2GetTickCount64 = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount64");
72 | // not a typo. GetTickCount actually returns 64 bits
73 | pt2RealGetTickCount = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount");
74 |
75 | uint64 frequency;
76 | QueryPerformanceCounter((LARGE_INTEGER*)&startPerformanceCounter);
77 | QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
78 | counterPerMicrosecond = (double)frequency / 1000000.0f;
79 | startGetTickCount = UTGetTickCount64();
80 | }
81 |
82 | static int64 abs64(int64 x) { return x < 0 ? -x : x; }
83 |
84 | static uint64 __GetMicroseconds()
85 | {
86 | static bool time_init = false;
87 | if (!time_init) {
88 | time_init = true;
89 | Time_Initialize();
90 | }
91 |
92 | uint64 counter;
93 | uint64 tick;
94 |
95 | QueryPerformanceCounter((LARGE_INTEGER*) &counter);
96 | tick = UTGetTickCount64();
97 |
98 | // unfortunately, QueryPerformanceCounter is not guaranteed
99 | // to be monotonic. Make it so.
100 | int64 ret = (int64)(((int64)counter - (int64)startPerformanceCounter) / counterPerMicrosecond);
101 | // if the QPC clock leaps more than one second off GetTickCount64()
102 | // something is seriously fishy. Adjust QPC to stay monotonic
103 | int64 tick_diff = tick - startGetTickCount;
104 | if (abs64(ret / 100000 - tick_diff / 100) > 10) {
105 | startPerformanceCounter -= (uint64)((int64)(tick_diff * 1000 - ret) * counterPerMicrosecond);
106 | ret = (int64)((counter - startPerformanceCounter) / counterPerMicrosecond);
107 | }
108 | return ret;
109 | }
110 |
111 | static inline uint64 UTP_GetMilliseconds()
112 | {
113 | return GetTickCount();
114 | }
115 |
116 | #else //!WIN32
117 |
118 | static inline uint64 UTP_GetMicroseconds(void);
119 | static inline uint64 UTP_GetMilliseconds()
120 | {
121 | return UTP_GetMicroseconds() / 1000;
122 | }
123 |
124 | #if defined(__APPLE__)
125 |
126 | static uint64 __GetMicroseconds()
127 | {
128 | // http://developer.apple.com/mac/library/qa/qa2004/qa1398.html
129 | // http://www.macresearch.org/tutorial_performance_and_time
130 | static mach_timebase_info_data_t sTimebaseInfo;
131 | static uint64_t start_tick = 0;
132 | uint64_t tick;
133 | // Returns a counter in some fraction of a nanoseconds
134 | tick = mach_absolute_time();
135 | if (sTimebaseInfo.denom == 0) {
136 | // Get the timer ratio to convert mach_absolute_time to nanoseconds
137 | mach_timebase_info(&sTimebaseInfo);
138 | start_tick = tick;
139 | }
140 | // Calculate the elapsed time, convert it to microseconds and return it.
141 | return ((tick - start_tick) * sTimebaseInfo.numer) / (sTimebaseInfo.denom * 1000);
142 | }
143 |
144 | #else // !__APPLE__
145 |
146 | #if ! (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC))
147 | #warning "Using non-monotonic function gettimeofday() in UTP_GetMicroseconds()"
148 | #endif
149 |
150 | /* Unfortunately, #ifdef CLOCK_MONOTONIC is not enough to make sure that
151 | POSIX clocks work -- we could be running a recent libc with an ancient
152 | kernel (think OpenWRT). -- jch */
153 |
154 | static uint64_t __GetMicroseconds()
155 | {
156 | struct timeval tv;
157 |
158 | #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)
159 | static int have_posix_clocks = -1;
160 | int rc;
161 |
162 | if (have_posix_clocks < 0) {
163 | struct timespec ts;
164 | rc = clock_gettime(CLOCK_MONOTONIC, &ts);
165 | if (rc < 0) {
166 | have_posix_clocks = 0;
167 | } else {
168 | have_posix_clocks = 1;
169 | }
170 | }
171 |
172 | if (have_posix_clocks) {
173 | struct timespec ts;
174 | rc = clock_gettime(CLOCK_MONOTONIC, &ts);
175 | return uint64(ts.tv_sec) * 1000000 + uint64(ts.tv_nsec) / 1000;
176 | }
177 | #endif
178 |
179 | gettimeofday(&tv, NULL);
180 | return uint64(tv.tv_sec) * 1000000 + tv.tv_usec;
181 | }
182 |
183 | #endif //!__APPLE__
184 |
185 | #endif //!WIN32
186 |
187 | /*
188 | * Whew. Okay. After that #ifdef maze above, we now know we have a working
189 | * __GetMicroseconds() implementation on all platforms.
190 | *
191 | * Because there are a number of assertions in libutp that will cause a crash
192 | * if monotonic time isn't monotonic, now apply some safety checks. While in
193 | * principle we're already protecting ourselves in cases where non-monotonic
194 | * time is likely to happen, this protects all versions.
195 | */
196 |
197 | static inline uint64 UTP_GetMicroseconds()
198 | {
199 | static uint64 offset = 0, previous = 0;
200 |
201 | uint64 now = __GetMicroseconds() + offset;
202 | if (previous > now) {
203 | /* Eek! */
204 | offset += previous - now;
205 | now = previous;
206 | }
207 | previous = now;
208 | return now;
209 | }
210 |
211 | #define ETHERNET_MTU 1500
212 | #define IPV4_HEADER_SIZE 20
213 | #define IPV6_HEADER_SIZE 40
214 | #define UDP_HEADER_SIZE 8
215 | #define GRE_HEADER_SIZE 24
216 | #define PPPOE_HEADER_SIZE 8
217 | #define MPPE_HEADER_SIZE 2
218 | // packets have been observed in the wild that were fragmented
219 | // with a payload of 1416 for the first fragment
220 | // There are reports of routers that have MTU sizes as small as 1392
221 | #define FUDGE_HEADER_SIZE 36
222 | #define TEREDO_MTU 1280
223 |
224 | #define UDP_IPV4_OVERHEAD (IPV4_HEADER_SIZE + UDP_HEADER_SIZE)
225 | #define UDP_IPV6_OVERHEAD (IPV6_HEADER_SIZE + UDP_HEADER_SIZE)
226 | #define UDP_TEREDO_OVERHEAD (UDP_IPV4_OVERHEAD + UDP_IPV6_OVERHEAD)
227 |
228 | #define UDP_IPV4_MTU (ETHERNET_MTU - IPV4_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)
229 | #define UDP_IPV6_MTU (ETHERNET_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)
230 | #define UDP_TEREDO_MTU (TEREDO_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE)
231 |
232 | uint64 utp_default_get_udp_mtu(utp_callback_arguments *args) {
233 | // Since we don't know the local address of the interface,
234 | // be conservative and assume all IPv6 connections are Teredo.
235 | return (args->address->sa_family == AF_INET6) ? UDP_TEREDO_MTU : UDP_IPV4_MTU;
236 | }
237 |
238 | uint64 utp_default_get_udp_overhead(utp_callback_arguments *args) {
239 | // Since we don't know the local address of the interface,
240 | // be conservative and assume all IPv6 connections are Teredo.
241 | return (args->address->sa_family == AF_INET6) ? UDP_TEREDO_OVERHEAD : UDP_IPV4_OVERHEAD;
242 | }
243 |
244 | uint64 utp_default_get_random(utp_callback_arguments *args) {
245 | return rand();
246 | }
247 |
248 | uint64 utp_default_get_milliseconds(utp_callback_arguments *args) {
249 | return UTP_GetMilliseconds();
250 | }
251 |
252 | uint64 utp_default_get_microseconds(utp_callback_arguments *args) {
253 | return UTP_GetMicroseconds();
254 | }
255 |
--------------------------------------------------------------------------------
/parse_log.py:
--------------------------------------------------------------------------------
1 | import os, sys, time
2 |
3 | # usage: parse_log.py log-file [socket-index to focus on]
4 |
5 |
6 | socket_filter = None
7 | if len(sys.argv) >= 3:
8 | socket_filter = sys.argv[2].strip()
9 |
10 | if socket_filter == None:
11 | print "scanning for socket with the most packets"
12 | file = open(sys.argv[1], 'rb')
13 |
14 | sockets = {}
15 |
16 | for l in file:
17 | if not 'our_delay' in l: continue
18 |
19 | try:
20 | a = l.strip().split(" ")
21 | socket_index = a[1][:-1]
22 | except:
23 | continue
24 |
25 | # msvc's runtime library doesn't prefix pointers
26 | # with '0x'
27 | # if socket_index[:2] != '0x':
28 | # continue
29 |
30 | if socket_index in sockets:
31 | sockets[socket_index] += 1
32 | else:
33 | sockets[socket_index] = 1
34 |
35 | items = sockets.items()
36 | items.sort(lambda x, y: y[1] - x[1])
37 |
38 | count = 0
39 | for i in items:
40 | print '%s: %d' % (i[0], i[1])
41 | count += 1
42 | if count > 5: break
43 |
44 | file.close()
45 | socket_filter = items[0][0]
46 | print '\nfocusing on socket %s' % socket_filter
47 |
48 | file = open(sys.argv[1], 'rb')
49 | out_file = 'utp.out%s' % socket_filter;
50 | out = open(out_file, 'wb')
51 |
52 | delay_samples = 'dots lc rgb "blue"'
53 | delay_base = 'steps lw 2 lc rgb "purple"'
54 | target_delay = 'steps lw 2 lc rgb "red"'
55 | off_target = 'dots lc rgb "blue"'
56 | cwnd = 'steps lc rgb "green"'
57 | window_size = 'steps lc rgb "sea-green"'
58 | rtt = 'lines lc rgb "light-blue"'
59 |
60 | metrics = {
61 | 'our_delay':['our delay (ms)', 'x1y2', delay_samples],
62 | 'upload_rate':['send rate (B/s)', 'x1y1', 'lines'],
63 | 'max_window':['cwnd (B)', 'x1y1', cwnd],
64 | 'target_delay':['target delay (ms)', 'x1y2', target_delay],
65 | 'cur_window':['bytes in-flight (B)', 'x1y1', window_size],
66 | 'cur_window_packets':['number of packets in-flight', 'x1y2', 'steps'],
67 | 'packet_size':['current packet size (B)', 'x1y2', 'steps'],
68 | 'rtt':['rtt (ms)', 'x1y2', rtt],
69 | 'off_target':['off-target (ms)', 'x1y2', off_target],
70 | 'delay_sum':['delay sum (ms)', 'x1y2', 'steps'],
71 | 'their_delay':['their delay (ms)', 'x1y2', delay_samples],
72 | 'get_microseconds':['clock (us)', 'x1y1', 'steps'],
73 | 'wnduser':['advertised window size (B)', 'x1y1', 'steps'],
74 |
75 | 'delay_base':['delay base (us)', 'x1y1', delay_base],
76 | 'their_delay_base':['their delay base (us)', 'x1y1', delay_base],
77 | 'their_actual_delay':['their actual delay (us)', 'x1y1', delay_samples],
78 | 'actual_delay':['actual_delay (us)', 'x1y1', delay_samples]
79 | }
80 |
81 | histogram_quantization = 1
82 | socket_index = None
83 |
84 | columns = []
85 |
86 | begin = None
87 |
88 | title = "-"
89 | packet_loss = 0
90 | packet_timeout = 0
91 |
92 | delay_histogram = {}
93 | window_size = {'0': 0, '1': 0}
94 |
95 | # [35301484] 0x00ec1190: actual_delay:1021583 our_delay:102 their_delay:-1021345 off_target:297 max_window:2687 upload_rate:18942 delay_base:1021481154 delay_sum:-1021242 target_delay:400 acked_bytes:1441 cur_window:2882 scaled_gain:2.432
96 |
97 | counter = 0
98 |
99 | print "reading log file"
100 |
101 | for l in file:
102 | if "UTP_Connect" in l:
103 | title = l[:-2]
104 | if socket_filter != None:
105 | title += ' socket: %s' % socket_filter
106 | else:
107 | title += ' sum of all sockets'
108 | continue
109 |
110 | try:
111 | a = l.strip().split(" ")
112 | t = a[0][1:-1]
113 | socket_index = a[1][:-1]
114 | except:
115 | continue
116 | # if socket_index[:2] != '0x':
117 | # continue
118 |
119 | if socket_filter != None and socket_index != socket_filter:
120 | continue
121 |
122 | counter += 1
123 | if (counter % 300 == 0):
124 | print "\r%d " % counter,
125 |
126 | if "lost." in l:
127 | packet_loss = packet_loss + 1
128 | continue
129 | if "Packet timeout" in l:
130 | packet_timeout = packet_timeout + 1
131 | continue
132 | if "our_delay:" not in l:
133 | continue
134 |
135 | # used for Logf timestamps
136 | # t, m = t.split(".")
137 | # t = time.strptime(t, "%H:%M:%S")
138 | # t = list(t)
139 | # t[0] += 107
140 | # t = tuple(t)
141 | # m = float(m)
142 | # m /= 1000.0
143 | # t = time.mktime(t) + m
144 |
145 | # used for tick count timestamps
146 | t = int(t)
147 |
148 | if begin is None:
149 | begin = t
150 | t = t - begin
151 | # print time. Convert from milliseconds to seconds
152 | print >>out, '%f\t' % (float(t)/1000.),
153 |
154 | #if t > 200000:
155 | # break
156 |
157 | fill_columns = not columns
158 | for i in a[2:]:
159 | try:
160 | n, v = i.split(':')
161 | except:
162 | continue
163 | v = float(v)
164 | if n == "our_delay":
165 | bucket = v / histogram_quantization
166 | delay_histogram[bucket] = 1 + delay_histogram.get(bucket, 0)
167 | if not n in metrics: continue
168 | if fill_columns:
169 | columns.append(n)
170 | if n == "max_window":
171 | window_size[socket_index] = v
172 | print >>out, '%f\t' % int(reduce(lambda a,b: a+b, window_size.values())),
173 | else:
174 | print >>out, '%f\t' % v,
175 | print >>out, float(packet_loss * 8000), float(packet_timeout * 8000)
176 | packet_loss = 0
177 | packet_timeout = 0
178 |
179 | out.close()
180 |
181 | out = open('%s.histogram' % out_file, 'wb')
182 | for d,f in delay_histogram.iteritems():
183 | print >>out, float(d*histogram_quantization) + histogram_quantization / 2, f
184 | out.close()
185 |
186 |
187 | plot = [
188 | {
189 | 'data': ['upload_rate', 'max_window', 'cur_window', 'wnduser', 'cur_window_packets', 'packet_size', 'rtt'],
190 | 'title': 'send-packet-size',
191 | 'y1': 'Bytes',
192 | 'y2': 'Time (ms)'
193 | },
194 | {
195 | 'data': ['our_delay', 'max_window', 'target_delay', 'cur_window', 'wnduser', 'cur_window_packets'],
196 | 'title': 'uploading',
197 | 'y1': 'Bytes',
198 | 'y2': 'Time (ms)'
199 | },
200 | {
201 | 'data': ['our_delay', 'max_window', 'target_delay', 'cur_window', 'cur_window_packets'],
202 | 'title': 'uploading_packets',
203 | 'y1': 'Bytes',
204 | 'y2': 'Time (ms)'
205 | },
206 | {
207 | 'data': ['get_microseconds'],
208 | 'title': 'timer',
209 | 'y1': 'Time microseconds',
210 | 'y2': 'Time (ms)'
211 | },
212 | {
213 | 'data': ['their_delay', 'target_delay', 'rtt'],
214 | 'title': 'their_delay',
215 | 'y1': '',
216 | 'y2': 'Time (ms)'
217 | },
218 | {
219 | 'data': ['their_actual_delay','their_delay_base'],
220 | 'title': 'their_delay_base',
221 | 'y1': 'Time (us)',
222 | 'y2': ''
223 | },
224 | {
225 | 'data': ['our_delay', 'target_delay', 'rtt'],
226 | 'title': 'our-delay',
227 | 'y1': '',
228 | 'y2': 'Time (ms)'
229 | },
230 | {
231 | 'data': ['actual_delay', 'delay_base'],
232 | 'title': 'our_delay_base',
233 | 'y1': 'Time (us)',
234 | 'y2': ''
235 | }
236 | ]
237 |
238 | out = open('utp.gnuplot', 'w+')
239 |
240 | files = ''
241 |
242 | #print >>out, 'set xtics 0, 20'
243 | print >>out, "set term png size 1280,800"
244 | print >>out, 'set output "%s.delays.png"' % out_file
245 | print >>out, 'set xrange [0:250]'
246 | print >>out, 'set xlabel "delay (ms)"'
247 | print >>out, 'set boxwidth 1'
248 | print >>out, 'set style fill solid'
249 | print >>out, 'set ylabel "number of packets"'
250 | print >>out, 'plot "%s.histogram" using 1:2 with boxes' % out_file
251 |
252 | print >>out, "set style data steps"
253 | #print >>out, "set yrange [0:*]"
254 | print >>out, "set y2range [*:*]"
255 | files += out_file + '.delays.png '
256 | #set hidden3d
257 | #set title "Peer bandwidth distribution"
258 | #set xlabel "Ratio"
259 |
260 | for p in plot:
261 | print >>out, 'set title "%s %s"' % (p['title'], title)
262 | print >>out, 'set xlabel "time (s)"'
263 | print >>out, 'set ylabel "%s"' % p['y1']
264 | print >>out, "set tics nomirror"
265 | print >>out, 'set y2tics'
266 | print >>out, 'set y2label "%s"' % p['y2']
267 | print >>out, 'set xrange [0:*]'
268 | print >>out, "set key box"
269 | print >>out, "set term png size 1280,800"
270 | print >>out, 'set output "%s-%s.png"' % (out_file, p['title'])
271 | files += '%s-%s.png ' % (out_file, p['title'])
272 |
273 | comma = ''
274 | print >>out, "plot",
275 |
276 | for c in p['data']:
277 | if not c in metrics: continue
278 | i = columns.index(c)
279 | print >>out, '%s"%s" using 1:%d title "%s-%s" axes %s with %s' % (comma, out_file, i + 2, metrics[c][0], metrics[c][1], metrics[c][1], metrics[c][2]),
280 | comma = ', '
281 | print >>out, ''
282 |
283 | out.close()
284 |
285 | os.system("gnuplot utp.gnuplot")
286 |
287 | os.system("open %s" % files)
288 |
289 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
2 | github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
3 | github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
4 | github.com/anacrolix/envpprof v1.1.0 h1:hz8QWMN1fA01YNQsUtVvl9hBXQWWMxSnHHoOK9IdrNY=
5 | github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
6 | github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
7 | github.com/anacrolix/log v0.13.1 h1:BmVwTdxHd5VcNrLylgKwph4P4wf+5VvPgOK4yi91fTY=
8 | github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
9 | github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM=
10 | github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
11 | github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
12 | github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
13 | github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
14 | github.com/anacrolix/missinggo v1.2.1 h1:0IE3TqX5y5D0IxeMwTyIgqdDew4QrzcXaaEnJQyjHvw=
15 | github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
16 | github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw=
17 | github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
18 | github.com/anacrolix/missinggo/v2 v2.10.0 h1:pg0iO4Z/UhP2MAnmGcaMtp5ZP9kyWsusENWN9aolrkY=
19 | github.com/anacrolix/missinggo/v2 v2.10.0/go.mod h1:nCRMW6bRCMOVcw5z9BnSYKF+kDbtenx+hQuphf4bK8Y=
20 | github.com/anacrolix/mmsg v1.0.1 h1:TxfpV7kX70m3f/O7ielL/2I3OFkMPjrRCPo7+4X5AWw=
21 | github.com/anacrolix/mmsg v1.0.1/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
22 | github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778 h1:XpCDEixzXOB8yaTW/4YBzKrJdMcFI0DzpPTYNv75wzk=
23 | github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
24 | github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
25 | github.com/anacrolix/tagflag v1.1.0 h1:0lHtP7w9MczBioGf/b4jeQ+OiJWOPfQwPbBPGnkovvU=
26 | github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
27 | github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
28 | github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
29 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
30 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
31 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
32 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
33 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
34 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
36 | github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
37 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
38 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
39 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
40 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
41 | github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
42 | github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
43 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
44 | github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
45 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
46 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
47 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
48 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
49 | github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
50 | github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
51 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
52 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
53 | github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
54 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
55 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
56 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
57 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
58 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
59 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
60 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
61 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
62 | github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
63 | github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
64 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
65 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
66 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
67 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
68 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
69 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
70 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
71 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
72 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
73 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
74 | github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
75 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
76 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
77 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
78 | github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
79 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
80 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
81 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
82 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
83 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
84 | github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
85 | github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
86 | golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
87 | golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
88 | golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
89 | golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
90 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
91 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
92 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
93 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
94 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
95 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
96 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
97 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
98 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
99 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
100 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
101 |
--------------------------------------------------------------------------------
/libutp.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 | WinRTDebug
22 | Win32
23 |
24 |
25 | WinRTDebug
26 | x64
27 |
28 |
29 | WinRTRelease
30 | Win32
31 |
32 |
33 | WinRTRelease
34 | x64
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {5984D5CD-6ADD-4EB7-82E7-A555888FBBBD}
59 | libutp2012
60 | libutp
61 |
62 |
63 |
64 | StaticLibrary
65 | true
66 | v140_xp
67 | Unicode
68 |
69 |
70 | StaticLibrary
71 | true
72 | v120_xp
73 | Unicode
74 |
75 |
76 | StaticLibrary
77 | true
78 | v120
79 | Unicode
80 |
81 |
82 | StaticLibrary
83 | true
84 | v120
85 | Unicode
86 |
87 |
88 | StaticLibrary
89 | false
90 | true
91 | Unicode
92 | v140_xp
93 |
94 |
95 | StaticLibrary
96 | false
97 | true
98 | Unicode
99 | v120_xp
100 |
101 |
102 | StaticLibrary
103 | false
104 | v120
105 | true
106 | Unicode
107 |
108 |
109 | StaticLibrary
110 | false
111 | v120
112 | true
113 | Unicode
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Level3
163 | Disabled
164 | Sync
165 | 4996
166 |
167 |
168 | true
169 |
170 |
171 |
172 |
173 | Level3
174 | Disabled
175 | MultiThreadedDebugDLL
176 |
177 |
178 | true
179 |
180 |
181 |
182 |
183 | Level3
184 | Disabled
185 |
186 |
187 | true
188 |
189 |
190 |
191 |
192 | Level3
193 | Disabled
194 |
195 |
196 | true
197 |
198 |
199 |
200 |
201 | Level3
202 | MaxSpeed
203 | true
204 | true
205 | _WIN32_WINNT=0x501;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
206 | Sync
207 |
208 |
209 | true
210 | true
211 | true
212 |
213 |
214 |
215 |
216 | Level3
217 | MaxSpeed
218 | true
219 | true
220 | %(PreprocessorDefinitions)
221 | MultiThreadedDLL
222 |
223 |
224 | true
225 | true
226 | true
227 |
228 |
229 |
230 |
231 | Level3
232 | MaxSpeed
233 | true
234 | true
235 |
236 |
237 | true
238 | true
239 | true
240 |
241 |
242 |
243 |
244 | Level3
245 | MaxSpeed
246 | true
247 | true
248 |
249 |
250 | true
251 | true
252 | true
253 |
254 |
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/socket.go:
--------------------------------------------------------------------------------
1 | package utp
2 |
3 | /*
4 | #include "utp.h"
5 | #include
6 |
7 | struct utp_process_udp_args {
8 | const byte *buf;
9 | size_t len;
10 | const struct sockaddr *sa;
11 | socklen_t sal;
12 | };
13 |
14 | void process_received_messages(utp_context *ctx, struct utp_process_udp_args *args, size_t argslen)
15 | {
16 | bool gotUtp = false;
17 | size_t i;
18 | for (i = 0; i < argslen; i++) {
19 | struct utp_process_udp_args *a = &args[i];
20 | //if (!a->len) continue;
21 | if (utp_process_udp(ctx, a->buf, a->len, a->sa, a->sal)) {
22 | gotUtp = true;
23 | }
24 | }
25 | if (gotUtp) {
26 | utp_issue_deferred_acks(ctx);
27 | utp_check_timeouts(ctx);
28 | }
29 | }
30 | */
31 | import "C"
32 |
33 | import (
34 | "context"
35 | "errors"
36 | "fmt"
37 | "math"
38 | "net"
39 | "syscall"
40 | "time"
41 | "unsafe"
42 |
43 | "github.com/anacrolix/log"
44 | "github.com/anacrolix/missinggo"
45 | "github.com/anacrolix/missinggo/inproc"
46 | "github.com/anacrolix/mmsg"
47 | )
48 |
49 | const (
50 | utpCheckTimeoutInterval = 500 * time.Millisecond
51 | issueDeferredUtpAcksDelay = 1000 * time.Microsecond
52 | )
53 |
54 | type Socket struct {
55 | pc net.PacketConn
56 | ctx *utpContext
57 | backlog chan *Conn
58 | closed bool
59 | conns map[*C.utp_socket]*Conn
60 | nonUtpReads chan packet
61 | writeDeadline time.Time
62 | readDeadline time.Time
63 |
64 | // This is called without the package mutex, without knowing if the result will be needed.
65 | asyncFirewallCallback FirewallCallback
66 | // Whether the next accept is to be blocked.
67 | asyncBlock bool
68 |
69 | // This is called with the package mutex, and preferred.
70 | syncFirewallCallback FirewallCallback
71 |
72 | acksScheduled bool
73 | ackTimer *time.Timer
74 |
75 | utpTimeoutChecker *time.Timer
76 |
77 | logger log.Logger
78 | }
79 |
80 | // A firewall callback returns true if an incoming connection request should be ignored. This is
81 | // better than just accepting and closing, as it means no acknowledgement packet is sent.
82 | type FirewallCallback func(net.Addr) bool
83 |
84 | var (
85 | _ net.PacketConn = (*Socket)(nil)
86 | _ net.Listener = (*Socket)(nil)
87 | errSocketClosed = errors.New("Socket closed")
88 | )
89 |
90 | type packet struct {
91 | b []byte
92 | from net.Addr
93 | }
94 |
95 | func listenPacket(network, addr string) (pc net.PacketConn, err error) {
96 | if network == "inproc" {
97 | return inproc.ListenPacket(network, addr)
98 | }
99 | return net.ListenPacket(network, addr)
100 | }
101 |
102 | type NewSocketOpt func(s *Socket)
103 |
104 | func WithLogger(l log.Logger) NewSocketOpt {
105 | return func(s *Socket) {
106 | s.logger = l
107 | }
108 | }
109 |
110 | func NewSocket(network, addr string, opts ...NewSocketOpt) (*Socket, error) {
111 | pc, err := listenPacket(network, addr)
112 | if err != nil {
113 | return nil, err
114 | }
115 |
116 | s := &Socket{
117 | pc: pc,
118 | backlog: make(chan *Conn, 5),
119 | conns: make(map[*C.utp_socket]*Conn),
120 | nonUtpReads: make(chan packet, 100),
121 | logger: Logger,
122 | }
123 | s.ackTimer = time.AfterFunc(math.MaxInt64, s.ackTimerFunc)
124 | s.ackTimer.Stop()
125 |
126 | for _, opt := range opts {
127 | opt(s)
128 | }
129 |
130 | func() {
131 | mu.Lock()
132 | defer mu.Unlock()
133 | ctx := (*utpContext)(C.utp_init(2))
134 | if ctx == nil {
135 | panic(ctx)
136 | }
137 | s.ctx = ctx
138 | ctx.setCallbacks()
139 | if utpLogging {
140 | ctx.setOption(C.UTP_LOG_NORMAL, 1)
141 | ctx.setOption(C.UTP_LOG_MTU, 1)
142 | ctx.setOption(C.UTP_LOG_DEBUG, 1)
143 | }
144 | libContextToSocket[ctx] = s
145 | s.utpTimeoutChecker = time.AfterFunc(0, s.timeoutCheckerTimerFunc)
146 | }()
147 | go s.packetReader()
148 | return s, nil
149 | }
150 |
151 | func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
152 | delete(s.conns, ls)
153 | }
154 |
155 | func (s *Socket) newConn(us *C.utp_socket) *Conn {
156 | c := &Conn{
157 | s: s,
158 | us: us,
159 | localAddr: s.pc.LocalAddr(),
160 | }
161 | c.cond.L = &mu
162 | s.conns[us] = c
163 | c.writeDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
164 | c.readDeadlineTimer = time.AfterFunc(-1, c.cond.Broadcast)
165 | return c
166 | }
167 |
168 | const maxNumBuffers = 16
169 |
170 | func (s *Socket) packetReader() {
171 | mc := mmsg.NewConn(s.pc)
172 | // Increasing the messages increases the memory use, but also means we can
173 | // reduces utp_issue_deferred_acks and syscalls which should improve
174 | // efficiency. On the flip side, not all OSs implement batched reads.
175 | ms := make([]mmsg.Message, func() int {
176 | if mc.Err() == nil {
177 | return maxNumBuffers
178 | } else {
179 | return 1
180 | }
181 | }())
182 | for i := range ms {
183 | // The IPv4 UDP limit is allegedly about 64 KiB, and this message has
184 | // been seen on receiving on Windows with just 0x1000: wsarecvfrom: A
185 | // message sent on a datagram socket was larger than the internal
186 | // message buffer or some other network limit, or the buffer used to
187 | // receive a datagram into was smaller than the datagram itself.
188 | ms[i].Buffers = [][]byte{make([]byte, 0x10000)}
189 | }
190 | // Some crap OSs like Windoze will raise errors in Reads that don't
191 | // actually mean we should stop.
192 | consecutiveErrors := 0
193 | for {
194 | // In C, all the reads are processed and when it threatens to block,
195 | // we're supposed to call utp_issue_deferred_acks.
196 | n, err := mc.RecvMsgs(ms)
197 | if n == 1 {
198 | singleMsgRecvs.Add(1)
199 | }
200 | if n > 1 {
201 | multiMsgRecvs.Add(1)
202 | }
203 | if err != nil {
204 | mu.Lock()
205 | closed := s.closed
206 | mu.Unlock()
207 | if closed {
208 | // We don't care.
209 | return
210 | }
211 | // See https://github.com/anacrolix/torrent/issues/83. If we get
212 | // an endless stream of errors (such as the PacketConn being
213 | // Closed outside of our control, this work around may need to be
214 | // reconsidered.
215 | s.logger.Printf("ignoring socket read error: %s", err)
216 | consecutiveErrors++
217 | if consecutiveErrors >= 100 {
218 | s.logger.Print("too many consecutive errors, closing socket")
219 | s.Close()
220 | return
221 | }
222 | continue
223 | }
224 | consecutiveErrors = 0
225 | expMap.Add("successful mmsg receive calls", 1)
226 | expMap.Add("received messages", int64(n))
227 | s.processReceivedMessages(ms[:n])
228 | }
229 | }
230 |
231 | func (s *Socket) processReceivedMessages(ms []mmsg.Message) {
232 | mu.Lock()
233 | defer mu.Unlock()
234 | if s.closed {
235 | return
236 | }
237 | if processPacketsInC {
238 | var args [maxNumBuffers]C.struct_utp_process_udp_args
239 | for i, m := range ms {
240 | a := &args[i]
241 | a.buf = (*C.byte)(&m.Buffers[0][0])
242 | a.len = C.size_t(m.N)
243 | var rsa syscall.RawSockaddrAny
244 | rsa, a.sal = netAddrToLibSockaddr(m.Addr)
245 | a.sa = (*C.struct_sockaddr)(unsafe.Pointer(&rsa))
246 | }
247 | C.process_received_messages(s.ctx.asCPtr(), &args[0], C.size_t(len(ms)))
248 | } else {
249 | gotUtp := false
250 | for _, m := range ms {
251 | gotUtp = s.processReceivedMessage(m.Buffers[0][:m.N], m.Addr) || gotUtp
252 | }
253 | if gotUtp && !s.closed {
254 | s.afterReceivingUtpMessages()
255 | }
256 | }
257 | }
258 |
259 | func (s *Socket) afterReceivingUtpMessages() {
260 | if s.acksScheduled {
261 | return
262 | }
263 | s.ackTimer.Reset(issueDeferredUtpAcksDelay)
264 | s.acksScheduled = true
265 | }
266 |
267 | func (s *Socket) issueDeferredAcks() {
268 | expMap.Add("utp_issue_deferred_acks calls", 1)
269 | C.utp_issue_deferred_acks(s.ctx.asCPtr())
270 | }
271 |
272 | func (s *Socket) checkUtpTimeouts() {
273 | expMap.Add("utp_check_timeouts calls", 1)
274 | C.utp_check_timeouts(s.ctx.asCPtr())
275 | }
276 |
277 | func (s *Socket) ackTimerFunc() {
278 | mu.Lock()
279 | defer mu.Unlock()
280 | if !s.acksScheduled || s.ctx == nil {
281 | return
282 | }
283 | s.acksScheduled = false
284 | s.issueDeferredAcks()
285 | }
286 |
287 | func (s *Socket) processReceivedMessage(b []byte, addr net.Addr) (utp bool) {
288 | if s.utpProcessUdp(b, addr) {
289 | socketUtpPacketsReceived.Add(1)
290 | return true
291 | } else {
292 | s.onReadNonUtp(b, addr)
293 | return false
294 | }
295 | }
296 |
297 | // Process packet batches entirely from C, reducing CGO overhead. Currently
298 | // requires GODEBUG=cgocheck=0.
299 | const processPacketsInC = false
300 |
301 | var staticRsa syscall.RawSockaddrAny
302 |
303 | // Wraps libutp's utp_process_udp, returning relevant information.
304 | func (s *Socket) utpProcessUdp(b []byte, addr net.Addr) (utp bool) {
305 | if len(b) == 0 {
306 | // The implementation of utp_process_udp rejects null buffers, and
307 | // anything smaller than the UTP header size. It's also prone to
308 | // assert on those, which we don't want to trigger.
309 | return false
310 | }
311 | if missinggo.AddrPort(addr) == 0 {
312 | return false
313 | }
314 | mu.Unlock()
315 | // TODO: If it's okay to call the firewall callback without the package lock, aren't we assuming
316 | // that the next UDP packet to be processed by libutp has to be the one we've just used the
317 | // callback for? Why can't we assign directly to Socket.asyncBlock?
318 | asyncBlock := func() bool {
319 | if s.asyncFirewallCallback == nil || s.syncFirewallCallback != nil {
320 | return false
321 | }
322 | return s.asyncFirewallCallback(addr)
323 | }()
324 | mu.Lock()
325 | s.asyncBlock = asyncBlock
326 | if s.closed {
327 | return false
328 | }
329 | var sal C.socklen_t
330 | staticRsa, sal = netAddrToLibSockaddr(addr)
331 | ret := C.utp_process_udp(s.ctx.asCPtr(), (*C.byte)(&b[0]), C.size_t(len(b)), (*C.struct_sockaddr)(unsafe.Pointer(&staticRsa)), sal)
332 | switch ret {
333 | case 1:
334 | return true
335 | case 0:
336 | return false
337 | default:
338 | panic(ret)
339 | }
340 | }
341 |
342 | func (s *Socket) timeoutCheckerTimerFunc() {
343 | mu.Lock()
344 | ok := s.ctx != nil
345 | if ok {
346 | s.checkUtpTimeouts()
347 | }
348 | if ok {
349 | s.utpTimeoutChecker.Reset(utpCheckTimeoutInterval)
350 | }
351 | mu.Unlock()
352 | }
353 |
354 | func (s *Socket) Close() error {
355 | mu.Lock()
356 | defer mu.Unlock()
357 | return s.closeLocked()
358 | }
359 |
360 | func (s *Socket) closeLocked() error {
361 | if s.closed {
362 | return nil
363 | }
364 | // Calling this deletes the pointer. It must not be referred to after
365 | // this.
366 | C.utp_destroy(s.ctx.asCPtr())
367 | s.ctx = nil
368 | s.pc.Close()
369 | close(s.backlog)
370 | close(s.nonUtpReads)
371 | s.closed = true
372 | s.ackTimer.Stop()
373 | s.utpTimeoutChecker.Stop()
374 | s.acksScheduled = false
375 | return nil
376 | }
377 |
378 | func (s *Socket) Addr() net.Addr {
379 | return s.pc.LocalAddr()
380 | }
381 |
382 | func (s *Socket) LocalAddr() net.Addr {
383 | return s.pc.LocalAddr()
384 | }
385 |
386 | func (s *Socket) Accept() (net.Conn, error) {
387 | nc, ok := <-s.backlog
388 | if !ok {
389 | return nil, errors.New("closed")
390 | }
391 | return nc, nil
392 | }
393 |
394 | func (s *Socket) Dial(addr string) (net.Conn, error) {
395 | return s.DialTimeout(addr, 0)
396 | }
397 |
398 | func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
399 | ctx := context.Background()
400 | if timeout != 0 {
401 | var cancel context.CancelFunc
402 | ctx, cancel = context.WithTimeout(ctx, timeout)
403 | defer cancel()
404 | }
405 | return s.DialContext(ctx, "", addr)
406 | }
407 |
408 | func (s *Socket) resolveAddr(network, addr string) (net.Addr, error) {
409 | if network == "" {
410 | network = s.Addr().Network()
411 | }
412 | return resolveAddr(network, addr)
413 | }
414 |
415 | func resolveAddr(network, addr string) (net.Addr, error) {
416 | switch network {
417 | case "inproc":
418 | return inproc.ResolveAddr(network, addr)
419 | default:
420 | return net.ResolveUDPAddr(network, addr)
421 | }
422 | }
423 |
424 | // Passing an empty network will use the network of the Socket's listener.
425 | func (s *Socket) DialContext(ctx context.Context, network, addr string) (_ net.Conn, err error) {
426 | if network == "" {
427 | network = s.pc.LocalAddr().Network()
428 | }
429 | ua, err := resolveAddr(network, addr)
430 | if err != nil {
431 | return nil, fmt.Errorf("error resolving address: %v", err)
432 | }
433 | sa, sl := netAddrToLibSockaddr(ua)
434 | mu.Lock()
435 | defer mu.Unlock()
436 | if s.closed {
437 | return nil, errSocketClosed
438 | }
439 | utpSock := utpCreateSocketAndConnect(s.ctx.asCPtr(), sa, sl)
440 | c := s.newConn(utpSock)
441 | c.setRemoteAddr()
442 | err = c.waitForConnect(ctx)
443 | if err != nil {
444 | c.close()
445 | return
446 | }
447 | return c, err
448 | }
449 |
450 | func (s *Socket) pushBacklog(c *Conn) {
451 | select {
452 | case s.backlog <- c:
453 | default:
454 | c.close()
455 | }
456 | }
457 |
458 | func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
459 | p, ok := <-s.nonUtpReads
460 | if !ok {
461 | err = errors.New("closed")
462 | return
463 | }
464 | n = copy(b, p.b)
465 | addr = p.from
466 | return
467 | }
468 |
469 | func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
470 | if s.closed {
471 | return
472 | }
473 | socketNonUtpPacketsReceived.Add(1)
474 | select {
475 | case s.nonUtpReads <- packet{append([]byte(nil), b...), from}:
476 | default:
477 | // log.Printf("dropped non utp packet: no room in buffer")
478 | nonUtpPacketsDropped.Add(1)
479 | }
480 | }
481 |
482 | func (s *Socket) SetReadDeadline(t time.Time) error {
483 | panic("not implemented")
484 | }
485 |
486 | func (s *Socket) SetWriteDeadline(t time.Time) error {
487 | panic("not implemented")
488 | }
489 |
490 | func (s *Socket) SetDeadline(t time.Time) error {
491 | panic("not implemented")
492 | }
493 |
494 | func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
495 | return s.pc.WriteTo(b, addr)
496 | }
497 |
498 | func (s *Socket) ReadBufferLen() int {
499 | mu.Lock()
500 | defer mu.Unlock()
501 | return int(C.utp_context_get_option(s.ctx.asCPtr(), C.UTP_RCVBUF))
502 | }
503 |
504 | func (s *Socket) WriteBufferLen() int {
505 | mu.Lock()
506 | defer mu.Unlock()
507 | return int(C.utp_context_get_option(s.ctx.asCPtr(), C.UTP_SNDBUF))
508 | }
509 |
510 | func (s *Socket) SetWriteBufferLen(len int) {
511 | mu.Lock()
512 | defer mu.Unlock()
513 | i := C.utp_context_set_option(s.ctx.asCPtr(), C.UTP_SNDBUF, C.int(len))
514 | if i != 0 {
515 | panic(i)
516 | }
517 | }
518 |
519 | func (s *Socket) SetOption(opt Option, val int) int {
520 | mu.Lock()
521 | defer mu.Unlock()
522 | return int(C.utp_context_set_option(s.ctx.asCPtr(), opt, C.int(val)))
523 | }
524 |
525 | // The callback is used before each packet is processed by libutp without the this package's mutex
526 | // being held. libutp may not actually need the result as the packet might not be a connection
527 | // attempt. If the callback function is expensive, it may be worth setting a synchronous callback
528 | // using SetSyncFirewallCallback.
529 | func (s *Socket) SetFirewallCallback(f FirewallCallback) {
530 | mu.Lock()
531 | s.asyncFirewallCallback = f
532 | mu.Unlock()
533 | }
534 |
535 | // SetSyncFirewallCallback sets a synchronous firewall callback. It's only called as needed by
536 | // libutp. It is called with the package-wide mutex held. Any locks acquired by the callback should
537 | // not also be held by code that might use this package.
538 | func (s *Socket) SetSyncFirewallCallback(f FirewallCallback) {
539 | mu.Lock()
540 | s.syncFirewallCallback = f
541 | mu.Unlock()
542 | }
543 |
--------------------------------------------------------------------------------