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.
فیلدهای اجباری
| فیلد | نوع | توضیح |
|---|---|---|
settings | object | اجباری است و نباید خالی باشد. |
settings.auth-client-node-name | string | اجباری است مگر اینکه no-auth صریحاً true باشد. باید به یک نود AuthenticationClient موجود اشاره کند و نمیتواند خود همین Socks5Server باشد. |
settings.no-auth | boolean | فقط وقتی لازم است که auth client ندارید؛ برای فعال شدن حالت بدون احراز هویت باید true باشد. |
settings.ipv4 / settings.udp-ipv4 | string | وقتی udp فعال است اجباری است. آدرس IPv4 که در پاسخ UDP ASSOCIATE اعلام میشود. |
next | string | اجباری است و به مسیر outbound واقعی اشاره میکند. |
حداقل یکی از connect یا udp باید فعال باشد.
فیلدهای اختیاری
| فیلد | پیشفرض | توضیح |
|---|---|---|
settings.connect | true | فعالسازی CONNECT. |
settings.udp | false | فعالسازی UDP ASSOCIATE و پردازش datagramهای UDP. |
settings.verbose | false | log بیشتر، از جمله دلیل رد شدن auth. |
settings.sweep-interval-ms | 1000 | به 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 تبدیل میشود:
- مقصد درخواستی SOCKS داخل
line->dest_ctxکپی میشود. - protocol مقصد TCP تنظیم میشود.
Initبه سمت نود بعدی ارسال میشود.- payloadها تا برقرار شدن سمت بعدی در صف میمانند.
- وقتی
Estاز سمت بعدی برگشت، پاسخ موفق SOCKS5 به client فرستاده میشود. - سپس
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 برسد:
- association مربوط به sender lookup میشود.
- header مربوط به SOCKS5 UDP اعتبارسنجی میشود.
- اگر
FRAG != 0باشد، datagram محافظهکارانه نادیده گرفته میشود. - مقصد remote از هدر خوانده میشود.
- برای آن مقصد یک backend UDP line داخلی ساخته یا reuse میشود.
- هدر SOCKS5 UDP حذف میشود و فقط payload از backend line به سمت بالا ارسال میشود.
وقتی پاسخ از نود بعدی برگردد:
- payload در یک header پاسخ SOCKS5 UDP قرار میگیرد.
- source address داخل header همان remote destination مربوط به backend UDP line است.
- 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 انجام میشود
متادیتای نود
| ویژگی | مقدار |
|---|---|
| flag | kNodeFlagChainHead |
| previous node | مجاز |
| next node | اجباری |
| layer group | kNodeLayerAnything |
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فعال نکنید. - بعد از
Socks5Serverauthenticated بهصورت دستیUserControllerنگذارید. - انتظار پشتیبانی از
BINDیا reassembly برای UDP fragmentation نداشته باشید.