mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
ss: support udp in server mode
This commit is contained in:
parent
5f661de379
commit
99ad1eb762
27
conn.go
27
conn.go
@ -52,3 +52,30 @@ func relay(left, right net.Conn) (int64, int64, error) {
|
|||||||
}
|
}
|
||||||
return n, rs.N, err
|
return n, rs.N, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy from src to dst at target with read timeout
|
||||||
|
func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration, srcIncluded bool) error {
|
||||||
|
buf := make([]byte, udpBufSize)
|
||||||
|
|
||||||
|
for {
|
||||||
|
src.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
n, raddr, err := src.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcIncluded { // server -> client: add original packet source
|
||||||
|
srcAddr := ParseAddr(raddr.String())
|
||||||
|
copy(buf[len(srcAddr):], buf[:n])
|
||||||
|
copy(buf, srcAddr)
|
||||||
|
_, err = dst.WriteTo(buf[:len(srcAddr)+n], target)
|
||||||
|
} else { // client -> user: strip original packet source
|
||||||
|
srcAddr := SplitAddr(buf[:n])
|
||||||
|
_, err = dst.WriteTo(buf[len(srcAddr):n], target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7
dns.go
7
dns.go
@ -138,10 +138,15 @@ func (s *DNS) ListenAndServe() {
|
|||||||
// length is not needed in udp dns response. (2 bytes)
|
// length is not needed in udp dns response. (2 bytes)
|
||||||
// SEE RFC1035, section 4.2.2 TCP: The message is prefixed with a two byte length field which gives the message length, excluding the two byte length field.
|
// SEE RFC1035, section 4.2.2 TCP: The message is prefixed with a two byte length field which gives the message length, excluding the two byte length field.
|
||||||
if respLen > 0 {
|
if respLen > 0 {
|
||||||
|
|
||||||
|
// run handle functions before send to client so RULE and IPSET can take effect
|
||||||
|
// TODO: add PRE_HANDLERS
|
||||||
query := parseQuery(respMsg)
|
query := parseQuery(respMsg)
|
||||||
if (query.QueryType == DNSQueryTypeA || query.QueryType == DNSQueryTypeAAAA) &&
|
if (query.QueryType == DNSQueryTypeA || query.QueryType == DNSQueryTypeAAAA) &&
|
||||||
len(respMsg) > query.Offset {
|
len(respMsg) > query.Offset {
|
||||||
|
|
||||||
answers := parseAnswers(respMsg[query.Offset:])
|
answers := parseAnswers(respMsg[query.Offset:])
|
||||||
|
|
||||||
for _, answer := range answers {
|
for _, answer := range answers {
|
||||||
if answer.IP != "" {
|
if answer.IP != "" {
|
||||||
ip += answer.IP + ","
|
ip += answer.IP + ","
|
||||||
@ -150,9 +155,7 @@ func (s *DNS) ListenAndServe() {
|
|||||||
for _, h := range s.answerHandlers {
|
for _, h := range s.answerHandlers {
|
||||||
h(query.DomainName, answer.IP)
|
h(query.DomainName, answer.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.WriteTo(respMsg, clientAddr)
|
_, err = c.WriteTo(respMsg, clientAddr)
|
||||||
|
18
main.go
18
main.go
@ -13,20 +13,20 @@ const VERSION = "0.4.3"
|
|||||||
|
|
||||||
func dialerFromConf() Dialer {
|
func dialerFromConf() Dialer {
|
||||||
// global forwarders in xx.conf
|
// global forwarders in xx.conf
|
||||||
var forwarders []Dialer
|
var fwdrs []Dialer
|
||||||
for _, chain := range conf.Forward {
|
for _, chain := range conf.Forward {
|
||||||
var forward Dialer
|
var fwdr Dialer
|
||||||
var err error
|
var err error
|
||||||
for _, url := range strings.Split(chain, ",") {
|
for _, url := range strings.Split(chain, ",") {
|
||||||
forward, err = DialerFromURL(url, forward)
|
fwdr, err = DialerFromURL(url, fwdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forwarders = append(forwarders, forward)
|
fwdrs = append(fwdrs, fwdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewStrategyDialer(conf.Strategy, forwarders, conf.CheckWebSite, conf.CheckDuration)
|
return NewStrategyDialer(conf.Strategy, fwdrs, conf.CheckWebSite, conf.CheckDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -55,10 +55,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rule
|
// rule
|
||||||
for _, frwder := range conf.rules {
|
for _, fwdr := range conf.rules {
|
||||||
for _, domain := range frwder.Domain {
|
for _, domain := range fwdr.Domain {
|
||||||
if len(frwder.DNSServer) > 0 {
|
if len(fwdr.DNSServer) > 0 {
|
||||||
dns.SetServer(domain, frwder.DNSServer[0])
|
dns.SetServer(domain, fwdr.DNSServer[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
rule.go
12
rule.go
@ -21,20 +21,20 @@ func NewRuleDialer(rules []*RuleConf, gDialer Dialer) *RuleDialer {
|
|||||||
rd := &RuleDialer{gDialer: gDialer}
|
rd := &RuleDialer{gDialer: gDialer}
|
||||||
|
|
||||||
for _, r := range rules {
|
for _, r := range rules {
|
||||||
var forwarders []Dialer
|
var fwdrs []Dialer
|
||||||
for _, chain := range r.Forward {
|
for _, chain := range r.Forward {
|
||||||
var forward Dialer
|
var fwdr Dialer
|
||||||
var err error
|
var err error
|
||||||
for _, url := range strings.Split(chain, ",") {
|
for _, url := range strings.Split(chain, ",") {
|
||||||
forward, err = DialerFromURL(url, forward)
|
fwdr, err = DialerFromURL(url, fwdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forwarders = append(forwarders, forward)
|
fwdrs = append(fwdrs, fwdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
sDialer := NewStrategyDialer(r.Strategy, forwarders, r.CheckWebSite, r.CheckDuration)
|
sDialer := NewStrategyDialer(r.Strategy, fwdrs, r.CheckWebSite, r.CheckDuration)
|
||||||
|
|
||||||
for _, domain := range r.Domain {
|
for _, domain := range r.Domain {
|
||||||
rd.domainMap.Store(domain, sDialer)
|
rd.domainMap.Store(domain, sDialer)
|
||||||
@ -123,7 +123,7 @@ func (rd *RuleDialer) AddDomainIP(domain, ip string) error {
|
|||||||
// find in domainMap
|
// find in domainMap
|
||||||
if dialer, ok := rd.domainMap.Load(pDomain); ok {
|
if dialer, ok := rd.domainMap.Load(pDomain); ok {
|
||||||
rd.ipMap.Store(ip, dialer)
|
rd.ipMap.Store(ip, dialer)
|
||||||
logf("rule add `ip=%s`, based on rule: `domain=%s`, domain/ip: %s/%s\n", ip, pDomain, domain, ip)
|
logf("rule add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, pDomain, domain, ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ func (s *SOCKS5) Serve(c net.Conn) {
|
|||||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||||
return // ignore i/o timeout
|
return // ignore i/o timeout
|
||||||
}
|
}
|
||||||
logf("relay error: %v", err)
|
logf("proxy-socks5 relay error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
85
ss.go
85
ss.go
@ -5,6 +5,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||||
)
|
)
|
||||||
@ -37,6 +39,12 @@ func NewSS(addr, method, pass string, cDialer Dialer, sDialer Dialer) (*SS, erro
|
|||||||
|
|
||||||
// ListenAndServe serves ss requests.
|
// ListenAndServe serves ss requests.
|
||||||
func (s *SS) ListenAndServe() {
|
func (s *SS) ListenAndServe() {
|
||||||
|
go s.ListenAndServeUDP()
|
||||||
|
s.ListenAndServeTCP()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServeTCP serves tcp ss requests.
|
||||||
|
func (s *SS) ListenAndServeTCP() {
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("proxy-ss failed to listen on %s: %v", s.addr, err)
|
logf("proxy-ss failed to listen on %s: %v", s.addr, err)
|
||||||
@ -51,12 +59,12 @@ func (s *SS) ListenAndServe() {
|
|||||||
logf("proxy-ss failed to accept: %v", err)
|
logf("proxy-ss failed to accept: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go s.Serve(c)
|
go s.ServeTCP(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve .
|
// ServeTCP serves tcp ss requests.
|
||||||
func (s *SS) Serve(c net.Conn) {
|
func (s *SS) ServeTCP(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if c, ok := c.(*net.TCPConn); ok {
|
if c, ok := c.(*net.TCPConn); ok {
|
||||||
@ -125,7 +133,76 @@ func (s *SS) Serve(c net.Conn) {
|
|||||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||||
return // ignore i/o timeout
|
return // ignore i/o timeout
|
||||||
}
|
}
|
||||||
logf("relay error: %v", err)
|
logf("proxy-ss relay error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServeUDP serves udp ss requests.
|
||||||
|
// TODO: Forwarder chain not supported now.
|
||||||
|
func (s *SS) ListenAndServeUDP() {
|
||||||
|
c, err := net.ListenPacket("udp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
logf("proxy-ss-udp failed to listen on %s: %v", s.addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
logf("proxy-ss-udp listening UDP on %s", s.addr)
|
||||||
|
|
||||||
|
c = s.PacketConn(c)
|
||||||
|
|
||||||
|
var nm sync.Map
|
||||||
|
buf := make([]byte, udpBufSize)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, raddr, err := c.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
logf("proxy-ss-udp remote read error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tgtAddr := SplitAddr(buf[:n])
|
||||||
|
if tgtAddr == nil {
|
||||||
|
logf("proxy-ss-udp failed to split target address from packet: %q", buf[:n])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
|
||||||
|
if err != nil {
|
||||||
|
logf("proxy-ss-udp failed to resolve target UDP address: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("proxy-ss-udp %s <-> %s", raddr, tgtAddr)
|
||||||
|
|
||||||
|
payload := buf[len(tgtAddr):n]
|
||||||
|
|
||||||
|
var pc net.PacketConn
|
||||||
|
v, ok := nm.Load(raddr.String())
|
||||||
|
if !ok && v == nil {
|
||||||
|
pc, err = net.ListenPacket("udp", "")
|
||||||
|
if err != nil {
|
||||||
|
logf("proxy-ss-udp remote listen error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nm.Store(raddr.String(), pc)
|
||||||
|
go func() {
|
||||||
|
timedCopy(c, raddr, pc, 5*time.Minute, true)
|
||||||
|
pc.Close()
|
||||||
|
nm.Delete(raddr.String())
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
pc = v.(net.PacketConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pc.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
|
||||||
|
if err != nil {
|
||||||
|
logf("proxy-ss-udp remote write error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user