girl 0.95.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of girl might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/girl.gemspec +0 -2
- data/lib/girl/proxy_worker.rb +312 -313
- data/lib/girl/proxyd_worker.rb +227 -178
- data/lib/girl/version.rb +1 -1
- metadata +2 -4
- data/lib/girl/udp.rb +0 -326
- data/lib/girl/udpd.rb +0 -316
    
        data/lib/girl/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: girl
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - takafan
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-11-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: escape evil.
         | 
| 14 14 | 
             
            email:
         | 
| @@ -27,8 +27,6 @@ files: | |
| 27 27 | 
             
            - lib/girl/proxyd.rb
         | 
| 28 28 | 
             
            - lib/girl/proxyd_custom.rb
         | 
| 29 29 | 
             
            - lib/girl/proxyd_worker.rb
         | 
| 30 | 
            -
            - lib/girl/udp.rb
         | 
| 31 | 
            -
            - lib/girl/udpd.rb
         | 
| 32 30 | 
             
            - lib/girl/version.rb
         | 
| 33 31 | 
             
            homepage: https://github.com/takafan/girl
         | 
| 34 32 | 
             
            licenses:
         | 
    
        data/lib/girl/udp.rb
    DELETED
    
    | @@ -1,326 +0,0 @@ | |
| 1 | 
            -
            require 'girl/version'
         | 
| 2 | 
            -
            require 'socket'
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            ##
         | 
| 5 | 
            -
            # Girl::Udp - udp透明转发,近端。
         | 
| 6 | 
            -
            #
         | 
| 7 | 
            -
            # usage
         | 
| 8 | 
            -
            # ======
         | 
| 9 | 
            -
            #
         | 
| 10 | 
            -
            # Girl::Udpd.new( 3030 ).looping # 远端
         | 
| 11 | 
            -
            #
         | 
| 12 | 
            -
            # Girl::Udp.new( 'your.server.ip', 3030, 1313 ).looping # 近端
         | 
| 13 | 
            -
            #
         | 
| 14 | 
            -
            # iptables -t nat -A PREROUTING -p udp -d game.server.ip -j REDIRECT --to-ports 1313
         | 
| 15 | 
            -
            #
         | 
| 16 | 
            -
            module Girl
         | 
| 17 | 
            -
              class Udp
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                def initialize( udpd_host, udpd_port = 3030, redir_port = 1313 )
         | 
| 20 | 
            -
                  @udpd_host = udpd_host
         | 
| 21 | 
            -
                  @udpd_addr = Socket.sockaddr_in( udpd_port, udpd_host )
         | 
| 22 | 
            -
                  @mutex = Mutex.new
         | 
| 23 | 
            -
                  @reads = []
         | 
| 24 | 
            -
                  @writes = []
         | 
| 25 | 
            -
                  @roles = {}        # :dotr / :redir / :tun
         | 
| 26 | 
            -
                  @redir_wbuffs = [] # [ src_addr data ] ...
         | 
| 27 | 
            -
                  @tuns = {}         # [ orig_src_addr dst_addr ]=> tun
         | 
| 28 | 
            -
                  @mappings = {}     # src_addr => [ orig_src_addr dst_addr ]
         | 
| 29 | 
            -
                  @tun_infos = {}    # tun => {}
         | 
| 30 | 
            -
                                     #   orig_src_addr: sockaddr
         | 
| 31 | 
            -
                                     #   dst_addr: sockaddr
         | 
| 32 | 
            -
                                     #   src_addr: sockaddr
         | 
| 33 | 
            -
                                     #   tund_addr: sockaddr
         | 
| 34 | 
            -
                                     #   rbuffs: []
         | 
| 35 | 
            -
                                     #   wbuffs: []
         | 
| 36 | 
            -
                                     #   last_traff_at: now
         | 
| 37 | 
            -
                  dotr, dotw = IO.pipe
         | 
| 38 | 
            -
                  @dotw = dotw
         | 
| 39 | 
            -
                  add_read( dotr, :dotr )
         | 
| 40 | 
            -
                  new_a_redir( redir_port )
         | 
| 41 | 
            -
                end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                def looping
         | 
| 44 | 
            -
                  loop_expire
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                  loop do
         | 
| 47 | 
            -
                    rs, ws = IO.select( @reads, @writes )
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                    @mutex.synchronize do
         | 
| 50 | 
            -
                      rs.each do | sock |
         | 
| 51 | 
            -
                        case @roles[ sock ]
         | 
| 52 | 
            -
                        when :dotr then
         | 
| 53 | 
            -
                          read_dotr( sock )
         | 
| 54 | 
            -
                        when :redir then
         | 
| 55 | 
            -
                          read_redir( sock )
         | 
| 56 | 
            -
                        when :tun then
         | 
| 57 | 
            -
                          read_tun( sock )
         | 
| 58 | 
            -
                        end
         | 
| 59 | 
            -
                      end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                      ws.each do | sock |
         | 
| 62 | 
            -
                        case @roles[ sock ]
         | 
| 63 | 
            -
                        when :redir then
         | 
| 64 | 
            -
                          write_redir( sock )
         | 
| 65 | 
            -
                        when :tun then
         | 
