VPN

Android میاناهای برنامه‌سازی کاربردی‌ای را برای توسعه‌دهندگان فراهم می‌کند تا راهکارهای شبکه خصوصی مجازی (VPN) ایجاد کنند. پس‌از خواندن این راهنما، با نحوه توسعه و آزمایش کردن کارخواه وی‌پی‌ان خودتان برای دستگاه‌های مجهز به Android آشنا خواهید شد.

نمای کلی

«وی‌پی‌ان» به دستگاه‌هایی که ازنظر فیزیکی در شبکه نیستند اجازه می‌دهد به‌طور ایمن به شبکه دسترسی داشته باشند.

‫Android شامل کارخواه VPN داخلی (PPTP و L2TP/IPSec) است که گاهی اوقات VPN قدیمی نامیده می‌شود. ‫Android 4.0 (سطح میانای برنامه کاربردی ۱۴) میاناهای برنامه کاربردی را معرفی کرد تا توسعه‌دهندگان برنامه بتوانند راهکارهای VPN خودشان را ارائه دهند. راه‌حل وی‌پی‌ان خود را در برنامه‌ای بسته‌بندی می‌کنید که افراد روی دستگاه نصب می‌کنند. توسعه‌دهندگان معمولاً به‌یکی از دلایل زیر برنامه VPN می‌سازند:

  • برای ارائه پروتکل‌های VPN که کلاینت داخلی پشتیبانی نمی‌کند.
  • برای کمک به افراد جهت متصل شدن به سرویس VPN بدون پیکربندی پیچیده.

بقیه این راهنما نحوه توسعه برنامه‌های VPN (ازجمله همیشه روشن و به‌ازای هر برنامه VPN) را توضیح می‌دهد و کارخواه VPN داخلی را پوشش نمی‌دهد.

تجربه کاربری

Android میانای کاربری (UI) ارائه می‌دهد تا به افراد کمک کند راهکار VPN شما را پیکربندی، شروع، و متوقف کنند. میانای کاربر سامانه همچنین فردی را که از دستگاه استفاده می‌کند از اتصال فعال وی‌پی‌ان مطلع می‌کند. Android عناصر رابط کاربری زیر را برای اتصال‌های VPN نشان می‌دهد:

  • قبل‌از اینکه برنامه وی‌پی‌ان برای اولین‌بار فعال شود، سیستم کادر گفتگوی درخواست اتصال را نمایش می‌دهد. کادر گفتگویی از فردی که از دستگاه استفاده می‌کند می‌خواهد تأیید کند که به VPN اعتماد دارد و درخواست را بپذیرد.
  • صفحه تنظیمات VPN («تنظیمات» > «شبکه و اینترنت» > VPN) برنامه‌های VPN را که فرد درخواست‌های اتصال را پذیرفته است نشان می‌دهد. دکمه‌ای برای پیکربندی گزینه‌های سیستم یا فراموش کردن VPN وجود دارد.
  • وقتی اتصالی فعال باشد، سینی «تنظیمات فوری» پانل اطلاعاتی را نشان می‌دهد. با تک‌ضرب زدن روی برچسب، کادر گفتگویی با اطلاعات بیشتر و پیوندی به «تنظیمات» نمایش داده می‌شود.
  • نوار وضعیت شامل نماد VPN (کلید) است که نشان‌دهنده اتصال فعال است.

برنامه شما همچنین باید یک واسط کاربر ارائه دهد تا فردی که از دستگاه استفاده می‌کند بتواند گزینه‌های سرویس شما را پیکربندی کند. برای مثال، ممکن است راهکار شما نیاز داشته باشد تنظیمات اصالت‌سنجی حساب را ضبط کند. برنامه‌ها باید میانای کاربر زیر را نشان دهند:

  • کنترل‌هایی برای شروع و توقف دستی اتصال. وی‌پی‌ان همیشه روشن می‌تواند درصورت نیاز متصل شود، اما به افراد اجازه می‌دهد اتصال را در اولین باری که از وی‌پی‌ان شما استفاده می‌کنند پیکربندی کنند.
  • اعلان بسته‌نشدنی وقتی سرویس فعال است. اعلان می‌تواند وضعیت اتصال را نشان دهد یا اطلاعات بیشتری (مثلاً آمار شبکه) ارائه دهد. با تک‌ضرب زدن روی اعلان، برنامه به پیش‌زمینه می‌آید. پس‌از غیرفعال شدن سرویس، اعلان را بردارید.

سرویس VPN

برنامه شما شبکه سیستم را برای کاربر (یا نمایه کاری) به دروازه وی‌پی‌ان متصل می‌کند. هر کاربر (یا نمایه کاری) می‌تواند برنامه VPN متفاوتی را اجرا کند. شما سرویس VPN ایجاد می‌کنید که سیستم از آن برای شروع و توقف VPN شما و پیگیری وضعیت اتصال استفاده می‌کند. سرویس VPN شما از VpnService ارث‌بری می‌کند.

این سرویس همچنین به‌عنوان ظرفی برای اتصالات دروازه VPN و میاناهای دستگاه محلی آن‌ها عمل می‌کند. روش‌های فراخوانی نمونه سرویس شما VpnService.Builder برای ایجاد میانای محلی جدید.

شکل ۱. نحوه اتصال VpnService شبکه‌سازی Android به دروازه وی‌پی‌ان
نمودار معماری بلوکی که نشان می‌دهد VpnService چگونه رابط TUN محلی در شبکه سیستم ایجاد می‌کند.

برنامه شما داده‌های زیر را برای اتصال دستگاه به درگاه VPN منتقل می‌کند:

  • بسته‌های IP خروجی را از توصیفگر فایل میانای محلی می‌خواند، آن‌ها را رمزگذاری می‌کند، و به دروازه وی‌پی‌ان ارسال می‌کند.
  • بسته‌های ورودی (دریافت‌شده و رمزگشایی‌شده از درگاه VPN) را در توصیفگر فایل رابط محلی می‌نویسد.

فقط یک سرویس فعال برای هر کاربر یا نمایه وجود دارد. شروع سرویس جدید، سرویس موجود را به‌طور خودکار متوقف می‌کند.

افزودن سرویس

برای افزودن سرویس وی‌پی‌ان به برنامه‌تان، سرویس Android را ایجاد کنید که از VpnService ارث می‌برد. سرویس وی‌پی‌ان را در فایل مانیفست برنامه‌تان با افزوده‌های زیر اعلام کنید:

  • خدمات را با اجازه BIND_VPN_SERVICE محافظت کنید تا فقط سیستم بتواند به خدمات شما متصل شود.
  • سرویس را با فیلتر قصد "android.net.VpnService" تبلیغ کنید تا سیستم بتواند سرویس شما را پیدا کند.

این مثال نشان می‌دهد که چگونه می‌توانید این سرویس را در فایل مانیفست برنامه‌تان اعلام کنید:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
</service>

اکنون که برنامه‌تان سرویس را اعلام می‌کند، سیستم می‌تواند سرویس VPN برنامه شما را درصورت نیاز به‌طور خودکار شروع و متوقف کند. برای مثال، وقتی وی‌پی‌ان همیشه روشن درحال اجرا است، سیستم سرویس شما را کنترل می‌کند.

آماده کردن سرویس

برای آماده کردن برنامه برای تبدیل شدن به سرویس VPN فعلی کاربر، VpnService.prepare() را فراخوانی کنید. اگر فردی که از دستگاه استفاده می‌کند قبلاً به برنامه شما اجازه نداده باشد، این روش هدف فعالیت را برمی‌گرداند. از این هدف برای شروع فعالیت سیستم که درخواست اجازه می‌کند استفاده می‌کنید. سیستم کادر گفتگویی شبیه به کادرهای گفتگوی اجازه‌های دیگر، مثل دسترسی به دوربین یا مخاطبین، نشان می‌دهد. اگر برنامه‌تان ازقبل آماده شده باشد، روش null را برمی‌گرداند.

فقط یک برنامه می‌تواند سرویس VPN آماده فعلی باشد. همیشه VpnService.prepare() تماس بگیرید زیرا ممکن است فرد برنامه دیگری را از زمان آخرین تماس برنامه شما با این روش به‌عنوان سرویس VPN تنظیم کرده باشد. برای اطلاعات بیشتر، بخش چرخه حیات سرویس را ببینید.

متصل کردن سرویس

وقتی سرویس اجرا شد، می‌توانید میانای محلی جدیدی ایجاد کنید که به درگاه VPN متصل است. برای درخواست اجازه و اتصال به سرویس خود به درگاه وی‌پی‌ان، باید مراحل زیر را به این ترتیب تکمیل کنید:

  1. با VpnService.prepare() تماس بگیرید تا اجازه بگیرید (درصورت نیاز).
  2. VpnService.protect() را فراخوانی کنید تا سوکت تونل برنامه‌تان را خارج از VPN سیستم نگه دارید و از اتصال دایره‌ای جلوگیری کنید.
  3. برای متصل کردن سوکت تونل برنامه به دروازه VPN، با DatagramSocket.connect() تماس بگیرید.
  4. روش‌های VpnService.Builder را برای پیکربندی میانای TUN محلی جدید در دستگاه برای ترافیک VPN فراخوانی کنید.
  5. VpnService.Builder.establish() را فراخوانی کنید تا سیستم میانای TUN محلی را ایجاد کند و مسیریابی ترافیک را ازطریق میانای مربوطه آغاز کند.

دروازه VPN معمولاً تنظیماتی را برای میانای TUN محلی درطول دست‌دهی پیشنهاد می‌کند. برنامه شما روش‌های VpnService.Builder را برای پیکربندی سرویس فراخوانی می‌کند، همان‌طور که در نمونه زیر نشان داده شده است:

کاتلین

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
val builder = Builder()

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
val localTunnel = builder
        .addAddress("192.168.2.2", 24)
        .addRoute("0.0.0.0", 0)
        .addDnsServer("192.168.1.1")
        .establish()

جاوا

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
VpnService.Builder builder = new VpnService.Builder();

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
ParcelFileDescriptor localTunnel = builder
    .addAddress("192.168.2.2", 24)
    .addRoute("0.0.0.0", 0)
    .addDnsServer("192.168.1.1")
    .establish();

مثال موجود در بخش وی‌پی‌ان برای هر برنامه پیکربندی IPv6 را نشان می‌دهد که شامل گزینه‌های بیشتری است. قبل‌از اینکه بتوانید میانای جدیدی ایجاد کنید، باید مقادیر VpnService.Builder زیر را اضافه کنید:

addAddress()
حداقل یک نشانی IPv4 یا IPv6 به‌همراه پوشش زیرشبکه اضافه کنید که سیستم به‌عنوان نشانی میانای TUN محلی اختصاص می‌دهد. برنامه شما معمولاً نشانی‌های IP و پوشش‌های زیرشبکه را درطول دست‌دهی از درگاه VPN دریافت می‌کند.
addRoute()
اگر می‌خواهید سیستم ترافیک را ازطریق میانای VPN ارسال کند، حداقل یک مسیر اضافه کنید. مسیرها براساس نشانی‌های مقصد فیلتر می‌شوند. برای پذیرفتن همه ترافیک، مسیر بازی مثل 0.0.0.0/0 یا ::/0 تنظیم کنید.

روش establish() نمونه‌ای از ParcelFileDescriptor را برمی‌گرداند که برنامه شما از آن برای خواندن و نوشتن بسته‌ها در بافر میانای استفاده می‌کند. اگر برنامه شما آماده نباشد یا کسی اجازه را پس بگیرد، روش establish() مقدار null را برمی‌گرداند.

چرخه حیات سرویس

برنامه شما باید وضعیت VPN انتخاب‌شده سیستم و هرگونه اتصال فعال را ردیابی کند. میانای کاربر (UI) برنامه‌تان را به‌روز کنید تا فردی که از دستگاه استفاده می‌کند از هرگونه تغییر مطلع شود.

شروع کردن سرویس

سرویس VPN شما می‌تواند به روش‌های زیر شروع شود:

  • برنامه شما سرویس را شروع می‌کند—معمولاً به این دلیل که فردی روی دکمه اتصال تک‌ضرب زده است.
  • سیستم سرویس را شروع می‌کند زیرا وی‌پی‌ان همیشه روشن روشن است.

برنامه شما سرویس VPN را با ارسال یک هدف به startService() شروع می‌کند. برای کسب اطلاعات بیشتر، شروع کردن سرویس را بخوانید.

سیستم سرویس شما را در پس‌زمینه با فراخوانی onStartCommand() شروع می‌کند. بااین‌حال، Android در نسخه ۸.۰ (سطح میانای برنامه‌سازی کاربردی ۲۶) یا بالاتر محدودیت‌هایی برای برنامه‌های پس‌زمینه‌ای اعمال می‌کند. اگر از این سطوح API پشتیبانی می‌کنید، باید با فراخوانی Service.startForeground()، سرویس خود را به پیش‌زمینه منتقل کنید. برای کسب اطلاعات بیشتر، اجرای سرویس در پیش‌زمینه را بخوانید.

توقف سرویس

فردی که از دستگاه استفاده می‌کند می‌تواند بااستفاده از میانای کاربر برنامه شما، سرویس شما را متوقف کند. به‌جای بستن اتصال، سرویس را متوقف کنید. وقتی فردی که از دستگاه استفاده می‌کند در صفحه VPN برنامه «تنظیمات» کارهای زیر را انجام دهد، سیستم اتصال فعال را نیز متوقف می‌کند:

  • اتصال برنامه VPN را قطع کند یا آن را فراموش کند
  • وی‌پی‌ان همیشه روشن را برای اتصال فعال خاموش می‌کند

سیستم روش onRevoke() سرویس شما را فراخوانی می‌کند اما این فراخوانی ممکن است در رشته اصلی اتفاق نیفتد. وقتی سیستم این روش را فرا می‌خواند، یک میانای شبکه جایگزین ازقبل ترافیک را مسیریابی می‌کند. می‌توانید منابع زیر را با خیال راحت دور بیندازید:

  • با فراخوانی DatagramSocket.close()، سوکت تونل محافظت‌شده را به دروازه وی‌پی‌ان ببندید.
  • توصیفگر فایل بسته را با فراخوانی ParcelFileDescriptor.close() ببندید (نیازی نیست آن را تخلیه کنید).

VPN همیشه فعال

وقتی دستگاه راه‌اندازی می‌شود، Android می‌تواند سرویس وی‌پی‌ان را شروع کند و تا زمانی که دستگاه روشن است آن را اجرا کند. این ویژگی VPN همیشه روشن نام دارد و در Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) یا بالاتر دردسترس است. درحالی‌که Android چرخه حیات سرویس را حفظ می‌کند، سرویس VPN شما مسئول اتصال درگاه VPN است. «وی‌پی‌ان همیشه روشن» می‌تواند اتصال‌هایی را که از وی‌پی‌ان استفاده نمی‌کنند نیز مسدود کند.

تجربه کاربری

در Android 8.0 یا بالاتر، سیستم کادرهای گفتگوی زیر را نمایش می‌دهد تا فردی که از دستگاه استفاده می‌کند از وی‌پی‌ان همیشه روشن مطلع شود:

  • وقتی اتصال‌های وی‌پی‌ان همیشه روشن قطع می‌شود یا نمی‌تواند متصل شود، افراد اعلانی را می‌بینند که نمی‌توانند آن را ببندند. با تک‌ضرب زدن روی اعلان، کادر گفتگویی نشان داده می‌شود که توضیحات بیشتری ارائه می‌دهد. وقتی VPN دوباره متصل شود یا کسی گزینه VPN همیشه روشن را خاموش کند، اعلان ناپدید می‌شود.
  • «وی‌پی‌ان همیشه روشن» به فردی که از دستگاه استفاده می‌کند اجازه می‌دهد هرگونه اتصال شبکه‌ای را که از وی‌پی‌ان استفاده نمی‌کند مسدود کند. وقتی این گزینه را روشن می‌کنید، برنامه «تنظیمات» قبل‌از اینکه VPN متصل شود به افراد هشدار می‌دهد که اتصال اینترنت ندارند. برنامه «تنظیمات» از فردی که از دستگاه استفاده می‌کند می‌خواهد ادامه دهد یا لغو کند.

ازآنجایی‌که سیستم (و نه شخص) اتصال همیشه روشن را شروع و متوقف می‌کند، باید رفتار و میانای کاربر برنامه‌تان را تطبیق دهید:

  1. هر رابط کاربری‌ای را که اتصال را قطع می‌کند غیرفعال کنید زیرا سیستم و برنامه «تنظیمات» اتصال را کنترل می‌کنند.
  2. پیکربندی بین هر شروع برنامه را ذخیره کنید و اتصالی با جدیدترین تنظیمات پیکربندی کنید. چون سیستم برنامه شما را براساس تقاضا راه‌اندازی می‌کند، فردی که از دستگاه استفاده می‌کند ممکن است همیشه نخواهد اتصالی را پیکربندی کند.

همچنین می‌توانید از پیکربندی‌های مدیریت‌شده برای پیکربندی اتصال استفاده کنید. پیکربندی‌های مدیریت‌شده به سرپرست فناوری اطلاعات کمک می‌کند وی‌پی‌ان شما را ازراه‌دور پیکربندی کند.

تشخیص همیشه روشن

Android میاناهای برنامه‌سازی کاربردی برای تأیید اینکه سیستم سرویس VPN شما را راه‌اندازی کرده است یا نه ندارد. اما وقتی برنامه‌تان نمونه‌های سرویسی را که شروع می‌کند پرچم‌گذاری می‌کند، می‌توانید فرض کنید که سیستم سرویس‌های بدون پرچم را برای وی‌پی‌ان همیشه روشن شروع کرده است. مثالی در اینجا آمده است:

  1. برای شروع سرویس VPN، نمونه Intent ایجاد کنید.
  2. با افزودن یک هدف اضافی، سرویس وی‌پی‌ان را علامت‌گذاری کنید.
  3. در روش onStartCommand() سرویس، پرچم را در افزوده‌های آرگومان intent جستجو کنید.

اتصال‌های مسدودشده

فردی که از دستگاه استفاده می‌کند (یا سرپرست فناوری اطلاعات) می‌تواند همه ترافیک را مجبور کند از VPN استفاده کند. سیستم هرگونه ترافیک شبکه‌ای را که از VPN استفاده نمی‌کند مسدود می‌کند. افرادی که از دستگاه استفاده می‌کنند می‌توانند کلید مسدود کردن اتصال‌های بدون وی‌پی‌ان را در پانل گزینه‌های وی‌پی‌ان در «تنظیمات» پیدا کنند.

انصراف از «همیشه روشن»

اگر برنامه شما درحال‌حاضر نمی‌تواند از VPN همیشه روشن پشتیبانی کند، می‌توانید با تنظیم فراداده سرویس SERVICE_META_DATA_SUPPORTS_ALWAYS_ON روی false، از این ویژگی انصراف دهید (در Android نسخه ۸.۱ یا بالاتر). نمونه مانیفست برنامه زیر نحوه افزودن عنصر فراداده را نشان می‌دهد:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
     <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
             android:value=false/>
</service>

وقتی برنامه‌تان از VPN همیشه روشن انصراف می‌دهد، سیستم کنترل‌های میانای کاربر گزینه‌ها را در «تنظیمات» غیرفعال می‌کند.

وی‌پی‌ان برای هر برنامه

برنامه‌های وی‌پی‌ان می‌توانند فیلتر کنند که کدام برنامه‌های نصب‌شده مجازند ترافیک را ازطریق اتصال وی‌پی‌ان ارسال کنند. می‌توانید فهرست مجاز یا فهرست غیرمجاز ایجاد کنید، اما نمی‌توانید هر دو را ایجاد کنید. اگر فهرست‌های مجاز یا غیرمجاز ایجاد نکنید، سیستم همه ترافیک شبکه را ازطریق VPN ارسال می‌کند.

