getaddrinfo(3)

الاسم
getaddrinfo, freeaddrinfo, gai_strerror - ترجمة عنوان الشبكة والخدمة

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *restrict node,
                const char *restrict service,
                const struct addrinfo *restrict hints,
                struct addrinfo **restrict res);

void freeaddrinfo(struct addrinfo *res);

const char *gai_strerror(int errcode);

    متطلبات ماكروات اختبار الميزات لـ glibc (انظر feature_test_macros(7)):

getaddrinfo(), freeaddrinfo(), gai_strerror():
    منذ glibc 2.22:
        _POSIX_C_SOURCE >= 200112L
    glibc 2.21 وما قبله:
        _POSIX_C_SOURCE

الوصف
بمعطى node و service، اللذان يُعرِّفان مضيف إنترنت وخدمة، تُرجع getaddrinfo() بنية addrinfo واحدة أو أكثر، كل منها يحتوي على عنوان إنترنت يمكن تحديده في استدعاء لـ bind(2) أو connect(2). تدمج دالة getaddrinfo() الوظائف التي توفرها دالتا gethostbyname(3) و getservbyname(3) في واجهة واحدة، ولكن على عكس الدالتين الأخيرتين، فإن getaddrinfo() قابلة لإعادة الدخول وتسمح للبرامج بإزالة التبعيات بين IPv4 و IPv6.

تحتوي بنية addrinfo المستخدمة بواسطة getaddrinfo() على الحقول التالية:

    struct addrinfo {
        int              ai_flags;
        int              ai_family;
        int              ai_socktype;
        int              ai_protocol;
        socklen_t        ai_addrlen;
        struct sockaddr *ai_addr;
        char            *ai_canonname;
        struct addrinfo *ai_next;
    };

يشير معامل hints إلى بنية addrinfo تحدد معايير اختيار بنى عنوان المقبس المُعادة في القائمة المشار إليها بواسطة res. إذا لم يكن hints NULL، فإنه يشير إلى بنية addrinfo تحدد حقولها ai_family و ai_socktype و ai_protocol معايير تحد من مجموعة عناوين المقبس التي تُرجعها getaddrinfo()، كما يلي:

ai_family
    يحدد هذا الحقل عائلة العنوان المطلوبة للعناوين المُعادة. تتضمن القيم الصالحة لهذا الحقل AF_INET و AF_UNSPEC. تشير القيمة AF_UNSPEC إلى أن getaddrinfo() يجب أن تُعيد عناوين مقبس لأي عائلة عنوان (مثل IPv4 أو IPv6) يمكن استخدامها مع node و service.

ai_socktype
    يحدد هذا الحقل نوع المقبس المفضل، على سبيل المثال SOCK_STREAM أو SOCK_DGRAM. يشير تحديد 0 في هذا الحقل إلى أن عناوين مقبس من أي نوع يمكن أن تُعاد بواسطة getaddrinfo().

ai_protocol
    يحدد هذا الحقل البروتوكول لعناوين المقبس المُعادة. يشير تحديد 0 في هذا الحقل إلى أن عناوين مقبس مع أي بروتوكول يمكن أن تُعاد بواسطة getaddrinfo().

ai_flags
    يحدد هذا الحقل خيارات إضافية، موصوفة أدناه. تُحدد أعلام متعددة بإجراء عملية OR منطقية عليها معًا.

يجب أن تحتوي جميع الحقول الأخرى في البنية المشار إليها بواسطة hints إما على 0 أو مؤشر فارغ، حسب الاقتضاء.

تحديد hints كـ NULL يعادل ضبط ai_socktype و ai_protocol على 0؛ و ai_family على AF_UNSPEC؛ و ai_flags على (AI_V4MAPPED | AI_ADDRCONFIG). (يحدد POSIX قيمًا مبدئية مختلفة لـ ai_flags؛ انظر الملاحظات.) يحدد node إما عنوان شبكة رقمي (لـ IPv4، ترميز الأرقام والنقاط كما تدعمه inet_aton(3)؛ لـ IPv6، تنسيق سلسلة محارف سداسي عشري كما تدعمه inet_pton(3))، أو اسم مضيف شبكة، تُبحث عناوين شبكته وتُحل. إذا احتوى hints.ai_flags على العلم AI_NUMERICHOST، فيجب أن يكون node عنوان شبكة رقمي. يمنع العلم AI_NUMERICHOST أي عمليات بحث عن عنوان مضيف شبكة قد تكون طويلة.

إذا حُدد العلم AI_PASSIVE في hints.ai_flags، وكان node NULL، فإن عناوين المقبس المُعادة ستكون مناسبة لـ bind(2) مقبس سيقبل accept(2) اتصالات. سيحتوي عنوان المقبس المُعاد على "عنوان البدل" (INADDR_ANY لعناوين IPv4، IN6ADDR_ANY_INIT لعنوان IPv6). يُستخدم عنوان البدل بواسطة التطبيقات (عادة الخوادم) التي تنوي قبول اتصالات على أي من عناوين شبكة المضيف. إذا لم يكن node NULL، فإن العلم AI_PASSIVE يُتجاهل.

إذا لم يُضبط العلم AI_PASSIVE في hints.ai_flags، فإن عناوين المقابس المُعادة ستكون مناسبة للاستخدام مع connect(2) أو sendto(2) أو sendmsg(2). إذا كان node NULL، فسيُضبط عنوان الشبكة على عنوان واجهة الاسترجاع (INADDR_LOOPBACK لعناوين IPv4، IN6ADDR_LOOPBACK_INIT لعناوين IPv6)؛ يُستخدم هذا من قبل التطبيقات التي تنوي التواصل مع الأقران العاملين على نفس المضيف.

يُضبط service المنفذ في كل بنية عنوان مُعادة. إذا كانت هذه الوسيطة اسم خدمة (انظر services(5))، فتُترجم إلى رقم المنفذ المقابل. يمكن أيضًا تحديد هذه الوسيطة كرقم عشري، يُحوّل ببساطة إلى ثنائي. إذا كان service NULL، فسيُترك رقم منفذ عناوين المقابس المُعادة غير مهيأ. إذا حُدد AI_NUMERICSERV في hints.ai_flags وكان service ليس NULL، فيجب أن يشير service إلى سلسلة محارف تحتوي على رقم منفذ عددي. يُستخدم هذا العلم لمنع استدعاء خدمة تحليل الأسماء في الحالات التي يُعرف فيها أنها غير مطلوبة.

قد يكون إما node أو service، وليس كلاهما، NULL.

تخصص الدالة getaddrinfo() وتهيئ قائمة مرتبطة من بنى addrinfo، واحدة لكل عنوان شبكة يطابق node و service، مع مراعاة أي قيود يفرضها hints، وتُعيد مؤشرًا إلى بداية القائمة في res. تُربط العناصر في القائمة المرتبطة بواسطة الحقل ai_next.

هناك عدة أسباب قد تجعل القائمة المرتبطة تحتوي على أكثر من بنية addrinfo، منها: المضيف الشبكي متعدد الواجهات، يمكن الوصول إليه عبر بروتوكولات متعددة (مثل AF_INET و AF_INET6)؛ أو أن نفس الخدمة متاحة من أنواع مقابس متعددة (عنوان SOCK_STREAM وآخر SOCK_DGRAM مثلاً). عادةً، يجب على التطبيق محاولة استخدام العناوين بالترتيب الذي أُعيدت به. دالة الفرز المستخدمة داخل getaddrinfo() مُعرّفة في RFC 3484؛ يمكن تعديل الترتيب لنظام معين بتحرير /etc/gai.conf (متاح منذ glibc 2.5).

إذا تضمن hints.ai_flags العلم AI_CANONNAME، فسيُضبط الحقل ai_canonname لأول بنية addrinfo في القائمة المُعادة ليشير إلى الاسم الرسمي للمضيف.

تُهيأ الحقول المتبقية لكل بنية addrinfo مُعادة كما يلي:

•   تُعيد الحقول ai_family و ai_socktype و ai_protocol معاملات إنشاء المقبس (أي أن هذه الحقول لها نفس معنى الوسائط المقابلة لـ socket(2)). على سبيل المثال، قد يُعيد ai_family AF_INET أو AF_INET6؛ قد يُعيد ai_socktype SOCK_DGRAM أو SOCK_STREAM؛ ويُعيد ai_protocol البروتوكول للمقبس.

