def test_ipv4info_invalid(self): tmppath = self.tempfile.mkdtemp() set_cwd(tmppath) create_cwd(tmppath) geo = GeoInfo() res = geo.ipv4info("8adna87dasd87asd") assert res["country"] == "unknown" assert res["country_code"] == "unknown" assert res["city"] == "unknown"
def test_ipv4info_unknown(self): tmppath = self.tempfile.mkdtemp() set_cwd(tmppath) create_cwd(tmppath) geo = GeoInfo() res = geo.ipv4info("10.0.0.5") assert res["country"] == "unknown" assert res["country_code"] == "unknown" assert res["city"] == "unknown"
def test_ipv4info(self): tmppath = self.tempfile.mkdtemp() set_cwd(tmppath) create_cwd(tmppath) geo = GeoInfo() res = geo.ipv4info("93.184.216.34") assert res["country"] == "United States" assert res["country_code"] == "US" assert res["city"] == "Norwell"
def add(self, host, port, username=None, password=None, description=None): """Add a socks5 server. :param host: IP or a valid hostname of the socks5 server. Should be unique. :param port: Port of the socks5 server (int) :param username: Username of the socks5 server (optional) :param password: Password for the socks5 server user (optional). Password will be stored in plaintext! :param description: Description to store with the socks5 server (optional) :return: A dictionary containing the provided information, the generated id, the determined country, country code, and city. :rtype: dict :raises: Socks5CreationError :example: >>> from socks5man.manager import Manager >>> Manager().add("example.com", 8456) { 'username': None, 'city': u'Norwell', 'host': 'example.com', 'country_code': u'US', 'country': u'United States', 'password': None, 'port': 8456, 'id': 1 } .. note:: It is only possible to provide both a username and a password. Hostname/IP should be unique. If a socks5 exists with the provided hostname/IP, a Socks5CreationError will be thrown. """ if (not username and password) or (not password and username): raise Socks5CreationError( "Either no password and no password or both a password and a" "username should be provided on socks5 creation. It is not " "possible to provide only a username or password" ) valid_entry = validify_host_port(host, port) if not valid_entry: raise Socks5CreationError( "Invalid host or port used. Invalid IP, non-existing hostname" ", or an invalid port specified. Host: %s, port: %s" % ( host, port ) ) existing = db.view_socks5(host=host, port=port) if existing: raise Socks5CreationError( "Socks5 host and port combination: '%s:%s' already exists." " Socks5 ID is: %s" % ( host, port, existing.id ) ) entry = Dictionary( host=host, port=port, username=username, password=password ) entry.update(GeoInfo.ipv4info(valid_entry.ip)) socksid = db.add_socks5( entry.host, entry.port, entry.country, entry.country_code, city=entry.city, username=entry.username, password=entry.password, description=description ) entry["id"] = socksid return entry
def bulk_add(self, socks5_dict_list, description=None): """Bulk add multiple socks5 server. No duplicate checking is done. :param socks5_dict_list: A list of dictionaries that at a minimum contain the keys and valid values for 'host' and 'port'. :param description: A description to be added to all provided servers :returns: The amount of socks5 server that were successfully added :rtype: int :raises: Socks5CreationError :example: >>> from socks5man.manager import Manager >>> Manager().bulk_add([{"host": "example.com", "port": 1234}, {"host": "example.org", "port": 1234}]) 2 .. note:: It is only possible to provide both a username and a password for a server. Hostname/IP should be unique. Socks5 servers with invalid hostnames or missing fields will be skipped. Socks5CreationError is raised if no valid servers are in the list. """ new = [] for entry in socks5_dict_list: if "host" not in entry or "port" not in entry: continue password = entry.get("password") username = entry.get("username") if (not username and password) or (not password and username): log.warning( "Either no password and no password or both a password " "and a username should be provided on socks5 creation. It " "is not possible to provide only a username or password" ) continue valid_entry = validify_host_port(entry["host"], entry["port"]) if not valid_entry: log.error("Invalid host or port provided") continue if description: entry["description"] = description new_entry = { "host": entry["host"], "port": valid_entry.port, "country": None, "country_code": None, "city": None, "username": username, "password": password, "operational": False, "description": entry.get("description") } new_entry.update(GeoInfo.ipv4info(valid_entry.ip)) new.append(new_entry) if not new: raise Socks5CreationError("No socks5 servers to add provided") db.bulk_add_socks5(new) return len(new)
def update_geodb(): version_file = cwd("geodb", ".version") if not os.path.isfile(version_file): log.error("No geodb version file '%s' is missing", version_file) return with open(version_file, "rb") as fp: current_version = fp.read() try: latest_version = urllib2.urlopen(cfg("geodb", "geodb_md5_url")).read() except urllib2.URLError as e: log.error("Error retrieving latest geodb version hash: %s", e) return if current_version == latest_version: log.info("GeoIP database at latest version") return extracted = cwd("geodb", "extracted") renamed = None if os.path.exists(extracted): renamed = cwd("geodb", "old-extracted") os.rename(extracted, renamed) try: url = cfg("geodb", "geodb_url") log.info("Downloading latest version: '%s'", url) mmdbtar = urllib2.urlopen(url).read() except urllib2.URLError as e: log.error( "Failed to download new mmdb tar. Is the URL correct? %s", e ) if renamed: log.error("Restoring old version..") os.rename(renamed, extracted) return tarpath = cwd("geodb", "geodblite.tar.gz") with open(tarpath, "wb") as fw: fw.write(mmdbtar) os.mkdir(extracted) unpack_mmdb(tarpath, cwd("geodb", "extracted", "geodblite.mmdb")) log.info("Version update complete") if renamed: log.debug("Removing old version") shutil.rmtree(renamed) log.info("Updating geo IP information for all existing servers") GeoInfo.georeader = None for socks5 in db.list_socks5(): log.debug( "Updating server: '%s'. Current country: %s", socks5.host, socks5.country ) ip = socks5.host if not is_ipv4(ip): ip = get_ipv4_hostname(ip) geoinfo = GeoInfo.ipv4info(ip) old = (socks5.country, socks5.country_code, socks5.city) new = (geoinfo["country"], geoinfo["country_code"], geoinfo["city"]) if old == new: log.debug("Geo IP info unchanged") continue log.debug( "Geo IP info changed. New country=%s, country_code=%s, city=%s", geoinfo["country"], geoinfo["country_code"], geoinfo["city"] ) db.update_geoinfo( socks5.id, country=geoinfo["country"], country_code=geoinfo["country_code"], city=geoinfo["city"] )
def add(self, host, port, username=None, password=None, dnsport=None, description=None, private=False): """Add a socks5 server. :param host: IP or a valid hostname of the socks5 server. Should be unique. :param port: Port of the socks5 server (int) :param username: Username of the socks5 server (optional) :param password: Password for the socks5 server user (optional). Password will be stored in plaintext! :param dnsport: Port to forward dns requests (optional) :param description: Description to store with the socks5 server (optional) :param private: IP type, private server (optional) :return: A dictionary containing the provided information, the generated id, the determined country, country code, and city. :rtype: dict :raises: Socks5CreationError :example: >>> from socks5man.manager import Manager >>> Manager().add("example.com", 8456) { 'username': None, 'city': u'Norwell', 'host': 'example.com', 'country_code': u'US', 'country': u'United States', 'password': None, 'port': 8456, 'id': 1 } .. note:: It is only possible to provide both a username and a password. Hostname/IP should be unique. If a socks5 exists with the provided hostname/IP, a Socks5CreationError will be thrown. """ if (not username and password) or (not password and username): raise Socks5CreationError( "Either no password and no password or both a password and a" "username should be provided on socks5 creation. It is not " "possible to provide only a username or password") valid_entry = validify_host_port(host, port) if not valid_entry: raise Socks5CreationError( "Invalid host or port used. Invalid IP, non-existing hostname" ", or an invalid port specified. Host: %s, port: %s" % (host, port)) existing = db.view_socks5(host=host, port=port) if existing: raise Socks5CreationError( "Socks5 host and port combination: '%s:%s' already exists." " Socks5 ID is: %s" % (host, port, existing.id)) entry = Dictionary( host=host, port=port, username=username, password=password, ) entry.update(GeoInfo.ipv4info(valid_entry.ip)) socksid = db.add_socks5( entry.host, entry.port, entry.country, entry.country_code, city=entry.city, username=entry.username, password=entry.password, dnsport=dnsport, description=description, private=private, ) entry["id"] = socksid return entry
def bulk_add(self, socks5_dict_list, description=None): """Bulk add multiple socks5 server. No duplicate checking is done. :param socks5_dict_list: A list of dictionaries that at a minimum contain the keys and valid values for 'host' and 'port'. :param description: A description to be added to all provided servers :returns: The amount of socks5 server that were successfully added :rtype: int :raises: Socks5CreationError :example: >>> from socks5man.manager import Manager >>> Manager().bulk_add([{"host": "example.com", "port": 1234}, {"host": "example.org", "port": 1234}]) 2 .. note:: It is only possible to provide both a username and a password for a server. Hostname/IP should be unique. Socks5 servers with invalid hostnames or missing fields will be skipped. Socks5CreationError is raised if no valid servers are in the list. """ new = [] for entry in socks5_dict_list: if "host" not in entry or "port" not in entry: continue password = entry.get("password") username = entry.get("username") if (not username and password) or (not password and username): log.warning( "Either no password and no password or both a password " "and a username should be provided on socks5 creation. It " "is not possible to provide only a username or password") continue valid_entry = validify_host_port(entry["host"], entry["port"]) if not valid_entry: log.error("Invalid host or port provided") continue if description: entry["description"] = description new_entry = { "host": entry["host"], "port": valid_entry.port, "country": None, "country_code": None, "city": None, "username": username, "password": password, "operational": False, "dnsport": entry.get("dnsport"), "description": entry.get("description"), "private": entry.get("private"), } new_entry.update(GeoInfo.ipv4info(valid_entry.ip)) new.append(new_entry) if not new: raise Socks5CreationError("No socks5 servers to add provided") db.bulk_add_socks5(new) return len(new)