mirror of
https://github.com/tsl0922/ttyd.git
synced 2025-12-22 20:04:19 +01:00
Execute command after authentication
This commit is contained in:
@@ -82,9 +82,11 @@ OPTIONS:
|
|||||||
--ssl-cert, -C SSL certificate file path
|
--ssl-cert, -C SSL certificate file path
|
||||||
--ssl-key, -K SSL key file path
|
--ssl-key, -K SSL key file path
|
||||||
--ssl-ca, -A SSL CA file path for client certificate verification
|
--ssl-ca, -A SSL CA file path for client certificate verification
|
||||||
--debug, -d Set log level (0-9, default: 7)
|
--debug, -d Set log level (default: 7)
|
||||||
--version, -v Print the version and exit
|
--version, -v Print the version and exit
|
||||||
--help, -h Print this text and exit
|
--help, -h Print this text and exit
|
||||||
|
|
||||||
|
Visit https://github.com/tsl0922/ttyd to get more information and report bugs.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
var terminalContainer = document.getElementById('terminal-container'),
|
var terminalContainer = document.getElementById('terminal-container'),
|
||||||
httpsEnabled = window.location.protocol == "https:",
|
httpsEnabled = window.location.protocol == "https:",
|
||||||
url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws',
|
url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws',
|
||||||
|
authToken = (typeof tty_auth_token !== 'undefined') ? tty_auth_token : null,
|
||||||
protocols = ["tty"],
|
protocols = ["tty"],
|
||||||
autoReconnect = -1,
|
autoReconnect = -1,
|
||||||
term, pingTimer, wsError;
|
term, pingTimer, wsError;
|
||||||
@@ -10,10 +11,9 @@
|
|||||||
var ws = new WebSocket(url, protocols);
|
var ws = new WebSocket(url, protocols);
|
||||||
|
|
||||||
ws.onopen = function(event) {
|
ws.onopen = function(event) {
|
||||||
|
console.log("Websocket connection opened");
|
||||||
wsError = false;
|
wsError = false;
|
||||||
if (typeof tty_auth_token !== 'undefined') {
|
ws.send(JSON.stringify({AuthToken: authToken}));
|
||||||
ws.send(JSON.stringify({AuthToken: tty_auth_token}));
|
|
||||||
}
|
|
||||||
pingTimer = setInterval(sendPing, 30 * 1000, ws);
|
pingTimer = setInterval(sendPing, 30 * 1000, ws);
|
||||||
|
|
||||||
if (typeof term !== 'undefined') {
|
if (typeof term !== 'undefined') {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
term = new Terminal();
|
term = new Terminal();
|
||||||
|
|
||||||
term.on('resize', function (size) {
|
term.on('resize', function(size) {
|
||||||
if (ws.readyState === WebSocket.OPEN) {
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
ws.send("2" + JSON.stringify({columns: size.cols, rows: size.rows}));
|
ws.send("2" + JSON.stringify({columns: size.cols, rows: size.rows}));
|
||||||
}
|
}
|
||||||
@@ -30,22 +30,26 @@
|
|||||||
term.showOverlay(size.cols + 'x' + size.rows);
|
term.showOverlay(size.cols + 'x' + size.rows);
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
term.on("data", function(data) {
|
term.on("data", function(data) {
|
||||||
if (ws.readyState === WebSocket.OPEN) {
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
ws.send("0" + data);
|
ws.send("0" + data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
term.on('open', function() {
|
||||||
window.onresize = function(event) {
|
window.onresize = function(event) {
|
||||||
term.fit();
|
term.fit();
|
||||||
};
|
};
|
||||||
|
term.fit();
|
||||||
|
term.focus();
|
||||||
|
});
|
||||||
|
|
||||||
while (terminalContainer.firstChild) {
|
while (terminalContainer.firstChild) {
|
||||||
terminalContainer.removeChild(terminalContainer.firstChild);
|
terminalContainer.removeChild(terminalContainer.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
term.open(terminalContainer);
|
term.open(terminalContainer);
|
||||||
term.fit();
|
|
||||||
term.focus();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
@@ -68,12 +72,13 @@
|
|||||||
break;
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
autoReconnect = JSON.parse(data);
|
autoReconnect = JSON.parse(data);
|
||||||
console.log("Enabling reconnect: " + autoReconnect + " seconds")
|
console.log("Enabling reconnect: " + autoReconnect + " seconds");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = function(event) {
|
ws.onclose = function(event) {
|
||||||
|
console.log("Websocket connection closed with code: " + event.code);
|
||||||
if (term) {
|
if (term) {
|
||||||
term.off('data');
|
term.off('data');
|
||||||
term.off('resize');
|
term.off('resize');
|
||||||
@@ -86,11 +91,6 @@
|
|||||||
setTimeout(openWs, autoReconnect * 1000);
|
setTimeout(openWs, autoReconnect * 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = function(event) {
|
|
||||||
wsError = true;
|
|
||||||
term.showOverlay("Websocket handshake failed", null);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var sendPing = function(ws) {
|
var sendPing = function(ws) {
|
||||||
|
|||||||
20
src/http.c
20
src/http.c
@@ -50,21 +50,19 @@ check_auth(struct lws *wsi) {
|
|||||||
int
|
int
|
||||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
||||||
unsigned char buffer[4096 + LWS_PRE], *p, *end;
|
unsigned char buffer[4096 + LWS_PRE], *p, *end;
|
||||||
char buf[256];
|
char buf[256], name[100], rip[50];
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case LWS_CALLBACK_HTTP:
|
case LWS_CALLBACK_HTTP:
|
||||||
{
|
// only GET method is allowed
|
||||||
char name[100], rip[50];
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || len < 1) {
|
||||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, sizeof(rip));
|
|
||||||
lwsl_notice("HTTP connect from %s (%s), path: %s\n", name, rip, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 1) {
|
|
||||||
lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL);
|
lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL);
|
||||||
goto try_to_reuse;
|
goto try_to_reuse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, sizeof(rip));
|
||||||
|
lwsl_notice("HTTP %s - %s (%s)\n", in, rip, name);
|
||||||
|
|
||||||
switch (check_auth(wsi)) {
|
switch (check_auth(wsi)) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
@@ -75,10 +73,6 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, voi
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a legal POST URL, let it continue and accept data
|
|
||||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
p = buffer + LWS_PRE;
|
p = buffer + LWS_PRE;
|
||||||
end = p + sizeof(buffer) - LWS_PRE;
|
end = p + sizeof(buffer) - LWS_PRE;
|
||||||
|
|
||||||
@@ -130,7 +124,6 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, voi
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
goto try_to_reuse;
|
goto try_to_reuse;
|
||||||
|
|
||||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||||
if (!len || (SSL_get_verify_result((SSL *) in) != X509_V_OK)) {
|
if (!len || (SSL_get_verify_result((SSL *) in) != X509_V_OK)) {
|
||||||
int err = X509_STORE_CTX_get_error((X509_STORE_CTX *) user);
|
int err = X509_STORE_CTX_get_error((X509_STORE_CTX *) user);
|
||||||
@@ -140,7 +133,6 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, voi
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/index.html
vendored
2
src/index.html
vendored
@@ -16,6 +16,6 @@ break;case 33:t.shiftKey?e.scrollDisp=-(this.rows-1):e.key="[5~";break;case 34:
|
|||||||
<body>
|
<body>
|
||||||
<div id="terminal-container"></div>
|
<div id="terminal-container"></div>
|
||||||
<script src="auth_token.js"></script>
|
<script src="auth_token.js"></script>
|
||||||
<script>!function(){var e,n,o,t=document.getElementById("terminal-container"),s="https:"==window.location.protocol,a=(s?"wss://":"ws://")+window.location.host+window.location.pathname+"ws",i=["tty"],c=-1,r=function(){var s=new WebSocket(a,i);s.onopen=function(a){for(o=!1,"undefined"!=typeof tty_auth_token&&s.send(JSON.stringify({AuthToken:tty_auth_token})),n=setInterval(l,3e4,s),"undefined"!=typeof e&&e.destroy(),e=new Terminal,e.on("resize",function(n){s.readyState===WebSocket.OPEN&&s.send("2"+JSON.stringify({columns:n.cols,rows:n.rows})),setTimeout(function(){e.showOverlay(n.cols+"x"+n.rows)},500)}),e.on("data",function(e){s.readyState===WebSocket.OPEN&&s.send("0"+e)}),window.onresize=function(n){e.fit()};t.firstChild;)t.removeChild(t.firstChild);e.open(t),e.fit(),e.focus()},s.onmessage=function(n){var o=n.data.slice(1);switch(n.data[0]){case"0":e.writeUTF8(window.atob(o));break;case"1":break;case"2":document.title=o;break;case"3":var t=JSON.parse(o);Object.keys(t).forEach(function(n){console.log("Setting "+n+": "+t[n]),e.setOption(n,t[n])});break;case"4":c=JSON.parse(o),console.log("Enabling reconnect: "+c+" seconds")}},s.onclose=function(t){e&&(e.off("data"),e.off("resize"),o||e.showOverlay("Connection Closed",null)),clearInterval(n),c>0&&setTimeout(r,1e3*c)},s.onerror=function(n){o=!0,e.showOverlay("Websocket handshake failed",null)}},l=function(e){e.send("1")};r()}();</script>
|
<script>!function(){var e,o,n,t=document.getElementById("terminal-container"),c="https:"==window.location.protocol,s=(c?"wss://":"ws://")+window.location.host+window.location.pathname+"ws",i="undefined"!=typeof tty_auth_token?tty_auth_token:null,a=["tty"],r=-1,l=function(){var c=new WebSocket(s,a);c.onopen=function(s){for(console.log("Websocket connection opened"),n=!1,c.send(JSON.stringify({AuthToken:i})),o=setInterval(d,3e4,c),"undefined"!=typeof e&&e.destroy(),e=new Terminal,e.on("resize",function(o){c.readyState===WebSocket.OPEN&&c.send("2"+JSON.stringify({columns:o.cols,rows:o.rows})),setTimeout(function(){e.showOverlay(o.cols+"x"+o.rows)},500)}),e.on("data",function(e){c.readyState===WebSocket.OPEN&&c.send("0"+e)}),e.on("open",function(){window.onresize=function(o){e.fit()},e.fit(),e.focus()});t.firstChild;)t.removeChild(t.firstChild);e.open(t)},c.onmessage=function(o){var n=o.data.slice(1);switch(o.data[0]){case"0":e.writeUTF8(window.atob(n));break;case"1":break;case"2":document.title=n;break;case"3":var t=JSON.parse(n);Object.keys(t).forEach(function(o){console.log("Setting "+o+": "+t[o]),e.setOption(o,t[o])});break;case"4":r=JSON.parse(n),console.log("Enabling reconnect: "+r+" seconds")}},c.onclose=function(t){console.log("Websocket connection closed with code: "+t.code),e&&(e.off("data"),e.off("resize"),n||e.showOverlay("Connection Closed",null)),clearInterval(o),r>0&&setTimeout(l,1e3*r)}},d=function(e){e.send("1")};l()}();</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
// client message
|
|
||||||
#define INPUT '0'
|
|
||||||
#define PING '1'
|
|
||||||
#define RESIZE_TERMINAL '2'
|
|
||||||
#define JSON_DATA '{'
|
|
||||||
|
|
||||||
// server message
|
|
||||||
#define OUTPUT '0'
|
|
||||||
#define PONG '1'
|
|
||||||
#define SET_WINDOW_TITLE '2'
|
|
||||||
#define SET_PREFERENCES '3'
|
|
||||||
#define SET_RECONNECT '4'
|
|
||||||
|
|
||||||
#define BUF_SIZE 1024
|
#define BUF_SIZE 1024
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -42,30 +29,29 @@ send_initial_message(struct lws *wsi) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct winsize *
|
bool
|
||||||
parse_window_size(const char *json) {
|
parse_window_size(const char *json, struct winsize *size) {
|
||||||
int columns, rows;
|
int columns, rows;
|
||||||
json_object *obj = json_tokener_parse(json);
|
json_object *obj = json_tokener_parse(json);
|
||||||
struct json_object *o = NULL;
|
struct json_object *o = NULL;
|
||||||
|
|
||||||
if (!json_object_object_get_ex(obj, "columns", &o)) {
|
if (!json_object_object_get_ex(obj, "columns", &o)) {
|
||||||
lwsl_err("columns field not exists!\n");
|
lwsl_err("columns field not exists!\n");
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
columns = json_object_get_int(o);
|
columns = json_object_get_int(o);
|
||||||
if (!json_object_object_get_ex(obj, "rows", &o)) {
|
if (!json_object_object_get_ex(obj, "rows", &o)) {
|
||||||
lwsl_err("rows field not exists!\n");
|
lwsl_err("rows field not exists!\n");
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
rows = json_object_get_int(o);
|
rows = json_object_get_int(o);
|
||||||
json_object_put(obj);
|
json_object_put(obj);
|
||||||
|
|
||||||
struct winsize *size = xmalloc(sizeof(struct winsize));
|
|
||||||
memset(size, 0, sizeof(struct winsize));
|
memset(size, 0, sizeof(struct winsize));
|
||||||
size->ws_col = (unsigned short) columns;
|
size->ws_col = (unsigned short) columns;
|
||||||
size->ws_row = (unsigned short) rows;
|
size->ws_row = (unsigned short) rows;
|
||||||
|
|
||||||
return size;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -93,11 +79,9 @@ check_host_origin(struct lws *wsi) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
tty_client_destroy(struct tty_client *client) {
|
tty_client_destroy(struct tty_client *client) {
|
||||||
if (client->exit || client->pid <= 0)
|
if (!client->running || client->pid <= 0)
|
||||||
return;
|
return;
|
||||||
|
client->running = false;
|
||||||
// stop event loop
|
|
||||||
client->exit = true;
|
|
||||||
|
|
||||||
// kill process and free resource
|
// kill process and free resource
|
||||||
lwsl_notice("sending %s to process %d\n", server->sig_name, client->pid);
|
lwsl_notice("sending %s to process %d\n", server->sig_name, client->pid);
|
||||||
@@ -114,7 +98,7 @@ tty_client_destroy(struct tty_client *client) {
|
|||||||
if (client->buffer != NULL)
|
if (client->buffer != NULL)
|
||||||
free(client->buffer);
|
free(client->buffer);
|
||||||
|
|
||||||
// remove from clients list
|
// remove from client list
|
||||||
pthread_mutex_lock(&server->lock);
|
pthread_mutex_lock(&server->lock);
|
||||||
LIST_REMOVE(client, list);
|
LIST_REMOVE(client, list);
|
||||||
server->client_count--;
|
server->client_count--;
|
||||||
@@ -150,14 +134,16 @@ thread_run_command(void *args) {
|
|||||||
lwsl_notice("started process, pid: %d\n", pid);
|
lwsl_notice("started process, pid: %d\n", pid);
|
||||||
client->pid = pid;
|
client->pid = pid;
|
||||||
client->pty = pty;
|
client->pty = pty;
|
||||||
|
client->running = true;
|
||||||
|
if (client->size.ws_row > 0 && client->size.ws_col > 0)
|
||||||
|
ioctl(client->pty, TIOCSWINSZ, &client->size);
|
||||||
|
|
||||||
while (!client->exit) {
|
while (client->running) {
|
||||||
FD_ZERO (&des_set);
|
FD_ZERO (&des_set);
|
||||||
FD_SET (pty, &des_set);
|
FD_SET (pty, &des_set);
|
||||||
|
|
||||||
if (select(pty + 1, &des_set, NULL, NULL, NULL) < 0) {
|
if (select(pty + 1, &des_set, NULL, NULL, NULL) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (FD_ISSET (pty, &des_set)) {
|
if (FD_ISSET (pty, &des_set)) {
|
||||||
memset(buf, 0, BUF_SIZE);
|
memset(buf, 0, BUF_SIZE);
|
||||||
@@ -173,7 +159,6 @@ thread_run_command(void *args) {
|
|||||||
pthread_mutex_unlock(&client->lock);
|
pthread_mutex_unlock(&client->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tty_client_destroy(client);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,22 +169,27 @@ int
|
|||||||
callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
||||||
void *user, void *in, size_t len) {
|
void *user, void *in, size_t len) {
|
||||||
struct tty_client *client = (struct tty_client *) user;
|
struct tty_client *client = (struct tty_client *) user;
|
||||||
struct winsize *size;
|
char buf[256];
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
||||||
if (server->once && server->client_count > 0) {
|
if (server->once && server->client_count > 0) {
|
||||||
lwsl_notice("refuse to serve new client due to the --once option.\n");
|
lwsl_warn("refuse to serve WS client due to the --once option.\n");
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) <= 0 || strcmp(buf, WS_PATH)) {
|
||||||
|
lwsl_warn("refuse to serve WS client for illegal ws path: %s\n", buf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (server->check_origin && !check_host_origin(wsi)) {
|
if (server->check_origin && !check_host_origin(wsi)) {
|
||||||
lwsl_notice("refuse to serve new client from different origin due to the --check-origin option.\n");
|
lwsl_warn("refuse to serve WS client from different origin due to the --check-origin option.\n");
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
client->exit = false;
|
client->running = false;
|
||||||
client->initialized = false;
|
client->initialized = false;
|
||||||
client->authenticated = false;
|
client->authenticated = false;
|
||||||
client->wsi = wsi;
|
client->wsi = wsi;
|
||||||
@@ -208,17 +198,14 @@ callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
client->hostname, sizeof(client->hostname),
|
client->hostname, sizeof(client->hostname),
|
||||||
client->address, sizeof(client->address));
|
client->address, sizeof(client->address));
|
||||||
STAILQ_INIT(&client->queue);
|
STAILQ_INIT(&client->queue);
|
||||||
if (pthread_create(&client->thread, NULL, thread_run_command, client) != 0) {
|
|
||||||
lwsl_err("pthread_create\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&server->lock);
|
pthread_mutex_lock(&server->lock);
|
||||||
LIST_INSERT_HEAD(&server->clients, client, list);
|
LIST_INSERT_HEAD(&server->clients, client, list);
|
||||||
server->client_count++;
|
server->client_count++;
|
||||||
pthread_mutex_unlock(&server->lock);
|
pthread_mutex_unlock(&server->lock);
|
||||||
|
lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI);
|
||||||
|
|
||||||
lwsl_notice("client connected from %s (%s), total: %d\n", client->hostname, client->address, server->client_count);
|
lwsl_notice("WS %s - %s (%s), clients: %d\n", buf, client->address, client->hostname, server->client_count);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
@@ -280,8 +267,8 @@ callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
|
|
||||||
// check auth
|
// check auth
|
||||||
if (server->credential != NULL && !client->authenticated && command != JSON_DATA) {
|
if (server->credential != NULL && !client->authenticated && command != JSON_DATA) {
|
||||||
lwsl_notice("websocket authentication failed\n");
|
lwsl_warn("WS client not authenticated\n");
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if there are more fragmented messages
|
// check if there are more fragmented messages
|
||||||
@@ -291,6 +278,8 @@ callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case INPUT:
|
case INPUT:
|
||||||
|
if (client->pty == 0)
|
||||||
|
break;
|
||||||
if (server->readonly)
|
if (server->readonly)
|
||||||
return 0;
|
return 0;
|
||||||
if (write(client->pty, client->buffer + 1, client->len - 1) < client->len - 1) {
|
if (write(client->pty, client->buffer + 1, client->len - 1) < client->len - 1) {
|
||||||
@@ -308,32 +297,34 @@ callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RESIZE_TERMINAL:
|
case RESIZE_TERMINAL:
|
||||||
size = parse_window_size(client->buffer + 1);
|
if (parse_window_size(client->buffer + 1, &client->size) && client->pty > 0) {
|
||||||
if (size != NULL) {
|
if (ioctl(client->pty, TIOCSWINSZ, &client->size) == -1) {
|
||||||
if (ioctl(client->pty, TIOCSWINSZ, size) == -1) {
|
|
||||||
lwsl_err("ioctl TIOCSWINSZ: %d (%s)\n", errno, strerror(errno));
|
lwsl_err("ioctl TIOCSWINSZ: %d (%s)\n", errno, strerror(errno));
|
||||||
}
|
}
|
||||||
free(size);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSON_DATA:
|
case JSON_DATA:
|
||||||
if (server->credential == NULL)
|
if (client->pid > 0)
|
||||||
break;
|
break;
|
||||||
{
|
if (server->credential != NULL) {
|
||||||
json_object *obj = json_tokener_parse(client->buffer);
|
json_object *obj = json_tokener_parse(client->buffer);
|
||||||
struct json_object *o = NULL;
|
struct json_object *o = NULL;
|
||||||
if (json_object_object_get_ex(obj, "AuthToken", &o)) {
|
if (json_object_object_get_ex(obj, "AuthToken", &o)) {
|
||||||
const char *token = json_object_get_string(o);
|
const char *token = json_object_get_string(o);
|
||||||
if (strcmp(token, server->credential)) {
|
if (token == NULL || strcmp(token, server->credential)) {
|
||||||
lwsl_notice("websocket authentication failed with token: %s\n", token);
|
lwsl_warn("WS authentication failed with token: %s\n", token);
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client->authenticated = true;
|
client->authenticated = true;
|
||||||
}
|
}
|
||||||
|
if (pthread_create(&client->thread, NULL, thread_run_command, client) != 0) {
|
||||||
|
lwsl_err("pthread_create\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
lwsl_notice("unknown message type: %c\n", command);
|
lwsl_warn("unknown message type: %c\n", command);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +336,7 @@ callback_tty(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
|
|
||||||
case LWS_CALLBACK_CLOSED:
|
case LWS_CALLBACK_CLOSED:
|
||||||
tty_client_destroy(client);
|
tty_client_destroy(client);
|
||||||
lwsl_notice("client disconnected from %s (%s), total: %d\n", client->hostname, client->address, server->client_count);
|
lwsl_notice("WS closed from %s (%s), clients: %d\n", client->address, client->hostname, server->client_count);
|
||||||
if (server->once && server->client_count == 0) {
|
if (server->once && server->client_count == 0) {
|
||||||
lwsl_notice("exiting due to the --once option.\n");
|
lwsl_notice("exiting due to the --once option.\n");
|
||||||
force_exit = true;
|
force_exit = true;
|
||||||
|
|||||||
36
src/server.c
36
src/server.c
@@ -67,9 +67,10 @@ void print_help() {
|
|||||||
" --ssl-cert, -C SSL certificate file path\n"
|
" --ssl-cert, -C SSL certificate file path\n"
|
||||||
" --ssl-key, -K SSL key file path\n"
|
" --ssl-key, -K SSL key file path\n"
|
||||||
" --ssl-ca, -A SSL CA file path for client certificate verification\n"
|
" --ssl-ca, -A SSL CA file path for client certificate verification\n"
|
||||||
" --debug, -d Set log level (0-9, default: 7)\n"
|
" --debug, -d Set log level (default: 7)\n"
|
||||||
" --version, -v Print the version and exit\n"
|
" --version, -v Print the version and exit\n"
|
||||||
" --help, -h Print this text and exit\n",
|
" --help, -h Print this text and exit\n\n"
|
||||||
|
"Visit https://github.com/tsl0922/ttyd to get more information and report bugs.\n",
|
||||||
TTYD_VERSION
|
TTYD_VERSION
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,7 @@ main(int argc, char **argv) {
|
|||||||
info.extensions = extensions;
|
info.extensions = extensions;
|
||||||
info.timeout_secs = 5;
|
info.timeout_secs = 5;
|
||||||
|
|
||||||
int debug_level = 7;
|
int debug_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||||
char iface[128] = "";
|
char iface[128] = "";
|
||||||
bool ssl = false;
|
bool ssl = false;
|
||||||
char cert_path[1024] = "";
|
char cert_path[1024] = "";
|
||||||
@@ -371,6 +372,7 @@ main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl) {
|
if (ssl) {
|
||||||
info.ssl_cert_filepath = cert_path;
|
info.ssl_cert_filepath = cert_path;
|
||||||
info.ssl_private_key_filepath = key_path;
|
info.ssl_private_key_filepath = key_path;
|
||||||
@@ -395,16 +397,8 @@ main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(SIGINT, sig_handler); // ^C
|
lwsl_notice("ttyd %s (libwebsockets %s)\n", TTYD_VERSION, LWS_LIBRARY_VERSION);
|
||||||
signal(SIGTERM, sig_handler); // kill
|
lwsl_notice("tty configuration:\n");
|
||||||
|
|
||||||
context = lws_create_context(&info);
|
|
||||||
if (context == NULL) {
|
|
||||||
lwsl_err("libwebsockets init failed\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwsl_notice("TTY configuration:\n");
|
|
||||||
if (server->credential != NULL)
|
if (server->credential != NULL)
|
||||||
lwsl_notice(" credential: %s\n", server->credential);
|
lwsl_notice(" credential: %s\n", server->credential);
|
||||||
lwsl_notice(" start command: %s\n", server->command);
|
lwsl_notice(" start command: %s\n", server->command);
|
||||||
@@ -420,13 +414,27 @@ main(int argc, char **argv) {
|
|||||||
lwsl_notice(" custom index.html: %s\n", server->index);
|
lwsl_notice(" custom index.html: %s\n", server->index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, sig_handler); // ^C
|
||||||
|
signal(SIGTERM, sig_handler); // kill
|
||||||
|
|
||||||
|
context = lws_create_context(&info);
|
||||||
|
if (context == NULL) {
|
||||||
|
lwsl_err("libwebsockets init failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (server->socket_path != NULL) {
|
||||||
|
lwsl_notice("listening on socket %s\n", server->socket_path);
|
||||||
|
} else {
|
||||||
|
lwsl_notice("listening on port %d\n", info.port);
|
||||||
|
}
|
||||||
|
|
||||||
// libwebsockets main loop
|
// libwebsockets main loop
|
||||||
while (!force_exit) {
|
while (!force_exit) {
|
||||||
pthread_mutex_lock(&server->lock);
|
pthread_mutex_lock(&server->lock);
|
||||||
if (!LIST_EMPTY(&server->clients)) {
|
if (!LIST_EMPTY(&server->clients)) {
|
||||||
struct tty_client *client;
|
struct tty_client *client;
|
||||||
LIST_FOREACH(client, &server->clients, list) {
|
LIST_FOREACH(client, &server->clients, list) {
|
||||||
if (!STAILQ_EMPTY(&client->queue)) {
|
if (client->running && !STAILQ_EMPTY(&client->queue)) {
|
||||||
lws_callback_on_writable(client->wsi);
|
lws_callback_on_writable(client->wsi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/server.h
19
src/server.h
@@ -35,6 +35,22 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
// client message
|
||||||
|
#define INPUT '0'
|
||||||
|
#define PING '1'
|
||||||
|
#define RESIZE_TERMINAL '2'
|
||||||
|
#define JSON_DATA '{'
|
||||||
|
|
||||||
|
// server message
|
||||||
|
#define OUTPUT '0'
|
||||||
|
#define PONG '1'
|
||||||
|
#define SET_WINDOW_TITLE '2'
|
||||||
|
#define SET_PREFERENCES '3'
|
||||||
|
#define SET_RECONNECT '4'
|
||||||
|
|
||||||
|
// websocket url path
|
||||||
|
#define WS_PATH "/ws"
|
||||||
|
|
||||||
extern volatile bool force_exit;
|
extern volatile bool force_exit;
|
||||||
extern struct lws_context *context;
|
extern struct lws_context *context;
|
||||||
extern struct tty_server *server;
|
extern struct tty_server *server;
|
||||||
@@ -46,13 +62,14 @@ struct pty_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct tty_client {
|
struct tty_client {
|
||||||
bool exit;
|
bool running;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
bool authenticated;
|
bool authenticated;
|
||||||
char hostname[100];
|
char hostname[100];
|
||||||
char address[50];
|
char address[50];
|
||||||
|
|
||||||
struct lws *wsi;
|
struct lws *wsi;
|
||||||
|
struct winsize size;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
size_t len;
|
size_t len;
|
||||||
int pid;
|
int pid;
|
||||||
|
|||||||
Reference in New Issue
Block a user