•   يُوضع مؤشر إلى عنوان المقبس في الحقل ai_addr، ويُوضع حجم عنوان المقبس، بالبايت، في الحقل ai_addrlen.

إذا تضمن hints.ai_flags العلم AI_ADDRCONFIG، فتُعاد عناوين IPv4 في القائمة المشار إليها بـ res فقط إذا كان النظام المحلي يحتوي على عنوان IPv4 واحد على الأقل مُهيأ، وتُعاد عناوين IPv6 فقط إذا كان النظام المحلي يحتوي على عنوان IPv6 واحد على الأقل مُهيأ. لا يُعتبر عنوان الاسترجاع صالحًا كعنوان مُهيأ في هذه الحالة. هذا العلم مفيد، على سبيل المثال، في الأنظمة التي تعمل بـ IPv4 فقط، لضمان أن getaddrinfo() لا تُعيد عناوين مقابس IPv6 التي ستفشل دائمًا في connect(2) أو bind(2).

إذا حدد hints.ai_flags العلم AI_V4MAPPED، وحُدد hints.ai_family كـ AF_INET6، ولم يُعثر على عناوين IPv6 مطابقة، فتُعاد عناوين IPv6 المعينة من IPv4 في القائمة المشار إليها بـ res. إذا حُدد كل من AI_V4MAPPED و AI_ALL في hints.ai_flags، فتُعاد عناوين IPv6 وعناوين IPv6 المعينة من IPv4 في القائمة المشار إليها بـ res. يُتجاهل AI_ALL إذا لم يُحدد AI_V4MAPPED أيضًا.

تحرر الدالة freeaddrinfo() الذاكرة التي خُصصت للقائمة المرتبطة المخصصة ديناميكيًا res.

  إضافات إلى getaddrinfo() لأسماء النطاقات المعولمة
بدءًا من glibc 2.3.4، وُسعت getaddrinfo() للسماح بشكل انتقائي بتحويل أسماء المضيفات الواردة والصادرة بشفافية من وإلى تنسيق اسم النطاق المعولم (IDN) (انظر RFC 3490، تعميم أسماء النطاقات في التطبيقات (IDNA)). عُرفت أربعة أعلام جديدة:

AI_IDN
    إذا حُدد هذا العلم، فسيُحوّل اسم العقدة المُعطى في node إلى تنسيق IDN إذا لزم الأمر. الترميز المصدر هو ترميز الإعدادات المحلية الحالية.

    إذا احتوى اسم الإدخال على محارف غير ASCII، فسيُستخدم ترميز IDN. تُرمّز أجزاء اسم العقدة (المحددة بالنقاط) التي تحتوي على محارف غير ASCII باستخدام الترميز المتوافق مع ASCII (ACE) قبل تمريرها إلى دوال تحليل الأسماء.

AI_CANONIDN
    بعد بحث ناجح عن الاسم، وإذا حُدد العلم AI_CANONNAME، فستُعيد getaddrinfo() الاسم القانوني للعقدة المقابلة لقيمة بنية addrinfo المُعادة. القيمة المُعادة هي نسخة طبق الأصل من القيمة التي أُعيدت بواسطة دالة تحليل الأسماء.

    إذا كان الاسم مُرمّزًا باستخدام ACE، فسيحتوي على البادئة xn-- لمكون أو أكثر من مكونات الاسم. لتحويل هذه المكونات إلى شكل قابل للقراءة، يمكن تمرير العلم AI_CANONIDN بالإضافة إلى AI_CANONNAME. السلسلة الناتجة مُرمّزة باستخدام ترميز الإعدادات المحلية الحالية.

AI_IDN_ALLOW_UNASSIGNED
AI_IDN_USE_STD3_ASCII_RULES
    سيؤدي ضبط هذه الأعلام إلى تفعيل علم IDNA_ALLOW_UNASSIGNED (السماح بنقاط كود يونيكود غير المعينة) وعلم IDNA_USE_STD3_ASCII_RULES (فحص المخرجات للتأكد من أنها اسم مضيف متوافق مع STD3) على التوالي، لاستخدامهما في معالجة IDNA.

قيمة الإرجاع
تُرجع الدالة getaddrinfo() القيمة 0 عند النجاح، أو أحد رموز الخطأ غير الصفرية التالية:

EAI_ADDRFAMILY
    المُضيف الشبكي المُحدد لا يمتلك أي عناوين شبكية في عائلة العناوين المطلوبة.

EAI_AGAIN
    أرجع خادم الأسماء إشارة فشل مؤقتة. حاول مجددًا لاحقًا.

EAI_BADFLAGS
    يحتوي hints.ai_flags على أعلام غير صالحة؛ أو تضمن hints.ai_flags العلم AI_CANONNAME وكان node فارغًا.

EAI_FAIL
    أرجع خادم الأسماء إشارة فشل دائمة.

EAI_FAMILY
    عائلة العناوين المطلوبة غير مدعومة.

EAI_MEMORY
    نفدت الذاكرة.

EAI_NODATA
    المُضيف الشبكي المُحدد موجود، لكنه لا يمتلك أي عناوين شبكية مُعرّفة.

EAI_NONAME
    node أو service غير معروف؛ أو كلاهما فارغ؛ أو تم تحديد AI_NUMERICSERV في hints.ai_flags ولم يكن service سلسلة محارف رقمية لرقم المنفذ.

EAI_SERVICE
    الخدمة المطلوبة غير متوفرة لنوع المقبس المطلوب. قد تكون متوفرة عبر نوع مقبس آخر. على سبيل المثال، قد يحدث هذا الخطأ إذا كانت service هي "shell" (خدمة متوفرة فقط على مقابس التدفق)، وكان hints.ai_protocol هو IPPROTO_UDP، أو كان hints.ai_socktype هو SOCK_DGRAM؛ أو قد يحدث الخطأ إذا لم تكن service فارغة، وكان hints.ai_socktype هو SOCK_RAW (نوع مقبس لا يدعم مفهوم الخدمات).

EAI_SOCKTYPE
    نوع المقبس المطلوب غير مدعوم. قد يحدث هذا، على سبيل المثال، إذا كان hints.ai_socktype و hints.ai_protocol غير متوافقين (مثل SOCK_DGRAM و IPPROTO_TCP على التوالي).

EAI_SYSTEM
    خطأ نظام آخر؛ يُضبط errno للإشارة إلى الخطأ.

تُترجم الدالة gai_strerror() رموز الخطأ هذه إلى سلسلة محارف قابلة للقراءة البشرية، مناسبة للإبلاغ عن الأخطاء.

الملفات
/etc/gai.conf

السمات
للاطلاع على شرح للمصطلحات المستخدمة في هذا القسم، انظر attributes(7).
الواجهة | السمة | القيمة
getaddrinfo() | سلامة الخيوط | بيئة محلية آمنة لتعدد الخيوط (MT-Safe)
freeaddrinfo(), gai_strerror() | سلامة الخيوط | MT-Safe

الإصدارات
وفقًا لـ POSIX.1، يجب أن يؤدي تحديد hints كـ NULL إلى افتراض ai_flags كـ 0. تفترض مكتبة GNU C بدلاً من ذلك قيمة (AI_V4MAPPED | AI_ADDRCONFIG) لهذه الحالة، حيث تُعتبر هذه القيمة تحسينًا على المواصفات.

المعايير
POSIX.1-2008.

getaddrinfo()
    RFC 2553.

التاريخ
POSIX.1-2001.

AI_ADDRCONFIG
AI_ALL
AI_V4MAPPED
    glibc 2.3.3.

AI_NUMERICSERV
    glibc 2.3.4.

ملاحظات
تدعم getaddrinfo() الترميز address%scope-id لتحديد نطاق IPv6 scope-ID.

أمثلة
توضح البرامج التالية استخدام getaddrinfo()، gai_strerror()، freeaddrinfo()، و getnameinfo(3). البرامج هي خادم وعميل صدى لبيانات UDP.

  برنامج الخادم
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <unistd.h>

    #define BUF_SIZE 500

    int
    main(int argc, char *argv[])
    {
        int                      sfd, s;
        char                     buf[BUF_SIZE];
        ssize_t                  nread;
        socklen_t                peer_addrlen;
        struct addrinfo          hints;
        struct addrinfo          *result, *rp;
        struct sockaddr_storage  peer_addr;

        if (argc != 2) {
            fprintf(stderr, "Usage: %s port\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
        hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
        hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
        hints.ai_protocol = 0;          /* Any protocol */
        hints.ai_canonname = NULL;
        hints.ai_addr = NULL;
        hints.ai_next = NULL;

        s = getaddrinfo(NULL, argv[1], &hints, &result);
        if (s != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
            exit(EXIT_FAILURE);
        }

        /* getaddrinfo() returns a list of address structures.
           Try each address until we successfully bind(2).
           If socket(2) (or bind(2)) fails, we (close the socket
           and) try the next address.  */

        for (rp = result; rp != NULL; rp = rp->ai_next) {
            sfd = socket(rp->ai_family, rp->ai_socktype,
                         rp->ai_protocol);
            if (sfd == -1)
                continue;

            if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
                break;                  /* Success */

            close(sfd);
        }

        freeaddrinfo(result);           /* No longer needed */

        if (rp == NULL) {               /* No address succeeded */
            fprintf(stderr, "Could not bind\n");
            exit(EXIT_FAILURE);
        }

        /* Read datagrams and echo them back to sender.  */

        for (;;) {
            char host[NI_MAXHOST], service[NI_MAXSERV];

            peer_addrlen = sizeof(peer_addr);
            nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                             (struct sockaddr *) &peer_addr, &peer_addrlen);
            if (nread == -1)
                continue;               /* Ignore failed request */

            s = getnameinfo((struct sockaddr *) &peer_addr,
                            peer_addrlen, host, NI_MAXHOST,
                            service, NI_MAXSERV, NI_NUMERICSERV);
            if (s == 0)
                printf("Received %zd bytes from %s:%s\n",
                       nread, host, service);
            else
                fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));

            if (sendto(sfd, buf, nread, 0, (struct sockaddr *) &peer_addr,
                       peer_addrlen) != nread)
            {
                fprintf(stderr, "Error sending response\n");
            }
        }
    }

  برنامج العميل
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <unistd.h>

    #define BUF_SIZE 500

    int
    main(int argc, char *argv[])
    {
        int              sfd, s;
        char             buf[BUF_SIZE];
        size_t           size;
        ssize_t          nread;
        struct addrinfo  hints;
        struct addrinfo  *result, *rp;

        if (argc < 3) {
            fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        /* Obtain address(es) matching host/port.  */

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
        hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
        hints.ai_flags = 0;
        hints.ai_protocol = 0;          /* Any protocol */

        s = getaddrinfo(argv[1], argv[2], &hints, &result);
        if (s != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
            exit(EXIT_FAILURE);
        }

        /* getaddrinfo() returns a list of address structures.
           Try each address until we successfully connect(2).
           If socket(2) (or connect(2)) fails, we (close the socket
           and) try the next address.  */

        for (rp = result; rp != NULL; rp = rp->ai_next) {
            sfd = socket(rp->ai_family, rp->ai_socktype,
                         rp->ai_protocol);
            if (sfd == -1)
                continue;

            if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
                break;                  /* Success */

            close(sfd);
        }

        freeaddrinfo(result);           /* No longer needed */

        if (rp == NULL) {               /* No address succeeded */
            fprintf(stderr, "Could not connect\n");
            exit(EXIT_FAILURE);
        }

        /* Send remaining command-line arguments as separate
           datagrams, and read responses from server.  */

        for (size_t j = 3; j < argc; j++) {
            size = strlen(argv[j]) + 1;
                    /* +1 for terminating null byte */

            if (size > BUF_SIZE) {
                fprintf(stderr,
                        "Ignoring long message in argument %zu\n", j);
                continue;
            }

            if (write(sfd, argv[j], size) != size) {
                fprintf(stderr, "partial/failed write\n");
                exit(EXIT_FAILURE);
            }

            nread = read(sfd, buf, BUF_SIZE);
            if (nread == -1) {
                perror("read");
                exit(EXIT_FAILURE);
            }

            printf("Received %zd bytes: %s\n", nread, buf);
        }

        exit(EXIT_SUCCESS);
    }

انظر أيضًا
getaddrinfo_a(3), gethostbyname(3), getnameinfo(3), inet(3), gai.conf(5), hostname(7), ip(7)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.