| 66 | 
            -
                          write_tun( sock )
         | 
| 67 | 
            -
                        end
         | 
| 68 | 
            -
                      end
         | 
| 69 | 
            -
                    end
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
                end
         | 
| 72 | 
            -
             | 
| 73 | 
            -
                def quit!
         | 
| 74 | 
            -
                  exit
         | 
| 75 | 
            -
                end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                private
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                def loop_expire
         | 
| 80 | 
            -
                  Thread.new do
         | 
| 81 | 
            -
                    loop do
         | 
| 82 | 
            -
                      sleep 30
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                      @mutex.synchronize do
         | 
| 85 | 
            -
                        trigger = false
         | 
| 86 | 
            -
                        now = Time.new
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                        @tun_infos.each do | tun, tun_info |
         | 
| 89 | 
            -
                          # net.netfilter.nf_conntrack_udp_timeout_stream
         | 
| 90 | 
            -
                          if now - tun_info[ :last_traff_at ] > 180 then
         | 
| 91 | 
            -
                            set_is_closing( tun )
         | 
| 92 | 
            -
                            trigger = true
         | 
| 93 | 
            -
                          end
         | 
| 94 | 
            -
                        end
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                        next_tick if trigger
         | 
| 97 | 
            -
                      end
         | 
| 98 | 
            -
                    end
         | 
| 99 | 
            -
                  end
         | 
| 100 | 
            -
                end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                def new_a_redir( redir_port )
         | 
| 103 | 
            -
                  redir = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
         | 
| 104 | 
            -
                  redir.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
         | 
| 105 | 
            -
                  redir.bind( Socket.sockaddr_in( redir_port, '0.0.0.0' ) )
         | 
| 106 | 
            -
                  puts "redir bound on #{ redir_port } #{ Time.new }"
         | 
| 107 | 
            -
                  @redir = redir
         | 
| 108 | 
            -
                  add_read( redir, :redir )
         | 
| 109 | 
            -
                end
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                def new_a_tun( orig_src_addr, dst_addr, src_addr )
         | 
| 112 | 
            -
                  tun = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
         | 
| 113 | 
            -
                  tun.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
         | 
| 114 | 
            -
                  tun.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                  @tun_infos[ tun ] = {
         | 
| 117 | 
            -
                    orig_src_addr: orig_src_addr,
         | 
| 118 | 
            -
                    dst_addr: dst_addr,
         | 
| 119 | 
            -
                    src_addr: src_addr,
         | 
| 120 | 
            -
                    tund_addr: nil,
         | 
| 121 | 
            -
                    rbuffs: [],
         | 
| 122 | 
            -
                    wbuffs: [],
         | 
| 123 | 
            -
                    last_traff_at: Time.new,
         | 
| 124 | 
            -
                    is_closing: false
         | 
| 125 | 
            -
                  }
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                  @tuns[ [ orig_src_addr, dst_addr ].join ] = tun
         | 
| 128 | 
            -
                  add_read( tun, :tun )
         | 
| 129 | 
            -
             | 
| 130 | 
            -
                  tun
         | 
| 131 | 
            -
                end
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                def add_redir_wbuff( redir, to_addr, data )
         | 
| 134 | 
            -
                  @redir_wbuffs << [ to_addr, data ]
         | 
| 135 | 
            -
                  add_write( redir )
         | 
| 136 | 
            -
                end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                def add_tun_wbuff( tun, to_addr, data )
         | 
| 139 | 
            -
                  tun_info = @tun_infos[ tun ]
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                  if to_addr then
         | 
| 142 | 
            -
                    tun_info[ :wbuffs ] << [ to_addr, data ]
         | 
| 143 | 
            -
                    add_write( tun )
         | 
| 144 | 
            -
                  else
         | 
| 145 | 
            -
                    tun_info[ :rbuffs ] << data
         | 
| 146 | 
            -
                  end
         | 
| 147 | 
            -
                end
         | 
| 148 | 
            -
             | 
| 149 | 
            -
                def add_read( sock, role )
         | 
| 150 | 
            -
                  unless @reads.include?( sock ) then
         | 
| 151 | 
            -
                    @reads << sock
         | 
| 152 | 
            -
                  end
         | 
| 153 | 
            -
             | 
| 154 | 
            -
                  @roles[ sock ] = role
         | 
| 155 | 
            -
                end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
                def add_write( sock )
         | 
| 158 | 
            -
                  unless @writes.include?( sock ) then
         | 
| 159 | 
            -
                    @writes << sock
         | 
| 160 | 
            -
                  end
         | 
| 161 | 
            -
                end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                def set_is_closing( tun )
         | 
| 164 | 
            -
                  if tun && !tun.closed? then
         | 
| 165 | 
            -
                    # puts "debug1 set tun is closing"
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                    tun_info = @tun_infos[ tun ]
         | 
| 168 | 
            -
                    tun_info[ :is_closing ] = true
         | 
| 169 | 
            -
             | 
| 170 | 
            -
                    @reads.delete( tun )
         | 
| 171 | 
            -
                    add_write( tun )
         | 
| 172 | 
            -
                  end
         | 
| 173 | 
            -
                end
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                def send_data( sock, data, to_addr )
         | 
| 176 | 
            -
                  begin
         | 
| 177 | 
            -
                    sock.sendmsg( data, 0, to_addr )
         | 
| 178 | 
            -
                  rescue IO::WaitWritable, Errno::EINTR
         | 
| 179 | 
            -
                    return false
         | 
| 180 | 
            -
                  rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN => e
         | 
| 181 | 
            -
                    if @roles[ sock ] == :tun then
         | 
| 182 | 
            -
                      puts "#{ Time.new } #{ e.class }, close tun"
         | 
| 183 | 
            -
                      close_tun( sock )
         | 
| 184 | 
            -
                      return false
         | 
| 185 | 
            -
                    end
         | 
| 186 | 
            -
                  end
         | 
| 187 | 
            -
             | 
| 188 | 
            -
                  true
         | 
| 189 | 
            -
                end
         | 
| 190 | 
            -
             | 
| 191 | 
            -
                def close_tun( tun )
         | 
| 192 | 
            -
                  tun.close
         | 
| 193 | 
            -
                  @reads.delete( tun )
         | 
| 194 | 
            -
                  @writes.delete( tun )
         | 
| 195 | 
            -
                  @roles.delete( tun )
         | 
| 196 | 
            -
                  tun_info = @tun_infos.delete( tun )
         | 
| 197 | 
            -
                  @tuns.delete( [ tun_info[ :orig_src_addr ], tun_info[ :dst_addr ] ].join )
         | 
| 198 | 
            -
             | 
| 199 | 
            -
                  if @mappings.include?( tun_info[ :src_addr ] ) then
         | 
| 200 | 
            -
                    orig_src_addr, dst_addr, timeout, read_at = @mappings[ tun_info[ :src_addr ] ]
         | 
| 201 | 
            -
             | 
| 202 | 
            -
                    if orig_src_addr == tun_info[ :orig_src_addr ] && dst_addr == tun_info[ :dst_addr ] then
         | 
| 203 | 
            -
                      @mappings.delete( tun_info[ :src_addr ] )
         | 
| 204 | 
            -
                    end
         | 
| 205 | 
            -
                  end
         | 
| 206 | 
            -
                end
         | 
| 207 | 
            -
             | 
| 208 | 
            -
                def write_redir( redir )
         | 
| 209 | 
            -
                  while @redir_wbuffs.any? do
         | 
| 210 | 
            -
                    to_addr, data = @redir_wbuffs.first
         | 
| 211 | 
            -
                    return unless send_data( redir, data, to_addr )
         | 
| 212 | 
            -
                    @redir_wbuffs.shift
         | 
| 213 | 
            -
                  end
         | 
| 214 | 
            -
             | 
| 215 | 
            -
                  @writes.delete( redir )
         | 
| 216 | 
            -
                end
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                def write_tun( tun )
         | 
| 219 | 
            -
                  tun_info = @tun_infos[ tun ]
         | 
| 220 | 
            -
             | 
| 221 | 
            -
                  if tun_info[ :is_closing ] then
         | 
| 222 | 
            -
                    close_tun( tun )
         | 
| 223 | 
            -
                    return
         | 
| 224 | 
            -
                  end
         | 
| 225 | 
            -
             | 
| 226 | 
            -
                  while tun_info[ :wbuffs ].any? do
         | 
| 227 | 
            -
                    to_addr, data = tun_info[ :wbuffs ].first
         | 
| 228 | 
            -
                    return unless send_data( tun, data, to_addr )
         | 
| 229 | 
            -
                    tun_info[ :wbuffs ].shift
         | 
| 230 | 
            -
                  end
         | 
| 231 | 
            -
             | 
| 232 | 
            -
                  @writes.delete( tun )
         | 
| 233 | 
            -
                end
         | 
| 234 | 
            -
             | 
| 235 | 
            -
                def read_dotr( dotr )
         | 
| 236 | 
            -
                  dotr.read( 1 )
         | 
| 237 | 
            -
                end
         | 
| 238 | 
            -
             | 
| 239 | 
            -
                def read_redir( redir )
         | 
| 240 | 
            -
                  data, addrinfo, rflags, *controls = redir.recvmsg
         | 
| 241 | 
            -
                  src_addr = addrinfo.to_sockaddr
         | 
| 242 | 
            -
                  is_hit_cache = false
         | 
| 243 | 
            -
                  now = Time.new
         | 
| 244 | 
            -
                  # puts "debug redir recv #{ data.inspect } from #{ addrinfo.inspect }"
         | 
| 245 | 
            -
             | 
| 246 | 
            -
                  if @mappings.include?( src_addr ) then
         | 
| 247 | 
            -
                    orig_src_addr, dst_addr, timeout, read_at = @mappings[ src_addr ]
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                    if now - read_at < timeout then
         | 
| 250 | 
            -
                      # puts "debug hit cache #{ addrinfo.inspect }"
         | 
| 251 | 
            -
                      is_hit_cache = true
         | 
| 252 | 
            -
                    else
         | 
| 253 | 
            -
                      # puts "debug cache timeout #{ addrinfo.inspect }"
         | 
