پرش به مطلب اصلی

Socks5Server

Socks5Server سمت server پروتکل SOCKS5 را در یک chain واتروال پیاده می‌کند. این نود ترافیک control را از نود قبلی می‌گیرد، method را مذاکره می‌کند، از طریق AuthenticationClient یا حالت صریح no-auth احراز هویت را انجام می‌دهد و درخواست‌های SOCKS5 را به ترافیک واتروال تبدیل می‌کند.

این نود از این موارد پشتیبانی می‌کند:

  • method negotiation در SOCKS5
  • username/password authentication از طریق AuthenticationClient
  • دستور CONNECT
  • دستور UDP ASSOCIATE
  • UDP relay association وابسته به احراز هویت

دستور BIND پشتیبانی نمی‌شود و رد می‌شود.

جایگاه رایج

برای TCP:

TcpListener -> Socks5Server -> TcpConnector

برای TCP و UDP:

TcpListener -> Socks5Server -> TcpConnector
UdpListener -> Socks5Server -> UdpConnector

listener مربوط به TCP، control connection پروتکل SOCKS5 را می‌پذیرد. listener مربوط به UDP نیز فقط پس از ساخته شدن یک association معتبر از طریق همان control connection، datagramهای SOCKS5 را قبول می‌کند.

در حالت احرازشده، UserController را دستی پس از Socks5Server قرار ندهید؛ نود خودش یک UserController داخلی می‌سازد و پیش از next وارد chain می‌کند.

نمونه authenticated

{
"name": "socks-server",
"type": "Socks5Server",
"settings": {
"auth-client-node-name": "auth-client",
"connect": true,
"udp": true,
"ipv4": "0.0.0.0",
"sweep-interval-ms": 1000,
"verbose": false
},
"next": "outbound"
}

auth-client باید نام نودی موجود از نوع AuthenticationClient در همان config باشد. Socks5Server از آن برای lookup استفاده می‌کند و خودش authentication client تازه‌ای نمی‌سازد.

نمونه no-auth

{
"name": "socks-server",
"type": "Socks5Server",
"settings": {
"no-auth": true,
"connect": true,
"udp": false
},
"next": "outbound"
}

دقیقاً یکی از دو حالت احراز هویت باید انتخاب شود: auth-client-node-name یا no-auth: true.

فیلدهای اجباری

فیلدنوعتوضیح
settingsobjectاجباری است و نباید خالی باشد.
settings.auth-client-node-namestringاجباری است مگر اینکه no-auth صریحاً true باشد. باید به یک نود AuthenticationClient موجود اشاره کند و نمی‌تواند خود همین Socks5Server باشد.
settings.no-authbooleanفقط وقتی لازم است که auth client ندارید؛ برای فعال شدن حالت بدون احراز هویت باید true باشد.
settings.ipv4 / settings.udp-ipv4stringوقتی udp فعال است اجباری است. آدرس IPv4 که در پاسخ UDP ASSOCIATE اعلام می‌شود.
nextstringاجباری است و به مسیر outbound واقعی اشاره می‌کند.

حداقل یکی از connect یا udp باید فعال باشد.

فیلدهای اختیاری

فیلدپیش‌فرضتوضیح
settings.connecttrueفعال‌سازی CONNECT.
settings.udpfalseفعال‌سازی UDP ASSOCIATE و پردازش datagramهای UDP.
settings.verbosefalselog بیشتر، از جمله دلیل رد شدن auth.
settings.sweep-interval-ms1000به UserController داخلی در حالت authenticated پاس داده می‌شود.

تنظیمات قدیمی username، password، users و accounts دیگر پذیرفته نمی‌شوند. اگر هرکدام از این کلیدها وجود داشته باشد، ساخت نود شکست می‌خورد. برای احراز هویت از AuthenticationServer همراه AuthenticationClient استفاده کنید، یا صریحاً no-auth: true بگذارید.

مدل احراز هویت

در حالت authenticated، client باید روش username/password را در SOCKS5 پیشنهاد کند. Socks5Server username و password را می‌گیرد و یک رشته lookup با این فرمت می‌سازد:

username:password

سپس همین رشته را از طریق AuthenticationClient به‌عنوان password کاربر lookup می‌کند.

برای userهایی که در AuthenticationServer برای SOCKS5 می‌سازید، جفت credential را دقیقاً با همین فرم username:password داخل فیلد password قرار دهید. فیلد name کاربر برای username روی پروتکل SOCKS5 استفاده نمی‌شود و می‌تواند صرفا metadata مدیریتی باشد.

بعد از auth موفق:

  • user_handle_t برگشتی داخل line state ذخیره می‌شود
  • handle با lineAddUser() به line اضافه می‌شود
  • username/password خام هم به‌عنوان marker credential روی line کپی می‌شود تا ruleهای downstream بتوانند روی username/password route کنند
  • UserController داخلی پیش از رسیدن line به next، محدودیت‌های user از جمله تعداد connection زنده، IP، ترافیک، تاریخ انقضا و وضعیت enabled را اعمال می‌کند

در حالت no-auth یک marker handle خالی anonymous روی line ثبت می‌شود.

username و password در حالت احرازشده باید غیرخالی باشند. NUL داخلی رد می‌شود، چون lookup نهایی به‌صورت C string انجام می‌شود.

رفتار CONNECT

