Ejemplo n.º 1
0
    def run(self):
        CgfwUtil.mkDirAndClear(self.param.tmpDir)
        try:
            sys.stdout = StdoutRedirector(os.path.join(self.param.tmpDir, "fpemud-cgfw.out"))
            sys.stderr = sys.stdout

            logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
            logging.getLogger().setLevel(logging.INFO)

            # check configuration
            if len(CgfwCommon.getCgfwCfgList(self.param.etcDir)) == 0:
                raise Exception("no cgfw config file")

            # create main loop
            DBusGMainLoop(set_as_default=True)
            self.param.mainloop = GLib.MainLoop()
            self.param.dbusMainObject = DbusMainObject(self.param, self)

            # write pid file
            with open(os.path.join(self.param.tmpDir, "fpemud-cgfw.pid"), "w") as f:
                f.write(str(os.getpid()))

            # modify dns server configuration
            with open("/etc/resolv.conf", "r") as f:
                self.resloveFileContent = f.read()
            self.dnsmasqProc = self._runDnsmasq()
            with open("/etc/resolv.conf", "w") as f:
                f.write("# Generated by fpemud-cgfw\n")
                f.write("nameserver 127.0.0.1\n")
            logging.info("DNS resolution path modified.")

            # run vpn client
            GObject.timeout_add_seconds(0, self._timeoutCallback)

            # start main loop
            logging.info("Mainloop begins.")
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, self._sigHandlerINT, None)
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, self._sigHandlerTERM, None)
            GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGHUP, self._sigHandlerHUP, None)
            GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGUSR1, self._sigHandlerUSR1, None)
            self.param.mainloop.run()
            logging.info("Mainloop exits.")
        finally:
            if self.vpnClientPidWatch is not None:
                GLib.source_remove(self.vpnClientPidWatch)
            if self.vpnClientProc is not None:
                self.vpnClientProc.terminate()
                self.vpnClientProc.wait()

            # revert dns resolution path
            if self.resloveFileContent is not None:
                with open("/etc/resolv.conf", "w") as f:
                    f.write(self.resloveFileContent)
            if self.dnsmasqProc is not None:
                self.dnsmasqProc.terminate()
                self.dnsmasqProc.wait()

            logging.shutdown()
            shutil.rmtree(self.param.tmpDir)
Ejemplo n.º 2
0
    def _runVpnClient(self):
        assert self.vpnClientProc is None and self.vpnClientPidWatch is None

        # randomly select a config
        cgfwCfg = random.choice(CgfwCommon.getCgfwCfgList(self.param.etcDir))

        # start vpn client process
        logging.info("CGFW VPN %s establishing." % (cgfwCfg.name))
        if cgfwCfg.vtype == "pptp":
            cmd = "/usr/sbin/pppd call %s nodetach" % (cgfwCfg.name)
            self.vpnClientProc = subprocess.Popen(cmd, shell=True, universal_newlines=True)
        else:
            assert False

        # wait for the vpn interface
        time.sleep(0.1)
        while not CgfwUtil.interfaceExists(cgfwCfg.interface):
            if self.vpnClientProc.poll() is not None:
                self.vpnClientProc = None
                logging.info("CGFW VPN %s failed to establish, retry in %d seconds." % (cgfwCfg.name, self.param.retryTimeout))
                GObject.timeout_add_seconds(self.param.retryTimeout, self._timeoutCallback)
                return
            time.sleep(1.0)

        # add routes
        for ip in self.param.nameServerList:
            CgfwUtil.shell("/bin/route add -host %s dev %s" % (ip, cgfwCfg.interface), "stdout")
        for net in CgfwCommon.getPrefixList(self.param.gfwDir):
            r = net.with_netmask.split("/")
            ip = r[0]
            mask = r[1]
            CgfwUtil.shell("/bin/route add -net %s netmask %s dev %s" % (ip, mask, cgfwCfg.interface), "stdout")

        # add dns server
        # dbusObj = dbus.SystemBus().get_object('org.freedesktop.resolve1', '/org/freedesktop/resolve1')
        # ifid = CgfwUtil.getInterfaceIfIndex(cgfwCfg.interface)
        # dbusObj.SetLinkDNS(ifid, [CgfwUtil.ip2ipar("8.8.8.8"), CgfwUtil.ip2ipar("8.8.4.4")], dbus_interface="org.freedesktop.resolve1.Manager")
        # logging.info("CGFW DNS server installed.")

        # watch vpn client process
        # bad things happen if the vpn process terminates before this operation
        self.vpnClientPidWatch = GLib.child_watch_add(self.vpnClientProc.pid, self._childWatchCallback)
        self.curCgfwCfg = cgfwCfg
        logging.info("CGFW VPN %s established." % (cgfwCfg.name))
        self.param.dbusMainObject.VpnConnected(self.bDataChanged)
Ejemplo n.º 3
0
    def cmdUpdate(self):
        CgfwUtil.printInfo("Checking IP ranges:")
        if True:
            prefixList = CgfwCommon.getPrefixList(self.param.gfwDir)

            CgfwUtil.printInfoNoNewLine("    Checking private network...")
            priList = CgfwUtil.getReservedIpv4NetworkList()
            for net in prefixList:
                for net2 in priList:
                    if net.overlaps(net2):
                        raise CgfwCmdException("GFWed prefix %s overlaps private network %s" % (net.with_prefixlen, net2.with_prefixlen))
            print("Done.")

            CgfwUtil.printInfoNoNewLine("    Checking non-GFWed network...")
            try:
                lcmList = CgfwCommon.getLatestChinaMainLandIpv4NetworkList()
                for net in prefixList:
                    for net2 in lcmList:
                        if net.overlaps(net2):
                            raise CgfwCmdException("GFWed prefix %s overlaps non-GFWed network %s" % (net.with_prefixlen, net2.with_prefixlen))
                print("Done.")
            except Exception as e:
                if isinstance(e, CgfwCmdException):
                    raise
                else:
                    print("Failed, but however it's better to continue.")

        CgfwUtil.printInfo("Modifying configuration files:")
        for cgfwCfg in CgfwCommon.getCgfwCfgList(self.param.etcDir):
            if cgfwCfg.vtype == "pptp":
                self._syncPptpVpnScript(cgfwCfg)
            else:
                assert False

        # send signal to daemon process if exists
        try:
            with open(os.path.join(self.param.tmpDir, "fpemud-cgfw.pid")) as f:
                os.kill(int(f.read()), signal.SIGHUP)
        except:
            pass
Ejemplo n.º 4
0
    def run(self):
        try:
            logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
            logging.getLogger().setLevel(
                CgfwUtil.getLoggingLevel(self.param.logLevel))
            logging.info("Program begins.")

            # read configuration
            for section, data in CgfwCommon.getCgfwCfg().items():
                tmpDir2 = os.path.join(self.param.tmpDir, section)
                if self.varDir is None:
                    varDir2 = None
                else:
                    varDir2 = os.path.join(self.param.varDir, section)
                self.curProviderList.append(
                    CgfwCommon.getProvider(section, data, tmpDir2, varDir2))

            # create main loop
            mainloop = GLib.MainLoop()

            # start business
            if not os.path.exists(self.param.tmpDir):
                os.makedirs(self.param.tmpDir)
            self.vpnRestartTimer = GObject.timeout_add_seconds(
                0, self._vpnRestartTimerCallback)

            # start main loop
            logging.info("Mainloop begins.")
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, on_sig_int,
                                 None)
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM,
                                 on_sig_term, None)
            mainloop.run()
            logging.info("Mainloop exits.")
        finally:
            logging.shutdown()
Ejemplo n.º 5
0
    def _syncPptpVpnScript(self, cgfwCfg):
        # 1. add the same routes again when ppp interface changes from spoofing-up to real-up
        #    it is because pppd deletes the original ppp interface and add a new ppp interface with the same name, this implementation sucks
        # 2. add nat rules
        fn = "/etc/ppp/ip-up.d/99-pptp-%s" % (cgfwCfg.name) + ".sh"
        CgfwUtil.printInfoNoNewLine("    Modifying %s..." % (fn))

        buf = ""
        buf += "#!/bin/bash\n"
        buf += "\n"
        buf += "if [ \"$6\" == \"%s\" ] ; then\n" % (cgfwCfg.name)
        if True:
            for ip in self.param.nameServerList:
                buf += "    /bin/route add -host %s dev %s\n" % (ip, cgfwCfg.interface)
        buf += "\n"
        if True:
            for net in CgfwCommon.getPrefixList(self.param.gfwDir):
                r = net.with_netmask.split("/")
                ip = r[0]
                mask = r[1]
                buf += "    /bin/route add -net %s netmask %s dev %s\n" % (ip, mask, cgfwCfg.interface)
        buf += "\n"
        if True:
            pidf = os.path.join(self.param.tmpDir, "fpemud-cgfw.pid")
            buf += "    if [ -f \"%s\" ] ; then\n" % (pidf)
            buf += "        /bin/kill -10 $(/bin/cat \"%s\")\n" % (pidf)    # send SIGUSR1
            buf += "    fi\n"
        buf += "fi\n"

        with open(fn, "w") as f:
            f.write(buf)
        print("Done.")

        # 1. routes are auto removed when ppp interface is removed
        # 2. remove nat rules
        fn = "/etc/ppp/ip-down.d/99-pptp-%s" % (cgfwCfg.name) + ".sh"
        CgfwUtil.printInfoNoNewLine("    Modifying %s..." % (fn))

        buf = ""
        buf += "#!/bin/bash\n"
        buf += "\n"
        buf += "if [ \"$6\" == \"%s\" ] ; then\n" % (cgfwCfg.name)
        buf += "    ;\n"
        buf += "fi\n"

        with open(fn, "w") as f:
            f.write(buf)
        print("Done.")
Ejemplo n.º 6
0
    def _upCallback(self):
        serverName, dummy, dummy = self.curProviderList[0].get_server()
        logging.info("CGFW VPN %s connected." % (serverName))

        CgfwCommon.ntfacSendEntityNameserverNew(
            self.param.nameServerList,
            CgfwCommon.getDomainList(self.param.gfwDomainDir))
        CgfwCommon.ntfacSendEntityGatewayNew((None, "cgfw"), [
            x.with_netmask for x in CgfwCommon.getPrefixList(self.param.gfwDir)
        ])

        self.upCalled = True
Ejemplo n.º 7
0
    def _runDnsmasq(self):
        # trick: dnsmasq debug is seldomly needed
        trickDebug = False

        # generate dnsmasq config file. so that GFWed domain is resolved
        # through our nameservers, non-GFWed domain is resolved through the
        # original nameservers. dnsmasq listens on 127.0.0.1:80
        buf = ""
        if trickDebug:
            buf += "log-queries=extra\n"
            buf += "log-facility=%s\n" % (os.path.join(self.param.tmpDir, "dnsmasq.debug"))
            buf += "\n"
        buf += "strict-order\n"
        buf += "bind-interfaces\n"                            # don't listen on 0.0.0.0
        buf += "listen-address=127.0.0.1\n"
        buf += "user=root\n"
        buf += "group=root\n"
        buf += "domain-needed\n"
        buf += "bogus-priv\n"
        buf += "no-hosts\n"
        buf += "\n"
        buf += "resolv-file=%s\n" % (os.path.join(self.param.tmpDir, "orig-resolve.conf"))
        buf += "no-poll\n"
        buf += "\n"
        for domain in CgfwCommon.getDomainList(self.param.gfwDomainDir):
            for ns in self.param.nameServerList:
                buf += "server=/%s/%s\n" % (domain, ns)
        cfgf = os.path.join(self.param.tmpDir, "dnsmasq.conf")
        with open(cfgf, "w") as f:
            f.write(buf)

        # generate alternative resolve.conf file
        with open(os.path.join(self.param.tmpDir, "orig-resolve.conf"), "w") as f:
            f.write(self.resloveFileContent)

        # run dnsmasq process
        cmd = "/usr/sbin/dnsmasq"
        cmd += " --keep-in-foreground"
        cmd += " --conf-file=\"%s\"" % (cfgf)
        cmd += " --pid-file=%s" % (os.path.join(self.param.tmpDir, "dnsmasq.pid"))
        proc = subprocess.Popen(cmd, shell=True, universal_newlines=True)

        return proc
Ejemplo n.º 8
0
 def GetPrefixList(self):
     nets = CgfwCommon.getPrefixList(self.param.gfwDir)
     nets = [x.with_netmask.split("/") for x in nets]
     return nets
Ejemplo n.º 9
0
 def cmdCheck(self):
     for cgfwCfg in CgfwCommon.getCgfwCfgList(self.param.etcDir):
         if cgfwCfg.vtype == "pptp":
             self._checkPptpVpn(cgfwCfg)
         else:
             assert False