Skip to content

Instantly share code, notes, and snippets.

@Twister915
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Twister915/cfa3bba8c5a2c331d2b1 to your computer and use it in GitHub Desktop.
Save Twister915/cfa3bba8c5a2c331d2b1 to your computer and use it in GitHub Desktop.
Checks a list of servers (specified in a strange JSON format, checkout masscan for more details) for offline/online mode. Prints all online servers to stdout
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.twister915.scanner</groupId>
<artifactId>OfflineModeScanner</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.spacehq</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1.8-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.2</version>
</dependency>
</dependencies>
</project>
package me.twister915.scanner;
import org.spacehq.mc.protocol.MinecraftProtocol;
import org.spacehq.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
import org.spacehq.packetlib.Client;
import org.spacehq.packetlib.Session;
import org.spacehq.packetlib.event.session.*;
import org.spacehq.packetlib.tcp.TcpSessionFactory;
import java.net.InetSocketAddress;
import java.util.function.Function;
public final class CheckerFunction implements Function<InetSocketAddress, Boolean> {
private final Thread MAIN_THREAD;
private final static MinecraftProtocol protocol = new MinecraftProtocol("asdf1234");
private final static TcpSessionFactory sessionFactory = new TcpSessionFactory();
private final SessionListener sessionListener = new SessionListener() {
public void packetReceived(final PacketReceivedEvent packetReceivedEvent) {
if (packetReceivedEvent.getPacket() instanceof ServerJoinGamePacket) {
MAIN_THREAD.interrupt();
}
}
public void packetSent(PacketSentEvent packetSentEvent) {}
public void connected(ConnectedEvent connectedEvent) {}
public void disconnecting(final DisconnectingEvent disconnectingEvent) { notifyLock(); }
public void disconnected(final DisconnectedEvent disconnectedEvent) { notifyLock(); }
};
private void notifyLock() {
synchronized (LOCK) {
LOCK.notifyAll();
}
}
{
MAIN_THREAD = Thread.currentThread();
}
private final Object LOCK = new Object();
public Boolean apply(InetSocketAddress inetSocketAddress) {
final Client client = new Client(inetSocketAddress.getAddress().getHostAddress(), inetSocketAddress.getPort(), protocol, sessionFactory);
Session session = client.getSession();
session.addListener(sessionListener);
session.connect(false);
synchronized (LOCK) {
try {
LOCK.wait(OfflineModeScanner.WAIT_TIME);
} catch (InterruptedException e) {
return true;
} finally {
if (session.isConnected()) session.disconnect("quit");
}
}
return false;
}
}
package me.twister915.scanner;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.net.InetSocketAddress;
import static me.twister915.scanner.OfflineModeScanner.printDebug;
@EqualsAndHashCode(callSuper = false)
@Data
public final class CheckerThread implements Runnable {
private final ResultRecord[] results;
public void run() {
final CheckerFunction checkerFunction = new CheckerFunction();
for (final ResultRecord record : results) {
final String ip = record.getIp();
for (PortRecord portRecord : record.getPorts()) {
final Integer port = portRecord.getPort();
if (checkerFunction.apply(new InetSocketAddress(ip, port))) System.out.println(ip + ":" + port);
else if (printDebug) System.err.println(ip + ":" + port + " failed");
}
}
}
}
package me.twister915.scanner;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public final class OfflineModeScanner {
public static final boolean printDebug = false;
public static final int THREADS = 50, WAIT_TIME = 3500;
public static void main(final String[] args) throws IOException {
if (args.length < 1) throw new IllegalArgumentException("You must specify a target file!");
InputStream input;
String arg = args[0];
if (arg.equals("-")) input = System.in;
else input = new FileInputStream(new File(arg));
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
Gson gson = new GsonBuilder().create();
ResultRecord[] resultRecords = gson.fromJson(reader, ResultRecord[].class);
int threadCountRaw = resultRecords.length / THREADS, remain = resultRecords.length % THREADS;
long startTime = System.nanoTime();
if (printDebug) System.err.println("Creating threads with " + threadCountRaw + " servers each for " + resultRecords.length + " records.");
ExecutorService executorService = Executors.newCachedThreadPool();
System.out.println("#Starting scan, using " + THREADS + " threads (or possibly " + (THREADS + 1) + " threads) to check " + resultRecords.length + " servers authentication mode (offline vs online). Anything printed below tested positive for offline. The error console will " + (printDebug ? "only" : "not only") + " contain exceptions.");
System.out.println("#Should take about " + ((((float)WAIT_TIME/1000f) * resultRecords.length) / THREADS) + "s.");
for (int i = 0, actualThreads = (THREADS + (remain > 0 ? 1 : 0)), pos = 0; i < actualThreads; i++) {
int at = Math.min(threadCountRaw * (i + 1), resultRecords.length);
if (printDebug) System.err.println("Creating thread #" + (i + 1) + " from [" + pos + "," + at + ").");
executorService.execute(new CheckerThread(Arrays.copyOfRange(resultRecords, pos, at)));
pos = at;
}
executorService.shutdown();
try {
executorService.awaitTermination(365, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("#Main thread was interrupted, did not complete successfully!");
}
System.out.println("#Finished scan (" + ((System.nanoTime() - startTime)/1000000000) + "s);");
}
}
package me.twister915.scanner;
import lombok.Data;
@Data
public final class PortRecord {
private final Integer port, ttl;
private final String proto, status, reason;
}
package me.twister915.scanner;
import lombok.Data;
@Data
public final class ResultRecord {
private final String ip;
private final PortRecord[] ports;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment