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

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
}
]
}
}

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

فیلدهای سطح اصلی:

فیلدنوعتوضیح
namestringنام انتخابی نود که باید در پیکربندی یکتا باشد.
typestringباید دقیقاً "AuthenticationServer" باشد.
settingsobjectتنظیمات پایگاه داده، ذخیره‌سازی، backup، session و auth client.

فیلدهای اجباری داخل settings:

فیلدنوعتوضیح
db-pathstringمسیر فایل دیتابیس JSON کاربران.
file-save-rate-msعدد مثبتبازه ذخیره دوره‌ای به میلی‌ثانیه.
auth-clientsآرایه غیرخالیکلاینت‌های control-plane که اجازه دارند در این سرور احراز هویت شوند.

AuthenticationServer پیکربندی‌هایی را که در آن‌ها next تعریف شده باشد نمی‌پذیرد.

تنظیمات اختیاری

گزینهنوعپیش‌فرضتوضیح
session-idle-timeout-msعدد مثبت600000timeout پیش‌فرض بی‌کار ماندن session، بر حسب میلی‌ثانیه.
verbosebooleanfalseلاگ‌های debug مربوط به روند احراز هویت، parse درخواست‌ها، دسترسی‌ها و روند پاسخ را فعال می‌کند.
normal-backupsstringغیرفعالحالت backup تاریخی: "hourly"، "daily" یا "weekly".
normal-backups-pathstringتنظیم نشدهدایرکتوری 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 تعریف می‌کند. این موارد با کاربران عادی ترافیک در پایگاه داده کاربران فرق دارند.

هر ورودی باید این فیلدها را داشته باشد:

فیلدنوعتوضیح
namenon-empty stringنام کلاینت احراز هویت.
secretnon-empty stringsecret مشترک با همان کلاینت احراز هویت.

فیلدهای اختیاری هر کلاینت:

فیلدنوعپیش‌فرضتوضیح
allow-stats-pushbooleanfalseبه ماژول‌های همگام‌سازی مصرف ترافیک اجازه اجرا می‌دهد.
allow-user-pullbooleanfalseبه ماژول‌های جست‌وجوی کاربر و دریافت کامل جدول اجازه اجرا می‌دهد.
allow-user-writebooleanfalseماژول‌های ایجاد/به‌روزرسانی کاربر را مجاز می‌کند.
session-idle-timeout-msعدد مثبتtimeout سطح اصلیtimeout پیش‌فرض sessionهای ساخته‌شده برای این کلاینت را جایگزین می‌کند.

این دسترسی‌ها به session مربوط‌اند، نه به کاربران:

Permissionمجاز می‌کند
allow-user-pullGetAllUsers، user lookup با password، SHA-224 یا SHA-256.
allow-stats-pushPushUserStats و UpdateUserTraficStatsDiff.
allow-user-writeAddNewUser و 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-idunsigned integer غیرصفرشناسه منطقی و ماندگار کاربر که باید یکتا و ثابت باشد.
password یا passstringکلید جست‌وجوی 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 نوشته می‌شود:

  1. از جدول فعلی کاربران در حافظه، JSON ساخته می‌شود.
  2. آن JSON به db-path.backup نوشته می‌شود.
  3. همان JSON به db-path نوشته می‌شود.

بازیابی هنگام راه‌اندازی با این ترتیب انجام می‌شود:

  1. فایل db-path بارگذاری و محتوای آن تجزیه می‌شود.
  2. اگر موفق باشد، به‌عنوان پایگاه داده درون‌حافظه‌ای استفاده می‌شود.
  3. اگر شکست بخورد، db-path.backup امتحان می‌شود.
  4. اگر backup با موفقیت بارگذاری شود، همان نسخه استفاده می‌شود، فایل backup حذف می‌شود و db-path دوباره نوشته می‌شود.
  5. اگر بارگذاری فایل اصلی و 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 چنین است:

  1. احراز هویت کنید و token برگشتی را نگه دارید.
  2. جدول کاربران کامل را با GetAllUsers دریافت کنید.
  3. یک جدول محلی از کاربران در سمت کلاینت برای نودهای سرویس‌دهنده ترافیک نگه دارید.
  4. اجازه دهید نودهایی مانند Socks5Server فیلدهای محلی مصرف در runtime را به‌روز کنند.
  5. اطلاعات آماری ترافیک را به‌صورت دوره‌ای با PushUserStats بفرستید.
  6. هرگاه revisionهای سرور یا needs-pull نشان دادند نسخه محلی قدیمی است، جدول کامل را دوباره دریافت کنید.

