Thread: Rune-Server TopList Secure Callbacks | Vote for Items/Cash

Results 1 to 5 of 5
  1. #1 Rune-Server TopList Secure Callbacks | Vote for Items/Cash 
    Renown Programmer

    Join Date
    Aug 2008
    Posts
    34
    Thanks given
    35
    Thanks received
    122
    Rep Power
    486
    A fairly simple yet secure way of doing callbacks for the Rune-Server top list. It shouldn't be too hard to adapt to other top lists with call backs.

    Setting Up
    In CallbackListener change CALLBACK_PORT to a suitable unused port number. Or you could just leave it as 9001. This is used in your callback URL.
    In CallbackListener change key to any random secure string you want. This is used in your callback URL below.
    On the top list change your callback URL to:
    http://yoursite.com:#CALLBACK_PORT#/callback#KEY#
    where #CALLBACK_PORT# and #KEY# are the ones you set them to.
    In Vote change SERVER_ID to your server's ID on the top list.
    Change line 52 of Vote to link the player to the vote URL, or you could insert the incentive into a database and show it on your website.
    Change line 65 of Vote to give the item any rewards.
    Somewhere in the code where your server starts up add this:
    Code:
    CallbackListener callbackListener = new CallbackListener();
    Thread callbackThread = new Thread(callbackListener);
    callbackThread.start();
    Then when you want to send them the vote URL run this code:
    Code:
    Vote.doVote(client);
    where client is the client voting.


    I'm not going to spoon feed it to anyone. If you can't get it figured out it's your problem not mine.


    Code for CallbackListener:
    Code:
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    
    public class CallbackListener implements Runnable {
    	private ServerSocket callbackSocket;
    
    	/*
    	 * Starts a very basic HTTP server listening on the port below for the call backs.
    	 */
    	private int CALLBACK_PORT = 9001;
    
    	@Override
    	public void run() {
    		while (true) {
    			try {
    				//Accept connection.
    				Socket socket = callbackSocket.accept();
    				CallbackHandler handler = new CallbackHandler(socket);
    				Thread handlerThread = new Thread(handler);
    				handlerThread.start();
    			} catch (IOException e) {
    				//Failed to accept connection.
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public CallbackListener() throws IOException {
    		this.callbackSocket = new ServerSocket(CALLBACK_PORT);
    	}
    }
    Code for CallbackHandler:
    Code:
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    
    public class CallbackHandler implements Runnable {
    
    	private Socket socket;
    	public CallbackHandler(Socket socket) {
    		this.socket = socket;
    	}
    
    	/*
    	 * SECRET KEY
    	 * Change this to some random sequence of letters
    	 * I would recommend only using 8 to 64 lower case alphanumeric characters
    	 * 
    	 * CHANGE THIS TO SOMETHING RANDOM AND SECURE BEFORE USE!
    	 */
    	String key = "abcdefgh";
    
    	Pattern callbackPattern = Pattern.compile("callback" + key + "\\?postback=(\\S*)");
    	String callbackResponse = "HTTP/1.1 200 OK\nContent-Type: text/plain; charset=utf-8\nContent-Length: 2\n\nok";
    
    	@Override
    	public void run() {
    		while (true) {
    			try {
    				try {
    					socket.setSoTimeout(10000); //To prevent denial of service by leaving connections open.
    					OutputStream out = socket.getOutputStream();
    					BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    					String request = in.readLine();
    					Matcher matcher = callbackPattern.matcher(request);
    					if (matcher.find()) {
    						//Handle a correctly formatted request.
    						Vote.handleCallback(matcher.group(1));
    					}
    					out.write(callbackResponse.getBytes("utf-8"));
    					socket.close();
    				} catch (SocketException e) {
    					//Connection timed out.
    					socket.close();
    				}
    			} catch (IOException e) {
    				//Failed to accept connection.
    				e.printStackTrace();
    			}
    		}
    	}
    }
    Code for Vote:
    Code:
    import java.math.BigInteger;
    import java.security.SecureRandom;
    import java.util.ArrayList;
    import java.util.Iterator;
    
    
    public class Vote {
    	private String key;
    	private Client client;
    
    	/*
    	 * Change this to your server ID on the top list.
    	 */
    	private static int SERVER_ID = 0;
    
    	private static SecureRandom random = new SecureRandom();
    
    	private static ArrayList<Vote> votes = new ArrayList<Vote>();
    
    	public Vote(String key, Client client) {
    		this.key = key;
    		this.client = client;
    	}
    
    	public static void doVote(Client client) {
    		String key = new BigInteger(128, random).toString(32);
    		Vote vote = new Vote(key, client);
    
    		//Check if this person already has a vote key assigned to them.
    		boolean hasDuplicateKey = true;
    		while (hasDuplicateKey) {
    			hasDuplicateKey = false;
    			for (Vote tvote : votes) {
    				if (tvote.getClient() == client) {
    					//Handle client collision.
    					vote = tvote;
    					key = vote.getKey();
    					break;
    				} else if (tvote.getKey().equals(vote.getKey())) {
    					//Handle key collision.
    					key = new BigInteger(128, random).toString(32);
    					hasDuplicateKey = true;
    				} else {
    					//Nothing unusual happened, no key or client collision.
    				}
    			}
    		}
    
    		/*
    		 * Change this to send the users to the URL somehow.
    		 */
    		System.out.println("http://www.rune-server.org/toplist.php?do=vote&sid=" + Integer.toString(SERVER_ID) + "&incentive=" + key);
    
    		votes.add(vote);
    	}
    
    	public static void handleCallback(String key) {
    		Iterator<Vote> votesIterator = votes.iterator();
    		while (votesIterator.hasNext()) {
    			Vote vote = (Vote) votesIterator.next();
    			if (vote.getKey().equals(key)) {
    				/*
    				 * Key is valid, give them (vote.getClient()) the reward here!
    				 */
    				System.out.println("Valid callback for " + vote.getClient());
    
    				votesIterator.remove();
    				break;
    			}
    		}
    	}
    
    	public String getKey() {
    		return key;
    	}
    
    	public Client getClient() {
    		return client;
    	}
    }
    Reply With Quote  
     

  2. #2  
    Renown Programmer

    Join Date
    Aug 2008
    Posts
    34
    Thanks given
    35
    Thanks received
    122
    Rep Power
    486
    Just made a slight change so that each request is processed in a different thread. Also added a timeout to the socket. This should prevent denial of service by opening connections without sending data.
    Reply With Quote  
     

  3. #3  
    Respected Member


    Join Date
    Jan 2009
    Posts
    5,743
    Thanks given
    1,162
    Thanks received
    3,603
    Rep Power
    5000
    can you not just use a thread pool instead of spawning new threads over and over
    Reply With Quote  
     

  4. #4  
    Renown Programmer

    Join Date
    Aug 2008
    Posts
    34
    Thanks given
    35
    Thanks received
    122
    Rep Power
    486
    As votes aren't done particularly often I figured the overhead from creating new threads would have a negligible difference to the gains from having a pool. Also means I don't have to worry about having more callbacks at once than the pool can handle.
    Reply With Quote  
     

  5. Thankful user:


  6. #5  
    Registered Member
    craig903's Avatar
    Join Date
    Sep 2007
    Age
    30
    Posts
    1,357
    Thanks given
    14
    Thanks received
    92
    Rep Power
    238
    Quote Originally Posted by Jonneh View Post
    As votes aren't done particularly often I figured the overhead from creating new threads would have a negligible difference to the gains from having a pool. Also means I don't have to worry about having more callbacks at once than the pool can handle.
    I know this is just a general tutorial but quite a lot off servers now a days have threadpools and it would probably be wise to use one. its more then likely that when you invoke vote that there is an active thread within the pool. I understand your logic though that not many people are going to vote offen but it may be something to consider doing in the future when anyone uses this in a more demanding server.

    Good job though. It will be usefull!
    Visit Rune Miracle Here
    Reply With Quote  
     


Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Rune-Zone Toplist
    By Silis in forum Website Development
    Replies: 10
    Last Post: 07-27-2011, 02:49 AM
  2. Make your server more secure.
    By Ammar_ in forum Tutorials
    Replies: 47
    Last Post: 05-15-2011, 10:49 PM
  3. Secure Server Threads
    By Lmctruck30 in forum RS2 Server
    Replies: 23
    Last Post: 12-18-2010, 06:47 PM
  4. Table simular to Rune-server toplist?
    By Zee Best in forum Application Development
    Replies: 4
    Last Post: 11-23-2010, 12:20 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •