Skip to content

Commit 29e1d7f

Browse files
committed
Release 0.0.28
1 parent 77952cb commit 29e1d7f

File tree

6 files changed

+107
-160
lines changed

6 files changed

+107
-160
lines changed

app/components/Head.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@ class Head extends React.Component {
3939
}
4040
}
4141

42-
getCert() {
43-
ipcRenderer.send('getCert');
44-
}
45-
4642
changePort(e) {
4743
const port = Number(e.target.value);
4844
config.Config.Proxy.port = port;
@@ -60,7 +56,6 @@ class Head extends React.Component {
6056
<Input label="Port" defaultValue={config.Config.Proxy.port} onChange={this.changePort.bind(this)} />
6157
</Menu.Item>
6258
<Menu.Item position="right">
63-
<Button content="Get Cert" icon="share" labelPosition="right" onClick={this.getCert.bind(this)} />
6459
<Button
6560
content={this.state.proxyRunning ? 'Stop Proxy' : 'Start Proxy'}
6661
icon={this.state.proxyRunning ? 'stop' : 'play'}

app/main.js

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,6 @@ ipcMain.on('proxyStop', () => {
8989
proxy.stop();
9090
});
9191

92-
ipcMain.on('getCert', async () => {
93-
const fileExists = await fs.pathExists(path.join(app.getPath('userData'), 'swcerts', 'certs', 'ca.pem'));
94-
if (fileExists) {
95-
await fs.copy(
96-
path.join(app.getPath('userData'), 'swcerts', 'certs', 'ca.pem'),
97-
path.join(app.getPath('desktop'), `${app.getName()} Files`, 'cert', 'ca.pem')
98-
);
99-
proxy.log({
100-
type: 'success',
101-
source: 'proxy',
102-
message: `Certificate copied to ${path.join(app.getPath('desktop'), `${app.getName()} Files`, 'cert', 'ca.pem')}.`
103-
});
104-
} else {
105-
proxy.log({
106-
type: 'info',
107-
source: 'proxy',
108-
message: 'No certificate available yet. You might have to start the proxy once and then try again.'
109-
});
110-
}
111-
});
112-
11392
ipcMain.on('logGetEntries', event => {
11493
event.returnValue = proxy.getLogEntries();
11594
});

app/proxy/SWProxy.js

Lines changed: 94 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
const EventEmitter = require('events');
2-
const { app } = require('electron');
3-
const fs = require('fs-extra');
4-
const path = require('path');
2+
const http = require('http');
3+
const httpProxy = require('http-proxy');
54
const os = require('os');
65
const net = require('net');
76
const url = require('url');
87
const uuidv4 = require('uuid/v4');
9-
const Proxy = require('http-mitm-proxy');
108

119
const { decrypt_request, decrypt_response } = require('./smon_decryptor');
1210

@@ -19,82 +17,120 @@ class SWProxy extends EventEmitter {
1917
this.addresses = [];
2018
}
2119
start(port) {
22-
console.log(app.getPath('userData'));
23-
const self = this;
24-
this.proxy = Proxy();
20+
const self = this; // so event callbacks can access this SWProxy class
2521

26-
this.proxy.onError(function(ctx, e, errorKind) {
27-
if (e.code === 'EADDRINUSE') {
28-
self.log({ type: 'warning', source: 'proxy', message: 'Port is in use from another process. Try another port.' });
29-
}
30-
});
22+
if (port === undefined) {
23+
port = 8080;
24+
}
3125

32-
this.proxy.onRequest(function(ctx, callback) {
33-
if (ctx.clientToProxyRequest.url.includes('/api/gateway_c2.php')) {
34-
ctx.SWRequestChunks = [];
35-
ctx.SWResponseChunks = [];
36-
ctx.onRequestData(function(ctx, chunk, callback) {
37-
ctx.SWRequestChunks.push(chunk);
38-
return callback(null, chunk);
39-
});
26+
let parsedRequests = [];
4027

41-
ctx.onResponseData(function(ctx, chunk, callback) {
42-
ctx.SWResponseChunks.push(chunk);
43-
return callback(null, chunk);
28+
this.proxy = httpProxy.createProxyServer({}).on('proxyRes', (proxyResp, req) => {
29+
let respChunks = [];
30+
31+
if (req.url.indexOf('qpyou.cn/api/gateway_c2.php') >= 0) {
32+
proxyResp.on('data', chunk => {
33+
respChunks.push(chunk);
4434
});
45-
ctx.onResponseEnd(function(ctx, callback) {
46-
let reqData;
47-
let respData;
4835

36+
proxyResp.on('end', () => {
37+
let respData;
4938
try {
50-
reqData = decrypt_request(Buffer.concat(ctx.SWRequestChunks).toString());
51-
respData = decrypt_response(Buffer.concat(ctx.SWResponseChunks).toString());
39+
respData = decrypt_response(respChunks.join());
5240
} catch (e) {
5341
// Error decrypting the data, log and do not fire an event
54-
self.log({ type: 'debug', source: 'proxy', message: `Error decrypting request data - ignoring. ${e}` });
55-
return callback();
42+
self.log({ type: 'debug', source: 'proxy', message: `Error decrypting response data - ignoring. ${e}` });
43+
return;
5644
}
5745

5846
const { command } = respData;
59-
if (config.Config.App.clearLogOnLogin && (command === 'HubUserLogin' || command === 'GuestLogin')) {
60-
self.clearLogs();
61-
}
6247

63-
// Emit events, one for the specific API command and one for all commands
64-
self.emit(command, reqData, respData);
65-
self.emit('apiCommand', reqData, respData);
66-
return callback();
48+
if (parsedRequests[command]) {
49+
// We have a complete request/response pair
50+
const reqData = parsedRequests[command];
51+
52+
if (config.Config.App.clearLogOnLogin && (command === 'HubUserLogin' || command === 'GuestLogin')) {
53+
self.clearLogs();
54+
}
55+
56+
// Emit events, one for the specific API command and one for all commands
57+
self.emit(command, reqData, respData);
58+
self.emit('apiCommand', reqData, respData);
59+
delete parsedRequests[command];
60+
}
6761
});
6862
}
69-
return callback();
7063
});
71-
this.proxy.onConnect(function(req, socket, head, callback) {
72-
const serverUrl = url.parse(`https://${req.url}`);
73-
if (req.url.includes('qpyou.cn')) {
74-
return callback();
75-
} else {
76-
const srvSocket = net.connect(serverUrl.port, serverUrl.hostname, () => {
77-
socket.write('HTTP/1.1 200 Connection Established\r\n' + 'Proxy-agent: Node-Proxy\r\n' + '\r\n');
78-
srvSocket.pipe(socket);
79-
socket.pipe(srvSocket);
80-
});
81-
srvSocket.on('error', () => {});
8264

83-
socket.on('error', () => {});
65+
this.proxy.on('error', (error, req, resp) => {
66+
resp.writeHead(500, {
67+
'Content-Type': 'text/plain'
68+
});
69+
70+
resp.end('Something went wrong.');
71+
});
72+
73+
this.httpServer = http
74+
.createServer((req, resp) => {
75+
// Request has been intercepted from game client
76+
let reqChunks = [];
77+
if (req.url.indexOf('qpyou.cn/api/gateway_c2.php') >= 0) {
78+
req.on('data', chunk => {
79+
reqChunks.push(chunk);
80+
});
81+
req.on('end', () => {
82+
// Parse the request
83+
let reqData;
84+
try {
85+
reqData = decrypt_request(reqChunks.join());
86+
} catch (e) {
87+
// Error decrypting the data, log and do not fire an event
88+
self.log({ type: 'debug', source: 'proxy', message: `Error decrypting request data - ignoring. ${e}` });
89+
return;
90+
}
91+
92+
const { command } = reqData;
93+
94+
// Add command request to an object so we can handle multiple requests at a time
95+
parsedRequests[command] = reqData;
96+
});
97+
}
98+
99+
this.proxy.web(req, resp, { target: req.url, prependPath: false });
100+
})
101+
.listen(port, () => {
102+
this.log({ type: 'info', source: 'proxy', message: `Now listening on port ${port}` });
103+
if (process.env.autostart) {
104+
console.log(`SW Exporter Proxy is listening on port ${port}`);
105+
}
106+
win.webContents.send('proxyStarted');
107+
});
108+
109+
this.httpServer.on('error', e => {
110+
if (e.code === 'EADDRINUSE') {
111+
self.log({ type: 'warning', source: 'proxy', message: 'Port is in use from another process. Try another port.' });
84112
}
85113
});
86-
this.proxy.listen({ port, sslCaDir: path.join(app.getPath('userData'), 'swcerts') });
87114

88-
this.log({ type: 'info', source: 'proxy', message: `Now listening on port ${port}` });
89-
if (process.env.autostart) {
90-
console.log(`SW Exporter Proxy is listening on port ${port}`);
91-
}
92-
win.webContents.send('proxyStarted');
115+
this.httpServer.on('connect', (req, socket) => {
116+
const serverUrl = url.parse(`https://${req.url}`);
117+
118+
const srvSocket = net.connect(serverUrl.port, serverUrl.hostname, () => {
119+
socket.write('HTTP/1.1 200 Connection Established\r\n' + 'Proxy-agent: Node-Proxy\r\n' + '\r\n');
120+
srvSocket.pipe(socket);
121+
socket.pipe(srvSocket);
122+
});
123+
124+
srvSocket.on('error', () => {});
125+
126+
socket.on('error', () => {});
127+
});
93128
}
94129

95130
stop() {
96131
this.proxy.close();
97-
this.proxy = null;
132+
this.httpServer.close();
133+
98134
win.webContents.send('proxyStopped');
99135
this.log({ type: 'info', source: 'proxy', message: 'Proxy stopped' });
100136
}
@@ -114,7 +150,7 @@ class SWProxy extends EventEmitter {
114150
}
115151

116152
isRunning() {
117-
if (this.proxy) {
153+
if (this.httpServer && this.httpServer.address()) {
118154
return true;
119155
}
120156
return false;

css/style.css

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ body {
2222
border-bottom: none;
2323
border-left: none;
2424
}
25-
.main-menu .item.right button {
26-
margin-left: 15px !important;
27-
}
2825

2926
.rune .content .star-line .star img {
3027
width: 11px;

package-lock.json

Lines changed: 12 additions & 72 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)