بعد از negotiation و authentication، درخواست CONNECT به یک مسیر stream عادی Waterwall تبدیل می‌شود:

  1. مقصد درخواستی SOCKS داخل line->dest_ctx کپی می‌شود.
  2. protocol مقصد TCP تنظیم می‌شود.
  3. Init به سمت نود بعدی ارسال می‌شود.
  4. payloadها تا برقرار شدن سمت بعدی در صف می‌مانند.
  5. وقتی Est از سمت بعدی برگشت، پاسخ موفق SOCKS5 به client فرستاده می‌شود.
  6. سپس Est به سمت نود قبلی فرستاده و payloadهای صف‌شده ارسال می‌شوند.

اگر connect غیرفعال باشد، درخواست CONNECT با command not supported رد می‌شود.

رفتار UDP ASSOCIATE

UDP ASSOCIATE یک association می‌سازد که مالک آن TCP control line است. این command از همان control line یک stream upstream عادی باز نمی‌کند.

پاسخ UDP associate از این دو مقدار ساخته می‌شود:

  • آدرس settings.ipv4 یا settings.udp-ipv4
  • پورت listener محلی TCP که control connection را قبول کرده است

برای مثال، اگر control connection روی port محلی 443 پذیرفته شده باشد و ipv4 برابر 0.0.0.0 باشد، پاسخ به client عملاً 0.0.0.0:443 را اعلام می‌کند.

اگر hint مربوط به UDP peer در request شامل IP و port مشخص باشد، association با همان source کلیدگذاری می‌شود. اگر address از نوع any باشد، source IP مربوط به TCP control connection استفاده خواهد شد.

مدل امنیتی UDP

listener مربوط به UDP یک open proxy نیست. datagram تنها در این شرایط پذیرفته می‌شود:

  • sender با یک association ثبت‌شده مطابقت داشته باشد
  • association توسط یک TCP control line موفق ساخته شده باشد
  • entry مربوط به association هنوز وجود داشته باشد

وقتی TCP control line بسته شود، UDP association همان لحظه unregister می‌شود و datagramهای بعدی رد می‌شوند.

associationها در یک registry با 64 shard داخل tunnel state نگه داشته می‌شوند. entryها فقط metadata کپی‌شده دارند:

  • generation token
  • worker id مالک
  • user_handle_t احراز هویت‌شده
  • username/password خام کپی‌شده، در حالت authenticated

در registry هیچ line_t * قابل استفاده‌ای ذخیره نمی‌شود. generation token نمی‌گذارد یک control line قدیمی هنگام بسته شدن، association جدیدی با همان key را به اشتباه حذف کند.

رفتار UDP payload

وقتی یک datagram SOCKS UDP برسد:

  1. association مربوط به sender lookup می‌شود.
  2. header مربوط به SOCKS5 UDP اعتبارسنجی می‌شود.
  3. اگر FRAG != 0 باشد، datagram محافظه‌کارانه نادیده گرفته می‌شود.
  4. مقصد remote از هدر خوانده می‌شود.
  5. برای آن مقصد یک backend UDP line داخلی ساخته یا reuse می‌شود.
  6. هدر SOCKS5 UDP حذف می‌شود و فقط payload از backend line به سمت بالا ارسال می‌شود.

وقتی پاسخ از نود بعدی برگردد:

  1. payload در یک header پاسخ SOCKS5 UDP قرار می‌گیرد.
  2. source address داخل header همان remote destination مربوط به backend UDP line است.
  3. datagram حاصل در جهت downstream به سمت UDP listener و client فرستاده می‌شود.

backend UDP lineها lineهای معمول واتروال‌اند که پشت سمت UDP مربوط به client ساخته می‌شوند. این طراحی association رو به client را از مقصدهای outbound جدا نگه می‌دارد و lifecycle معمول lineها را حفظ می‌کند.

رفتار Finish

پیاده‌سازی ترتیب close در Waterwall را حفظ می‌کند:

  • line state محلی پیش از فرستادن Finish واقعی از بین می‌رود
  • control line قبل از مرگ، association UDP خودش را unregister می‌کند
  • backend UDP lineهای داخلی قبل از بسته شدن از client line detach می‌شوند
  • اگر قبل از established شدن لازم باشد پاسخ failure SOCKS5 فرستاده شود، این کار از مسیر close محافظت‌شده در برابر callbackهای re-entrant انجام می‌شود

متادیتای نود

ویژگیمقدار
flagkNodeFlagChainHead
previous nodeمجاز
next nodeاجباری
layer groupkNodeLayerAnything
required_padding_leftبدترین حالت هدر SOCKS5 UDP: 4 + 1 + 255 + 2 بایت

این مقدار padding اجازه می‌دهد header مربوط به SOCKS5 UDP به ابتدای پاسخ اضافه شود، بدون آن‌که قرارداد padding واتروال نقض شود. اگر buffer پاسخ فضای کافی در سمت چپ نداشته باشد، tunnel یک buffer جایگزین می‌گیرد و پیش از افزودن header، payload را در آن کپی می‌کند.

اشتباه‌های رایج

  • username, password, users, یا accounts را تنظیم نکنید؛ این کلیدهای legacy رد می‌شوند.
  • هم‌زمان auth-client-node-name و no-auth: true نگذارید.
  • UDP را بدون ipv4 یا udp-ipv4 فعال نکنید.
  • بعد از Socks5Server authenticated به‌صورت دستی UserController نگذارید.
  • انتظار پشتیبانی از BIND یا reassembly برای UDP fragmentation نداشته باشید.