Python1320

GLSock SRCDS Server Query

Nov 19th, 2011
261
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.87 KB | None | 0 0
  1. -- helps --
  2. require'glsock'
  3. local errd={}
  4. for k,v in pairs(_G) do
  5.     if k:find("GLSOCK_") then
  6.     errd[v]=k:match("GLSOCK_ERROR_(.+)") or k
  7.     end
  8. end
  9.  
  10. local mod   = math.fmod
  11. local floor = math.floor
  12.  
  13. local function rshift(x,n)
  14.     return floor((x%4294967296)/2^n)
  15. end
  16.  
  17. local function band(x,y)
  18.     local z,i,j = 0,1
  19.     for j = 0,31 do
  20.         if (mod(x,2)==1 and mod(y,2)==1) then
  21.             z = z + i
  22.         end
  23.         x = rshift(x,1)
  24.         y = rshift(y,1)
  25.         i = i*2
  26.     end
  27.     return z
  28. end
  29.  
  30.  
  31. local function err(id) local name=errd[id]
  32.     name=name and name or "UNKNOWN???"
  33. return name..'('..tostring(id)..')' end
  34.  
  35. local Err=function(...)
  36.     Msg"[SrvQuery]"print(...)
  37. end
  38.  
  39. local function TOIP(x)
  40.     return x and x[5] and tonumber(x[5]) and x[1].."."..x[2].."."..x[3].."."..x[4]..":"..x[5]
  41. end
  42. local c=string.char
  43. local A2S_SERVERQUERY_GETCHALLENGE=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)..c(0x57)
  44. local A2S_SERVERQUERY_GETCHALLENGE_HACK=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)..c(0x55)..c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)
  45. local A2S_INFO=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)..c(0x54).."Source Engine Query"..c(0x00)
  46. local A2A_PING=c(0x69)
  47. local A2S_PLAYER=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)..c(0x55)
  48. local A2S_RULES=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)..c(0x56)
  49.  
  50. local valid={
  51.     ["A2S_SERVERQUERY_GETCHALLENGE"]=A2S_SERVERQUERY_GETCHALLENGE,
  52.     ["A2S_INFO"]=A2S_INFO,
  53.     ["A2A_PING"]=A2A_PING,
  54.     ["A2S_PLAYER"]=A2S_PLAYER,
  55.     ["A2S_RULES"]=A2S_RULES
  56. }
  57.  
  58.  
  59.  
  60. ServerInfo = ServerInfo or {Queries=valid}
  61.  
  62. local socket
  63. if ServerInfo.socket then
  64.     print"Resetting ServerInfo"
  65.     ServerInfo.socket:Cancel()
  66.     socket=ServerInfo.socket
  67. end
  68. ------------------------------
  69. ServerInfo.Servers=ServerInfo.Servers or {}
  70. ------------------------------
  71. local srvinfo=ServerInfo.Servers
  72.     local function SrvData(ip,port)
  73.         if not port then
  74.             ip,port = ip:match("(%d+.%d+.%d+.%d+)%:?(%d*)")
  75.         end
  76.         port = tonumber(port) or 27015
  77.        
  78.         if not ip then error"invalid ip" end
  79.         if port<=0 or port>=65535 then error"invalid port" end
  80.        
  81.         srvinfo[port]=srvinfo[port] or {}
  82.         srvinfo[port][ip] = srvinfo[port][ip] or {ip,port,{},{}}
  83.         local data = srvinfo[port][ip]
  84.         return data
  85.     end
  86.     local function GetSrvData(ip,port)
  87.         if not port then
  88.             ip,port = ip:match("(%d+.%d+.%d+.%d+)%:?(%d*)")
  89.             if not ip then error"invalid ip" end
  90.         end
  91.         port = tonumber(port) or 27015
  92.         if port<=0 or port>=65535 then error"invalid port" end
  93.        
  94.         return srvinfo[port] and srvinfo[port][ip]
  95.     end
  96.     ServerInfo.Find=GetSrvData
  97. ------------------------------
  98.  
  99.  
  100. local function ProcessPing(buf,ip,port,data)
  101.     print("ProcessPing for",ip,port)
  102. end
  103.  
  104. local minusone=c(0xFF)..c(0xFF)..c(0xFF)..c(0xFF)
  105. local function GetSendBuffer(info,challenge)
  106.     info = info == A2S_SERVERQUERY_GETCHALLENGE and A2S_SERVERQUERY_GETCHALLENGE_HACK or info
  107.    
  108.    
  109.     local buffer = GLSockBuffer()
  110.     local sanity = buffer:Write(info)
  111.     assert((info):len()==sanity)
  112.    
  113.     if info==A2S_SERVERQUERY_GETCHALLENGE_HACK then
  114.         return buffer
  115.     end
  116.    
  117.     if ( info==A2S_RULES or info==A2S_PLAYER ) then
  118.         print("\tAppending challenge ("..challenge..") to SendBuffer")
  119.         if challenge then
  120.             buffer:WriteLong(challenge)
  121.         else
  122.             buffer:Write(minusone)
  123.         end
  124.     end
  125.     return buffer
  126. end
  127.  
  128. local function ProcessChallenge(buf,ip,port,data)
  129.     print("ProcessChallenge for",ip,port)
  130.     local _,challenge=buf:ReadLong()
  131.     print("\t","Challenge="..challenge)
  132.     if data.challenge then
  133.         print("\t","Old Challenge="..data.challenge)
  134.     end
  135.     data.challenge = challenge
  136. end
  137.  
  138.  
  139. local function ProcessInfo(buf,ip,port,data)
  140.  
  141.     print("ProcessInfo for",ip,port)
  142.     local _,version = buf:ReadByte( )
  143.     local _,hostname = buf:ReadString( )
  144.     local _,map = buf:ReadString( )
  145.     local _,gamedirectory = buf:ReadString( )
  146.     local _,gamedescription = buf:ReadString( )
  147.     local _,shortappid = buf:ReadShort( )
  148.     local _,numplayers = buf:ReadByte( )
  149.     local _,maxplayers = buf:ReadByte( )
  150.     local _,numofbots = buf:ReadByte( )
  151.     local _,dedicated = buf:ReadByte( )
  152.     local _,os = buf:ReadByte( )
  153.     local _,password = buf:ReadByte( )
  154.     local _,secure = buf:ReadByte( )
  155.     local _,gameversion = buf:ReadString( )
  156.     local _,extra = buf:ReadByte( )
  157.    
  158.     local _,gameport = band(extra,0x80)==0x80
  159.         if gameport then _,gameport = buf:ReadShort( )  end
  160.    
  161.     local steamid = band(extra,0x10)==0x10
  162.         if steamid then steamid = {select(2,buf:ReadLong( )),select(2,buf:ReadLong( ))}  end
  163.    
  164.     local spectport = band(extra,0x40)==0x40
  165.         if spectport then _,spectport = buf:ReadShort( )  end
  166.    
  167.     local spectname = band(extra,0x40)==0x40
  168.         if spectname then _,spectname = buf:ReadString( )  end
  169.    
  170.     local gametag = band(extra,0x20)==0x20
  171.         if gametag then _,gametag = buf:ReadString( )  end
  172.    
  173.     local gameid = band(extra,0x01)==0x01
  174.         if gameid then gameid = {select(2,buf:ReadLong( )),select(2,buf:ReadLong( ))}  end
  175.    
  176.     local PSSQ_INFO_REPLY = {
  177.         ["version"] = version,
  178.         ["hostname"] = hostname,
  179.         ["map"] = map,
  180.         ["gamedirectory"] = gamedirectory,
  181.         ["gamedescription"] = gamedescription,
  182.         ["shortappid"] = shortappid,
  183.         ["numplayers"] = numplayers,
  184.         ["maxplayers"] = maxplayers,
  185.         ["numofbots"] = numofbots,
  186.         ["dedicated"] = c(dedicated),
  187.         ["os"] = c(os),
  188.         ["password"] = password,
  189.         ["secure"] = secure,
  190.         ["gameversion"] = gameversion,
  191.        
  192.         ["extra"] = extra,     
  193.             ["gameport"] = gameport,
  194.             ["steamid"] = steamid,
  195.             ["spectport"] = spectport,
  196.             ["spectname"] = spectname,
  197.             ["gametag"] = gametag,
  198.             ["gameid"] = gameid,
  199.     }
  200.     Msg"PSSQ_INFO_REPLY = "
  201.     PrintTable(PSSQ_INFO_REPLY)
  202. end
  203.  
  204. local function ProcessPlayers(buf,ip,port,data)
  205.     print("ProcessPlayers for",ip,port)
  206.     local _,NumPlayers=buf:ReadShort()
  207.     print("\tNumPlayers="..NumPlayers)
  208.     local i=1
  209.     local playerstbl={}
  210.     while i<=NumPlayers do
  211.         local _,id=buf:ReadByte()
  212.         local _,name=buf:ReadString()
  213.         local _,kills=buf:ReadLong()
  214.         local _,playtime=buf:ReadFloat()
  215.        
  216.         if _<1 then Err"players parsing failed, too little data?" break end
  217.         playerstbl[i]={
  218.             id=id,
  219.             name=name,
  220.             kills=kills,
  221.             playtime=playtime
  222.         }
  223.         i=i+1
  224.     end
  225.     print("\tplayers tbl="..table.Count(playerstbl))
  226.     Msg"players = "
  227.         PrintTable(playerstbl)
  228. end
  229.  
  230. local function ProcessRules(buf,ip,port,data)
  231.     print("ProcessRules for",ip,port)
  232.     local _,rules=buf:ReadShort()
  233.     print("\tRules="..rules)
  234.     local i=1
  235.     local rulestbl={}
  236.     while i<=rules do
  237.         local _,k=buf:ReadString()
  238.         local _,v=buf:ReadString()
  239.         if _<1 then Err"rules parsing failed, too little data?" break end
  240.         rulestbl[k]=v
  241.         i=i+1
  242.     end
  243.     print("\tRules tbl="..table.Count(rulestbl))
  244.     MsgN"Rules: "
  245.     for k,v in pairs(rulestbl) do
  246.         if k:find("toolmode",1,true) then continue end
  247.         MsgN(k.." = "..v)
  248.     end
  249. end
  250.  
  251. local PROCESS_RECEIVED_STUFF=PROCESS_RECEIVED_STUFF
  252.  
  253. local function JumpTo(b5,...)
  254.  
  255.     if b5==0x41 then
  256.         return ProcessChallenge(...) or true
  257.     elseif b5==0x49 then
  258.         return ProcessInfo(...) or true
  259.     elseif b5==0x44 then
  260.         return ProcessPlayers(...) or true
  261.     elseif b5==0x45 then
  262.         return ProcessRules(...) or true
  263.     end
  264.    
  265.     return false
  266.    
  267. end
  268.  
  269. local function CombineSplit(split)
  270.     local buf = GLSockBuffer()
  271.     local lastk=0
  272.     for k,v in pairs(split) do
  273.         Msg"."
  274.         if k-1!=lastk then
  275.             Err"CombineSplit received missing packets. Aborting!"
  276.             return
  277.         end
  278.         local safe=0
  279.         while safe and safe<8192 do safe=safe+1
  280.             local read,byte=v:ReadByte()
  281.             if read==0 then
  282.                 break
  283.             end
  284.             local wrote=buf:WriteByte(byte)
  285.             assert(wrote>0)
  286.         end
  287.        
  288.         lastk=k
  289.     end
  290.     buf:Seek(0,GLSOCKBUFFER_SEEK_SET)
  291.     return buf
  292. end
  293.  
  294. local function ProcessSplit(buf,ip,port,data)
  295. --  print("Split from",ip)
  296.  
  297.     local _,b2=buf:ReadByte()  
  298.     local _,b3=buf:ReadByte()
  299.     local _,b4=buf:ReadByte()
  300.    
  301.     if b2!=0xFF or b3!=0xFF or b4!=0xFF then
  302.         Err("Split packet weird data from",ip,port,"Bytes: ",0xFE,b2,b3,b4 )
  303.     end
  304.    
  305.     local _,ReqID=buf:ReadLong()
  306.     if _<0 then Err"Split packet Unexpected EOF" return end
  307.    
  308.     local bz2=ReqID>=32768
  309.     if bz2 then
  310.         --ReqID=ReqID-32768
  311.     end
  312.    
  313.     local _,NumPackets=buf:ReadByte()  
  314.     if _<0 then Err"Split packet Unexpected EOF" return end
  315.    
  316.     local _,Packet=buf:ReadByte()
  317.     if _<0 then Err"Split packet Unexpected EOF" return end
  318.    
  319.     local _,SplitLen=buf:ReadShort()
  320.     if _<0 then Err"Split packet Unexpected EOF" return end
  321.    
  322.     /*Err("   DBG: "..
  323.         " ID: "..ReqID..(bz2 and " (COMPRESSED) " or "")..
  324.         " Packet: "..(Packet+1)..
  325.         "/"..NumPackets..
  326.         " Len: "..SplitLen
  327.     )*/
  328.    
  329.     if bz2 then
  330.         -- TODO -- :(
  331.         Err("Response from ",ip,port," uses compression. No BZ2 in GMod. Byebye!")
  332.         return
  333.        
  334.     end
  335.     local splits=data[3]
  336.     if Packet==0 then
  337.         splits[ReqID]={}
  338.     end
  339.     local split=splits[ReqID]
  340.     split[Packet+1]=buf
  341.    
  342.     if Packet+1==NumPackets then
  343.         print("\tReceived final split packet, combining and sending to processor")
  344.         local buf = CombineSplit(split)
  345.         --splits[ReqID]=nil
  346.         PROCESS_RECEIVED_STUFF(buf,ip,port,data,true)
  347.     end
  348.    
  349. end
  350.  
  351.  
  352. function PROCESS_RECEIVED_STUFF(buf,ip,port,data,processing_split)
  353.     --MsgN("RECV SIZE ",buf:Size())
  354.    
  355.     if buf:Size()<1 then
  356.         Err("Received weird buffer",buf:Size(),"from",ip,port)
  357.         return
  358.     end
  359.    
  360.     local _,b1=buf:ReadByte()
  361.    
  362.     if b1==0x6A then
  363.         return ProcessPing(buf,ip,port,data)
  364.     elseif b1==0xFE then
  365.         return ProcessSplit(buf,ip,port,data)
  366.     elseif b1==0xFF then
  367.         -- normal
  368.     else
  369.         print("Potential weirdness incoming! byte 1 was",b1)
  370.     end
  371.    
  372.     local _,b2=buf:ReadByte()  
  373.     local _,b3=buf:ReadByte()
  374.     local _,b4=buf:ReadByte()
  375.    
  376.     if buf:EOB() then
  377.         Err("Invalid Buffer size (",buf:Size(),") for this packet type from",ip,port,"Bytes: ",b1,b2,b3,b4 )
  378.         return
  379.     end
  380.    
  381.     if b2!=0xFF or b3!=0xFF or b4!=0xFF then
  382.         Err("Received weird data from",ip,port,"Bytes: ",b1,b2,b3,b4 )
  383.     end
  384.    
  385.     if b4!=0xFF then
  386.         Err("Received weird data from",ip,port,"Bytes (4th is faulty): ",b1,b2,b3,b4 )
  387.         return
  388.     end
  389.    
  390.     local _,b5=buf:ReadByte()
  391.      
  392.     if JumpTo(b5,buf,ip,port,data) then return end
  393.    
  394.     Err("Received unknown respose from",ip,port,"ID: ",b5 )
  395.    
  396. end
  397.  
  398. local function onread(self, address, port, buffer, errno)
  399.     if errno ~= GLSOCK_ERROR_SUCCESS then Err("READ ERROR: " .. err(errno)) return end
  400.     --print("Received data: ",address,port)
  401.     local data = GetSrvData(address,port)
  402.     if not data then
  403.         Err("No data found for ",address,port)
  404.     end
  405.     PROCESS_RECEIVED_STUFF(buffer,address,port,data)
  406.     socket:ReadFrom(1500, onread)
  407. end
  408.  
  409.  
  410. local SendData = function (data,info,ip,port,challenge)
  411.     local buffer = GetSendBuffer(info,challenge)
  412.     socket:SendTo(buffer, ip, port, function(self, bytes, errno)
  413.         if errno==GLSOCK_ERROR_SUCCESS then return end
  414.         Err("Send failed: "..err(errno))
  415.     end)
  416. end
  417.    
  418.  
  419.  
  420. local startedreading=false
  421.  
  422.  
  423. ------------------------------------------------
  424. function   ServerInfo.Get (server,info)
  425. ------------------------------------------------
  426.    
  427.     -- init socket
  428.     if not socket then
  429.         ServerInfo.socket = ServerInfo.socket or GLSock(GLSOCK_TYPE_UDP)
  430.         socket = ServerInfo.socket
  431.     end
  432.    
  433.     info=info==true and A2S_INFO or info
  434.    
  435.     local infotype   for k,v in pairs(ServerInfo.Queries) do    if v==info then infotype = k break end end
  436.    
  437.    
  438.     if not info or not infotype then
  439.         error("invalid info data parameter",2)
  440.     end
  441.    
  442.     local ip,port = (server or ""):match("(%d+.%d+.%d+.%d+)%:?(%d*)")
  443.    
  444.     if not ip then error"invalid address" end
  445.    
  446.     local port = tonumber(port) or 27015
  447.     if port<=0 or port>=65535 then error"invalid port" end
  448.    
  449.     print("Sending ",infotype,"to",ip..":"..port)
  450.    
  451.     local data = GetSrvData(ip,port)
  452.     local challenge = data and data.challenge
  453.     if ( info==A2S_RULES or info==A2S_PLAYER ) and not challenge then
  454.         Err("Tried to request data without valid challenge, cancelling")
  455.         return false
  456.     end
  457.    
  458.     if not data then
  459.         print"\tAssigning data for new server"
  460.         data = SrvData(ip,port)
  461.     end
  462.    
  463.     assert(data!=nil)  
  464.     assert(GetSrvData(ip,port)!=nil)
  465.     assert(data==GetSrvData(ip,port))
  466.    
  467.  
  468.     if not startedreading then
  469.         startedreading = true
  470.         --print"SOCK: starting reading"
  471.         socket:ReadFrom(1500, onread)
  472.     end
  473.    
  474.     SendData(data,info,ip,port,challenge)
  475.    
  476.     return true
  477. end
  478.  
  479.  
  480.  
Advertisement
Add Comment
Please, Sign In to add comment