ajaxmap.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-"
  3. """
  4. This file is part of the UFONet project, https://ufonet.03c8.net
  5. Copyright (c) 2013/2020 | psy <epsylon@riseup.net>
  6. You should have received a copy of the GNU General Public License along
  7. with UFONet; if not, write to the Free Software Foundation, Inc., 51
  8. Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  9. """
  10. import socket, threading, re, os, time, base64, traceback
  11. import webbrowser, subprocess, json, sys, shlex
  12. import urllib.request, urllib.error
  13. from urllib.parse import urlparse as urlparse
  14. from .main import UFONet
  15. try:
  16. import pygeoip
  17. except:
  18. print("\n[Error] [AI] Cannot import lib: pygeoip. \n\n To install it try:\n\n $ 'sudo apt-get install python3-geoip' or 'pip3 install pygeoip'\n")
  19. sys.exit(2)
  20. class AjaxMap(object):
  21. def __init__(self):
  22. self.geo_db_mirror1 = 'https://turina.space/bordercheck/maps.tar.gz' # Turina Server
  23. self._geoip=None
  24. self._geoasn=None
  25. self._geoipstatus='nomap'
  26. self._err=''
  27. ufonet = UFONet()
  28. ufonet.create_options()
  29. try:
  30. self.zombies = ufonet.extract_zombies()
  31. aliens_army = ufonet.extract_aliens()
  32. droids_army = ufonet.extract_droids()
  33. ucavs_army = ufonet.extract_ucavs()
  34. rpcs_army = ufonet.extract_rpcs()
  35. self.zombies.extend(aliens_army)
  36. self.zombies.extend(droids_army)
  37. self.zombies.extend(ucavs_army)
  38. self.zombies.extend(rpcs_army)
  39. except:
  40. return
  41. def get_err(self):
  42. return self._err
  43. # check for geoip data status
  44. # basic lock file mechanism to avoid multiple downloads
  45. def get_status(self):
  46. if os.path.exists('maps.downloading'):
  47. if not os.path.exists('maps.downloadmsg'):
  48. f=open("maps.downloadmsg","wb")
  49. f.write("".encode('utf-8'))
  50. f.close()
  51. print("[Info] [AI] [Control] GeoIP data download started! -> [OK!]\n")
  52. self._geoipstatus='downloading'
  53. elif os.path.isdir('maps'):
  54. if self._geoip == None :
  55. self._geoip = pygeoip.GeoIP('maps/GeoLiteCity.dat')
  56. if self._geoasn == None :
  57. self._geoasn = pygeoip.GeoIP('maps/GeoIPASNum.dat')
  58. if os.path.exists("maps.downloadmsg") :
  59. os.remove("maps.downloadmsg")
  60. self._geoipstatus='ok'
  61. return self._geoipstatus
  62. def retrieve(self,url,name):
  63. try:
  64. handle = urllib.request.urlopen(url)
  65. CHUNK = 16384
  66. with open(name,'wb') as fp:
  67. while True:
  68. chunk = handle.read(CHUNK)
  69. if not chunk:
  70. break
  71. fp.write(chunk)
  72. except:
  73. traceback.print_exc()
  74. def download_maps(self):
  75. # generate geolocation values on a map
  76. if self.get_status() != 'nomap':
  77. return self._geoipstatus == 'ok'
  78. if os.path.exists("maps.downloadmsg"):
  79. os.remove("maps.downloadmsg")
  80. f=open("maps.downloading",'w')
  81. f.write("download started<script>$'('#ufomsg').load('/js/ajax.js?fetchmap=')")
  82. f.close()
  83. self._geoipstatus="downloading"
  84. try: # mirror 1
  85. print("\n[Info] [AI] Fetching maps from 'Turina Server':", self.geo_db_mirror1 + "\n")
  86. response = self.retrieve(self.geo_db_mirror1, 'maps.tar.gz')
  87. except:
  88. print(("[Error] [AI] Something wrong fetching maps from remote servers! -> [Aborting!]"), "\n")
  89. traceback.print_exc()
  90. return False #sys.exit(2)
  91. subprocess.call(shlex.split('tar zxfv maps.tar.gz'))
  92. print("\n[Info] [AI] [Control] GeoIP maps and databases -> [OK!]\n")
  93. # set pygeoip data sources
  94. self._geoip = pygeoip.GeoIP('maps/GeoLiteCity.dat')
  95. self._geoasn = pygeoip.GeoIP('maps/GeoIPASNum.dat')
  96. self._geoipstatus='ok'
  97. os.remove('maps.tar.gz')
  98. os.remove('maps.downloading')
  99. return True
  100. # fetches geoip data for specified zombie
  101. def geo_ip(self, zombie):
  102. # check for status, downloading is done by ajax() method
  103. if self.get_status() != 'ok':
  104. if self._geoipstatus =='downloading':
  105. print("\n[Info] [AI] [Control] GeoIP maps and databases -> [Downloading!]\n")
  106. self._err= "ufomsg('[Info] [AI] Downloading maps... -> [Waiting!]')"
  107. elif not os.path.exists('maps/GeoIPASNum.dat') or not os.path.exists('maps/GeoLiteCity.dat'):
  108. print("\n[Info] [AI] GeoIP maps and databases -> [Starting!]\n")
  109. self._err= "ufomsg('[Info] [AI] Map download starting')\n$('#ufomsg').load('/js/ajax.js?fetchgeoip=')"
  110. else:
  111. print("\n[Error] [AI] GeoIP maps and databases: FAILED! -> [Discarding!]\n")
  112. self._err= "ufomsg('<font color='red'>[Info] [AI]</font> Maps: unknown error -> [Discarding!]')"
  113. return None
  114. if re.match(r'^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$', zombie) or re.match(r'^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$', zombie) or re.match(r'^192.168\.\d{1,3}\.\d{1,3}$', zombie) or re.match(r'^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$', zombie) or re.match('localhost', zombie):
  115. self._err= "ufomsg('<font color='red'>[Info] [AI] [Control]</font> Maps: invalid ip data -> [Discarding!]')"
  116. return None
  117. # create geoip data skeleton
  118. geo_zombie={}
  119. geo_zombie['qq']=zombie
  120. url = urlparse(zombie)
  121. geo_zombie['city'] = '-'
  122. geo_zombie['country'] = '-'
  123. geo_zombie['country_code'] = '-'
  124. geo_zombie['longitude'] = '-'
  125. geo_zombie['latitude'] = '-'
  126. geo_zombie['ip'] = '-'
  127. geo_zombie['host_name'] = '-'
  128. geo_zombie['asn'] = '-'
  129. geo_zombie['latitude'] = '-'
  130. # retrieve and allocate geoip data
  131. try:
  132. ip = socket.gethostbyname(url.netloc)
  133. except:
  134. try:
  135. import dns.resolver
  136. r = dns.resolver.Resolver()
  137. r.nameservers = ['8.8.8.8', '8.8.4.4'] # google DNS resolvers
  138. a = r.query(url.netloc, "A") # A record
  139. for rd in a:
  140. ip = str(rd)
  141. except:
  142. self._err= "ufomsg('<font color='yellow'>[Error] [AI]</font> GeoIP: hostbyname failed for "+str(url.netloc)+"...')"
  143. return None
  144. if ip:
  145. if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",ip):
  146. geo_zombie['ip'] = ip
  147. try:
  148. record = self._geoip.record_by_addr(ip)
  149. except:
  150. self._err= "ufomsg('<font color='yellow'>[Error] [AI] </font> GeoIP: lookup failed for "+ip+", page reload required...')"
  151. return None
  152. try:
  153. asn = self._geoasn.org_by_addr(ip)
  154. if asn is not None:
  155. geo_zombie['asn'] = asn
  156. except:
  157. geo_zombie['asn'] = 'No ASN provided'
  158. try:
  159. geo_zombie['host_name'] = socket.gethostbyaddr(ip)[0]
  160. except:
  161. geo_zombie['host_name'] = 'No hostname'
  162. try:
  163. longitude = str(float(record['longitude']))
  164. geo_zombie['longitude'] = longitude
  165. latitude = str(float(record['latitude']))
  166. geo_zombie['latitude'] = latitude
  167. except:
  168. pass
  169. try:
  170. geo_zombie['country'] = record["country_name"]
  171. geo_zombie['country_code'] = record["country_code"].lower()
  172. if record['city'] is not None:
  173. geo_zombie['city'] = record["city"]
  174. except:
  175. pass
  176. else:
  177. geo_zombie = None
  178. return geo_zombie
  179. # generates javascript for adding a new zombie with geoip data
  180. def get_js(self,z):
  181. ret = ""
  182. gz = self.geo_ip(z)
  183. if gz is not None and gz['latitude']!= '-':
  184. ret = "Zombies.add('"+str(z)+"',Array(new L.LatLng("+str(gz['latitude'])+","+str(gz['longitude'])+"),'"+str(gz['city'])+"','"+str(gz['country'])+"','"+str(gz['country_code'])+"','"+str(gz['asn'])+"','"+str(gz['ip'])+"','"+str(gz['host_name'])+"'))\n"
  185. else:
  186. ret += "dead_zombies.push('"+z+"')\n"
  187. ret += "last_zombie = '"+z+"'\n"
  188. return ret
  189. # fetches next zombie from list (using all types of zombies)
  190. def get_next_zombie(self,name):
  191. if name in self.zombies:
  192. for z in self.zombies:
  193. if name == None:
  194. return z
  195. if z == name:
  196. name = None
  197. return None
  198. else:
  199. return self.zombies[0]
  200. # ajax controller
  201. def ajax(self,pGet={}):
  202. if 'fetchgeoip' in list(pGet.keys()):
  203. if self.get_status() == "nomap":
  204. self.download_maps()
  205. return "[Info] [AI] [Control] Geoip data download! -> [OK!]<br/>"
  206. if 'stats' in list(pGet.keys()):
  207. stat='<script>$(".ufo_stat_div").show()</script>'
  208. if os.path.exists('/tmp/ufonet.html'):
  209. for x in open(r'/tmp/ufonet.html').readlines():
  210. stat = stat + x
  211. else:
  212. stat="<i>[Info] [AI] [Control] Generating statistics... -> [Waiting!]</i>"
  213. return stat+"</div>"
  214. if self.get_status() != "ok":
  215. dljs=""
  216. if self.get_status() == "nomap":
  217. dljs+="$('#ufomsg').load('/js/ajax.js?fetchgeoip=')\n"
  218. if 'doll' in list(pGet.keys()):
  219. dljs+="$('#ufomsg').load('/js/ajax.js?fetchdoll="+pGet['doll']+"')\n"
  220. dljs+="doll=new Doll('"+pGet["doll"]+"')\n"
  221. return "[Info] [AI] GeoIP data download in progress...<br><i>See console for errors</i>+<script>"+dljs+"</script>"
  222. if 'zombie' in list(pGet.keys()):
  223. zn=base64.b64decode(pGet['zombie']).decode('utf-8')
  224. nzn=self.get_next_zombie(zn)
  225. if nzn is not None:
  226. zombie=self.get_js(nzn)
  227. return """ <script>
  228. """+str(zombie)+"""
  229. ufomsg('[Info] [AI] [Control] Adding zombie: """+str(nzn)+"""...')
  230. </script>"""
  231. else:
  232. return "<script>zdone=true\nufomsg('[Info] [AI] [Control] All zombies deployed! -> [OK!]')\n </script>\n"
  233. if 'fetchdoll' in list(pGet.keys()):
  234. tn=pGet['fetchdoll']
  235. target = self.geo_ip(tn)
  236. if target is None:
  237. return "doll waiting for geoip data !"
  238. return """ doll up !<script>
  239. doll.setData(Array(new L.LatLng("""+str(target['latitude'])+","+str(target['longitude'])+"),'"+target['city']+"','"+target['country']+"','"+target['country_code']+"','"+target['asn']+"','"+target['ip']+"','"+target['host_name']+"'))\nufomsg('[Info] Adding target: """+tn+"""...')\ndoll.show() </script>"""
  240. if 'doll' in list(pGet.keys()):
  241. tn=pGet['doll']
  242. return """<script>
  243. doll=new Doll('"""+tn+"""')\n</script>"""
  244. return "\n"