Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / faithd / test / faithd.rb
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.1.1 1999/08/08 23:29:31 itojun Exp $
7 # $FreeBSD: src/usr.sbin/faithd/test/faithd.rb,v 1.1 2000/01/27 09:28:38 shin Exp $
8
9 require "socket"
10 require "thread"
11
12 # XXX should be derived from system headers
13 IPPROTO_IPV6 = 41
14 IPV6_FAITH = 29
15 DEBUG = true
16 DEBUG_LOOPBACK = true
17
18 # TODO: OOB data handling
19 def 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)
35 end
36
37 def 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
54 end
55
56 def 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]
65 end
66
67 def 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
106 end
107
108 def 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
139 end
140
141 def 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
183 end
184
185 def 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
233 end
234
235 def usage()
236   STDERR.print "usage: #{$0} [-f] port...\n"
237 end
238
239 #------------------------------------------------------------
240
241 $mode = "tcp"
242
243 while 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
252 end
253
254 if ARGV.length == 0
255   usage()
256   exit 1
257 end
258
259 ftpport = Socket.getservbyname("ftp")
260
261 res = []
262 for 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
270 end
271
272 sockpool = []
273 names = []
274 listenthreads = []
275
276 res.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
285 end
286
287 if DEBUG
288   (0 .. sockpool.size - 1).each do |i|
289     STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG
290   end
291 end
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
306 end
307
308 for i in listenthreads
309   i.join
310 end
311
312 exit 0