| 254 | 
            -
                      @mappings.delete( src_addr )
         | 
| 255 | 
            -
                    end
         | 
| 256 | 
            -
                  end
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                  unless is_hit_cache then
         | 
| 259 | 
            -
                    # 2 udp 4 timeout 5 src 7 sport 9 [UNREPLIED] 11 dst 13 dport
         | 
| 260 | 
            -
                    # 2 udp 4 timeout 5 src 7 sport 10 dst 12 dport
         | 
| 261 | 
            -
                    bin = IO.binread( '/proc/net/nf_conntrack' )
         | 
| 262 | 
            -
                    rows = bin.split( "\n" ).map { | line | line.split( ' ' ) }
         | 
| 263 | 
            -
                    row = rows.find { | _row | _row[ 2 ] == 'udp' && ( ( _row[ 10 ].split( '=' )[ 1 ] == addrinfo.ip_address && _row[ 12 ].split( '=' )[ 1 ].to_i == addrinfo.ip_port ) || ( _row[ 9 ] == '[UNREPLIED]' && _row[ 11 ].split( '=' )[ 1 ] == addrinfo.ip_address && _row[ 13 ].split( '=' )[ 1 ].to_i == addrinfo.ip_port ) ) }
         | 
| 264 | 
            -
             | 
| 265 | 
            -
                    unless row then
         | 
| 266 | 
            -
                      puts "miss conntrack #{ addrinfo.inspect } #{ Time.new }"
         | 
| 267 | 
            -
                      IO.binwrite( '/tmp/nf_conntrack', bin )
         | 
| 268 | 
            -
                      return
         | 
| 269 | 
            -
                    end
         | 
| 270 | 
            -
             | 
| 271 | 
            -
                    timeout = row[ 4 ].to_i
         | 
| 272 | 
            -
                    orig_src_ip = row[ 5 ].split( '=' )[ 1 ]
         | 
| 273 | 
            -
                    orig_src_port = row[ 7 ].split( '=' )[ 1 ].to_i
         | 
| 274 | 
            -
                    dst_ip = row[ 6 ].split( '=' )[ 1 ]
         | 
| 275 | 
            -
                    dst_port = row[ 8 ].split( '=' )[ 1 ].to_i
         | 
| 276 | 
            -
                    orig_src_addr = Socket.sockaddr_in( orig_src_port, orig_src_ip )
         | 
| 277 | 
            -
                    dst_addr = Socket.sockaddr_in( dst_port, dst_ip )
         | 
| 278 | 
            -
             | 
| 279 | 
            -
                    if Addrinfo.new( dst_addr ).ipv4_private? then
         | 
| 280 | 
            -
                      puts "dst is private? #{ Addrinfo.new( dst_addr ).inspect } #{ Addrinfo.new( src_addr ).inspect } #{ Addrinfo.new( orig_src_addr ).inspect } #{ Time.new }"
         | 
| 281 | 
            -
                      add_redir_wbuff( redir, dst_addr, data )
         | 
| 282 | 
            -
                      return
         | 
| 283 | 
            -
                    end
         | 
| 284 | 
            -
             | 
| 285 | 
            -
                    # puts "debug save cache #{ addrinfo.inspect } #{ Addrinfo.new( orig_src_addr ).inspect } #{ Addrinfo.new( dst_addr ).inspect } #{ timeout } #{ now }"
         | 
| 286 | 
            -
                    @mappings[ src_addr ] = [ orig_src_addr, dst_addr, timeout, now ]
         | 
| 287 | 
            -
                  end
         | 
| 288 | 
            -
             | 
| 289 | 
            -
                  tun = @tuns[ [ orig_src_addr, dst_addr ].join ]
         | 
| 290 | 
            -
             | 
| 291 | 
            -
                  unless tun then
         | 
| 292 | 
            -
                    tun = new_a_tun( orig_src_addr, dst_addr, src_addr )
         | 
| 293 | 
            -
             | 
| 294 | 
            -
                    # puts "debug tun send to udpd #{ Addrinfo.new( orig_src_addr ).inspect } #{ Addrinfo.new( dst_addr ).inspect }"
         | 
| 295 | 
            -
                    ctlmsg = [ orig_src_addr, dst_addr ].join
         | 
| 296 | 
            -
                    add_tun_wbuff( tun, @udpd_addr, ctlmsg )
         | 
| 297 | 
            -
                  end
         | 
| 298 | 
            -
             | 
| 299 | 
            -
                  tun_info = @tun_infos[ tun ]
         | 
| 300 | 
            -
                  add_tun_wbuff( tun, tun_info[ :tund_addr ], data )
         | 
| 301 | 
            -
                end
         | 
| 302 | 
            -
             | 
| 303 | 
            -
                def read_tun( tun )
         | 
| 304 | 
            -
                  data, addrinfo, rflags, *controls = tun.recvmsg
         | 
| 305 | 
            -
                  from_addr = addrinfo.to_sockaddr
         | 
| 306 | 
            -
                  tun_info = @tun_infos[ tun ]
         | 
| 307 | 
            -
                  tun_info[ :last_traff_at ] = Time.new
         | 
| 308 | 
            -
             | 
