מידע רשת

getservbyport#

לחפש שירות רשת לפי הפורט המספרי והפרוטוקול שלו.

getservbyport מתייעץ עם מסד נתוני השירותים של המערכת (/etc/services ב־Linux, בתוספת מקורות NSS שמוגדרים) ומחזיר את הרשומה עבור השירות שמאזין על PORT תחת הפרוטוקול PROTO. PORT הוא מספר שלם בסדר בתים של רשת - הערך שהיית מעביר ל־bind או מקבל מ־sockaddr_in, לא המספר העשרוני שמוצג ב־/etc/services. PROTO הוא מחרוזת באותיות קטנות כמו "tcp" או "udp". הקריאה עוטפת את getservbyport(3) של ספריית C.

תקציר#

getservbyport PORT, PROTO          # list context: full record
scalar getservbyport PORT, PROTO   # scalar context: service name

מה מקבלים בחזרה#

בהקשר רשימה, רשימה בת חמישה אלמנטים שמתארת את השירות:

my ($name, $aliases, $port, $proto) = getservbyport($port_n, "tcp");

אינדקס

שם

משמעות

0

$name

שם שירות קנוני, לדוגמה "http"

1

$aliases

רשימת aliases מופרדת ברווחים, לדוגמה "www www-http"

2

$port

מספר פורט בסדר בתים של רשת

3

$proto

שם פרוטוקול, לדוגמה "tcp"

אם הרשומה אינה קיימת הרשימה ריקה, כך שהשמה בהקשר רשימה משאירה כל מטרה undef. קריאה חשופה בהקשר רשימה מחזירה ערך אמת יחיד חסר משמעות בהחמצה - תמיד יש להשים לרשימה ולבדוק את האלמנט הראשון, או לקרוא בהקשר סקלרי.

בהקשר סקלרי, מקבלים את שם השירות הקנוני (אותו $name כמו לעיל), או undef אם אין שירות רשום באותו פורט עבור אותו פרוטוקול:

my $name = getservbyport($port_n, "tcp")
    or warn "port $port not registered for tcp";

מצב גלובלי שהוא נוגע בו#

  • $! אינו מוגדר באופן אמין בהחמצה - לפונקציה getservbyport(3) של C אין חוזה errno סטנדרטי עבור ״לא נמצא״. יש להתייחס להחזרה של רשימה ריקה או undef כאות הכישלון האמין היחיד.

  • מצב איטרציה שמוחזק על־ידי מסד נתוני השירותים (משותף עם getservent, setservent, ו־endservent) הוא רוחב־תהליך ולא בטוח ל־threads.

מלכודת סדר בתים#

PORT חייב להיות בסדר בתים של רשת. עמודת הפורט ב־/etc/services היא עשרוני אנושי; הערך שהקרנל וספריית C משתמשים בו הוא קידוד big-endian בן 16 ביט. יש להמיר עם pack / unpack או עם עוזרי Socket:

use Socket;

# Host decimal 80 -> network-order short:
my $port_n = pack("n", 80);            # two bytes, big-endian
my @rec    = getservbyport($port_n, "tcp");

# Or going the other way, a port extracted from sockaddr_in is
# already network order:
my ($port_n_from_peer, $iaddr) = sockaddr_in(getpeername($sock));
my $name = getservbyport($port_n_from_peer, "tcp");

העברת מספר שלם בסדר מארח כמו 80 עובדת על חומרת little-endian רק במקרה עבור פורטים נמוכים שהקידוד שלהם במקרה אינו אפס בבית הנמוך - רוב הפורטים פשוט יחמיצו. זוהי הסיבה הנפוצה ביותר היחידה לכך שקריאה ל־getservbyport ״מחזירה כלום בשקט״.

דוגמאות#

לזהות את השירות בפורט 443 על TCP:

use Socket;
my $port_n = pack("n", 443);
my ($name) = getservbyport($port_n, "tcp");
print $name, "\n";                      # https

לשייך עם getpeername כדי לנקוב בשם הפורט של חיבור נכנס:

use Socket;
my ($peer_port_n, $peer_addr) = sockaddr_in(getpeername($client));
my $service = getservbyport($peer_port_n, "tcp") // "unknown";
print "client connected from service $service\n";

פליטת רשומה מלאה - שימושית כאשר רוצים את הפרוטוקול או ה־aliases:

use Socket;
my @rec = getservbyport(pack("n", 53), "udp");
if (@rec) {
    my ($name, $aliases, $port_n, $proto) = @rec;
    printf "%s (%s) on %d/%s\n",
        $name, $aliases, unpack("n", pack("n", $port_n)), $proto;
}

הקשר סקלרי, סגנון פסוקית שמירה:

my $svc = getservbyport(pack("n", $port), "tcp")
    or die "no TCP service registered on port $port";

לעבור על כל שירות רשום (מסד הנתונים הבסיסי משותף עם getservent):

while (my ($n, $a, $p, $proto) = getservent()) {
    next unless $proto eq "tcp";
    # …do something with $n / $p…
}
endservent();

מקרי קצה#

  • רשומה חסרה: רשימה ריקה בהקשר רשימה, undef בהקשר סקלרי. if (getservbyport(...)) חשוף בהקשר רשימה הוא אמת גם עבור רשומה חסרה בגלל ההחזרה הישנה ״ערך אמת יחיד חסר משמעות״ - תמיד יש להשים לרשימה או לאלץ הקשר סקלרי.

  • פרוטוקול לא־מוכר: מחרוזת PROTO לא־מזוהה (לדוגמה "sctp" על מארח שמסד הנתונים שלו אינו כולל אותו) מטופלת על־ידי ספריית C כ־no-match. הקריאה מחזירה empty/undef, לא שגיאה.

  • אותיות גדולות/קטנות ב־PROTO: רגיש לאותיות ברוב מימושי libc של Linux. יש להשתמש באותיות קטנות "tcp" / "udp"; "TCP" יחמיץ.

  • פורט בסדר מארח: העברת 80 במקום pack("n", 80) היא הבאג הקלאסי - ראו מלכודת סדר בתים לעיל.

  • PORT מספרי מול מחרוזת: Perl ממיר את PORT למספר לפני העברה ל־libc. מחרוזת ארוזה בת שני בתים עובדת מכיוון ש־pack עם "n" מייצר בתים שמומרים למספר באופן דטרמיניסטי בפלטפורמות הנתמכות; בכל ספק, יש לבצע unpack("n", $packed) קודם ולהעביר את המספר השלם כדי לשמור על כוונה מפורשת.

  • קדימות עם אופרטורי רשימה: כמו אופרטורים אחרים נקובי־שם אונאריים / רשימה, getservbyport נקשר באופן רופף. יש לעטוף בסוגריים כאשר התוצאה מזינה אופרטור אחר:

    getservbyport $port_n, "tcp" eq "https"     # WRONG
    (getservbyport($port_n, "tcp")) eq "https"  # correct
    
  • Threads: החיפוש הבסיסי משתף מצב עם getservent / setservent / endservent. שני threads שעוברים במקביל ישלבו רשומות. יש להשתמש בעטיפות thread־local כאשר זה חשוב.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • getservbyname - חיפוש הפוך, שם שירות לפורט; יש לפנות אליו כאשר ידוע השם ורוצים את המספר

  • getservent - לעבור על כל רשומת שירות; יש להשתמש כאשר נדרשת סריקה מלאה במקום חיפוש יחיד

  • setservent - להחזיר את האיטרטור של השירותים, דגל STAYOPEN אופציונלי להשארת הקובץ פתוח לרוחב fork

  • endservent - לסגור את מסד נתוני השירותים לאחר איטרציה

  • getprotobynumber - חיפוש מלווה מצד הפרוטוקול; משמש לעיתים קרובות מיד לאחר getservbyport כדי לתרגם את שדה $proto

  • pack - לבנות את ערך הפורט בסדר רשת ש־getservbyport מצפה לו כארגומנט הראשון

  • Socket - קבועים ועוזרי sockaddr_in שמייצרים פורטים בסדר רשת ישירות ממבני קרנל