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

SniffRouter

SniffRouter یک content router در لایه ۴ است. این نود نخستین upstream payload هر connection را بررسی می‌کند، بر اساس بایت‌های قابل مشاهده تصمیم می‌گیرد و تمام connection را از نخستین route مناسب عبور می‌دهد.

این نود از Router ساده‌تر و تخصصی‌تر است. اگر فقط به routing بر پایه HTTP/1 Host، TLS ClientHello SNI یا handshake داخلی ReverseClient/ReverseServer نیاز دارید، SniffRouter انتخاب مناسبی است. برای شرط‌هایی مانند source IP، port، GeoIP، GeoSite، username/password یا ترکیب چند شرط، از Router استفاده کنید.

مدل ذهنی

  • upstream Init بلافاصله به شاخه‌ها فرستاده نمی‌شود.
  • نود منتظر اولین upstream payload می‌ماند.
  • payload نخست به‌طور موقت در buffer نگه داشته می‌شود.
  • routeها به ترتیب JSON بررسی می‌شوند.
  • نخستین route مطابق، برنده است.
  • اگر هیچ routeای match نشود، مسیر top-level next استفاده می‌شود.
  • شاخه انتخاب‌شده مقداردهی اولیه می‌شود و بایت‌های bufferشده روی همان شاخه دوباره پخش می‌شوند.
  • payloadهای بعدی دیگر دوباره classify نمی‌شوند.
هشدار

SniffRouter برای انتخاب مسیر باید بایت‌هایی از سمت client ببیند. در protocolهای server-first تا رسیدن upstream payload هیچ شاخه‌ای انتخاب نمی‌شود.

جایگاه رایج

بعد از TLS termination برای HTTP Host:

TcpListener -> TlsServer -> SniffRouter
|-- host a.example.com -> backend_a
|-- host b.example.com -> backend_b
`-- default next -> camouflage_site

قبل از TLS termination برای SNI:

TcpListener -> SniffRouter
|-- SNI a.example.com -> tls_site_a
|-- SNI b.example.com -> tls_site_b
`-- default next -> default_tls_site

برای جدا کردن reverse tunnel از سایت camouflage بعد از decrypt شدن stream:

