Exemple #1
0
    def getCgfwCfg():
        ret = dict()

        for cgfwFile in sorted(glob.glob("/etc/cgfw/*.cgfw")):
            cfg = configparser.SafeConfigParser()
            cfg.read(cgfwFile)

            if cfg.has_option("cgfw-entry", "provider"):
                provider = CgfwUtil.stripComment(cfg.get("cgfw-entry", "provider"))
                if provider not in ["myvpnpp"]:
                    raise Exception("invalid provider %s in cgfw-entry" % (provider))
                ret[provider] = dict()
            else:
                raise Exception("no type in cgfw-entry")

            if cfg.has_option("cgfw-entry", "username"):
                ret[provider]["username"] = CgfwUtil.stripComment(cfg.get("cgfw-entry", "username"))
            else:
                raise Exception("no username in cgfw-entry")

            if cfg.has_option("cgfw-entry", "password"):
                ret[provider]["password"] = CgfwUtil.stripComment(cfg.get("cgfw-entry", "password"))
            else:
                raise Exception("no password in cgfw-entry")

        return ret
Exemple #2
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)
Exemple #3
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.")
Exemple #4
0
    def getPrefixList(gfwDir):
        """Returns list of ipaddress.IPv4Network"""

        # get GFWed network list
        nets = []
        for fn in os.listdir(gfwDir):
            fullfn = os.path.join(gfwDir, fn)
            with open(fullfn) as f:
                lineList = f.read().split("\n")
                for i in range(0, len(lineList)):
                    line = CgfwUtil.getLineWithoutBlankAndComment(lineList[i])
                    if line is None:
                        continue
                    nets.append(ipaddress.IPv4Network(line))

        # optimize network list
        nets.sort()
        i = 0
        while i < len(nets):
            j = i + 1
            while j < len(nets):
                if nets[i].overlaps(nets[j]):
                    # big network is in front of small network, so we remove small network.
                    # I think network can only "wholly contain" each other, does "partly contain" really exist?
                    del nets[j]
                    continue
                j += 1
            i += 1

        return nets
Exemple #5
0
    def getPrefixList(gfwDir):
        """Returns list of ipaddress.IPv4Network"""

        # get GFWed network list
        nets = []
        for fn in os.listdir(gfwDir):
            fullfn = os.path.join(gfwDir, fn)
            with open(fullfn) as f:
                lineList = f.read().split("\n")
                for i in range(0, len(lineList)):
                    line = CgfwUtil.getLineWithoutBlankAndComment(lineList[i])
                    if line is None:
                        continue
                    nets.append(ipaddress.IPv4Network(line))

        # optimize network list
        nets.sort()
        i = 0
        while i < len(nets):
            j = i + 1
            while j < len(nets):
                if nets[i].overlaps(nets[j]):
                    # big network is in front of small network, so we remove small network.
                    # I think network can only "wholly contain" each other, does "partly contain" really exist?
                    del nets[j]
                    continue
                j += 1
            i += 1

        return nets
Exemple #6
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)
Exemple #7
0
 def getDomainList(gfwDomainDir):
     # get GFWed domain list
     ret = []
     for fn in os.listdir(gfwDomainDir):
         fullfn = os.path.join(gfwDomainDir, fn)
         with open(fullfn) as f:
             lineList = f.read().split("\n")
             for i in range(0, len(lineList)):
                 line = CgfwUtil.getLineWithoutBlankAndComment(lineList[i])
                 if line is None:
                     continue
                 ret.append(line)
     return ret
Exemple #8
0
 def getDomainList(gfwDomainDir):
     # get GFWed domain list
     ret = []
     for fn in os.listdir(gfwDomainDir):
         fullfn = os.path.join(gfwDomainDir, fn)
         with open(fullfn) as f:
             lineList = f.read().split("\n")
             for i in range(0, len(lineList)):
                 line = CgfwUtil.getLineWithoutBlankAndComment(lineList[i])
                 if line is None:
                     continue
                 ret.append(line)
     return ret
Exemple #9
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
Exemple #10
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()