socket_proxy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +15 -0
  2. data/bin/socket_proxy +63 -0
  3. data/lib/socket_proxy.rb +301 -0
  4. data/sample/monitor.rb +137 -0
  5. metadata +49 -0
data/README ADDED
@@ -0,0 +1,15 @@
1
+ socket_proxy - Creates I/O pipes for TCP socket tunneling.
2
+
3
+
4
+ == Author
5
+
6
+ Name:: Hiroshi Nakamura
7
+ E-mail:: nahi@ruby-lang.org
8
+ Project web site:: http://github.com/nahi/socket_proxy
9
+
10
+
11
+ == License
12
+
13
+ This program is copyrighted free software by Hiroshi Nakamura. You can
14
+ redistribute it and/or modify it under the same terms of Ruby's license;
15
+ either the dual license version in 2003, or any later version.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # socket_proxy.rb -- Creates I/O pipes for TCP socket tunneling.
4
+ # Copyright (C) 1999-2001, 2003 NAKAMURA, Hiroshi
5
+
6
+ # This application is copyrighted free software by NAKAMURA, Hiroshi.
7
+ # You can redistribute it and/or modify it under the same term as Ruby.
8
+
9
+ require 'socket_proxy'
10
+ require 'getoptlong'
11
+
12
+ def main
13
+ opts = GetoptLong.new(['--debug', '-d', GetoptLong::NO_ARGUMENT], ['--daemon', '-s', GetoptLong::NO_ARGUMENT])
14
+ opts.each do |name, _|
15
+ case name
16
+ when '--debug'
17
+ debug = true
18
+ when '--daemon'
19
+ daemon = true
20
+ end
21
+ end
22
+
23
+ destname = ARGV.shift
24
+ portpairs = []
25
+ while srcport = ARGV.shift
26
+ destport = ARGV.shift or raise ArgumentError.new("Port must be given as a pair of src and dest.")
27
+ portpairs << [srcport, destport]
28
+ end
29
+ usage if portpairs.empty? or !destname
30
+
31
+ # To run as a daemon...
32
+ if daemon
33
+ exit! if fork
34
+ Process.setsid
35
+ exit! if fork
36
+ STDIN.close
37
+ STDOUT.close
38
+ STDERR.close
39
+ end
40
+
41
+ app = SocketProxy.new(destname, portpairs)
42
+ app.dump_response = true if debug
43
+ app.start
44
+ end
45
+
46
+ def usage
47
+ STDERR.print <<EOM
48
+ Usage: #{$0} [OPTIONS] destname srcport destport [[srcport destport]...]
49
+
50
+ Creates I/O pipes for TCP socket tunneling.
51
+
52
+ destname ... hostname of a destination(name or ip-addr).
53
+ srcport .... source TCP port# or UNIX domain socket name of localhost.
54
+ destport ... destination port# of the destination host.
55
+
56
+ OPTIONS:
57
+ -d ......... dumps data from destination port(not dumped by default).
58
+ -s ......... run as a daemon.
59
+ EOM
60
+ exit 1
61
+ end
62
+
63
+ main
@@ -0,0 +1,301 @@
1
+ # socket_proxy.rb -- Creates I/O pipes for TCP socket tunneling.
2
+ # Copyright (C) 1999-2001, 2003 NAKAMURA, Hiroshi
3
+
4
+ # This application is copyrighted free software by NAKAMURA, Hiroshi.
5
+ # You can redistribute it and/or modify it under the same term as Ruby.
6
+
7
+ # Ruby bundled library
8
+ require 'socket'
9
+ require 'logger'
10
+
11
+ class SocketProxy < Logger::Application
12
+ module Dump
13
+ # Written by Arai-san and published at [ruby-list:31987].
14
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/31987
15
+ def hexdump(str)
16
+ offset = 0
17
+ result = []
18
+ while raw = str.slice(offset, 16) and raw.length > 0
19
+ # data field
20
+ data = ''
21
+ for v in raw.unpack('N* a*')
22
+ if v.kind_of? Integer
23
+ data << sprintf("%08x ", v)
24
+ else
25
+ v.each_byte {|c| data << sprintf("%02x", c) }
26
+ end
27
+ end
28
+ # text field
29
+ text = raw.tr("\000-\037\177-\377", ".")
30
+ result << sprintf("%08x %-36s %s", offset, data, text)
31
+ offset += 16
32
+ # omit duplicate line
33
+ if /^(#{ Regexp.quote(raw) })+/n =~ str[offset .. -1]
34
+ result << sprintf("%08x ...", offset)
35
+ offset += $&.length
36
+ # should print at the end
37
+ if offset == str.length
38
+ result << sprintf("%08x %-36s %s", offset-16, data, text)
39
+ end
40
+ end
41
+ end
42
+ result
43
+ end
44
+ module_function :hexdump
45
+ end
46
+
47
+ include Logger::Severity
48
+ include Socket::Constants
49
+
50
+ attr_accessor :dump_request
51
+ attr_accessor :dump_response
52
+
53
+ private
54
+
55
+ Timeout = 100 # [sec]
56
+ ReadBlockSize = 10 * 1024 # [byte]
57
+
58
+ class SessionPool
59
+ def initialize
60
+ @pool = []
61
+ @sockets = []
62
+ end
63
+
64
+ def sockets
65
+ @sockets
66
+ end
67
+
68
+ def each
69
+ @pool.each do |i|
70
+ yield i
71
+ end
72
+ end
73
+
74
+ def add(svrsock, clntsock)
75
+ @pool.push(Session.new(svrsock, clntsock))
76
+ @sockets.push(svrsock, clntsock)
77
+ end
78
+
79
+ def del(session)
80
+ @pool.delete(session)
81
+ @sockets.delete(session.server)
82
+ @sockets.delete(session.client)
83
+ end
84
+
85
+ private
86
+
87
+ class Session
88
+ attr(:server)
89
+ attr(:client)
90
+
91
+ def closed?
92
+ @server.closed? and @client.closed?
93
+ end
94
+
95
+ private
96
+
97
+ def initialize(server = nil, client = nil)
98
+ @server = server
99
+ @client = client
100
+ end
101
+ end
102
+ end
103
+
104
+ class ListenSocketHash < Hash
105
+ def sockets
106
+ keys
107
+ end
108
+ end
109
+
110
+ AppName = File.basename(__FILE__)
111
+ ShiftAge = 0
112
+ ShiftSize = 0
113
+
114
+ def initialize(destname, portpairs)
115
+ super(AppName)
116
+ set_log(AppName + '.log', ShiftAge, ShiftSize)
117
+ @log.level = Logger::INFO
118
+ @destname = destname
119
+ @portpairs = portpairs
120
+ @dump_request = true
121
+ @dump_response = false
122
+ @sessionpool = SessionPool.new
123
+ @waitsockets = nil
124
+ end
125
+
126
+ def init_waitsockets
127
+ @waitsockets = ListenSocketHash.new
128
+ @portpairs.each do |srcport, destport|
129
+ wait = if is_for_tcp(srcport)
130
+ TCPServer.new(srcport.to_i)
131
+ else
132
+ UNIXServer.new(srcport)
133
+ end
134
+ @waitsockets[wait] = [srcport, destport]
135
+ dump_start(srcport, destport)
136
+ end
137
+ end
138
+
139
+ def terminate_waitsockets
140
+ @waitsockets.each do |sock, portpair|
141
+ sock.close
142
+ srcport, destport = portpair
143
+ File.unlink(srcport) unless is_for_tcp(srcport)
144
+ dump_end(srcport, destport)
145
+ end
146
+ end
147
+
148
+ def is_for_tcp(srcport)
149
+ srcport.to_i != 0
150
+ end
151
+
152
+ def run
153
+ begin
154
+ init_waitsockets
155
+ while true
156
+ readwait = @sessionpool.sockets + @waitsockets.sockets
157
+ readready, = IO.select(readwait, nil, nil, Timeout)
158
+ next unless readready
159
+ readready.each do |sock|
160
+ if (portpair = @waitsockets[sock])
161
+ newsock = sock.accept
162
+ dump_accept(newsock.peeraddr[2], portpair)
163
+ if !add_session(newsock, portpair)
164
+ log(WARN) { 'Closing server socket...' }
165
+ newsock.close
166
+ end
167
+ else
168
+ @sessionpool.each do |session|
169
+ if sock.equal?(session.server)
170
+ transfer(session, true)
171
+ next
172
+ elsif sock.equal?(session.client)
173
+ transfer(session, false)
174
+ next
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ ensure
181
+ terminate_waitsockets
182
+ end
183
+ end
184
+
185
+ def transfer(session, is_server)
186
+ readsock = nil
187
+ writesock = nil
188
+ if is_server
189
+ readsock = session.server
190
+ writesock = session.client
191
+ else
192
+ readsock = session.client
193
+ writesock = session.server
194
+ end
195
+
196
+ readbuf = nil
197
+ begin
198
+ readbuf = readsock.sysread(ReadBlockSize)
199
+ rescue IOError
200
+ # not opend for reading
201
+ close_session(session, readsock, writesock)
202
+ return
203
+ rescue EOFError
204
+ close_session(session, readsock, writesock)
205
+ return
206
+ rescue Errno::ECONNRESET
207
+ log(INFO) { "#{$!} while reading." }
208
+ close_session(session, readsock, writesock)
209
+ return
210
+ rescue
211
+ log(WARN) { "Detected an exception. Stopping ..." }
212
+ log(WARN) { $! }
213
+ log(WARN) { $@ }
214
+ close_session(session, readsock, writesock)
215
+ return
216
+ end
217
+
218
+ if is_server
219
+ dump_transfer_data(true, readbuf) if @dump_request
220
+ else
221
+ dump_transfer_data(false, readbuf) if @dump_response
222
+ end
223
+
224
+ writesize = 0
225
+ while (writesize < readbuf.size)
226
+ begin
227
+ writesize += writesock.syswrite(readbuf[writesize..-1])
228
+ rescue Errno::ECONNRESET
229
+ log(INFO) { "#{$!} while writing." }
230
+ log(INFO) { $@ }
231
+ close_session(session, readsock, writesock)
232
+ return
233
+ rescue
234
+ log(WARN) { "Detected an exception. Stopping ..." }
235
+ log(WARN) { $@ }
236
+ close_session(session, readsock, writesock)
237
+ return
238
+ end
239
+ end
240
+ end
241
+
242
+ def add_session(svrsock, portpair)
243
+ srcport, destport = portpair
244
+ begin
245
+ clntsock = TCPSocket.new(@destname, destport)
246
+ rescue
247
+ log(ERROR) { 'Create client socket failed.' }
248
+ return
249
+ end
250
+ @sessionpool.add(svrsock, clntsock)
251
+ dump_add_session(portpair)
252
+ end
253
+
254
+ def close_session(session, readsock, writesock)
255
+ readsock.close_read
256
+ writesock.close_write
257
+ if session.closed?
258
+ @sessionpool.del(session)
259
+ dump_close_session
260
+ end
261
+ end
262
+
263
+ def dump_start(srcport, destport)
264
+ log(INFO) { 'Started ... src=%s, dest=%s@%s' % [srcport, destport, @destname] }
265
+ end
266
+
267
+ def dump_accept(from, portpair)
268
+ log(INFO) {
269
+ srcport, destport = portpair
270
+ "Accepted ... src=%s from %s" % [srcport, from]
271
+ }
272
+ end
273
+
274
+ def dump_add_session(portpair)
275
+ log(INFO) {
276
+ srcport, destport = portpair
277
+ 'Connection established ... src=%s dest=%s@%s' % [srcport, destport, @destname]
278
+ }
279
+ end
280
+
281
+ def dump_transfer_data(is_src2dest, data)
282
+ if is_src2dest
283
+ log(INFO) { 'Transfer data ... [src] -> [dest]' }
284
+ else
285
+ log(INFO) { 'Transfer data ... [src] <- [dest]' }
286
+ end
287
+ dump_data(data)
288
+ end
289
+
290
+ def dump_data(data)
291
+ log(INFO) { "Transferred data;\n" << Dump.hexdump(data).join("\n") }
292
+ end
293
+
294
+ def dump_close_session
295
+ log(INFO) { 'Connection closed.' }
296
+ end
297
+
298
+ def dump_end(srcport, destport)
299
+ log(INFO) { 'Stopped ... src=%s, dest=%s@%s' % [srcport, destport, @destname] }
300
+ end
301
+ end
@@ -0,0 +1,137 @@
1
+ #!/home/achilles/nakahiro/bin/ruby
2
+ #
3
+ # TCP Tunnel
4
+ # Copyright (c) 2001 by Michael Neumann (neumann@s-direktnet.de)
5
+ #
6
+ # $Id: monitor.rb,v 1.2 2001/07/13 04:24:29 nakahiro Exp $
7
+ #
8
+ # Modified to use TCPSocketPipe and some tk view.
9
+ # Copyright (c) 2001 NAKAMURA Hiroshi.
10
+
11
+ # This application is copyrighted free software by Michael Neumann and
12
+ # NAKAMURA, Hiroshi. You can redistribute it and/or modify it under
13
+ # the same term as Ruby or under BSD License.
14
+
15
+ require 'TCPSocketPipe'
16
+ require 'tk'
17
+
18
+
19
+ unless ARGV.size == 3
20
+ puts "USAGE: #$0 srcPort destName destPort"
21
+ puts " e.g. #$0 8070 localhost 8080"
22
+ exit 1
23
+ end
24
+
25
+ LISTENHOST = 'localhost'
26
+ LISTENPORT = ARGV.shift
27
+ TUNNELHOST = ARGV.shift
28
+ TUNNELPORT = ARGV.shift
29
+
30
+ WIDTH = 50
31
+ HEIGHT = 35
32
+
33
+
34
+ root = TkRoot.new { title "TCP Tunnel/Monitor: Tunneling #{LISTENHOST}:#{LISTENPORT} to #{TUNNELHOST}:#{TUNNELPORT}" }
35
+
36
+ top = TkFrame.new(root) {
37
+ pack( 'side' => 'top', 'fill' => 'x' )
38
+ }
39
+
40
+ bottom2 = TkFrame.new(root) {
41
+ pack( 'side' => 'bottom', 'fill' => 'both' )
42
+ }
43
+
44
+ bottom3 = TkFrame.new(bottom2) {
45
+ pack 'side' => 'bottom', 'fill' => 'x'
46
+ }
47
+
48
+ bottom = TkFrame.new(bottom2) {
49
+ pack( 'side' => 'top', 'fill' => 'both' )
50
+ }
51
+
52
+ bot_label = TkLabel.new(bottom3) {
53
+ text "Listening for connections on port #{LISTENPORT} for host #{LISTENHOST}"
54
+ pack
55
+ }
56
+
57
+ llabel = TkLabel.new(top) {
58
+ text "From #{LISTENHOST}:#{LISTENPORT}"
59
+ pack 'side' => 'right'
60
+ }
61
+ rlabel = TkLabel.new(top) {
62
+ text "From #{TUNNELHOST}:#{TUNNELPORT} "
63
+ pack 'side' => 'left'
64
+ }
65
+
66
+ $ltext = TkText.new(bottom, 'width' => WIDTH, 'height' => HEIGHT) {
67
+ pack( 'side' => 'left', 'fill' => 'y' )
68
+ }
69
+ $rtext = TkText.new(bottom, 'width' => WIDTH, 'height' => HEIGHT) {
70
+ pack( 'side' => 'right', 'fill' => 'y' )
71
+ }
72
+
73
+ scroll = TkScrollbar.new(bottom) {
74
+ command proc { |arg|
75
+ $ltext.yview *arg
76
+ $rtext.yview *arg
77
+ }
78
+ pack( 'side' => 'right', 'fill' => 'y' )
79
+ }
80
+
81
+ $ltext.configure( 'yscrollcommand' => proc { |arg| scroll.set *arg } )
82
+ $ltext.yscrollcommand( proc { |arg| scroll.set *arg } )
83
+ $rtext.configure( 'yscrollcommand' => proc { |arg| scroll.set *arg } )
84
+ $rtext.yscrollcommand( proc { |arg| scroll.set *arg } )
85
+
86
+ $sessionCount = 0
87
+ $sessionResetP = false
88
+ TkButton.new(top) {
89
+ text "Clear"
90
+ command {
91
+ $ltext.value = ""
92
+ $rtext.value = ""
93
+ $sessionResetP = true
94
+ }
95
+ pack
96
+ }
97
+
98
+
99
+ class TCPSocketPipe < Application
100
+ def dumpTransferData( isFromSrcToDestP, data )
101
+ if isFromSrcToDestP
102
+ log( SEV_INFO, 'Transfer data ... [src] -> [dest]' )
103
+ $ltext.insert( 'end', data ) unless $sessionResetP
104
+ else
105
+ log( SEV_INFO, 'Transfer data ... [src] <- [dest]' )
106
+ $rtext.insert( 'end', data ) unless $sessionResetP
107
+ end
108
+ dumpData( data )
109
+ end
110
+
111
+ def dumpAddSession
112
+ $sessionCount += 1
113
+ str = "--<open: ##{ $sessionCount }>--"
114
+ $ltext.insert( 'end', str + '-' * ( WIDTH - str.size ) << "\n" )
115
+ $rtext.insert( 'end', str + '-' * ( WIDTH - str.size ) << "\n" )
116
+ end
117
+
118
+ def dumpCloseSession
119
+ if $sessionResetP
120
+ $sessionCount = 0
121
+ $sessionResetP = false
122
+ return
123
+ end
124
+
125
+ str = "--<close: ##{ $sessionCount }>--"
126
+ $ltext.insert( 'end', "\n" << str << '-' * ( WIDTH - str.size ) << "\n" )
127
+ $rtext.insert( 'end', "\n" << str << '-' * ( WIDTH - str.size ) << "\n" )
128
+ end
129
+ end
130
+
131
+ Thread.new {
132
+ app = TCPSocketPipe.new( LISTENPORT, TUNNELHOST, TUNNELPORT )
133
+ app.dumpResponse = true
134
+ app.start()
135
+ }
136
+
137
+ Tk.mainloop
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: socket_proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Hiroshi Nakamura
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-22 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: nahi@ruby-lang.org
16
+ executables:
17
+ - socket_proxy
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/socket_proxy.rb
22
+ - bin/socket_proxy
23
+ - sample/monitor.rb
24
+ - README
25
+ homepage: http://github.com/nahi/socket_proxy
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.11
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: Creates I/O pipes for TCP socket tunneling.
49
+ test_files: []