258a21fe7a
- simplify by strdup. - set ai_protocol in hints to TCP. - g/c FAITH_NS (no description, not maintained for years) - warn if connection from IPv4 mapped is reached. - IPV6_V6ONLY if possible. - unifdef -UFAITH4. - drop rsh/rlogin support. - deal with negative return value from wait3. Obtained from: KAME
313 lines
7.1 KiB
Ruby
313 lines
7.1 KiB
Ruby
# faithd, ruby version. requires v6-enabled ruby.
|
|
#
|
|
# highly experimental (not working right at all) and very limited
|
|
# functionality.
|
|
#
|
|
# $Id: faithd.rb,v 1.1.2.4 1999/05/10 17:06:30 itojun Exp $
|
|
# $FreeBSD$
|
|
|
|
require "socket"
|
|
require "thread"
|
|
|
|
# XXX should be derived from system headers
|
|
IPPROTO_IPV6 = 41
|
|
IPV6_FAITH = 29
|
|
DEBUG = true
|
|
DEBUG_LOOPBACK = true
|
|
|
|
# TODO: OOB data handling
|
|
def tcpcopy(s1, s2, m)
|
|
STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG
|
|
buf = ""
|
|
while TRUE
|
|
begin
|
|
buf = s1.sysread(100)
|
|
s2.syswrite(buf)
|
|
rescue EOFError
|
|
break
|
|
rescue IOError
|
|
break
|
|
end
|
|
end
|
|
STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG
|
|
s1.shutdown(0)
|
|
s2.shutdown(1)
|
|
end
|
|
|
|
def relay_ftp_passiveconn(s6, s4, dport6, dport4)
|
|
Thread.start do
|
|
d6 = TCPserver.open("::", dport6).accept
|
|
d4 = TCPsocket.open(s4.getpeer[3], dport4)
|
|
t = []
|
|
t[0] = Thread.start do
|
|
tcpcopy(d6, d4)
|
|
end
|
|
t[1] = Thread.start do
|
|
tcpcopy(d4, d6)
|
|
end
|
|
for i in t
|
|
i.join
|
|
end
|
|
d4.close
|
|
d6.close
|
|
end
|
|
end
|
|
|
|
def ftp_parse_2428(line)
|
|
if (line[0] != line[line.length - 1])
|
|
return nil
|
|
end
|
|
t = line.split(line[0 .. 0]) # as string
|
|
if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/)
|
|
return nil
|
|
end
|
|
return t[1 .. 3]
|
|
end
|
|
|
|
def relay_ftp_command(s6, s4, state)
|
|
STDERR.print "relay_ftp_command start\n" if DEBUG
|
|
while TRUE
|
|
begin
|
|
STDERR.print "s6.gets\n" if DEBUG
|
|
line = s6.gets
|
|
STDERR.print "line is #{line}\n" if DEBUG
|
|
if line == nil
|
|
return nil
|
|
end
|
|
|
|
# translate then copy
|
|
STDERR.print "line is #{line}\n" if DEBUG
|
|
if (line =~ /^EPSV\r\n/i)
|
|
STDERR.print "EPSV -> PASV\n" if DEBUG
|
|
line = "PASV\n"
|
|
state = "EPSV"
|
|
elsif (line =~ /^EPRT\s+(.+)\r\n/i)
|
|
t = ftp_parse_2428($1)
|
|
if t == nil
|
|
s6.puts "501 illegal parameter to EPRT\r\n"
|
|
next
|
|
end
|
|
|
|
# some tricks should be here
|
|
s6.puts "501 illegal parameter to EPRT\r\n"
|
|
next
|
|
end
|
|
STDERR.print "fail: send #{line} as is\n" if DEBUG
|
|
s4.puts(line)
|
|
break
|
|
rescue EOFError
|
|
return nil
|
|
rescue IOError
|
|
return nil
|
|
end
|
|
end
|
|
STDERR.print "relay_ftp_command finish\n" if DEBUG
|
|
return state
|
|
end
|
|
|
|
def relay_ftp_status(s4, s6, state)
|
|
STDERR.print "relay_ftp_status start\n" if DEBUG
|
|
while TRUE
|
|
begin
|
|
line = s4.gets
|
|
if line == nil
|
|
return nil
|
|
end
|
|
|
|
# translate then copy
|
|
s6.puts(line)
|
|
|
|
next if line =~ /^\d\d\d-/
|
|
next if line !~ /^\d/
|
|
|
|
# special post-processing
|
|
case line
|
|
when /^221 / # result to QUIT
|
|
s4.shutdown(0)
|
|
s6.shutdown(1)
|
|
end
|
|
|
|
break if (line =~ /^\d\d\d /)
|
|
rescue EOFError
|
|
return nil
|
|
rescue IOError
|
|
return nil
|
|
end
|
|
end
|
|
STDERR.print "relay_ftp_status finish\n" if DEBUG
|
|
return state
|
|
end
|
|
|
|
def relay_ftp(sock, name)
|
|
STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG
|
|
while TRUE
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG
|
|
s = sock.accept
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
|
|
Thread.start do
|
|
threads = []
|
|
STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
|
|
s6 = s
|
|
dest6 = s.addr[3]
|
|
if !DEBUG_LOOPBACK
|
|
t = s.getsockname.unpack("x8 x12 C4")
|
|
dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
|
|
port4 = s.addr[1]
|
|
else
|
|
dest4 = "127.0.0.1"
|
|
port4 = "ftp"
|
|
end
|
|
if DEBUG
|
|
STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
|
|
end
|
|
STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
|
|
s4 = TCPsocket.open(dest4, port4)
|
|
STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
|
|
state = 0
|
|
while TRUE
|
|
# translate status line
|
|
state = relay_ftp_status(s4, s6, state)
|
|
break if state == nil
|
|
# translate command line
|
|
state = relay_ftp_command(s6, s4, state)
|
|
break if state == nil
|
|
end
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG
|
|
s4.close
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG
|
|
s6.close
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG
|
|
end
|
|
end
|
|
STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG
|
|
end
|
|
|
|
def relay_tcp(sock, name)
|
|
STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG
|
|
while TRUE
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG
|
|
s = sock.accept
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
|
|
Thread.start do
|
|
threads = []
|
|
STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
|
|
s6 = s
|
|
dest6 = s.addr[3]
|
|
if !DEBUG_LOOPBACK
|
|
t = s.getsockname.unpack("x8 x12 C4")
|
|
dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
|
|
port4 = s.addr[1]
|
|
else
|
|
dest4 = "127.0.0.1"
|
|
port4 = "telnet"
|
|
end
|
|
if DEBUG
|
|
STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
|
|
end
|
|
STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
|
|
s4 = TCPsocket.open(dest4, port4)
|
|
STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
|
|
[0, 1].each do |i|
|
|
threads[i] = Thread.start do
|
|
if (i == 0)
|
|
tcpcopy(s6, s4)
|
|
else
|
|
tcpcopy(s4, s6)
|
|
end
|
|
end
|
|
end
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG
|
|
for i in threads
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG
|
|
i.join
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG
|
|
end
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG
|
|
s4.close
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG
|
|
s6.close
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG
|
|
end
|
|
end
|
|
STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG
|
|
end
|
|
|
|
def usage()
|
|
STDERR.print "usage: #{$0} [-f] port...\n"
|
|
end
|
|
|
|
#------------------------------------------------------------
|
|
|
|
$mode = "tcp"
|
|
|
|
while ARGV[0] =~ /^-/ do
|
|
case ARGV[0]
|
|
when /^-f/
|
|
$mode = "ftp"
|
|
else
|
|
usage()
|
|
exit 0
|
|
end
|
|
ARGV.shift
|
|
end
|
|
|
|
if ARGV.length == 0
|
|
usage()
|
|
exit 1
|
|
end
|
|
|
|
ftpport = Socket.getservbyname("ftp")
|
|
|
|
res = []
|
|
for port in ARGV
|
|
t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM,
|
|
nil, Socket::AI_PASSIVE)
|
|
if (t.size <= 0)
|
|
STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n"
|
|
exit 1
|
|
end
|
|
res += t
|
|
end
|
|
|
|
sockpool = []
|
|
names = []
|
|
listenthreads = []
|
|
|
|
res.each do |i|
|
|
s = TCPserver.new(i[3], i[1])
|
|
n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ")
|
|
if i[6] == IPPROTO_IPV6
|
|
s.setsockopt(i[6], IPV6_FAITH, 1)
|
|
end
|
|
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
|
sockpool.push s
|
|
names.push n
|
|
end
|
|
|
|
if DEBUG
|
|
(0 .. sockpool.size - 1).each do |i|
|
|
STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG
|
|
end
|
|
end
|
|
|
|
(0 .. sockpool.size - 1).each do |i|
|
|
listenthreads[i] = Thread.start do
|
|
if DEBUG
|
|
STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
|
|
end
|
|
STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
|
|
case $mode
|
|
when "tcp"
|
|
relay_tcp(sockpool[i], names[i])
|
|
when "ftp"
|
|
relay_ftp(sockpool[i], names[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
for i in listenthreads
|
|
i.join
|
|
end
|
|
|
|
exit 0
|
|
|