From 4b0388a47d5af6cc1b6a2af6f832854f65de0b5a Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Mon, 11 Sep 2017 15:02:59 +0800 Subject: [PATCH] udptun: use routine to handle upstream connection --- README.md | 4 +-- tproxy.go | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- uottun.go | 32 +++++++++++---------- 3 files changed, 102 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 3c1335a..bb2b225 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![Build Status](https://img.shields.io/travis/nadoo/glider.svg?style=flat-square)](https://travis-ci.org/nadoo/glider) [![Go Report Card](https://goreportcard.com/badge/github.com/nadoo/glider?style=flat-square)](https://goreportcard.com/report/github.com/nadoo/glider) -[![Go Version](https://img.shields.io/badge/go-1.9%2B-brightgreen.svg?style=flat-square)](https://golang.org/dl/) -[![License](https://img.shields.io/badge/license-GPL3.0-blue.svg?style=flat-square)](https://github.com/nadoo/glider/blob/master/LICENSE) [![GitHub tag](https://img.shields.io/github/tag/nadoo/glider.svg?style=flat-square)](https://github.com/nadoo/glider/releases) [![GitHub release](https://img.shields.io/github/release/nadoo/glider.svg?style=flat-square)](https://github.com/nadoo/glider/releases) @@ -61,7 +59,7 @@ TODO: Binary: - [https://github.com/nadoo/glider/releases](https://github.com/nadoo/glider/releases) -Go Get (requires **Go 1.9 or newer**): +Go Get (requires **Go 1.9+** ): ```bash go get -u github.com/nadoo/glider ``` diff --git a/tproxy.go b/tproxy.go index 0058e93..03e74ad 100644 --- a/tproxy.go +++ b/tproxy.go @@ -1,10 +1,18 @@ // +build linux +// ref: https://www.kernel.org/doc/Documentation/networking/tproxy.txt +// @LiamHaworth: https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go + package main import ( + "bytes" + "encoding/binary" + "fmt" "net" + "strconv" "syscall" + "unsafe" ) // TProxy struct @@ -42,13 +50,13 @@ func (s *TProxy) ListenAndServeUDP() { return } - listener, err := net.ListenUDP("udp", laddr) + lc, err := net.ListenUDP("udp", laddr) if err != nil { logf("proxy-tproxy failed to listen on %s: %v", s.addr, err) return } - fd, err := listener.File() + fd, err := lc.File() if err != nil { logf("proxy-tproxy failed to get file descriptor: %v", err) return @@ -68,4 +76,78 @@ func (s *TProxy) ListenAndServeUDP() { return } + for { + + buf := make([]byte, 1024) + _, srcAddr, dstAddr, err := ReadFromUDP(lc, buf) + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Temporary() { + logf("proxy-tproxy Temporary error while reading data: %s", netErr) + } + + logf("proxy-tproxy Unrecoverable error while reading data: %s", err) + return + } + + logf("proxy-tproxy Accepting UDP connection from %s with destination of %s", srcAddr.String(), dstAddr.String()) + + } + +} + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// Out-of-band data is also read in so that the original destination +// address can be identified and parsed. +func ReadFromUDP(conn *net.UDPConn, b []byte) (int, *net.UDPAddr, *net.UDPAddr, error) { + oob := make([]byte, 1024) + n, oobn, _, addr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return 0, nil, nil, err + } + + msgs, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + return 0, nil, nil, fmt.Errorf("parsing socket control message: %s", err) + } + + var originalDst *net.UDPAddr + for _, msg := range msgs { + if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR { + originalDstRaw := &syscall.RawSockaddrInet4{} + if err = binary.Read(bytes.NewReader(msg.Data), binary.LittleEndian, originalDstRaw); err != nil { + return 0, nil, nil, fmt.Errorf("reading original destination address: %s", err) + } + + switch originalDstRaw.Family { + case syscall.AF_INET: + pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(originalDstRaw)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + originalDst = &net.UDPAddr{ + IP: net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3]), + Port: int(p[0])<<8 + int(p[1]), + } + + case syscall.AF_INET6: + pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(originalDstRaw)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + originalDst = &net.UDPAddr{ + IP: net.IP(pp.Addr[:]), + Port: int(p[0])<<8 + int(p[1]), + Zone: strconv.Itoa(int(pp.Scope_id)), + } + + default: + return 0, nil, nil, fmt.Errorf("original destination is an unsupported network family") + } + } + } + + if originalDst == nil { + return 0, nil, nil, fmt.Errorf("unable to obtain original destination: %s", err) + } + + return n, addr, originalDst, nil } diff --git a/uottun.go b/uottun.go index 9803832..62e6247 100644 --- a/uottun.go +++ b/uottun.go @@ -45,24 +45,26 @@ func (s *UoTTun) ListenAndServe() { continue } - // NOTE: acturally udp over tcp - rc, err := s.sDialer.Dial("udp", s.raddr) - if err != nil { - logf("failed to connect to server %v: %v", s.raddr, err) - continue - } + go func() { + // NOTE: acturally udp over tcp + rc, err := s.sDialer.Dial("udp", s.raddr) + if err != nil { + logf("failed to connect to server %v: %v", s.raddr, err) + return + } - rc.Write(buf[:n]) + rc.Write(buf[:n]) - resp, err := ioutil.ReadAll(rc) - if err != nil { - logf("error in ioutil.ReadAll: %s\n", err) - return - } - rc.Close() + resp, err := ioutil.ReadAll(rc) + if err != nil { + logf("error in ioutil.ReadAll: %s\n", err) + return + } + rc.Close() - c.WriteTo(resp, clientAddr) + c.WriteTo(resp, clientAddr) - logf("proxy-uottun %s <-> %s", clientAddr, s.raddr) + logf("proxy-uottun %s <-> %s", clientAddr, s.raddr) + }() } }