Experimental Telnet server for Raspberry Pi Pico W
pico_telnetd provides a light-weight Telnet (or "Raw TCP") server for Pico W. This is meant to be used with the Pico-SDK.
- Provides stdio driver so a Telnet (or raw TCP) connection can be used as "console" for Pico W.
- "Interface" library that is easy to include in existing Pico-SDK projects.
- Support authentication via authentication callback. Includes support for (Linux) SHA-512 Crypt and SHA-256 Crypt password hashes.
First, get the library (this example adds it as a submodule to existing repository):
$ mkdir libs
$ cd libs
$ git submodule add https://github.com/tjko/pico-telnetd.git
Then to use pico_telnetd, add it in your CMakeLists.txt:
# include pico-telnetd library
add_subdirectory(libs/pico-telnetd)
To add "telnet" support to a Pico W project, that allows accessing the unit over a Telnet connection just like from Serial or USB (CDC) connection
#include <pico_telnetd.h>
...
<initialize networking>
...
tcp_server_t *telnetserver = telnet_server_init(2048, 8192); // input and output buffer sizes
if (!telnetserver)
panic("out of memory);
telnetserver->mode = TELNET_MODE;
telnet_server_start(telnetserver, true); // parameter tells whether to enable stdio driver or not... */
To enable authentication, we must specify a callback function that handles the authentication. This example uses the included example callback that uses list of login/pwhash pairs to authenticate against.
#include <pico_telnetd.h>
#include <pico_telnetd/util.h>
/* list of users and their (Linux) SHA-512 Crypt password hashes... (this example used "admin/admin") */
user_pwhash_entry_t users[] = {
{ "admin", "$6$caRtcnraEpbI48d3$YizNnV2hIwqZ/Gu4jh9ebV/DXCRhCzvUM2E0yTF3BgGrMw1HrfYIJJ9CQ0rcVBbpScCfwBtKhynVpKSnW/5o.." },
{ NULL, NULL }
};
cp_server_t *telnetserver = telnet_server_init(2048, 8192);
if (!telnetserver)
panic("out of memory);
telnetserver->mode = TELNET_MODE;
telnetserver->port = 8000;
telnetserver->auth_cb = sha512crypt_auth_cb;
telnetserver->auth_cb_param = (void*)users;
telnet_server_start(telnetserver, true); // parameter tells whether to enable stdio driver or not... */
pico_telnetd support simple password based authentication. To enable authentication auth_cb needs to be set (and optionally auth_cb_param to specify parameters to pass to the authentication function.
int my_auth_cb(void *param, const char *login, const char *password)
{
if <user password is valid> {
return 0;
}
return -1;
}
...
tcpserver->auth_cb = my_auth_cb;
tcp_server_start(telnetserver, true);
...
By default pico-telnetd logs errors to stdout. Loggin verbosity can be configured by setting logging level:
#include <pico_telnetd/log.h>
telnetd_log_level(LOG_INFO);
(see pico_telnetd/log.h for the logging levels)
To completely control logging, it is possible to provide custom callback for the logging:
void my_logger(int priority, const char *format, ...)
{
<implement custom logging here>
}
...
telnetserver->log_cb = my_logger;
telnet_server_start(telnetserver, true);
...
To disable logging done by the library completely. Simply set the log_cb to NULL.
...
telnetserver->log_cb = NULL;
telnet_server_start(telnetserver, true);
...
Telnet server can alternatively be used withouth stdio, by setting stdio parameter to false:
telnet_server_start(telnetserver, false);
Received data is store in the rb_in ringbuffer.
Ringbuffer can be read charcter by character using telnet_ringbuffer_read_char() function:
...
int in;
while ((in = telnet_ringbuffer_read_char(&telnetserver->rb_in)) >= 0) {
printf("Received byte: %02x (%c)\n", in, isprint(in) ? in : '?');
}
...
Alternatively larger blocks can be read from ring buffer using telnet_ringbuffer_read() function:
size_t bytes_waiting = telnet_ringbuffer_size(&telnetserver->rb_in);
if (bytes_waiting > 0) {
telnet_ringbuffer_read(&telnetserver->rb_in, buffer, bytes_waiting); // make sure buffer is large enough...
}
Data added to ringbuffer rb_out, will be transmitted to the client.
Data can be added to ringbuffer either one character at the time using telnet_ringbuffer_add_char() function:
for (int i = 0; i < strlen(buf); i++) {
telnet_ringbuffer_add_char(&telnetserver->rb_out, buf[i], true); // Last argument controls wheter to overwrite in case ringbuffer fills uup...
}
Or larger blocks can be sent uainf telnet_ringbuffer_add() function:
// add data to ringbuffer withouth overwriting data if buffer woud fill up (overwrite parameter set to false)
int err = telnet_ringbuffer_add(&telnetserver->rb_out, buf, buffer_len, false);
if (err != 0) {
// buffer would fill up
}