├── .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 | [![GoDoc](https://godoc.org/github.com/anacrolix/go-libutp?status.svg)](http://godoc.org/github.com/anacrolix/go-libutp) 4 | [![CircleCI](https://circleci.com/gh/anacrolix/go-libutp.svg?style=shield)](https://circleci.com/gh/anacrolix/go-libutp) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/anacrolix/go-libutp)](https://goreportcard.com/report/github.com/anacrolix/go-libutp) 6 | [![Appveyor Status](https://ci.appveyor.com/api/projects/status/github/anacrolix/go-libutp?branch=master&svg=true)](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 | --------------------------------------------------------------------------------