Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ logs/
cfg.js
.save
npm-debug.*
.idea/
ssl/
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ MYSQL_PASSWORD: "",

The required database tables get generated automatically.

## Proxy Setup
Newer versions of Pokemon Go application have negated the custom endpoint support of [rastapasta](https://github.com/rastapasta)'s [Pokemon Go Xposed](https://github.com/rastapasta/pokemon-go-xposed/releases) app. But the certificate pinning bypass still appears functional.

Based on a rough dissection of [rastapasta](https://github.com/rastapasta)'s [pokemon-go-mitm](https://github.com/rastapasta/pokemon-go-mitm) for reference, an MITM proxy server, PAC generator and cert downloader have all been added.

Multiple client connection/configuration options are available:

* (Android) Settings->Wi-Fi->[Connect]->Advanced Options->Proxy->Proxy Auto-Config `http://<server_ip>:<web port>/proxy/pac`
* (Android) Settings->Wi-Fi->[Connect]->Advanced Options->Proxy->Manual: Proxy hostname `<server_ip>`, Proxy port: `<proxy port>` (May require CA cert)

In-case the client device needs the CA cert: `http://<server_ip>:<web port>/proxy/ca.(pem|crt|der)`

## Server setup

You need at minimum [Node.js](https://nodejs.org/en/) version 6.x.
Expand Down
9 changes: 9 additions & 0 deletions cfg.js.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ export default {
DEFAULT_CONSOLE_COLOR: 32,
TRANSFER_ACCOUNTS: false,

// Proxy Server settings
PROXY: {
port: 3001,
forceSNI: process.env.PROXY_SNI || true,
sslCaDir: process.cwd() + '/ssl',
debug: (typeof process.env.PROXY_DEBUG === "boolean") ? process.env.PROXY_DEBUG : false,
silent: (typeof process.env.PROXY_SILENT === "boolean") ? process.env.PROXY_SILENT : true,
},

// Server debug options
DEBUG_DUMP_PATH: "logs/",
DEBUG_DUMP_TRAFFIC: true,
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
"babel-preset-stage-0": "^6.5.0",
"directory-tree": "^1.1.1",
"fs-extra": "^0.30.0",
"http-mitm-proxy": "git+https://github.com/joeferner/node-http-mitm-proxy.git",
"jwt-decode": "^2.1.0",
"mysql": "^2.11.1",
"nodegit": "^0.16.0",
"node-pogo-protos": "^1.4.0",
"nodegit": "^0.16.0",
"pcrypt": "git+https://github.com/laverdet/pcrypt.git",
"pogo-asset-downloader": "^0.3.1",
"pokerare": "^0.1.1",
"pokemongo-protobuf": "^1.11.0",
"pokerare": "^0.1.1",
"prompt": "^1.0.0",
"s2-geometry": "^1.2.9",
"url": "^0.11.0"
Expand Down
125 changes: 125 additions & 0 deletions src/http-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import fs from "fs";
import Proxy from "http-mitm-proxy";
import DNS from "dns";

import print from "./print";
import CFG from "../cfg";

/**
* @return {Proxy}
*/
export function createServerProxy() {
let dnsEntries = this.dns = {
domains: {},
ips: {}
};

/* Quick DNS lookups */
['pgorelease.nianticlabs.com'].map((domain)=> {
DNS.resolve4(domain, (err, result)=> {
dnsEntries.domains[domain] = dnsEntries.ips[result[0]] = {domain: domain, ip: result[0]};
});
});

let proxy = new Proxy();
proxy.use(Proxy.gunzip);
proxy.use(Proxy.wildcard);

/* Proxy error handler */
proxy.onError((ctx, err, errorKind) => {
// ctx may be null
if (errorKind !== "PROXY_TO_SERVER_REQUEST_ERROR") {
var url = (ctx && ctx.clientToProxyRequest) ? ctx.clientToProxyRequest.url : '';
print(errorKind + ' on ' + url + ':' + err, 31);
}
});

/* IP<->Domain Translation */
proxy.onConnect((req, socket, head, callback) => {
var ip;
if (!req.url.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443/)) {
return callback();
}
ip = req.url.split(/:/)[0];
if (dnsEntries.ips[ip]) {
req.url = dnsEntries.ips[ip].domain + ':443';
}
return callback();
});

/* Request handler */
proxy.onRequest((reqCtx, reqCB) => {
if (dnsEntries.domains[reqCtx.clientToProxyRequest.headers.host] && this.world.isFull()) {
print(`Server is full! Refused ${reqCtx.clientToProxyRequest.headers.host}`, 31);
return void 0;
}
var chunks = [];
reqCtx.onRequestData((ctx, chunk, callback)=> {
if (dnsEntries.domains[ctx.clientToProxyRequest.headers.host]) {
chunks.push(chunk);
} else {
callback();
}
});
reqCtx.onRequestEnd((ctx, callback)=> {
if (dnsEntries.domains[ctx.clientToProxyRequest.headers.host]) {
let buffer = Buffer.concat(chunks);
ctx.clientToProxyRequest.body = buffer;
this.routeRequest(ctx.clientToProxyRequest, ctx.proxyToClientResponse);
} else {
callback();
}
});
return reqCB(); //? Can't recall if i was supposed to disable this
});

/* Start server */
proxy.listen(CFG.PROXY);
return (proxy);
}

export function shutdownProxy() {
this.proxy.close(() => {
print("Closed proxy server!", 33);
this.closeConnection(() => {
print("Closed database connection!", 33);
print("Server shutdown!", 31);
setTimeout(() => process.exit(1), 2e3);
});
});
}

export function processProxyFiles(req, res, route) {
let item = route[2];
switch (item) {
case "pac":
print(`Sent Proxy PAC file to client`, 36);
let localIPv4 = this.getLocalIPv4();
let response = ['function FindProxyForURL(url,host){'];
response.push("\tif(shExpMatch(host,\"(" + Object.keys(this.dns.domains).join('|') + ")\")){");
// response.push("\t\treturn \"PROXY " + localIPv4 + ":" + CFG.PROXY.port + "\";");
response.push("\t\treturn \"PROXY " + (CFG.LOCAL_IP || localIPv4) + ":" + CFG.PROXY.port + "\";");
response.push("\t} else {");
response.push("\t\treturn \"DIRECT\";");
response.push("\t}");
response.push("}\n");
res.end(response.join("\n"));
break;
case "ca.crt":
case "ca.der":
case "ca.pem":
fs.readFile(CFG.PROXY.sslCaDir + "/certs/ca.pem", (error, data) => {
if (error) {
print(`Error file resolving CA file:` + error, 31);
return void 0;
}
print(`Sent Proxy Certificate file to client`, 36);
res.end(data);
});
break;
default:
res.end("");
break;
}

}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as _api from "./api";
import * as _commands from "./commands";
import * as _dump from "./dump";
import * as _http from "./http";
import * as _proxy from "./http-proxy";
import * as _setup from "./setup";
import * as _cycle from "./cycle";
import * as _request from "./request";
Expand Down Expand Up @@ -196,6 +197,7 @@ inherit(GameServer, _api);
inherit(GameServer, _commands);
inherit(GameServer, _dump);
inherit(GameServer, _http);
inherit(GameServer, _proxy);
inherit(GameServer, _setup);
inherit(GameServer, _cycle);
inherit(GameServer, _request);
Expand Down
1 change: 1 addition & 0 deletions src/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function processCommand(cmd, data) {
// Exit the server
case "/exit":
this.shutdown();
this.shutdownProxy();
break;
case "/kick":
this.world.kickPlayer(data[0]);
Expand Down
3 changes: 3 additions & 0 deletions src/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export function routeRequest(req, res) {
case "model":
this.processModelRequest(req, res, route);
break;
case "proxy":
this.processProxyFiles(req,res,route);
break;
case "api":
if (!CFG.API_ENABLE) {
print(`API is disabled! Denied API access for ${host}!`, 31);
Expand Down
4 changes: 3 additions & 1 deletion src/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ export function setup() {
return void 0;
}
this.socket = this.createHTTPServer();
this.proxy = this.createServerProxy();
setTimeout(this::this.cycle, 1);
let localIPv4 = this.getLocalIPv4();
print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33);
print(`Server listening at ${CFG.LOCAL_IP || localIPv4}:${CFG.PORT}`, 33);
print(`Proxy Server listening at ${CFG.LOCAL_IP || localIPv4}:${CFG.PROXY.port}`, 33);
resolve();
});

Expand Down