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 |
| שם שירות קנוני, לדוגמה |
1 |
| רשימת aliases מופרדת ברווחים, לדוגמה |
2 |
| מספר פורט בסדר בתים של רשת |
3 |
| שם פרוטוקול, לדוגמה |
אם הרשומה אינה קיימת הרשימה ריקה, כך שהשמה בהקשר רשימה משאירה כל מטרה 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אופציונלי להשארת הקובץ פתוח לרוחבforkendservent- לסגור את מסד נתוני השירותים לאחר איטרציהgetprotobynumber- חיפוש מלווה מצד הפרוטוקול; משמש לעיתים קרובות מיד לאחרgetservbyportכדי לתרגם את שדה$protopack- לבנות את ערך הפורט בסדר רשת ש־getservbyportמצפה לו כארגומנט הראשוןSocket- קבועים ועוזריsockaddr_inשמייצרים פורטים בסדר רשת ישירות ממבני קרנל