diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2026-02-25 01:27:12 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2026-02-25 02:35:32 +0100 |
| commit | ad88be9e7ea4ae74b5b012d75e5b92ff12ca273d (patch) | |
| tree | faea314f187a523bedab77eaee69197a3b172574 | |
| parent | c920ecd73df764ade3d02444d24222d6244dc826 (diff) | |
| download | busybox-w32-ad88be9e7ea4ae74b5b012d75e5b92ff12ca273d.tar.gz busybox-w32-ad88be9e7ea4ae74b5b012d75e5b92ff12ca273d.tar.bz2 busybox-w32-ad88be9e7ea4ae74b5b012d75e5b92ff12ca273d.zip | |
*: don't use getservbyname, it links in a static buffer
function old new delta
bb_get_servport_by_name - 348 +348
bb_lookup_port 83 111 +28
reread_config_file 886 907 +21
static.se 16 - -16
getservbyname 53 - -53
getservbyname_r 284 - -284
------------------------------------------------------------------------------
(add/remove: 2/5 grow/shrink: 2/0 up/down: 397/-353) Total: 44 bytes
text data bss dec hex filename
1080084 555 5024 1085663 1090df busybox_old
1080144 555 4992 1085691 1090fb busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | include/libbb.h | 4 | ||||
| -rw-r--r-- | libbb/bb_get_servname_by_port.c | 6 | ||||
| -rw-r--r-- | libbb/bb_get_servport_by_name.c | 129 | ||||
| -rw-r--r-- | libbb/xconnect.c | 21 | ||||
| -rw-r--r-- | networking/inetd.c | 22 |
5 files changed, 145 insertions, 37 deletions
diff --git a/include/libbb.h b/include/libbb.h index e282066b8..0fee62929 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
| @@ -911,9 +911,11 @@ char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) FAST_FUNC RETU | |||
| 911 | /* inet_[ap]ton on steroids */ | 911 | /* inet_[ap]ton on steroids */ |
| 912 | char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; | 912 | char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; |
| 913 | char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; | 913 | char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; |
| 914 | // NB: unlike getservbyport, port parameter is NOT in network order | 914 | // NB: unlike getservbyport, port parameter/retval are in host, NOT network order! |
| 915 | #define getservbyport dont_use_getservbyport_uses_global_buffer | 915 | #define getservbyport dont_use_getservbyport_uses_global_buffer |
| 916 | char* bb_get_servname_by_port(char **p_etc_services, int port, const char *type) FAST_FUNC RETURNS_MALLOC; | 916 | char* bb_get_servname_by_port(char **p_etc_services, int port, const char *type) FAST_FUNC RETURNS_MALLOC; |
| 917 | #define getservbyname dont_use_getservbyname_uses_global_buffer | ||
| 918 | int bb_get_servport_by_name(char **p_etc_services, const char *name, const char *type) FAST_FUNC; | ||
| 917 | // "old" (ipv4 only) API | 919 | // "old" (ipv4 only) API |
| 918 | // users: traceroute.c hostname.c - use _list_ of all IPs | 920 | // users: traceroute.c hostname.c - use _list_ of all IPs |
| 919 | struct hostent *xgethostbyname(const char *name) FAST_FUNC; | 921 | struct hostent *xgethostbyname(const char *name) FAST_FUNC; |
diff --git a/libbb/bb_get_servname_by_port.c b/libbb/bb_get_servname_by_port.c index 3846072f9..970a718fe 100644 --- a/libbb/bb_get_servname_by_port.c +++ b/libbb/bb_get_servname_by_port.c | |||
| @@ -9,11 +9,9 @@ | |||
| 9 | //kbuild:lib-$(CONFIG_PSCAN) += bb_get_servname_by_port.o | 9 | //kbuild:lib-$(CONFIG_PSCAN) += bb_get_servname_by_port.o |
| 10 | #include "libbb.h" | 10 | #include "libbb.h" |
| 11 | 11 | ||
| 12 | //Rationale for exising: | 12 | //Rationale for existing: |
| 13 | //#define getservbyport dont_use_getservbyport_uses_global_buffer | 13 | //#define getservbyport dont_use_getservbyport_uses_global_buffer |
| 14 | //(for example: -280 bytes on musl, x86-32) | 14 | //(for example: -280 bytes on musl, x86-32) |
| 15 | //TODO: | ||
| 16 | //avoid getservbyname() as well | ||
| 17 | 15 | ||
| 18 | char* FAST_FUNC bb_get_servname_by_port(char **p_etc_services, int port, const char *type) | 16 | char* FAST_FUNC bb_get_servname_by_port(char **p_etc_services, int port, const char *type) |
| 19 | { | 17 | { |
| @@ -45,7 +43,7 @@ char* FAST_FUNC bb_get_servname_by_port(char **p_etc_services, int port, const c | |||
| 45 | end = is_prefixed_with(end, type); | 43 | end = is_prefixed_with(end, type); |
| 46 | if (!end | 44 | if (!end |
| 47 | || !(isspace(*end) || *end == '\0' | 45 | || !(isspace(*end) || *end == '\0' |
| 48 | || *end == '#') // glibc treats "http 80/tcp#COMMENT" as valid | 46 | || *end == '#') // glibc treats "http 80/tcp#COMMENT" (no space!) as valid |
| 49 | ) { | 47 | ) { |
| 50 | goto next; | 48 | goto next; |
| 51 | } | 49 | } |
diff --git a/libbb/bb_get_servport_by_name.c b/libbb/bb_get_servport_by_name.c new file mode 100644 index 000000000..96580c900 --- /dev/null +++ b/libbb/bb_get_servport_by_name.c | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /* | ||
| 2 | * Utility routines. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2026 Denys Vlasenko | ||
| 5 | * | ||
| 6 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
| 7 | */ | ||
| 8 | //kbuild:lib-y += bb_get_servport_by_name.o | ||
| 9 | //kbuild:lib-y += bb_get_servport_by_name.o | ||
| 10 | #include "libbb.h" | ||
| 11 | |||
| 12 | //Rationale for existing: | ||
| 13 | //#define getservbyname dont_use_getservbyname_uses_global_buffer | ||
| 14 | //(for example: -32 BSS bytes on musl, x86-32) | ||
| 15 | |||
| 16 | int FAST_FUNC bb_get_servport_by_name(char **p_etc_services, const char *name, const char *proto) | ||
| 17 | { | ||
| 18 | unsigned namelen; | ||
| 19 | char *sp; | ||
| 20 | |||
| 21 | if (!name[0]) // This can hang the search (strstr advances by 0 bytes) | ||
| 22 | return -1; // bad service name form: "" | ||
| 23 | // Any other bogosity to reject? | ||
| 24 | // Or just enforce isalnum_or_dash_or_slash(name[i])? | ||
| 25 | // Yes, service name like "cl/1" is allowed and _exists_. | ||
| 26 | // | ||
| 27 | // Protect against finding service "www#c" in "http 10/tcp www#c" line | ||
| 28 | //if (strchr(name, '#')) | ||
| 29 | // return -1; // bad service name form | ||
| 30 | //^^^^ the main code already catches this possibility. | ||
| 31 | // | ||
| 32 | // Current code allows e.g. service names like "http 80/tcp" | ||
| 33 | // to map to port 80. Probably harmless? | ||
| 34 | //if (skip_non_whitespace(name)[0]) | ||
| 35 | // return -1; // bad service name form (has whitespace) | ||
| 36 | |||
| 37 | sp = *p_etc_services; | ||
| 38 | if (!sp) { | ||
| 39 | //TODO: we need mmap_entire_file() for this use case! | ||
| 40 | sp = xmalloc_open_read_close("/etc/services", NULL); | ||
| 41 | if (!sp) | ||
| 42 | return -1; | ||
| 43 | *p_etc_services = sp; | ||
| 44 | } | ||
| 45 | namelen = strlen(name); | ||
| 46 | while (*sp) { | ||
| 47 | const char *pnstr; | ||
| 48 | char *start, *end; | ||
| 49 | unsigned n; | ||
| 50 | |||
| 51 | // First, find a possible service name without regard for line separators (!) | ||
| 52 | start = strstr(sp, name); | ||
| 53 | if (!start) | ||
| 54 | return -1; | ||
| 55 | sp = start + namelen; | ||
| 56 | if (start != *p_etc_services && !isspace(start[-1])) { | ||
| 57 | // There is a char before it, and it's not whitespace | ||
| 58 | continue; | ||
| 59 | } | ||
| 60 | if (!(isspace(*sp) || *sp == '\0' || *sp == '#')) | ||
| 61 | // After it: not whitespace/EOF/comment | ||
| 62 | continue; | ||
| 63 | // The found substring _is_ correctly delimited before and after | ||
| 64 | |||
| 65 | // Find beginning of the line we are on | ||
| 66 | while (start != *p_etc_services && start[-1] != '\n') | ||
| 67 | start--; | ||
| 68 | start = skip_whitespace(start); | ||
| 69 | // Is there a comment char between start of line and "service name"? | ||
| 70 | if (memchr(start, '#', sp - start)) { | ||
| 71 | // The "service name" we found is actually in comment / has comment in it. | ||
| 72 | // Also rejects names with #: | ||
| 73 | // service "www#c" won't be found even on "http 80/tcp www#c" line | ||
| 74 | continue; | ||
| 75 | } | ||
| 76 | // Is the [start...sp) of the form "SERVNAME NUM/PROTO[ ALIAS[ ALIAS...][ ]]"? | ||
| 77 | pnstr = skip_whitespace(skip_non_whitespace(start)); // go to NUM... | ||
| 78 | |||
| 79 | // I've seen this line in /etc/services of a real-world machine: | ||
| 80 | //914c/g 211/tcp 914c-g | ||
| 81 | // Now consider just a bit more pathological example: | ||
| 82 | //914c/tcp 914/tcp 914/tcp | ||
| 83 | // If we search for service name "914/tcp", | ||
| 84 | // we'll find it at second word first, yet we must not reject this line | ||
| 85 | // because the THIRD word also matches, and it's a valid match (tested on glibc!). | ||
| 86 | // But in this case we must not match: | ||
| 87 | //914c/tcp 914/tcp something-else | ||
| 88 | if (sp - namelen == pnstr) | ||
| 89 | continue; // the match is at NUM..., try matching further | ||
| 90 | |||
| 91 | n = bb_strtou(pnstr, &end, 10); | ||
| 92 | if (n > 0xffff || *end != '/') | ||
| 93 | continue; // NUM... part is not a valid port#, or has no slash | ||
| 94 | |||
| 95 | if (proto) { | ||
| 96 | end++; // points to PROTO | ||
| 97 | end = is_prefixed_with(end, proto); | ||
| 98 | if (!end | ||
| 99 | || !(isspace(*end) || *end == '\0' | ||
| 100 | || *end == '#') // glibc treats "http 80/tcp#COMMENT" (no space!) as valid | ||
| 101 | ) { | ||
| 102 | continue; // PROTO does not match | ||
| 103 | } | ||
| 104 | } | ||
| 105 | // By now, either WORD or one of the ALIASes has to be the part | ||
| 106 | // which was found by strstr()! | ||
| 107 | return n; | ||
| 108 | } | ||
| 109 | return -1; | ||
| 110 | } | ||
| 111 | |||
| 112 | // Return port number for a service. | ||
| 113 | // If "port" is a number use it as the port. | ||
| 114 | // If "port" is a name it is looked up in /etc/services. | ||
| 115 | // if NULL, return default_port | ||
| 116 | unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned port_nr) | ||
| 117 | { | ||
| 118 | if (port) { | ||
| 119 | port_nr = bb_strtou(port, NULL, 10); | ||
| 120 | if (errno || port_nr > 65535) { | ||
| 121 | char *p_etc_services = NULL; | ||
| 122 | port_nr = bb_get_servport_by_name(&p_etc_services, port, protocol); | ||
| 123 | if (port_nr > 0xffff) // -1? | ||
| 124 | bb_error_msg_and_die("bad port '%s'", port); | ||
| 125 | free(p_etc_services); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | return (uint16_t)port_nr; | ||
| 129 | } | ||
diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 0e0b247b8..095f7ff47 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c | |||
| @@ -113,29 +113,8 @@ void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) | |||
| 113 | } | 113 | } |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | /* Return port number for a service. | ||
| 117 | * If "port" is a number use it as the port. | ||
| 118 | * If "port" is a name it is looked up in /etc/services. | ||
| 119 | * if NULL, return default_port | ||
| 120 | */ | ||
| 121 | unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned port_nr) | ||
| 122 | { | ||
| 123 | if (port) { | ||
| 124 | port_nr = bb_strtou(port, NULL, 10); | ||
| 125 | if (errno || port_nr > 65535) { | ||
| 126 | struct servent *tserv = getservbyname(port, protocol); | ||
| 127 | if (!tserv) | ||
| 128 | bb_error_msg_and_die("bad port '%s'", port); | ||
| 129 | port_nr = ntohs(tserv->s_port); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | return (uint16_t)port_nr; | ||
| 133 | } | ||
| 134 | |||
| 135 | |||
| 136 | /* "New" networking API */ | 116 | /* "New" networking API */ |
| 137 | 117 | ||
| 138 | |||
| 139 | int FAST_FUNC get_nport(const struct sockaddr *sa) | 118 | int FAST_FUNC get_nport(const struct sockaddr *sa) |
| 140 | { | 119 | { |
| 141 | #if ENABLE_FEATURE_IPV6 | 120 | #if ENABLE_FEATURE_IPV6 |
diff --git a/networking/inetd.c b/networking/inetd.c index e63edcd9d..6220a08e3 100644 --- a/networking/inetd.c +++ b/networking/inetd.c | |||
| @@ -969,9 +969,8 @@ static void reread_config_file(int sig UNUSED_PARAM) | |||
| 969 | servtab_t *sep, *cp, **sepp; | 969 | servtab_t *sep, *cp, **sepp; |
| 970 | len_and_sockaddr *lsa; | 970 | len_and_sockaddr *lsa; |
| 971 | sigset_t omask; | 971 | sigset_t omask; |
| 972 | unsigned n; | ||
| 973 | uint16_t port; | ||
| 974 | int save_errno = errno; | 972 | int save_errno = errno; |
| 973 | char *p_etc_services = NULL; | ||
| 975 | 974 | ||
| 976 | if (!reopen_config_file()) | 975 | if (!reopen_config_file()) |
| 977 | goto ret; | 976 | goto ret; |
| @@ -1039,7 +1038,9 @@ static void reread_config_file(int sig UNUSED_PARAM) | |||
| 1039 | break; | 1038 | break; |
| 1040 | 1039 | ||
| 1041 | default: /* case AF_INET, case AF_INET6 */ | 1040 | default: /* case AF_INET, case AF_INET6 */ |
| 1042 | n = bb_strtou(sep->se_service, NULL, 10); | 1041 | { |
| 1042 | unsigned portno; | ||
| 1043 | portno = bb_strtou(sep->se_service, NULL, 10); | ||
| 1043 | #if ENABLE_FEATURE_INETD_RPC | 1044 | #if ENABLE_FEATURE_INETD_RPC |
| 1044 | if (is_rpc_service(sep)) { | 1045 | if (is_rpc_service(sep)) { |
| 1045 | sep->se_rpcprog = n; | 1046 | sep->se_rpcprog = n; |
| @@ -1059,26 +1060,23 @@ static void reread_config_file(int sig UNUSED_PARAM) | |||
| 1059 | } | 1060 | } |
| 1060 | #endif | 1061 | #endif |
| 1061 | /* what port to listen on? */ | 1062 | /* what port to listen on? */ |
| 1062 | port = htons(n); | 1063 | if (errno || portno > 0xffff) { /* se_service is not numeric */ |
| 1063 | if (errno || n > 0xffff) { /* se_service is not numeric */ | ||
| 1064 | char protoname[4]; | 1064 | char protoname[4]; |
| 1065 | struct servent *sp; | ||
| 1066 | /* can result only in "tcp" or "udp": */ | 1065 | /* can result only in "tcp" or "udp": */ |
| 1067 | safe_strncpy(protoname, sep->se_proto, 4); | 1066 | safe_strncpy(protoname, sep->se_proto, 4); |
| 1068 | sp = getservbyname(sep->se_service, protoname); | 1067 | portno = bb_get_servport_by_name(&p_etc_services, sep->se_service, protoname); |
| 1069 | if (sp == NULL) { | 1068 | if (portno > 0xffff) { |
| 1070 | bb_error_msg("%s/%s: unknown service", | 1069 | bb_error_msg("%s/%s: unknown service", |
| 1071 | sep->se_service, sep->se_proto); | 1070 | sep->se_service, sep->se_proto); |
| 1072 | goto next_cp; | 1071 | goto next_cp; |
| 1073 | } | 1072 | } |
| 1074 | port = sp->s_port; | ||
| 1075 | } | 1073 | } |
| 1076 | if (LONE_CHAR(sep->se_local_hostname, '*')) { | 1074 | if (LONE_CHAR(sep->se_local_hostname, '*')) { |
| 1077 | lsa = xzalloc_lsa(sep->se_family); | 1075 | lsa = xzalloc_lsa(sep->se_family); |
| 1078 | set_nport(&lsa->u.sa, port); | 1076 | set_nport(&lsa->u.sa, htons(portno)); |
| 1079 | } else { | 1077 | } else { |
| 1080 | lsa = host_and_af2sockaddr(sep->se_local_hostname, | 1078 | lsa = host_and_af2sockaddr(sep->se_local_hostname, |
| 1081 | ntohs(port), sep->se_family); | 1079 | portno, sep->se_family); |
| 1082 | if (!lsa) { | 1080 | if (!lsa) { |
| 1083 | bb_error_msg("%s/%s: unknown host '%s'", | 1081 | bb_error_msg("%s/%s: unknown host '%s'", |
| 1084 | sep->se_service, sep->se_proto, | 1082 | sep->se_service, sep->se_proto, |
| @@ -1087,6 +1085,7 @@ static void reread_config_file(int sig UNUSED_PARAM) | |||
| 1087 | } | 1085 | } |
| 1088 | } | 1086 | } |
| 1089 | break; | 1087 | break; |
| 1088 | }//default: | ||
| 1090 | } /* end of "switch (sep->se_family)" */ | 1089 | } /* end of "switch (sep->se_family)" */ |
| 1091 | 1090 | ||
| 1092 | /* did lsa change? Then close/open */ | 1091 | /* did lsa change? Then close/open */ |
| @@ -1134,6 +1133,7 @@ static void reread_config_file(int sig UNUSED_PARAM) | |||
| 1134 | } | 1133 | } |
| 1135 | restore_sigmask(&omask); | 1134 | restore_sigmask(&omask); |
| 1136 | ret: | 1135 | ret: |
| 1136 | free(p_etc_services); | ||
| 1137 | errno = save_errno; | 1137 | errno = save_errno; |
| 1138 | } | 1138 | } |
| 1139 | 1139 | ||
