public static void Main(string[] args) { string cmdLine = Environment.GetCommandLineArgs()[0]; if (args.Length < 3) { Console.WriteLine("Usage: " + cmdLine + " <proxy port> <gameserver ip> <gameserver port>"); Environment.Exit(1); } IPAddress targetIP; int targetPort; int localPort; if (!int.TryParse(args[0], out localPort) || localPort < 1 || localPort > 65535) { Console.WriteLine("Invalid proxy port!"); Environment.Exit(2); } if (!IPAddress.TryParse(args[1], out targetIP)) { Console.WriteLine("Invalid gameserver IP address!"); Environment.Exit(3); } if (!int.TryParse(args[2], out targetPort) || targetPort < 1 || targetPort > 65535) { Console.WriteLine("Invalid gameserver port!"); Environment.Exit(4); } EndPoint requestingEP = Init(localPort, targetIP, targetPort); if (requestingEP == null) { Console.WriteLine("Cannot bind proxy port!"); Environment.Exit(4); } Console.WriteLine( "iptables -t nat -I PREROUTING -p udp -d {0} --dport {1} -m u32 --u32 '0>>22&0x3C@8=0xFFFFFFFF && 0>>22&0x3C@12=0x54536F75 && 0>>22&0x3C@16=0x72636520 && 0>>22&0x3C@20=0x456E6769 && 0>>22&0x3C@24=0x6E652051 && 0>>22&0x3C@28=0x75657279' -j REDIRECT --to-port {2}", args[1], args[2], args[0] ); // Give our time trackers an initial time lastInfoTime = DateTime.Now - TimeSpan.FromSeconds(30); lastPlayersTime = DateTime.Now - TimeSpan.FromSeconds(30); lastRulesTime = DateTime.Now - TimeSpan.FromSeconds(30); lastPrint = DateTime.Now; //// Main query receiving loop while (true) { byte[] reqPacket = new byte[maxPacket]; try { publicSock.ReceiveFrom(reqPacket, ref requestingEP); } catch (Exception) { continue; } ThreadWorkerInfo info = new ThreadWorkerInfo(); info.reqPacket = reqPacket; info.requestingEP = requestingEP; ThreadPool.QueueUserWorkItem(HandleQuery, info); if (DateTime.Now.AddSeconds(-10) >= lastPrint) { int workerThreads = 0, completionPortThreads = 0; ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); Console.WriteLine( "{0}/{1} info queries and {2}/{3} other queries in last {4} seconds, availability {5}/{6}", recvInfoQueries, infoQueries, recvOtherQueries, otherQueries, (DateTime.Now - lastPrint).Seconds, workerThreads, completionPortThreads ); Interlocked.Exchange(ref infoQueries, 0); Interlocked.Exchange(ref otherQueries, 0); Interlocked.Exchange(ref recvInfoQueries, 0); Interlocked.Exchange(ref recvOtherQueries, 0); lastPrint = DateTime.Now; } } }
public static void HandleQuery(Object data) { ThreadWorkerInfo info = (ThreadWorkerInfo)data; EndPoint requestingEP = info.requestingEP; byte[] reqPacket = info.reqPacket; switch (reqPacket[4]) { case 0x54: // Info Queries { Interlocked.Increment(ref infoQueries); if (lastInfoTime + TimeSpan.FromSeconds(5) <= DateTime.Now) { if (updateInfoCacheLock.WaitOne(100)) { // Update our cached values if (!UpdateCache(reqPacket[4])) { return; } lastInfoTime = DateTime.Now; updateInfoCacheLock.ReleaseMutex(); } } // If we get this far, we send our cached values. try { infoCacheLock.AcquireReaderLock(100); try { publicSock.SendTo(infoCache, requestingEP); Interlocked.Increment(ref recvInfoQueries); } catch { return; } finally { infoCacheLock.ReleaseReaderLock(); } } catch (ApplicationException) { } break; } case 0x55: // Player list { Interlocked.Increment(ref otherQueries); if (!ChallengeIsValid(reqPacket)) { // We check that the client is using the correct challenge code try { publicSock.SendTo(BuildRequest(0x41), requestingEP); } catch { return; } // We'll send the client the correct challenge code to use. break; } if (lastPlayersTime + TimeSpan.FromSeconds(3) <= DateTime.Now) { if (updatePlayerCacheLock.WaitOne(100)) { if (!UpdateCache(reqPacket[4])) { return; } lastPlayersTime = DateTime.Now; updatePlayerCacheLock.ReleaseMutex(); } } try { playerCacheLock.AcquireReaderLock(100); bool success = true; for (int i = 0; i < playerCache.Length; i++) { try { publicSock.SendTo(playerCache[i], requestingEP); } catch { success = false; break; } } if (success) { Interlocked.Increment(ref recvOtherQueries); } playerCacheLock.ReleaseReaderLock(); } catch (ApplicationException) { } break; } case 0x56: //Rules list { Interlocked.Increment(ref otherQueries); if (!ChallengeIsValid(reqPacket)) { try { publicSock.SendTo(BuildRequest(0x41), requestingEP); } catch { return; } break; } if (lastRulesTime + TimeSpan.FromSeconds(10) <= DateTime.Now) { if (updateRulesCacheLock.WaitOne(100)) { if (!UpdateCache(reqPacket[4])) { return; } lastRulesTime = DateTime.Now; updateRulesCacheLock.ReleaseMutex(); } } try { rulesCacheLock.AcquireReaderLock(100); bool success = true; for (int i = 0; i < rulesCache.Length; i++) { try { publicSock.SendTo(rulesCache[i], requestingEP); } catch { success = false; break; } } if (success) { Interlocked.Increment(ref recvOtherQueries); } rulesCacheLock.ReleaseReaderLock(); } catch (ApplicationException) { } break; } case 0x57: // Challenge request { Interlocked.Increment(ref otherQueries); try { // Send challenge response. publicSock.SendTo(BuildRequest(0x41), requestingEP); Interlocked.Increment(ref recvOtherQueries); } catch { return; } break; } } return; }