Welcome, guest | Sign In | My Account | Store | Cart

PortForwarder forwards incoming tcp/ip data (defined port and ip address) towards an assigned remote ip address and port. Lowlevel security is done by checking on remote ip address (which is not secure but blocks)

Tcl, 167 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/local/bin/tclsh
#
# PFW - Portforwarder tcltk use at own risk, Norman Deppenbroek 2001
#
# forwarding raw data from a listening port, towards a standard remote address:port
# block by session limits and accesslist
#


namespace eval pfw {

        variable version        v0.93
        variable copyright      "PortForwarder $pfw::version by Nodep"
        variable debug          False	# if true show packet size on stdout 
        variable localhost      0
        variable localport      0
        variable remotehost     0
        variable remoteport     0
        variable maxsession     0
        variable allowlist      0
        variable denylist       0


        proc time  {}           { return [ clock format [ clock seconds ] -format %D-%T ] }
        proc print { syntax }   { puts "[ pfw::time ] --> $syntax" }
                
                namespace eval db {
                        variable cnt 0    

                        proc inc { } {
                                return [ incr pfw::db::cnt ]
                        }

                        proc dec { } {
                                return [ set pfw::db::cnt [ expr $pfw::db::cnt - 1 ] ]
                        }
                
                }
}


 
# PROCEDURES START HERE #

proc sio { fromsock tosock ip port } {
        
         if { [ catch { set data [read $fromsock] } merror ] } {
                pfw::print "ERR: #$pfw::db::cnt \t $merror"
                pfw::print "CLR: #$pfw::db::cnt \t $pfw::remotehost:$pfw::remoteport <-> $pfw::localhost:$pfw::localport <-> $ip:$port"
                catch { close $fromsock } 
                catch { close $tosock   } 
                pfw::db::dec
                return
         }


          if {[string length $data] == 0} {

                catch { close $fromsock }
                catch { close $tosock }  
                pfw::print "CLR: #$pfw::db::cnt \t $pfw::localhost:$pfw::localport <-> $pfw::remotehost:$pfw::remoteport <->  $ip:$port"
                pfw::db::dec
                return      
          }
                if { $pfw::debug } { pfw::print "TRX: #$pfw::db::cnt \t $pfw::remotehost:$pfw::remoteport <-> $pfw::localhost:$pfw::localport

                if { [ catch { puts -nonewline $tosock $data } merror ] } {
                        pfw::print "ERR: #$pfw::db::cnt \t $merror"
                        pfw::print "CLR: #$pfw::db::cnt \t $pfw::remotehost:$pfw::remoteport <-> $pfw::localhost:$pfw::localport <-> $ip:$port
                        catch { close $fromsock }
                        catch { close $tosock }  
                        pfw::db::dec
                }
}
 
 
proc connect { serverhost serverport sockclient ip port} {

        pfw::db::inc

        if { $pfw::db::cnt < $pfw::maxsession } {

           if { [ lsearch -exact $pfw::allowlist $ip ] != -1 } {

                  if { [ catch { set sockserver [ socket $pfw::remotehost $pfw::remoteport ] } merror ] } {
                        pfw::print "ERR: #$pfw::db::cnt \t $merror"
                        pfw::print "CLR: #$pfw::db::cnt \t $pfw::localhost:$pfw::localport <-> $ip:$port"
                        catch { close $sockclient }
                        catch { close $sockserver }
                        pfw::db::dec
                        return
                  }

                  pfw::print "NEW: #$pfw::db::cnt \t $pfw::localhost:$pfw::localport <-> $ip:$port" 
                  pfw::print "CON: #$pfw::db::cnt \t $pfw::localhost:$pfw::localport <-> $ip:$port "
                  fconfigure $sockclient -blocking 0 -buffering none -translation binary 
                  fconfigure $sockserver -blocking 0 -buffering none -translation binary 
                  fileevent  $sockclient  readable [list sio $sockclient $sockserver $ip $port ]
                  fileevent  $sockserver  readable [list sio $sockserver $sockclient $ip $port ]

          } else {

                  pfw::print "INT: #$pfw::db::cnt \t $ip:$port rejected by accesslist!"
                  catch { close $sockclient }
                  pfw::db::dec

          }


        } else {

                pfw::print "INT: #$pfw::db::cnt \t $ip:$port rejected, maxsession reached!"
                catch { close $sockclient }
                pfw::db::dec
        }
 
}

# MAIN STARTS HERE #

if { $argc == 7 } {

                set pfw::db::cnt        0
                set pfw::localhost      [ lindex $argv 0 ]
                set pfw::localport      [ lindex $argv 1 ]
                set pfw::remotehost     [ lindex $argv 2 ]
                set pfw::remoteport     [ lindex $argv 3 ]
                set pfw::maxsession     [ lindex $argv 4 ]
                set pfw::debug          [ lindex $argv 5 ]

                if { [ catch { set infile [ open [ lindex $argv 6 ]] } merror ] } {
                        puts "ERROR - $merror"
                        exit

                } else {

                        set pfw::allowlist [ read $infile ]
                        if { [ catch { close $infile } merror ] } {
                                puts "ERROR - $merror"
                                exit
                        } 
                }
                 
                pfw::print "---------------------------------------------------------------------------------------"
                pfw::print "$pfw::copyright - $pfw::localhost:$pfw::localport <-> $pfw::remotehost:$pfw::remoteport"
                pfw::print "Allowing connections from:"
                for {set x 0} { $x < [ llength $pfw::allowlist ]} {incr x} {pfw::print [ lindex $pfw::allowlist $x ] }
                pfw::print "---------------------------------------------------------------------------------------"  
                socket -server [list connect $pfw::remotehost $pfw::remoteport ] -myaddr $pfw::localhost $pfw::localport
                vwait forever

} else {

        puts "\n\n"
        puts "------------------------------------"
        puts "$pfw::copyright - Usage:"
        puts "------------------------------------"
        puts "$argv0 localhost localport remotehost remoteport maxsessions false|true xslist.pfw\n\n"
}
 




# make a file called xslist.pfw and store it in de directory where pfw.tcl
# is listed and executed. XSLIST.PFW contains 1 line of allowed ip addresses:
127.0.0.1 192.168.168.110 212.121.221.121 192.168.168.120 192.168.168.12

Forwarding data from one machine to a static ip could be used to passby Firewalls when running the PortForwarder on an outside machine with a granted (by firewall) port (like 80). The example is fully functional, but I would like to have a look using the fcopy command instead and also to see if its possible to do with less catch checks on IO. (could be faster but also have some disadvantages using Fcopy)

Use the code for free and its a great example on how flexible tcl code is when it comes to socket tranfers.

2 comments

Keith Vetter 20 years, 11 months ago  # | flag

See also sockspy at http://mini.net/tcl/sockspy for a similar port forwarding program but one designed more for the spying upon the data being sent on the socket.

Frank Bannon 15 years, 3 months ago  # | flag

Mini.net is offline. Find the new page for sockspy at wiki.tcl.tk or sourceforge.

Created by Norman Deppenbroek on Sun, 1 Dec 2002 (MIT)
Tcl recipes (162)
Norman Deppenbroek's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks