Skip to content

Commit 5eb9a37

Browse files
committed
Add access control header to responses, HTTPS support via MITM
1 parent e82f8b8 commit 5eb9a37

7 files changed

Lines changed: 157 additions & 58 deletions

File tree

.github/workflows/nexus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
GOARCH: "386"
2828
run: go build -o "Flashpoint Proxy.exe" .
2929
- name: Package
30-
run: zip Server.zip "./Flashpoint Proxy.exe" ./proxySettings.json
30+
run: zip Server.zip "./Flashpoint Proxy.exe" ./proxySettings.json ./fpproxy.crt ./fpproxy.key
3131
- name: Generate Metadata
3232
run: |
3333
sudo apt install libarchive-zip-perl -y

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
22
main.exe
3+
fpproxy.csr
34
Flashpoint Proxy.exe
45
fpProxy.exe

fpproxy.crt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICJDCCAcsCFFCWJV/hBHpY18k/14yUbDA6V/TTMAoGCCqGSM49BAMCMIGTMQsw
3+
CQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEoMCYGA1UECgwfRmxhc2hw
4+
b2ludCBQcm94eSBVbnRydXN0ZWQgTUlUTTEoMCYGA1UECwwfRmxhc2hwb2ludCBQ
5+
cm94eSBVbnRydXN0ZWQgTUlUTTEbMBkGA1UEAwwSZnBwcm94eS5sb2NhbC5zaXRl
6+
MCAXDTIzMTAxNDEzNTQxNVoYDzIxMjMwOTIwMTM1NDE1WjCBkzELMAkGA1UEBhMC
7+
VVMxEzARBgNVBAgMClNvbWUtU3RhdGUxKDAmBgNVBAoMH0ZsYXNocG9pbnQgUHJv
8+
eHkgVW50cnVzdGVkIE1JVE0xKDAmBgNVBAsMH0ZsYXNocG9pbnQgUHJveHkgVW50
9+
cnVzdGVkIE1JVE0xGzAZBgNVBAMMEmZwcHJveHkubG9jYWwuc2l0ZTBZMBMGByqG
10+
SM49AgEGCCqGSM49AwEHA0IABDOkMb4Fb+waYfEXg5OszAyjNqcp8PLTqSC2fcfC
11+
gX3Wqgvq4Vf46F4FViDKyo+E+6fOm3MauI3Vg2FGKUXf9jowCgYIKoZIzj0EAwID
12+
RwAwRAIgHyjrkkCwuOQm5JO5SKeH3Om8dQm6m6a+1k5max2RqakCICQRzrm0ERo4
13+
siAXSthMrOdDignP/cM10AcBe/J00Vw8
14+
-----END CERTIFICATE-----

fpproxy.key

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN EC PARAMETERS-----
2+
BggqhkjOPQMBBw==
3+
-----END EC PARAMETERS-----
4+
-----BEGIN EC PRIVATE KEY-----
5+
MHcCAQEEIGfj1mtowe1WiAMA3mK1VjgXV1lgUkliUxnk6lr5y/g5oAoGCCqGSM49
6+
AwEHoUQDQgAEM6QxvgVv7Bph8ReDk6zMDKM2pynw8tOpILZ9x8KBfdaqC+rhV/jo
7+
XgVWIMrKj4T7p86bcxq4jdWDYUYpRd/2Og==
8+
-----END EC PRIVATE KEY-----

gen-ca.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
openssl ecparam -out fpproxy.key -name prime256v1 -genkey
2+
openssl req -new -sha256 -key fpproxy.key -out fpproxy.csr
3+
openssl x509 -req -sha256 -days 36500 -in fpproxy.csr -signkey fpproxy.key -out fpproxy.crt

main.go

Lines changed: 129 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package main
22

