|
@@ -0,0 +1,84 @@
|
|
|
|
|
+package clashapi
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "context"
|
|
|
|
|
+ "net/http"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/sagernet/sing-box/adapter"
|
|
|
|
|
+ "github.com/sagernet/sing/common"
|
|
|
|
|
+ "github.com/sagernet/sing/service"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/go-chi/chi/v5"
|
|
|
|
|
+ "github.com/go-chi/render"
|
|
|
|
|
+ "github.com/gofrs/uuid/v5"
|
|
|
|
|
+ "howett.net/plist"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+func mitmRouter(ctx context.Context) http.Handler {
|
|
|
|
|
+ r := chi.NewRouter()
|
|
|
|
|
+ r.Get("/mobileconfig", getMobileConfig(ctx))
|
|
|
|
|
+ r.Get("/certificate", getCertificate(ctx))
|
|
|
|
|
+ return r
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func getMobileConfig(ctx context.Context) http.HandlerFunc {
|
|
|
|
|
+ return func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
|
+ engine := service.FromContext[adapter.MITMEngine](ctx)
|
|
|
|
|
+ if engine == nil {
|
|
|
|
|
+ http.NotFound(writer, request)
|
|
|
|
|
+ render.PlainText(writer, request, "MITM not enabled")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ certificate := engine.ExportCertificate()
|
|
|
|
|
+ if certificate == nil {
|
|
|
|
|
+ http.NotFound(writer, request)
|
|
|
|
|
+ render.PlainText(writer, request, "Certificate not configured")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ writer.Header().Set("Content-Type", "application/x-apple-aspen-config")
|
|
|
|
|
+ uuidGen := common.Must1(uuid.NewV4()).String()
|
|
|
|
|
+ mobileConfig := map[string]interface{}{
|
|
|
|
|
+ "PayloadContent": []interface{}{
|
|
|
|
|
+ map[string]interface{}{
|
|
|
|
|
+ "PayloadCertificateFileName": "Certificate.cer",
|
|
|
|
|
+ "PayloadContent": certificate.Raw,
|
|
|
|
|
+ "PayloadDescription": "Adds a root certificate",
|
|
|
|
|
+ "PayloadDisplayName": certificate.Subject.CommonName,
|
|
|
|
|
+ "PayloadIdentifier": "com.apple.security.root." + uuidGen,
|
|
|
|
|
+ "PayloadType": "com.apple.security.root",
|
|
|
|
|
+ "PayloadUUID": uuidGen,
|
|
|
|
|
+ "PayloadVersion": 1,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "PayloadDisplayName": certificate.Subject.CommonName,
|
|
|
|
|
+ "PayloadIdentifier": "io.nekohasekai.sfa.ca.profile." + uuidGen,
|
|
|
|
|
+ "PayloadRemovalDisallowed": false,
|
|
|
|
|
+ "PayloadType": "Configuration",
|
|
|
|
|
+ "PayloadUUID": uuidGen,
|
|
|
|
|
+ "PayloadVersion": 1,
|
|
|
|
|
+ }
|
|
|
|
|
+ encoder := plist.NewEncoder(writer)
|
|
|
|
|
+ encoder.Indent("\t")
|
|
|
|
|
+ encoder.Encode(mobileConfig)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func getCertificate(ctx context.Context) http.HandlerFunc {
|
|
|
|
|
+ return func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
|
+ engine := service.FromContext[adapter.MITMEngine](ctx)
|
|
|
|
|
+ if engine == nil {
|
|
|
|
|
+ http.NotFound(writer, request)
|
|
|
|
|
+ render.PlainText(writer, request, "MITM not enabled")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ certificate := engine.ExportCertificate()
|
|
|
|
|
+ if certificate == nil {
|
|
|
|
|
+ http.NotFound(writer, request)
|
|
|
|
|
+ render.PlainText(writer, request, "Certificate not configured")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ writer.Header().Set("Content-Type", "application/x-x509-ca-cert")
|
|
|
|
|
+ writer.Header().Set("Content-Disposition", "attachment; filename=Certificate.crt")
|
|
|
|
|
+ writer.Write(certificate.Raw)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|