203 lines
5.3 KiB
Go
203 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
|
|
"github.com/elazarl/goproxy"
|
|
|
|
"github.com/tidwall/sjson"
|
|
"vaclive.party/software/lychee-slicer/patcher"
|
|
"vaclive.party/software/lychee-slicer/platform"
|
|
)
|
|
|
|
const ip = "127.0.0.1"
|
|
|
|
type graphqlRequest struct {
|
|
OperationName string `json:"operationName"`
|
|
}
|
|
|
|
type userData struct {
|
|
operationName string
|
|
}
|
|
|
|
func main() {
|
|
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
|
|
program := flag.String("program", "", "program to patch and/or proxy")
|
|
runPatch := flag.Bool("patch", false, "only patch the program")
|
|
runProxy := flag.Bool("proxy", false, "only start the proxy and program")
|
|
flag.Parse()
|
|
if *program == "" {
|
|
*program = platform.FindProgram()
|
|
if *program == "" {
|
|
flag.Usage()
|
|
log.Fatal("Program not specified")
|
|
}
|
|
}
|
|
|
|
if !*runPatch && !*runProxy {
|
|
log.Println("No flags specified, running both patch and proxy")
|
|
*runProxy = true
|
|
*runPatch = true
|
|
}
|
|
|
|
if *runPatch {
|
|
err := patcher.Patch(*program)
|
|
if err != nil {
|
|
log.Fatal("Patch:", err)
|
|
}
|
|
}
|
|
|
|
if !*runProxy {
|
|
return
|
|
}
|
|
|
|
log.Println("Starting proxy")
|
|
proxy := goproxy.NewProxyHttpServer()
|
|
proxy.Verbose = *verbose
|
|
|
|
host := "graph.mango3d.io:443"
|
|
proxy.OnRequest(goproxy.ReqHostIs(host)).HandleConnectFunc(goproxy.AlwaysMitm)
|
|
proxy.OnRequest(goproxy.ReqHostIs(host)).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (ret_req *http.Request, ret_res *http.Response) {
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
ctx.Warnf("Panic: %v", e)
|
|
ret_req = req
|
|
ret_res = goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusBadGateway, "Panic")
|
|
}
|
|
}()
|
|
|
|
if req.Method != "POST" {
|
|
return req, nil
|
|
}
|
|
|
|
body, err := io.ReadAll(req.Body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
req.Body.Close()
|
|
req.Body = io.NopCloser(bytes.NewBuffer(body))
|
|
|
|
var graphql graphqlRequest
|
|
if err := json.Unmarshal(body, &graphql); err != nil {
|
|
ctx.Warnf("Unmarshal: %v", err)
|
|
return req, nil
|
|
}
|
|
|
|
ctx.UserData = &userData{operationName: graphql.OperationName}
|
|
|
|
fmt.Println("OperationName:", graphql.OperationName)
|
|
return req, nil
|
|
})
|
|
|
|
modifyResponse := func(name string, callback func(string) string) {
|
|
proxy.OnResponse(operationNameIs(name)).DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) (ret *http.Response) {
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
ret = goproxy.NewResponse(resp.Request, goproxy.ContentTypeText, http.StatusBadGateway, "Panic")
|
|
}
|
|
}()
|
|
|
|
bb, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
resp.Body.Close()
|
|
|
|
b := string(bb)
|
|
log.Println(name+":", b)
|
|
|
|
b = callback(b)
|
|
|
|
resp.Body = io.NopCloser(bytes.NewBuffer([]byte(b)))
|
|
return resp
|
|
})
|
|
}
|
|
|
|
modifyResponse("GetUser", func(b string) string {
|
|
b, _ = sjson.Set(b, "data.user.status.isPro", true)
|
|
b, _ = sjson.Set(b, "data.user.status.isLifetime", true)
|
|
b, _ = sjson.Set(b, "data.user.status.type", "lifetime")
|
|
b, _ = sjson.Set(b, "data.user.status.featureTags", []string{"LYCHEE_SLICER_PRO_SLA", "LYCHEE_SLICER_PRO_FDM", "LYCHEE_SLICER_PREMIUM"})
|
|
b, _ = sjson.Set(b, "data.user.licenses.0.macAddressesLimit", 9999999)
|
|
return b
|
|
})
|
|
|
|
modifyResponse("userCheck", func(b string) string {
|
|
b, _ = sjson.Set(b, "data.consumer.user.status.isPremium", true)
|
|
b, _ = sjson.Set(b, "data.consumer.user.status.isPro", true)
|
|
b, _ = sjson.Set(b, "data.consumer.user.status.isLifetime", true)
|
|
b, _ = sjson.Set(b, "data.consumer.user.status.featureTags", []string{"LYCHEE_SLICER_PRO_SLA", "LYCHEE_SLICER_PRO_FDM", "LYCHEE_SLICER_PREMIUM"})
|
|
b, _ = sjson.Set(b, "data.consumer.user.status.type", "lifetime")
|
|
return b
|
|
})
|
|
|
|
modifyResponse("checkLicenseWithoutDuration", func(b string) string {
|
|
b, _ = sjson.Set(b, "data.checkLicenseWithoutDuration.isPro", true)
|
|
b, _ = sjson.Set(b, "data.checkLicenseWithoutDuration.isLifetime", true)
|
|
b, _ = sjson.Set(b, "data.checkLicenseWithoutDuration.type", "lifetime")
|
|
b, _ = sjson.Set(b, "data.checkLicenseWithoutDuration.mac_addresses_limit", 9999999)
|
|
return b
|
|
})
|
|
|
|
modifyResponse("getLicense", func(b string) string {
|
|
return b
|
|
})
|
|
|
|
modifyResponse("CanUserGetTrial", func(b string) string {
|
|
return b
|
|
})
|
|
|
|
port, err := getFreePort()
|
|
if err != nil {
|
|
log.Fatal("GetFreePort:", err)
|
|
}
|
|
|
|
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
|
|
if err != nil {
|
|
log.Fatal("Listen:", err)
|
|
}
|
|
|
|
log.Println("Listening on", ln.Addr().String(), port)
|
|
|
|
go func() {
|
|
cmd := exec.Command(*program, fmt.Sprintf("--proxy-server=%s:%d", ip, port), "--ignore-certificate-errors")
|
|
cmd.Args = append(cmd.Args, flag.Args()...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
log.Fatal(cmd.Run())
|
|
os.Exit(0)
|
|
}()
|
|
|
|
log.Fatal(http.Serve(ln, proxy))
|
|
}
|
|
|
|
func getFreePort() (port int, err error) {
|
|
var a *net.TCPAddr
|
|
if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil {
|
|
var l *net.TCPListener
|
|
if l, err = net.ListenTCP("tcp", a); err == nil {
|
|
defer l.Close()
|
|
return l.Addr().(*net.TCPAddr).Port, nil
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func operationNameIs(operationName string) goproxy.RespCondition {
|
|
return goproxy.RespConditionFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) bool {
|
|
if ctx.UserData == nil {
|
|
return false
|
|
}
|
|
userData := ctx.UserData.(*userData)
|
|
return userData.operationName == operationName
|
|
})
|
|
}
|