#!@RUBY@ # # dtcpd, Turmpet Dynamic Tunnel Configuration Protocol daemon # # # Copyright (c) 2000-2006 Hajimu UMEMOTO # All rights reserved. # # Copyright (C) 1999 WIDE Project. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the project nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Id: dtcps.rb,v 1.3 2000/04/21 14:21:21 jinmei Exp $ # $Mahoroba: src/dtcp/dtcps.rb,v 1.70 2006/01/08 18:24:02 ume Exp $ # require 'getopts' require "socket" require "thread" require "md5" require "dbm" require "etc" require 'syslog' AUTHTIMEOUT = 60 TUNTIMEOUT = 300 IF_MAXUNIT = 0x7fff # must be less than 10, against RIPng and PIM6 - useless TRAFFICTIMEOUT = 0 POPAUTHUID = 'pop' ROUTETABLE = '@PREFIX@/etc/routetable' PIDFILE = '/var/run/dtcps.pid' UDP_TUNNEL_PORT = 4028 IPPORT_MAX = 65535 ROUTE_GATEWAY = 0 ROUTE_CHANGE = 1 ROUTE_INTERFACE = 2 ROUTE_IFP = 3 # FreeBSD port of qpopper 4.X #POPAUTHDB = '/usr/local/etc/qpopper/pop.auth' # FreeBSD port of qpopper 2.X #POPAUTHDB = '/usr/local/etc/popper/pop.auth' # NetBSD pkg of qpopper 4.X #POPAUTHDB = '/usr/pkg/etc/apop.auth' # POPAUTHDB = '@POPAUTHDB@' # NetBSD 1.5.x or earlier # and OpenBSD #TUNIF = "gif[0-9]+" #TUNIF_CLONING = false #TUNNEL_CREATE = 'ifconfig %s tunnel %s %s' #TUNNEL_DELETE = 'ifconfig %s deletetunnel' #ROUTE_METHOD = ROUTE_CHANGE # # FreeBSD 4.6-RELEASE or later # and NetBSD 1.6 # (We don't support gif cloning, yet. You must create gifs before) #TUNIF = "gif0" #TUNIF_CLONING = true #TUNNEL_CREATE = 'ifconfig %s tunnel %s %s' #TUNNEL_DELETE = 'ifconfig %s deletetunnel' #ROUTE_METHOD = ROUTE_IFP # # FreeBSD 4.4-RELEASE and 4.5-RELEASE # (We don't support gif cloning, yet. You must create gifs before) #TUNIF = "gif0" #TUNIF_CLONING = true #TUNNEL_CREATE = 'ifconfig %s tunnel %s %s' #TUNNEL_DELETE = 'ifconfig %s deletetunnel' #ROUTE_METHOD = ROUTE_GATEWAY # # FreeBSD 4.3-RELASE or earlyer #TUNIF = "gif[0-9]+" #TUNIF_CLONING = false #TUNNEL_CREATE = 'gifconfig %s %s %s' #TUNNEL_DELETE = 'gifconfig %s delete' #ROUTE_METHOD = ROUTE_GATEWAY # TUNIF = '@DTCPS_TUNIF@' TUNIF_CLONING = true TUNNEL_CREATE = 'ifconfig %s tunnel %s %s' TUNNEL_DELETE = 'ifconfig %s deletetunnel' ROUTE_METHOD = ROUTE_IFP class TunnelInfo attr :thread attr :tun private def initialize(tun) @thread = Thread.current @tun = tun end end def daemon(nochdir, noclose) pid = fork if pid == -1 return -1 elsif pid != nil exit 0 end Process.setsid() Dir.chdir('/') if (nochdir == 0) if noclose == 0 devnull = open("/dev/null", "r+") $stdin.reopen(devnull) $stdout.reopen(devnull) $stderr.reopen(devnull) end return 0 end def logmsg(msg) if $syslog.opened? $syslog.notice('%s', msg) else $stderr.print msg end end def debugmsg(msg) logmsg(msg) if ($debug) end def execute(cmd) debugmsg("#{cmd}\n") system(cmd) end class Interface attr :name def up execute("ifconfig #{@name} up") end def down execute("ifconfig #{@name} down") end def addaddr(addr) execute("ifconfig #{@name} inet6 #{addr} alias") end def deladdr(addr) execute("ifconfig #{@name} inet6 #{addr} -alias") end def setmtu(mtu = 1500) execute("ifconfig #{@name} mtu #{mtu}") end def linklocal `ifconfig #{@name} inet6`.each { |s| if s =~ /inet6 (fe80::[^ ]*)/ return $1 end } return nil end def hostid laddr = linklocal if !laddr return nil end return laddr.sub(/^fe80::/, '').sub(/%.*$/, '') end private def initialize(name) @name = name end end class ClonedInterface < Interface attr :created def create @name = @tunif if @cloning cmd = sprintf("ifconfig %s create", @name) debugmsg("#{cmd}\n") `#{cmd}`.each { |l| if l =~ /^(#{@name}[0-9]+)/ @name = $1 break end } end @created = true end def created? return @created end def delete if @cloning && !@create_only execute(sprintf("ifconfig %s destroy", @name)) end @created = false end private def initialize(tunif, cloning, create_only) @tunif = tunif @cloning = cloning @create_only = create_only @created = false end end class GenericTunnel < ClonedInterface def create(me = nil, her = nil) super() if me && her tunnel(me, her) end end def delete down execute(sprintf(TUNNEL_DELETE, @name)) super end def tunnel(me, her) execute(sprintf(TUNNEL_CREATE, @name, me, her)) up end def addpeer(me, her) execute("ifconfig #{@name} inet6 #{me} #{her} prefixlen 128 alias") end def delpeer(me, her) execute("ifconfig #{@name} inet6 #{me} #{her} prefixlen 128 -alias") end def setmtu(mtu = 1280) super(mtu) end end class NetgraphInterface < Interface def create if !@tunif || @tunif == "ng" @name = mkpeer @created = true return end if @tunif !~ /^ng([0-9]+)$/ raise "#{@tunif}: wrong name" end unitmax = $1.to_i shutdown(@tunif) bogus = Array.new while TRUE @name = mkpeer if @name == @tunif @created = true break end bogus.push(@name) if @name !~ /^ng([0-9]+)$/ raise "#{@name}: wrong name" end unit = $1.to_i if unit > unitmax raise "#{@name}: not expected" end end bogus.each { |iface| shutdown(iface) } end def created? return @created end def delete shutdown(@name) @created = false end private def initialize(tunif = nil) @tunif = tunif @created = false end def mkpeer() iface = nil f = IO.popen("ngctl -f - 2> /dev/null", "w+") f.write("mkpeer iface dummy inet6\n") f.write("msg dummy nodeinfo\n") f.write("quit\n") f.flush f.each_line { |line| if line =~ / name="(ng[0-9]+)"/ iface = $1 break end } f.close if !iface raise "ngctl failed" end return iface end def shutdown(iface = nil) if !iface iface = @name end execute("ngctl shutdown #{iface}: >/dev/null 2>&1") end end class UDPTunnel < NetgraphInterface attr :me_port attr :her_port def create(me = nil, her = nil) super() if me && her tunnel(me, her) end end def delete down super if @me_port < $udp_tunnel_port $udp_tunnel_port = @me_port end end def tunnel(me_addr, her_addr, her_port) if !ksocket raise "ngctl: mkpeer ksocket fail" end @me_port = -1 $udp_tunnel_port.upto(IPPORT_MAX) do |port| if bind(me_addr, port) @me_port = port $udp_tunnel_port = port + 1 break end end if @me_port < 0 raise "ngctl: bind fail addr=#{me_addr} port=#{$udp_tunnel_port}" end if !connect(her_addr, her_port) raise "ngctl: connect fail addr=#{her_addr} port=#{her_port}" end @her_port = her_port up end def addpeer(me, her) execute("ifconfig #{@name} inet6 #{me} #{her} prefixlen 128 alias") end def delpeer(me, her) execute("ifconfig #{@name} inet6 #{me} #{her} prefixlen 128 -alias") end private def ksocket() execute("ngctl mkpeer #{@name}: ksocket inet6 inet/dgram/udp") end def bind(addr, port) execute("ngctl msg #{@name}:inet6 bind inet/#{addr}:#{port} >/dev/null 2>&1") end def connect(addr, port) execute("ngctl msg #{@name}:inet6 connect inet/#{addr}:#{port}") end end class InterfaceInfo attr :tunif attr_accessor :status private def initialize(tunif, status) @tunif = tunif @status = status end end class InterfacePool INTERFACE_EXIST = 1 INTERFACE_INUSE = 2 def assign(ifname = nil) if ifname if ifname !~ /^([a-zA-Z]+)(\d+)$/ return nil end if_name = $1 unit = $2.to_i if @cloning if if_name == @if_name && (@unit_low < 0 || unit >= @unit_low) return nil end else if ifname =~ /^#{@tunif}$/ return nil end end tunif = GenericTunnel.new(ifname, @cloning, @create_only) tunif.create return tunif end if @cloning if @unit_min >= 0 @unit_min.upto(IF_MAXUNIT) do |unit| s = "#{@if_name}#{unit}" if @interface.has_key?(unit) next if !@create_only next if @interface[unit].status == INTERFACE_INUSE tunif = @interface[unit].tunif @interface[unit].status = INTERFACE_INUSE else tunif = GenericTunnel.new(s, @cloning, @create_only) tunif.create if !@create_only # XXX: Creation failure should be checked actually. next if !tunif.created? end @interface[unit] = InterfaceInfo.new(tunif, INTERFACE_INUSE) end @unit_min = unit + 1 break end else tunif = GenericTunnel.new(@ifname, @cloning, @create_only) tunif.create end else `ifconfig -a`.each { |s| next if s !~ /^#{@ifname}:/ || s =~ /UP/o ifname = s[0, s.index(':')] tunif = GenericTunnel.new(ifname, @cloning, @create_only) tunif.create break } end return tunif end def resign(tunif) tunif.name =~ /^([a-zA-Z]+)(\d+)$/ if_name = $1 unit = $2.to_i tunif.delete return unless @cloning # XXX: Is @create_only check needed? if if_name != @if_name && !@create_only return end if @interface.has_key?(unit) if @create_only @interface[unit].status = INTERFACE_EXIST else @interface.delete(unit) end end if @unit_low >= 0 && unit >= @unit_low && unit < @unit_min @unit_min = unit end end private def initialize(ifname, cloning, create_only) @ifname = ifname @cloning = cloning @create_only = create_only if @ifname =~ /^([a-zA-Z]+)(\d+)$/ @if_name = $1 @unit_low = $2.to_i @unit_min = $2.to_i elsif @ifname =~ /^([a-zA-Z]+)$/ @if_name = $1 @unit_low = -1 @unit_min = -1 @create_only = false else @if_name = "" @unit_low = -1 @unit_min = -1 @cloning = false end @interface = Hash.new end end class NetgraphInterfacePool def assign(ifname = nil) if ifname if ifname !~ /^(ng)(\d+)$/ return nil end if_name = $1 unit = $2.to_i if if_name == @if_name && (@unit_low < 0 || unit >= @unit_low) return nil end tunif = UDPTunnel.new(ifname) tunif.create return tunif end if @unit_min >= 0 tunif = UDPTunnel.new("#{@if_name}#{@unit_min}") tunif.create if tunif.name !~ /^(ng)(\d+)$/ return nil end @unit_min = $2.to_i else tunif = UDPTunnel.new(@ifname) tunif.create end return tunif end def resign(tunif) if tunif.name !~ /^(ng)(\d+)$/ return end if_name = $1 unit = $2.to_i tunif.delete if if_name != @if_name return end if @unit_low >= 0 && unit >= @unit_low && unit < @unit_min @unit_min = unit end end private def initialize(ifname) @ifname = ifname if @ifname =~ /^(ng)(\d+)$/ @if_name = $1 @unit_low = $2.to_i @unit_min = $2.to_i elsif @ifname =~ /^(ng)$/ @if_name = $1 @unit_low = -1 @unit_min = -1 else raise "#{@ifname}: invalid interface name" end end end def route_add(dest, tunif) case ROUTE_METHOD when ROUTE_CHANGE cmd = "route add -inet6 #{dest} ::1; route change -inet6 #{dest} -ifp #{tunif}" when ROUTE_INTERFACE cmd = "route add -inet6 #{dest} -interface #{tunif}" when ROUTE_IFP cmd = "route add -inet6 #{dest} ::1 -ifp #{tunif}" else laddr = getladdr(tunif) if !laddr return nil end cmd = "route add -inet6 #{dest} #{laddr}" end return cmd end def getladdr(tunif) `ifconfig #{tunif} inet6`.each { |s| if s =~ /inet6 (fe80::[^ ]*)/ return $1 end } return nil end def getprefix(user) prefixes = nil tunif = nil begin open(ROUTETABLE) do |f| f.each_line do |l| l.chop!.sub!('\s*#.*$', '') next if l =~ /^\s*$/o if l =~ /^#{user}\s+/ t = l.split(/\s+/) (1 .. t.size - 1).each { |i| t[i] = nil if t[i] == "-" } prefixes = t[1] tunif = t[2] break end end end rescue debugmsg("routetable is not found\n") return nil, nil end return prefixes, tunif end def routesetup(tunif, prefixes) prefixes.split(/\s*,\s*/).each { |prefix| heraddr, herlen = prefix.split('/') cmd = route_add("#{heraddr} -prefixlen #{herlen}", tunif) if !cmd debugmsg("#{user}: cannot get link-local address of #{tunif}\n") return false end execute(cmd) } return true end class Tunnel attr_accessor :tunif attr_accessor :info def delete logmsg("#{@tunif.name} disconnected\n") case @info.length when 3 # network type tunnel @info[2].split(/\s*,\s*/).each { |her_prefix| heraddr, herlen = her_prefix.split('/') execute("route delete -inet6 #{heraddr} -prefixlen #{herlen}") } when 4 # host type tunnel delpeer(@tunif, @info[3], @info[2]) when 5 # network type tunnel @info[4].split(/\s*,\s*/).each { |her_prefix| heraddr, herlen = her_prefix.split('/') execute("route delete -inet6 #{heraddr} -prefixlen #{herlen}") } delpeer(@tunif, @info[3], @info[2]) end _delete(@tunif) @tunif = nil end private def initialize(s, user, type, mtu = 0, her_port = -1) me = s.addr()[3] her = s.peeraddr()[3] debugmsg("#{s}: tunnel #{me} -> #{her}\n") if $prefix == nil case type when 'host' debugmsg("#{s}: tunnel type #{type} not supported\n") raise "unsupported tunnel type #{type}" when 'network' if $network_with_peeraddr debugmsg("#{s}: internal error: tunnel configuration is wrong\n") raise 'internal error: tunnel configuration is wrong' end end end case type when 'network' her_prefixes, ifname = getprefix(user) if !her_prefixes logmsg("#{user}: not registered to use network tunnel type\n") raise 'prefix is not assigned' end when 'tunnelonly', 'host' her_prefixes, ifname = getprefix(user) else debugmsg("#{s}: unsupported tunnel type #{type}\n") raise "unsupported tunnel type #{type}" end @mtu = mtu tunif = create(me, her, ifname, her_port) if tunif == nil debugmsg("#{s}: tunnel interface sold out\n") raise 'tunnel interface sold out' end debugmsg("#{s}: tunnel interface #{tunif.name}\n") myaddr = nil if type == 'host' || (type == 'network' && $network_with_peeraddr) myaddr, heraddr = addpeer(tunif, $prefix) if !myaddr || !heraddr _delete(tunif) raise 'internal error: tunnel interface name format is wrong' end end if type == 'network' if !routesetup(tunif.name, her_prefixes) if myaddr delpeer(tunif, myaddr, heraddr) end _delete(tunif) raise 'prefix is not assigned' end end @tunif = tunif me = $global ? $global : me if tunif.instance_of?(UDPTunnel) me.concat(";#{tunif.me_port}") end case type when 'tunnelonly' @info = [her, me] when 'host' @info = [her, me, heraddr, myaddr] when 'network' if $network_with_peeraddr @info = [her, me, heraddr, myaddr, her_prefixes] else @info = [her, me, her_prefixes] end else debugmsg("#{s}: unsupported tunnel type #{type}\n") raise "unsupported tunnel type #{type}" end end def create(me, her, ifname, her_port) if her_port >= 0 if ifname !~ /^ng(\d+)$/ ifname = nil end tunif = $ng_interface.assign(ifname) else if ifname !~ /^gif(\d+)$/ ifname = nil end tunif = $interface.assign(ifname) end if tunif if tunif.instance_of?(UDPTunnel) tunif.tunnel(me, her, her_port) else tunif.tunnel(me, her) end if @mtu > 0 tunif.setmtu(@mtu) end end return tunif end def _delete(tunif) if @mtu > 0 tunif.setmtu end if tunif.instance_of?(UDPTunnel) $ng_interface.resign(tunif) else $interface.resign(tunif) end end def addpeer(tunif, prefix) if tunif.name !~ /(\d+)$/ return nil, nil end tunid = $1.to_i heraddr = sprintf("%s%04x", prefix, (tunid + 1) * 4 + 2) myaddr = sprintf("%s%04x", prefix, (tunid + 1) * 4 + 1) tunif.addpeer(myaddr, heraddr) return myaddr, heraddr end def delpeer(tunif, me, her) tunif.delpeer(me, her) end end def getipkts(intface) tmpfile = "/tmp/getipkts#{$$}.#{intface}" system("netstat -in -I #{intface} > #{tmpfile}") f = open(tmpfile, "r") s = f.readline s = f.readline f.close File::unlink(tmpfile) t = s.split(/[ \t]+/) if t.length < 5 debugmsg("#{intface} ipkts unknown, returning -1\n") end debugmsg("#{intface} ipkts = #{t[t.length - 5]}\n") return t[t.length - 5] end def checktraffic(tun) return if TRAFFICTIMEOUT == 0 ipkts = getipkts(tun.name) while TRUE sleep TRAFFICTIMEOUT i = getipkts(tun.name) next if i == -1 break if ipkts >= i ipkts = i end end def sendmsg(sock, msg) debugmsg("#{sock}: sent <#{msg}>\n") sock.print "#{msg}\r\n" end def service_dtcp(sock, name) debugmsg("service_dtcp(#{sock}, #{name})\n") while TRUE debugmsg("service_dtcp(#{sock}, #{name}) accepting\n") Thread.start(sock.accept) { |s| debugmsg("service_dtcp(#{sock}, #{name}) accepted #{s}\n") tun = nil user = nil # send challenge challenge = seed() s.print "+OK #{challenge} KAME tunnel server\r\n" # check response # tunnel itojun RESPONSE type while TRUE t = select([s], [], [s], tun == nil ? AUTHTIMEOUT : TUNTIMEOUT) if t == nil s.print "-ERR connection timed out, disconnecting\r\n" break end if s.eof? break end if user # be careful. it may accesses non existence member wrongly. # so, make sure to copy context, 1st. alive = $tunnel[user] if !alive debugmsg("#{user} was disconnected\n") break end if alive.thread != Thread.current debugmsg("#{user} has another new session\n") break end end response = s.readline response.gsub!(/[\n\r]/, '') if response != '' t = response.split(/ /) t[0].tr!('A-Z', 'a-z') else t = [''] end debugmsg("#{s}: got <#{response}>\n") case t[0] when 'tunnel' mtu = 0 her_port = 0 udp_tunnel = false if t.length >= 5 opterr = false 4.upto(t.length - 1) do |opt| if t[opt] =~ /^[0-9]+$/ # backward compatibility mtu = t[opt].to_i else var, val = t[opt].split(/=/) case var when 'mtu' if val !~ /^[0-9]+$/ opterr = true break end mtu = val.to_i when 'port' if val !~ /^[0-9]+$/ opterr = true break end her_port = val.to_i when 'proto' case val when 'udp' if !$udp_tunnel opterr = true break end udp_tunnel = true else opterr = true break end else opterr = true break end end end if opterr logmsg("client #{s} sent wrong #{t[0]} command\n") sendmsg(s, "-ERR authentication failed.") next end end if !udp_tunnel her_port = -1 end user = t[1] type = (t[3] == 'tunnelroute') ? 'network' : t[3] pass = getpopauth(user) if pass == nil logmsg("client #{s} has no password in database for #{user}\n") sendmsg(s, "-ERR authentication failed.") next end # get password from the username # $stderr.print "authenticate(#{user} #{challenge} #{pass}): " # debugmsg(authenticate(user, challenge, pass) + "\n") # debugmsg("target: #{t[2]}\n") if (authenticate(user, challenge, pass) == t[2]) debugmsg("client #{s.peeraddr()[3]} on #{s}\n") logmsg("client #{s.peeraddr()[3]} authenticated as #{user}\n") auth = true err = '' $mutex.synchronize { if $tunnel.has_key?(user) logmsg("#{user}: duplicate login was detected\n") $tunnel[user].tun.delete $tunnel.delete(user) end her = s.peeraddr()[3] if !udp_tunnel $tunnel.each { |u, t| if t.tun.tunif.instance_of?(GenericTunnel) && t.tun.info[0] == her logmsg("#{user}: her IPv4 address #{her} was conflicted with #{u}\n") t.tun.delete $tunnel.delete(u) break end } end begin tun = Tunnel.new(s, user, type, mtu, her_port) rescue => e err = e.to_str end if tun != nil $tunnel[user] = TunnelInfo.new(tun) end } if tun == nil logmsg("failed to configure for #{user} type #{type}: #{err}\n") sendmsg(s, "-ERR #{err}") else t = tun.info.join(' ') logmsg("#{tun.tunif.name} configured for #{user} type #{type}: #{t}\n") sendmsg(s, "+OK #{t}") end else logmsg("client #{s} not authenticated\n") sendmsg(s, "-ERR authentication failed.") end when 'ping' sendmsg(s, "+OK hi, happy to hear from you") when 'help' sendmsg(s, "+OK valid commands are: TUNNEL PING QUIT") when 'quit' sendmsg(s, "+OK see you soon.") break else debugmsg("client #{s} sent invalid command #{t[0]}\n") sendmsg(s, "-ERR invalid command") end end if tun != nil $mutex.synchronize { if $tunnel.has_key?(user) && $tunnel[user].thread == Thread.current checktraffic(tun) tun.delete $tunnel.delete(user) end } end begin s.flush rescue end begin s.shutdown(1) rescue end begin s.close rescue end debugmsg("shutdown #{s} #{Thread.current}\n") } end debugmsg("service_dtcp(#{sock}, #{name}) finished\n") end def usage() $stderr.print "usage: #{File.basename($0)} [-dDU] [-b udp-port] [-i interfaces] [-I udp-interface] [-p port] [prefix]\n" end def seed() m = MD5.new(Time.now.to_s) m.update($$.to_s) m.update(Socket.gethostname()) return m.digest.unpack("H32")[0].tr('a-f', 'A-F') end def authenticate(user, seed, pass) m = MD5.new(user) m.update(seed) m.update(pass) return m.digest.unpack("H32")[0].tr('a-f', 'A-F') end # NOTE: strings are terminated by "\0"... def getpopauth(user) pw = Etc.getpwnam(POPAUTHUID) if pw == nil debugmsg("no user named pop\n") return nil end origuid = Process.euid # XXX begin seteuid(pop) Process.euid = pw[2] f = DBM.open(POPAUTHDB, nil) if f == nil debugmsg("no password database found\n") Process.euid = origuid return nil end p = f[user + "\0"] f.close Process.euid = origuid # XXX end seteuid(pop) if p == nil debugmsg("no relevant password database item found\n") return nil end while p.length > 0 && p[p.length - 1] == 0 p = p[0, p.length - 1] end for i in 0 .. p.length - 1 p[i] = [p[i] ^ 0xff].pack('C') end debugmsg("ok, relevant password database item found\n") return p end def terminate debugmsg("signal was received\n") $mutex.synchronize { $tunnel.each_key { |user| $tunnel[user].tun.delete $tunnel.delete(user) } } # for safety if !$cloning `ifconfig -lu`.chop.split(/ +/o).grep(/^#{$tunif}$/).each { |i| execute("ifconfig #{i} down") execute(sprintf(TUNNEL_DELETE, i)) } end if $daemonize File.unlink(PIDFILE) end exit 0 end #------------------------------------------------------------ port = 20200 $tunif = TUNIF $ng_tunif = "ng" $cloning = TUNIF_CLONING $global = nil $prefix = nil $network_with_peeraddr = nil $udp_tunnel_port = UDP_TUNNEL_PORT if !getopts('acdDoU', 'b:', 'g:', 'i:', 'I:', 'p:') usage() exit 0 end $network_with_peeraddr = $OPT_a if $OPT_a $udp_tunnel_port = $OPT_b if $OPT_b $cloning = false if $OPT_c $debug = $OPT_d $daemonize = !$OPT_D $global = $OPT_g if $OPT_g $tunif = $OPT_i if $OPT_i $ng_tunif = $OPT_I if $OPT_I $create_only = $OPT_o port = $OPT_p if $OPT_p $udp_tunnel = $OPT_U case ARGV.length when 0 $prefix = nil when 1 $prefix = ARGV[0] if $prefix !~ /^[0-9a-fA-f:]*::$/ usage() exit 1 end else usage() exit 1 end res = [] t = Socket.getaddrinfo(nil, port, Socket::PF_INET, Socket::SOCK_STREAM, nil, Socket::AI_PASSIVE) if (t.size <= 0) logmsg("FATAL: getaddrinfo failed (port=#{port})\n") exit 1 end res += t $syslog = Syslog.instance if $daemonize daemon(0, 0) $syslog.open(File.basename($0), Syslog::LOG_PID, Syslog::LOG_DAEMON) open(PIDFILE, "w") { |pid| pid.print "#{$$}\n" } end $mutex = Mutex.new $tunnel = Hash.new $interface = InterfacePool.new($tunif, $cloning, $create_only) if $udp_tunnel $ng_interface = NetgraphInterfacePool.new($ng_tunif) end sockpool = [] names = [] listenthreads = [] res.each do |i| s = TCPserver.new(i[3], i[1]) n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ") s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) sockpool.push s names.push n end if $debug (0 .. sockpool.size - 1).each do |i| debugmsg("listen[#{i}]: #{sockpool[i]} #{names[i]}\n") end end trap("SIGTERM") { terminate } trap("SIGINT") { terminate } trap("SIGHUP", "SIG_IGN") (0 .. sockpool.size - 1).each do |i| listenthreads[i] = Thread.start { debugmsg("listen[#{i}]: thread #{Thread.current}\n") service_dtcp(sockpool[i], names[i]) } end for i in listenthreads if VERSION =~ /^1\.2/ Thread.join(i) else i.join end end if $daemonize File.unlink(PIDFILE) end exit 0