PushUserStats مسیری محدود برای ادغام آمار ترافیک است. از هر ورودی فقط کلید جست‌وجوی password و شمارنده‌های ترافیک را می‌خواند و تغییر دلخواه اطلاعات کاربر را نمی‌پذیرد. تغییر پیکربندی کاربران باید از ماژول‌های مدیریتی دارای دسترسی allow-user-write انجام شود.

فریم‌بندی درخواست و پاسخ

بایت‌های upstream به شکل stream بافر می‌شوند. envelope بیرونی درخواست چنین ساختاری دارد:

Bytesفیلد
4Unsigned big-endian body size.
64Session token.
بقیه bodyیک یا چند request frame.

اندازه body شامل token شصت‌وچهاربایتی به‌علاوه payload فریم درخواست است. در درخواست احرازنشده Authenticate، همه بایت‌های token می‌توانند صفر باشند. درخواست‌های دیگر به token معتبر نیاز دارند.

هر request frame:

Bytesفیلد
1Request type.
4Correlation ID.
4Unsigned big-endian request data length.
variableRequest data bytes.

ساختار envelope پاسخ:

Bytesفیلد
4Unsigned big-endian body size.
8Unsigned big-endian config_revision.
8Unsigned big-endian stats_revision.
بقیه bodyیک یا چند response frame.

هر response frame:

Bytesفیلد
1Response type.
4Correlation ID کپی‌شده از request.
4Unsigned big-endian response data length.
variableResponse data bytes.

پیامی که session token معتبر دارد نباید شامل Authenticate باشد. Envelope خراب، فریم ناقص، payload بزرگ‌تر از حد مجاز یا بایت‌های اضافه‌ای که حتی یک header کامل درخواست را تشکیل نمی‌دهند، باعث بسته شدن امن اتصال منطقی می‌شوند.

انواع درخواست

نوعماژولPermissionداده requestresponse موفق
1pingvalid sessionliteral pingنوع 1، data pong
2GetUserBySHA256Hexallow-user-pull64 کاراکتر hexنوع 2، user JSON
3GetUserBySHA256Base64allow-user-pullpadded base64 SHA-256 digestنوع 2، user JSON
4GetUserBySHA256allow-user-pullraw 32-byte SHA-256 digestنوع 2، user JSON
5GetUserByPasswordallow-user-pullکلید جستجوی password plaintextنوع 2، user JSON
6AddNewUserallow-user-writeیک user JSON objectنوع 0، user-added
7UpdateUserallow-user-writefull user JSON objectنوع 0، user-updated
8UpdateUserTraficStatsDiffallow-stats-pushfull user JSON objectنوع 0، user-traffic-stats-updated
9GetAllUsersallow-user-pullخالینوع 3، users JSON به اضافه server-time-ms
10AuthenticateعمومیJSON با name و secretنوع 4، token 64 بایتی
11PushUserStatsallow-stats-pushstats hints JSONنوع 0، compact status JSON
12GetUserBySHA224Hexallow-user-pull56 کاراکتر hexنوع 2، user JSON
13GetUserBySHA224Base64allow-user-pullpadded base64 SHA-224 digestنوع 2، user JSON
14GetUserBySHA224allow-user-pullraw 28-byte SHA-224 digestنوع 2، user JSON

انواع پاسخ:

نوعمعنی
0ok
1pong
2user
3users database
4session token
255error

برای نوع درخواست ناشناخته، پاسخ خطای 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.up
  • stats.traffic.u
  • stats.traffic.down
  • stats.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 استفاده می‌کند.