em_server.rb 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. #!/usr/bin/env ruby
  2. require 'eventmachine'
  3. require 'digest/md5'
  4. require 'fiber'
  5. require 'geoip'
  6. require 'socket'
  7. require 'timeout'
  8. # .. allowed commands .. ROLES = CAPABILITIES
  9. # normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config.
  10. $version = "0.4em_cooldns_classes_for_caching"
  11. $debug = 0
  12. $role_commands = Hash[
  13. #noinspection RubyStringKeysInHashInspection
  14. 'everyone' => %w(PING WHO C PART),
  15. 'broadcast_admin' => %w(BC_ID BC BC_ENDCOUNT),
  16. 'broadcast' => %w(REQ_BC BC_RE),
  17. 'specbot_admin' => %w(REQ_ASSIGN REQ_UNASSIGN REQ_PING REQ_ASSIGNMENTS),
  18. 'specbot' => %w(ASSIGN_RE UNASSIGN_RE PING_RE ASSIGNMENTS_RE REQ_DNS),
  19. ]
  20. $default_role = "everyone"
  21. # which role is talking to which role?
  22. # effectively it says: this (local) command is sent to that (remote) topic .. that certain topic is read by that user with that role.
  23. $role_dialogs = Hash[
  24. #noinspection RubyStringKeysInHashInspection
  25. 'everyone' => %w(everyone),
  26. 'broadcast_admin' => %w(broadcast),
  27. 'broadcast' => %w(broadcast_admin),
  28. 'specbot_admin' => %w(specbot),
  29. 'specbot' => %w(specbot_admin),
  30. ]
  31. $user_roles = Hash[
  32. #noinspection RubyStringKeysInHashInspection
  33. 'paul_dev_eggdrop' => %w(everyone broadcast),
  34. 'paul_eggdrop' => %w(everyone broadcast),
  35. 'paul_dev_specbot' => %w(everyone broadcast specbot),
  36. 'paul_specbot' => %w(everyone broadcast specbot),
  37. 'qw.nu' => %w(everyone broadcast),
  38. 'qw.nu_poster' => %w(everyone broadcast),
  39. 'mihawk_dev_specbot' => %w(everyone broadcast specbot),
  40. 'mihawk_specbot' => %w(everyone broadcast specbot),
  41. 'armitage_specbot' => %w(everyone broadcast specbot),
  42. ]
  43. $qw_list = Array.new
  44. $qw_list = [
  45. "89.149.194.72:27700",
  46. "84.200.47.113:30000",
  47. "89.149.194.72:27500",
  48. "84.200.47.113:28502",
  49. "89.149.194.72:30000",
  50. "89.149.194.72:27600",
  51. "93.186.192.16:28502",
  52. "84.200.47.113:28501",
  53. "93.186.192.16:28501",
  54. "188.40.130.10:27502",
  55. "93.186.192.16:30000",
  56. "188.40.130.10:27599",
  57. "188.40.130.10:27503",
  58. "194.109.69.75:28000",
  59. "194.109.69.75:27500",
  60. "194.109.69.75:27501",
  61. "194.109.69.76:28504",
  62. "188.165.243.56:30000",
  63. "188.40.130.10:27501",
  64. "194.109.69.76:28502",
  65. "194.109.69.76:28501",
  66. "194.109.69.76:28503",
  67. "62.24.64.11:27501",
  68. "62.24.64.11:44444",
  69. "188.40.103.81:27600",
  70. "62.24.64.11:30000",
  71. "194.109.69.75:27502",
  72. "91.121.69.201:30000",
  73. "37.59.63.97:28504",
  74. "188.40.130.10:27500",
  75. "188.165.243.56:28009",
  76. "37.59.63.97:28501",
  77. "62.24.64.11:27500",
  78. "37.59.63.97:28503",
  79. "194.109.69.76:30000",
  80. "188.165.243.56:28008",
  81. "188.165.243.56:28006",
  82. "188.165.243.56:28001",
  83. "188.165.243.56:28002",
  84. "93.186.192.16:27500",
  85. "91.121.69.201:27600",
  86. "37.59.63.97:28502",
  87. "91.121.223.163:28001",
  88. "188.165.243.56:28003",
  89. "109.74.195.224:30000",
  90. "178.79.183.178:27500",
  91. "77.74.194.189:27501",
  92. "91.121.69.201:27502",
  93. "188.165.243.56:28007",
  94. "109.74.195.224:27500",
  95. "213.5.176.135:27502",
  96. "77.74.194.189:27504",
  97. "178.79.172.251:27600",
  98. "77.74.194.189:27502",
  99. "77.74.194.189:27503",
  100. "213.5.176.135:27500",
  101. "109.74.195.224:27501",
  102. "89.104.194.146:27504",
  103. "89.104.194.146:27503",
  104. "89.104.194.146:27508",
  105. "213.5.176.136:27510",
  106. "213.5.176.135:27501",
  107. "213.5.176.135:27503",
  108. "77.74.194.189:30000",
  109. "89.149.194.72:27800",
  110. "89.104.194.146:27502",
  111. "89.104.194.146:27666",
  112. "89.104.194.146:27507",
  113. "89.104.194.146:27509",
  114. "89.104.194.146:30000",
  115. "188.165.243.56:28004",
  116. "89.104.194.146:27500",
  117. "89.104.194.146:27501",
  118. "194.109.69.76:27500",
  119. "89.104.194.146:27506",
  120. "94.236.92.49:27501",
  121. "91.121.69.201:27501",
  122. "213.239.214.134:27500",
  123. "89.104.194.146:27510",
  124. "95.131.48.86:27502",
  125. "94.236.92.49:27500",
  126. "94.236.92.49:30000",
  127. "95.131.48.86:27504",
  128. "78.137.161.109:27501",
  129. "188.165.243.56:28005",
  130. "178.217.185.104:27500",
  131. "178.217.185.104:27600",
  132. "178.217.185.104:30000",
  133. "87.102.202.23:27502",
  134. "78.137.161.109:27500",
  135. "89.104.194.146:27505",
  136. "87.102.202.23:27505",
  137. "178.217.185.104:27501",
  138. "91.121.69.201:27500",
  139. "82.141.152.3:27501",
  140. "82.141.152.3:27500",
  141. "83.179.23.16:28002",
  142. "109.74.7.60:27500",
  143. "212.62.234.153:27502",
  144. "83.179.23.16:28003",
  145. "195.54.182.34:27500",
  146. "83.179.23.16:28005",
  147. "212.62.234.153:27503",
  148. "95.143.243.24:27600",
  149. "83.179.23.16:28001",
  150. "212.62.234.153:27504",
  151. "78.137.161.109:27502",
  152. "83.179.23.16:28004",
  153. "212.62.234.153:27501",
  154. "95.143.243.24:27500",
  155. "109.228.137.161:28501",
  156. "95.143.243.24:27900",
  157. "83.226.149.218:27500",
  158. "83.226.149.218:28001",
  159. "83.226.149.218:28002",
  160. "83.226.149.218:28003",
  161. "193.1.40.166:27975",
  162. "87.237.112.11:30000",
  163. "217.30.184.104:27500",
  164. "78.108.53.19:27500",
  165. "78.108.53.19:27501",
  166. "195.54.142.7:28001",
  167. "193.1.40.166:27500",
  168. "87.237.112.11:27501",
  169. "195.54.142.7:30000",
  170. "195.54.142.7:28007",
  171. "217.119.36.79:30000",
  172. "95.131.48.86:27501",
  173. "195.54.142.7:28002",
  174. "195.54.142.7:28008",
  175. "80.101.105.103:27500",
  176. "87.237.112.11:27502",
  177. "217.119.36.79:27500",
  178. "81.170.128.75:28501",
  179. "217.119.36.79:28001",
  180. "193.1.40.167:27500",
  181. "217.119.36.79:28003",
  182. "217.119.36.79:28002",
  183. "81.170.128.75:30000",
  184. "81.170.128.75:28504",
  185. "195.54.142.7:28005",
  186. "195.54.142.7:28010",
  187. "81.170.128.75:28503",
  188. "93.81.254.63:27502",
  189. "212.42.38.88:27504",
  190. "81.170.128.75:28502",
  191. "93.81.254.63:27500",
  192. "81.170.128.75:28000",
  193. "212.42.38.88:27500",
  194. "82.203.213.117:28002",
  195. "212.42.38.88:30000",
  196. "93.81.254.63:30000",
  197. "82.203.213.117:28001",
  198. "212.42.38.88:27501",
  199. "212.42.38.88:27503",
  200. "93.81.254.63:27503",
  201. "93.81.254.63:27501",
  202. "82.203.213.117:30000",
  203. "95.131.48.86:27503",
  204. "83.252.244.76:27500",
  205. "212.42.38.88:27502",
  206. "195.54.142.7:28003",
  207. "95.31.4.132:30000",
  208. "83.252.244.76:27501",
  209. "93.81.254.63:30001",
  210. "195.54.142.7:28006",
  211. "82.203.213.117:28003",
  212. "95.143.243.24:27700",
  213. "84.234.185.215:27503",
  214. "84.234.185.215:27500",
  215. "84.234.185.215:27519",
  216. "84.234.185.215:27508",
  217. "84.234.185.215:27506",
  218. "84.234.185.215:27505",
  219. "195.54.142.7:28004",
  220. "84.234.185.215:27501",
  221. "195.54.142.7:28009",
  222. "212.109.128.148:27501",
  223. "84.234.185.215:27502",
  224. "31.209.7.104:28501",
  225. "194.79.85.66:27501",
  226. "194.79.85.66:30000",
  227. "95.84.164.245:27501",
  228. "194.79.85.66:27502",
  229. "83.222.112.157:30000",
  230. "212.109.128.148:27500",
  231. "94.100.6.66:27500",
  232. "129.241.205.153:28000",
  233. "84.234.185.215:27507",
  234. "129.241.205.153:27500",
  235. "95.84.164.245:27500",
  236. "69.31.82.226:27501",
  237. "69.31.82.226:30000",
  238. "130.240.207.177:30000",
  239. "69.31.82.226:28100",
  240. "69.31.82.226:27500",
  241. "69.31.82.226:28101",
  242. "69.31.82.226:28010",
  243. "69.31.82.226:30001",
  244. "69.31.82.226:28000",
  245. "69.31.82.226:28002",
  246. "93.186.192.16:28000",
  247. "195.222.130.83:27500",
  248. "84.200.47.113:28000",
  249. "130.85.56.131:27500",
  250. "108.174.51.73:28003",
  251. "96.8.113.36:27501",
  252. "96.8.113.36:27500",
  253. "68.100.130.114:27501",
  254. "65.31.69.75:27500",
  255. "108.174.51.73:28006",
  256. "65.31.69.75:27508",
  257. "108.174.51.73:30000",
  258. "67.81.59.41:27500",
  259. "108.174.51.73:28004",
  260. "217.18.138.23:27505",
  261. "108.174.51.73:28001",
  262. "68.100.130.114:27500",
  263. "174.49.198.60:27502",
  264. "174.49.198.60:27503",
  265. "209.239.113.236:27500",
  266. "174.49.198.60:27515",
  267. "217.119.36.79:28000",
  268. "108.174.51.73:28005",
  269. "74.91.115.244:28001",
  270. "174.49.198.60:27500",
  271. "74.91.115.244:28000",
  272. "199.101.96.48:27501",
  273. "96.8.113.36:30000",
  274. "174.101.185.59:27500",
  275. "67.228.69.114:27502",
  276. "199.101.96.48:27500",
  277. "108.174.51.73:28002",
  278. "199.192.229.74:28001",
  279. "74.86.171.201:27502",
  280. "67.228.69.114:27501",
  281. "74.86.171.201:27500",
  282. "67.228.69.114:26666",
  283. "199.192.228.71:27501",
  284. "199.192.229.74:28003",
  285. "74.86.171.201:27501",
  286. "199.192.229.74:30000",
  287. "199.192.229.74:28002",
  288. "74.91.115.244:28002",
  289. "199.192.228.71:27500",
  290. "199.101.96.48:30000",
  291. "199.101.96.48:28000",
  292. "199.192.229.74:28004",
  293. "199.192.228.71:30000",
  294. "65.31.238.37:27500",
  295. "31.209.7.104:28000",
  296. "208.131.136.169:27500",
  297. "66.212.17.78:27500",
  298. "200.177.229.11:27510",
  299. "200.177.229.11:27522",
  300. "200.177.229.11:27521",
  301. "200.177.229.11:27500",
  302. "190.96.80.67:27500",
  303. "200.177.229.11:27511",
  304. "200.177.229.11:27520",
  305. "190.96.80.67:27000",
  306. "202.37.129.186:27500",
  307. "202.37.129.186:27505",
  308. "202.37.129.186:27501",
  309. "219.88.241.81:27500",
  310. "202.172.99.2:28001",
  311. "202.172.99.2:28002",
  312. "202.172.99.2:28003",
  313. "202.172.99.2:27500",
  314. "210.50.4.11:27501",
  315. "202.172.99.2:27501",
  316. "210.50.4.11:27508",
  317. "210.50.4.11:27511",
  318. "210.50.4.11:27509",
  319. "210.50.4.11:27510",
  320. "122.99.118.2:28001",
  321. "210.50.4.11:27500",
  322. "210.50.4.11:27503",
  323. "210.50.4.11:27506",
  324. "210.50.4.11:27505",
  325. "210.50.4.11:27504",
  326. ]
  327. class GameServers
  328. attr_accessor :gameservers # now, @gameservers is accessible via GameServers.gameservers
  329. def initialize
  330. @gameservers = Hash.new
  331. @gameservers.default = {
  332. :reverse_dns => "",
  333. :hostname_dns => "",
  334. :cool_dns => "",
  335. :type => "",
  336. :serverinfos => "",
  337. :timestamp => 0,
  338. }
  339. @gameservers["blah:blah"]
  340. wat = Hash.new(@gameservers["blah:blah"])
  341. wat
  342. wat.store(:reverse_dns, "6")
  343. @gameservers.merge(wat)
  344. p @gameservers
  345. p @gameservers.default
  346. wat = Hash.new(@gameservers["blah:blah"])
  347. wat
  348. wat.store(:hostname_dns, "12")
  349. @gameservers.merge(wat)
  350. p @gameservers
  351. p @gameservers.default
  352. end
  353. def scanserver(iphostport, type="qw", force=false)
  354. put_log "scanserver drin"
  355. if iphostport =~ /^(\d+\.\d+\.\d+\.\d+):(\d{1,5})/
  356. iphost = $1
  357. ipport = $2
  358. if type == "qw"
  359. #p current
  360. # check if it already exists
  361. #if @gameservers["#{iphost}:#{ipport}"][:timestamp] > 0
  362. # if old general data, then freshly get general data...
  363. # if @gameservers["#{iphost}:#{ipport}"][:timestamp] + 60 * 60 * 20 < Time.now.utc.to_i || force == true
  364. # end # of old
  365. #else
  366. current = Hash.new
  367. serverinfos = qwstatus(iphost, ipport)
  368. current.store(:serverinfos, serverinfos)
  369. current.store(:reverse_dns, find_reverse_dname(iphost))
  370. current.store(:hostname_dns, qw_find_dname_by_serverinfos(serverinfos))
  371. current.store(:type, "qw")
  372. # set new timestamp
  373. current.store(:timestamp, Time.now.utc.to_i)
  374. @gameservers.store("#{iphost}:#{ipport}", current) # save it all.
  375. current.store(:cool_dns, find_cooldns(iphost, ipport))
  376. @gameservers.store("#{iphost}:#{ipport}", current) # save it all.
  377. #put_log "the saved one: #{@gameservers.fetch("#{iphost}:#{ipport}").fetch(:cool_dns)}"
  378. #puts "ALL"
  379. #p @gameservers
  380. #puts "DEFAULT of the hash"
  381. #p @gameservers.default
  382. #end
  383. end # of type qw
  384. end # of check form of iphostport parameter
  385. end # of scan()
  386. def scanserverlist(gs_array, type="qw")
  387. put_log "scanserverlist drin"
  388. gs_array.each do
  389. |gserver|
  390. scanserver(gserver, type)
  391. end
  392. put_log "End of scanning."
  393. put_log "foppa: #{@gameservers.fetch("89.104.194.146:27503")} "
  394. end # of scanserverlist()
  395. def get_cool_dns(iphost, ipport)
  396. put_log "get_cool_dns drin"
  397. @gameservers.fetch("#{iphost}:#{ipport}").fetch(:cool_dns)
  398. rescue
  399. scanserver("#{iphost}:#{ipport}", "qw")
  400. @gameservers.fetch("#{iphost}:#{ipport}").fetch(:cool_dns)
  401. end
  402. private # all following methods are private
  403. # returns serverinfo hash
  404. def qwstatus(iphost, ipport)
  405. put_log "qwstatus drin"
  406. udp_payload = [0xFF, 0xFF, 0xFF, 0xFF]
  407. udp_payload.concat(string2bytearray("status 23"))
  408. udp_payload.concat([0x0a]) # linefeed at the end
  409. udp_payload = udp_payload.pack("C*")
  410. #p udp_payload
  411. Timeout::timeout(2) do
  412. begin
  413. u2 = UDPSocket.new
  414. #put_log "#{iphost} #{ipport} #{udp_payload}"
  415. u2.send(udp_payload, 0, iphost, ipport)
  416. #put_log "sent"
  417. the_return = u2.recv(500)
  418. u2.close
  419. #put_log "muh #{the_return}"
  420. if the_return =~ /\W\W\W\Wn\\(.+)$/
  421. line = $1
  422. #put_log "line #{line}"
  423. matches = line.scan(/(.+?)\\(.+?)(\\|$)/)
  424. the_hash = Hash.new
  425. matches.each {
  426. |k, v, _|
  427. the_hash[k] = "#{v}"
  428. }
  429. return the_hash
  430. else
  431. return false
  432. end
  433. rescue Exception => e
  434. puts e.message
  435. end # of begin
  436. end # of Timeout
  437. end
  438. def find_cooldns_full(iphost, ipport)
  439. targetname = iphost
  440. # resolve it to a dns name (reverse lookup)
  441. if targetname == iphost # if it is still default
  442. put_log "Ip not resolved .. we try hostname dns finder"
  443. targetname = qw_find_dname_in_hostnames(iphost, ipport)
  444. end
  445. # resolve it to a dns name (reverse lookup)
  446. if targetname == iphost
  447. put_log "Still no resolve .. we try reverse dns lookup"
  448. targetname = find_reverse_dname(iphost)
  449. end
  450. return targetname
  451. end
  452. def find_cooldns(iphost, ipport)
  453. put_log "find_cooldns drin"
  454. current = @gameservers["#{iphost}:#{ipport}"] # only use this for reading...
  455. my_cooldns = iphost
  456. # we still haven't found a cool dns
  457. if (my_cooldns == iphost) && (not current[:hostname_dns].to_s.empty?)
  458. put_log "Try if #{current[:hostname_dns]} resolves to #{iphost}"
  459. begin
  460. ip_of_hostnamedns = Resolv.getaddress(current[:hostname_dns])
  461. if ip_of_hostnamedns && (ip_of_hostnamedns == iphost)
  462. # ok, we take it.
  463. put_log "Ok, we take #{current[:hostname_dns]} for #{iphost} here.."
  464. my_cooldns = current[:hostname_dns]
  465. else
  466. put_log "Found #{current[:hostname_dns]} but #{ip_of_hostnamedns} is not #{iphost}"
  467. end
  468. rescue Exception => e
  469. my_cooldns = iphost
  470. end
  471. end
  472. # we still haven't found a cool dns
  473. unless current[:reverse_dns].to_s.empty?
  474. if my_cooldns == iphost
  475. rdns = current[:reverse_dns]
  476. # if the resulting dns name...
  477. # .. is too long, use ip-address instead.
  478. # .. has too many dots, use ip-address instead.
  479. # .. has too many numbers, use ip-address instead.
  480. if rdns.length > 21 || rdns.split(".").size > 3 || rdns.scan(/\d/).size > 3
  481. put_log "cutting down host_name: #{rdns}, because:"
  482. put_log "length: #{rdns.length}"
  483. put_log "chunks count: #{rdns.split(".").size}"
  484. put_log "num count: #{rdns.scan(/\d/).size}"
  485. else
  486. my_cooldns = rdns
  487. end # of Resolv.getname
  488. end
  489. end
  490. put_log "COOOOOOOOOOOOLDNS: #{my_cooldns}"
  491. my_cooldns
  492. end
  493. def find_reverse_dname(iphost)
  494. put_log "find_reverse_dname drin"
  495. dname = Resolv.getname(iphost)
  496. ip_of_dname = Resolv.getaddress(dname)
  497. if ip_of_dname == iphost
  498. return dname
  499. end
  500. rescue
  501. iphost
  502. end
  503. def qw_find_dname_by_udp(iphost, ipport)
  504. targetname = iphost # set a default
  505. # get hostname from a qw status packet! perhaps there's a DNS name inside, which we can use!
  506. status = qwstatus(iphost, ipport)
  507. p status
  508. if status["hostname"] =~ /([\w\.-]{3,}\.\w{2,4})/
  509. hostnamedns = $1.downcase
  510. begin
  511. ip_of_hostnamedns = Resolv.getaddress(hostnamedns)
  512. if ip_of_hostnamedns && (ip_of_hostnamedns == iphost)
  513. # ok, we take it.
  514. put_log "Ok, we take #{hostnamedns} for #{iphost} here.."
  515. targetname = hostnamedns
  516. else
  517. put_log "Found #{hostnamedns} but #{ip_of_hostnamedns} is not #{iphost}"
  518. end
  519. rescue Exception => e
  520. targetname = iphost
  521. end
  522. end
  523. targetname
  524. end
  525. def qw_find_dname_by_serverinfos(serverinfos)
  526. hostnamedns = ""
  527. begin
  528. hostname = serverinfos.fetch("hostname")
  529. if hostname =~ /([\w\.-]{3,}\.\w{2,4})/
  530. hostnamedns = $1.downcase
  531. end
  532. return hostnamedns
  533. rescue
  534. return ""
  535. end
  536. end
  537. end # of Class GameServers!
  538. def string2bytearray(text)
  539. return_array = Array.new
  540. text.each_byte{
  541. |b|
  542. return_array.push(b)
  543. }
  544. return_array
  545. end
  546. def put_log(msg)
  547. puts "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}"
  548. end
  549. module CentralProtocolHandler
  550. @@connected_clients = Array.new
  551. @@broadcasts = Hash.new
  552. put_log "Server started"
  553. # default method that is being run on connection!
  554. def post_init # connection of someone starts here...
  555. @username = nil
  556. end
  557. ### getters
  558. def entered_username?
  559. !@username.nil? && !@username.empty? # then it's true
  560. end
  561. def username
  562. @username
  563. end
  564. def my_roles
  565. $user_roles[@username]
  566. end
  567. def my_cmds
  568. my_roles.collect {|v| $role_commands[v]}.flatten
  569. end
  570. # returns online users by default by searching through saved connections
  571. def ousers
  572. users = Array.new
  573. @@connected_clients.each {|c| users.push(c.username)}
  574. users
  575. end # of ousers
  576. ### checkers
  577. def allowed_cmd(inputmessage)
  578. my_cmds.each{|c|
  579. #print "A #{c} B #{inputmessage}\n"
  580. if inputmessage =~ /^#{c}/
  581. return true
  582. end
  583. }
  584. false
  585. end
  586. def online(user)
  587. if ousers.include? user
  588. true
  589. else
  590. false
  591. end
  592. end
  593. ### actions
  594. # searches through our hash of saved connections and writes them a messages.
  595. def write_user(input, username = nil)
  596. if username.nil? || username.empty?
  597. username = @username
  598. end
  599. if @@connected_clients.find { |c| c.username == username }
  600. connection = @@connected_clients.find { |c| c.username == username }
  601. put_log("to #{username}: #{input}")
  602. sometime = "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z")}"
  603. line = "#{sometime}: #{input}\n"
  604. connection.send_data line
  605. end
  606. end # of write_user
  607. # searches through roles and writes those users on their connections
  608. def write_role(role, input, *exempts)
  609. #find users with role
  610. users = Array.new
  611. $user_roles.each {|k, v|
  612. if v.include? role
  613. users.push(k)
  614. end
  615. }
  616. # find users that are online and inside Array "users"
  617. lala = Array.new
  618. users.each {|v|
  619. if ousers.include? v
  620. lala.push(v)
  621. end
  622. }
  623. # now write to them
  624. lala.each {|user|
  625. if not exempts.include? user
  626. write_user(input, user)
  627. end
  628. }
  629. end # of write_role()
  630. # what happens with what input?!
  631. def inputting(input)
  632. put_log("SYS #{@username} typed: #{input}")
  633. #write_user("SYS You typed: #{input}")
  634. if input =~ /^([A-Z_]+)/
  635. cmd = $1
  636. end
  637. # now we have the cmd .. or not ;)
  638. if input =~ /^[A-Z_]+ (.+)/
  639. payload = $1
  640. end
  641. # now we can use payload
  642. if allowed_cmd(cmd)
  643. ### typical system commands follow
  644. if cmd == "PING"
  645. write_user("PONG")
  646. elsif cmd == "C"
  647. if payload =~ /^(.+)$/
  648. write_role($default_role, "C #{@username}: #{$1}")
  649. else
  650. write_user("SYS Format is C <chattext>")
  651. end
  652. elsif cmd == "WHO"
  653. ousers.each {|ouser| write_user("WHO_RE #{ouser} ROLES: #{$user_roles[ouser].join(", ")}")}
  654. elsif cmd == "PART"
  655. write_user("SYS Goodbye '#{@username}'.")
  656. write_role($default_role, "PARTED User '#{@username}' leaves the party.", @username)
  657. return "bye"
  658. ###now for the good stuff, broadcasting
  659. elsif cmd == "REQ_BC"
  660. # help with format .. but send the raw payload
  661. if payload =~ /^(.+),(.+),(.+),'(.+)','(.+)'$/
  662. # n f s ni txt
  663. error = 0
  664. # $&
  665. # The string matched by the last successful pattern match in this scope, or nil if the last pattern match failed. (Mnemonic: like & in some editors.) This variable is r
  666. network = $1
  667. freqname = $2
  668. source = $3
  669. nickname = $4
  670. text = $5
  671. if not network =~ /^(QWalt)|(QDEV)/
  672. write_user("SYS Network name #{network} is unknown: QWalt or QDEV allowed.")
  673. error = 1
  674. end
  675. if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/
  676. write_user("SYS Frequency name is unknown #{freqname}")
  677. error = 1
  678. end
  679. if source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/
  680. else
  681. write_user("SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/) was: #{source}")
  682. error = 1
  683. end
  684. if not nickname =~ /^.+/
  685. write_user("SYS Nickname string is not in the form ^.+ #{nickname}")
  686. error = 1
  687. end
  688. if not text =~ /^.+/
  689. write_user("SYS Message string is not in the form ^.+ #{text}")
  690. error = 1
  691. end
  692. else # of payload has format
  693. write_user("SYS Command format is REQ_BC <network>,<frequency>,<source/channel>,'<nickname>','<message>'")
  694. error = 1
  695. end # of payload has format
  696. if error == 0
  697. # send REQ_BC to the corresponding role.
  698. bcid = Digest::MD5.hexdigest("%d %s %s %s %s %s" % [Time.now.utc.to_i, network, freqname, source, nickname, text])
  699. # so it only reaches the issuer of REQ_BC
  700. write_user("BC_ID #{bcid} for: #{network},#{freqname},#{source}")
  701. @@broadcasts[bcid] = @username
  702. # qw:// ip resolving
  703. if source =~ /^qw:\/\/(.+):(\d+)(.*)$/
  704. # ip address is 15 in length
  705. iphost = $1
  706. targetname = iphost # set a default
  707. ipport = $2
  708. rest = $3
  709. if iphost =~ /^\d+\.\d+\.\d+\.\d+/
  710. targetname = $gs.get_cool_dns(iphost, ipport)
  711. # find country code
  712. cc = GeoIP.new('GeoIP.dat').country(iphost)[3].to_s.downcase
  713. rest.prepend " #{cc}" unless cc.nil? || cc.empty? || targetname =~ /\.#{cc}$/
  714. end # if ip is x.x.x.x
  715. source = "qw://#{targetname}:#{ipport}#{rest}"
  716. end # of if source qw://x.x.x.x:portzzz
  717. # resolve
  718. finalmessage = "BC %s %s,%s,%s,'%s','%s'" % [bcid, network, freqname, source, nickname, text]
  719. write_role("broadcast", finalmessage, @username) # write to the role with the exempt of myself.
  720. end
  721. #end of REQ_BC here..
  722. elsif cmd == "REQ_DNS"
  723. # help with format .. but send the raw payload
  724. if payload =~ /^(\d+\.\d+\.\d+\.\d+):(\d{1,5})/
  725. iphost = $1
  726. ipport = $2
  727. targetname = $gs.get_cool_dns(iphost, ipport)
  728. write_user("DNS_RE #{iphost}:#{ipport} #{targetname}") # write back to asking user.
  729. else # of payload has format
  730. write_user("SYS Command format is REQ_DNS <serverip>:<serverport>")
  731. error = 1
  732. end # of if payload has format
  733. #end of REQ_DNS here..
  734. elsif cmd == "BC_RE"
  735. if payload =~ /^(.+) (.+)=(\d+),(.+)=(\d+)$/
  736. bcid = $1
  737. user_string = $2
  738. user_count = $3
  739. item_string = $4
  740. item_count = $5
  741. payload = "%s %s=%d,%s=%d" % [bcid, user_string, user_count, item_string, item_count]
  742. # according bcid it is possible to get the originating user.. so send only to his topic!
  743. if @@broadcasts[bcid]
  744. to_user = @@broadcasts[bcid]
  745. write_user("SYS Broadcast reply bcid: #{bcid} underway to #{to_user}.")
  746. write_user("#{cmd} #{payload}", to_user)
  747. else
  748. write_user("SYS Broadcast reply bcid: #{bcid} underway.")
  749. write_role("broadcast", "#{cmd} #{payload}", @username)
  750. end
  751. else # of bc_re format check
  752. put_log "SYS Format is BC_RE <bcid> <userstring>=<usercount>,<itemstring>=<itemcount>"
  753. end
  754. end
  755. else # of if allowed command
  756. if input.length > 15
  757. input = input.slice(0,14) + ".."
  758. end
  759. write_user("SYS Command '#{input}' not allowed, your commands are: #{my_cmds.join(", ")}.")
  760. end # of if allowed command
  761. end # of inputting!
  762. # default method that is being run on receiving data!
  763. def receive_data(data) # connection receives a line on the server end
  764. data = data.chomp unless data.chomp.nil?
  765. data.each_line do |line|
  766. unless line.empty?
  767. if entered_username? # oh, it's a known user
  768. # it's alive! kill ping timer, set a new one:
  769. if @ping_timer
  770. EventMachine.cancel_timer(@ping_timer)
  771. end
  772. @ping_timer = EventMachine.add_periodic_timer 180, proc { write_user("PING alive?") }
  773. # work on input
  774. inputter = inputting(line)
  775. if inputter == "bye"
  776. close_connection
  777. end
  778. else # of username known, then it seems to be the first connect
  779. # and this line is the user name coming in!
  780. username = line
  781. if online(username)
  782. put_log "SYS User '#{username}' tried to double connect. Say bye bye."
  783. send_data "SYS Hey '#{username}', only one connection allowed. Bye.\n"
  784. EventMachine.add_timer 1, proc {
  785. write_user("PING check forced", username)
  786. close_connection
  787. }
  788. return false
  789. end
  790. if $user_roles.any? {|k| k.include? username}
  791. @username = username
  792. @@connected_clients.push(self)
  793. @ping_timer = EventMachine.add_periodic_timer 90, proc { write_user("PING alive?") }
  794. # starting a pinger
  795. put_log("SYS '#{@username}' connected. Online now: #{ousers.join(", ")}")
  796. write_user("HELLO Hi user '#{@username}'! How are you? I'm '#{$version}'")
  797. write_user("ROLES #{my_roles.join(", ")}")
  798. write_user("COMMANDS #{my_cmds.join(", ")}")
  799. write_role($default_role, "JOINED User '#{@username}' just joined the party.", @username)
  800. else
  801. put_log "SYS User '#{username}' unknown to me. Saying bye."
  802. send_data "SYS User '#{username}' unknown to me. Bye.\n"
  803. # disconnect him
  804. close_connection
  805. end
  806. end # of username known
  807. end # of unless line.empty?
  808. end # of data.each_line
  809. end # of receive data
  810. # default method that is being run on disconnection!
  811. def unbind # a connection goes bye bye
  812. if @username
  813. @@connected_clients.delete(self)
  814. if @ping_timer
  815. EventMachine.cancel_timer(@ping_timer)
  816. end
  817. put_log "SYS '#{@username}' disconnected."
  818. write_role($default_role, "SYS User '#{@username}' disconnected.", @username)
  819. end
  820. put_log "SYS Online users now: #{ousers.join(", ")}"
  821. end
  822. end
  823. def main
  824. # #run: Note that this will block current thread.
  825. $gs = GameServers.new
  826. EventMachine.run do
  827. # initial scan
  828. EM.defer do
  829. $gs.scanserverlist($qw_list, "qw") # initial scan
  830. end
  831. # periodic scan, 20 hours
  832. EventMachine.add_periodic_timer( 60 * 60 * 20 ) {
  833. EM.defer do
  834. $gs.scanserverlist($qw_list, "qw") # initial scan
  835. end
  836. }
  837. EventMachine.start_server("0.0.0.0", 7337, CentralProtocolHandler)
  838. end
  839. end # of main
  840. main