33
import (
44
"bufio"
5+
"bytes"
56
"context"
7+
"crypto/tls"
68
"encoding/json"
79
"flag"
810
"fmt"
11+
"io/ioutil"
912
"log"
1013
"net"
1114
"net/http"
@@ -130,86 +133,155 @@ func setContentType(r *http.Request, resp *http.Response) {
130133

131134
rext := filepath.Ext(resp.Header.Get("ZIPSVR_FILENAME"))
132135
ext := filepath.Ext(r.URL.Path)
136+
mime := ""
133137

134-
//If the request already has an extension, just use it.
138+
// If the request already has an extension, fetch the mime via extension
135139
if ext != "" {
136140
resp.Header.Set("Content-Type", proxySettings.ExtMimeTypes[ext[1:]])
137-
return
141+
mime = proxySettings.ExtMimeTypes[ext[1:]]
138142
}
139143

140-
//If the response has an extension, use that.
141-
if rext != "" {
144+
// If the response has an extension, try and fetch the mime for that via extension
145+
if mime == "" && rext != "" {
142146
resp.Header.Set("Content-Type", proxySettings.ExtMimeTypes[rext[1:]])
143-
return
147+
mime = proxySettings.ExtMimeTypes[rext[1:]]
144148
}
145149

146-
//Finally, just use the default type
147-
resp.Header.Set("Content-Type", proxySettings.ExtMimeTypes["default"])
150+
if mime == "" {
151+
//Finally, just use the default type
152+
mime = proxySettings.ExtMimeTypes["default"]
153+
}
148154

155+
// If mime isn't accepted (and an accept header is given), then falsify the mime type
156+
accepted := r.Header.Get("Accept")
157+
if accepted != "" {
158+
if !strings.Contains(accepted, mime) {
159+
mime = strings.Split(accepted, ",")[0]
160+
}
161+
}
162+
163+
// Set content type header
164+
resp.Header.Set("Content-Type", mime)
149165
}
150166

151-
func main() {
152-
//Handle the re-routing to local files or what not.
153-
proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
154-
// Remove port from host if exists (old apps don't clean it before sending requests?)
155-
r.URL.Host = strings.Split(r.URL.Host, ":")[0]
156-
fmt.Printf("Proxy Request: %s\n", r.URL.Host+r.URL.Path)
157-
newURL := *r.URL
158-
if r.TLS == nil {
159-
//HTTP request
160-
newURL.Path = "content/" + r.URL.Host + r.URL.Path
161-
newURL.Host = "127.0.0.1:" + proxySettings.ServerHTTPPort
162-
} else {
163-
//HTTPS request, currently goes to the same server
164-
newURL.Path = "content/" + r.URL.Host + r.URL.Path
165-
newURL.Host = "127.0.0.1:" + proxySettings.ServerHTTPSPort
166-
}
167+
func handleRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
168+
// Remove port from host if exists (old apps don't clean it before sending requests?)
169+
r.URL.Host = strings.Split(r.URL.Host, ":")[0]
170+
fmt.Printf("Proxy Request: %s\n", r.URL.Host+r.URL.Path)
171+
172+
// Copy the original request
173+
gamezipRequest := &http.Request{
174+
Method: r.Method,
175+
URL: &url.URL{
176+
Scheme: "http",
177+
Host: "127.0.0.1:" + proxySettings.ServerHTTPPort,
178+
Path: "content/" + r.URL.Host + r.URL.Path,
179+
RawQuery: r.URL.RawQuery,
180+
},
181+
Header: make(http.Header),
182+
Body: r.Body,
183+
}
184+
185+
// Clone the body into both requests by reading and making 2 new readers
186+
contents, _ := ioutil.ReadAll(r.Body)
187+
gamezipRequest.Body = ioutil.NopCloser(bytes.NewReader(contents))
188+
189+
// Make the request to the zip server.
190+
client := &http.Client{}
191+
proxyReq, err := http.NewRequest(gamezipRequest.Method, gamezipRequest.URL.String(), gamezipRequest.Body)
192+
if err != nil {
193+
fmt.Printf("UNHANDLED GAMEZIP ERROR: %s\n", err)
194+
}
195+
proxyReq.Header = gamezipRequest.Header
196+
proxyResp, err := client.Do(proxyReq)
167197

168-
// Make the request to the zip server.
169-
client := &http.Client{}
170-
proxyReq, err := http.NewRequest(r.Method, newURL.String(), r.Body)
171-
proxyReq.Header = r.Header
172-
proxyResp, err := client.Do(proxyReq)
198+
if proxyResp.StatusCode < 400 {
199+
fmt.Printf("\tServing from Zip...\n")
200+
}
173201

174-
if proxyResp.StatusCode < 400 {
175-
fmt.Printf("\tServing from Zip...\n")
202+
// Check Legacy
203+
if proxyResp.StatusCode >= 400 {
204+
// Copy the original request
205+
legacyRequest := &http.Request{
206+
Method: r.Method,
207+
URL: &url.URL{
208+
Scheme: "http",
209+
Host: r.URL.Host,
210+
Path: r.URL.Path,
211+
RawQuery: r.URL.RawQuery,
212+
},
213+
Header: make(http.Header),
214+
Body: r.Body,
215+
}
216+
// Copy in a new body reader
217+
legacyRequest.Body = ioutil.NopCloser(bytes.NewReader(contents))
218+
219+
port := proxySettings.LegacyPHPPort
220+
221+
// Set the Proxy URL and apply it to the Transpor layer so that the request respects the proxy.
222+
proxyURL, _ := url.Parse("http://127.0.0.1:" + port)
223+
proxy := http.ProxyURL(proxyURL)
224+
transport := &http.Transport{Proxy: proxy}
225+
226+
// A custom Dialer is required for the "localflash" urls, instead of using the DNS, we use this.
227+
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
228+
//Set Dialer timeout and keepalive to 30 seconds and force the address to localhost.
229+
dialer := &net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}
230+
addr = "127.0.0.1:" + port
231+
return dialer.DialContext(ctx, network, addr)
176232
}
177233

178-
// Check Legacy
179-
if proxyResp.StatusCode >= 400 {
234+
// Make the request with the custom transport
235+
client := &http.Client{Transport: transport, Timeout: 300 * time.Second}
236+
legacyResp, err := client.Do(legacyRequest)
237+
// An error occured, log it for debug purposes
238+
if err == nil {
180239
fmt.Printf("\tServing from Legacy...\n")
240+
proxyResp = legacyResp
241+
} else {
242+
fmt.Printf("UNHANDLED LEGACY ERROR: %s\n", err)
243+
fmt.Printf("\tfailure legacy\n")
244+
}
245+
}
181246

182-
port := proxySettings.LegacyPHPPort
247+
// Update the content type based upon ext for now.
248+
setContentType(r, proxyResp)
183249

184-
// Set the Proxy URL and apply it to the Transpor layer so that the request respects the proxy.
185-
proxyURL, _ := url.Parse("http://127.0.0.1:" + port)
186-
proxy := http.ProxyURL(proxyURL)
187-
transport := &http.Transport{Proxy: proxy}
250+
// Add extra headers
251+
proxyResp.Header.Set("Access-Control-Allow-Origin", "*")
252+
// Keep Alive
253+
if strings.ToLower(r.Header.Get("Connection")) == "keep-alive" {
254+
proxyResp.Header.Set("Connection", "Keep-Alive")
255+
proxyResp.Header.Set("Keep-Alive", "timeout=5; max=100")
256+
}
188257

189-
// A custom Dialer is required for the "localflash" urls, instead of using the DNS, we use this.
190-
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
191-
//Set Dialer timeout and keepalive to 30 seconds and force the address to localhost.
192-
dialer := &net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}
193-
addr = "127.0.0.1:" + port
194-
return dialer.DialContext(ctx, network, addr)
195-
}
258+
return r, proxyResp
259+
}
196260

