init
This commit is contained in:
362
vendor/github.com/anacrolix/utp/LICENSE
generated
vendored
Normal file
362
vendor/github.com/anacrolix/utp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
1
vendor/github.com/anacrolix/utp/NOTES
generated
vendored
Normal file
1
vendor/github.com/anacrolix/utp/NOTES
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* Perhaps state packets should be sent when the wnd_size would change because user-code has made Reads from the read buffer. Otherwise if the receive window size was advertised as zero, we might never receive a packet or send a state packet again.
|
17
vendor/github.com/anacrolix/utp/README.md
generated
vendored
Normal file
17
vendor/github.com/anacrolix/utp/README.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# utp
|
||||
[](https://godoc.org/github.com/anacrolix/utp)
|
||||
[](https://circleci.com/gh/anacrolix/utp)
|
||||
|
||||
Package utp implements uTP, the micro transport protocol as used with Bittorrent. It opts for simplicity and reliability over strict adherence to the (poor) spec.
|
||||
|
||||
## Supported
|
||||
|
||||
* Multiple uTP connections switched on a single PacketConn, including those initiated locally.
|
||||
* Raw access to the PacketConn for non-uTP purposes, like sharing the PacketConn with a DHT implementation.
|
||||
|
||||
## Implementation characteristics
|
||||
|
||||
* There is no MTU path discovery.
|
||||
* A fixed 64 slot selective ack window is used in both sending and receiving.
|
||||
|
||||
Patches welcomed.
|
15
vendor/github.com/anacrolix/utp/addr.go
generated
vendored
Normal file
15
vendor/github.com/anacrolix/utp/addr.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package utp
|
||||
|
||||
import "net"
|
||||
|
||||
type addr struct {
|
||||
socket net.Addr
|
||||
}
|
||||
|
||||
func (me addr) Network() string {
|
||||
return "utp/" + me.socket.Network()
|
||||
}
|
||||
|
||||
func (me addr) String() string {
|
||||
return me.socket.String()
|
||||
}
|
618
vendor/github.com/anacrolix/utp/conn.go
generated
vendored
Normal file
618
vendor/github.com/anacrolix/utp/conn.go
generated
vendored
Normal file
@@ -0,0 +1,618 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
// Conn is a uTP stream and implements net.Conn. It owned by a Socket, which
|
||||
// handles dispatching packets to and from Conns.
|
||||
type Conn struct {
|
||||
recv_id, send_id uint16
|
||||
seq_nr, ack_nr uint16
|
||||
lastAck uint16
|
||||
lastTimeDiff uint32
|
||||
peerWndSize uint32
|
||||
cur_window uint32
|
||||
connKey connKey
|
||||
|
||||
// Data waiting to be Read.
|
||||
readBuf []byte
|
||||
readBufNotEmpty missinggo.Event
|
||||
|
||||
socket *Socket
|
||||
remoteSocketAddr net.Addr
|
||||
// The uTP timestamp.
|
||||
startTimestamp uint32
|
||||
// When the conn was allocated.
|
||||
created time.Time
|
||||
|
||||
synAcked bool // Syn is acked by the acceptor. Initiator also tracks it.
|
||||
gotFin missinggo.Event
|
||||
wroteFin missinggo.Event
|
||||
err error
|
||||
closed missinggo.Event
|
||||
destroyed missinggo.Event
|
||||
canWrite missinggo.Event
|
||||
|
||||
unackedSends []*send
|
||||
// Inbound payloads, the first is ack_nr+1.
|
||||
inbound []recv
|
||||
inboundWnd int
|
||||
connDeadlines
|
||||
latencies []time.Duration
|
||||
|
||||
// We need to send state packet.
|
||||
pendingSendState bool
|
||||
sendPendingSendSendStateTimer *time.Timer
|
||||
// Send state is being delayed until sendStateTimer fires, which may have
|
||||
// been set at the beginning of a batch of received packets.
|
||||
sendPendingSendStateTimerActive bool
|
||||
|
||||
// This timer fires when no packet has been received for a period.
|
||||
packetReadTimeoutTimer *time.Timer
|
||||
}
|
||||
|
||||
var (
|
||||
_ net.Conn = &Conn{}
|
||||
)
|
||||
|
||||
func (c *Conn) age() time.Duration {
|
||||
return time.Since(c.created)
|
||||
}
|
||||
|
||||
func (c *Conn) timestamp() uint32 {
|
||||
return nowTimestamp() - c.startTimestamp
|
||||
}
|
||||
|
||||
func (c *Conn) sendPendingSendStateTimerCallback() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.sendPendingSendStateTimerActive = false
|
||||
c.sendPendingSendSendStateTimer.Stop()
|
||||
c.sendPendingState()
|
||||
}
|
||||
|
||||
// Send a state packet, if one is needed.
|
||||
func (c *Conn) sendPendingState() {
|
||||
if c.destroyed.IsSet() {
|
||||
c.sendReset()
|
||||
} else {
|
||||
c.sendState()
|
||||
}
|
||||
}
|
||||
|
||||
// So far as the spec makes clear, this is how many more, as-yet-unacked bytes
|
||||
// we can fit into our receive buffers.
|
||||
func (c *Conn) wndSize() uint32 {
|
||||
if len(c.readBuf)+c.inboundWnd > readBufferLen {
|
||||
return 0
|
||||
}
|
||||
return uint32(readBufferLen - len(c.readBuf) - c.inboundWnd)
|
||||
}
|
||||
|
||||
func (c *Conn) makePacket(_type st, connID, seqNr uint16, payload []byte) (p []byte) {
|
||||
var selAck selectiveAckBitmask
|
||||
for i := 1; i < len(c.inbound); i++ {
|
||||
if c.inbound[i].seen {
|
||||
selAck.SetBit(i - 1)
|
||||
}
|
||||
}
|
||||
h := header{
|
||||
Type: _type,
|
||||
Version: 1,
|
||||
ConnID: connID,
|
||||
SeqNr: seqNr,
|
||||
AckNr: c.ack_nr,
|
||||
WndSize: c.wndSize(),
|
||||
Timestamp: c.timestamp(),
|
||||
TimestampDiff: c.lastTimeDiff,
|
||||
}
|
||||
if len(selAck.Bytes) != 0 {
|
||||
// The spec requires the number of bytes for a selective ACK to be at
|
||||
// least 4, and a multiple of 4.
|
||||
if len(selAck.Bytes)%4 != 0 {
|
||||
panic(len(selAck.Bytes))
|
||||
}
|
||||
h.Extensions = append(h.Extensions, extensionField{
|
||||
Type: extensionTypeSelectiveAck,
|
||||
Bytes: selAck.Bytes,
|
||||
})
|
||||
}
|
||||
p = sendBufferPool.Get().([]byte)[:0:minMTU]
|
||||
n := h.Marshal(p)
|
||||
p = p[:n]
|
||||
// Extension headers are currently fixed in size.
|
||||
if n > maxHeaderSize {
|
||||
panic("header has unexpected size")
|
||||
}
|
||||
p = append(p, payload...)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the given payload with an up to date header.
|
||||
func (c *Conn) send(_type st, connID uint16, payload []byte, seqNr uint16) (err error) {
|
||||
p := c.makePacket(_type, connID, seqNr, payload)
|
||||
n1, err := c.socket.writeTo(p, c.remoteSocketAddr)
|
||||
sendBufferPool.Put(p[:0:minMTU])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n1 != len(p) {
|
||||
panic(n1)
|
||||
}
|
||||
if c.unpendSendState() && _type != stState {
|
||||
// We needed to send a state packet, but this packet suppresses that
|
||||
// need.
|
||||
unsentStatePackets.Add(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) unpendSendState() (wasPending bool) {
|
||||
wasPending = c.pendingSendState
|
||||
c.pendingSendState = false
|
||||
c.sendPendingSendSendStateTimer.Stop()
|
||||
c.sendPendingSendStateTimerActive = false
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) pendSendState() {
|
||||
if c.pendingSendState {
|
||||
// A state packet is pending but hasn't been sent, and we want to send
|
||||
// another.
|
||||
unsentStatePackets.Add(1)
|
||||
}
|
||||
c.pendingSendState = true
|
||||
if !c.sendPendingSendStateTimerActive {
|
||||
c.sendPendingSendSendStateTimer.Reset(pendingSendStateDelay)
|
||||
c.sendPendingSendStateTimerActive = true
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Conn) writeSyn() {
|
||||
me.write(stSyn, me.recv_id, nil, me.seq_nr)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) write(_type st, connID uint16, payload []byte, seqNr uint16) (n int, err error) {
|
||||
switch _type {
|
||||
case stSyn, stFin, stData:
|
||||
default:
|
||||
panic(_type)
|
||||
}
|
||||
if c.wroteFin.IsSet() {
|
||||
panic("can't write after fin")
|
||||
}
|
||||
if len(payload) > maxPayloadSize {
|
||||
payload = payload[:maxPayloadSize]
|
||||
}
|
||||
err = c.send(_type, connID, payload, seqNr)
|
||||
if err != nil {
|
||||
c.destroy(fmt.Errorf("error sending packet: %s", err))
|
||||
return
|
||||
}
|
||||
n = len(payload)
|
||||
// Copy payload so caller to write can continue to use the buffer.
|
||||
if payload != nil {
|
||||
payload = append(sendBufferPool.Get().([]byte)[:0:minMTU], payload...)
|
||||
}
|
||||
send := &send{
|
||||
payloadSize: uint32(len(payload)),
|
||||
started: missinggo.MonotonicNow(),
|
||||
_type: _type,
|
||||
connID: connID,
|
||||
payload: payload,
|
||||
seqNr: seqNr,
|
||||
conn: c,
|
||||
}
|
||||
send.resendTimer = time.AfterFunc(c.resendTimeout(), send.timeoutResend)
|
||||
c.unackedSends = append(c.unackedSends, send)
|
||||
c.cur_window += send.payloadSize
|
||||
c.updateCanWrite()
|
||||
c.seq_nr++
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Introduce a minimum latency.
|
||||
func (c *Conn) latency() (ret time.Duration) {
|
||||
if len(c.latencies) == 0 {
|
||||
return initialLatency
|
||||
}
|
||||
for _, l := range c.latencies {
|
||||
ret += l
|
||||
}
|
||||
ret = (ret + time.Duration(len(c.latencies)) - 1) / time.Duration(len(c.latencies))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) sendState() {
|
||||
c.send(stState, c.send_id, nil, c.seq_nr)
|
||||
sentStatePackets.Add(1)
|
||||
}
|
||||
|
||||
func (c *Conn) sendReset() {
|
||||
c.send(stReset, c.send_id, nil, c.seq_nr)
|
||||
}
|
||||
|
||||
func (c *Conn) addLatency(l time.Duration) {
|
||||
c.latencies = append(c.latencies, l)
|
||||
if len(c.latencies) > 10 {
|
||||
c.latencies = c.latencies[len(c.latencies)-10:]
|
||||
}
|
||||
}
|
||||
|
||||
// Ack our send with the given sequence number.
|
||||
func (c *Conn) ack(nr uint16) {
|
||||
if !seqLess(c.lastAck, nr) {
|
||||
// Already acked.
|
||||
return
|
||||
}
|
||||
i := nr - c.lastAck - 1
|
||||
if int(i) >= len(c.unackedSends) {
|
||||
// Remote has acknowledged receipt of packets we haven't even sent.
|
||||
acksReceivedAheadOfSyn.Add(1)
|
||||
// log.Printf("got ack ahead of syn (%x > %x)", nr, c.seq_nr-1)
|
||||
return
|
||||
}
|
||||
s := c.unackedSends[i]
|
||||
latency, first := s.Ack()
|
||||
if first {
|
||||
c.cur_window -= s.payloadSize
|
||||
c.updateCanWrite()
|
||||
c.addLatency(latency)
|
||||
}
|
||||
// Trim sends that aren't needed anymore.
|
||||
for len(c.unackedSends) != 0 {
|
||||
if !c.unackedSends[0].acked.IsSet() {
|
||||
// Can't trim unacked sends any further.
|
||||
return
|
||||
}
|
||||
// Trim the front of the unacked sends.
|
||||
c.unackedSends = c.unackedSends[1:]
|
||||
c.updateCanWrite()
|
||||
c.lastAck++
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) ackTo(nr uint16) {
|
||||
if !seqLess(nr, c.seq_nr) {
|
||||
return
|
||||
}
|
||||
for seqLess(c.lastAck, nr) {
|
||||
c.ack(c.lastAck + 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the send state for the sequence number. Returns nil if there's no
|
||||
// outstanding send for that sequence number.
|
||||
func (c *Conn) seqSend(seqNr uint16) *send {
|
||||
if !seqLess(c.lastAck, seqNr) {
|
||||
// Presumably already acked.
|
||||
return nil
|
||||
}
|
||||
i := int(seqNr - c.lastAck - 1)
|
||||
if i >= len(c.unackedSends) {
|
||||
// No such send.
|
||||
return nil
|
||||
}
|
||||
return c.unackedSends[i]
|
||||
}
|
||||
|
||||
func (c *Conn) resendTimeout() time.Duration {
|
||||
l := c.latency()
|
||||
ret := missinggo.JitterDuration(3*l, l)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Conn) ackSkipped(seqNr uint16) {
|
||||
send := c.seqSend(seqNr)
|
||||
if send == nil {
|
||||
return
|
||||
}
|
||||
send.acksSkipped++
|
||||
if send.acked.IsSet() {
|
||||
return
|
||||
}
|
||||
switch send.acksSkipped {
|
||||
case 3, 60:
|
||||
ackSkippedResends.Add(1)
|
||||
send.resend()
|
||||
send.resendTimer.Reset(c.resendTimeout() * time.Duration(send.numResends))
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a packet destined for this connection.
|
||||
func (c *Conn) receivePacket(h header, payload []byte) {
|
||||
c.packetReadTimeoutTimer.Reset(packetReadTimeout)
|
||||
c.processDelivery(h, payload)
|
||||
}
|
||||
|
||||
func (c *Conn) receivePacketTimeoutCallback() {
|
||||
mu.Lock()
|
||||
c.destroy(errors.New("no packet read timeout"))
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *Conn) lazyDestroy() {
|
||||
if c.wroteFin.IsSet() && len(c.unackedSends) <= 1 && (c.gotFin.IsSet() || c.closed.IsSet()) {
|
||||
c.destroy(errors.New("lazily destroyed"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) processDelivery(h header, payload []byte) {
|
||||
deliveriesProcessed.Add(1)
|
||||
defer c.lazyDestroy()
|
||||
c.assertHeader(h)
|
||||
c.peerWndSize = h.WndSize
|
||||
c.applyAcks(h)
|
||||
if h.Timestamp == 0 {
|
||||
c.lastTimeDiff = 0
|
||||
} else {
|
||||
c.lastTimeDiff = c.timestamp() - h.Timestamp
|
||||
}
|
||||
|
||||
if h.Type == stReset {
|
||||
c.destroy(errors.New("peer reset"))
|
||||
return
|
||||
}
|
||||
if !c.synAcked {
|
||||
if h.Type != stState {
|
||||
return
|
||||
}
|
||||
c.synAcked = true
|
||||
c.updateCanWrite()
|
||||
c.ack_nr = h.SeqNr - 1
|
||||
return
|
||||
}
|
||||
if h.Type == stState {
|
||||
return
|
||||
}
|
||||
// Even if we didn't need or want this packet, we need to inform the peer
|
||||
// what our state is, in case they missed something.
|
||||
c.pendSendState()
|
||||
if !seqLess(c.ack_nr, h.SeqNr) {
|
||||
// Already received this packet.
|
||||
return
|
||||
}
|
||||
inboundIndex := int(h.SeqNr - c.ack_nr - 1)
|
||||
if inboundIndex < len(c.inbound) && c.inbound[inboundIndex].seen {
|
||||
// Already received this packet.
|
||||
return
|
||||
}
|
||||
// Derived from running in production:
|
||||
// grep -oP '(?<=packet out of order, index=)\d+' log | sort -n | uniq -c
|
||||
// 64 should correspond to 8 bytes of selective ack.
|
||||
if inboundIndex >= maxUnackedInbound {
|
||||
// Discard packet too far ahead.
|
||||
if logLevel >= 1 {
|
||||
log.Printf("received packet from %s %d ahead of next seqnr (%x > %x)", c.remoteSocketAddr, inboundIndex, h.SeqNr, c.ack_nr+1)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Extend inbound so the new packet has a place.
|
||||
for inboundIndex >= len(c.inbound) {
|
||||
c.inbound = append(c.inbound, recv{})
|
||||
}
|
||||
c.inbound[inboundIndex] = recv{true, payload, h.Type}
|
||||
c.inboundWnd += len(payload)
|
||||
c.processInbound()
|
||||
}
|
||||
|
||||
func (c *Conn) applyAcks(h header) {
|
||||
c.ackTo(h.AckNr)
|
||||
for _, ext := range h.Extensions {
|
||||
switch ext.Type {
|
||||
case extensionTypeSelectiveAck:
|
||||
c.ackSkipped(h.AckNr + 1)
|
||||
bitmask := selectiveAckBitmask{ext.Bytes}
|
||||
for i := 0; i < bitmask.NumBits(); i++ {
|
||||
if bitmask.BitIsSet(i) {
|
||||
nr := h.AckNr + 2 + uint16(i)
|
||||
// log.Printf("selectively acked %d", nr)
|
||||
c.ack(nr)
|
||||
} else {
|
||||
c.ackSkipped(h.AckNr + 2 + uint16(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) assertHeader(h header) {
|
||||
if h.Type == stSyn {
|
||||
if h.ConnID != c.send_id {
|
||||
panic(fmt.Sprintf("%d != %d", h.ConnID, c.send_id))
|
||||
}
|
||||
} else {
|
||||
if h.ConnID != c.recv_id {
|
||||
panic("erroneous delivery")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) updateReadBufNotEmpty() {
|
||||
c.readBufNotEmpty.SetBool(len(c.readBuf) != 0)
|
||||
}
|
||||
|
||||
func (c *Conn) processInbound() {
|
||||
// Consume consecutive next packets.
|
||||
for !c.gotFin.IsSet() && len(c.inbound) > 0 && c.inbound[0].seen && len(c.readBuf) < readBufferLen {
|
||||
c.ack_nr++
|
||||
p := c.inbound[0]
|
||||
c.inbound = c.inbound[1:]
|
||||
c.inboundWnd -= len(p.data)
|
||||
c.readBuf = append(c.readBuf, p.data...)
|
||||
c.updateReadBufNotEmpty()
|
||||
if p.Type == stFin {
|
||||
c.gotFin.Set()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) waitAck(seq uint16) {
|
||||
send := c.seqSend(seq)
|
||||
if send == nil {
|
||||
return
|
||||
}
|
||||
missinggo.WaitEvents(&mu, &send.acked, &c.destroyed)
|
||||
return
|
||||
}
|
||||
|
||||
// Waits for sent SYN to be ACKed. Returns any errors.
|
||||
func (c *Conn) recvSynAck() (err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.waitAck(1)
|
||||
if c.err != nil {
|
||||
err = c.err
|
||||
}
|
||||
c.synAcked = true
|
||||
c.updateCanWrite()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) writeFin() {
|
||||
if c.wroteFin.IsSet() {
|
||||
return
|
||||
}
|
||||
c.write(stFin, c.send_id, nil, c.seq_nr)
|
||||
c.wroteFin.Set()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) destroy(reason error) {
|
||||
c.destroyed.Set()
|
||||
if c.err == nil {
|
||||
c.err = reason
|
||||
}
|
||||
c.detach()
|
||||
}
|
||||
|
||||
func (c *Conn) closeNow() (err error) {
|
||||
c.closed.Set()
|
||||
c.writeFin()
|
||||
c.destroy(errors.New("destroyed"))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) Close() (err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.closed.Set()
|
||||
c.writeFin()
|
||||
c.lazyDestroy()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return addr{c.socket.Addr()}
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for {
|
||||
n = copy(b, c.readBuf)
|
||||
c.readBuf = c.readBuf[n:]
|
||||
c.updateReadBufNotEmpty()
|
||||
if n != 0 {
|
||||
// Inbound packets are backed up when the read buffer is too big.
|
||||
c.processInbound()
|
||||
return
|
||||
}
|
||||
if c.gotFin.IsSet() || c.closed.IsSet() {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
if c.destroyed.IsSet() {
|
||||
if c.err == nil {
|
||||
panic("closed without receiving fin, and no error")
|
||||
}
|
||||
err = c.err
|
||||
return
|
||||
}
|
||||
if c.connDeadlines.read.passed.IsSet() {
|
||||
err = errTimeout
|
||||
return
|
||||
}
|
||||
missinggo.WaitEvents(&mu,
|
||||
&c.gotFin,
|
||||
&c.closed,
|
||||
&c.destroyed,
|
||||
&c.connDeadlines.read.passed,
|
||||
&c.readBufNotEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return addr{c.remoteSocketAddr}
|
||||
}
|
||||
|
||||
func (c *Conn) String() string {
|
||||
return fmt.Sprintf("<UTPConn %s-%s (%d)>", c.LocalAddr(), c.RemoteAddr(), c.recv_id)
|
||||
}
|
||||
|
||||
func (c *Conn) updateCanWrite() {
|
||||
c.canWrite.SetBool(c.synAcked &&
|
||||
len(c.unackedSends) < maxUnackedSends &&
|
||||
c.cur_window <= c.peerWndSize)
|
||||
}
|
||||
|
||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for len(p) != 0 {
|
||||
if c.wroteFin.IsSet() || c.closed.IsSet() {
|
||||
err = errClosed
|
||||
return
|
||||
}
|
||||
if c.destroyed.IsSet() {
|
||||
err = c.err
|
||||
return
|
||||
}
|
||||
if c.connDeadlines.write.passed.IsSet() {
|
||||
err = errTimeout
|
||||
return
|
||||
}
|
||||
// If peerWndSize is 0, we still want to send something, so don't
|
||||
// block until we exceed it.
|
||||
if c.canWrite.IsSet() {
|
||||
var n1 int
|
||||
n1, err = c.write(stData, c.send_id, p, c.seq_nr)
|
||||
n += n1
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if n1 == 0 {
|
||||
panic(len(p))
|
||||
}
|
||||
p = p[n1:]
|
||||
continue
|
||||
}
|
||||
missinggo.WaitEvents(&mu,
|
||||
&c.wroteFin,
|
||||
&c.closed,
|
||||
&c.destroyed,
|
||||
&c.connDeadlines.write.passed,
|
||||
&c.canWrite)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) detach() {
|
||||
s := c.socket
|
||||
_, ok := s.conns[c.connKey]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delete(s.conns, c.connKey)
|
||||
s.lazyDestroy()
|
||||
}
|
71
vendor/github.com/anacrolix/utp/deadlines.go
generated
vendored
Normal file
71
vendor/github.com/anacrolix/utp/deadlines.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type deadline struct {
|
||||
t time.Time
|
||||
passed missinggo.Event
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
func (me *deadline) set(t time.Time) {
|
||||
me.t = t
|
||||
me.passed.Clear()
|
||||
if me.timer != nil {
|
||||
me.timer.Stop()
|
||||
}
|
||||
me.update()
|
||||
}
|
||||
|
||||
func (me *deadline) update() {
|
||||
if me.t.IsZero() {
|
||||
return
|
||||
}
|
||||
if time.Now().Before(me.t) {
|
||||
if me.timer == nil {
|
||||
me.timer = time.AfterFunc(me.t.Sub(time.Now()), me.callback)
|
||||
} else {
|
||||
me.timer.Reset(me.t.Sub(time.Now()))
|
||||
}
|
||||
return
|
||||
}
|
||||
me.passed.Set()
|
||||
}
|
||||
|
||||
func (me *deadline) callback() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
me.update()
|
||||
}
|
||||
|
||||
// This is embedded in Conn and Socket to provide deadline methods for
|
||||
// net.Conn.
|
||||
type connDeadlines struct {
|
||||
read, write deadline
|
||||
}
|
||||
|
||||
func (c *connDeadlines) SetDeadline(t time.Time) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.read.set(t)
|
||||
c.write.set(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *connDeadlines) SetReadDeadline(t time.Time) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.read.set(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *connDeadlines) SetWriteDeadline(t time.Time) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c.write.set(t)
|
||||
return nil
|
||||
}
|
19
vendor/github.com/anacrolix/utp/expvar.go
generated
vendored
Normal file
19
vendor/github.com/anacrolix/utp/expvar.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package utp
|
||||
|
||||
import "expvar"
|
||||
|
||||
var (
|
||||
ackSkippedResends = expvar.NewInt("utpAckSkippedResends")
|
||||
// Inbound packets processed by a Conn.
|
||||
deliveriesProcessed = expvar.NewInt("utpDeliveriesProcessed")
|
||||
sentStatePackets = expvar.NewInt("utpSentStatePackets")
|
||||
acksReceivedAheadOfSyn = expvar.NewInt("utpAcksReceivedAheadOfSyn")
|
||||
unexpectedPacketsRead = expvar.NewInt("utpUnexpectedPacketsRead")
|
||||
// State packets that we managed not to send.
|
||||
unsentStatePackets = expvar.NewInt("utpUnsentStatePackets")
|
||||
unusedReads = expvar.NewInt("utpUnusedReads")
|
||||
unusedReadsDropped = expvar.NewInt("utpUnusedReadsDropped")
|
||||
|
||||
largestReceivedUTPPacket int
|
||||
largestReceivedUTPPacketExpvar = expvar.NewInt("utpLargestReceivedPacket")
|
||||
)
|
135
vendor/github.com/anacrolix/utp/header.go
generated
vendored
Normal file
135
vendor/github.com/anacrolix/utp/header.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
extensionTypeSelectiveAck = 1
|
||||
)
|
||||
|
||||
type extensionField struct {
|
||||
Type byte
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
type header struct {
|
||||
Type st
|
||||
Version int
|
||||
ConnID uint16
|
||||
Timestamp uint32
|
||||
TimestampDiff uint32
|
||||
WndSize uint32
|
||||
SeqNr uint16
|
||||
AckNr uint16
|
||||
Extensions []extensionField
|
||||
}
|
||||
|
||||
func unmarshalExtensions(_type byte, b []byte) (n int, ef []extensionField, err error) {
|
||||
for _type != 0 {
|
||||
if _type != extensionTypeSelectiveAck {
|
||||
// An extension type that is not known to us. Generally we're
|
||||
// unmarshalling an packet that isn't actually uTP but we don't
|
||||
// yet know for sure until we try to deliver it.
|
||||
|
||||
// logonce.Stderr.Printf("utp extension %d", _type)
|
||||
}
|
||||
if len(b) < 2 || len(b) < int(b[1])+2 {
|
||||
err = fmt.Errorf("buffer ends prematurely: %x", b)
|
||||
return
|
||||
}
|
||||
ef = append(ef, extensionField{
|
||||
Type: _type,
|
||||
Bytes: append([]byte(nil), b[2:int(b[1])+2]...),
|
||||
})
|
||||
_type = b[0]
|
||||
n += 2 + int(b[1])
|
||||
b = b[2+int(b[1]):]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var errInvalidHeader = errors.New("invalid header")
|
||||
|
||||
func (h *header) Unmarshal(b []byte) (n int, err error) {
|
||||
h.Type = st(b[0] >> 4)
|
||||
h.Version = int(b[0] & 0xf)
|
||||
if h.Type > stMax || h.Version != 1 {
|
||||
err = errInvalidHeader
|
||||
return
|
||||
}
|
||||
n, h.Extensions, err = unmarshalExtensions(b[1], b[20:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h.ConnID = binary.BigEndian.Uint16(b[2:4])
|
||||
h.Timestamp = binary.BigEndian.Uint32(b[4:8])
|
||||
h.TimestampDiff = binary.BigEndian.Uint32(b[8:12])
|
||||
h.WndSize = binary.BigEndian.Uint32(b[12:16])
|
||||
h.SeqNr = binary.BigEndian.Uint16(b[16:18])
|
||||
h.AckNr = binary.BigEndian.Uint16(b[18:20])
|
||||
n += 20
|
||||
return
|
||||
}
|
||||
|
||||
func (h *header) Marshal(p []byte) (n int) {
|
||||
n = 20 + func() (ret int) {
|
||||
for _, ext := range h.Extensions {
|
||||
ret += 2 + len(ext.Bytes)
|
||||
}
|
||||
return
|
||||
}()
|
||||
p = p[:n]
|
||||
p[0] = byte(h.Type<<4 | 1)
|
||||
binary.BigEndian.PutUint16(p[2:4], h.ConnID)
|
||||
binary.BigEndian.PutUint32(p[4:8], h.Timestamp)
|
||||
binary.BigEndian.PutUint32(p[8:12], h.TimestampDiff)
|
||||
binary.BigEndian.PutUint32(p[12:16], h.WndSize)
|
||||
binary.BigEndian.PutUint16(p[16:18], h.SeqNr)
|
||||
binary.BigEndian.PutUint16(p[18:20], h.AckNr)
|
||||
// Pointer to the last type field so the next extension can set it.
|
||||
_type := &p[1]
|
||||
// We're done with the basic header.
|
||||
p = p[20:]
|
||||
for _, ext := range h.Extensions {
|
||||
*_type = ext.Type
|
||||
// The next extension's type will go here.
|
||||
_type = &p[0]
|
||||
p[1] = uint8(len(ext.Bytes))
|
||||
if int(p[1]) != copy(p[2:], ext.Bytes) {
|
||||
panic("unexpected extension length")
|
||||
}
|
||||
p = p[2+len(ext.Bytes):]
|
||||
}
|
||||
*_type = 0
|
||||
if len(p) != 0 {
|
||||
panic("header length changed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type selectiveAckBitmask struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
func (me *selectiveAckBitmask) expandBytesForBit(index int) {
|
||||
minLen := (3 + (index / 8) + 1) / 4 * 4
|
||||
for len(me.Bytes) < minLen {
|
||||
me.Bytes = append(me.Bytes, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *selectiveAckBitmask) NumBits() int {
|
||||
return len(me.Bytes) * 8
|
||||
}
|
||||
|
||||
func (me *selectiveAckBitmask) SetBit(index int) {
|
||||
me.expandBytesForBit(index)
|
||||
me.Bytes[index/8] |= 1 << uint(index%8)
|
||||
}
|
||||
|
||||
func (me *selectiveAckBitmask) BitIsSet(index int) bool {
|
||||
return me.Bytes[index/8]>>uint(index%8)&1 == 1
|
||||
}
|
11
vendor/github.com/anacrolix/utp/nop.go
generated
vendored
Normal file
11
vendor/github.com/anacrolix/utp/nop.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package utp
|
||||
|
||||
import "net"
|
||||
|
||||
type packetConnNopCloser struct {
|
||||
net.PacketConn
|
||||
}
|
||||
|
||||
func (packetConnNopCloser) Close() error {
|
||||
return nil
|
||||
}
|
68
vendor/github.com/anacrolix/utp/pingpong
generated
vendored
Executable file
68
vendor/github.com/anacrolix/utp/pingpong
generated
vendored
Executable file
@@ -0,0 +1,68 @@
|
||||
# This shell script uses nc-like executables to send and receive the file at
|
||||
# $1, and prints the checksums. 3 such executables are
|
||||
# github.com/h2so5/utp/ucat, invoked as h2so5-ucat, libutp-ucat, which is the
|
||||
# ucat or ucat-static generated by the C++ libutp, and lastly, ./cmd/ucat from
|
||||
# this repository. A good file in my experiments is no more than a few 100MB,
|
||||
# or you'll be waiting a while.
|
||||
|
||||
set -eu
|
||||
# set -x
|
||||
|
||||
# Passed to invocations of godo for package ./cmd/ucat.
|
||||
#GODOFLAGS=-race
|
||||
|
||||
#export GO_UTP_PACKET_DROP=0.1
|
||||
export GOPPROF=
|
||||
|
||||
# Invokes the implementation to test against. If there's an arg, then it's
|
||||
# expected to listen.
|
||||
function other_ucat() {
|
||||
if [[ $# != 0 ]]; then
|
||||
libutp-ucat -l -p "$port"
|
||||
# h2so5-ucat -l :"$port"
|
||||
else
|
||||
libutp-ucat localhost "$port"
|
||||
# h2so5-ucat localhost:"$port"
|
||||
fi
|
||||
}
|
||||
|
||||
function md5cmd() {
|
||||
(which md5sum > /dev/null && md5sum "$@") || (which md5 > /dev/null && md5 "$@") || md5sum "$@"
|
||||
}
|
||||
|
||||
# Check what the correct result is.
|
||||
md5cmd "$1"
|
||||
|
||||
rate() {
|
||||
pv -a -W -b
|
||||
}
|
||||
|
||||
port=4000
|
||||
|
||||
echo 'utp->other_ucat'
|
||||
# Send from this uTP implementation to another client.
|
||||
other_ucat -l | rate | md5cmd &
|
||||
# sleep 1
|
||||
godo ${GODOFLAGS-} ./cmd/ucat localhost "$port" < "$1"
|
||||
wait
|
||||
|
||||
echo 'other_ucat->utp'
|
||||
# Send from the other implementation, to this one.
|
||||
GO_UTP_LOGGING=0 godo ${GODOFLAGS-} ./cmd/ucat -l -p "$port" | rate | md5cmd &
|
||||
# Never receive from h2so5's ucat without a small sleep first. Don't know why.
|
||||
sleep 1
|
||||
other_ucat < "$1"
|
||||
wait
|
||||
|
||||
echo 'libutp->libutp'
|
||||
libutp-ucat -l -p "$port" | rate | md5cmd &
|
||||
libutp-ucat localhost "$port" < "$1"
|
||||
wait
|
||||
|
||||
echo 'utp->utp'
|
||||
godo ./cmd/ucat -l -p "$port" | rate | md5cmd &
|
||||
sleep 1
|
||||
godo ./cmd/ucat localhost "$port" < "$1"
|
||||
wait
|
||||
|
||||
# Now check the hashes match (yes you).
|
72
vendor/github.com/anacrolix/utp/send.go
generated
vendored
Normal file
72
vendor/github.com/anacrolix/utp/send.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type send struct {
|
||||
acked missinggo.Event
|
||||
payloadSize uint32
|
||||
started missinggo.MonotonicTime
|
||||
_type st
|
||||
connID uint16
|
||||
payload []byte
|
||||
seqNr uint16
|
||||
conn *Conn
|
||||
|
||||
acksSkipped int
|
||||
resendTimer *time.Timer
|
||||
numResends int
|
||||
}
|
||||
|
||||
// first is true if this is the first time the send is acked. latency is
|
||||
// calculated for the first ack.
|
||||
func (s *send) Ack() (latency time.Duration, first bool) {
|
||||
first = !s.acked.IsSet()
|
||||
if first {
|
||||
latency = missinggo.MonotonicSince(s.started)
|
||||
}
|
||||
if s.payload != nil {
|
||||
sendBufferPool.Put(s.payload[:0:minMTU])
|
||||
s.payload = nil
|
||||
}
|
||||
s.acked.Set()
|
||||
if s.resendTimer != nil {
|
||||
s.resendTimer.Stop()
|
||||
s.resendTimer = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *send) timedOut() {
|
||||
s.conn.destroy(errAckTimeout)
|
||||
}
|
||||
|
||||
func (s *send) timeoutResend() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if missinggo.MonotonicSince(s.started) >= writeTimeout {
|
||||
s.timedOut()
|
||||
return
|
||||
}
|
||||
if s.acked.IsSet() || s.conn.destroyed.IsSet() {
|
||||
return
|
||||
}
|
||||
rt := s.conn.resendTimeout()
|
||||
s.resend()
|
||||
s.numResends++
|
||||
s.resendTimer.Reset(rt * time.Duration(s.numResends))
|
||||
}
|
||||
|
||||
func (s *send) resend() {
|
||||
if s.acked.IsSet() {
|
||||
return
|
||||
}
|
||||
err := s.conn.send(s._type, s.connID, s.payload, s.seqNr)
|
||||
if err != nil {
|
||||
log.Printf("error resending packet: %s", err)
|
||||
}
|
||||
}
|
597
vendor/github.com/anacrolix/utp/socket.go
generated
vendored
Normal file
597
vendor/github.com/anacrolix/utp/socket.go
generated
vendored
Normal file
@@ -0,0 +1,597 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/missinggo"
|
||||
"github.com/anacrolix/missinggo/inproc"
|
||||
"github.com/anacrolix/missinggo/pproffd"
|
||||
)
|
||||
|
||||
var (
|
||||
_ net.Listener = &Socket{}
|
||||
_ net.PacketConn = &Socket{}
|
||||
)
|
||||
|
||||
// Uniquely identifies any uTP connection on top of the underlying packet
|
||||
// stream.
|
||||
type connKey struct {
|
||||
remoteAddr resolvedAddrStr
|
||||
connID uint16
|
||||
}
|
||||
|
||||
// A Socket wraps a net.PacketConn, diverting uTP packets to its child uTP
|
||||
// Conns.
|
||||
type Socket struct {
|
||||
pc net.PacketConn
|
||||
conns map[connKey]*Conn
|
||||
|
||||
backlogNotEmpty missinggo.Event
|
||||
backlog map[syn]struct{}
|
||||
|
||||
closed missinggo.Event
|
||||
destroyed missinggo.Event
|
||||
|
||||
wgReadWrite sync.WaitGroup
|
||||
|
||||
unusedReads chan read
|
||||
connDeadlines
|
||||
// If a read error occurs on the underlying net.PacketConn, it is put
|
||||
// here. This is because reading is done in its own goroutine to dispatch
|
||||
// to uTP Conns.
|
||||
ReadErr error
|
||||
}
|
||||
|
||||
func listenPacket(network, addr string) (pc net.PacketConn, err error) {
|
||||
if network == "inproc" {
|
||||
return inproc.ListenPacket(network, addr)
|
||||
}
|
||||
return net.ListenPacket(network, addr)
|
||||
}
|
||||
|
||||
// NewSocket creates a net.PacketConn with the given network and address, and
|
||||
// returns a Socket dispatching on it.
|
||||
func NewSocket(network, addr string) (s *Socket, err error) {
|
||||
if network == "" {
|
||||
network = "udp"
|
||||
}
|
||||
pc, err := listenPacket(network, addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return NewSocketFromPacketConn(pc)
|
||||
}
|
||||
|
||||
// Create a Socket, using the provided net.PacketConn. If you want to retain
|
||||
// use of the net.PacketConn after the Socket closes it, override the
|
||||
// net.PacketConn's Close method, or use NetSocketFromPacketConnNoClose.
|
||||
func NewSocketFromPacketConn(pc net.PacketConn) (s *Socket, err error) {
|
||||
s = &Socket{
|
||||
backlog: make(map[syn]struct{}, backlog),
|
||||
pc: pc,
|
||||
unusedReads: make(chan read, 100),
|
||||
wgReadWrite: sync.WaitGroup{},
|
||||
}
|
||||
mu.Lock()
|
||||
sockets[s] = struct{}{}
|
||||
mu.Unlock()
|
||||
go s.reader()
|
||||
return
|
||||
}
|
||||
|
||||
// Create a Socket using the provided PacketConn, that doesn't close the
|
||||
// PacketConn when the Socket is closed.
|
||||
func NewSocketFromPacketConnNoClose(pc net.PacketConn) (s *Socket, err error) {
|
||||
return NewSocketFromPacketConn(packetConnNopCloser{pc})
|
||||
}
|
||||
|
||||
func (s *Socket) unusedRead(read read) {
|
||||
unusedReads.Add(1)
|
||||
select {
|
||||
case s.unusedReads <- read:
|
||||
default:
|
||||
// Drop the packet.
|
||||
unusedReadsDropped.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socket) strNetAddr(str string) (a net.Addr) {
|
||||
var err error
|
||||
switch n := s.network(); n {
|
||||
case "udp":
|
||||
a, err = net.ResolveUDPAddr(n, str)
|
||||
case "inproc":
|
||||
a, err = inproc.ResolveAddr(n, str)
|
||||
default:
|
||||
panic(n)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socket) pushBacklog(syn syn) {
|
||||
if _, ok := s.backlog[syn]; ok {
|
||||
return
|
||||
}
|
||||
// Pop a pseudo-random syn to make room. TODO: Use missinggo/orderedmap,
|
||||
// coz that's what is wanted here.
|
||||
for k := range s.backlog {
|
||||
if len(s.backlog) < backlog {
|
||||
break
|
||||
}
|
||||
delete(s.backlog, k)
|
||||
// A syn is sent on the remote's recv_id, so this is where we can send
|
||||
// the reset.
|
||||
s.reset(s.strNetAddr(k.addr), k.seq_nr, k.conn_id)
|
||||
}
|
||||
s.backlog[syn] = struct{}{}
|
||||
s.backlogChanged()
|
||||
}
|
||||
|
||||
func (s *Socket) reader() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
defer s.destroy()
|
||||
var b [maxRecvSize]byte
|
||||
for {
|
||||
s.wgReadWrite.Add(1)
|
||||
mu.Unlock()
|
||||
n, addr, err := s.pc.ReadFrom(b[:])
|
||||
s.wgReadWrite.Done()
|
||||
mu.Lock()
|
||||
if s.destroyed.IsSet() {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("error reading Socket PacketConn: %s", err)
|
||||
s.ReadErr = err
|
||||
return
|
||||
}
|
||||
s.handleReceivedPacket(read{
|
||||
append([]byte(nil), b[:n]...),
|
||||
addr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func receivedUTPPacketSize(n int) {
|
||||
if n > largestReceivedUTPPacket {
|
||||
largestReceivedUTPPacket = n
|
||||
largestReceivedUTPPacketExpvar.Set(int64(n))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socket) connForRead(h header, from net.Addr) (c *Conn, ok bool) {
|
||||
c, ok = s.conns[connKey{
|
||||
resolvedAddrStr(from.String()),
|
||||
func() uint16 {
|
||||
if h.Type == stSyn {
|
||||
// SYNs have a ConnID one lower than the eventual recvID, and we index
|
||||
// the connections with that, so use it for the lookup.
|
||||
return h.ConnID + 1
|
||||
} else {
|
||||
return h.ConnID
|
||||
}
|
||||
}(),
|
||||
}]
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socket) handlePacketReceivedForEstablishedConn(h header, from net.Addr, data []byte, c *Conn) {
|
||||
if h.Type == stSyn {
|
||||
if h.ConnID == c.send_id-2 {
|
||||
// This is a SYN for connection that cannot exist locally. The
|
||||
// connection the remote wants to establish here with the proposed
|
||||
// recv_id, already has an existing connection that was dialled
|
||||
// *out* from this socket, which is why the send_id is 1 higher,
|
||||
// rather than 1 lower than the recv_id.
|
||||
log.Print("resetting conflicting syn")
|
||||
s.reset(from, h.SeqNr, h.ConnID)
|
||||
return
|
||||
} else if h.ConnID != c.send_id {
|
||||
panic("bad assumption")
|
||||
}
|
||||
}
|
||||
c.receivePacket(h, data)
|
||||
}
|
||||
|
||||
func (s *Socket) handleReceivedPacket(p read) {
|
||||
if len(p.data) < 20 {
|
||||
s.unusedRead(p)
|
||||
return
|
||||
}
|
||||
var h header
|
||||
hEnd, err := h.Unmarshal(p.data)
|
||||
if err != nil || h.Type > stMax || h.Version != 1 {
|
||||
s.unusedRead(p)
|
||||
return
|
||||
}
|
||||
if c, ok := s.connForRead(h, p.from); ok {
|
||||
receivedUTPPacketSize(len(p.data))
|
||||
s.handlePacketReceivedForEstablishedConn(h, p.from, p.data[hEnd:], c)
|
||||
return
|
||||
}
|
||||
// Packet doesn't belong to an existing connection.
|
||||
switch h.Type {
|
||||
case stSyn:
|
||||
s.pushBacklog(syn{
|
||||
seq_nr: h.SeqNr,
|
||||
conn_id: h.ConnID,
|
||||
addr: p.from.String(),
|
||||
})
|
||||
return
|
||||
case stReset:
|
||||
// Could be a late arriving packet for a Conn we're already done with.
|
||||
// If it was for an existing connection, we would have handled it
|
||||
// earlier.
|
||||
default:
|
||||
unexpectedPacketsRead.Add(1)
|
||||
// This is an unexpected packet. We'll send a reset, but also pass it
|
||||
// on. I don't think you can reset on the received packets ConnID if
|
||||
// it isn't a SYN, as the send_id will differ in this case.
|
||||
s.reset(p.from, h.SeqNr, h.ConnID)
|
||||
// Connection initiated by remote.
|
||||
s.reset(p.from, h.SeqNr, h.ConnID-1)
|
||||
// Connection initiated locally.
|
||||
s.reset(p.from, h.SeqNr, h.ConnID+1)
|
||||
}
|
||||
s.unusedRead(p)
|
||||
}
|
||||
|
||||
// Send a reset in response to a packet with the given header.
|
||||
func (s *Socket) reset(addr net.Addr, ackNr, connId uint16) {
|
||||
b := make([]byte, 0, maxHeaderSize)
|
||||
h := header{
|
||||
Type: stReset,
|
||||
Version: 1,
|
||||
ConnID: connId,
|
||||
AckNr: ackNr,
|
||||
}
|
||||
b = b[:h.Marshal(b)]
|
||||
go s.writeTo(b, addr)
|
||||
}
|
||||
|
||||
// Return a recv_id that should be free. Handling the case where it isn't is
|
||||
// deferred to a more appropriate function.
|
||||
func (s *Socket) newConnID(remoteAddr resolvedAddrStr) (id uint16) {
|
||||
// Rather than use math.Rand, which requires generating all the IDs up
|
||||
// front and allocating a slice, we do it on the stack, generating the IDs
|
||||
// only as required. To do this, we use the fact that the array is
|
||||
// default-initialized. IDs that are 0, are actually their index in the
|
||||
// array. IDs that are non-zero, are +1 from their intended ID.
|
||||
var idsBack [0x10000]int
|
||||
ids := idsBack[:]
|
||||
for len(ids) != 0 {
|
||||
// Pick the next ID from the untried ids.
|
||||
i := rand.Intn(len(ids))
|
||||
id = uint16(ids[i])
|
||||
// If it's zero, then treat it as though the index i was the ID.
|
||||
// Otherwise the value we get is the ID+1.
|
||||
if id == 0 {
|
||||
id = uint16(i)
|
||||
} else {
|
||||
id--
|
||||
}
|
||||
// Check there's no connection using this ID for its recv_id...
|
||||
_, ok1 := s.conns[connKey{remoteAddr, id}]
|
||||
// and if we're connecting to our own Socket, that there isn't a Conn
|
||||
// already receiving on what will correspond to our send_id. Note that
|
||||
// we just assume that we could be connecting to our own Socket. This
|
||||
// will halve the available connection IDs to each distinct remote
|
||||
// address. Presumably that's ~0x8000, down from ~0x10000.
|
||||
_, ok2 := s.conns[connKey{remoteAddr, id + 1}]
|
||||
_, ok4 := s.conns[connKey{remoteAddr, id - 1}]
|
||||
if !ok1 && !ok2 && !ok4 {
|
||||
return
|
||||
}
|
||||
// The set of possible IDs is shrinking. The highest one will be lost, so
|
||||
// it's moved to the location of the one we just tried.
|
||||
ids[i] = len(ids) // Conveniently already +1.
|
||||
// And shrink.
|
||||
ids = ids[:len(ids)-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
zeroipv4 = net.ParseIP("0.0.0.0")
|
||||
zeroipv6 = net.ParseIP("::")
|
||||
|
||||
ipv4lo = mustResolveUDP("127.0.0.1")
|
||||
ipv6lo = mustResolveUDP("::1")
|
||||
)
|
||||
|
||||
func mustResolveUDP(addr string) net.IP {
|
||||
u, err := net.ResolveIPAddr("ip", addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u.IP
|
||||
}
|
||||
|
||||
func realRemoteAddr(addr net.Addr) net.Addr {
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if ok {
|
||||
if udpAddr.IP.Equal(zeroipv4) {
|
||||
udpAddr.IP = ipv4lo
|
||||
}
|
||||
if udpAddr.IP.Equal(zeroipv6) {
|
||||
udpAddr.IP = ipv6lo
|
||||
}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func (s *Socket) newConn(addr net.Addr) (c *Conn) {
|
||||
addr = realRemoteAddr(addr)
|
||||
|
||||
c = &Conn{
|
||||
socket: s,
|
||||
remoteSocketAddr: addr,
|
||||
created: time.Now(),
|
||||
}
|
||||
c.sendPendingSendSendStateTimer = missinggo.StoppedFuncTimer(c.sendPendingSendStateTimerCallback)
|
||||
c.packetReadTimeoutTimer = time.AfterFunc(packetReadTimeout, c.receivePacketTimeoutCallback)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socket) Dial(addr string) (net.Conn, error) {
|
||||
return s.DialContext(context.Background(), "", addr)
|
||||
}
|
||||
|
||||
func (s *Socket) resolveAddr(network, addr string) (net.Addr, error) {
|
||||
n := s.network()
|
||||
if network != "" {
|
||||
n = network
|
||||
}
|
||||
if n == "inproc" {
|
||||
return inproc.ResolveAddr(n, addr)
|
||||
}
|
||||
return net.ResolveUDPAddr(n, addr)
|
||||
}
|
||||
|
||||
func (s *Socket) network() string {
|
||||
return s.pc.LocalAddr().Network()
|
||||
}
|
||||
|
||||
func (s *Socket) startOutboundConn(addr net.Addr) (c *Conn, err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c = s.newConn(addr)
|
||||
c.recv_id = s.newConnID(resolvedAddrStr(c.RemoteAddr().String()))
|
||||
c.send_id = c.recv_id + 1
|
||||
if logLevel >= 1 {
|
||||
log.Printf("dial registering addr: %s", c.RemoteAddr().String())
|
||||
}
|
||||
if !s.registerConn(c.recv_id, resolvedAddrStr(c.RemoteAddr().String()), c) {
|
||||
err = errors.New("couldn't register new connection")
|
||||
log.Println(c.recv_id, c.RemoteAddr().String())
|
||||
for k, c := range s.conns {
|
||||
log.Println(k, c, c.age())
|
||||
}
|
||||
log.Printf("that's %d connections", len(s.conns))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.seq_nr = 1
|
||||
c.writeSyn()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socket) DialContext(ctx context.Context, network, addr string) (nc net.Conn, err error) {
|
||||
netAddr, err := s.resolveAddr(network, addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c, err := s.startOutboundConn(netAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
connErr := make(chan error, 1)
|
||||
go func() {
|
||||
connErr <- c.recvSynAck()
|
||||
}()
|
||||
select {
|
||||
case err = <-connErr:
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
}
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
c.destroy(errors.New("dial timeout"))
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
c.updateCanWrite()
|
||||
mu.Unlock()
|
||||
nc = pproffd.WrapNetConn(c)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Socket) writeTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
apdc := artificialPacketDropChance
|
||||
if apdc != 0 {
|
||||
if rand.Float64() < apdc {
|
||||
n = len(b)
|
||||
return
|
||||
}
|
||||
}
|
||||
n, err = me.pc.WriteTo(b, addr)
|
||||
return
|
||||
}
|
||||
|
||||
// Returns true if the connection was newly registered, false otherwise.
|
||||
func (s *Socket) registerConn(recvID uint16, remoteAddr resolvedAddrStr, c *Conn) bool {
|
||||
if s.conns == nil {
|
||||
s.conns = make(map[connKey]*Conn)
|
||||
}
|
||||
key := connKey{remoteAddr, recvID}
|
||||
if _, ok := s.conns[key]; ok {
|
||||
return false
|
||||
}
|
||||
c.connKey = key
|
||||
s.conns[key] = c
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Socket) backlogChanged() {
|
||||
if len(s.backlog) != 0 {
|
||||
s.backlogNotEmpty.Set()
|
||||
} else {
|
||||
s.backlogNotEmpty.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socket) nextSyn() (syn syn, err error) {
|
||||
for {
|
||||
missinggo.WaitEvents(&mu, &s.closed, &s.backlogNotEmpty, &s.destroyed)
|
||||
if s.closed.IsSet() {
|
||||
err = errClosed
|
||||
return
|
||||
}
|
||||
if s.destroyed.IsSet() {
|
||||
err = s.ReadErr
|
||||
return
|
||||
}
|
||||
for k := range s.backlog {
|
||||
syn = k
|
||||
delete(s.backlog, k)
|
||||
s.backlogChanged()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ACK a SYN, and return a new Conn for it. ok is false if the SYN is bad, and
|
||||
// the Conn invalid.
|
||||
func (s *Socket) ackSyn(syn syn) (c *Conn, ok bool) {
|
||||
c = s.newConn(s.strNetAddr(syn.addr))
|
||||
c.send_id = syn.conn_id
|
||||
c.recv_id = c.send_id + 1
|
||||
c.seq_nr = uint16(rand.Int())
|
||||
c.lastAck = c.seq_nr - 1
|
||||
c.ack_nr = syn.seq_nr
|
||||
c.synAcked = true
|
||||
c.updateCanWrite()
|
||||
if !s.registerConn(c.recv_id, resolvedAddrStr(syn.addr), c) {
|
||||
// SYN that triggered this accept duplicates existing connection.
|
||||
// Ack again in case the SYN was a resend.
|
||||
c = s.conns[connKey{resolvedAddrStr(syn.addr), c.recv_id}]
|
||||
if c.send_id != syn.conn_id {
|
||||
panic(":|")
|
||||
}
|
||||
c.sendState()
|
||||
return
|
||||
}
|
||||
c.sendState()
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// Accept and return a new uTP connection.
|
||||
func (s *Socket) Accept() (net.Conn, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for {
|
||||
syn, err := s.nextSyn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, ok := s.ackSyn(syn)
|
||||
if ok {
|
||||
c.updateCanWrite()
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The address we're listening on for new uTP connections.
|
||||
func (s *Socket) Addr() net.Addr {
|
||||
return s.pc.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *Socket) CloseNow() error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
s.closed.Set()
|
||||
for _, c := range s.conns {
|
||||
c.closeNow()
|
||||
}
|
||||
s.destroy()
|
||||
s.wgReadWrite.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Socket) Close() error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
s.closed.Set()
|
||||
s.lazyDestroy()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Socket) lazyDestroy() {
|
||||
if len(s.conns) != 0 {
|
||||
return
|
||||
}
|
||||
if !s.closed.IsSet() {
|
||||
return
|
||||
}
|
||||
s.destroy()
|
||||
}
|
||||
|
||||
func (s *Socket) destroy() {
|
||||
delete(sockets, s)
|
||||
s.destroyed.Set()
|
||||
s.pc.Close()
|
||||
for _, c := range s.conns {
|
||||
c.destroy(errors.New("Socket destroyed"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socket) LocalAddr() net.Addr {
|
||||
return s.pc.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *Socket) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
select {
|
||||
case read, ok := <-s.unusedReads:
|
||||
if !ok {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
n = copy(p, read.data)
|
||||
addr = read.from
|
||||
return
|
||||
case <-s.connDeadlines.read.passed.LockedChan(&mu):
|
||||
err = errTimeout
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socket) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
mu.Lock()
|
||||
if s.connDeadlines.write.passed.IsSet() {
|
||||
err = errTimeout
|
||||
}
|
||||
s.wgReadWrite.Add(1)
|
||||
defer s.wgReadWrite.Done()
|
||||
mu.Unlock()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return s.pc.WriteTo(b, addr)
|
||||
}
|
24
vendor/github.com/anacrolix/utp/status.go
generated
vendored
Normal file
24
vendor/github.com/anacrolix/utp/status.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func WriteStatus(w io.Writer) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
for s := range sockets {
|
||||
writeSocketStatus(w, s)
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeSocketStatus(w io.Writer, s *Socket) {
|
||||
fmt.Fprintf(w, "%s\n", s.pc.LocalAddr())
|
||||
fmt.Fprintf(w, "%d attached conns\n", len(s.conns))
|
||||
fmt.Fprintf(w, "backlog: %d\n", len(s.backlog))
|
||||
fmt.Fprintf(w, "closed: %v\n", s.closed.IsSet())
|
||||
fmt.Fprintf(w, "unused reads: %d\n", len(s.unusedReads))
|
||||
fmt.Fprintf(w, "readerr: %v\n", s.ReadErr)
|
||||
}
|
193
vendor/github.com/anacrolix/utp/utp.go
generated
vendored
Normal file
193
vendor/github.com/anacrolix/utp/utp.go
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
// Package utp implements uTP, the micro transport protocol as used with
|
||||
// Bittorrent. It opts for simplicity and reliability over strict adherence to
|
||||
// the (poor) spec. It allows using the underlying OS-level transport despite
|
||||
// dispatching uTP on top to allow for example, shared socket use with DHT.
|
||||
// Additionally, multiple uTP connections can share the same OS socket, to
|
||||
// truly realize uTP's claim to be light on system and network switching
|
||||
// resources.
|
||||
//
|
||||
// Socket is a wrapper of net.UDPConn, and performs dispatching of uTP packets
|
||||
// to attached uTP Conns. Dial and Accept is done via Socket. Conn implements
|
||||
// net.Conn over uTP, via aforementioned Socket.
|
||||
package utp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pprofsync "github.com/anacrolix/sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Maximum received SYNs that haven't been accepted. If more SYNs are
|
||||
// received, a pseudo randomly selected SYN is replied to with a reset to
|
||||
// make room.
|
||||
backlog = 50
|
||||
|
||||
// IPv6 min MTU is 1280, -40 for IPv6 header, and ~8 for fragment header?
|
||||
minMTU = 1438 // Why?
|
||||
// uTP header of 20, +2 for the next extension, and an optional selective
|
||||
// ACK.
|
||||
maxHeaderSize = 20 + 2 + (((maxUnackedInbound+7)/8)+3)/4*4
|
||||
maxPayloadSize = minMTU - maxHeaderSize
|
||||
maxRecvSize = 0x2000
|
||||
|
||||
// Maximum out-of-order packets to buffer.
|
||||
maxUnackedInbound = 256
|
||||
maxUnackedSends = 256
|
||||
|
||||
readBufferLen = 1 << 20 // ~1MiB
|
||||
|
||||
// How long to wait before sending a state packet, after one is required.
|
||||
// This prevents spamming a state packet for every packet received, and
|
||||
// non-state packets that are being sent also fill the role.
|
||||
pendingSendStateDelay = 500 * time.Microsecond
|
||||
)
|
||||
|
||||
var (
|
||||
sendBufferPool = sync.Pool{
|
||||
New: func() interface{} { return make([]byte, minMTU) },
|
||||
}
|
||||
// This is the latency we assume on new connections. It should be higher
|
||||
// than the latency we expect on most connections to prevent excessive
|
||||
// resending to peers that take a long time to respond, before we've got a
|
||||
// better idea of their actual latency.
|
||||
initialLatency time.Duration
|
||||
// If a write isn't acked within this period, destroy the connection.
|
||||
writeTimeout time.Duration
|
||||
// Assume the connection has been closed by the peer getting no packets of
|
||||
// any kind for this time.
|
||||
packetReadTimeout time.Duration
|
||||
)
|
||||
|
||||
func setDefaultDurations() {
|
||||
// An approximate upper bound for most connections across the world.
|
||||
initialLatency = 400 * time.Millisecond
|
||||
// Getting no reply for this period for a packet, we can probably rule out
|
||||
// latency and client lag.
|
||||
writeTimeout = 15 * time.Second
|
||||
// Somewhere longer than the BitTorrent grace period (90-120s), and less
|
||||
// than default TCP reset (4min).
|
||||
packetReadTimeout = 2 * time.Minute
|
||||
}
|
||||
|
||||
func init() {
|
||||
setDefaultDurations()
|
||||
}
|
||||
|
||||
// Strongly-type guarantee of resolved network address.
|
||||
type resolvedAddrStr string
|
||||
|
||||
type read struct {
|
||||
data []byte
|
||||
from net.Addr
|
||||
}
|
||||
|
||||
type syn struct {
|
||||
seq_nr, conn_id uint16
|
||||
// net.Addr.String() of a Socket's real net.PacketConn.
|
||||
addr string
|
||||
}
|
||||
|
||||
var (
|
||||
mu pprofsync.RWMutex
|
||||
sockets = map[*Socket]struct{}{}
|
||||
logLevel = 0
|
||||
artificialPacketDropChance = 0.0
|
||||
)
|
||||
|
||||
func init() {
|
||||
logLevel, _ = strconv.Atoi(os.Getenv("GO_UTP_LOGGING"))
|
||||
fmt.Sscanf(os.Getenv("GO_UTP_PACKET_DROP"), "%f", &artificialPacketDropChance)
|
||||
}
|
||||
|
||||
var (
|
||||
errClosed = errors.New("closed")
|
||||
errTimeout net.Error = timeoutError{"i/o timeout"}
|
||||
errAckTimeout = timeoutError{"timed out waiting for ack"}
|
||||
)
|
||||
|
||||
type timeoutError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (me timeoutError) Timeout() bool { return true }
|
||||
func (me timeoutError) Error() string { return me.msg }
|
||||
func (me timeoutError) Temporary() bool { return false }
|
||||
|
||||
type st int
|
||||
|
||||
func (me st) String() string {
|
||||
switch me {
|
||||
case stData:
|
||||
return "stData"
|
||||
case stFin:
|
||||
return "stFin"
|
||||
case stState:
|
||||
return "stState"
|
||||
case stReset:
|
||||
return "stReset"
|
||||
case stSyn:
|
||||
return "stSyn"
|
||||
default:
|
||||
panic(fmt.Sprintf("%d", me))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
stData st = 0
|
||||
stFin = 1
|
||||
stState = 2
|
||||
stReset = 3
|
||||
stSyn = 4
|
||||
|
||||
// Used for validating packet headers.
|
||||
stMax = stSyn
|
||||
)
|
||||
|
||||
type recv struct {
|
||||
seen bool
|
||||
data []byte
|
||||
Type st
|
||||
}
|
||||
|
||||
// Attempt to connect to a remote uTP listener, creating a Socket just for
|
||||
// this connection.
|
||||
func Dial(addr string) (net.Conn, error) {
|
||||
return DialContext(context.Background(), addr)
|
||||
}
|
||||
|
||||
// Same as Dial with a timeout parameter. Creates a Socket just for the
|
||||
// connection, which will be closed with the Conn is. To reuse another Socket,
|
||||
// see Socket.Dial.
|
||||
func DialContext(ctx context.Context, addr string) (nc net.Conn, err error) {
|
||||
s, err := NewSocket("udp", ":0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer s.Close()
|
||||
return s.DialContext(ctx, "", addr)
|
||||
}
|
||||
|
||||
// Listen creates listener Socket to accept incoming connections.
|
||||
func Listen(laddr string) (net.Listener, error) {
|
||||
return NewSocket("udp", laddr)
|
||||
}
|
||||
|
||||
func nowTimestamp() uint32 {
|
||||
return uint32(time.Now().UnixNano() / int64(time.Microsecond))
|
||||
}
|
||||
|
||||
func seqLess(a, b uint16) bool {
|
||||
if b < 0x8000 {
|
||||
return a < b || a >= b-0x8000
|
||||
} else {
|
||||
return a < b && a >= b-0x8000
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user