| 309 | 
            -
                  if from_addr == @udpd_addr then
         | 
| 310 | 
            -
                    tund_port = data[ 0, 2 ].unpack( 'n' ).first
         | 
| 311 | 
            -
                    tund_addr = Socket.sockaddr_in( tund_port, @udpd_host )
         | 
| 312 | 
            -
                    tun_info[ :tund_addr ] = tund_addr
         | 
| 313 | 
            -
             | 
| 314 | 
            -
                    if tun_info[ :rbuffs ].any? then
         | 
| 315 | 
            -
                      tun_info[ :wbuffs ] += tun_info[ :rbuffs ].map{ | rbuff | [ tund_addr, rbuff ] }
         | 
| 316 | 
            -
                      tun_info[ :rbuffs ].clear
         | 
| 317 | 
            -
                      add_write( tun )
         | 
| 318 | 
            -
                    end
         | 
| 319 | 
            -
             | 
| 320 | 
            -
                  elsif from_addr == tun_info[ :tund_addr ] then
         | 
| 321 | 
            -
                    add_redir_wbuff( @redir, tun_info[ :src_addr ], data )
         | 
| 322 | 
            -
                  end
         | 
| 323 | 
            -
                end
         | 
| 324 | 
            -
             | 
| 325 | 
            -
              end
         | 
| 326 | 
            -
            end
         | 
    
        data/lib/girl/udpd.rb
    DELETED
    
    | @@ -1,316 +0,0 @@ | |
| 1 | 
            -
            require 'girl/version'
         | 
| 2 | 
            -
            require 'socket'
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            ##
         | 
| 5 | 
            -
            # Girl::Udpd - udp透明转发,远端。
         | 
| 6 | 
            -
            #
         | 
| 7 | 
            -
            module Girl
         | 
| 8 | 
            -
              class Udpd
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                def initialize( port = 3030 )
         | 
| 11 | 
            -
                  @mutex = Mutex.new
         | 
| 12 | 
            -
                  @reads = []
         | 
| 13 | 
            -
                  @writes = []
         | 
| 14 | 
            -
                  @roles = {}       # :dotr / :udpd / :tund
         | 
| 15 | 
            -
                  @udpd_wbuffs = [] # [ tun_addr ctlmsg ] ...
         | 
| 16 | 
            -
                  @tunds = {}       # [ tun_ip_addr orig_src_addr ] => tund
         | 
| 17 | 
            -
                  @tund_infos = {}  # tund => {}
         | 
| 18 | 
            -
                                    #   port: port
         | 
| 19 | 
            -
                                    #   tun_ip_addr: sockaddr
         | 
| 20 | 
            -
                                    #   orig_src_addr: sockaddr
         | 
| 21 | 
            -
                                    #   wbuffs: [] # [ to_addr, data ] ...
         | 
| 22 | 
            -
                                    #   dst_addrs: { tun_addr => dst_addr }
         | 
| 23 | 
            -
                                    #   tun_addrs: { dst_addr => tun_addr }
         | 
| 24 | 
            -
                                    #   is_tunneleds: { [ tun_addr dst_addr ] => false }
         | 
| 25 | 
            -
                                    #   unpaired_dst_rbuffs: { dst_addr => [] }
         | 
| 26 | 
            -
                                    #   last_traff_at: now
         | 
| 27 | 
            -
                  dotr, dotw = IO.pipe
         | 
| 28 | 
            -
                  @dotw = dotw
         | 
| 29 | 
            -
                  add_read( dotr, :dotr )
         | 
| 30 | 
            -
                  new_a_udpd( port )
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                def looping
         | 
| 34 | 
            -
                  loop_expire
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  loop do
         | 
| 37 | 
            -
                    rs, ws = IO.select( @reads, @writes )
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                    @mutex.synchronize do
         | 
| 40 | 
            -
                      ws.each do | sock |
         | 
| 41 | 
            -
                        case @roles[ sock ]
         | 
| 42 | 
            -
                        when :udpd then
         | 
| 43 | 
            -
                          write_udpd( sock )
         | 
| 44 | 
            -
                        when :tund then
         | 
| 45 | 
            -
                          write_tund( sock )
         | 
| 46 | 
            -
                        end
         | 
| 47 | 
            -
                      end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                      rs.each do | sock |
         | 
| 50 | 
            -
                        case @roles[ sock ]
         | 
| 51 | 
            -
                        when :dotr then
         | 
| 52 | 
            -
                          read_dotr( sock )
         | 
| 53 | 
            -
                        when :udpd then
         | 
| 54 | 
            -
                          read_udpd( sock )
         | 
| 55 | 
            -
                        when :tund then
         | 
| 56 | 
            -
                          read_tund( sock )
         | 
| 57 | 
            -
                        end
         | 
| 58 | 
            -
                      end
         | 
| 59 | 
            -
                    end
         | 
| 60 | 
            -
                  end
         | 
| 61 | 
            -
                end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                def quit!
         | 
| 64 | 
            -
                  exit
         | 
| 65 | 
            -
                end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                private
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                def loop_expire
         | 
| 70 | 
            -
                  Thread.new do
         | 
| 71 | 
            -
                    loop do
         | 
| 72 | 
            -
                      sleep 30
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                      @mutex.synchronize do
         | 
| 75 | 
            -
                        trigger = false
         | 
| 76 | 
            -
                        now = Time.new
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                        @tund_infos.each do | tund, tund_info |
         | 
| 79 | 
            -
                          # net.netfilter.nf_conntrack_udp_timeout_stream
         | 
| 80 | 
            -
                          if now - tund_info[ :last_traff_at ] > 180 then
         | 
| 81 | 
            -
                            set_is_closing( tund )
         | 
| 82 | 
            -
                            trigger = true
         | 
| 83 | 
            -
                          end
         | 
| 84 | 
            -
                        end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                        next_tick if trigger
         | 
| 87 | 
            -
                      end
         | 
| 88 | 
            -
                    end
         | 
| 89 | 
            -
                  end
         | 
| 90 | 
            -
                end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                def new_a_udpd( port )
         | 
| 93 | 
            -
                  udpd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
         | 
| 94 | 
            -
                  udpd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
         | 
| 95 | 
            -
                  udpd.bind( Socket.sockaddr_in( port, '0.0.0.0' ) )
         | 
| 96 | 
            -
                  puts "udpd bound on #{ port } #{ Time.new }"
         | 
| 97 | 
            -
                  @udpd = udpd
         | 
| 98 | 
            -
                  add_read( udpd, :udpd )
         | 
| 99 | 
            -
                end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                def pair_tund( tun_addr, tun_ip_addr, orig_src_addr, dst_addr )
         | 
| 102 | 
            -
                  from_addr = [ tun_ip_addr, orig_src_addr ].join
         | 
| 103 | 
            -
                  td_addr = [ tun_addr, dst_addr ].join
         | 
| 104 | 
            -
                  tund = @tunds[ from_addr ]
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                  if tund then
         | 
| 107 | 
            -
                    tund_info = @tund_infos[ tund ]
         | 
| 108 | 
            -
                    tund_info[ :dst_addrs ][ tun_addr ] = dst_addr
         | 
| 109 | 
            -
                    tund_info[ :tun_addrs ][ dst_addr ] = tun_addr
         | 
| 110 | 
            -
                    tund_info[ :is_tunneleds ][ td_addr ] = false
         | 
| 111 | 
            -
                  else
         | 
| 112 | 
            -
                    tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
         | 
| 113 | 
            -
                    tund.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
         | 
| 114 | 
            -
                    tund.setsockopt( Socket::SOL_SOCKET, Socket::SO_BROADCAST, 1 )
         | 
| 115 | 
            -
                    tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
         | 
| 116 | 
            -
                    tund_port = tund.local_address.ip_unpack.last
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                    @tund_infos[ tund ] = {
         | 
| 119 | 
            -
                      port: tund_port,
         | 
| 120 | 
            -
                      tun_ip_addr: tun_ip_addr,
         | 
| 121 | 
            -
                      orig_src_addr: orig_src_addr,
         | 
| 122 | 
            -
                      wbuffs: [],
         | 
| 123 | 
            -
                      dst_addrs: { tun_addr => dst_addr },
         | 
| 124 | 
            -
                      tun_addrs: { dst_addr => tun_addr },
         | 
| 125 | 
            -
                      is_tunneleds: { td_addr => false },
         | 
| 126 | 
            -
                      unpaired_dst_rbuffs: {},
         | 
| 127 | 
            -
                      last_traff_at: Time.new,
         | 
| 128 | 
            -
                      is_closing: false
         | 
| 129 | 
            -
                    }
         | 
| 130 | 
            -
             | 
| 131 | 
            -
                    @tunds[ from_addr ] = tund
         | 
| 132 | 
            -
                    add_read( tund, :tund )
         | 
| 133 | 
            -
                  end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                  tund
         | 
| 136 | 
            -
                end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                def add_tund_wbuff( tund, to_addr, data )
         | 
| 139 | 
            -
                  tund_info = @tund_infos[ tund ]
         | 
| 140 | 
            -
                  tund_info[ :wbuffs ] << [ to_addr, data ]
         | 
| 141 | 
            -
             | 
| 142 | 
            -
                  add_write( tund )
         | 
| 143 | 
            -
                end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                def add_read( sock, role )
         | 
| 146 | 
            -
                  unless @reads.include?( sock ) then
         | 
| 147 | 
            -
                    @reads << sock
         | 
| 148 | 
            -
                  end
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                  @roles[ sock ] = role
         | 
| 151 | 
            -
                end
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                def add_write( sock )
         | 
| 154 | 
            -
                  unless @writes.include?( sock ) then
         | 
| 155 | 
            -
                    @writes << sock
         | 
| 156 | 
            -
                  end
         | 
| 157 | 
            -
                end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                def set_is_closing( tund )
         | 
| 160 | 
            -
                  if tund && !tund.closed? then
         | 
| 161 | 
            -
                    # puts "debug1 set tund is closing"
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                    tund_info = @tund_infos[ tund ]
         | 
| 164 | 
            -
                    tund_info[ :is_closing ] = true
         | 
| 165 | 
            -
             | 
| 166 | 
            -
                    @reads.delete( tund )
         | 
