1
1
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' ) ;
5
4
const os = require ( 'os' ) ;
6
5
const net = require ( 'net' ) ;
7
6
const url = require ( 'url' ) ;
8
7
const uuidv4 = require ( 'uuid/v4' ) ;
9
- const Proxy = require ( 'http-mitm-proxy' ) ;
10
8
11
9
const { decrypt_request, decrypt_response } = require ( './smon_decryptor' ) ;
12
10
@@ -19,82 +17,120 @@ class SWProxy extends EventEmitter {
19
17
this . addresses = [ ] ;
20
18
}
21
19
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
25
21
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
+ }
31
25
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 = [ ] ;
40
27
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 ) ;
44
34
} ) ;
45
- ctx . onResponseEnd ( function ( ctx , callback ) {
46
- let reqData ;
47
- let respData ;
48
35
36
+ proxyResp . on ( 'end' , ( ) => {
37
+ let respData ;
49
38
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 ( ) ) ;
52
40
} catch ( e ) {
53
41
// 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 ;
56
44
}
57
45
58
46
const { command } = respData ;
59
- if ( config . Config . App . clearLogOnLogin && ( command === 'HubUserLogin' || command === 'GuestLogin' ) ) {
60
- self . clearLogs ( ) ;
61
- }
62
47
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
+ }
67
61
} ) ;
68
62
}
69
- return callback ( ) ;
70
63
} ) ;
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' , ( ) => { } ) ;
82
64
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.' } ) ;
84
112
}
85
113
} ) ;
86
- this . proxy . listen ( { port, sslCaDir : path . join ( app . getPath ( 'userData' ) , 'swcerts' ) } ) ;
87
114
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
+ } ) ;
93
128
}
94
129
95
130
stop ( ) {
96
131
this . proxy . close ( ) ;
97
- this . proxy = null ;
132
+ this . httpServer . close ( ) ;
133
+
98
134
win . webContents . send ( 'proxyStopped' ) ;
99
135
this . log ( { type : 'info' , source : 'proxy' , message : 'Proxy stopped' } ) ;
100
136
}
@@ -114,7 +150,7 @@ class SWProxy extends EventEmitter {
114
150
}
115
151
116
152
isRunning ( ) {
117
- if ( this . proxy ) {
153
+ if ( this . httpServer && this . httpServer . address ( ) ) {
118
154
return true ;
119
155
}
120
156
return false ;
0 commit comments