aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-02-25 01:27:12 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-02-25 02:35:32 +0100
commitad88be9e7ea4ae74b5b012d75e5b92ff12ca273d (patch)
treefaea314f187a523bedab77eaee69197a3b172574
parentc920ecd73df764ade3d02444d24222d6244dc826 (diff)
downloadbusybox-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.h4
-rw-r--r--libbb/bb_get_servname_by_port.c6
-rw-r--r--libbb/bb_get_servport_by_name.c129
-rw-r--r--libbb/xconnect.c21
-rw-r--r--networking/inetd.c22
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 */
912char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; 912char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
913char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; 913char* 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
916char* bb_get_servname_by_port(char **p_etc_services, int port, const char *type) FAST_FUNC RETURNS_MALLOC; 916char* 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
918int 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
919struct hostent *xgethostbyname(const char *name) FAST_FUNC; 921struct 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
18char* FAST_FUNC bb_get_servname_by_port(char **p_etc_services, int port, const char *type) 16char* 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
16int 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
116unsigned 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 */
121unsigned 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
139int FAST_FUNC get_nport(const struct sockaddr *sa) 118int 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