197-
// TODO: Investigate if I need to blank this out... I don't think this is required.
198-
r.RequestURI = ""
261+
func main() {
262+
// To create CA cert, refer to https://wiki.mozilla.org/SecurityEngineering/x509Certs#Self_Signed_Certs
263+
// Replace CA in GoProxy
264+
certFile := "fpproxy.crt"
265+
keyFile := "fpproxy.key"
199266

200-
// Make the request with the custom transport.
201-
client := &http.Client{Transport: transport, Timeout: 300 * time.Second}
202-
proxyResp, err = client.Do(r)
203-
}
267+
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
268+
if err != nil {
269+
panic(err)
270+
}
204271

205-
// An error occured, log it for debug purposes
206-
if err != nil {
207-
fmt.Printf("UNHANDLED ERROR: %s\n", err)
208-
}
272+
goproxy.MitmConnect.TLSConfig = goproxy.TLSConfigFromCA(&cert)
273+
274+
// Handle HTTPS requests (DOES NOT HANDLE HTTP)
275+
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
276+
proxy.OnRequest().HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
277+
_, resp := handleRequest(req, ctx)
278+
resp.Write(client)
279+
client.Close()
280+
})
209281

210-
// Update the content type based upon ext for now.
211-
setContentType(r, proxyResp)
212-
return r, proxyResp
282+
// Handle HTTP requests (DOES NOT HANDLE HTTPS)
283+
proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
284+
return handleRequest(r, ctx)
213285
})
214286

215287
//Start ZIP server

proxySettings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
"htm": "text/html",
150150
"html": "text/html",
151151
"js": "text/javascript",
152+
"json": "application/json",
152153
"xpg": "text/x-xpg",
153154
"svf": "vector/x-svf",
154155
"afl": "video/animaflex",

0 commit comments

Comments
 (0)