aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2026-03-11 10:14:49 +0000
committerRon Yorston <rmy@pobox.com>2026-03-11 10:14:49 +0000
commitede205bd07573813337b5706acb0cae3b127a36f (patch)
tree1f79073ae9b7fa6fc8bd6b5f090380fd972f77dc
parentb8b19b18ad8f189e1a6f52043dabe8d38d19b883 (diff)
parente527bd22dbda697d09d4dff8786c6290d79d577a (diff)
downloadbusybox-w32-ede205bd07573813337b5706acb0cae3b127a36f.tar.gz
busybox-w32-ede205bd07573813337b5706acb0cae3b127a36f.tar.bz2
busybox-w32-ede205bd07573813337b5706acb0cae3b127a36f.zip
Merge branch 'busybox' into merge
-rw-r--r--archival/libarchive/get_header_tar.c2
-rw-r--r--coreutils/od_bloaty.c7
-rw-r--r--coreutils/paste.c23
-rw-r--r--coreutils/printf.c2
-rw-r--r--editors/awk.c2
-rw-r--r--include/libbb.h131
-rw-r--r--klibc-utils/resume.c2
-rw-r--r--libbb/bb_get_servname_by_port.c56
-rw-r--r--libbb/bb_get_servport_by_name.c129
-rw-r--r--libbb/get_last_path_component.c6
-rw-r--r--libbb/getpty.c82
-rw-r--r--libbb/ioloop.c264
-rw-r--r--libbb/login.c2
-rw-r--r--libbb/read_key.c75
-rw-r--r--libbb/replace.c3
-rw-r--r--libbb/strrstr.c4
-rw-r--r--libbb/xconnect.c21
-rw-r--r--libbb/xfuncs_printf.c2
-rw-r--r--libpwdgrp/uidgid_get.c3
-rw-r--r--loginutils/getty.c4
-rw-r--r--miscutils/bc.c2
-rw-r--r--miscutils/less.c145
-rw-r--r--networking/httpd.c2
-rw-r--r--networking/ifconfig.c2
-rw-r--r--networking/ifupdown.c2
-rw-r--r--networking/inetd.c22
-rw-r--r--networking/nbd-client.c2
-rw-r--r--networking/netstat.c11
-rw-r--r--networking/pscan.c12
-rw-r--r--networking/route.c36
-rw-r--r--networking/ssl_client.c105
-rw-r--r--networking/ssl_server.c121
-rw-r--r--networking/telnet.c1262
-rw-r--r--networking/telnet.c.OLD702
-rw-r--r--networking/telnet.txt53
-rw-r--r--networking/telnetd.c1994
-rw-r--r--networking/telnetd.c.OLD924
-rw-r--r--networking/tls.c1574
-rw-r--r--networking/tls.h18
-rw-r--r--networking/tls_fe.c47
-rw-r--r--networking/tls_rsa.c173
-rw-r--r--networking/tls_rsa.h12
-rw-r--r--networking/tls_sp_c32.c46
-rw-r--r--networking/udhcp/d6_socket.c3
-rw-r--r--networking/udhcp/socket.c3
-rw-r--r--printutils/lpr.c2
-rw-r--r--procps/nmeter.c5
-rw-r--r--procps/powertop.c5
-rw-r--r--scripts/kconfig/confdata.c2
-rw-r--r--shell/ash.c26
-rw-r--r--util-linux/fdisk.c17
-rw-r--r--util-linux/fdisk_gpt.c16
52 files changed, 6426 insertions, 1740 deletions
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
index 9beed93cf..b83523fb2 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -460,7 +460,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
460 460
461 /* Everything up to and including last ".." component is stripped */ 461 /* Everything up to and including last ".." component is stripped */
462 strip_unsafe_prefix(file_header->name); 462 strip_unsafe_prefix(file_header->name);
463 if (file_header->link_target) { 463 if (file_header->link_target && !S_ISLNK(file_header->mode)) {
464 /* GNU tar 1.34 examples: 464 /* GNU tar 1.34 examples:
465 * tar: Removing leading '/' from hard link targets 465 * tar: Removing leading '/' from hard link targets
466 * tar: Removing leading '../' from hard link targets 466 * tar: Removing leading '../' from hard link targets
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 641d93503..b268368bf 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -1162,7 +1162,7 @@ dump_strings(off_t address, off_t end_offset)
1162 leading '+' return nonzero and set *OFFSET to the offset it denotes. */ 1162 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
1163 1163
1164static int 1164static int
1165parse_old_offset(const char *s, off_t *offset) 1165parse_old_offset(char *s, off_t *offset)
1166{ 1166{
1167 static const struct suffix_mult Bb[] ALIGN_SUFFIX = { 1167 static const struct suffix_mult Bb[] ALIGN_SUFFIX = {
1168 { "B", 1024 }, 1168 { "B", 1024 },
@@ -1188,7 +1188,8 @@ parse_old_offset(const char *s, off_t *offset)
1188 radix = 16; 1188 radix = 16;
1189 1189
1190 *offset = xstrtooff_sfx(s, radix, Bb); 1190 *offset = xstrtooff_sfx(s, radix, Bb);
1191 if (p) p[0] = '.'; 1191 if (p)
1192 p[0] = '.'; /* undo cheating */
1192 1193
1193 return (*offset >= 0); 1194 return (*offset >= 0);
1194} 1195}
@@ -1241,7 +1242,7 @@ int od_main(int argc UNUSED_PARAM, char **argv)
1241 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { 1242 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
1242 '7', '7', '6', /* '?' */ 1243 '7', '7', '6', /* '?' */
1243 }; 1244 };
1244 char *p; 1245 const char *p;
1245 int pos; 1246 int pos;
1246 p = strchr(doxn, str_A[0]); 1247 p = strchr(doxn, str_A[0]);
1247 if (!p) 1248 if (!p)
diff --git a/coreutils/paste.c b/coreutils/paste.c
index 78a5c2a14..363340e3d 100644
--- a/coreutils/paste.c
+++ b/coreutils/paste.c
@@ -34,23 +34,16 @@
34 34
35static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) 35static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt)
36{ 36{
37#if ENABLE_PLATFORM_MINGW32
38 char **line = xmalloc(file_cnt * sizeof(char *)); 37 char **line = xmalloc(file_cnt * sizeof(char *));
39#else
40 char *line;
41#endif
42 char delim; 38 char delim;
43 int active_files = file_cnt; 39 int active_files = file_cnt;
44 int i; 40 int i;
45 41
46 while (active_files > 0) { 42 while (active_files > 0) {
47 int del_idx = 0; 43 int del_idx = 0;
48#if ENABLE_PLATFORM_MINGW32
49 int got_line = FALSE; 44 int got_line = FALSE;
50#endif
51 45
52 for (i = 0; i < file_cnt; ++i) { 46 for (i = 0; i < file_cnt; ++i) {
53#if ENABLE_PLATFORM_MINGW32
54 if (files[i]) { 47 if (files[i]) {
55 line[i] = xmalloc_fgetline(files[i]); 48 line[i] = xmalloc_fgetline(files[i]);
56 if (!line[i]) { 49 if (!line[i]) {
@@ -73,20 +66,6 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt)
73 fputs_stdout(line[i]); 66 fputs_stdout(line[i]);
74 free(line[i]); 67 free(line[i]);
75 } 68 }
76#else
77 if (files[i] == NULL)
78 continue;
79
80 line = xmalloc_fgetline(files[i]);
81 if (!line) {
82 fclose_if_not_stdin(files[i]);
83 files[i] = NULL;
84 --active_files;
85 continue;
86 }
87 fputs_stdout(line);
88 free(line);
89#endif
90 delim = '\n'; 69 delim = '\n';
91 if (i != file_cnt - 1) { 70 if (i != file_cnt - 1) {
92 delim = delims[del_idx++]; 71 delim = delims[del_idx++];
@@ -97,9 +76,7 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt)
97 fputc(delim, stdout); 76 fputc(delim, stdout);
98 } 77 }
99 } 78 }
100#if ENABLE_PLATFORM_MINGW32
101 free(line); 79 free(line);
102#endif
103} 80}
104 81
105static void paste_files_separate(FILE** files, char* delims, int del_cnt) 82static void paste_files_separate(FILE** files, char* delims, int del_cnt)
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 379c00cc6..0c2502f1b 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -419,7 +419,7 @@ static char **print_formatted(char *f, char **argv, int *conv_err)
419 /* Add "ll" if integer modifier, then print */ 419 /* Add "ll" if integer modifier, then print */
420 { 420 {
421 static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; 421 static const char format_chars[] ALIGN1 = "diouxXfeEgGcs";
422 char *p = strchr(format_chars, *f); 422 char *p = (char*)strchr(format_chars, *f);
423 /* needed - try "printf %" without it */ 423 /* needed - try "printf %" without it */
424 if (p == NULL || *f == '\0') { 424 if (p == NULL || *f == '\0') {
425 bb_error_msg("%s: invalid format", direc_start); 425 bb_error_msg("%s: invalid format", direc_start);
diff --git a/editors/awk.c b/editors/awk.c
index b3ec99d3f..81d7005b3 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -2841,7 +2841,7 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2841 l = strlen(as[0]) - ll; 2841 l = strlen(as[0]) - ll;
2842 if (ll > 0 && l >= 0) { 2842 if (ll > 0 && l >= 0) {
2843 if (!icase) { 2843 if (!icase) {
2844 char *s = strstr(as[0], as[1]); 2844 const char *s = strstr(as[0], as[1]);
2845 if (s) 2845 if (s)
2846 n = (s - as[0]) + 1; 2846 n = (s - as[0]) + 1;
2847 } else { 2847 } else {
diff --git a/include/libbb.h b/include/libbb.h
index 6ce99abda..ee3a5b56a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -501,6 +501,11 @@ void *mmap_anon(size_t size) FAST_FUNC;
501void *xmmap_anon(size_t size) FAST_FUNC; 501void *xmmap_anon(size_t size) FAST_FUNC;
502 502
503#if defined(__x86_64__) || defined(i386) 503#if defined(__x86_64__) || defined(i386)
504/* 0x7f would be better, but it causes alignment problems */
505# define ARCH_GLOBAL_PTR_OFF 0x80
506#endif
507
508#if defined(__x86_64__) || defined(i386)
504# define BB_ARCH_FIXED_PAGESIZE 4096 509# define BB_ARCH_FIXED_PAGESIZE 4096
505#elif defined(__arm__) /* only 32bit, 64bit ARM has variable page size */ 510#elif defined(__arm__) /* only 32bit, 64bit ARM has variable page size */
506# define BB_ARCH_FIXED_PAGESIZE 4096 511# define BB_ARCH_FIXED_PAGESIZE 4096
@@ -511,11 +516,6 @@ void *xmmap_anon(size_t size) FAST_FUNC;
511//sparc64,alpha,openrisc: fixed 8k pages 516//sparc64,alpha,openrisc: fixed 8k pages
512#endif 517#endif
513 518
514#if defined(__x86_64__) || defined(i386)
515/* 0x7f would be better, but it causes alignment problems */
516# define ARCH_GLOBAL_PTR_OFF 0x80
517#endif
518
519#if defined BB_ARCH_FIXED_PAGESIZE 519#if defined BB_ARCH_FIXED_PAGESIZE
520# define IF_VARIABLE_ARCH_PAGESIZE(...) /*nothing*/ 520# define IF_VARIABLE_ARCH_PAGESIZE(...) /*nothing*/
521# define bb_getpagesize() BB_ARCH_FIXED_PAGESIZE 521# define bb_getpagesize() BB_ARCH_FIXED_PAGESIZE
@@ -780,6 +780,76 @@ struct fd_pair { int rd; int wr; };
780#define piped_pair(pair) pipe(&((pair).rd)) 780#define piped_pair(pair) pipe(&((pair).rd))
781#define xpiped_pair(pair) xpipe(&((pair).rd)) 781#define xpiped_pair(pair) xpipe(&((pair).rd))
782 782
783
784int parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC;
785time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC;
786char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
787char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
788void xgettimeofday(struct timeval *tv) FAST_FUNC;
789void xsettimeofday(const struct timeval *tv) FAST_FUNC;
790
791
792/* Generic I/O loop */
793struct connection;
794typedef struct ioloop_state {
795 struct connection *conns;
796 unsigned flags;
797 unsigned max_timeout;
798 unsigned current_iteration_timeout;
799 unsigned last_timeout;
800 int (*pre_poll_callback)(struct ioloop_state*);
801} ioloop_state_t;
802
803#define STRUCT_CONNECTION \
804 struct connection *next; \
805 ioloop_state_t *io; \
806 int read_fd; \
807 int write_fd; \
808 int (*have_buffer_to_read_into)(void *this); \
809 int (*have_data_to_write)(void *this); \
810 int (*read)(void *this); \
811 int (*write)(void *this); \
812
813typedef struct connection {
814 STRUCT_CONNECTION
815} connection_t;
816
817static ALWAYS_INLINE void free_connection(connection_t *conn)
818{
819 free(conn);
820}
821
822void FAST_FUNC conn_close_fds(connection_t *conn);
823void FAST_FUNC conn_close_fds_remove_and_free(connection_t *conn);
824static ALWAYS_INLINE ioloop_state_t *new_ioloop_state(void)
825{
826 ioloop_state_t *io = xzalloc(sizeof(*io));
827 return io;
828}
829static ALWAYS_INLINE void free_ioloop_state(ioloop_state_t *io)
830{
831 free(io);
832}
833static ALWAYS_INLINE void ioloop_decrease_current_timeout(ioloop_state_t *io, unsigned n)
834{
835 if (io->current_iteration_timeout > n)
836 io->current_iteration_timeout = n;
837}
838
839void FAST_FUNC ioloop_insert_conn(ioloop_state_t *io, connection_t *conn);
840void FAST_FUNC ioloop_remove_conn(ioloop_state_t *io, connection_t *conn);
841enum {
842 IOLOOP_FLAG_EXIT_IF_TIMEOUT = (1 << 0),
843 IOLOOP_FLAG_EXIT_IF_EINTR = (1 << 1),
844 //IOLOOP_FLAG_EXIT_IF_ALL_NOT_READY = (1 << 1),
845 /* ioloop_run return values */
846 IOLOOP_NO_CONNS = 0,
847 IOLOOP_TIMEOUT = 1,
848 IOLOOP_EINTR = 2,
849};
850int FAST_FUNC ioloop_run(ioloop_state_t *io);
851
852
783/* Useful for having small structure members/global variables */ 853/* Useful for having small structure members/global variables */
784typedef int8_t socktype_t; 854typedef int8_t socktype_t;
785typedef int8_t family_t; 855typedef int8_t family_t;
@@ -808,14 +878,6 @@ struct BUG_too_small {
808}; 878};
809 879
810 880
811int parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC;
812time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC;
813char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
814char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
815void xgettimeofday(struct timeval *tv) FAST_FUNC;
816void xsettimeofday(const struct timeval *tv) FAST_FUNC;
817
818
819int xsocket(int domain, int type, int protocol) FAST_FUNC; 881int xsocket(int domain, int type, int protocol) FAST_FUNC;
820void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; 882void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
821void xlisten(int s, int backlog) FAST_FUNC; 883void xlisten(int s, int backlog) FAST_FUNC;
@@ -926,6 +988,11 @@ char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) FAST_FUNC RETU
926/* inet_[ap]ton on steroids */ 988/* inet_[ap]ton on steroids */
927char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; 989char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
928char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC; 990char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
991// NB: unlike getservbyport, port parameter/retval are in host, NOT network order!
992#define getservbyport dont_use_getservbyport_uses_global_buffer
993char* bb_get_servname_by_port(char **p_etc_services, int port, const char *type) FAST_FUNC RETURNS_MALLOC;
994#define getservbyname dont_use_getservbyname_uses_global_buffer
995int bb_get_servport_by_name(char **p_etc_services, const char *name, const char *type) FAST_FUNC;
929// "old" (ipv4 only) API 996// "old" (ipv4 only) API
930// users: traceroute.c hostname.c - use _list_ of all IPs 997// users: traceroute.c hostname.c - use _list_ of all IPs
931struct hostent *xgethostbyname(const char *name) FAST_FUNC; 998struct hostent *xgethostbyname(const char *name) FAST_FUNC;
@@ -976,8 +1043,11 @@ typedef struct tls_state {
976 int ofd; 1043 int ofd;
977 int ifd; 1044 int ifd;
978 1045
979 unsigned min_encrypted_len_on_read; 1046#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
1047 smallint expecting_first_packet;
1048#endif
980 uint16_t cipher_id; 1049 uint16_t cipher_id;
1050 unsigned min_encrypted_len_on_read;
981 unsigned MAC_size; 1051 unsigned MAC_size;
982 unsigned key_size; 1052 unsigned key_size;
983 unsigned IV_size; 1053 unsigned IV_size;
@@ -1002,21 +1072,27 @@ typedef struct tls_state {
1002 /*uint64_t read_seq64_be;*/ 1072 /*uint64_t read_seq64_be;*/
1003 uint64_t write_seq64_be; 1073 uint64_t write_seq64_be;
1004 1074
1005 /*uint8_t *server_write_MAC_key;*/ 1075 uint8_t *our_write_MAC_key;
1006 uint8_t *client_write_key; 1076 uint8_t *peer_write_MAC_key;
1007 uint8_t *server_write_key; 1077 uint8_t *our_write_key;
1008 uint8_t *client_write_IV; 1078 uint8_t *peer_write_key;
1009 uint8_t *server_write_IV; 1079 uint8_t *our_write_IV;
1010 uint8_t client_write_MAC_key[TLS_MAX_MAC_SIZE]; 1080 uint8_t *peer_write_IV;
1011 uint8_t server_write_MAC_k__[TLS_MAX_MAC_SIZE]; 1081 uint8_t key_block[TLS_MAX_MAC_SIZE];
1012 uint8_t client_write_k__[TLS_MAX_KEY_SIZE]; 1082 uint8_t key_block2[TLS_MAX_MAC_SIZE];
1013 uint8_t server_write_k__[TLS_MAX_KEY_SIZE]; 1083 uint8_t key_block3[TLS_MAX_KEY_SIZE];
1014 uint8_t client_write_I_[TLS_MAX_IV_SIZE]; 1084 uint8_t key_block4[TLS_MAX_KEY_SIZE];
1015 uint8_t server_write_I_[TLS_MAX_IV_SIZE]; 1085 uint8_t key_block5[TLS_MAX_IV_SIZE];
1086 uint8_t key_block6[TLS_MAX_IV_SIZE];
1016 1087
1017 struct tls_aes aes_encrypt; 1088 struct tls_aes aes_encrypt;
1018 struct tls_aes aes_decrypt; 1089 struct tls_aes aes_decrypt;
1019 uint8_t H[16]; //used by AES_GCM 1090 uint8_t H[16]; //used by AES_GCM
1091
1092#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
1093 /* For ECDHE: server's ephemeral EC private key */
1094 //uint8_t ecc_priv_key32[32];
1095#endif
1020} tls_state_t; 1096} tls_state_t;
1021#endif 1097#endif
1022 1098
@@ -1025,8 +1101,9 @@ static inline tls_state_t *new_tls_state(void)
1025 tls_state_t *tls = xzalloc(sizeof(*tls)); 1101 tls_state_t *tls = xzalloc(sizeof(*tls));
1026 return tls; 1102 return tls;
1027} 1103}
1028 1104void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni);
1029void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; 1105void FAST_FUNC tls_handshake_as_server(tls_state_t *tls,
1106 const char *pem_filename);
1030#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) 1107#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0)
1031void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; 1108void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC;
1032 1109
diff --git a/klibc-utils/resume.c b/klibc-utils/resume.c
index 179413627..cb8314c75 100644
--- a/klibc-utils/resume.c
+++ b/klibc-utils/resume.c
@@ -31,7 +31,7 @@
31 * - /dev/ram (alias to /dev/ram0) 31 * - /dev/ram (alias to /dev/ram0)
32 * - /dev/mtd 32 * - /dev/mtd
33 */ 33 */
34static dev_t name_to_dev_t(const char *devname) 34static dev_t name_to_dev_t(char *devname)
35{ 35{
36 char devfile[sizeof(int)*3 * 2 + 4]; 36 char devfile[sizeof(int)*3 * 2 + 4];
37 char *sysname; 37 char *sysname;
diff --git a/libbb/bb_get_servname_by_port.c b/libbb/bb_get_servname_by_port.c
new file mode 100644
index 000000000..970a718fe
--- /dev/null
+++ b/libbb/bb_get_servname_by_port.c
@@ -0,0 +1,56 @@
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-$(CONFIG_NETSTAT) += bb_get_servname_by_port.o
9//kbuild:lib-$(CONFIG_PSCAN) += bb_get_servname_by_port.o
10#include "libbb.h"
11
12//Rationale for existing:
13//#define getservbyport dont_use_getservbyport_uses_global_buffer
14//(for example: -280 bytes on musl, x86-32)
15
16char* FAST_FUNC bb_get_servname_by_port(char **p_etc_services, int port, const char *type)
17{
18 char *sp;
19
20 sp = *p_etc_services;
21 if (!sp) {
22//TODO: we need mmap_entire_file() for this use case!
23 sp = xmalloc_open_read_close("/etc/services", NULL);
24 if (!sp)
25 return NULL;
26 *p_etc_services = sp;
27 }
28 while (*sp) {
29 const char *pnstr, *sp_end;
30 char *end;
31 unsigned n;
32
33 sp = skip_whitespace(sp);
34 if (*sp == '#')
35 goto next;
36 sp_end = skip_non_whitespace(sp);
37 pnstr = skip_whitespace(sp_end);
38 n = bb_strtou(pnstr, &end, 10);
39 if (n != port || *end != '/')
40 goto next;
41 if (type) {
42 end++;
43 end = is_prefixed_with(end, type);
44 if (!end
45 || !(isspace(*end) || *end == '\0'
46 || *end == '#') // glibc treats "http 80/tcp#COMMENT" (no space!) as valid
47 ) {
48 goto next;
49 }
50 }
51 return auto_string(xstrndup(sp, sp_end - sp));
52 next:
53 sp = strchrnul(sp, '\n');
54 }
55 return NULL;
56}
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/get_last_path_component.c b/libbb/get_last_path_component.c
index 46a87d7fc..9be813f80 100644
--- a/libbb/get_last_path_component.c
+++ b/libbb/get_last_path_component.c
@@ -48,20 +48,20 @@ char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path)
48{ 48{
49#if ENABLE_PLATFORM_MINGW32 49#if ENABLE_PLATFORM_MINGW32
50 const char *start = path + root_len(path); 50 const char *start = path + root_len(path);
51 char *slash = get_last_slash(path); 51 const char *slash = get_last_slash(path);
52 52
53 if (!slash && has_dos_drive_prefix(path) && path[2] != '\0') 53 if (!slash && has_dos_drive_prefix(path) && path[2] != '\0')
54 return (char *)path + 2; 54 return (char *)path + 2;
55 if (!slash || (slash == start && !slash[1])) 55 if (!slash || (slash == start && !slash[1]))
56 return (char*)path; 56 return (char*)path;
57#else 57#else
58 char *slash = strrchr(path, '/'); 58 const char *slash = strrchr(path, '/');
59 59
60 if (!slash || (slash == path && !slash[1])) 60 if (!slash || (slash == path && !slash[1]))
61 return (char*)path; 61 return (char*)path;
62#endif 62#endif
63 63
64 return slash + 1; 64 return (char*)slash + 1;
65} 65}
66 66
67/* 67/*
diff --git a/libbb/getpty.c b/libbb/getpty.c
index 9ec6265ad..2471812c6 100644
--- a/libbb/getpty.c
+++ b/libbb/getpty.c
@@ -9,35 +9,69 @@
9 9
10#define DEBUG 0 10#define DEBUG 0
11 11
12// On modern systems (old ways were more kludgy with setuid "pt_chown" binary):
13// ptyfd = open("/dev/ptmx"):
14// Kernel creates slave /dev/pts/N with owner = real UID of opener,
15// group and mode as configured by /dev/pts mount options.
16// The mode is safe with standard mount options (gid=5,mode=620).
17//
18// grantpt(ptyfd) is now only a verification step. In glibc:
19// Calls ioctl(fd, TIOCGPTN, &minor) to confirm ptyfd is valid master pty.
20// If ioctl fails with ENOTTY, remaps to EINVAL (POSIX).
21// Otherwise returns 0. (No chown/chmod occurs.)
22//
23// unlockpt(ptyfd) is ioctl(TIOCSPTLCK, 0): clears optional protection flag
24// on slave. If flag was set, open(slave) fails with EIO for everyone (even root)
25// until unlocked. [Do modern kernels still set the lock in open("/dev/ptmx")?]
26// The lock historically prevented opening slave before grantpt() fixes perms
27// in misconfigured/old setups where kernel didn't set 0620 atomically.
28// Today it's mostly belt-and-suspenders + POSIX compatibility.
29//
30// The attack thwarted by the lock (assuming unprivileged adversary):
31// you open /dev/ptmx, kernel allocates /dev/pts/N with overly permissive mode.
32// Adversary manages to open /dev/pts/N before your grantpt() call
33// locks down permissions.
34// Typically, /dev/pts mount options are gid=5,mode=620, 5 = "tty" group,
35// system is set up with only trusted binaries and no users in "tty" group.
36// In this case, the locking mechanism is overkill (would be safe without it).
37// To fortify more, you can mount with gid=0,mode=600. Tools like "wall",
38// "mesg" would stop working.
39// Processes with my own UID can still race against me, but I probably
40// trust myself...
41//
42// ptsname(ptyfd), internally ioctl(TIOCGPTN): get index, build
43// "/dev/pts/NN" string which refers to the slave pty.
12int FAST_FUNC xgetpty(char *line) 44int FAST_FUNC xgetpty(char *line)
13{ 45{
14 int p;
15
16#if ENABLE_FEATURE_DEVPTS 46#if ENABLE_FEATURE_DEVPTS
17 p = open("/dev/ptmx", O_RDWR); 47 int ptyfd;
18 if (p >= 0) { 48
19 grantpt(p); /* chmod+chown corresponding slave pty */ 49 ptyfd = open("/dev/ptmx", O_RDWR);
20 unlockpt(p); /* (what does this do?) */ 50 if (ptyfd < 0)
51 bb_simple_perror_msg_and_die("can't find free pty");
52 grantpt(ptyfd); /* chmod+chown corresponding slave pty */
53 unlockpt(ptyfd); /* allow open() on slave /dev node */
21# ifndef HAVE_PTSNAME_R 54# ifndef HAVE_PTSNAME_R
22 { 55 {
23 const char *name; 56 const char *name;
24 name = ptsname(p); /* find out the name of slave pty */ 57 name = ptsname(ptyfd); /* find out the name of slave pty */
25 if (!name) { 58 if (!name) {
26 bb_simple_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
27 }
28 safe_strncpy(line, name, GETPTY_BUFSIZE);
29 }
30# else
31 /* find out the name of slave pty */
32 if (ptsname_r(p, line, GETPTY_BUFSIZE-1) != 0) {
33 bb_simple_perror_msg_and_die("ptsname error (is /dev/pts mounted?)"); 59 bb_simple_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
34 } 60 }
35 line[GETPTY_BUFSIZE-1] = '\0'; 61 safe_strncpy(line, name, GETPTY_BUFSIZE);
36# endif
37 return p;
38 } 62 }
63# else
64 /* find out the name of slave pty */
65 if (ptsname_r(ptyfd, line, GETPTY_BUFSIZE-1) != 0) {
66 bb_simple_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
67 }
68 line[GETPTY_BUFSIZE-1] = '\0';
69# endif
70 return ptyfd;
71
39#else 72#else
40 struct stat stb; 73 struct stat stb;
74 int ptyfd;
41 int i; 75 int i;
42 int j; 76 int j;
43 77
@@ -53,13 +87,13 @@ int FAST_FUNC xgetpty(char *line)
53 line[9] = j < 10 ? j + '0' : j - 10 + 'a'; 87 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
54 if (DEBUG) 88 if (DEBUG)
55 fprintf(stderr, "Trying to open device: %s\n", line); 89 fprintf(stderr, "Trying to open device: %s\n", line);
56 p = open(line, O_RDWR | O_NOCTTY); 90 ptyfd = open(line, O_RDWR | O_NOCTTY);
57 if (p >= 0) { 91 if (ptyfd >= 0) {
58 line[5] = 't'; 92 line[5] = 't';
59 return p; 93 return ptyfd;
60 } 94 }
61 } 95 }
62 } 96 }
63#endif /* FEATURE_DEVPTS */
64 bb_simple_error_msg_and_die("can't find free pty"); 97 bb_simple_error_msg_and_die("can't find free pty");
98#endif /* FEATURE_DEVPTS */
65} 99}
diff --git a/libbb/ioloop.c b/libbb/ioloop.c
new file mode 100644
index 000000000..cc9876487
--- /dev/null
+++ b/libbb/ioloop.c
@@ -0,0 +1,264 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2026 Denys Vlasenko <vda.linux@googlemail.com>
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//kbuild:lib-$(CONFIG_TELNETD) += ioloop.o
8
9#include "libbb.h"
10
11#define DEBUG 0
12
13#if DEBUG
14# define dbg(...) bb_error_msg(__VA_ARGS__)
15static char *bin_to_hex(const void *hash_value, unsigned hash_length)
16{
17 /* xzalloc zero-terminates */
18 char *hex_value = xzalloc((hash_length * 2) + 1);
19 bin2hex(hex_value, (char*)hash_value, hash_length);
20 return auto_string(hex_value);
21}
22#else
23# define dbg(...) ((void)0)
24#endif
25
26void FAST_FUNC ioloop_insert_conn(ioloop_state_t *io, connection_t *conn)
27{
28 conn->io = io;
29 conn->next = io->conns;
30 io->conns = conn;
31}
32
33void FAST_FUNC ioloop_remove_conn(ioloop_state_t *io, connection_t *conn)
34{
35 connection_t **pp = &io->conns;
36 while (*pp) {
37 if (*pp == conn) {
38 *pp = conn->next;
39 return;
40 }
41 pp = &(*pp)->next;
42 }
43}
44
45void FAST_FUNC conn_close_fds(connection_t *conn)
46{
47 if (conn->read_fd >= 0)
48 close(conn->read_fd);
49 if (conn->write_fd >= 0 && conn->write_fd != conn->read_fd)
50 close(conn->write_fd);
51 conn->write_fd = -1;
52 conn->read_fd = -1;
53}
54
55void FAST_FUNC conn_close_fds_remove_and_free(connection_t *conn)
56{
57 conn_close_fds(conn);
58 ioloop_remove_conn(conn->io, conn);
59 free_connection(conn);
60}
61
62#ifdef UNUSED
63void FAST_FUNC ioloop_close_fd_in_all_conns(ioloop_state_t *io, int fd)
64{
65 connection_t *conn;
66
67 close(fd);
68 conn = io->conns;
69 while (conn) {
70 if (conn->read_fd == fd)
71 conn->read_fd = -1;
72 if (conn->write_fd == fd)
73 conn->write_fd = -1;
74 conn = conn->next;
75 }
76}
77#endif
78
79// have_data_to_write() - Do we have data to write?
80// May return error if knows that write side is closed, even if it has free buffer space.
81// > 0: Has data to write (or can generate such data)
82// In this case, write_fd must be valid! (it's a bug if it is < 0, can crash)
83// 0: No data to write currently
84// < 0: error, I probably freed myself (do not use my structure in this iteration,
85// on next iteration, if I indeed freed myself, you won't find me in the list).
86//
87// have_buffer_to_read_into() - Is there a buffer to read into?
88// May return error if knows that write side is closed, even if it has free buffer space.
89// > 0: Healthy, has buffer space, please poll read_fd
90// In this case, read_fd must be valid! (it's a bug if it is < 0)
91// 0: One of:
92// Buffer is full (hopefully write() will free some)
93// Got error/EOF, want to drain write buffer first
94// < 0: Error/EOF, I probably freed myself (do not use my structure)
95//
96// Essentially: add your "this connection is dead, close+ioloop_remove_conn(this)+free(this)" code
97// to either of the above two functions.
98// Either would work equally well from ioloop POV.
99//
100// write() - Perform write
101// Even if poll shows that write_fd become ready, the method will not be called if conn->write_fd become < 0.
102// However, changing it to another fd >= 0 is a bug.
103// >= 0: No error (ioloop() does not handle 0 specially)
104// < 0: Error occurred, I probably freed myself (do not use my structure)
105//
106// Error returns from read() and write() will skip the opposite op.
107// (Which one happens first depends on ioloop_run() code, as I write this, write()
108// is done first - therefore read() can't skip write()).
109// IOW: if you "remove(this)" in either of them you MUST return < 0.
110//
111// Write function, if it performs some processing, needs to account for the possibility
112// that any "incomplete data, can't write, wait for more" logic should also check whether
113// "more" is even possible: your other code may know that the read side is at EOF,
114// and arrange for it to not be polled. Thus a hang: read not reading, write waits for more date.
115//
116// read() - Perform read
117// Will not be called if read_fd become < 0.
118// >= 0: No error (0 is EOF, ioloop() does not handle 0 specially)
119// < 0: Error occurred, I probably freed myself (do not use my structure)
120//
121// Putting "this connection is dead, close+remove+free" code into read function
122// is often inconvenient: if you got EOF/error on read, you still want to poll write side
123// and try to write out the buffered data to it. Which means read() can't "remove+free".
124// Instead, you can remember EOF/error and make future have_buffer_to_read_into() respond 0.
125// One way is to close (if possible) read_fd, set it to -1 and use as a flag.
126//
127// If need to support one-sided close, such as when HTTP/1.x client sends us
128// "GET / HTTP/1.1\r\n\r\n" and closes its writing side with shutdown(SHUT_WR),
129// the idiom is that when read() sees EOF, it sets conn->read_fd to -1
130// and subsequently have_buffer_to_read_into() always return 0 (no more attempts to read);
131// write() flushes all remaining data to conn->write_fd and then signals EOF
132// to write_fd: shutdown(SHUT_WR) for sockets, close() for pipes
133// (how to do this for ptys!?).
134// After this, have_data_to_write() can return 0 if fd has to stay open (socket)
135// or can return -1 and free itself if fd is closed.
136
137static ALWAYS_INLINE int have_data_to_write(connection_t *conn)
138{
139 return conn->have_data_to_write(conn);
140}
141static ALWAYS_INLINE int have_buffer_to_read_into(connection_t *conn)
142{
143 return conn->have_buffer_to_read_into(conn);
144}
145static ALWAYS_INLINE int write_from_buf(connection_t *conn)
146{
147 return conn->write(conn);
148}
149static ALWAYS_INLINE int read_to_buf(connection_t *conn)
150{
151 return conn->read(conn);
152}
153
154int FAST_FUNC ioloop_run(ioloop_state_t *io)
155{
156 connection_t *conn;
157 unsigned poll_timeout_us;
158 int maxfd;
159 int count;
160 struct timeval tv;
161 struct timeval *tv_ptr;
162 fd_set rdfdset, wrfdset;
163
164 io->current_iteration_timeout = io->max_timeout ? io->max_timeout : UINT_MAX;
165//bb_error_msg("io->current_iteration_timeout:%d", io->current_iteration_timeout);
166 again:
167 FD_ZERO(&rdfdset);
168 FD_ZERO(&wrfdset);
169
170 conn = io->conns;
171 if (!conn)
172 return IOLOOP_NO_CONNS;
173
174 maxfd = -1;
175 while (conn) {
176 connection_t *next;
177 int rcw, rcr;
178
179 next = conn->next; /* in case conn is freed */
180 rcw = have_data_to_write(conn);
181 if (rcw < 0) {
182 /* often indicates that conn is gone (freed), do not use it anymore */
183 goto next;
184 }
185 /* Do not add conn->write_fd to waiting set yet,
186 * the *reader* may decide to abort (return rcr < 0)!
187 * Check it first:
188 */
189 rcr = have_buffer_to_read_into(conn);
190 if (rcr < 0)
191 goto next;
192 if (rcw > 0) {
193 FD_SET(conn->write_fd, &wrfdset);
194//FIXME: what if fd > 1023
195 if (conn->write_fd > maxfd)
196 maxfd = conn->write_fd;
197 }
198 if (rcr > 0) {
199 FD_SET(conn->read_fd, &rdfdset);
200 if (conn->read_fd > maxfd)
201 maxfd = conn->read_fd;
202 }
203 next:
204 conn = next;
205 }
206 if (!io->conns)
207 return IOLOOP_NO_CONNS; /* all conns removed themselves before we reached polling */
208
209 poll_timeout_us = io->current_iteration_timeout;
210 /* May be useful to know after loop exit: */
211 io->last_timeout = poll_timeout_us;
212 dbg("poll_timeout_us:%d", poll_timeout_us);
213
214 tv_ptr = NULL; /* infinite timeout */
215 if (poll_timeout_us != UINT_MAX) {
216 tv.tv_sec = poll_timeout_us / 1000000;
217 tv.tv_usec = poll_timeout_us % 1000000;
218 tv_ptr = &tv;
219 }
220 maxfd++;
221 count = select(maxfd, maxfd ? &rdfdset : NULL, maxfd ? &wrfdset : NULL, NULL, tv_ptr);
222 dbg("select:%d", count);
223
224 /* Any function in the loop can change it, modifying next select() timeout */
225 io->current_iteration_timeout = io->max_timeout ? io->max_timeout : UINT_MAX;
226 dbg("io->current_iteration_timeout:%d", io->current_iteration_timeout);
227
228 if (count <= 0) {
229 /* 0: timeout */
230 if (count == 0 && (io->flags & IOLOOP_FLAG_EXIT_IF_TIMEOUT))
231 return IOLOOP_TIMEOUT;
232 /* < 0: EINTR or ENOMEM */
233 if (count < 0 && (io->flags & IOLOOP_FLAG_EXIT_IF_EINTR) && errno == EINTR)
234 return IOLOOP_EINTR;
235 goto again;
236 }
237
238 conn = io->conns;
239 while (conn) {
240 connection_t *next = conn->next; /* in case conn freed */
241 int rcw;
242
243 /* We do check for valid fd >=0: this means that conns
244 * may prevent I/O of other conns by setting fds to -1.
245 */
246 dbg("conn->write_fd:%d ?", conn->write_fd);
247 if (conn->write_fd >= 0 && FD_ISSET(conn->write_fd, &wrfdset)) {
248 dbg("conn->write_fd:%d ready", conn->write_fd);
249 rcw = write_from_buf(conn);
250 if (rcw < 0) {
251 /* often indicates that conn is gone (freed), do not use it anymore */
252 goto next1;
253 }
254 }
255 dbg("conn->read_fd:%d ?", conn->read_fd);
256 if (conn->read_fd >= 0 && FD_ISSET(conn->read_fd, &rdfdset)) {
257 dbg("conn->read_fd:%d ready", conn->read_fd);
258 read_to_buf(conn);
259 }
260 next1:
261 conn = next;
262 }
263 goto again;
264}
diff --git a/libbb/login.c b/libbb/login.c
index af860c277..ca0da70b4 100644
--- a/libbb/login.c
+++ b/libbb/login.c
@@ -28,8 +28,6 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
28 time(&t); 28 time(&t);
29 uname(&uts); 29 uname(&uts);
30 30
31 puts("\r"); /* start a new line */
32
33 fp = fopen_for_read(issue_file); 31 fp = fopen_for_read(issue_file);
34 if (!fp) 32 if (!fp)
35 return; 33 return;
diff --git a/libbb/read_key.c b/libbb/read_key.c
index 2414105ee..07cf1af5a 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -26,7 +26,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
26 'd' |0x80,KEYCODE_ALT_D , 26 'd' |0x80,KEYCODE_ALT_D ,
27 /* lineedit mimics bash: Alt-f and Alt-b are forward/backward 27 /* lineedit mimics bash: Alt-f and Alt-b are forward/backward
28 * word jumps. We cheat here and make them return ALT_LEFT/RIGHT 28 * word jumps. We cheat here and make them return ALT_LEFT/RIGHT
29 * keycodes. This way, lineedit need no special code to handle them. 29 * keycodes. This way, lineedit needs no special code to handle them.
30 * If we'll need to distinguish them, introduce new ALT_F/B keycodes, 30 * If we'll need to distinguish them, introduce new ALT_F/B keycodes,
31 * and update lineedit to react to them. 31 * and update lineedit to react to them.
32 */ 32 */
@@ -40,12 +40,12 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
40 'O','F' |0x80,KEYCODE_END , 40 'O','F' |0x80,KEYCODE_END ,
41#if 0 41#if 0
42 'O','P' |0x80,KEYCODE_FUN1 , 42 'O','P' |0x80,KEYCODE_FUN1 ,
43 /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ 43 // [ESC] ESC O [2] P - [Alt-][Shift-]F1
44 /* ESC [ O 1 ; 2 P - Shift-F1 */ 44 // ESC [ O 1 ; 2 P - Shift-F1
45 /* ESC [ O 1 ; 3 P - Alt-F1 */ 45 // ESC [ O 1 ; 3 P - Alt-F1
46 /* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ 46 // ESC [ O 1 ; 4 P - Alt-Shift-F1
47 /* ESC [ O 1 ; 5 P - Ctrl-F1 */ 47 // ESC [ O 1 ; 5 P - Ctrl-F1
48 /* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ 48 // ESC [ O 1 ; 6 P - Ctrl-Shift-F1
49 'O','Q' |0x80,KEYCODE_FUN2 , 49 'O','Q' |0x80,KEYCODE_FUN2 ,
50 'O','R' |0x80,KEYCODE_FUN3 , 50 'O','R' |0x80,KEYCODE_FUN3 ,
51 'O','S' |0x80,KEYCODE_FUN4 , 51 'O','S' |0x80,KEYCODE_FUN4 ,
@@ -54,29 +54,29 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
54 '[','B' |0x80,KEYCODE_DOWN , 54 '[','B' |0x80,KEYCODE_DOWN ,
55 '[','C' |0x80,KEYCODE_RIGHT , 55 '[','C' |0x80,KEYCODE_RIGHT ,
56 '[','D' |0x80,KEYCODE_LEFT , 56 '[','D' |0x80,KEYCODE_LEFT ,
57 /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow> */ 57 // ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow>
58 /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> - implemented below */ 58 // ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> - implemented below
59 /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow> */ 59 // ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow>
60 /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below */ 60 // ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below
61 /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow> */ 61 // ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow>
62 /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt-<arrow> */ 62 // ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt-<arrow>
63 /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift-<arrow> */ 63 // ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift-<arrow>
64 '[','H' |0x80,KEYCODE_HOME , /* xterm */ 64 '[','H' |0x80,KEYCODE_HOME , /* xterm */
65 '[','F' |0x80,KEYCODE_END , /* xterm */ 65 '[','F' |0x80,KEYCODE_END , /* xterm */
66 /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */ 66 // [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?)
67 /* '[','Z' |0x80,KEYCODE_SHIFT_TAB, */ 67 // '[','Z' |0x80,KEYCODE_SHIFT_TAB,
68 '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ 68 '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */
69 '[','2','~' |0x80,KEYCODE_INSERT , 69 '[','2','~' |0x80,KEYCODE_INSERT ,
70 /* ESC [ 2 ; 3 ~ - Alt-Insert */ 70 // ESC [ 2 ; 3 ~ - Alt-Insert
71 '[','3','~' |0x80,KEYCODE_DELETE , 71 '[','3','~' |0x80,KEYCODE_DELETE ,
72 /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ 72 // [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete
73 /* ESC [ 3 ; 3 ~ - Alt-Delete */ 73 // ESC [ 3 ; 3 ~ - Alt-Delete
74 /* ESC [ 3 ; 5 ~ - Ctrl-Delete */ 74 // ESC [ 3 ; 5 ~ - Ctrl-Delete
75 '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ 75 '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */
76 '[','5','~' |0x80,KEYCODE_PAGEUP , 76 '[','5','~' |0x80,KEYCODE_PAGEUP ,
77 /* ESC [ 5 ; 3 ~ - Alt-PgUp */ 77 // ESC [ 5 ; 3 ~ - Alt-PgUp
78 /* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ 78 // ESC [ 5 ; 5 ~ - Ctrl-PgUp
79 /* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ 79 // ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp
80 '[','6','~' |0x80,KEYCODE_PAGEDOWN, 80 '[','6','~' |0x80,KEYCODE_PAGEDOWN,
81 '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ 81 '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */
82 '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ 82 '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */
@@ -86,7 +86,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
86 '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */ 86 '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */
87 '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */ 87 '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */
88 '[','1','5','~'|0x80,KEYCODE_FUN5 , 88 '[','1','5','~'|0x80,KEYCODE_FUN5 ,
89 /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ 89 // [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5
90 '[','1','7','~'|0x80,KEYCODE_FUN6 , 90 '[','1','7','~'|0x80,KEYCODE_FUN6 ,
91 '[','1','8','~'|0x80,KEYCODE_FUN7 , 91 '[','1','8','~'|0x80,KEYCODE_FUN7 ,
92 '[','1','9','~'|0x80,KEYCODE_FUN8 , 92 '[','1','9','~'|0x80,KEYCODE_FUN8 ,
@@ -94,21 +94,21 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
94 '[','2','1','~'|0x80,KEYCODE_FUN10 , 94 '[','2','1','~'|0x80,KEYCODE_FUN10 ,
95 '[','2','3','~'|0x80,KEYCODE_FUN11 , 95 '[','2','3','~'|0x80,KEYCODE_FUN11 ,
96 '[','2','4','~'|0x80,KEYCODE_FUN12 , 96 '[','2','4','~'|0x80,KEYCODE_FUN12 ,
97 /* ESC [ 2 4 ; 2 ~ - Shift-F12 */ 97 // ESC [ 2 4 ; 2 ~ - Shift-F12
98 /* ESC [ 2 4 ; 3 ~ - Alt-F12 */ 98 // ESC [ 2 4 ; 3 ~ - Alt-F12
99 /* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ 99 // ESC [ 2 4 ; 4 ~ - Alt-Shift-F12
100 /* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ 100 // ESC [ 2 4 ; 5 ~ - Ctrl-F12
101 /* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ 101 // ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12
102#endif 102#endif
103 /* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused */ 103 // '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unused
104 /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */ 104 // '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unused
105 '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT,
106 '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT ,
107 /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unused */
108 /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unused */
109 '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT, 105 '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT,
110 '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT , 106 '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT ,
111 /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused */ 107 // '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused
108 // '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused
109 '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT,
110 '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT ,
111 // '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused
112 0 112 0
113 }; 113 };
114 114
@@ -210,7 +210,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
210 /* Forward to last char */ 210 /* Forward to last char */
211 while (!(*seq & 0x80)) 211 while (!(*seq & 0x80))
212 seq++; 212 seq++;
213 /* Skip it and the keycode which follows */ 213 /* Skip seq terminating byte, and KEYCODE_xyz byte that follows */
214 seq += 2; 214 seq += 2;
215 break; 215 break;
216 } 216 }
@@ -222,6 +222,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
222 * but we never read ahead that much, 222 * but we never read ahead that much,
223 * and n == i here. */ 223 * and n == i here. */
224 buffer[-1] = 0; 224 buffer[-1] = 0;
225 /* Return KEYCODE_xyz (always negative) */
225 return (signed char)seq[i+1]; 226 return (signed char)seq[i+1];
226 } 227 }
227 i++; 228 i++;
diff --git a/libbb/replace.c b/libbb/replace.c
index bc26b04cc..273330f8a 100644
--- a/libbb/replace.c
+++ b/libbb/replace.c
@@ -28,7 +28,8 @@ unsigned FAST_FUNC count_strstr(const char *str, const char *sub)
28 28
29char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl) 29char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl)
30{ 30{
31 char *buf, *dst, *end; 31 char *buf, *dst;
32 const char *end;
32 size_t sub_len = strlen(sub); 33 size_t sub_len = strlen(sub);
33 size_t repl_len = strlen(repl); 34 size_t repl_len = strlen(repl);
34 35
diff --git a/libbb/strrstr.c b/libbb/strrstr.c
index a173b034f..bea5d1773 100644
--- a/libbb/strrstr.c
+++ b/libbb/strrstr.c
@@ -19,10 +19,10 @@ char* FAST_FUNC strrstr(const char *haystack, const char *needle)
19 if (!needle[0]) 19 if (!needle[0])
20 return (char*)haystack + strlen(haystack); 20 return (char*)haystack + strlen(haystack);
21 while (1) { 21 while (1) {
22 char *p = strstr(haystack, needle); 22 const char *p = strstr(haystack, needle);
23 if (!p) 23 if (!p)
24 return r; 24 return r;
25 r = p; 25 r = (char *)p;
26 haystack = p + 1; 26 haystack = p + 1;
27 } 27 }
28} 28}
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index 65b1cb8de..c0ed94336 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -115,29 +115,8 @@ void FAST_FUNC xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen)
115 } 115 }
116} 116}
117 117
118/* Return port number for a service.
119 * If "port" is a number use it as the port.
120 * If "port" is a name it is looked up in /etc/services.
121 * if NULL, return default_port
122 */
123unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned port_nr)
124{
125 if (port) {
126 port_nr = bb_strtou(port, NULL, 10);
127 if (errno || port_nr > 65535) {
128 struct servent *tserv = getservbyname(port, protocol);
129 if (!tserv)
130 bb_error_msg_and_die("bad port '%s'", port);
131 port_nr = ntohs(tserv->s_port);
132 }
133 }
134 return (uint16_t)port_nr;
135}
136
137
138/* "New" networking API */ 118/* "New" networking API */
139 119
140
141int FAST_FUNC get_nport(const struct sockaddr *sa) 120int FAST_FUNC get_nport(const struct sockaddr *sa)
142{ 121{
143#if ENABLE_FEATURE_IPV6 122#if ENABLE_FEATURE_IPV6
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index f4d1b913a..3eb30fd94 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -381,7 +381,7 @@ void FAST_FUNC bb_unsetenv(const char *var)
381 char onstack[128 - 16]; /* smaller stack setup code on x86 */ 381 char onstack[128 - 16]; /* smaller stack setup code on x86 */
382 char *tp; 382 char *tp;
383 383
384 tp = strchr(var, '='); 384 tp = (char*)strchr(var, '=');
385 if (tp) { 385 if (tp) {
386 /* In case var was putenv'ed, we can't replace '=' 386 /* In case var was putenv'ed, we can't replace '='
387 * with NUL and unsetenv(var) - it won't work, 387 * with NUL and unsetenv(var) - it won't work,
diff --git a/libpwdgrp/uidgid_get.c b/libpwdgrp/uidgid_get.c
index d76eb8298..2aa444416 100644
--- a/libpwdgrp/uidgid_get.c
+++ b/libpwdgrp/uidgid_get.c
@@ -32,7 +32,8 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug)
32{ 32{
33 struct passwd *pwd; 33 struct passwd *pwd;
34 struct group *gr; 34 struct group *gr;
35 char *user, *group; 35 char *user;
36 const char *group;
36 unsigned n; 37 unsigned n;
37 38
38 user = (char*)ug; 39 user = (char*)ug;
diff --git a/loginutils/getty.c b/loginutils/getty.c
index 67a08f487..232fa2b84 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -471,8 +471,10 @@ static char *get_logname(void)
471 do { 471 do {
472 /* Write issue file and prompt */ 472 /* Write issue file and prompt */
473#ifdef ISSUE 473#ifdef ISSUE
474 if (!(option_mask32 & F_NOISSUE)) 474 if (!(option_mask32 & F_NOISSUE)) {
475 puts("\r"); /* start a new line */
475 print_login_issue(G.issue, G.tty_name); 476 print_login_issue(G.issue, G.tty_name);
477 }
476#endif 478#endif
477 print_login_prompt(); 479 print_login_prompt();
478 480
diff --git a/miscutils/bc.c b/miscutils/bc.c
index 31485ae9c..b855c111d 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -5528,7 +5528,7 @@ static void xc_program_printString(const char *str)
5528 char c = *str++; 5528 char c = *str++;
5529 if (c == '\\') { 5529 if (c == '\\') {
5530 static const char esc[] ALIGN1 = "nabfrt""e\\"; 5530 static const char esc[] ALIGN1 = "nabfrt""e\\";
5531 char *n; 5531 const char *n;
5532 5532
5533 c = *str++; 5533 c = *str++;
5534 n = strchr(esc, c); // note: if c is NUL, n = \0 at end of esc 5534 n = strchr(esc, c); // note: if c is NUL, n = \0 at end of esc
diff --git a/miscutils/less.c b/miscutils/less.c
index 91403a252..6da1f88a7 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -205,9 +205,10 @@ struct globals {
205#if ENABLE_FEATURE_LESS_WINCH 205#if ENABLE_FEATURE_LESS_WINCH
206 unsigned winch_counter; 206 unsigned winch_counter;
207#endif 207#endif
208 ssize_t eof_error; /* eof if 0, error if < 0 */ 208 smallint ndelay_set;
209 ssize_t readpos; 209 ssize_t eof_error_ok; /* eof if 0, error if < 0, > 0 if last read did not indicate either */
210 ssize_t readeof; /* must be signed */ 210 size_t readpos;
211 size_t read_size;
211 const char **buffer; 212 const char **buffer;
212 const char **flines; 213 const char **flines;
213 const char *empty_line_marker; 214 const char *empty_line_marker;
@@ -255,9 +256,8 @@ struct globals {
255#define winch_counter (G.winch_counter ) 256#define winch_counter (G.winch_counter )
256/* This one is 100% not cached by compiler on read access */ 257/* This one is 100% not cached by compiler on read access */
257#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter) 258#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
258#define eof_error (G.eof_error )
259#define readpos (G.readpos ) 259#define readpos (G.readpos )
260#define readeof (G.readeof ) 260#define read_size (G.read_size )
261#define buffer (G.buffer ) 261#define buffer (G.buffer )
262#define flines (G.flines ) 262#define flines (G.flines )
263#define empty_line_marker (G.empty_line_marker ) 263#define empty_line_marker (G.empty_line_marker )
@@ -285,7 +285,7 @@ struct globals {
285 less_gets_pos = -1; \ 285 less_gets_pos = -1; \
286 empty_line_marker = "~"; \ 286 empty_line_marker = "~"; \
287 current_file = 1; \ 287 current_file = 1; \
288 eof_error = 1; \ 288 G.eof_error_ok = 1; \
289 terminated = 1; \ 289 terminated = 1; \
290 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \ 290 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \
291} while (0) 291} while (0)
@@ -466,22 +466,21 @@ static int at_end(void)
466 * that it was seen]) 466 * that it was seen])
467 * max_lineno - last line's number, this one doesn't increment 467 * max_lineno - last line's number, this one doesn't increment
468 * on line wrap, only on "real" new lines. 468 * on line wrap, only on "real" new lines.
469 * readbuf[0..readeof-1] - small preliminary buffer. 469 * readbuf[0..read_size-1] - small preliminary buffer.
470 * readbuf[readpos] - next character to add to current line. 470 * readbuf[readpos] - next character to add to current line.
471 * last_line_pos - screen line position of next char to be read 471 * last_line_pos - screen line position of next char to be read
472 * (takes into account tabs and backspaces) 472 * (takes into account tabs and backspaces)
473 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error 473 * G.eof_error_ok - < 0 error, == 0 EOF, > 0 not EOF/error
474 * 474 *
475 * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs, 475 * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs,
476 * "/search on very long input" and "reaching max line count" corner cases. 476 * "/search on very long input" and "reaching max line count" corner cases.
477 */ 477 */
478static void read_lines(void) 478static void read_lines(void)
479{ 479{
480 int ndelay_set, eagain, fdflags;
480 char *current_line, *p; 481 char *current_line, *p;
481 int w = width; 482 int w = width;
482 char last_terminated = terminated; 483 char last_terminated = terminated;
483 time_t last_time = 0;
484 int retry_EAGAIN = 2;
485#if ENABLE_FEATURE_LESS_REGEXP 484#if ENABLE_FEATURE_LESS_REGEXP
486 unsigned old_max_fline = max_fline; 485 unsigned old_max_fline = max_fline;
487#endif 486#endif
@@ -507,36 +506,43 @@ static void read_lines(void)
507 last_line_pos = 0; 506 last_line_pos = 0;
508 } 507 }
509 508
509 // Consider these cases:
510 // "less FILE": can set O_NONBLOCK on open.
511 // "true | less": can't.
512 // " { less; cat; } <FILE": can't. And must not confuse cat.
513 ndelay_set = -1; // "don't know whether stdin is nonblocking"
514 eagain = 0;
515
510 while (1) { /* read lines until we reach cur_fline or wanted_match */ 516 while (1) { /* read lines until we reach cur_fline or wanted_match */
511 *p = '\0'; 517 *p = '\0';
512 terminated = 0; 518 terminated = 0;
513 while (1) { /* read chars until we have a line */ 519 while (1) { /* read chars until we have a line */
514 char c; 520 char c;
515 /* if no unprocessed chars left, eat more */ 521 /* if no unprocessed chars left, eat more */
516 if (readpos >= readeof) { 522 if (readpos >= read_size) {
517 int flags = ndelay_on(0); 523 // Read stdin, temporarily make it nonblocking (if it's not already)
518 524 if (ndelay_set < 0) {
519 while (1) { 525 ndelay_set = G.ndelay_set; // can be only 0 or 1
520 time_t t; 526 if (ndelay_set == 0) {
521 527 fdflags = ndelay_on(STDIN_FILENO);
522 errno = 0; 528 if (fdflags & O_NONBLOCK)
523 eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE); 529 ndelay_set = 2; // it _was_ nonblocking, do NOT restore later
524 if (errno != EAGAIN) 530 } //else: G.ndelay_set=1: set nonblocking at open(FILE) time
525 break; 531 } //else: we were here already, stdin is already nonblocking
526 t = time(NULL); 532
527 if (t != last_time) { 533 // NB: we do NOT check last eof_error_ok before reading.
528 last_time = t; 534 // This has the effect that e.g. PageDown on a regular file
529 if (--retry_EAGAIN < 0) 535 // where we already reached EOF *will try reading anyway*,
530 break; 536 // if the file is a growing log file, less *will* show the new data.
531 }
532 sched_yield();
533 }
534 fcntl(0, F_SETFL, flags); /* ndelay_off(0) */
535 readpos = 0; 537 readpos = 0;
536 readeof = eof_error; 538 G.eof_error_ok = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
537 if (eof_error <= 0) 539 read_size = G.eof_error_ok;
540 if (G.eof_error_ok <= 0) {
541 if (G.eof_error_ok < 0 && errno == EAGAIN)
542 eagain = 1;
543 read_size = 0; // -1 would be seen as UINT_MAX, prevent
538 goto reached_eof; 544 goto reached_eof;
539 retry_EAGAIN = 1; 545 }
540 } 546 }
541 c = readbuf[readpos]; 547 c = readbuf[readpos];
542 /* backspace? [needed for manpages] */ 548 /* backspace? [needed for manpages] */
@@ -619,7 +625,7 @@ static void read_lines(void)
619 max_lineno++; 625 max_lineno++;
620 626
621 if (max_fline >= MAXLINES) { 627 if (max_fline >= MAXLINES) {
622 eof_error = 0; /* Pretend we saw EOF */ 628 G.eof_error_ok = 0; /* Pretend we saw EOF */
623 break; 629 break;
624 } 630 }
625 if (!at_end()) { 631 if (!at_end()) {
@@ -634,7 +640,11 @@ static void read_lines(void)
634 break; 640 break;
635#endif 641#endif
636 } 642 }
637 if (eof_error <= 0) { 643 if (G.eof_error_ok <= 0) { /* EOF or error? */
644 if (eagain) {
645 G.eof_error_ok = 1; // "neither EOF nor error: stdin not ready"
646 //^^^ needed to make main loop's poll() check stdin
647 }
638 break; 648 break;
639 } 649 }
640 max_fline++; 650 max_fline++;
@@ -643,15 +653,16 @@ static void read_lines(void)
643 last_line_pos = 0; 653 last_line_pos = 0;
644 } /* end of "read lines until we reach cur_fline" loop */ 654 } /* end of "read lines until we reach cur_fline" loop */
645 655
646 if (eof_error < 0) { 656 if (ndelay_set == 0) // stdin was not nonblocking, restore that
647 if (errno == EAGAIN) { 657 fcntl(STDIN_FILENO, F_SETFL, fdflags);
648 eof_error = 1; 658
649 } else { 659 // Will not be seen (overwritten immediately)
650 print_statusline(bb_msg_read_error); 660 //if (G.eof_error_ok < 0) { // error?
651 } 661 // print_statusline(bb_msg_read_error);
652 } 662 // bb_error_msg("G.eof_error_ok:%d", G.eof_error_ok); sleep(5);
663 //} else
653#if ENABLE_FEATURE_LESS_FLAGS 664#if ENABLE_FEATURE_LESS_FLAGS
654 else if (eof_error == 0) 665 if (G.eof_error_ok == 0) // EOF?
655 num_lines = max_lineno; 666 num_lines = max_lineno;
656#endif 667#endif
657 668
@@ -731,17 +742,17 @@ static void m_status_print(void)
731 clear_line(); 742 clear_line();
732 printf(HIGHLIGHT"%s", filename); 743 printf(HIGHLIGHT"%s", filename);
733 if (num_files > 1) 744 if (num_files > 1)
734 printf(" (file %i of %i)", current_file, num_files); 745 printf(" (file %d of %d)", current_file, num_files);
735 746
736 first = safe_lineno(cur_fline); 747 first = safe_lineno(cur_fline);
737 last = (option_mask32 & FLAG_S) 748 last = (option_mask32 & FLAG_S)
738 ? MIN(first + max_displayed_line, max_lineno) 749 ? MIN(first + max_displayed_line, max_lineno)
739 : safe_lineno(cur_fline + max_displayed_line); 750 : safe_lineno(cur_fline + max_displayed_line);
740 printf(" lines %i-%i", first, last); 751 printf(" lines %d-%d", first, last);
741 752
742 update_num_lines(); 753 update_num_lines();
743 if (num_lines >= 0) 754 if (num_lines >= 0)
744 printf("/%i", num_lines); 755 printf("/%d", num_lines);
745 756
746 if (at_end()) { 757 if (at_end()) {
747 printf(" (END)"); 758 printf(" (END)");
@@ -749,8 +760,11 @@ static void m_status_print(void)
749 printf(" - next: %s", files[current_file]); 760 printf(" - next: %s", files[current_file]);
750 } else if (num_lines > 0) { 761 } else if (num_lines > 0) {
751 percent = (100 * last + num_lines/2) / num_lines; 762 percent = (100 * last + num_lines/2) / num_lines;
752 printf(" %i%%", percent <= 100 ? percent : 100); 763 printf(" %d%%", percent <= 100 ? percent : 100);
753 } 764 }
765 if (G.eof_error_ok < 0)
766 // Reproducer: strace -oLOG -e fault=read:error=EIO:when=2 less FILE
767 printf(" %s", bb_msg_read_error);
754 printf(NORMAL); 768 printf(NORMAL);
755} 769}
756#endif 770#endif
@@ -780,6 +794,9 @@ static void status_print(void)
780 p = "(END)"; 794 p = "(END)";
781 if (!cur_fline) 795 if (!cur_fline)
782 p = filename; 796 p = filename;
797 if (G.eof_error_ok < 0)
798 // Reproducer: strace -oLOG -e fault=read:error=EIO:when=2 less FILE
799 p = bb_msg_read_error;
783 if (num_files > 1) { 800 if (num_files > 1) {
784 printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, 801 printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
785 p, current_file, num_files); 802 p, current_file, num_files);
@@ -944,9 +961,9 @@ static void buffer_print(void)
944 print_ascii(buffer[i]); 961 print_ascii(buffer[i]);
945 } 962 }
946 if ((option_mask32 & (FLAG_E|FLAG_F)) 963 if ((option_mask32 & (FLAG_E|FLAG_F))
947 && eof_error <= 0 964 && G.eof_error_ok <= 0
948 ) { 965 ) {
949 i = option_mask32 & FLAG_F ? 0 : cur_fline; 966 i = (option_mask32 & FLAG_F) ? 0 : cur_fline;
950 if (max_fline - i <= max_displayed_line) 967 if (max_fline - i <= max_displayed_line)
951 less_exit(); 968 less_exit();
952 } 969 }
@@ -999,7 +1016,7 @@ static void goto_lineno(int target)
999 while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline) 1016 while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline)
1000 ++cur_fline; 1017 ++cur_fline;
1001 /* target not reached but more input is available */ 1018 /* target not reached but more input is available */
1002 if (LINENO(flines[cur_fline]) != target && eof_error > 0) { 1019 if (LINENO(flines[cur_fline]) != target && G.eof_error_ok > 0) {
1003 read_lines(); 1020 read_lines();
1004 goto retry; 1021 goto retry;
1005 } 1022 }
@@ -1082,8 +1099,11 @@ static void buffer_lineno(int lineno)
1082 1099
1083static void open_file_and_read_lines(void) 1100static void open_file_and_read_lines(void)
1084{ 1101{
1102 G.ndelay_set = 0;
1085 if (filename) { 1103 if (filename) {
1086 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO); 1104 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
1105 ndelay_on(STDIN_FILENO);
1106 G.ndelay_set = 1;
1087#if ENABLE_FEATURE_LESS_FLAGS 1107#if ENABLE_FEATURE_LESS_FLAGS
1088 num_lines = REOPEN_AND_COUNT; 1108 num_lines = REOPEN_AND_COUNT;
1089#endif 1109#endif
@@ -1096,7 +1116,7 @@ static void open_file_and_read_lines(void)
1096#endif 1116#endif
1097 } 1117 }
1098 readpos = 0; 1118 readpos = 0;
1099 readeof = 0; 1119 read_size = 0;
1100 last_line_pos = 0; 1120 last_line_pos = 0;
1101 terminated = 1; 1121 terminated = 1;
1102 read_lines(); 1122 read_lines();
@@ -1128,13 +1148,17 @@ static void reinitialize(void)
1128 buffer_fill_and_print(); 1148 buffer_fill_and_print();
1129} 1149}
1130 1150
1151/* Poll stdin and keyboard.
1152 * If stdin has more data, redraw and repeat.
1153 * Return keycode when a key is pressed.
1154 */
1131#if ENABLE_PLATFORM_MINGW32 1155#if ENABLE_PLATFORM_MINGW32
1132static int64_t unix_getch_nowait(void) 1156static int64_t unix_getch_nowait(void)
1133#else 1157#else
1134static int64_t getch_nowait(void) 1158static int64_t getch_nowait(void)
1135#endif 1159#endif
1136{ 1160{
1137 int rd; 1161 int dont_poll_stdin;
1138 int64_t key64; 1162 int64_t key64;
1139 struct pollfd pfd[2]; 1163 struct pollfd pfd[2];
1140 1164
@@ -1150,11 +1174,11 @@ static int64_t getch_nowait(void)
1150 * Even if select/poll says that input is available, read CAN block 1174 * Even if select/poll says that input is available, read CAN block
1151 * (switch fd into O_NONBLOCK'ed mode to avoid it) 1175 * (switch fd into O_NONBLOCK'ed mode to avoid it)
1152 */ 1176 */
1153 rd = 1; 1177 dont_poll_stdin = 1;
1154 /* Are we interested in stdin? */ 1178 /* Are we interested in stdin? */
1155 if (at_end()) { 1179 if (at_end()) {
1156 if (eof_error > 0) /* did NOT reach eof yet */ 1180 if (G.eof_error_ok > 0) /* did NOT reach EOF/error yet */
1157 rd = 0; /* yes, we are interested in stdin */ 1181 dont_poll_stdin = 0; /* yes, we are interested in stdin */
1158 } 1182 }
1159 /* Position cursor if line input is done */ 1183 /* Position cursor if line input is done */
1160 if (less_gets_pos >= 0) 1184 if (less_gets_pos >= 0)
@@ -1166,21 +1190,24 @@ static int64_t getch_nowait(void)
1166 while (1) { 1190 while (1) {
1167 int r; 1191 int r;
1168 /* NB: SIGWINCH interrupts poll() */ 1192 /* NB: SIGWINCH interrupts poll() */
1169 r = poll(pfd + rd, 2 - rd, -1); 1193 r = poll(pfd + dont_poll_stdin, 2 - dont_poll_stdin, -1);
1170 if (/*r < 0 && errno == EINTR &&*/ winch_counter) 1194 if (/*r < 0 && errno == EINTR &&*/ winch_counter != 0)
1171 return '\\'; /* anything which has no defined function */ 1195 return '\\'; /* anything which has no defined function */
1172 if (r) break; 1196 if (r) break;
1173 } 1197 }
1174#else 1198#else
1175 safe_poll(pfd + rd, 2 - rd, -1); 1199 safe_poll(pfd + dont_poll_stdin, 2 - dont_poll_stdin, -1);
1176#endif 1200#endif
1177 } 1201 }
1178 1202
1203 if (pfd[1].revents == 0)
1204 goto no_kbd_input;
1179 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key() 1205 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key()
1180 * would not block even if there is no input available */ 1206 * would not block even if there is no input available */
1181 key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2); 1207 key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2);
1182 if ((int)key64 == -1) { 1208 if ((int)key64 == -1) {
1183 if (errno == EAGAIN) { 1209 if (errno == EAGAIN) {
1210 no_kbd_input:
1184 /* No keyboard input available. Since poll() did return, 1211 /* No keyboard input available. Since poll() did return,
1185 * we should have input on stdin */ 1212 * we should have input on stdin */
1186 read_lines(); 1213 read_lines();
@@ -1408,7 +1435,7 @@ static void goto_match(int match)
1408 if (match < 0) 1435 if (match < 0)
1409 match = 0; 1436 match = 0;
1410 /* Try to find next match if eof isn't reached yet */ 1437 /* Try to find next match if eof isn't reached yet */
1411 if (match >= num_matches && eof_error > 0) { 1438 if (match >= num_matches && G.eof_error_ok > 0) {
1412 wanted_match = match; /* "I want to read until I see N'th match" */ 1439 wanted_match = match; /* "I want to read until I see N'th match" */
1413 read_lines(); 1440 read_lines();
1414 } 1441 }
@@ -2014,7 +2041,7 @@ int less_main(int argc, char **argv)
2014 int64_t keypress; 2041 int64_t keypress;
2015 2042
2016#if ENABLE_FEATURE_LESS_WINCH 2043#if ENABLE_FEATURE_LESS_WINCH
2017 while (WINCH_COUNTER) { 2044 while (WINCH_COUNTER != 0) {
2018 again: 2045 again:
2019 winch_counter--; 2046 winch_counter--;
2020 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line); 2047 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
diff --git a/networking/httpd.c b/networking/httpd.c
index 02507b8f0..ddb346f18 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -2006,7 +2006,7 @@ static void send_cgi_and_exit(
2006 */ 2006 */
2007static NOINLINE void send_file_and_exit(const char *url, int what) 2007static NOINLINE void send_file_and_exit(const char *url, int what)
2008{ 2008{
2009 char *suffix; 2009 const char *suffix;
2010 int fd; 2010 int fd;
2011 ssize_t count; 2011 ssize_t count;
2012 2012
diff --git a/networking/ifconfig.c b/networking/ifconfig.c
index 9ee232a66..32638e2a3 100644
--- a/networking/ifconfig.c
+++ b/networking/ifconfig.c
@@ -336,7 +336,7 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
336#endif 336#endif
337 char *p; 337 char *p;
338 /*char host[128];*/ 338 /*char host[128];*/
339 const char *host = NULL; /* make gcc happy */ 339 char *host = NULL; /* make gcc happy */
340 IF_FEATURE_IFCONFIG_STATUS(char *show_all_param;) 340 IF_FEATURE_IFCONFIG_STATUS(char *show_all_param;)
341 341
342 did_flags = 0; 342 did_flags = 0;
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index bc2dca506..6832ee0d4 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -363,7 +363,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
363 break; 363 break;
364 case '%': 364 case '%':
365 { 365 {
366 char *nextpercent; 366 const char *nextpercent;
367 char *varvalue; 367 char *varvalue;
368 368
369 command++; 369 command++;
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
diff --git a/networking/nbd-client.c b/networking/nbd-client.c
index 556fa8c97..4fda66125 100644
--- a/networking/nbd-client.c
+++ b/networking/nbd-client.c
@@ -260,7 +260,7 @@ int nbdclient_main(int argc, char **argv)
260 // needs some other process to sit in ioctl(nbd, NBD_DO_IT). 260 // needs some other process to sit in ioctl(nbd, NBD_DO_IT).
261 if (fork() == 0) { 261 if (fork() == 0) {
262 /* child */ 262 /* child */
263 char *s = strrchr(device, '/'); 263 const char *s = strrchr(device, '/');
264 sprintf(data, "/sys/block/%.32s/pid", s ? s + 1 : device); 264 sprintf(data, "/sys/block/%.32s/pid", s ? s + 1 : device);
265 // Is it up yet? 265 // Is it up yet?
266 for (;;) { 266 for (;;) {
diff --git a/networking/netstat.c b/networking/netstat.c
index d7afa8fdd..d31928957 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -176,6 +176,7 @@ struct globals {
176 smallint prg_cache_loaded; 176 smallint prg_cache_loaded;
177 struct prg_node *prg_hash[PRG_HASH_SIZE]; 177 struct prg_node *prg_hash[PRG_HASH_SIZE];
178#endif 178#endif
179 char *p_etc_services;
179#if ENABLE_FEATURE_NETSTAT_PRG 180#if ENABLE_FEATURE_NETSTAT_PRG
180 const char *progname_banner; 181 const char *progname_banner;
181#endif 182#endif
@@ -378,15 +379,15 @@ static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr)
378 379
379static const char *get_sname(int port, const char *proto, int numeric) 380static const char *get_sname(int port, const char *proto, int numeric)
380{ 381{
381 if (!port) 382 if (port == 0)
382 return "*"; 383 return "*";
383 if (!numeric) { 384 if (!numeric) {
384 struct servent *se = getservbyport(port, proto); 385 const char *se = bb_get_servname_by_port(&G.p_etc_services, port, proto);
385 if (se) 386 if (se)
386 return se->s_name; 387 return se;
387 } 388 }
388 /* hummm, we may return static buffer here!! */ 389 /* hummm, we may return static buffer here!! */
389 return itoa(ntohs(port)); 390 return itoa(port);
390} 391}
391 392
392static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric) 393static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
@@ -402,7 +403,7 @@ static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int
402 if (!host) 403 if (!host)
403 host = xmalloc_sockaddr2dotted_noport(addr); 404 host = xmalloc_sockaddr2dotted_noport(addr);
404 405
405 xasprintf_inplace(host, "%s:%s", host, get_sname(htons(port), proto, numeric)); 406 xasprintf_inplace(host, "%s:%s", host, get_sname(port, proto, numeric));
406 return host; 407 return host;
407} 408}
408 409
diff --git a/networking/pscan.c b/networking/pscan.c
index 13785deca..454dba3fb 100644
--- a/networking/pscan.c
+++ b/networking/pscan.c
@@ -37,13 +37,13 @@
37#define DERR(...) ((void)0) 37#define DERR(...) ((void)0)
38#endif 38#endif
39 39
40static const char *port_name(unsigned port) 40static const char *port_name(char **p_etc_services, unsigned port)
41{ 41{
42 struct servent *server; 42 char *server;
43 43
44 server = getservbyport(htons(port), NULL); 44 server = bb_get_servname_by_port(p_etc_services, port, NULL);
45 if (server) 45 if (server)
46 return server->s_name; 46 return server;
47 return "unknown"; 47 return "unknown";
48} 48}
49 49
@@ -62,6 +62,7 @@ int pscan_main(int argc UNUSED_PARAM, char **argv)
62 * Rule of thumb: with min_rtt of N msec, scanning 1000 ports 62 * Rule of thumb: with min_rtt of N msec, scanning 1000 ports
63 * will take N seconds at absolute minimum */ 63 * will take N seconds at absolute minimum */
64 const char *opt_min_rtt = "5"; /* -T: default min rtt in msec */ 64 const char *opt_min_rtt = "5"; /* -T: default min rtt in msec */
65 char *p_etc_services = NULL;
65 const char *result_str; 66 const char *result_str;
66 len_and_sockaddr *lsap; 67 len_and_sockaddr *lsap;
67 int s; 68 int s;
@@ -152,7 +153,8 @@ int pscan_main(int argc UNUSED_PARAM, char **argv)
152 DMSG("out of loop @%u", diff); 153 DMSG("out of loop @%u", diff);
153 if (result_str) 154 if (result_str)
154 printf("%5u" "\t" "tcp" "\t" "%s" "\t" "%s" "\n", 155 printf("%5u" "\t" "tcp" "\t" "%s" "\t" "%s" "\n",
155 port, result_str, port_name(port)); 156 port, result_str, port_name(&p_etc_services, port)
157 );
156 158
157 /* Estimate new rtt - we don't want to wait entire timeout 159 /* Estimate new rtt - we don't want to wait entire timeout
158 * for each port. *4 allows for rise in net delay. 160 * for each port. *4 allows for rise in net delay.
diff --git a/networking/route.c b/networking/route.c
index 6e2d30cfd..9d9b72416 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -179,7 +179,7 @@ static NOINLINE void INET_setroute(int action, char **args)
179 memset(rt, 0, sizeof(*rt)); 179 memset(rt, 0, sizeof(*rt));
180 180
181 { 181 {
182 const char *target = *args++; 182 char *target = *args++;
183 char *prefix; 183 char *prefix;
184 184
185 /* recognize x.x.x.x/mask format. */ 185 /* recognize x.x.x.x/mask format. */
@@ -353,25 +353,25 @@ static NOINLINE void INET6_setroute(int action, char **args)
353 int prefix_len, skfd; 353 int prefix_len, skfd;
354 const char *devname; 354 const char *devname;
355 355
356 /* We know args isn't NULL from the check in route_main. */ 356 /* We know args isn't NULL from the check in route_main. */
357 const char *target = *args++; 357 char *target = *args++;
358 358
359 if (strcmp(target, "default") == 0) { 359 if (strcmp(target, "default") == 0) {
360 prefix_len = 0; 360 prefix_len = 0;
361 memset(&sa6, 0, sizeof(sa6)); 361 memset(&sa6, 0, sizeof(sa6));
362 } else {
363 char *cp;
364 cp = strchr(target, '/'); /* Yes... const to non is ok. */
365 if (cp) {
366 *cp = '\0';
367 prefix_len = xatoul_range(cp + 1, 0, 128);
362 } else { 368 } else {
363 char *cp; 369 prefix_len = 128;
364 cp = strchr(target, '/'); /* Yes... const to non is ok. */
365 if (cp) {
366 *cp = '\0';
367 prefix_len = xatoul_range(cp + 1, 0, 128);
368 } else {
369 prefix_len = 128;
370 }
371 if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
372 bb_error_msg_and_die("resolving %s", target);
373 }
374 } 370 }
371 if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
372 bb_error_msg_and_die("resolving %s", target);
373 }
374 }
375 375
376 /* Clean out the RTREQ structure. */ 376 /* Clean out the RTREQ structure. */
377 memset(&rt, 0, sizeof(rt)); 377 memset(&rt, 0, sizeof(rt));
diff --git a/networking/ssl_client.c b/networking/ssl_client.c
index 757745896..4d021a4ec 100644
--- a/networking/ssl_client.c
+++ b/networking/ssl_client.c
@@ -16,7 +16,7 @@
16 16
17//usage:#define ssl_client_trivial_usage 17//usage:#define ssl_client_trivial_usage
18//usage: IF_NOT_PLATFORM_MINGW32( 18//usage: IF_NOT_PLATFORM_MINGW32(
19//usage: "[-e] -s FD [-r FD] [-n SNI]" 19//usage: "[-n SNI] { -s FD [-r FD] | HOST | -e PROG ARGS }"
20//usage: ) 20//usage: )
21//usage: IF_PLATFORM_MINGW32( 21//usage: IF_PLATFORM_MINGW32(
22//usage: "[-e] -h handle [-n SNI]" 22//usage: "[-e] -h handle [-n SNI]"
@@ -28,51 +28,104 @@
28int ssl_client_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 28int ssl_client_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
29int ssl_client_main(int argc UNUSED_PARAM, char **argv) 29int ssl_client_main(int argc UNUSED_PARAM, char **argv)
30{ 30{
31 int exit_if_stdin_closed;
31 tls_state_t *tls; 32 tls_state_t *tls;
32 const char *sni = NULL; 33 const char *sni = NULL;
33 int opt; 34 int opt;
34#if ENABLE_PLATFORM_MINGW32 35#if ENABLE_PLATFORM_MINGW32
35 char *hstr = NULL; 36 char *hstr = NULL;
36 HANDLE h; 37 HANDLE h;
38 enum {
39 /* wrong name so exit_if_stdin_closed is set later */
40 OPT_s = (1 << 0),
41 OPT_h = (1 << 1),
42 OPT_n = (1 << 2),
43 };
44#else
45 enum {
46 OPT_s = (1 << 0),
47 OPT_r = (1 << 1),
48 OPT_n = (1 << 2),
49 OPT_e = (1 << 3),
50 };
37#endif 51#endif
38 52
39 // INIT_G(); 53 // INIT_G();
40
41 tls = new_tls_state(); 54 tls = new_tls_state();
42#if !ENABLE_PLATFORM_MINGW32 55#if ENABLE_PLATFORM_MINGW32
43 opt = getopt32(argv, "es:+r:+n:", &tls->ofd, &tls->ifd, &sni);
44 if (!(opt & (1<<2))) {
45 /* -r N defaults to -s N */
46 tls->ifd = tls->ofd;
47 }
48#else
49 opt = getopt32(argv, "eh:n:", &hstr, &sni); 56 opt = getopt32(argv, "eh:n:", &hstr, &sni);
50#endif
51 57
52 if (!(opt & (3<<1))) { 58 if (!hstr || sscanf(hstr, "%p", &h) != 1)
53 if (!argv[1]) 59 bb_error_msg_and_die("invalid handle");
60 init_winsock();
61 tls->ifd = tls->ofd = _open_osfhandle((intptr_t)h, _O_RDWR|_O_BINARY);
62#else
63 /* "+": stop on first non-option */
64 opt = getopt32(argv, "^+" "s:+r:+n:e" "\0"
65 "e--s:e--r:s--e:r--e", &tls->ofd, &tls->ifd, &sni
66 );
67 argv += optind;
68
69 if (opt & OPT_e) {
70 /* -e PROG: run PROG and talk TLS to its stdin/stdout */
71 // Talk to local HTTP server behind local TLS server:
72 // printf "GET / HTTP/1.1\r\n\r\n" | ssl_client -e ssl_server -d PRIVKEY.der -e httpd -i
73 struct fd_pair to_prog;
74 struct fd_pair from_prog;
75
76 pid_t pid;
77
78 if (!argv[0])
54 bb_show_usage(); 79 bb_show_usage();
55 /* Undocumented debug feature: without -s and -r, takes HOST arg and connects to it */ 80
56 // 81 xpiped_pair(to_prog);
82 xpiped_pair(from_prog);
83
84 pid = xvfork();
85 if (pid == 0) {
86 /* Child: run the program */
87
88 /* NB: close _first_, then move fds! */
89 close(to_prog.wr);
90 close(from_prog.rd);
91 xmove_fd(to_prog.rd, STDIN_FILENO);
92 xmove_fd(from_prog.wr, STDOUT_FILENO);
93
94 BB_EXECVP_or_die(argv);
95 }
96
97 /* Parent: close child ends of pipes */
98 close(to_prog.rd);
99 close(from_prog.wr);
100
101 tls->ofd = to_prog.wr; /* write to program's stdin */
102 tls->ifd = from_prog.rd; /* read from program's stdout */
103
104 } else if (!(opt & (OPT_s|OPT_r))) {
105 /* Not -e/-s/-r: connect to HOST */
57 // Talk to kernel.org: 106 // Talk to kernel.org:
58 // printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | busybox ssl_client kernel.org 107 // printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ssl_client kernel.org
108 if (!argv[0] || argv[1])
109 bb_show_usage();
59 if (!sni) 110 if (!sni)
60 sni = argv[1]; 111 sni = argv[0];
61 tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443); 112 tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[0], 443);
62 } 113
63#if ENABLE_PLATFORM_MINGW32 114 } else {
64 else { 115 /* -s FD [-r FD] */
65 if (!hstr || sscanf(hstr, "%p", &h) != 1) 116 if (!(opt & OPT_s) || argv[0])
66 bb_error_msg_and_die("invalid handle"); 117 bb_show_usage();
67 init_winsock(); 118 if (!(opt & OPT_r)) {
68 tls->ifd = tls->ofd = _open_osfhandle((intptr_t)h, _O_RDWR|_O_BINARY); 119 /* -r FD defaults to -s FD */
120 tls->ifd = tls->ofd;
121 }
69 } 122 }
70#endif 123#endif
71 124
72 tls_handshake(tls, sni); 125 tls_handshake(tls, sni);
73 126
74 BUILD_BUG_ON(TLSLOOP_EXIT_ON_LOCAL_EOF != 1); 127 exit_if_stdin_closed = (opt & OPT_s) ? TLSLOOP_EXIT_ON_LOCAL_EOF : 0;
75 tls_run_copy_loop(tls, /*flags*/ opt & 1); 128 tls_run_copy_loop(tls, /*flags*/ exit_if_stdin_closed);
76 129
77 return EXIT_SUCCESS; 130 return EXIT_SUCCESS;
78} 131}
diff --git a/networking/ssl_server.c b/networking/ssl_server.c
new file mode 100644
index 000000000..2a89dae6c
--- /dev/null
+++ b/networking/ssl_server.c
@@ -0,0 +1,121 @@
1/*
2 * Licensed under GPLv2, see file LICENSE in this source tree.
3 */
4//config:config SSL_SERVER
5//config: bool "ssl_server (test TLS server)"
6//config: default y
7//config: select TLS
8//config: help
9//config: inetd-style TLS server. Stdin/stdout are already connected
10//config: to an accepted TCP socket.
11
12//applet:IF_SSL_SERVER(APPLET(ssl_server, BB_DIR_USR_BIN, BB_SUID_DROP))
13
14//kbuild:lib-$(CONFIG_SSL_SERVER) += ssl_server.o
15
16//usage:#define ssl_server_trivial_usage
17//usage: "-f PRIVKEY_CERT.pem PROG ARGS"
18//usage:#define ssl_server_full_usage ""
19//usage: "Inetd-style TLS server\n"
20//usage: "\n -f PEMFILE HAProxy-style CRT file"
21/*
22# Generate RSA key and certificate
23openssl req -x509 -newkey rsa:4096 \
24 -keyout $HOSTNAME-rsa.key \
25 -out $HOSTNAME-rsa.crt \
26 -sha256 -days 9999 -nodes \
27 -subj /CN=$HOSTNAME \
28 -addext "subjectAltName=DNS:$HOSTNAME"
29# Generate ECDSA key and certificate
30openssl genpkey -algorithm EC \
31 -pkeyopt ec_paramgen_curve:prime256v1 \
32 -out $HOSTNAME-ecdsa.key
33fopenssl req -new -x509 \
34 -key $HOSTNAME-ecdsa.key \
35 -out $HOSTNAME-ecdsa.crt \
36 -sha256 -days 9999 \
37 -subj "/CN=$HOSTNAME" \
38 -addext "subjectAltName=DNS:$HOSTNAME"
39# Concatenate all these files into PRIVKEY_CERT.pem
40{ cat $HOSTNAME-rsa.key
41 cat $HOSTNAME-rsa.crt
42 cat $HOSTNAME-ecdsa.key
43 cat $HOSTNAME-ecdsa.crt
44} >PRIVKEY_CERT.pem
45*/
46#include "libbb.h"
47
48/* TLS server applet.
49 *
50 * To generate a test RSA certificate and key:
51 * openssl req -x509 -newkey rsa:2048 -days 9999 -nodes \
52 * -subj '/CN=localhost' \
53 * -out cert.pem -keyout privkey.pem
54 * Convert to DER format:
55 * openssl x509 -in cert.pem -outform DER -out cert.der
56 * openssl rsa -in privkey.pem -outform DER -out privkey.der
57 *
58 * Run the server:
59 * tcpsvd 127.0.0.1 4433 ssl_server -p privkey.der -c cert.der -e echo 'Hello world'
60 *
61 * Test with:
62 * openssl s_client -connect localhost:4433
63 */
64int ssl_server_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
65int ssl_server_main(int argc UNUSED_PARAM, char **argv)
66{
67 struct fd_pair to_prog;
68 struct fd_pair from_prog;
69 pid_t pid;
70 tls_state_t *tls;
71 const char *pem_file;
72 unsigned opt;
73
74 tls = new_tls_state();
75
76 /* "+": stop on first non-option */
77 opt = getopt32(argv, "+""vf:",
78 &pem_file
79 );
80 argv += optind;
81 if (!argv[0] || !(opt & 2))
82 bb_show_usage();
83
84 /* In inetd mode, stdin/stdout are the socket.
85 * But tls_run_copy_loop() needs *non-TLS* fds on STDIN and STDOUT.
86 * Shuffle them.
87 */
88 xdup2(STDIN_FILENO, 3);
89 xdup2(STDOUT_FILENO, 4);
90 tls->ifd = 3;
91 tls->ofd = 4;
92
93 /* This can abort on errors */
94 tls_handshake_as_server(tls, pem_file);
95
96 /* Run PROG, wrap its data in TLS and I/O to socket */
97 xpiped_pair(to_prog);
98 xpiped_pair(from_prog);
99 pid = xvfork();
100 if (pid == 0) {
101 /* Child: run the program */
102
103 /* NB: close _first_, then move fds! */
104 close(to_prog.wr);
105 close(from_prog.rd);
106 xmove_fd(to_prog.rd, STDIN_FILENO);
107 xmove_fd(from_prog.wr, STDOUT_FILENO);
108
109 BB_EXECVP_or_die(argv);
110 }
111 /* Parent: close child ends of pipes */
112 close(to_prog.rd);
113 close(from_prog.wr);
114
115 /* tls_run_copy_loop() needs non-TLS fds on STDIN and STDOUT */
116 xmove_fd(from_prog.rd, STDIN_FILENO);
117 xmove_fd(to_prog.wr, STDOUT_FILENO);
118 tls_run_copy_loop(tls, /*flags*/ TLSLOOP_EXIT_ON_LOCAL_EOF);
119
120 return EXIT_SUCCESS;
121}
diff --git a/networking/telnet.c b/networking/telnet.c
index 7a0253525..a0eadc91b 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -11,13 +11,11 @@
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 * 12 *
13 * HISTORY 13 * HISTORY
14 * Revision 3.1 1994/04/17 11:31:54 too 14 * 1994/04/17 Initial revision
15 * initial revision 15 * 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
16 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org> 16 * 2001/05/07 add ability to pass TTYPE to remote host by Jim McQuillan <jam@ltsp.org>
17 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan 17 * 2004/02/11 add ability to pass the USER variable to remote host by Fernando Silveira <swrh@gmx.net>
18 * <jam@ltsp.org> 18 * 2026/02/20 Almost total rewrite to use ioloop()
19 * Modified 2004/02/11 to add ability to pass the USER variable to remote host
20 * by Fernando Silveira <swrh@gmx.net>
21 */ 19 */
22//config:config TELNET 20//config:config TELNET
23//config: bool "telnet (8.8 kb)" 21//config: bool "telnet (8.8 kb)"
@@ -54,20 +52,14 @@
54 52
55//kbuild:lib-$(CONFIG_TELNET) += telnet.o 53//kbuild:lib-$(CONFIG_TELNET) += telnet.o
56 54
57//usage:#if ENABLE_FEATURE_TELNET_AUTOLOGIN
58//usage:#define telnet_trivial_usage 55//usage:#define telnet_trivial_usage
59//usage: "[-a] [-l USER] HOST [PORT]" 56//usage: IF_FEATURE_TELNET_AUTOLOGIN("[-a] [-l USER] ")"HOST [PORT]"
60//usage:#define telnet_full_usage "\n\n"
61//usage: "Connect to telnet server\n"
62//usage: "\n -a Automatic login with $USER variable"
63//usage: "\n -l USER Automatic login as USER"
64//usage:
65//usage:#else
66//usage:#define telnet_trivial_usage
67//usage: "HOST [PORT]"
68//usage:#define telnet_full_usage "\n\n" 57//usage:#define telnet_full_usage "\n\n"
69//usage: "Connect to telnet server" 58//usage: "Connect to telnet server"
70//usage:#endif 59//usage: IF_FEATURE_TELNET_AUTOLOGIN("\n"
60//usage: "\n -a Automatic login with $USER envvar"
61//usage: "\n -l USER Automatic login as USER"
62//usage: )
71 63
72#include <arpa/telnet.h> 64#include <arpa/telnet.h>
73#include <netinet/in.h> 65#include <netinet/in.h>
@@ -80,285 +72,183 @@
80# define DONT 254 /* you are not to use option */ 72# define DONT 254 /* you are not to use option */
81# define DO 253 /* please, you use option */ 73# define DO 253 /* please, you use option */
82# define WONT 252 /* I won't use option */ 74# define WONT 252 /* I won't use option */
83# define WILL 251 /* I will use option */ 75# define WILL 251 /* I will use option (if I see your "IAC DO <opt>" confirmation */
84# define SB 250 /* interpret as subnegotiation */ 76# define SB 250 /* begin subnegotiation */
85# define SE 240 /* end sub negotiation */ 77# define SE 240 /* end subnegotiation */
86# define TELOPT_ECHO 1 /* echo */ 78# define TELOPT_ECHO 1 /* echo */
87# define TELOPT_SGA 3 /* suppress go ahead */ 79# define TELOPT_SGA 3 /* suppress go ahead */
88# define TELOPT_TTYPE 24 /* terminal type */ 80# define TELOPT_TTYPE 24 /* terminal type */
89# define TELOPT_NAWS 31 /* window size */ 81# define TELOPT_NAWS 31 /* window size */
90#endif 82#endif
91 83
92enum { 84#define SUPPORT_FOR_LOCAL_OPTS (0 \
93 DATABUFSIZE = 128, 85 || ENABLE_FEATURE_TELNET_WIDTH \
94 IACBUFSIZE = 128, 86 || ENABLE_FEATURE_TELNET_TTYPE \
87 || ENABLE_FEATURE_TELNET_AUTOLOGIN \
88 )
89
90// -v option
91#define VERBOSE 0
92// lots of unconditional debug
93#define DEBUG 0
95 94
96 CHM_TRY = 0, 95#if DEBUG
97 CHM_ON = 1, 96# define dbg(...) bb_error_msg(__VA_ARGS__)
98 CHM_OFF = 2, 97static char *bin_to_hex(const void *hash_value, unsigned hash_length)
98{
99 /* xzalloc zero-terminates */
100 char *hex_value = xzalloc((hash_length * 2) + 1);
101 bin2hex(hex_value, (char*)hash_value, hash_length);
102 return auto_string(hex_value);
103}
104#else
105# define dbg(...) ((void)0)
106#endif
99 107
100 UF_ECHO = 0x01, 108#if VERBOSE
101 UF_SGA = 0x02, 109# define log1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while (0)
110# define IF_VERBOSE(...) __VA_ARGS__
111#else
112# define log1(...) ((void)0)
113# define IF_VERBOSE(...) /* nothing */
114#endif
102 115
116enum {
103 TS_NORMAL = 0, 117 TS_NORMAL = 0,
104 TS_COPY = 1, 118 TS_IAC = 1,
105 TS_IAC = 2, 119 TS_OPT = 2,
106 TS_OPT = 3, 120 TS_SUB1 = 3,
107 TS_SUB1 = 4, 121 TS_SUB2 = 4,
108 TS_SUB2 = 5, 122 TS_CR = 5,
109 TS_CR = 6, 123
124 MAX_NAWS_SIZE = 13, // pathological 65535x65535 case needs full escaping
125
126 netfd = 3,
110}; 127};
111 128
112typedef unsigned char byte; 129typedef unsigned char byte;
113 130
114enum { netfd = 3 }; 131typedef struct stdin_to_net {
132 STRUCT_CONNECTION
133 int rdidx, wridx, size;
134 //byte buf[BUFSIZE];
135} stdin_to_net_t;
136typedef struct net_to_stdout {
137 STRUCT_CONNECTION
138 int rdidx, wridx, size;
139 byte input_state;
140 byte negotiation_verb;
141 //byte buf[BUFSIZE];
142} net_to_stdout_t;
143#if DEBUG
144static void set_input_state(net_to_stdout_t *conn, int new_state, int c)
145{
146 if (conn->input_state != new_state) {
147 conn->input_state = new_state;
148 dbg("input_state=%d at char:0x%02x '%c'", new_state, c,
149 c >= 32 && c < 127 ? c : '.');
150 }
151}
152# define SET_INPUT_STATE(conn, state, c) set_input_state(conn, state, c)
153#else
154# define SET_INPUT_STATE(conn, state, c) ((conn)->input_state = (state))
155#endif
115 156
116struct globals { 157struct globals {
117 int iaclen; /* could even use byte, but it's a loss on x86 */ 158 unsigned flags;
118 byte telstate; /* telnet negotiation state from network input */ 159// Set when server agreed to use NAWS:
119 byte telwish; /* DO, DONT, WILL, WONT */ 160#define FLAGS_NAWS_ON (1 << 0)
120 byte charmode; 161// SGA option seen and responded to, no longer look for it:
121 byte telflags; 162#define FLAGS_SGA_SEEN (1 << 1)
122 byte do_termios; 163// Seen telnet protocol from server and sent our WILLs:
123#if ENABLE_FEATURE_TELNET_TTYPE 164#define INITIAL_SENT (1 << 2)
124 char *ttype; 165#define DO_TERMIOS (1 << 3)
125#endif 166
126#if ENABLE_FEATURE_TELNET_AUTOLOGIN 167 byte word_aligned_bytes[2];
127 const char *autologin; 168#define changes_seen word_aligned_bytes[0]
128#endif 169#define CHANGED_ECHO (1 << 0)
129#if ENABLE_FEATURE_TELNET_WIDTH 170#define CHANGED_NAWS (1 << 1)
130 unsigned win_width, win_height; 171// These happen only once:
131#endif 172#define CHANGED_SGA (1 << 2)
132 /* same buffer used both for network and console read/write */ 173#define CHANGED_TTYPE (1 << 3)
133 char buf[DATABUFSIZE]; 174#define CHANGED_NEW_ENVIRON (1 << 4)
134 /* buffer to handle telnet negotiations */ 175// The second byte is changed async, by signal handler:
135 char iacbuf[IACBUFSIZE]; 176#define got_SIGWINCH word_aligned_bytes[1]
136 struct termios termios_def; 177#define G_changes_or_WINCH (*(uint16_t*)G.word_aligned_bytes)
137 struct termios termios_raw; 178// The "shared word" trick is unsafe on only-word-store arches such as DEC Alpha or SHARC,
179// but Alpha retired in 2004 and SHARC does not run linux (it's a DSP).
180
181 byte optstate_ECHO;
182// On program start:
183#define OPT_ECHO_UNKNOWN 0xff
184#define OPT_ECHO_OFF 0
185// We operate tty in rawmode only when echo ON (IOW: we saw server say that)
186#define OPT_ECHO_ON 1
187
188 byte echo_sga_response_size;
189
190 IF_VERBOSE( unsigned verbose;)
191 IF_FEATURE_TELNET_TTYPE( const char *ttype;)
192 IF_FEATURE_TELNET_AUTOLOGIN(const char *autologin;)
193 ioloop_state_t io;
194 stdin_to_net_t conn_stdin2net;
195 net_to_stdout_t conn_net2stdout;
196 struct termios termios_def;
197 struct termios termios_raw;
198// buf[] arrays in conn structs are conceptually cleaner, but they
199// make G.member offsets larger -> larger code
200#define BUF_TTY2NET ((byte*)bb_common_bufsiz1)
201#define BUF_NET2TTY G.buf2
202#define BUFSIZE 1024
203// Note: can't just increase BUFSIZE arbitrarily: common bufsiz1 is not guaranteed to be >1k!
204#define BUFMASK (BUFSIZE - 1)
205 byte buf2[BUFSIZE];
138} FIX_ALIASING; 206} FIX_ALIASING;
139#define G (*(struct globals*)bb_common_bufsiz1) 207#define G (*ptr_to_globals)
140#define INIT_G() do { \ 208#define INIT_G() do { \
141 setup_common_bufsiz(); \ 209 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
142 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
143} while (0) 210} while (0)
144 211
145 212static int remaining_free_bytes(int n)
146static void rawmode(void);
147static void cookmode(void);
148static void do_linemode(void);
149static void will_charmode(void);
150static void telopt(byte c);
151static void subneg(byte c);
152
153static void iac_flush(void)
154{ 213{
155 if (G.iaclen != 0) { 214 return BUFSIZE - n;
156 full_write(netfd, G.iacbuf, G.iaclen);
157 G.iaclen = 0;
158 }
159} 215}
160 216
161static void doexit(int ev) NORETURN; 217#if ENABLE_FEATURE_TELNET_WIDTH
162static void doexit(int ev) 218static void handle_SIGWINCH(int sig UNUSED_PARAM)
163{ 219{
164 cookmode(); 220 // Only if server said DO NAWS and seen our WILL NAWS:
165 exit(ev); 221 if ((G.flags & (FLAGS_NAWS_ON|INITIAL_SENT)) == (FLAGS_NAWS_ON|INITIAL_SENT))
222 G.got_SIGWINCH = 1;
166} 223}
224#endif
167 225
168static void con_escape(void) 226static void rawmode(void)
169{ 227{
170 char b; 228 if (G.flags & DO_TERMIOS)
171 229 tcsetattr(0, TCSADRAIN, &G.termios_raw);
172 if (bb_got_signal) /* came from line mode... go raw */
173 rawmode();
174
175 full_write1_str("\r\nConsole escape. Commands are:\r\n\n"
176 " l go to line mode\r\n"
177 " c go to character mode\r\n"
178 " z suspend telnet\r\n"
179 " e exit telnet\r\n");
180
181 if (read(STDIN_FILENO, &b, 1) <= 0)
182 doexit(EXIT_FAILURE);
183
184 switch (b) {
185 case 'l':
186 if (!bb_got_signal) {
187 do_linemode();
188 goto ret;
189 }
190 break;
191 case 'c':
192 if (bb_got_signal) {
193 will_charmode();
194 goto ret;
195 }
196 break;
197 case 'z':
198 cookmode();
199 kill(0, SIGTSTP);
200 rawmode();
201 break;
202 case 'e':
203 doexit(EXIT_SUCCESS);
204 }
205
206 full_write1_str("continuing...\r\n");
207
208 if (bb_got_signal)
209 cookmode();
210 ret:
211 bb_got_signal = 0;
212} 230}
213 231
214static void handle_net_output(int len) 232static void cookmode(void)
215{ 233{
216 byte outbuf[2 * DATABUFSIZE]; 234 if (G.flags & DO_TERMIOS)
217 byte *dst = outbuf; 235 tcsetattr(0, TCSADRAIN, &G.termios_def);
218 byte *src = (byte*)G.buf;
219 byte *end = src + len;
220
221 while (src < end) {
222 byte c = *src++;
223 if (c == 0x1d) {
224 con_escape();
225 return;
226 }
227 *dst = c;
228 if (c == IAC)
229 *++dst = c; /* IAC -> IAC IAC */
230 else
231 if (c == '\r' || c == '\n') {
232 /* Enter key sends '\r' in raw mode and '\n' in cooked one.
233 *
234 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
235 * Using CR LF instead of other allowed possibilities
236 * like CR NUL - easier to talk to HTTP/SMTP servers.
237 */
238 *dst = '\r'; /* Enter -> CR LF */
239 *++dst = '\n';
240 }
241#if 0
242/* putty's "special commands" mode does this: */
243/* Korenix 3005 switch needs at least the backspace tweak */
244 if (c == 0x08 || c == 0x7f) { /* ctrl+h || backspace */
245 *dst = IAC;
246 *++dst = EC;
247 }
248 if (c == 0x03) { /* ctrl+c */
249 *dst = IAC;
250 *++dst = IP;
251 }
252#endif
253 dst++;
254 }
255 if (dst - outbuf != 0)
256 full_write(netfd, outbuf, dst - outbuf);
257} 236}
258 237
259static void handle_net_input(int len) 238static void doexit(int ev) NORETURN;
239static void doexit(int ev)
260{ 240{
261 byte c; 241 cookmode();
262 int i; 242 exit(ev);
263 int cstart = 0;
264
265 i = 0;
266 //bb_error_msg("[%u,'%.*s']", G.telstate, len, G.buf);
267 if (G.telstate == TS_NORMAL) { /* most typical state */
268 while (i < len) {
269 c = G.buf[i];
270 i++;
271 if (c == IAC) /* unlikely */
272 goto got_IAC;
273 if (c != '\r') /* likely */
274 continue;
275 G.telstate = TS_CR;
276 cstart = i;
277 goto got_special;
278 }
279 full_write(STDOUT_FILENO, G.buf, len);
280 return;
281 got_IAC:
282 G.telstate = TS_IAC;
283 cstart = i - 1;
284 got_special: ;
285 }
286
287 for (; i < len; i++) {
288 c = G.buf[i];
289
290 switch (G.telstate) {
291 case TS_CR:
292 /* Prev char was CR. If cur one is NUL, ignore it.
293 * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling.
294 */
295 G.telstate = TS_COPY;
296 if (c == '\0')
297 break;
298 /* else: fall through - need to handle CR IAC ... properly */
299
300 case TS_COPY: /* Prev char was ordinary */
301 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */
302 if (c == IAC)
303 G.telstate = TS_IAC;
304 else {
305 G.buf[cstart++] = c;
306 if (c == '\r')
307 G.telstate = TS_CR;
308 }
309 break;
310
311 case TS_IAC: /* Prev char was IAC */
312 switch (c) {
313 case IAC: /* IAC IAC -> one IAC */
314 G.buf[cstart++] = c;
315 G.telstate = TS_COPY;
316 break;
317 case SB:
318 G.telstate = TS_SUB1;
319 break;
320 case DO:
321 case DONT:
322 case WILL:
323 case WONT:
324 G.telwish = c;
325 G.telstate = TS_OPT;
326 break;
327 /* DATA MARK must be added later */
328 default:
329 G.telstate = TS_COPY;
330 }
331 break;
332
333 case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */
334 telopt(c);
335 G.telstate = TS_COPY;
336 break;
337
338 case TS_SUB1: /* Subnegotiation */
339 case TS_SUB2: /* Subnegotiation */
340 subneg(c); /* can change G.telstate */
341 break;
342 }
343 }
344
345 /* We had some IACs, or CR */
346 iac_flush();
347 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
348 G.telstate = TS_NORMAL;
349 if (cstart != 0)
350 full_write(STDOUT_FILENO, G.buf, cstart);
351} 243}
352 244
353static void put_iac(int c) 245static void put_iac(int c)
354{ 246{
355 int iaclen = G.iaclen; 247 stdin_to_net_t *conn = &G.conn_stdin2net;
356 if (iaclen >= IACBUFSIZE) { 248 /* Write directly to stdin2net buffer */
357 iac_flush(); 249 BUF_TTY2NET[conn->rdidx] = c; /* "... & 0xff" is implicit */
358 iaclen = 0; 250 conn->rdidx = (conn->rdidx + 1) & BUFMASK;
359 } 251 conn->size++;
360 G.iacbuf[iaclen] = c; /* "... & 0xff" is implicit */
361 G.iaclen = iaclen + 1;
362} 252}
363 253
364static void put_iac2_msb_lsb(unsigned x_y) 254static void put_iac2_msb_lsb(unsigned x_y)
@@ -368,15 +258,23 @@ static void put_iac2_msb_lsb(unsigned x_y)
368} 258}
369#define put_iac2_x_y(x,y) put_iac2_msb_lsb(((x)<<8) + (y)) 259#define put_iac2_x_y(x,y) put_iac2_msb_lsb(((x)<<8) + (y))
370 260
371#if ENABLE_FEATURE_TELNET_WIDTH \ 261#if SUPPORT_FOR_LOCAL_OPTS
372 || ENABLE_FEATURE_TELNET_TTYPE \
373 || ENABLE_FEATURE_TELNET_AUTOLOGIN
374static void put_iac4_msb_lsb(unsigned x_y_z_t) 262static void put_iac4_msb_lsb(unsigned x_y_z_t)
375{ 263{
376 put_iac2_msb_lsb(x_y_z_t >> 16); 264 put_iac2_msb_lsb(x_y_z_t >> 16);
377 put_iac2_msb_lsb(x_y_z_t); /* "... & 0xffff" is implicit */ 265 put_iac2_msb_lsb(x_y_z_t); /* "... & 0xffff" is implicit */
378} 266}
379#define put_iac4_x_y_z_t(x,y,z,t) put_iac4_msb_lsb(((x)<<24) + ((y)<<16) + ((z)<<8) + (t)) 267#define put_iac4_x_y_z_t(x,y,z,t) \
268 put_iac4_msb_lsb(((x)<<24) + ((y)<<16) + ((z)<<8) + (t))
269
270/* Send a byte in subnegotiation, escaping IAC (0xFF) as IAC IAC */
271static void put_iac_byte_escaped(int c)
272{
273 c = (byte)c;
274 put_iac(c);
275 if (c == IAC)
276 put_iac(IAC);
277}
380#endif 278#endif
381 279
382static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c) 280static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c)
@@ -384,237 +282,656 @@ static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c)
384 put_iac(IAC); 282 put_iac(IAC);
385 put_iac2_msb_lsb(wwdd_and_c); 283 put_iac2_msb_lsb(wwdd_and_c);
386} 284}
387#define put_iac3_IAC_x_y(wwdd,c) put_iac3_IAC_x_y_merged(((wwdd)<<8) + (c)) 285#define put_iac3_IAC_x_y(wwdd,c) \
286 put_iac3_IAC_x_y_merged(((wwdd)<<8) + (c))
388 287
389#if ENABLE_FEATURE_TELNET_TTYPE 288#if ENABLE_FEATURE_TELNET_TTYPE
390static void put_iac_subopt(byte c, char *str) 289static void put_iac_subopt(byte c, const char *str)
391{ 290{
392 put_iac4_x_y_z_t(IAC, SB, c, 0); 291 put_iac4_x_y_z_t(IAC, SB, c, 0);
393 292
394 while (*str) 293 while (*str) {
395 put_iac(*str++); 294 put_iac_byte_escaped(*str);
295 str++;
296 }
396 297
397 put_iac2_x_y(IAC, SE); 298 put_iac2_x_y(IAC, SE);
398} 299}
399#endif 300#endif
400 301
401#if ENABLE_FEATURE_TELNET_AUTOLOGIN 302#if ENABLE_FEATURE_TELNET_AUTOLOGIN
402static void put_iac_subopt_autologin(void) 303static void put_iac_subopt_autologin(const char *p)
403{ 304{
404 const char *p;
405
406 put_iac4_x_y_z_t(IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS); 305 put_iac4_x_y_z_t(IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS);
407 put_iac4_x_y_z_t(NEW_ENV_VAR, 'U', 'S', 'E'); /* "USER" */ 306 put_iac4_x_y_z_t(NEW_ENV_VAR, 'U', 'S', 'E'); /* "USER" */
408 put_iac2_x_y('R', NEW_ENV_VALUE); 307 put_iac2_x_y('R', NEW_ENV_VALUE);
409 308
410 p = G.autologin;
411 while (*p) 309 while (*p)
412 put_iac(*p++); 310 put_iac_byte_escaped(*p++);
413 311
414 put_iac2_x_y(IAC, SE); 312 put_iac2_x_y(IAC, SE);
415} 313}
416#endif 314#endif
417 315
418#if ENABLE_FEATURE_TELNET_WIDTH 316#if ENABLE_FEATURE_TELNET_WIDTH
419static void put_iac_naws(byte c, int x, int y) 317static void put_iac_naws(void)
420{ 318{
421 put_iac3_IAC_x_y(SB, c); 319 unsigned width, height;
422 320
423 put_iac4_msb_lsb((x << 16) + y); 321 put_iac3_IAC_x_y(SB, TELOPT_NAWS);
322
323 /* Send width and height as 16-bit big-endian, escaping IAC bytes */
324 get_terminal_width_height(0, &width, &height);
325 log1("C:SB NAWS %dx%d", width, height);
326 put_iac_byte_escaped(width >> 8);
327 put_iac_byte_escaped(width);
328 put_iac_byte_escaped(height >> 8);
329 put_iac_byte_escaped(height);
424 330
425 put_iac2_x_y(IAC, SE); 331 put_iac2_x_y(IAC, SE);
426} 332}
427#endif 333#endif
428 334
429static void setConMode(void) 335// Telnet option handling strategy:
336//
337// We send nothing on startup on our own (think "telnet www.kernel.org 80").
338// As soon as we see any DO X or WILL X message known to us, we respond
339// to them: send DO/DONT ECHO, DO SGA if server talked about them, then send
340// WILL NAWS, WILL TTYPE, WILL NEW_ENVIRON
341// without waiting for server to advertise those options.
342// (Why? Some servers may decide to not advertise all 123 options they know).
343//
344// TELOPT_ECHO (1) - remote: server echoes our keystrokes back to us
345// - Server says: WILL ECHO or WONT ECHO
346// - If we don't see it, or see WONT: line mode (local terminal echoes), aka cooked mode
347// - When active: We're in character mode (server echoes), aka raw mode
348// - Response logic:
349// - Server says WILL ECHO: we send DO ECHO, enter raw mode
350// (this is the most usual case for non-ancient servers)
351// - Server says WONT ECHO: we send DONT ECHO, enter cooked mode
352// - We mirror server's changes, we do allow it to change during session
353// unlike one-shot options which are negotiated once during startup.
354// - If user changes mode from ^] menu, we send corresponding DO/DONT ECHO.
355//
356// TELOPT_SGA (3) - remote: "Suppress Go Ahead", full-duplex
357// - Server says: WILL SGA or WONT SGA
358// - Response logic: one-shot.
359// - We expect all sane servers to choose WILL SGA at startup.
360// - If that happens, we send DO SGA. After that, we never react to any
361// further SGA messages (we expect them to not repeat in practice)
362//
363// TELOPT_NAWS (31) - Window Size - local: we send our window size
364// - If server says: DO NAWS, it understands NAWS. We require this before
365// - sending SB NAWS ...
366// - We send window size during initial handshake and on window resize
367//
368// TELOPT_TTYPE (24) - Terminal Type - local: we send our terminal type
369// - Response logic: one-shot.
370// - If server says DO TTYPE: send "IAC SB TTYPE 0 '$TERM' IAC SE"
371//
372// TELOPT_NEW_ENVIRON (39) - Environment (Autologin) - local: we send our username
373// - Response logic: one-shot.
374// - If server says DO NEW_ENVIRON: send "IAC SB NEW_ENVIRON IS VAR 'USER' VALUE 'name' IAC SE"
375//
376// Unknown options:
377// Response: NONE - just ignore them
378// By protocol definition, an option is not in effect until BOTH sides agree.
379// We don't respond to unknown options - they simply won't be in effect.
380//
381// Testing at discworld.atuin.net:23 reveal servers often do NOT negotiate ECHO/SGA
382// in initial handshake while supporting NAWS and TTYPE:
383//telnet: S:DO 24 <--TTYPE
384//telnet: TTYPE:'xterm-256color' setting CHANGED_TTYPE
385//LPmud version : DW OS v1.02 on port 4242.
386//telnet: changed:8 flags:8
387//telnet: C:SB TTYPE 'xterm-256color'
388//telnet: S:DO 31 <--NAWS
389//telnet: S:WILL 86
390//telnet: S:DO 91
391//telnet: S:WILL 70
392//telnet: S:WILL 93
393//telnet: S:DO 39
394//telnet: S:WILL 201
395//......text.....
396//telnet: changed:2 flags:8
397//telnet: C:SB NAWS 226x53
398//...text....
399//telnet: S:SB 24 <--TTYPE
400//...text....
401//Setting your network terminal type to "xterm-256color".
402//> cols
403//Columns currently set to 79.
404//> rows
405//Rows currently set to 24.
406//^^^ server understood TTYPE but not NAWS. ?!
407static void handle_changes_in_options(stdin_to_net_t *conn)
430{ 408{
431 if (G.telflags & UF_ECHO) { 409 int count;
432 if (G.charmode == CHM_TRY) { 410
433 G.charmode = CHM_ON; 411 log1("changed:%x flags:%x", G.changes_seen, G.flags);
434 printf("\r\nEntering %s mode" 412
435 "\r\nEscape character is '^%c'.\r\n", "character", ']'); 413 count = remaining_free_bytes(conn->size);
436 rawmode(); 414 // As soon as we see any DO/DONT/WILL/WONT known to us,
415 // we assume it *is* a telnet server
416 // (we are not in "telnet www.kernel.org 80" scenario),
417 // and we respond to them, also expressing our own
418 // wishes: WILL NAWS etc. We send our WILLs once, all of them.
419 // We send actual SB with option contents after WILLs
420 // only if that option was requested.
421 // We repeatedly send only NAWS (when our window changes).
422 // Repeated DO requests are ignored.
423 if (G.changes_seen
424 && (count >= G.echo_sga_response_size)
425 ) {
426 if (G.changes_seen & CHANGED_ECHO) {
427 // Server said WILL/WONT ECHO - confirm every time
428 log1("C:%s ECHO", G.optstate_ECHO ? "DO" : "DONT");
429 put_iac3_IAC_x_y(G.optstate_ECHO ? DO : DONT, TELOPT_ECHO);
437 } 430 }
438 } else { 431 if (G.changes_seen & CHANGED_SGA) {
439 if (G.charmode != CHM_OFF) { 432 // Server said WILL SGA - confirm once
440 G.charmode = CHM_OFF; 433 log1("C:DO SGA");
441 printf("\r\nEntering %s mode" 434 put_iac3_IAC_x_y(DO, TELOPT_SGA);
442 "\r\nEscape character is '^%c'.\r\n", "line", 'C'); 435 G.flags |= FLAGS_SGA_SEEN; // remember we did it
443 cookmode(); 436 G.changes_seen -= CHANGED_SGA;
444 } 437 }
438 G.changes_seen &= ~(CHANGED_ECHO|CHANGED_SGA);
439
440 if (!(G.flags & INITIAL_SENT)) {
441 // From now on, we'll only do DO/DONT ECHO and maybe DO SGA
442 // in the "if (G.changes_seen)" block.
443 G.flags |= INITIAL_SENT;
444 G.echo_sga_response_size = (G.flags & FLAGS_SGA_SEEN) ? 3 : 6;
445
446 // Send initial IAC sequences for local options we want to advertise
447 // (there may be servers which send DO X only after we say WILL X)
448#if ENABLE_FEATURE_TELNET_WIDTH
449 log1("C:WILL %s", "NAWS");
450 put_iac3_IAC_x_y(WILL, TELOPT_NAWS);
451#endif
452#if ENABLE_FEATURE_TELNET_TTYPE
453 if (G.ttype) {
454 log1("C:WILL %s", "TTYPE");
455 put_iac3_IAC_x_y(WILL, TELOPT_TTYPE);
456 }
457#endif
458#if ENABLE_FEATURE_TELNET_AUTOLOGIN
459 if (G.autologin) {
460 log1("C:WILL %s", "NEW_ENVIRON");
461 put_iac3_IAC_x_y(WILL, TELOPT_NEW_ENVIRON);
462 }
463#endif
464 }
465 }
466
467 // If any of the checks below are true, then we know we
468 // went through INITIAL_SENT code path above.
469 // Therefore we always send WILL X before SB X...
470#if ENABLE_FEATURE_TELNET_WIDTH
471 if (remaining_free_bytes(conn->size) > MAX_NAWS_SIZE) {
472 if (G.changes_seen & CHANGED_NAWS) {
473 G.flags |= FLAGS_NAWS_ON; // remember we did it
474 G.changes_seen -= CHANGED_NAWS;
475 goto generate_naws;
476 }
477 // Handle window resize: send updated NAWS
478 if (G.got_SIGWINCH) {
479 // we know that INITIAL_SENT is set, signal handler checks that
480 generate_naws:
481 // Clear the flag before put_iac_naws() to avoid race
482 G.got_SIGWINCH = 0;
483//log1("C:WILL %s", "NAWS, the second time, I'm telling you it's fine!");
484//put_iac3_IAC_x_y(WILL, TELOPT_NAWS);
485 put_iac_naws();
486 }
487 }
488#endif
489#if ENABLE_FEATURE_TELNET_TTYPE
490 if ((G.changes_seen & CHANGED_TTYPE)
491 && remaining_free_bytes(conn->size) > 6 + 2 * strlen(G.ttype)
492 ) {
493 log1("C:SB %s '%s'", "TTYPE", G.ttype);
494 put_iac_subopt(TELOPT_TTYPE, G.ttype);
495 G.ttype = NULL; // remember we did it
496 G.changes_seen -= CHANGED_TTYPE;
497 }
498#endif
499#if ENABLE_FEATURE_TELNET_AUTOLOGIN
500 if ((G.changes_seen & CHANGED_NEW_ENVIRON)
501 && remaining_free_bytes(conn->size) > 12 + 2 * strlen(G.autologin)
502 ) {
503 log1("C:SB %s '%s'", "NEW_ENVIRON", G.autologin);
504 put_iac_subopt_autologin(G.autologin);
505 G.autologin = NULL; // remember we did it
506 G.changes_seen -= CHANGED_NEW_ENVIRON;
445 } 507 }
508#endif
446} 509}
447 510
448static void will_charmode(void) 511static void announce_rawmode(void)
449{ 512{
450 G.charmode = CHM_TRY; 513 printf("\r\nEntering %s mode"
451 G.telflags |= (UF_ECHO | UF_SGA); 514 "\r\nEscape character is '^%c'.\r\n", "character", ']'
452 setConMode(); 515 );
453 516 rawmode();
454 put_iac3_IAC_x_y(DO, TELOPT_ECHO);
455 put_iac3_IAC_x_y(DO, TELOPT_SGA);
456 iac_flush();
457} 517}
458 518static void announce_and_switch_to_rawmode(void)
459static void do_linemode(void)
460{ 519{
461 G.charmode = CHM_TRY; 520 announce_rawmode();
462 G.telflags &= ~(UF_ECHO | UF_SGA); 521 rawmode();
463 setConMode();
464
465 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
466 put_iac3_IAC_x_y(DONT, TELOPT_SGA);
467 iac_flush();
468} 522}
469 523static void announce_and_switch_to_cookmode(void)
470static void to_notsup(char c)
471{ 524{
472 if (G.telwish == WILL) 525 printf("\r\nEntering %s mode"
473 put_iac3_IAC_x_y(DONT, c); 526 "\r\nEscape character is '^%c'.\r\n", "line", 'C'
474 else if (G.telwish == DO) 527 );
475 put_iac3_IAC_x_y(WONT, c); 528 cookmode();
476} 529}
477 530
478static void to_echo(void) 531static void show_menu(void)
479{ 532{
480 /* if server requests ECHO, don't agree */ 533 char b;
481 if (G.telwish == DO) { 534
482 put_iac3_IAC_x_y(WONT, TELOPT_ECHO); 535 rawmode();
483 return; 536
537 full_write1_str("\r\nConsole escape. Commands are:\r\n"
538 "l go to line mode\r\n"
539 "c go to character mode\r\n"
540 "z suspend telnet\r\n"
541 "e exit\r\n");
542 if (read(STDIN_FILENO, &b, 1) <= 0
543 || b == 'e'
544 ) {
545 doexit(EXIT_FAILURE);
484 } 546 }
485 if (G.telwish == DONT)
486 return;
487 547
488 if (G.telflags & UF_ECHO) { 548 switch (b) {
489 if (G.telwish == WILL) 549 case 'l':
550 if (G.optstate_ECHO == OPT_ECHO_ON) { /* are we in rawmode? */
551 G.optstate_ECHO = OPT_ECHO_OFF;
552 announce_and_switch_to_cookmode();
553 goto echo_changed;
554 }
555 break;
556 case 'c':
557 if (G.optstate_ECHO != OPT_ECHO_ON) { /* OFF or UNKNOWN? (both operate as linemode) */
558 G.optstate_ECHO = OPT_ECHO_ON;
559 announce_rawmode(); /* no "_and_switch_": we are already in rawmode */
560 echo_changed:
561 if (G.flags & INITIAL_SENT)
562 G.changes_seen |= CHANGED_ECHO; /* inform the server at next send */
490 return; 563 return;
491 } else if (G.telwish == WONT) 564 }
492 return; 565 break;
566 case 'z':
567 cookmode();
568 kill(0, SIGTSTP);
569 rawmode();
570 break;
571 }
493 572
494 if (G.charmode != CHM_OFF) 573 full_write1_str("continuing...\r\n");
495 G.telflags ^= UF_ECHO;
496 574
497 if (G.telflags & UF_ECHO) 575 if (G.optstate_ECHO != OPT_ECHO_ON)
498 put_iac3_IAC_x_y(DO, TELOPT_ECHO); 576 cookmode();
499 else 577}
500 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
501 578
502 setConMode(); 579static int have_buffer_to_read_from_stdin(void *this)
503 full_write1_str("\r\n"); /* sudden modec */ 580{
581 stdin_to_net_t *conn = this;
582 if (conn->read_fd < 0)
583 return 0;
584 return conn->size < BUFSIZE;
504} 585}
505 586
506static void to_sga(void) 587static int read_from_stdin(void *this)
507{ 588{
508 /* daemon always sends will/wont, client do/dont */ 589 stdin_to_net_t *conn = this;
590 int count, rem, expand_count;
591 byte *start, *src, *end;
592 byte c;
509 593
510 if (G.telflags & UF_SGA) { 594 //if (conn->read_fd < 0)
511 if (G.telwish == WILL) 595 // return 0; /* Already stopped reading stdin */
512 return; 596 //ioloop_run() guarantees it won't call us with fd < 0
513 } else if (G.telwish == WONT) 597
514 return; 598 count = BUFSIZE - conn->size;
515 599 count = MIN(BUFSIZE - conn->rdidx, count);
516 G.telflags ^= UF_SGA; /* toggle */ 600 count /= 2; /* Reserve room for worst-case expansion */
517 if (G.telflags & UF_SGA) 601 if (count == 0)
518 put_iac3_IAC_x_y(DO, TELOPT_SGA); 602 return 0;
519 else 603
520 put_iac3_IAC_x_y(DONT, TELOPT_SGA); 604 start = BUF_TTY2NET + conn->rdidx;
605 count = safe_read(conn->read_fd, start, count);
606 if (count <= 0) {
607 conn->read_fd = -1;
608 return 0; /* Error or EOF - didn't read anything */
609 }
610
611 /* First pass: scan forward counting characters that need expansion */
612 src = start;
613 expand_count = 0;
614 rem = count; /* Remaining bytes to scan */
615 do {
616 c = *src++;
617 if (c == 0x1d) {
618 /* Escape character - process bytes before it, then handle escape */
619 count -= rem; /* Drop the remaining tail */
620#define found_escape (rem != 0)
621 break;
622 }
623 if (c == IAC) {
624 expand_count++; /* IAC -> IAC IAC (one extra byte) */
625 } else if (c == '\r' || c == '\n') {
626 expand_count++; /* \r or \n -> \r\n (one extra byte) */
627 }
628 } while (--rem != 0);
629
630 if (expand_count != 0) {
631 /* Slow path: expand in place working backwards */
632 src = start + count - 1; /* Last read byte */
633 end = src + expand_count; /* Last byte after expansion */
634
635 /* As soon as src == end, the remaining bytes do not need processing (think about it!) */
636 while (src < end) {
637 c = *src--;
638 if (c == IAC) {
639 *end-- = IAC;
640 } else if (c == '\r' || c == '\n') {
641 *end-- = '\n';
642 c = '\r';
643 }
644 *end-- = c;
645 }
646 count += expand_count;
647 }
648
649 conn->size += count;
650 conn->rdidx = (conn->rdidx + count) & BUFMASK;
651
652 if (found_escape)
653 show_menu();
654#undef found_escape
655
656 return count;
521} 657}
522 658
523#if ENABLE_FEATURE_TELNET_TTYPE 659static int have_data_to_write_to_net(void *this)
524static void to_ttype(void)
525{ 660{
526 /* Tell server we will (or won't) do TTYPE */ 661 stdin_to_net_t *conn = this;
527 if (G.ttype) 662 if (conn->size == 0 && conn->read_fd < 0) {
528 put_iac3_IAC_x_y(WILL, TELOPT_TTYPE); 663 /* buffer drained and stdin EOF - signal EOF to server */
529 else 664 shutdown(conn->write_fd, SHUT_WR);
530 put_iac3_IAC_x_y(WONT, TELOPT_TTYPE); 665 /* Remove this pipe */
666 ioloop_remove_conn(conn->io, (connection_t*)conn);
667 return -1;
668 }
669 return conn->size > 0 || G_changes_or_WINCH != 0;
531} 670}
532#endif
533 671
534#if ENABLE_FEATURE_TELNET_AUTOLOGIN 672static int write_to_net(void *this)
535static void to_new_environ(void)
536{ 673{
537 /* Tell server we will (or will not) do AUTOLOGIN */ 674 stdin_to_net_t *conn = this;
538 if (G.autologin) 675 int count;
539 put_iac3_IAC_x_y(WILL, TELOPT_NEW_ENVIRON); 676
540 else 677 /* Do we have option or NAWS changes to handle? */
541 put_iac3_IAC_x_y(WONT, TELOPT_NEW_ENVIRON); 678 if (G_changes_or_WINCH)
679 handle_changes_in_options(conn); /* yes */
680
681 count = MIN(BUFSIZE - conn->wridx, conn->size);
682 // can be zero due to handle_changes_in_options()
683 if (count == 0)
684 return 0;
685 count = safe_write(conn->write_fd, BUF_TTY2NET + conn->wridx, count);
686 if (count <= 0) {
687 if (count < 0 && errno == EAGAIN)
688 return 0;
689 full_write1_str("Error writing to foreign host\r\n");
690 ioloop_remove_conn(conn->io, (connection_t*)conn);
691 return -1; /* "I'm gone" */
692 }
693
694 conn->wridx = (conn->wridx + count) & BUFMASK;
695 conn->size -= count;
696 if (conn->size == 0) {
697 conn->rdidx = 0;
698 conn->wridx = 0;
699 }
700 return count;
542} 701}
543#endif
544 702
545#if ENABLE_FEATURE_TELNET_WIDTH 703static int have_buffer_to_read_from_net(void *this)
546static void to_naws(void)
547{ 704{
548 /* Tell server we will do NAWS */ 705 net_to_stdout_t *conn = this;
549 put_iac3_IAC_x_y(WILL, TELOPT_NAWS); 706 if (conn->read_fd < 0)
707 return 0;
708 return conn->size < BUFSIZE;
550} 709}
551#endif
552 710
553static void telopt(byte c) 711static int read_from_net(void *this)
554{ 712{
555 switch (c) { 713 net_to_stdout_t *conn = this;
556 case TELOPT_ECHO: 714 int count;
557 to_echo(); break; 715 byte *src, *dst;
558 case TELOPT_SGA: 716 byte c;
559 to_sga(); break; 717 byte oldstate_ECHO;
718
719 count = BUFSIZE - conn->size;
720 //if (count == 0)
721 // return 0; /* buffer full */
722 //ioloop_run() ensures this does not happen
723 count = MIN(BUFSIZE - conn->rdidx, count); /* can't be zero */
724
725 /* Read directly into circular buffer's linear fragment */
726 dst = BUF_NET2TTY + conn->rdidx;
727 count = safe_read(conn->read_fd, dst, count);
728 dbg("read_from_net bytes:%d input_state:%d %s",
729 count, conn->input_state, bin_to_hex(dst, count > 0 ? count : 0));
730 if (count <= 0) {
731 if (count < 0 && errno == EAGAIN)
732 return 0;
733 full_write1_str("EOF on read from foreign host\r\n");
734 conn->read_fd = -1;
735 /* Imagine scenario: "cat" is running in the server.
736 * We connect and press ^D. Byte 0x04 is transmitted to "cat",
737 * it sees that as EOF and exits.
738 * We see EOF here (netfd read side is closed).
739 * Our stdin is still open, but we have no buffered data to write to net.
740 * ioloop_run() will not _poll_ the netfd for writing.
741 * We will not realize that netfd write side is also closed (!!!)
742 * until something is typed in our stdin and we poll netfd to write that data.
743 */
744//FIXME: send a few IAC NOPs to see whether the peer can still receive?
745 /* This is a workaround: pretend that our stdin is also closed: */
746 G.conn_stdin2net.read_fd = -1;
747 /* Unlike just exiting, this will try to send any buffered data */
748
749 return 0;
750 }
751
752 /* Copy option states - will be updated during processing, then compared after */
753 oldstate_ECHO = G.optstate_ECHO;
754
755 /* Optimization: do not load/store-in-place if unnecessary */
756 if (conn->input_state == TS_NORMAL) {
757 while (count != 0 && *dst != IAC && *dst != '\r')
758 count--, dst++;
759 }
760 /* Process IAC sequences in place:
761 * - Update option states (G.oldstate_XYZ)
762 * - Decode/remove IAC sequences, compacting the data
763 * - src = read pointer, dst = write pointer (for compaction)
764 */
765 src = dst;
766 while (--count >= 0) {
767 c = *src++;
768
769 switch (conn->input_state) {
770 int will;
771 case TS_NORMAL:
772 normal:
773 if (c == IAC) {
774 SET_INPUT_STATE(conn, TS_IAC, c);
775 continue;
776 }
777 if (c == '\r') {
778 SET_INPUT_STATE(conn, TS_CR, c);
779 }
780 *dst++ = c;
781 continue;
782
783 case TS_CR:
784 SET_INPUT_STATE(conn, TS_NORMAL, c);
785 if (c == '\0') /* Skip NUL after CR (telnet EOL: CR NUL) */
786 continue;
787 goto normal;
788
789 case TS_IAC:
790 if (c == IAC) {
791 /* IAC IAC -> single IAC */
792 *dst++ = c;
793 SET_INPUT_STATE(conn, TS_NORMAL, c);
794 } else if (c == SB) {
795 conn->negotiation_verb = 0xff; /* reuse as counter */
796 SET_INPUT_STATE(conn, TS_SUB1, c);
797 log1("S:SB %d", count ? *src : 0);
798 } else if (c == WILL || c == WONT || c == DO || c == DONT) { /* 251-254 */
799 conn->negotiation_verb = c;
800 SET_INPUT_STATE(conn, TS_OPT, c);
801 } else {
802 /* Unknown IAC command, ignore */
803 SET_INPUT_STATE(conn, TS_NORMAL, c);
804 }
805 break;
806
807 case TS_OPT:
808#if VERBOSE
809 {
810 static const char verbs[] ALIGN1 = "WILL\0""WONT\0""DO\0""DONT";
811 log1("S:%s %d", nth_string(verbs, conn->negotiation_verb - WILL), c);
812 }
813#endif
814 /* Process option negotiation */
815 will = (conn->negotiation_verb == WILL);
816 if (will || conn->negotiation_verb == WONT) {
817 switch (c) {
818 case TELOPT_ECHO: /* Remote option: server echoes our typing */
819 G.optstate_ECHO = will;
820 break;
821 case TELOPT_SGA: /* Remote option: "suppress go ahead" */
822 if (will && !(G.flags & FLAGS_SGA_SEEN))
823 G.changes_seen |= CHANGED_SGA;
824 break;
825 }
826 } else if (conn->negotiation_verb == DO) {
827 switch (c) {
560#if ENABLE_FEATURE_TELNET_TTYPE 828#if ENABLE_FEATURE_TELNET_TTYPE
561 case TELOPT_TTYPE: 829 case TELOPT_TTYPE: /* Local option: we send terminal type */
562 to_ttype(); break; 830 log1("TTYPE:'%s' %ssetting CHANGED_TTYPE", G.ttype, G.ttype ? "" : "not ");
831 if (G.ttype)
832 G.changes_seen |= CHANGED_TTYPE;
833 break;
563#endif 834#endif
564#if ENABLE_FEATURE_TELNET_AUTOLOGIN 835#if ENABLE_FEATURE_TELNET_AUTOLOGIN
565 case TELOPT_NEW_ENVIRON: 836 case TELOPT_NEW_ENVIRON: /* Local option: we send username */
566 to_new_environ(); break; 837 if (G.autologin)
838 G.changes_seen |= CHANGED_NEW_ENVIRON;
839 break;
567#endif 840#endif
568#if ENABLE_FEATURE_TELNET_WIDTH 841#if ENABLE_FEATURE_TELNET_WIDTH
569 case TELOPT_NAWS: 842 case TELOPT_NAWS: /* Local option: we send window size */
570 to_naws(); 843 if (!(G.flags & FLAGS_NAWS_ON))
571 put_iac_naws(c, G.win_width, G.win_height); 844 G.changes_seen |= CHANGED_NAWS;
572 break; 845 break;
573#endif 846#endif
574 default: 847 }
575 to_notsup(c); 848 }
576 break; 849 /* else: "DONT <something>": ignore */
850 SET_INPUT_STATE(conn, TS_NORMAL, c);
851 break;
852
853 case TS_SUB1:
854 /* Avoid being stuck in TS_SUB1 forever (with detours into TS_SUB2)
855 * if IAC SE is never seen (buggy server response?).
856 */
857 if (--conn->negotiation_verb == 0) {
858 log1("unterminated SB (server bug?)");
859 SET_INPUT_STATE(conn, TS_NORMAL, c);
860 } else
861 /* Skip over subnegotiation bytes until we see IAC */
862 if (c == IAC) {
863 SET_INPUT_STATE(conn, TS_SUB2, c);
864 }
865 break;
866
867 case TS_SUB2:
868 /* After IAC in subnegotiation, check for SE */
869 if (c == SE) {
870 /* End of subnegotiation */
871 SET_INPUT_STATE(conn, TS_NORMAL, c);
872 } else {
873 /* IAC followed by something other than SE, back to SUB1 */
874 SET_INPUT_STATE(conn, TS_SUB1, c);
875 }
876 break;
877 }
577 } 878 }
578}
579 879
580/* subnegotiation -- ignore all (except TTYPE,NAWS) */ 880 if (oldstate_ECHO != G.optstate_ECHO) {
581static void subneg(byte c) 881 /* Tell net writer to generate a confirmation */
582{ 882 G.changes_seen |= CHANGED_ECHO;
583 switch (G.telstate) { 883 /* Print the banner and set termios */
584 case TS_SUB1: 884 if (G.optstate_ECHO == OPT_ECHO_ON)
585 if (c == IAC) 885 announce_and_switch_to_rawmode();
586 G.telstate = TS_SUB2;
587#if ENABLE_FEATURE_TELNET_TTYPE
588 else 886 else
589 if (c == TELOPT_TTYPE && G.ttype) 887 announce_and_switch_to_cookmode();
590 put_iac_subopt(TELOPT_TTYPE, G.ttype);
591#endif
592#if ENABLE_FEATURE_TELNET_AUTOLOGIN
593 else
594 if (c == TELOPT_NEW_ENVIRON && G.autologin)
595 put_iac_subopt_autologin();
596#endif
597 break;
598 case TS_SUB2:
599 if (c == SE) {
600 G.telstate = TS_COPY;
601 return;
602 }
603 G.telstate = TS_SUB1;
604 break;
605 } 888 }
889
890 /* Update circular buffer: only the compacted data */
891 count = dst - (BUF_NET2TTY + conn->rdidx);
892 conn->size += count;
893 conn->rdidx = (conn->rdidx + count) & BUFMASK;
894
895 dbg("read_from_net: user bytes:%d input_state:%d", count, conn->input_state);
896
897 return count;
606} 898}
607 899
608static void rawmode(void) 900static int have_data_to_write_to_stdout(void *this)
609{ 901{
610 if (G.do_termios) 902 net_to_stdout_t *conn = this;
611 tcsetattr(0, TCSADRAIN, &G.termios_raw); 903 if (conn->size == 0 && conn->read_fd < 0) {
904 /* buffer drained and network read EOF */
905 /* Remove this pipe */
906 ioloop_remove_conn(conn->io, (connection_t*)conn);
907 return -1;
908 }
909 return conn->size > 0;
612} 910}
613 911
614static void cookmode(void) 912static int write_to_stdout(void *this)
615{ 913{
616 if (G.do_termios) 914 net_to_stdout_t *conn = this;
617 tcsetattr(0, TCSADRAIN, &G.termios_def); 915 int wr = MIN(BUFSIZE - conn->wridx, conn->size);
916 ssize_t count;
917
918 dbg("write_to_stdout: wr:%d %s", wr, bin_to_hex(BUF_NET2TTY + conn->wridx, wr));
919 count = safe_write(conn->write_fd, BUF_NET2TTY + conn->wridx, wr);
920 if (count <= 0) {
921 if (count < 0 && errno == EAGAIN)
922 return 0;
923 //full_write1_str("Error writing to stdout\r\n");
924 ioloop_remove_conn(conn->io, (connection_t*)conn);
925 return -1; /* "I'm gone" */
926 }
927
928 conn->wridx = (conn->wridx + count) & BUFMASK;
929 conn->size -= count;
930 if (conn->size == 0) {
931 conn->rdidx = 0;
932 conn->wridx = 0;
933 }
934 return count;
618} 935}
619 936
620int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 937int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -622,81 +939,102 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
622{ 939{
623 char *host; 940 char *host;
624 int port; 941 int port;
625 int len; 942 int opts;
626 struct pollfd ufds[2];
627 943
628 INIT_G(); 944 INIT_G();
629 945
630#if ENABLE_FEATURE_TELNET_TTYPE 946#if ENABLE_FEATURE_TELNET_TTYPE
631 G.ttype = getenv("TERM"); 947 G.ttype = getenv("TERM");
632#endif 948#endif
633 949 opts = getopt32(argv, ""IF_VERBOSE("^")
634 if (tcgetattr(0, &G.termios_def) >= 0) { 950 IF_FEATURE_TELNET_AUTOLOGIN("al:")IF_VERBOSE("v")
635 G.do_termios = 1; 951 IF_VERBOSE("\0" "vv")
636 G.termios_raw = G.termios_def; 952 IF_FEATURE_TELNET_AUTOLOGIN(, &G.autologin)
637 cfmakeraw(&G.termios_raw); 953 IF_VERBOSE(, &G.verbose)
638 } 954 );
639
640#if ENABLE_FEATURE_TELNET_AUTOLOGIN 955#if ENABLE_FEATURE_TELNET_AUTOLOGIN
641 if (1 == getopt32(argv, "al:", &G.autologin)) { 956 if ((opts & 3) == 1) {
642 /* Only -a without -l USER picks $USER from envvar */ 957 // -a without -l USER picks USER from envvar:
643 G.autologin = getenv("USER"); 958 G.autologin = getenv("USER");
644 } 959 }
645 argv += optind;
646#else
647 argv++;
648#endif 960#endif
961 argv += optind;
649 if (!*argv) 962 if (!*argv)
650 bb_show_usage(); 963 bb_show_usage();
651 host = *argv++; 964 host = *argv++;
652 port = *argv ? bb_lookup_port(*argv++, "tcp", 23) 965 port = *argv ? bb_lookup_port(*argv++, "tcp", 23)
653 : bb_lookup_std_port("telnet", "tcp", 23); 966 : bb_lookup_std_port("telnet", "tcp", 23);
654 if (*argv) /* extra params?? */ 967 if (*argv) // extra params??
655 bb_show_usage(); 968 bb_show_usage();
656 969
970 // Save our termios
971 if (tcgetattr(0, &G.termios_def) >= 0) {
972 G.flags |= DO_TERMIOS;
973 G.termios_raw = G.termios_def;
974 cfmakeraw(&G.termios_raw);
975 }
976
657 xmove_fd(create_and_connect_stream_or_die(host, port), netfd); 977 xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
658 printf("Connected to %s\n", host); 978 printf("Connected to %s\n", host);
659 979
660 setsockopt_keepalive(netfd); 980 setsockopt_keepalive(netfd);
981 ndelay_on(netfd);
661 982
662#if ENABLE_FEATURE_TELNET_WIDTH 983#if ENABLE_FEATURE_TELNET_WIDTH
663 get_terminal_width_height(0, &G.win_width, &G.win_height); 984 signal(SIGWINCH, handle_SIGWINCH);
664//TODO: support dynamic resize?
665#endif 985#endif
666
667 signal(SIGINT, record_signo); 986 signal(SIGINT, record_signo);
668 987 // Without this, SIGPIPE was seen on loopback connections:
669 ufds[0].fd = STDIN_FILENO; 988 signal(SIGPIPE, SIG_IGN);
670 ufds[0].events = POLLIN; 989
671 ufds[1].fd = netfd; 990 // Initialize connections
672 ufds[1].events = POLLIN; 991 G.conn_stdin2net.have_buffer_to_read_into = have_buffer_to_read_from_stdin;
673 992 G.conn_stdin2net.have_data_to_write = have_data_to_write_to_net;
674 while (1) { 993 G.conn_stdin2net.read = read_from_stdin;
675 if (poll(ufds, 2, -1) < 0) { 994 G.conn_stdin2net.write = write_to_net;
676 /* error, ignore and/or log something, bay go to loop */ 995 if (STDIN_FILENO != 0)
677 if (bb_got_signal) 996 G.conn_stdin2net.read_fd = STDIN_FILENO;
678 con_escape(); 997 G.conn_stdin2net.write_fd = netfd;
679 else 998
680 sleep1(); 999 G.conn_net2stdout.have_buffer_to_read_into = have_buffer_to_read_from_net;
681 continue; 1000 G.conn_net2stdout.have_data_to_write = have_data_to_write_to_stdout;
1001 G.conn_net2stdout.read = read_from_net;
1002 G.conn_net2stdout.write = write_to_stdout;
1003 G.conn_net2stdout.read_fd = netfd;
1004 G.conn_net2stdout.write_fd = STDOUT_FILENO;
1005 if (TS_NORMAL != 0)
1006 G.conn_net2stdout.input_state = TS_NORMAL;
1007
1008 ioloop_insert_conn(&G.io, (connection_t*)&G.conn_stdin2net);
1009 ioloop_insert_conn(&G.io, (connection_t*)&G.conn_net2stdout);
1010
1011 G.echo_sga_response_size = 3 * (2
1012 IF_FEATURE_TELNET_WIDTH( + 1)
1013 IF_FEATURE_TELNET_TTYPE(+ !!G.ttype)
1014 IF_FEATURE_TELNET_AUTOLOGIN(+ !!G.autologin)
1015 );
1016 // Make *any* ECHO negotiation from server, positive or negative,
1017 // trigger "we have a change" logic:
1018 G.optstate_ECHO = OPT_ECHO_UNKNOWN;
1019#if DEBUG || VERBOSE
1020 // Terminal can change to raw mode, fix line printing
1021 msg_eol = "\r\n";
1022#endif
1023 // EINTR flag and looping is only needed to handle ^C
1024 // in line mode, otherwise just a call to ioloop_run() would do.
1025 // TODO: replace primitive line mode with read_line_input()!!!
1026 G.io.flags |= IOLOOP_FLAG_EXIT_IF_EINTR;
1027 for (;;) {
1028 int rc = ioloop_run(&G.io);
1029 if (rc == IOLOOP_NO_CONNS) {
1030 dbg("connection is closed");
1031 break;
682 } 1032 }
683 1033 if (bb_got_signal /*&& rc == IOLOOP_EINTR*/) {
684// FIXME: reads can block. Need full bidirectional buffering. 1034 bb_got_signal = 0;
685 1035 show_menu();
686 if (ufds[0].revents) {
687 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
688 if (len <= 0)
689 doexit(EXIT_SUCCESS);
690 handle_net_output(len);
691 } 1036 }
1037 }
692 1038
693 if (ufds[1].revents) { 1039 doexit(EXIT_SUCCESS);
694 len = safe_read(netfd, G.buf, DATABUFSIZE);
695 if (len <= 0) {
696 full_write1_str("Connection closed by foreign host\r\n");
697 doexit(EXIT_FAILURE);
698 }
699 handle_net_input(len);
700 }
701 } /* while (1) */
702} 1040}
diff --git a/networking/telnet.c.OLD b/networking/telnet.c.OLD
new file mode 100644
index 000000000..7a0253525
--- /dev/null
+++ b/networking/telnet.c.OLD
@@ -0,0 +1,702 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * telnet implementation for busybox
4 *
5 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 *
13 * HISTORY
14 * Revision 3.1 1994/04/17 11:31:54 too
15 * initial revision
16 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
17 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
18 * <jam@ltsp.org>
19 * Modified 2004/02/11 to add ability to pass the USER variable to remote host
20 * by Fernando Silveira <swrh@gmx.net>
21 */
22//config:config TELNET
23//config: bool "telnet (8.8 kb)"
24//config: default y
25//config: help
26//config: Telnet is an interface to the TELNET protocol, but is also commonly
27//config: used to test other simple protocols.
28//config:
29//config:config FEATURE_TELNET_TTYPE
30//config: bool "Pass TERM type to remote host"
31//config: default y
32//config: depends on TELNET
33//config: help
34//config: Setting this option will forward the TERM environment variable to the
35//config: remote host you are connecting to. This is useful to make sure that
36//config: things like ANSI colors and other control sequences behave.
37//config:
38//config:config FEATURE_TELNET_AUTOLOGIN
39//config: bool "Pass USER type to remote host"
40//config: default y
41//config: depends on TELNET
42//config: help
43//config: Setting this option will forward the USER environment variable to the
44//config: remote host you are connecting to. This is useful when you need to
45//config: log into a machine without telling the username (autologin). This
46//config: option enables '-a' and '-l USER' options.
47//config:
48//config:config FEATURE_TELNET_WIDTH
49//config: bool "Enable window size autodetection"
50//config: default y
51//config: depends on TELNET
52
53//applet:IF_TELNET(APPLET(telnet, BB_DIR_USR_BIN, BB_SUID_DROP))
54
55//kbuild:lib-$(CONFIG_TELNET) += telnet.o
56
57//usage:#if ENABLE_FEATURE_TELNET_AUTOLOGIN
58//usage:#define telnet_trivial_usage
59//usage: "[-a] [-l USER] HOST [PORT]"
60//usage:#define telnet_full_usage "\n\n"
61//usage: "Connect to telnet server\n"
62//usage: "\n -a Automatic login with $USER variable"
63//usage: "\n -l USER Automatic login as USER"
64//usage:
65//usage:#else
66//usage:#define telnet_trivial_usage
67//usage: "HOST [PORT]"
68//usage:#define telnet_full_usage "\n\n"
69//usage: "Connect to telnet server"
70//usage:#endif
71
72#include <arpa/telnet.h>
73#include <netinet/in.h>
74#include "libbb.h"
75#include "common_bufsiz.h"
76
77#ifdef __BIONIC__
78/* should be in arpa/telnet.h */
79# define IAC 255 /* interpret as command: */
80# define DONT 254 /* you are not to use option */
81# define DO 253 /* please, you use option */
82# define WONT 252 /* I won't use option */
83# define WILL 251 /* I will use option */
84# define SB 250 /* interpret as subnegotiation */
85# define SE 240 /* end sub negotiation */
86# define TELOPT_ECHO 1 /* echo */
87# define TELOPT_SGA 3 /* suppress go ahead */
88# define TELOPT_TTYPE 24 /* terminal type */
89# define TELOPT_NAWS 31 /* window size */
90#endif
91
92enum {
93 DATABUFSIZE = 128,
94 IACBUFSIZE = 128,
95
96 CHM_TRY = 0,
97 CHM_ON = 1,
98 CHM_OFF = 2,
99
100 UF_ECHO = 0x01,
101 UF_SGA = 0x02,
102
103 TS_NORMAL = 0,
104 TS_COPY = 1,
105 TS_IAC = 2,
106 TS_OPT = 3,
107 TS_SUB1 = 4,
108 TS_SUB2 = 5,
109 TS_CR = 6,
110};
111
112typedef unsigned char byte;
113
114enum { netfd = 3 };
115
116struct globals {
117 int iaclen; /* could even use byte, but it's a loss on x86 */
118 byte telstate; /* telnet negotiation state from network input */
119 byte telwish; /* DO, DONT, WILL, WONT */
120 byte charmode;
121 byte telflags;
122 byte do_termios;
123#if ENABLE_FEATURE_TELNET_TTYPE
124 char *ttype;
125#endif
126#if ENABLE_FEATURE_TELNET_AUTOLOGIN
127 const char *autologin;
128#endif
129#if ENABLE_FEATURE_TELNET_WIDTH
130 unsigned win_width, win_height;
131#endif
132 /* same buffer used both for network and console read/write */
133 char buf[DATABUFSIZE];
134 /* buffer to handle telnet negotiations */
135 char iacbuf[IACBUFSIZE];
136 struct termios termios_def;
137 struct termios termios_raw;
138} FIX_ALIASING;
139#define G (*(struct globals*)bb_common_bufsiz1)
140#define INIT_G() do { \
141 setup_common_bufsiz(); \
142 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
143} while (0)
144
145
146static void rawmode(void);
147static void cookmode(void);
148static void do_linemode(void);
149static void will_charmode(void);
150static void telopt(byte c);
151static void subneg(byte c);
152
153static void iac_flush(void)
154{
155 if (G.iaclen != 0) {
156 full_write(netfd, G.iacbuf, G.iaclen);
157 G.iaclen = 0;
158 }
159}
160
161static void doexit(int ev) NORETURN;
162static void doexit(int ev)
163{
164 cookmode();
165 exit(ev);
166}
167
168static void con_escape(void)
169{
170 char b;
171
172 if (bb_got_signal) /* came from line mode... go raw */
173 rawmode();
174
175 full_write1_str("\r\nConsole escape. Commands are:\r\n\n"
176 " l go to line mode\r\n"
177 " c go to character mode\r\n"
178 " z suspend telnet\r\n"
179 " e exit telnet\r\n");
180
181 if (read(STDIN_FILENO, &b, 1) <= 0)
182 doexit(EXIT_FAILURE);
183
184 switch (b) {
185 case 'l':
186 if (!bb_got_signal) {
187 do_linemode();
188 goto ret;
189 }
190 break;
191 case 'c':
192 if (bb_got_signal) {
193 will_charmode();
194 goto ret;
195 }
196 break;
197 case 'z':
198 cookmode();
199 kill(0, SIGTSTP);
200 rawmode();
201 break;
202 case 'e':
203 doexit(EXIT_SUCCESS);
204 }
205
206 full_write1_str("continuing...\r\n");
207
208 if (bb_got_signal)
209 cookmode();
210 ret:
211 bb_got_signal = 0;
212}
213
214static void handle_net_output(int len)
215{
216 byte outbuf[2 * DATABUFSIZE];
217 byte *dst = outbuf;
218 byte *src = (byte*)G.buf;
219 byte *end = src + len;
220
221 while (src < end) {
222 byte c = *src++;
223 if (c == 0x1d) {
224 con_escape();
225 return;
226 }
227 *dst = c;
228 if (c == IAC)
229 *++dst = c; /* IAC -> IAC IAC */
230 else
231 if (c == '\r' || c == '\n') {
232 /* Enter key sends '\r' in raw mode and '\n' in cooked one.
233 *
234 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
235 * Using CR LF instead of other allowed possibilities
236 * like CR NUL - easier to talk to HTTP/SMTP servers.
237 */
238 *dst = '\r'; /* Enter -> CR LF */
239 *++dst = '\n';
240 }
241#if 0
242/* putty's "special commands" mode does this: */
243/* Korenix 3005 switch needs at least the backspace tweak */
244 if (c == 0x08 || c == 0x7f) { /* ctrl+h || backspace */
245 *dst = IAC;
246 *++dst = EC;
247 }
248 if (c == 0x03) { /* ctrl+c */
249 *dst = IAC;
250 *++dst = IP;
251 }
252#endif
253 dst++;
254 }
255 if (dst - outbuf != 0)
256 full_write(netfd, outbuf, dst - outbuf);
257}
258
259static void handle_net_input(int len)
260{
261 byte c;
262 int i;
263 int cstart = 0;
264
265 i = 0;
266 //bb_error_msg("[%u,'%.*s']", G.telstate, len, G.buf);
267 if (G.telstate == TS_NORMAL) { /* most typical state */
268 while (i < len) {
269 c = G.buf[i];
270 i++;
271 if (c == IAC) /* unlikely */
272 goto got_IAC;
273 if (c != '\r') /* likely */
274 continue;
275 G.telstate = TS_CR;
276 cstart = i;
277 goto got_special;
278 }
279 full_write(STDOUT_FILENO, G.buf, len);
280 return;
281 got_IAC:
282 G.telstate = TS_IAC;
283 cstart = i - 1;
284 got_special: ;
285 }
286
287 for (; i < len; i++) {
288 c = G.buf[i];
289
290 switch (G.telstate) {
291 case TS_CR:
292 /* Prev char was CR. If cur one is NUL, ignore it.
293 * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling.
294 */
295 G.telstate = TS_COPY;
296 if (c == '\0')
297 break;
298 /* else: fall through - need to handle CR IAC ... properly */
299
300 case TS_COPY: /* Prev char was ordinary */
301 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */
302 if (c == IAC)
303 G.telstate = TS_IAC;
304 else {
305 G.buf[cstart++] = c;
306 if (c == '\r')
307 G.telstate = TS_CR;
308 }
309 break;
310
311 case TS_IAC: /* Prev char was IAC */
312 switch (c) {
313 case IAC: /* IAC IAC -> one IAC */
314 G.buf[cstart++] = c;
315 G.telstate = TS_COPY;
316 break;
317 case SB:
318 G.telstate = TS_SUB1;
319 break;
320 case DO:
321 case DONT:
322 case WILL:
323 case WONT:
324 G.telwish = c;
325 G.telstate = TS_OPT;
326 break;
327 /* DATA MARK must be added later */
328 default:
329 G.telstate = TS_COPY;
330 }
331 break;
332
333 case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */
334 telopt(c);
335 G.telstate = TS_COPY;
336 break;
337
338 case TS_SUB1: /* Subnegotiation */
339 case TS_SUB2: /* Subnegotiation */
340 subneg(c); /* can change G.telstate */
341 break;
342 }
343 }
344
345 /* We had some IACs, or CR */
346 iac_flush();
347 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
348 G.telstate = TS_NORMAL;
349 if (cstart != 0)
350 full_write(STDOUT_FILENO, G.buf, cstart);
351}
352
353static void put_iac(int c)
354{
355 int iaclen = G.iaclen;
356 if (iaclen >= IACBUFSIZE) {
357 iac_flush();
358 iaclen = 0;
359 }
360 G.iacbuf[iaclen] = c; /* "... & 0xff" is implicit */
361 G.iaclen = iaclen + 1;
362}
363
364static void put_iac2_msb_lsb(unsigned x_y)
365{
366 put_iac(x_y >> 8); /* "... & 0xff" is implicit */
367 put_iac(x_y); /* "... & 0xff" is implicit */
368}
369#define put_iac2_x_y(x,y) put_iac2_msb_lsb(((x)<<8) + (y))
370
371#if ENABLE_FEATURE_TELNET_WIDTH \
372 || ENABLE_FEATURE_TELNET_TTYPE \
373 || ENABLE_FEATURE_TELNET_AUTOLOGIN
374static void put_iac4_msb_lsb(unsigned x_y_z_t)
375{
376 put_iac2_msb_lsb(x_y_z_t >> 16);
377 put_iac2_msb_lsb(x_y_z_t); /* "... & 0xffff" is implicit */
378}
379#define put_iac4_x_y_z_t(x,y,z,t) put_iac4_msb_lsb(((x)<<24) + ((y)<<16) + ((z)<<8) + (t))
380#endif
381
382static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c)
383{
384 put_iac(IAC);
385 put_iac2_msb_lsb(wwdd_and_c);
386}
387#define put_iac3_IAC_x_y(wwdd,c) put_iac3_IAC_x_y_merged(((wwdd)<<8) + (c))
388
389#if ENABLE_FEATURE_TELNET_TTYPE
390static void put_iac_subopt(byte c, char *str)
391{
392 put_iac4_x_y_z_t(IAC, SB, c, 0);
393
394 while (*str)
395 put_iac(*str++);
396
397 put_iac2_x_y(IAC, SE);
398}
399#endif
400
401#if ENABLE_FEATURE_TELNET_AUTOLOGIN
402static void put_iac_subopt_autologin(void)
403{
404 const char *p;
405
406 put_iac4_x_y_z_t(IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS);
407 put_iac4_x_y_z_t(NEW_ENV_VAR, 'U', 'S', 'E'); /* "USER" */
408 put_iac2_x_y('R', NEW_ENV_VALUE);
409
410 p = G.autologin;
411 while (*p)
412 put_iac(*p++);
413
414 put_iac2_x_y(IAC, SE);
415}
416#endif
417
418#if ENABLE_FEATURE_TELNET_WIDTH
419static void put_iac_naws(byte c, int x, int y)
420{
421 put_iac3_IAC_x_y(SB, c);
422
423 put_iac4_msb_lsb((x << 16) + y);
424
425 put_iac2_x_y(IAC, SE);
426}
427#endif
428
429static void setConMode(void)
430{
431 if (G.telflags & UF_ECHO) {
432 if (G.charmode == CHM_TRY) {
433 G.charmode = CHM_ON;
434 printf("\r\nEntering %s mode"
435 "\r\nEscape character is '^%c'.\r\n", "character", ']');
436 rawmode();
437 }
438 } else {
439 if (G.charmode != CHM_OFF) {
440 G.charmode = CHM_OFF;
441 printf("\r\nEntering %s mode"
442 "\r\nEscape character is '^%c'.\r\n", "line", 'C');
443 cookmode();
444 }
445 }
446}
447
448static void will_charmode(void)
449{
450 G.charmode = CHM_TRY;
451 G.telflags |= (UF_ECHO | UF_SGA);
452 setConMode();
453
454 put_iac3_IAC_x_y(DO, TELOPT_ECHO);
455 put_iac3_IAC_x_y(DO, TELOPT_SGA);
456 iac_flush();
457}
458
459static void do_linemode(void)
460{
461 G.charmode = CHM_TRY;
462 G.telflags &= ~(UF_ECHO | UF_SGA);
463 setConMode();
464
465 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
466 put_iac3_IAC_x_y(DONT, TELOPT_SGA);
467 iac_flush();
468}
469
470static void to_notsup(char c)
471{
472 if (G.telwish == WILL)
473 put_iac3_IAC_x_y(DONT, c);
474 else if (G.telwish == DO)
475 put_iac3_IAC_x_y(WONT, c);
476}
477
478static void to_echo(void)
479{
480 /* if server requests ECHO, don't agree */
481 if (G.telwish == DO) {
482 put_iac3_IAC_x_y(WONT, TELOPT_ECHO);
483 return;
484 }
485 if (G.telwish == DONT)
486 return;
487
488 if (G.telflags & UF_ECHO) {
489 if (G.telwish == WILL)
490 return;
491 } else if (G.telwish == WONT)
492 return;
493
494 if (G.charmode != CHM_OFF)
495 G.telflags ^= UF_ECHO;
496
497 if (G.telflags & UF_ECHO)
498 put_iac3_IAC_x_y(DO, TELOPT_ECHO);
499 else
500 put_iac3_IAC_x_y(DONT, TELOPT_ECHO);
501
502 setConMode();
503 full_write1_str("\r\n"); /* sudden modec */
504}
505
506static void to_sga(void)
507{
508 /* daemon always sends will/wont, client do/dont */
509
510 if (G.telflags & UF_SGA) {
511 if (G.telwish == WILL)
512 return;
513 } else if (G.telwish == WONT)
514 return;
515
516 G.telflags ^= UF_SGA; /* toggle */
517 if (G.telflags & UF_SGA)
518 put_iac3_IAC_x_y(DO, TELOPT_SGA);
519 else
520 put_iac3_IAC_x_y(DONT, TELOPT_SGA);
521}
522
523#if ENABLE_FEATURE_TELNET_TTYPE
524static void to_ttype(void)
525{
526 /* Tell server we will (or won't) do TTYPE */
527 if (G.ttype)
528 put_iac3_IAC_x_y(WILL, TELOPT_TTYPE);
529 else
530 put_iac3_IAC_x_y(WONT, TELOPT_TTYPE);
531}
532#endif
533
534#if ENABLE_FEATURE_TELNET_AUTOLOGIN
535static void to_new_environ(void)
536{
537 /* Tell server we will (or will not) do AUTOLOGIN */
538 if (G.autologin)
539 put_iac3_IAC_x_y(WILL, TELOPT_NEW_ENVIRON);
540 else
541 put_iac3_IAC_x_y(WONT, TELOPT_NEW_ENVIRON);
542}
543#endif
544
545#if ENABLE_FEATURE_TELNET_WIDTH
546static void to_naws(void)
547{
548 /* Tell server we will do NAWS */
549 put_iac3_IAC_x_y(WILL, TELOPT_NAWS);
550}
551#endif
552
553static void telopt(byte c)
554{
555 switch (c) {
556 case TELOPT_ECHO:
557 to_echo(); break;
558 case TELOPT_SGA:
559 to_sga(); break;
560#if ENABLE_FEATURE_TELNET_TTYPE
561 case TELOPT_TTYPE:
562 to_ttype(); break;
563#endif
564#if ENABLE_FEATURE_TELNET_AUTOLOGIN
565 case TELOPT_NEW_ENVIRON:
566 to_new_environ(); break;
567#endif
568#if ENABLE_FEATURE_TELNET_WIDTH
569 case TELOPT_NAWS:
570 to_naws();
571 put_iac_naws(c, G.win_width, G.win_height);
572 break;
573#endif
574 default:
575 to_notsup(c);
576 break;
577 }
578}
579
580/* subnegotiation -- ignore all (except TTYPE,NAWS) */
581static void subneg(byte c)
582{
583 switch (G.telstate) {
584 case TS_SUB1:
585 if (c == IAC)
586 G.telstate = TS_SUB2;
587#if ENABLE_FEATURE_TELNET_TTYPE
588 else
589 if (c == TELOPT_TTYPE && G.ttype)
590 put_iac_subopt(TELOPT_TTYPE, G.ttype);
591#endif
592#if ENABLE_FEATURE_TELNET_AUTOLOGIN
593 else
594 if (c == TELOPT_NEW_ENVIRON && G.autologin)
595 put_iac_subopt_autologin();
596#endif
597 break;
598 case TS_SUB2:
599 if (c == SE) {
600 G.telstate = TS_COPY;
601 return;
602 }
603 G.telstate = TS_SUB1;
604 break;
605 }
606}
607
608static void rawmode(void)
609{
610 if (G.do_termios)
611 tcsetattr(0, TCSADRAIN, &G.termios_raw);
612}
613
614static void cookmode(void)
615{
616 if (G.do_termios)
617 tcsetattr(0, TCSADRAIN, &G.termios_def);
618}
619
620int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
621int telnet_main(int argc UNUSED_PARAM, char **argv)
622{
623 char *host;
624 int port;
625 int len;
626 struct pollfd ufds[2];
627
628 INIT_G();
629
630#if ENABLE_FEATURE_TELNET_TTYPE
631 G.ttype = getenv("TERM");
632#endif
633
634 if (tcgetattr(0, &G.termios_def) >= 0) {
635 G.do_termios = 1;
636 G.termios_raw = G.termios_def;
637 cfmakeraw(&G.termios_raw);
638 }
639
640#if ENABLE_FEATURE_TELNET_AUTOLOGIN
641 if (1 == getopt32(argv, "al:", &G.autologin)) {
642 /* Only -a without -l USER picks $USER from envvar */
643 G.autologin = getenv("USER");
644 }
645 argv += optind;
646#else
647 argv++;
648#endif
649 if (!*argv)
650 bb_show_usage();
651 host = *argv++;
652 port = *argv ? bb_lookup_port(*argv++, "tcp", 23)
653 : bb_lookup_std_port("telnet", "tcp", 23);
654 if (*argv) /* extra params?? */
655 bb_show_usage();
656
657 xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
658 printf("Connected to %s\n", host);
659
660 setsockopt_keepalive(netfd);
661
662#if ENABLE_FEATURE_TELNET_WIDTH
663 get_terminal_width_height(0, &G.win_width, &G.win_height);
664//TODO: support dynamic resize?
665#endif
666
667 signal(SIGINT, record_signo);
668
669 ufds[0].fd = STDIN_FILENO;
670 ufds[0].events = POLLIN;
671 ufds[1].fd = netfd;
672 ufds[1].events = POLLIN;
673
674 while (1) {
675 if (poll(ufds, 2, -1) < 0) {
676 /* error, ignore and/or log something, bay go to loop */
677 if (bb_got_signal)
678 con_escape();
679 else
680 sleep1();
681 continue;
682 }
683
684// FIXME: reads can block. Need full bidirectional buffering.
685
686 if (ufds[0].revents) {
687 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
688 if (len <= 0)
689 doexit(EXIT_SUCCESS);
690 handle_net_output(len);
691 }
692
693 if (ufds[1].revents) {
694 len = safe_read(netfd, G.buf, DATABUFSIZE);
695 if (len <= 0) {
696 full_write1_str("Connection closed by foreign host\r\n");
697 doexit(EXIT_FAILURE);
698 }
699 handle_net_input(len);
700 }
701 } /* while (1) */
702}
diff --git a/networking/telnet.txt b/networking/telnet.txt
new file mode 100644
index 000000000..87b1d1aa6
--- /dev/null
+++ b/networking/telnet.txt
@@ -0,0 +1,53 @@
1// 2-byte sequences:
2// IAC SE (240) End of subnegotiation parameters
3// IAC NOP (241) No operation
4// IAC DM (242) Data Mark (used with TCP urgent pointer)
5// IAC BRK (243) Break (simulate serial line break) - perhaps tcsendbreak(master_ptyfd)? [untested]
6// IAC IP (244) Interrupt Process ("send SIGINT to foreground process group a-la ^C")
7// IAC AO (245) Abort Output
8// IAC AYT (246) Are You There
9// IAC EC (247) Erase Character
10// IAC EL (248) Erase Line
11// IAC GA (249) Go Ahead
12// IAC IAC (255) I'm sending literal byte 255
13// 3-byte sequences:
14// IAC WILL <option> (251) sender declares willingness to enable option
15// IAC WONT <option> (252) sender refuses / disables option
16// IAC DO <option> (253) sender requests peer to send option, or enable option on peer's side
17// IAC DONT <option> (254) sender requests peer to NOT send option, or disable option on peer's side
18// Example:
19// IAC DO 31 (request NAWS)
20// IAC WILL 24 (offer TERMINAL-TYPE)
21//
22// Variable length: subnegotiation (250)
23// IAC SB <option> <data...> IAC SE
24// option = single byte
25// data = zero or more bytes
26// Subnegotiation can not be nested, and can not have 2- and 3-byte commands inside.
27// Inside subnegotiation data, IAC IAC represents literal 255.
28// An IAC byte inside SB:
29// IAC IAC: literal byte 255
30// IAC SE: end of subnegotiation
31// IAC <anything else>: protocol error
32// Common options that use subnegotiation:
33// TERMINAL-TYPE (24)
34// IAC SB 24 SEND(1) IAC SE
35// IAC SB 24 IS(0) "xterm-256color" IAC SE
36// NAWS - Negotiate About Window Size (31)
37// IAC SB 31 <width_hi> <width_lo> <height_hi> <height_lo> IAC SE
38// width and height are 16-bit big endian. HOWTO:
39// Server: IAC DO NAWS
40// Client: IAC WILL NAWS
41// Client: IAC SB NAWS <w> <h> IAC SE
42// ...sometime later: client's window is resized by user...
43// Client: IAC SB NAWS <w> <h> IAC SE
44// TERMINAL-SPEED (32)
45// IAC SB 32 IS(0) "rx,tx" IAC SE
46// REMOTE-FLOW-CONTROL (33)
47// LINEMODE (34)
48// X-DISPLAY-LOCATION (35)
49// ENVIRON (36)
50// AUTHENTICATION (37)
51// ENCRYPT (38)
52// NEW-ENVIRON (39)
53// COM-PORT-OPTION (44)
diff --git a/networking/telnetd.c b/networking/telnetd.c
index a5a783047..3eb9446a2 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -61,6 +61,14 @@
61//config: 61//config:
62//config: with all that done, telnetd _should_ work.... 62//config: with all that done, telnetd _should_ work....
63//config: 63//config:
64//config:config FEATURE_TELNETD_SELFTEST_DEBUG
65//config: bool "Include self-test (telnetd -@)"
66//config: default n
67//config: depends on TELNETD
68//config: help
69//config: Include self-test code for pty/vhangup() behavior.
70//config: Useful for development and validation on new platforms.
71//config:
64//config:config FEATURE_TELNETD_STANDALONE 72//config:config FEATURE_TELNETD_STANDALONE
65//config: bool "Support standalone telnetd (not inetd only)" 73//config: bool "Support standalone telnetd (not inetd only)"
66//config: default y 74//config: default y
@@ -119,49 +127,54 @@
119//usage: "\n -S Log to syslog (implied by -i or without -F and -w)" 127//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
120//usage: ) 128//usage: )
121//usage: ) 129//usage: )
122 130//usage: "\n -v Verbose"
123#define DEBUG 0
124 131
125#include "libbb.h" 132#include "libbb.h"
126#include "common_bufsiz.h" 133#include "common_bufsiz.h"
127#include <syslog.h> 134#include <syslog.h>
135#include <arpa/telnet.h>
136
137#define DEBUG 0
138#define DEBUG_CLOSING 0
128 139
129#if DEBUG 140#if DEBUG
130# define TELCMDS 141# define dbg(...) bb_error_msg(__VA_ARGS__)
131# define TELOPTS 142#else
143# define dbg(...) ((void)0)
144#endif
145#if DEBUG_CLOSING
146# define dbg_close(...) bb_error_msg(__VA_ARGS__)
147#else
148# define dbg_close(...) ((void)0)
132#endif 149#endif
133#include <arpa/telnet.h>
134 150
151#define SELFTEST_VHANGUP 1
152#define SELFTEST_NET2PTY 1
153#define SELFTEST_PTY2NET 1
135 154
136struct tsession { 155#define SAVE_NET2PTY_EXPECTED 0
137 struct tsession *next; 156#define SAVE_NET2PTY_ACTUAL 0
138 pid_t shell_pid;
139 int sockfd_read;
140 int sockfd_write;
141 int ptyfd;
142 smallint buffered_IAC_for_pty;
143
144 /* two circular buffers */
145 /*char *buf1, *buf2;*/
146/*#define TS_BUF1(ts) ts->buf1*/
147/*#define TS_BUF2(ts) TS_BUF2(ts)*/
148#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
149#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
150 int rdidx1, wridx1, size1;
151 int rdidx2, wridx2, size2;
152};
153 157
154/* Two buffers are directly after tsession in malloced memory. 158#define SAVE_PTY2NET_EXPECTED 0
155 * Make whole thing fit in 4k */ 159#define SAVE_PTY2NET_ACTUAL 0
156enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
157 160
161/* Must match getopt32 string */
162enum {
163 OPT_WATCHCHILD = (1 << 2), /* -K */
164 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
165 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
166 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
167 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
168 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
169};
158 170
159/* Globals */ 171/* Globals */
160struct globals { 172struct globals {
161 struct tsession *sessions; 173 ioloop_state_t io;
162 const char *loginpath; 174 const char *loginpath;
163 const char *issuefile; 175 const char *issuefile;
164 int maxfd; 176 unsigned verbose;
177 IF_FEATURE_TELNETD_INETD_WAIT(unsigned sec_linger;)
165} FIX_ALIASING; 178} FIX_ALIASING;
166#define G (*(struct globals*)bb_common_bufsiz1) 179#define G (*(struct globals*)bb_common_bufsiz1)
167#define INIT_G() do { \ 180#define INIT_G() do { \
@@ -170,303 +183,688 @@ struct globals {
170 G.issuefile = "/etc/issue.net"; \ 183 G.issuefile = "/etc/issue.net"; \
171} while (0) 184} while (0)
172 185
186#define log1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while (0)
187
188static NOINLINE void fabricate_ctrl_D_on_pty(int fd)
189{
190 struct termios tio;
191 // Master pty can see slave's termios - abuse that
192 if (tcgetattr(fd, &tio) != 0) {
193 dbg_close("%s: no termios(%d)", __func__, fd);
194 return;
195 }
196 dbg_close("%s: termios(%d) ICANON:%d,VEOF:%02x", __func__,
197 fd, !!(tio.c_lflag & ICANON), (uint8_t)tio.c_cc[VEOF]);
198 if ((tio.c_lflag & ICANON) && tio.c_cc[VEOF] != 0) {
199 // _Should_ look like EOF on input on the slave side...
200 write(fd, &tio.c_cc[VEOF], 1); // usually ^D
201 // One ^D often won't do. If a program was blocked on read already,
202 // ^D makes _that_ read return:
203 // read(0, "some data", 64) = 9
204 // but the program will likely read more
205 // (it did not interpret above as EOF), block, and die:
206 // read(0, 0x123456, 64) = ...<blocked>... -1 EIO
207 // +++ killed by SIGHUP +++
208 // Well, I'm not greedy...
209 write(fd, &tio.c_cc[VEOF], 1); // here is another EOF for you
210 // If this doesn't work, they can see 0x04 bytes. Tough cookies
211 }
212}
213
214#define TELNET_CONNECTION \
215 STRUCT_CONNECTION \
216 pid_t shell_pid;
173 217
174/* Write some buf1 data to pty, processing IACs. 218typedef struct telnet_conn {
175 * Update wridx1 and size1. Return < 0 on error. 219 TELNET_CONNECTION
176 * Buggy if IAC is present but incomplete: skips them. 220} telnet_conn_t;
221
222struct net_to_pty;
223
224typedef struct pty_to_net {
225 TELNET_CONNECTION
226 struct net_to_pty *sibling; /* must be directly after TELNET_CONNECTION */
227 int rdidx, wridx, size;
228 unsigned deadline_us;
229 unsigned eio_count;
230//TODO: these two can be unified into one:
231 smallint respond_to_AYT;
232 char extra_byte_to_write;
233 /* circular buffer follows */
234#define BUF2NET(ts) ((unsigned char*)(ts + 1))
235} pty_to_net_t ALIGN8;
236/* The buffer is directly after malloced memory, aligned to 64 bits */
237enum { TO_NET_BUFSIZE = 2 * 1024 };
238enum { TO_NET_BUFMASK = TO_NET_BUFSIZE - 1 };
239#define BUF2NET_INC(n, inc) ((n) = ((n) + (inc)) & TO_NET_BUFMASK)
240
241typedef struct net_to_pty {
242 TELNET_CONNECTION
243 pty_to_net_t *sibling; /* must be directly after TELNET_CONNECTION */
244 int rdidx, wridx, size;
245 unsigned deadline_us;
246 smallint skip_LF_or_NUL;
247 /* circular buffer follows */
248#define BUF2PTY(ts) ((unsigned char*)(ts + 1))
249} net_to_pty_t ALIGN8;
250/* The buffer is directly after malloced memory, aligned to 64 bits */
251enum { TO_PTY_BUFSIZE = 2 * 1024 };
252enum { TO_PTY_BUFMASK = TO_PTY_BUFSIZE - 1 };
253#define BUF2PTY_INC(n, inc) ((n) = ((n) + (inc)) & TO_PTY_BUFMASK)
254#define BUF2PTY_DEC(n, dec) ((n) = ((n) - (dec)) & TO_PTY_BUFMASK)
255
256/* The mutual pipes are linked by to_pty, to_net.
257 * The rules:
258 * = always test for !NULL before use.
259 * = if you are freeing one of the structs, set
260 * "me->sibling->sibling = NULL".
261 * Use one of these helpers:
177 */ 262 */
178static ssize_t 263static void remove_and_free_to_pty(net_to_pty_t *ts)
179safe_write_to_pty_decode_iac(struct tsession *ts) 264{
265 dbg_close("remove_and_free:%p rd:%d wr:%d sibling:%p",
266 ts, ts->read_fd, ts->write_fd, ts->sibling);
267 if (ts->sibling) {
268 /* "you have no sibling now" */
269 ts->sibling->sibling = NULL;
270 }
271 conn_close_fds_remove_and_free((void*)ts);
272}
273static void ALWAYS_INLINE remove_and_free_to_net(pty_to_net_t *ts)
180{ 274{
275 /* Works because ->sibling have the same offset */
276 remove_and_free_to_pty((void*)ts);
277}
278
279// Theory of operation
280// (AKA "when should I close fds? when should I detach from ioloop?").
281// The fds are named read_fd and write_fd, but for clarity let's call them netfd and ptyfd.
282// net_to_pty::have_data_to_write
283// net_to_pty::have_buffer_to_read_into
284// if ptyfd < 0: //sibling told us ptyfd is down?
285// if sibling && sibling->netfd >= 0: netfd = -1; //do not close netfd, sibling uses it (if sibling exists)!
286// close_and_detach;
287// if netfd < 0: return 0;
288// net_to_pty::write
289// if ptyfd write detects error (not EAGAIN):
290// //if (sibling):
291// // sibling->ptyfd = -1; // let sibling know ptyfd is closed
292// // if sibling->netfd >= 0: netfd = -1; // do not close netfd, sibling uses it!
293// //close_and_detach;
294// let pty_to_net detect this (ignore the error)
295// net_to_pty::read
296// if netfd read detects EOF or error (there is no way to tell pty the difference):
297// if no sibling: close(netfd);
298// netfd = -1; //no longer try to read
299// return 0; //but do not detach yet
300
301static unsigned char read_byte_unescaping_IAC(int *iac_cnt, unsigned char **pp)
302{
303 unsigned char c = *(*pp)++;
304 if (c == IAC && **pp == IAC) {
305 (*iac_cnt)++;
306 (*pp)++; /* Skip the second IAC in IAC IAC sequence */
307 }
308 return c;
309}
310
311static int net_to_pty__have_data_to_write(void *this)
312{
313 //connection_t *conn = this;
314 net_to_pty_t *ts = this;
315 unsigned char *buf;
316 int size, increment;
317
318 if (ts->write_fd < 0) /* did slave close its pty? */
319 return 0;
320 again:
321 size = ts->size;
322 if (size == 0)
323 return 0;
324
325 buf = BUF2PTY(ts) + ts->wridx;
326 if (ts->skip_LF_or_NUL) {
327 /* last char we wrote was '\r' */
328 ts->skip_LF_or_NUL = 0;
329 /* we have the next one: examine */
330 if (buf[0] == '\n' || buf[0] == '\0') {
331 increment = 1; /* drop it now */
332 inc:
333 BUF2PTY_INC(ts->wridx, increment);
334 ts->size -= increment;
335 goto again;
336 }
337 /* else: not '\r\n' or '\rNUL' */
338 }
339 if (buf[0] != IAC) /* very likely */
340 return size;
341 if (size == 1) { /* we only have one char: IAC */
342 cant_write:
343 if (ts->read_fd < 0)
344 ts->size = 0; /* EOF was seen: ignore incomplete IAC, prevent infinite loop */
345 //bb_error_msg("cant_write");
346 return 0; /* "can't write" */
347 }
348
349 /* size is >= 2 and buf[0] is IAC */
350 //dbg_iac("size:%d %02x %02x", size, buf[0], buf[1]);
351
352 /* Cannot process IACs which wrap around buffer's end.
353 * Worst case: IAC SB NAWS with all bytes IAC-escaped = 13 bytes */
354 while (ts->wridx > TO_PTY_BUFSIZE - 14) {
355 uint64_t v64;
356 if (ts->wridx < ts->rdidx) {
357 /* |.......WRxRD.| */
358 /* Buffer is not wrapped */
359 break;
360 }
361 /* Possible situations: */
362 /* |xRD.......WRx| wrapped */
363 /* |xxxxxxxxRDWRx| wrapped and full! rdidx = wridx */
364 /* Rotate entire buffer 8 bytes back */
365 v64 = *(uint64_t*)BUF2PTY(ts);
366 memmove(BUF2PTY(ts), BUF2PTY(ts) + 8, TO_PTY_BUFSIZE - 8);
367 *(uint64_t*)(BUF2PTY(ts) + TO_PTY_BUFSIZE - 8) = v64;
368 ts->wridx -= 8; /* can't underflow */
369 buf -= 8;
370 BUF2PTY_DEC(ts->rdidx, 8); /* can underflow, use DEC() */
371 }
372
373 if (buf[1] == IAC) /* IAC-IAC: we have something to write */
374 return size;
375
376 if (buf[1] >= 240 && buf[1] <= 249) {
377 /* 2-byte commands (240..250 and 255):
378 * IAC IAC (255) Literal 255.
379 * IAC SE (240) End of subnegotiation. Treated as NOP.
380 * IAC NOP (241) NOP.
381 * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()?
382 * IAC AYT (246) Are you there.
383 * These don't look useful:
384 * IAC DM (242) Data mark. What is this?
385 * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C).
386 * IAC AO (245) Abort output. "You can continue running, but do not send me the output".
387 * IAC EC (247) Erase character. The receiver should delete the last received char.
388 * IAC EL (248) Erase line. The receiver should delete everything up to last newline.
389 * IAC GA (249) Go ahead. For half-duplex lines: "now you talk".
390 */
391 if (buf[1] == AYT && ts->sibling) /* notify other pipe that AYT was seen */
392 ts->sibling->respond_to_AYT = 1;
393 /* NOP (241). Ignore (putty keepalive, etc) */
394 /* All other 2-byte commands also treated as NOPs here */
395 increment = 2;
396 goto inc;
397 }
398 if (size <= 2) {
399 /* only 2 bytes of the longer IAC is in the buffer */
400 goto cant_write; /* incomplete, can't process */
401 }
402 if (buf[1] == SB) {
403 if (buf[2] == TELOPT_NAWS) {
404 // IAC,SB,TELOPT_NAWS,<4 bytes possibly IAC-escaped>,IAC,SE
405 struct {
406 struct winsize ws;
407 int iac_cnt;
408 } s;
409 unsigned char *p;
410 unsigned char byte45, byte67, byte89;
411
412 // The usual: IAC,SB,TELOPT_NAWS,w,w,h,h (IAC,SE later): 7 + 2 = 9 bytes
413 // 255*HH: IAC,SB,TELOPT_NAWS,0,255,255,h,h: 8 bytes (10 with IAC,SE)
414 // The worst: IAC,SB,TELOPT_NAWS,w,w,w,w,h,h,h,h (four IACs): 11 bytes (not counting IAC,SE)
415 // We can't simply check for size < 11:
416 // will mishandle IAC,SB,TELOPT_NAWS,w,w,h,h,IAC,SE,'A' (10 bytes)
417 // the write of 'A' (ordinary visible char) can be delayed!
418 // Also mishandles 255*HH case (10 bytes) by delaying its processing
419 // until at least one more byte after it is received and size becomes >= 11.
420 if (size < 9) // postpone parsing until have 9+ bytes
421 goto cant_write;
422 memset(&s, 0, sizeof(s)); // pixel sizes are set to 0
423 //s.iac_cnt = 0; //done
424 p = buf + 3;
425 byte45 = read_byte_unescaping_IAC(&s.iac_cnt, &p);
426 byte67 = read_byte_unescaping_IAC(&s.iac_cnt, &p);
427 byte89 = read_byte_unescaping_IAC(&s.iac_cnt, &p); // fetches at most byte#9 - allowed by size
428 // If any one of these is IAC, the NAWS seq must be at least 10 bytes.
429 // IOW: it can't be case 'A' above. *Can* postpone if size == 10!
430 if (size < 9 + s.iac_cnt) // if IAC was seen: postpone parsing until have 10+ bytes
431 goto cant_write; // if 2+ IACs seen: postpone parsing until have 11+ bytes
432 s.ws.ws_col = (byte45 << 8) | byte67;
433 s.ws.ws_row = (byte89 << 8) | read_byte_unescaping_IAC(&s.iac_cnt, &p); // fetches _at most_ byte#11 (if four IACs)
434
435 if (s.ws.ws_col != 0 && s.ws.ws_row != 0) // don't provoke bugs elsewhere with "zero-sized screen"
436 ioctl(ts->write_fd, TIOCSWINSZ, (char *)&s.ws);
437 log1("pfd:%d window size:%dx%d", ts->write_fd, s.ws.ws_row, s.ws.ws_col);
438 increment = p - buf;
439 goto inc;
440 // trailing IAC,SE will be eaten separately, as 2-byte NOP
441 }
442//fixme: skip them correctly
443 /* else: other subnegs not supported yet */
444 }
445
446 /* Assume it is a 3-byte WILL/WONT/DO/DONT 251..254 command and skip it */
447 dbg("ignoring IAC,%02x,%02x", buf[1], buf[2]);
448 increment = 3;
449 goto inc;
450}
451
452/* Write some buf data to pty, processing IACs.
453 * Update wridx and size. Return < 0 on error.
454 */
455static int net_to_pty__write(void *this)
456{
457 //connection_t *conn = this;
458 net_to_pty_t *ts = this;
181 unsigned wr; 459 unsigned wr;
182 ssize_t rc; 460 ssize_t rc;
183 unsigned char *buf; 461 unsigned char *buf;
184 unsigned char *found; 462 unsigned char *found;
185 463
186 buf = TS_BUF1(ts) + ts->wridx1; 464 if (ts->write_fd < 0) /* did slave close its pty? */
187 wr = MIN(BUFSIZE - ts->wridx1, ts->size1); 465 return 0;
188 /* wr is at least 1 here */
189 466
190 if (ts->buffered_IAC_for_pty) { 467 buf = BUF2PTY(ts) + ts->wridx;
191 /* Last time we stopped on a "dangling" IAC byte. 468 wr = MIN(TO_PTY_BUFSIZE - ts->wridx, ts->size);
192 * We removed it from the buffer back then. 469 /* wr is at least 1 here */
193 * Now pretend it's still there, and jump to IAC processing.
194 */
195 ts->buffered_IAC_for_pty = 0;
196 wr++;
197 ts->size1++;
198 buf--; /* Yes, this can point before the buffer. It's ok */
199 ts->wridx1--;
200 goto handle_iac;
201 }
202 470
203 found = memchr(buf, IAC, wr); 471 found = memchr(buf, IAC, wr);
204 if (found != buf) { 472 if (found == buf) {
205 /* There is a "prefix" of non-IAC chars. 473 /* The first char is IAC.
206 * Write only them, and return. 474 * have_data_to_write() ensures we are only called this way
475 * if there are two IACs.
476 * It also ensures the buffer is not wrapping within 7 chars.
477 * Write one IAC. If that works, skip both.
207 */ 478 */
208 if (found) 479//TODO: advance ptr by one, and memchr up to _next_ IAC (if any):
209 wr = found - buf; 480//this would write more data
210 481 rc = safe_write(ts->write_fd, buf, 1);
211 /* We map \r\n ==> \r for pragmatic reasons: 482 if (rc > 0) {
212 * many client implementations send \r\n when 483 //dbg_iac("wrote: IAC");
213 * the user hits the CarriageReturn key. 484 ts->wridx += 2; /* this can't overflow */
214 * See RFC 1123 3.3.1 Telnet End-of-Line Convention. 485 ts->size -= 2;
215 */
216 rc = wr;
217 found = memchr(buf, '\r', wr);
218 if (found)
219 rc = found - buf + 1;
220 rc = safe_write(ts->ptyfd, buf, rc);
221 if (rc <= 0)
222 return rc;
223 if (rc < wr /* don't look past available data */
224 && buf[rc-1] == '\r' /* need this: imagine that write was _short_ */
225 && (buf[rc] == '\n' || buf[rc] == '\0')
226 ) {
227 rc++;
228 } 486 }
229 goto update_and_return; 487 /* Leave it to pty2net side to deal with closing pty on errors
488 * (it'll see them when it tries to read pty).
489 */
490 return 0; /* "didn't write anything" */
230 } 491 }
231 492 /* No leading IACs: found != buf.
232 /* buf starts with IAC char. Process that sequence. 493 * IOW: there is a "prefix" of non-IAC chars.
233 * Example: we get this from our own (bbox) telnet client: 494 * Write only them, and return.
234 * read(5, "\377\374\1""\377\373\37""\377\372\37\0\262\0@\377\360""\377\375\1""\377\375\3"):
235 * IAC WONT ECHO, IAC WILL NAWS, IAC SB NAWS <cols> <rows> IAC SE, IAC DO SGA
236 * Another example (telnet-0.17 from old-netkit):
237 * read(4, "\377\375\3""\377\373\30""\377\373\37""\377\373 ""\377\373!""\377\373\"""\377\373'"
238 * "\377\375\5""\377\373#""\377\374\1""\377\372\37\0\257\0I\377\360""\377\375\1"):
239 * IAC DO SGA, IAC WILL TTYPE, IAC WILL NAWS, IAC WILL TSPEED, IAC WILL LFLOW, IAC WILL LINEMODE, IAC WILL NEW_ENVIRON,
240 * IAC DO STATUS, IAC WILL XDISPLOC, IAC WONT ECHO, IAC SB NAWS <cols> <rows> IAC SE, IAC DO ECHO
241 */ 495 */
242 if (wr <= 1) { 496 if (found)
243 /* Only the single IAC byte is in the buffer, eat it 497 wr = found - buf;
244 * and set a flag "process the rest of the sequence 498
245 * next time we are here". 499 /* We map \r\n ==> \r for pragmatic reasons:
246 */ 500 * many client implementations send \r\n when
247 //bb_error_msg("dangling IAC!"); 501 * the user hits the CarriageReturn key.
248 ts->buffered_IAC_for_pty = 1; 502 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
249 rc = 1;
250 goto update_and_return;
251 }
252
253 handle_iac:
254 /* 2-byte commands (240..250 and 255):
255 * IAC IAC (255) Literal 255. Supported.
256 * IAC SE (240) End of subnegotiation. Treated as NOP.
257 * IAC NOP (241) NOP. Supported.
258 * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()?
259 * IAC AYT (246) Are you there.
260 * These don't look useful:
261 * IAC DM (242) Data mark. What is this?
262 * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C).
263 * IAC AO (245) Abort output. "You can continue running, but do not send me the output".
264 * IAC EC (247) Erase character. The receiver should delete the last received char.
265 * IAC EL (248) Erase line. The receiver should delete everything up tp last newline.
266 * IAC GA (249) Go ahead. For half-duplex lines: "now you talk".
267 * Implemented only as part of NAWS:
268 * IAC SB (250) Subnegotiation of an option follows.
269 */ 503 */
270 if (buf[1] == IAC) { 504 found = memchr(buf, '\r', wr);
271 /* Literal 255 (emacs M-DEL) */ 505 if (found)
272 //bb_error_msg("255!"); 506 wr = found - buf + 1; /* write up to and including \r */
273 rc = safe_write(ts->ptyfd, &buf[1], 1); 507 rc = safe_write(ts->write_fd, buf, wr);
274 /* 508 if (rc <= 0)
275 * If we went through buffered_IAC_for_pty==1 path, 509 return 0; /* "didn't write anything" */
276 * bailing out on error like below messes up the buffer. 510 //dbg_iac("wrote: %s", bin_to_hex(buf, rc));
277 * EAGAIN is highly unlikely here, other errors will be 511 /* check is needed: imagine that write was _short_ */
278 * repeated on next write, let's just skip error check. 512 if (buf[rc - 1] == '\r') /* last written byte was CR? */
279 */ 513 ts->skip_LF_or_NUL = 1;
280#if 0 514
281 if (rc <= 0) 515 BUF2PTY_INC(ts->wridx, rc);
282 return rc; 516 ts->size -= rc;
283#endif 517 if (ts->size == 0) { /* very typical */
284 rc = 2; 518 /* Avoid buffer wrapping most of the time (-> have bigger writes) */
285 goto update_and_return; 519 //dbg_buffer("zero size");
520 ts->rdidx = 0;
521 ts->wridx = 0;
286 } 522 }
287 if (buf[1] == AYT) { 523 return rc;
288 if (ts->size2 == 0) { /* if nothing buffered yet... */ 524}
289 /* Send back evidence that AYT was seen */ 525
290 unsigned char *buf2 = TS_BUF2(ts); 526/* Check if buffer has space to read into */
291 buf2[0] = IAC; 527static int net_to_pty__have_buffer_to_read_into(void *this)
292 buf2[1] = NOP; 528{
293 ts->wridx2 = 0; 529 //connection_t *conn = this;
294 ts->rdidx2 = ts->size2 = 2; 530 net_to_pty_t *ts = this;
531
532 /* the sibling may have closed our ptyfd after getting 10 EIO's */
533 if (ts->write_fd < 0
534 || ts->shell_pid < 0 /* -K option, and our pty's pid has exited */
535 ) {
536 if (ts->sibling) {
537 /* do not close our netfd, it's in use by sibling */
538 ts->read_fd = -1;
295 } 539 }
296 rc = 2; 540 dbg_close("%s:%d: remove_and_free_to_pty:%p wr:%d pid:%d",
297 goto update_and_return; 541 __func__, __LINE__, ts, ts->write_fd, ts->shell_pid);
542 remove_and_free_to_pty(ts);
543 return -1; /* "don't use me anymore, I'm gone" */
298 } 544 }
299 if (buf[1] >= 240 && buf[1] <= 249) { 545
300 /* NOP (241). Ignore (putty keepalive, etc) */ 546 if (ts->read_fd < 0) { /* we've seen EOF/error on netfd */
301 /* All other 2-byte commands also treated as NOPs here */ 547 dbg_close("EOF on netfd was seen, ts->size:%d", ts->size);
302 rc = 2; 548 if (ts->size == 0) { /* we wrote everything */
303 goto update_and_return; 549 unsigned rem;
550 /* close ptyfd, but after a 20 ms pause */
551
552 /* pty has a design problem: close(master_ptyfd)
553 * is defined as causing hangup on slave pty.
554 * Which discards all currently buffered unread data
555 * (in our case, all data we just wrote).
556 * usleep(20000) is a crude solution.
557 * A better one (does not block other conns):
558 */
559 dbg_close("going to close ptyfd:%d deadline_us:%u",
560 ts->write_fd, ts->deadline_us);
561 if (!ts->deadline_us) {
562 fabricate_ctrl_D_on_pty(ts->write_fd);
563 ts->deadline_us = (monotonic_us() + 20000) | 1;
564 rem = 20000;
565 } else {
566 rem = ts->deadline_us - monotonic_us();
567 }
568 dbg_close("until ptyfd_close:%d", rem);
569 if (rem <= 20000) {
570 ioloop_decrease_current_timeout(ts->io, rem);
571 return 0; /* "do not read" */
572 }
573 /* rem undeflowed (is "negative"): we waited at least 1000us */
574 dbg_close("EOF on netfd, closing ptyfd:%d", ts->write_fd);
575 if (ts->sibling)
576 ts->sibling->read_fd = -1;
577 remove_and_free_to_pty(ts); /* closes ts->write_fd (ptyfd) */
578 return -1; /* "don't use me anymore, I'm gone" */
579 }
580 return 0; /* "don't want to read" */
304 } 581 }
305 582
306 if (wr <= 2) { 583 return ts->size < TO_PTY_BUFSIZE;
307/* BUG: only 2 bytes of the IAC is in the buffer, we just eat them. 584}
308 * This is not a practical problem since >2 byte IACs are seen only 585
309 * in initial negotiation, when buffer is empty 586/* Read from socket to buffer, stripping trailing NUL if present */
310 */ 587static int net_to_pty__read(void *this)
311 rc = 2; 588{
312 goto update_and_return; 589 net_to_pty_t *ts = this;
590 ssize_t count;
591 int avail;
592
593 /* Calculate available space and contiguous segment */
594 avail = TO_PTY_BUFSIZE - ts->size;
595 //if (avail <= 0)
596 // return 0; /* buffer full */
597 // ioloop logic ensures this is false
598
599 /* Read into contiguous segment from rdidx */
600 count = MIN(TO_PTY_BUFSIZE - ts->rdidx, avail);
601 count = safe_read(ts->read_fd, BUF2PTY(ts) + ts->rdidx, count);
602 if (count <= 0) {
603 dbg_close("EOF/error on netfd:%d sibling:%p", ts->read_fd, ts->sibling);
604 /* There is no way to signal to pty that input is in "error state",
605 * therefore treat both EOF and error the same.
606 */
607 if (!ts->sibling)
608 close(ts->read_fd); /* close netfd if not in use by sibling */
609 ts->read_fd = -1;
610 return 0; /* "didn't read anything" */
313 } 611 }
314 612
315 if (buf[1] == SB) { 613 ts->size += count;
316 if (buf[2] == TELOPT_NAWS) { 614 BUF2PTY_INC(ts->rdidx, count);
317 /* IAC SB, TELOPT_NAWS, 4-byte, IAC SE */ 615
318 struct winsize ws; 616 return count;
319 if (wr <= 6) { 617}
320/* BUG: incomplete, can't process */ 618
321 rc = wr; 619static net_to_pty_t *new_net_to_pty(int from, int to)
322 goto update_and_return; 620{
621 net_to_pty_t *this = xzalloc(sizeof(*this) + TO_PTY_BUFSIZE);
622 this->have_buffer_to_read_into = net_to_pty__have_buffer_to_read_into;
623 this->have_data_to_write = net_to_pty__have_data_to_write;
624 this->read = net_to_pty__read;
625 this->write = net_to_pty__write;
626 this->read_fd = from;
627 this->write_fd = to;
628 /* indexes and size are all 0 */
629 return this;
630}
631
632static int pty_to_net__have_buffer_to_read_into(void *this)
633{
634 //connection_t *conn = this;
635 pty_to_net_t *ts = this;
636
637 if (ts->read_fd < 0 /* we saw fatal read error before */
638 || ts->shell_pid < 0 /* -K option, and our pty's pid has exited */
639 ) {
640 if (ts->size == 0) { /* output flushed? */
641 remove_and_free_to_net(ts);
642 return -1; /* "don't use me anymore, I'm gone" */
643 }
644 return 0; /* "don't read" */
645 }
646 if (ts->eio_count != 0) {
647 unsigned rem;
648
649 //if (ts->eio_count >= 10)
650 // return 0; /* "do not read" */
651 //^^^ can't reach, caught by "ts->read_fd < 0" above.
652
653 /* last read was EIO. Still waiting before retry? */
654 rem = ts->deadline_us - monotonic_us();
655 if (rem <= 1000) {
656 /* tell io loop that poll timeout should now be "rem" us, not infinity */
657 ioloop_decrease_current_timeout(ts->io, rem);
658 return 0; /* "do not read me (yet)" */
659 }
660 /* else: rem underflowed: 1000us passed, can try reading again */
661 }
662 return ts->size < TO_NET_BUFSIZE;
663}
664
665/* Read from pty to buffer, handling vhangup() EIO retries */
666static int pty_to_net__read(void *this)
667{
668 //connection_t *conn = this;
669 pty_to_net_t *ts = this;
670 ssize_t count;
671 int avail;
672
673 /* Calculate available space and contiguous segment */
674 avail = TO_NET_BUFSIZE - ts->size;
675 //if (avail <= 0)
676 // return 0; /* buffer full */
677 // io loop logic ensures this is false
678
679 /* Read into contiguous segment from rdidx */
680 count = MIN(TO_NET_BUFSIZE - ts->rdidx, avail);
681 count = safe_read(ts->read_fd, BUF2NET(ts) + ts->rdidx, count);
682 if (count <= 0) {
683 if (count < 0) {
684 if (errno == EAGAIN)
685 return 0;
686 /* login process might call vhangup(),
687 * which causes intermittent EIOs on read above
688 * (observed on kernel 4.12.0, not seen on 5.18.0).
689 * Try up to 10*1000 us.
690 */
691 if (errno == EIO) {
692 ts->eio_count++;
693 dbg_close("EIO ptyfd:%d ts->eio_count:%d %06u",
694 ts->read_fd, ts->eio_count,
695 (unsigned)(monotonic_us() % 1000000)
696 );
697 if (ts->eio_count >= 10)
698 goto close_ptyfd;
699 ts->deadline_us = monotonic_us() + 1000;
700 return 0; /* "read nothing" */
323 } 701 }
324 memset(&ws, 0, sizeof(ws)); /* pixel sizes are set to 0 */ 702 dbg_close("error on ptyfd:%d %m", ts->read_fd);
325 ws.ws_col = (buf[3] << 8) | buf[4]; 703 /* (what other read errors from pty are possible, apart from EIO?) */
326 ws.ws_row = (buf[5] << 8) | buf[6]; 704 ts->eio_count = 10; /* "fatally bad error" */
327 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); 705 goto close_ptyfd;
328 rc = 7;
329 /* trailing IAC SE will be eaten separately, as 2-byte NOP */
330 goto update_and_return;
331 } 706 }
332 /* else: other subnegs not supported yet */ 707 /* EOF. (Doesn't happen with master ptys on Linux when slave closes - always EIO instead?) */
708 dbg_close("EOF on ptyfd:%d", ts->read_fd);
709 ts->eio_count = 0;
710 close_ptyfd:
711 dbg_close("closing ptyfd:%d", ts->read_fd);
712 close(ts->read_fd);
713 if (ts->sibling)
714 /* net2pty pipe needs to close (nowhere to write its data) */
715 ts->sibling->write_fd = -1;
716 ts->read_fd = -1;
717 /* Returning -1 here would mean "do not try writing to net", which is wrong */
718 return 0; /* EOF or error, but we do not prevent draining to net */
333 } 719 }
720 ts->eio_count = 0;
334 721
335 /* Assume it is a 3-byte WILL/WONT/DO/DONT 251..254 command and skip it */ 722 ts->size += count;
336#if DEBUG 723 BUF2NET_INC(ts->rdidx, count);
337 fprintf(stderr, "Ignoring IAC %s,%s\n", 724
338 TELCMD(buf[1]), TELOPT(buf[2])); 725 return count;
339#endif 726}
340 rc = 3; 727
341 728static int pty_to_net__have_data_to_write(void *this)
342 update_and_return: 729{
343 ts->wridx1 += rc; 730 pty_to_net_t *ts = this;
344 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ 731 if (ts->size == 0) {
345 ts->wridx1 = 0; 732 /* Wrote everything and pty is dead? */
346 ts->size1 -= rc; 733 if (ts->read_fd < 0) {
347 /* 734 dbg_close("%s:%d: remove_and_free_to_net:%p rd:%d eio_count:%d",
348 * Hack. We cannot process IACs which wrap around buffer's end. 735 __func__, __LINE__, ts, ts->read_fd, ts->eio_count);
349 * Since properly fixing it requires writing bigger code, 736 if (ts->sibling)
350 * we rely instead on this code making it virtually impossible 737 ts->write_fd = -1;
351 * to have wrapped IAC (people don't type at 2k/second). 738 remove_and_free_to_net(ts);
352 * It also allows for bigger reads in common case. 739 return -1; /* "I'm gone" */
353 */ 740 }
354 if (ts->size1 == 0) { /* very typical */
355 //bb_error_msg("zero size1");
356 ts->rdidx1 = 0;
357 ts->wridx1 = 0;
358 return rc;
359 }
360 wr = ts->wridx1;
361 if (wr != 0 && wr < ts->rdidx1) {
362 /* Buffer is not wrapped yet.
363 * We can easily move it to the beginning.
364 */
365 //bb_error_msg("moved %d", wr);
366 memmove(TS_BUF1(ts), TS_BUF1(ts) + wr, ts->size1);
367 ts->rdidx1 -= wr;
368 ts->wridx1 = 0;
369 } 741 }
370 return rc; 742 return ts->size > 0 || ts->respond_to_AYT || ts->extra_byte_to_write;
371} 743}
372 744
373/* 745/* Write to socket from buffer, escaping IAC bytes */
374 * Converting single IAC into double on output 746static int pty_to_net__write(void *this)
375 */
376static size_t safe_write_double_iac(int fd, const char *buf, size_t count)
377{ 747{
378 const char *IACptr; 748 pty_to_net_t *ts = this;
379 size_t wr, rc, total; 749 ssize_t count;
750 unsigned char *buf;
751 unsigned char *iac_ptr;
752 size_t wr;
753
754 /* Did we fail to send a byte last time? */
755 if (ts->extra_byte_to_write) {
756 count = safe_write(ts->write_fd, &ts->extra_byte_to_write, 1);
757 if (count == 1)
758 ts->extra_byte_to_write = 0;
759 goto ret;
760 }
380 761
381 total = 0; 762 if (ts->size == 0) {
382 while (1) { 763 if (ts->respond_to_AYT) { /* other side pinged us? */
383 if (count == 0) 764 /* Send back evidence that AYT was seen: IAC NOP */
384 return total; 765 static const char ayt_response[2] = { IAC, NOP };
385 if (*buf == (char)IAC) { 766 count = safe_write(ts->write_fd, ayt_response, 2);
386 static const char IACIAC[] ALIGN1 = { IAC, IAC }; 767 if (count >= 1) {
387 rc = safe_write(fd, IACIAC, 2); 768 if (count == 1) /* goddamit */
388/* BUG: if partial write was only 1 byte long, we end up emitting just one IAC */ 769 ts->extra_byte_to_write = NOP;
389 if (rc != 2) 770 ts->respond_to_AYT = 0;
390 break; 771 }
391 buf++; 772 goto ret;
392 total++;
393 count--;
394 continue;
395 } 773 }
396 /* count != 0, *buf != IAC */ 774 return 0;
397 IACptr = memchr(buf, IAC, count); 775 }
398 wr = count; 776
399 if (IACptr) 777 buf = BUF2NET(ts) + ts->wridx;
400 wr = IACptr - buf; 778 wr = MIN(TO_NET_BUFSIZE - ts->wridx, ts->size);
401 rc = safe_write(fd, buf, wr); 779 dbg("rem:%d sz:%d ch:0x%02x", TO_NET_BUFSIZE - ts->wridx, ts->size, *buf);
402 if (rc != wr) 780
403 break; 781 if (*buf == IAC) {
404 buf += rc; 782 static const char IACIAC[2] ALIGN1 = { IAC, IAC };
405 total += rc; 783 count = safe_write(ts->write_fd, IACIAC, 2);
406 count -= rc; 784 if (count <= 1) {
785 if (count < 1) /* wrote nothing? */
786 goto ret;
787 /* Wrote only one byte of two. Amazing... */
788 ts->extra_byte_to_write = IAC;
789 }
790 /* Consume one byte from buffer */
791 count = 1;
792 } else {
793 /* Find next IAC or write up to end of segment */
794 iac_ptr = memchr(buf, IAC, wr);
795 if (iac_ptr)
796 wr = iac_ptr - buf;
797 count = safe_write(ts->write_fd, buf, wr);
798 if (count <= 0)
799 goto ret;
800 }
801
802 BUF2NET_INC(ts->wridx, count);
803 ts->size -= count;
804 if (ts->size == 0) { /* very typical */
805 /* Avoid buffer wrapping most of the time (-> have bigger writes) */
806 //dbg_buffer("zero size");
807 ts->rdidx = 0;
808 ts->wridx = 0;
407 } 809 }
408 /* here: rc - result of last short write */ 810 ret:
409 if ((ssize_t)rc < 0) { /* error? */ 811 if (count >= 0)
410 if (total == 0) 812 return count; /* "not error" */
411 return rc; 813 if (errno == EAGAIN)
412 rc = 0; 814 return 0; /* "not error" */
815
816 /* Assuming fatal write error */
817 /* If peer closes its read side with
818 * shutdown(SHUT_RD), we'll see EPIPE
819 * or ECONNRESET.
820 * (both were seen to occur in practice)
821 */
822 if (ts->sibling) {
823 /* Sibling exists, don't close yet */
824 ts->read_fd = -1;
825 ts->write_fd = -1;
413 } 826 }
414 return total + rc; 827 dbg_close("%s:%d: remove_and_free_to_net:%p", __func__, __LINE__, ts);
828 remove_and_free_to_net(ts);
829 return -1; /* "I'm gone" */
415} 830}
416 831
417/* Must match getopt32 string */ 832static pty_to_net_t *new_pty_to_net(int from, int to)
418enum { 833{
419 OPT_WATCHCHILD = (1 << 2), /* -K */ 834 pty_to_net_t *this = xzalloc(sizeof(*this) + TO_NET_BUFSIZE);
420 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ 835 this->have_buffer_to_read_into = pty_to_net__have_buffer_to_read_into;
421 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */ 836 this->have_data_to_write = pty_to_net__have_data_to_write;
422 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ 837 this->read = pty_to_net__read;
423 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */ 838 this->write = pty_to_net__write;
424 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */ 839 this->read_fd = from;
425}; 840 this->write_fd = to;
841 /* indexes and size are all 0 */
842 return this;
843}
426 844
427static struct tsession *
428make_new_session(
429 IF_FEATURE_TELNETD_STANDALONE(int sock)
430 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
431) {
432#if !ENABLE_FEATURE_TELNETD_STANDALONE 845#if !ENABLE_FEATURE_TELNETD_STANDALONE
433 enum { sock = 0 }; 846#define make_new_session(io, sockrd) \
847 make_new_session(io)
434#endif 848#endif
849static void make_new_session(ioloop_state_t *io, int sockrd)
850{
851 IF_NOT_FEATURE_TELNETD_STANDALONE(enum { sockrd = 0 };)
852 const int sockwr = sockrd != 0 ? sockrd : 1;
435 const char *login_argv[2]; 853 const char *login_argv[2];
436 struct termios termbuf; 854 struct termios termbuf;
437 int fd, pid; 855 int ptyfd, pid;
438 char tty_name[GETPTY_BUFSIZE]; 856 char tty_name[GETPTY_BUFSIZE];
439 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
440 857
441 /*ts->buf1 = (char *)(ts + 1);*/ 858 /* Got a new connection, set up a pty */
442 /*ts->buf2 = ts->buf1 + BUFSIZE;*/ 859 ptyfd = xgetpty(tty_name);
443 860 ndelay_on(ptyfd);
444 /* Got a new connection, set up a tty */ 861 close_on_exec_on(ptyfd);
445 fd = xgetpty(tty_name);
446 if (fd > G.maxfd)
447 G.maxfd = fd;
448 ts->ptyfd = fd;
449 ndelay_on(fd);
450 close_on_exec_on(fd);
451 862
452 /* SO_KEEPALIVE by popular demand */ 863 /* SO_KEEPALIVE by popular demand */
453 setsockopt_keepalive(sock); 864 setsockopt_keepalive(sockrd);
454#if ENABLE_FEATURE_TELNETD_STANDALONE 865 ndelay_on(sockrd);
455 ts->sockfd_read = sock; 866 if (sockrd == 0) /* called with fd 0 (inetd mode?) */
456 ndelay_on(sock); 867 ndelay_on(sockwr);
457 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
458 sock++; /* so use fd 1 for output */
459 ndelay_on(sock);
460 }
461 ts->sockfd_write = sock;
462 if (sock > G.maxfd)
463 G.maxfd = sock;
464#else
465 /* ts->sockfd_read = 0; - done by xzalloc */
466 ts->sockfd_write = 1;
467 ndelay_on(0);
468 ndelay_on(1);
469#endif
470 868
471 /* Make the telnet client understand we will echo characters so it 869 /* Make the telnet client understand we will echo characters so it
472 * should not do it locally. We don't tell the client to run linemode, 870 * should not do it locally. We don't tell the client to run linemode,
@@ -474,41 +872,48 @@ make_new_session(
474 * stuff that requires char-by-char support. */ 872 * stuff that requires char-by-char support. */
475 { 873 {
476 static const char iacs_to_send[] ALIGN1 = { 874 static const char iacs_to_send[] ALIGN1 = {
477 IAC, DO, TELOPT_ECHO, 875 IAC, WILL, TELOPT_ECHO, // "I will echo your chars"
478 IAC, DO, TELOPT_NAWS, 876 // (Not really, _we_ won't - our programs in pty usually
479 /* This requires telnetd.ctrlSQ.patch (incomplete) */ 877 // handle that. In practice, this says: "do not echo
480 /*IAC, DO, TELOPT_LFLOW,*/ 878 // your user's input chars back to him _on your side_".)
481 IAC, WILL, TELOPT_ECHO, 879 IAC, WILL, TELOPT_SGA, // "I assume full-duplex, won't send GA's"
482 IAC, WILL, TELOPT_SGA 880 //IAC, DO, TELOPT_ECHO, //WRONG: "can you echo my chars to me"??
881 IAC, DO, TELOPT_NAWS, // "can you send me terminal size data?"
882 // This requires telnetd.ctrlSQ.patch (incomplete):
883 //IAC, DO, TELOPT_LFLOW,
483 }; 884 };
484 /* This confuses safe_write_double_iac(), it will try to duplicate 885//Theoretically, our "WILL X" are requests and should only activate when client responds with "DO X".
485 * each IAC... */ 886//However, we do not wait/check for "DO"s. Why?
486 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send)); 887//There is nothing to "activate" on our side for TELOPT_ECHO.
487 //ts->rdidx2 = sizeof(iacs_to_send); 888//For TELOPT_SGA, we don't even have code to support sending/understanding GAs,
488 //ts->size2 = sizeof(iacs_to_send); 889//so SGA is "always activated".
489 /* So just stuff it into TCP stream! (no error check...) */ 890 /* Just stuff it into TCP stream! (no error check...) */
490#if ENABLE_FEATURE_TELNETD_STANDALONE 891 safe_write(sockwr, iacs_to_send, sizeof(iacs_to_send));
491 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
492#else
493 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
494#endif
495 /*ts->rdidx2 = 0; - xzalloc did it */
496 /*ts->size2 = 0;*/
497 } 892 }
498 893
499 fflush_all(); 894 fflush_all();
500 pid = vfork(); /* NOMMU-friendly */ 895 pid = vfork(); /* NOMMU-friendly */
501 if (pid < 0) { 896 if (pid < 0) {
502 free(ts); 897 close(ptyfd);
503 close(fd); 898 close(sockrd);
504 /* sock will be closed by caller */
505 bb_simple_perror_msg("vfork"); 899 bb_simple_perror_msg("vfork");
506 return NULL; 900 return;
507 } 901 }
508 if (pid > 0) { 902 if (pid > 0) {
509 /* Parent */ 903 /* Parent */
510 ts->shell_pid = pid; 904 pty_to_net_t *to_net;
511 return ts; 905 net_to_pty_t *to_pty;
906
907 to_net = new_pty_to_net(ptyfd, sockwr);
908 to_pty = new_net_to_pty(sockrd, ptyfd);
909 dbg("to_net:%p to_pty:%p", to_net, to_pty);
910 to_pty->sibling = to_net;
911 to_net->sibling = to_pty;
912 to_net->shell_pid = pid;
913 to_pty->shell_pid = pid;
914 ioloop_insert_conn(io, (connection_t *)to_net);
915 ioloop_insert_conn(io, (connection_t *)to_pty);
916 return;
512 } 917 }
513 918
514 /* Child */ 919 /* Child */
@@ -517,31 +922,30 @@ make_new_session(
517 /* Restore default signal handling ASAP */ 922 /* Restore default signal handling ASAP */
518 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); 923 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
519 924
520 pid = getpid(); 925 /* Make new session and process group */
926 //pid = getpid(); // redundant, setsid gives us our pid
927 pid = setsid();
928
929 /* Open the child's side of the pty */
930 /* NB: setsid() disconnects from any previous ctty's. Therefore
931 * we must open child's side of the tty AFTER setsid! */
932 close(0);
933 xopen(tty_name, O_RDWR); /* fd 0: becomes our ctty */
934 xdup2(0, 1);
935 xdup2(0, 2);
936 tcsetpgrp(0, pid); /* switch this tty's process group to us */
521 937
522 if (ENABLE_FEATURE_UTMP) { 938 if (ENABLE_FEATURE_UTMP) {
523 len_and_sockaddr *lsa = get_peer_lsa(sock); 939 len_and_sockaddr *lsa = get_peer_lsa(sockrd);
524 char *hostname = NULL; 940 char *hostname = NULL;
525 if (lsa) { 941 if (lsa) {
526 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa); 942 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
527 free(lsa); 943 free(lsa);
528 } 944 }
529 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname); 945 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
530 free(hostname); 946 IF_FEATURE_CLEAN_UP(free(hostname);)
531 } 947 }
532 948
533 /* Make new session and process group */
534 setsid();
535
536 /* Open the child's side of the tty */
537 /* NB: setsid() disconnects from any previous ctty's. Therefore
538 * we must open child's side of the tty AFTER setsid! */
539 close(0);
540 xopen(tty_name, O_RDWR); /* becomes our ctty */
541 xdup2(0, 1);
542 xdup2(0, 2);
543 tcsetpgrp(0, pid); /* switch this tty's process group to us */
544
545 /* The pseudo-terminal allocated to the client is configured to operate 949 /* The pseudo-terminal allocated to the client is configured to operate
546 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */ 950 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
547 tcgetattr(0, &termbuf); 951 tcgetattr(0, &termbuf);
@@ -565,8 +969,8 @@ make_new_session(
565 login_argv[1] = NULL; 969 login_argv[1] = NULL;
566 /* exec busybox applet (if PREFER_APPLETS=y), if that fails, 970 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
567 * exec external program. 971 * exec external program.
568 * NB: sock is either 0 or has CLOEXEC set on it. 972 * NB: sockrd is either 0 or has CLOEXEC set on it.
569 * fd has CLOEXEC set on it too. These two fds will be closed here. 973 * ptyfd has CLOEXEC set on it too. These two fds will be closed here.
570 */ 974 */
571 BB_EXECVP(G.loginpath, (char **)login_argv); 975 BB_EXECVP(G.loginpath, (char **)login_argv);
572 /* _exit is safer with vfork, and we shouldn't send message 976 /* _exit is safer with vfork, and we shouldn't send message
@@ -574,123 +978,708 @@ make_new_session(
574 _exit_FAILURE(); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/ 978 _exit_FAILURE(); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
575} 979}
576 980
981
577#if ENABLE_FEATURE_TELNETD_STANDALONE 982#if ENABLE_FEATURE_TELNETD_STANDALONE
983struct accept_conn {
984 TELNET_CONNECTION
985};
578 986
579static void 987static int accept_conn__can_accept(void *this UNUSED_PARAM)
580free_session(struct tsession *ts)
581{ 988{
582 struct tsession *t; 989 return 1;
583 990}
584 if (option_mask32 & OPT_INETD)
585 exit_SUCCESS();
586 991
587 /* Unlink this telnet session from the session list */ 992static int accept_conn__accept(void *this)
588 t = G.sessions; 993{
589 if (t == ts) 994 struct accept_conn *ts = this;
590 G.sessions = ts->next; 995 int fd;
591 else {
592 while (t->next != ts)
593 t = t->next;
594 t->next = ts->next;
595 }
596 996
597#if 0 997 dbg("%s:%d", __func__, __LINE__);
598 /* It was said that "normal" telnetd just closes ptyfd, 998 fd = accept(ts->read_fd, NULL, NULL);
599 * doesn't send SIGKILL. When we close ptyfd, 999//TODO: accept4(SOCK_NONBLOCK) - can remove ndelay_on() in make_new_session
600 * kernel sends SIGHUP to processes having slave side opened. */ 1000 if (fd >= 0) {
601 kill(ts->shell_pid, SIGKILL); 1001 close_on_exec_on(fd);
602 waitpid(ts->shell_pid, NULL, 0); 1002 /* Create two new pipes and insert them into ioloop */
603#endif 1003 make_new_session(ts->io, fd);
604 close(ts->ptyfd);
605 close(ts->sockfd_read);
606 /* We do not need to close(ts->sockfd_write), it's the same
607 * as sockfd_read unless we are in inetd mode. But in inetd mode
608 * we do not reach this */
609 free(ts);
610
611 /* Scan all sessions and find new maxfd */
612 G.maxfd = 0;
613 ts = G.sessions;
614 while (ts) {
615 if (G.maxfd < ts->ptyfd)
616 G.maxfd = ts->ptyfd;
617 if (G.maxfd < ts->sockfd_read)
618 G.maxfd = ts->sockfd_read;
619#if 0
620 /* Again, sockfd_write == sockfd_read here */
621 if (G.maxfd < ts->sockfd_write)
622 G.maxfd = ts->sockfd_write;
623#endif
624 ts = ts->next;
625 } 1004 }
1005 return 0;
626} 1006}
627 1007
628#else /* !FEATURE_TELNETD_STANDALONE */ 1008static int accept_conn__return_zero(void *this UNUSED_PARAM)
629 1009{
630/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */ 1010 return 0;
631#define free_session(ts) return 0 1011}
632 1012
1013static struct accept_conn *new_accept_conn(int fd)
1014{
1015 struct accept_conn *this = xzalloc(sizeof(*this));
1016 this->have_buffer_to_read_into = accept_conn__can_accept;
1017 this->have_data_to_write = accept_conn__return_zero;
1018 this->read = accept_conn__accept;
1019 //this->write = accept_conn__return_zero; //never called
1020 this->read_fd = fd;
1021 this->write_fd = -1;
1022 return this;
1023}
633#endif 1024#endif
634 1025
635static void handle_sigchld(int sig UNUSED_PARAM) 1026static void handle_sigchld(int sig UNUSED_PARAM)
636{ 1027{
637 pid_t pid;
638 struct tsession *ts;
639 int save_errno = errno; 1028 int save_errno = errno;
640 1029
641 /* Looping: more than one child may have exited */ 1030 /* Looping: more than one child may have exited */
642 while (1) { 1031 while (1) {
1032 pid_t pid;
1033 struct telnet_conn *conn;
1034
643 pid = wait_any_nohang(NULL); 1035 pid = wait_any_nohang(NULL);
644 if (pid <= 0) 1036 if (pid <= 0)
645 break; 1037 break;
646 ts = G.sessions; 1038 update_utmp_DEAD_PROCESS(pid);
647 while (ts) { 1039 conn = (void*)G.io.conns;
648 if (ts->shell_pid == pid) { 1040 while (conn) {
649 ts->shell_pid = -1; 1041 if (conn->shell_pid == pid) {
650 update_utmp_DEAD_PROCESS(pid); 1042 /* mark the conn to close soon */
651 break; 1043 conn->shell_pid = -1;
652 } 1044 }
653 ts = ts->next; 1045 conn = (void*)conn->next;
654 } 1046 }
655 } 1047 }
656 1048
657 errno = save_errno; 1049 errno = save_errno;
658} 1050}
659 1051
1052#if ENABLE_FEATURE_TELNETD_SELFTEST_DEBUG
1053
1054static char *bin_to_hex(const void *hash_value, unsigned hash_length)
1055{
1056 /* xzalloc zero-terminates */
1057 char *hex_value = xzalloc((hash_length * 2) + 1);
1058 bin2hex(hex_value, (char*)hash_value, hash_length);
1059 return auto_string(hex_value);
1060}
1061
1062static int test_vhangup(void)
1063{
1064 char tty_name[GETPTY_BUFSIZE];
1065 int master_fd;
1066 int last_rc = 999;
1067 int last_errno = 0;
1068 unsigned long long last_change_ns, base_ns;
1069
1070 bb_simple_info_msg("selftest: pty vhangup() behavior");
1071
1072 master_fd = xgetpty(tty_name);
1073 ndelay_on(master_fd);
1074
1075 fflush_all();
1076 if (xfork() == 0) {
1077 /* Child */
1078 //int slave_fd;
1079 int fd;
1080
1081 /* vhangup() sends SIGHUP to the session, ignore it */
1082 signal(SIGHUP, SIG_IGN);
1083
1084 applet_name = "vhangup child";
1085
1086 close(master_fd);
1087 setsid();
1088 /*slave_fd =*/ xopen(tty_name, O_RDWR);
1089
1090 fd = open("/dev/tty", O_RDWR);
1091 if (fd >= 0) {
1092 bb_simple_info_msg("/dev/tty opened OK - we have a ctty");
1093 close(fd);
1094 } else {
1095 bb_simple_perror_msg("/dev/tty");
1096 }
1097
1098 bb_simple_info_msg("sleeping 0.2 sec before vhangup()");
1099 usleep(200*1000);
1100
1101 bb_simple_info_msg("calling vhangup()");
1102 vhangup();
1103
1104 fd = open("/dev/tty", O_RDWR);
1105 if (fd >= 0) {
1106 bb_simple_info_msg("/dev/tty opened OK - we still have a ctty");
1107 close(fd);
1108 } else {
1109 bb_simple_perror_msg("/dev/tty");
1110 }
1111
1112 bb_simple_info_msg("sleeping 0.2 sec after vhangup()");
1113 usleep(200*1000);
1114
1115 bb_simple_error_msg_and_die("exiting");
1116 }
1117
1118 /* Parent */
1119 bb_simple_info_msg("nonblocking read() from pty master in a loop...");
1120 last_change_ns = monotonic_ns();
1121
1122 base_ns = monotonic_ns();
1123 for (;;) {
1124 int rc;
1125 char buf[1];
1126 unsigned long long now_ns;
1127
1128 errno = 0;
1129 rc = read(master_fd, buf, 1);
1130 now_ns = monotonic_ns() - base_ns;
1131
1132 if (rc != last_rc || errno != last_errno) {
1133 bb_error_msg("t=%lluns: read()=%d errno=%d(%s)",
1134 now_ns,
1135 rc, errno, strerror(errno));
1136 last_rc = rc;
1137 last_errno = errno;
1138 last_change_ns = now_ns;
1139 }
1140
1141 /* Exit if no change for a few seconds */
1142 if ((now_ns - last_change_ns) > 1000*1000*1000) {
1143 bb_simple_info_msg("no change for 1 second, loop stopped");
1144 break;
1145 }
1146
1147 usleep(100); /* 100us polling - fast enough to catch transitions */
1148 }
1149
1150 close(master_fd);
1151 waitpid(-1, NULL, 0);
1152
1153 bb_simple_error_msg("vhangup test completed");
1154 return 0;
1155}
1156static int test_net_to_pty_data_integrity(void)
1157{
1158 enum { TESTDATA_SIZE = 16 * 1024 * 1024 };
1159 unsigned char *input_data;
1160 unsigned char *expected_data;
1161 int input_size;
1162 int expected_size;
1163 uint32_t randbits;
1164 uint8_t expected_hash32[32];
1165 uint8_t hash_in_pty32[32];
1166 uint8_t dummy_byte;
1167 int sock_pair[2];
1168 char tty_name[GETPTY_BUFSIZE];
1169 int ptyfd;
1170 int i;
1171 int waitstatus, exitcode;
1172
1173 bb_simple_info_msg("selftest: net_to_pty");
1174
1175 /* Allocate buffer with paranoia space */
1176 input_data = xzalloc(TESTDATA_SIZE + 64);
1177
1178 /* Generate nightmare input data:
1179 * - Mostly random bytes
1180 * - IAC bytes sprinkled in (1 in 16 probability)
1181 * - IAC IAC sequences (1 in 32)
1182 * - IAC SB TELOPT_NAWS sequences(1 in 32)
1183 * - All sorts of IAC commands with random parameters
1184 * - Malformed sequences
1185 */
1186 randbits = 0;
1187 for (i = 0; i < TESTDATA_SIZE;) {
1188 unsigned _32possibilities;
1189 if (randbits == 0)
1190 randbits = random();
1191 _32possibilities = (randbits & 0x1f);
1192 randbits >>= 5;
1193 if (_32possibilities == 3 && (randbits & 0x7f) == 0) { /* 1 in 32*127=4*1k prob: long run of printables */
1194 if (i < TESTDATA_SIZE - 4*1024) {
1195 int end = i + ((4*1024 - 1) & random());
1196 while (i < end)
1197 input_data[i++] = (random() & 0x3f) + 0x20;
1198 continue;
1199 }
1200 }
1201 if (_32possibilities == 3) { /* CR,LF/NUL: 1 in 32 prob */
1202 input_data[i++] = '\r';
1203 input_data[i++] = randbits & 1 ? '\n' : '\0';
1204 } else
1205 if (_32possibilities == 2) { /* NAWS: 1 in 32 prob */
1206 input_data[i++] = IAC;
1207 input_data[i++] = SB;
1208 input_data[i++] = TELOPT_NAWS;
1209 } else
1210 if (_32possibilities == 1) { /* IAC: 1 in 32 prob */
1211 input_data[i++] = IAC;
1212 if (random() & 1) /* IAC IAC: 1 in 64 prob */
1213 input_data[i++] = IAC;
1214 } else {
1215 input_data[i++] = random();
1216 }
1217 }
1218 input_size = TESTDATA_SIZE;
1219 bb_error_msg("generated %d bytes of test data", input_size);
1220
1221 /* Allocate expected output buffer */
1222 expected_data = xmalloc(TESTDATA_SIZE);
1223
1224 /* Process input_data simulating net_to_pty transformations:
1225 * - IAC IAC → single IAC
1226 * - IAC SB TELOPT_NAWS [4 bytes] IAC SE → stripped
1227 * - Other IAC sequences → stripped
1228 * - \r\n → \r (skip the \n)
1229 * - \r\0 → \r (skip the \0)
1230 */
1231 memset(input_data + input_size, 0, 64);
1232 expected_size = 0;
1233 i = 0;
1234 while (i < input_size) {
1235 /* Tail of 64 extra zero bytes allows to safely read beyond the end */
1236 if (input_data[i] == IAC) {
1237 if (input_data[i + 1] == IAC) {
1238 /* IAC IAC → single IAC */
1239 expected_data[expected_size++] = IAC;
1240 i += 2;
1241 continue;
1242 }
1243 if (input_data[i + 1] >= 240 && input_data[i + 1] <= 249) {
1244 /* 2-byte IAC command (NOP, etc.) - skip */
1245 i += 2;
1246 continue;
1247 }
1248 if (input_data[i + 1] == SB) {
1249 /* IAC SB ... skip subnegotiation */
1250 if (input_data[i + 2] == TELOPT_NAWS) {
1251//TODO: simulate unusual NAWS
1252 /* IAC SB TELOPT_NAWS [4 bytes] */
1253 i += 3; /* skip IAC SB TELOPT_NAWS */
1254 /* Skip 4 bytes of window size */
1255 i += 4;
1256 continue;
1257// } else {
1258//should do: /* Other subneg, skip to IAC SE */ but that's not what telnetd code is doing yet...
1259 }
1260 }
1261 /* Assumed 3-byte IAC WILL/WONT/DO/DONT - skip */
1262 i += 3;
1263 continue;
1264 }
1265 if (input_data[i] == '\r') {
1266 /* Write \r, check if followed by \n or \0 */
1267 expected_data[expected_size++] = '\r';
1268 i++;
1269 if (input_data[i] == '\n' || input_data[i] == '\0')
1270 i++; /* skip \n or \0 after \r */
1271 continue;
1272 }
1273 /* Regular byte */
1274 expected_data[expected_size++] = input_data[i++];
1275 }
1276 sha256_block(expected_data, expected_size, expected_hash32);
1277 bb_error_msg("expected %d bytes after net_to_pty processing. hash:%s", expected_size, bin_to_hex(expected_hash32, 32));
1278#if SAVE_NET2PTY_EXPECTED
1279 {
1280 int fd = xopen("expected.bin", O_WRONLY | O_CREAT | O_TRUNC);
1281 xwrite(fd, expected_data, expected_size);
1282 close(fd);
1283 bb_error_msg("wrote expected.bin");
1284 }
1285#endif
1286 free(expected_data);
1287 expected_data = NULL; /* to catch use-after-free */
1288
1289 /* Create socketpair for network side */
1290 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair) < 0)
1291 bb_simple_perror_msg_and_die("socketpair");
1292
1293 fflush_all();
1294 if (xfork() == 0) {
1295 /* Fork writer child: writes telnet data to socket in random chunks */
1296 int written = 0;
1297 close(sock_pair[0]); /* close read end */
1298
1299 applet_name = "tty input";
1300
1301 while (written < input_size) {
1302 int chunk = (random() & 4095) + 1; /* 1 to 4096 bytes */
1303 if (written + chunk > input_size)
1304 chunk = input_size - written;
1305
1306 chunk = safe_write(sock_pair[1], input_data + written, chunk);
1307 if (chunk > 0)
1308 written += chunk;
1309
1310 /* Small random delay to vary timing */
1311 if ((random() & 0xf) == 0)
1312 usleep(random() & 0x3ff);
1313 }
1314 _exit(0);
1315 }
1316 close(sock_pair[1]); /* close write end */
1317
1318 free(input_data);
1319 input_data = NULL; /* to catch use-after-free */
1320
1321 /* Create pty pair */
1322 ptyfd = xgetpty(tty_name);
1323
1324 if (xfork() == 0) {
1325 /* Fork reader child: reads from PTY slave */
1326 int slave_fd;
1327 int retry;
1328 int n;
1329 unsigned char read_buf[8192];
1330 int total_read = 0;
1331 struct termios termbuf;
1332 sha256_ctx_t ctx;
1333#if SAVE_NET2PTY_ACTUAL
1334 int out_fd = xopen("actual.bin", O_WRONLY | O_CREAT | O_TRUNC);
1335#endif
1336 applet_name = "pty reader";
1337
1338 close(sock_pair[0]);
1339 close(ptyfd);
1340
1341 slave_fd = xopen(tty_name, O_RDWR);
1342 setsid(); /* this loses ctty (no tty signals ^C, ^Z, ^\; no SIGHUP on master close (probably?)) */
1343
1344 tcgetattr(slave_fd, &termbuf);
1345 cfmakeraw(&termbuf);
1346 tcsetattr(slave_fd, TCSANOW, &termbuf);
1347 //bb_error_msg("tty_name:'%s'", tty_name);
1348
1349 /* Synchronize with master: "raw set, you can start writing" */
1350 dummy_byte = '!';
1351 safe_write(slave_fd, &dummy_byte, 1);
1352
1353 sha256_begin(&ctx);
1354 /* Retry logic shows how master pty close looks on slave side:
1355 * on kernel 5.18.0, I see EIO once, then (if I retry reads),
1356 * I see EOFs (zero reads).
1357 * What is not visible (but does happen) is that any
1358 * unread-by-me buffered data is lost - something that
1359 * wouldn't happen on pipes/socketpairs!
1360 */
1361 retry = 0;
1362 for (;;) {
1363 n = safe_read(slave_fd, read_buf, sizeof(read_buf));
1364 if (n == 0) {
1365 retry++;
1366 bb_error_msg("EOF on read(%d)", retry);
1367 if (retry < 3) continue;
1368 break; /* EOF (parent closed master) - expected */
1369 }
1370 if (n < 0) {
1371 retry++;
1372 bb_perror_msg("read(%d)", retry);
1373 if (retry < 3) continue;
1374 break; /* error (parent closed master) - expected */
1375 }
1376 /* If happens, probably kernel bug! */
1377 if (retry != 0) bb_error_msg_and_die("READ DATA AFTER EOF/ERROR RETRY!!!");
1378#if SAVE_NET2PTY_ACTUAL
1379 xwrite(out_fd, read_buf, n);
1380#endif
1381 sha256_hash(&ctx, read_buf, n);
1382 total_read += n;
1383 // Test premature close on pty read side:
1384 //if (total_read >= 128*1024) _exit(0);
1385 }
1386
1387 sha256_end(&ctx, hash_in_pty32);
1388 n = memcmp(hash_in_pty32, expected_hash32, 32);
1389 bb_error_msg("received %d bytes, hash:%s%s", total_read, bin_to_hex(hash_in_pty32, 32), n ? " MISMATCH" : "");
1390 _exit(!!n);
1391 }
1392
1393 /* Parent: run the net_to_pty logic */
1394 {
1395 net_to_pty_t *conn;
1396 ioloop_state_t *io;
1397 int rc;
1398
1399 io = new_ioloop_state();
1400 conn = new_net_to_pty(sock_pair[0], ptyfd);
1401 ioloop_insert_conn(io, (void*)conn);
1402
1403 /* We must allow the pty child to change its tty to RAW
1404 * before we start writing. Otherwise (it was observed),
1405 * it can see a few \r -> \n remapped.
1406 */
1407 safe_read(ptyfd, &dummy_byte, 1);
1408
1409 rc = ioloop_run(io);
1410 bb_error_msg("ioloop_run:%d", rc);
1411
1412 /* This should be done inside ioloop. Check where possible: */
1413 //free_connection(conn);
1414 if (close(sock_pair[0]) == 0) /* should be EBADF */
1415 bb_error_msg_and_die("BUG: net read side wasn't closed");
1416 if (close(ptyfd) == 0)
1417 bb_error_msg_and_die("BUG: pty write side wasn't closed");
1418
1419 free_ioloop_state(io);
1420 }
1421
1422 /* Wait for all children */
1423 exitcode = 0;
1424 while (safe_waitpid(-1, &waitstatus, 0) > 0)
1425 exitcode |= (waitstatus != 0);
1426
1427 bb_error_msg("pty_to_net test completed: %d", exitcode);
1428 return exitcode;
1429}
1430
1431static int test_pty_to_net_data_integrity(void)
1432{
1433 enum { TESTDATA_SIZE = 16 * 1024 * 1024 };
1434 unsigned char *input_data;
1435 unsigned char *expected_data;
1436 int input_size;
1437 int expected_size;
1438 uint32_t randbits;
1439 uint8_t expected_hash32[32];
1440 uint8_t hash_from_net32[32];
1441 int sock_pair[2];
1442 char tty_name[GETPTY_BUFSIZE];
1443 int ptyfd;
1444 int i;
1445 int waitstatus, exitcode;
1446
1447 bb_simple_info_msg("selftest: pty_to_net");
1448
1449 /* Allocate buffer */
1450 input_data = xzalloc(TESTDATA_SIZE);
1451
1452 /* Generate random data - this time we want lots of 0xFF (IAC) bytes
1453 * to test IAC escaping (IAC → IAC IAC)
1454 */
1455 randbits = 0;
1456 for (i = 0; i < TESTDATA_SIZE;) {
1457 unsigned _32possibilities;
1458 if (randbits == 0)
1459 randbits = random();
1460 _32possibilities = (randbits & 0x1f);
1461 randbits >>= 5;
1462
1463 if (_32possibilities == 1 && (randbits & 0x7f) == 0) {
1464 /* Long run of printables (1 in ~4k prob) */
1465 if (i < TESTDATA_SIZE - 4*1024) {
1466 int end = i + ((4*1024 - 1) & random());
1467 while (i < end)
1468 input_data[i++] = (random() & 0x3f) + 0x20;
1469 continue;
1470 }
1471 }
1472 if (_32possibilities == 1) {
1473 /* IAC byte: 1 in 32 prob */
1474 input_data[i++] = IAC;
1475 } else {
1476 input_data[i++] = random();
1477 }
1478 }
1479 input_size = TESTDATA_SIZE;
1480 bb_error_msg("generated %d bytes of test data", input_size);
1481
1482 /* Allocate expected output buffer (worst case: all IACs doubled) */
1483 expected_data = xmalloc(TESTDATA_SIZE * 2);
1484
1485 /* Process input_data simulating pty_to_net transformations:
1486 * - IAC (0xFF) → IAC IAC (doubled)
1487 * - All other bytes pass through unchanged
1488 */
1489 expected_size = 0;
1490 for (i = 0; i < input_size; i++) {
1491 if (input_data[i] == IAC) {
1492 expected_data[expected_size++] = IAC;
1493 expected_data[expected_size++] = IAC;
1494 } else {
1495 expected_data[expected_size++] = input_data[i];
1496 }
1497 }
1498
1499 sha256_block(expected_data, expected_size, expected_hash32);
1500 bb_error_msg("expected %d bytes after pty_to_net processing. hash:%s",
1501 expected_size, bin_to_hex(expected_hash32, 32));
1502
1503#if SAVE_PTY2NET_EXPECTED
1504 {
1505 int fd = xopen("expected.bin", O_WRONLY | O_CREAT | O_TRUNC);
1506 xwrite(fd, expected_data, expected_size);
1507 close(fd);
1508 bb_error_msg("wrote expected.bin");
1509 }
1510#endif
1511
1512 free(expected_data);
1513 expected_data = NULL;
1514
1515 /* Create socketpair for network side */
1516 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair) < 0)
1517 bb_simple_perror_msg_and_die("socketpair");
1518
1519 /* Create pty pair */
1520 ptyfd = xgetpty(tty_name);
1521
1522 fflush_all();
1523 if (xfork() == 0) {
1524 /* Fork writer child: writes data to PTY slave */
1525 int slave_fd;
1526 int written = 0;
1527 struct termios termbuf;
1528
1529 applet_name = "pty writer";
1530
1531 close(sock_pair[0]);
1532 close(sock_pair[1]);
1533 close(ptyfd);
1534
1535 slave_fd = xopen(tty_name, O_RDWR);
1536 //setsid(); /* lose ctty */
1537
1538 /* Put PTY in raw mode */
1539 tcgetattr(slave_fd, &termbuf);
1540 cfmakeraw(&termbuf);
1541 tcsetattr(slave_fd, TCSANOW, &termbuf);
1542
1543 while (written < input_size) {
1544 int n = (random() & 4095) + 1; /* 1 to 4096 bytes */
1545 if (n > input_size - written)
1546 n = input_size - written;
1547 n = safe_write(slave_fd, input_data + written, n);
1548 if (n < 0)
1549 bb_perror_msg_and_die("%s:%d: write error", __func__, __LINE__);
1550 written += n;
1551 /* Small random delay */
1552 if ((random() & 0xf) == 0)
1553 usleep(random() & 0x3ff);
1554 }
1555 _exit(0);
1556 }
1557
1558 free(input_data);
1559 input_data = NULL;
1560
1561 if (xfork() == 0) {
1562 /* Fork reader child: reads from network socket */
1563 int n;
1564 unsigned char read_buf[8192];
1565 int total_read = 0;
1566 sha256_ctx_t ctx;
1567#if SAVE_PTY2NET_ACTUAL
1568 int out_fd = xopen("actual.bin", O_WRONLY | O_CREAT | O_TRUNC);
1569#endif
1570 applet_name = "net reader";
1571
1572 close(sock_pair[1]); /* close write end */
1573 close(ptyfd);
1574
1575 sha256_begin(&ctx);
1576 for (;;) {
1577 n = safe_read(sock_pair[0], read_buf, sizeof(read_buf));
1578 if (n <= 0) {
1579 if (n < 0)
1580 bb_simple_perror_msg("read");
1581 break; /* EOF or error */
1582 }
1583 // To test what writer sees if we close read end:
1584 //shutdown(sock_pair[0], SHUT_RD);
1585 //sleep(2);
1586 //_exit(1);
1587#if SAVE_PTY2NET_ACTUAL
1588 xwrite(out_fd, read_buf, n);
1589#endif
1590 sha256_hash(&ctx, read_buf, n);
1591 total_read += n;
1592 }
1593
1594 sha256_end(&ctx, hash_from_net32);
1595 n = memcmp(hash_from_net32, expected_hash32, 32);
1596 bb_error_msg("received %d bytes, hash:%s%s",
1597 total_read, bin_to_hex(hash_from_net32, 32), n ? " MISMATCH" : "");
1598 _exit(!!n);
1599 }
1600
1601 close(sock_pair[0]); /* close read end */
1602
1603 /* Parent: run the pty_to_net logic */
1604 {
1605 pty_to_net_t *conn;
1606 ioloop_state_t *io;
1607 int rc;
1608
1609 io = new_ioloop_state();
1610
1611 conn = new_pty_to_net(ptyfd, sock_pair[1]);
1612 ioloop_insert_conn(io, (void*)conn);
1613
1614 rc = ioloop_run(io);
1615 bb_error_msg("ioloop_run:%d", rc);
1616
1617 /* This should be done inside ioloop. Check where possible: */
1618 //free_connection(conn);
1619 if (close(sock_pair[1]) == 0) /* should be EBADF */
1620 bb_error_msg_and_die("BUG: net write side wasn't closed");
1621 if (close(ptyfd) == 0)
1622 bb_error_msg_and_die("BUG: pty read side wasn't closed");
1623
1624 free_ioloop_state(io);
1625 }
1626
1627 /* Wait for all children */
1628 exitcode = 0;
1629 while (safe_waitpid(-1, &waitstatus, 0) > 0)
1630 exitcode |= (waitstatus != 0);
1631
1632 bb_error_msg("pty_to_net test completed: %d", exitcode);
1633 return exitcode;
1634}
1635
1636static void selftest(void)
1637{
1638 int exitcode = 0;
1639 srandom(12345);
1640 if (SELFTEST_VHANGUP) exitcode |= test_vhangup();
1641 if (SELFTEST_NET2PTY) exitcode |= test_net_to_pty_data_integrity();
1642 if (SELFTEST_PTY2NET) exitcode |= test_pty_to_net_data_integrity();
1643 bb_error_msg("selftest completed: %d", exitcode);
1644 _exit(exitcode);
1645}
1646#endif
1647
660int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1648int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
661int telnetd_main(int argc UNUSED_PARAM, char **argv) 1649int telnetd_main(int argc UNUSED_PARAM, char **argv)
662{ 1650{
663 fd_set rdfdset, wrfdset;
664 unsigned opt; 1651 unsigned opt;
665 int count;
666 struct tsession *ts;
667#if ENABLE_FEATURE_TELNETD_STANDALONE 1652#if ENABLE_FEATURE_TELNETD_STANDALONE
668#define IS_INETD (opt & OPT_INETD) 1653#define IS_INETD (opt & OPT_INETD)
669 int master_fd = master_fd; /* for compiler */
670 int sec_linger = sec_linger;
671 char *opt_bindaddr = NULL; 1654 char *opt_bindaddr = NULL;
672 char *opt_portnbr; 1655 char *opt_portnbr;
673#else 1656#else
674 enum { 1657 enum { IS_INETD = 1 };
675 IS_INETD = 1,
676 master_fd = -1,
677 };
678#endif 1658#endif
679 INIT_G(); 1659 INIT_G();
680 1660
681 /* Even if !STANDALONE, we accept (and ignore) -i, thus people 1661 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
682 * don't need to guess whether it's ok to pass -i to us */ 1662 * don't need to guess whether it's ok to pass -i to us */
683 opt = getopt32(argv, "^" 1663 opt = getopt32(argv, "^"
1664 IF_FEATURE_TELNETD_SELFTEST_DEBUG("@")
684 "f:l:Ki" 1665 "f:l:Ki"
685 IF_FEATURE_TELNETD_STANDALONE("p:b:F") 1666 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
686 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+") /* -w NUM */ 1667 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+v") /* -w NUM */
687 "\0" 1668 "\0"
688 /* -w implies -F. -w and -i don't mix */ 1669 /* -w implies -F. -w and -i don't mix. -v counter */
689 IF_FEATURE_TELNETD_INETD_WAIT("wF:i--w:w--i"), 1670 IF_FEATURE_TELNETD_INETD_WAIT("wF:i--w:w--i:vv")
690 &G.issuefile, &G.loginpath 1671 , &G.issuefile, &G.loginpath
691 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr) 1672 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
692 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger) 1673 IF_FEATURE_TELNETD_INETD_WAIT(, &G.io.max_timeout)
1674 , &G.verbose
693 ); 1675 );
1676
1677#if ENABLE_FEATURE_TELNETD_SELFTEST_DEBUG
1678 if (opt & 1) { /* -@ is first option */
1679 selftest(); /* does not return */
1680 }
1681 opt >>= 1; /* Shift away the selftest bit */
1682#endif
694 if (!IS_INETD /*&& !re_execed*/) { 1683 if (!IS_INETD /*&& !re_execed*/) {
695 /* inform that we start in standalone mode? 1684 /* inform that we start in standalone mode?
696 * May be useful when people forget to give -i */ 1685 * May be useful when people forget to give -i */
@@ -706,28 +1695,28 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
706 openlog(applet_name, LOG_PID, LOG_DAEMON); 1695 openlog(applet_name, LOG_PID, LOG_DAEMON);
707 logmode = LOGMODE_SYSLOG; 1696 logmode = LOGMODE_SYSLOG;
708 } 1697 }
709#if ENABLE_FEATURE_TELNETD_STANDALONE 1698
710 if (IS_INETD) { 1699 if (IS_INETD) {
711 G.sessions = make_new_session(0); 1700 make_new_session(&G.io, 0);
712 if (!G.sessions) /* pty opening or vfork problem, exit */ 1701 }
713 return 1; /* make_new_session printed error message */ 1702#if ENABLE_FEATURE_TELNETD_STANDALONE
714 } else { 1703 else {
715 master_fd = 0; 1704 int master_fd = 0;
1705 /* For -w SEC, listening socket is 0. Otherwise: */
716 if (!(opt & OPT_WAIT)) { 1706 if (!(opt & OPT_WAIT)) {
717 unsigned portnbr = CONFIG_FEATURE_TELNETD_PORT_DEFAULT; 1707 unsigned portnbr = CONFIG_FEATURE_TELNETD_PORT_DEFAULT;
718 if (opt & OPT_PORT) 1708 if (opt & OPT_PORT)
719 portnbr = xatou16(opt_portnbr); 1709 portnbr = xatou16(opt_portnbr);
1710 /* If someone would run "telnetd 0>&-", we can get terribly confused. */
1711 /* Nake sure master_fd, or accept() result fd, etc, cant become 0 or 1: */
1712 bb_sanitize_stdio();
720 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); 1713 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
721 xlisten(master_fd, 1); 1714 xlisten(master_fd, 1);
722 } 1715 }
723 close_on_exec_on(master_fd); 1716 close_on_exec_on(master_fd);
1717 ioloop_insert_conn(&G.io, (void *)new_accept_conn(master_fd));
724 } 1718 }
725#else
726 G.sessions = make_new_session();
727 if (!G.sessions) /* pty opening or vfork problem, exit */
728 return 1; /* make_new_session printed error message */
729#endif 1719#endif
730
731 /* We don't want to die if just one session is broken */ 1720 /* We don't want to die if just one session is broken */
732 signal(SIGPIPE, SIG_IGN); 1721 signal(SIGPIPE, SIG_IGN);
733 1722
@@ -736,189 +1725,30 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
736 else /* prevent dead children from becoming zombies */ 1725 else /* prevent dead children from becoming zombies */
737 signal(SIGCHLD, SIG_IGN); 1726 signal(SIGCHLD, SIG_IGN);
738 1727
739/*
740 This is how the buffers are used. The arrows indicate data flow.
741
742 +-------+ wridx1++ +------+ rdidx1++ +----------+
743 | | <-------------- | buf1 | <-------------- | |
744 | | size1-- +------+ size1++ | |
745 | pty | | socket |
746 | | rdidx2++ +------+ wridx2++ | |
747 | | --------------> | buf2 | --------------> | |
748 +-------+ size2++ +------+ size2-- +----------+
749
750 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
751 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
752
753 Each session has got two buffers. Buffers are circular. If sizeN == 0,
754 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
755 rdidxN == wridxN.
756*/
757 again:
758 FD_ZERO(&rdfdset);
759 FD_ZERO(&wrfdset);
760
761 /* Select on the master socket, all telnet sockets and their
762 * ptys if there is room in their session buffers.
763 * NB: scalability problem: we recalculate entire bitmap
764 * before each select. Can be a problem with 500+ connections. */
765 ts = G.sessions;
766 while (ts) {
767 struct tsession *next = ts->next; /* in case we free ts */
768 if (ts->shell_pid == -1) {
769 /* Child died and we detected that */
770 free_session(ts);
771 } else {
772 if (ts->size1 > 0) /* can write to pty */
773 FD_SET(ts->ptyfd, &wrfdset);
774 if (ts->size1 < BUFSIZE) /* can read from socket */
775 FD_SET(ts->sockfd_read, &rdfdset);
776 if (ts->size2 > 0) /* can write to socket */
777 FD_SET(ts->sockfd_write, &wrfdset);
778 if (ts->size2 < BUFSIZE) /* can read from pty */
779 FD_SET(ts->ptyfd, &rdfdset);
780 }
781 ts = next;
782 }
783 if (!IS_INETD) {
784 FD_SET(master_fd, &rdfdset);
785 /* This is needed because free_session() does not
786 * take master_fd into account when it finds new
787 * maxfd among remaining fd's */
788 if (master_fd > G.maxfd)
789 G.maxfd = master_fd;
790 }
791
792 {
793 struct timeval *tv_ptr = NULL;
794#if ENABLE_FEATURE_TELNETD_INETD_WAIT 1728#if ENABLE_FEATURE_TELNETD_INETD_WAIT
795 struct timeval tv; 1729 if (G.io.max_timeout > UINT_MAX / 1000000) /* -w TMOUT capped at 4294 sec */
796 if ((opt & OPT_WAIT) && !G.sessions) { 1730 G.io.max_timeout = (UINT_MAX / 1000000);
797 tv.tv_sec = sec_linger; 1731 G.io.max_timeout *= 1000000;
798 tv.tv_usec = 0;
799 tv_ptr = &tv;
800 }
801#endif 1732#endif
802 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr); 1733 G.io.flags |= IOLOOP_FLAG_EXIT_IF_TIMEOUT;
803 } 1734 for (;;) {
804 if (count == 0) /* "telnetd -w SEC" timed out */ 1735 int rc = ioloop_run(&G.io);
805 return 0; 1736 if (rc == IOLOOP_NO_CONNS) {
806 if (count < 0) 1737 /* inetd mode (not -w), and it's closed now */
807 goto again; /* EINTR or ENOMEM */ 1738 dbg_close("connection is closed");
808 1739 break;
809#if ENABLE_FEATURE_TELNETD_STANDALONE
810 /* Check for and accept new sessions */
811 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
812 int fd;
813 struct tsession *new_ts;
814
815 fd = accept(master_fd, NULL, NULL);
816 if (fd < 0)
817 goto again;
818 close_on_exec_on(fd);
819
820 /* Create a new session and link it into active list */
821 new_ts = make_new_session(fd);
822 if (new_ts) {
823 new_ts->next = G.sessions;
824 G.sessions = new_ts;
825 } else {
826 close(fd);
827 }
828 }
829#endif
830
831 /* Then check for data tunneling */
832 ts = G.sessions;
833 while (ts) { /* For all sessions... */
834 struct tsession *next = ts->next; /* in case we free ts */
835
836 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
837 /* Write to pty from buffer 1 */
838 count = safe_write_to_pty_decode_iac(ts);
839 if (count < 0) {
840 if (errno == EAGAIN)
841 goto skip1;
842 goto kill_session;
843 }
844 } 1740 }
845 skip1: 1741 /* else: timeout */
846 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { 1742#if ENABLE_FEATURE_TELNETD_INETD_WAIT
847 /* Write to socket from buffer 2 */ 1743 if (1 /* rc == IOLOOP_TIMEOUT */
848 count = MIN(BUFSIZE - ts->wridx2, ts->size2); 1744 && (!G.io.conns || !G.io.conns->next) /* only accept conn exists */
849 count = safe_write_double_iac(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); 1745 ) {
850 if (count < 0) { 1746 dbg_close("-w TIMEOUT:%dus", G.io.max_timeout);
851 if (errno == EAGAIN) 1747 break;
852 goto skip2;
853 goto kill_session;
854 }
855 ts->wridx2 += count;
856 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
857 ts->wridx2 = 0;
858 ts->size2 -= count;
859 if (ts->size2 == 0) {
860 ts->rdidx2 = 0;
861 ts->wridx2 = 0;
862 }
863 } 1748 }
864 skip2: 1749#endif
865 1750 dbg("timeout was due to temporary EIO: continue looping");
866 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { 1751 }
867 /* Read from socket to buffer 1 */ 1752 dbg_close("terminating");
868 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); 1753 return EXIT_SUCCESS;
869 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
870 if (count <= 0) {
871 if (count < 0 && errno == EAGAIN)
872 goto skip3;
873 goto kill_session;
874 }
875 /* Ignore trailing NUL if it is there */
876 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
877 --count;
878 }
879 ts->size1 += count;
880 ts->rdidx1 += count;
881 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
882 ts->rdidx1 = 0;
883 }
884 skip3:
885 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
886 /* Read from pty to buffer 2 */
887 int eio = 0;
888 read_pty:
889 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
890 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
891 if (count <= 0) {
892 if (count < 0) {
893 if (errno == EAGAIN)
894 goto skip4;
895 /* login process might call vhangup(),
896 * which causes intermittent EIOs on read above
897 * (observed on kernel 4.12.0). Try up to 10 ms.
898 */
899 if (errno == EIO && eio < 10) {
900 eio++;
901 //bb_error_msg("EIO pty %u", eio);
902 usleep(1000);
903 goto read_pty;
904 }
905 }
906 goto kill_session;
907 }
908 ts->size2 += count;
909 ts->rdidx2 += count;
910 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
911 ts->rdidx2 = 0;
912 }
913 skip4:
914 ts = next;
915 continue;
916 kill_session:
917 if (ts->shell_pid > 0)
918 update_utmp_DEAD_PROCESS(ts->shell_pid);
919 free_session(ts);
920 ts = next;
921 }
922
923 goto again;
924} 1754}
diff --git a/networking/telnetd.c.OLD b/networking/telnetd.c.OLD
new file mode 100644
index 000000000..a5a783047
--- /dev/null
+++ b/networking/telnetd.c.OLD
@@ -0,0 +1,924 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7 *
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
11 *
12 * The telnetd manpage says it all:
13 *
14 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
15 * a client, then creating a login process which has the slave side of the
16 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
17 * master side of the pseudo-terminal, implementing the telnet protocol and
18 * passing characters between the remote client and the login process.
19 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
21 * Set process group corrections, initial busybox port
22 */
23//config:config TELNETD
24//config: bool "telnetd (13 kb)"
25//config: default y
26//config: select FEATURE_SYSLOG
27//config: help
28//config: A daemon for the TELNET protocol, allowing you to log onto the host
29//config: running the daemon. Please keep in mind that the TELNET protocol
30//config: sends passwords in plain text. If you can't afford the space for an
31//config: SSH daemon and you trust your network, you may say 'y' here. As a
32//config: more secure alternative, you should seriously consider installing the
33//config: very small Dropbear SSH daemon instead:
34//config: http://matt.ucc.asn.au/dropbear/dropbear.html
35//config:
36//config: Note that for busybox telnetd to work you need several things:
37//config: First of all, your kernel needs:
38//config: CONFIG_UNIX98_PTYS=y
39//config:
40//config: Next, you need a /dev/pts directory on your root filesystem:
41//config:
42//config: $ ls -ld /dev/pts
43//config: drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
44//config:
45//config: Next you need the pseudo terminal master multiplexer /dev/ptmx:
46//config:
47//config: $ ls -la /dev/ptmx
48//config: crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
49//config:
50//config: Any /dev/ttyp[0-9]* files you may have can be removed.
51//config: Next, you need to mount the devpts filesystem on /dev/pts using:
52//config:
53//config: mount -t devpts devpts /dev/pts
54//config:
55//config: You need to be sure that busybox has LOGIN and
56//config: FEATURE_SUID enabled. And finally, you should make
57//config: certain that busybox has been installed setuid root:
58//config:
59//config: chown root.root /bin/busybox
60//config: chmod 4755 /bin/busybox
61//config:
62//config: with all that done, telnetd _should_ work....
63//config:
64//config:config FEATURE_TELNETD_STANDALONE
65//config: bool "Support standalone telnetd (not inetd only)"
66//config: default y
67//config: depends on TELNETD
68//config: help
69//config: Selecting this will make telnetd able to run standalone.
70//config:
71//config:config FEATURE_TELNETD_PORT_DEFAULT
72//config: int "Default port"
73//config: default 23
74//config: range 1 65535
75//config: depends on FEATURE_TELNETD_STANDALONE
76//config:
77//config:config FEATURE_TELNETD_INETD_WAIT
78//config: bool "Support -w SEC option (inetd wait mode)"
79//config: default y
80//config: depends on FEATURE_TELNETD_STANDALONE
81//config: help
82//config: This option allows you to run telnetd in "inet wait" mode.
83//config: Example inetd.conf line (note "wait", not usual "nowait"):
84//config:
85//config: telnet stream tcp wait root /bin/telnetd telnetd -w10
86//config:
87//config: In this example, inetd passes _listening_ socket_ as fd 0
88//config: to telnetd when connection appears.
89//config: telnetd will wait for connections until all existing
90//config: connections are closed, and no new connections
91//config: appear during 10 seconds. Then it exits, and inetd continues
92//config: to listen for new connections.
93//config:
94//config: This option is rarely used. "tcp nowait" is much more usual
95//config: way of running tcp services, including telnetd.
96//config: You most probably want to say N here.
97
98//applet:IF_TELNETD(APPLET(telnetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
99
100//kbuild:lib-$(CONFIG_TELNETD) += telnetd.o
101
102//usage:#define telnetd_trivial_usage
103//usage: "[OPTIONS]"
104//usage:#define telnetd_full_usage "\n\n"
105//usage: "Handle incoming telnet connections"
106//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
107//usage: "\n -l LOGIN Exec LOGIN on connect (default /bin/login)"
108//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue.net"
109//usage: "\n -K Close connection as soon as login exits"
110//usage: "\n (normally wait until all programs close slave pty)"
111//usage: IF_FEATURE_TELNETD_STANDALONE(
112//usage: "\n -p PORT Port to listen on. Default "STR(CONFIG_FEATURE_TELNETD_PORT_DEFAULT)
113//usage: "\n -b ADDR[:PORT] Address to bind to"
114//usage: "\n -F Run in foreground"
115//usage: "\n -i Inetd mode"
116//usage: IF_FEATURE_TELNETD_INETD_WAIT(
117//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC"
118//usage: "\n inetd.conf line: 23 stream tcp wait root telnetd telnetd -w10"
119//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
120//usage: )
121//usage: )
122
123#define DEBUG 0
124
125#include "libbb.h"
126#include "common_bufsiz.h"
127#include <syslog.h>
128
129#if DEBUG
130# define TELCMDS
131# define TELOPTS
132#endif
133#include <arpa/telnet.h>
134
135
136struct tsession {
137 struct tsession *next;
138 pid_t shell_pid;
139 int sockfd_read;
140 int sockfd_write;
141 int ptyfd;
142 smallint buffered_IAC_for_pty;
143
144 /* two circular buffers */
145 /*char *buf1, *buf2;*/
146/*#define TS_BUF1(ts) ts->buf1*/
147/*#define TS_BUF2(ts) TS_BUF2(ts)*/
148#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
149#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
150 int rdidx1, wridx1, size1;
151 int rdidx2, wridx2, size2;
152};
153
154/* Two buffers are directly after tsession in malloced memory.
155 * Make whole thing fit in 4k */
156enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
157
158
159/* Globals */
160struct globals {
161 struct tsession *sessions;
162 const char *loginpath;
163 const char *issuefile;
164 int maxfd;
165} FIX_ALIASING;
166#define G (*(struct globals*)bb_common_bufsiz1)
167#define INIT_G() do { \
168 setup_common_bufsiz(); \
169 G.loginpath = "/bin/login"; \
170 G.issuefile = "/etc/issue.net"; \
171} while (0)
172
173
174/* Write some buf1 data to pty, processing IACs.
175 * Update wridx1 and size1. Return < 0 on error.
176 * Buggy if IAC is present but incomplete: skips them.
177 */
178static ssize_t
179safe_write_to_pty_decode_iac(struct tsession *ts)
180{
181 unsigned wr;
182 ssize_t rc;
183 unsigned char *buf;
184 unsigned char *found;
185
186 buf = TS_BUF1(ts) + ts->wridx1;
187 wr = MIN(BUFSIZE - ts->wridx1, ts->size1);
188 /* wr is at least 1 here */
189
190 if (ts->buffered_IAC_for_pty) {
191 /* Last time we stopped on a "dangling" IAC byte.
192 * We removed it from the buffer back then.
193 * Now pretend it's still there, and jump to IAC processing.
194 */
195 ts->buffered_IAC_for_pty = 0;
196 wr++;
197 ts->size1++;
198 buf--; /* Yes, this can point before the buffer. It's ok */
199 ts->wridx1--;
200 goto handle_iac;
201 }
202
203 found = memchr(buf, IAC, wr);
204 if (found != buf) {
205 /* There is a "prefix" of non-IAC chars.
206 * Write only them, and return.
207 */
208 if (found)
209 wr = found - buf;
210
211 /* We map \r\n ==> \r for pragmatic reasons:
212 * many client implementations send \r\n when
213 * the user hits the CarriageReturn key.
214 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
215 */
216 rc = wr;
217 found = memchr(buf, '\r', wr);
218 if (found)
219 rc = found - buf + 1;
220 rc = safe_write(ts->ptyfd, buf, rc);
221 if (rc <= 0)
222 return rc;
223 if (rc < wr /* don't look past available data */
224 && buf[rc-1] == '\r' /* need this: imagine that write was _short_ */
225 && (buf[rc] == '\n' || buf[rc] == '\0')
226 ) {
227 rc++;
228 }
229 goto update_and_return;
230 }
231
232 /* buf starts with IAC char. Process that sequence.
233 * Example: we get this from our own (bbox) telnet client:
234 * read(5, "\377\374\1""\377\373\37""\377\372\37\0\262\0@\377\360""\377\375\1""\377\375\3"):
235 * IAC WONT ECHO, IAC WILL NAWS, IAC SB NAWS <cols> <rows> IAC SE, IAC DO SGA
236 * Another example (telnet-0.17 from old-netkit):
237 * read(4, "\377\375\3""\377\373\30""\377\373\37""\377\373 ""\377\373!""\377\373\"""\377\373'"
238 * "\377\375\5""\377\373#""\377\374\1""\377\372\37\0\257\0I\377\360""\377\375\1"):
239 * IAC DO SGA, IAC WILL TTYPE, IAC WILL NAWS, IAC WILL TSPEED, IAC WILL LFLOW, IAC WILL LINEMODE, IAC WILL NEW_ENVIRON,
240 * IAC DO STATUS, IAC WILL XDISPLOC, IAC WONT ECHO, IAC SB NAWS <cols> <rows> IAC SE, IAC DO ECHO
241 */
242 if (wr <= 1) {
243 /* Only the single IAC byte is in the buffer, eat it
244 * and set a flag "process the rest of the sequence
245 * next time we are here".
246 */
247 //bb_error_msg("dangling IAC!");
248 ts->buffered_IAC_for_pty = 1;
249 rc = 1;
250 goto update_and_return;
251 }
252
253 handle_iac:
254 /* 2-byte commands (240..250 and 255):
255 * IAC IAC (255) Literal 255. Supported.
256 * IAC SE (240) End of subnegotiation. Treated as NOP.
257 * IAC NOP (241) NOP. Supported.
258 * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()?
259 * IAC AYT (246) Are you there.
260 * These don't look useful:
261 * IAC DM (242) Data mark. What is this?
262 * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C).
263 * IAC AO (245) Abort output. "You can continue running, but do not send me the output".
264 * IAC EC (247) Erase character. The receiver should delete the last received char.
265 * IAC EL (248) Erase line. The receiver should delete everything up tp last newline.
266 * IAC GA (249) Go ahead. For half-duplex lines: "now you talk".
267 * Implemented only as part of NAWS:
268 * IAC SB (250) Subnegotiation of an option follows.
269 */
270 if (buf[1] == IAC) {
271 /* Literal 255 (emacs M-DEL) */
272 //bb_error_msg("255!");
273 rc = safe_write(ts->ptyfd, &buf[1], 1);
274 /*
275 * If we went through buffered_IAC_for_pty==1 path,
276 * bailing out on error like below messes up the buffer.
277 * EAGAIN is highly unlikely here, other errors will be
278 * repeated on next write, let's just skip error check.
279 */
280#if 0
281 if (rc <= 0)
282 return rc;
283#endif
284 rc = 2;
285 goto update_and_return;
286 }
287 if (buf[1] == AYT) {
288 if (ts->size2 == 0) { /* if nothing buffered yet... */
289 /* Send back evidence that AYT was seen */
290 unsigned char *buf2 = TS_BUF2(ts);
291 buf2[0] = IAC;
292 buf2[1] = NOP;
293 ts->wridx2 = 0;
294 ts->rdidx2 = ts->size2 = 2;
295 }
296 rc = 2;
297 goto update_and_return;
298 }
299 if (buf[1] >= 240 && buf[1] <= 249) {
300 /* NOP (241). Ignore (putty keepalive, etc) */
301 /* All other 2-byte commands also treated as NOPs here */
302 rc = 2;
303 goto update_and_return;
304 }
305
306 if (wr <= 2) {
307/* BUG: only 2 bytes of the IAC is in the buffer, we just eat them.
308 * This is not a practical problem since >2 byte IACs are seen only
309 * in initial negotiation, when buffer is empty
310 */
311 rc = 2;
312 goto update_and_return;
313 }
314
315 if (buf[1] == SB) {
316 if (buf[2] == TELOPT_NAWS) {
317 /* IAC SB, TELOPT_NAWS, 4-byte, IAC SE */
318 struct winsize ws;
319 if (wr <= 6) {
320/* BUG: incomplete, can't process */
321 rc = wr;
322 goto update_and_return;
323 }
324 memset(&ws, 0, sizeof(ws)); /* pixel sizes are set to 0 */
325 ws.ws_col = (buf[3] << 8) | buf[4];
326 ws.ws_row = (buf[5] << 8) | buf[6];
327 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
328 rc = 7;
329 /* trailing IAC SE will be eaten separately, as 2-byte NOP */
330 goto update_and_return;
331 }
332 /* else: other subnegs not supported yet */
333 }
334
335 /* Assume it is a 3-byte WILL/WONT/DO/DONT 251..254 command and skip it */
336#if DEBUG
337 fprintf(stderr, "Ignoring IAC %s,%s\n",
338 TELCMD(buf[1]), TELOPT(buf[2]));
339#endif
340 rc = 3;
341
342 update_and_return:
343 ts->wridx1 += rc;
344 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
345 ts->wridx1 = 0;
346 ts->size1 -= rc;
347 /*
348 * Hack. We cannot process IACs which wrap around buffer's end.
349 * Since properly fixing it requires writing bigger code,
350 * we rely instead on this code making it virtually impossible
351 * to have wrapped IAC (people don't type at 2k/second).
352 * It also allows for bigger reads in common case.
353 */
354 if (ts->size1 == 0) { /* very typical */
355 //bb_error_msg("zero size1");
356 ts->rdidx1 = 0;
357 ts->wridx1 = 0;
358 return rc;
359 }
360 wr = ts->wridx1;
361 if (wr != 0 && wr < ts->rdidx1) {
362 /* Buffer is not wrapped yet.
363 * We can easily move it to the beginning.
364 */
365 //bb_error_msg("moved %d", wr);
366 memmove(TS_BUF1(ts), TS_BUF1(ts) + wr, ts->size1);
367 ts->rdidx1 -= wr;
368 ts->wridx1 = 0;
369 }
370 return rc;
371}
372
373/*
374 * Converting single IAC into double on output
375 */
376static size_t safe_write_double_iac(int fd, const char *buf, size_t count)
377{
378 const char *IACptr;
379 size_t wr, rc, total;
380
381 total = 0;
382 while (1) {
383 if (count == 0)
384 return total;
385 if (*buf == (char)IAC) {
386 static const char IACIAC[] ALIGN1 = { IAC, IAC };
387 rc = safe_write(fd, IACIAC, 2);
388/* BUG: if partial write was only 1 byte long, we end up emitting just one IAC */
389 if (rc != 2)
390 break;
391 buf++;
392 total++;
393 count--;
394 continue;
395 }
396 /* count != 0, *buf != IAC */
397 IACptr = memchr(buf, IAC, count);
398 wr = count;
399 if (IACptr)
400 wr = IACptr - buf;
401 rc = safe_write(fd, buf, wr);
402 if (rc != wr)
403 break;
404 buf += rc;
405 total += rc;
406 count -= rc;
407 }
408 /* here: rc - result of last short write */
409 if ((ssize_t)rc < 0) { /* error? */
410 if (total == 0)
411 return rc;
412 rc = 0;
413 }
414 return total + rc;
415}
416
417/* Must match getopt32 string */
418enum {
419 OPT_WATCHCHILD = (1 << 2), /* -K */
420 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
421 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
422 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
423 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
424 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
425};
426
427static struct tsession *
428make_new_session(
429 IF_FEATURE_TELNETD_STANDALONE(int sock)
430 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
431) {
432#if !ENABLE_FEATURE_TELNETD_STANDALONE
433 enum { sock = 0 };
434#endif
435 const char *login_argv[2];
436 struct termios termbuf;
437 int fd, pid;
438 char tty_name[GETPTY_BUFSIZE];
439 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
440
441 /*ts->buf1 = (char *)(ts + 1);*/
442 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
443
444 /* Got a new connection, set up a tty */
445 fd = xgetpty(tty_name);
446 if (fd > G.maxfd)
447 G.maxfd = fd;
448 ts->ptyfd = fd;
449 ndelay_on(fd);
450 close_on_exec_on(fd);
451
452 /* SO_KEEPALIVE by popular demand */
453 setsockopt_keepalive(sock);
454#if ENABLE_FEATURE_TELNETD_STANDALONE
455 ts->sockfd_read = sock;
456 ndelay_on(sock);
457 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
458 sock++; /* so use fd 1 for output */
459 ndelay_on(sock);
460 }
461 ts->sockfd_write = sock;
462 if (sock > G.maxfd)
463 G.maxfd = sock;
464#else
465 /* ts->sockfd_read = 0; - done by xzalloc */
466 ts->sockfd_write = 1;
467 ndelay_on(0);
468 ndelay_on(1);
469#endif
470
471 /* Make the telnet client understand we will echo characters so it
472 * should not do it locally. We don't tell the client to run linemode,
473 * because we want to handle line editing and tab completion and other
474 * stuff that requires char-by-char support. */
475 {
476 static const char iacs_to_send[] ALIGN1 = {
477 IAC, DO, TELOPT_ECHO,
478 IAC, DO, TELOPT_NAWS,
479 /* This requires telnetd.ctrlSQ.patch (incomplete) */
480 /*IAC, DO, TELOPT_LFLOW,*/
481 IAC, WILL, TELOPT_ECHO,
482 IAC, WILL, TELOPT_SGA
483 };
484 /* This confuses safe_write_double_iac(), it will try to duplicate
485 * each IAC... */
486 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
487 //ts->rdidx2 = sizeof(iacs_to_send);
488 //ts->size2 = sizeof(iacs_to_send);
489 /* So just stuff it into TCP stream! (no error check...) */
490#if ENABLE_FEATURE_TELNETD_STANDALONE
491 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
492#else
493 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
494#endif
495 /*ts->rdidx2 = 0; - xzalloc did it */
496 /*ts->size2 = 0;*/
497 }
498
499 fflush_all();
500 pid = vfork(); /* NOMMU-friendly */
501 if (pid < 0) {
502 free(ts);
503 close(fd);
504 /* sock will be closed by caller */
505 bb_simple_perror_msg("vfork");
506 return NULL;
507 }
508 if (pid > 0) {
509 /* Parent */
510 ts->shell_pid = pid;
511 return ts;
512 }
513
514 /* Child */
515 /* Careful - we are after vfork! */
516
517 /* Restore default signal handling ASAP */
518 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
519
520 pid = getpid();
521
522 if (ENABLE_FEATURE_UTMP) {
523 len_and_sockaddr *lsa = get_peer_lsa(sock);
524 char *hostname = NULL;
525 if (lsa) {
526 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
527 free(lsa);
528 }
529 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
530 free(hostname);
531 }
532
533 /* Make new session and process group */
534 setsid();
535
536 /* Open the child's side of the tty */
537 /* NB: setsid() disconnects from any previous ctty's. Therefore
538 * we must open child's side of the tty AFTER setsid! */
539 close(0);
540 xopen(tty_name, O_RDWR); /* becomes our ctty */
541 xdup2(0, 1);
542 xdup2(0, 2);
543 tcsetpgrp(0, pid); /* switch this tty's process group to us */
544
545 /* The pseudo-terminal allocated to the client is configured to operate
546 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
547 tcgetattr(0, &termbuf);
548 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
549 termbuf.c_oflag |= ONLCR | XTABS;
550 termbuf.c_iflag |= ICRNL;
551 termbuf.c_iflag &= ~IXOFF;
552 /*termbuf.c_lflag &= ~ICANON;*/
553 tcsetattr_stdin_TCSANOW(&termbuf);
554
555 /* Uses FILE-based I/O to stdout, but does fflush_all(),
556 * so should be safe with vfork.
557 * I fear, though, that some users will have ridiculously big
558 * issue files, and they may block writing to fd 1,
559 * (parent is supposed to read it, but parent waits
560 * for vforked child to exec!) */
561 print_login_issue(G.issuefile, tty_name);
562
563 /* Exec shell / login / whatever */
564 login_argv[0] = G.loginpath;
565 login_argv[1] = NULL;
566 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
567 * exec external program.
568 * NB: sock is either 0 or has CLOEXEC set on it.
569 * fd has CLOEXEC set on it too. These two fds will be closed here.
570 */
571 BB_EXECVP(G.loginpath, (char **)login_argv);
572 /* _exit is safer with vfork, and we shouldn't send message
573 * to remote clients anyway */
574 _exit_FAILURE(); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
575}
576
577#if ENABLE_FEATURE_TELNETD_STANDALONE
578
579static void
580free_session(struct tsession *ts)
581{
582 struct tsession *t;
583
584 if (option_mask32 & OPT_INETD)
585 exit_SUCCESS();
586
587 /* Unlink this telnet session from the session list */
588 t = G.sessions;
589 if (t == ts)
590 G.sessions = ts->next;
591 else {
592 while (t->next != ts)
593 t = t->next;
594 t->next = ts->next;
595 }
596
597#if 0
598 /* It was said that "normal" telnetd just closes ptyfd,
599 * doesn't send SIGKILL. When we close ptyfd,
600 * kernel sends SIGHUP to processes having slave side opened. */
601 kill(ts->shell_pid, SIGKILL);
602 waitpid(ts->shell_pid, NULL, 0);
603#endif
604 close(ts->ptyfd);
605 close(ts->sockfd_read);
606 /* We do not need to close(ts->sockfd_write), it's the same
607 * as sockfd_read unless we are in inetd mode. But in inetd mode
608 * we do not reach this */
609 free(ts);
610
611 /* Scan all sessions and find new maxfd */
612 G.maxfd = 0;
613 ts = G.sessions;
614 while (ts) {
615 if (G.maxfd < ts->ptyfd)
616 G.maxfd = ts->ptyfd;
617 if (G.maxfd < ts->sockfd_read)
618 G.maxfd = ts->sockfd_read;
619#if 0
620 /* Again, sockfd_write == sockfd_read here */
621 if (G.maxfd < ts->sockfd_write)
622 G.maxfd = ts->sockfd_write;
623#endif
624 ts = ts->next;
625 }
626}
627
628#else /* !FEATURE_TELNETD_STANDALONE */
629
630/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
631#define free_session(ts) return 0
632
633#endif
634
635static void handle_sigchld(int sig UNUSED_PARAM)
636{
637 pid_t pid;
638 struct tsession *ts;
639 int save_errno = errno;
640
641 /* Looping: more than one child may have exited */
642 while (1) {
643 pid = wait_any_nohang(NULL);
644 if (pid <= 0)
645 break;
646 ts = G.sessions;
647 while (ts) {
648 if (ts->shell_pid == pid) {
649 ts->shell_pid = -1;
650 update_utmp_DEAD_PROCESS(pid);
651 break;
652 }
653 ts = ts->next;
654 }
655 }
656
657 errno = save_errno;
658}
659
660int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
661int telnetd_main(int argc UNUSED_PARAM, char **argv)
662{
663 fd_set rdfdset, wrfdset;
664 unsigned opt;
665 int count;
666 struct tsession *ts;
667#if ENABLE_FEATURE_TELNETD_STANDALONE
668#define IS_INETD (opt & OPT_INETD)
669 int master_fd = master_fd; /* for compiler */
670 int sec_linger = sec_linger;
671 char *opt_bindaddr = NULL;
672 char *opt_portnbr;
673#else
674 enum {
675 IS_INETD = 1,
676 master_fd = -1,
677 };
678#endif
679 INIT_G();
680
681 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
682 * don't need to guess whether it's ok to pass -i to us */
683 opt = getopt32(argv, "^"
684 "f:l:Ki"
685 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
686 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+") /* -w NUM */
687 "\0"
688 /* -w implies -F. -w and -i don't mix */
689 IF_FEATURE_TELNETD_INETD_WAIT("wF:i--w:w--i"),
690 &G.issuefile, &G.loginpath
691 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
692 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
693 );
694 if (!IS_INETD /*&& !re_execed*/) {
695 /* inform that we start in standalone mode?
696 * May be useful when people forget to give -i */
697 /*bb_error_msg("listening for connections");*/
698 if (!(opt & OPT_FOREGROUND)) {
699 /* DAEMON_CHDIR_ROOT was giving inconsistent
700 * behavior with/without -F, -i */
701 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
702 }
703 }
704 /* Redirect log to syslog early, if needed */
705 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
706 openlog(applet_name, LOG_PID, LOG_DAEMON);
707 logmode = LOGMODE_SYSLOG;
708 }
709#if ENABLE_FEATURE_TELNETD_STANDALONE
710 if (IS_INETD) {
711 G.sessions = make_new_session(0);
712 if (!G.sessions) /* pty opening or vfork problem, exit */
713 return 1; /* make_new_session printed error message */
714 } else {
715 master_fd = 0;
716 if (!(opt & OPT_WAIT)) {
717 unsigned portnbr = CONFIG_FEATURE_TELNETD_PORT_DEFAULT;
718 if (opt & OPT_PORT)
719 portnbr = xatou16(opt_portnbr);
720 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
721 xlisten(master_fd, 1);
722 }
723 close_on_exec_on(master_fd);
724 }
725#else
726 G.sessions = make_new_session();
727 if (!G.sessions) /* pty opening or vfork problem, exit */
728 return 1; /* make_new_session printed error message */
729#endif
730
731 /* We don't want to die if just one session is broken */
732 signal(SIGPIPE, SIG_IGN);
733
734 if (opt & OPT_WATCHCHILD)
735 signal(SIGCHLD, handle_sigchld);
736 else /* prevent dead children from becoming zombies */
737 signal(SIGCHLD, SIG_IGN);
738
739/*
740 This is how the buffers are used. The arrows indicate data flow.
741
742 +-------+ wridx1++ +------+ rdidx1++ +----------+
743 | | <-------------- | buf1 | <-------------- | |
744 | | size1-- +------+ size1++ | |
745 | pty | | socket |
746 | | rdidx2++ +------+ wridx2++ | |
747 | | --------------> | buf2 | --------------> | |
748 +-------+ size2++ +------+ size2-- +----------+
749
750 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
751 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
752
753 Each session has got two buffers. Buffers are circular. If sizeN == 0,
754 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
755 rdidxN == wridxN.
756*/
757 again:
758 FD_ZERO(&rdfdset);
759 FD_ZERO(&wrfdset);
760
761 /* Select on the master socket, all telnet sockets and their
762 * ptys if there is room in their session buffers.
763 * NB: scalability problem: we recalculate entire bitmap
764 * before each select. Can be a problem with 500+ connections. */
765 ts = G.sessions;
766 while (ts) {
767 struct tsession *next = ts->next; /* in case we free ts */
768 if (ts->shell_pid == -1) {
769 /* Child died and we detected that */
770 free_session(ts);
771 } else {
772 if (ts->size1 > 0) /* can write to pty */
773 FD_SET(ts->ptyfd, &wrfdset);
774 if (ts->size1 < BUFSIZE) /* can read from socket */
775 FD_SET(ts->sockfd_read, &rdfdset);
776 if (ts->size2 > 0) /* can write to socket */
777 FD_SET(ts->sockfd_write, &wrfdset);
778 if (ts->size2 < BUFSIZE) /* can read from pty */
779 FD_SET(ts->ptyfd, &rdfdset);
780 }
781 ts = next;
782 }
783 if (!IS_INETD) {
784 FD_SET(master_fd, &rdfdset);
785 /* This is needed because free_session() does not
786 * take master_fd into account when it finds new
787 * maxfd among remaining fd's */
788 if (master_fd > G.maxfd)
789 G.maxfd = master_fd;
790 }
791
792 {
793 struct timeval *tv_ptr = NULL;
794#if ENABLE_FEATURE_TELNETD_INETD_WAIT
795 struct timeval tv;
796 if ((opt & OPT_WAIT) && !G.sessions) {
797 tv.tv_sec = sec_linger;
798 tv.tv_usec = 0;
799 tv_ptr = &tv;
800 }
801#endif
802 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
803 }
804 if (count == 0) /* "telnetd -w SEC" timed out */
805 return 0;
806 if (count < 0)
807 goto again; /* EINTR or ENOMEM */
808
809#if ENABLE_FEATURE_TELNETD_STANDALONE
810 /* Check for and accept new sessions */
811 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
812 int fd;
813 struct tsession *new_ts;
814
815 fd = accept(master_fd, NULL, NULL);
816 if (fd < 0)
817 goto again;
818 close_on_exec_on(fd);
819
820 /* Create a new session and link it into active list */
821 new_ts = make_new_session(fd);
822 if (new_ts) {
823 new_ts->next = G.sessions;
824 G.sessions = new_ts;
825 } else {
826 close(fd);
827 }
828 }
829#endif
830
831 /* Then check for data tunneling */
832 ts = G.sessions;
833 while (ts) { /* For all sessions... */
834 struct tsession *next = ts->next; /* in case we free ts */
835
836 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
837 /* Write to pty from buffer 1 */
838 count = safe_write_to_pty_decode_iac(ts);
839 if (count < 0) {
840 if (errno == EAGAIN)
841 goto skip1;
842 goto kill_session;
843 }
844 }
845 skip1:
846 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
847 /* Write to socket from buffer 2 */
848 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
849 count = safe_write_double_iac(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
850 if (count < 0) {
851 if (errno == EAGAIN)
852 goto skip2;
853 goto kill_session;
854 }
855 ts->wridx2 += count;
856 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
857 ts->wridx2 = 0;
858 ts->size2 -= count;
859 if (ts->size2 == 0) {
860 ts->rdidx2 = 0;
861 ts->wridx2 = 0;
862 }
863 }
864 skip2:
865
866 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
867 /* Read from socket to buffer 1 */
868 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
869 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
870 if (count <= 0) {
871 if (count < 0 && errno == EAGAIN)
872 goto skip3;
873 goto kill_session;
874 }
875 /* Ignore trailing NUL if it is there */
876 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
877 --count;
878 }
879 ts->size1 += count;
880 ts->rdidx1 += count;
881 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
882 ts->rdidx1 = 0;
883 }
884 skip3:
885 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
886 /* Read from pty to buffer 2 */
887 int eio = 0;
888 read_pty:
889 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
890 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
891 if (count <= 0) {
892 if (count < 0) {
893 if (errno == EAGAIN)
894 goto skip4;
895 /* login process might call vhangup(),
896 * which causes intermittent EIOs on read above
897 * (observed on kernel 4.12.0). Try up to 10 ms.
898 */
899 if (errno == EIO && eio < 10) {
900 eio++;
901 //bb_error_msg("EIO pty %u", eio);
902 usleep(1000);
903 goto read_pty;
904 }
905 }
906 goto kill_session;
907 }
908 ts->size2 += count;
909 ts->rdidx2 += count;
910 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
911 ts->rdidx2 = 0;
912 }
913 skip4:
914 ts = next;
915 continue;
916 kill_session:
917 if (ts->shell_pid > 0)
918 update_utmp_DEAD_PROCESS(ts->shell_pid);
919 free_session(ts);
920 ts = next;
921 }
922
923 goto again;
924}
diff --git a/networking/tls.c b/networking/tls.c
index 46bfd9b24..c7015e044 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -57,13 +57,15 @@
57#endif 57#endif
58 58
59#if TLS_DEBUG 59#if TLS_DEBUG
60# define dbg(...) fprintf(stderr, __VA_ARGS__) 60# define dbg(...) bb_error_msg(__VA_ARGS__)
61# define dbgcont(...) fprintf(stderr, __VA_ARGS__)
61#else 62#else
62# define dbg(...) ((void)0) 63# define dbg(...) ((void)0)
64# define dbgcont(...) ((void)0)
63#endif 65#endif
64 66
65#if TLS_DEBUG_DER 67#if TLS_DEBUG_DER
66# define dbg_der(...) fprintf(stderr, __VA_ARGS__) 68# define dbg_der(...) bb_error_msg(__VA_ARGS__)
67#else 69#else
68# define dbg_der(...) ((void)0) 70# define dbg_der(...) ((void)0)
69#endif 71#endif
@@ -188,6 +190,76 @@
188#define TLS_MAX_CRYPTBLOCK_SIZE 16 190#define TLS_MAX_CRYPTBLOCK_SIZE 16
189#define TLS_MAX_OUTBUF (1 << 14) 191#define TLS_MAX_OUTBUF (1 << 14)
190 192
193/* Cipher suites we support, in preference order (best first) */
194#define NUM_CIPHERS (0 \
195 + 4 * ENABLE_FEATURE_TLS_SHA1 \
196 + ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 \
197 + ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 \
198 + ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \
199 + ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 \
200 + 2 * ENABLE_FEATURE_TLS_SHA1 \
201 + ALLOW_RSA_WITH_AES_128_CBC_SHA256 \
202 + ALLOW_RSA_WITH_AES_256_CBC_SHA256 \
203 + ALLOW_RSA_WITH_AES_128_GCM_SHA256 \
204 + ALLOW_RSA_NULL_SHA256 \
205 )
206static const uint8_t client_hello_ciphers[] = {
207 0x00,2 * (1 + NUM_CIPHERS), //len16_be
208 0x00,0xFF, //not a cipher - TLS_EMPTY_RENEGOTIATION_INFO_SCSV
209 /* ^^^^^^ RFC 5746 Renegotiation Indication Extension - some servers will refuse to work with us otherwise */
210#if ENABLE_FEATURE_TLS_SHA1
211 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/
212 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/
213 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA
214 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl)
215// 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA
216// 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA
217#endif
218#if ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
219 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/
220#endif
221// 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
222#if ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256
223 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256
224#endif
225// 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
226#if ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
227 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/
228#endif
229// 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC"
230//TODO: GCM_SHA384 ciphers can be supported, only need sha384-based PRF?
231#if ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256
232 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256
233#endif
234// 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac"
235//possibly these too:
236#if ENABLE_FEATURE_TLS_SHA1
237// 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
238// 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
239#endif
240// 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
241// 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
242#if ENABLE_FEATURE_TLS_SHA1
243 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA
244 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA
245#endif
246#if ALLOW_RSA_WITH_AES_128_CBC_SHA256
247 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256
248#endif
249#if ALLOW_RSA_WITH_AES_256_CBC_SHA256
250 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256
251#endif
252#if ALLOW_RSA_WITH_AES_128_GCM_SHA256
253 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256
254#endif
255// 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac"
256#if ALLOW_RSA_NULL_SHA256
257 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256
258#endif
259 0x01,0x00, //not a cipher - comprtypes_len, comprtype
260};
261#define supported_ciphers (client_hello_ciphers + 4)
262
191enum { 263enum {
192 AES128_KEYSIZE = 16, 264 AES128_KEYSIZE = 16,
193 AES256_KEYSIZE = 32, 265 AES256_KEYSIZE = 32,
@@ -241,11 +313,75 @@ enum {
241 GOT_CERT_RSA_KEY_ALG = 1 << 1, 313 GOT_CERT_RSA_KEY_ALG = 1 << 1,
242 GOT_CERT_ECDSA_KEY_ALG = 1 << 2, // so far unused 314 GOT_CERT_ECDSA_KEY_ALG = 1 << 2, // so far unused
243 GOT_EC_KEY = 1 << 3, 315 GOT_EC_KEY = 1 << 3,
244 GOT_EC_CURVE_X25519 = 1 << 4, // else P256 316 /* Client: server sent x25519 key in SERVER_KEY_EXCHANGE (else P256)
317 * Server: we chose x25519 based on client's supported_groups (else P256) */
318 USE_EC_CURVE_X25519 = 1 << 4,
245 ENCRYPTION_AESGCM = 1 << 5, // else AES-SHA (or NULL-SHA if ALLOW_RSA_NULL_SHA256=1) 319 ENCRYPTION_AESGCM = 1 << 5, // else AES-SHA (or NULL-SHA if ALLOW_RSA_NULL_SHA256=1)
246 ENCRYPT_ON_WRITE = 1 << 6,
247}; 320};
248 321
322#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
323/* Note: return value matches KEY_RSA (0) / KEY_ECDSA (1) enum values */
324static int is_cipher_ECDSA(const uint8_t *cipherid)
325{
326 uint8_t cipher_lo;
327 if (cipherid[0] != 0xC0)
328 return 0;
329 /* ECDHE cipher - check if ECDSA or RSA */
330 cipher_lo = cipherid[1];
331 return (cipher_lo == 0x09 || cipher_lo == 0x0A
332 || cipher_lo == 0x23 || cipher_lo == 0x2B
333 );
334}
335#endif
336
337/* Set cipher parameters based on selected cipher_id */
338static void set_cipher_parameters(tls_state_t *tls, const uint8_t *cipherid)
339{
340 uint8_t cipherid1 = cipherid[1];
341
342 tls->cipher_id = 0x100 * cipherid[0] + cipherid1;
343
344 /* Set defaults for RSA with AES-256 */
345 tls->key_size = AES256_KEYSIZE;
346 tls->MAC_size = SHA256_OUTSIZE;
347 tls->IV_size = 0;
348
349 if (cipherid[0] == 0xC0) {
350 /* All C0xx are ECDHE */
351 tls->flags |= NEED_EC_KEY;
352 if (cipherid1 & 1) {
353 /* Odd numbered C0xx use AES128 (even ones use AES256) */
354 tls->key_size = AES128_KEYSIZE;
355 }
356 if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x19) {
357 tls->MAC_size = SHA1_OUTSIZE;
358 } else
359 if (cipherid1 >= 0x2B && cipherid1 <= 0x30) {
360 /* C02B,2C,2F,30 are AES-GCM */
361 tls->flags |= ENCRYPTION_AESGCM;
362 tls->MAC_size = 0;
363 tls->IV_size = 4;
364 }
365 } else {
366 /* All 00xx are RSA */
367 if ((ENABLE_FEATURE_TLS_SHA1 && cipherid1 == 0x2F)
368 || cipherid1 == 0x3C
369 || cipherid1 == 0x9C
370 ) {
371 tls->key_size = AES128_KEYSIZE;
372 }
373 if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x35) {
374 tls->MAC_size = SHA1_OUTSIZE;
375 } else
376 if (cipherid1 == 0x9C /*|| cipherid1 == 0x9D*/) {
377 /* 009C,9D are AES-GCM */
378 tls->flags |= ENCRYPTION_AESGCM;
379 tls->MAC_size = 0;
380 tls->IV_size = 4;
381 }
382 }
383}
384
249struct record_hdr { 385struct record_hdr {
250 uint8_t type; 386 uint8_t type;
251 uint8_t proto_maj, proto_min; 387 uint8_t proto_maj, proto_min;
@@ -271,14 +407,46 @@ struct tls_handshake_data {
271/* HANDSHAKE HASH: */ 407/* HANDSHAKE HASH: */
272 //unsigned saved_client_hello_size; 408 //unsigned saved_client_hello_size;
273 //uint8_t saved_client_hello[1]; 409 //uint8_t saved_client_hello[1];
274};
275 410
411#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
412 smallint reneg_info_requested;
413 /* Server certificate and key data */
414 char *keys[2];
415 char *certs[2];
416 unsigned keysize[2];
417 unsigned certsize[2];
418 int key_type_chosen;
419 psRsaKey_t rsa_priv_key;
420
421 /* For ECDHE: server's ephemeral EC private key */
422 uint8_t ecc_priv_key32[32];
423#endif
424};
425enum {
426 KEY_RSA,
427 KEY_ECDSA,
428};
276 429
277static unsigned get24be(const uint8_t *p) 430static unsigned get24be(const uint8_t *p)
278{ 431{
279 return 0x100*(0x100*p[0] + p[1]) + p[2]; 432 return 0x100*(0x100*p[0] + p[1]) + p[2];
280} 433}
281 434
435#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
436static int is_minor_version_valid(tls_state_t *tls, uint8_t minor_ver)
437{
438 if (tls->expecting_first_packet == 1) {
439 /* First packet: accept TLS 1.0 (3.1) through TLS 1.3 (3.4)
440 * for compatibility with clients using other record versions */
441 return (minor_ver > 0 && minor_ver <= 4);
442 }
443 /* Subsequent packets: must match negotiated version exactly */
444 return minor_ver == TLS_MIN;
445}
446#else
447#define is_minor_version_valid(tls, minor_ver) ((minor_ver) == TLS_MIN)
448#endif
449
282#if TLS_DEBUG 450#if TLS_DEBUG
283/* Nondestructively see the current hash value */ 451/* Nondestructively see the current hash value */
284# if TLS_DEBUG_HASH 452# if TLS_DEBUG_HASH
@@ -295,7 +463,7 @@ static void dump_hex(const char *fmt, const void *vp, int len)
295 const uint8_t *p = vp; 463 const uint8_t *p = vp;
296 464
297 bin2hex(hexbuf, (void*)p, len)[0] = '\0'; 465 bin2hex(hexbuf, (void*)p, len)[0] = '\0';
298 dbg(fmt, hexbuf); 466 bb_error_msg(fmt, hexbuf);
299} 467}
300 468
301static void dump_tls_record(const void *vp, int len) 469static void dump_tls_record(const void *vp, int len)
@@ -305,20 +473,20 @@ static void dump_tls_record(const void *vp, int len)
305 while (len > 0) { 473 while (len > 0) {
306 unsigned xhdr_len; 474 unsigned xhdr_len;
307 if (len < RECHDR_LEN) { 475 if (len < RECHDR_LEN) {
308 dump_hex("< |%s|\n", p, len); 476 dump_hex("< |%s|", p, len);
309 return; 477 return;
310 } 478 }
311 xhdr_len = 0x100*p[3] + p[4]; 479 xhdr_len = 0x100*p[3] + p[4];
312 dbg("< hdr_type:%u ver:%u.%u len:%u", p[0], p[1], p[2], xhdr_len); 480 dbgcont("%s: < hdr_type:%u ver:%u.%u len:%u", applet_name, p[0], p[1], p[2], xhdr_len);
313 p += RECHDR_LEN; 481 p += RECHDR_LEN;
314 len -= RECHDR_LEN; 482 len -= RECHDR_LEN;
315 if (len >= 4 && p[-RECHDR_LEN] == RECORD_TYPE_HANDSHAKE) { 483 if (len >= 4 && p[-RECHDR_LEN] == RECORD_TYPE_HANDSHAKE) {
316 unsigned len24 = get24be(p + 1); 484 unsigned len24 = get24be(p + 1);
317 dbg(" type:%u len24:%u", p[0], len24); 485 dbgcont(" type:%u len24:%u", p[0], len24);
318 } 486 }
319 if (xhdr_len > len) 487 if (xhdr_len > len)
320 xhdr_len = len; 488 xhdr_len = len;
321 dump_hex(" |%s|\n", p, xhdr_len); 489 dump_hex(" |%s|", p, xhdr_len);
322 p += xhdr_len; 490 p += xhdr_len;
323 len -= xhdr_len; 491 len -= xhdr_len;
324 } 492 }
@@ -348,12 +516,12 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
348 dbg(" (%u bytes) ", (int)len); 516 dbg(" (%u bytes) ", (int)len);
349 len = sha_peek(&tls->hsd->handshake_hash_ctx, h); 517 len = sha_peek(&tls->hsd->handshake_hash_ctx, h);
350 if (ENABLE_FEATURE_TLS_SHA1 && len == SHA1_OUTSIZE) 518 if (ENABLE_FEATURE_TLS_SHA1 && len == SHA1_OUTSIZE)
351 dump_hex("sha1:%s\n", h, len); 519 dump_hex("sha1:%s", h, len);
352 else 520 else
353 if (len == SHA256_OUTSIZE) 521 if (len == SHA256_OUTSIZE)
354 dump_hex("sha256:%s\n", h, len); 522 dump_hex("sha256:%s", h, len);
355 else 523 else
356 dump_hex("sha???:%s\n", h, len); 524 dump_hex("sha???:%s", h, len);
357 } 525 }
358#endif 526#endif
359} 527}
@@ -550,11 +718,11 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
550 xhdr->proto_min = TLS_MIN; 718 xhdr->proto_min = TLS_MIN;
551 /* fake unencrypted record len for MAC calculation */ 719 /* fake unencrypted record len for MAC calculation */
552 xhdr->len16_hi = size >> 8; 720 xhdr->len16_hi = size >> 8;
553 xhdr->len16_lo = size & 0xff; 721 xhdr->len16_lo = size; // & 0xff implicit
554 722
555 /* Calculate MAC signature */ 723 /* Calculate MAC signature */
556 hmac_blocks(tls, buf + size, /* result */ 724 hmac_blocks(tls, buf + size, /* result */
557 tls->client_write_MAC_key, TLS_MAC_SIZE(tls), 725 tls->our_write_MAC_key, TLS_MAC_SIZE(tls),
558 &tls->write_seq64_be, sizeof(tls->write_seq64_be), 726 &tls->write_seq64_be, sizeof(tls->write_seq64_be),
559 xhdr, RECHDR_LEN, 727 xhdr, RECHDR_LEN,
560 buf, size, 728 buf, size,
@@ -602,10 +770,10 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
602 ) { 770 ) {
603 /* No encryption, only signing */ 771 /* No encryption, only signing */
604 xhdr->len16_hi = size >> 8; 772 xhdr->len16_hi = size >> 8;
605 xhdr->len16_lo = size & 0xff; 773 xhdr->len16_lo = size; // & 0xff implicit
606 dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); 774 dump_raw_out(">> %s", xhdr, RECHDR_LEN + size);
607 xwrite(tls->ofd, xhdr, RECHDR_LEN + size); 775 xwrite(tls->ofd, xhdr, RECHDR_LEN + size);
608 dbg("wrote %u bytes (NULL crypt, SHA256 hash)\n", size); 776 dbg("wrote %u bytes (NULL crypt, SHA256 hash)", size);
609 return; 777 return;
610 } 778 }
611 779
@@ -646,7 +814,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
646 // AES_256_CBC Block 32 16 16 814 // AES_256_CBC Block 32 16 16
647 815
648 tls_get_random(buf - AES_BLOCK_SIZE, AES_BLOCK_SIZE); /* IV */ 816 tls_get_random(buf - AES_BLOCK_SIZE, AES_BLOCK_SIZE); /* IV */
649 dbg("before crypt: 5 hdr + %u data + %u hash bytes\n", 817 dbg("before crypt: 5 hdr + %u data + %u hash bytes",
650 size - TLS_MAC_SIZE(tls), TLS_MAC_SIZE(tls)); 818 size - TLS_MAC_SIZE(tls), TLS_MAC_SIZE(tls));
651 819
652 /* Fill IV and padding in outbuf */ 820 /* Fill IV and padding in outbuf */
@@ -679,14 +847,14 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
679 ); 847 );
680 848
681 /* Write out */ 849 /* Write out */
682 dbg("writing 5 + %u IV + %u encrypted bytes, padding_length:0x%02x\n", 850 dbg("writing 5 + %u IV + %u encrypted bytes, padding_length:0x%02x",
683 AES_BLOCK_SIZE, size, padding_length); 851 AES_BLOCK_SIZE, size, padding_length);
684 size += AES_BLOCK_SIZE; /* + IV */ 852 size += AES_BLOCK_SIZE; /* + IV */
685 xhdr->len16_hi = size >> 8; 853 xhdr->len16_hi = size >> 8;
686 xhdr->len16_lo = size & 0xff; 854 xhdr->len16_lo = size; // & 0xff implicit
687 dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); 855 dump_raw_out(">> %s", xhdr, RECHDR_LEN + size);
688 xwrite(tls->ofd, xhdr, RECHDR_LEN + size); 856 xwrite(tls->ofd, xhdr, RECHDR_LEN + size);
689 dbg("wrote %u bytes\n", (int)RECHDR_LEN + size); 857 dbg("wrote %u bytes", (int)RECHDR_LEN + size);
690} 858}
691 859
692/* Example how GCM encryption combines nonce, aad, input and generates 860/* Example how GCM encryption combines nonce, aad, input and generates
@@ -714,7 +882,7 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
714 uint64_t t64; 882 uint64_t t64;
715 883
716 buf = tls->outbuf + OUTBUF_PFX; /* see above for the byte it points to */ 884 buf = tls->outbuf + OUTBUF_PFX; /* see above for the byte it points to */
717 dump_hex("xwrite_encrypted_aesgcm plaintext:%s\n", buf, size); 885 dump_hex("xwrite_encrypted_aesgcm plaintext:%s", buf, size);
718 886
719 xhdr = (void*)(buf - 8 - RECHDR_LEN); 887 xhdr = (void*)(buf - 8 - RECHDR_LEN);
720 xhdr->type = type; /* do it here so that "type" param no longer used */ 888 xhdr->type = type; /* do it here so that "type" param no longer used */
@@ -726,7 +894,7 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
726 /* set aad[12], and clear aad[13..15] */ 894 /* set aad[12], and clear aad[13..15] */
727 COUNTER(aad) = SWAP_LE32(size & 0xff); 895 COUNTER(aad) = SWAP_LE32(size & 0xff);
728 896
729 memcpy(nonce, tls->client_write_IV, 4); 897 memcpy(nonce, tls->our_write_IV, 4);
730 t64 = tls->write_seq64_be; 898 t64 = tls->write_seq64_be;
731 move_to_unaligned64(nonce + 4, t64); 899 move_to_unaligned64(nonce + 4, t64);
732 move_to_unaligned64(aad, t64); 900 move_to_unaligned64(aad, t64);
@@ -767,11 +935,11 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
767 xhdr->proto_maj = TLS_MAJ; 935 xhdr->proto_maj = TLS_MAJ;
768 xhdr->proto_min = TLS_MIN; 936 xhdr->proto_min = TLS_MIN;
769 xhdr->len16_hi = size >> 8; 937 xhdr->len16_hi = size >> 8;
770 xhdr->len16_lo = size & 0xff; 938 xhdr->len16_lo = size; // & 0xff implicit
771 size += RECHDR_LEN; 939 size += RECHDR_LEN;
772 dump_raw_out(">> %s\n", xhdr, size); 940 dump_raw_out(">> %s", xhdr, size);
773 xwrite(tls->ofd, xhdr, size); 941 xwrite(tls->ofd, xhdr, size);
774 dbg("wrote %u bytes\n", size); 942 dbg("wrote %u bytes", size);
775#undef COUNTER 943#undef COUNTER
776} 944}
777 945
@@ -793,24 +961,19 @@ static void xwrite_handshake_record(tls_state_t *tls, unsigned size)
793 xhdr->proto_maj = TLS_MAJ; 961 xhdr->proto_maj = TLS_MAJ;
794 xhdr->proto_min = TLS_MIN; 962 xhdr->proto_min = TLS_MIN;
795 xhdr->len16_hi = size >> 8; 963 xhdr->len16_hi = size >> 8;
796 xhdr->len16_lo = size & 0xff; 964 xhdr->len16_lo = size; // & 0xff implicit
797 dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); 965 dump_raw_out(">> %s", xhdr, RECHDR_LEN + size);
798 xwrite(tls->ofd, xhdr, RECHDR_LEN + size); 966 xwrite(tls->ofd, xhdr, RECHDR_LEN + size);
799 dbg("wrote %u bytes\n", (int)RECHDR_LEN + size); 967 dbg("wrote %u bytes", (int)RECHDR_LEN + size);
800} 968}
801 969
802static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size) 970static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size)
803{ 971{
804 if (!(tls->flags & ENCRYPT_ON_WRITE)) { 972 uint8_t *buf;
805 uint8_t *buf; 973 xwrite_handshake_record(tls, size);
806 974 /* Handshake hash does not include record headers */
807 xwrite_handshake_record(tls, size); 975 buf = tls->outbuf + OUTBUF_PFX;
808 /* Handshake hash does not include record headers */ 976 hash_handshake(tls, ">> hash:%s", buf, size);
809 buf = tls->outbuf + OUTBUF_PFX;
810 hash_handshake(tls, ">> hash:%s", buf, size);
811 return;
812 }
813 xwrite_encrypted(tls, size, RECORD_TYPE_HANDSHAKE);
814} 977}
815 978
816static int tls_has_buffered_record(tls_state_t *tls) 979static int tls_has_buffered_record(tls_state_t *tls)
@@ -894,7 +1057,7 @@ static void tls_aesgcm_decrypt(tls_state_t *tls, uint8_t *buf, int size)
894 ///* set aad[12], and clear aad[13..15] */ 1057 ///* set aad[12], and clear aad[13..15] */
895 //COUNTER(aad) = SWAP_LE32(size & 0xff); 1058 //COUNTER(aad) = SWAP_LE32(size & 0xff);
896 1059
897 memcpy(nonce, tls->server_write_IV, 4); 1060 memcpy(nonce, tls->peer_write_IV, 4);
898 memcpy(nonce + 4, buf, 8); 1061 memcpy(nonce + 4, buf, 8);
899 1062
900 cnt = 1; 1063 cnt = 1;
@@ -928,12 +1091,12 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
928 int target; 1091 int target;
929 1092
930 again: 1093 again:
931 dbg("ofs_to_buffered:%u buffered_size:%u\n", tls->ofs_to_buffered, tls->buffered_size); 1094 dbg("ofs_to_buffered:%u buffered_size:%u", tls->ofs_to_buffered, tls->buffered_size);
932 total = tls->buffered_size; 1095 total = tls->buffered_size;
933 if (total != 0) { 1096 if (total != 0) {
934 memmove(tls->inbuf, tls->inbuf + tls->ofs_to_buffered, total); 1097 memmove(tls->inbuf, tls->inbuf + tls->ofs_to_buffered, total);
935 //dbg("<< remaining at %d [%d] ", tls->ofs_to_buffered, total); 1098 //dbg("<< remaining at %d [%d] ", tls->ofs_to_buffered, total);
936 //dump_raw_in("<< %s\n", tls->inbuf, total); 1099 //dump_raw_in("<< %s", tls->inbuf, total);
937 } 1100 }
938 errno = 0; 1101 errno = 0;
939 target = MAX_INBUF; 1102 target = MAX_INBUF;
@@ -946,12 +1109,12 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
946 1109
947 if (target > MAX_INBUF /* malformed input (too long) */ 1110 if (target > MAX_INBUF /* malformed input (too long) */
948 || xhdr->proto_maj != TLS_MAJ 1111 || xhdr->proto_maj != TLS_MAJ
949 || xhdr->proto_min != TLS_MIN 1112 || !is_minor_version_valid(tls, xhdr->proto_min)
950 ) { 1113 ) {
951 sz = total < target ? total : target; 1114 sz = total < target ? total : target;
952 bad_record_die(tls, expected, sz); 1115 bad_record_die(tls, expected, sz);
953 } 1116 }
954 dbg("xhdr type:%d ver:%d.%d len:%d\n", 1117 dbg("xhdr type:%d ver:%d.%d len:%d",
955 xhdr->type, xhdr->proto_maj, xhdr->proto_min, 1118 xhdr->type, xhdr->proto_maj, xhdr->proto_min,
956 0x100 * xhdr->len16_hi + xhdr->len16_lo 1119 0x100 * xhdr->len16_hi + xhdr->len16_lo
957 ); 1120 );
@@ -965,7 +1128,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
965 tls->inbuf_size += MAX_INBUF / 8; 1128 tls->inbuf_size += MAX_INBUF / 8;
966 if (tls->inbuf_size > MAX_INBUF) 1129 if (tls->inbuf_size > MAX_INBUF)
967 tls->inbuf_size = MAX_INBUF; 1130 tls->inbuf_size = MAX_INBUF;
968 dbg("inbuf_size:%d\n", tls->inbuf_size); 1131 dbg("inbuf_size:%d", tls->inbuf_size);
969 rem = tls->inbuf_size - total; 1132 rem = tls->inbuf_size - total;
970 tls->inbuf = xrealloc(tls->inbuf, tls->inbuf_size); 1133 tls->inbuf = xrealloc(tls->inbuf, tls->inbuf_size);
971 } 1134 }
@@ -973,7 +1136,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
973 if (sz <= 0) { 1136 if (sz <= 0) {
974 if (sz == 0 && total == 0) { 1137 if (sz == 0 && total == 0) {
975 /* "Abrupt" EOF, no TLS shutdown (seen from kernel.org) */ 1138 /* "Abrupt" EOF, no TLS shutdown (seen from kernel.org) */
976 dbg("EOF (without TLS shutdown) from peer\n"); 1139 dbg("EOF (without TLS shutdown) from peer");
977 tls->buffered_size = 0; 1140 tls->buffered_size = 0;
978 goto end; 1141 goto end;
979 } 1142 }
@@ -982,13 +1145,13 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
982 bb_perror_msg_and_die("%s header: got %d bytes", "truncated TLS record", total); 1145 bb_perror_msg_and_die("%s header: got %d bytes", "truncated TLS record", total);
983 bb_perror_msg_and_die("%s: expected %d, got %d bytes", "truncated TLS record", target, total); 1146 bb_perror_msg_and_die("%s: expected %d, got %d bytes", "truncated TLS record", target, total);
984 } 1147 }
985 dump_raw_in("<< %s\n", tls->inbuf + total, sz); 1148 dump_raw_in("<< %s", tls->inbuf + total, sz);
986 total += sz; 1149 total += sz;
987 } 1150 }
988 tls->buffered_size = total - target; 1151 tls->buffered_size = total - target;
989 tls->ofs_to_buffered = target; 1152 tls->ofs_to_buffered = target;
990 //dbg("<< stashing at %d [%d] ", tls->ofs_to_buffered, tls->buffered_size); 1153 //dbg("<< stashing at %d [%d] ", tls->ofs_to_buffered, tls->buffered_size);
991 //dump_hex("<< %s\n", tls->inbuf + tls->ofs_to_buffered, tls->buffered_size); 1154 //dump_hex("<< %s", tls->inbuf + tls->ofs_to_buffered, tls->buffered_size);
992 1155
993 sz = target - RECHDR_LEN; 1156 sz = target - RECHDR_LEN;
994 1157
@@ -1003,7 +1166,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
1003 1166
1004 sz -= 8 + AES_BLOCK_SIZE; /* we will overwrite nonce, drop hash */ 1167 sz -= 8 + AES_BLOCK_SIZE; /* we will overwrite nonce, drop hash */
1005 tls_aesgcm_decrypt(tls, p, sz); 1168 tls_aesgcm_decrypt(tls, p, sz);
1006 dbg("encrypted size:%u\n", sz); 1169 dbg("encrypted size:%u", sz);
1007 } else 1170 } else
1008 if (tls->min_encrypted_len_on_read > TLS_MAC_SIZE(tls)) { 1171 if (tls->min_encrypted_len_on_read > TLS_MAC_SIZE(tls)) {
1009 /* AES+SHA */ 1172 /* AES+SHA */
@@ -1022,7 +1185,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
1022 p /* plaintext */ 1185 p /* plaintext */
1023 ); 1186 );
1024 padding_len = p[sz - 1]; 1187 padding_len = p[sz - 1];
1025 dbg("encrypted size:%u type:0x%02x padding_length:0x%02x\n", sz, p[0], padding_len); 1188 dbg("encrypted size:%u type:0x%02x padding_length:0x%02x", sz, p[0], padding_len);
1026 padding_len++; 1189 padding_len++;
1027 sz -= TLS_MAC_SIZE(tls) + padding_len; /* drop MAC and padding */ 1190 sz -= TLS_MAC_SIZE(tls) + padding_len; /* drop MAC and padding */
1028 } else { 1191 } else {
@@ -1034,12 +1197,12 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
1034 if (sz < 0) 1197 if (sz < 0)
1035 bb_simple_error_msg_and_die("encrypted data too short"); 1198 bb_simple_error_msg_and_die("encrypted data too short");
1036 1199
1037 //dump_hex("<< %s\n", tls->inbuf, RECHDR_LEN + sz); 1200 //dump_hex("<< %s", tls->inbuf, RECHDR_LEN + sz);
1038 1201
1039 xhdr = (void*)tls->inbuf; 1202 xhdr = (void*)tls->inbuf;
1040 if (xhdr->type == RECORD_TYPE_ALERT && sz >= 2) { 1203 if (xhdr->type == RECORD_TYPE_ALERT && sz >= 2) {
1041 uint8_t *p = tls->inbuf + RECHDR_LEN; 1204 uint8_t *p = tls->inbuf + RECHDR_LEN;
1042 dbg("ALERT size:%d level:%d description:%d\n", sz, p[0], p[1]); 1205 dbg("ALERT size:%d level:%d description:%d", sz, p[0], p[1]);
1043 if (p[0] == 2) { /* fatal */ 1206 if (p[0] == 2) { /* fatal */
1044 bb_error_msg_and_die("TLS %s from peer (alert code %d): %s", 1207 bb_error_msg_and_die("TLS %s from peer (alert code %d): %s",
1045 "error", 1208 "error",
@@ -1048,7 +1211,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
1048 } 1211 }
1049 if (p[0] == 1) { /* warning */ 1212 if (p[0] == 1) { /* warning */
1050 if (p[1] == 0) { /* "close_notify" warning: it's EOF */ 1213 if (p[1] == 0) { /* "close_notify" warning: it's EOF */
1051 dbg("EOF (TLS encoded) from peer\n"); 1214 dbg("EOF (TLS encoded) from peer");
1052 sz = 0; 1215 sz = 0;
1053 goto end; 1216 goto end;
1054 } 1217 }
@@ -1076,7 +1239,7 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
1076 hash_handshake(tls, "<< hash:%s", tls->inbuf + RECHDR_LEN, sz); 1239 hash_handshake(tls, "<< hash:%s", tls->inbuf + RECHDR_LEN, sz);
1077 } 1240 }
1078 end: 1241 end:
1079 dbg("got block len:%u\n", sz); 1242 dbg("got block len:%u", sz);
1080 return sz; 1243 return sz;
1081} 1244}
1082 1245
@@ -1136,7 +1299,7 @@ static uint8_t *enter_der_item(uint8_t *der, uint8_t **endp)
1136{ 1299{
1137 uint8_t *new_der; 1300 uint8_t *new_der;
1138 unsigned len = get_der_len(&new_der, der, *endp); 1301 unsigned len = get_der_len(&new_der, der, *endp);
1139 dbg_der("entered der @%p:0x%02x len:%u inner_byte @%p:0x%02x\n", der, der[0], len, new_der, new_der[0]); 1302 dbg_der("entered der @%p:0x%02x len:%u inner_byte @%p:0x%02x", der, der[0], len, new_der, new_der[0]);
1140 /* Move "end" position to cover only this item */ 1303 /* Move "end" position to cover only this item */
1141 *endp = new_der + len; 1304 *endp = new_der + len;
1142 return new_der; 1305 return new_der;
@@ -1148,7 +1311,7 @@ static uint8_t *skip_der_item(uint8_t *der, uint8_t *end)
1148 unsigned len = get_der_len(&new_der, der, end); 1311 unsigned len = get_der_len(&new_der, der, end);
1149 /* Skip body */ 1312 /* Skip body */
1150 new_der += len; 1313 new_der += len;
1151 dbg_der("skipped der 0x%02x, next byte 0x%02x\n", der[0], new_der[0]); 1314 dbg_der("skipped der 0x%02x, next byte 0x%02x", der[0], new_der[0]);
1152 return new_der; 1315 return new_der;
1153} 1316}
1154 1317
@@ -1157,7 +1320,7 @@ static void der_binary_to_pstm(pstm_int *pstm_n, uint8_t *der, uint8_t *end)
1157 uint8_t *bin_ptr; 1320 uint8_t *bin_ptr;
1158 unsigned len = get_der_len(&bin_ptr, der, end); 1321 unsigned len = get_der_len(&bin_ptr, der, end);
1159 1322
1160 dbg_der("binary bytes:%u, first:0x%02x\n", len, bin_ptr[0]); 1323 dbg_der("binary bytes:%u, first:0x%02x", len, bin_ptr[0]);
1161 binary_to_pstm(pstm_n, bin_ptr, len); 1324 binary_to_pstm(pstm_n, bin_ptr, len);
1162} 1325}
1163 1326
@@ -1304,11 +1467,11 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len)
1304 //42.134.72.206.61.3.1.7 is curve_secp256r1 1467 //42.134.72.206.61.3.1.7 is curve_secp256r1
1305 }; 1468 };
1306 if (memcmp(der, OID_RSA_KEY_ALG, sizeof(OID_RSA_KEY_ALG)) == 0) { 1469 if (memcmp(der, OID_RSA_KEY_ALG, sizeof(OID_RSA_KEY_ALG)) == 0) {
1307 dbg("RSA key\n"); 1470 dbg("RSA key");
1308 tls->flags |= GOT_CERT_RSA_KEY_ALG; 1471 tls->flags |= GOT_CERT_RSA_KEY_ALG;
1309 } else 1472 } else
1310 if (memcmp(der, OID_ECDSA_KEY_ALG, sizeof(OID_ECDSA_KEY_ALG)) == 0) { 1473 if (memcmp(der, OID_ECDSA_KEY_ALG, sizeof(OID_ECDSA_KEY_ALG)) == 0) {
1311 dbg("ECDSA key\n"); 1474 dbg("ECDSA key");
1312 //UNUSED: tls->flags |= GOT_CERT_ECDSA_KEY_ALG; 1475 //UNUSED: tls->flags |= GOT_CERT_ECDSA_KEY_ALG;
1313 } else 1476 } else
1314 bb_simple_error_msg_and_die("not RSA or ECDSA cert"); 1477 bb_simple_error_msg_and_die("not RSA or ECDSA cert");
@@ -1323,7 +1486,7 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len)
1323 //die_if_not_this_der_type(der, end, 0x03); /* must be BITSTRING */ 1486 //die_if_not_this_der_type(der, end, 0x03); /* must be BITSTRING */
1324 der = enter_der_item(der, &end); 1487 der = enter_der_item(der, &end);
1325 1488
1326 dbg("key bytes:%u, first:0x%02x\n", (int)(end - der), der[0]); 1489 dbg("key bytes:%u, first:0x%02x", (int)(end - der), der[0]);
1327 if (end - der < 14) 1490 if (end - der < 14)
1328 xfunc_die(); 1491 xfunc_die();
1329 /* example format: 1492 /* example format:
@@ -1341,7 +1504,7 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len)
1341 der = skip_der_item(der, end); 1504 der = skip_der_item(der, end);
1342 der_binary_to_pstm(&tls->hsd->server_rsa_pub_key.e, der, end); /* exponent */ 1505 der_binary_to_pstm(&tls->hsd->server_rsa_pub_key.e, der, end); /* exponent */
1343 tls->hsd->server_rsa_pub_key.size = pstm_unsigned_bin_size(&tls->hsd->server_rsa_pub_key.N); 1506 tls->hsd->server_rsa_pub_key.size = pstm_unsigned_bin_size(&tls->hsd->server_rsa_pub_key.N);
1344 dbg("server_rsa_pub_key.size:%d\n", tls->hsd->server_rsa_pub_key.size); 1507 dbg("server_rsa_pub_key.size:%d", tls->hsd->server_rsa_pub_key.size);
1345 } 1508 }
1346 /* else: ECDSA key. It is not used for generating encryption keys, 1509 /* else: ECDSA key. It is not used for generating encryption keys,
1347 * it is used only to sign the EC public key (which comes in ServerKey message). 1510 * it is used only to sign the EC public key (which comes in ServerKey message).
@@ -1364,7 +1527,7 @@ static int tls_xread_handshake_block(tls_state_t *tls, int min_len)
1364 ) { 1527 ) {
1365 bad_record_die(tls, "handshake record", len); 1528 bad_record_die(tls, "handshake record", len);
1366 } 1529 }
1367 dbg("got HANDSHAKE\n"); 1530 dbg("got HANDSHAKE");
1368 return len; 1531 return len;
1369} 1532}
1370 1533
@@ -1379,78 +1542,18 @@ static ALWAYS_INLINE void fill_handshake_record_hdr(void *buf, unsigned type, un
1379 h->type = type; 1542 h->type = type;
1380 h->len24_hi = len >> 16; 1543 h->len24_hi = len >> 16;
1381 h->len24_mid = len >> 8; 1544 h->len24_mid = len >> 8;
1382 h->len24_lo = len & 0xff; 1545 h->len24_lo = len; // & 0xff implicit
1546}
1547
1548static void *get_outbuf_fill_handshake_record(tls_state_t *tls, unsigned type, unsigned len)
1549{
1550 void *record = tls_get_zeroed_outbuf(tls, len);
1551 fill_handshake_record_hdr(record, type, len);
1552 return record;
1383} 1553}
1384 1554
1385static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni) 1555static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1386{ 1556{
1387#define NUM_CIPHERS (0 \
1388 + 4 * ENABLE_FEATURE_TLS_SHA1 \
1389 + ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 \
1390 + ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 \
1391 + ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \
1392 + ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 \
1393 + 2 * ENABLE_FEATURE_TLS_SHA1 \
1394 + ALLOW_RSA_WITH_AES_128_CBC_SHA256 \
1395 + ALLOW_RSA_WITH_AES_256_CBC_SHA256 \
1396 + ALLOW_RSA_WITH_AES_128_GCM_SHA256 \
1397 + ALLOW_RSA_NULL_SHA256 \
1398 )
1399 static const uint8_t ciphers[] = {
1400 0x00,2 * (1 + NUM_CIPHERS), //len16_be
1401 0x00,0xFF, //not a cipher - TLS_EMPTY_RENEGOTIATION_INFO_SCSV
1402 /* ^^^^^^ RFC 5746 Renegotiation Indication Extension - some servers will refuse to work with us otherwise */
1403#if ENABLE_FEATURE_TLS_SHA1
1404 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/
1405 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/
1406 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA
1407 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl)
1408 // 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA
1409 // 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA
1410#endif
1411#if ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
1412 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/
1413#endif
1414 // 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1415#if ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256
1416 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256
1417#endif
1418 // 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1419#if ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
1420 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/
1421#endif
1422 // 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC"
1423//TODO: GCM_SHA384 ciphers can be supported, only need sha384-based PRF?
1424#if ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256
1425 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256
1426#endif
1427 // 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac"
1428 //possibly these too:
1429#if ENABLE_FEATURE_TLS_SHA1
1430 // 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
1431 // 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
1432#endif
1433 // 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
1434 // 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1435#if ENABLE_FEATURE_TLS_SHA1
1436 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA
1437 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA
1438#endif
1439#if ALLOW_RSA_WITH_AES_128_CBC_SHA256
1440 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256
1441#endif
1442#if ALLOW_RSA_WITH_AES_256_CBC_SHA256
1443 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256
1444#endif
1445#if ALLOW_RSA_WITH_AES_128_GCM_SHA256
1446 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256
1447#endif
1448 // 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac"
1449#if ALLOW_RSA_NULL_SHA256
1450 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256
1451#endif
1452 0x01,0x00, //not a cipher - comprtypes_len, comprtype
1453 };
1454 struct client_hello { 1557 struct client_hello {
1455 uint8_t type; 1558 uint8_t type;
1456 uint8_t len24_hi, len24_mid, len24_lo; 1559 uint8_t len24_hi, len24_mid, len24_lo;
@@ -1528,9 +1631,8 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1528 1631
1529 /* +2 is for "len of all extensions" 2-byte field */ 1632 /* +2 is for "len of all extensions" 2-byte field */
1530 len = sizeof(*record) + 2 + ext_len; 1633 len = sizeof(*record) + 2 + ext_len;
1531 record = tls_get_zeroed_outbuf(tls, len); 1634 record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_CLIENT_HELLO, len);
1532 1635
1533 fill_handshake_record_hdr(record, HANDSHAKE_CLIENT_HELLO, len);
1534 record->proto_maj = TLS_MAJ; /* the "requested" version of the protocol, */ 1636 record->proto_maj = TLS_MAJ; /* the "requested" version of the protocol, */
1535 record->proto_min = TLS_MIN; /* can be higher than one in record headers */ 1637 record->proto_min = TLS_MIN; /* can be higher than one in record headers */
1536 tls_get_random(record->rand32, sizeof(record->rand32)); 1638 tls_get_random(record->rand32, sizeof(record->rand32));
@@ -1538,8 +1640,8 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1538 memset(record->rand32, 0x11, sizeof(record->rand32)); 1640 memset(record->rand32, 0x11, sizeof(record->rand32));
1539 /* record->session_id_len = 0; - already is */ 1641 /* record->session_id_len = 0; - already is */
1540 1642
1541 BUILD_BUG_ON(sizeof(ciphers) != 2 * (1 + 1 + NUM_CIPHERS + 1)); 1643 BUILD_BUG_ON(sizeof(client_hello_ciphers) != 2 * (1 + 1 + NUM_CIPHERS + 1));
1542 memcpy(&record->cipherid_len16_hi, ciphers, sizeof(ciphers)); 1644 memcpy(&record->cipherid_len16_hi, client_hello_ciphers, sizeof(client_hello_ciphers));
1543 1645
1544 ptr = (void*)(record + 1); 1646 ptr = (void*)(record + 1);
1545 *ptr++ = ext_len >> 8; 1647 *ptr++ = ext_len >> 8;
@@ -1565,7 +1667,7 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1565 tls->hsd->saved_client_hello_size = len; 1667 tls->hsd->saved_client_hello_size = len;
1566 memcpy(tls->hsd->saved_client_hello, record, len); 1668 memcpy(tls->hsd->saved_client_hello, record, len);
1567 */ 1669 */
1568 dbg(">> CLIENT_HELLO\n"); 1670 dbg(">> CLIENT_HELLO");
1569 /* Can hash immediately only if we know which MAC hash to use. 1671 /* Can hash immediately only if we know which MAC hash to use.
1570 * So far we do know: it's sha256: 1672 * So far we do know: it's sha256:
1571 */ 1673 */
@@ -1594,7 +1696,6 @@ static void get_server_hello(tls_state_t *tls)
1594 1696
1595 struct server_hello *hp; 1697 struct server_hello *hp;
1596 uint8_t *cipherid; 1698 uint8_t *cipherid;
1597 uint8_t cipherid1;
1598 int len, len24; 1699 int len, len24;
1599 1700
1600 len = tls_xread_handshake_block(tls, 74 - 32); 1701 len = tls_xread_handshake_block(tls, 74 - 32);
@@ -1629,80 +1730,13 @@ static void get_server_hello(tls_state_t *tls)
1629 1730
1630 if (len24 < 70) 1731 if (len24 < 70)
1631 bad_record_die(tls, "'server hello'", len); 1732 bad_record_die(tls, "'server hello'", len);
1632 dbg("<< SERVER_HELLO\n"); 1733 dbg("<< SERVER_HELLO");
1633 1734
1634 memcpy(tls->hsd->client_and_server_rand32 + 32, hp->rand32, sizeof(hp->rand32)); 1735 memcpy(tls->hsd->client_and_server_rand32 + 32, hp->rand32, sizeof(hp->rand32));
1635 1736
1636 /* Set up encryption params based on selected cipher */ 1737 set_cipher_parameters(tls, cipherid);
1637#if 0 1738 dbg("server chose cipher %04x", tls->cipher_id);
1638 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/ 1739 dbg("key_size:%u MAC_size:%u IV_size:%u", tls->key_size, tls->MAC_size, tls->IV_size);
1639 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/
1640 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA
1641 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl)
1642 // 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA
1643 // 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA
1644 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/
1645 // 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1646 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256
1647 // 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1648 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/
1649 // 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC"
1650 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256
1651 // 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac"
1652 //possibly these too:
1653 // 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
1654 // 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
1655 // 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
1656 // 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet
1657 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA
1658 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA
1659 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256
1660 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256
1661 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256
1662 // 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac"
1663 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256
1664#endif
1665 cipherid1 = cipherid[1];
1666 tls->cipher_id = 0x100 * cipherid[0] + cipherid1;
1667 tls->key_size = AES256_KEYSIZE;
1668 tls->MAC_size = SHA256_OUTSIZE;
1669 /*tls->IV_size = 0; - already is */
1670 if (cipherid[0] == 0xC0) {
1671 /* All C0xx are ECDHE */
1672 tls->flags |= NEED_EC_KEY;
1673 if (cipherid1 & 1) {
1674 /* Odd numbered C0xx use AES128 (even ones use AES256) */
1675 tls->key_size = AES128_KEYSIZE;
1676 }
1677 if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x19) {
1678 tls->MAC_size = SHA1_OUTSIZE;
1679 } else
1680 if (cipherid1 >= 0x2B && cipherid1 <= 0x30) {
1681 /* C02B,2C,2F,30 are AES-GCM */
1682 tls->flags |= ENCRYPTION_AESGCM;
1683 tls->MAC_size = 0;
1684 tls->IV_size = 4;
1685 }
1686 } else {
1687 /* All 00xx are RSA */
1688 if ((ENABLE_FEATURE_TLS_SHA1 && cipherid1 == 0x2F)
1689 || cipherid1 == 0x3C
1690 || cipherid1 == 0x9C
1691 ) {
1692 tls->key_size = AES128_KEYSIZE;
1693 }
1694 if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x35) {
1695 tls->MAC_size = SHA1_OUTSIZE;
1696 } else
1697 if (cipherid1 == 0x9C /*|| cipherid1 == 0x9D*/) {
1698 /* 009C,9D are AES-GCM */
1699 tls->flags |= ENCRYPTION_AESGCM;
1700 tls->MAC_size = 0;
1701 tls->IV_size = 4;
1702 }
1703 }
1704 dbg("server chose cipher %04x\n", tls->cipher_id);
1705 dbg("key_size:%u MAC_size:%u IV_size:%u\n", tls->key_size, tls->MAC_size, tls->IV_size);
1706 1740
1707 /* Handshake hash eventually destined to FINISHED record 1741 /* Handshake hash eventually destined to FINISHED record
1708 * is sha256 regardless of cipher 1742 * is sha256 regardless of cipher
@@ -1732,7 +1766,7 @@ static void get_server_cert(tls_state_t *tls)
1732 certbuf = (void*)(xhdr + 1); 1766 certbuf = (void*)(xhdr + 1);
1733 if (certbuf[0] != HANDSHAKE_CERTIFICATE) 1767 if (certbuf[0] != HANDSHAKE_CERTIFICATE)
1734 bad_record_die(tls, "certificate", len); 1768 bad_record_die(tls, "certificate", len);
1735 dbg("<< CERTIFICATE\n"); 1769 dbg("<< CERTIFICATE");
1736 // 4392 bytes: 1770 // 4392 bytes:
1737 // 0b 00|11|24 00|11|21 00|05|b0 30|82|05|ac|30|82|04|94|a0|03|02|01|02|02|11|00|9f|85|bf|66|4b|0c|dd|af|ca|50|86|79|50|1b|2b|e4|30|0d... 1771 // 0b 00|11|24 00|11|21 00|05|b0 30|82|05|ac|30|82|04|94|a0|03|02|01|02|02|11|00|9f|85|bf|66|4b|0c|dd|af|ca|50|86|79|50|1b|2b|e4|30|0d...
1738 //Cert len=4388 ChainLen CertLen^ DER encoded X509 starts here. openssl x509 -in FILE -inform DER -noout -text 1772 //Cert len=4388 ChainLen CertLen^ DER encoded X509 starts here. openssl x509 -in FILE -inform DER -noout -text
@@ -1813,12 +1847,12 @@ static void process_server_key(tls_state_t *tls, int len)
1813 keybuf += 4; 1847 keybuf += 4;
1814 switch (t32) { 1848 switch (t32) {
1815 case _0x03001d20: //curve_x25519 1849 case _0x03001d20: //curve_x25519
1816 dbg("got x25519 eccPubKey\n"); 1850 dbg("got x25519 eccPubKey");
1817 tls->flags |= GOT_EC_CURVE_X25519; 1851 tls->flags |= USE_EC_CURVE_X25519;
1818 memcpy(tls->hsd->ecc_pub_key32, keybuf, 32); 1852 memcpy(tls->hsd->ecc_pub_key32, keybuf, 32);
1819 break; 1853 break;
1820 case _0x03001741: //curve_secp256r1 (aka P256) 1854 case _0x03001741: //curve_secp256r1 (aka P256)
1821 dbg("got P256 eccPubKey\n"); 1855 dbg("got P256 eccPubKey");
1822 /* P256 point can be transmitted odd- or even-compressed 1856 /* P256 point can be transmitted odd- or even-compressed
1823 * (first byte is 3 or 2) or uncompressed (4). 1857 * (first byte is 3 or 2) or uncompressed (4).
1824 */ 1858 */
@@ -1842,19 +1876,89 @@ static void send_empty_client_cert(tls_state_t *tls)
1842 }; 1876 };
1843 struct client_empty_cert *record; 1877 struct client_empty_cert *record;
1844 1878
1845 record = tls_get_zeroed_outbuf(tls, sizeof(*record)); 1879 record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_CERTIFICATE, sizeof(*record));
1846 //fill_handshake_record_hdr(record, HANDSHAKE_CERTIFICATE, sizeof(*record)); 1880 dbg(">> CERTIFICATE");
1847 //record->cert_chain_len24_hi = 0;
1848 //record->cert_chain_len24_mid = 0;
1849 //record->cert_chain_len24_lo = 0;
1850 // same as above:
1851 record->type = HANDSHAKE_CERTIFICATE;
1852 record->len24_lo = 3;
1853
1854 dbg(">> CERTIFICATE\n");
1855 xwrite_and_update_handshake_hash(tls, sizeof(*record)); 1881 xwrite_and_update_handshake_hash(tls, sizeof(*record));
1856} 1882}
1857 1883
1884static void derive_master_secret_and_keys(tls_state_t *tls, uint8_t *premaster, int premaster_size)
1885{
1886 uint8_t tmp64[64];
1887 // RFC 5246
1888 // For all key exchange methods, the same algorithm is used to convert
1889 // the pre_master_secret into the master_secret. The pre_master_secret
1890 // should be deleted from memory once the master_secret has been
1891 // computed.
1892 // master_secret = PRF(pre_master_secret, "master secret",
1893 // ClientHello.random + ServerHello.random)
1894 // [0..47];
1895 // The master secret is always exactly 48 bytes in length. The length
1896 // of the premaster secret will vary depending on key exchange method.
1897 prf_hmac_sha256(/*tls,*/
1898 tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
1899 premaster, premaster_size,
1900 "master secret",
1901 tls->hsd->client_and_server_rand32, sizeof(tls->hsd->client_and_server_rand32)
1902 );
1903 dump_hex("master secret:%s", tls->hsd->master_secret, sizeof(tls->hsd->master_secret));
1904
1905 // RFC 5246
1906 // 6.3. Key Calculation
1907 //
1908 // The Record Protocol requires an algorithm to generate keys required
1909 // by the current connection state (see Appendix A.6) from the security
1910 // parameters provided by the handshake protocol.
1911 //
1912 // The master secret is expanded into a sequence of secure bytes, which
1913 // is then split to a client write MAC key, a server write MAC key, a
1914 // client write encryption key, and a server write encryption key. Each
1915 // of these is generated from the byte sequence in that order. Unused
1916 // values are empty. Some AEAD ciphers may additionally require a
1917 // client write IV and a server write IV (see Section 6.2.3.3).
1918 //
1919 // When keys and MAC keys are generated, the master secret is used as an
1920 // entropy source.
1921 //
1922 // To generate the key material, compute
1923 //
1924 // key_block = PRF(SecurityParameters.master_secret,
1925 // "key expansion",
1926 // SecurityParameters.server_random +
1927 // SecurityParameters.client_random);
1928 //
1929 // until enough output has been generated. Then, the key_block is
1930 // partitioned as follows:
1931 //
1932 // client_write_MAC_key[SecurityParameters.mac_key_length]
1933 // server_write_MAC_key[SecurityParameters.mac_key_length]
1934 // client_write_key[SecurityParameters.enc_key_length]
1935 // server_write_key[SecurityParameters.enc_key_length]
1936 // client_write_IV[SecurityParameters.fixed_iv_length]
1937 // server_write_IV[SecurityParameters.fixed_iv_length]
1938
1939 /* make "server_rand32 + client_rand32" */
1940 memcpy(&tmp64[0] , &tls->hsd->client_and_server_rand32[32], 32);
1941 memcpy(&tmp64[32], &tls->hsd->client_and_server_rand32[0] , 32);
1942
1943 prf_hmac_sha256(/*tls,*/
1944 tls->key_block, 2 * (tls->MAC_size + tls->key_size + tls->IV_size),
1945 tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
1946 "key expansion",
1947 tmp64, 64
1948 );
1949}
1950
1951static void initialize_aes_keys(tls_state_t *tls)
1952{
1953 uint8_t iv[AES_BLOCK_SIZE];
1954 aes_setkey(&tls->aes_decrypt, tls->peer_write_key, tls->key_size);
1955 aes_setkey(&tls->aes_encrypt, tls->our_write_key, tls->key_size);
1956 if (1) { //if AESGCM
1957 memset(iv, 0, AES_BLOCK_SIZE);
1958 aes_encrypt_one_block(&tls->aes_encrypt, iv, tls->H);
1959 }
1960}
1961
1858static void send_client_key_exchange(tls_state_t *tls) 1962static void send_client_key_exchange(tls_state_t *tls)
1859{ 1963{
1860 struct client_key_exchange { 1964 struct client_key_exchange {
@@ -1863,11 +1967,13 @@ static void send_client_key_exchange(tls_state_t *tls)
1863 uint8_t key[2 + 4 * 1024]; // size?? 1967 uint8_t key[2 + 4 * 1024]; // size??
1864 }; 1968 };
1865//FIXME: better size estimate 1969//FIXME: better size estimate
1866 struct client_key_exchange *record = tls_get_zeroed_outbuf(tls, sizeof(*record)); 1970 struct client_key_exchange *record;
1867 uint8_t premaster[RSA_PREMASTER_SIZE > EC_CURVE_KEYSIZE ? RSA_PREMASTER_SIZE : EC_CURVE_KEYSIZE]; 1971 uint8_t premaster[RSA_PREMASTER_SIZE > EC_CURVE_KEYSIZE ? RSA_PREMASTER_SIZE : EC_CURVE_KEYSIZE];
1868 int premaster_size; 1972 int premaster_size;
1869 int len; 1973 int len;
1870 1974
1975 record = tls_get_zeroed_outbuf(tls, sizeof(*record));
1976
1871 if (!(tls->flags & NEED_EC_KEY)) { 1977 if (!(tls->flags & NEED_EC_KEY)) {
1872 /* RSA */ 1978 /* RSA */
1873 if (!(tls->flags & GOT_CERT_RSA_KEY_ALG)) 1979 if (!(tls->flags & GOT_CERT_RSA_KEY_ALG))
@@ -1882,7 +1988,7 @@ static void send_client_key_exchange(tls_state_t *tls)
1882 // version negotiated for the connection." 1988 // version negotiated for the connection."
1883 premaster[0] = TLS_MAJ; 1989 premaster[0] = TLS_MAJ;
1884 premaster[1] = TLS_MIN; 1990 premaster[1] = TLS_MIN;
1885 dump_hex("premaster:%s\n", premaster, sizeof(premaster)); 1991 dump_hex("premaster:%s", premaster, sizeof(premaster));
1886 len = psRsaEncryptPub(/*pool:*/ NULL, 1992 len = psRsaEncryptPub(/*pool:*/ NULL,
1887 /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key, 1993 /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key,
1888 premaster, /*inlen:*/ RSA_PREMASTER_SIZE, 1994 premaster, /*inlen:*/ RSA_PREMASTER_SIZE,
@@ -1891,7 +1997,7 @@ static void send_client_key_exchange(tls_state_t *tls)
1891 ); 1997 );
1892 /* keylen16 exists for RSA (in TLS, not in SSL), but not for some other key types */ 1998 /* keylen16 exists for RSA (in TLS, not in SSL), but not for some other key types */
1893 record->key[0] = len >> 8; 1999 record->key[0] = len >> 8;
1894 record->key[1] = len & 0xff; 2000 record->key[1] = len; // & 0xff implicit
1895 len += 2; 2001 len += 2;
1896 premaster_size = RSA_PREMASTER_SIZE; 2002 premaster_size = RSA_PREMASTER_SIZE;
1897 } else { 2003 } else {
@@ -1899,9 +2005,9 @@ static void send_client_key_exchange(tls_state_t *tls)
1899 if (!(tls->flags & GOT_EC_KEY)) 2005 if (!(tls->flags & GOT_EC_KEY))
1900 bb_simple_error_msg_and_die("server did not provide EC key"); 2006 bb_simple_error_msg_and_die("server did not provide EC key");
1901 2007
1902 if (tls->flags & GOT_EC_CURVE_X25519) { 2008 if (tls->flags & USE_EC_CURVE_X25519) {
1903 /* ECDHE, curve x25519 */ 2009 /* ECDHE, curve x25519 */
1904 dbg("computing x25519_premaster\n"); 2010 dbg("computing x25519_premaster");
1905 curve_x25519_compute_pubkey_and_premaster( 2011 curve_x25519_compute_pubkey_and_premaster(
1906 record->key + 1, premaster, 2012 record->key + 1, premaster,
1907 /*point:*/ tls->hsd->ecc_pub_key32 2013 /*point:*/ tls->hsd->ecc_pub_key32
@@ -1912,7 +2018,7 @@ static void send_client_key_exchange(tls_state_t *tls)
1912 //premaster_size = CURVE25519_KEYSIZE; 2018 //premaster_size = CURVE25519_KEYSIZE;
1913 } else { 2019 } else {
1914 /* ECDHE, curve P256 */ 2020 /* ECDHE, curve P256 */
1915 dbg("computing P256_premaster\n"); 2021 dbg("computing P256_premaster");
1916 curve_P256_compute_pubkey_and_premaster( 2022 curve_P256_compute_pubkey_and_premaster(
1917 record->key + 2, premaster, 2023 record->key + 2, premaster,
1918 /*point:*/ tls->hsd->ecc_pub_key32 2024 /*point:*/ tls->hsd->ecc_pub_key32
@@ -1928,104 +2034,28 @@ static void send_client_key_exchange(tls_state_t *tls)
1928 record->type = HANDSHAKE_CLIENT_KEY_EXCHANGE; 2034 record->type = HANDSHAKE_CLIENT_KEY_EXCHANGE;
1929 /* record->len24_hi = 0; - already is */ 2035 /* record->len24_hi = 0; - already is */
1930 record->len24_mid = len >> 8; 2036 record->len24_mid = len >> 8;
1931 record->len24_lo = len & 0xff; 2037 record->len24_lo = len;
1932 len += 4; 2038 len += 4;
1933 2039
1934 dbg(">> CLIENT_KEY_EXCHANGE\n"); 2040 dbg(">> CLIENT_KEY_EXCHANGE");
1935 xwrite_and_update_handshake_hash(tls, len); 2041 xwrite_and_update_handshake_hash(tls, len);
1936 2042
1937 // RFC 5246 2043 derive_master_secret_and_keys(tls, premaster, premaster_size);
1938 // For all key exchange methods, the same algorithm is used to convert 2044 // The key_block is partitioned as follows:
1939 // the pre_master_secret into the master_secret. The pre_master_secret 2045 tls->our_write_MAC_key = tls->key_block; // client_write_MAC_key[]
1940 // should be deleted from memory once the master_secret has been 2046 tls->peer_write_MAC_key = tls->key_block + tls->MAC_size; // server_write_MAC_key[]
1941 // computed. 2047 tls->our_write_key = tls->peer_write_MAC_key + tls->MAC_size; // client_write_key[]
1942 // master_secret = PRF(pre_master_secret, "master secret", 2048 tls->peer_write_key = tls->our_write_key + tls->key_size; // server_write_key[]
1943 // ClientHello.random + ServerHello.random) 2049 tls->our_write_IV = tls->peer_write_key + tls->key_size; // client_write_IV[]
1944 // [0..47]; 2050 tls->peer_write_IV = tls->our_write_IV + tls->IV_size; // server_write_IV[]
1945 // The master secret is always exactly 48 bytes in length. The length 2051 dump_hex("client write_MAC_key:%s", tls->our_write_MAC_key, tls->MAC_size);
1946 // of the premaster secret will vary depending on key exchange method. 2052 dump_hex("client write_key:%s", tls->our_write_key, tls->key_size);
1947 prf_hmac_sha256(/*tls,*/ 2053 dump_hex("client write_IV:%s", tls->our_write_IV, tls->IV_size);
1948 tls->hsd->master_secret, sizeof(tls->hsd->master_secret), 2054 dump_hex("server write_MAC_key:%s", tls->peer_write_MAC_key, tls->MAC_size);
1949 premaster, premaster_size, 2055 dump_hex("server write_key:%s", tls->peer_write_key, tls->key_size);
1950 "master secret", 2056 dump_hex("server write_IV:%s", tls->peer_write_IV, tls->IV_size);
1951 tls->hsd->client_and_server_rand32, sizeof(tls->hsd->client_and_server_rand32) 2057
1952 ); 2058 initialize_aes_keys(tls);
1953 dump_hex("master secret:%s\n", tls->hsd->master_secret, sizeof(tls->hsd->master_secret));
1954
1955 // RFC 5246
1956 // 6.3. Key Calculation
1957 //
1958 // The Record Protocol requires an algorithm to generate keys required
1959 // by the current connection state (see Appendix A.6) from the security
1960 // parameters provided by the handshake protocol.
1961 //
1962 // The master secret is expanded into a sequence of secure bytes, which
1963 // is then split to a client write MAC key, a server write MAC key, a
1964 // client write encryption key, and a server write encryption key. Each
1965 // of these is generated from the byte sequence in that order. Unused
1966 // values are empty. Some AEAD ciphers may additionally require a
1967 // client write IV and a server write IV (see Section 6.2.3.3).
1968 //
1969 // When keys and MAC keys are generated, the master secret is used as an
1970 // entropy source.
1971 //
1972 // To generate the key material, compute
1973 //
1974 // key_block = PRF(SecurityParameters.master_secret,
1975 // "key expansion",
1976 // SecurityParameters.server_random +
1977 // SecurityParameters.client_random);
1978 //
1979 // until enough output has been generated. Then, the key_block is
1980 // partitioned as follows:
1981 //
1982 // client_write_MAC_key[SecurityParameters.mac_key_length]
1983 // server_write_MAC_key[SecurityParameters.mac_key_length]
1984 // client_write_key[SecurityParameters.enc_key_length]
1985 // server_write_key[SecurityParameters.enc_key_length]
1986 // client_write_IV[SecurityParameters.fixed_iv_length]
1987 // server_write_IV[SecurityParameters.fixed_iv_length]
1988 {
1989 uint8_t tmp64[64];
1990
1991 /* make "server_rand32 + client_rand32" */
1992 memcpy(&tmp64[0] , &tls->hsd->client_and_server_rand32[32], 32);
1993 memcpy(&tmp64[32], &tls->hsd->client_and_server_rand32[0] , 32);
1994
1995 prf_hmac_sha256(/*tls,*/
1996 tls->client_write_MAC_key, 2 * (tls->MAC_size + tls->key_size + tls->IV_size),
1997 // also fills:
1998 // server_write_MAC_key[]
1999 // client_write_key[]
2000 // server_write_key[]
2001 // client_write_IV[]
2002 // server_write_IV[]
2003 tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
2004 "key expansion",
2005 tmp64, 64
2006 );
2007 tls->client_write_key = tls->client_write_MAC_key + (2 * tls->MAC_size);
2008 tls->server_write_key = tls->client_write_key + tls->key_size;
2009 tls->client_write_IV = tls->server_write_key + tls->key_size;
2010 tls->server_write_IV = tls->client_write_IV + tls->IV_size;
2011 dump_hex("client_write_MAC_key:%s\n",
2012 tls->client_write_MAC_key, tls->MAC_size
2013 );
2014 dump_hex("client_write_key:%s\n",
2015 tls->client_write_key, tls->key_size
2016 );
2017 dump_hex("client_write_IV:%s\n",
2018 tls->client_write_IV, tls->IV_size
2019 );
2020
2021 aes_setkey(&tls->aes_decrypt, tls->server_write_key, tls->key_size);
2022 aes_setkey(&tls->aes_encrypt, tls->client_write_key, tls->key_size);
2023 {
2024 uint8_t iv[AES_BLOCK_SIZE];
2025 memset(iv, 0, AES_BLOCK_SIZE);
2026 aes_encrypt_one_block(&tls->aes_encrypt, iv, tls->H);
2027 }
2028 }
2029} 2059}
2030 2060
2031static const uint8_t rec_CHANGE_CIPHER_SPEC[] ALIGN1 = { 2061static const uint8_t rec_CHANGE_CIPHER_SPEC[] ALIGN1 = {
@@ -2035,7 +2065,7 @@ static const uint8_t rec_CHANGE_CIPHER_SPEC[] ALIGN1 = {
2035 2065
2036static void send_change_cipher_spec(tls_state_t *tls) 2066static void send_change_cipher_spec(tls_state_t *tls)
2037{ 2067{
2038 dbg(">> CHANGE_CIPHER_SPEC\n"); 2068 dbg(">> CHANGE_CIPHER_SPEC");
2039 xwrite(tls->ofd, rec_CHANGE_CIPHER_SPEC, sizeof(rec_CHANGE_CIPHER_SPEC)); 2069 xwrite(tls->ofd, rec_CHANGE_CIPHER_SPEC, sizeof(rec_CHANGE_CIPHER_SPEC));
2040} 2070}
2041 2071
@@ -2076,36 +2106,81 @@ static void send_change_cipher_spec(tls_state_t *tls)
2076// suite. Any cipher suite which does not explicitly specify 2106// suite. Any cipher suite which does not explicitly specify
2077// verify_data_length has a verify_data_length equal to 12. This 2107// verify_data_length has a verify_data_length equal to 12. This
2078// includes all existing cipher suites. 2108// includes all existing cipher suites.
2079static void send_client_finished(tls_state_t *tls) 2109static void send_finished(tls_state_t *tls, const char *msg_to_encrypt)
2080{ 2110{
2081 struct finished { 2111 struct finished {
2082 uint8_t type; 2112 uint8_t type;
2083 uint8_t len24_hi, len24_mid, len24_lo; 2113 uint8_t len24_hi, len24_mid, len24_lo;
2084 uint8_t prf_result[12]; 2114 uint8_t prf_result[12];
2085 }; 2115 };
2086 struct finished *record = tls_get_outbuf(tls, sizeof(*record)); 2116 struct finished *record;
2087 uint8_t handshake_hash[TLS_MAX_MAC_SIZE]; 2117 uint8_t handshake_hash[TLS_MAX_MAC_SIZE];
2088 unsigned len; 2118 unsigned len;
2089 2119
2090 fill_handshake_record_hdr(record, HANDSHAKE_FINISHED, sizeof(*record)); 2120 record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_FINISHED, sizeof(*record));
2091 2121
2092 len = sha_end(&tls->hsd->handshake_hash_ctx, handshake_hash); 2122 len = sha_end(&tls->hsd->handshake_hash_ctx, handshake_hash);
2093 2123
2094 prf_hmac_sha256(/*tls,*/ 2124 prf_hmac_sha256(/*tls,*/
2095 record->prf_result, sizeof(record->prf_result), 2125 record->prf_result, sizeof(record->prf_result),
2096 tls->hsd->master_secret, sizeof(tls->hsd->master_secret), 2126 tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
2097 "client finished", 2127 msg_to_encrypt,
2098 handshake_hash, len 2128 handshake_hash, len
2099 ); 2129 );
2100 dump_hex("from secret: %s\n", tls->hsd->master_secret, sizeof(tls->hsd->master_secret)); 2130 dump_hex("from secret: %s", tls->hsd->master_secret, sizeof(tls->hsd->master_secret));
2101 dump_hex("from labelSeed: %s", "client finished", sizeof("client finished")-1); 2131 dump_hex("from labelSeed: %s", msg_to_encrypt, strlen(msg_to_encrypt));
2102 dump_hex("%s\n", handshake_hash, sizeof(handshake_hash)); 2132 dump_hex("handshake_hash: %s", handshake_hash, sizeof(handshake_hash));
2103 dump_hex("=> digest: %s\n", record->prf_result, sizeof(record->prf_result)); 2133 dump_hex("=> digest: %s", record->prf_result, sizeof(record->prf_result));
2104 2134
2105 dbg(">> FINISHED\n"); 2135 dbg(">> FINISHED");
2106 xwrite_encrypted(tls, sizeof(*record), RECORD_TYPE_HANDSHAKE); 2136 xwrite_encrypted(tls, sizeof(*record), RECORD_TYPE_HANDSHAKE);
2107} 2137}
2108 2138
2139/* Receive and process ChangeCipherSpec */
2140static void get_change_cipher_spec(tls_state_t *tls)
2141{
2142 int len;
2143
2144 /* Get CHANGE_CIPHER_SPEC */
2145 len = tls_xread_record(tls, "switch to encrypted traffic");
2146 if (len != 1 || memcmp(tls->inbuf, rec_CHANGE_CIPHER_SPEC, 6) != 0)
2147 bad_record_die(tls, "switch to encrypted traffic", len);
2148 dbg("<< CHANGE_CIPHER_SPEC");
2149
2150 /* Enable decryption for incoming packets */
2151 if (ALLOW_RSA_NULL_SHA256
2152 && tls->cipher_id == TLS_RSA_WITH_NULL_SHA256
2153 ) {
2154 tls->min_encrypted_len_on_read = tls->MAC_size;
2155 } else
2156 if (!(tls->flags & ENCRYPTION_AESGCM)) {
2157 unsigned mac_blocks = (unsigned)(TLS_MAC_SIZE(tls) + AES_BLOCK_SIZE-1) / AES_BLOCK_SIZE;
2158 /* all incoming packets now should be encrypted and have
2159 * at least IV + (MAC padded to blocksize):
2160 */
2161 tls->min_encrypted_len_on_read = AES_BLOCK_SIZE + (mac_blocks * AES_BLOCK_SIZE);
2162 } else {
2163 tls->min_encrypted_len_on_read = 8 + AES_BLOCK_SIZE;
2164 }
2165 dbg("min_encrypted_len_on_read: %u", tls->min_encrypted_len_on_read);
2166}
2167
2168/* Receive encrypted Finished message */
2169static void get_finished(tls_state_t *tls, const char *expected)
2170{
2171 int len;
2172
2173 len = tls_xread_record(tls, expected);
2174 if (len < 4 || tls->inbuf[RECHDR_LEN] != HANDSHAKE_FINISHED)
2175 bad_record_die(tls, expected, len);
2176 dbg("<< FINISHED");
2177
2178 /* TODO: Verify the Finished message contents */
2179 /* The Finished message contains verify_data which is:
2180 * PRF(master_secret, "client finished"/"server finished", SHA256(handshake_messages))
2181 */
2182}
2183
2109void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni) 2184void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2110{ 2185{
2111 // Client RFC 5246 Server 2186 // Client RFC 5246 Server
@@ -2156,8 +2231,8 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2156 // This message is used to convey the server's ephemeral ECDH public key 2231 // This message is used to convey the server's ephemeral ECDH public key
2157 // (and the corresponding elliptic curve domain parameters) to the 2232 // (and the corresponding elliptic curve domain parameters) to the
2158 // client. 2233 // client.
2159 dbg("<< SERVER_KEY_EXCHANGE len:%u\n", len); 2234 dbg("<< SERVER_KEY_EXCHANGE len:%u", len);
2160 dump_raw_in("<< %s\n", tls->inbuf, RECHDR_LEN + len); 2235 dump_raw_in("<< %s", tls->inbuf, RECHDR_LEN + len);
2161 if (tls->flags & NEED_EC_KEY) 2236 if (tls->flags & NEED_EC_KEY)
2162 process_server_key(tls, len); 2237 process_server_key(tls, len);
2163 2238
@@ -2167,7 +2242,7 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2167 2242
2168 got_cert_req = (tls->inbuf[RECHDR_LEN] == HANDSHAKE_CERTIFICATE_REQUEST); 2243 got_cert_req = (tls->inbuf[RECHDR_LEN] == HANDSHAKE_CERTIFICATE_REQUEST);
2169 if (got_cert_req) { 2244 if (got_cert_req) {
2170 dbg("<< CERTIFICATE_REQUEST\n"); 2245 dbg("<< CERTIFICATE_REQUEST");
2171 // RFC 5246: "If no suitable certificate is available, 2246 // RFC 5246: "If no suitable certificate is available,
2172 // the client MUST send a certificate message containing no 2247 // the client MUST send a certificate message containing no
2173 // certificates. That is, the certificate_list structure has a 2248 // certificates. That is, the certificate_list structure has a
@@ -2185,7 +2260,7 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2185 bad_record_die(tls, "'server hello done'", len); 2260 bad_record_die(tls, "'server hello done'", len);
2186 } 2261 }
2187 // 0e 000000 (len:0) 2262 // 0e 000000 (len:0)
2188 dbg("<< SERVER_HELLO_DONE\n"); 2263 dbg("<< SERVER_HELLO_DONE");
2189 2264
2190 if (got_cert_req) 2265 if (got_cert_req)
2191 send_empty_client_cert(tls); 2266 send_empty_client_cert(tls);
@@ -2193,39 +2268,16 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2193 send_client_key_exchange(tls); 2268 send_client_key_exchange(tls);
2194 2269
2195 send_change_cipher_spec(tls); 2270 send_change_cipher_spec(tls);
2271
2196 /* from now on we should send encrypted */ 2272 /* from now on we should send encrypted */
2197 /* tls->write_seq64_be = 0; - already is */
2198 tls->flags |= ENCRYPT_ON_WRITE;
2199 2273
2200 send_client_finished(tls); 2274 send_finished(tls, "client finished");
2201 2275
2202 /* Get CHANGE_CIPHER_SPEC */ 2276 get_change_cipher_spec(tls);
2203 len = tls_xread_record(tls, "switch to encrypted traffic");
2204 if (len != 1 || memcmp(tls->inbuf, rec_CHANGE_CIPHER_SPEC, 6) != 0)
2205 bad_record_die(tls, "switch to encrypted traffic", len);
2206 dbg("<< CHANGE_CIPHER_SPEC\n");
2207
2208 if (ALLOW_RSA_NULL_SHA256
2209 && tls->cipher_id == TLS_RSA_WITH_NULL_SHA256
2210 ) {
2211 tls->min_encrypted_len_on_read = tls->MAC_size;
2212 } else
2213 if (!(tls->flags & ENCRYPTION_AESGCM)) {
2214 unsigned mac_blocks = (unsigned)(TLS_MAC_SIZE(tls) + AES_BLOCK_SIZE-1) / AES_BLOCK_SIZE;
2215 /* all incoming packets now should be encrypted and have
2216 * at least IV + (MAC padded to blocksize):
2217 */
2218 tls->min_encrypted_len_on_read = AES_BLOCK_SIZE + (mac_blocks * AES_BLOCK_SIZE);
2219 } else {
2220 tls->min_encrypted_len_on_read = 8 + AES_BLOCK_SIZE;
2221 }
2222 dbg("min_encrypted_len_on_read: %u\n", tls->min_encrypted_len_on_read);
2223 2277
2224 /* Get (encrypted) FINISHED from the server */ 2278 /* Get (encrypted) FINISHED from the server */
2225 len = tls_xread_record(tls, "'server finished'"); 2279 get_finished(tls, "'server finished'");
2226 if (len < 4 || tls->inbuf[RECHDR_LEN] != HANDSHAKE_FINISHED) 2280
2227 bad_record_die(tls, "'server finished'", len);
2228 dbg("<< FINISHED\n");
2229 /* application data can be sent/received */ 2281 /* application data can be sent/received */
2230 2282
2231 /* free handshake data */ 2283 /* free handshake data */
@@ -2238,7 +2290,7 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
2238 2290
2239static void tls_xwrite(tls_state_t *tls, int len) 2291static void tls_xwrite(tls_state_t *tls, int len)
2240{ 2292{
2241 dbg(">> DATA\n"); 2293 dbg(">> DATA");
2242 xwrite_encrypted(tls, len, RECORD_TYPE_APPLICATION_DATA); 2294 xwrite_encrypted(tls, len, RECORD_TYPE_APPLICATION_DATA);
2243} 2295}
2244 2296
@@ -2313,7 +2365,7 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2313 if (pfds[0].revents) { 2365 if (pfds[0].revents) {
2314 void *buf; 2366 void *buf;
2315 2367
2316 dbg("STDIN HAS DATA\n"); 2368 dbg("STDIN HAS DATA");
2317 buf = tls_get_outbuf(tls, inbuf_size); 2369 buf = tls_get_outbuf(tls, inbuf_size);
2318 nread = safe_read(STDIN_FILENO, buf, inbuf_size); 2370 nread = safe_read(STDIN_FILENO, buf, inbuf_size);
2319 if (nread < 1) { 2371 if (nread < 1) {
@@ -2329,6 +2381,7 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2329 tls_free_outbuf(tls); /* mem usage optimization */ 2381 tls_free_outbuf(tls); /* mem usage optimization */
2330 if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF) 2382 if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF)
2331 break; 2383 break;
2384 //TODO: if (pfds[1].revents) network has data, do a last read from it before exiting.
2332 } else { 2385 } else {
2333 if (nread == inbuf_size) { 2386 if (nread == inbuf_size) {
2334 /* TLS has per record overhead, if input comes fast, 2387 /* TLS has per record overhead, if input comes fast,
@@ -2338,11 +2391,16 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2338 if (inbuf_size > TLS_MAX_OUTBUF) 2391 if (inbuf_size > TLS_MAX_OUTBUF)
2339 inbuf_size = TLS_MAX_OUTBUF; 2392 inbuf_size = TLS_MAX_OUTBUF;
2340 } 2393 }
2394//BUG: in this example: printf '\n' | ssl_client -e ssl_server -OPTS echo 'Hi'
2395//if ssl_client arrives here (if it sees stdin before it sees the server's "Hi" message),
2396//it can be killed by SIGPIPE because server has already exited.
2397//(If we disable SIGPIPE, it would die on write error).
2398//Which means it won't get and won't print to stdout the server's response!
2341 tls_xwrite(tls, nread); 2399 tls_xwrite(tls, nread);
2342 } 2400 }
2343 } 2401 }
2344 if (pfds[1].revents) { 2402 if (pfds[1].revents) {
2345 dbg("NETWORK HAS DATA\n"); 2403 dbg("NETWORK HAS DATA");
2346 read_record: 2404 read_record:
2347 nread = tls_xread_record(tls, "encrypted data"); 2405 nread = tls_xread_record(tls, "encrypted data");
2348 if (nread < 1) { 2406 if (nread < 1) {
@@ -2366,6 +2424,784 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2366 } 2424 }
2367 } 2425 }
2368} 2426}
2427
2428#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
2429
2430/* =============== SERVER-SIDE CODE =============== */
2431
2432static void get_client_hello(tls_state_t *tls)
2433{
2434 struct client_hello {
2435 uint8_t type;
2436 uint8_t len24_hi, len24_mid, len24_lo;
2437 uint8_t proto_maj, proto_min;
2438 uint8_t rand32[32];
2439 uint8_t session_id_len;
2440 /* followed by session_id, cipher suites, compression methods, extensions */
2441 };
2442 unsigned i, j;
2443 struct client_hello *hp;
2444 uint8_t *p;
2445 int cipher_list_len;
2446 int extensions_len;
2447 int len;
2448
2449 len = tls_xread_handshake_block(tls, sizeof(*hp));
2450 /* NB: the recv'd block is already hashed by tls_xread_handshake_block() */
2451 hp = (void*)(tls->inbuf + RECHDR_LEN);
2452 if (hp->type != HANDSHAKE_CLIENT_HELLO
2453 || len != get24be(&hp->len24_hi) + 4
2454 || hp->proto_maj != TLS_MAJ
2455 || !is_minor_version_valid(tls, hp->proto_min)
2456 ) {
2457 bad_record_die(tls, "'client hello'", len);
2458 }
2459 dbg("<< CLIENT_HELLO len:%d len24:%d", len, get24be(&len24_hi));
2460
2461 /* Save client random */
2462 memcpy(tls->hsd->client_and_server_rand32, hp->rand32, 32);
2463
2464 /* Skip session ID and handshake header */
2465 p = (uint8_t*)(hp + 1) + hp->session_id_len;
2466 len -= (sizeof(*hp) + hp->session_id_len);
2467
2468 /* Parse cipher suites to select one we support */
2469 if (len < 2) {
2470 bb_simple_error_msg_and_die("malformed ClientHello");
2471 }
2472 cipher_list_len = (p[0] << 8) | p[1];
2473 p += 2;
2474 len -= 2;
2475
2476 if (len < cipher_list_len) {
2477 bb_simple_error_msg_and_die("malformed ClientHello");
2478 }
2479
2480 /* Check whether we have TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
2481 for (j = 0; j < cipher_list_len; j += 2) {
2482 if (p[j] == TLS_EMPTY_RENEGOTIATION_INFO_SCSV >> 8
2483 && p[j + 1] == (uint8_t)TLS_EMPTY_RENEGOTIATION_INFO_SCSV
2484 ) {
2485 dbg("got TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
2486 tls->hsd->reneg_info_requested = 1;
2487 break;
2488 }
2489 }
2490
2491 /* Select cipher + cert pair from client's list, preferring our ciphers in order */
2492 for (i = 0; i < NUM_CIPHERS*2; i += 2) {
2493 const uint8_t *our_cipher = &supported_ciphers[i];
2494 int key_type;
2495
2496 /* Determine required key type for this cipher */
2497 key_type = is_cipher_ECDSA(our_cipher);
2498 if (key_type == KEY_ECDSA) {
2499 if (!tls->hsd->keys[KEY_ECDSA])
2500 /* No ECDSA cert configured, can't choose this */
2501 continue;
2502 //TODO: ECDSA not supported yet at all
2503 continue;
2504 } else {
2505 if (!tls->hsd->keys[KEY_RSA])
2506 /* No RSA cert configured, can't choose this */
2507 continue;
2508 /* We _can_ choose this! */
2509 }
2510
2511 /* Check if this cipher is in client's list */
2512 for (j = 0; j < cipher_list_len; j += 2) {
2513 if (p[j] == our_cipher[0] && p[j + 1] == our_cipher[1]) {
2514 /* Found a match! */
2515 set_cipher_parameters(tls, our_cipher);
2516 dbg("Selected cipher: %04x", tls->cipher_id);
2517 tls->hsd->key_type_chosen = key_type;
2518 goto cipher_selected;
2519 }
2520 }
2521 /* try our next cipherid */
2522 }
2523 bb_simple_error_msg_and_die("no common cipher suites");
2524
2525 cipher_selected:
2526 /* Skip past cipher list */
2527 p += cipher_list_len;
2528 len -= cipher_list_len;
2529
2530 /* Skip compression methods */
2531 len -= 1 + p[0];
2532 p += 1 + p[0];
2533
2534 /* Parse extensions if present */
2535 if (len < 2) {
2536 dbg("No extensions");
2537 return; /* no extensions */
2538 }
2539 extensions_len = (p[0] << 8) | p[1];
2540 p += 2;
2541 len -= 2;
2542 dbg("Extensions total length: %u, remaining len: %d", extensions_len, len);
2543
2544 if (len < extensions_len) {
2545 dbg("Malformed extensions length (len %d < extensions_len %u)", len, extensions_len);
2546 return; /* malformed extensions, ignore */
2547 }
2548
2549 /* Process extensions */
2550 while (extensions_len >= 4) {
2551 unsigned ext_type = (p[0] << 8) | p[1];
2552 int ext_len = (p[2] << 8) | p[3];
2553 dbg("Extension type: 0x%04x, len: %u", ext_type, ext_len);
2554
2555 p += 4;
2556 extensions_len -= 4 + ext_len;
2557 if (extensions_len < 0) {
2558 dbg("Extension length overflow");
2559 return; /* malformed */
2560 }
2561
2562 if (ext_type == 0x000a) { /* supported_groups */
2563 /* Parse named curve list */
2564 int curve_list_len = (p[0] << 8) | p[1];
2565 dbg("Found supported_groups extension");
2566 p += 2;
2567 ext_len -= 2;
2568 if (ext_len != curve_list_len || (ext_len & 1))
2569 return; /* malformed */
2570 while (1) {
2571 unsigned curve;
2572 ext_len -= 2; /* skip (presumably existing) curve id */
2573 if (ext_len < 0)
2574 break; /* oops, it didn't */
2575 curve = (p[0] << 8) | p[1];
2576 p += 2;
2577 if (curve == 0x001d) /* x25519 */
2578 tls->flags |= USE_EC_CURVE_X25519;
2579// if (curve == 0x0017) /* secp256r1 (P256) */
2580// /* We'll try P256 as fallback without checking client support */
2581 }
2582 dbg("Client supports:%s",
2583 (tls->flags & USE_EC_CURVE_X25519) ? " x25519" : " P256(assumed)");
2584 continue;
2585 }
2586
2587 if (ext_type == 0xff01) {
2588 dbg("got reneg_info extension ff01");
2589 tls->hsd->reneg_info_requested = 1;
2590 }
2591 p += ext_len;
2592 }
2593}
2594
2595static void send_server_hello(tls_state_t *tls)
2596{
2597 struct server_hello {
2598 uint8_t type;
2599 uint8_t len24_hi, len24_mid, len24_lo;
2600 uint8_t proto_maj, proto_min;
2601 uint8_t rand32[32];
2602 uint8_t session_id_len;
2603 uint8_t cipherid_hi, cipherid_lo;
2604 uint8_t comprtype;
2605 uint8_t extensions_len_hi, extensions_len_lo;
2606 /* extensions follow */
2607 uint8_t ext_reneg_info[5]; /* ff 01 00 01 00 */
2608 };
2609 struct server_hello *record;
2610 unsigned len = sizeof(*record);
2611
2612 if (!tls->hsd->reneg_info_requested)
2613 len -= 7;
2614
2615 record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_SERVER_HELLO, len);
2616
2617 record->proto_maj = TLS_MAJ;
2618 record->proto_min = TLS_MIN;
2619
2620 /* Generate server random */
2621 tls_get_random(record->rand32, sizeof(record->rand32));
2622 memcpy(tls->hsd->client_and_server_rand32 + 32, record->rand32, 32);
2623
2624 /* No session ID */
2625 //record->session_id_len = 0;
2626
2627 /* Selected cipher suite */
2628 record->cipherid_hi = tls->cipher_id >> 8;
2629 record->cipherid_lo = tls->cipher_id; // & 0xff implicit
2630
2631 /* No compression */
2632 //record->comprtype = 0;
2633
2634 if (tls->hsd->reneg_info_requested) {
2635 /* Extensions */
2636 //record->extensions_len_hi = 0;
2637 record->extensions_len_lo = 5; /* length of renegotiation_info extension */
2638
2639 /* Renegotiation info extension (RFC 5746) */
2640 record->ext_reneg_info[0] = 0xff;
2641 record->ext_reneg_info[1] = 0x01; /* extension type */
2642 //record->ext_reneg_info[2] = 0x00;
2643 record->ext_reneg_info[3] = 0x01; /* extension data length: 1 byte */
2644 //record->ext_reneg_info[4] = 0x00; /* renegotiation info length: 0 (no previous connection) */
2645 }
2646
2647 dbg(">> SERVER_HELLO");
2648 xwrite_and_update_handshake_hash(tls, len);
2649}
2650
2651static void send_server_certificate(tls_state_t *tls)
2652{
2653 void *record;
2654 int n = tls->hsd->key_type_chosen;
2655 int sz = tls->hsd->certsize[n];
2656
2657 record = tls_get_outbuf(tls, sz);
2658 memcpy(record, tls->hsd->certs[n], sz);
2659 dbg(">> CERTIFICATE");
2660 xwrite_and_update_handshake_hash(tls, sz);
2661}
2662
2663static void send_server_key_exchange(tls_state_t *tls)
2664{
2665 struct server_key_exchange {
2666 uint8_t type;
2667 uint8_t len24_hi, len24_mid, len24_lo;
2668 uint8_t curve_type; /* 3 = named curve */
2669 uint8_t curve_id_hi, curve_id_lo;
2670 uint8_t pubkey_len;
2671 uint8_t pubkey[1 + 2 * 32]; /* for P256: 04 + x(32) + y(32) */
2672 /* Followed by signature: hash_alg(1) + sign_alg(1) + sig_len(2) + signature */
2673 };
2674 struct server_key_exchange *record;
2675 uint8_t *p;
2676 int pubkey_len;
2677 int params_len;
2678 int total_len;
2679 uint8_t hash[32]; /* SHA256 hash */
2680 sha256_ctx_t sha256_ctx;
2681 int32 sig_len;
2682
2683 record = tls_get_zeroed_outbuf(tls, sizeof(*record) + 4 + 512); /* extra space for signature */
2684
2685 record->type = HANDSHAKE_SERVER_KEY_EXCHANGE;
2686 record->curve_type = 3; /* named curve */
2687
2688 /* Determine which curve to use based on client's supported_groups extension.
2689 * Prefer x25519 (faster) if client supports it, otherwise use P256.
2690 * If client didn't send supported_groups, default to P256 (most widely supported).
2691 */
2692 if (tls->flags & USE_EC_CURVE_X25519) { /* Use x25519 */
2693 //record->curve_id_hi = 0x00; /* already zero from tls_get_zeroed_outbuf() */
2694 record->curve_id_lo = 0x1d; /* x25519 */
2695
2696 /* Generate ephemeral keypair directly into output buffer */
2697 curve_x25519_generate_keypair(tls->hsd->ecc_priv_key32, record->pubkey);
2698
2699 pubkey_len = 32;
2700 dbg("Using x25519 for ECDHE");
2701 } else { /* Use P256 (default or if client advertised it) */
2702 //record->curve_id_hi = 0x00; /* already zero from tls_get_zeroed_outbuf() */
2703 record->curve_id_lo = 0x17; /* secp256r1 (P256) */
2704
2705 /* Generate ephemeral keypair directly into output buffer */
2706 record->pubkey[0] = 0x04; /* uncompressed point */
2707 curve_P256_generate_keypair(tls->hsd->ecc_priv_key32, record->pubkey + 1);
2708
2709 pubkey_len = 1 + 2 * 32;
2710 /* P256 is the default, no need to set USE_EC_CURVE_X25519 flag */
2711 dbg("Using P256 for ECDHE");
2712 }
2713
2714 record->pubkey_len = pubkey_len;
2715
2716 /* ServerECDHParams length: curve_type(1) + curve_id(2) + pubkey_len(1) + pubkey */
2717 params_len = 4 + pubkey_len;
2718
2719 /* Sign the parameters with our RSA private key:
2720 * Signature is over SHA256(client_random + server_random + ServerECDHParams)
2721 */
2722 sha256_begin(&sha256_ctx);
2723 sha256_hash(&sha256_ctx, tls->hsd->client_and_server_rand32, 2 * 32);
2724 sha256_hash(&sha256_ctx, &record->curve_type, params_len);
2725 sha256_end(&sha256_ctx, hash);
2726
2727 /* Pointer to where signature data goes (after ServerECDHParams) */
2728 p = record->pubkey + pubkey_len;
2729
2730 /* SignatureAndHashAlgorithm: hash=SHA256(4), signature=RSA(1) */
2731 *p++ = 4; /* SHA256 */
2732 *p++ = 1; /* RSA */
2733
2734 /* Sign the hash */
2735 sig_len = privRsaEncryptSignedElement(NULL, &tls->hsd->rsa_priv_key,
2736 hash, 32, p + 2, 512, NULL);
2737 if (sig_len < 0) {
2738 bb_error_msg_and_die("RSA signature failed");
2739 }
2740
2741 /* Signature length (2 bytes, big-endian) */
2742 p[0] = sig_len >> 8;
2743 p[1] = sig_len; // & 0xff implicit
2744
2745 /* Total handshake message length: params + hash_alg(1) + sign_alg(1) + sig_len(2) + signature */
2746 total_len = params_len + 2 + 2 + sig_len;
2747
2748 //record->len24_hi = 0; /* already zero from tls_get_zeroed_outbuf() */
2749 record->len24_mid = total_len >> 8;
2750 record->len24_lo = total_len; // & 0xff implicit
2751
2752 /* Total wire length */
2753 total_len += 4; /* type + len24 */
2754
2755 dbg(">> SERVER_KEY_EXCHANGE");
2756 xwrite_and_update_handshake_hash(tls, total_len);
2757}
2758
2759static void send_server_hello_done(tls_state_t *tls)
2760{
2761 struct server_hello_done {
2762 uint8_t type;
2763 uint8_t len24_hi, len24_mid, len24_lo;
2764 };
2765 struct server_hello_done *record;
2766
2767 record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_SERVER_HELLO_DONE, sizeof(*record));
2768
2769 dbg(">> SERVER_HELLO_DONE");
2770 xwrite_and_update_handshake_hash(tls, sizeof(*record));
2771}
2772
2773/* Receive and process ClientKeyExchange */
2774static void get_client_key_exchange(tls_state_t *tls)
2775{
2776 struct client_key_exchange {
2777 uint8_t type;
2778 uint8_t len24_hi, len24_mid, len24_lo;
2779 uint8_t key[1]; /* Variable length: encrypted premaster (RSA) or EC point (ECDHE) */
2780 };
2781 struct client_key_exchange *record;
2782 uint8_t premaster[RSA_PREMASTER_SIZE > EC_CURVE_KEYSIZE ? RSA_PREMASTER_SIZE : EC_CURVE_KEYSIZE];
2783 int len, premaster_size;
2784 uint8_t *key_data;
2785
2786 len = tls_xread_handshake_block(tls, sizeof(*record));
2787 record = (void*)(tls->inbuf + RECHDR_LEN);
2788
2789 if (record->type != HANDSHAKE_CLIENT_KEY_EXCHANGE) {
2790 bad_record_die(tls, "'client key exchange'", len);
2791 }
2792 dbg("<< CLIENT_KEY_EXCHANGE");
2793
2794 key_data = record->key;
2795
2796 if (!(tls->flags & NEED_EC_KEY)) {
2797 /* RSA key exchange */
2798 int enckey_len;
2799
2800 /* Get the length of the encrypted premaster secret */
2801 enckey_len = (key_data[0] << 8) | key_data[1];
2802 key_data += 2;
2803 dbg("enckey_len:%d len:%d", enckey_len, len);
2804
2805 if (enckey_len < 128 || enckey_len > 512) {
2806 bb_simple_error_msg_and_die("bad encrypted premaster length");
2807 }
2808
2809 /* Decrypt the premaster secret using server's private RSA key */
2810 {
2811 int32 ret;
2812 uint32 plen;
2813 psRsaKey_t *key = &tls->hsd->rsa_priv_key;
2814
2815 plen = RSA_PREMASTER_SIZE;
2816 ret = psRsaDecryptPriv(NULL, key,
2817 key_data, enckey_len,
2818 premaster, plen, NULL);
2819
2820 if (ret != RSA_PREMASTER_SIZE) {
2821 bb_error_msg_and_die("RSA decrypt failed or wrong premaster size: %d", ret);
2822 }
2823
2824 dbg("Decrypted premaster secret (%d bytes)", ret);
2825
2826 /* Verify premaster format: should start with version 0x03 0x03 (TLS 1.2) */
2827 if (premaster[0] != 0x03 || premaster[1] != 0x03) {
2828 bb_simple_error_msg_and_die("bad premaster secret version");
2829 }
2830 }
2831 premaster_size = RSA_PREMASTER_SIZE;
2832 } else {
2833 /* ECDHE key exchange */
2834 int pubkey_len;
2835 uint8_t *client_pubkey;
2836
2837 /* Get client's ephemeral public key length */
2838 pubkey_len = *key_data++;
2839 client_pubkey = key_data;
2840
2841 dbg("ECDHE: client pubkey_len:%d", pubkey_len);
2842
2843 /* Compute shared secret using client's public key and our private key */
2844 if (tls->flags & USE_EC_CURVE_X25519) {
2845 /* x25519 */
2846 if (pubkey_len != CURVE25519_KEYSIZE) {
2847 bb_simple_error_msg_and_die("bad x25519 public key length");
2848 }
2849 curve_x25519_compute_premaster(
2850 tls->hsd->ecc_priv_key32, client_pubkey,
2851 premaster
2852 );
2853 premaster_size = CURVE25519_KEYSIZE;
2854 } else {
2855 /* P256 */
2856 if (pubkey_len != 1 + 2 * P256_KEYSIZE) {
2857 bb_simple_error_msg_and_die("bad P256 public key length");
2858 }
2859 if (*client_pubkey++ != 0x04) {
2860 bb_simple_error_msg_and_die("compressed EC points not supported");
2861 }
2862 curve_P256_compute_premaster(
2863 tls->hsd->ecc_priv_key32, client_pubkey,
2864 premaster
2865 );
2866 premaster_size = P256_KEYSIZE;
2867 }
2868
2869 dbg("Computed ECDHE premaster secret (%d bytes)", premaster_size);
2870 }
2871
2872 derive_master_secret_and_keys(tls, premaster, premaster_size);
2873 // The key_block[] is partitioned as follows:
2874 tls->peer_write_MAC_key = tls->key_block; // client_write_MAC_key[]
2875 tls->our_write_MAC_key = tls->key_block + tls->MAC_size; // server_write_MAC_key[]
2876 tls->peer_write_key = tls->our_write_MAC_key + tls->MAC_size; // client_write_key[]
2877 tls->our_write_key = tls->peer_write_key + tls->key_size; // server_write_key[]
2878 tls->peer_write_IV = tls->our_write_key + tls->key_size; // client_write_IV[]
2879 tls->our_write_IV = tls->peer_write_IV + tls->IV_size; // server_write_IV[]
2880 dump_hex("server write_MAC_key:%s", tls->our_write_MAC_key, tls->MAC_size);
2881 dump_hex("server write_key:%s", tls->our_write_key, tls->key_size);
2882 dump_hex("server write_IV:%s", tls->our_write_IV, tls->IV_size);
2883 dump_hex("client write_MAC_key:%s", tls->peer_write_MAC_key, tls->MAC_size);
2884 dump_hex("client write_key:%s", tls->peer_write_key, tls->key_size);
2885 dump_hex("client write_IV:%s", tls->peer_write_IV, tls->IV_size);
2886
2887 initialize_aes_keys(tls);
2888}
2889
2890/* Load RSA private key from DER file (supports PKCS#8 or PKCS#1)
2891 * PKCS#8 PrivateKeyInfo ::= SEQUENCE {
2892 * version INTEGER,
2893 * algorithm AlgorithmIdentifier,
2894 * privateKey OCTET STRING -- contains PKCS#1 RSAPrivateKey
2895 * }
2896 * PKCS#1 RSAPrivateKey ::= SEQUENCE {
2897 * version INTEGER, -- 0
2898 * modulus INTEGER, -- N
2899 * publicExponent INTEGER, -- e
2900 * privateExponent INTEGER, -- d
2901 * prime1 INTEGER, -- p
2902 * prime2 INTEGER, -- q
2903 * exponent1 INTEGER, -- dP (d mod (p-1))
2904 * exponent2 INTEGER, -- dQ (d mod (q-1))
2905 * coefficient INTEGER -- qP ((inverse of q) mod p)
2906 * }
2907 */
2908static NOINLINE /* don't inline - large stack use */
2909void load_rsa_priv_key(psRsaKey_t *key, uint8_t *buf, ssize_t sz)
2910{
2911 uint8_t *der, *end;
2912
2913 der = buf;
2914 end = der + sz;
2915
2916 /* Enter the outer SEQUENCE */
2917 der = enter_der_item(der, &end);
2918
2919 /* Check if this is PKCS#8 or PKCS#1 format
2920 * PKCS#8 starts with version 0 (02 01 00) followed by AlgorithmIdentifier (30 0d...)
2921 * PKCS#1 starts with version 0 (02 01 00) followed by modulus (02 82...)
2922 * We can distinguish by checking if the second element is a SEQUENCE (0x30) or INTEGER (0x02)
2923 */
2924 der = skip_der_item(der, end); /* Skip version */
2925
2926 if (*der == 0x30) {
2927 /* PKCS#8 format - skip AlgorithmIdentifier and enter OCTET STRING */
2928 der = skip_der_item(der, end); /* Skip AlgorithmIdentifier */
2929 der = enter_der_item(der, &end); /* Enter OCTET STRING containing PKCS#1 key */
2930 der = enter_der_item(der, &end); /* Enter the PKCS#1 SEQUENCE */
2931 der = skip_der_item(der, end); /* Skip version again */
2932 }
2933 /* else: PKCS#1 format - we already skipped the version */
2934
2935 /* Read the key components */
2936 der_binary_to_pstm(&key->N, der, end); /* modulus */
2937 der = skip_der_item(der, end);
2938
2939 der_binary_to_pstm(&key->e, der, end); /* publicExponent */
2940 der = skip_der_item(der, end);
2941
2942 der_binary_to_pstm(&key->d, der, end); /* privateExponent */
2943 der = skip_der_item(der, end);
2944
2945 der_binary_to_pstm(&key->p, der, end); /* prime1 */
2946 der = skip_der_item(der, end);
2947
2948 der_binary_to_pstm(&key->q, der, end); /* prime2 */
2949 der = skip_der_item(der, end);
2950
2951 der_binary_to_pstm(&key->dP, der, end); /* exponent1 */
2952 der = skip_der_item(der, end);
2953
2954 der_binary_to_pstm(&key->dQ, der, end); /* exponent2 */
2955 der = skip_der_item(der, end);
2956
2957 der_binary_to_pstm(&key->qP, der, end); /* coefficient */
2958
2959 key->size = pstm_unsigned_bin_size(&key->N);
2960 key->optimized = 1; /* We have CRT parameters */
2961}
2962
2963static char *decode_base64_or_die(char *dst, const char *src)
2964{
2965 char *dst_end = decode_base64(dst, &src);
2966 if (*src != '\0')
2967 bb_error_msg_and_die("base64 decode error");
2968 return dst_end;
2969}
2970
2971/* Parse PEM file and extract key + cert chain pairs
2972 * Returns number of pairs loaded
2973 */
2974static void load_pem_key_cert_pairs(tls_state_t *tls, const char *pem_filename)
2975{
2976 static const char BLOCK_NAMES[] ALIGN1 =
2977 "EC PARAMETERS" "\0"
2978 "CERTIFICATE" "\0"
2979 "EC PRIVATE KEY" "\0"
2980 "PRIVATE KEY" "\0"
2981 "RSA PRIVATE KEY""\0"
2982 ;
2983 enum {
2984 str_EC_PARAMETERS = 0,
2985 str_CERTIFICATE = 1,
2986 str_EC_KEY = 2,
2987 };
2988 char *p;
2989 char *pem_data;
2990 size_t pem_size;
2991 char *der_data;
2992 unsigned der_size;
2993 int keyidx;
2994
2995 /* Read PEM file */
2996 pem_size = 64 * 1024; /* sanity limit */
2997 pem_data = xmalloc_xopen_read_close(pem_filename, &pem_size);
2998
2999 der_data = NULL;
3000 der_size = 0;
3001 keyidx = -1; /* "we did not see any KEY yet" */
3002
3003 p = pem_data;
3004 while (1) {
3005 unsigned n;
3006 char *block_end;
3007 char *block_type_end;
3008
3009 /* Find next PEM block */
3010 p = skip_whitespace(p);
3011 if (*p == '\0')
3012 break; /* end of file */
3013 p = is_prefixed_with(p, "-----BEGIN ");
3014 if (!p)
3015 goto err;
3016 block_type_end = strstr(p, "-----\n");
3017 if (!block_type_end)
3018 goto err;
3019 block_type_end += 5;
3020 block_end = strstr(block_type_end, "\n-----END ");
3021 if (!block_end)
3022 goto err;
3023 *block_end = '\0';
3024 block_end += 10;
3025//-----BEGIN PRIVATE KEY-----\n
3026// ^p ^block_type_end
3027//BASE64HERE-BASE64HERE
3028//-----END PRIVATE KEY-----
3029// ^block_end
3030 /* The headers must match */
3031 *block_type_end = '\0';
3032 if (!is_prefixed_with(block_end, p))
3033 goto err;
3034 /* Truncate trailing dashes from block type name */
3035 block_type_end[-5] = '\0';
3036
3037 block_end += (block_type_end - p);
3038 block_type_end++;
3039//BASE64HERE-BASE64HERE
3040//^block_type_end
3041//-----END PRIVATE KEY-----
3042// ^block_end
3043 n = index_in_strings(BLOCK_NAMES, p);
3044 if ((int)n < 0)
3045 bb_error_msg_and_die("'%s': unknown PEM block '%s'", pem_filename, p);
3046
3047 /* Note: may point to "\n" or even NUL */
3048 p = block_end;
3049
3050 /* What block do we see? */
3051
3052 if (n == str_EC_PARAMETERS) {
3053 /* "openssl ecparam -genkey" generates these, skip silently */
3054 continue; /* skip */
3055 }
3056
3057 if (n == str_CERTIFICATE) {
3058 struct certificate_msg {
3059 uint8_t type;
3060 uint8_t len24_hi, len24_mid, len24_lo;
3061 uint8_t cert_chain_len24_hi, cert_chain_len24_mid, cert_chain_len24_lo;
3062 uint8_t cert1_len24_hi, cert1_len24_mid, cert1_len24_lo;
3063 /* followed by certificate DER data */
3064 /* followed by cert2_len24, cert2 DER data, ... */
3065 };
3066 struct certificate_msg *cert_msg;
3067 unsigned start;
3068
3069 if (keyidx < 0)
3070 bb_error_msg_and_die("'%s': certificate must be after key", pem_filename);
3071
3072 /* We create or update a full HANDSHAKE_CERTIFICATE message */
3073 if (der_size == 0) {
3074 der_size = sizeof(*cert_msg);
3075 der_data = xzalloc(der_size);
3076 cert_msg = (void*)der_data;
3077 cert_msg->type = HANDSHAKE_CERTIFICATE;
3078 } else {
3079 /* We are here when we decode second or later cert */
3080 der_size += 3; /* for len24 */
3081 }
3082
3083 /* Decode BASE64 */
3084 start = der_size;
3085 der_size += block_end - block_type_end; /* worst case size */
3086 der_data = xrealloc(der_data, der_size);
3087 der_size = decode_base64_or_die(der_data + start, block_type_end) - der_data;
3088 der_data = xrealloc(der_data, der_size);
3089
3090 /* Fill last cert's len24 */
3091 n = der_size - start;
3092 der_data[start - 3] = n >> 16;
3093 der_data[start - 2] = n >> 8;
3094 der_data[start - 1] = n;
3095 /* Update sizes in header */
3096 cert_msg = (void*)der_data;
3097 n = der_size - 4;
3098 cert_msg->len24_hi = n >> 16;
3099 cert_msg->len24_mid = n >> 8;
3100 cert_msg->len24_lo = n;
3101 n -= 3;
3102 cert_msg->cert_chain_len24_hi = n >> 16;
3103 cert_msg->cert_chain_len24_mid = n >> 8;
3104 cert_msg->cert_chain_len24_lo = n;
3105
3106 tls->hsd->certs[keyidx] = der_data;
3107 tls->hsd->certsize[keyidx] = der_size;
3108 continue;
3109 }
3110
3111 /* We see a key */
3112
3113 /* Decode BASE64 */
3114 der_size = block_end - block_type_end; /* worst case size */
3115 der_data = xmalloc(der_size);
3116 der_size = decode_base64_or_die(der_data, block_type_end) - der_data;
3117 der_data = xrealloc(der_data, der_size);
3118
3119 keyidx = (n == str_EC_KEY) ? KEY_ECDSA : KEY_RSA;
3120 if (tls->hsd->keys[keyidx])
3121 bb_error_msg_and_die("'%s': more than one key", pem_filename);
3122 tls->hsd->keys[keyidx] = der_data;
3123 tls->hsd->keysize[keyidx] = der_size;
3124
3125 der_data = NULL;
3126 der_size = 0;
3127 } /* while (parsing PEM) */
3128 free(pem_data);
3129
3130 if (!tls->hsd->keys[KEY_RSA] && !tls->hsd->keys[KEY_ECDSA])
3131 bb_error_msg_and_die("'%s': no private keys", pem_filename);
3132
3133 if (tls->hsd->keys[KEY_RSA]) {
3134 if (!tls->hsd->certs[KEY_RSA])
3135 bb_error_msg_and_die("'%s': key with no cert", pem_filename);
3136 /* Parse RSA key from DER */
3137 load_rsa_priv_key(&tls->hsd->rsa_priv_key, (uint8_t*)tls->hsd->keys[KEY_RSA], tls->hsd->keysize[KEY_RSA]);
3138 }
3139 if (tls->hsd->keys[KEY_ECDSA]) {
3140 if (!tls->hsd->certs[KEY_ECDSA])
3141 bb_error_msg_and_die("'%s': key with no cert", pem_filename);
3142 bb_error_msg("'%s': ECDSA keys not supported", pem_filename);
3143 }
3144
3145 return;
3146 err:
3147 bb_error_msg_and_die("malformed PEM file at '%.*s'", (int)(skip_whitespace(p) - p), p);
3148}
3149
3150void FAST_FUNC tls_handshake_as_server(tls_state_t *tls,
3151 const char *pem_filename)
3152{
3153 /* Allocate handshake data */
3154 tls->hsd = xzalloc(sizeof(*tls->hsd));
3155
3156 /* Load server key(s) and certificate(s) from PEM file */
3157 load_pem_key_cert_pairs(tls, pem_filename);
3158
3159 sha256_begin(&tls->hsd->handshake_hash_ctx);
3160
3161 /* Server handshake sequence:
3162 * 1. Receive ClientHello
3163 * 2. Send ServerHello
3164 * 3. Send Certificate
3165 * 4. [Send ServerKeyExchange] - only for DHE/ECDHE
3166 * 5. [Send CertificateRequest] - optional, skip for now
3167 * 6. Send ServerHelloDone
3168 * 7. Receive ClientKeyExchange
3169 * 8. Receive ChangeCipherSpec
3170 * 9. Receive Finished (encrypted)
3171 * 10. Send ChangeCipherSpec
3172 * 11. Send Finished (encrypted)
3173 */
3174 tls->expecting_first_packet = 1;
3175 get_client_hello(tls);
3176 tls->expecting_first_packet = 0;
3177 send_server_hello(tls);
3178 send_server_certificate(tls);
3179 if (tls->flags & NEED_EC_KEY)
3180 send_server_key_exchange(tls);
3181 send_server_hello_done(tls);
3182
3183 get_client_key_exchange(tls);
3184 get_change_cipher_spec(tls);
3185 /* Get (encrypted) FINISHED from the client */
3186 get_finished(tls, "'cliend finished'");
3187
3188 send_change_cipher_spec(tls);
3189 send_finished(tls, "server finished");
3190
3191 /* application data can be sent/received */
3192
3193 /* free handshake data */
3194 psRsaKey_clear(&tls->hsd->rsa_priv_key);
3195 free(tls->hsd->keys[0]);
3196 free(tls->hsd->keys[1]);
3197 free(tls->hsd->certs[0]);
3198 free(tls->hsd->certs[1]);
3199// if (PARANOIA)
3200// memset(tls->hsd, 0, sizeof(*tls->hsd));
3201 free(tls->hsd);
3202 tls->hsd = NULL;
3203}
3204#endif
2369#else 3205#else
2370#include <stdbool.h> 3206#include <stdbool.h>
2371 3207
diff --git a/networking/tls.h b/networking/tls.h
index fde2e4d3d..0252b161f 100644
--- a/networking/tls.h
+++ b/networking/tls.h
@@ -49,6 +49,7 @@
49#define PS_PLATFORM_FAIL -7 /* Failure as a result of system call error */ 49#define PS_PLATFORM_FAIL -7 /* Failure as a result of system call error */
50#define PS_MEM_FAIL -8 /* Failure to allocate requested memory */ 50#define PS_MEM_FAIL -8 /* Failure to allocate requested memory */
51#define PS_LIMIT_FAIL -9 /* Failure on sanity/limit tests */ 51#define PS_LIMIT_FAIL -9 /* Failure on sanity/limit tests */
52#define PS_UNSUPPORTED_FAIL -10 /* Unsupported algorithm or operation */
52 53
53#define PS_TRUE 1 54#define PS_TRUE 1
54#define PS_FALSE 0 55#define PS_FALSE 0
@@ -90,6 +91,7 @@ void tls_get_random(void *buf, unsigned len) FAST_FUNC;
90 91
91#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS) 92#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS)
92 93
94#define psMalloc(pool, size) xmalloc(size)
93#define psFree(p, pool) free(p) 95#define psFree(p, pool) free(p)
94#define psTraceCrypto(msg) bb_simple_error_msg_and_die(msg) 96#define psTraceCrypto(msg) bb_simple_error_msg_and_die(msg)
95 97
@@ -109,6 +111,22 @@ void tls_get_random(void *buf, unsigned len) FAST_FUNC;
109#define P256_KEYSIZE 32 111#define P256_KEYSIZE 32
110#define CURVE25519_KEYSIZE 32 112#define CURVE25519_KEYSIZE 32
111 113
114/* Separate keypair generation and premaster computation functions */
115void curve_x25519_generate_keypair(
116 uint8_t *privkey32, uint8_t *pubkey32) FAST_FUNC;
117void curve_x25519_compute_premaster(
118 const uint8_t *privkey32, const uint8_t *peerkey32,
119 uint8_t *premaster32) FAST_FUNC;
120
121#if ENABLE_SSL_SERVER
122void curve_P256_generate_keypair(
123 uint8_t *privkey32, uint8_t *pubkey2x32) FAST_FUNC;
124void curve_P256_compute_premaster(
125 const uint8_t *privkey32, const uint8_t *peerkey2x32,
126 uint8_t *premaster32) FAST_FUNC;
127#endif
128
129/* Combined operations (for client-side use) */
112void curve_x25519_compute_pubkey_and_premaster( 130void curve_x25519_compute_pubkey_and_premaster(
113 uint8_t *pubkey32, uint8_t *premaster32, 131 uint8_t *pubkey32, uint8_t *premaster32,
114 const uint8_t *peerkey32) FAST_FUNC; 132 const uint8_t *peerkey32) FAST_FUNC;
diff --git a/networking/tls_fe.c b/networking/tls_fe.c
index e5580fbcf..479d0aaee 100644
--- a/networking/tls_fe.c
+++ b/networking/tls_fe.c
@@ -607,22 +607,47 @@ static void curve25519(byte *result, const byte *e, const byte *q)
607 fe_normalize(result); 607 fe_normalize(result);
608} 608}
609 609
610/* interface to bbox's TLS code: */ 610/* interface to bbox's TLS code:
611 *
612 * Wire format for elliptic curve points differs between curves:
613 * - P256: point is (x,y) where each coordinate is a 256-bit (32-byte) big-endian integer.
614 * Wire format: 64 bytes total (plus 0x04 prefix byte for "uncompressed point").
615 * - x25519: point is a single 256-bit (32-byte) little-endian integer.
616 * Wire format: 32 bytes.
617 *
618 * The interface functions below accept and generate EC points in their respective
619 * wire formats. Internal calculations may use different representations, but all
620 * conversions are handled internally within these functions.
621 * (Note: x25519 implementation in this file uses wire format internally as well)
622 */
623
624/* Generate x25519 keypair: random private key + corresponding public key */
625void FAST_FUNC curve_x25519_generate_keypair(uint8_t *privkey32, uint8_t *pubkey32)
626{
627 /* Generate random private key, see RFC 7748 */
628 tls_get_random(privkey32, CURVE25519_KEYSIZE);
629 privkey32[0] &= 0xf8;
630 privkey32[CURVE25519_KEYSIZE-1] = ((privkey32[CURVE25519_KEYSIZE-1] & 0x7f) | 0x40);
631
632 /* Compute public key from private key */
633 curve25519(pubkey32, privkey32, NULL /* "use base point of x25519" */);
634}
635
636/* Compute shared secret (premaster) from our private key and peer's public key */
637void FAST_FUNC curve_x25519_compute_premaster(
638 const uint8_t *privkey32, const uint8_t *peerkey32,
639 uint8_t *premaster32)
640{
641 curve25519(premaster32, privkey32, peerkey32);
642}
611 643
644/* Combined operation: generate keypair and compute premaster in one call */
612void FAST_FUNC curve_x25519_compute_pubkey_and_premaster( 645void FAST_FUNC curve_x25519_compute_pubkey_and_premaster(
613 uint8_t *pubkey, uint8_t *premaster, 646 uint8_t *pubkey, uint8_t *premaster,
614 const uint8_t *peerkey32) 647 const uint8_t *peerkey32)
615{ 648{
616 uint8_t privkey[CURVE25519_KEYSIZE]; //[32] 649 uint8_t privkey[CURVE25519_KEYSIZE]; //[32]
617 650
618 /* Generate random private key, see RFC 7748 */ 651 curve_x25519_generate_keypair(privkey, pubkey);
619 tls_get_random(privkey, sizeof(privkey)); 652 curve_x25519_compute_premaster(privkey, peerkey32, premaster);
620 privkey[0] &= 0xf8;
621 privkey[CURVE25519_KEYSIZE-1] = ((privkey[CURVE25519_KEYSIZE-1] & 0x7f) | 0x40);
622
623 /* Compute public key */
624 curve25519(pubkey, privkey, NULL /* "use base point of x25519" */);
625
626 /* Compute premaster using peer's public key */
627 curve25519(premaster, privkey, peerkey32);
628} 653}
diff --git a/networking/tls_rsa.c b/networking/tls_rsa.c
index 2dd5a02f4..4dcbd3a4d 100644
--- a/networking/tls_rsa.c
+++ b/networking/tls_rsa.c
@@ -211,3 +211,176 @@ int32 FAST_FUNC psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key,
211 } 211 }
212 return size; 212 return size;
213} 213}
214
215#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
216
217#define psRsaEncryptPriv(pool, key, in, inlen, out, outlen, data) \
218 psRsaEncryptPriv( key, in, inlen, out, outlen)
219static //bbox
220int32 psRsaEncryptPriv(psPool_t *pool, psRsaKey_t *key,
221 unsigned char *in, uint32 inlen,
222 unsigned char *out, uint32 outlen, void *data)
223{
224 int32 err;
225 uint32 size;
226
227 size = key->size;
228 if (outlen < size) {
229 psTraceCrypto("Error on bad outlen parameter to psRsaEncryptPriv\n");
230 return PS_ARG_FAIL;
231 }
232 if ((err = pkcs1Pad(in, inlen, out, size, PUBKEY_TYPE, data)) < PS_SUCCESS){
233 psTraceCrypto("Error padding psRsaEncryptPriv. Likely data too long\n");
234 return err;
235 }
236 if ((err = psRsaCrypt(pool, out, size, out, (uint32*)&outlen, key,
237 PRIVKEY_TYPE, data)) < PS_SUCCESS) {
238 psTraceCrypto("Error performing psRsaEncryptPriv\n");
239 return err;
240 }
241 if (outlen != size) {
242 psTraceCrypto("Encrypted size error in psRsaEncryptPriv\n");
243 return PS_FAILURE;
244 }
245 return size;
246}
247
248#define ASN_OVERHEAD_LEN_RSA_SHA2 19
249//#define ASN_OVERHEAD_LEN_RSA_SHA1 15
250
251/* ASN.1 DigestInfo wrappers for hash algorithms */
252static const unsigned char asn256dsWrap[] = {0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60,
253 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
254//#ifdef USE_SHA384
255//static const unsigned char asn384dsWrap[] = {0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60,
256// 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
257//#endif
258//static const unsigned char asn1dsWrap[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,
259// 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14};
260
261int32 FAST_FUNC privRsaEncryptSignedElement(psPool_t *pool, psRsaKey_t *key, //bbox: was psPubKey_t
262 unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen,
263 void *data)
264{
265 unsigned char *c;
266 uint32 inlenWithAsn;
267 int32 rc;
268
269 if (inlen == 32) { //SHA256_HASH_SIZE
270 inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA2;
271 c = psMalloc(pool, inlenWithAsn);
272 memcpy(c, asn256dsWrap, ASN_OVERHEAD_LEN_RSA_SHA2);
273 memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA2, in, inlen);
274// } else if (inlen == SHA1_HASH_SIZE) {
275// inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA1;
276// c = psMalloc(pool, inlenWithAsn);
277// memcpy(c, asn1dsWrap, ASN_OVERHEAD_LEN_RSA_SHA1);
278// memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA1, in, inlen);
279//#ifdef USE_SHA384
280// } else if (inlen == SHA384_HASH_SIZE) {
281// inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA2;
282// c = psMalloc(pool, inlenWithAsn);
283// memcpy(c, asn384dsWrap, ASN_OVERHEAD_LEN_RSA_SHA2);
284// memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA2, in, inlen);
285//#endif
286 } else {
287 return PS_UNSUPPORTED_FAIL;
288 }
289
290 rc = psRsaEncryptPriv(pool, key, c, inlenWithAsn, //bbox: was (psRsaKey_t*)key->key
291 out, outlen, data);
292
293 psFree(c, pool);
294 return rc;
295}
296
297/* Remove PKCS#1 padding (Type 2) from decrypted data
298 * Format: 00 || 02 || PS || 00 || M
299 * Returns length of unpadded message, or negative on error
300 */
301#define pkcs1Unpad(in, inlen, out, outlen) \
302 pkcs1Unpad(in, inlen, out, outlen)
303static //bbox
304int32 pkcs1Unpad(unsigned char *in, uint32 inlen, unsigned char *out,
305 uint32 outlen)
306{
307 unsigned char *c, *end;
308 uint32 msglen;
309
310 if (inlen < 11) { /* Minimum: 00 02 [8 bytes PS] 00 */
311 psTraceCrypto("pkcs1Unpad: input too short\n");
312 return PS_FAILURE;
313 }
314
315 c = in;
316 end = in + inlen;
317
318 /* Check padding type byte */
319 if (*c++ != 0x00) {
320 psTraceCrypto("pkcs1Unpad: bad first byte\n");
321 return PS_FAILURE;
322 }
323 if (*c++ != 0x02) {
324 psTraceCrypto("pkcs1Unpad: bad padding type\n");
325 return PS_FAILURE;
326 }
327
328 /* Skip padding string (non-zero bytes) until we find 0x00 */
329 while (c < end && *c != 0x00) {
330 c++;
331 }
332
333 if (c >= end) {
334 psTraceCrypto("pkcs1Unpad: no 0x00 separator found\n");
335 return PS_FAILURE;
336 }
337
338 /* Skip the 0x00 separator */
339 c++;
340
341 /* Calculate message length */
342 msglen = (uint32)(end - c);
343
344 if (msglen > outlen) {
345 psTraceCrypto("pkcs1Unpad: output buffer too small\n");
346 return PS_FAILURE;
347 }
348
349 /* Copy message to output */
350 memcpy(out, c, msglen);
351
352 return msglen;
353}
354
355/* RSA private key decryption (PKCS#1 v1.5)
356 * Decrypts with private key and removes PKCS#1 padding
357 */
358#define psRsaDecryptPriv(pool, key, in, inlen, out, outlen, data) \
359 psRsaDecryptPriv( key, in, inlen, out, outlen)
360int32 FAST_FUNC psRsaDecryptPriv(psPool_t *pool, psRsaKey_t *key,
361 unsigned char *in, uint32 inlen,
362 unsigned char *out, uint32 outlen, void *data)
363{
364 int32 err;
365 uint32 ptLen;
366
367 if (inlen != key->size) {
368 psTraceCrypto("Error on bad inlen parameter to psRsaDecryptPriv\n");
369 return PS_ARG_FAIL;
370 }
371 ptLen = inlen;
372 if ((err = psRsaCrypt(pool, in, inlen, in, &ptLen, key,
373 PRIVKEY_TYPE, data)) < PS_SUCCESS) {
374 psTraceCrypto("Error performing psRsaDecryptPriv\n");
375 return err;
376 }
377 if (ptLen != inlen) {
378 psTraceCrypto("Decrypted size error in psRsaDecryptPriv\n");
379 return PS_FAILURE;
380 }
381 err = pkcs1Unpad(in, inlen, out, outlen);
382 memset(in, 0x0, inlen);
383 return err;
384}
385
386#endif /* server */
diff --git a/networking/tls_rsa.h b/networking/tls_rsa.h
index 82bea2a67..a8db73b9c 100644
--- a/networking/tls_rsa.h
+++ b/networking/tls_rsa.h
@@ -30,3 +30,15 @@ static ALWAYS_INLINE void psRsaKey_clear(psRsaKey_t *key)
30int32 psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key, 30int32 psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key,
31 unsigned char *in, uint32 inlen, 31 unsigned char *in, uint32 inlen,
32 unsigned char *out, uint32 outlen, void *data) FAST_FUNC; 32 unsigned char *out, uint32 outlen, void *data) FAST_FUNC;
33
34#define psRsaDecryptPriv(pool, key, in, inlen, out, outlen, data) \
35 psRsaDecryptPriv( key, in, inlen, out, outlen)
36int32 psRsaDecryptPriv(psPool_t *pool, psRsaKey_t *key,
37 unsigned char *in, uint32 inlen,
38 unsigned char *out, uint32 outlen, void *data) FAST_FUNC;
39
40#define privRsaEncryptSignedElement(pool, key, in, inlen, out, outlen, data) \
41 privRsaEncryptSignedElement( key, in, inlen, out, outlen)
42int32 privRsaEncryptSignedElement(psPool_t *pool, psRsaKey_t *key,
43 unsigned char *in, uint32 inlen,
44 unsigned char *out, uint32 outlen, void *data) FAST_FUNC;
diff --git a/networking/tls_sp_c32.c b/networking/tls_sp_c32.c
index e493c436a..90ab61a56 100644
--- a/networking/tls_sp_c32.c
+++ b/networking/tls_sp_c32.c
@@ -1514,6 +1514,52 @@ static void sp_ecc_make_key_256(sp_digit privkey[8], uint8_t *pubkey)
1514 memset(point, 0, sizeof(point)); //paranoia 1514 memset(point, 0, sizeof(point)); //paranoia
1515} 1515}
1516 1516
1517/* interface to bbox's TLS code:
1518 *
1519 * Wire format for elliptic curve points differs between curves:
1520 * - P256: point is (x,y) where each coordinate is a 256-bit (32-byte) big-endian integer.
1521 * Wire format: 64 bytes total (plus 0x04 prefix byte for "uncompressed point").
1522 * - x25519: point is a single 256-bit (32-byte) little-endian integer.
1523 * Wire format: 32 bytes.
1524 *
1525 * The interface functions below accept and generate EC points in their respective
1526 * wire formats. Internal calculations may use different representations, but all
1527 * conversions are handled internally within these functions.
1528 */
1529
1530#if ENABLE_SSL_SERVER
1531/* Generate P256 keypair: random private key + corresponding public key */
1532void FAST_FUNC curve_P256_generate_keypair(uint8_t *privkey32, uint8_t *pubkey2x32)
1533{
1534 sp_digit privkey_sp[8];
1535
1536 /* Generate keypair using internal representation */
1537 sp_ecc_make_key_256(privkey_sp, pubkey2x32);
1538
1539 /* Convert private key to binary format for storage */
1540 sp_256_to_bin_8(privkey_sp, privkey32);
1541
1542 memset(privkey_sp, 0, sizeof(privkey_sp)); //paranoia
1543}
1544
1545/* Compute shared secret (premaster) from our private key and peer's public key */
1546void FAST_FUNC curve_P256_compute_premaster(
1547 const uint8_t *privkey32, const uint8_t *peerkey2x32,
1548 uint8_t *premaster32)
1549{
1550 sp_digit privkey_sp[8];
1551
1552 /* Convert binary private key to internal representation */
1553 sp_256_from_bin_8(privkey_sp, privkey32);
1554
1555 /* Compute shared secret */
1556 sp_ecc_secret_gen_256(privkey_sp, peerkey2x32, premaster32);
1557
1558 memset(privkey_sp, 0, sizeof(privkey_sp)); //paranoia
1559}
1560#endif
1561
1562/* Combined operation: generate keypair and compute premaster in one call */
1517void FAST_FUNC curve_P256_compute_pubkey_and_premaster( 1563void FAST_FUNC curve_P256_compute_pubkey_and_premaster(
1518 uint8_t *pubkey2x32, uint8_t *premaster32, 1564 uint8_t *pubkey2x32, uint8_t *premaster32,
1519 const uint8_t *peerkey2x32) 1565 const uint8_t *peerkey2x32)
diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c
index acf108367..83df4b396 100644
--- a/networking/udhcp/d6_socket.c
+++ b/networking/udhcp/d6_socket.c
@@ -116,7 +116,8 @@ int FAST_FUNC d6_listen_socket(int port, const char *inf)
116 bb_simple_perror_msg_and_die("SO_BROADCAST"); 116 bb_simple_perror_msg_and_die("SO_BROADCAST");
117 117
118 /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */ 118 /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */
119 colon = strrchr(inf, ':'); 119 colon = (char*)strrchr(inf, ':');
120 /* NB: inf can really be a *const* string if it's a default, but defaults have no ':' */
120 if (colon) 121 if (colon)
121 *colon = '\0'; 122 *colon = '\0';
122 123
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c
index 35e10688b..5d2283eaf 100644
--- a/networking/udhcp/socket.c
+++ b/networking/udhcp/socket.c
@@ -90,7 +90,8 @@ int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
90 bb_simple_perror_msg_and_die("SO_BROADCAST"); 90 bb_simple_perror_msg_and_die("SO_BROADCAST");
91 91
92 /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */ 92 /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */
93 colon = strrchr(inf, ':'); 93 colon = (char*)strrchr(inf, ':');
94 /* NB: inf can really be a *const* string if it's a default, but defaults have no ':' */
94 if (colon) 95 if (colon)
95 *colon = '\0'; 96 *colon = '\0';
96 97
diff --git a/printutils/lpr.c b/printutils/lpr.c
index 25b0f7235..464208c65 100644
--- a/printutils/lpr.c
+++ b/printutils/lpr.c
@@ -122,7 +122,7 @@ int lpqr_main(int argc UNUSED_PARAM, char **argv)
122 122
123 { 123 {
124 // queue name is to the left of '@' 124 // queue name is to the left of '@'
125 char *s = strchr(queue, '@'); 125 char *s = (char *)strchr(queue, '@');
126 if (s) { 126 if (s) {
127 // server name is to the right of '@' 127 // server name is to the right of '@'
128 *s = '\0'; 128 *s = '\0';
diff --git a/procps/nmeter.c b/procps/nmeter.c
index dca07eac6..fd8907aac 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -913,7 +913,8 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
913 // parameters as seen by e.g. ps. Making a copy... 913 // parameters as seen by e.g. ps. Making a copy...
914 cur = xstrdup(argv[0]); 914 cur = xstrdup(argv[0]);
915 while (1) { 915 while (1) {
916 char *param, *p; 916 char *param;
917 const char *p;
917 prev = cur; 918 prev = cur;
918 again: 919 again:
919 cur = strchr(cur, '%'); 920 cur = strchr(cur, '%');
@@ -929,7 +930,7 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
929 // format: %[foptstring] 930 // format: %[foptstring]
930 cur++; 931 cur++;
931 p = strchr(options, cur[0]); 932 p = strchr(options, cur[0]);
932 param = cur+1; 933 param = cur + 1;
933 while (cur[0] != ']') { 934 while (cur[0] != ']') {
934 if (!cur[0]) 935 if (!cur[0])
935 bb_show_usage(); 936 bb_show_usage();
diff --git a/procps/powertop.c b/procps/powertop.c
index 6fe892540..a24748efc 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -238,7 +238,7 @@ static void save_line(const char *string, int count)
238#if ENABLE_FEATURE_POWERTOP_PROCIRQ 238#if ENABLE_FEATURE_POWERTOP_PROCIRQ
239static int is_hpet_irq(const char *name) 239static int is_hpet_irq(const char *name)
240{ 240{
241 char *p; 241 const char *p;
242# if BLOATY_HPET_IRQ_NUM_DETECTION 242# if BLOATY_HPET_IRQ_NUM_DETECTION
243 long hpet_chan; 243 long hpet_chan;
244 244
@@ -423,7 +423,8 @@ static NOINLINE int process_timer_stats(void)
423// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup) 423// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
424// 331 total events, 249.059 events/sec 424// 331 total events, 249.059 events/sec
425 while (fgets(buf, sizeof(buf), fp)) { 425 while (fgets(buf, sizeof(buf), fp)) {
426 const char *count, *process, *func; 426 const char *process, *func;
427 char *count;
427 char *p; 428 char *p;
428 int idx; 429 int idx;
429 unsigned cnt; 430 unsigned cnt;
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 2f7fa6618..777c27660 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -348,7 +348,7 @@ int conf_write(const char *name)
348 dirname[0] = 0; 348 dirname[0] = 0;
349 if (name && name[0]) { 349 if (name && name[0]) {
350 struct stat st; 350 struct stat st;
351 char *slash; 351 const char *slash;
352 352
353 if (!stat(name, &st) && S_ISDIR(st.st_mode)) { 353 if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
354 strcpy(dirname, name); 354 strcpy(dirname, name);
diff --git a/shell/ash.c b/shell/ash.c
index 7647a42cf..bf73d6bc2 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6939,7 +6939,7 @@ tryexec_applet(int applet_no, int noexec, const char *cmd, char **argv, char **e
6939#endif 6939#endif
6940 6940
6941#if ENABLE_PLATFORM_MINGW32 6941#if ENABLE_PLATFORM_MINGW32
6942static struct builtincmd *find_builtin(const char *name); 6942static const struct builtincmd *find_builtin(const char *name);
6943static void 6943static void
6944tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp) 6944tryexec(const char *cmd, const char *path, int noexec, char **argv, char **envp)
6945{ 6945{
@@ -9663,7 +9663,7 @@ test_exec(/*const char *fullname,*/ struct stat *statb)
9663} 9663}
9664 9664
9665/* Circular dep: find_command->find_builtin->builtintab[]->hashcmd->find_command */ 9665/* Circular dep: find_command->find_builtin->builtintab[]->hashcmd->find_command */
9666static struct builtincmd *find_builtin(const char *name); 9666static const struct builtincmd *find_builtin(const char *name);
9667#if ENABLE_ASH_BASH_NOT_FOUND_HOOK 9667#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
9668static int evalfun(struct funcnode *func, int argc, char **argv, int flags); 9668static int evalfun(struct funcnode *func, int argc, char **argv, int flags);
9669#endif 9669#endif
@@ -9684,7 +9684,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
9684 struct stat statb; 9684 struct stat statb;
9685 int e; 9685 int e;
9686 int updatetbl; 9686 int updatetbl;
9687 struct builtincmd *bcmd; 9687 const struct builtincmd *bcmd;
9688 int len; 9688 int len;
9689 9689
9690#if !ENABLE_PLATFORM_MINGW32 9690#if !ENABLE_PLATFORM_MINGW32
@@ -10303,9 +10303,7 @@ static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
10303#endif 10303#endif
10304 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)), 10304 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
10305 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)), 10305 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
10306#if ENABLE_PLATFORM_MINGW32
10307 [NFROMSTR ] = SHELL_ALIGN(sizeof(struct nfile)), 10306 [NFROMSTR ] = SHELL_ALIGN(sizeof(struct nfile)),
10308#endif
10309 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)), 10307 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
10310 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)), 10308 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
10311 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)), 10309 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
@@ -12074,10 +12072,10 @@ static const struct builtincmd builtintab[] = {
12074/* 12072/*
12075 * Search the table of builtin commands. 12073 * Search the table of builtin commands.
12076 */ 12074 */
12077static struct builtincmd * 12075static const struct builtincmd *
12078find_builtin(const char *name) 12076find_builtin(const char *name)
12079{ 12077{
12080 struct builtincmd *bp; 12078 const struct builtincmd *bp;
12081 12079
12082 bp = bsearch( 12080 bp = bsearch(
12083 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), 12081 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
@@ -12093,11 +12091,7 @@ ash_command_name(int i)
12093 int n; 12091 int n;
12094 12092
12095 if (/*i >= 0 &&*/ i < ARRAY_SIZE(builtintab)) 12093 if (/*i >= 0 &&*/ i < ARRAY_SIZE(builtintab))
12096#if ENABLE_PLATFORM_MINGW32
12097 return builtintab[i].name; 12094 return builtintab[i].name;
12098#else
12099 return builtintab[i].name + 1;
12100#endif
12101 i -= ARRAY_SIZE(builtintab); 12095 i -= ARRAY_SIZE(builtintab);
12102 12096
12103 for (n = 0; n < CMDTABLESIZE; n++) { 12097 for (n = 0; n < CMDTABLESIZE; n++) {
@@ -14316,12 +14310,12 @@ decode_dollar_squote(void)
14316{ 14310{
14317 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; 14311 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
14318 int c, cnt; 14312 int c, cnt;
14319 char *p;
14320 char buf[4]; 14313 char buf[4];
14321 14314
14322 c = pgetc(); 14315 c = pgetc();
14323 p = strchr(C_escapes, c); 14316 if (strchr(C_escapes, c)) {
14324 if (p) { 14317 char *p;
14318
14325 buf[0] = c; 14319 buf[0] = c;
14326 p = buf; 14320 p = buf;
14327 cnt = 3; 14321 cnt = 3;
@@ -15815,11 +15809,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15815 "------------------\n"); 15809 "------------------\n");
15816 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { 15810 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
15817 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), 15811 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
15818#if ENABLE_PLATFORM_MINGW32
15819 builtintab[i].name); 15812 builtintab[i].name);
15820#else
15821 builtintab[i].name + 1);
15822#endif
15823 if (col > 60) { 15813 if (col > 60) {
15824 out1fmt("\n"); 15814 out1fmt("\n");
15825 col = 0; 15815 col = 0;
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index 6611d3954..5ab350c74 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -93,7 +93,7 @@
93 93
94//usage:#define fdisk_trivial_usage 94//usage:#define fdisk_trivial_usage
95//usage: "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] " 95//usage: "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] "
96//usage: "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK" 96//usage: "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] [-t PARTTYPE] DISK"
97//usage:#define fdisk_full_usage "\n\n" 97//usage:#define fdisk_full_usage "\n\n"
98//usage: "Change partition table\n" 98//usage: "Change partition table\n"
99//usage: "\n -u Start and End are in sectors (instead of cylinders)" 99//usage: "\n -u Start and End are in sectors (instead of cylinders)"
@@ -104,6 +104,7 @@
104//but in fact, util-linux 2.41.1 shows the size in KILOBYTES! 104//but in fact, util-linux 2.41.1 shows the size in KILOBYTES!
105//usage: ) 105//usage: )
106//usage: "\n -b 2048 (for certain MO disks) use 2048-byte sectors" 106//usage: "\n -b 2048 (for certain MO disks) use 2048-byte sectors"
107//usage: "\n -T PARTTYPE Force 'dos' partition if 'gpt' also present"
107//usage: "\n -C CYLINDERS Set number of cylinders/heads/sectors" 108//usage: "\n -C CYLINDERS Set number of cylinders/heads/sectors"
108//usage: "\n -H HEADS Typically 255" 109//usage: "\n -H HEADS Typically 255"
109//usage: "\n -S SECTORS Typically 63" 110//usage: "\n -S SECTORS Typically 63"
@@ -186,7 +187,8 @@ enum {
186 OPT_l = 1 << 3, 187 OPT_l = 1 << 3,
187 OPT_S = 1 << 4, 188 OPT_S = 1 << 4,
188 OPT_u = 1 << 5, 189 OPT_u = 1 << 5,
189 OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE, 190 OPT_t = 1 << 6,
191 OPT_s = (1 << 7) * ENABLE_FEATURE_FDISK_BLKSIZE,
190}; 192};
191#define USER_SET_SECTOR_SIZE (option_mask32 & OPT_b) 193#define USER_SET_SECTOR_SIZE (option_mask32 & OPT_b)
192#define NOWARN_OPT_ls (!ENABLE_FEATURE_FDISK_WRITABLE || (option_mask32 & (OPT_l|OPT_s))) 194#define NOWARN_OPT_ls (!ENABLE_FEATURE_FDISK_WRITABLE || (option_mask32 & (OPT_l|OPT_s)))
@@ -464,6 +466,7 @@ struct globals {
464 sector_t extended_offset; /* offset of link pointers */ 466 sector_t extended_offset; /* offset of link pointers */
465 sector_t total_number_of_sectors; 467 sector_t total_number_of_sectors;
466 468
469 const char *opt_t;
467#if ENABLE_FEATURE_GPT_LABEL 470#if ENABLE_FEATURE_GPT_LABEL
468 struct gpt_header *gpt_hdr; 471 struct gpt_header *gpt_hdr;
469 char *gpt_part_array; 472 char *gpt_part_array;
@@ -1209,7 +1212,7 @@ static void
1209warn_cylinders(void) 1212warn_cylinders(void)
1210{ 1213{
1211 if (LABEL_IS_DOS && g_cylinders > 1024 && !NOWARN_OPT_ls) 1214 if (LABEL_IS_DOS && g_cylinders > 1024 && !NOWARN_OPT_ls)
1212 printf("\n" 1215 printf(
1213"The number of cylinders for this disk is set to %u.\n" 1216"The number of cylinders for this disk is set to %u.\n"
1214"There is nothing wrong with that, but this is larger than 1024,\n" 1217"There is nothing wrong with that, but this is larger than 1024,\n"
1215"and could in certain setups cause problems with:\n" 1218"and could in certain setups cause problems with:\n"
@@ -3064,10 +3067,14 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
3064 3067
3065 close_dev_fd(); /* needed: fd 3 must not stay closed */ 3068 close_dev_fd(); /* needed: fd 3 must not stay closed */
3066 3069
3067 opt = getopt32(argv, "^" "b:+C:+H:+lS:+u"IF_FEATURE_FDISK_BLKSIZE("s")"\0" 3070 //G.opt_t = NULL;
3071 opt = getopt32(argv, "^" "b:+C:+H:+lS:+ut:"IF_FEATURE_FDISK_BLKSIZE("s")"\0"
3068 /* among -s and -l, the last one takes precedence */ 3072 /* among -s and -l, the last one takes precedence */
3069 IF_FEATURE_FDISK_BLKSIZE("s-l:l-s"), 3073 IF_FEATURE_FDISK_BLKSIZE("s-l:l-s"),
3070 &sector_size, &user_cylinders, &user_heads, &user_sectors); 3074 &sector_size, // -b
3075 &user_cylinders, &user_heads, &user_sectors, //-CHS
3076 &G.opt_t
3077 );
3071 argv += optind; 3078 argv += optind;
3072 3079
3073#if ENABLE_FEATURE_FDISK_BLKSIZE 3080#if ENABLE_FEATURE_FDISK_BLKSIZE
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c
index c085af79c..696b4937c 100644
--- a/util-linux/fdisk_gpt.c
+++ b/util-linux/fdisk_gpt.c
@@ -150,12 +150,13 @@ check_gpt_label(void)
150 struct pte pe; 150 struct pte pe;
151 uint32_t crc; 151 uint32_t crc;
152 152
153 current_label_type = LABEL_DOS;
154
153 /* LBA 0 contains the legacy MBR */ 155 /* LBA 0 contains the legacy MBR */
154 156
155 if (!valid_part_table_flag(MBRbuffer) 157 if (!valid_part_table_flag(MBRbuffer)
156 || first->sys_ind != LEGACY_GPT_TYPE 158 || first->sys_ind != LEGACY_GPT_TYPE
157 ) { 159 ) {
158 current_label_type = LABEL_DOS;
159 return 0; 160 return 0;
160 } 161 }
161 162
@@ -165,7 +166,6 @@ check_gpt_label(void)
165 G.gpt_hdr = (void *)pe.sectorbuffer; 166 G.gpt_hdr = (void *)pe.sectorbuffer;
166 167
167 if (G.gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) { 168 if (G.gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
168 current_label_type = LABEL_DOS;
169 return 0; 169 return 0;
170 } 170 }
171 171
@@ -188,7 +188,6 @@ check_gpt_label(void)
188 || SWAP_LE32(G.gpt_hdr->hdr_size) > sector_size 188 || SWAP_LE32(G.gpt_hdr->hdr_size) > sector_size
189 ) { 189 ) {
190 puts("\nwarning: can't parse GPT disklabel"); 190 puts("\nwarning: can't parse GPT disklabel");
191 current_label_type = LABEL_DOS;
192 return 0; 191 return 0;
193 } 192 }
194 193
@@ -204,10 +203,15 @@ check_gpt_label(void)
204 puts("\nwarning: GPT array CRC is invalid"); 203 puts("\nwarning: GPT array CRC is invalid");
205 } 204 }
206 205
207 puts("Found valid GPT with protective MBR; using GPT"); 206 fputs_stdout("Found valid GPT with protective MBR; ");
208 207
209 current_label_type = LABEL_GPT; 208 if (!G.opt_t || strcasecmp(G.opt_t, "gpt") == 0) {
210 return 1; 209 puts("using GPT (-t dos to override)");
210 current_label_type = LABEL_GPT;
211 return 1;
212 }
213 puts("NOT using it (-t specified)");
214 return 0;
211} 215}
212 216
213#endif /* GPT_LABEL */ 217#endif /* GPT_LABEL */