udptun: use routine to handle upstream connection

This commit is contained in:
nadoo 2017-09-11 15:02:59 +08:00
parent 3dc02c246b
commit 4b0388a47d
3 changed files with 102 additions and 20 deletions

View File

@ -2,8 +2,6 @@
[![Build Status](https://img.shields.io/travis/nadoo/glider.svg?style=flat-square)](https://travis-ci.org/nadoo/glider) [![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 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 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) [![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: Binary:
- [https://github.com/nadoo/glider/releases](https://github.com/nadoo/glider/releases) - [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 ```bash
go get -u github.com/nadoo/glider go get -u github.com/nadoo/glider
``` ```

View File

@ -1,10 +1,18 @@
// +build linux // +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 package main
import ( import (
"bytes"
"encoding/binary"
"fmt"
"net" "net"
"strconv"
"syscall" "syscall"
"unsafe"
) )
// TProxy struct // TProxy struct
@ -42,13 +50,13 @@ func (s *TProxy) ListenAndServeUDP() {
return return
} }
listener, err := net.ListenUDP("udp", laddr) lc, err := net.ListenUDP("udp", laddr)
if err != nil { if err != nil {
logf("proxy-tproxy failed to listen on %s: %v", s.addr, err) logf("proxy-tproxy failed to listen on %s: %v", s.addr, err)
return return
} }
fd, err := listener.File() fd, err := lc.File()
if err != nil { if err != nil {
logf("proxy-tproxy failed to get file descriptor: %v", err) logf("proxy-tproxy failed to get file descriptor: %v", err)
return return
@ -68,4 +76,78 @@ func (s *TProxy) ListenAndServeUDP() {
return 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
} }

View File

@ -45,11 +45,12 @@ func (s *UoTTun) ListenAndServe() {
continue continue
} }
go func() {
// NOTE: acturally udp over tcp // NOTE: acturally udp over tcp
rc, err := s.sDialer.Dial("udp", s.raddr) rc, err := s.sDialer.Dial("udp", s.raddr)
if err != nil { if err != nil {
logf("failed to connect to server %v: %v", s.raddr, err) logf("failed to connect to server %v: %v", s.raddr, err)
continue return
} }
rc.Write(buf[:n]) rc.Write(buf[:n])
@ -64,5 +65,6 @@ func (s *UoTTun) ListenAndServe() {
c.WriteTo(resp, clientAddr) c.WriteTo(resp, clientAddr)
logf("proxy-uottun %s <-> %s", clientAddr, s.raddr) logf("proxy-uottun %s <-> %s", clientAddr, s.raddr)
}()
} }
} }