func (ut *udpConnTrack) run() { // connect to socks var e error for i := 0; i < 2; i++ { ut.socksConn, e = dialLocalSocks(ut.localSocksAddr) if e != nil { log.Printf("fail to connect SOCKS proxy: %s", e) } else { // need to finish handshake in 1 mins ut.socksConn.SetDeadline(time.Now().Add(time.Minute * 1)) break } } if ut.socksConn == nil { close(ut.socksClosed) close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) return } // create one UDP to recv/send packets socksAddr := ut.socksConn.LocalAddr().(*net.TCPAddr) udpBind, err := net.ListenUDP("udp", &net.UDPAddr{ IP: socksAddr.IP, Port: 0, Zone: socksAddr.Zone, }) if err != nil { log.Printf("error in binding local UDP: %s", err) ut.socksConn.Close() close(ut.socksClosed) close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) return } // socks request/reply _, e = gosocks.WriteSocksRequest(ut.socksConn, &gosocks.SocksRequest{ Cmd: gosocks.SocksCmdUDPAssociate, HostType: gosocks.SocksIPv4Host, DstHost: "0.0.0.0", DstPort: 0, }) if e != nil { log.Printf("error to send socks request: %s", e) ut.socksConn.Close() close(ut.socksClosed) close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) return } reply, e := gosocks.ReadSocksReply(ut.socksConn) if e != nil { log.Printf("error to read socks reply: %s", e) ut.socksConn.Close() close(ut.socksClosed) close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) return } if reply.Rep != gosocks.SocksSucceeded { log.Printf("socks connect request fail, retcode: %d", reply.Rep) ut.socksConn.Close() close(ut.socksClosed) close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) return } relayAddr := gosocks.SocksAddrToNetAddr("udp", reply.BndHost, reply.BndPort).(*net.UDPAddr) ut.socksConn.SetDeadline(time.Time{}) // monitor socks TCP connection go gosocks.ConnMonitor(ut.socksConn, ut.socksClosed) // read UDP packets from relay quitUDP := make(chan bool) chRelayUDP := make(chan *gosocks.UDPPacket) go gosocks.UDPReader(udpBind, chRelayUDP, quitUDP) for { var t *time.Timer if ut.t2s.isDNS(ut.remoteIP.String(), ut.remotePort) { t = time.NewTimer(10 * time.Second) } else { t = time.NewTimer(2 * time.Minute) } select { // pkt from relay case pkt, ok := <-chRelayUDP: if !ok { ut.socksConn.Close() udpBind.Close() close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) close(quitUDP) return } if pkt.Addr.String() != relayAddr.String() { continue } udpReq, err := gosocks.ParseUDPRequest(pkt.Data) if err != nil { log.Printf("error to parse UDP request from relay: %s", err) continue } if udpReq.Frag != gosocks.SocksNoFragment { continue } ut.send(udpReq.Data) if ut.t2s.isDNS(ut.remoteIP.String(), ut.remotePort) { // DNS-without-fragment only has one request-response log.Printf("DNS session") if ut.t2s.cache != nil { ut.t2s.cache.store(udpReq.Data) } ut.socksConn.Close() udpBind.Close() close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) close(quitUDP) return } // pkt from tun case pkt := <-ut.fromTunCh: req := &gosocks.UDPRequest{ Frag: 0, HostType: gosocks.SocksIPv4Host, DstHost: pkt.ip.DstIP.String(), DstPort: uint16(pkt.udp.DstPort), Data: pkt.udp.Payload, } datagram := gosocks.PackUDPRequest(req) _, err := udpBind.WriteToUDP(datagram, relayAddr) releaseUDPPacket(pkt) if err != nil { log.Printf("error to send UDP packet to relay: %s", err) ut.socksConn.Close() udpBind.Close() close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) close(quitUDP) return } case <-ut.socksClosed: ut.socksConn.Close() udpBind.Close() close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) close(quitUDP) return case <-t.C: ut.socksConn.Close() udpBind.Close() close(ut.quitBySelf) ut.t2s.clearUDPConnTrack(ut.id) close(quitUDP) return case <-ut.quitByOther: log.Printf("udpConnTrack quitByOther") ut.socksConn.Close() udpBind.Close() close(quitUDP) return } t.Stop() } }
func (tt *tcpConnTrack) tcpSocks2Tun(dstIP net.IP, dstPort uint16, conn net.Conn, readCh chan<- []byte, writeCh <-chan *tcpPacket, closeCh chan bool) { _, e := gosocks.WriteSocksRequest(conn, &gosocks.SocksRequest{ Cmd: gosocks.SocksCmdConnect, HostType: gosocks.SocksIPv4Host, DstHost: dstIP.String(), DstPort: dstPort, }) if e != nil { log.Printf("error to send socks request: %s", e) conn.Close() close(closeCh) return } reply, e := gosocks.ReadSocksReply(conn) if e != nil { log.Printf("error to read socks reply: %s", e) conn.Close() close(closeCh) return } if reply.Rep != gosocks.SocksSucceeded { log.Printf("socks connect request fail, retcode: %d", reply.Rep) conn.Close() close(closeCh) return } // writer go func() { loop: for { select { case <-closeCh: break loop case pkt := <-writeCh: conn.Write(pkt.tcp.Payload) // increase window when processed wnd := atomic.LoadInt32(&tt.recvWindow) wnd += int32(len(pkt.tcp.Payload)) if wnd > int32(MAX_RECV_WINDOW) { wnd = int32(MAX_RECV_WINDOW) } atomic.StoreInt32(&tt.recvWindow, wnd) releaseTCPPacket(pkt) } } }() // reader for { var buf [MTU - 40]byte // tt.sendWndCond.L.Lock() var wnd int32 var cur int32 wnd = atomic.LoadInt32(&tt.sendWindow) if wnd <= 0 { for wnd <= 0 { tt.sendWndCond.L.Lock() tt.sendWndCond.Wait() wnd = atomic.LoadInt32(&tt.sendWindow) } tt.sendWndCond.L.Unlock() } cur = wnd if cur > MTU-40 { cur = MTU - 40 } // tt.sendWndCond.L.Unlock() n, e := conn.Read(buf[:cur]) if e != nil { log.Printf("error to read from socks: %s", e) conn.Close() break } else { b := make([]byte, n) copy(b, buf[:n]) readCh <- b // tt.sendWndCond.L.Lock() nxt := wnd - int32(n) if nxt < 0 { nxt = 0 } // if sendWindow does not equal to wnd, it is already updated by a // received pkt from TUN atomic.CompareAndSwapInt32(&tt.sendWindow, wnd, nxt) // tt.sendWndCond.L.Unlock() } } close(closeCh) }