socket_proxy 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []