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