Sendmail
هندسة تطبيقات البرمجيات مفتوحة المصدر (المجلد الأول)Sendmail
هندسة تطبيقات البرمجيات مفتوحة المصدر (المجلد الأول) Sendmail
إريك ألمان
إذا استمتعتُ بهذه الكتب، فقد تستمتع أيضًا بـ Software Design by Example in Python و Software Design by Example in JavaScript.
يغلب على الناس أن يروا البريد الإلكتروني في البرنامج الذي يتعاملون معه—عميل بريدهم، ويُعرّف تقنيًا بـ «وكيل مستخدم البريد» (Mail User Agent, MUA). لكن جزءًا آخر مهمًّا من البريد الإلكتروني هو البرمجيات التي تنقل البريد فعليًا من المُرسِل إلى المُرسَل إليه—«وكيل نقل البريد» (Mail Transfer Agent, MTA). كان أول MTA على الإنترنت، ولا يزال أشيعها، هو sendmail.
أُنشئ sendmail أول مرة قبل أن يوجد الإنترنت رسميًا. وكان نجاحه استثنائيًّا؛ فقد نما منذ عام 1981، حين لم يكن من البديهي إطلاقًا أن يفوق الإنترنت كونه مجرد تجربة أكاديمية تضم بضع مئات من المضيفات، إلى اليوم حيث تجاوز عدد مضيفات الإنترنت 800 مليون حتى يناير 2011[1]. ولا يزال sendmail من أكثر تطبيقات SMTP استخدامًا على الإنترنت.
17.1. في زمنٍ سالف…
كُتبت أولى نسخ البرنامج الذي سيُعرف لاحقًا باسم sendmail عام 1980. بدأ كاختراق سريع (quick hack) لتوجيه الرسائل بين شبكات مختلفة. كان الإنترنت قيد التطوير لكنه لم يكن عاملًا بعد. بل إن كثيرًا من الشبكات المختلفة قد طُرحت دون أن يظهر أي إجماع واضح. كانت Arpanet مستخدَمة في الولايات المتحدة، وكان الإنترنت يُصمَّم بوصفه ترقية لها، لكن أوروبا ضاعفت جهدها خلف مجهود OSI (Open Systems Interconnect)، ول awhile بدا وكأن OSI قد ينتصر. استخدم الاثنان خطوطًا مستأجرة من شركات الهاتف، وكان ذلك المعدل في الولايات المتحدة 56 Kbps.
ربما كانت أنجح شبكة في ذلك الوقت، من حيث عدد الحواسيب والأشخاص المتصلين، هي شبكة UUCP، وكانت شاذة insofar أنها لم تملك أي سلطة مركزية على الإطلاق. كانت، بمعنى ما، الشبكة الأصلية من نظير إلى نظير (peer-to-peer)، التي تعمل فوق خطوط الهاتف المتصلة بالطلب (dial-up): فسرعة 9600 bps كانت تقريبًا أسرع ما يتاح لبعض الوقت. أما أسرع شبكة (بسرعة 3 Mbps) فكانت مبنية على Ethernet من Xerox، وكانت تشغّل بروتوكولًا اسمه XNS (Xerox Network Systems)—لكنها لم تكن تعمل خارج موقع محلي واحد.
كانت بيئة تلك الحقبة تختلف اختلافًا كبيرًا عمّا هو موجود اليوم. كانت الحواسيب شديدة التنوع إلى حدّ أنه لم يكن هناك حتى اتفاق كامل على استخدام بايتات بعرض 8 بت. فعلى سبيل المثال، ضمّت الحواسيب الأخرى PDP-10 (كلمات 36 بت، بايتات 9 بت)، و PDP-11 (كلمات 16 بت، بايتات 8 بت)، وسلسلة CDC 6000 (كلمات 60 بت، محارف 6 بت)، و IBM 360 (كلمات 32 بت، بايتات 8 بت)، و XDS 940، و ICL 470، و Sigma 7. وكانت إحدى المنصات الصاعدة هي Unix، وكانت آتية آنذاك من مختبرات بيل. وكانت معظم الحواسيب المبنية على Unix ذات فضاءات عناوين 16 بت: في ذلك الوقت كانت PDP-11 هي حاسوب Unix الرئيس، مع Data General 8/32 و VAX-11/780 اللذين بالكاد ظهرا. ولم تكن الخيوط (threads) موجودة—بل إن مفهوم العمليات الديناميكية (dynamic processes) كان لا يزال جديدًا إلى حدٍّ كبير (فإن Unix كانت تملكها، لكن الأنظمة «الجادة» مثل OS/360 من IBM لم تكن كذلك). ولم يكن تشفير الملفات مدعومًا في نواة Unix (لكن الحيل كانت ممكنة عبر روابط نظام الملفات).
إلى الحدّ الذي وُجدت به أصلًا، كانت الشبكات عمومًا بطيئة (كثير منها مبنية على خطوط TTY بسرعة 9600 باود؛ أما الأثرياء حقًا فقد يكون Ethernet في متناولهم، لكن للاستخدام المحلي فقط). ولم تكن واجهة المقابس (sockets) المحترمة لتُبتكر قبل سنوات عديدة. كما لم يكن تشفير المفتاح العام قد اختُرع بعد، لذا لم تكن معظم منظومات أمن الشبكات كما نعرفها اليوم ممكنة.
كانت رسالة الشبكات موجودة أصلًا على Unix، لكنها أُنشئت بالحيل. كان وكيل المستخدم الأساسي في ذلك الوقت هو الأمر /bin/mail (ويُشار إليه أحيانًا اليوم بـ binmail أو v7mail)، لكن بعض المواقع كانت تملك وكلاء مستخدمين آخرين مثل Mail من بيركلي، وهو في الواقع يفهم معاملة الرسائل بوصفها عناصر منفصلة، بدلًا من أن يكون مجرد برنامج cat مُفخَّم. وكان كل وكيل مستخدم يقرأ /usr/spool/mail مباشرةً (وكثيرًا ما يكتب إليها أيضًا!)، ولم يكن هناك أي تجريد (abstraction) لكيفية تخزين الرسائل فعليًا.
لم يكن منطق توجيه رسالة إلى الشبكة مقابل البريد المحلي أكثر من النظر في ما إذا كان العنوان يحتوي على علامة تعجب (UUCP) أو على نقطتين (BerkNET). وكان على من لديهم اتصال بـ Arpanet استخدام برنامج بريد منفصل تمامًا، لا يتوافق مع بقية الشبكات، والذي كان حتى يخزّن البريد المحلي في مكان مختلف وبصيغة مختلفة.
ولجعل الأمور أكثر إثارة، لم يكن هناك أي توحيد في المعايير تقريبًا لصيغة الرسائل نفسها. كان هناك اتفاق عام على أن تأتي في أعلى الرسالة كتلة من حقول الترويسة، وأن يكون كل حقل ترويسة في سطر جديد، وأن يفصل بين أسماء حقول الترويسة وقيمها بنقطتين. وما عدا ذلك، لم يكن هناك قدر يُذكر من التوحيد القياسي، لا في اختيار أسماء حقول الترويسة ولا في صياغات (syntaxes) الحقول الفردية. فعلى سبيل المثال، استخدمت بعض الأنظمة Subj: بدلًا من Subject:، وكانت حقول Date: بصيغ مختلفة، ولم تكن بعض الأنظمة تفهم الأسماء الكاملة في حقل From:. وفوق كل ذلك، كان ما هو موثَّق غالبًا ملتبسًا أو غير مطابق تمامًا لما كان مستخدمًا فعليًا. وبخاصة، فإن RFC 733 (الذي يزعم أنه يصف صيغة رسائل Arpanet) كان يختلف عمّا استُخدم فعليًا بطرق دقيقة لكنها أحيانًا جوهرية، وطريقة إرسال الرسائل فعليًا لم تكن موثَّقة رسميًا على الإطلاق (مع أن عدة RFCs أشارت إلى الآلية، إلا أن أيًّا منها لم يعرّفها). وكانت النتيجة نشوء نوع من الكهنوت (priesthood) حول منظومات الرسائل.
في عام 1979، حصل مشروع نظام إدارة قاعدة البيانات العلائقية INGRES (المعروف أيضًا بـ «عملي النهاري») على منحة من DARPA، ومعها اتصال بـ Arpanet بسرعة 9600bps إلى جهازنا PDP-11. وكان اتصال Arpanet الوحيد المتاح في قسم علوم الحاسوب، لذا أراد الجميع الوصول إلى أجهزتنا ليتمكّنوا من بلوغ Arpanet. لكن ذلك الجهاز كان مشغولًا إلى أقصى حدّ، لذا لم نتمكن إلا من توفير منفذي تسجيل دخول (login ports) للجميع في القسم ليتشاركاهما. وقد سبّب ذلك تضاربًا كبيرًا ونزاعات متكررة. غير أنني لاحظت أن ما يريده الناس أكثر من أي شيء آخر ليس تسجيل الدخول عن بُعد ولا نقل الملفات، بل البريد الإلكتروني.
إلى هذا كله، ظهر sendmail (المسمى أولًا delivermail) كمحاولة لتوحيد هذه الفوضى في مكان واحد. كان كل MUA (وكيل مستخدم البريد، أو عميل البريد) يستدعي delivermail فحسب لتسليم البريد الإلكتروني، بدلًا من محاولة اكتشاف كيفية ذلك على أساس ارتجالي (وغالبًا غير متوافق). ولم يحاول delivermail/sendmail أي شيء لتحديد كيفية تخزين البريد المحلي أو تسليمه؛ ولم يفعل شيئًا سوى خلط البريد بين البرامج الأخرى. (وقد تغيّر هذا حين أُضيف SMTP، كما سنرى بعد قليل.) وبمعنى ما، لم يكن سوى غراء (glue) يُمسك مختلف منظومات البريد معًا، بدلًا من أن يكون منظومة بريد بذاته.
أثناء تطوير sendmail تحوّل Arpanet إلى الإنترنت. كانت التغييرات واسعة، من الحزم منخفضة المستوى على السلك وصولًا إلى بروتوكولات التطبيق، ولم تحدث فجأة. طُوِّر sendmail فعليًا بالتزامن مع المعايير، بل أثّر فيها أحيانًا. ومن اللافت أن sendmail قد صمد، بل ازدهر، مع اتساع «الشبكة» (كما نفهمها اليوم) من بضع مئات من المضيفات إلى مئات الملايين منها.
شبكة أخرى
تجدر الإشارة إلى أنه طُرح في ذلك الوقت معيار بريدي منفصل تمامًا آخر اسمه X.400، كان جزءًا من ISO/OSI (منظمة المعايير الدولية/الربط بين الأنظمة المفتوحة). كان X.400 بروتوكولًا ثنائيًا (binary)، تُرمَّز فيه الرسالة باستخدام ASN.1 (Abstract Syntax Notation 1)، وهو لا يزال مستخدمًا في بعض بروتوكولات الإنترنت اليوم مثل LDAP. وكان LDAP بدوره تبسيطًا لـ X.500، وهو خدمة الدليل التي استخدمها X.400. ولم يحاول sendmail إطلاقًا أن يكون متوافقًا مباشرةً مع X.400، رغم وجود بعض خدمات البوابات آنذاك. ورغم أن X.400 تبنّاه في البداية كثير من المورّدين التجاريين في ذلك الوقت، إلا أن بريد الإنترنت و SMTP انتصرا في السوق.
17.2. مبادئ التصميم
أثناء تطوير sendmail التزمتُ عدة مبادئ تصميمية. وكل هذه المبادئ، بمعنى ما، تنحدر جميعها إلى أمر واحد: افعل أقل ما يمكن. وهذا في تناقض حادّ مع بعض مجهودات الحقبة الأخرى التي كانت أهدافها أوسع بكثير وكانت تتطلب تنفيذات أكبر بكثير.
17.2.1. اقبل أن المبرمج الواحد محدود
كتبتُ sendmail كمشروع جانبي بلا أجر. كان المقصود منه أن يكون طريقة سريعة لجعل بريد Arpanet في متناول أشخاص في جامعة كاليفورنيا بيركلي. وكانت المفتاحية هي توجيه البريد بين الشبكات القائمة، وكلها منفَّذة كبرامج مستقلة لا يعلمون بوجود أكثر من شبكة واحدة أصلًا. فلم يكن تعديل أكثر من قدر ضئيل جدًا من البرمجيات القائمة ممكنًا مع مبرمج واحد يعمل بدوام جزئي. وكان على التصميم أن يقلّل إلى أدنى حدّ حجم الشيفرة القائمة التي تحتاج إلى تعديل، وكذلك حجم الشيفرة الجديدة التي تحتاج إلى كتابة. وهذا القيد هو ما قدَّم معظم مبادئ التصميم الباقية. وتبيّن لاحقًا، في معظم الحالات، أنها كانت الخيار الصحيح حتى لو كان هناك فريق أكبر متاح.
17.2.2. لا تُعِد تصميم وكلاء المستخدم
«وكيل مستخدم البريد» (MUA) هو ما يفكر فيه معظم المستخدمين النهائيين بأنّه «منظومة البريد»—إنه البرنامج الذي يقرأون به البريد ويكتبونه ويردونّ عليه. وهو مختلف تمامًا عن «وكيل نقل البريد» (MTA) الذي يوجّه البريد الإلكتروني من المُرسِل إلى المُستلِم. في وقت كتابة sendmail كانت كثير من التنفيذات تجمع هاتين الوظيفتين على الأقل جزئيًا، لذا كانت تُطوَّر بالتوازي. ومحاولة العمل على الأمرين في الوقت نفسه كانت أكثر مما يُطاق، لذا تخلّى sendmail تمامًا عن مسألة واجهة المستخدم: فالتغييرات الوحيدة في MUAs كانت أن تستدعي sendmail بدلًا من أن تقوم بالتوجيه الخاص بها. وبخاصة، كان هناك بالفعل عدة وكلاء مستخدمين، وكان الناس كثيرًا ما يكونون عاطفيين جدًا تجاه طريقة تفاعلهم مع البريد. ومحاولة العمل على الأمرين في الوقت نفسه كانت أكثر مما يُطاق. وهذا الفصل بين MUA و MTA صار اليوم حكمةً مقبولة، لكنه كان بعيدًا كل البعد عن الممارسة المعتادة آنذاك.
17.2.3. لا تُعِد تصميم مخزن البريد المحلي
لم يكن مخزن البريد المحلي (حيث تُحفظ الرسائل حتى يأتي المستلِم ليقرأها) معيَّنًا رسميًا. أعجبت بعض المواقع تخزينها في مكان مركزي مثل /usr/mail أو /var/mail أو /var/spool/mail. وأعجبت مواقع أخرى تخزينها في دليل المستخدم الرئيسي (مثلًا في ملف اسمه .mail). بدأت معظم المواقع كل رسالة بسطر يبدأ بـ "From" تليها مسافة (وهو قرار سيئ للغاية، لكنه كان العُرف السائد آنذاك)، لكن المواقع التي تركز على Arpanet كانت تفصل عادةً الرسائل بسطر يحتوي على أربع محارف تحكّم-A (control-A). وحاولت بعض المواقع قفل صندوق البريد لمنع التصادمات، لكنها استخدمت أعرافًا مختلفة للقفل (لم تكن بداءات قفل الملفات متاحة بعد). باختصار، لم يكن خيارًا معقولًا إلا التعامل مع التخزين المحلي للبريد بوصفه صندوقًا أسود (black box). باختصار، لم يكن خيارًا معقولًا إلا التعامل مع التخزين المحلي للبريد بوصفه صندوقًا أسود (black box).
على كل المواقع تقريبًا، كانت آلية تخزين صندوق البريد المحلي فعليًا متجسّدة في البرنامج /bin/mail. وكان هذا البرنامج يجمع في داخله واجهة مستخدم (بدائية إلى حدٍّ بعيد) وتوجيهًا وتخزينًا. ولإدماج sendmail، استُخرج جزء التوجيه واستُبدل باستدعاء إلى sendmail. وأُضيف الخيار -d لإجبار التسليم النهائي، أي أنه يمنع /bin/mail من استدعاء sendmail لأداء التوجيه. وفي السنوات اللاحقة استُخرجت الشيفرة المستخدمة لتسليم رسالة إلى صندوق بريد فعلي إلى برنامج آخر اسمه mail.local. ولم يبقَ برنامج /bin/mail موجودًا اليوم إلا ليوفّر قاسمًا مشتركًا أدنى حدًّا تتيح به السكربتات إرسال البريد.
17.2.4. اجعل sendmail يتكيّف مع العالم، لا العكس
كانت بروتوكولات مثل UUCP و BerkNET منفَّذة بالفعل كبرامج مستقلة لها تركيب سطر أوامر خاص بها، أحيانًا غريب. وفي بعض الحالات كان يجري تطويرها في الوقت نفسه الذي كان يُطوَّر فيه sendmail. وكان واضحًا أن إعادة تنفيذها (مثلًا لتحويلها إلى عُرف استدعاء موحّد) ستكون تجربة موجعة. وقد أدّى هذا مباشرةً إلى المبدأ القائل بأن sendmail ينبغي أن يتكيّف مع بقية العالم بدلًا من محاولة جعل بقية العالم يتكيّف معه.
17.2.5. غيّر أقل ما يمكن
إلى أقصى حدٍ ممكن، أثناء تطوير sendmail لم ألمس أي شيء لم تكن الحاجة ملحّة إلى لمسه. وبالإضافة إلى مجرد أن الوقت لم يكن كافيًا للقيام بذلك، كانت هناك ثقافة في بيركلي آنذاك تتجنّب معظم الملكية الرسمية للشيفرة لصالح سياسة «آخر شخص لمس الشيفرة هو المرجع في ذلك البرنامج» (أو بكلمات أبسط: «من يلمسها يملكها»). ورغم أن هذا يبدو فوضويًا بمعايير اليوم، فقد كان يعمل على نحو جيد إلى حدٍّ كبير في عالم لم يكن فيه أحد في بيركلي مكلَّفًا بدوام كامل بالعمل على Unix؛ كان الأفراد يعملون على أجزاء النظام التي تهمّهم والتزموا بها، ولا يمسون بقية قاعدة الشيفرة إلا في الحالات القصوى.
17.2.6. فكّر في الموثوقية مبكرًا
لم تكن منظومة البريد السابقة لـ sendmail (بما في ذلك معظم منظومات النقل) قلقةً بشأن الموثوقية إلى حدٍّ كبير. فعلى سبيل المثال، لم تكن إصدارات Unix التي تسبق 4.2BSD تملك أقفال ملفات أصلية، رغم إمكانية محاكاتها بإنشاء ملف مؤقت ثم ربطه بملف قفل (وإذا كان ملف القفل موجودًا مسبقًا فإن استدعاء الربط يفشل). لكن أحيانًا كانت برامج مختلفة تكتب ملف بيانات واحدًا دون أن تتفق على كيفية القفل (مثلًا قد تستخدم اسم ملف قفل مختلفًا، أو لا تحاول القفل أصلًا)، ولم يكن فقدان البريد بالتالي نادرًا. واتخذ sendmail موقفًا مفاده أن فقدان البريد ليس خيارًا (وربما كان ذلك نتيجة خلفيتي كرجل قواعد بيانات، حيث فقدان البيانات خطيئة مميتة).
17.2.7. ما الذي لم يشمله العمل
كانت هناك أشياء كثيرة لم تُنجَز في الإصدارات المبكرة. لم أحاول إعادة بناء معمارية منظومة البريد ولا بناء حل عام تمامًا: كان من الممكن إضافة الوظائف عند الحاجة. ولم تكن الإصدارات الأولى جدًا مُعدّة أصلًا لتكون قابلة للتهيئة بالكامل من دون الوصول إلى الشيفرة المصدرية ومُصرِّف (مع أن هذا تغيّر مبكرًا نسبيًا). وعامةً، كانت طريقة العمل في sendmail هي الحصول على شيء يعمل بسرعة ثم تحسين الشيفرة العاملة حسب الحاجة وحسبما يتضح فهم المشكلة.
17.3. مراحل التطوير
مثل معظم البرمجيات طويلة العمر، طُوِّر sendmail على مراحل، لكل منها طابعها وإحساسها الأساسي الخاص.
17.3.1. الموجة الأولى: delivermail
كان التجسيد الأول لـ sendmail معروفًا باسم delivermail. كان بالغ البساطة، إن لم يكن ساذجًا. فمهمّته الوحيدة كانت توجيه البريد من برنامج إلى آخر؛ وبخاصة، لم يكن لديه دعم لـ SMTP، ولم يكن لذا يُنشئ أي اتصالات شبكة مباشرة. ولم تكن هناك حاجة إلى طابور (queueing) لأن كل شبكة كانت تملك طابورها الخاص، لذا كان البرنامج في جوهره مجرد مفتاح تحويل (crossbar switch). ولأن delivermail لم يكن يملك دعمًا مباشرًا لبروتوكولات الشبكة، لم يكن هناك سبب ليشتغل كبرنامج خدمي دائم (daemon)—كان يُستدعى لتوجيه كل رسالة لحظة تقديمها، ويمرّرها إلى البرنامج المناسب الذي ينفّذ القفزة التالية، ثم ينتهي. كما لم تكن هناك أي محاولة لإعادة كتابة الترويسات لتطابق الشبكة التي تُسلَّم إليها الرسالة. وكثيرًا ما نتج عن ذلك رسائل تُوجَّه ولا يمكن الرد عليها. وكانت الحالة سيئة إلى حدّ أن كُتب كتاب كامل عن عنونة البريد (واسمه بما يناسبه تمامًا !%@:: A Directory of Electronic Mail Addressing & Networks AF94).
كان كل التهيئة في delivermail مُدمجة في الشيفرة المُصرَّفة، وكانت تستند إلى محارف خاصة في كل عنوان. وكانت تلك المحارف ذات أسبقية. فمثلًا، قد يبحث تكوين عنيد عن علامة "@"، فإن وُجدت واحدة أرسل العنوان كاملًا إلى مضيف مُعيَّن على Arpanet. وإلا، فقد يبحث عن نقطتين، ويرسل الرسالة إلى BerkNET مع المضيف والمستخدم المُعيَّنين إن وُجدتا، ثم يفحص علامة تعجب ("!") التي تشير إلى أن الرسالة يجب توجيهها إلى مُرحِّل UUCP مُعيَّن. وإلا فسيحاول التسليم المحلي. وقد ينتج عن هذا التكوين ما يلي:
| المُدخل | يُرسَل إلى {net, host, user} |
|---|---|
| foo@bar | {Arpanet, bar, foo} |
| foo:bar | {Berknet, foo, bar} |
| foo!bar!baz | {Uucp, foo, bar!baz} |
| foo!bar@baz | {Arpanet, baz, foo!bar} |
لاحظ أن فواصل العناوين تختلف في ترابطها (associativity)، مما ينتج عنه غموض لا يمكن حله إلا بالاستدلال (heuristics). فمثلًا، قد يُفسَّر المثال الأخير على نحو معقول بأنه {Uucp, foo, bar@baz} في موقع آخر.
أُدمجت التهيئة في الشيفرة المُصرَّفة لعدة أسباب: أولًا، مع فضاء عناوين 16 بت وذاكرة محدودة، كان تحليل تهيئة وقت التشغيل باهظ التكلفة. ثانيًا، كانت أنظمة تلك الحقبة مخصَّصة إلى حدٍّ كبير بحيث كان إعادة التصريف فكرة جيدة، لمجرد التأكد من أن لديك الإصدارات المحلية من المكتبات (لم تكن المكتبات المشتركة موجودة في الإصدار السادس من Unix).
أُوزِّع delivermail مع 4.0 و 4.1 BSD، وكان أنجح مما كان متوقعًا؛ فلم تكن بيركلي الموقع الوحيد ذو معماريات الشبكات المختلطة. وأصبح واضحًا أن المزيد من العمل مطلوب.
17.3.2. الموجة الثانية: sendmail 3 و 4 و 5
أُوزِّع الإصداران 1 و 2 تحت اسم delivermail. وفي مارس 1981 بدأ العمل على الإصدار 3، الذي كان سيُوزَّع تحت اسم sendmail. وفي هذه المرحلة كانت PDP-11 ذات 16 بت لا تزال مستخدمة على نطاق واسع، لكن VAX-11 ذات 32 بت كانت تكتسب شعبية، لذا بدأت كثير من القيود الأصلية المرتبطة بفضاءات العناوين الصغيرة في التخفيف.
كانت الأهداف الأولية لـ sendmail هي الانتقال إلى تهيئة وقت التشغيل، والسماح بتعديل الرسائل لتوفير التوافق عبر الشبكات للبريد المُوجَّه، وامتلاك لغة أغنى لاتخاذ قرارات التوجيه. وكانت التقنية المستخدمة في جوهرها إعادة كتابة نصية للعناوين (بناءً على رموز (tokens) لا على سلاسل محارف)، وهي آلية استُخدمت في بعض الأنظمة الخبيرة آنذاك. وكانت هناك شيفرة ارتجالية لاستخراج أي سلاسل تعليقات (بين قوسين) وحفظها، وكذلك لإعادة إدراجها بعد اكتمال إعادة الكتابة البرمجية. وكان من المهم أيضًا القدرة على إضافة حقول ترويسة أو استكمالها (مثل إضافة حقل ترويسة Date، أو تضمين الاسم الكامل للمرسل في ترويسة From إن كان معروفًا).
بدأ تطوير SMTP في نوفمبر 1981. كانت مجموعة أبحاث علوم الحاسوب (Computer Science Research Group, CSRG) في جامعة كاليفورنيا بيركلي قد حصلت على عقد من DARPA لإنتاج منصّة قائمة على Unix لدعم الأبحاث الممولة من DARPA، بنيّة تسهيل المشاركة بين المشاريع. وقد أُنجز العمل الأوّلي على منظومة TCP/IP في ذلك الوقت، رغم أن تفاصيل واجهة المقابس (sockets) كانت لا تزال تتغيّر. وكانت بروتوكولات التطبيق الأساسية مثل Telnet و FTP قد أُنجزت، لكن SMTP لم يكن قد نُفِّذ بعد. بل إن بروتوكول SMTP نفسه لم يكن قد استقر بعد؛ فكانت هناك نقاش حادّ حول كيفية إرسال البريد باستخدام بروتوكول سيُسمّى إبداعيًا Mail Transfer Protocol (MTP). وبينما اشتعل الجدال، أصبح MTP أكثر تعقيدًا شيئًا فشيئًا حتى دُرس SMTP (Simple Mail Transfer Protocol) بشكل شبه نهائي، من فرط الإحباط (لكنه لم يُنشر رسميًا إلا في أغسطس 1982). رسميًا، كنتُ أعمل في نظام إدارة قواعد البيانات العلائقية INGRES، لكن بما أنني كنتُ أعرف منظومة البريد أكثر من أي شخص آخر حول بيركلي آنذاك، فقد قُنعوني بتنفيذ SMTP.
كانت فكرتي الأولى أن أصنع عامل بريد SMTP منفصلًا له طابوره وبرنامج خدمي دائم الخاصان به؛ وسيتّصل ذلك النظام الفرعي بـ sendmail ليؤدي التوجيه. لكن عدة ميزات في SMTP جعلت هذا مشكلًا. فمثلًا، كان الأمران EXPN و VRFY يتطلبان الوصول إلى وحدات التحليل والترميز المستعار (aliasing) والتحقق من العناوين المحلية. كما فكّرت آنذاك أن الأمر RCPT من المهم أن يعود فورًا إذا كان العنوان مجهولًا، بدلًا من قبول الرسالة ثم إرسال رسالة فشل في التسليم لاحقًا. وقد ثبت أن هذا القرار كان بليغًا في التوقّع. ومن المفارقة أن كثيرًا من وكلاء نقل البريد (MTAs) في وقت لاحق أخطأوا في هذا، مما جعل مشكلة ارتداد الرسائل المزعجة (spam backscatter) أسوأ. وقد دفعت هذه المسائل إلى قرار إدراج SMTP ضمن sendmail نفسه.
أُوزِّع sendmail 3 مع 4.1a و 4.1c BSD (إصدارات تجريبية)، وأُوزِّع sendmail 4 مع 4.2 BSD، وأُوزِّع sendmail 5 مع 4.3 BSD.
17.3.3. الموجة الثالثة: سنوات الفوضى
بعد أن تركتُ بيركلي وذهبت إلى شركة ناشئة، انخفض بسرعة الوقت المتاح لي للعمل على sendmail. لكن كان الإنترنت قد بدأ ينفجر فعلًا، وكان sendmail يُستخدم في مجموعة متنوعة من البيئات الجديدة (والأكبر). فأنشأ معظم بائعي أنظمة Unix (وبخاصة Sun و DEC و IBM) نسخهم الخاصة من sendmail، وجميعها كانت غير متوافقة فيما بينها. وكانت هناك أيضًا محاولات لبناء نسخ مفتوحة المصدر، وعلى رأسها IDA sendmail و KJS.
جاءت IDA sendmail من جامعة لينكوبينغ (Linköping University). تضمَّنت IDA امتدادات تسهّل التثبيت والإدارة في البيئات الأكبر، ونظام تهيئة جديد تمامًا. وكانت إحدى الميزات الرئيسية الجديدة إدراج خرائط قواعد بيانات dbm(3) لدعم المواقع شديدة الديناميكية. وكانت هذه متاحة عبر بناء جملة جديد في ملف التهيئة، واستُخدمت للوظائف العديدة بما فيها ربط العناوين من وإلى بناء نحوي خارجي (مثلًا، إرسال البريد باسم john_doe@example.com بدلًا من johnd@example.com) والتوجيه.
كان King James Sendmail (KJS، من إنتاج Paul Vixie) محاولة لتوحيد مختلف إصدارات sendmail التي ظهرت. وللأسف لم يحظَ أبدًا بالزخم الكافي لتحقيق الأثر المطلوب. وقد ساقت هذه الحقبة أيضًا فيضانًا من التقنيات الجديدة التي انعكست في منظومة البريد. فمثلًا، أضافت Sun مع العناقيد بلا أقراص (diskless clusters) خدمات الدليل YP (المعروفة لاحقًا باسم NIS) و NFS، وهو نظام الملفات الشبكي. وبخاصة، كان على YP أن تكون مرئية لـ sendmail، لأن الأسماء المستعارة (aliases) كانت مخزَّنة في YP لا في الملفات المحلية.
17.3.4. الموجة الرابعة: sendmail 8
بعد عدة سنوات، عدتُ إلى بيركلي بصفتي عضوًا في هيئة التدريس. كانت وظيفتي إدارة مجموعة تُنشّئ وتدعم بنية تحتية مشتركة للبحث حول قسم علوم الحاسوب. ولكي ينجح ذلك، كان على البيئات المرتجلة في الغالب لدى مجموعات البحث الفردية أن تتوحّد بطريقة عقلانية ما. ومثل أيام الإنترنت الأولى تمامًا، كانت مجموعات البحث المختلفة تعمل على منصّات مختلفة جذريًا، بعضها قديم إلى حدٍّ كبير. وبصفة عامة، كانت كل مجموعة بحث تشغّل أنظمتها الخاصة، ورغم أن بعضها كان مُدارًا بإتقان، فإن معظمها عانى من «الصيانة المؤجَّلة».
في معظم الحالات كان البريد الإلكتروني مجزَّأً على نحو مماثل. كان عنوان بريد كل شخص هو person@host.berkeley.edu، حيث يكون host اسم محطة العمل في مكتبه أو الخادم المشترك الذي يستخدمه (لم يكن في الحرم الجامعي حتى نطاقات فرعية داخلية)، باستثناء بضعة أشخاص خاصة كان لهم عناوين تنتهي بـ @berkeley.edu. وكان الهدف هو الانتقال إلى نطاقات فرعية داخلية (بحيث تقع كل المضيفات الفردية في النطاق الفرعي cs.berkeley.edu)، وامتلاك منظومة بريد موحّدة (بحيث يكون لكل شخص عنوان ينتهي بـ @cs.berkeley.edu). وقد أُنجز هذا الهدف على أيسر وجه بإنشاء إصدار جديد من sendmail يمكن استخدامه في أنحاء القسم.
بدأتُ بدراسة كثير من تنويعات sendmail التي أصبحت رائجة. لم يكن قصدي أن أبدأ من قاعدة شيفرة مختلفة، بل أن أفهم الوظائف التي وجدها الآخرون مفيدة. تسللت كثير من تلك الأفكار إلى sendmail 8، غالبًا مع تعديلات لدمج الأفكار المترابطة أو جعلها أكثر عمومية. فمثلًا، كانت عدة إصدارات من sendmail قادرة على الوصول إلى قواعد بيانات خارجية مثل dbm(3) أو NIS؛ ودمج sendmail 8 هذه في آلية «خريطة» (map) واحدة قادرة على التعامل مع أنواع متعددة من قواعد البيانات (بل وحتى تحويلات لا علاقة لها بقواعد البيانات). وبالمثل، أُدمجت قاعدة بيانات «generics» (الربط بين الاسم الداخلي والخارجي) من IDA sendmail.
يتضمَّن sendmail 8 أيضًا حزمة تهيئة جديدة تستخدم معالج الماكروهات m4(1). وكان المقصود أن تكون أكثر تصريحيةً (declarative) من حزمة تهيئة sendmail 5 التي كانت إجرائية إلى حدٍّ كبير. أي أن حزمة تهيئة sendmail 5 كانت تطلب من المدير أن يضع ملف التهيئة كاملًا بيده، مستخدمًا في ذلك فعليًا مرفق «include» الخاص بـ m4 بوصفه اختصارًا. أما ملف تهيئة sendmail 8 فقد سمح للمدير بأن يصرّح فقط بما يلزم من ميزات ووكالات بريد (mailers) وما شابه، ثم يضع m4 ملف التهيئة النهائي.
يتناول كثير من القسم 17.7 التحسينات في sendmail 8.
17.3.5. الموجة الخامسة: السنوات التجارية
مع نمو الإنترنت وتوسّع عدد مواقع sendmail، أصبح دعم قاعدة المستخدمين المتنامية يومًا بعد يوم أكثر إشكالًا. لفترة ما تمكّنتُ من الاستمرار في الدعم عبر تشكيل مجموعة من المتطوعين (تُسمّى غير رسميًا «مجمع Sendmail»، أو sendmail.org) الذين كانوا يقدّمون دعمًا مجانيًا عبر البريد الإلكتروني ومجموعات الأخبار. لكن بحلول أواخر التسعينيات من القرن الماضي، كانت القاعدة المثبّتة قد نمت إلى حدّ جعل دعمها على أساس تطوعي شبه مستحيل. وبالشراكة مع صديق أكثر وعيًا بالأعمال، أسّستُ شركة Sendmail, Inc.[2]، على أمل الحصول على موارد جديدة تُوجَّه إلى الشيفرة.
رغم أن المنتج التجاري استند في الأصل أساسًا كبيرًا إلى أدوات التهيئة والإدارة، فقد أُضيفت كثير من الميزات الجديدة إلى وكيل نقل البريد مفتوح المصدر لتلبية احتياجات العالم التجاري. ومن أبرز ما أضافته الشركة دعمَ TLS (تشفير الاتصال)، والمصادقة عبر SMTP، وتحسينات أمنية للموقع مثل الحماية من حجب الخدمة، والأهم إضافات ترشيح البريد (واجهة Milter الموضحة أدناه).
عند كتابة هذا النص، توسّع المنتج التجاري ليشمل مجموعة كبيرة من التطبيقات القائمة على البريد الإلكتروني، وكلها تقريبًا مبنية على الامتدادات التي أُضيفت إلى sendmail في السنوات القليلة الأولى من عمر الشركة.
17.3.6. ماذا حدث لإصداري sendmail 6 و 7؟
كان sendmail 6 في الجوهر النسخة التجريبية (beta) من sendmail 8. لم يُطلَق رسميًا قط، لكنه كان موزَّعًا على نطاق واسع إلى حدٍّ لا بأس به. أما sendmail 7 فلم يوجد قط؛ إذ قفز sendmail مباشرةً إلى الإصدار 8 لأن جميع ملفات المصدر الأخرى لتوزيع BSD رُفعت إلى الإصدار 8 عند إصدار 4.4 BSD في يونيو 1993.
17.4. قرارات التصميم
كانت بعض قرارات التصميم صائبة. وبعضها بدأ صائبًا ثم أصبح خاطئًا مع تغيّر العالم. وبعضها كان موضع شك ولم يصبح أقلّ شكًّا.
17.4.1. بناء جملة ملف التهيئة
كان بناء جملة ملف التهيئة دافعًا بعدة قضايا. أولًا، كان على التطبيق بأكمله أن يتّسع في فضاء عناوين 16 بت، لذا كان على المحلّل (parser) أن يكون صغيرًا. ثانيًا، كانت التهيئات المبكرة قصيرة نسبيًا (أقل من صفحة)، لذا كان الملف مفهومًا رغم غموض بنائه. لكن مع مرور الوقت، انتقلت قرارات تشغيلية أكثر فأكثر من شيفرة C إلى ملف التهيئة، وبدأ الملف في النمو. واكتسب ملف التهيئة سمعةً بأنه «غامض حتى الإزعاج» (arcane). ومن الإحباطات الخاصة لدى كثيرين اختيار محرف الجدولة (tab) كعنصر بناء جملة فعّال. وهذا كان خطأً نُقل عن أنظمة أخرى في تلك الحقبة، وعلى رأسها make. وأصبحت هذه المشكلة تحديدًا أكثر حدّة مع توافر أنظمة النوافذ (وبالتالي القص واللصق، اللذين لا يحفظان الجدولة عادةً).
وبمنظار الرؤية المتأخرة، مع ضخامة الملف وغلبة الآلات ذات 32 بت، كان من المعقول إعادة النظر في البناء. وهناك وقت فكّرت فيه في ذلك لكنني تراجعت، لأنني لم أرغب في كسر القاعدة المثبّتة «الكبيرة» (والتي كانت حينها على الأرجح بضع مئات من الآلات). وبمنظار الرؤية المتأخرة كان هذا خطأً؛ فلم أقدّر ببساطة مدى نمو القاعدة المثبّتة، ولا كم ساعة كان ليسوفّرها عليّ لو غيّرت البناء مبكرًا. كذلك، حين استقرّت المعايير إلى حدٍّ كبير، كان من الممكن إعادة قدر كبير من العمومية إلى قاعدة شيفرة C، ممّا يبسّط التهيئات.
كان موضع اهتمام خاص هو كيفية انتقال مزيد من الوظائف إلى ملف التهيئة. كنتُ أطوّر sendmail في الوقت نفسه الذي كان فيه معيار SMTP يتطور. وبنقل القرارات التشغيلية إلى ملف التهيئة تمكّنتُ من الاستجابة بسرعة لتغيّرات التصميم—عادةً في أقل من 24 ساعة. وأعتقد أن هذا قد حسّن معيار SMTP، إذ أصبح من الممكن الحصول على خبرة تشغيلية بتغيير تصميم مقترح بسرعة كبيرة، لكن ذلك كان بثمن جعل ملف التهيئة صعب الفهم.
17.4.2. قواعد إعادة الكتابة
كان أحد القرارات الصعبة عند كتابة sendmail هو كيفية إجراء إعادة الكتابة اللازمة للسماح بالتوجيه بين الشبكات دون مخالفة معايير الشبكة المستلِمة. كانت التحويلات المطلوبة تغيّر محارف التعريف (metacharacters) (مثلًا، استخدمت BerkNET النقطتين كفاصل، وهو أمر غير قانوني في عناوين SMTP)، وإعادة ترتيب مكوّنات العنوان، وإضافة مكوّنات أو حذفها، إلخ. فمثلًا، كانت إعادة الكتابات التالية مطلوبة في ظروف معينة:
| من | إلى |
|---|---|
| a:foo | a.foo@berkeley.edu |
| a!b!c | b!c@a.uucp |
| <@a.net,@b.org:user@c.com> | <@b.org:user@c.com> |
لم تكن التعبيرات النمطية (regular expressions) خيارًا جيدًا لأنها لم تكن تدعم جيدًا حدود الكلمات والاقتباس وما شابه. وأصبح من البديهي بسرعة أنه سيكون من المستحيل تقريبًا كتابة تعبيرات نمطية دقيقة، فضلًا عن أن تكون مفهومة. وبخاصة، تحجز التعبيرات النمطية عددًا من محارف التعريف، منها "." و "*" و "+" و "{[}" و "{]}"، وجميعها قد تظهر في عناوين البريد الإلكتروني. كان من الممكن تفادي هذه المحارف في ملفات التهيئة، لكني رأيت ذلك معقّدًا ومربكًا وقبيحًا بعض الشيء. (وقد جُرِّب هذا من قِبل UPAS من مختبرات بيل، وهو وكيل البريد في الإصدار الثامن من Unix، لكنه لم ينتشر أبدًا[3].) وبدلًا من ذلك، كانت هناك حاجة إلى مرحلة مسح (scanning) تُنتج رموزًا (tokens) يمكن معالجتها بعدها كثيرًا كما تُعالَج المحارف في التعبيرات النمطية. وقد كفى معامل واحد يصف «محارف العمليات»، التي هي نفسها في الوقت نفسه رموز وفواصل بين الرموز. كانت المسافات تفصل بين الرموز لكنها ليست رموزًا بذاتها. وكانت قواعد إعادة الكتابة مجرد أزواج من «مطابقة نمط/استبدال»، نُظِّمت بما يشبه الدوال الفرعية (subroutines).
بدلًا من العدد الكبير من محارف التعريف التي كان يجب تفاديها للتخلص من خصائصها «السحرية» (كما في التعبيرات النمطية)، استخدمتُ محرف «هروب» (escape) واحدًا يتّحد مع المحارف العادية لتمثيل أنماط البدل (wildcard patterns) (لمطابقة كلمة اعتباطية مثلًا). كان نهج Unix التقليدي هو استخدام الشرطة المائلة العكسية، لكن هذه كانت مستخدمة بالفعل كمحرف اقتباس في بعض صياغات العناوين. وقد تبيّن أن "$" كان أحد المحارف القليلة التي لم تكن قد استُخدمت بالفعل كمحرف ترقيم في صياغة بريد ما.
كان أحد القرارات السيئة في الأصل، في المفارقة، مجرد مسألة تتعلق بكيفية استخدام المسافات البيضاء. فكان محرف المسافة فاصلًا، كما في معظم المدخلات الممسوحة، لذا كان من الممكن استخدامه بحرية بين الرموز في الأنماط. لكن ملفات التهيئة الأصلية المُوزَّعة لم تتضمّن مسافات، مما أنتج أنماطًا أصعب بكثير في الفهم مما ينبغي. تأمّل الفرق بين النمطين التاليين (المتكافئين دلاليًا):
$+ + $* @ $+ . $={mydomain}
$++$*@$+.$={mydomain}
17.4.3. استخدام إعادة الكتابة للتحليل
اقترح بعض الناس أن على sendmail أن يستخدم تقنيات التحليل المعتمدة على القواعد (grammar-based parsing) لتحليل العناوين بدلًا من قواعد إعادة الكتابة، ويترك قواعد إعادة الكتابة لتعديل العناوين. للوهلة الأولى يبدو هذا منطقيًا، بالنظر إلى أن المعايير تعرّف العناوين باستخدام قاعدة (grammar). والسبب الرئيسي لإعادة استخدام قواعد إعادة الكتابة هو أنه في بعض الحالات كان من الضروري تحليل عناوين حقول الترويسة (مثلًا، لاستخراج مُرسِل المظروف (envelope) من ترويسة عند استلام بريد من شبكة لا تملك مظروفًا رسميًا). وهذه العناوين ليست سهلة التحليل بواسطة محلّل LALR(1) مثل YACC وماسح تقليدي، بسبب حجم النظر الأمامي (lookahead) المطلوب. فمثلًا، تحليل العنوان: allman@foo.bar.baz.com يتطلب نظرًا أماميًا من الماسح أو من المحلّل؛ فأنت لا تستطيع أن تعرف أن "allman@…" الابتدائية ليست عنوانًا حتى ترى الرمز "<". وبما أن محلّلات LALR(1) لا تملك سوى رمز واحد للنظر الأمامي، لكان على هذا العمل أن يجري في الماسح، وهو ما كان سيعقّده كثيرًا. وبما أن قواعد إعادة الكتابة كانت تملك بالفعل ارتدادًا حرًا (أي أنها تستطيع النظر إلى الأمام مسافة اعتباطية)، فقد كانت كافية.
كان سبب ثانٍ هو أنه كان من السهل نسبيًا جعل الأنماط تتعرّف على المدخلات التالفة وتُصلحها. وأخيرًا، كانت إعادة الكتابة أكثر من كافية لتأدية المهمة، وكان إعادة استخدام أي شيفرة أمرًا حكيمًا.
توجد نقطة غير معتادة في قواعد إعادة الكتابة: عند إجراء مطابقة النمط، من المفيد أن يكون كلٌّ من المدخل والنمط مُقطَّعًا إلى رموز. ومن ثم يُستخدم الماسح نفسه للمداخل (عناوين) وللأنماط نفسها. وهذا يتطلب أن يُستدعى الماسح بجداول أنواع محارف مختلفة للمدخلات المختلفة.
17.4.4. دمج SMTP والطوابير داخل sendmail
كانت طريقة «بديهية» لتنفيذ SMTP الصادر (جانب العميل) هي بناءه كوكيل بريد خارجي، على غرار UUCP، لكن هذا كان يثير عددًا من الأسئلة الأخرى. فمثلًا، هل سيكون الطابور (queueing) منوطًا بـ sendmail أم بوحدة عميل SMTP؟ لو كان منوطًا بـ sendmail، فيلزم إرسال نسخ منفصلة من الرسالة إلى كل مُرسَل إليه (أي لا يوجد «piggybacking» حيث يمكن فتح اتصال واحد ثم إرسال أوامر RCPT متعددة)، أو يلزم مسار اتصال عائد أغنى بكثير لنقل الحالة اللازمة لكل مُرسَل إليه مما هو ممكن باستخدام رموز خروج Unix البسيطة. ولو كان الطابور في وحدة العميل، لكان هناك احتمال لكميات كبيرة من التكرار؛ وبخاصة، كانت شبكات أخرى مثل XNS آنذاك لا تزال مرشّحة محتملة. وإضافةً إلى ذلك، فإن إدراج الطابور داخل sendmail نفسه وفّر طريقة أكثر أناقة للتعامل مع بعض أنواع الأعطال، وعلى رأسها المشكلات العابرة مثل استنزاف الموارد.
كان SMTP الوارد (جانب الخادم) ينطوي على مجموعة مختلفة من القرارات. كنتُ أشعر آنذاك بأن من المهم تنفيذ أوامر VRFY و EXPN الخاصة بـ SMTP بإتقان، وهو ما يتطلب الوصول إلى آلية الأسماء المستعارة (alias). وكان هذا يتطلب مرة أخرى تبادل بروتوكول أغنى بكثير بين وحدة خادم SMTP و sendmail مما هو ممكن باستخدام أسطر الأوامر ورموز الخروج—بل بروتوكول شبيه بـ SMTP نفسه.
أميل اليوم أكثر بكثير إلى إبقاء الطابور في نواة sendmail مع نقل جانبَي تنفيذ SMTP إلى عمليَّتين منفصلتين. وأحد الأسباب هو كسب أمني: ففور أن يفتح جانب الخادم نسخة على المنفذ 25 لم تعد هناك حاجة إلى صلاحيات root. تُعقّد الامتدادات الحديثة مثل توقيع TLS و DKIM جانب العميل (لأن المفاتيح الخاصة لا ينبغي أن تكون متاحة للمستخدمين غير المخوَّلين)، لكن من الناحية الصارمة لم تعد صلاحيات root ضرورية. ورغم أن مسألة الأمان ما تزال قائمة هنا، إلا أنه إذا كان عميل SMTP يعمل باسم مستخدم غير root يستطيع قراءة المفاتيح الخاصة، فإن هذا المستخدم يملك بحكم التعريف امتيازات خاصة، ولا ينبغي له بالتالي أن يتواصل مباشرةً مع المواقع الأخرى. ويمكن صقل كل هذه المسائل بقدرٍ من العمل.
17.4.5. تنفيذ الطابور
اتّبع sendmail أعراف تلك الحقبة في تخزين ملفات الطابور. في الواقع، الصيغة المستخدمة شديدة الشبه بالنظام الفرعي lpr في تلك الحقبة. كان لكل مهمة ملفان، أحدهما يحمل معلومات التحكّم والآخر يحمل البيانات. وكان ملف التحكّم ملفًا نصيًا مسطّحًا، يمثّل أول محرف في كل سطر معنى ذلك السطر.
عندما أراد sendmail معالجة الطابور، كان عليه قراءة كل ملفات التحكّم وتخزين المعلوماتذات الصلة في الذاكرة، ثم فرز القائمة. كان ذلك يعمل على نحو سليم مع عدد صغير نسبيًا من الرسائل في الطابور، لكنه بدأ ينهار عند نحو 10,000 رسالة في الطابور. وتحديدًا، عندما يصبح الدليل كبيرًا بما يكفي لاستلزم كتلًا غير مباشرة (indirect blocks) في نظام الملفات، تظهر «ركبة» أداء جوهرية يمكن أن تخفض الأداء بمقدار رتبة كاملة (order of magnitude). وكان من الممكن التخفيف من هذه المشكلة بأن يفهم sendmail أدلة طوابير متعددة، لكن ذلك كان في أفضل الأحوال حلًا مؤقتًا.
قد تكون تنفيذية بديلة أن تُخزَّن كل ملفات التحكّم في ملف قاعدة بيانات واحد. ولم يحدث ذلك لأن بدء كتابة شيفرة sendmail لم تكن هناك حزمة قواعد بيانات متاحة على نطاق عام، وعندما أصبح dbm(3) متاحًا كان به عدة عيوب، منها عدم القدرة على استعادة المساحة، واشتراط أن تناسب جميع المفاتيح التي تُجرَّى عليها التجزئة صفحة واحدة (512 بايت)، وغياب القفل. ولم تظهر حزم قواعد البيانات المتينة إلا بعد سنوات عديدة.
كان التنفيذ البديل الآخر أن يكون هناك برنامج خدمي دائم منفصل يحتفظ بحالة الطابور في الذاكرة، ويحتمل يكتب سجلًا يسمح بالتعافي. ونظرًا إلى أن أحجام حركة البريد الإلكتروني في تلك الحقبة كانت نسبيًا منخفضة، وأن ذاكرة معظم الآلات كانت شحيحة، وأن كلفة العمليات الخلفية كانت مرتفعة نسبيًا، وأن تنفيذ عملية كهذه معقّد، فلا بد أن يبدو ذلك مخاطرة غير موفّقة في تلك الحقبة.
كان قرار تصميم آخر هو تخزين ترويسة الرسالة في ملف تحكّم الطابور بدلًا من ملف البيانات. والمنطلق هنا هو أن معظم الترويسات تحتاج إلى إعادة كتابة كبيرة تختلف من وجهة إلى أخرى (ولأن الرسالة قد يكون لها أكثر من وجهة، فلا بد من تخصيصها أكثر من مرة)، وأن كلفة تحليل الترويسات تبدو مرتفعة، لذا بدا تخزينها بصيغة محلَّلة مسبقًا توفيرًا. وبمنظار الرؤية المتأخرة لم يكن هذا قرارًا جيدًا، وكذلك كان تخزين جسم الرسالة بصيغة معيار Unix (بنهايات سطر) بدلًا من الصيغة التي وردت بها (والتي قد تستخدم أسطرًا جديدة، أو carriage-return/line-feed، أو carriage-return مجرّدًا، أو line-feed/carriage-return). ومع تطوّر عالم البريد الإلكتروني واعتماد المعايير، قلّت الحاجة إلى إعادة الكتابة، وحتى إعادة الكتابة التي تبدو بريئة تحمل خطر الخطأ.
17.4.6. قبول المدخلات التالفة وإصلاحها
نظرًا لأن sendmail أُنشئ في عالم متعدد البروتوكولات يكاد يخلو من المعايير المكتوبة، فقد قرّرتُ تنظيف الرسائل المشوّهة أينما أمكن. وهذا يطابق «مبدأ المتانة» (Robustness Principle، المعروف أيضًا بـ«قانون بوستل» (Postel's Law)) كما صاغه RFC 793[4]. كانت بعض هذه التغييرات بديهية بل ضرورية: عند إرسال رسالة UUCP إلى Arpanet، كانت عناوين UUCP تحتاج إلى التحويل إلى عناوين Arpanet، ولو لمجرد السماح لأوامر «الرد» بالعمل على نحو صحيح، وكانت نهايات الأسطر تحتاج إلى التحويل بين أعراف المنصّات المختلفة، وهكذا. وكانت بعضها أقلّ وضوحًا: فإذا وردت رسالة لا تتضمّن حقل ترويسة From: المطلوب في مواصفات الإنترنت، فهل تضيف حقل ترويسة From:، أم تمرّر الرسالة دون حقل الترويسة From:، أم ترفض الرسالة؟ في ذلك الوقت كان اعتباري الأول هو التوافقية (interoperability)، لذا رقّع sendmail الرسالة، مثلًا بإضافة حقل ترويسة From:. غير أن هذا قيل إنه سمح بإطالة عمر منظومات البريد المعطوبة البعيدة عن الإصلاح أو الإجهاض بعد وقت طويل كان ينبغي أن تُصلَح فيه أو تُطرح بعيدًا.
أعتقد أن قراري كان صحيحًا في حينه، لكنه مسألة مزعجة اليوم. فدرجة عالية من التوافقية كانت مهمة كي يتدفّق البريد دون عوائق. لو كنتُ أرفض الرسائل المشوّهة، لأُرفض معظم الرسائل في ذلك الوقت. ولو كنتُ أمرّرها دون إصلاح، لتلقّى المستلِمون رسائل لا يستطيعون الرد عليها، وفي بعض الحالات لا يستطيعون حتى تحديد من أرسل الرسالة—أو كانت الرسالة ستُرفض من وكيل بريد آخر.
أصبحت المعايير اليوم مكتوبة، وهي في معظمها دقيقة وكاملة. ولم يعد الحال أن معظم الرسائل ستُرفض، ومع ذلك لا تزال هناك برمجيات بريد ترسل رسائل مشوّهة. وهذا يخلق بلا داعٍ مشكلات لا تعدّ لبرمجيات أخرى على الإنترنت.
17.4.7. التهيئة واستخدام M4
لمدةٍ ما كنتُ أُجري تعديلات منتظمة على ملفات تهيئة sendmail وأدعم شخصيًا كثيرًا من الآلات. ونظرًا لأن قدرًا كبيرًا من ملف التهيئة كان متطابقًا بين الآلات المختلفة، فقد كان من المرغوب استخدام أداة لبناء ملفات التهيئة. وكان معالج الماكروهات m4 مضمَّنًا مع Unix. صُمِّم ليكون واجهة أمامية للغة البرمجة (وبخاصة ratfor). والأهم من ذلك، كانت له قدرات «include»، مثل "#include" في لغة C. وكانت ملفات التهيئة الأصلية لا تستخدم أكثر من هذه القدرة بكثير، إلى جانب بعض توسيع الماكروهات الصغير.
استخدمت IDA sendmail أيضًا m4، لكن بطريقة مختلفة جذريًا. وبمنظار الرؤية المتأخرة كان ينبغي لي على الأرجح أن أدرس هذه النماذج الأولية بتفصيل أكبر. كانت تحتوي على أفكار ذكية كثيرة، وبخاصة الطريقة التي تعاملت بها مع الاقتباس.
انطلاقًا من sendmail 6، أُعيدت كتابة ملفات تهيئة m4 بالكامل لتصبح بأسلوب أكثر تصريحيةً (declarative) وأصغر بكثير. وهذا استخدم قدرًا أكبر بكثير من قدرات معالج m4، وهو ما كان مشكلًا عندما غيّر إدخال GNU m4 بعض الدلالات بطرق دقيقة.
كانت الخطة الأصلية أن تتبع تهيئات m4 قاعدة 80/20: أن تكون بسيطة (وبالتالي 20% من العمل) وأن تغطي 80% من الحالات. وقد انهار هذا سريعًا لسببين. فالسبب الطفيف هو أنه تبيّن أن معالجة الغالبية العظمى من الحالات كانت سهلة نسبيًا، على الأقل في البداية. لكنها صارت أصعب بكثير مع تطوّر sendmail وتطوّر العالم، وبخاصة مع إدراج ميزات مثل تشفير TLS ومصادقة SMTP، لكن هذه لم تأتِ إلا بعد وقت طويل.
السبب المهم هو أنه أصبح واضحًا أن ملف التهيئة الخام ببساطة صعب جدًا على معظم الناس في إدارته. وفي الجوهر، أصبح تنسيق .cf (الخام) أشبه بشيفرة تجميع (assembly code)—قابلًا للتحرير من حيث المبدأ، لكنه معتم تمامًا في الواقع. وكانت «الشيفرة المصدرية» نصًّا برمجيًا بلغة m4 مخزَّنًا في ملف .mc.
من الفروق المهمة أيضًا أن ملف التهيئة الخام كان في الواقع لغة برمجة. كان فيه شيفرة إجرائية (مجموعات قواعد (rulesets))، واستدعاءات دوال فرعية، وتوسيع معاملات، وحلقات (لكن بلا قفزات gotos). وكان البناء غامضًا، لكنه في جوانب كثيرة يشبه أوامر sed و awk، على الأقل من حيث المفهوم. أما تنسيق m4 فكان تصريحيًا: فرغم كان من الممكن النزول إلى اللغة الخام منخفضة المستوى، إلا أن هذه التفاصيل كانت مخفية عن المستخدم عمليًا.
من غير الواضح إن كان هذا القرار صحيحًا أم خاطئًا. شعرتُ في ذلك الوقت (ولا أزل أشعر) بأن الأنظمة المعقدة قد يكون من المفيد فيها تنفيذ ما يشبه لغة خاصة بالمجال (Domain Specific Language, DSL) لبناء أجزاء معيّنة من تلك المنظومة. لكن كشف تلك اللغة للمستخدم النهائي كمنهج للتهيئة يحوّل جوهرًا كل محاولة لتهيئة نظام إلى مسألة برمجة. ويترتّب على ذلك قوة عظيمة، لكن بثمن ليس بسيطًا.
17.5. اعتبارات أخرى
تستحق عدة نقاط معمارية وتطويرية أخرى الإشارة إليها.
17.5.1. كلمة حول تحسين الأنظمة بحجم الإنترنت
في معظم الأنظمة المعتمدة على الشبكة هناك توتر بين العميل والخادم. فقد تكون استراتيجية جيدة للعميل هي الشيء الخطأ للخادم، والعكس. فمثلًا، يودّ الخادم، متى أمكن، تقليل تكاليف المعالجة بودفع أكبر قدر ممكن إلى العميل، وبالطبع يشعر العميل بالرغبة نفسها لكن في الاتجاه المعاكس. فمثلًا، قد يريد خادم أن يبقي الاتصال مفتوحًا أثناء تنفيذ معالجة البريد المزعج، لأن ذلك يخفّض كلفة رفض رسالة (وهي الحالة الشائعة في هذه الأيام)، لكن العميل يريد الانتقال إلى التالي في أقرب وقت. وبالنظر إلى المنظومة كاملة، أي الإنترنت ككل، قد يكون الحل الأمثل هو موازنة هذين الاحتياجين.
لقد كانت هناك حالات لوكلاء نقل البريد (MTAs) استخدموا استراتيجيات تفضّل صراحةً إما العميل أو الخادم. ويمكنهم ذلك فقط لأن قاعدة التثبيت لديهم صغيرة نسبيًا. وعندما يُستخدم نظامك على جزء كبير من الإنترنت، عليك أن تصمّمه بحيث يوازن الحمل بين الطرفين في محاولة لتحسين الإنترنت ككل. وقد يعقّد ذلك وجود وكالات نقل بريد (MTAs) مائلة كليًا في اتجاه أو الآخر على الدوام—فمثلًا، لا تهتم أنظمة البريد الجماعي إلا بتحسين الجانب الصادر.
عند تصميم منظومة تضمّ جانبَي الاتصال، من المهم تجنّب محاباة أحد الطرفين. لاحظ أن هذا في تناقض تام مع عدم التماثل المعتاد بين العملاء والخدمات—فمثلًا، خوادم الويب وعملاء الويب لا يطوّرهم عادةً المجموعات نفسها.
17.5.2. Milter
كانت واجهة Milter (mail filter) من أهم الإضافات إلى sendmail. تتيح Milter استخدام إضافات (plugins) خارج العملية (offboard)، أي أنها تعمل في عملية منفصلة لمعالجة البريد. وقد صُمِّمت هذه في الأصل لمعالجة البريد المزعج. ويعمل بروتوكول Milter بصورة متزامنة (synchronous) مع بروتوكول خادم SMTP. ومع ورود كل أمر SMTP جديد من العميل، يستدعي sendmail إضافات Milter بالمعلومات الواردة في ذلك الأمر. ولـ Milter فرصة قبول الأمر أو إرسال رفض، وهو ما يرفض مرحلة البروتوكول المناسبة لأمر SMTP ذاك. وتُنمذج Milters على هيئة استدعاءات راجعة (callbacks)، فمع ورود أمر SMTP تُستدعى الدالة الفرعية المناسبة. وتعمل Milters في خيوط متعددة (threaded)، مع تمرير مؤشر سياق خاص بكل اتصال إلى كل روتين للسماح بتمرير الحالة.
نظريًا كان بإمكان Milters أن تعمل كوحدات قابلة للتحميل (loadable modules) في فضاء عناوين sendmail. ورفضنا ذلك لثلاثة أسباب. أولًا، كانت المسائل الأمنية بالغة الخطورة: حتى لو كان sendmail يعمل بمعرّف مستخدم فريد غير root، فإن ذلك المستخدم سيتمكّن من الوصول إلى حالة كل الرسائل الأخرى. وعلى نحو مماثل، كان حتميًا أن يحاول بعض مطوّري Milter الوصول إلى الحالة الداخلية لـ sendmail.
ثانيًا، أردنا إنشاء جدار حماية (firewall) بين sendmail و Milters: فإذا تعطّل أحدها أردنا أن يكون واضحًا من هو المخطئ، وأن يستمر البريد (محتملًا) في التدفق. ثالثًا، كان من الأسهل بكثير لمطوّر Milter أن ينقّح عملية مستقلة من sendmail ككل.
أصبح من الوضوح بسرعة أن Milter مفيدة لأكثر من مجرد معالجة البريد المزعج. فموقع milter.org[5] على الويب يسرد Milters لمكافحة البريد المزعج، ولمكافحة الفيروسات، وللأرشفة، ولمراقبة المحتوى، وللتسجيل، ولتشكيل حركة البيانات، وفئات كثيرة أخرى، من إنتاج شركات تجارية ومشاريع مفتوحة المصدر. وقد أضاف وكيل البريد postfix[6] دعمًا لـ Milters باستخدام الواجهة نفسها. وقد أثبتت Milters أنها إحدى النجاحات الكبرى لـ sendmail.
17.5.3. جداول الإصدار
تدور نقاش شعبي بين مدرستَي التفكير «أَصدِر مبكرًا وبكثرة» و«أَصدِر أنظمة مستقرة». وقد استخدم sendmail كلا الأسلوبين في أوقات مختلفة. وخلال فترات التغيّر الكبير كنتُ أحيانًا أُصدر أكثر من إصدار في اليوم الواحد. وكانت فلسفتي العامة هي إصدار نسخة بعد كل تغيير. وهذا يشبه إتاحة الوصول العام إلى شجرة نظام إدارة المصدر. وأنا أفضّل شخصيًا الإصدارات على إتاحة أشجار المصدر علنًا، على الأقل جزئيًا، لأنني أستخدم إدارة المصدر بطريقة صارت الآن غير معتمدة: فبالنسبة للتغييرات الكبيرة، أُودع لقطات غير عاملة أثناء كتابة الشيفرة. وإذا كانت الشجرة مشتركة، فإنني أستخدم الفروع لهذه اللقطات، لكن في كل الأحوال تكون متاحة للعالم لترى وقد تخلق ارتباكًا كبيرًا. أيضًا، إنشاء إصدار يعني وضع رقم عليه، مما يجعل تتبع التغييرات أسهل عند مراجعة بلاغ خطأ. وبالطبع، هذا يتطلب أن تكون الإصدارات سهلة التوليد، وهو ليس دائمًا كذلك.
مع ازدياد استخدام sendmail في بيئات إنتاج متزايدة الأهمية، بدأ هذا يصبح مشكلة. لم يكن من السهل دائمًا للآخرين أن يميزوا بين التغييرات التي أردتها للنشر ليختبرها الناس وبين التغييرات التي كان المقصود حقًا استخدامها في العالم الحقيقي. وتخفيف المشكلة بإطلاق الإصدارات بصيغة «ألفا» (alpha) أو «بيتا» (beta) لم يُصلحها. وكانت النتيجة أنه مع نضج sendmail اتجه إلى إصدارات أقلّ تكرارًا لكن أكبر حجمًا. وأصبح هذا أكثر حدّة خصوصًا حين دُمج sendmail في شركة تجارية كان عندها عملاء يريدون الأحدث والأفضل لكن أيضًا الإصدارات المستقرة فقط، ولا يقبلون أن الأمرين متعارضان.
لن يزول هذا التوتر أبدًا بين احتياجات مطوّري المصادر المفتوحة واحتياجات المنتج التجاري. فهناك فوائد كثيرة للإصدار المبكر المتكرر، وعلى رأسها الجمهور الواسع المحتمل من المختبِرين الشجعان (والمتهورين أحيانًا) الذين يضغطون على النظام بطرق لا تكاد تتخيّل أن تُعاد في منظومة تطوير قياسية. لكن ما إن يصبح المشروع ناجحًا حتى يميل إلى التحول إلى منتج (حتى لو كان ذلك المنتج مفتوح المصدر ومجانيًا)، والمنتجات لها احتياجات مختلفة عن المشاريع.
17.6. الأمن
كانت لدى sendmail حياة مضطربة من ناحية الأمن. بعض ذلك مستحق، وبعضه لا، إذ تغيّر مفهومنا لـ«الأمن» من تحت أقدامنا. بدأ الإنترنت بقاعدة مستخدمين من بضعة آلاف من الأشخاص، معظمهم في بيئات أكاديمية وبحثية. وكان، بجوانب كثيرة، إنترنت ألطف وأرقّ مما نعرفه اليوم. صُمّمت الشبكة لتشجّع المشاركة لا لبناء جُدُر حماية (وهو مفهوم آخر لم يكن موجودًا في الأيام الأولى). أما الشبكة اليوم فهي مكان خطير وعدائي مليء بمرسلي البريد المزعج والمخترقين. ويجري وصفها على نحو متزايد بأنها منطقة حرب، وفي مناطق الحرب تكون هناك ضحايا مدنيون.
من الصعب كتابة خوادم الشبكة بأمان، خاصةً عندما يكون البروتوكول أبعد من أبسط الأشكال. وقد عانت كل البرامج تقريبًا من مشكلات، ولو طفيفة؛ بل إن تطبيقات TCP/IP الشائعة هوجمت بنجاح. ولم تَبدُ لغات التطبيق الأعلى مستوىً علاجًا شافيًا، بل خَلَقت حتى ثغراتها الخاصة. والعبارة الواجب التذكّر بها هي «لا تثق بأي مُدخل»، مهما جاء من أين. ويشمل عدم الثقة بالمدخلات المدخلات الثانوية، مثل ما يأتي من خوادم DNS و Milters. ومثل معظم برمجيات الشبكة المبكرة، كان sendmail في إصداراته المبكرة واثقًا إلى حدٍّ مفرط.
لكن أكبر مشكلة في sendmail كانت أن الإصدارات المبكرة كانت تعمل بصلاحيات root. فصلاحيات root لازمة لفتح مقبس الاستماع لـ SMTP، ولقراءة معلومات إعادة التوجيه الخاصة بكل مستخدم، وللتسليم إلى صناديق بريد المستخدمين وأدلتهم الرئيسية. لكن في معظم الأنظمة اليوم صار مفهوم اسم صندوق البريد منفصلًا عن مفهوم مستخدم النظام، مما يُلغي فعليًا الحاجة إلى صلاحيات root باستثناء فتح مقبس الاستماع لـ SMTP. وأصبح لدى sendmail اليوم القدرة على التنازل عن صلاحيات root قبل أن يعالج اتصالًا، مما يُغني عن هذا القلق في البيئات التي تدعم ذلك. ومن الجدير بالذكر أنه في تلك الأنظمة التي لا تسلّم مباشرةً إلى صناديق بريد المستخدمين، يمكن لـ sendmail أيضًا أن يعمل في بيئة chroot، مما يسمح بعزل إضافي للصلاحيات.
للأسف، مع أن sendmail اكتسب سمعةً بالضعف الأمني، بدأ يُلام على مشكلات لا علاقة لها به إطلاقًا. فمثلًا، جعل أحد مديري الأنظمة مجلد /etc قابلًا للكتابة من الجميع، ثم لام sendmail عندما استبدل أحدهم ملف /etc/passwd. وقد أدّت حوادث كهذه إلى تشديد الأمن بدرجة كبيرة، بما في ذلك الفحص الصريح لملكية الملفات والأدلة وأنواع صلاحياتها (modes) التي يصل إليها sendmail. وكانت هذه القيود قاسية إلى حدّ ألزمنا بإدراج الخيار DontBlameSendmail لأجل إيقاف هذه الفحوص (بشكل انتقائي).
هناك جوانب أخرى من الأمن لا تتعلق بحماية فضاء عناوين البرنامج نفسه. فمثلًا، أدّى ظهور البريد المزعج إلى ظهور جلب العناوين (address harvesting) أيضًا. صُمِّما الأمران VRFY و EXPN في SMTP تحديدًا للتحقق من العناوين الفردية وتوسيع محتويات قوائم البريد على التوالي. وقد أُسيء استغلالهما من قِبل مرسلي البريد المزعج إلى حدّ أصبح معه معظم المواقع يعطّلهما كليًا. وهذا أمر مؤسف، على الأقل فيما يخص VRFY، لأن هذا الأمر كان يُستخدم أحيانًا من قِبل بعض وكلاء مكافحة البريد المزعج للتحقق من عنوان الإرسال المُفترَض.
وبالمثل، كانت الحماية من الفيروسات تُرى في يوم من الأيام مشكلة خاصة بسطح المكتب، ثم ارتفعت أهميتها حتى صار على أي وكيل نقل بريد بمستوى تجاري أن يوفّر فحصًا للفيروسات. وتشمل متطلبات أخرى متعلقة بالأمن في الإعدادات الحديثة إلزامية تشفير البيانات الحساسة، وحماية فقدان البيانات، وإنفاذ المتطلبات التنظيمية، مثل HIPPA.
كان أحد المبادئ التي أخذها sendmail على عاتقه مبكرًا هو الموثوقية—يجب أن تُسلَّم كل رسالة أو يُبلَّغ عن ذلك للمُرسِل. لكن مشكلة «حِيل جو» (joe-jobs؛ أي المهاجمون الذين يزوّرون عنوان الإرجاع في رسالة، وهو ما يعتبره كثيرون مسألة أمنية) دفعت كثيرًا من المواقع إلى تعطيل إنشاء رسائل الارتداد (bounce). وإذا أمكن تحديد العطل بينما يكون اتصال SMTP لا يزال مفتوحًا، فيستطيع الخادم الإبلاغ عن المشكلة بإفشال الأمر، لكن بعد إغلاق اتصال SMTP تختفي رسالة موجَّهة خطأً في صمت. ولحسن الحظ، معظم البريد المشروع اليوم من قفزة واحدة (single hop)، لذا ستُبلَّغ عن المشكلات، لكن على الأقل من حيث المبدأ، قرر العالم أن الأمن يغلب على الموثوقية.
17.7. تطوّر Sendmail
لا تصمد البرمجيات في بيئة متسارعة التغيّر دون أن تتطور لتتلاءم مع البيئة المتغيّرة. تظهر تقنيات عتاد جديدة، فتدفع للتغييرات في نظام التشغيل، فتدفع للتغييرات في المكتبات والأطر، فتدفع للتغييرات في التطبيقات. وإذا نجح تطبيق، فسيُستخدم في بيئات أكثر إشكالًا على نحو متزايد. والتغيير حتمي؛ ولتنجح عليك أن تقبل التغيير وتحتضنه. ويصف هذا القسم بعض التغييرات الأهم التي حدثت مع تطوّر sendmail.
17.7.1. أصبحت التهيئة أكثر تفصيلاً
كانت التهيئة الأصلية لـ sendmail موجزة إلى حدٍّ كبير. فمثلًا، كانت أسماء الخيارات والماكروهات كلها محارف مفردة. وهناك ثلاثة أسباب لذلك. أولًا، يجعل هذا التحليل بسيطًا جدًا (وهذا مهمٌّ في بيئة 16 بت). ثانيًا، لم تكن هناك خيارات كثيرة، لذا لم يكن ابتكار أسماء تذكيرية صعبًا. ثالثًا، كان عُرف المحرف المفرد مستقرًّا أصلًا مع رايات سطر الأوامر.
وكذلك، كانت مجموعات قواعد إعادة الكتابة مُرقَّمة في الأصل بدلًا من مُسمّاة. ربما كان ذلك محتمَلًا مع عدد قليل من المجموعات، لكن مع ازدياد عددها أصبح من المهم أن تحمل أسماء تذكيرية أغنى.
ومع تزايد تعقيد البيئة التي يعمل فيها sendmail، ومع تلاشي بيئة 16 بت، أصبح واضحًا الحاجة إلى لغة تهيئة أغنى. ولحسن الحظ، كان من الممكن إجراء هذه التغييرات بطريقة متوافقة مع الإصدارات السابقة. وقد حسّنت هذه التغييرات قابلية فهم ملف التهيئة بدرجة كبيرة.
17.7.2. اتصالات أكثر مع الأنظمة الفرعية الأخرى: تكامل أكبر
عندما كُتب sendmail كانت منظومة البريد معزولة إلى حدٍّ كبير عن بقية نظام التشغيل. وكانت هناك بعض الخدمات التي تتطلب التكامل، مثل ملفَّي /etc/passwd و /etc/hosts. ولم تكن مفاتيح الخدمات (service switches) قد اخترعت بعد، ولم تكن خدمات الدليل موجودة، وكانت التهيئة صغيرة ويُصان يدويًا.
تغيّر ذلك بسرعة. كان أحد أول الإضافات هو DNS. ورغم أن تجريد البحث عن المضيف في النظام (gethostbyname) كان يعمل للبحث عن عناوين IP، فقد كان على البريد الإلكتروني أن يستخدم استعلامات أخرى مثل MX. وفي وقت لاحق، أضافت IDA sendmail وظيفة بحث في قاعدة بيانات خارجية باستخدام ملفات dbm(3). وحدّث sendmail 8 تلك الوظيفة لتصبح خدمة ربط عامة (mapping service) تسمح بأنواع قواعد بيانات أخرى، بما فيها قواعد البيانات الخارجية والتحويلات الداخلية التي لا يمكن إجراؤها باستخدام إعادة الكتابة (مثل فكّ الاقتباس عن عنوان).
يعتمد نظام البريد الإلكتروني اليوم على خدمات خارجية كثيرة، لا تُصمَّم في الغالب خصيصًا لاستخدام البريد الإلكتروني وحده. وقد دفع هذا sendmail نحو مزيد من التجريدات في الشيفرة. كما جعل صيانة منظومة البريد أصعب مع إضافة المزيد من «الأجزاء المتحركة».
17.7.3. التكيّف مع عالم عدائي
طُوِّر sendmail في عالم يبدو غريبًا تمامًا بمعايير اليوم. كانت قاعدة المستخدمين على الشبكة المبكة في معظمهم باحثون ذوو نوايا طيبة نسبيًا، رغم أن السياسة الأكاديمية كانت أحيانًا شرسة. وقد عكس sendmail العالم الذي أُنشئ فيه، يضع وزنًا كبيرًا على تمرير البريد بأقصى درجة موثوقية ممكنة، حتى في وجه أخطاء المستخدمين.
عالم اليوم أكثر عدائية بكثير. الغالبية العظمى من البريد الإلكتروني خبيثة. وقد انتقل هدف وكيل نقل البريد (MTA) من تمرير البريد إلى منع البريد السيئ. والترشيح (filtering) على الأرجح أولوية لأي MTA اليوم. وقد تطلّب ذلك عددًا من التغييرات في sendmail.
فمثلًا، أُضيفت كثير من مجموعات القواعد للسماح بفحص معاملات أوامر SMTP الواردة من أجل التقاط المشكلات في أبكر وقت ممكن. فالأرخص من حيث الكلفة هو رفض الرسالة أثناء قراءة المظروف، لا بعد أن التزمتَ بقراءة الرسالة كاملة، وهو أبهظ تكلفةً حتى بعد أن قبلتَ الرسالة للتسليم. وفي الأيام الأولى كان الترشيح يُجرى عمومًا بقبول الرسالة وتمريرها إلى برنامج ترشيح، ثم إرسالها إلى نسخة أخرى من sendmail إذا اجتازت الرسالة الفحص (وهي ما تسمى إعدادات «الساندويتش»). وهذا مكلف إلى حدٍّ بعيد أكثر مما يحتمله عالم اليوم.
وبالمثل، انتقل sendmail من كونه مستهلكًا بسيطًا جدًا لاتصالات TCP/IP إلى كونه أكثر تعقيدًا بكثير، إذ يفعل أمورًا مثل «الطلّعة» (peeking) في مدخلات الشبكة للتحقق مما إذا كان المرسل يرسل أوامر قبل الإقرار بالأمر السابق. وهذا يكسر بعض التجريدات السابقة التي صُمِّمت لجعل sendmail قابلاً للتكيّف مع أنواع شبكات متعددة. فالاليوم، سيتطلب، مثلًا، ربط sendmail بشبكة XNS أو DECnet قدرًا كبيرًا من العمل، لأن معرفة TCP/IP قد كُتبت في أجزاء كبيرة من الشيفرة.
أُضيفت كثير من ميزات التهيئة لمعالجة العالم العدائي، مثل دعم جداول الوصول، وقوائم الحجب الفوري (Realtime Blackhole Lists)، وتخفيف جلب العناوين، والحماية من حجب الخدمة، وترشيح البريد المزعج. وقد جعل هذا مهمة تهيئة منظومة البريد معقّدة إلى حدٍّ كبير، لكنه كان ضروريًا تمامًا للتكيّف مع عالم اليوم.
17.7.4. إدماج التقنيات الجديدة
لقد ظهرت معايير جديدة كثيرة على مرّ السنين تطلّبت تغييرات جوهرية في sendmail. فمثلًا، تطلّبت إضافة TLS (التشفير) تغييرات جوهرية في معظم الشيفرة. وتطلّب تجميع الأوامر في SMTP (pipelining) النظر داخل تدفّق TCP/IP منخفض المستوى لتفادي الجمود (deadlocks). وتطلّبت إضافة منفذ التسليم (587) القدرة على الاستماع إلى عدة منافذ واردة، بما في ذلك سلوكيات مختلفة بحسب منفذ الوصول.
كانت ضغوط أخرى مفروضة من الظروف لا من المعايير. فمثلًا، كانت إضافة واجهة Milter استجابة مباشرة للـ spam. ورغم أن Milter لم يكن معيارًا منشورًا، إلا أنه كان تقنية جديدة مهمة.
وفي جميع الحالات، حسّنت هذه التغييرات منظومة البريد بصورة ما، سواء بزيادة الأمان أو بتحسين الأداء أو بإضافة وظائف جديدة. غير أنها جميعًا جاءت بثمن، وفي كل الحالات تقريبًا زاد تعقيد كلٍّ من قاعدة الشيفرة وملف التهيئة.
17.8. ماذا لو كنتُ أفعل ذلك اليوم؟
الحكمة المتأخرة حكمة كاملة. هناك أشياء كثيرة كنتُ سأفعلها بشكل مختلف اليوم. بعضها لم يكن يمكن التنبؤ به حينها (مثل: كيف سيغيّر البريد المزعج تصوّرنا عن البريد الإلكتروني، أو كيف ستبدو مجموعات الأدوات الحديثة، إلخ)، وبعضها كان متوقّعًا إلى حدٍّ بعيد. وبعضها كان ببساطة أنني تعلّمتُ الكثير عن البريد الإلكتروني وعن TCP/IP وعن البرمجة نفسها أثناء كتابة sendmail—الجميع ينمو وهو يكتب شيفرته.
لكن هناك أيضًا أشياء كثيرة كنتُ سأفعلها بالطريقة نفسها، بعضها في تناقض مع الحكمة المتعارف عليها.
17.8.1. أشياء كنتُ سأفعلها بشكل مختلف
ربما كان أكبر خطأ ارتكبته مع sendmail هو أنني لم أدرك مبكرًا بما يكفي مدى أهمية ما سيصير إليه. كانت لديّ عدة فرص لدفع العالم في الاتجاه الصحيح لكنني لم أعتنمها؛ بل في بعض الحالات أضررتُ، مثلًا بعدم جعل sendmail أكثر صرامةً في التعامل مع المدخلات التالفة عندما حان الوقت المناسب لذلك. وبالمثل، أدركتُ مبكرًا نوعًا ما أن بناء جملة ملف التهيئة يحتاج إلى تحسين، حين كان قد يكون منشورًا بضع مئات من نسخ sendmail، لكنني قررتُ ألا أغيّر شيئًا لأنني لم أرغب في إلحاق ألم غير مبرَّر بقاعدة المستخدمين المثبّتة. وبمنظار الرؤية المتأخرة كان من الأفضل تحسين الأمور مبكرًا والتسبّب في ألم مؤقت من أجل نتيجة أفضل على المدى الطويل.
بناء جملة صندوق البريد في الإصدار 7
كان أحد الأمثلة على ذلك طريقة فصل الرسائل في صناديق البريد في الإصدار 7. فقد استخدموا سطرًا يبدأ بـ "From␣" (حيث "␣" يمثّل محرف المسافة في ASCII، أي 0x20) لفصل الرسائل. فإذا وردت رسالة تحتوي كلمة "From␣" في بداية السطر، فإن برمجيات صندوق البريد المحلية تحوّلها إلى ">From␣". وكان أحد التحسينات في بعض الأنظمة لا كلها هو اشتراط سطر فارغ سابق، لكن لا يمكن الاعتماد عليه. وحتى اليوم، تظهر ">From" في أماكن لا يُتوقّع فيها إطلاقًا، ولا علاقة واضحة لها بالبريد الإلكتروني (لكنها على الأرجح عولجت بالبريد الإلكتروني مرة أو أخرى). وبمنظار الرؤية المتأخرة كان بإمكاني على الأرجح تحويل منظومة بريد BSD إلى بناء جملة جديد. ولقد لُعنتُ في ذلك الوقت بكلمات نارية، لكنني كنتُ سأوفّر على العالم عناءً كبيرًا.
بناء جملة ملف التهيئة ومحتوياته
ربما كان أكبر خطأ في بناء جملة ملف التهيئة هو استخدام محرف الجدولة (HT, 0x09) في قواعد إعادة الكتابة لفصل النمط عن الاستبدال. في ذلك الوقت كنتُ أحاكي make، ولم أعلم إلا بعد سنوات أن Stuart Feldman، مؤلف make، كان يعتبر ذلك أحد أكبر أخطائه. وفوق كونه غير بديهي عند النظر في التهيئة على الشاشة، فإن محرف الجدولة لا ينجو من القص واللصق في معظم أنظمة النوافذ.
رغم أنني أعتقد أن قواعد إعادة الكتابة كانت الفكرة الصحيحة (انظر أدناه)، فإنني كنتُ سأغيّر البنية العامة لملف التهيئة. فمثلًا، لم أتوقّع الحاجة إلى التسلسلات الهرمية (hierarchies) في التهيئة (مثل خيارات تُضبط بشكل مختلف لمنافذ مستمعي SMTP المختلفة). وفي وقت تصميم ملف التهيئة لم تكن هناك تنسيقات «قياسية». اليوم، كنتُ سأميل إلى استخدام تنسيق بأسلوب Apache—إنه نظيف ومرتّب وله تعبير كافٍ—أو ربما حتى تضمين لغة مثل Lua.
عندما طُوِّر sendmail كانت فضاءات العناوين صغيرة والبروتوكولات لا تزال في تغيّر. بدا وضع أكبر قدر ممكن في ملف التهيئة فكرة جيدة. لكن هذا يبدو اليوم خطأً: فلدينا مساحة عناوين وفيرة (بالنسبة إلى MTA) والمعايير مستقرة إلى حدٍّ كبير. وإلى جانب ذلك، فإن جزءًا من «ملف التهيئة» هو في الحقيقة شيفرة تحتاج إلى تحديث في كل إصدار جديد. وملف التهيئة .mc يعالج ذلك، لكن إعادة بناء التهيئة في كل مرة تُحدِّث فيها البرمجيات أمر ممل. والحل البسيط لذلك هو ببساطة وجود ملفَّي تهيئة يقرأهما sendmail، أحدهما مخفي ويُثبَّت مع كل إصدار برمجيات جديد، والآخر ظاهر ويُستخدم للتهيئة المحلية.
استخدام الأدوات
هناك أدوات جديدة كثيرة متاحة اليوم—مثلًا، لتهيئة البرمجيات وبنائها. قد تكون الأدوات رافعة قوية إذا احتجتها، لكنها قد تكون أيضًا مبالغة، فتجعل النظام أصعب في الفهم مما ينبغي. فمثلًا، لا ينبغي أن تستخدم أبدًا قاعدة yacc(1) عندما كل ما تحتاجه هو strtok(3). لكن إعادة اختراع العجلة ليست فكرة جيدة أيضًا. وبخاصة، ورغم بعض التحفّظات، فإنني من المؤكد كنتُ سأستخدم autoconf اليوم.
التوافق مع الإصدارات السابقة
بالاستفادة من الحكمة المتأخرة، ومعرفة مدى انتشار sendmail، كنتُ لن أقلق كثيرًا من كسر التثبيتات القائمة في أيام التطوير الأولى. وعندما تكون الممارسة القائمة معطوبة بشكل جوهري، فيجب إصلاحها لا التكيّف معها. ومع ذلك، كنتُ ما زلت لن أجري فحصًا صارمًا لكل صيغ الرسائل؛ فبعض المشكلات يمكن تجاهلها أو ترقيعها بسهولة وأمان. فمثلًا، كان من المحتمل أنني سأُدرج حقل ترويسة Message-Id: في الرسائل التي لا تحتوي عليه، لكنني أكثر ميلًا اليوم إلى رفض الرسائل التي لا تحتوي على حقل ترويسة From: بدلًا من محاولة إنشائه من المعلومات الموجودة في المظروف.
التجريدات الداخلية
هناك بعض التجريدات الداخلية التي ما كنتُ سأجربها ثانية، وأخرى كنتُ سأضيفها. فمثلًا، كنتُ لن أستخدم السلاسل المنتهية بمُنهي NUL، وسأختار بدلًا منها زوجًا من الطول والقيمة، رغم أن هذا يعني صعوبة في استخدام أجزاء كثيرة من مكتبة C القياسية. فالدلالات الأمنية لهذا وحده تجعل الأمر مجديًا. وعلى العكس، ما كنتُ سأحاول بناء معالجة استثناءات (exception handling) في C، لكنني كنتُ سأنشئ نظامًا متسقًا لأكواد الحالة يُستخدم في أنحاء الشيفرة كلها، بدلًا من جعل الدوال تُعيد null أو false أو أرقامًا سالبة للدلالة على الأخطاء.
كان من المؤكد أنني كنتُ سأستخلص مفهوم أسماء صناديق البريد من معرّفات مستخدمي Unix. فعند كتابة sendmail كان النموذج أنك لا ترسل الرسائل إلا إلى مستخدمي Unix. أما اليوم، فالأمر نادرًا ما يكون كذلك؛ حتى في الأنظمة التي تستخدم ذلك النموذج، هناك حسابات نظام لا ينبغي أن تتلقى بريدًا إلكترونيًا أبدًا.
17.8.2. أشياء كنتُ سأفعلها بالطريقة نفسها
بالطبع، هناك أشياء نجحت جيدًا…
Syslog
كان syslog أحد المشاريع الجانبية الناجحة المنبثقة من sendmail. في وقت كتابة sendmail، كانت البرامج التي تحتاج إلى تسجيل تملك ملفًا محددًا تكتب إليه. وكانت هذه الملفات متناثرة في أماكن متفرقة من نظام الملفات. كان syslog صعب الكتابة في ذلك الوقت (فلم يكن UDP موجودًا بعد، لذا استخدمتُ شيئًا يُسمّى ملفات mpx)، لكنه كان يستحق العناء. غير أنني كنتُ سأجري تغييرًا واحدًا محددًا: كنتُ سأولي عناية أكبر لجعل بناء جملة الرسائل المسجَّلة قابلًا للتحليل آليًا—بعبارة أخرى، فاتني توقّع وجود مراقبة السجلات.
قواعد إعادة الكتابة
لقد تعرضت قواعد إعادة الكتابة للسخرية كثيرًا، لكنني كنتُ سأستخدمها من جديد (مع أنني ربما لن أستخدمها في كل شيء كما تُستخدم الآن). كان استخدام محرف الجدولة خطأً واضحًا، لكن مع قيود ASCII وبنية عناوين البريد الإلكتروني، فربما يلزم محرف هروب على أي حال[7]. وبصفة عامة، فإن فكرة استخدام نموذج نمط/استبدال نجحت جيدًا وكانت مرنة إلى حدٍّ كبير.
تجنّب الأدوات غير الضرورية
رغم ملاحظتي أعلاه بأنني سأستخدم المزيد من الأدوات القائمة، فإنني متردد في استخدام كثير من مكتبات وقت التشغيل المتاحة اليوم. وفي رأيي إن عددًا كبيرًا منها متضخّم إلى حدّ أنه أصبح خطرًا. ينبغي اختيار المكتبات بعناية، بموازنة مزايا إعادة الاستخدام في مقابل مشكلات استعمال أداة قوية أكثر من اللازم لحل مسألة بسيطة. وأداة بعينها كنتُ سأتجنّبها هي XML، على الأقل كلغة تهيئة. فأعتقد أن بِنتها باذخة أكثر مما ينبغي لمعظم ما استُخدمت من أجله. وXML له مكانه، لكنه مُفرط في الاستخدام اليوم.
الشيفرة بلغة C
اقترح بعض الناس أن Java أو C++ هما لغتا التنفيذ الأنسب بطبعتهما. ورغم المشكلات المعروفة جيدًا في C، فإنني كنتُ سأستخدمها كلغة تنفيذ. وهذا الأمر في جزء منه شخصي: فأنا أعرف C أفضل بكثير مما أعرف Java أو C++. لكنني أيضًا منزعج من الموقف المستهين الذي تتخذه معظم اللغات كائنية التوجه تجاه تخصيص الذاكرة. فلتخصيص الذاكرة مشكلات أداء كثيرة يصعب تحديد أبعادها. ويستخدم sendmail مفاهيم كائنية التوجه داخليًا حيث يكون ذلك مناسبًا (مثل تنفيذ أصناف الخرائط)، لكن في رأيي الانتقال الكامل إلى التوجه الكائني إهدار وقيود أكثر مما ينبغي.
17.9. الخلاصات
وُلد وكيل بريد sendmail في عالم من الفوضى الهائلة، نوع من «الغرب المتوحش» كان قائمًا حين كان البريد الإلكتروني ارتجاليًا ولم تُصاغ معايير البريد الحالية بعد. وخلال الـ 31 سنة المنقضية، تحوّل «مشكلة البريد الإلكتروني» من مجرد العمل بشكل موثوق، إلى العمل مع رسائل كبيرة وأحمال ثقيلة، إلى حماية المواقع من البريد المزعج والفيروسات، وأخيرًا اليوم إلى استخدامه كمنصّة لمجموعة من التطبيقات القائمة على البريد الإلكتروني. وقد تطوّر sendmail إلى عملٍّ متعب ينصح به حتى أكثر الشركات تحفّظًا على المخاطرة، حتى مع تطوّر البريد الإلكتروني من تواصل نصي خالص بين الأشخاص إلى جزء حرج من البنية التحتية يعتمد على الوسائط المتعددة.
أسباب هذا النجاح ليست دائمًا واضحة. فبناء برنامج يصمد بل يزدهر في عالم متغيّر بسرعة مع عدد قليل من المطورين الذين يعملون بدوام جزئي لا يمكن أن يتم بالمنهجيات التقليدية لتطوير البرمجيات. وآمل أنني قدّمتُ بعض الرؤى حول كيف نجح sendmail.
الحواشي
http://ftp.isc.org/www/survey/reports/2011/01/http://www.sendmail.comhttp://doc.cat-v.org/bell_labs/upas_mail_system/upas.pdf- "كن محافظًا فيما تفعله، وكن متساهلاً فيما تقبله من الآخرين"
http://milter.orghttp://postfix.org- لعلّ استخدام Unicode للتهيئة لن يجد قبولًا، فهذا ما أشك فيه.
http://ftp.isc.org/www/survey/reports/2011/01/↩︎http://www.sendmail.com↩︎http://doc.cat-v.org/bell_labs/upas_mail_system/upas.pdf↩︎«كن محافظًا فيما تفعله، وكن متساهلًا فيما تقبله من الآخرين» ↩︎
http://milter.org↩︎http://postfix.org↩︎لعلّ استخدام Unicode للتهيئة لن يجد قبولًا، فهذا ما أشك فيه. ↩︎