TcpListener -> TlsServer -> SniffRouter
|-- reverse handshake -> ReverseServer
`-- default next -> TcpConnector nginx

برای تشخیص reverse، handshake باید در حالت decrypt‌شده دیده شود. اگر SniffRouter پیش از TLS termination باشد، handshake هنوز رمز شده است و قابل تشخیص نیست.

نمونه تنظیم

HTTP Host routing:

{
"name": "sniff-router",
"type": "SniffRouter",
"settings": {
"routes": [
{
"domains": [
"a.example.com",
"b.example.com"
],
"next": "example_backend"
},
{
"domains": [
"*.static.example.com"
],
"next": "static_backend"
}
]
},
"next": "default_backend"
}

TLS SNI routing:

{
"name": "sniff-router",
"type": "SniffRouter",
"settings": {
"routes": [
{
"detection": "tls",
"domains": [
"site-a.example.com"
],
"next": "tls_site_a"
},
{
"detection": "tls",
"domains": [
"site-b.example.com"
],
"next": "tls_site_b"
}
]
},
"next": "default_tls_site"
}

فیلدهای اصلی

فیلدنوعتوضیح
namestringنام نود.
typestringباید دقیقاً "SniffRouter" باشد.
nextstringاجباری؛ مسیر fallback وقتی هیچ routeای match نشود.

settings می‌تواند حذف شود یا خالی باشد. در نبود route، همه connectionها به next می‌روند.

تنظیمات

گزینهنوعپیش‌فرضتوضیح
routesarrayunsetفهرست routeها به ترتیب اولویت. اگر نوشته شود باید آرایه‌ای غیرخالی باشد.
reverse-secret-lengthinteger640طول handshake برای reverse detection. بازه معتبر 1..1024.
reverse-secretASCII stringunsetsecret مورد استفاده برای ساخت signature مربوط به reverse handshake. باید با نودهای Reverse یکسان باشد.

تنظیمات reverse handshake حتی در نبود route مربوط به reverse نیز خوانده می‌شوند، اما فقط برای routeهایی اثر دارند که مقدار detection آن‌ها شامل reverse است.

Route Object

فیلدنوعضروریتوضیح
domainsstring یا arrayبرای http1 و tls ضروریdomain patternها.
domainstringنهalias برای یک domain. با domains هم‌زمان استفاده نشود.
detectionstring یا arrayنهپیش‌فرض "http1".
nextstringبلهtarget node برای route.
targetstringنهalias برای route next.

مقدارهای detection:

مقدارمعنی
http1خواندن HTTP/1 Host.
tlsخواندن TLS ClientHello SNI.
reverseتشخیص reverse-link handshake.
reverse-tlsalias برای reverse.
reverse-handshakealias برای reverse.

مقدارهای حذف‌شده:

مقدار قدیمیمقدار جدید
httphttp1
client-hellotls
tls-client-hellotls

اگر route شامل reverse باشد، تشخیص reverse به domains وابسته نیست. با این حال اگر در همان route مقدار http1 یا tls نیز وجود داشته باشد، برای آن روش‌های تشخیص مبتنی بر domain همچنان domains لازم است.

ترتیب Routeها

routeها به ترتیب نوشته‌شده در JSON بررسی می‌شوند و نخستین match برنده است. routeهای خاص را قبل از wildcardها قرار دهید.

{
"routes": [
{
"domain": "api.example.com",
"next": "api_backend"
},
{
"domain": "*.example.com",
"next": "wildcard_backend"
}
]
}

در این مثال، api.example.com به api_backend می‌رود، چون route دقیق پیش از wildcard آمده است. اگر ترتیب برعکس بود، wildcard در صورت تطبیق الگو آن را می‌گرفت. توجه کنید که *.example.com با subdomainها مطابقت دارد، اما با خود example.com نه.

Domain Matching

matching مربوط به domain به حروف کوچک و بزرگ حساس نیست. الگوهای config به lowercase تبدیل می‌شوند و نقطه انتهایی حذف می‌شود.

patternمعنی
example.commatch دقیق.
*.example.comبا subdomainهایی مانند www.example.com مطابقت دارد، اما با خود example.com نه.
*هر Host/SNI غیرخالی.

الگوهای نامعتبر:

  • رشته خالی
  • *. بدون suffix
  • wildcardهایی غیر از فرم *.example.com
  • URL مثل https://example.com
  • host:port مثل example.com:443
  • مقدارهای شامل / یا \

اگر HTTP Host روی wire شامل port باشد، sniffer آن را پیش از matching حذف می‌کند؛ برای نمونه Host: example.com:443 با الگوی example.com مطابقت دارد.

HTTP Host Detection

روش تشخیص پیش‌فرض routeها http1 است:

{
"domains": [
"app.example.com"
],
"next": "app_backend"
}

در حالت http1، SniffRouter انتظار دارد نخستین payload قابل مشاهده یک request از نوع HTTP/1 باشد. سپس header مربوط به Host را می‌خواند و آن را با الگوهای domain در route مقایسه می‌کند.

برای HTTPS معمولاً SniffRouter را بعد از TlsServer می‌گذاریم:

TcpListener -> TlsServer -> SniffRouter

preface مربوط به HTTP/2 cleartext، HTTP/1 Host ندارد؛ بنابراین در نبود match از روش دیگری، ترافیک به next می‌رود.

TLS SNI Detection

برای تطبیق TLS ClientHello SNI از detection: "tls" استفاده کنید:

{
"detection": "tls",
"domains": [
"*.example.com"
],
"next": "tls_branch"
}

این حالت معمولاً قبل از TLS termination استفاده می‌شود:

TcpListener -> SniffRouter -> TlsServer

اگر SNI پیدا نشود یا با routeها match نشود، مسیر fallback یعنی top-level next استفاده می‌شود.

Reverse Detection

reverse handshake داخلی ReverseClient/ReverseServer را تشخیص می‌دهد:

{
"name": "sniff-router",
"type": "SniffRouter",
"settings": {
"routes": [
{
"detection": "reverse",
"next": "reverse_server"
}
]
},
"next": "tcp_to_nginx"
}

این قابلیت زمانی مفید است که یک TLS entrypoint هم reverse tunnel و هم سایت camouflage را با SNI یکسان حمل می‌کند. routing بر پایه Host/SNI نمی‌تواند این دو را جدا کند، اما reverse link پس از decrypt شدن signature مشخصی دارد.

signature پیش‌فرض:

640 bytes of 0xFF

اگر reverse-secret-length یا reverse-secret تنظیم شود، SniffRouter همان الگوریتم ReverseClient و ReverseServer را استفاده می‌کند: byteهای handshake پیش‌فرض تکرار می‌شوند و با byteهای ASCII مربوط به reverse-secret به‌صورت تکرارشونده XOR می‌شوند.

این تنظیمات باید با Reverse nodeها یکی باشند:

{
"settings": {
"reverse-secret-length": 640,
"reverse-secret": "shared-secret",
"routes": [
{
"detection": "reverse",
"next": "reverse_server"
}
]
},
"next": "camouflage_site"
}

نکته‌های مهم:

  • handshake باید دقیقاً ابتدای decrypted stream باشد.
  • SniffRouter فقط بایت‌ها را نگاه می‌کند و آن‌ها را دست‌نخورده برای ReverseServer بازپخش می‌کند.
  • ReverseServer دوباره handshake را اعتبارسنجی می‌کند و از stream برمی‌دارد.
  • reverse route به domains نگاه نمی‌کند.
  • اگر payload نخست فقط بخشی از prefix مربوط به handshake باشد، SniffRouter یک warning ثبت می‌کند و مستقیم مسیر default next را انتخاب می‌کند.
  • اگر proxy جلویی PROXY protocol header اضافه می‌کند، باید قبل از SniffRouter حذف شود.

بافر Payload اول

SniffRouter بایت‌های ابتدایی upstream را تا زمان تعیین route در buffer نگه می‌دارد.

برای HTTP Host و TLS SNI، SniffRouter می‌تواند تا کامل شدن request یا ClientHello مقدار بیشتری byte بخواهد. پنجره sniffing مشترک محدود است:

8192 bytes

تشخیص reverse سخت‌گیرانه‌تر است: اگر payload نخست فقط prefix ناقصی از reverse handshake باشد، نود منتظر بایت‌های بیشتر نمی‌ماند و از next در سطح اصلی به‌عنوان fallback استفاده می‌کند.

پس از انتخاب route، payloadهای بعدی مستقیماً به target انتخاب‌شده یا next پیش‌فرض می‌روند و connection دوباره بررسی نمی‌شود.

Target Branchها

next یا target در هر route نام یک نود واقعی در همان config است. SniffRouter هنگام ساخت chain، targetها را وارد chain خودش می‌کند تا:

  • targetها line state داشته باشند.
  • downstream traffic از branchها دوباره از SniffRouter برگردد.
  • lifecycle chain حفظ شود.

قوانین target:

  • target node باید وجود داشته باشد
  • target نمی‌تواند خود SniffRouter باشد
  • شاخه target نباید از قبل به previous ناسازگاری متصل شده باشد
  • target می‌تواند همان node مربوط به top-level next باشد

اگر شاخه در بخش دیگری از config قرار دارد، یک Bridge را به‌عنوان target قرار دهید.

Lifecycle

callbackرفتار
upstream Initفقط state داخلی SniffRouter را مقداردهی اولیه می‌کند.
اولین upstream Payloadداده را در buffer نگه می‌دارد، route را انتخاب و target/default را مقداردهی اولیه می‌کند و سپس داده را بازپخش می‌کند.
payloadهای بعدیمستقیم به target یا default می‌روند.
upstream Pause / Resumeفقط پس از انتخاب route فرستاده می‌شوند.
upstream Estفقط به شاخه پیش‌فرض فرستاده می‌شود. targetهای route اغلب نودهای adapter/server هستند که upstream Est برای آن‌ها معتبر نیست.
upstream Finishstate داخلی را از بین می‌برد و شاخه انتخاب‌شده را با Finish می‌بندد.
downstream callbacksبه previous node برمی‌گردند.
downstream Finishپیش از فرستادن Finish به previous node، state داخلی را از بین می‌برد.

SniffRouter یا Router؟

از SniffRouter استفاده کنید اگر فقط این‌ها را لازم دارید:

  • HTTP Host routing
  • TLS SNI routing
  • تشخیص reverse handshake برای reverse tunnelی که یک TLS entrypoint را با سایت camouflage شریک است
  • یک انتخاب‌گر شاخه کوچک و صرفاً مبتنی بر content

از Router استفاده کنید اگر این‌ها را می‌خواهید:

  • source/destination IP یا port
  • GeoIP یا GeoSite
  • username/password
  • ترکیب protocol یا attributes با شرط‌های دیگر
  • چند شرط ترکیبی با AND

Metadata نود

ویژگیمقدار
node flags`kNodeFlagChainHead
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayerAnything
layer_group_next_nodekNodeLayerAnything
required_padding_left0 bytes

SniffRouter header تازه‌ای به ابتدای payload اضافه نمی‌کند و به left padding نیاز ندارد.