mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
Merge remote-tracking branch 'origin/mr/258'
This commit is contained in:
commit
f502eca67d
12 changed files with 276 additions and 24 deletions
|
@ -3,11 +3,16 @@ package util
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/realclientip/realclientip-go"
|
||||
)
|
||||
|
||||
func SerializeSessionDescription(desc *webrtc.SessionDescription) (string, error) {
|
||||
|
@ -97,3 +102,66 @@ func StripLocalAddresses(str string) string {
|
|||
}
|
||||
return string(bts)
|
||||
}
|
||||
|
||||
// Attempts to retrieve the client IP of where the HTTP request originating.
|
||||
// There is no standard way to do this since the original client IP can be included in a number of different headers,
|
||||
// depending on the proxies and load balancers between the client and the server. We attempt to check as many of these
|
||||
// headers as possible to determine a "best guess" of the client IP
|
||||
// Using this as a reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
|
||||
func GetClientIp(req *http.Request) string {
|
||||
// We check the "Fowarded" header first, followed by the "X-Forwarded-For" header, and then use the "RemoteAddr" as
|
||||
// a last resort. We use the leftmost address since it is the closest one to the client.
|
||||
strat := realclientip.NewChainStrategy(
|
||||
realclientip.Must(realclientip.NewLeftmostNonPrivateStrategy("Forwarded")),
|
||||
realclientip.Must(realclientip.NewLeftmostNonPrivateStrategy("X-Forwarded-For")),
|
||||
realclientip.RemoteAddrStrategy{},
|
||||
)
|
||||
clientIp := strat.ClientIP(req.Header, req.RemoteAddr)
|
||||
return clientIp
|
||||
}
|
||||
|
||||
// Returns a list of IP addresses of ICE candidates, roughly in descending order for accuracy for geolocation
|
||||
func GetCandidateAddrs(sdpStr string) []net.IP {
|
||||
var desc sdp.SessionDescription
|
||||
err := desc.Unmarshal([]byte(sdpStr))
|
||||
if err != nil {
|
||||
log.Printf("GetCandidateAddrs: failed to unmarshal SDP: %v\n", err)
|
||||
return []net.IP{}
|
||||
}
|
||||
|
||||
iceCandidates := make([]ice.Candidate, 0)
|
||||
|
||||
for _, m := range desc.MediaDescriptions {
|
||||
for _, a := range m.Attributes {
|
||||
if a.IsICECandidate() {
|
||||
c, err := ice.UnmarshalCandidate(a.Value)
|
||||
if err == nil {
|
||||
iceCandidates = append(iceCandidates, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ICE candidates are first sorted in asecending order of priority, to match convention of providing a custom Less
|
||||
// function to sort
|
||||
sort.Slice(iceCandidates, func(i, j int) bool {
|
||||
if iceCandidates[i].Type() != iceCandidates[j].Type() {
|
||||
// Sort by candidate type first, in the order specified in https://datatracker.ietf.org/doc/html/rfc8445#section-5.1.2.2
|
||||
// Higher priority candidate types are more efficient, which likely means they are closer to the client
|
||||
// itself, providing a more accurate result for geolocation
|
||||
return ice.CandidateType(iceCandidates[i].Type().Preference()) < ice.CandidateType(iceCandidates[j].Type().Preference())
|
||||
}
|
||||
// Break ties with the ICE candidate's priority property
|
||||
return iceCandidates[i].Priority() < iceCandidates[j].Priority()
|
||||
})
|
||||
slices.Reverse(iceCandidates)
|
||||
|
||||
sortedIpAddr := make([]net.IP, 0)
|
||||
for _, c := range iceCandidates {
|
||||
ip := net.ParseIP(c.Address())
|
||||
if ip != nil {
|
||||
sortedIpAddr = append(sortedIpAddr, ip)
|
||||
}
|
||||
}
|
||||
return sortedIpAddr
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
@ -25,4 +27,49 @@ func TestUtil(t *testing.T) {
|
|||
|
||||
So(StripLocalAddresses(offer), ShouldEqual, offerStart+goodCandidate+offerEnd)
|
||||
})
|
||||
|
||||
Convey("GetClientIp", t, func() {
|
||||
// Should use Forwarded header
|
||||
req1, _ := http.NewRequest("GET", "https://example.com", nil)
|
||||
req1.Header.Add("X-Forwarded-For", "1.1.1.1, 2001:db8:cafe::99%eth0, 3.3.3.3, 192.168.1.1")
|
||||
req1.Header.Add("Forwarded", `For=fe80::abcd;By=fe80::1234, Proto=https;For=::ffff:188.0.2.128, For="[2001:db8:cafe::17]:4848", For=fc00::1`)
|
||||
req1.RemoteAddr = "192.168.1.2:8888"
|
||||
So(GetClientIp(req1), ShouldEqual, "188.0.2.128")
|
||||
|
||||
// Should use X-Forwarded-For header
|
||||
req2, _ := http.NewRequest("GET", "https://example.com", nil)
|
||||
req2.Header.Add("X-Forwarded-For", "1.1.1.1, 2001:db8:cafe::99%eth0, 3.3.3.3, 192.168.1.1")
|
||||
req2.RemoteAddr = "192.168.1.2:8888"
|
||||
So(GetClientIp(req2), ShouldEqual, "1.1.1.1")
|
||||
|
||||
// Should use RemoteAddr
|
||||
req3, _ := http.NewRequest("GET", "https://example.com", nil)
|
||||
req3.RemoteAddr = "192.168.1.2:8888"
|
||||
So(GetClientIp(req3), ShouldEqual, "192.168.1.2")
|
||||
|
||||
// Should return empty client IP
|
||||
req4, _ := http.NewRequest("GET", "https://example.com", nil)
|
||||
So(GetClientIp(req4), ShouldEqual, "")
|
||||
})
|
||||
|
||||
Convey("GetCandidateAddrs", t, func() {
|
||||
// Should prioritize type in the following order: https://datatracker.ietf.org/doc/html/rfc8445#section-5.1.2.2
|
||||
// Break ties using priority value
|
||||
const offerStart = "v=0\r\no=- 4358805017720277108 2 IN IP4 8.8.8.8\r\ns=-\r\nt=0 0\r\na=group:BUNDLE data\r\na=msid-semantic: WMS\r\nm=application 56688 DTLS/SCTP 5000\r\nc=IN IP4 8.8.8.8\r\n"
|
||||
const offerEnd = "a=ice-ufrag:aMAZ\r\na=ice-pwd:jcHb08Jjgrazp2dzjdrvPPvV\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C8:88:EE:B9:E7:02:2E:21:37:ED:7A:D1:EB:2B:A3:15:A2:3B:5B:1C:3D:D4:D5:1F:06:CF:52:40:03:F8:DD:66\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n"
|
||||
|
||||
const sdp = offerStart + "a=candidate:3769337065 1 udp 2122260223 8.8.8.8 56688 typ prflx\r\n" +
|
||||
"a=candidate:3769337065 1 udp 2122260223 129.97.124.13 56688 typ relay\r\n" +
|
||||
"a=candidate:3769337065 1 udp 2122260223 129.97.124.14 56688 typ srflx\r\n" +
|
||||
"a=candidate:3769337065 1 udp 2122260223 129.97.124.15 56688 typ host\r\n" +
|
||||
"a=candidate:3769337065 1 udp 2122260224 129.97.124.16 56688 typ host\r\n" + offerEnd
|
||||
|
||||
So(GetCandidateAddrs(sdp), ShouldEqual, []net.IP{
|
||||
net.ParseIP("129.97.124.16"),
|
||||
net.ParseIP("129.97.124.15"),
|
||||
net.ParseIP("8.8.8.8"),
|
||||
net.ParseIP("129.97.124.14"),
|
||||
net.ParseIP("129.97.124.13"),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue