1# faithd, ruby version. requires v6-enabled ruby. 2# 3# highly experimental (not working right at all) and very limited 4# functionality. 5# 6# $Id: faithd.rb,v 1.1.2.4 1999/05/10 17:06:30 itojun Exp $ 7# $FreeBSD$ 8 9require "socket" 10require "thread" 11 12# XXX should be derived from system headers 13IPPROTO_IPV6 = 41 14IPV6_FAITH = 29 15DEBUG = true 16DEBUG_LOOPBACK = true 17 18# TODO: OOB data handling 19def tcpcopy(s1, s2, m) 20 STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG 21 buf = "" 22 while TRUE 23 begin 24 buf = s1.sysread(100) 25 s2.syswrite(buf) 26 rescue EOFError 27 break 28 rescue IOError 29 break 30 end 31 end 32 STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG 33 s1.shutdown(0) 34 s2.shutdown(1) 35end 36 37def relay_ftp_passiveconn(s6, s4, dport6, dport4) 38 Thread.start do 39 d6 = TCPserver.open("::", dport6).accept 40 d4 = TCPsocket.open(s4.getpeer[3], dport4) 41 t = [] 42 t[0] = Thread.start do 43 tcpcopy(d6, d4) 44 end 45 t[1] = Thread.start do 46 tcpcopy(d4, d6) 47 end 48 for i in t 49 i.join 50 end 51 d4.close 52 d6.close 53 end 54end 55 56def ftp_parse_2428(line) 57 if (line[0] != line[line.length - 1]) 58 return nil 59 end 60 t = line.split(line[0 .. 0]) # as string 61 if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/) 62 return nil 63 end 64 return t[1 .. 3] 65end 66 67def relay_ftp_command(s6, s4, state) 68 STDERR.print "relay_ftp_command start\n" if DEBUG 69 while TRUE 70 begin 71 STDERR.print "s6.gets\n" if DEBUG 72 line = s6.gets 73 STDERR.print "line is #{line}\n" if DEBUG 74 if line == nil 75 return nil 76 end 77 78 # translate then copy 79 STDERR.print "line is #{line}\n" if DEBUG 80 if (line =~ /^EPSV\r\n/i) 81 STDERR.print "EPSV -> PASV\n" if DEBUG 82 line = "PASV\n" 83 state = "EPSV" 84 elsif (line =~ /^EPRT\s+(.+)\r\n/i) 85 t = ftp_parse_2428($1) 86 if t == nil 87 s6.puts "501 illegal parameter to EPRT\r\n" 88 next 89 end 90 91 # some tricks should be here 92 s6.puts "501 illegal parameter to EPRT\r\n" 93 next 94 end 95 STDERR.print "fail: send #{line} as is\n" if DEBUG 96 s4.puts(line) 97 break 98 rescue EOFError 99 return nil 100 rescue IOError 101 return nil 102 end 103 end 104 STDERR.print "relay_ftp_command finish\n" if DEBUG 105 return state 106end 107 108def relay_ftp_status(s4, s6, state) 109 STDERR.print "relay_ftp_status start\n" if DEBUG 110 while TRUE 111 begin 112 line = s4.gets 113 if line == nil 114 return nil 115 end 116 117 # translate then copy 118 s6.puts(line) 119 120 next if line =~ /^\d\d\d-/ 121 next if line !~ /^\d/ 122 123 # special post-processing 124 case line 125 when /^221 / # result to QUIT 126 s4.shutdown(0) 127 s6.shutdown(1) 128 end 129 130 break if (line =~ /^\d\d\d /) 131 rescue EOFError 132 return nil 133 rescue IOError 134 return nil 135 end 136 end 137 STDERR.print "relay_ftp_status finish\n" if DEBUG 138 return state 139end 140 141def relay_ftp(sock, name) 142 STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG 143 while TRUE 144 STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG 145 s = sock.accept 146 STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG 147 Thread.start do 148 threads = [] 149 STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG 150 s6 = s 151 dest6 = s.addr[3] 152 if !DEBUG_LOOPBACK 153 t = s.getsockname.unpack("x8 x12 C4") 154 dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" 155 port4 = s.addr[1] 156 else 157 dest4 = "127.0.0.1" 158 port4 = "ftp" 159 end 160 if DEBUG 161 STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG 162 end 163 STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG 164 s4 = TCPsocket.open(dest4, port4) 165 STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG 166 state = 0 167 while TRUE 168 # translate status line 169 state = relay_ftp_status(s4, s6, state) 170 break if state == nil 171 # translate command line 172 state = relay_ftp_command(s6, s4, state) 173 break if state == nil 174 end 175 STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG 176 s4.close 177 STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG 178 s6.close 179 STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG 180 end 181 end 182 STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG 183end 184 185def relay_tcp(sock, name) 186 STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG 187 while TRUE 188 STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG 189 s = sock.accept 190 STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG 191 Thread.start do 192 threads = [] 193 STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG 194 s6 = s 195 dest6 = s.addr[3] 196 if !DEBUG_LOOPBACK 197 t = s.getsockname.unpack("x8 x12 C4") 198 dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" 199 port4 = s.addr[1] 200 else 201 dest4 = "127.0.0.1" 202 port4 = "telnet" 203 end 204 if DEBUG 205 STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG 206 end 207 STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG 208 s4 = TCPsocket.open(dest4, port4) 209 STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG 210 [0, 1].each do |i| 211 threads[i] = Thread.start do 212 if (i == 0) 213 tcpcopy(s6, s4) 214 else 215 tcpcopy(s4, s6) 216 end 217 end 218 end 219 STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG 220 for i in threads 221 STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG 222 i.join 223 STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG 224 end 225 STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG 226 s4.close 227 STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG 228 s6.close 229 STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG 230 end 231 end 232 STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG 233end 234 235def usage() 236 STDERR.print "usage: #{$0} [-f] port...\n" 237end 238 239#------------------------------------------------------------ 240 241$mode = "tcp" 242 243while ARGV[0] =~ /^-/ do 244 case ARGV[0] 245 when /^-f/ 246 $mode = "ftp" 247 else 248 usage() 249 exit 0 250 end 251 ARGV.shift 252end 253 254if ARGV.length == 0 255 usage() 256 exit 1 257end 258 259ftpport = Socket.getservbyname("ftp") 260 261res = [] 262for port in ARGV 263 t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM, 264 nil, Socket::AI_PASSIVE) 265 if (t.size <= 0) 266 STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n" 267 exit 1 268 end 269 res += t 270end 271 272sockpool = [] 273names = [] 274listenthreads = [] 275 276res.each do |i| 277 s = TCPserver.new(i[3], i[1]) 278 n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ") 279 if i[6] == IPPROTO_IPV6 280 s.setsockopt(i[6], IPV6_FAITH, 1) 281 end 282 s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) 283 sockpool.push s 284 names.push n 285end 286 287if DEBUG 288 (0 .. sockpool.size - 1).each do |i| 289 STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG 290 end 291end 292 293(0 .. sockpool.size - 1).each do |i| 294 listenthreads[i] = Thread.start do 295 if DEBUG 296 STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG 297 end 298 STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG 299 case $mode 300 when "tcp" 301 relay_tcp(sockpool[i], names[i]) 302 when "ftp" 303 relay_ftp(sockpool[i], names[i]) 304 end 305 end 306end 307 308for i in listenthreads 309 i.join 310end 311 312exit 0 313