| 167 | 
            -
                    add_write( tund )
         | 
| 168 | 
            -
                  end
         | 
| 169 | 
            -
                end
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                def send_data( sock, data, to_addr )
         | 
| 172 | 
            -
                  begin
         | 
| 173 | 
            -
                    sock.sendmsg( data, 0, to_addr )
         | 
| 174 | 
            -
                  rescue IO::WaitWritable, Errno::EINTR
         | 
| 175 | 
            -
                    return false
         | 
| 176 | 
            -
                  rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN => e
         | 
| 177 | 
            -
                    if @roles[ sock ] == :tund then
         | 
| 178 | 
            -
                      puts "#{ Time.new } #{ e.class }, close tund"
         | 
| 179 | 
            -
                      close_tund( sock )
         | 
| 180 | 
            -
                      return false
         | 
| 181 | 
            -
                    end
         | 
| 182 | 
            -
                  end
         | 
| 183 | 
            -
             | 
| 184 | 
            -
                  true
         | 
| 185 | 
            -
                end
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                def close_tund( tund )
         | 
| 188 | 
            -
                  tund.close
         | 
| 189 | 
            -
                  @reads.delete( tund )
         | 
| 190 | 
            -
                  @writes.delete( tund )
         | 
| 191 | 
            -
                  @roles.delete( tund )
         | 
| 192 | 
            -
                  tund_info = @tund_infos.delete( tund )
         | 
| 193 | 
            -
                  @tunds.delete( [ tund_info[ :tun_ip_addr ], tund_info[ :orig_src_addr ] ].join )
         | 
| 194 | 
            -
                end
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                def next_tick
         | 
| 197 | 
            -
                  @dotw.write( '.' )
         | 
| 198 | 
            -
                end
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                def write_udpd( udpd )
         | 
| 201 | 
            -
                  while @udpd_wbuffs.any? do
         | 
| 202 | 
            -
                    to_addr, data = @udpd_wbuffs.first
         | 
| 203 | 
            -
                    return unless send_data( udpd, data, to_addr )
         | 
| 204 | 
            -
                    @udpd_wbuffs.shift
         | 
| 205 | 
            -
                  end
         | 
| 206 | 
            -
             | 
| 207 | 
            -
                  @writes.delete( udpd )
         | 
| 208 | 
            -
                end
         | 
| 209 | 
            -
             | 
| 210 | 
            -
                def write_tund( tund )
         | 
| 211 | 
            -
                  tund_info = @tund_infos[ tund ]
         | 
| 212 | 
            -
             | 
| 213 | 
            -
                  if tund_info[ :is_closing ] then
         | 
| 214 | 
            -
                    close_tund( tund )
         | 
| 215 | 
            -
                    return
         | 
| 216 | 
            -
                  end
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                  while tund_info[ :wbuffs ].any? do
         | 
| 219 | 
            -
                    to_addr, data = tund_info[ :wbuffs ].first
         | 
| 220 | 
            -
                    return unless send_data( tund, data, to_addr )
         | 
| 221 | 
            -
                    tund_info[ :wbuffs ].shift
         | 
| 222 | 
            -
                  end
         | 
| 223 | 
            -
             | 
| 224 | 
            -
                  @writes.delete( tund )
         | 
| 225 | 
            -
                end
         | 
| 226 | 
            -
             | 
| 227 | 
            -
                def read_dotr( dotr )
         | 
| 228 | 
            -
                  dotr.read( 1 )
         | 
| 229 | 
            -
                end
         | 
| 230 | 
            -
             | 
| 231 | 
            -
                def read_udpd( udpd )
         | 
| 232 | 
            -
                  data, addrinfo, rflags, *controls = udpd.recvmsg
         | 
| 233 | 
            -
                  # puts "debug udpd recv #{ data.inspect } from #{ addrinfo.inspect }"
         | 
| 234 | 
            -
                  orig_src_addr = data[ 0, 16 ]
         | 
| 235 | 
            -
                  dst_addr = data[ 16, 16 ]
         | 
| 236 | 
            -
                  tun_addr = addrinfo.to_sockaddr
         | 
| 237 | 
            -
                  tun_ip_addr = Addrinfo.ip( addrinfo.ip_address ).to_sockaddr
         | 
| 238 | 
            -
             | 
| 239 | 
            -
                  return unless Addrinfo.new( orig_src_addr ).ipv4?
         | 
| 240 | 
            -
             | 
| 241 | 
            -
                  dst_addrinfo = Addrinfo.new( dst_addr )
         | 
| 242 | 
            -
                  return unless dst_addrinfo.ipv4?
         | 
| 243 | 
            -
                  return if dst_addrinfo.ipv4_private?
         | 
| 244 | 
            -
             | 
| 245 | 
            -
                  tund = pair_tund( tun_addr, tun_ip_addr, orig_src_addr, dst_addr )
         | 
| 246 | 
            -
                  tund_info = @tund_infos[ tund ]
         | 
| 247 | 
            -
                  tund_port = tund_info[ :port ]
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                  # puts "debug udpd send to tun #{ tund_port } #{ addrinfo.inspect }"
         | 
| 250 | 
            -
                  ctlmsg = [ tund_port ].pack( 'n' )
         | 
| 251 | 
            -
                  @udpd_wbuffs << [ tun_addr, ctlmsg ]
         | 
| 252 | 
            -
                  add_write( udpd )
         | 
| 253 | 
            -
                end
         | 
| 254 | 
            -
             | 
| 255 | 
            -
                def read_tund( tund )
         | 
| 256 | 
            -
                  data, addrinfo, rflags, *controls = tund.recvmsg
         | 
| 257 | 
            -
                  from_addr = addrinfo.to_sockaddr
         | 
| 258 | 
            -
                  tund_info = @tund_infos[ tund ]
         | 
| 259 | 
            -
                  tund_info[ :last_traff_at ] = Time.new
         | 
| 260 | 
            -
                  to_addr = tund_info[ :dst_addrs ][ from_addr ]
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                  if to_addr then
         | 
| 263 | 
            -
                    # 来自tun,发给dst。
         | 
| 264 | 
            -
                    td_addr = [ from_addr, to_addr ].join
         | 
| 265 | 
            -
                    is_tunneled = tund_info[ :is_tunneleds ][ td_addr ]
         | 
| 266 | 
            -
             | 
| 267 | 
            -
                    unless is_tunneled then
         | 
| 268 | 
            -
                      # puts "debug first traffic from tun #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
         | 
| 269 | 
            -
                      # 发暂存
         | 
| 270 | 
            -
                      if tund_info[ :unpaired_dst_rbuffs ].include?( to_addr ) then
         | 
| 271 | 
            -
                        rbuffs = tund_info[ :unpaired_dst_rbuffs ].delete( to_addr )
         | 
| 272 | 
            -
                        # puts "debug move tund.dst.rbuffs to tund.wbuffs #{ rbuffs.inspect }"
         | 
| 273 | 
            -
                        tund_info[ :wbuffs ] += rbuffs.map{ | rbuff | [ from_addr, rbuff ] }
         | 
| 274 | 
            -
                      end
         | 
| 275 | 
            -
             | 
| 276 | 
            -
                      tund_info[ :is_tunneleds ][ td_addr ] = true
         | 
| 277 | 
            -
                    end
         | 
| 278 | 
            -
             | 
| 279 | 
            -
                    # 如果对面没来过流量,且在nat里,nat规则是只对去过的目的地做接收,那么,先过去的流量会撞死。
         | 
| 280 | 
            -
                    # 没关系,撞死的流量通常是打洞数据,在应用计算之内,打洞数据通常是连发的。
         | 
| 281 | 
            -
                    # puts "debug #{ data.inspect } from #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
         | 
| 282 | 
            -
                    add_tund_wbuff( tund, to_addr, data )
         | 
| 283 | 
            -
                    return
         | 
| 284 | 
            -
                  end
         | 
| 285 | 
            -
             | 
| 286 | 
            -
                  to_addr = tund_info[ :tun_addrs ][ from_addr ]
         | 
| 287 | 
            -
             | 
| 288 | 
            -
                  if to_addr then
         | 
| 289 | 
            -
                    # 来自dst,发给tun。
         | 
| 290 | 
            -
                    # puts "debug #{ data.inspect } from #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
         | 
| 291 | 
            -
             | 
| 292 | 
            -
                    td_addr = [ to_addr, from_addr ].join
         | 
| 293 | 
            -
                    is_tunneled = tund_info[ :is_tunneleds ][ td_addr ]
         | 
| 294 | 
            -
             | 
| 295 | 
            -
                    if is_tunneled then
         | 
| 296 | 
            -
                      add_tund_wbuff( tund, to_addr, data )
         | 
| 297 | 
            -
                      return
         | 
| 298 | 
            -
                    end
         | 
| 299 | 
            -
             | 
| 300 | 
            -
                    # puts "debug #{ Addrinfo.new( to_addr ).inspect } #{ addrinfo.inspect } not tunneled"
         | 
| 301 | 
            -
                  end
         | 
| 302 | 
            -
             | 
| 303 | 
            -
                  # 来自未知的地方,或者对应的tun还没来流量,记暂存
         | 
| 304 | 
            -
                  unless tund_info[ :unpaired_dst_rbuffs ][ from_addr ] then
         | 
| 305 | 
            -
                    tund_info[ :unpaired_dst_rbuffs ][ from_addr ] = []
         | 
| 306 | 
            -
                  end
         | 
| 307 | 
            -
             | 
| 308 | 
            -
                  # 暂存5条(连发打洞数据,不需要存多)。
         | 
| 309 | 
            -
                  if tund_info[ :unpaired_dst_rbuffs ][ from_addr ].size < 5 then
         | 
| 310 | 
            -
                    # puts "debug save other dst rbuff #{ addrinfo.inspect } #{ data.inspect }"
         | 
| 311 | 
            -
                    tund_info[ :unpaired_dst_rbuffs ][ from_addr ] << data
         | 
| 312 | 
            -
                  end
         | 
| 313 | 
            -
                end
         | 
| 314 | 
            -
             | 
| 315 | 
            -
              end
         | 
| 316 | 
            -
            end
         |