aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-10-19 17:07:41 +0100
committerRon Yorston <rmy@pobox.com>2016-10-19 17:07:41 +0100
commit03aa1e26b8885cd0ae28b676bed0e646e93433fb (patch)
tree6ea4dd81cc9fb84c7da3a42731363144ee7ff0c3
parent2e04fb569646e1882496f7175a1f5da6f33e36e1 (diff)
parent6bbb48fadf2265c7ed806027781da8039e561865 (diff)
downloadbusybox-w32-03aa1e26b8885cd0ae28b676bed0e646e93433fb.tar.gz
busybox-w32-03aa1e26b8885cd0ae28b676bed0e646e93433fb.tar.bz2
busybox-w32-03aa1e26b8885cd0ae28b676bed0e646e93433fb.zip
Merge branch 'busybox' into merge
-rw-r--r--archival/libarchive/init_handle.c4
-rw-r--r--examples/var_service/README218
-rw-r--r--networking/ifupdown.c43
-rw-r--r--networking/tcpudp.c69
-rw-r--r--networking/telnet.c7
-rw-r--r--networking/telnetd.IAC_test.sh87
-rw-r--r--networking/telnetd.c289
-rw-r--r--util-linux/more.c11
8 files changed, 564 insertions, 164 deletions
diff --git a/archival/libarchive/init_handle.c b/archival/libarchive/init_handle.c
index cbae06ac3..dcba6666f 100644
--- a/archival/libarchive/init_handle.c
+++ b/archival/libarchive/init_handle.c
@@ -17,6 +17,10 @@ archive_handle_t* FAST_FUNC init_handle(void)
17 archive_handle->action_data = data_skip; 17 archive_handle->action_data = data_skip;
18 archive_handle->filter = filter_accept_all; 18 archive_handle->filter = filter_accept_all;
19 archive_handle->seek = seek_by_jump; 19 archive_handle->seek = seek_by_jump;
20#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
21 archive_handle->cpio__owner.uid = (uid_t)-1L;
22 archive_handle->cpio__owner.gid = (gid_t)-1L;
23#endif
20 24
21 return archive_handle; 25 return archive_handle;
22} 26}
diff --git a/examples/var_service/README b/examples/var_service/README
index d096ad0b9..938cce91d 100644
--- a/examples/var_service/README
+++ b/examples/var_service/README
@@ -1,54 +1,158 @@
1In many cases, network configuration makes it necessary to run several daemons: 1 Daemontools and runit
2dhcp, zeroconf, ppp, openvpn and such. They need to be controlled, 2
3and in many cases you also want to babysit them. runsvdir is a good tool for this. 3Tired of PID files, needing root access, and writing init scripts just
4examples/var_service directory provides a few examples. It is meant to be used 4to have your UNIX apps start when your server boots? Want a simpler,
5this way: copy it somewhere (say, /var/service) and run something like 5better alternative that will also restart them if they crash? If so,
6this is an introduction to process supervision with runit/daemontools.
7
8
9 Background
10
11Classic init scripts, e.g. /etc/init.d/apache, are widely used for
12starting processes at system boot time, when they are executed by init.
13Sadly, init scripts are cumbersome and error-prone to write, they must
14typically be edited and run as root, and the processes they launch do
15not get restarted automatically if they crash.
16
17In an alternative scheme called "process supervision", each important
18process is looked after by a tiny supervising process, which deals with
19starting and stopping the important process on request, and re-starting
20it when it exits unexpectedly. Those supervising processes can in turn
21be supervised by other supervising processes.
22
23Dan Bernstein wrote the process supervision toolkit, "daemontools",
24which is a set of small, reliable programs that cooperate in the
25UNIX tradition to manage process supervision trees.
26
27Runit is a more conveniently licensed and more actively maintained
28reimplementation of daemontools, written by Gerrit Pape.
29
30Here I’ll use runit, however, the ideas are the same for other
31daemontools-like projects (there are several).
32
33
34 Service directories and scripts
35
36In runit parlance a "service" is simply a directory containing a script
37named "run".
6 38
7env - PATH=... <other vars=...> runsvdir /var/service & 39There are just two key programs in runit. Firstly, runsv supervises the
40process for an individual service. Service directories themselves sit
41inside a containing directory, and the runsvdir program supervises that
42directory, running one child runsv process for the service in each
43subdirectory. A typical choice is to start an instance of runsvdir
44which supervises services in subdirectories of /var/service/.
8 45
9from one of system startup scripts. (Google "man runsvdir" and "man runsv" 46If /var/service/log/ exists, runsv will supervise two services,
10for more info about these tools). 47and will connect stdout of main service to the stdin of log service.
48This is primarily used for logging.
11 49
12You can try or debug an individual service by running its SERVICE_DIR/run script. 50You can debug an individual service by running its SERVICE_DIR/run script.
13In this case, its stdout and stderr go to your terminal. 51In this case, its stdout and stderr go to your terminal.
14 52
15You can also run "runsv SERVICE_DIR", which runs both the service 53You can also run "runsv SERVICE_DIR", which runs both the service
16and its logger service (SERVICE_DIR/log/run) if logger service exists. 54and its logger service (SERVICE_DIR/log/run) if logger service exists.
17If logger service exists, the output will go to it instead of the terminal. 55If logger service exists, the output will go to it instead of the terminal.
18 56
19"runsvdir DIR" merely runs "runsv SERVICE_DIR" for every subdirectory in DIR. 57"runsvdir /var/service" merely runs "runsv SERVICE_DIR" for every subdirectory
58in /var/service.
59
60
61 Examples
62
63This directory contains some examples of services:
64
65 var_service/getty_<tty>
66
67Runs a getty on <tty>. (run script looks at $PWD and extracts suffix
68after "_" as tty name). Create copies (or symlinks) of this directory
69with different names to run many gettys on many ttys.
70
71 var_service/gpm
72
73Runs gpm, the cut and paste utility and mouse server for text consoles.
74
75 var_service/inetd
76
77Runs inetd. This is an example of a service with log. Log service
78writes timestamped, rotated log data to /var/log/service/inetd/*
79using "svlogd -tt". p_log and w_log scripts demonstrage how you can
80"page log" and "watch log".
81
82Other services which have logs handle them in the same way.
83
84 var_service/nmeter
85
86Runs nmeter '%t %c ....' with output to /dev/tty9. This gives you
87a 1-second sampling of server load and health on a dedicated text console.
88
20 89
21Some existing examples: 90 Networking examples
91
92In many cases, network configuration makes it necessary to run several daemons:
93dhcp, zeroconf, ppp, openvpn and such. They need to be controlled,
94and in many cases you also want to babysit them.
95
96They present a case where different services need to control (start, stop,
97restart) each other.
98
99 var_service/dhcp_if
22 100
23var_service/dhcp_if -
24controls a udhcpc instance which provides dhpc-assigned IP 101controls a udhcpc instance which provides dhpc-assigned IP
25address on interface named "if". Copy/rename this directory as needed to run 102address on interface named "if". Copy/rename this directory as needed to run
26udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix 103udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix
27of the parent directory as interface name). When IP address is obtained or lost, 104of the parent directory as interface name).
28var_service/dhcp_if/dhcp_handler is run. It saves new config data to 105
29/var/run/service/fw/dhcp_if.ipconf and (re)starts /var/service/fw service. 106When IP address is obtained or lost, var_service/dhcp_if/dhcp_handler is run.
30This example can be used as a template for other dynamic network link services 107It saves new config data to /var/run/service/fw/dhcp_if.ipconf and (re)starts
31(ppp/vpn/zcip). 108/var/service/fw service. This example can be used as a template for other
32 109dynamic network link services (ppp/vpn/zcip).
33var_service/ifplugd_if - 110
34watches link status of interface if. Downs and ups /var/service/dhcp_if 111This is an example of service with has a "finish" script. If downed ("sv d"),
112"finish" is executed. For this service, it removes DHCP address from
113the interface. This is useful when ifplugd detects that the the link is dead
114(cable is no longer attached anywhere) and downs us - keeping DHCP configured
115addresses on the interface would make kernel still try to use it.
116
117 var_service/zcip_if
118
119Zeroconf IP service: assigns a 169.254.x.y/16 address to interface "if".
120This allows to talk to other devices on a network without DHCP server
121(if they also assign 169.254 addresses to themselves).
122
123 var_service/ifplugd_if
124
125Watches link status of interface "if". Downs and ups /var/service/dhcp_if
35service accordingly. In effect, it allows you to unplug/plug-to-different-network 126service accordingly. In effect, it allows you to unplug/plug-to-different-network
36and have your IP properly re-negotiated at once. 127and have your IP properly re-negotiated at once.
37 128
38var_service/dhcp_if_pinger - 129 var_service/dhcp_if_pinger
130
39Uses var_service/dhcp_if's data to determine router IP. Pings it. 131Uses var_service/dhcp_if's data to determine router IP. Pings it.
40If ping fails, restarts /var/service/dhcp_if service. 132If ping fails, restarts /var/service/dhcp_if service.
41Basically, an example of watchdog service for networks which are not reliable 133Basically, an example of watchdog service for networks which are not reliable
42and need babysitting. 134and need babysitting.
43 135
44var_service/fw - 136 var_service/supplicant_if
45A *one-shot* service which reconfigures network based on current known state 137
46of ALL interfaces. Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf 138Wireless supplicant (wifi association and encryption daemon) service for
139interface "if".
140
141 var_service/fw
142
143"Firewall" script, although it is tasked with much more than setting up firewall.
144It is responsible for all aspects of network configuration.
145
146This is an example of *one-shot* service.
147
148It reconfigures network based on current known state of ALL interfaces.
149Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf
47(dynamic config from dhcp/ppp/vpn/etc) to determine what to do. 150(dynamic config from dhcp/ppp/vpn/etc) to determine what to do.
151
48One-shot-ness of this service means that it shuts itself off after single run. 152One-shot-ness of this service means that it shuts itself off after single run.
49IOW: it is not a constantly running daemon sort of thing. 153IOW: it is not a constantly running daemon sort of thing.
50It starts, it configures the network, it shuts down, all done 154It starts, it configures the network, it shuts down, all done
51(unlike infamous NetworkManagers which sit in RAM forever, doing hell knows what). 155(unlike infamous NetworkManagers which sit in RAM forever).
52 156
53However, any dhcp/ppp/vpn or similar service can restart it anytime 157However, any dhcp/ppp/vpn or similar service can restart it anytime
54when it senses the change in network configuration. 158when it senses the change in network configuration.
@@ -59,10 +163,74 @@ This is achieved very simply by having
59 # Make ourself one-shot 163 # Make ourself one-shot
60 sv o . 164 sv o .
61at the very beginning of fw/run script, not at the end. 165at the very beginning of fw/run script, not at the end.
166
62Therefore, any "sv u /var/run/service/fw" command by any other 167Therefore, any "sv u /var/run/service/fw" command by any other
63script "undoes" o(ne-shot) command if fw still runs, thus 168script "undoes" o(ne-shot) command if fw still runs, thus
64runsv will rerun it; or start it in a normal way if fw is not running. 169runsv will rerun it; or start it in a normal way if fw is not running.
65 170
171This mechanism is the reason why fw is a service, not just a script.
172
66System administrators are expected to edit fw/run script, since 173System administrators are expected to edit fw/run script, since
67network configuration needs are likely to be very complex and different 174network configuration needs are likely to be very complex and different
68for non-trivial installations. 175for non-trivial installations.
176
177 var_service/ftpd
178 var_service/httpd
179 var_service/tftpd
180 var_service/ntpd
181
182Examples of typical network daemons.
183
184
185 Process tree
186
187Here is an example of the process tree from a live system with these services
188(and a few others). An interesting detail are ftpd and vpnc services, where
189you can see only logger process. These services are "downed" at the moment:
190their daemons are not launched.
191
192PID TIME COMMAND
193553 0:04 runsvdir -P /var/service
194561 0:00 runsv sshd
195576 0:00 svlogd -tt /var/log/service/sshd
196589 0:00 /usr/sbin/sshd -D -e -p22 -u0 -h /var/service/sshd/ssh_host_rsa_key
197562 0:00 runsv dhcp_eth0
198568 0:00 svlogd -tt /var/log/service/dhcp_eth0
199850 0:00 udhcpc -vv --foreground --interface=eth0
200 --pidfile=/var/service/dhcp_eth0/udhcpc.pid
201 --script=/var/service/dhcp_eth0/dhcp_handler -x hostname bbox
202563 0:00 runsv ntpd
203573 0:01 svlogd -tt /var/log/service/ntpd
204845 0:00 busybox ntpd -dddnNl -S ./ntp.script -p 10.x.x.x -p 10.x.x.x
205564 0:00 runsv ifplugd_wlan0
206598 0:00 svlogd -tt /var/log/service/ifplugd_wlan0
207614 0:05 ifplugd -apqns -t3 -u0 -d0 -i wlan0
208 -r /var/service/ifplugd_wlan0/ifplugd_handler
209565 0:08 runsv dhcp_wlan0_pinger
210911 0:00 sleep 67
211566 0:00 runsv unscd
212583 0:03 svlogd -tt /var/log/service/unscd
213599 0:02 nscd -dddd
214567 0:00 runsv dhcp_wlan0
215591 0:00 svlogd -tt /var/log/service/dhcp_wlan0
216802 0:00 udhcpc -vv -C -o -V --foreground --interface=wlan0
217 --pidfile=/var/service/dhcp_wlan0/udhcpc.pid
218 --script=/var/service/dhcp_wlan0/dhcp_handler
219569 0:00 runsv fw
220570 0:00 runsv ifplugd_eth0
221597 0:00 svlogd -tt /var/log/service/ifplugd_eth0
222612 0:05 ifplugd -apqns -t3 -u8 -d8 -i eth0
223 -r /var/service/ifplugd_eth0/ifplugd_handler
224571 0:00 runsv zcip_eth0
225590 0:00 svlogd -tt /var/log/service/zcip_eth0
226607 0:01 zcip -fvv eth0 /var/service/zcip_eth0/zcip_handler
227572 0:00 runsv ftpd
228604 0:00 svlogd -tt /var/log/service/ftpd
229574 0:00 runsv vpnc
230603 0:00 svlogd -tt /var/log/service/vpnc
231575 0:00 runsv httpd
232602 0:00 svlogd -tt /var/log/service/httpd
233622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root
234577 0:00 runsv supplicant_wlan0
235627 0:00 svlogd -tt /var/log/service/supplicant_wlan0
236638 0:03 wpa_supplicant -i wlan0 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index b0bc0d70f..1d0fc53cf 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -56,6 +56,7 @@
56#endif 56#endif
57 57
58#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS 58#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
59#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH
59 60
60#define debug_noise(args...) /*fprintf(stderr, args)*/ 61#define debug_noise(args...) /*fprintf(stderr, args)*/
61 62
@@ -1200,7 +1201,7 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1200static llist_t *read_iface_state(void) 1201static llist_t *read_iface_state(void)
1201{ 1202{
1202 llist_t *state_list = NULL; 1203 llist_t *state_list = NULL;
1203 FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH); 1204 FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH);
1204 1205
1205 if (state_fp) { 1206 if (state_fp) {
1206 char *start, *end_ptr; 1207 char *start, *end_ptr;
@@ -1215,6 +1216,38 @@ static llist_t *read_iface_state(void)
1215 return state_list; 1216 return state_list;
1216} 1217}
1217 1218
1219/* read the previous state from the state file */
1220static FILE *open_new_state_file(void)
1221{
1222 int fd, flags, cnt;
1223
1224 cnt = 0;
1225 flags = (O_WRONLY | O_CREAT | O_EXCL);
1226 for (;;) {
1227 fd = open(IFSTATE_FILE_PATH".new", flags, 0666);
1228 if (fd >= 0)
1229 break;
1230 if (errno != EEXIST
1231 || flags == (O_WRONLY | O_CREAT | O_TRUNC)
1232 ) {
1233 bb_perror_msg_and_die("can't open '%s'",
1234 IFSTATE_FILE_PATH".new");
1235 }
1236 /* Someone else created the .new file */
1237 if (cnt > 30 * 1000) {
1238 /* Waited for 30*30/2 = 450 milliseconds, still EEXIST.
1239 * Assuming a stale file, rewriting it.
1240 */
1241 flags = (O_WRONLY | O_CREAT | O_TRUNC);
1242 continue;
1243 }
1244 usleep(cnt);
1245 cnt += 1000;
1246 }
1247
1248 return xfdopen_for_write(fd);
1249}
1250
1218 1251
1219int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1252int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1220int ifupdown_main(int argc UNUSED_PARAM, char **argv) 1253int ifupdown_main(int argc UNUSED_PARAM, char **argv)
@@ -1348,7 +1381,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1348 any_failures = 1; 1381 any_failures = 1;
1349 } else if (!NO_ACT) { 1382 } else if (!NO_ACT) {
1350 /* update the state file */ 1383 /* update the state file */
1351 FILE *state_fp; 1384 FILE *new_state_fp = open_new_state_file();
1352 llist_t *state; 1385 llist_t *state;
1353 llist_t *state_list = read_iface_state(); 1386 llist_t *state_list = read_iface_state();
1354 llist_t *iface_state = find_iface_state(state_list, iface); 1387 llist_t *iface_state = find_iface_state(state_list, iface);
@@ -1368,15 +1401,15 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1368 } 1401 }
1369 1402
1370 /* Actually write the new state */ 1403 /* Actually write the new state */
1371 state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
1372 state = state_list; 1404 state = state_list;
1373 while (state) { 1405 while (state) {
1374 if (state->data) { 1406 if (state->data) {
1375 fprintf(state_fp, "%s\n", state->data); 1407 fprintf(new_state_fp, "%s\n", state->data);
1376 } 1408 }
1377 state = state->link; 1409 state = state->link;
1378 } 1410 }
1379 fclose(state_fp); 1411 fclose(new_state_fp);
1412 xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
1380 llist_free(state_list, free); 1413 llist_free(state_list, free);
1381 } 1414 }
1382 next: 1415 next:
diff --git a/networking/tcpudp.c b/networking/tcpudp.c
index fbd1f1c45..b27cf3ea9 100644
--- a/networking/tcpudp.c
+++ b/networking/tcpudp.c
@@ -34,37 +34,56 @@
34/* with not-implemented options: */ 34/* with not-implemented options: */
35/* //usage: "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */ 35/* //usage: "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */
36//usage:#define tcpsvd_full_usage "\n\n" 36//usage:#define tcpsvd_full_usage "\n\n"
37//usage: "Create TCP socket, bind to IP:PORT and listen\n" 37//usage: "Create TCP socket, bind to IP:PORT and listen for incoming connections.\n"
38//usage: "for incoming connection. Run PROG for each connection.\n" 38//usage: "Run PROG for each connection.\n"
39//usage: "\n IP IP to listen on, 0 = all" 39//usage: "\n IP PORT IP:PORT to listen on"
40//usage: "\n PORT Port to listen on"
41//usage: "\n PROG ARGS Program to run" 40//usage: "\n PROG ARGS Program to run"
42//usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)"
43//usage: "\n -u USER[:GRP] Change to user/group after bind" 41//usage: "\n -u USER[:GRP] Change to user/group after bind"
44//usage: "\n -c N Handle up to N connections simultaneously" 42//usage: "\n -c N Up to N connections simultaneously (default 30)"
45//usage: "\n -b N Allow a backlog of approximately N TCP SYNs" 43//usage: "\n -b N Allow backlog of approximately N TCP SYNs (default 20)"
46//usage: "\n -C N[:MSG] Allow only up to N connections from the same IP" 44//usage: "\n -C N[:MSG] Allow only up to N connections from the same IP:"
47//usage: "\n New connections from this IP address are closed" 45//usage: "\n new connections from this IP address are closed"
48//usage: "\n immediately. MSG is written to the peer before close" 46//usage: "\n immediately, MSG is written to the peer before close"
47//usage: "\n -E Don't set up environment"
49//usage: "\n -h Look up peer's hostname" 48//usage: "\n -h Look up peer's hostname"
50//usage: "\n -E Don't set up environment variables" 49//usage: "\n -l NAME Local hostname (else look up local hostname in DNS)"
51//usage: "\n -v Verbose" 50//usage: "\n -v Verbose"
51//usage: "\n"
52//usage: "\nEnvironment if no -E:"
53//usage: "\nPROTO='TCP'"
54//usage: "\nTCPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
55//usage: "\nTCPLOCALADDR='ip:port'"
56//usage: "\nTCPORIGDSTADDR='ip:port' of destination before firewall"
57//usage: "\n Useful for REDIRECTed-to-local connections:"
58//usage: "\n iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 8080"
59//usage: "\nTCPCONCURRENCY=num_of_connects_from_this_ip"
60//usage: "\nIf -h:"
61//usage: "\nTCPLOCALHOST='hostname' (-l NAME is used if specified)"
62//usage: "\nTCPREMOTEHOST='hostname'"
63
52//usage: 64//usage:
53//usage:#define udpsvd_trivial_usage 65//usage:#define udpsvd_trivial_usage
54//usage: "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG" 66//usage: "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG"
55//usage:#define udpsvd_full_usage "\n\n" 67//usage:#define udpsvd_full_usage "\n\n"
56//usage: "Create UDP socket, bind to IP:PORT and wait\n" 68//usage: "Create UDP socket, bind to IP:PORT and wait for incoming packets.\n"
57//usage: "for incoming packets. Run PROG for each packet,\n" 69//usage: "Run PROG for each packet, redirecting all further packets with same\n"
58//usage: "redirecting all further packets with same peer ip:port to it.\n" 70//usage: "peer ip:port to it.\n"
59//usage: "\n IP IP to listen on, 0 = all" 71//usage: "\n IP PORT IP:PORT to listen on"
60//usage: "\n PORT Port to listen on"
61//usage: "\n PROG ARGS Program to run" 72//usage: "\n PROG ARGS Program to run"
62//usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)"
63//usage: "\n -u USER[:GRP] Change to user/group after bind" 73//usage: "\n -u USER[:GRP] Change to user/group after bind"
64//usage: "\n -c N Handle up to N connections simultaneously" 74//usage: "\n -c N Up to N connections simultaneously (default 30)"
75//usage: "\n -E Don't set up environment"
65//usage: "\n -h Look up peer's hostname" 76//usage: "\n -h Look up peer's hostname"
66//usage: "\n -E Don't set up environment variables" 77//usage: "\n -l NAME Local hostname (else look up local hostname in DNS)"
67//usage: "\n -v Verbose" 78//usage: "\n -v Verbose"
79//usage: "\n"
80//usage: "\nEnvironment if no -E:"
81//usage: "\nPROTO='UDP'"
82//usage: "\nUDPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
83//usage: "\nUDPLOCALADDR='ip:port'"
84//usage: "\nIf -h:"
85//usage: "\nUDPLOCALHOST='hostname' (-l NAME is used if specified)"
86//usage: "\nUDPREMOTEHOST='hostname'"
68 87
69#include "libbb.h" 88#include "libbb.h"
70#include "common_bufsiz.h" 89#include "common_bufsiz.h"
@@ -240,7 +259,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
240 ); 259 );
241#else 260#else
242 /* "+": stop on first non-option */ 261 /* "+": stop on first non-option */
243 opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", 262 opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:hpt:v",
244 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, 263 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
245 &backlog, &str_t, &verbose 264 &backlog, &str_t, &verbose
246 ); 265 );
@@ -349,16 +368,20 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
349 again: 368 again:
350 hccp = NULL; 369 hccp = NULL;
351 370
371 again1:
372 close(0);
373 /* It's important to close(0) _before_ wait loop:
374 * fd#0 can be a shared connection fd.
375 * If kept open by us, peer can't detect PROG closing it.
376 */
352 while (cnum >= cmax) 377 while (cnum >= cmax)
353 wait_for_any_sig(); /* expecting SIGCHLD */ 378 wait_for_any_sig(); /* expecting SIGCHLD */
354 379
355 /* Accept a connection to fd #0 */
356 again1:
357 close(0);
358 again2: 380 again2:
359 sig_unblock(SIGCHLD); 381 sig_unblock(SIGCHLD);
360 local.len = remote.len = sa_len; 382 local.len = remote.len = sa_len;
361 if (tcp) { 383 if (tcp) {
384 /* Accept a connection to fd #0 */
362 conn = accept(sock, &remote.u.sa, &remote.len); 385 conn = accept(sock, &remote.u.sa, &remote.len);
363 } else { 386 } else {
364 /* In case recv_from_to won't be able to recover local addr. 387 /* In case recv_from_to won't be able to recover local addr.
diff --git a/networking/telnet.c b/networking/telnet.c
index d2daf5c8c..1a6986b94 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -311,15 +311,16 @@ static void put_iac(int c)
311 G.iacbuf[G.iaclen++] = c; 311 G.iacbuf[G.iaclen++] = c;
312} 312}
313 313
314static void put_iac2(byte wwdd, byte c) 314static void put_iac2_merged(unsigned wwdd_and_c)
315{ 315{
316 if (G.iaclen + 3 > IACBUFSIZE) 316 if (G.iaclen + 3 > IACBUFSIZE)
317 iac_flush(); 317 iac_flush();
318 318
319 put_iac(IAC); 319 put_iac(IAC);
320 put_iac(wwdd); 320 put_iac(wwdd_and_c >> 8);
321 put_iac(c); 321 put_iac(wwdd_and_c & 0xff);
322} 322}
323#define put_iac2(wwdd,c) put_iac2_merged(((wwdd)<<8) + (c))
323 324
324#if ENABLE_FEATURE_TELNET_TTYPE 325#if ENABLE_FEATURE_TELNET_TTYPE
325static void put_iac_subopt(byte c, char *str) 326static void put_iac_subopt(byte c, char *str)
diff --git a/networking/telnetd.IAC_test.sh b/networking/telnetd.IAC_test.sh
new file mode 100644
index 000000000..a36ee3aa0
--- /dev/null
+++ b/networking/telnetd.IAC_test.sh
@@ -0,0 +1,87 @@
1#!/bin/sh
2# Testcase for IAC input processing.
3# The bug also required a small and odd BUFSIZE ("enum { BUFSIZE = 37 };")
4# in telnetd.c to trigger easily.
5
6echo "\
7Run telnetd like this:
8 busybox telnetd -l./save.sh -F
9where save.sh is
10 #!/bin/sh
11 stty -echo
12 exec cat >save.dat
13Now I'll try to connect to it and feed it 2048 0xff bytes.
14Check that save.dat does contain 2048 0xff bytes.
15"
16
17ff()
18{
19echo -en \
20'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
21'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
22'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
23'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
24'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
25'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
26'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
27'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
28'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
29'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
30'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
31'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
32'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
33'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
34'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
35'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
36'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
37'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
38'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
39'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
40'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
41'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
42'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
43'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
44'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
45'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
46'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
47'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
48'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
49'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
50'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
51'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
52'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
53'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
54'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
55'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
56'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
57'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
58'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
59'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
60'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
61'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
62'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
63'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
64'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
65'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
66'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
67'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
68'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
69'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
70'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
71'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
72'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
73'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
74'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
75'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
76'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
77'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
78'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
79'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
80'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
81'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
82'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
83'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
84'\r\n'; }
85
86ff | wc -c
87{ ff; sleep 2; } | busybox telnet 127.0.0.1
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 2fbdc3bb3..303ef1be7 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -60,6 +60,7 @@ struct tsession {
60 int sockfd_read; 60 int sockfd_read;
61 int sockfd_write; 61 int sockfd_write;
62 int ptyfd; 62 int ptyfd;
63 smallint buffered_IAC_for_pty;
63 64
64 /* two circular buffers */ 65 /* two circular buffers */
65 /*char *buf1, *buf2;*/ 66 /*char *buf1, *buf2;*/
@@ -91,107 +92,197 @@ struct globals {
91} while (0) 92} while (0)
92 93
93 94
94/* 95/* Write some buf1 data to pty, processing IACs.
95 Remove all IAC's from buf1 (received IACs are ignored and must be removed 96 * Update wridx1 and size1. Return < 0 on error.
96 so as to not be interpreted by the terminal). Make an uninterrupted 97 * Buggy if IAC is present but incomplete: skips them.
97 string of characters fit for the terminal. Do this by packing
98 all characters meant for the terminal sequentially towards the end of buf.
99
100 Return a pointer to the beginning of the characters meant for the terminal
101 and make *num_totty the number of characters that should be sent to
102 the terminal.
103
104 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
105 past (bf + len) then that IAC will be left unprocessed and *processed
106 will be less than len.
107
108 CR-LF ->'s CR mapping is also done here, for convenience.
109
110 NB: may fail to remove iacs which wrap around buffer!
111 */ 98 */
112static unsigned char * 99static ssize_t
113remove_iacs(struct tsession *ts, int *pnum_totty) 100safe_write_to_pty_decode_iac(struct tsession *ts)
114{ 101{
115 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1; 102 unsigned wr;
116 unsigned char *ptr = ptr0; 103 ssize_t rc;
117 unsigned char *totty = ptr; 104 unsigned char *buf;
118 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); 105 unsigned char *found;
119 int num_totty; 106
120 107 buf = TS_BUF1(ts) + ts->wridx1;
121 while (ptr < end) { 108 wr = MIN(BUFSIZE - ts->wridx1, ts->size1);
122 if (*ptr != IAC) { 109 /* wr is at least 1 here */
123 char c = *ptr; 110
124 111 if (ts->buffered_IAC_for_pty) {
125 *totty++ = c; 112 /* Last time we stopped on a "dangling" IAC byte.
126 ptr++; 113 * We removed it from the buffer back then.
127 /* We map \r\n ==> \r for pragmatic reasons. 114 * Now pretend it's still there, and jump to IAC processing.
128 * Many client implementations send \r\n when 115 */
129 * the user hits the CarriageReturn key. 116 ts->buffered_IAC_for_pty = 0;
130 * See RFC 1123 3.3.1 Telnet End-of-Line Convention. 117 wr++;
131 */ 118 ts->size1++;
132 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0')) 119 buf--; /* Yes, this can point before the buffer. It's ok */
133 ptr++; 120 ts->wridx1--;
134 continue; 121 goto handle_iac;
135 } 122 }
136 123
137 if ((ptr+1) >= end) 124 found = memchr(buf, IAC, wr);
138 break; 125 if (found != buf) {
139 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */ 126 /* There is a "prefix" of non-IAC chars.
140 ptr += 2; 127 * Write only them, and return.
141 continue; 128 */
142 } 129 if (found)
143 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */ 130 wr = found - buf;
144 *totty++ = ptr[1];
145 ptr += 2;
146 continue;
147 }
148 131
149 /* 132 /* We map \r\n ==> \r for pragmatic reasons:
150 * TELOPT_NAWS support! 133 * many client implementations send \r\n when
134 * the user hits the CarriageReturn key.
135 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
151 */ 136 */
152 if ((ptr+2) >= end) { 137 rc = wr;
153 /* Only the beginning of the IAC is in the 138 found = memchr(buf, '\r', wr);
154 buffer we were asked to process, we can't 139 if (found)
155 process this char */ 140 rc = found - buf + 1;
156 break; 141 rc = safe_write(ts->ptyfd, buf, rc);
142 if (rc <= 0)
143 return rc;
144 if (rc < wr /* don't look past available data */
145 && buf[rc-1] == '\r' /* need this: imagine that write was _short_ */
146 && (buf[rc] == '\n' || buf[rc] == '\0')
147 ) {
148 rc++;
157 } 149 }
150 goto update_and_return;
151 }
152
153 /* buf starts with IAC char. Process that sequence.
154 * Example: we get this from our own (bbox) telnet client:
155 * read(5, "\377\374\1""\377\373\37""\377\372\37\0\262\0@\377\360""\377\375\1""\377\375\3"):
156 * IAC WONT ECHO, IAC WILL NAWS, IAC SB NAWS <cols> <rows> IAC SE, IAC DO SGA
157 * Another example (telnet-0.17 from old-netkit):
158 * read(4, "\377\375\3""\377\373\30""\377\373\37""\377\373 ""\377\373!""\377\373\"""\377\373'"
159 * "\377\375\5""\377\373#""\377\374\1""\377\372\37\0\257\0I\377\360""\377\375\1"):
160 * IAC DO SGA, IAC WILL TTYPE, IAC WILL NAWS, IAC WILL TSPEED, IAC WILL LFLOW, IAC WILL LINEMODE, IAC WILL NEW_ENVIRON,
161 * IAC DO STATUS, IAC WILL XDISPLOC, IAC WONT ECHO, IAC SB NAWS <cols> <rows> IAC SE, IAC DO ECHO
162 */
163 if (wr <= 1) {
164 /* Only the single IAC byte is in the buffer, eat it
165 * and set a flag "process the rest of the sequence
166 * next time we are here".
167 */
168 //bb_error_msg("dangling IAC!");
169 ts->buffered_IAC_for_pty = 1;
170 rc = 1;
171 goto update_and_return;
172 }
173
174 handle_iac:
175 /* 2-byte commands (240..250 and 255):
176 * IAC IAC (255) Literal 255. Supported.
177 * IAC SE (240) End of subnegotiation. Treated as NOP.
178 * IAC NOP (241) NOP. Supported.
179 * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()?
180 * IAC AYT (246) Are you there. Send back evidence that AYT was seen. TODO (send NOP back)?
181 * These don't look useful:
182 * IAC DM (242) Data mark. What is this?
183 * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C).
184 * IAC AO (245) Abort output. "You can continue running, but do not send me the output".
185 * IAC EC (247) Erase character. The receiver should delete the last received char.
186 * IAC EL (248) Erase line. The receiver should delete everything up tp last newline.
187 * IAC GA (249) Go ahead. For half-duplex lines: "now you talk".
188 * Implemented only as part of NAWS:
189 * IAC SB (250) Subnegotiation of an option follows.
190 */
191 if (buf[1] == IAC) {
192 /* Literal 255 (emacs M-DEL) */
193 //bb_error_msg("255!");
194 rc = safe_write(ts->ptyfd, &buf[1], 1);
158 /* 195 /*
159 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE 196 * If we went through buffered_IAC_for_pty==1 path,
197 * bailing out on error like below messes up the buffer.
198 * EAGAIN is highly unlikely here, other errors will be
199 * repeated on next write, let's just skip error check.
160 */ 200 */
161 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { 201#if 0
202 if (rc <= 0)
203 return rc;
204#endif
205 rc = 2;
206 goto update_and_return;
207 }
208 if (buf[1] >= 240 && buf[1] <= 249) {
209 /* NOP (241). Ignore (putty keepalive, etc) */
210 /* All other 2-byte commands also treated as NOPs here */
211 rc = 2;
212 goto update_and_return;
213 }
214
215 if (wr <= 2) {
216/* BUG: only 2 bytes of the IAC is in the buffer, we just eat them.
217 * This is not a practical problem since >2 byte IACs are seen only
218 * in initial negotiation, when buffer is empty
219 */
220 rc = 2;
221 goto update_and_return;
222 }
223
224 if (buf[1] == SB) {
225 if (buf[2] == TELOPT_NAWS) {
226 /* IAC SB, TELOPT_NAWS, 4-byte, IAC SE */
162 struct winsize ws; 227 struct winsize ws;
163 if ((ptr+8) >= end) 228 if (wr <= 6) {
164 break; /* incomplete, can't process */ 229/* BUG: incomplete, can't process */
165 ws.ws_col = (ptr[3] << 8) | ptr[4]; 230 rc = wr;
166 ws.ws_row = (ptr[5] << 8) | ptr[6]; 231 goto update_and_return;
232 }
233 memset(&ws, 0, sizeof(ws)); /* pixel sizes are set to 0 */
234 ws.ws_col = (buf[3] << 8) | buf[4];
235 ws.ws_row = (buf[5] << 8) | buf[6];
167 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); 236 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
168 ptr += 9; 237 rc = 7;
169 continue; 238 /* trailing IAC SE will be eaten separately, as 2-byte NOP */
239 goto update_and_return;
170 } 240 }
171 /* skip 3-byte IAC non-SB cmd */ 241 /* else: other subnegs not supported yet */
242 }
243
244 /* Assume it is a 3-byte WILL/WONT/DO/DONT 251..254 command and skip it */
172#if DEBUG 245#if DEBUG
173 fprintf(stderr, "Ignoring IAC %s,%s\n", 246 fprintf(stderr, "Ignoring IAC %s,%s\n",
174 TELCMD(ptr[1]), TELOPT(ptr[2])); 247 TELCMD(buf[1]), TELOPT(buf[2]));
175#endif 248#endif
176 ptr += 3; 249 rc = 3;
250
251 update_and_return:
252 ts->wridx1 += rc;
253 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
254 ts->wridx1 = 0;
255 ts->size1 -= rc;
256 /*
257 * Hack. We cannot process IACs which wrap around buffer's end.
258 * Since properly fixing it requires writing bigger code,
259 * we rely instead on this code making it virtually impossible
260 * to have wrapped IAC (people don't type at 2k/second).
261 * It also allows for bigger reads in common case.
262 */
263 if (ts->size1 == 0) { /* very typical */
264 //bb_error_msg("zero size1");
265 ts->rdidx1 = 0;
266 ts->wridx1 = 0;
267 return rc;
177 } 268 }
178 269 wr = ts->wridx1;
179 num_totty = totty - ptr0; 270 if (wr != 0 && wr < ts->rdidx1) {
180 *pnum_totty = num_totty; 271 /* Buffer is not wrapped yet.
181 /* The difference between ptr and totty is number of iacs 272 * We can easily move it to the beginning.
182 we removed from the stream. Adjust buf1 accordingly */ 273 */
183 if ((ptr - totty) == 0) /* 99.999% of cases */ 274 //bb_error_msg("moved %d", wr);
184 return ptr0; 275 memmove(TS_BUF1(ts), TS_BUF1(ts) + wr, ts->size1);
185 ts->wridx1 += ptr - totty; 276 ts->rdidx1 -= wr;
186 ts->size1 -= ptr - totty; 277 ts->wridx1 = 0;
187 /* Move chars meant for the terminal towards the end of the buffer */ 278 }
188 return memmove(ptr - num_totty, ptr0, num_totty); 279 return rc;
189} 280}
190 281
191/* 282/*
192 * Converting single IAC into double on output 283 * Converting single IAC into double on output
193 */ 284 */
194static size_t iac_safe_write(int fd, const char *buf, size_t count) 285static size_t safe_write_double_iac(int fd, const char *buf, size_t count)
195{ 286{
196 const char *IACptr; 287 const char *IACptr;
197 size_t wr, rc, total; 288 size_t wr, rc, total;
@@ -203,6 +294,7 @@ static size_t iac_safe_write(int fd, const char *buf, size_t count)
203 if (*buf == (char)IAC) { 294 if (*buf == (char)IAC) {
204 static const char IACIAC[] ALIGN1 = { IAC, IAC }; 295 static const char IACIAC[] ALIGN1 = { IAC, IAC };
205 rc = safe_write(fd, IACIAC, 2); 296 rc = safe_write(fd, IACIAC, 2);
297/* BUG: if partial write was only 1 byte long, we end up emitting just one IAC */
206 if (rc != 2) 298 if (rc != 2)
207 break; 299 break;
208 buf++; 300 buf++;
@@ -298,7 +390,7 @@ make_new_session(
298 IAC, WILL, TELOPT_ECHO, 390 IAC, WILL, TELOPT_ECHO,
299 IAC, WILL, TELOPT_SGA 391 IAC, WILL, TELOPT_SGA
300 }; 392 };
301 /* This confuses iac_safe_write(), it will try to duplicate 393 /* This confuses safe_write_double_iac(), it will try to duplicate
302 * each IAC... */ 394 * each IAC... */
303 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send)); 395 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
304 //ts->rdidx2 = sizeof(iacs_to_send); 396 //ts->rdidx2 = sizeof(iacs_to_send);
@@ -649,51 +741,34 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
649 struct tsession *next = ts->next; /* in case we free ts */ 741 struct tsession *next = ts->next; /* in case we free ts */
650 742
651 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { 743 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
652 int num_totty;
653 unsigned char *ptr;
654 /* Write to pty from buffer 1 */ 744 /* Write to pty from buffer 1 */
655 ptr = remove_iacs(ts, &num_totty); 745 count = safe_write_to_pty_decode_iac(ts);
656 count = safe_write(ts->ptyfd, ptr, num_totty);
657 if (count < 0) { 746 if (count < 0) {
658 if (errno == EAGAIN) 747 if (errno == EAGAIN)
659 goto skip1; 748 goto skip1;
660 goto kill_session; 749 goto kill_session;
661 } 750 }
662 ts->size1 -= count;
663 ts->wridx1 += count;
664 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
665 ts->wridx1 = 0;
666 } 751 }
667 skip1: 752 skip1:
668 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { 753 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
669 /* Write to socket from buffer 2 */ 754 /* Write to socket from buffer 2 */
670 count = MIN(BUFSIZE - ts->wridx2, ts->size2); 755 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
671 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); 756 count = safe_write_double_iac(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
672 if (count < 0) { 757 if (count < 0) {
673 if (errno == EAGAIN) 758 if (errno == EAGAIN)
674 goto skip2; 759 goto skip2;
675 goto kill_session; 760 goto kill_session;
676 } 761 }
677 ts->size2 -= count;
678 ts->wridx2 += count; 762 ts->wridx2 += count;
679 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ 763 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
680 ts->wridx2 = 0; 764 ts->wridx2 = 0;
765 ts->size2 -= count;
766 if (ts->size2 == 0) {
767 ts->rdidx2 = 0;
768 ts->wridx2 = 0;
769 }
681 } 770 }
682 skip2: 771 skip2:
683 /* Should not be needed, but... remove_iacs is actually buggy
684 * (it cannot process iacs which wrap around buffer's end)!
685 * Since properly fixing it requires writing bigger code,
686 * we rely instead on this code making it virtually impossible
687 * to have wrapped iac (people don't type at 2k/second).
688 * It also allows for bigger reads in common case. */
689 if (ts->size1 == 0) {
690 ts->rdidx1 = 0;
691 ts->wridx1 = 0;
692 }
693 if (ts->size2 == 0) {
694 ts->rdidx2 = 0;
695 ts->wridx2 = 0;
696 }
697 772
698 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { 773 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
699 /* Read from socket to buffer 1 */ 774 /* Read from socket to buffer 1 */
diff --git a/util-linux/more.c b/util-linux/more.c
index 4812f1bc5..f6923efda 100644
--- a/util-linux/more.c
+++ b/util-linux/more.c
@@ -76,7 +76,16 @@ int more_main(int argc UNUSED_PARAM, char **argv)
76 76
77 INIT_G(); 77 INIT_G();
78 78
79 argv++; 79 /* Parse options */
80 /* Accepted but ignored: */
81 /* -d Display help instead of ringing bell is pressed */
82 /* -f Count logical lines (IOW: long lines are not folded) */
83 /* -l Do not pause after any line containing a ^L (form feed) */
84 /* -s Squeeze blank lines into one */
85 /* -u Suppress underlining */
86 getopt32(argv, "dflsu");
87 argv += optind;
88
80 /* Another popular pager, most, detects when stdout 89 /* Another popular pager, most, detects when stdout
81 * is not a tty and turns into cat. This makes sense. */ 90 * is not a tty and turns into cat. This makes sense. */
82 if (!isatty(STDOUT_FILENO)) 91 if (!isatty(STDOUT_FILENO))