AuthenticationServer
AuthenticationServer نودی منطقی در انتهای chain برای مدیریت پایگاه داده کاربران WaterWall است. این نود socket باز نمیکند و مستقیماً I/O شبکه انجام نمیدهد؛ در عوض، یک پایگاه داده درونحافظهای از نوع users_t نگه میدارد، آن را در قالب JSON بارگذاری و ذخیره میکند و به درخواستهای فریمبندیشده AuthenticationClient پاسخ میدهد.
جایگاه رایج
AuthenticationClient -> AuthenticationServer
این نود در انتهای chain قرار میگیرد و نباید next داشته باشد.
این نود چه میکند؟
- کاربران را از
db-pathدر یک جدول درونحافظهایusers_tبارگذاری میکند. - اگر فایل اصلی پایگاه داده JSON بارگذاری نشود، سراغ
db-path.backupمیرود. - پس از بازیابی موفق از backup، فایل اصلی پایگاه داده را دوباره مینویسد.
- پایگاه داده فعلی در حافظه را بهصورت دورهای ذخیره میکند.
db-path.backupرا قبل از نوشتنdb-pathدر ذخیرهسازیهای عادی مینویسد.- میتواند snapshotهای تاریخی backup را بهصورت ساعتی، روزانه یا هفتگی بنویسد.
- پیامهای درخواست فریمبندیشده را از
AuthenticationClientمیپذیرد. - از چند فریم درخواست در یک پیام بیرونی پشتیبانی میکند.
- هر فریم درخواست را به ماژول داخلی مناسب برای احراز هویت یا مدیریت کاربر میفرستد.
- برای هر پیام پردازششده، یک پیام پاسخ ترکیبی شامل همه فریمهای پاسخ را در جهت downstream میفرستد.
AuthenticationServer مرجع اصلی اطلاعات کاربران است. نودهایی که ترافیک کاربران را سرویس میدهند باید از طریق AuthenticationClient با آن ارتباط بگیرند و نباید فریمهای پروتکل سرور را مستقیم بفرستند.
نمونه تنظیم
{
"name": "auth-db",
"type": "AuthenticationServer",
"settings": {
"db-path": "users.json",
"file-save-rate-ms": 10000,
"normal-backups": "daily",
"normal-backups-path": "backups/",
"normal-backups-count-limit": 10,
"session-idle-timeout-ms": 600000,
"verbose": false,
"auth-clients": [
{
"name": "edge-1",
"secret": "long-random-secret",
"allow-stats-push": true,
"allow-user-pull": true,
"allow-user-write": false,
"session-idle-timeout-ms": 600000
}
]
}
}
فیلدهای اجباری
فیلدهای سطح اصلی:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام انتخابی نود که باید در پیکربندی یکتا باشد. |
type | string | باید دقیقاً "AuthenticationServer" باشد. |
settings | object | تنظیمات پایگاه داده، ذخیرهسازی، backup، session و auth client. |
فیلدهای اجباری داخل settings:
| فیلد | نوع | توضیح |
|---|---|---|
db-path | string | مسیر فایل دیتابیس JSON کاربران. |
file-save-rate-ms | عدد مثبت | بازه ذخیره دورهای به میلیثانیه. |
auth-clients | آرایه غیرخالی | کلاینتهای control-plane که اجازه دارند در این سرور احراز هویت شوند. |
AuthenticationServer پیکربندیهایی را که در آنها next تعریف شده باشد نمیپذیرد.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
session-idle-timeout-ms | عدد مثبت | 600000 | timeout پیشفرض بیکار ماندن session، بر حسب میلیثانیه. |
verbose | boolean | false | لاگهای debug مربوط به روند احراز هویت، parse درخواستها، دسترسیها و روند پاسخ را فعال میکند. |
normal-backups | string | غیرفعال | حالت backup تاریخی: "hourly"، "daily" یا "weekly". |
normal-backups-path | string | تنظیم نشده | دایرکتوری snapshotهای تاریخی backup. اگر normal-backups تنظیم شود، این گزینه اجباری است. |
normal-backups-count-limit | عدد مثبت | 10 | حداکثر فایلهای backup تاریخی که برای این دیتابیس و حالت backup نگه داشته میشوند. |
normal-backups و normal-backups-path باید با هم تنظیم شوند. اگر بدون فعال کردن backupهای عادی، normal-backups-count-limit را تنظیم کنید، راهاندازی با خطا متوقف میشود.
normal-backups-path میتواند مسیر نسبی یا مطلق باشد. اگر دایرکتوری وجود نداشته باشد سرور آن را میسازد؛ اما اگر مسیر وجود داشته باشد و دایرکتوری نباشد، راهاندازی شکست میخورد.
Auth Clients
auth-clients مشخصات ورود control-plane را برای instanceهای AuthenticationClient تعریف میکند. این موارد با کاربران عادی ترافیک در پایگاه داده کاربران فرق دارند.
هر ورودی باید این فیلدها را داشته باشد:
| فیلد | نوع | توضیح |
|---|---|---|
name | non-empty string | نام کلاینت احراز هویت. |
secret | non-empty string | secret مشترک با همان کلاینت احراز هویت. |
فیلدهای اختیاری هر کلاینت:
| فیلد | نوع | پیشفرض | توضیح |
|---|---|---|---|
allow-stats-push | boolean | false | به ماژولهای همگامسازی مصرف ترافیک اجازه اجرا میدهد. |
allow-user-pull | boolean | false | به ماژولهای جستوجوی کاربر و دریافت کامل جدول اجازه اجرا میدهد. |
allow-user-write | boolean | false | ماژولهای ایجاد/بهروزرسانی کاربر را مجاز میکند. |
session-idle-timeout-ms | عدد مثبت | timeout سطح اصلی | timeout پیشفرض sessionهای ساختهشده برای این کلاینت را جایگزین میکند. |
این دسترسیها به session مربوطاند، نه به کاربران:
| Permission | مجاز میکند |
|---|---|
allow-user-pull | GetAllUsers، user lookup با password، SHA-224 یا SHA-256. |
allow-stats-push | PushUserStats و UpdateUserTraficStatsDiff. |
allow-user-write | AddNewUser و UpdateUser. |
Authenticate برای همه قابل استفاده است. تمام ماژولهای دیگر به یک session token معتبر نیاز دارند.
فرمت دیتابیس
فرمت پیشنهادی روی دیسک، یک object دارای آرایه users است:
{
"users": [
{
"id": 1001,
"name": "alice",
"password": "alice:alice-secret",
"email": "alice@example.com",
"wireguard-allowed-ips": "10.44.0.23/32",
"enabled": true,
"limit": {
"traffic": {
"up": 1073741824,
"down": 1073741824,
"total": 2147483648
},
"bandwidth": {
"up": 1048576,
"down": 1048576
},
"ips": 4,
"devices": 2,
"connections-in": 16,
"connections-out": 16
},
"time": {
"created-at-ms": 1735689600000,
"expire-at-ms": 1767225600000
},
"stats": {
"traffic": {
"up": 0,
"down": 0
},
"connections-in": 0,
"connections-out": 0
}
}
]
}
Loader چند شکل مختلف از ورودی را میپذیرد:
| Layout | توضیح |
|---|---|
null، آرایه خالی، یا object خالی | بهعنوان جدول کاربران خالی پذیرفته میشود. |
[user, ...] | آرایهای از objectهای کاربر. |
{"users": [user, ...]} | ساختار پیشنهادی. |
{"alice": user, "bob": user} | یک object map. اگر کاربر name غیرخالی نداشته باشد، کلید فقط بهعنوان راهنمای نام کاربری استفاده میشود. |
| یک object مستقل کاربر | در صورتی پذیرفته میشود که id یا user-id و همچنین password یا pass داشته باشد. |
Saver همیشه پایگاه داده را با ساختار {"users": [...]} بازنویسی میکند.
Object کاربر
هر object کاربر باید این فیلدها را داشته باشد:
| فیلد | نوع | توضیح |
|---|---|---|
id یا user-id | unsigned integer غیرصفر | شناسه منطقی و ماندگار کاربر که باید یکتا و ثابت باشد. |
password یا pass | string | کلید جستوجوی password که indexهای مشتقشده بر اساس آن ساخته میشوند. |
فیلدهای اختیاری:
| فیلد | توضیح |
|---|---|
name | نام خوانای کاربر. نامهای غیرخالی باید یکتا باشند. |
email | اطلاعات ایمیل کاربر. |
notes | یادداشت آزاد مدیر سیستم. |
gid یا group-id | شناسه گروه. |
enabled یا enable | پیشفرض true؛ برای غیرفعال کردن کاربر false بگذارید. |
record-stat-interval-ms | فاصله ثبت آمار. |
wireguard-allowed-ips | یک CIDR داخلی و اختیاری WireGuard برای این کاربر، مانند 10.44.0.23/32 یا fd00::23/128. مقدار خالی، null یا نبودن این فیلد یعنی تنظیم نشده است. IPv4 و IPv6 پذیرفته میشوند؛ مقدار به شکل استاندارد network ذخیره میشود و محدوده آن نباید با کاربر دیگری همپوشانی داشته باشد. |
limit | محدودیتهای کاربر. مقادیر مفقود یا صفر به معنی نامحدود است. |
time | اطلاعات زمانی کاربر. |
stats | آمار runtime که معمولاً WaterWall آن را نگه میدارد. |
برای Socks5Server، کلید نهایی جستوجو را در password و با قالب username:password ذخیره کنید؛ برای مثال "alice:alice-secret".
Passwordها بهصورت plaintext در پایگاه داده JSON ذخیره میشوند، زیرا اطلاعات exportشده کاربران فیلد password را هم در بر دارد. با permissionهای مناسب فایلسیستم از db-path، فایل db-path.backup و دایرکتوری backupهای عادی که حاوی اطلاعات محرمانهاند محافظت کنید.
کلیدهای جستوجوی مشتقشده از password مستقیماً در JSON ذخیره نمیشوند. هنگام بارگذاری کاربران، SHA-224، SHA-256، مشخصات UUID و public keyهای سازگار با WireGuard دوباره از password ساخته میشوند. هیچ دو کاربری نباید password/SHA-224، SHA-256، UUID یا public key مشتقشده WireGuard یکسان داشته باشند.
wireguard-allowed-ips بخشی ماندگار از پیکربندی کاربر است و از password مشتق نمیشود. این فیلد در JSON ذخیره و با GetAllUsers همگام میشود؛ همچنین اعتبارسنجی میشود تا محدودههای کاربران با یکدیگر همپوشانی نداشته باشند.
محدودیتها، زمان و آمار
limit میتواند داشته باشد:
| فیلد | توضیح |
|---|---|
traffic | محدودیت تعداد بایت برای up، down و total. نامهای مستعار u و d نیز پذیرفته میشوند. |
bandwidth | محدودیت بایت بر ثانیه برای up و down. نامهای مستعار u و d نیز پذیرفته میشوند. |
ips یا ip | حداکثر تعداد IP. |
devices | حداکثر تعداد دستگاه. |
connections-in یا cons-in | حداکثر تعداد اتصال ورودی. |
connections-out یا cons-out | حداکثر تعداد اتصال خروجی. |
time میتواند داشته باشد:
| فیلد |
|---|
created-at-ms یا created_at_ms |
first-usage-at-ms یا first_usage_at_ms |
expire-at-ms یا expires-at-ms |
expire-after-first-usage-ms یا expire-after-first-use-ms |
همین فیلدهای زمانی در سطح اصلی object کاربر نیز پذیرفته میشوند. همه زمانها تعداد میلیثانیه از Unix epoch هستند؛ بهجز expire-after-first-usage-ms که یک مدتزمان است.
stats میتواند داشته باشد:
| فیلد | توضیح |
|---|---|
traffic | شمارندههای بایت برای up و down. نامهای مستعار u و d نیز پذیرفته میشوند. |
speed | شمارندههای سرعت برای up و down. نامهای مستعار u و d نیز پذیرفته میشوند. |
ips یا ip | تعداد IP runtime. |
devices | تعداد دستگاهها در runtime. |
connections-in یا cons-in | تعداد اتصالهای ورودی در runtime. |
connections-out یا cons-out | تعداد اتصالهای خروجی در runtime. |
مقادیر عددی بزرگ را میتوان بهصورت رشته دهدهی خواند و نوشت.
ذخیرهسازی و بازیابی
در هر ذخیره عادی، ابتدا backup نوشته میشود:
- از جدول فعلی کاربران در حافظه، JSON ساخته میشود.
- آن JSON به
db-path.backupنوشته میشود. - همان JSON به
db-pathنوشته میشود.
بازیابی هنگام راهاندازی با این ترتیب انجام میشود:
- فایل
db-pathبارگذاری و محتوای آن تجزیه میشود. - اگر موفق باشد، بهعنوان پایگاه داده درونحافظهای استفاده میشود.
- اگر شکست بخورد،
db-path.backupامتحان میشود. - اگر backup با موفقیت بارگذاری شود، همان نسخه استفاده میشود، فایل backup حذف میشود و
db-pathدوباره نوشته میشود. - اگر بارگذاری فایل اصلی و backup هر دو شکست بخورند، نود ساخته نمیشود.
Snapshotهای backup عادی فقط نسخههای تاریخی برای مدیر سیستم هستند و در بازیابی زمان راهاندازی نقشی ندارند؛ این بازیابی فقط از db-path.backup استفاده میکند.
هنگام اجرای WaterWall فایل اصلی و backup را دستی ویرایش نکنید. ذخیرهساز دورهای ممکن است تغییرات دستی را با جدول فعلی موجود در حافظه بازنویسی کند.
Sessions و Revisionها
وقتی یک AuthenticationClient احراز هویت میشود، سرور یک session درونحافظهای با اطلاعات زیر میسازد:
- یک session token 64 بایتی
- نام کلاینت
- دسترسیهای کپیشده از ورودی متناظر در
auth-clients - idle timeout مخصوص session کلاینت
- timestamp آخرین فعالیت
- یک نسخه baseline خصوصی از جدول کاربران فعلی
- baseline
config_revisionوstats_revision
سرور یک جدول مرجع کاربران و دو شمارنده revision در سطح store نگه میدارد:
| Revision | چه زمانی افزایش مییابد |
|---|---|
config_revision | با تغییر پیکربندی یا اطلاعات کاربر، مانند AddNewUser یا UpdateUser. |
stats_revision | با تغییر اطلاعات مربوط به مصرف، مانند پذیرفته شدن delta ترافیک یا ثبت زمان اولین استفاده. |
مقدار اولیه revisionها 1 است.
جدول کاربران درونحافظهای مرجع اصلی است: همین جدول از دیسک بارگذاری میشود، بهصورت دورهای ذخیره میشود و بهعنوان مقصد ادغام بهروزرسانیهای کلاینت به کار میرود. پیامهای درخواست زیر rwlock مربوط به state سرور اجرا میشوند. point readهای مستقل میتوانند read lock را همزمان در اختیار داشته باشند، اما احراز هویت، جایگزینی baseline، ذخیره پایگاه داده، ادغام آمار و تغییرات کاربر write lock را میگیرند.
هر پیام درخواست احرازشده، timestamp آخرین فعالیت session را بهروز میکند. dispatch درخواست هنگام استفاده از اشارهگر session، read lock یا write lock مربوط به state را نگه میدارد. Worker شماره 0 هر 60000 میلیثانیه با گرفتن state write lock، sessionهای بیکار را پاکسازی میکند؛ بنابراین expiry نمیتواند session در حال استفاده یک درخواست را از بین ببرد. timestamp آخرین فعالیت atomic است تا point readهای همزمان بتوانند هنگام اشتراک read lock آن را بهروز کنند. Sessionها فقط state زمان اجرا هستند و هنگام restart ذخیره نمیشوند.
مدل همگامسازی
روند پیشنهادی همگامسازی برای AuthenticationClient چنین است:
- احراز هویت کنید و token برگشتی را نگه دارید.
- جدول کاربران کامل را با
GetAllUsersدریافت کنید. - یک جدول محلی از کاربران در سمت کلاینت برای نودهای سرویسدهنده ترافیک نگه دارید.
- اجازه دهید نودهایی مانند
Socks5Serverفیلدهای محلی مصرف در runtime را بهروز کنند. - اطلاعات آماری ترافیک را بهصورت دورهای با
PushUserStatsبفرستید. - هرگاه revisionهای سرور یا
needs-pullنشان دادند نسخه محلی قدیمی است، جدول کامل را دوباره دریافت کنید.
PushUserStats مسیری محدود برای ادغام آمار ترافیک است. از هر ورودی فقط کلید جستوجوی password و شمارندههای ترافیک را میخواند و تغییر دلخواه اطلاعات کاربر را نمیپذیرد. تغییر پیکربندی کاربران باید از ماژولهای مدیریتی دارای دسترسی allow-user-write انجام شود.
فریمبندی درخواست و پاسخ
بایتهای upstream به شکل stream بافر میشوند. envelope بیرونی درخواست چنین ساختاری دارد:
| Bytes | فیلد |
|---|---|
4 | Unsigned big-endian body size. |
64 | Session token. |
| بقیه body | یک یا چند request frame. |
اندازه body شامل token شصتوچهاربایتی بهعلاوه payload فریم درخواست است. در درخواست احرازنشده Authenticate، همه بایتهای token میتوانند صفر باشند. درخواستهای دیگر به token معتبر نیاز دارند.
هر request frame:
| Bytes | فیلد |
|---|---|
1 | Request type. |
4 | Correlation ID. |
4 | Unsigned big-endian request data length. |
| variable | Request data bytes. |
ساختار envelope پاسخ:
| Bytes | فیلد |
|---|---|
4 | Unsigned big-endian body size. |
8 | Unsigned big-endian config_revision. |
8 | Unsigned big-endian stats_revision. |
| بقیه body | یک یا چند response frame. |
هر response frame:
| Bytes | فیلد |
|---|---|
1 | Response type. |
4 | Correlation ID کپیشده از request. |
4 | Unsigned big-endian response data length. |
| variable | Response data bytes. |
پیامی که session token معتبر دارد نباید شامل Authenticate باشد. Envelope خراب، فریم ناقص، payload بزرگتر از حد مجاز یا بایتهای اضافهای که حتی یک header کامل درخواست را تشکیل نمیدهند، باعث بسته شدن امن اتصال منطقی میشوند.
انواع درخواست
| نوع | ماژول | Permission | داده request | response موفق |
|---|---|---|---|---|
1 | ping | valid session | literal ping | نوع 1، data pong |
2 | GetUserBySHA256Hex | allow-user-pull | 64 کاراکتر hex | نوع 2، user JSON |
3 | GetUserBySHA256Base64 | allow-user-pull | padded base64 SHA-256 digest | نوع 2، user JSON |
4 | GetUserBySHA256 | allow-user-pull | raw 32-byte SHA-256 digest | نوع 2، user JSON |
5 | GetUserByPassword | allow-user-pull | کلید جستجوی password plaintext | نوع 2، user JSON |
6 | AddNewUser | allow-user-write | یک user JSON object | نوع 0، user-added |
7 | UpdateUser | allow-user-write | full user JSON object | نوع 0، user-updated |
8 | UpdateUserTraficStatsDiff | allow-stats-push | full user JSON object | نوع 0، user-traffic-stats-updated |
9 | GetAllUsers | allow-user-pull | خالی | نوع 3، users JSON به اضافه server-time-ms |
10 | Authenticate | عمومی | JSON با name و secret | نوع 4، token 64 بایتی |
11 | PushUserStats | allow-stats-push | stats hints JSON | نوع 0، compact status JSON |
12 | GetUserBySHA224Hex | allow-user-pull | 56 کاراکتر hex | نوع 2، user JSON |
13 | GetUserBySHA224Base64 | allow-user-pull | padded base64 SHA-224 digest | نوع 2، user JSON |
14 | GetUserBySHA224 | allow-user-pull | raw 28-byte SHA-224 digest | نوع 2، user JSON |
انواع پاسخ:
| نوع | معنی |
|---|---|
0 | ok |
1 | pong |
2 | user |
3 | users database |
4 | session token |
255 | error |
برای نوع درخواست ناشناخته، پاسخ خطای unknown-request-type برگردانده میشود.
یادداشتهای ماژول
Authenticate
Authenticate انتظار دارد:
{"name":"edge-1","secret":"long-random-secret"}
اگر مشخصات ورود با یکی از ورودیهای settings.auth-clients منطبق باشد، سرور یک session میسازد و token شصتوچهاربایتی برمیگرداند. این token از ۳۲ بایت تصادفی ساخته و بهصورت ۶۴ بایت hexadecimal با حروف کوچک encode میشود. اگر منبع تصادفی امن در دسترس نباشد، ساخت token شکست میخورد.
GetAllUsers
GetAllUsers به payload خالی نیاز دارد. در صورت موفقیت، JSON استانداردشده کاربران را همراه با فیلد server-time-ms که فقط در پاسخ وجود دارد برمیگرداند:
{"users":[],"server-time-ms":1767225600000}
server-time-ms در پایگاه داده نوشته نمیشود. این مقدار به AuthenticationClient کمک میکند زمانهای انقضای متعلق به سرور را به ساعت محلی کلاینت تبدیل کند.
در صورت موفقیت، baseline مربوط به session با جدول برگشتی و revisionهای فعلی سرور جایگزین میشود.
AddNewUser
AddNewUser یک object کاربر در قالب JSON میخواهد. کاربر فرستادهشده باید id یا user-id یکتا و غیرصفر داشته باشد.
ماژول نام یا id تکراری و نیز کلید جستوجوی تکراری مشتقشده از password را نمیپذیرد. پس از موفقیت، پایگاه داده را بیدرنگ ذخیره میکند. اگر ذخیره شکست بخورد، تلاش میکند افزودن درونحافظهای را rollback کند و database-save-failed برمیگرداند.
UpdateUser
UpdateUser یک object کامل کاربر در قالب JSON میخواهد. فیلد password فقط برای پیدا کردن کاربر موجود با hashِ SHA-256 آن به کار میرود؛ این ماژول خود password یا hash آن را تغییر نمیدهد.
id یا user-id ارسالی باید وجود داشته باشد، غیرصفر باشد و با کاربری که از طریق hashِ password پیدا شده مطابقت کند. اطلاعات قابل تغییر، محدودیتها، اطلاعات زمانی، آمار و فاصله ثبت آمار را میتوان در حافظه بهروز کرد. ماندگار کردن این تغییرات با timer عادی ذخیره دورهای انجام میشود.
UpdateUserTraficStatsDiff
UpdateUserTraficStatsDiff یک object کامل کاربر در قالب JSON میخواهد. Password و id فقط برای شناسایی و اعتبارسنجی کاربر موجود استفاده میشوند. سپس فقط مقادیر stats.traffic.up و stats.traffic.down بهعنوان delta اضافه میشوند و هیچ فیلد دیگری تغییر نمیکند.
اگر یک delta غیرصفر ترافیک پذیرفته شود و کاربر هنوز مقدار مرجع first-usage-at-ms نداشته باشد، سرور این فیلد را با زمان فعلی خودش مقداردهی میکند.
PushUserStats
PushUserStats یک یا چند ورودی جزئی آمار میپذیرد. ورودی میتواند یکی از شکلهای زیر باشد:
- یک آرایه از objectهای آمار
- یک object با آرایه
users - یک object map که مقدارهایش object آمار هستند
- یک object مستقل آمار
هر ورودی باید password یا pass و دستکم یکی از شمارندههای ترافیک زیر را داشته باشد:
stats.traffic.upstats.traffic.ustats.traffic.downstats.traffic.d
فیلدهای دیگر نادیده گرفته میشوند. سرور هر شمارنده را با baseline همان session مقایسه میکند، شمارندههایی را که به عقب برگشتهاند و کاربران ناشناخته را نمیپذیرد، ورودیهای تکراری برای یک کلید password/SHA-256 را رد میکند و فقط deltaهای پذیرفتهشده ترافیک را در جدول مرجع اعمال میکند.
در صورت موفقیت، داده پاسخ یک JSON فشرده است:
{
"status": "stats-updated",
"applied-deltas": 2,
"needs-pull": false,
"config-revision": 1,
"stats-revision": 3
}
اگر needs-pull برابر true باشد، AuthenticationClient باید GetAllUsers را فراخوانی کند تا جدول محلی کاربران بهروز شود.
رفتار Lifecycle
با دریافت init در جهت upstream، AuthenticationServer وضعیت بافر مخصوص همان line را مقداردهی اولیه میکند و est را در جهت downstream به تونل قبلی میفرستد.
در payloadِ upstream، بایتها را بافر میکند، پیامهای بیرونی کامل را بیرون میکشد، هر فریم درخواست را پردازش میکند و با tunnelPrevDownStreamPayload() یک پیام پاسخ ترکیبی در جهت downstream میفرستد.
با دریافت finish در جهت upstream، فقط state مخصوص همان line را از بین میبرد. چون سازنده line نبوده، lineDestroy() را فراخوانی نمیکند.
اگر سرور به دلیل داده malformed یا بزرگتر از حد مجاز اتصال منطقی را ببندد، ابتدا state خودش برای line را از بین میبرد و سپس finish را در جهت downstream به تونل قبلی میفرستد.
نکتهها
- سقف payload پیام بیرونی
16 MiBاست. - سقف داده درخواست
16 MiBاست. - سقف payload پاسخ
16 MiBاست. - سقف پاسخهای صفشده
16 MiBاست. - این نود به left padding نیاز ندارد و چیزی را درجا prepend نمیکند.
- این نود packet tunnel نیست و از قواعد packet line استفاده نمیکند.
- املای نام ماژول
UpdateUserTraficStatsDiffعمداً همان املای فعلی در کد منبع است.
عملکرد پایگاه داده کاربران
- SHA-256 کلید اصلی جستوجوی password متنی است. هر جستوجوی password فقط یک بار SHA-256 را محاسبه میکند و بدون fallback scan روی جدول کاربران، از index هش SHA-256 نتیجه را پیدا میکند؛ بنابراین هزینه متوسط hit و miss برابر O(1) است. مقایسه دقیق password متنی پس از hit شدن index، بررسی نهایی و مقاوم در برابر برخورد hash است.
- تمام جستوجوهای کلیددار، شامل SHA-224، SHA-256، UUID، کلید عمومی WireGuard، id ماندگار و name غیرخالی، هزینه متوسط O(1) دارند. جستوجوی آدرس Allowed-IP مربوط به WireGuard و تشخیص همپوشانی rangeها با یک index بازهای مرتبشده و هزینه O(log M) انجام میشود.
- snapshotهای baseline مربوط به session برای
GetAllUsersوAuthenticateبهجای رفتوبرگشت از طریق JSON از deep copy بومیusersCopy()استفاده میکنند.GetAllUsersجدول را فقط یک بار برای پاسخ شبکه serialize میکند و یک کپی بومی از همان جدول را برای baseline مربوط به session به کار میگیرد. - پیامهای صرفاً point read، شامل
Ping،GetUserBySHA-224/256وGetUserByPassword، read lock مربوط به state سرور را بهاشتراک میگذارند و میتوانند همزمان اجرا شوند. هر پیام شامل احراز هویت،GetAllUsers، تغییر کاربر، تغییر آمار یا درخواست ناشناخته یا ترکیبیِ غیر point read، در تمام مدت dispatch از state write lock استفاده میکند.