Telnetd is a Telnet server program
: Http://www.busybox.net/
Decompress the package and enter the busybox directory.
Make defconfig
Make
Make install
Then the _ install directory will be generated, which is the compiled executable file.
The source code is located in./networking/telnetd. C.
Program flowchart:
The most important thing in the program is that two buckets are located after the struct tsession struct.
/* This is how the buffers are used. The arrows indicate data flow. +-------+ wridx1++ +------+ rdidx1++ +----------+ | | <-------------- | buf1 | <-------------- | | | | size1-- +------+ size1++ | | | pty | | socket | | | rdidx2++ +------+ wridx2++ | | | | --------------> | buf2 | --------------> | | +-------+ size2++ +------+ size2-- +----------+ size1: "how many bytes are buffered for pty between rdidx1 and wridx1?" size2: "how many bytes are buffered for socket between rdidx2 and wridx2?" Each session has got two buffers. Buffers are circular. If sizeN == 0, buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases rdidxN == wridxN.*/
Socket receives remote data and writes count bytes to the idle zone in buf1 starting from rdidx1. Then size1 + = count; rdidx1 + = count;
Pty can be written. Read count bytes from buf1 and write them to Pty. Then, size1-= count; wridx1 + = count;
Pty can be read, write count bytes to the free zone starting from rdidx2 in buf2, and then size2 + = count; rdidx2 + = count;
The socket can send data to the Remote End, read count bytes from the position where wridx2 starts in buf2, send the data through socket, and then size2-= count; wridx2 + = count;
Master_fd = create_and_bind_stream_or_die (opt_bindaddr, portnbr); xlisten (master_fd, 1); fd_zero (& rdfdset); fd_zero (& wrfdset); TS = G. sessions; while (TS) {struct tsession * Next = ts-> next;/* in case we free TS */If (ts-> shell_pid =-1) {/* child died and we detected that */free_session (TS);} else {If (ts-> size1> 0) /* can write to Pty */fd_set (ts-> ptyfd, & wrfdset); If (ts-> size1 <bufsize )/* Can read from socket */fd_set (ts-> sockfd_read, & rdfdset); If (ts-> size2> 0) /* can write to socket */fd_set (ts-> sockfd_write, & wrfdset); If (ts-> size2 <bufsize) /* can read from Pty */fd_set (ts-> ptyfd, & rdfdset);} Ts = next;} fd_set (master_fd, & rdfdset); If (master_fd> G. maxfd) g. maxfd = master_fd; Count = select (G. maxfd + 1, & rdfdset, & wrfdset, null, TV _ptr); If (! Is_inetd & fd_isset (master_fd, & rdfdset) {int FD; struct tsession * new_ts; FD = accept (master_fd, null, null); new_ts = make_new_session (FD ); new_ts-> next = G. sessions; G. sessions = new_ts;}/* Then check for data tunneling */TS = G. sessions; while (TS) {/* for all sessions... */struct tsession * Next = ts-> next;/* in case we free TS */If (/* ts-> size1 & */fd_isset (ts-> ptyfd, & wrfdset) {int n Um_totty; unsigned char * PTR;/* write to Pty from buffer 1 */PTR = remove_iacs (TS, & num_totty); Count = safe_write (ts-> ptyfd, PTR, num_totty); If (count <0) {If (errno = eagain) goto skip1; goto kill_session;} ts-> size1-= count; TS-> wridx1 + = count; if (ts-> wridx1> = bufsize)/* actually = bufsize */ts-> wridx1 = 0;} skip1: if (/* ts-> size2 & */fd_isset (ts-> sockfd_write, & wrfdset) {/* WR ITE to socket from Buffer 2 */COUNT = min (bufsize-ts-> wridx2, ts-> size2); Count = iac_safe_write (ts-> sockfd_write, (void *) (ts_buf2 (TS) + ts-> wridx2), count); If (count <0) {If (errno = eagain) goto skip2; goto kill_session ;} ts-> size2-= count; TS-> wridx2 + = count; If (ts-> wridx2> = bufsize) /* actually = bufsize */ts-> wridx2 = 0;} skip2:/* shocould not be needed,... remove_iacs is Act Ually buggy * (it cannot process IACS which wrap around buffer's end )! * Since properly fixing it requires writing bigger code, * we rely instead on this Code making it internally impossible * To have wrapped IAC (people don't type at 2 k/second ). * It also allows for bigger reads in common case. */If (ts-> size1 = 0) {ts-> rdidx1 = 0; TS-> wridx1 = 0;} If (ts-> size2 = 0) {ts-> rdidx2 = 0; TS-> wridx2 = 0;} If (/* ts-> size1 <bufsize & */fd_isset (ts-> sockfd_read, & Rd Fdset) {/* read from socket to buffer 1 */COUNT = min (bufsize-ts-> rdidx1, bufsize-ts-> size1 ); count = safe_read (ts-> sockfd_read, ts_buf1 (TS) + ts-> rdidx1, count); If (count <= 0) {If (count <0 & errno = eagain) goto skip3; goto kill_session;}/* ignore trailing NUL if it is there */If (! Ts_buf1 (TS) [ts-> rdidx1 + count-1]) {-- count;} ts-> size1 + = count; TS-> rdidx1 + = count; if (ts-> rdidx1> = bufsize)/* actually = bufsize */ts-> rdidx1 = 0;} skip3: if (/* ts-> size2 <bufsize & */fd_isset (ts-> ptyfd, & rdfdset )) {/* read from Pty to buffer 2 */COUNT = min (bufsize-ts-> rdidx2, bufsize-ts-> size2); Count = safe_read (ts-> ptyfd, ts_buf2 (TS) + ts-> rdidx2, count); If (count <= 0 ){ If (count <0 & errno = eagain) goto skip4; goto kill_session;} ts-> size2 + = count; TS-> rdidx2 + = count; if (ts-> rdidx2> = bufsize)/* actually = bufsize */ts-> rdidx2 = 0;} skip4: TS = next; continue; kill_session: if (ts-> shell_pid> 0) update_utmp (ts-> shell_pid, dead_process,/* tty_name: */null,/* Username: */null,/* hostname: */null); free_session (TS); TS = next ;}--------------------------- ------ | ------------------------------------- ^ | Ptr0 endremove_iacs function operates the buffer zone with the length of size1 starting from wridx1 in buf1, extract the actually valid command line statement, move the string to the header, and update wridx1 + = PTR-totty; size1-= PTR-totty; num_totty is the number of bytes of the actually valid command line statement, and the first address is returned. Static unsigned char * remove_iacs (struct tsession * ts, int * pnum_totty) {unsigned char * ptr0 = ts_buf1 (TS) + ts-> wridx1; unsigned char * PTR = ptr0; unsigned char * totty = PTR; unsigned char * end = PTR + min (bufsize-ts-> wridx1, ts-> size1); int num_totty; while (PTR <End) {/* string processing */} num_totty = totty-ptr0; * pnum_totty = num_totty;/* the difference between PTR and Totty is number of IACS we removed from the stream. adjust buf1 accordingly */If (PTR-totty) = 0)/* 99.999% of cases */return ptr0; TS-> wridx1 + = PTR-totty; ts-> size1-= PTR-totty;/* Move chars meant for the terminal towards the end of the buffer */return memmove (PTR-num_totty, ptr0, num_totty );} free_session function from G. the sessions linked list header deletes the structure pointed to by ts, closes the Pty and socket file handles, releases the memory, and updates G. maxfdstatic voidfree_session (struct tsession * TS)
The make_new_session function is critical. It calls xgetpty to open a Pseudo Terminal and call vfork to create a sub-process. The parent process stores the opened Pseudo Terminal and related handles.
Then, the sub-process calls setsid, closes the standard input, opens the Pseudo Terminal, redirects 0 to the standard output and standard error, and then executes/bin/login and login.
Start the shell program after the verification process.
In the future, as long as the parent process writes data to the obtained Pseudo Terminal handle, the input is written to the shell started by the child process. After the shell is executed, the parent process reads the pseudo
Terminal handle to read the standard output of shell.
Static struct tsession * make_new_session {struct tsession * Ts = xzarloc FD = xgetpty (tty_name); TS-> ptyfd = FD; pid = vfork (); If (pid> 0) {// parent process return ts;} // sub-process setsid (); // set Sid close (0); // close the standard input xopen (tty_name, o_rdwr ); // open the Pseudo Terminal. Note that the handle with the smallest number is returned by default, that is, 0 dup2 (); // redirect the Pseudo Terminal handle to the standard output dup2 ); // redirect the Pseudo Terminal handle to the standard error execvp ("/bin/login",); // execute/bin/login _ exit (exit_failure); // exit directly afterwards}