Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import logging 

2from typing import Tuple, Union 

3from django.conf import settings 

4import requests 

5import socket 

6from django.http.request import HttpRequest 

7from ipware import get_client_ip # type: ignore 

8from rest_framework.request import Request 

9 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14def get_ip(request: Union[HttpRequest, Request]) -> str: 

15 """ 

16 Returns best-guess IP for given request. 

17 Uses ipware library get_client_ip. 

18 If you need to know is IP routable or not, use ipware get_client_ip directly. 

19 See ipware documentation for more info. 

20 

21 Note: Why such a simple function wrapper? I'm generally against wrappers like this, 

22 but in this case made an exceptions: I used to use ipware get_real_ip() everywhere before 

23 it was deprecated and had quite big update process to change all code to use ipware get_client_ip. 

24 I want to avoid such process again so added this wrapper. 

25 

26 :param request: Djangos HttpRequest or DRF Request 

27 :return: IP-address or None 

28 """ 

29 return get_client_ip(request)[0] 

30 

31 

32def get_geo_ip(ip: str, exceptions: bool = False, timeout: int = 10) -> dict: 

33 """ 

34 Returns geo IP info or empty dict if geoip query fails at http://ipstack.com. 

35 requires settings.IPSTACK_TOKEN set as valid access token to the API. 

36 

37 Example replies: 

38 

39 {'country_name': 'United States', 'country_code': 'US', 'region_code': 'TX', 'region_name': 'Texas', 

40 'ip': '76.184.236.184', 'latitude': 33.1507, 'time_zone': 'America/Chicago', 'metro_code': 623, 'city': 

41 'Frisco', 'longitude': -96.8236, 'zip_code': '75033'} 

42 

43 {'latitude': 60.1641, 'country_name': 'Finland', 'zip_code': '02920', 'region_name': 'Uusimaa', 'city': 

44 'Espoo', 'metro_code': 0, 'ip': '194.100.27.41', 'time_zone': 'Europe/Helsinki', 'country_code': 'FI', 

45 'longitude': 24.7136, 'region_code': '18'} 

46 

47 :param ip: str 

48 :param exceptions: if True raises Exception on failure 

49 :param timeout: timeout in seconds 

50 :return: dict 

51 """ 

52 try: 

53 res = requests.get( 

54 "http://api.ipstack.com/{}?access_key={}&format=1".format(ip, settings.IPSTACK_TOKEN), timeout=timeout 

55 ) 

56 if res.status_code != 200: 56 ↛ 57line 56 didn't jump to line 57, because the condition on line 56 was never true

57 if exceptions: 

58 raise Exception("api.ipstack.com HTTP {}".format(res.status_code)) 

59 return {} 

60 return res.json() 

61 except Exception as e: 

62 msg = "geoip({}) failed: {}".format(ip, e) 

63 logger.error(msg) 

64 if exceptions: 

65 raise 

66 return {} 

67 

68 

69def get_ip_info(ip: str, exceptions: bool = False, timeout: int = 10) -> Tuple[str, str, str]: 

70 """ 

71 Returns (ip, country_code, host) tuple of the IP address. 

72 :param ip: IP address 

73 :param exceptions: Raise Exception or not 

74 :param timeout: Timeout in seconds. Note that timeout only affects geo IP part, not getting host name. 

75 :return: (ip, country_code, host) 

76 """ 

77 if not ip: # localhost 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true

78 return "", "", "" 

79 host = "" 

80 country_code = get_geo_ip(ip, exceptions=exceptions, timeout=timeout).get("country_code", "") 

81 try: 

82 res = socket.gethostbyaddr(ip) 

83 host = res[0][:255] if ip else "" 

84 except Exception as e: 

85 msg = "socket.gethostbyaddr({}) failed: {}".format(ip, e) 

86 logger.error(msg) 

87 if exceptions: 

88 raise e 

89 return ip, country_code, host