הוספת Outline SDK לאפליקציה לנייד

במאמר הזה מוסבר איך לשלב את Outline SDK באפליקציות לנייד, עם דגש על ספריית MobileProxy לניהול פשוט של פרוקסי מקומי.

MobileProxy היא ספרייה מבוססת-Go שנועדה לייעל את השילוב של פונקציות proxy באפליקציות לנייד. הוא משתמש ב-Go Mobile כדי ליצור ספריות לנייד, וכך מאפשר לכם להגדיר את ספריות הרשת של האפליקציה להפניית תנועה דרך שרת proxy מקומי.

אפליקציה ללא MobileProxy

אפליקציית תוכן ללא MobileProxy

אפליקציה עם MobileProxy

אפליקציית תוכן עם MobileProxy

שלב 1: בניית ספריות לנייד של MobileProxy

משתמשים ב-gomobile כדי לקמפל את קוד Go לספריות ל-Android ול-iOS.

  1. משכפלים את מאגר Outline SDK:

    git clone https://github.com/Jigsaw-Code/outline-sdk.git
    cd outline-sdk/x
    
  2. יוצרים את הקבצים הבינאריים של Go Mobile באמצעות go build:

    go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
    

    הוספת תמיכה ב-Psiphon

    כדי להוסיף תמיכה לשימוש ברשת Psiphon, צריך לבצע את השלבים הנוספים הבאים:

    • כדי לקבל קובץ הגדרות שיאפשר לכם גישה לרשת שלהם, צריך לפנות לצוות של Psiphon. יכול להיות שיהיה צורך בחוזה.
    • מוסיפים את תצורת Psiphon שהתקבלה לקטע fallback בתצורת SmartDialer.
    • מריצים את הפקודה -tags psiphon כדי ליצור את פרוקסי לנייד:

      go build -tags psiphon -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
      
    • רושמים את Psiphon בחייגן החכם בקוד המקורי.

    השימוש בדגל -tags psiphon נדרש כי בסיס הקוד של Psiphon מורשה במסגרת GPL, שיכולה להטיל הגבלות על הרישיון של הקוד שלכם. אולי כדאי לך לקבל מהם רישיון מיוחד.

  3. יוצרים ספריות לנייד ומוסיפים אותן לפרויקט:

    Android

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    ב-Android Studio, בוחרים באפשרות File > Import Project…‎ (קובץ > ייבוא פרויקט…) כדי לייבא את חבילת out/mobileproxy.aar שנוצרה. לקבלת עזרה נוספת, אפשר לעיין במאמר Building and deploying to Android (בנייה ופריסה ב-Android) של Go Mobile.

    iOS

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "$(pwd)/out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    גוררים את חבילת out/mobileproxy.xcframework לפרויקט Xcode. למידע נוסף, אפשר לעיין במאמר יצירה ופריסה ב-iOS של Go Mobile.

שלב 2: מריצים את MobileProxy

  1. מאתחלים ומפעילים את ה-proxy המקומי MobileProxy בזמן הריצה של האפליקציה. אפשר להשתמש בהגדרת העברה סטטית או ב-Smart Proxy כדי לבחור שיטה באופן דינמי.
  • הגדרת העברה סטטית: משתמשים בפונקציה RunProxy עם כתובת מקומית והגדרת העברה.

    Android

    import mobileproxy.*
    
    val dialer = StreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    let dialer = MobileproxyStreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    let proxy = MobileproxyRunProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    
  • Smart Proxy: ה-Smart Proxy בוחר באופן דינמי אסטרטגיות DNS ו-TLS על סמך דומיינים שצוינו לבדיקה. צריך לציין את אסטרטגיית ההגדרה בפורמט YAML (דוגמה).

    Android

    val testDomains = Mobileproxy.newListFromLines("www.youtube.com\ni.ytimg.com")
    val strategiesConfig = "..."  // Config YAML.
    val dialer = Mobileproxy.newSmartStreamDialer(testDomains, strategiesConfig, Mobileproxy.newStderrLogWriter())
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    var dialerError: NSError?
    let testDomains = MobileproxyNewListFromLines("www.youtube.com\ni.ytimg.com")
    let strategiesConfig = "..."  // Config YAML.
    let dialer = MobileproxyNewSmartStreamDialer(
        testDomains,
        strategiesConfig,
        MobileproxyNewStderrLogWriter(),
        &dialerError
    )
    
    var proxyError: NSError?
    // Use port zero to let the system pick an open port for you.
    MobileproxyRunProxy("localhost:0", dialer, &proxyError)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    
  1. לאחר מכן, אם אתם משתמשים ב-Psiphon, עליכם לרשום את Psiphon באפשרויות של Smart Dialer בקוד המקורי.

Android:

import mobileproxy.Mobileproxy
import psiphon.Psiphon

// ...

val testDomains = Mobileproxy.newListFromLines("www.google.com\ni.ytimg.com")
// You can get a Psiphon config from the Psiphon team at sponsor@psiphon.ca.
val psiphonConfig = "<YOUR_PSIPHON_CONFIG_JSON_HERE>"
val config = """
dns:
  - {system: {}}
tls:
  - ""
fallback:
  - {"psiphon": \(psiphonConfig)}
"""

val options = Mobileproxy.newSmartDialerOptions(testDomains, config)

// Register Psiphon
Psiphon.registerConfig(options, "psiphon")

try {
    // Create the dialer
    val dialer = options.newStreamDialer()
    // ... use the dialer
} catch (e: Exception) {
    // Handle error
}

iOS:

import Mobileproxy
import Psiphon

// ...

let testDomains = MobileproxyNewListFromLines("www.google.com\ni.ytimg.com")
// You can get a Psiphon config from the Psiphon team at sponsor@psiphon.ca.
let psiphonConfig = "<YOUR_PSIPHON_CONFIG_JSON_HERE>"
let config = """
dns:
  - {system: {}}
tls:
  - ""
fallback:
  - {"psiphon": \(psiphonConfig)}
"""

let options = MobileproxyNewSmartDialerOptions(testDomains, config)

// Register Psiphon
PsiphonRegisterConfig(options, "psiphon")

do {
    // Create the dialer
    let dialer = try options.newStreamDialer()
    // ... use the dialer
} catch {
    // Handle error
}

שלב 3: הגדרת לקוחות HTTP וספריות רשת

מגדירים את ספריות הרשת כך שישתמשו בכתובת ובפורט של ה-proxy המקומי.

Dart/Flutter HttpClient

מגדירים את ה-Proxy באמצעות HttpClient.findProxy.

HttpClient client = HttpClient();
client.findProxy = (Uri uri) {
  return "PROXY " + proxy.address();
};

OkHttp ‏ (Android)

מגדירים את ה-Proxy באמצעות OkHttpClient.Builder.proxy.

val proxyConfig = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxy.host(), proxy.port()))
val client = OkHttpClient.Builder().proxy(proxyConfig).build()

‫JVM (Java, ‏ Kotlin)

מגדירים את ה-proxy לשימוש במאפייני המערכת:

System.setProperty("http.proxyHost", proxy.host())
System.setProperty("http.proxyPort", String.valueOf(proxy.port()))
System.setProperty("https.proxyHost", proxy.host())
System.setProperty("https.proxyPort", String.valueOf(proxy.port()))

Android Web View

כדי להחיל הגדרת Proxy על כל תצוגות האינטרנט באפליקציה, משתמשים בספרייה androidx.webview:

ProxyController.getInstance()
    .setProxyOverride(
        ProxyConfig.Builder()
            .addProxyRule(this.proxy!!.address())
            .build(),
        {}, // execution context for the following callback - do anything needed here once the proxy is applied, like refreshing web views
        {} // callback to be called once the ProxyConfig is applied
    )

תצוגת אינטרנט ב-iOS

החל מ-iOS 17, אפשר להוסיף הגדרת שרת proxy ל-WKWebView באמצעות המאפיין WKWebsiteDataStore שלו:

let configuration = WKWebViewConfiguration()
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(proxyHost), port: NWEndpoint.Port(proxyPort)!)
let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint)
let websiteDataStore = WKWebsiteDataStore.default()
websiteDataStore.proxyConfigurations = [proxyConfig]
let webview = WKWebView(configuration: configuration)

מתקדם: יצירת ספרייה מותאמת אישית לנייד

בתרחישי שימוש מתקדמים, אפשר ליצור ספריות משלכם לנייד:

  1. יצירת ספריית Go: פיתוח חבילת Go שעוטפת את הפונקציות הנדרשות של ה-SDK.
  2. יצירת ספריות לנייד: אפשר להשתמש ב-gomobile bind כדי ליצור ארכיונים של Android ‏ (AAR) ומסגרות של Apple. דוגמאות:
  3. שילוב באפליקציה: מוסיפים את הספרייה שנוצרה לאפליקציה לנייד.