/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package gcb; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * * @author wizardus */ public class GCBReverseHost { ReverseServer[] servers; int counter; //counts which server we're on GarenaInterface garena; ByteBuffer buf; //UDP broadcast socket DatagramSocket udpSocket; InetAddress udpTarget; int udpPort; int reversePort; int reverseNum; //config int war3version = 26; public GCBReverseHost(GarenaInterface garena) { this.garena = garena; buf = ByteBuffer.allocate(4096); //config reversePort = GCBConfig.configuration.getInteger("gcb_reverse_port", 0); reverseNum = GCBConfig.configuration.getInteger("gcb_reverse_num", 5); //number of servers to host udpPort = GCBConfig.configuration.getInteger("gcb_broadcastport", 6112); String udpTargetName = GCBConfig.configuration.getString("gcb_reverse_target", "127.0.0.1"); try { udpTarget = InetAddress.getByName(udpTargetName); } catch(UnknownHostException uhe) { if(Main.DEBUG) { uhe.printStackTrace(); } Main.println(1, "[GCBReverseHost] Error with broadcast address: " + uhe.getLocalizedMessage()); } war3version = GCBConfig.configuration.getInt("gcb_reverse_war3version", 26); } public void init() { servers = new ReverseServer[reverseNum]; for(int i = 0; i < reverseNum; i++) { //if reversePort is 0, let OS decide the port int port = reversePort == 0 ? 0 : reversePort + i; if(port == 0) { Main.println(4, "[GCBReverseHost] Initiating server instance " + i); } else { Main.println(4, "[GCBReverseHost] Initiating on port " + port); } try { servers[i] = new ReverseServer(garena, port); } catch(IOException ioe) { if(Main.DEBUG) { ioe.printStackTrace(); } Main.println(1, "[GCBReverseHost] Error while initiating server: " + ioe.getLocalizedMessage()); } } Main.println(4, "[GCBReverseHost] Creating UDP socket..."); try { udpSocket = new DatagramSocket(); } catch(IOException ioe) { if(Main.DEBUG) { ioe.printStackTrace(); } Main.println(1, "[GCBReverseHost] Error while initiating UDP socket: " + ioe.getLocalizedMessage()); } } public void start() { for(ReverseServer server : servers) { server.start(); } } public void sendSearch() { Main.println(4, "[GCBReverseHost] Sending W3GS SEARCH with version " + war3version + "..."); byte[] productId = new byte[] {80, 88, 51, 87}; ByteBuffer lbuf = ByteBuffer.allocate(16); lbuf.order(ByteOrder.LITTLE_ENDIAN); lbuf.put((byte) 247); //W3GS header constant lbuf.put((byte) 47); //W3GS search game lbuf.putShort((short) 16); //packet length in little endian lbuf.put(productId); //want product id in same array order lbuf.putInt(war3version); //war3version in little endian lbuf.putInt(0); //unknown byte[] bytes = lbuf.array(); garena.broadcastUDPEncap(udpPort, udpPort, bytes, 0, bytes.length); } private int getCounter() { int current = counter; counter++; if(counter >= servers.length) { counter = 0; } return current; } public void receivedUDP(ByteBuffer lbuf, InetAddress address, int port, int senderId) { buf.clear(); //use buf to create our own packet lbuf.order(ByteOrder.LITTLE_ENDIAN); buf.order(ByteOrder.LITTLE_ENDIAN); int header = GarenaEncrypt.unsignedByte(lbuf.get()); //W3GS header constant if(header != 247) { Main.println(4, "[GCBReverseHost] Invalid W3GS header constant " + header); return; } int identifier = GarenaEncrypt.unsignedByte(lbuf.get()); //packet type identifier if(identifier != 48) { //not GAMEINFO packet return; } buf.put((byte) header); buf.put((byte) identifier); buf.putShort((short) 0); //packet size; do later lbuf.getShort(); //read packet size short (actually little endian) int productid = lbuf.getInt(); buf.putInt(productid); //product ID int version = lbuf.getInt(); buf.putInt(war3version); //version buf.putInt(lbuf.getInt()); //32-bit host counter buf.putInt(lbuf.getInt()); //unknown String gamename = GarenaEncrypt.getTerminatedString(lbuf); //gamename = "gcb"; byte[] bytes = gamename.getBytes(); buf.put(bytes); buf.put((byte) 0); //null terminator buf.put(lbuf.get()); //unknown byte[] array = GarenaEncrypt.getTerminatedArray(lbuf); buf.put(array); //StatString buf.put((byte) 0); //null terminator buf.putInt(lbuf.getInt()); //slots total buf.putInt(lbuf.getInt()); //game type buf.putInt(lbuf.getInt()); //unknown buf.putInt(lbuf.getInt()); //slots open buf.putInt(lbuf.getInt()); //up time //get the sender's port, but use our own reverseserver's port int senderPort = GarenaEncrypt.unsignedShort(lbuf.getShort()); ReverseServer server = servers[getCounter()]; server.update(address, port, senderId, senderPort); buf.putShort((short) server.port); //port //assign length in little endian int length = buf.position(); buf.putShort(2, (short) length); //get bytes byte[] packetBytes = new byte[length]; buf.position(0); buf.get(packetBytes); //send packet to LAN, or to udpTarget DatagramPacket packet = new DatagramPacket(packetBytes, packetBytes.length, udpTarget, udpPort); Main.println(4, "[GCBReverseHost] Broadcasting with gamename [" + gamename + "]; version: " + version + "; productid: " + productid + "; senderport: " + senderPort + "; serverport: " + server.port); try { udpSocket.send(packet); } catch(IOException ioe) { if(Main.DEBUG) { ioe.printStackTrace(); } Main.println(1, "[GCBReverseHost] Error while broadcast UDP: " + ioe.getLocalizedMessage()); } } } class ReverseServer extends Thread { ServerSocket server; GarenaInterface garena; int port; InetAddress lastAddress; int lastPort; //GP2PP port int lastId; int lastDestination; //destination TCP port public ReverseServer(GarenaInterface garena, int port) throws IOException { this.garena = garena; this.port = port; server = new ServerSocket(port); if(this.port == 0) { this.port = server.getLocalPort(); } } public void update(InetAddress addr, int port, int id, int dest) { lastAddress = addr; lastPort = port; lastId = id; lastDestination = dest; } public void run() { while(true) { Socket client = null; try { client = server.accept(); } catch(IOException ioe) { if(Main.DEBUG) { ioe.printStackTrace(); } Main.println(1, "[ReverseServer] Accept failed: " + ioe.getLocalizedMessage()); } Main.println(0, "[ReverseServer] New connection from " + client.getInetAddress()); garena.sendTCPInit(lastAddress, lastPort, lastDestination, lastId, client); } } }