برنامه وی‌پی‌ان شما باید فهرست‌ها را قبل‌از برقراری اتصال تنظیم کند. اگر لازم است فهرست‌ها را تغییر دهید، اتصال VPN جدیدی برقرار کنید. وقتی برنامه‌ای را به فهرست اضافه می‌کنید، باید در دستگاه نصب شده باشد.

کاتلین

// The apps that will have access to the VPN.
val appPackages = arrayOf(
        "com.android.chrome",
        "com.google.android.youtube",
        "com.example.a.missing.app")

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
val builder = Builder()
for (appPackage in appPackages) {
    try {
        packageManager.getPackageInfo(appPackage, 0)
        builder.addAllowedApplication(appPackage)
    } catch (e: PackageManager.NameNotFoundException) {
        // The app isn't installed.
    }
}

// Complete the VPN interface config.
val localTunnel = builder
        .addAddress("2001:db8::1", 64)
        .addRoute("::", 0)
        .establish()

جاوا

// The apps that will have access to the VPN.
String[] appPackages = {
    "com.android.chrome",
    "com.google.android.youtube",
    "com.example.a.missing.app"};

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
VpnService.Builder builder = new VpnService.Builder();
PackageManager packageManager = getPackageManager();
for (String appPackage: appPackages) {
  try {
    packageManager.getPackageInfo(appPackage, 0);
    builder.addAllowedApplication(appPackage);
  } catch (PackageManager.NameNotFoundException e) {
    // The app isn't installed.
  }
}

// Complete the VPN interface config.
ParcelFileDescriptor localTunnel = builder
    .addAddress("2001:db8::1", 64)
    .addRoute("::", 0)
    .establish();

برنامه‌های مجاز

برای افزودن برنامه به فهرست مجاز، با VpnService.Builder.addAllowedApplication() تماس بگیرید. اگر فهرست شامل یک یا چند برنامه باشد، فقط برنامه‌های موجود در فهرست از VPN استفاده می‌کنند. همه برنامه‌های دیگر (که در فهرست نیستند) از شبکه‌های سیستم استفاده می‌کنند، انگار که وی‌پی‌ان اجرا نمی‌شود. وقتی فهرست مجاز خالی باشد، همه برنامه‌ها از VPN استفاده می‌کنند.

برنامه‌های غیرمجاز

برای افزودن برنامه به فهرست غیرمجاز، VpnService.Builder.addDisallowedApplication() را فراخوانی کنید. برنامه‌های غیرمجاز از شبکه سیستم استفاده می‌کنند، گویی VPN درحال اجرا نیست—همه برنامه‌های دیگر از VPN استفاده می‌کنند.

دور زدن VPN

«وی‌پی‌ان» شما می‌تواند به برنامه‌ها اجازه دهد «وی‌پی‌ان» را دور بزنند و شبکه خودشان را انتخاب کنند. برای دور زدن وی‌پی‌ان، هنگام ایجاد رابط وی‌پی‌ان با VpnService.Builder.allowBypass() تماس بگیرید. پس‌از شروع سرویس VPN، نمی‌توانید این مقدار را تغییر دهید. اگر برنامه‌ای فرایند یا سوکت خود را به شبکه خاصی متصل نکند، ترافیک شبکه برنامه ازطریق VPN ادامه می‌یابد.

برنامه‌هایی که به شبکه خاصی متصل می‌شوند، وقتی کسی ترافیکی را که از VPN عبور نمی‌کند مسدود کند، اتصال ندارند. برای ارسال ترافیک ازطریق شبکه خاص، برنامه‌ها قبل‌از متصل کردن سوکت، روش‌هایی مثل ConnectivityManager.bindProcessToNetwork() یا Network.bindSocket() را فراخوانی می‌کنند.

کد نمونه

«پروژه منبع باز Android» شامل برنامه نمونه‌ای به نام ToyVPN است. این برنامه نشان می‌دهد چگونه سرویس وی‌پی‌ان را راه‌اندازی و متصل کنید.