156668Sshin# faithd, ruby version. requires v6-enabled ruby. 256668Sshin# 356668Sshin# highly experimental (not working right at all) and very limited 456668Sshin# functionality. 556668Sshin# 6122679Sume# $Id: faithd.rb,v 1.1.2.4 1999/05/10 17:06:30 itojun Exp $ 756668Sshin# $FreeBSD$ 856668Sshin 956668Sshinrequire "socket" 1056668Sshinrequire "thread" 1156668Sshin 1256668Sshin# XXX should be derived from system headers 1356668SshinIPPROTO_IPV6 = 41 1456668SshinIPV6_FAITH = 29 1556668SshinDEBUG = true 1656668SshinDEBUG_LOOPBACK = true 1756668Sshin 1856668Sshin# TODO: OOB data handling 1956668Sshindef tcpcopy(s1, s2, m) 2056668Sshin STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG 2156668Sshin buf = "" 2256668Sshin while TRUE 2356668Sshin begin 2456668Sshin buf = s1.sysread(100) 2556668Sshin s2.syswrite(buf) 2656668Sshin rescue EOFError 2756668Sshin break 2856668Sshin rescue IOError 2956668Sshin break 3056668Sshin end 3156668Sshin end 3256668Sshin STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG 3356668Sshin s1.shutdown(0) 3456668Sshin s2.shutdown(1) 3556668Sshinend 3656668Sshin 3756668Sshindef relay_ftp_passiveconn(s6, s4, dport6, dport4) 3856668Sshin Thread.start do 3956668Sshin d6 = TCPserver.open("::", dport6).accept 4056668Sshin d4 = TCPsocket.open(s4.getpeer[3], dport4) 4156668Sshin t = [] 4256668Sshin t[0] = Thread.start do 4356668Sshin tcpcopy(d6, d4) 4456668Sshin end 4556668Sshin t[1] = Thread.start do 4656668Sshin tcpcopy(d4, d6) 4756668Sshin end 4856668Sshin for i in t 4956668Sshin i.join 5056668Sshin end 5156668Sshin d4.close 5256668Sshin d6.close 5356668Sshin end 5456668Sshinend 5556668Sshin 5656668Sshindef ftp_parse_2428(line) 5756668Sshin if (line[0] != line[line.length - 1]) 5856668Sshin return nil 5956668Sshin end 6056668Sshin t = line.split(line[0 .. 0]) # as string 6156668Sshin if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/) 6256668Sshin return nil 6356668Sshin end 6456668Sshin return t[1 .. 3] 6556668Sshinend 6656668Sshin 6756668Sshindef relay_ftp_command(s6, s4, state) 6856668Sshin STDERR.print "relay_ftp_command start\n" if DEBUG 6956668Sshin while TRUE 7056668Sshin begin 7156668Sshin STDERR.print "s6.gets\n" if DEBUG 7256668Sshin line = s6.gets 7356668Sshin STDERR.print "line is #{line}\n" if DEBUG 7456668Sshin if line == nil 7556668Sshin return nil 7656668Sshin end 7756668Sshin 7856668Sshin # translate then copy 7956668Sshin STDERR.print "line is #{line}\n" if DEBUG 8056668Sshin if (line =~ /^EPSV\r\n/i) 8156668Sshin STDERR.print "EPSV -> PASV\n" if DEBUG 8256668Sshin line = "PASV\n" 8356668Sshin state = "EPSV" 8456668Sshin elsif (line =~ /^EPRT\s+(.+)\r\n/i) 8556668Sshin t = ftp_parse_2428($1) 8656668Sshin if t == nil 8756668Sshin s6.puts "501 illegal parameter to EPRT\r\n" 8856668Sshin next 8956668Sshin end 9056668Sshin 9156668Sshin # some tricks should be here 9256668Sshin s6.puts "501 illegal parameter to EPRT\r\n" 9356668Sshin next 9456668Sshin end 9556668Sshin STDERR.print "fail: send #{line} as is\n" if DEBUG 9656668Sshin s4.puts(line) 9756668Sshin break 9856668Sshin rescue EOFError 9956668Sshin return nil 10056668Sshin rescue IOError 10156668Sshin return nil 10256668Sshin end 10356668Sshin end 10456668Sshin STDERR.print "relay_ftp_command finish\n" if DEBUG 10556668Sshin return state 10656668Sshinend 10756668Sshin 10856668Sshindef relay_ftp_status(s4, s6, state) 10956668Sshin STDERR.print "relay_ftp_status start\n" if DEBUG 11056668Sshin while TRUE 11156668Sshin begin 11256668Sshin line = s4.gets 11356668Sshin if line == nil 11456668Sshin return nil 11556668Sshin end 11656668Sshin 11756668Sshin # translate then copy 11856668Sshin s6.puts(line) 11956668Sshin 12056668Sshin next if line =~ /^\d\d\d-/ 12156668Sshin next if line !~ /^\d/ 12256668Sshin 12356668Sshin # special post-processing 12456668Sshin case line 12556668Sshin when /^221 / # result to QUIT 12656668Sshin s4.shutdown(0) 12756668Sshin s6.shutdown(1) 12856668Sshin end 12956668Sshin 13056668Sshin break if (line =~ /^\d\d\d /) 13156668Sshin rescue EOFError 13256668Sshin return nil 13356668Sshin rescue IOError 13456668Sshin return nil 13556668Sshin end 13656668Sshin end 13756668Sshin STDERR.print "relay_ftp_status finish\n" if DEBUG 13856668Sshin return state 13956668Sshinend 14056668Sshin 14156668Sshindef relay_ftp(sock, name) 14256668Sshin STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG 14356668Sshin while TRUE 14456668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG 14556668Sshin s = sock.accept 14656668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG 14756668Sshin Thread.start do 14856668Sshin threads = [] 14956668Sshin STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG 15056668Sshin s6 = s 15156668Sshin dest6 = s.addr[3] 15256668Sshin if !DEBUG_LOOPBACK 15356668Sshin t = s.getsockname.unpack("x8 x12 C4") 15456668Sshin dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" 15556668Sshin port4 = s.addr[1] 15656668Sshin else 15756668Sshin dest4 = "127.0.0.1" 15856668Sshin port4 = "ftp" 15956668Sshin end 16056668Sshin if DEBUG 16156668Sshin STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG 16256668Sshin end 16356668Sshin STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG 16456668Sshin s4 = TCPsocket.open(dest4, port4) 16556668Sshin STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG 16656668Sshin state = 0 16756668Sshin while TRUE 16856668Sshin # translate status line 16956668Sshin state = relay_ftp_status(s4, s6, state) 17056668Sshin break if state == nil 17156668Sshin # translate command line 17256668Sshin state = relay_ftp_command(s6, s4, state) 17356668Sshin break if state == nil 17456668Sshin end 17556668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG 17656668Sshin s4.close 17756668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG 17856668Sshin s6.close 17956668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG 18056668Sshin end 18156668Sshin end 18256668Sshin STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG 18356668Sshinend 18456668Sshin 18556668Sshindef relay_tcp(sock, name) 18656668Sshin STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG 18756668Sshin while TRUE 18856668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG 18956668Sshin s = sock.accept 19056668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG 19156668Sshin Thread.start do 19256668Sshin threads = [] 19356668Sshin STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG 19456668Sshin s6 = s 19556668Sshin dest6 = s.addr[3] 19656668Sshin if !DEBUG_LOOPBACK 19756668Sshin t = s.getsockname.unpack("x8 x12 C4") 19856668Sshin dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" 19956668Sshin port4 = s.addr[1] 20056668Sshin else 20156668Sshin dest4 = "127.0.0.1" 20256668Sshin port4 = "telnet" 20356668Sshin end 20456668Sshin if DEBUG 20556668Sshin STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG 20656668Sshin end 20756668Sshin STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG 20856668Sshin s4 = TCPsocket.open(dest4, port4) 20956668Sshin STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG 21056668Sshin [0, 1].each do |i| 21156668Sshin threads[i] = Thread.start do 21256668Sshin if (i == 0) 21356668Sshin tcpcopy(s6, s4) 21456668Sshin else 21556668Sshin tcpcopy(s4, s6) 21656668Sshin end 21756668Sshin end 21856668Sshin end 21956668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG 22056668Sshin for i in threads 22156668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG 22256668Sshin i.join 22356668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG 22456668Sshin end 22556668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG 22656668Sshin s4.close 22756668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG 22856668Sshin s6.close 22956668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG 23056668Sshin end 23156668Sshin end 23256668Sshin STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG 23356668Sshinend 23456668Sshin 23556668Sshindef usage() 23656668Sshin STDERR.print "usage: #{$0} [-f] port...\n" 23756668Sshinend 23856668Sshin 23956668Sshin#------------------------------------------------------------ 24056668Sshin 24156668Sshin$mode = "tcp" 24256668Sshin 24356668Sshinwhile ARGV[0] =~ /^-/ do 24456668Sshin case ARGV[0] 24556668Sshin when /^-f/ 24656668Sshin $mode = "ftp" 24756668Sshin else 24856668Sshin usage() 24956668Sshin exit 0 25056668Sshin end 25156668Sshin ARGV.shift 25256668Sshinend 25356668Sshin 25456668Sshinif ARGV.length == 0 25556668Sshin usage() 25656668Sshin exit 1 25756668Sshinend 25856668Sshin 25956668Sshinftpport = Socket.getservbyname("ftp") 26056668Sshin 26156668Sshinres = [] 26256668Sshinfor port in ARGV 26356668Sshin t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM, 26456668Sshin nil, Socket::AI_PASSIVE) 26556668Sshin if (t.size <= 0) 26656668Sshin STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n" 26756668Sshin exit 1 26856668Sshin end 26956668Sshin res += t 27056668Sshinend 27156668Sshin 27256668Sshinsockpool = [] 27356668Sshinnames = [] 27456668Sshinlistenthreads = [] 27556668Sshin 27656668Sshinres.each do |i| 27756668Sshin s = TCPserver.new(i[3], i[1]) 27856668Sshin n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ") 27956668Sshin if i[6] == IPPROTO_IPV6 28056668Sshin s.setsockopt(i[6], IPV6_FAITH, 1) 28156668Sshin end 28256668Sshin s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) 28356668Sshin sockpool.push s 28456668Sshin names.push n 28556668Sshinend 28656668Sshin 28756668Sshinif DEBUG 28856668Sshin (0 .. sockpool.size - 1).each do |i| 28956668Sshin STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG 29056668Sshin end 29156668Sshinend 29256668Sshin 29356668Sshin(0 .. sockpool.size - 1).each do |i| 29456668Sshin listenthreads[i] = Thread.start do 29556668Sshin if DEBUG 29656668Sshin STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG 29756668Sshin end 29856668Sshin STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG 29956668Sshin case $mode 30056668Sshin when "tcp" 30156668Sshin relay_tcp(sockpool[i], names[i]) 30256668Sshin when "ftp" 30356668Sshin relay_ftp(sockpool[i], names[i]) 30456668Sshin end 30556668Sshin end 30656668Sshinend 30756668Sshin 30856668Sshinfor i in listenthreads 30956668Sshin i.join 31056668Sshinend 31156668Sshin 31256668Sshinexit 0 313